def action_invoice_create(self, grouped=False, final=False): inv_obj = self.env['account.invoice'] precision = self.env['decimal.precision'].precision_get('Product Unit of Measure') invoices = {} for order in self: group_key = order.id if grouped else (order.partner_id.id, order.currency_id.id) for line in order.order_line.sorted(key=lambda l: l.qty_to_invoice < 0): if float_is_zero(line.qty_to_invoice, precision_digits=precision): continue if group_key not in invoices: inv_data = order._prepare_invoice() invoice = inv_obj.create(inv_data) invoices[group_key] = invoice if line.qty_to_invoice > 0: line.invoice_line_create(invoices[group_key].id, line.qty_to_invoice) elif line.qty_to_invoice < 0 and (final or invoices[group_key].amount_untaxed > abs(line.qty_to_invoice * line.price_unit)): line.invoice_line_create(invoices[group_key].id, line.qty_to_invoice) for invoice in invoices.values(): # If invoice is negative, do a refund invoice instead if invoice.amount_untaxed < 0: invoice.type = 'out_refund' for line in invoice.invoice_line_ids: line.quantity = -line.quantity # Necessary to force computation of taxes. In account_invoice, they are triggered # by onchanges, which are not triggered when doing a create. invoice.compute_taxes() return [inv.id for inv in invoices.values()]
def invoice_line_create(self, invoice_id, qty): precision = self.env['decimal.precision'].precision_get('Product Unit of Measure') for line in self: if not float_is_zero(qty, precision_digits=precision): vals = line._prepare_invoice_line(qty=qty) vals.update({'invoice_id': invoice_id, 'sale_line_ids': [(6, 0, [line.id])]}) self.env['account.invoice.line'].create(vals)
def action_invoice_create(self, grouped=False, final=False): """ Create the invoice associated to the SO. :param grouped: if True, invoices are grouped by SO id. If False, invoices are grouped by (partner_invoice_id, currency) :param final: if True, refunds will be generated if necessary :returns: list of created invoices """ inv_obj = self.env['account.invoice'] precision = self.env['decimal.precision'].precision_get('Product Unit of Measure') invoices = {} references = {} for order in self: group_key = order.id if grouped else (order.partner_invoice_id.id, order.currency_id.id) for line in order.order_line.sorted(key=lambda l: l.qty_to_invoice < 0): if float_is_zero(line.qty_to_invoice, precision_digits=precision): continue if group_key not in invoices: inv_data = order._prepare_invoice() invoice = inv_obj.create(inv_data) references[invoice] = order invoices[group_key] = invoice elif group_key in invoices: vals = {} if order.name not in invoices[group_key].origin.split(', '): vals['origin'] = invoices[group_key].origin + ', ' + order.name if order.client_order_ref and order.client_order_ref not in invoices[group_key].name.split(', '): vals['name'] = invoices[group_key].name + ', ' + order.client_order_ref invoices[group_key].write(vals) if line.qty_to_invoice > 0: line.invoice_line_create(invoices[group_key].id, line.qty_to_invoice) elif line.qty_to_invoice < 0 and final: line.invoice_line_create(invoices[group_key].id, line.qty_to_invoice) if references.get(invoices.get(group_key)): if order not in references[invoices[group_key]]: references[invoice] = references[invoice] | order if not invoices: raise UserError(_('There is no invoicable line.')) for invoice in invoices.values(): if not invoice.invoice_line_ids: raise UserError(_('There is no invoicable line.')) # If invoice is negative, do a refund invoice instead if invoice.amount_untaxed < 0: invoice.type = 'out_refund' for line in invoice.invoice_line_ids: line.quantity = -line.quantity # Use additional field helper function (for account extensions) for line in invoice.invoice_line_ids: line._set_additional_fields(invoice) # Necessary to force computation of taxes. In account_invoice, they are triggered # by onchanges, which are not triggered when doing a create. invoice.compute_taxes() invoice.message_post_with_view('mail.message_origin_link', values={'self': invoice, 'origin': references[invoice]}, subtype_id=self.env.ref('mail.mt_note').id) return [inv.id for inv in invoices.values()]
def _quants_get_order_all_locations(self, product, quantity, domain=[], orderby='in_date'): domain += [('product_id', '=', product.id)] if self.env.context.get('force_company'): domain += [('company_id', '=', self.env.context.get('force_company'))] else: domain += [('company_id', '=', self.env.user.company_id.id)] res = [] offset = 0 while float_compare(quantity, 0, precision_rounding=product.uom_id.rounding) > 0: quants = self.search(domain, order=orderby, limit=10, offset=offset) # for quant in quants: # location_id = quant.location_id.id # if location_id not in _location_usable_cache: # _location_usable_cache[location_id] = product.with_context( # location=location_id # ).qty_usable if not quants: res.append((None, quantity)) break for quant in quants: rounding = product.uom_id.rounding apply_qty = quant.qty if float_compare(value1=apply_qty, value2=0, precision_rounding=rounding) <= 0: continue res += [(quant, apply_qty)] quantity -= apply_qty if float_is_zero(quantity, precision_rounding=rounding): break offset += 10 return res
def _compute_invoice_status(self): """ Compute the invoice status of a SO line. Possible statuses: - no: if the SO is not in status 'sale' or 'done', we consider that there is nothing to invoice. This is also hte default value if the conditions of no other status is met. - to invoice: we refer to the quantity to invoice of the line. Refer to method `_get_to_invoice_qty()` for more information on how this quantity is calculated. - upselling: this is possible only for a product invoiced on ordered quantities for which we delivered more than expected. The could arise if, for example, a project took more time than expected but we decided not to invoice the extra cost to the client. This occurs onyl in state 'sale', so that when a SO is set to done, the upselling opportunity is removed from the list. - invoiced: the quantity invoiced is larger or equal to the quantity ordered. """ precision = self.env['decimal.precision'].precision_get('Product Unit of Measure') for line in self: if line.state not in ('sale', 'done'): line.invoice_status = 'no' elif not float_is_zero(line.qty_to_invoice, precision_digits=precision): line.invoice_status = 'to invoice' elif line.state == 'sale' and line.product_id.invoice_policy == 'order' and\ float_compare(line.qty_delivered, line.product_uom_qty, precision_digits=precision) == 1: line.invoice_status = 'upselling' elif float_compare(line.qty_invoiced, line.product_uom_qty, precision_digits=precision) >= 0: line.invoice_status = 'invoiced' else: line.invoice_status = 'no'
def _resolve_inventory_line(self, inventory_line): if inventory_line.product_id.cost_method == 'real': price = inventory_line.get_price( ) product_qty = inventory_line.product_qty if not float_is_zero(abs(inventory_line.standard_price - price), precision_digits=2 ): line_price = inventory_line.standard_price inventory_line.write( {'standard_price': price, 'product_qty':0.0 } ) inventory_line.product_id.write({'standard_price':price} ) move_id = super(stock_inventory_line,self)._resolve_inventory_line( inventory_line ) if move_id: move = self.env['stock.move'].browse(move_id) move.action_done() inventory_line.write( {'standard_price': line_price, 'product_qty':product_qty + inventory_line.theoretical_qty } ) inventory_line.product_id.write( {'standard_price':inventory_line.standard_price} ) move_id = super(stock_inventory_line,self)._resolve_inventory_line( inventory_line ) if product_qty <> inventory_line.product_qty: inventory_line.write( {'product_qty':product_qty } ) if move_id: move = self.env['stock.move'].browse(move_id) move.action_done() return move_id
def compute_full_after_batch_reconcile(self): aml_id, partial_rec_id = super(AccountMoveLine, self).compute_full_after_batch_reconcile() #check if the reconcilation is full partial_rec_set = self.env['account.partial.reconcile'] total_debit = 0 total_credit = 0 total_amount_currency = 0 currency = False # If we have an exchange rate entry, add it's value to amount_currency if aml_id and partial_rec_id: exchange_rate_aml = self.browse([aml_id]) exchange_rate_partial_rec = self.env['account.partial.reconcile'].browse([partial_rec_id]) total_amount_currency += exchange_rate_aml.amount_currency partial_rec_set |= exchange_rate_partial_rec for aml in self: total_debit += aml.debit total_credit += aml.credit if not currency and aml.currency_id: currency = aml.currency_id if aml.currency_id and aml.currency_id == currency: total_amount_currency += aml.amount_currency partial_rec_set |= aml.matched_debit_ids | aml.matched_credit_ids partial_rec_ids = [x.id for x in list(partial_rec_set)] #if the total debit and credit are equal, and the total amount in currency is 0, the reconciliation is full digits_rounding_precision = self[0].company_id.currency_id.rounding if float_compare(total_debit, total_credit, precision_rounding=digits_rounding_precision) == 0 \ and (not currency or float_is_zero(total_amount_currency, precision_rounding=currency.rounding)): #in that case, mark the reference on the partial reconciliations and the entries self.env['account.full.reconcile'].with_context(check_move_validity=False).create({ 'partial_reconcile_ids': [(4, p_id) for p_id in partial_rec_ids], 'reconciled_line_ids': [(4, r_id) for r_id in ((self + exchange_rate_aml).ids if aml_id else self.ids)], })
def _compute_residual(self): fp_company = self.env['account.fiscal.position'].search( [('id', '=', self.company_id.partner_id.property_account_position_id.id)]) company_tax_ids = [base_tax.tax_id.id for base_tax in fp_company.tax_ids_invoice] residual = 0.0 residual_company_signed = 0.0 sign = self.type in ['in_refund', 'out_refund'] and -1 or 1 for line in self.sudo().move_id.line_ids: if line.tax_line_id.id not in company_tax_ids: if line.account_id.internal_type in ('receivable', 'payable'): residual_company_signed += line.amount_residual if line.currency_id == self.currency_id: residual += line.amount_residual_currency if line.currency_id else line.amount_residual else: from_currency = (line.currency_id and line.currency_id.with_context(date=line.date)) or line.company_id.currency_id.with_context(date=line.date) residual += from_currency.compute(line.amount_residual, self.currency_id) self.residual_company_signed = abs(residual_company_signed) * sign self.residual_signed = abs(residual) * sign self.residual = abs(residual) digits_rounding_precision = self.currency_id.rounding if float_is_zero(self.residual, precision_rounding=digits_rounding_precision): self.reconciled = True else: self.reconciled = False
def _resolve_inventory_line(self, inventory_line): product_qty = inventory_line.product_qty if inventory_line.product_id.cost_method == 'real': price = inventory_line.get_price( ) if not float_is_zero(abs(inventory_line.standard_price - price), precision_digits=2 ): # se completeaza o line de inventar cu cantitate zero si cu vechiul pret line_price = inventory_line.standard_price inventory_line.write( {'standard_price': price, 'product_qty':0.0 } ) inventory_line.product_id.write({'standard_price':price} ) move_id = super(stock_inventory_line,self)._resolve_inventory_line( inventory_line ) inventory_line.write( {'standard_price': line_price, 'product_qty':product_qty + inventory_line.theoretical_qty } ) inventory_line.product_id.write( {'standard_price':inventory_line.standard_price} ) # acutlizare pret in produs move_id = super(stock_inventory_line,self)._resolve_inventory_line( inventory_line ) if product_qty <> inventory_line.product_qty: inventory_line.write( {'product_qty':product_qty } ) if move_id: move = self.env['stock.move'].browse(move_id) move.action_done() return move_id
def action_produce(self, production_id, production_qty, production_mode, wiz=False): if production_mode == 'only_produce': production = self.browse(production_id) prod_name = production.name procurement_group = self.env['procurement.group'].search( [('name', '=', prod_name)], limit=1) if not procurement_group: procurement_group = self.env['procurement.group'].create( {'name': prod_name}) self = self.with_context(set_push_group=procurement_group.id) # Volvemos a hacer browse para que use el context correcto production = self.browse(production_id) precision = self.env['decimal.precision'].precision_get( 'Product Unit of Measure') production_qty_uom = self.env['product.uom']._compute_qty( production.product_uom.id, production_qty, production.product_id.uom_id.id) # To produce remaining qty of final product produced_products = {} for produced_product in production.move_created_ids2: if produced_product.scrapped: continue if not produced_products.get(produced_product.product_id.id, False): produced_products[produced_product.product_id.id] = 0 produced_products[produced_product.product_id.id] += \ produced_product.product_qty for produce_product in production.move_created_ids: subproduct_factor = production._get_subproduct_factor( produce_product.id) lot_id = False if wiz: lot_id = wiz.lot_id.id qty = min(subproduct_factor * production_qty_uom, produce_product.product_qty) new_moves = produce_product.action_consume( qty, location_id=produce_product.location_id.id, restrict_lot_id=lot_id) new_moves = self.env['stock.move'].browse(new_moves) new_moves.write({'production_id': production_id}) remaining_qty = subproduct_factor * production_qty_uom - qty if not float_is_zero(remaining_qty, precision_digits=precision): extra_move = produce_product.copy( default={'product_uom_qty': remaining_qty, 'production_id': production_id}) # Cancelamos disponibilidad de albaranes ya creados # Para que asigne con el nuevo movimiento pickings = (production.move_created_ids + production.move_created_ids2).mapped( 'move_dest_id.picking_id').filtered( lambda r: r.state == 'assigned') pickings.do_unreserve() extra_move.action_confirm() pickings.rereserve_pick() extra_move.action_done() return super(MrpProduction, self).action_produce( production_id, production_qty, production_mode, wiz)
def create(self, vals): res = super(AccountPartialReconcile, self).create(vals) #check if the reconcilation is full #first, gather all journal items involved in the reconciliation just created partial_rec_set = OrderedDict.fromkeys([x for x in res]) aml_set = self.env['account.move.line'] total_debit = 0 total_credit = 0 total_amount_currency = 0 currency = None for partial_rec in partial_rec_set: if currency is None: currency = partial_rec.currency_id for aml in [partial_rec.debit_move_id, partial_rec.credit_move_id]: if aml not in aml_set: total_debit += aml.debit total_credit += aml.credit aml_set |= aml if aml.currency_id and aml.currency_id == currency: total_amount_currency += aml.amount_currency for x in aml.matched_debit_ids | aml.matched_credit_ids: partial_rec_set[x] = None partial_rec_ids = [x.id for x in partial_rec_set.keys()] aml_ids = [x.id for x in aml_set] #then, if the total debit and credit are equal, or the total amount in currency is 0, the reconciliation is full digits_rounding_precision = aml_set[0].company_id.currency_id.rounding if float_compare(total_debit, total_credit, precision_rounding=digits_rounding_precision) == 0 \ or (currency and float_is_zero(total_amount_currency, precision_rounding=currency.rounding)): #in that case, mark the reference on the partial reconciliations and the entries self.env['account.full.reconcile'].with_context(check_move_validity=False).create({ 'partial_reconcile_ids': [(6, 0, partial_rec_ids)], 'reconciled_line_ids': [(6, 0, aml_ids)]}) return res
def compute_full_after_batch_reconcile(self): super(AccountMoveLine, self).compute_full_after_batch_reconcile() #check if the reconcilation is full partial_rec_set = self.env['account.partial.reconcile'] total_debit = 0 total_credit = 0 total_amount_currency = 0 currency = False for aml in self: total_debit += aml.debit total_credit += aml.credit if not currency and aml.currency_id: currency = aml.currency_id if aml.currency_id and aml.currency_id == currency: total_amount_currency += aml.amount_currency partial_rec_set |= aml.matched_debit_ids | aml.matched_credit_ids partial_rec_ids = [x.id for x in list(partial_rec_set)] #if the total debit and credit are equal, and the total amount in currency is 0, the reconciliation is full digits_rounding_precision = self[0].company_id.currency_id.rounding if float_compare(total_debit, total_credit, precision_rounding=digits_rounding_precision) == 0 \ and (not currency or float_is_zero(total_amount_currency, precision_rounding=currency.rounding)): #in that case, mark the reference on the partial reconciliations and the entries self.env['account.full.reconcile'].with_context(check_move_validity=False).create({ 'partial_reconcile_ids': [(6, 0, partial_rec_ids)], 'reconciled_line_ids': [(6, 0, self.ids)]})
def compute_depreciation_board(self): self.ensure_one() posted_depreciation_line_ids = self.depreciation_line_ids.filtered(lambda x: x.move_check).sorted(key=lambda l: l.depreciation_date) unposted_depreciation_line_ids = self.depreciation_line_ids.filtered(lambda x: not x.move_check) # Remove old unposted depreciation lines. We cannot use unlink() with One2many field commands = [(2, line_id.id, False) for line_id in unposted_depreciation_line_ids] if self.value_residual != 0.0: amount_to_depr = residual_amount = self.value_residual if self.prorata: depreciation_date = datetime.strptime(self._get_last_depreciation_date()[self.id], DF).date() else: # depreciation_date = 1st of January of purchase year if annual valuation, 1st of # purchase month in other cases if self.method_period >= 12: asset_date = datetime.strptime(self.date[:4] + '-01-01', DF).date() else: asset_date = datetime.strptime(self.date[:7] + '-01', DF).date() # if we already have some previous validated entries, starting date isn't 1st January but last entry + method period if posted_depreciation_line_ids and posted_depreciation_line_ids[-1].depreciation_date: last_depreciation_date = datetime.strptime(posted_depreciation_line_ids[-1].depreciation_date, DF).date() depreciation_date = last_depreciation_date + relativedelta(months=+self.method_period) else: depreciation_date = asset_date day = depreciation_date.day month = depreciation_date.month year = depreciation_date.year total_days = (year % 4) and 365 or 366 undone_dotation_number = self._compute_board_undone_dotation_nb(depreciation_date, total_days) for x in range(len(posted_depreciation_line_ids), undone_dotation_number): sequence = x + 1 amount = self._compute_board_amount(sequence, residual_amount, amount_to_depr, undone_dotation_number, posted_depreciation_line_ids, total_days, depreciation_date) amount = self.currency_id.round(amount) if float_is_zero(amount, precision_rounding=self.currency_id.rounding): continue residual_amount -= amount vals = { 'amount': amount, 'asset_id': self.id, 'sequence': sequence, 'name': (self.code or '') + '/' + str(sequence), 'remaining_value': residual_amount, 'depreciated_value': self.value - (self.salvage_value + residual_amount), 'depreciation_date': depreciation_date.strftime(DF), } commands.append((0, False, vals)) # Considering Depr. Period as months depreciation_date = date(year, month, day) + relativedelta(months=+self.method_period) day = depreciation_date.day month = depreciation_date.month year = depreciation_date.year self.write({'depreciation_line_ids': commands}) return True
def action_produce(self, cr, uid, production_id, production_qty, production_mode, wiz=False, context=None): if context is None: context = {} context = dict(context) stock_mov_obj = self.pool.get('stock.move') uom_obj = self.pool.get("product.uom") production = self.browse(cr, uid, production_id, context=context) production_qty_uom = uom_obj._compute_qty(cr, uid, production.product_uom.id, production_qty, production.product_id.uom_id.id) precision = self.pool['decimal.precision'].precision_get(cr, uid, 'Product Unit of Measure') main_production_move = False if production_mode == 'produce': # New Case: Produce Only produced_products = {} for produced_product in production.move_created_ids2: if produced_product.scrapped: continue if not produced_products.get(produced_product.product_id.id, False): produced_products[produced_product.product_id.id] = 0 produced_products[produced_product.product_id.id] += produced_product.product_qty for produce_product in production.move_created_ids: subproduct_factor = self._get_subproduct_factor(cr, uid, production.id, produce_product.id, context=context) lot_id = False if wiz: lot_id = wiz.lot_id.id qty = min(subproduct_factor * production_qty_uom, produce_product.product_qty) #Needed when producing more than maximum quantity new_moves = stock_mov_obj.action_consume(cr, uid, [produce_product.id], qty, location_id=produce_product.location_id.id, restrict_lot_id=lot_id, context=context) stock_mov_obj.write(cr, uid, new_moves, {'production_id': production_id}, context=context) remaining_qty = subproduct_factor * production_qty_uom - qty if not float_is_zero(remaining_qty, precision_digits=precision): # In case you need to make more than planned # consumed more in wizard than previously planned extra_move_id = stock_mov_obj.copy(cr, uid, produce_product.id, default={'product_uom_qty': remaining_qty, 'production_id': production_id}, context=context) stock_mov_obj.action_confirm(cr, uid, [extra_move_id], context=context) stock_mov_obj.action_done(cr, uid, [extra_move_id], context=context) if produce_product.product_id.id == production.product_id.id: main_production_move = produce_product.id if not production.move_created_ids and \ not (context.get('default_mode', False) and context['default_mode'] == 'consume'): self.signal_workflow(cr, uid, [production_id], 'button_finished_validated') else: if not main_production_move: main_production_move = production.move_created_ids2 and production.move_created_ids2[0].id context.update({'main_production_move': main_production_move}) # Para escribirlo en el write y en el action_consume() res = super(mrp_production, self).action_produce(cr, uid, production_id, production_qty, production_mode, wiz=wiz, context=context) if not production.move_created_ids and \ not (context.get('default_mode', False) and context['default_mode'] == 'consume'): self.signal_workflow(cr, uid, [production_id], 'button_finished_validated') if context.get('default_mode', False) and context['default_mode'] == 'consume': # Custom behaivor, set closed state self.signal_workflow(cr, uid, [production_id], 'button_validated_closed') return True
def is_zero(self, amount): """ Return true if `amount` is small enough to be treated as zero according to currency `self`'s rounding rules. Warning: ``is_zero(amount1-amount2)`` is not always equivalent to ``compare_amounts(amount1,amount2) == 0``, as the former will round after computing the difference, while the latter will round before, giving different results, e.g., 0.006 and 0.002 at 2 digits precision. """ return float_is_zero(amount, precision_rounding=self.rounding)
def is_zero(self, cr, uid, currency, amount): """Returns true if ``amount`` is small enough to be treated as zero according to ``currency``'s rounding rules. Warning: ``is_zero(amount1-amount2)`` is not always equivalent to ``compare_amounts(amount1,amount2) == 0``, as the former will round after computing the difference, while the latter will round before, giving different results for e.g. 0.006 and 0.002 at 2 digits precision. :param Record currency: currency for which we are rounding :param float amount: amount to compare with currency's zero """ return float_is_zero(amount, precision_rounding=currency.rounding)
def invoice_line_create(self, invoice_id, qty): """ Create an invoice line. The quantity to invoice can be positive (invoice) or negative (refund). :param invoice_id: integer :param qty: float quantity to invoice """ precision = self.env['decimal.precision'].precision_get('Product Unit of Measure') for line in self: if not float_is_zero(qty, precision_digits=precision): vals = line._prepare_invoice_line(qty=qty) vals.update({'invoice_id': invoice_id, 'sale_line_ids': [(6, 0, [line.id])]}) self.env['account.invoice.line'].create(vals)
def _get_progress(self): if self.ids: self.env.cr.execute("SELECT task_id, COALESCE(SUM(hours),0) FROM project_task_work WHERE task_id IN %s GROUP BY task_id",(tuple(self.ids),)) hours = dict(self.env.cr.fetchall()) for task in self: task.effective_hours = hours.get(task.id, 0.0) task.total_hours = (task.remaining_hours or 0.0) + hours.get(task.id, 0.0) task.delay_hours = task.total_hours - task.planned_hours task.progress = 0.0 if not float_is_zero(task.total_hours, precision_digits=2): task.progress = round(min(100.0 * hours.get(task.id, 0.0) / task.total_hours, 99.99),2) if task.stage_id and task.stage_id.use_progress: task.progress = task.stage_id.progress
def calc_price_percent(self): self.ensure_one() domain = eval( self.product_id.percent_domain ) domain.extend([('order_id','=',self.order_id.id),('id','!=',self.id)]) lines = self.env['sale.order.line'].search(domain) total_amount = 0 for line in lines: total_amount += line.price_subtotal total_amount = total_amount / 100 if not float_is_zero(self.price_unit - total_amount, precision_digits=2): self.write({'price_unit':total_amount})
def _compute_invoice_status(self): precision = self.env['decimal.precision'].precision_get('Product Unit of Measure') for line in self: if line.state not in ('sale', 'done'): line.invoice_status = 'no' elif not float_is_zero(line.qty_to_invoice, precision_digits=precision): line.invoice_status = 'to invoice' elif float_compare(line.qty_invoiced, line.product_uom_qty, precision_digits=precision) == 1 or\ float_compare(line.qty_invoiced, line.product_uom_qty, precision_digits=precision) >= 0 and\ float_compare(line.qty_delivered, line.product_uom_qty, precision_digits=precision) == 1: line.invoice_status = 'upselling' elif float_compare(line.qty_invoiced, line.product_uom_qty, precision_digits=precision) == 0: line.invoice_status = 'invoiced' else: line.invoice_status = 'no'
def test_timesheet_delivery(self): """ Test timesheet invoicing with 'invoice on delivery' timetracked products """ inv_obj = self.env['account.invoice'] # intial so prod_ts = self.env.ref('product.product_product_2') so_vals = { 'partner_id': self.partner.id, 'partner_invoice_id': self.partner.id, 'partner_shipping_id': self.partner.id, 'order_line': [(0, 0, {'name': prod_ts.name, 'product_id': prod_ts.id, 'product_uom_qty': 50, 'product_uom': prod_ts.uom_id.id, 'price_unit': prod_ts.list_price})], 'pricelist_id': self.env.ref('product.list0').id, } so = self.env['sale.order'].create(so_vals) so.action_confirm() self.assertEqual(so.invoice_status, 'no', 'Sale Timesheet: "invoice on delivery" should not need to be invoiced on so confirmation') # let's log some timesheets self.env['account.analytic.line'].create({ 'name': 'Test Line', 'account_id': so.project_id.id, 'unit_amount': 10.5, 'user_id': self.manager.id, 'is_timesheet': True, }) self.assertEqual(so.invoice_status, 'to invoice', 'Sale Timesheet: "invoice on delivery" timesheets should set the so in "to invoice" status when logged') inv_id = so.action_invoice_create() inv = inv_obj.browse(inv_id) self.assertTrue(float_is_zero(inv.amount_total - so.order_line.price_unit * 10.5, precision_digits=2), 'Sale: invoice generation on timesheets product is wrong') self.env['account.analytic.line'].create({ 'name': 'Test Line', 'account_id': so.project_id.id, 'unit_amount': 39.5, 'user_id': self.user.id, 'is_timesheet': True, }) self.assertEqual(so.invoice_status, 'to invoice', 'Sale Timesheet: "invoice on delivery" timesheets should not modify the invoice_status of the so') so.action_invoice_create() self.assertEqual(so.invoice_status, 'invoiced', 'Sale Timesheet: "invoice on delivery" timesheets should be invoiced completely by now') self.env['account.analytic.line'].create({ 'name': 'Test Line', 'account_id': so.project_id.id, 'unit_amount': 10, 'user_id': self.user.id, 'is_timesheet': True, }) self.assertEqual(so.invoice_status, 'to invoice', 'Sale Timesheet: supplementary timesheets do not change the status of the SO')
def _create_standard_deviation_entries(self, line, move_id, acc_prod=None): """ Create standard deviation journal items based on predefined product account valuation, gain and loss company's accounts """ if float_is_zero( line.additional_landed_cost, self.pool.get('decimal.precision').precision_get( self._cr, self._uid, 'Account')): return False valuation_account_id, gain_account_id, loss_account_id = \ self._get_deviation_accounts(line.product_id.id, acc_prod) return self._create_standard_deviation_entry_lines( line, move_id, valuation_account_id, gain_account_id, loss_account_id)
def _process_order(self, cr, uid, order, context=None): session = self.pool.get('pos.session').browse(cr, uid, order['pos_session_id'], context=context) if session.state == 'closing_control' or session.state == 'closed': session_id = self._get_valid_session(cr, uid, order, context=context) session = self.pool.get('pos.session').browse(cr, uid, session_id, context=context) order['pos_session_id'] = session_id order_id = self.create(cr, uid, self._order_fields(cr, uid, order, context=context),context) journal_ids = set() for payments in order['statement_ids']: self.add_payment(cr, uid, order_id, self._payment_fields(cr, uid, payments[2], context=context), context=context) journal_ids.add(payments[2]['journal_id']) if session.sequence_number <= order['sequence_number']: session.write({'sequence_number': order['sequence_number'] + 1}) session.refresh() if not float_is_zero(order['amount_return'], self.pool.get('decimal.precision').precision_get(cr, uid, 'Account')): # cash_journal = session.cash_journal_id.id cash_journal = False if session.config_id.journal_negativo_id: cash_journal = session.config_id.journal_negativo_id.id if not cash_journal: # Select for change one of the cash journals used in this payment cash_journal_ids = self.pool['account.journal'].search(cr, uid, [ ('type', '=', 'cash'), ('id', 'in', list(journal_ids)), ], limit=1, context=context) if not cash_journal_ids: # If none, select for change one of the cash journals of the POS # This is used for example when a customer pays by credit card # an amount higher than total amount of the order and gets cash back cash_journal_ids = [statement.journal_id.id for statement in session.statement_ids if statement.journal_id.type == 'cash'] if not cash_journal_ids: raise osv.except_osv( _('error!'), _("No cash statement found for this session. Unable to record returned cash.")) cash_journal = cash_journal_ids[0] self.add_payment(cr, uid, order_id, { 'amount': -order['amount_return'], 'payment_date': time.strftime('%Y-%m-%d %H:%M:%S'), 'payment_name': _('return'), 'journal': cash_journal, }, context=context) return order_id
def _prepare_reconciliation_move_line(self, move, amount): """ Prepare the dict of values to create the move line from a statement line. :param recordset move: the account.move to link the move line :param float amount: the amount of transaction that wasn't already reconciled """ company_currency = self.journal_id.company_id.currency_id statement_currency = self.journal_id.currency_id or company_currency st_line_currency = self.currency_id or statement_currency amount_currency = False if statement_currency != company_currency or st_line_currency != company_currency: # First get the ratio total mount / amount not already reconciled if statement_currency == company_currency: total_amount = self.amount elif st_line_currency == company_currency: total_amount = self.amount_currency else: total_amount = statement_currency.with_context({'date': self.date}).compute(self.amount, company_currency, round=False) if float_is_zero(total_amount - amount, precision_rounding=company_currency.rounding): ratio = total_amount / amount else: ratio = 1.0 # Then use it to adjust the statement.line field that correspond to the move.line amount_currency if statement_currency != company_currency: amount_currency = self.amount * ratio elif st_line_currency != company_currency: amount_currency = self.amount_currency * ratio return { 'name': self.name, 'date': self.date, 'ref': self.ref, 'move_id': move.id, 'partner_id': self.partner_id and self.partner_id.id or False, 'account_id': amount >= 0 \ and self.statement_id.journal_id.default_credit_account_id.id \ or self.statement_id.journal_id.default_debit_account_id.id, 'credit': amount < 0 and -amount or 0.0, 'debit': amount > 0 and amount or 0.0, 'statement_id': self.statement_id.id, 'journal_id': self.statement_id.journal_id.id, 'currency_id': statement_currency != company_currency and statement_currency.id or (st_line_currency != company_currency and st_line_currency.id or False), 'amount_currency': amount_currency, }
def _compute_residual(self): residual = 0.0 residual_signed = 0.0 residual_company_signed = 0.0 sign = self.type in ['in_refund', 'in_invoice'] and -1 or 1 for line in self.sudo().move_id.line_ids: if line.account_id.internal_type in ('receivable', 'payable'): residual_company_signed += line.amount_residual * sign if line.currency_id == self.currency_id: residual += line.amount_residual_currency if line.currency_id else line.amount_residual else: from_currency = (line.currency_id and line.currency_id.with_context(date=line.date)) or line.company_id.currency_id.with_context(date=line.date) residual += from_currency.compute(line.amount_residual, self.currency_id) self.residual_company_signed = residual_company_signed self.residual_signed = abs(residual) * sign self.residual = abs(residual) digits_rounding_precision = self.currency_id.rounding if float_is_zero(self.residual, digits_rounding_precision): self.reconciled = True
def action_invoice_create(self, grouped=False, final=False): """ Create the invoice associated to the SO. :param grouped: if True, invoices are grouped by SO id. If False, invoices are grouped by (partner, currency) :param final: if True, refunds will be generated if necessary :returns: list of created invoices """ inv_obj = self.env['account.invoice'] precision = self.env['decimal.precision'].precision_get('Product Unit of Measure') invoices = {} for order in self: group_key = order.id if grouped else (order.partner_id.id, order.currency_id.id) for line in order.order_line.sorted(key=lambda l: l.qty_to_invoice < 0): if float_is_zero(line.qty_to_invoice, precision_digits=precision): continue if group_key not in invoices: inv_data = order._prepare_invoice() invoice = inv_obj.create(inv_data) invoices[group_key] = invoice elif group_key in invoices and order.name not in invoices[group_key].origin.split(', '): invoices[group_key].write({'origin': invoices[group_key].origin + ', ' + order.name}) if line.qty_to_invoice > 0: line.invoice_line_create(invoices[group_key].id, line.qty_to_invoice) elif line.qty_to_invoice < 0 and final: line.invoice_line_create(invoices[group_key].id, line.qty_to_invoice) for invoice in invoices.values(): if not invoice.invoice_line_ids: raise UserError(_('There is no invoicable line.')) # If invoice is negative, do a refund invoice instead if invoice.amount_untaxed < 0: invoice.type = 'out_refund' for line in invoice.invoice_line_ids: line.quantity = -line.quantity # Necessary to force computation of taxes. In account_invoice, they are triggered # by onchanges, which are not triggered when doing a create. invoice.compute_taxes() return [inv.id for inv in invoices.values()]
def _process_mobility_order(self, cr, uid, order, context=None): if 'name' not in order or order['name'] is False: order['name'] = "/" if 'partner_id' not in order: order['partner_id'] = False if 'pos_session_id' not in order: session = super(pos_order, self)._default_session(cr, uid, context=context) if session is False: raise osv.except_osv( _('Error!'), _("POS order is not attached to an opened session.")) order['pos_session_id'] = session session = self.pool.get('pos.session').browse(cr, uid, order['pos_session_id'], context=context) # Create new order order_id = self.create(cr, uid, self._order_fields(cr, uid, order, context=context), context) for payments in order['statement_ids']: if ('statement_id' not in payments[2]): payments[2]['statement_id'] = False self.add_payment(cr, uid, order_id, self._payment_fields(cr, uid, payments[2], context=context), context=context) # Get order created order_row = self.browse(cr, uid, order_id, context=context) order['amount_return'] = abs(order_row['amount_total'] - order_row['amount_paid']) if not float_is_zero(order['amount_return'], self.pool.get('decimal.precision').precision_get(cr, uid, 'Account')): cash_journal = session.cash_journal_id if not cash_journal: cash_journal_ids = filter(lambda st: st.journal_id.type=='cash', session.statement_ids) if not len(cash_journal_ids): raise osv.except_osv( _('error!'), _("No cash statement found for this session. Unable to record returned cash.")) cash_journal = cash_journal_ids[0].journal_id self.add_payment(cr, uid, order_id, { 'amount': -order['amount_return'], 'payment_date': time.strftime('%Y-%m-%d %H:%M:%S'), 'payment_name': _('return'), 'journal': cash_journal.id, }, context=context) return order_id
def _process_order(self, cr, uid, order, context=None): product_obj = self.pool.get('product.product') line_obj = self.pool.get('pos.order.line') order_id = self.create(cr, uid, self._order_fields(cr, uid, order, context=context),context) for payments in order['statement_ids']: self.add_payment(cr, uid, order_id, self._payment_fields(cr, uid, payments[2], context=context), context=context) if order.get('disc_checked',False): prod_ids = product_obj.search(cr, uid, [('default_code','=','disc_fix')]) if prod_ids: pid = prod_ids[0] else: pid = product_obj.create(cr, uid, {'name': 'Discount Fixed Price', 'default_code' : 'disc_fix', 'type': 'service'}) line_vals = { 'product_id' : pid, 'qty' : 1, 'price_unit' : -(order.get('disc_amount',False)), 'order_id' : order_id } line_obj.create(cr, uid, line_vals) session = self.pool.get('pos.session').browse(cr, uid, order['pos_session_id'], context=context) if session.sequence_number <= order['sequence_number']: session.write({'sequence_number': order['sequence_number'] + 1}) session.refresh() if not float_is_zero(order['amount_return'], self.pool.get('decimal.precision').precision_get(cr, uid, 'Account')): cash_journal = session.cash_journal_id if not cash_journal: cash_journal_ids = filter(lambda st: st.journal_id.type=='cash', session.statement_ids) if not len(cash_journal_ids): raise osv.except_osv( _('error!'), _("No cash statement found for this session. Unable to record returned cash.")) cash_journal = cash_journal_ids[0].journal_id self.add_payment(cr, uid, order_id, { 'amount': -order['amount_return'], 'payment_date': time.strftime('%Y-%m-%d %H:%M:%S'), 'payment_name': _('return'), 'journal': cash_journal.id, }, context=context) return order_id
def _create_deviation_accounting_entries( self, move_id, product_id, old_avg, new_avg, qty, acc_prod=None): ''' This method takes the variation in value for average and books it as Inventory Valuation Deviation ''' amount = (old_avg - new_avg) * qty if float_is_zero( amount, self.pool.get('decimal.precision').precision_get( self._cr, self._uid, 'Account')): return False valuation_account_id, gain_account_id, loss_account_id = \ self._get_deviation_accounts(product_id, acc_prod) product_brw = self.env['product.product'].browse(product_id) return self._create_deviation_account_move_line( move_id, gain_account_id, loss_account_id, valuation_account_id, amount, product_brw)
def _get_invoice_line_vals(self, cr, uid, move, partner, inv_type, context=None): res = super(stock_move, self)._get_invoice_line_vals(cr, uid, move, partner, inv_type, context=context) if inv_type in ('out_invoice', 'out_refund') and move.procurement_id and move.procurement_id.sale_line_id: sale_line = move.procurement_id.sale_line_id res['invoice_line_tax_id'] = [(6, 0, [x.id for x in sale_line.tax_id])] res['account_analytic_id'] = sale_line.order_id.project_id and sale_line.order_id.project_id.id or False res['discount'] = sale_line.discount if move.product_id.id != sale_line.product_id.id: precision = self.pool.get('decimal.precision').precision_get(cr, uid, 'Discount') if float_is_zero(sale_line.discount, precision_digits=precision): res['price_unit'] = self.pool['product.pricelist'].price_get( cr, uid, [sale_line.order_id.pricelist_id.id], move.product_id.id, move.product_uom_qty or 1.0, sale_line.order_id.partner_id, context=context)[sale_line.order_id.pricelist_id.id] else: res['price_unit'] = move.product_id.lst_price else: res['price_unit'] = sale_line.price_unit uos_coeff = move.product_uom_qty and move.product_uos_qty / move.product_uom_qty or 1.0 res['price_unit'] = res['price_unit'] / uos_coeff return res
def process_bank_reconciliation(self, cr, uid, id, mv_line_dicts, context=None): """ Creates a move line for each item of mv_line_dicts and for the statement line. Reconcile a new move line with its counterpart_move_line_id if specified. Finally, mark the statement line as reconciled by putting the newly created move id in the column journal_entry_id. :param int id: id of the bank statement line :param list of dicts mv_line_dicts: move lines to create. If counterpart_move_line_id is specified, reconcile with it """ if context is None: context = {} st_line = self.browse(cr, uid, id, context=context) company_currency = st_line.journal_id.company_id.currency_id statement_currency = st_line.journal_id.currency or company_currency bs_obj = self.pool.get('account.bank.statement') am_obj = self.pool.get('account.move') aml_obj = self.pool.get('account.move.line') currency_obj = self.pool.get('res.currency') # Checks if st_line.bank_reconcile_id.id: raise osv.except_osv( _('Error!'), _('The bank statement line was already reconciled.')) for mv_line_dict in mv_line_dicts: for field in ['debit', 'credit', 'amount_currency']: if field not in mv_line_dict: mv_line_dict[field] = 0.0 if mv_line_dict.get('counterpart_move_line_id'): mv_line = aml_obj.browse( cr, uid, mv_line_dict.get('counterpart_move_line_id'), context=context) if mv_line.bank_reconcile_id: raise osv.except_osv( _('Error!'), _('A selected move line was already reconciled.')) bank_reconcile_obj = self.pool.get('account.bank.statement.reconcile') bank_reconcile_name = (st_line.statement_id.name or st_line.name) + "/" + str(st_line.sequence) bank_reconcile_id = bank_reconcile_obj.create( cr, uid, {'name': bank_reconcile_name}) st_line.write({'bank_reconcile_id': bank_reconcile_id}) move_created = False for mv_line_dict in mv_line_dicts: for field in ['debit', 'credit', 'amount_currency']: if field not in mv_line_dict: mv_line_dict[field] = 0.0 if mv_line_dict.get('counterpart_move_line_id'): mv_line = aml_obj.browse( cr, uid, mv_line_dict.get('counterpart_move_line_id'), context=context) mv_line.write({'bank_reconcile_id': bank_reconcile_id}) move_created = True if not move_created: # Create the move move_name = (st_line.statement_id.name or st_line.name) + "/" + str(st_line.sequence) move_vals = bs_obj._prepare_move(cr, uid, st_line, move_name, context=context) move_id = am_obj.create(cr, uid, move_vals, context=context) # Create the move line for the statement line if st_line.statement_id.currency.id != company_currency.id: if st_line.currency_id == company_currency: amount = st_line.amount_currency else: ctx = context.copy() ctx['date'] = st_line.date amount = currency_obj.compute( cr, uid, st_line.statement_id.currency.id, company_currency.id, st_line.amount, context=ctx) else: amount = st_line.amount bank_st_move_vals = bs_obj._prepare_bank_move_line( cr, uid, st_line, move_id, amount, company_currency.id, context=context) aml_obj.create(cr, uid, bank_st_move_vals, context=context) # Complete the dicts st_line_currency = st_line.currency_id or statement_currency st_line_currency_rate = st_line.currency_id and ( st_line.amount_currency / st_line.amount) or False to_create = [] for mv_line_dict in mv_line_dicts: if mv_line_dict.get('is_tax_line'): continue mv_line_dict['bank_reconcile_id'] = bank_reconcile_id mv_line_dict['ref'] = move_name mv_line_dict['move_id'] = move_id mv_line_dict['period_id'] = st_line.statement_id.period_id.id mv_line_dict['journal_id'] = st_line.journal_id.id mv_line_dict['company_id'] = st_line.company_id.id mv_line_dict['statement_id'] = st_line.statement_id.id if mv_line_dict.get('counterpart_move_line_id'): mv_line = aml_obj.browse( cr, uid, mv_line_dict['counterpart_move_line_id'], context=context) mv_line_dict[ 'partner_id'] = mv_line.partner_id.id or st_line.partner_id.id mv_line_dict['account_id'] = mv_line.account_id.id if st_line_currency.id != company_currency.id: ctx = context.copy() ctx['date'] = st_line.date mv_line_dict['amount_currency'] = mv_line_dict[ 'debit'] - mv_line_dict['credit'] mv_line_dict['currency_id'] = st_line_currency.id if st_line.currency_id and statement_currency.id == company_currency.id and st_line_currency_rate: debit_at_current_rate = self.pool.get( 'res.currency').round( cr, uid, company_currency, mv_line_dict['debit'] / st_line_currency_rate) credit_at_current_rate = self.pool.get( 'res.currency').round( cr, uid, company_currency, mv_line_dict['credit'] / st_line_currency_rate) elif st_line.currency_id and st_line_currency_rate: debit_at_current_rate = currency_obj.compute( cr, uid, statement_currency.id, company_currency.id, mv_line_dict['debit'] / st_line_currency_rate, context=ctx) credit_at_current_rate = currency_obj.compute( cr, uid, statement_currency.id, company_currency.id, mv_line_dict['credit'] / st_line_currency_rate, context=ctx) else: debit_at_current_rate = currency_obj.compute( cr, uid, st_line_currency.id, company_currency.id, mv_line_dict['debit'], context=ctx) credit_at_current_rate = currency_obj.compute( cr, uid, st_line_currency.id, company_currency.id, mv_line_dict['credit'], context=ctx) if mv_line_dict.get('counterpart_move_line_id'): # post an account line that use the same currency rate than the counterpart (to balance the account) and post the difference in another line ctx['date'] = mv_line.date if mv_line.currency_id.id == mv_line_dict['currency_id'] \ and float_is_zero(abs(mv_line.amount_currency) - abs(mv_line_dict['amount_currency']), precision_rounding=mv_line.currency_id.rounding): debit_at_old_rate = mv_line.credit credit_at_old_rate = mv_line.debit else: debit_at_old_rate = currency_obj.compute( cr, uid, st_line_currency.id, company_currency.id, mv_line_dict['debit'], context=ctx) credit_at_old_rate = currency_obj.compute( cr, uid, st_line_currency.id, company_currency.id, mv_line_dict['credit'], context=ctx) mv_line_dict['credit'] = credit_at_old_rate mv_line_dict['debit'] = debit_at_old_rate if debit_at_old_rate - debit_at_current_rate: currency_diff = debit_at_current_rate - debit_at_old_rate to_create.append( self.get_currency_rate_line(cr, uid, st_line, -currency_diff, move_id, context=context)) if credit_at_old_rate - credit_at_current_rate: currency_diff = credit_at_current_rate - credit_at_old_rate to_create.append( self.get_currency_rate_line(cr, uid, st_line, currency_diff, move_id, context=context)) if mv_line.currency_id and mv_line_dict[ 'currency_id'] == mv_line.currency_id.id: amount_unreconciled = mv_line.amount_residual_currency else: amount_unreconciled = currency_obj.compute( cr, uid, company_currency.id, mv_line_dict['currency_id'], mv_line.amount_residual, context=ctx) if float_is_zero(mv_line_dict['amount_currency'] + amount_unreconciled, precision_rounding=mv_line. currency_id.rounding): amount = mv_line_dict['debit'] or mv_line_dict[ 'credit'] sign = -1 if mv_line_dict['debit'] else 1 currency_rate_difference = sign * ( mv_line.amount_residual - amount) if not company_currency.is_zero( currency_rate_difference): exchange_lines = self._get_exchange_lines( cr, uid, st_line, mv_line, currency_rate_difference, mv_line_dict['currency_id'], move_id, context=context) for exchange_line in exchange_lines: to_create.append(exchange_line) else: mv_line_dict['debit'] = debit_at_current_rate mv_line_dict['credit'] = credit_at_current_rate elif statement_currency.id != company_currency.id: # statement is in foreign currency but the transaction is in company currency prorata_factor = ( mv_line_dict['debit'] - mv_line_dict['credit']) / st_line.amount_currency mv_line_dict[ 'amount_currency'] = prorata_factor * st_line.amount to_create.append(mv_line_dict) # If the reconciliation is performed in another currency than the company currency, the amounts are converted to get the right debit/credit. # If there is more than 1 debit and 1 credit, this can induce a rounding error, which we put in the foreign exchane gain/loss account. if st_line_currency.id != company_currency.id: diff_amount = bank_st_move_vals['debit'] - bank_st_move_vals['credit'] \ + sum(aml['debit'] for aml in to_create) - sum(aml['credit'] for aml in to_create) if not company_currency.is_zero(diff_amount): diff_aml = self.get_currency_rate_line(cr, uid, st_line, diff_amount, move_id, context=context) diff_aml['name'] = _( 'Rounding error from currency conversion') to_create.append(diff_aml) # Create move lines move_line_pairs_to_reconcile = [] for mv_line_dict in to_create: counterpart_move_line_id = None # NB : this attribute is irrelevant for aml_obj.create() and needs to be removed from the dict if mv_line_dict.get('counterpart_move_line_id'): counterpart_move_line_id = mv_line_dict[ 'counterpart_move_line_id'] del mv_line_dict['counterpart_move_line_id'] new_aml_id = aml_obj.create(cr, uid, mv_line_dict, context=context) if counterpart_move_line_id != None: move_line_pairs_to_reconcile.append( [new_aml_id, counterpart_move_line_id]) # Reconcile #for pair in move_line_pairs_to_reconcile: # aml_obj.reconcile_partial(cr, uid, pair, context=context) # Mark the statement line as reconciled self.write(cr, uid, id, {'journal_entry_id': move_id}, context=context)
def process_sheet(self, cr, uid, ids, context=None): move_pool = self.pool.get('account.move') hr_payslip_line_pool = self.pool['hr.payslip.line'] precision = self.pool.get('decimal.precision').precision_get( cr, uid, 'Payroll') for slip in self.browse(cr, uid, ids, context=context): line_ids = [] debit_sum = 0.0 credit_sum = 0.0 date = slip.date or slip.date_to partner_eps_id = slip.employee_id.eps_id.id partner_fp_id = slip.employee_id.fp_id.id partner_fc_id = slip.employee_id.fc_id.id default_partner_id = slip.employee_id.address_home_id.id name = _('Payslip of %s') % (slip.employee_id.name) move = { 'narration': name, 'ref': slip.number, 'journal_id': slip.journal_id.id, 'date': date, } for line in slip.details_by_salary_rule_category: amt = slip.credit_note and -line.total or line.total if float_is_zero(amt, precision_digits=precision): continue partner_id = line.salary_rule_id.register_id.partner_id and line.salary_rule_id.register_id.partner_id.id or default_partner_id debit_account_id = line.salary_rule_id.account_debit.id credit_account_id = line.salary_rule_id.account_credit.id if line.salary_rule_id.origin_partner == 'employee': partner_id = default_partner_id elif line.salary_rule_id.origin_partner == 'eps': partner_id = partner_eps_id elif line.salary_rule_id.origin_partner == 'fp': partner_id = partner_fp_id elif line.salary_rule_id.origin_partner == 'fc': partner_id = partner_fc_id elif line.salary_rule_id.origin_partner == 'rule': partner_id = line.salary_rule_id.partner_id.id else: partner_id = default_partner_id if debit_account_id: debit_line = ( 0, 0, { 'name': line.name, # 'partner_id': hr_payslip_line_pool._get_partner_id(cr, uid, line, credit_account=False, context=context), 'partner_id': partner_id, 'account_id': debit_account_id, 'journal_id': slip.journal_id.id, 'date': date, 'debit': amt > 0.0 and amt or 0.0, 'credit': amt < 0.0 and -amt or 0.0, 'analytic_account_id': line.salary_rule_id.analytic_account_id and line.salary_rule_id.analytic_account_id.id or False, 'tax_line_id': line.salary_rule_id.account_tax_id and line.salary_rule_id.account_tax_id.id or False, }) line_ids.append(debit_line) debit_sum += debit_line[2]['debit'] - debit_line[2][ 'credit'] if credit_account_id: credit_line = ( 0, 0, { 'name': line.name, # 'partner_id': hr_payslip_line_pool._get_partner_id(cr, uid, line, credit_account=True, context=context), 'partner_id': partner_id, 'account_id': credit_account_id, 'journal_id': slip.journal_id.id, 'date': date, 'debit': amt < 0.0 and -amt or 0.0, 'credit': amt > 0.0 and amt or 0.0, 'analytic_account_id': line.salary_rule_id.analytic_account_id and line.salary_rule_id.analytic_account_id.id or False, 'tax_line_id': line.salary_rule_id.account_tax_id and line.salary_rule_id.account_tax_id.id or False, }) line_ids.append(credit_line) credit_sum += credit_line[2]['credit'] - credit_line[2][ 'debit'] if float_compare(credit_sum, debit_sum, precision_digits=precision) == -1: acc_id = slip.journal_id.default_credit_account_id.id if not acc_id: raise UserError( _('The Expense Journal "%s" has not properly configured the Credit Account!' ) % (slip.journal_id.name)) adjust_credit = (0, 0, { 'name': _('Adjustment Entry'), 'partner_id': False, 'account_id': acc_id, 'journal_id': slip.journal_id.id, 'date': date, 'debit': 0.0, 'credit': debit_sum - credit_sum, }) line_ids.append(adjust_credit) elif float_compare(debit_sum, credit_sum, precision_digits=precision) == -1: acc_id = slip.journal_id.default_debit_account_id.id if not acc_id: raise UserError( _('The Expense Journal "%s" has not properly configured the Debit Account!' ) % (slip.journal_id.name)) adjust_debit = (0, 0, { 'name': _('Adjustment Entry'), 'partner_id': False, 'account_id': acc_id, 'journal_id': slip.journal_id.id, 'date': date, 'debit': credit_sum - debit_sum, 'credit': 0.0, }) line_ids.append(adjust_debit) move.update({'line_ids': line_ids}) move_id = move_pool.create(cr, uid, move, context=context) self.write(cr, uid, [slip.id], { 'move_id': move_id, 'date': date, 'state': 'done', 'paid': True }, context=context) move_pool.post(cr, uid, [move_id], context=context) return True
def _process_order(self, cr, uid, order, context=None): order_id = self.create( cr, uid, self._order_fields(cr, uid, order, context=context), context) for payments in order['statement_ids']: if not order.get('sale_mode') and order.get( 'parent_return_order', ''): payments[2]['amount'] = payments[2]['amount'] or 0.0 self.add_payment(cr, uid, order_id, self._payment_fields(cr, uid, payments[2], context=context), context=context) session = self.pool.get('pos.session').browse(cr, uid, order['pos_session_id'], context=context) if session.sequence_number <= order['sequence_number']: session.write({'sequence_number': order['sequence_number'] + 1}) session.refresh() if not order.get('parent_return_order', '') and not float_is_zero( order['amount_return'], self.pool.get('decimal.precision').precision_get( cr, uid, 'Account')): cash_journal = session.cash_journal_id if not cash_journal: cash_journal_ids = filter( lambda st: st.journal_id.type == 'cash', session.statement_ids) if not len(cash_journal_ids): raise osv.except_osv( _('error!'), _("No cash statement found for this session. Unable to record returned cash." )) cash_journal = cash_journal_ids[0].journal_id self.add_payment( cr, uid, order_id, { 'amount': -order['amount_return'], 'payment_date': time.strftime('%Y-%m-%d %H:%M:%S'), 'payment_name': _('return'), 'journal': cash_journal.id, }, context=context) if order.get('parent_return_order', '') and not float_is_zero( order['amount_return'], self.pool.get('decimal.precision').precision_get( cr, uid, 'Account')): cash_journal = session.cash_journal_id if not cash_journal: cash_journal_ids = filter( lambda st: st.journal_id.type == 'cash', session.statement_ids) if not len(cash_journal_ids): raise osv.except_osv( _('error!'), _("No cash statement found for this session. Unable to record returned cash." )) cash_journal = cash_journal_ids[0].journal_id self.add_payment( cr, uid, order_id, { 'amount': -order['amount_return'], 'payment_date': time.strftime('%Y-%m-%d %H:%M:%S'), 'payment_name': _('return'), 'journal': cash_journal.id, }, context=context) return order_id
def process_sheet(self): move_pool = self.env['account.move'] precision = self.env['decimal.precision'].precision_get('Payroll') timenow = time.strftime('%Y-%m-%d') for slip in self: line_ids = [] debit_sum = 0.0 credit_sum = 0.0 employee_partner = slip.employee_id.address_home_id name = _('Payslip of %s') % (slip.employee_id.name) move_vals = { 'narration': name, 'date': timenow, 'ref': slip.number, 'journal_id': slip.journal_id.id, } for line in slip.details_by_salary_rule_category: amt = -line.amount if slip.credit_note else line.amount if float_is_zero(amt, precision_digits=precision): continue rule = line.salary_rule_id register_partner = rule.register_id.partner_id debit_account_id = line.salary_rule_id.account_debit.id credit_account_id = line.salary_rule_id.account_credit.id analytic_account = rule.analytic_account_id if debit_account_id: if (rule.account_debit.internal_type not in ['receivable', 'payable']): partner_debit = self.env['res.partner'] elif register_partner: partner_debit = register_partner else: partner_debit = employee_partner debit_line = (0, 0, { 'name': line.name, 'date': timenow, 'partner_id': partner_debit.id, 'account_id': debit_account_id, 'journal_id': slip.journal_id.id, 'debit': amt > 0.0 and amt or 0.0, 'credit': amt < 0.0 and -amt or 0.0, 'analytic_account_id': analytic_account.id, }) line_ids.append(debit_line) debit_sum += (debit_line[2]['debit'] - debit_line[2]['credit']) if credit_account_id: if (rule.account_credit.internal_type not in ['receivable', 'payable']): partner_credit = self.env['res.partner'] elif register_partner: partner_credit = register_partner else: partner_credit = employee_partner credit_line = (0, 0, { 'name': line.name, 'date': timenow, 'partner_id': partner_credit.id, 'account_id': credit_account_id, 'journal_id': slip.journal_id.id, 'debit': amt < 0.0 and -amt or 0.0, 'credit': amt > 0.0 and amt or 0.0, 'analytic_account_id': analytic_account.id, }) line_ids.append(credit_line) credit_sum += (credit_line[2]['credit'] - credit_line[2]['debit']) if (float_compare(credit_sum, debit_sum, precision_digits=precision) == -1): acc_id = slip.journal_id.default_credit_account_id.id if not acc_id: raise ValidationError( _('The Expense Journal "%s" has not properly configured ' 'the Credit Account!') % slip.journal_id.name) adjust_credit = (0, 0, { 'name': _('Adjustment Entry'), 'date': timenow, 'partner_id': False, 'account_id': acc_id, 'journal_id': slip.journal_id.id, 'debit': 0.0, 'credit': debit_sum - credit_sum, }) line_ids.append(adjust_credit) elif (float_compare(debit_sum, credit_sum, precision_digits=precision) == -1): acc_id = slip.journal_id.default_debit_account_id.id if not acc_id: raise ValidationError( _('The Expense Journal "%s" has not properly configured ' 'the Debit Account!') % slip.journal_id.name) adjust_debit = (0, 0, { 'name': _('Adjustment Entry'), 'date': timenow, 'partner_id': False, 'account_id': acc_id, 'journal_id': slip.journal_id.id, 'debit': credit_sum - debit_sum, 'credit': 0.0, }) line_ids.append(adjust_debit) move_vals.update({'line_ids': line_ids}) move = move_pool.create(move_vals) slip.write({'move_id': move.id}) return super(HrPayslip, self).process_sheet()
def process_sheet(self, cr, uid, ids, context=None): move_pool = self.pool.get('account.move') period_pool = self.pool.get('account.period') precision = self.pool.get('decimal.precision').precision_get( cr, uid, 'Payroll') timenow = time.strftime('%Y-%m-%d') for slip in self.browse(cr, uid, ids, context=context): line_ids = [] debit_sum = 0.0 credit_sum = 0.0 if not slip.period_id: search_periods = period_pool.find(cr, uid, slip.date_to, context=context) period_id = search_periods[0] else: period_id = slip.period_id.id default_partner_id = slip.employee_id.address_home_id.id name = _('Payslip of %s') % (slip.employee_id.name) move = { 'narration': name, 'date': timenow, 'ref': slip.number, 'journal_id': slip.journal_id.id, 'period_id': period_id, } for line in slip.details_by_salary_rule_category: amt = slip.credit_note and -line.total or line.total if float_is_zero(amt, precision_digits=precision): continue partner_id = line.salary_rule_id.register_id.partner_id and line.salary_rule_id.register_id.partner_id.id or default_partner_id debit_account_id = line.salary_rule_id.account_debit.id credit_account_id = line.salary_rule_id.account_credit.id if debit_account_id: debit_line = (0, 0, { 'name': line.name, 'date': timenow, 'partner_id': (line.salary_rule_id.register_id.partner_id or line.salary_rule_id.account_debit.type in ('receivable', 'payable')) and partner_id or False, 'account_id': debit_account_id, 'journal_id': slip.journal_id.id, 'period_id': period_id, 'debit': amt > 0.0 and amt or 0.0, 'credit': amt < 0.0 and -amt or 0.0, 'analytic_account_id': line.salary_rule_id.analytic_account_id and line.salary_rule_id.analytic_account_id.id or False, 'tax_code_id': line.salary_rule_id.account_tax_id and line.salary_rule_id.account_tax_id.id or False, 'tax_amount': line.salary_rule_id.account_tax_id and amt or 0.0, }) line_ids.append(debit_line) debit_sum += debit_line[2]['debit'] - debit_line[2][ 'credit'] if credit_account_id: credit_line = (0, 0, { 'name': line.name, 'date': timenow, 'partner_id': (line.salary_rule_id.register_id.partner_id or line.salary_rule_id.account_credit.type in ('receivable', 'payable')) and partner_id or False, 'account_id': credit_account_id, 'journal_id': slip.journal_id.id, 'period_id': period_id, 'debit': amt < 0.0 and -amt or 0.0, 'credit': amt > 0.0 and amt or 0.0, 'analytic_account_id': line.salary_rule_id.analytic_account_id and line.salary_rule_id.analytic_account_id.id or False, 'tax_code_id': line.salary_rule_id.account_tax_id and line.salary_rule_id.account_tax_id.id or False, 'tax_amount': line.salary_rule_id.account_tax_id and amt or 0.0, }) line_ids.append(credit_line) credit_sum += credit_line[2]['credit'] - credit_line[2][ 'debit'] if float_compare(credit_sum, debit_sum, precision_digits=precision) == -1: acc_id = slip.journal_id.default_credit_account_id.id if not acc_id: raise UserError( _('The Expense Journal "%s" has not properly configured the Credit Account!' ) % (slip.journal_id.name)) adjust_credit = (0, 0, { 'name': _('Adjustment Entry'), 'date': timenow, 'partner_id': False, 'account_id': acc_id, 'journal_id': slip.journal_id.id, 'period_id': period_id, 'debit': 0.0, 'credit': debit_sum - credit_sum, }) line_ids.append(adjust_credit) elif float_compare(debit_sum, credit_sum, precision_digits=precision) == -1: acc_id = slip.journal_id.default_debit_account_id.id if not acc_id: raise UserError( _('The Expense Journal "%s" has not properly configured the Debit Account!' ) % (slip.journal_id.name)) adjust_debit = (0, 0, { 'name': _('Adjustment Entry'), 'date': timenow, 'partner_id': False, 'account_id': acc_id, 'journal_id': slip.journal_id.id, 'period_id': period_id, 'debit': credit_sum - debit_sum, 'credit': 0.0, }) line_ids.append(adjust_debit) move.update({'line_id': line_ids}) move_id = move_pool.create(cr, uid, move, context=context) self.write(cr, uid, [slip.id], { 'move_id': move_id, 'period_id': period_id }, context=context) if slip.journal_id.entry_posted: move_pool.post(cr, uid, [move_id], context=context) return super(hr_payslip, self).process_sheet(cr, uid, [slip.id], context=context)
def _add_trade_settlement_block(self, trade_transaction, sign, ns): self.ensure_one() prec = self.env['decimal.precision'].precision_get('Account') inv_currency_name = self.currency_id.name trade_settlement = etree.SubElement( trade_transaction, ns['ram'] + 'ApplicableSupplyChainTradeSettlement') payment_ref = etree.SubElement(trade_settlement, ns['ram'] + 'PaymentReference') payment_ref.text = self.number or self.state invoice_currency = etree.SubElement(trade_settlement, ns['ram'] + 'InvoiceCurrencyCode') invoice_currency.text = inv_currency_name if (self.payment_mode_id and not self.payment_mode_id.type.unece_code): raise UserError( _("Missing UNECE code on payment export type '%s'") % self.payment_mode_id.type.name) if (self.type == 'out_invoice' or (self.payment_mode_id and self.payment_mode_id.type.unece_code not in [31, 42])): self._add_trade_settlement_payment_means_block( trade_settlement, sign, ns) tax_basis_total = 0.0 if self.tax_line: for tline in self.tax_line: if not tline.base_code_id: raise UserError( _("Missing base code on tax line '%s'.") % tline.name) taxes = self.env['account.tax'].search([ ('base_code_id', '=', tline.base_code_id.id) ]) if not taxes: raise UserError( _("The tax code '%s' is not linked to a tax.") % tline.base_code_id.name) tax = taxes[0] if not tax.unece_type_code: raise UserError( _("Missing UNECE Tax Type on tax '%s'") % tax.name) if not tax.unece_categ_code: raise UserError( _("Missing UNECE Tax Category on tax '%s'") % tax.name) trade_tax = etree.SubElement(trade_settlement, ns['ram'] + 'ApplicableTradeTax') amount = etree.SubElement(trade_tax, ns['ram'] + 'CalculatedAmount', currencyID=inv_currency_name) amount.text = unicode(tline.amount * sign) tax_type = etree.SubElement(trade_tax, ns['ram'] + 'TypeCode') tax_type.text = tax.unece_type_code if (tax.unece_categ_code != 'S' and float_is_zero(tax.amount, precision_digits=prec) and self.fiscal_position and self.fiscal_position.note): exemption_reason = etree.SubElement( trade_tax, ns['ram'] + 'ExemptionReason') exemption_reason.text = self.with_context( lang=self.partner_id.lang or 'en_US').\ fiscal_position.note base = etree.SubElement(trade_tax, ns['ram'] + 'BasisAmount', currencyID=inv_currency_name) base.text = unicode(tline.base * sign) tax_basis_total += tline.base tax_categ_code = etree.SubElement(trade_tax, ns['ram'] + 'CategoryCode') tax_categ_code.text = tax.unece_categ_code if tax.type == 'percent': percent = etree.SubElement(trade_tax, ns['ram'] + 'ApplicablePercent') percent.text = unicode(tax.amount * 100) trade_payment_term = etree.SubElement( trade_settlement, ns['ram'] + 'SpecifiedTradePaymentTerms') trade_payment_term_desc = etree.SubElement(trade_payment_term, ns['ram'] + 'Description') # The 'Description' field of SpecifiedTradePaymentTerms # is a required field, so we must always give a value if self.payment_term: trade_payment_term_desc.text = self.payment_term.name else: trade_payment_term_desc.text =\ _('No specific payment term selected') if self.date_due: date_due_dt = fields.Date.from_string(self.date_due) self._add_date('DueDateDateTime', date_due_dt, trade_payment_term, ns) sums = etree.SubElement( trade_settlement, ns['ram'] + 'SpecifiedTradeSettlementMonetarySummation') line_total = etree.SubElement(sums, ns['ram'] + 'LineTotalAmount', currencyID=inv_currency_name) line_total.text = unicode(self.amount_untaxed * sign) charge_total = etree.SubElement(sums, ns['ram'] + 'ChargeTotalAmount', currencyID=inv_currency_name) charge_total.text = '0.00' allowance_total = etree.SubElement(sums, ns['ram'] + 'AllowanceTotalAmount', currencyID=inv_currency_name) allowance_total.text = '0.00' tax_basis_total_amt = etree.SubElement(sums, ns['ram'] + 'TaxBasisTotalAmount', currencyID=inv_currency_name) tax_basis_total_amt.text = unicode(tax_basis_total * sign) tax_total = etree.SubElement(sums, ns['ram'] + 'TaxTotalAmount', currencyID=inv_currency_name) tax_total.text = unicode(self.amount_tax * sign) total = etree.SubElement(sums, ns['ram'] + 'GrandTotalAmount', currencyID=inv_currency_name) total.text = unicode(self.amount_total * sign) prepaid = etree.SubElement(sums, ns['ram'] + 'TotalPrepaidAmount', currencyID=inv_currency_name) residual = etree.SubElement(sums, ns['ram'] + 'DuePayableAmount', currencyID=inv_currency_name) prepaid.text = unicode((self.amount_total - self.residual) * sign) residual.text = unicode(self.residual * sign)
def _get_billwise_move_lines(self, account_type, date_from, target_move, period_length): periods = {} start = datetime.strptime(date_from, "%Y-%m-%d") for i in range(5)[::-1]: stop = start - relativedelta(days=period_length) periods[str(i)] = { 'name': (i != 0 and (str( (5 - (i + 1)) * period_length) + '-' + str( (5 - i) * period_length)) or ('+' + str(4 * period_length))), 'stop': start.strftime('%Y-%m-%d'), 'start': (i != 0 and stop.strftime('%Y-%m-%d') or False), } start = stop - relativedelta(days=1) res = [] total = [] cr = self.env.cr user_company = self.env.user.company_id.id move_state = ['draft', 'posted'] if target_move == 'posted': move_state = ['posted'] arg_list = (tuple(move_state), tuple(account_type)) #build the reconciliation clause to see what partner needs to be printed reconciliation_clause = '(l.reconciled IS FALSE)' cr.execute( 'SELECT debit_move_id, credit_move_id FROM account_partial_reconcile where create_date > %s', (date_from, )) reconciled_after_date = [] for row in cr.fetchall(): reconciled_after_date += [row[0], row[1]] if reconciled_after_date: reconciliation_clause = '(l.reconciled IS FALSE OR l.id IN %s)' arg_list += (tuple(reconciled_after_date), ) arg_list += (date_from, user_company) query = ''' SELECT DISTINCT l.partner_id, UPPER(res_partner.name) FROM account_move_line AS l left join res_partner on l.partner_id = res_partner.id, account_account, account_move am WHERE (l.account_id = account_account.id) AND (l.move_id = am.id) AND (am.state IN %s) AND (account_account.internal_type IN %s) AND ''' + reconciliation_clause + ''' AND (l.date <= %s) AND l.company_id = %s ORDER BY UPPER(res_partner.name)''' cr.execute(query, arg_list) partners = cr.dictfetchall() # put a total of 0 for i in range(7): total.append(0) # Build a string like (1,2,3) for easy use in SQL query partner_ids = [ partner['partner_id'] for partner in partners if partner['partner_id'] ] lines = dict( (partner['partner_id'] or False, []) for partner in partners) if not partner_ids: return [], [], [] # This dictionary will store the not due amount of all partners undue_amounts = {} query = '''SELECT l.id FROM account_move_line AS l, account_account, account_move am WHERE (l.account_id = account_account.id) AND (l.move_id = am.id) AND (am.state IN %s) AND (account_account.internal_type IN %s) AND (COALESCE(l.date_maturity,l.date) > %s)\ AND ((l.partner_id IN %s) OR (l.partner_id IS NULL)) AND (l.date <= %s) AND l.company_id = %s''' cr.execute(query, (tuple(move_state), tuple(account_type), date_from, tuple(partner_ids), date_from, user_company)) aml_ids = cr.fetchall() aml_ids = aml_ids and [x[0] for x in aml_ids] or [] for line in self.env['account.move.line'].browse(aml_ids): partner_id = line.partner_id.id or False if partner_id not in undue_amounts: undue_amounts[partner_id] = 0.0 line_amount = line.balance if line.balance == 0: continue for partial_line in line.matched_debit_ids: if partial_line.create_date[:10] <= date_from: line_amount += partial_line.amount for partial_line in line.matched_credit_ids: if partial_line.create_date[:10] <= date_from: line_amount -= partial_line.amount undue_amounts[partner_id] += line_amount lines[partner_id].append({ 'line': line, 'amount': line_amount, 'period': 6, }) # Use one query per period and store results in history (a list variable) # Each history will contain: history[1] = {'<partner_id>': <partner_debit-credit>} history = [] for i in range(5): args_list = ( tuple(move_state), tuple(account_type), tuple(partner_ids), ) dates_query = '(COALESCE(l.date_maturity,l.date)' if periods[str(i)]['start'] and periods[str(i)]['stop']: dates_query += ' BETWEEN %s AND %s)' args_list += (periods[str(i)]['start'], periods[str(i)]['stop']) elif periods[str(i)]['start']: dates_query += ' >= %s)' args_list += (periods[str(i)]['start'], ) else: dates_query += ' <= %s)' args_list += (periods[str(i)]['stop'], ) args_list += (date_from, user_company) query = '''SELECT l.id FROM account_move_line AS l, account_account, account_move am WHERE (l.account_id = account_account.id) AND (l.move_id = am.id) AND (am.state IN %s) AND (account_account.internal_type IN %s) AND ((l.partner_id IN %s) OR (l.partner_id IS NULL)) AND ''' + dates_query + ''' AND (l.date <= %s) AND l.company_id = %s''' cr.execute(query, args_list) partners_amount = {} aml_ids = cr.fetchall() aml_ids = aml_ids and [x[0] for x in aml_ids] or [] for line in self.env['account.move.line'].browse(aml_ids): partner_id = line.partner_id.id or False if partner_id not in partners_amount: partners_amount[partner_id] = 0.0 line_amount = line.balance if line.balance == 0: continue for partial_line in line.matched_debit_ids: if partial_line.create_date[:10] <= date_from: line_amount += partial_line.amount for partial_line in line.matched_credit_ids: if partial_line.create_date[:10] <= date_from: line_amount -= partial_line.amount partners_amount[partner_id] += line_amount lines[partner_id].append({ 'line': line, 'amount': line_amount, 'period': i + 1, }) history.append(partners_amount) for partner in partners: at_least_one_amount = False values = {} undue_amt = 0.0 if partner[ 'partner_id'] in undue_amounts: # Making sure this partner actually was found by the query undue_amt = undue_amounts[partner['partner_id']] total[6] = total[6] + undue_amt values['direction'] = undue_amt if not float_is_zero(values['direction'], precision_rounding=self.env.user.company_id. currency_id.rounding): at_least_one_amount = True for i in range(5): during = False if partner['partner_id'] in history[i]: during = [history[i][partner['partner_id']]] # Adding counter total[(i)] = total[(i)] + (during and during[0] or 0) values[str(i)] = during and during[0] or 0.0 if not float_is_zero(values[str(i)], precision_rounding=self.env.user. company_id.currency_id.rounding): at_least_one_amount = True values['total'] = sum([values['direction']] + [values[str(i)] for i in range(5)]) ## Add for total total[(i + 1)] += values['total'] values['partner_id'] = partner['partner_id'] if partner['partner_id']: browsed_partner = self.env['res.partner'].browse( partner['partner_id']) values['name'] = browsed_partner.name and len( browsed_partner.name) >= 45 and browsed_partner.name[ 0:40] + '...' or browsed_partner.name values['trust'] = False else: values['name'] = _('Unknown Partner') values['trust'] = False if at_least_one_amount: res.append(values) return res, total, lines
def button_validate(self): self.ensure_one() precision_obj = self.pool.get('decimal.precision').precision_get( self._cr, self._uid, 'Account') quant_obj = self.env['stock.quant'] template_obj = self.pool.get('product.template') scp_obj = self.env['stock.card.product'] get_average = scp_obj.get_average stock_card_move_get = scp_obj._stock_card_move_get ctx = dict(self._context) for cost in self: if cost.state != 'draft': raise UserError(_('Only draft landed costs can be validated')) if not cost.valuation_adjustment_lines or \ not self._check_sum(cost): raise UserError( _('You cannot validate a landed cost which has no valid ' 'valuation adjustments lines. Did you click on ' 'Compute?')) move_id = self._model._create_account_move(self._cr, self._uid, cost, context=ctx) prod_dict = {} init_avg = {} first_lines = {} first_card = {} last_lines = {} prod_qty = {} acc_prod = {} quant_dict = {} for line in cost.valuation_adjustment_lines: if not line.move_id: continue product_id = line.product_id if product_id.id not in acc_prod: acc_prod[product_id.id] = \ template_obj.get_product_accounts( self._cr, self._uid, product_id.product_tmpl_id.id, context=ctx) if product_id.cost_method == 'standard': self._create_standard_deviation_entries( line, move_id, acc_prod) continue if product_id.cost_method == 'average': if product_id.id not in prod_dict: first_card = stock_card_move_get(product_id.id) prod_dict[product_id.id] = get_average(first_card) first_lines[product_id.id] = first_card['res'] init_avg[product_id.id] = product_id.standard_price prod_qty[product_id.id] = first_card['product_qty'] per_unit = line.final_cost / line.quantity diff = per_unit - line.former_cost_per_unit quants = [quant for quant in line.move_id.quant_ids] for quant in quants: if quant.id not in quant_dict: quant_dict[quant.id] = quant.cost + diff else: quant_dict[quant.id] += diff qty_out = 0 for quant in line.move_id.quant_ids: if quant.location_id.usage != 'internal': qty_out += quant.qty if product_id.cost_method == 'average': # /!\ NOTE: Inventory valuation self._create_landed_accounting_entries( line, move_id, 0.0, acc_prod) if product_id.cost_method == 'real': self._create_landed_accounting_entries( line, move_id, qty_out, acc_prod) for key, value in quant_dict.items(): quant_obj.sudo().browse(key).write({'cost': value}) # /!\ NOTE: This new update is taken out of for loop to improve # performance for prod_id in prod_dict: last_card = stock_card_move_get(prod_id) prod_dict[prod_id] = get_average(last_card) last_lines[prod_id] = last_card['res'] # /!\ NOTE: COGS computation # NOTE: After adding value to product with landing cost products # with costing method `average` need to be check in order to # find out the change in COGS in case of sales were performed prior # to landing costs to_cogs = {} for prod_id in prod_dict: to_cogs[prod_id] = zip(first_lines[prod_id], last_lines[prod_id]) for prod_id in to_cogs: fst_avg = 0.0 lst_avg = 0.0 ini_avg = init_avg[prod_id] diff = 0.0 for tpl in to_cogs[prod_id]: first_line = tpl[0] last_line = tpl[1] fst_avg = first_line['average'] lst_avg = last_line['average'] if first_line['qty'] >= 0: # /!\ TODO: This is not true for devolutions continue # NOTE: Rounding problems could arise here, this needs to # be checked diff += (lst_avg - fst_avg) * abs(first_line['qty']) if not float_is_zero(diff, precision_obj): self._create_cogs_accounting_entries( prod_id, move_id, diff, acc_prod) # TODO: Compute deviation diff = 0.0 if prod_qty[prod_id] and fst_avg != ini_avg and \ lst_avg != ini_avg: diff = (fst_avg - ini_avg) * prod_qty[prod_id] if not float_is_zero(diff, precision_obj): self._create_deviation_accounting_entries( move_id, prod_id, diff, acc_prod) # TODO: Write latest value for average cost.compute_average_cost(prod_dict) cost.write({'state': 'done', 'account_move_id': move_id}) # Post the account move if the journal's auto post true move_obj = self.env['account.move'].browse(move_id) if move_obj.journal_id.entry_posted: move_obj.post() move_obj.validate() return True
def _evaluate_threshold(self, bottom_th, computed_th, diff): """ Checks if bottom threshold is reached by computed threshold """ precision_id = self.env['decimal.precision'].precision_get('Account') return float_is_zero(diff, precision_id) or\ (self.standard_price and diff < 0 and computed_th > bottom_th)
def __execute_cron(self, cr, uid, ids=None, context=None): # noqa """This method is incorrectly designed due to the write on the product as obsolete should be a core feature not a cron feature. I do not delete it because I need review afterwards such feature. Dear Future me I am really sorry """ ids = ids or [] context = context or {} product_obj = self.pool.get('product.product') precision_obj = self.pool.get('decimal.precision').precision_get( cr, uid, 'Account') user = self.pool.get('res.users').browse(cr, uid, uid, context) std_bottom_threshold = user.company_id.std_price_neg_threshold product_ids = self._get_products(cr, uid, ids, context=context) message = 'Old price %(old)s, New price %(new)s' context['message'] = '' count = 0 total = len(product_ids) _logger.info('Cron Job will compute %(length)s products', dict(length=total)) msglog = 'Computing cost for product: [{prod_id}]. {count}/{total}' for product in product_ids: prod_brw = product_obj.browse(cr, uid, product) count += 1 _logger.info(msglog, dict(prod_id=product, total=total, count=count)) if product_obj.fetch_product_bom_states(cr, uid, product, state='obsolete', context=context): _logger.warning('product [%s] has obsolete children', product) continue # /!\ NOTE: Is it enough to call the qty like that? if prod_brw.qty_available == 0: prod_brw.write({'state': 'obsolete'}) continue context.update({ 'active_model': 'product.product', 'active_id': product }) std_preview = self._onchange_recursive(cr, uid, ids, recursive=True, context=context).get( product, 0.0) old = prod_brw.standard_price diff = std_preview - old # /!\ NOTE: Is it the right precision if float_is_zero(diff, precision_obj): # Do not update it is worthless continue if old and diff / old < std_bottom_threshold and \ prod_brw.qty_available > 0 and \ prod_brw.state != 'end': # Write product as obsolete # /!\ NOTE: Will not this cause more concurrence prod_brw.write({'state': 'end'}) price_id = self.create(cr, uid, { 'real_time_accounting': True, 'recursive': True }, context=context) try: if not prod_brw.cost_method == 'standard': new = 'Ignored Because product is not set as Standard' else: self.compute_from_bom(cr, uid, [price_id], context=context) new = prod_brw.standard_price except Exception as msg: # pylint: disable=W0703 new = msg context['message'] = message % dict(old=old, new=new) self._post_message(cr, uid, ids, context=context) # /!\ TODO: Write log for products that were ignored return True
def do_merge(self, remove_empty_invoice_lines=True): def make_key(br, fields): list_key = [] for field in fields: field_val = getattr(br, field) if field in ('product_id', 'account_id'): if not field_val: field_val = False if (isinstance(field_val, browse_record) and field != 'invoice_line_tax_ids' and field != 'sale_line_ids'): field_val = field_val.id elif isinstance(field_val, browse_null): field_val = False elif (isinstance(field_val, list) or field == 'invoice_line_tax_ids' or field == 'sale_line_ids'): field_val = ((6, 0, tuple([v.id for v in field_val])), ) list_key.append((field, field_val)) list_key.sort() return tuple(list_key) new_invoices = {} draft_invoices = [ invoice for invoice in self if invoice.state == 'draft' ] seen_origins = {} seen_client_refs = {} for account_invoice in draft_invoices: invoice_key = make_key(account_invoice, self._get_invoice_key_cols()) new_invoice = new_invoices.setdefault(invoice_key, ({}, [])) new_invoice[1].append(account_invoice.id) invoice_infos = new_invoice[0] if not invoice_infos: invoice_infos.update( self._get_first_invoice_fields(account_invoice)) for invoice_line in account_invoice.invoice_line_ids: cols = self._get_invoice_line_key_cols() line_key = make_key(invoice_line, cols) o_line = invoice_infos['invoice_line_ids'].setdefault( line_key, {}) if o_line: o_line['quantity'] += invoice_line.quantity else: o_line['quantity'] = invoice_line.quantity allinvoices = [] allnewinvoices = [] invoices_info = {} qty_prec = self.env['decimal.precision'].precision_get( 'Product Unit of Measure') for invoice_key, (invoice_data, old_ids) in new_invoices.iteritems(): if len(old_ids) < 2: allinvoices += (old_ids or []) continue for key, value in invoice_data['invoice_line_ids'].iteritems(): value.update(dict(key)) if remove_empty_invoice_lines: invoice_data['invoice_line_ids'] = [ (0, 0, value) for value in invoice_data['invoice_line_ids'].itervalues() if not float_is_zero(value['quantity'], precision_digits=qty_prec) ] else: invoice_data['invoice_line_ids'] = [ (0, 0, value) for value in invoice_data['invoice_line_ids'].itervalues() ] newinvoice = self.with_context(is_merge=True).create(invoice_data) invoices_info.update({newinvoice.id: old_ids}) allinvoices.append(newinvoice.id) allnewinvoices.append(newinvoice) for old_id in old_ids: old_invoice_id = self.sudo().browse(old_id) old_invoice_id.sudo().action_invoice_cancel() for new_invoice in allnewinvoices: new_invoice.compute_taxes() return invoices_info
def action_produce(self, production_id, production_qty, production_mode, wiz=False): stock_move_obj = self.env['stock.move'] uom_obj = self.env['product.uom'] production = self.browse(production_id) production_qty_uom = uom_obj._compute_qty( production.product_uom.id, production_qty, production.product_id.uom_id.id) precision = self.env['decimal.precision'].precision_get( 'Product Unit of Measure') main_production_move = False default_mode = self._context.get('default_mode', False) if production_mode == 'produce': # New Case: Produce Only produced_products = {} for produced_product in production.move_created_ids2: if produced_product.scrapped: continue if not produced_products.get(produced_product.product_id.id, False): produced_products[produced_product.product_id.id] = 0 produced_products[produced_product.product_id. id] += produced_product.product_qty for produce_product in production.move_created_ids: subproduct_factor = self._get_subproduct_factor( production.id, produce_product.id) lot_id = wiz and wiz.lot_id.id or False qty = min(subproduct_factor * production_qty_uom, produce_product.product_qty ) # Needed when producing more than maximum quantity new_moves = produce_product.action_consume( qty, location_id=produce_product.location_id.id, restrict_lot_id=lot_id) stock_move_obj.browse(new_moves).write( {'production_id': production_id}) remaining_qty = subproduct_factor * production_qty_uom - qty if not float_is_zero(remaining_qty, precision_digits=precision): # In case you need to make more than planned # consumed more in wizard than previously planned extra_move_id = produce_product.copy( default={ 'product_uom_qty': remaining_qty, 'production_id': production_id }) extra_move_id.action_confirm() extra_move_id.action_done() if produce_product.product_id == production.product_id: main_production_move = produce_product.id if default_mode != 'consume' and not production.move_created_ids: production.signal_workflow('button_finished_validated') else: if not main_production_move: main_production_move = production.move_created_ids2 and production.move_created_ids2[ 0].id # Añadimos al contexto 'main_production_move' # para poder escribirlo en el write y en el action_consume() self = self.with_context(main_production_move=main_production_move) res = super(MrpProduction, self).action_produce(production_id, production_qty, production_mode, wiz=wiz) if default_mode != 'consume' and not production.move_created_ids: production.signal_workflow('button_finished_validated') if default_mode == 'consume': # Custom behaivor, set closed state production.signal_workflow('button_validated_closed') return True
def action_invoice_create(self, grouped=False, final=False): """ Create the invoice associated to the SO. :param grouped: if True, invoices are grouped by SO id. If False, invoices are grouped by (partner_invoice_id, currency) :param final: if True, refunds will be generated if necessary :returns: list of created invoices """ inv_obj = self.env['account.invoice'] precision = self.env['decimal.precision'].precision_get( 'Product Unit of Measure') invoices = {} for order in self: group_key = order.id if grouped else (order.partner_invoice_id.id, order.currency_id.id) for line in order.actesmedicaux_lines.sorted( key=lambda l: l.qty_to_invoice < 0): if float_is_zero(line.qty_to_invoice, precision_digits=precision): continue if group_key not in invoices: inv_data = order._prepare_invoice() invoice = inv_obj.create(inv_data) invoices[group_key] = invoice elif group_key in invoices: vals = {} if order.name not in invoices[group_key].origin.split( ', '): vals['origin'] = invoices[ group_key].origin + ', ' + order.name if order.client_order_ref and order.client_order_ref not in invoices[ group_key].name.split(', '): vals['name'] = invoices[ group_key].name + ', ' + order.client_order_ref invoices[group_key].write(vals) if line.qty_to_invoice > 0: line.invoice_line_create(invoices[group_key].id, line.qty_to_invoice) elif line.qty_to_invoice < 0 and final: line.invoice_line_create(invoices[group_key].id, line.qty_to_invoice) if not invoices: raise UserError(_('There is no invoicable line.')) for invoice in invoices.values(): if not invoice.invoice_line_ids: raise UserError(_('There is no invoicable line.')) # If invoice is negative, do a refund invoice instead if invoice.amount_untaxed < 0: invoice.type = 'out_refund' for line in invoice.invoice_line_ids: line.quantity = -line.quantity # Use additional field helper function (for account extensions) for line in invoice.invoice_line_ids: line._set_additional_fields(invoice) # Necessary to force computation of taxes. In account_invoice, they are triggered # by onchanges, which are not triggered when doing a create. invoice.compute_taxes() return [inv.id for inv in invoices.values()]
def _process_order(self, cr, uid, order, context=None): session = self.pool.get('pos.session').browse(cr, uid, order['pos_session_id'], context=context) if session.state == 'closing_control' or session.state == 'closed': session_id = self._get_valid_session(cr, uid, order, context=context) session = self.pool.get('pos.session').browse(cr, uid, session_id, context=context) order['pos_session_id'] = session_id order_id = self.create( cr, uid, self._order_fields(cr, uid, order, context=context), context) journal_ids = set() for payments in order['statement_ids']: self.add_payment(cr, uid, order_id, self._payment_fields(cr, uid, payments[2], context=context), context=context) journal_ids.add(payments[2]['journal_id']) if session.sequence_number <= order['sequence_number']: session.write({'sequence_number': order['sequence_number'] + 1}) session.refresh() if not float_is_zero( order['amount_return'], self.pool.get('decimal.precision').precision_get( cr, uid, 'Account')): # cash_journal = session.cash_journal_id.id cash_journal = False if session.config_id.journal_negativo_id: cash_journal = session.config_id.journal_negativo_id.id if not cash_journal: # Select for change one of the cash journals used in this payment cash_journal_ids = self.pool['account.journal'].search( cr, uid, [ ('type', '=', 'cash'), ('id', 'in', list(journal_ids)), ], limit=1, context=context) if not cash_journal_ids: # If none, select for change one of the cash journals of the POS # This is used for example when a customer pays by credit card # an amount higher than total amount of the order and gets cash back cash_journal_ids = [ statement.journal_id.id for statement in session.statement_ids if statement.journal_id.type == 'cash' ] if not cash_journal_ids: raise osv.except_osv( _('error!'), _("No cash statement found for this session. Unable to record returned cash." )) cash_journal = cash_journal_ids[0] self.add_payment( cr, uid, order_id, { 'amount': -order['amount_return'], 'payment_date': time.strftime('%Y-%m-%d %H:%M:%S'), 'payment_name': _('return'), 'journal': cash_journal, }, context=context) return order_id
def post_cost(self): move_pool = self.env['account.move'] # period_pool = self.env['account.period'] precision = self.env['decimal.precision'].precision_get('Account') timenow = fields.Date.today() for cost in self: line_ids = [] debit_sum = 0.0 credit_sum = 0.0 # search_periods = period_pool.find(cost.date) # period_id = search_periods[0] period_id = False default_partner_id = cost.vendor_id.id name = _('%s cost of %s') % (cost.cost_subtype_id.name.capitalize(), cost.vehicle_id.name) move = { 'narration': name, 'date': timenow, 'ref': cost.ref, 'journal_id': cost.cost_subtype_id.journal_id, 'period_id': period_id, } amt = cost.amount if float_is_zero(amt, precision_digits=precision): return partner_id = default_partner_id account_info = cost.get_account_info() debit_account_id = account_info['account_debit'] credit_account_id = account_info['account_credit'] analytic_account_id = account_info['analytic_account_id'] journal_id = account_info['journal_id'] if debit_account_id: debit_line = (0, 0, { 'name': cost.name, 'date': timenow, 'partner_id': partner_id or False, 'account_id': debit_account_id, 'journal_id': journal_id.id, 'period_id': period_id, 'debit': amt > 0.0 and amt or 0.0, 'credit': amt < 0.0 and -amt or 0.0, 'analytic_account_id': analytic_account_id or False, }) line_ids.append(debit_line) debit_sum += debit_line[2]['debit'] - debit_line[2]['credit'] if credit_account_id: credit_line = (0, 0, { 'name': cost.name, 'date': timenow, 'partner_id': partner_id or False, 'account_id': debit_account_id, 'journal_id': journal_id.id, 'period_id': period_id, 'debit': amt < 0.0 and -amt or 0.0, 'credit': amt > 0.0 and amt or 0.0, 'analytic_account_id': analytic_account_id or False, }) line_ids.append(credit_line) credit_sum += credit_line[2]['credit'] - credit_line[2]['debit'] if float_compare(credit_sum, debit_sum, precision_digits=precision) == -1: acc_id = journal_id.default_credit_account_id.id if not acc_id: raise UserError(_('The Expense Journal "%s" has not properly configured the Credit Account!') % (journal_id.name)) adjust_credit = (0, 0, { 'name': _('Adjustment Entry'), 'date': timenow, 'partner_id': False, 'account_id': acc_id, 'journal_id': journal_id.id, 'period_id': period_id, 'debit': 0.0, 'credit': debit_sum - credit_sum, }) line_ids.append(adjust_credit) elif float_compare(debit_sum, credit_sum, precision_digits=precision) == -1: acc_id = cost.journal_id.default_debit_account_id.id if not acc_id: raise UserError(_('Configuration Error!'), _('The Expense Journal "%s" has not properly configured the Debit Account!') % (cost.journal_id.name)) adjust_debit = (0, 0, { 'name': _('Adjustment Entry'), 'date': timenow, 'partner_id': False, 'account_id': acc_id, 'journal_id': journal_id.id, 'period_id': period_id, 'debit': credit_sum - debit_sum, 'credit': 0.0, }) line_ids.append(adjust_debit) move.update({'line_id': line_ids}) move = move_pool.create(move) cost.move_id = move if journal_id.entry_posted: move.post() return move
def _get_partner_move_lines(self, form, account_type, date_from, target_move): res = [] self.total_account = [] cr = self.env.cr user_company = self.env.user.company_id.id move_state = ['draft', 'posted'] if target_move == 'posted': move_state = ['posted'] arg_list = (tuple(move_state), tuple(account_type)) #build the reconciliation clause to see what partner needs to be printed reconciliation_clause = '(l.reconciled IS FALSE)' cr.execute('SELECT debit_move_id, credit_move_id FROM account_partial_reconcile where create_date > %s', (date_from,)) reconciled_after_date = [] for row in cr.fetchall(): reconciled_after_date += [row[0], row[1]] if reconciled_after_date: reconciliation_clause = '(l.reconciled IS FALSE OR l.id IN %s)' arg_list += (tuple(reconciled_after_date),) arg_list += (date_from, user_company) query = ''' SELECT DISTINCT res_partner.id AS id, res_partner.name AS name, UPPER(res_partner.name) AS uppername FROM res_partner,account_move_line AS l, account_account, account_move am WHERE (l.account_id = account_account.id) AND (l.move_id = am.id) AND (am.state IN %s) AND (account_account.internal_type IN %s) AND ''' + reconciliation_clause + ''' AND (l.partner_id = res_partner.id) AND (l.date <= %s) AND l.company_id = %s ORDER BY UPPER(res_partner.name)''' cr.execute(query, arg_list) partners = cr.dictfetchall() # put a total of 0 for i in range(7): self.total_account.append(0) # Build a string like (1,2,3) for easy use in SQL query partner_ids = [partner['id'] for partner in partners] if not partner_ids: return [] # This dictionary will store the not due amount of all partners future_past = {} query = '''SELECT l.id FROM account_move_line AS l, account_account, account_move am WHERE (l.account_id = account_account.id) AND (l.move_id = am.id) AND (am.state IN %s) AND (account_account.internal_type IN %s) AND (COALESCE(l.date_maturity,l.date) > %s)\ AND (l.partner_id IN %s) AND (l.date <= %s) AND l.company_id = %s''' cr.execute(query, (tuple(move_state), tuple(account_type), date_from, tuple(partner_ids), date_from, user_company)) aml_ids = cr.fetchall() aml_ids = aml_ids and [x[0] for x in aml_ids] or [] for line in self.env['account.move.line'].browse(aml_ids): if line.partner_id.id not in future_past: future_past[line.partner_id.id] = 0.0 line_amount = line.balance if line.balance == 0: continue for partial_line in line.matched_debit_ids: if partial_line.create_date[:10] <= date_from: line_amount += partial_line.amount for partial_line in line.matched_credit_ids: if partial_line.create_date[:10] <= date_from: line_amount -= partial_line.amount future_past[line.partner_id.id] += line_amount # Use one query per period and store results in history (a list variable) # Each history will contain: history[1] = {'<partner_id>': <partner_debit-credit>} history = [] for i in range(5): args_list = (tuple(move_state), tuple(account_type), tuple(partner_ids),) dates_query = '(COALESCE(l.date_maturity,l.date)' if form[str(i)]['start'] and form[str(i)]['stop']: dates_query += ' BETWEEN %s AND %s)' args_list += (form[str(i)]['start'], form[str(i)]['stop']) elif form[str(i)]['start']: dates_query += ' >= %s)' args_list += (form[str(i)]['start'],) else: dates_query += ' <= %s)' args_list += (form[str(i)]['stop'],) args_list += (date_from, user_company) query = '''SELECT l.id FROM account_move_line AS l, account_account, account_move am WHERE (l.account_id = account_account.id) AND (l.move_id = am.id) AND (am.state IN %s) AND (account_account.internal_type IN %s) AND (l.partner_id IN %s) AND ''' + dates_query + ''' AND (l.date <= %s) AND l.company_id = %s''' cr.execute(query, args_list) partners_amount = {} aml_ids = cr.fetchall() aml_ids = aml_ids and [x[0] for x in aml_ids] or [] for line in self.env['account.move.line'].browse(aml_ids): if line.partner_id.id not in partners_amount: partners_amount[line.partner_id.id] = 0.0 line_amount = line.balance if line.balance == 0: continue for partial_line in line.matched_debit_ids: if partial_line.create_date[:10] <= date_from: line_amount += partial_line.amount for partial_line in line.matched_credit_ids: if partial_line.create_date[:10] <= date_from: line_amount -= partial_line.amount partners_amount[line.partner_id.id] += line_amount history.append(partners_amount) for partner in partners: at_least_one_amount = False values = {} # Query here is replaced by one query which gets the all the partners their 'after' value after = False if partner['id'] in future_past: # Making sure this partner actually was found by the query after = [future_past[partner['id']]] self.total_account[6] = self.total_account[6] + (after and after[0] or 0.0) values['direction'] = after and after[0] or 0.0 if not float_is_zero(values['direction'], precision_rounding=self.env.user.company_id.currency_id.rounding): at_least_one_amount = True for i in range(5): during = False if partner['id'] in history[i]: during = [history[i][partner['id']]] # Adding counter self.total_account[(i)] = self.total_account[(i)] + (during and during[0] or 0) values[str(i)] = during and during[0] or 0.0 if not float_is_zero(values[str(i)], precision_rounding=self.env.user.company_id.currency_id.rounding): at_least_one_amount = True values['total'] = sum([values['direction']] + [values[str(i)] for i in range(5)]) ## Add for total self.total_account[(i + 1)] += values['total'] values['name'] = partner['name'] if at_least_one_amount: res.append(values) total = 0.0 totals = {} for r in res: total += float(r['total'] or 0.0) for i in range(5) + ['direction']: totals.setdefault(str(i), 0.0) totals[str(i)] += float(r[str(i)] or 0.0) return res
def _create_closing_move(self, cr, uid, account_mapping_ids, period_ids, description, date, period_id, journal_id, company_id, fiscalyear_id, context=None): """Create a closing move with the given data, provided by another method. """ if context is None: context = {} move_lines = [] dest_accounts_totals = {} ctx = context.copy() ctx.update({ 'fiscalyear': fiscalyear_id, 'periods': period_ids, 'company_id': company_id }) account_obj = self.pool['account.account'] move_line_obj = self.pool['account.move.line'] decimal_precision_obj = self.pool['decimal.precision'] precision = decimal_precision_obj.precision_get(cr, uid, 'Account') # For each (parent) account in the mapping list for account_map in account_mapping_ids: # Init (if needed) the dictionary that will store the totals for # the dest accounts if account_map.dest_account_id and not \ dest_accounts_totals.get(account_map.dest_account_id.id): dest_accounts_totals[account_map.dest_account_id.id] = 0 # Find its children accounts (recursively) # FIXME: _get_children_and_consol is a protected member of # account_account, # but the OpenERP code base uses it like this :( child_ids = account_obj._get_children_and_consol( cr, uid, [account_map.source_account_id.id], ctx) # For each children account. (Notice the context filter! the # computed balanced is based on this filter) for account in account_obj.browse(cr, uid, child_ids, ctx): # Check if the children account needs to (and can) be closed if account.type == 'view': continue if account.user_type.close_method == 'balance': # Compute the balance for the account (uses the # previous browse context filter) balance = account.balance # Check if the balance is greater than the limit if not float_is_zero(balance, precision_digits=precision): # Add a new line to the move move_lines.append({ 'account_id': account.id, 'debit': balance < 0 and -balance, 'credit': balance > 0 and balance, 'name': description, 'date': date, 'partner_id': False, 'period_id': period_id, 'journal_id': journal_id, }) # Update the dest account total (with the inverse # of the balance) if account_map.dest_account_id: dest_id = account_map.dest_account_id.id dest_accounts_totals[dest_id] -= balance elif account.user_type.close_method == 'unreconciled': found_lines = move_line_obj.search(cr, uid, [ ('period_id', 'in', period_ids), ('account_id', '=', account.id), ('company_id', '=', company_id), ]) lines_by_partner = {} for line in move_line_obj.browse(cr, uid, found_lines): partner_id = line.partner_id.id balance = line.debit - line.credit lines_by_partner[partner_id] = ( lines_by_partner.get(partner_id, 0.0) + balance) for partner_id in lines_by_partner.keys(): balance = lines_by_partner[partner_id] if not float_is_zero(balance, precision_digits=precision): move_lines.append({ 'account_id': account.id, 'debit': balance < 0 and -balance, 'credit': balance > 0 and balance, 'name': description, 'date': date, 'period_id': period_id, 'journal_id': journal_id, 'partner_id': partner_id, }) # Update the dest account total (with the inverse # of the balance) if account_map.dest_account_id: dest_id = account_map.dest_account_id.id dest_accounts_totals[dest_id] -= balance elif account.user_type.close_method == 'detail': raise orm.except_orm( _('UserError'), _("Account type closing method is not supported")) else: # Account type has no closing method or method is not # listed continue # Add the dest lines for dest_account_id in dest_accounts_totals.keys(): balance = dest_accounts_totals[dest_account_id] move_lines.append({ 'account_id': dest_account_id, 'debit': balance < 0 and -balance, 'credit': balance > 0 and balance, 'name': description, 'date': date, 'partner_id': False, 'period_id': period_id, 'journal_id': journal_id, }) # Finally create the account move with all the lines (if needed) if len(move_lines): move_id = self.pool.get('account.move').create( cr, uid, { 'line_id': map(lambda x: (0, 0, x), move_lines), 'ref': description, 'date': date, 'period_id': period_id, 'journal_id': journal_id, }, context={}) else: move_id = False return move_id
def _process_order(self, order): # initialization pos_line_obj = self.env['pos.order.line'] move_obj = self.env['stock.move'] picking_obj = self.env['stock.picking'] stock_imm_tra_obj = self.env['stock.immediate.transfer'] draft_order_id = order.get('old_order_id') picking_type_id = False picking_id_cust = False picking_id_rev = False if order.get('draft_order'): if not draft_order_id: order.pop('draft_order') order_id = self.create(self._order_fields(order)) return order_id else: order_id = draft_order_id pos_line_ids = pos_line_obj.search([('order_id', '=', order_id) ]) if pos_line_ids: pos_line_obj.unlink(pos_line_ids) self.write([order_id], { 'lines': order['lines'], 'partner_id': order.get('partner_id') }) return order_id if not order.get('draft_order') and draft_order_id: order_id = draft_order_id order_obj = self.browse(order_id) pos_line_ids = pos_line_obj.search([('order_id', '=', order_id)]) if pos_line_ids: if not order.get('cancel_order'): for line_id in pos_line_ids: line_id.unlink() temp = order.copy() temp.pop('statement_ids', None) temp.pop('name', None) temp.update({'date_order': order.get('creation_date')}) warehouse_id = self.env['stock.warehouse'].search([ ('lot_stock_id', '=', order_obj.config_id.stock_location_id.id) ], limit=1) location_dest_id, supplierloc = self.env[ 'stock.warehouse']._get_partner_locations() if warehouse_id: picking_type_id = self.env['stock.picking.type'].search([ ('warehouse_id', '=', warehouse_id.id), ('code', '=', 'internal') ]) for line in order.get('lines'): prod_id = self.env['product.product'].browse( line[2].get('product_id')) prod_dict = line[2] if prod_id.type != 'service' and prod_dict and prod_dict.get( 'cancel_item'): # customer delivery order picking_type_out = self.env['stock.picking.type'].search( [('warehouse_id', '=', order_obj.picking_id.picking_type_id.warehouse_id.id ), ('code', '=', 'outgoing')], limit=1) if picking_type_out: picking_id_cust = picking_obj.create({ 'name': picking_type_out.sequence_id.next_by_id(), 'picking_type_id': picking_type_out.id, 'location_id': order_obj.config_id.reserve_stock_location_id.id, 'location_dest_id': location_dest_id.id, 'state': 'draft', 'origin': order_obj.name }) if order_obj.picking_id: # unreserve order picking_id_rev = picking_obj.create({ 'name': picking_type_out.sequence_id.next_by_id(), 'picking_type_id': order_obj.picking_id.picking_type_id.id, 'location_id': order_obj.config_id.reserve_stock_location_id.id, 'location_dest_id': order_obj.config_id.stock_location_id.id, 'state': 'draft', 'origin': order_obj.name }) if prod_dict.get( 'consider_qty' ) and not order_obj.order_status == 'partial' and not order.get( 'reserved'): move_obj.create({ 'product_id': prod_id.id, 'name': prod_id.name, 'product_uom_qty': prod_dict.get('consider_qty'), 'location_id': order_obj.config_id.reserve_stock_location_id. id, 'location_dest_id': location_dest_id.id, 'product_uom': prod_id.uom_id.id, 'origin': order_obj.name, 'picking_id': picking_id_cust.id }) if prod_dict.get('cancel_qty'): move_obj.create({ 'product_id': prod_id.id, 'name': prod_id.name, 'product_uom_qty': abs(prod_dict.get('cancel_qty')), 'location_id': order_obj.config_id.reserve_stock_location_id. id, 'location_dest_id': order_obj.config_id.stock_location_id.id, 'product_uom': prod_id.uom_id.id, 'origin': order_obj.name, 'picking_id': picking_id_rev.id }) if picking_id_cust and picking_id_cust.move_lines: picking_id_cust.action_confirm() picking_id_cust.action_assign() picking_id_cust.button_validate() stock_transfer_id = stock_imm_tra_obj.search( [('pick_ids', '=', picking_id_cust.id)], limit=1).process() if stock_transfer_id: stock_transfer_id.process() order_obj.with_context({ 'out_order': True }).write({ 'picking_id': picking_id_cust.id, 'unreserved': True }) elif picking_id_cust: picking_id_cust.unlink() if picking_id_rev and picking_id_rev.move_lines: picking_id_rev.action_confirm() picking_id_rev.action_assign() picking_id_rev.button_validate() stock_transfer_id = stock_imm_tra_obj.search( [('pick_ids', '=', picking_id_rev.id)], limit=1).process() if stock_transfer_id: stock_transfer_id.process() order_obj.with_context({ 'out_order': True }).write({ 'picking_id': picking_id_rev.id, 'unreserved': True }) elif picking_id_rev: picking_id_rev.unlink() total_price = 0.00 for line in temp.get('lines'): linedict = line[2] if order_obj.session_id.config_id.prod_for_payment.id == linedict.get( 'product_id'): temp.get('lines').remove(line) if order_obj.session_id.config_id.refund_amount_product_id.id == linedict.get( 'product_id'): temp.get('lines').remove(line) total_price += sum([ line[2].get('price_subtotal_incl') for line in temp.get('lines') ]) temp['amount_total'] = total_price order_obj.write(temp) for payments in order['statement_ids']: order_obj.with_context({ 'from_pos': True }).add_payment(self._payment_fields(payments[2])) session = self.env['pos.session'].browse(order['pos_session_id']) if session.sequence_number <= order['sequence_number']: session.write( {'sequence_number': order['sequence_number'] + 1}) session.refresh() if not float_is_zero( order['amount_return'], self.env['decimal.precision']. precision_get('Account')) or order['cancel_order']: cash_journal = session.cash_journal_id if not cash_journal: cash_journal_ids = session.statement_ids.filtered( lambda st: st.journal_id.type == 'cash') if not len(cash_journal_ids): raise Warning( _('error!'), _("No cash statement found for this session. Unable to record returned cash." )) cash_journal = cash_journal_ids[0].journal_id order_obj.with_context({ 'from_pos': True }).add_payment({ 'amount': -order['amount_return'], 'payment_date': time.strftime('%Y-%m-%d %H:%M:%S'), 'payment_name': _('return'), 'journal': cash_journal.id, }) return order_obj if not order.get('draft_order') and not draft_order_id: order_id = super(pos_order, self)._process_order(order) if order_id.reserved: order_id.do_internal_transfer() return order_id
def action_produce(self, cr, uid, production_id, production_qty, production_mode, wiz=False, context=None): """ To produce final product based on production mode (consume/consume&produce). If Production mode is consume, all stock move lines of raw materials will be done/consumed. If Production mode is consume & produce, all stock move lines of raw materials will be done/consumed and stock move lines of final product will be also done/produced. @param production_id: the ID of mrp.production object @param production_qty: specify qty to produce in the uom of the production order @param production_mode: specify production mode (consume/consume&produce). @param wiz: the mrp produce product wizard, which will tell the amount of consumed products needed @return: True """ stock_mov_obj = self.pool.get('stock.move') uom_obj = self.pool.get("product.uom") production = self.browse(cr, uid, production_id, context=context) production_qty_uom = uom_obj._compute_qty( cr, uid, production.product_uom.id, production_qty, production.product_id.uom_id.id) precision = self.pool['decimal.precision'].precision_get( cr, uid, 'Product Unit of Measure') main_production_move = False if production_mode == 'consume_produce': for produce_product in production.move_created_ids: if produce_product.product_id.id == production.product_id.id: main_production_move = produce_product.id total_consume_moves = set() if production_mode in ['consume', 'consume_produce']: if wiz: consume_lines = [] for cons in wiz.consume_lines: consume_lines.append({ 'product_id': cons.product_id.id, 'lot_id': cons.lot_id.id, 'product_qty': cons.product_qty }) else: consume_lines = self._calculate_qty(cr, uid, production, production_qty_uom, context=context) for consume in consume_lines: remaining_qty = consume['product_qty'] for raw_material_line in production.move_lines: if raw_material_line.state in ('done', 'cancel'): continue if remaining_qty <= 0: break if consume['product_id'] != raw_material_line.product_id.id: continue consumed_qty = min(remaining_qty, raw_material_line.product_qty) stock_mov_obj.action_consume( cr, uid, [raw_material_line.id], consumed_qty, raw_material_line.location_id.id, restrict_lot_id=consume['lot_id'], consumed_for=main_production_move, context=context) total_consume_moves.add(raw_material_line.id) remaining_qty -= consumed_qty if not float_is_zero(remaining_qty, precision_digits=precision): # consumed more in wizard than previously planned product = self.pool.get('product.product').browse( cr, uid, consume['product_id'], context=context) extra_move_id = self._make_consume_line_from_data( cr, uid, production, product, product.uom_id.id, remaining_qty, context=context) stock_mov_obj.write( cr, uid, [extra_move_id], { 'restrict_lot_id': consume['lot_id'], 'consumed_for': main_production_move }, context=context) stock_mov_obj.action_done(cr, uid, [extra_move_id], context=context) total_consume_moves.add(extra_move_id) if production_mode == 'consume_produce': # add production lines that have already been consumed since the last 'consume & produce' last_production_date = production.move_created_ids2 and max( production.move_created_ids2.mapped('date')) or False already_consumed_lines = production.move_lines2.filtered( lambda l: l.date > last_production_date) total_consume_moves = total_consume_moves.union( already_consumed_lines.ids) price_unit = 0 for produce_product in production.move_created_ids: is_main_product = ( produce_product.product_id.id == production.product_id.id ) and production.product_id.cost_method == 'real' if is_main_product: total_cost = self._calculate_total_cost( cr, uid, list(total_consume_moves), context=context) production_cost = self._calculate_workcenter_cost( cr, uid, production_id, context=context) price_unit = (total_cost + production_cost) / production_qty_uom subproduct_factor = self._get_subproduct_factor( cr, uid, production.id, produce_product.id, context=context) lot_id = False if wiz: lot_id = wiz.lot_id.id qty = min(subproduct_factor * production_qty_uom, produce_product.product_qty ) #Needed when producing more than maximum quantity # Verificamos si hay una línea de subproducto y si hay una diferencia. subproduct_diff = 0 subproduct_line = self.pool.get('mrp.subproduct.produce.line') if produce_product.product_id != production.product_id: subproduct_line = wiz.subproduct_created_ids.filtered( lambda x: x.product_id == produce_product.product_id) subproduct_qty = subproduct_line.product_uom_qty if subproduct_qty > qty: subproduct_diff = subproduct_qty - qty else: qty = subproduct_qty elif production_qty > produce_product.product_uom_qty: subproduct_diff = production_qty - produce_product.product_uom_qty if is_main_product and price_unit: stock_mov_obj.write(cr, uid, [produce_product.id], {'price_unit': price_unit}, context=context) new_moves = stock_mov_obj.action_consume( cr, uid, [produce_product.id], qty, location_id=produce_product.location_id.id, restrict_lot_id=lot_id, context=context) stock_mov_obj.write(cr, uid, new_moves, {'production_id': production_id}, context=context) remaining_qty = subproduct_diff remaining_qty = subproduct_diff if not float_is_zero(remaining_qty, precision_digits=precision): # In case you need to make more than planned # consumed more in wizard than previously planned extra_move_id = stock_mov_obj.copy(cr, uid, produce_product.id, default={ 'product_uom_qty': remaining_qty, 'production_id': production_id }, context=context) if is_main_product: stock_mov_obj.write(cr, uid, [extra_move_id], {'price_unit': price_unit}, context=context) stock_mov_obj.action_confirm(cr, uid, [extra_move_id], context=context) stock_mov_obj.action_done(cr, uid, [extra_move_id], context=context) self.message_post(cr, uid, production_id, body=_("%s produced") % self._description, context=context) # Remove remaining products to consume if no more products to produce # Verificamos los valores pendientes de producción solamente contra el producto principal. main_product_remaining = production.move_created_ids.filtered( lambda x: x.product_id == production.product_id) if not main_product_remaining: if production.move_lines: stock_mov_obj.action_cancel( cr, uid, [x.id for x in production.move_lines], context=context) if production.move_created_ids: stock_mov_obj.action_cancel( cr, uid, [x.id for x in production.move_created_ids], context=context) self.signal_workflow(cr, uid, [production_id], 'button_produce_done') return True
def _calc_price( self, cr, uid, bom, test=False, real_time_accounting=False, context=None): context = dict(context or {}) price = 0 uom_obj = self.pool.get("product.uom") tmpl_obj = self.pool.get('product.template') wizard_obj = self.pool.get("stock.change.standard.price") bom_obj = self.pool.get('mrp.bom') prod_obj = self.pool.get('product.product') user = self.pool.get('res.users').browse(cr, uid, uid, context) precision_id = self.pool.get('decimal.precision').precision_get( cr, uid, 'Account') model = 'product.product' def _get_sgmnt(prod_id): res = {} sum_sgmnt = 0.0 for fieldname in SEGMENTATION_COST: fn_cost = getattr(prod_id, fieldname) sum_sgmnt += fn_cost res[fieldname] = fn_cost if not sum_sgmnt: res['material_cost'] = prod_id.standard_price return res def _bom_find(prod_id): if model == 'product.product': # if not look for template bom_id = bom_obj._bom_find( cr, uid, product_id=prod_id, context=context) if bom_id: return bom_id prod_id = prod_obj.browse( cr, uid, prod_id, context=context).product_tmpl_id.id return bom_obj._bom_find( cr, uid, product_tmpl_id=prod_id, context=context) def _factor(factor, product_efficiency, product_rounding): factor = factor / (product_efficiency or 1.0) factor = _common.ceiling(factor, product_rounding) if factor < product_rounding: factor = product_rounding return factor factor = _factor(1.0, bom.product_efficiency, bom.product_rounding) sgmnt_dict = {}.fromkeys(SEGMENTATION_COST, 0.0) for sbom in bom.bom_line_ids: my_qty = sbom.product_qty / sbom.product_efficiency if sbom.attribute_value_ids: continue # No attribute_value_ids means the bom line is not variant # specific prod_costs_dict = {}.fromkeys(SEGMENTATION_COST, 0.0) product_id = sbom.product_id if product_id.cost_method == 'average': prod_costs_dict = _get_sgmnt(product_id) else: # NOTE: Case when product is REAL or STANDARD if test and context['_calc_price_recursive']: init_bom_id = _bom_find(product_id.id) if init_bom_id: prod_costs_dict['material_cost'] = self._calc_price( cr, uid, bom_obj.browse( cr, uid, init_bom_id, context=context), test=test, real_time_accounting=real_time_accounting, context=context) else: prod_costs_dict = _get_sgmnt(product_id) else: prod_costs_dict = _get_sgmnt(product_id) for fieldname in SEGMENTATION_COST: # NOTE: Is this price well Normalized if not prod_costs_dict[fieldname]: continue price_sgmnt = uom_obj._compute_price( cr, uid, product_id.uom_id.id, prod_costs_dict[fieldname], sbom.product_uom.id) * my_qty price += price_sgmnt sgmnt_dict[fieldname] += price_sgmnt if bom.routing_id: for wline in bom.routing_id.workcenter_lines: wc = wline.workcenter_id cycle = wline.cycle_nbr dd, mm = divmod(factor, wc.capacity_per_cycle) mult = (dd + (mm and 1.0 or 0.0)) hour = float( wline.hour_nbr * mult + ( (wc.time_start or 0.0) + (wc.time_stop or 0.0) + cycle * (wc.time_cycle or 0.0)) * ( wc.time_efficiency or 1.0)) routing_price = wc.costs_cycle * cycle + wc.costs_hour * hour routing_price = uom_obj._compute_price( cr, uid, bom.product_uom.id, routing_price, bom.product_id.uom_id.id) price += routing_price sgmnt_dict['production_cost'] += routing_price # Convert on product UoM quantities if price > 0: price = uom_obj._compute_price( cr, uid, bom.product_uom.id, price / bom.product_qty, bom.product_id.uom_id.id) if test: return price # NOTE: Instanciating BOM related product product_tmpl_id = tmpl_obj.browse( cr, uid, bom.product_tmpl_id.id, context=context) bottom_price_threshold = product_tmpl_id.company_id.\ std_price_neg_threshold if not bottom_price_threshold: bottom_price_threshold = user.company_id.std_price_neg_threshold current_price = product_tmpl_id.standard_price diff = price - current_price computed_th = current_price and abs(diff * 100 / current_price) or 0.0 if diff < 0 and current_price == 0: return price if float_is_zero(diff, precision_id) or \ (current_price and diff < 0 and computed_th > bottom_price_threshold): tmpl_obj.message_post(cr, uid, [product_tmpl_id.id], 'Not Updated Cost, But Segments only.', 'I cowardly did not update Standard new \n' 'price less than old price \n' 'new {new} old {old} \n' 'Segments where written CHECK AFTERWARDS.' '{segments}'. format(old=current_price, new=price, segments=str(sgmnt_dict))) # Just writting segments to be consistent with segmentation # feature. TODO: A report should show differences. tmpl_obj.write(cr, uid, [product_tmpl_id.id], sgmnt_dict, context=context) return price diff = product_tmpl_id.standard_price - price # Write standard price if product_tmpl_id.valuation == "real_time" and \ real_time_accounting and diff: # Call wizard function here ctx = context.copy() ctx.update( {'active_id': product_tmpl_id.id, 'active_model': 'product.template'}) wizard_id = wizard_obj.create( cr, uid, {'new_price': price}, context=ctx) wizard_obj.change_price(cr, uid, [wizard_id], context=ctx) else: tmpl_obj.write( cr, uid, [product_tmpl_id.id], {'standard_price': price}, context=context) tmpl_obj.write( cr, uid, [product_tmpl_id.id], sgmnt_dict, context=context) return price
def action_invoice_create(self, grouped=False, final=False, split_invoice=False): """ Create the invoice associated to the SO. :param grouped: if True, invoices are grouped by SO id. If False, invoices are grouped by (partner, currency) :param final: if True, refunds will be generated if necessary :returns: list of created invoices """ inv_obj = self.env['account.invoice'] if self.type_order == 'claim': res = [] for inv in self: inv_res = super(sale_order_entension, inv).action_invoice_create(grouped=grouped, final=final) result_obj = inv_obj.search([('id', 'in', inv_res)]) for result in result_obj: result.type_order = 'claim' if result.company_id.id == 1: result.carrier_id = inv.carrier_id.name result.amount_freight = inv.delivery_price return res # if self.invoice_ids and self.import_text : # print "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" # for result in self.invoice_ids: # print "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" # record.carrier_id = self.carrier_id.name or False # record.delivery_price = self.amount_freight if not split_invoice: res = [] for order in self: order_res = super(sale_order_entension, order).action_invoice_create(grouped=grouped, final=final) res.append(order_res) inv_rec = inv_obj.search([('id', 'in', order_res)]) for invoice in inv_rec: if order.import_text: invoice.invoice_lines_boolean = order.import_text if invoice.company_id.id == 1: invoice.carrier_id = order.carrier_id.name invoice.amount_freight = order.delivery_price for box_line in order.product_packaging_one2many: box_vals = { 'name': box_line.name.id, 'code': box_line.code, 'amount': box_line.amount, 'rate': box_line.rate, 'qty': box_line.qty, 'account_packaging_id': invoice.id } self.env['packaging.extension'].create(box_vals) return res else: precision = self.env['decimal.precision'].precision_get( 'Product Unit of Measure') invoices = {} for order in self: comp_dict = {} for box_line in order.product_packaging_one2many: c_id = box_line.name.company_id.id if c_id in comp_dict: comp_dict[c_id].append(box_line.code) else: comp_dict[c_id] = [box_line.code] for key, value in comp_dict.items(): invoice = False group_key = str(order.id) + "," + str(key) # for line in order.order_line.sorted(key=lambda l: l.qty_to_invoice < 0): for line in order.order_line.sorted( key=lambda l: l.product_id.product_packaging_id. code): if line.box_code.code in value: if float_is_zero(line.qty_to_invoice, precision_digits=precision): continue if group_key not in invoices: inv_data = order._prepare_invoice() inv_data['company_id'] = key inv_data[ 'invoice_lines_boolean'] = order.import_text if key == 1: inv_data[ 'carrier_id'] = order.carrier_id.name inv_data[ 'amount_freight'] = order.delivery_price invoice = inv_obj.create(inv_data) invoices[group_key] = invoice elif group_key in invoices and order.name not in invoices[ group_key].origin.split(', '): invoices[group_key].write({ 'origin': invoices[group_key].origin + ', ' + order.name }) if line.qty_to_invoice > 0: line.invoice_line_create( invoices[group_key].id, line.qty_to_invoice) elif line.qty_to_invoice < 0 and final: line.invoice_line_create( invoices[group_key].id, line.qty_to_invoice) for box_line in order.product_packaging_one2many: if box_line.code in value: box_vals = { 'name': box_line.name.id, 'code': box_line.code, 'amount': box_line.amount, 'rate': box_line.rate, 'qty': box_line.qty, 'account_packaging_id': invoice.id } self.env['packaging.extension'].create(box_vals) for invoice in invoices.values(): # If invoice is negative, do a refund invoice instead if invoice.amount_untaxed < 0: invoice.type = 'out_refund' for line in invoice.invoice_line_ids: line.quantity = -line.quantity # Necessary to force computation of taxes. In account_invoice, they are triggered # by onchanges, which are not triggered when doing a create. invoice.compute_taxes() return [inv.id for inv in invoices.values()]
def do_merge(self, keep_references=True, date_invoice=False, remove_empty_invoice_lines=True): """ To merge similar type of account invoices. Invoices will only be merged if: * Account invoices are in draft * Account invoices belong to the same partner * Account invoices are have same company, partner, address, currency, journal, currency, salesman, account, type Lines will only be merged if: * Invoice lines are exactly the same except for the quantity and unit @param self: The object pointer. @param keep_references: If True, keep reference of original invoices @return: new account invoice id """ def make_key(br, fields): list_key = [] for field in fields: field_val = getattr(br, field) if field in ('product_id', 'account_id'): if not field_val: field_val = False if (isinstance(field_val, browse_record) and field != 'invoice_line_tax_ids' and field != 'sale_line_ids'): field_val = field_val.id elif isinstance(field_val, browse_null): field_val = False elif (isinstance(field_val, list) or field == 'invoice_line_tax_ids' or field == 'sale_line_ids'): field_val = ((6, 0, tuple([v.id for v in field_val])), ) list_key.append((field, field_val)) list_key.sort() return tuple(list_key) # compute what the new invoices should contain new_invoices = {} draft_invoices = [ invoice for invoice in self if invoice.state == 'draft' ] seen_origins = {} seen_client_refs = {} for account_invoice in draft_invoices: invoice_key = make_key(account_invoice, self._get_invoice_key_cols()) new_invoice = new_invoices.setdefault(invoice_key, ({}, [])) origins = seen_origins.setdefault(invoice_key, set()) client_refs = seen_client_refs.setdefault(invoice_key, set()) new_invoice[1].append(account_invoice.id) invoice_infos = new_invoice[0] if not invoice_infos: invoice_infos.update( self._get_first_invoice_fields(account_invoice)) origins.add(account_invoice.origin) client_refs.add(account_invoice.reference) if not keep_references: invoice_infos.pop('name') else: if account_invoice.name and keep_references: invoice_infos['name'] = \ (invoice_infos['name'] or '') + ' ' + \ account_invoice.name if account_invoice.origin and \ account_invoice.origin not in origins: invoice_infos['origin'] = \ (invoice_infos['origin'] or '') + ' ' + \ account_invoice.origin origins.add(account_invoice.origin) if account_invoice.reference \ and account_invoice.reference not in client_refs: invoice_infos['reference'] = \ (invoice_infos['reference'] or '') + ' ' + \ account_invoice.reference client_refs.add(account_invoice.reference) for invoice_line in account_invoice.invoice_line_ids: line_key = make_key(invoice_line, self._get_invoice_line_key_cols()) o_line = invoice_infos['invoice_line_ids'].\ setdefault(line_key, {}) if o_line: # merge the line with an existing line o_line['quantity'] += invoice_line.quantity else: # append a new "standalone" line o_line['quantity'] = invoice_line.quantity allinvoices = [] allnewinvoices = [] invoices_info = {} qty_prec = self.env['decimal.precision'].precision_get( 'Product Unit of Measure') for invoice_key, (invoice_data, old_ids) in new_invoices.iteritems(): # skip merges with only one invoice if len(old_ids) < 2: allinvoices += (old_ids or []) continue # cleanup invoice line data for key, value in invoice_data['invoice_line_ids'].iteritems(): value.update(dict(key)) if remove_empty_invoice_lines: invoice_data['invoice_line_ids'] = [ (0, 0, value) for value in invoice_data['invoice_line_ids'].itervalues() if not float_is_zero(value['quantity'], precision_digits=qty_prec) ] else: invoice_data['invoice_line_ids'] = [ (0, 0, value) for value in invoice_data['invoice_line_ids'].itervalues() ] if date_invoice: invoice_data['date_invoice'] = date_invoice # create the new invoice newinvoice = self.with_context(is_merge=True).create(invoice_data) invoices_info.update({newinvoice.id: old_ids}) allinvoices.append(newinvoice.id) allnewinvoices.append(newinvoice) # make triggers pointing to the old invoices point to the new # invoice for old_id in old_ids: workflow.trg_redirect(self.env.uid, 'account.invoice', old_id, newinvoice.id, self.env.cr) workflow.trg_validate(self.env.uid, 'account.invoice', old_id, 'invoice_cancel', self.env.cr) # Make link between original sale order # None if sale is not installed so_obj = self.env['sale.order'] \ if 'sale.order' in self.env.registry else False invoice_line_obj = self.env['account.invoice.line'] for new_invoice_id in invoices_info: if so_obj is not False: todos = so_obj.search([('invoice_ids', 'in', invoices_info[new_invoice_id])]) todos.write({'invoice_ids': [(4, new_invoice_id)]}) for org_so in todos: for so_line in org_so.order_line: invoice_lines = invoice_line_obj.search([ ('product_id', '=', so_line.product_id.id), ('invoice_id', '=', new_invoice_id) ]) if invoice_lines: so_line.write( {'invoice_lines': [(6, 0, invoice_lines.ids)]}) # recreate link (if any) between original analytic account line # (invoice time sheet for example) and this new invoice anal_line_obj = self.env['account.analytic.line'] if 'invoice_id' in anal_line_obj._columns: for new_invoice_id in invoices_info: todos = anal_line_obj.search([('invoice_id', 'in', invoices_info[new_invoice_id])]) todos.write({'invoice_id': new_invoice_id}) for new_invoice in allnewinvoices: new_invoice.compute_taxes() return invoices_info
def _add_invoice_line_block(self, trade_transaction, iline, line_number, sign, ns): self.ensure_one() dpo = self.env['decimal.precision'] pp_prec = dpo.precision_get('Product Price') disc_prec = dpo.precision_get('Discount') qty_prec = dpo.precision_get('Product Unit of Measure') inv_currency_name = self.currency_id.name line_item = etree.SubElement( trade_transaction, ns['ram'] + 'IncludedSupplyChainTradeLineItem') line_doc = etree.SubElement( line_item, ns['ram'] + 'AssociatedDocumentLineDocument') etree.SubElement(line_doc, ns['ram'] + 'LineID').text = unicode(line_number) line_trade_agreement = etree.SubElement( line_item, ns['ram'] + 'SpecifiedSupplyChainTradeAgreement') # convert gross price_unit to tax_excluded value taxres = iline.invoice_line_tax_id.compute_all(iline.price_unit, 1) gross_price_val = float_round(taxres['total'], precision_digits=pp_prec) # Use oline.price_subtotal/qty to compute net unit price to be sure # to get a *tax_excluded* net unit price if float_is_zero(iline.quantity, precision_digits=qty_prec): net_price_val = 0.0 else: net_price_val = float_round(iline.price_subtotal / float(iline.quantity), precision_digits=pp_prec) gross_price = etree.SubElement( line_trade_agreement, ns['ram'] + 'GrossPriceProductTradePrice') gross_price_amount = etree.SubElement(gross_price, ns['ram'] + 'ChargeAmount', currencyID=inv_currency_name) gross_price_amount.text = unicode(gross_price_val) fc_discount = float_compare(iline.discount, 0.0, precision_digits=disc_prec) if fc_discount in [-1, 1]: trade_allowance = etree.SubElement( gross_price, ns['ram'] + 'AppliedTradeAllowanceCharge') charge_indic = etree.SubElement(trade_allowance, ns['ram'] + 'ChargeIndicator') indicator = etree.SubElement(charge_indic, ns['udt'] + 'Indicator') if fc_discount == 1: indicator.text = 'false' else: indicator.text = 'true' actual_amount = etree.SubElement(trade_allowance, ns['ram'] + 'ActualAmount', currencyID=inv_currency_name) actual_amount_val = float_round(gross_price_val - net_price_val, precision_digits=pp_prec) actual_amount.text = unicode(abs(actual_amount_val)) net_price = etree.SubElement(line_trade_agreement, ns['ram'] + 'NetPriceProductTradePrice') net_price_amount = etree.SubElement(net_price, ns['ram'] + 'ChargeAmount', currencyID=inv_currency_name) net_price_amount.text = unicode(net_price_val) line_trade_delivery = etree.SubElement( line_item, ns['ram'] + 'SpecifiedSupplyChainTradeDelivery') if iline.uos_id and iline.uos_id.unece_code: unitCode = iline.uos_id.unece_code else: unitCode = 'C62' if not iline.uos_id: logger.warning( "No unit of measure on invoice line '%s', " "using C62 (piece) as fallback", iline.name) else: logger.warning( 'Missing UNECE Code on unit of measure %s, ' 'using C62 (piece) as fallback', iline.uos_id.name) billed_qty = etree.SubElement(line_trade_delivery, ns['ram'] + 'BilledQuantity', unitCode=unitCode) billed_qty.text = unicode(iline.quantity * sign) line_trade_settlement = etree.SubElement( line_item, ns['ram'] + 'SpecifiedSupplyChainTradeSettlement') if iline.invoice_line_tax_id: for tax in iline.invoice_line_tax_id: trade_tax = etree.SubElement(line_trade_settlement, ns['ram'] + 'ApplicableTradeTax') trade_tax_typecode = etree.SubElement(trade_tax, ns['ram'] + 'TypeCode') if not tax.unece_type_code: raise UserError( _("Missing UNECE Tax Type on tax '%s'") % tax.name) trade_tax_typecode.text = tax.unece_type_code trade_tax_categcode = etree.SubElement( trade_tax, ns['ram'] + 'CategoryCode') if not tax.unece_categ_code: raise UserError( _("Missing UNECE Tax Category on tax '%s'") % tax.name) trade_tax_categcode.text = tax.unece_categ_code if tax.type == 'percent': trade_tax_percent = etree.SubElement( trade_tax, ns['ram'] + 'ApplicablePercent') trade_tax_percent.text = unicode(tax.amount * 100) subtotal = etree.SubElement( line_trade_settlement, ns['ram'] + 'SpecifiedTradeSettlementMonetarySummation') subtotal_amount = etree.SubElement(subtotal, ns['ram'] + 'LineTotalAmount', currencyID=inv_currency_name) subtotal_amount.text = unicode(iline.price_subtotal * sign) trade_product = etree.SubElement(line_item, ns['ram'] + 'SpecifiedTradeProduct') if iline.product_id: if iline.product_id.ean13: ean13 = etree.SubElement(trade_product, ns['ram'] + 'GlobalID', schemeID='0160') # 0160 = GS1 Global Trade Item Number (GTIN, EAN) ean13.text = iline.product_id.ean13 if iline.product_id.default_code: product_code = etree.SubElement(trade_product, ns['ram'] + 'SellerAssignedID') product_code.text = iline.product_id.default_code product_name = etree.SubElement(trade_product, ns['ram'] + 'Name') product_name.text = iline.name if iline.product_id and iline.product_id.description_sale: product_desc = etree.SubElement(trade_product, ns['ram'] + 'Description') product_desc.text = iline.product_id.description_sale
def _is_difference_zero(self): for bank_stmt in self: bank_stmt.is_difference_zero = float_is_zero(bank_stmt.difference, precision_digits=bank_stmt.currency_id.decimal_places)
def bouton_confirm(self): #preparing invoice for actesmedicaux in self: if not actesmedicaux.actesmedicaux_lines: raise UserError(_('There is no medical acts line.')) if actesmedicaux.actesmedicaux_lines: self.ensure_one() journal_id = self.env['account.invoice'].default_get( ['journal_id'])['journal_id'] if not journal_id: raise osv.except_osv( _('Attention!!'), _('Please define an accounting sale journal for this company.' )) invoice_vals = { 'name': actesmedicaux.patient_id.partner_id.name, 'actesmedicaux_id': actesmedicaux.id, 'physician_id': actesmedicaux.physician_id.id, 'origin': 'Hospitalization -> #%s' % (actesmedicaux.id), 'type': 'out_invoice', 'account_id': actesmedicaux.patient_id.partner_id. property_account_receivable_id.id, 'partner_id': actesmedicaux.patient_id.partner_id.id, 'journal_id': journal_id, 'currency_id': actesmedicaux.product_id.currency_id.id, 'comment': '------------- ', 'payment_term_id': actesmedicaux.patient_id.partner_id. property_payment_term_id.id, 'fiscal_position_id': actesmedicaux.patient_id.partner_id. property_account_position_id.id, 'company_id': actesmedicaux.patient_id.partner_id.company_id.id, 'user_id': actesmedicaux.create_uid.id, 'team_id': actesmedicaux.create_uid.team_id.id } invoice = self.env['account.invoice'].create(invoice_vals) for line in actesmedicaux.actesmedicaux_lines: if not line.product_id.property_account_income_id and not line.product_id.categ_id.property_account_income_categ_id: raise osv.except_osv( _('Attention!!'), _('Please specify the revenue account in the accounting tab of this Medical act or product(service).' )) #actesmedicaux.write({'end_date':fields.Date.context_today(self)}) #raise osv.except_osv(_('INVOICE %s' % (invoice.id)),_(' Is it the true?.')) #invoice= self.env['account.invoice'].browse(invoice.id) precision = self.env['decimal.precision'].precision_get( 'Product Unit of Measure') if float_is_zero(line.product_uom_qty, precision_digits=precision): continue property_account_income_id = False if line.product_id.property_account_income_id: property_account_income_id = line.product_id.property_account_income_id.id else: property_account_income_id = line.product_id.categ_id.property_account_income_categ_id.id invoice_line_vals = { 'name': line.product_id.name, 'origin': 'INV: %s => %s' % (invoice.id, actesmedicaux.patient_id.partner_id.name), 'sequence': 1, 'invoice_id': invoice.id, 'uom_id': line.product_id.uom_id.id, 'product_id': line.product_id.id, 'account_id': property_account_income_id, 'price_unit': line. price_unit, #Envisager d'appeler plutot la fonction qui renvoie le prix de la liste des prix (self.lit_id.categorie_id.product_id.list_price,) 'price_subtotal': line.price_subtotal, 'price_subtotal_signed': line.price_subtotal, 'quantity': line.product_uom_qty, 'discount': 0, 'company_id': actesmedicaux.patient_id.partner_id.company_id.id, 'partner_id': actesmedicaux.patient_id.partner_id.id, 'currency_id': line.product_id.currency_id.id, 'company_currency_id': line.product_id.currency_id.id, #'invoice_line_tax_ids': 'account_analytic_id': False } invoice_line = self.env['account.invoice.line'].create( invoice_line_vals) #raise osv.except_osv(_('INVOICE %s' % (invoice.id)),_(' Is it the true?.')) #invoice_line= self.env['account.invoice.line'].browse(ids_invoice_line[0]) invoice_line._set_taxes() invoice_line._compute_price() self.env['account.invoice.line'].write(invoice_line) invoice._onchange_invoice_line_ids() invoice._compute_amount() vals = { 'tax_line_ids': invoice.tax_line_ids, 'amount_untaxed': invoice.amount_untaxed, 'amount_tax': invoice.amount_tax, 'amount_total': invoice.amount_total, 'amount_total_company_signed': invoice.amount_total_company_signed, 'amount_total_signed': invoice.amount_total_signed, 'amount_untaxed_signed': invoice.amount_untaxed_signed, } invoice.write(vals) #invoice.invoice_validate()( #invoice.write(invoice) #invoice.post() #invoice_line.post() #raise osv.except_osv(_('INVOICE %s' % (invoice.id)),_(' Is it the true....?.')) actesmedicaux.write({ 'state': 'confirm', 'invoice_id': invoice.id }) #update = "update hospital_actesmedicaux set state = '%s',invoice_id = %s where id = %s" % ('confirm',invoice.id, actesmedicaux.id) #self.env.cr.execute(str(update)) #try: #except ValueError: # print("Error Value.") #except indexError: # print("Erorr index") #except : # print('error ') # La part des practiciens extérieurs # envisager aussi de calculer la part losque le praticien est employé if not actesmedicaux.physician_id.employee_id: # Le practicien est quelcun de l'exterieur. Si la règle est défini alors on génere la facture d'achat de service pour ce dernier #actesmedicaux.write({'end_date':fields.Date.context_today(self)}) invoice_vals = { 'name': actesmedicaux.physician_id.partner_id.name, 'actesmedicaux_id': actesmedicaux.id, 'physician_id': actesmedicaux.physician_id.id, 'origin': 'Medical Act -> #%s' % (actesmedicaux.id), 'type': 'in_invoice', 'account_id': actesmedicaux.physician_id.partner_id. property_account_payable_id.id, 'partner_id': actesmedicaux.physician_id.partner_id.id, 'journal_id': journal_id, 'currency_id': actesmedicaux.product_id.currency_id.id, 'comment': '------------- ', 'payment_term_id': actesmedicaux.physician_id.partner_id. property_supplier_payment_term_id.id, 'fiscal_position_id': actesmedicaux.physician_id.partner_id. property_account_position_id.id, 'company_id': actesmedicaux.physician_id.partner_id.company_id.id, 'user_id': actesmedicaux.create_uid.id, 'team_id': actesmedicaux.create_uid.team_id.id } invoice = self.env['account.invoice'].create(invoice_vals) for line in actesmedicaux.actesmedicaux_lines: if not line.product_id.property_account_expense_id and not line.product_id.categ_id.property_account_expense_categ_id: raise osv.except_osv( _('Attention!!'), _('Please specify the expense account in the accounting tab of this Medical act or product(service).' )) #raise osv.except_osv(_('INVOICE %s' % (invoice.id)),_(' Is it the true?.')) #invoice= self.env['account.invoice'].browse(invoice.id) precision = self.env[ 'decimal.precision'].precision_get( 'Product Unit of Measure') if float_is_zero( line.product_uom_qty, precision_digits=precision ) or line.product_id.product_tmpl_id.type != 'service': continue percent_phisician = 100 recs = self.env['physician.payment.term'].search([ '&', ('product_id', '=', line.product_id.id), ('physician_id', '=', actesmedicaux.physician_id.id), ('state', '=', 'activate') ]) if not recs: recs = self.env['physician.payment.term'].search([ '&', ('product_id', '=', line.product_id.id), ('state', '=', 'activate') ]) if not recs: recs = self.env[ 'physician.payment.term'].search([ '&', ('physician_id', '=', actesmedicaux.physician_id.id), ('state', '=', 'activate') ]) if recs: continue else: raise osv.except_osv( _('Configuration!!'), _('Please, for the service product %s specify the physician payment term if the physician is an external consultant or his employee id if he is an employee' % (line.product_id.name))) if recs: for term in recs: percent_phisician = term.percent_phisician property_account_expense_id = False if line.product_id.property_account_income_id: property_account_expense_id = line.product_id.property_account_expense_id.id else: property_account_expense_id = line.product_id.categ_id.property_account_expense_categ_id.id invoice_line_vals = { 'name': line.product_id.name, 'origin': 'INV: %s => %s' % (invoice.id, actesmedicaux.patient_id.partner_id.name), 'sequence': 1, 'invoice_id': invoice.id, 'uom_id': line.product_id.uom_id.id, 'product_id': line.product_id.id, 'account_id': property_account_expense_id, 'price_unit': line.price_unit * ( percent_phisician / 100 ), #Envisager d'appeler plutot la fonction qui renvoie le prix de la liste des prix (self.lit_id.categorie_id.product_id.list_price,) 'price_subtotal': line.product_uom_qty * line.price_unit * (percent_phisician / 100), 'price_subtotal_signed': line.product_uom_qty * line.price_unit * (percent_phisician / 100), 'quantity': line.product_uom_qty, 'discount': 0, 'company_id': actesmedicaux.physician_id.partner_id.company_id. id, 'partner_id': actesmedicaux.physician_id.partner_id.id, 'currency_id': line.product_id.currency_id.id, 'company_currency_id': line.product_id.currency_id.id, #'invoice_line_tax_ids': 'account_analytic_id': False } invoice_line = self.env['account.invoice.line'].create( invoice_line_vals) #raise osv.except_osv(_('INVOICE %s' % (invoice.id)),_(' Is it the true?.')) #invoice_line= self.env['account.invoice.line'].browse(ids_invoice_line[0]) invoice_line._set_taxes() invoice_line._compute_price() self.env['account.invoice.line'].write(invoice_line) invoice._onchange_invoice_line_ids() invoice._compute_amount() vals = { 'tax_line_ids': invoice.tax_line_ids, 'amount_untaxed': invoice.amount_untaxed, 'amount_tax': invoice.amount_tax, 'amount_total': invoice.amount_total, 'amount_total_company_signed': invoice.amount_total_company_signed, 'amount_total_signed': invoice.amount_total_signed, 'amount_untaxed_signed': invoice.amount_untaxed_signed, } invoice.write(vals) actesmedicaux.write({'supplier_invoice_id': invoice.id})
def validate(self): check_total = self.env['res.users'].has_group( 'donation.group_donation_check_total') precision = self.env['decimal.precision'].precision_get('Account') for donation in self: if not donation.line_ids: raise UserError( _("Cannot validate the donation of %s because it doesn't " "have any lines!") % donation.partner_id.name) if float_is_zero(donation.amount_total, precision_digits=precision): raise UserError( _("Cannot validate the donation of %s because the " "total amount is 0 !") % donation.partner_id.name) if donation.state != 'transfer': raise UserError( _("Cannot validate the donation of %s because it is not " "in transfer state.") % donation.partner_id.name) if check_total and donation.check_total != donation.amount_total: raise UserError( _("The amount of the donation of %s (%s) is different " "from the sum of the donation lines (%s).") % (donation.partner_id.name, donation.check_total, donation.amount_total)) vals = {'state': 'done'} if donation.amount_total: move_vals = donation._prepare_donation_move() move_analytic_vals = donation._prepare_analytic_line()[0] # when we have a full in-kind donation: no account move if move_vals: move = self.env['account.move'].create(move_vals) #move.post() move_id2 = move.id vals['move_id'] = move.id move_analytic = self.env['account.analytic.line'].create( move_analytic_vals) move_analytic2 = move_analytic.id analytic = self.campaign_id.analytic_account_id.id vals['move_analytic_id'] = move_analytic.id self.env.cr.execute( "SELECT id FROM account_move_line where move_id = '%s' and analytic_account_id ='%d'" % (move.id, analytic)) res111 = self.env.cr.fetchone()[0] #ress = (move.id*2) - 1 self.env.cr.execute( "UPDATE account_analytic_line set move_id= '%s' where id= '%d'" % (res111, move_analytic.id)) resss = self.account_id.id self.env.cr.execute( "UPDATE account_analytic_line set general_account_id= '%s' where id= '%d'" % (resss, move_analytic.id)) ressss = self.commercial_partner_id.id self.env.cr.execute( "UPDATE account_analytic_line set partner_id= '%s' where id= '%d'" % (ressss, move_analytic.id)) ress2 = move_analytic.id self.env.cr.execute( "SELECT place.tag_id FROM donation_donation inner join donation_place as place on donation_donation.donation_place = place.id where donation_donation.id= '%s'" % (self.id)) res11 = self.env.cr.fetchone()[0] self.env.cr.execute( "insert INTO account_analytic_line_tag_rel(line_id, tag_id) VALUES ('%s','%s')" % (ress2, res11)) self.env.cr.execute( "SELECT gov.tag_id FROM donation_donation inner join govs_villages_gov as gov on donation_donation.gov_id = gov.id where donation_donation.id= '%s'" % (self.id)) res12 = self.env.cr.fetchone()[0] self.env.cr.execute( "insert INTO account_analytic_line_tag_rel(line_id, tag_id) VALUES ('%s','%s')" % (ress2, res12)) self.env.cr.execute( "SELECT method.tag_id FROM donation_donation inner join donation_instrument as method on donation_donation.donation_method = method.id where donation_donation.id= '%s'" % (self.id)) res13 = self.env.cr.fetchone()[0] self.env.cr.execute( "insert INTO account_analytic_line_tag_rel(line_id, tag_id) VALUES ('%s','%s')" % (ress2, res13)) else: donation.message_post( _('Full in-kind donation: no account move generated')) if (donation.tax_receipt_option == 'each' and donation.tax_receipt_total and not donation.tax_receipt_id): receipt_vals = donation._prepare_each_tax_receipt() receipt = self.env['donation.tax.receipt'].create(receipt_vals) vals['tax_receipt_id'] = receipt.id donation.write(vals) return
def action_produce(self, cr, uid, production_id, production_qty, production_mode, wiz=False, context=None): """ Aplicado a usar precio promedio ponderado considerando 1.- Producción total.- se consumo y se da de alta lo que se planifica 2.- Producción parcial.- Cada consumo parcial genera diferente costo 3.- Exceso de consumo de insumos 4.- Exceso de producción de producto final """ stock_mov_obj = self.pool.get('stock.move') uom_obj = self.pool.get("product.uom") production = self.browse(cr, uid, production_id, context=context) production_qty_uom = uom_obj._compute_qty(cr, uid, production.product_uom.id, production_qty, production.product_id.uom_id.id) precision = self.pool['decimal.precision'].precision_get(cr, uid, 'Product Unit of Measure') main_production_move = False if production_mode == 'consume_produce': for produce_product in production.move_created_ids: if produce_product.product_id.id == production.product_id.id: main_production_move = produce_product.id total_consume_moves = set() if production_mode in ['consume', 'consume_produce']: if wiz: consume_lines = [] for cons in wiz.consume_lines: consume_lines.append({'product_id': cons.product_id.id, 'lot_id': cons.lot_id.id, 'product_qty': cons.product_qty}) else: consume_lines = self._calculate_qty(cr, uid, production, production_qty_uom, context=context) for consume in consume_lines: remaining_qty = consume['product_qty'] for raw_material_line in production.move_lines: if raw_material_line.state in ('done', 'cancel'): continue if remaining_qty <= 0: break if consume['product_id'] != raw_material_line.product_id.id: continue consumed_qty = min(remaining_qty, raw_material_line.product_qty) stock_mov_obj.action_consume(cr, uid, [raw_material_line.id], consumed_qty, raw_material_line.location_id.id, restrict_lot_id=consume['lot_id'], consumed_for=main_production_move, context=context) total_consume_moves.add(raw_material_line.id) remaining_qty -= consumed_qty if not float_is_zero(remaining_qty, precision_digits=precision): #consumed more in wizard than previously planned product = self.pool.get('product.product').browse(cr, uid, consume['product_id'], context=context) extra_move_id = self._make_consume_line_from_data(cr, uid, production, product, product.uom_id.id, remaining_qty, context=context) stock_mov_obj.write(cr, uid, [extra_move_id], {'restrict_lot_id': consume['lot_id'], 'consumed_for': main_production_move}, context=context) stock_mov_obj.action_done(cr, uid, [extra_move_id], context=context) total_consume_moves.add(extra_move_id) if production_mode == 'consume_produce': # add production lines that have already been consumed since the last 'consume & produce' last_production_date = production.move_created_ids2 and max(production.move_created_ids2.mapped('date')) or False already_consumed_lines = production.move_lines2.filtered(lambda l: l.date > last_production_date) total_consume_moves = total_consume_moves.union(already_consumed_lines.ids) price_unit = 0 for produce_product in production.move_created_ids: is_main_product = (produce_product.product_id.id == production.product_id.id) and production.product_id.cost_method=='real' #if is_main_product: if production.product_id.cost_method in ('real', 'average'): total_cost = self._calculate_total_cost(cr, uid, list(total_consume_moves), context=context) production_cost = self._calculate_workcenter_cost(cr, uid, production_id, context=context) price_unit = (total_cost + production_cost) / production_qty_uom subproduct_factor = self._get_subproduct_factor(cr, uid, production.id, produce_product.id, context=context) lot_id = False if wiz: lot_id = wiz.lot_id.id qty = min(subproduct_factor * production_qty_uom, produce_product.product_qty) #Needed when producing more than maximum quantity #if is_main_product and price_unit: if production.product_id.cost_method in ('real', 'average'): stock_mov_obj.write(cr, uid, [produce_product.id], {'price_unit': price_unit}, context=context) new_moves = stock_mov_obj.action_consume(cr, uid, [produce_product.id], qty, location_id=produce_product.location_id.id, restrict_lot_id=lot_id, context=context) stock_mov_obj.write(cr, uid, new_moves, {'production_id': production_id}, context=context) remaining_qty = subproduct_factor * production_qty_uom - qty if not float_is_zero(remaining_qty, precision_digits=precision): # In case you need to make more than planned #consumed more in wizard than previously planned extra_move_id = stock_mov_obj.copy(cr, uid, produce_product.id, default={'product_uom_qty': remaining_qty, 'production_id': production_id}, context=context) if is_main_product: stock_mov_obj.write(cr, uid, [extra_move_id], {'price_unit': price_unit}, context=context) stock_mov_obj.action_confirm(cr, uid, [extra_move_id], context=context) stock_mov_obj.action_done(cr, uid, [extra_move_id], context=context) self.message_post(cr, uid, production_id, body=_("%s produced") % self._description, context=context) # Remove remaining products to consume if no more products to produce if not production.move_created_ids and production.move_lines: stock_mov_obj.action_cancel(cr, uid, [x.id for x in production.move_lines], context=context) self.signal_workflow(cr, uid, [production_id], 'button_produce_done') return True