def _make_calculations_for_create_procurement( self, cr, uid, prods, op, use_new_cursor, context=None): procurement_obj = self.pool['procurement.order'] orderpoint_obj = self.pool['stock.warehouse.orderpoint'] result = float_compare(prods, op.product_min_qty, precision_rounding=op.product_uom.rounding) if result < 0: qty = (max(op.product_min_qty, op.product_max_qty) - prods) reste = (op.qty_multiple > 0 and qty % op.qty_multiple or 0.0) result = float_compare(reste, 0.0, precision_rounding=op.product_uom.rounding) if result > 0: qty += op.qty_multiple - reste result = float_compare(qty, 0.0, precision_rounding=op.product_uom.rounding) if result > 0: qty -= orderpoint_obj.subtract_procurements( cr, uid, op, context=context) qty_rounded = float_round( qty, precision_rounding=op.product_uom.rounding) if qty_rounded > 0: vals = self._prepare_orderpoint_procurement( cr, uid, op, qty_rounded, context=context) proc_id = procurement_obj.create( cr, uid, vals, context=context) self.check(cr, uid, [proc_id]) self.run(cr, uid, [proc_id]) if use_new_cursor: cr.commit()
def test_coda_file_import(self): cr, uid = self.cr, self.uid self.statement_import_model.import_file(cr, uid, [self.bank_statement_id], context=self.context) statement_id = self.bank_statement_model.search(cr, uid, [("name", "=", "135")])[0] bank_st_record = self.bank_statement_model.browse(cr, uid, statement_id) self.assertEqual(float_compare(bank_st_record.balance_start, 11812.70, precision_digits=2), 0) self.assertEqual(float_compare(bank_st_record.balance_end_real, 13646.05, precision_digits=2), 0)
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 _quant_create(self, cr, uid, qty, move, lot_id=False, owner_id=False, src_package_id=False, dest_package_id=False, force_location_from=False, force_location_to=False, context=None): quant_obj = self.pool.get('stock.quant') quant = super(stock_quant, self)._quant_create(cr, uid, qty, move, lot_id=lot_id, owner_id=owner_id, src_package_id=src_package_id, dest_package_id=dest_package_id, force_location_from=force_location_from, force_location_to=force_location_to, context=context) if move.product_id.valuation == 'real_time': self._account_entry_move(cr, uid, [quant], move, context) # If the precision required for the variable quant cost is larger than the accounting # precision, inconsistencies between the stock valuation and the accounting entries # may arise. # For example, a box of 13 units is bought 15.00. If the products leave the # stock one unit at a time, the amount related to the cost will correspond to # round(15/13, 2)*13 = 14.95. To avoid this case, we split the quant in 12 + 1, then # record the difference on the new quant. # We need to make sure to able to extract at least one unit of the product. There is # an arbitrary minimum quantity set to 2.0 from which we consider we can extract a # unit and adapt the cost. curr_rounding = move.company_id.currency_id.rounding cost_rounded = float_round(quant.cost, precision_rounding=curr_rounding) cost_correct = cost_rounded if float_compare(quant.product_id.uom_id.rounding, 1.0, precision_digits=1) == 0\ and float_compare(quant.qty * quant.cost, quant.qty * cost_rounded, precision_rounding=curr_rounding) != 0\ and float_compare(quant.qty, 2.0, precision_rounding=quant.product_id.uom_id.rounding) >= 0: qty = quant.qty cost = quant.cost quant_correct = quant_obj._quant_split(cr, uid, quant, quant.qty - 1.0, context=context) cost_correct += (qty * cost) - (qty * cost_rounded) quant_obj.write(cr, SUPERUSER_ID, [quant.id], {'cost': cost_rounded}, context=context) quant_obj.write(cr, SUPERUSER_ID, [quant_correct.id], {'cost': cost_correct}, context=context) return quant
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 _prepare_picking(self, receive): move_obj = self.env['stock.move'] move_ids = [] for line in receive.line_ids: qty = line.product_qty domain = [('product_id', '=', line.product_id.id), ('receive_line_id', '=', False), ('state', 'not in', ('done', 'cancel'))] if line.purchase_order_id: domain += [('purchase_line_id.order_id', '=', line.purchase_order_id.id)] else: domain += [('purchase_line_id.order_id.partner_id', '=', line.receive_id.partner_id.id)] for move in move_obj.search(domain): if tools.float_compare(qty, 0.0, precision_rounding=move.product_uom.rounding) <= 0: break if tools.float_compare(qty, move.product_uom_qty, precision_rounding=move.product_uom.rounding) < 0: move = move_obj.browse(move_obj.split(move, qty)) move.write({'picking_type_id': receive.picking_type_id.id, 'receive_line_id': line.id}) move_ids.append((4, move.id)) qty -= move.product_uom_qty if tools.float_compare(qty, 0.0, precision_rounding=line.product_uom_id.rounding) > 0: raise Warning(_(u'%s 订单数量不足,多出订单数量:%s') % (line.name, qty)) pick_vals = { 'picking_type_id': receive.picking_type_id.id, 'partner_id': receive.partner_id.id, 'date': receive.date_receive, 'move_lines': move_ids, 'origin': receive.name, } return pick_vals
def button_validate(self, cr, uid, ids, context=None): quant_obj = self.pool.get('stock.quant') for cost in self.browse(cr, uid, ids, context=context): if cost.state != 'draft': raise UserError(_('Only draft landed costs can be validated')) if not cost.valuation_adjustment_lines or not self._check_sum(cr, uid, cost, context=context): raise UserError(_('You cannot validate a landed cost which has no valid valuation adjustments lines. Did you click on Compute?')) move_id = self._create_account_move(cr, uid, cost, context=context) for line in cost.valuation_adjustment_lines: if not line.move_id: continue per_unit = line.final_cost / line.quantity diff = per_unit - line.former_cost_per_unit # If the precision required for the variable diff is larger than the accounting # precision, inconsistencies between the stock valuation and the accounting entries # may arise. # For example, a landed cost of 15 divided in 13 units. If the products leave the # stock one unit at a time, the amount related to the landed cost will correspond to # round(15/13, 2)*13 = 14.95. To avoid this case, we split the quant in 12 + 1, then # record the difference on the new quant. # We need to make sure to able to extract at least one unit of the product. There is # an arbitrary minimum quantity set to 2.0 from which we consider we can extract a # unit and adapt the cost. curr_rounding = line.move_id.company_id.currency_id.rounding diff_rounded = float_round(diff, precision_rounding=curr_rounding) diff_correct = diff_rounded quants = line.move_id.quant_ids.sorted(key=lambda r: r.qty, reverse=True) quant_correct = False if quants\ and float_compare(quants[0].product_id.uom_id.rounding, 1.0, precision_digits=1) == 0\ and float_compare(line.quantity * diff, line.quantity * diff_rounded, precision_rounding=curr_rounding) != 0\ and float_compare(quants[0].qty, 2.0, precision_rounding=quants[0].product_id.uom_id.rounding) >= 0: # Search for existing quant of quantity = 1.0 to avoid creating a new one quant_correct = quants.filtered(lambda r: float_compare(r.qty, 1.0, precision_rounding=quants[0].product_id.uom_id.rounding) == 0) if not quant_correct: quant_correct = quants[0]._quant_split(quants[0].qty - 1.0) else: quant_correct = quant_correct[0] quants = quants - quant_correct diff_correct += (line.quantity * diff) - (line.quantity * diff_rounded) diff = diff_rounded quant_dict = {} for quant in quants: quant_dict[quant.id] = quant.cost + diff if quant_correct: quant_dict[quant_correct.id] = quant_correct.cost + diff_correct for key, value in quant_dict.items(): quant_obj.write(cr, SUPERUSER_ID, key, {'cost': value}, context=context) qty_out = 0 for quant in line.move_id.quant_ids: if quant.location_id.usage != 'internal': qty_out += quant.qty self._create_accounting_entries(cr, uid, line, move_id, qty_out, context=context) self.write(cr, uid, cost.id, {'state': 'done', 'account_move_id': move_id}, context=context) self.pool.get('account.move').post(cr, uid, [move_id], context=context) return True
def _check_holidays(self): for holiday in self: if holiday.holiday_type != 'employee' or holiday.type != 'remove' or not holiday.employee_id or holiday.holiday_status_id.limit: continue leave_days = holiday.holiday_status_id.get_days(holiday.employee_id.id)[holiday.holiday_status_id.id] if float_compare(leave_days['remaining_leaves'], 0, precision_digits=2) == -1 or \ float_compare(leave_days['virtual_remaining_leaves'], 0, precision_digits=2) == -1: raise ValidationError(_('The number of remaining leaves is not sufficient for this leave type.\n' 'Please verify also the leaves waiting for validation.'))
def machin(cr, uid, ids, context=None): # Conversion de devises self.with_context(date=date).from_currency.compute(amount_to_convert, to_currency_obj, round=True) # FLOAT float_compare(value1, value2, precision_digits=None, precision_rounding=None) returns -1, 0 or 1, if ``value1`` is lower than, equal to, or greater than ``value2``, at the given precision.
def create_move(self, post_move=True): created_moves = self.env['account.move'] for line in self: depreciation_date = self.env.context.get('depreciation_date') or line.depreciation_date or fields.Date.context_today(self) company_currency = line.asset_id.company_id.currency_id current_currency = line.asset_id.currency_id amount = current_currency.compute(line.amount, company_currency) sign = (line.asset_id.category_id.journal_id.type == 'purchase' or line.asset_id.category_id.journal_id.type == 'sale' and 1) or -1 asset_name = line.asset_id.name + ' (%s/%s)' % (line.sequence, len(line.asset_id.depreciation_line_ids)) reference = line.asset_id.code journal_id = line.asset_id.category_id.journal_id.id partner_id = line.asset_id.partner_id.id categ_type = line.asset_id.category_id.type debit_account = line.asset_id.category_id.account_asset_id.id credit_account = line.asset_id.category_id.account_depreciation_id.id prec = self.env['decimal.precision'].precision_get('Account') move_line_1 = { 'name': asset_name, 'account_id': credit_account, 'debit': 0.0 if float_compare(amount, 0.0, precision_digits=prec) > 0 else -amount, 'credit': amount if float_compare(amount, 0.0, precision_digits=prec) > 0 else 0.0, 'journal_id': journal_id, 'partner_id': partner_id, 'currency_id': company_currency != current_currency and current_currency.id or False, 'amount_currency': company_currency != current_currency and - sign * line.amount or 0.0, 'analytic_account_id': line.asset_id.category_id.account_analytic_id.id if categ_type == 'sale' else False, 'date': depreciation_date, } move_line_2 = { 'name': asset_name, 'account_id': debit_account, 'credit': 0.0 if float_compare(amount, 0.0, precision_digits=prec) > 0 else -amount, 'debit': amount if float_compare(amount, 0.0, precision_digits=prec) > 0 else 0.0, 'journal_id': journal_id, 'partner_id': partner_id, 'currency_id': company_currency != current_currency and current_currency.id or False, 'amount_currency': company_currency != current_currency and sign * line.amount or 0.0, 'analytic_account_id': line.asset_id.category_id.account_analytic_id.id if categ_type == 'purchase' else False, 'date': depreciation_date, } move_vals = { 'ref': reference, 'date': depreciation_date or False, 'journal_id': line.asset_id.category_id.journal_id.id, 'line_ids': [(0, 0, move_line_1), (0, 0, move_line_2)], 'asset_id': line.asset_id.id, } move = self.env['account.move'].create(move_vals) line.write({'move_id': move.id, 'move_check': True}) created_moves |= move if post_move and created_moves: created_moves.filtered(lambda r: r.asset_id and r.asset_id.category_id and r.asset_id.category_id.open_asset).post() return [x.id for x in created_moves]
def test_iva_16_currency_supplier_almost_complete(self): """ Tests Supplier with currency USD and tax 16% and two payments First payment almost complete and exchange rate greater then invoice Second payment with exchange rate same that invoice """ cr, uid = self.cr, self.uid precision = self.precision_obj.precision_get(cr, uid, 'Account') invoice_id = self.create_invoice_supplier( cr, uid, 116, [self.tax_16], time.strftime('%Y') + '-06-30', self.currency_usd_id) # validate invoice self.registry('account.invoice').signal_workflow( cr, uid, [invoice_id], 'invoice_open') invoice_record = self.account_invoice_model.browse( cr, uid, [invoice_id]) # we search aml with account payable for line_invoice in invoice_record.move_id.line_id: if line_invoice.account_id.id == self.account_payable_id: line_id = line_invoice break # create payment almost complete move_line_ids_complete = self.create_statement( cr, uid, line_id, self.partner_agrolait_id, -115, self.bank_journal_usd_id, time.strftime('%Y') + '-06-01') for move_line in move_line_ids_complete: if move_line.account_id.id == self.acc_tax_16_payment: self.assertEquals(float_compare( move_line.amount_base, 77.25, precision_digits=precision), 0) self.assertNotEqual(move_line.tax_id_secondary, False) continue # create payment complete move_line_ids_complete = self.create_statement( cr, uid, line_id, self.partner_agrolait_id, -1, self.bank_journal_usd_id, time.strftime('%Y') + '-06-30') checked_line = 0 for move_line_complete in move_line_ids_complete: if move_line_complete.account_id.id == self.acc_tax_16_payment: self.assertEquals(float_compare( move_line_complete.amount_base, 0.56, precision_digits=precision), 0) self.assertNotEqual(move_line_complete.tax_id_secondary, False) checked_line += 1 continue self.assertEquals(checked_line, 1)
def do_transfer(self, cr, uid, picking_ids, context=None): """ If no pack operation, we do simple action_done of the picking Otherwise, do the pack operations """ if not context: context = {} stock_move_obj = self.pool.get('stock.move') for picking in self.browse(cr, uid, picking_ids, context=context): if not picking.pack_operation_ids: self.action_done(cr, uid, [picking.id], context=context) continue else: need_rereserve, all_op_processed = self.picking_recompute_remaining_quantities(cr, uid, picking, context=context) #create extra moves in the picking (unexpected product moves coming from pack operations) todo_move_ids = [] if not all_op_processed: todo_move_ids += self._create_extra_moves(cr, uid, picking, context=context) #split move lines if needed toassign_move_ids = [] for move in picking.move_lines: remaining_qty = move.remaining_qty if move.state in ('done', 'cancel'): #ignore stock moves cancelled or already done continue elif move.state == 'draft': toassign_move_ids.append(move.id) if float_compare(remaining_qty, 0, precision_rounding = move.product_id.uom_id.rounding) == 0: if move.state in ('draft', 'assigned', 'confirmed','shipping_process'): todo_move_ids.append(move.id) elif float_compare(remaining_qty,0, precision_rounding = move.product_id.uom_id.rounding) > 0 and \ float_compare(remaining_qty, move.product_qty, precision_rounding = move.product_id.uom_id.rounding) < 0: new_move = stock_move_obj.split(cr, uid, move, remaining_qty, context=context) todo_move_ids.append(move.id) #Assign move as it was assigned before toassign_move_ids.append(new_move) if need_rereserve or not all_op_processed: if not picking.location_id.usage in ("supplier", "production", "inventory"): self.rereserve_quants(cr, uid, picking, move_ids=todo_move_ids, context=context) self.do_recompute_remaining_quantities(cr, uid, [picking.id], context=context) if todo_move_ids and not context.get('do_only_split'): self.pool.get('stock.move').action_done(cr, uid, todo_move_ids, context=context) if picking.picking_type_id.code == 'incoming': self.send_mail_memco_department(cr,uid,todo_move_ids) elif context.get('do_only_split'): context = dict(context, split=todo_move_ids) self._create_backorder(cr, uid, picking, context=context) if toassign_move_ids: stock_move_obj.action_assign(cr, uid, toassign_move_ids, context=context) return True
def _create_link_for_product(operation_id, product_id, qty, move_id): '''method that creates the link between a given operation and move(s) of given product, for the given quantity. Returns True if it was possible to create links for the requested quantity (False if there was not enough quantity on stock moves)''' qty_to_assign = qty prod_obj = self.pool.get("product.product") product = prod_obj.browse(cr, uid, product_id) rounding = product.uom_id.rounding qtyassign_cmp = float_compare(qty_to_assign, 0.0, precision_rounding=rounding) if prod2move_ids.get(move_id): while prod2move_ids[move_id] and qtyassign_cmp > 0: qty_on_link = _create_link_for_index(operation_id, 0, product_id, qty_to_assign, move_id, quant_id=False) qty_to_assign -= qty_on_link qtyassign_cmp = float_compare(qty_to_assign, 0.0, precision_rounding=rounding) return qtyassign_cmp == 0
def _is_available(self): self.is_available = 'true' if self.order_id.state not in ['draft', 'sent']: return elif not self._check_routing(self.product_id, self.order_id.warehouse_id.id): if float_compare(self.product_id.virtual_available, self.product_uom_qty, precision_rounding=self.product_id.uom_id and self.product_id.uom_id.rounding or 0.01) == -1: self.is_available = 'false' elif self.order_id.purchase_ids: pline = None for p in self.order_id.purchase_ids: for l in p.order_line: if l.product_id.id == self.product_id.id: pline = l if pline and float_compare(pline.product_qty, self.product_uom_qty, precision_rounding=self.product_id.uom_id and self.product_id.uom_id.rounding or 0.01) == -1: self.is_available = 'false'
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 recreer_mouvements(self): """ Recréer les mouvements de stocks si nomenclature OF modifiée """ for obj in self: qt_reste=obj.is_qt_reste_uom for move in obj.move_created_ids: move.product_uom_qty=qt_reste cr, uid, context = self.env.args for move in obj.move_lines: move.action_cancel() stock_moves = [] for line in obj.product_lines: if line.product_id.type != 'service': qty=qt_reste*line.is_bom_qty #Si la quantité restante est à 0 , mettre 0.00001 pour ne pas solder le mouvement if float_compare(qty, 0, precision_rounding=line.product_uom.rounding) == 0: qty=line.product_uom.rounding line.product_qty=qty stock_move_id = obj._make_production_consume_line(line) stock_moves.append(stock_move_id) line.product_qty=obj.product_qty*line.is_bom_qty if stock_moves: move_obj=self.pool.get('stock.move') move_obj.action_confirm(cr, uid, stock_moves, context=context) move_obj.force_assign(cr, uid, stock_moves) #** Mise à jour des ordres de travaux sans supprimer les lignes **** res = self._prepare_lines(obj) results = res[1] # workcenter_lines for row in results: for line in obj.workcenter_lines: if row['sequence']==line.sequence: line.cycle = row['cycle'] line.hour = row['hour']
def _onchange_product_id_check_availability(self): if not self.product_id: self.product_packaging = False return {} precision = self.env['decimal.precision'].precision_get('Product Unit of Measure') self.product_tmpl_id = self.product_id.product_tmpl_id if self.product_id.type == 'product': product = self.product_id.with_context( lang=self.order_id.partner_id.lang, partner_id=self.order_id.partner_id.id, date_order=self.order_id.date_order, pricelist_id=self.order_id.pricelist_id.id, uom=self.product_uom.id, warehouse_id=self.order_id.warehouse_id.id ) if float_compare(product.virtual_available, self.product_uom_qty, precision_digits=precision) == -1: # Check if MTO, Cross-Dock or Drop-Shipping is_available = False for route in self.route_id+self.product_id.route_ids: for pull in route.pull_ids: if pull.location_id.id == self.order_id.warehouse_id.lot_stock_id.id: is_available = True if not is_available: warning_mess = { 'title': _('Not enough inventory!'), 'message' : _('You plan to sell %.2f %s but you only have %.2f %s available!\nThe stock on hand is %.2f %s.') % \ (self.product_uom_qty, self.product_uom.name or self.product_id.uom_id.name, product.virtual_available, self.product_uom.name or self.product_id.uom_id.name, product.qty_available, self.product_uom.name or self.product_id.uom_id.name) } return {'warning': warning_mess} return {}
def _action_procurement_create(self): """ Create procurements based on quantity ordered. If the quantity is increased, new procurements are created. If the quantity is decreased, no automated action is taken. """ precision = self.env['decimal.precision'].precision_get('Product Unit of Measure') new_procs = self.env['procurement.order'] #Empty recordset for line in self: if line.state != 'sale': continue qty = 0.0 for proc in line.procurement_ids: qty += proc.product_qty if float_compare(qty, line.product_uom_qty, precision_digits=precision) >= 0: return False if not line.order_id.procurement_group_id: vals = line.order_id._prepare_procurement_group() line.order_id.procurement_group_id = self.env["procurement.group"].create(vals) vals = line._prepare_order_line_procurement(group_id=line.order_id.procurement_group_id.id) vals['product_qty'] = line.product_uom_qty - qty new_proc = self.env["procurement.order"].create(vals) new_procs += new_proc new_procs.run() return new_procs
def _get_delivered_qty(self): self.ensure_one() precision = self.env['decimal.precision'].precision_get('Product Unit of Measure') # In the case of a kit, we need to check if all components are shipped. We use a all or # nothing policy. A product can have several BoMs, we don't know which one was used when the # delivery was created. bom_delivered = {} bom = self.env['mrp.bom']._bom_find(product=self.product_id) if bom and bom.type == 'phantom': bom_delivered[bom.id] = False product_uom_qty_bom = self.product_uom._compute_quantity(self.product_uom_qty, bom.product_uom_id) boms, lines = bom.explode(self.product_id, product_uom_qty_bom) for bom_line, data in lines: qty = 0.0 for move in self.procurement_ids.mapped('move_ids'): if move.state == 'done' and move.product_id.id == bom_line.product_id.id: qty += move.product_uom._compute_quantity(move.product_uom_qty, bom_line.product_uom_id) if float_compare(qty, data['qty'], precision_digits=precision) < 0: bom_delivered[bom.id] = False break else: bom_delivered[bom.id] = True if bom_delivered and any(bom_delivered.values()): return self.product_uom_qty elif bom_delivered: return 0.0 return super(SaleOrderLine, self)._get_delivered_qty()
def check_product_availability(self, cr, uid, product, qty=0, uom=False, context=None): product_uom_obj = self.pool.get('product.uom') product_obj = self.pool.get('product.product') uom2 = False if uom: uom2 = product_uom_obj.browse(cr, uid, uom, context=context) if product.uom_id.category_id.id != uom2.category_id.id or context.get('force_product_uom'): uom = False uom2 = False if not uom2: uom2 = product.uom_id compare_qty = float_compare(product.virtual_available * uom2.factor, qty * product.uom_id.factor, precision_rounding=product.uom_id.rounding) warning_msgs = '' if (product.type == 'product') and int(compare_qty) == -1 and (product.procure_method == 'make_to_stock'): warn_msg = _('You plan to sell %.2f %s of %s but you only have %.2f %s available !\nThe real stock of %s is %.2f %s. (without reservations)') % \ (qty, uom2 and uom2.name or product.uom_id.name, product.name, max(0,product.virtual_available), product.uom_id.name, product.name, max(0,product.qty_available), product.uom_id.name) warning_msgs += _("Not enough stock ! : ") + warn_msg + "\n\n" return warning_msgs
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 reconcile(self, writeoff_acc_id=False, writeoff_journal_id=False): res = super(AccountMoveLine, self).reconcile(writeoff_acc_id=writeoff_acc_id, writeoff_journal_id=writeoff_journal_id) account_move_ids = [l.move_id.id for l in self if float_compare(l.move_id.matched_percentage, 1, precision_digits=5) == 0] if account_move_ids: expenses = self.env['hr.expense'].search([('account_move_id', 'in', account_move_ids)]) expenses.paid_expenses() return res
def form_feedback(self, cr, uid, data, acquirer_name, context=None): """ Override to confirm the sale order, if defined, and if the transaction is done. """ tx = None res = super(PaymentTransaction, self).form_feedback(cr, uid, data, acquirer_name, context=context) # fetch the tx, check its state, confirm the potential SO try: tx_find_method_name = '_%s_form_get_tx_from_data' % acquirer_name if hasattr(self, tx_find_method_name): tx = getattr(self, tx_find_method_name)(cr, uid, data, context=context) _logger.info('<%s> transaction processed: tx ref:%s, tx amount: %s', acquirer_name, tx.reference if tx else 'n/a', tx.amount if tx else 'n/a') if tx and tx.sale_order_id: # verify SO/TX match, excluding tx.fees which are currently not included in SO amount_matches = (tx.sale_order_id.state in ['draft', 'sent'] and float_compare(tx.amount, tx.sale_order_id.amount_total, 2) == 0) if amount_matches: if tx.state == 'done': _logger.info('<%s> transaction completed, confirming order %s (ID %s)', acquirer_name, tx.sale_order_id.name, tx.sale_order_id.id) self.pool['sale.order'].action_button_confirm(cr, SUPERUSER_ID, [tx.sale_order_id.id], context=dict(context, send_email=True)) elif tx.state != 'cancel' and tx.sale_order_id.state == 'draft': _logger.info('<%s> transaction pending, sending quote email for order %s (ID %s)', acquirer_name, tx.sale_order_id.name, tx.sale_order_id.id) self.pool['sale.order'].force_quotation_send(cr, SUPERUSER_ID, [tx.sale_order_id.id], context=context) else: _logger.warning('<%s> transaction MISMATCH for order %s (ID %s)', acquirer_name, tx.sale_order_id.name, tx.sale_order_id.id) except Exception: _logger.exception('Fail to confirm the order or send the confirmation email%s', tx and ' for the transaction %s' % tx.reference or '') return res
def do_move_consume(self, cr, uid, ids, context=None): if context is None: context = {} move_obj = self.pool.get('stock.move') uom_obj = self.pool.get('product.uom') production_obj = self.pool.get('mrp.production') move_ids = context['active_ids'] move = move_obj.browse(cr, uid, move_ids[0], context=context) production_id = move.raw_material_production_id.id production = production_obj.browse(cr, uid, production_id, context=context) precision = self.pool['decimal.precision'].precision_get(cr, uid, 'Product Unit of Measure') for data in self.browse(cr, uid, ids, context=context): qty = uom_obj._compute_qty(cr, uid, data['product_uom'].id, data.product_qty, data.product_id.uom_id.id) remaining_qty = move.product_qty - qty #check for product quantity is less than previously planned if float_compare(remaining_qty, 0, precision_digits=precision) >= 0: move_obj.action_consume(cr, uid, move_ids, qty, data.location_id.id, restrict_lot_id=data.restrict_lot_id.id, context=context) else: consumed_qty = min(move.product_qty, qty) new_moves = move_obj.action_consume(cr, uid, move_ids, consumed_qty, data.location_id.id, restrict_lot_id=data.restrict_lot_id.id, context=context) #consumed more in wizard than previously planned extra_more_qty = qty - consumed_qty #create new line for a remaining qty of the product extra_move_id = production_obj._make_consume_line_from_data(cr, uid, production, data.product_id, data.product_id.uom_id.id, extra_more_qty, context=context) move_obj.write(cr, uid, [extra_move_id], {'restrict_lot_id': data.restrict_lot_id.id}, context=context) move_obj.action_done(cr, uid, [extra_move_id], context=context) return {'type': 'ir.actions.act_window_close'}
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 _get_delivered_qty(self): self.ensure_one() precision = self.env['decimal.precision'].precision_get('Product Unit of Measure') # In the case of a kit, we need to check if all components are shipped. We use a all or # nothing policy. A product can have several BoMs, we don't know which one was used when the # delivery was created. bom_delivered = {} for bom in self.product_id.product_tmpl_id.bom_ids: if bom.type != 'phantom': continue ctx = dict(self.env.context or {}) ctx.update({'bom_effectivity_date': getattr(self.order_id, 'effective_date', self.order_id.date_order)}) bom_delivered[bom.id] = False product_uom_qty_bom = self.env['product.uom']._compute_qty_obj(self.product_uom, self.product_uom_qty, bom.product_uom) bom_exploded = self.env['mrp.bom'].with_context(ctx)._bom_explode(bom, self.product_id, product_uom_qty_bom)[0] for bom_line in bom_exploded: qty = 0.0 for move in self.procurement_ids.mapped('move_ids'): if move.state == 'done' and move.product_id.id == bom_line.get('product_id', False): qty += self.env['product.uom']._compute_qty(move.product_uom.id, move.product_uom_qty, bom_line['product_uom']) if float_compare(qty, bom_line['product_qty'], precision_digits=precision) < 0: bom_delivered[bom.id] = False break else: bom_delivered[bom.id] = True if bom_delivered and any(bom_delivered.values()): return self.product_uom_qty elif bom_delivered: return 0.0 return super(SaleOrderLine, self)._get_delivered_qty()
def check_tax_lines(self, compute_taxes): account_invoice_tax = self.env['account.invoice.tax'] company_currency = self.company_id.currency_id if not self.tax_line: for tax in compute_taxes.values(): account_invoice_tax.create(tax) else: tax_key = [] precision = self.env['decimal.precision'].precision_get('Account') for tax in self.tax_line: if tax.manual: continue key = self.get_tax_key(tax.id) tax_key.append(key) if key not in compute_taxes: raise except_orm(_('Warning!'), _('Global taxes defined,\ but they are not in invoice lines !')) base = compute_taxes[key]['base'] if float_compare(abs(base - tax.base), company_currency.rounding, precision_digits=precision) == 1: raise except_orm(_('Warning!'), _('Tax base different!\nClick\ on compute to update the tax base.')) for key in compute_taxes: if self.check_missing_tax(): if key not in tax_key: raise except_orm(_('Warning!'), _('Taxes are missing!\nClick\ on compute button.'))
def _action_procurement_create(self): """ Create procurements based on quantity ordered. If the quantity is increased, new procurements are created. If the quantity is decreased, no automated action is taken. """ precision = self.env['decimal.precision'].precision_get('Product Unit of Measure') new_procs = self.env['procurement.order'] #Empty recordset for line in self: if line.state != 'sale' or not line.product_id._need_procurement(): continue qty = 0.0 for proc in line.procurement_ids: qty += proc.product_qty if float_compare(qty, line.product_uom_qty, precision_digits=precision) >= 0: continue if not line.order_id.procurement_group_id: vals = line.order_id._prepare_procurement_group() line.order_id.procurement_group_id = self.env["procurement.group"].create(vals) vals = line._prepare_order_line_procurement(group_id=line.order_id.procurement_group_id.id) vals['product_qty'] = line.product_uom_qty - qty new_proc = self.env["procurement.order"].create(vals) new_proc.message_post_with_view('mail.message_origin_link', values={'self': new_proc, 'origin': line.order_id}, subtype_id=self.env.ref('mail.mt_note').id) new_procs += new_proc new_procs.run() return new_procs
def _banklink_form_get_invalid_parameters(self, cr, uid, tx, data, context=None): invalid_parameters = [] acquirer = tx.acquirer_id if data.get('VK_SERVICE') not in ('1111', '1911'): invalid_parameters.append(('VK_SERVICE', data.get('VK_SERVICE'), '1111 or 1911')) if data.get('VK_VERSION') != '008': invalid_parameters.append(('VK_VERSION', data.get('VK_VERSION'), '008')) if data.get('VK_SND_ID') != acquirer.bank_id: invalid_parameters.append(('VK_SND_ID', data.get('VK_SND_ID'), acquirer.bank_id)) if data.get('VK_REC_ID') != acquirer.VK_SND_ID: invalid_parameters.append(('VK_REC_ID', data.get('VK_REC_ID'), acquirer.VK_SND_ID)) if int(data.get('VK_STAMP')) != tx.sale_order_id.id: invalid_parameters.append(('VK_STAMP', data.get('VK_STAMP'), tx.sale_order_id.id)) if data.get('VK_SERVICE') == '1111': if float_compare(float(data.get('VK_AMOUNT', '0.0')), tx.amount, 2) != 0: invalid_parameters.append(('VK_AMOUNT', data.get('VK_AMOUNT'), tx.amount)) if data.get('VK_CURR') != tx.currency_id.name: invalid_parameters.append(('VK_CURR', data.get('VK_CURR'), tx.currency_id.name)) try: tx.parse_date(data.get('VK_T_DATETIME')) except ValueError: invalid_parameters.append(('VK_T_DATETIME', data.get('VK_T_DATETIME'), '<time in DATETIME format, e.g. 2015-03-13T07:21:14+0200>')) tx_values = {k: v for k, v in data.iteritems() if k.startswith('VK_')} if not acquirer.verify_MAC_string(tx_values, data.get('VK_MAC'), acquirer.get_key('%s_get_bank_cert')): invalid_parameters.append(('VK_MAC', '-smth1-', '-smth2-')) return invalid_parameters
def _onchange_product_id_check_availability(self): if not self.product_id or not self.product_uom_qty or not self.product_uom: self.product_packaging = False return {} if self.product_id.type == "product": precision = self.env["decimal.precision"].precision_get("Product Unit of Measure") product_qty = self.env["product.uom"]._compute_qty_obj( self.product_uom, self.product_uom_qty, self.product_id.uom_id ) if float_compare(self.product_id.virtual_available, product_qty, precision_digits=precision) == -1: is_available = self._check_routing() if not is_available: warning_mess = { "title": _("Not enough inventory!"), "message": _( "You plan to sell %.2f %s but you only have %.2f %s available!\nThe stock on hand is %.2f %s." ) % ( self.product_uom_qty, self.product_uom.name, self.product_id.virtual_available, self.product_id.uom_id.name, self.product_id.qty_available, self.product_id.uom_id.name, ), } return {"warning": warning_mess} return {}
def clean_outdated_links(self, sale_line): missing_part_for_line = self.get_missing_part_for_line(sale_line) if missing_part_for_line: qty_comp = float_compare( sale_line.procurements_not_scheduled_qty, 0, precision_rounding=sale_line.product_uom.rounding) if qty_comp <= 0: missing_part_for_line.unlink() else: missing_part_for_line.product_qty = sale_line.procurements_not_scheduled_qty else: sale_line.increase_or_create_link()
def _validate_asset_values(self): for rec in self: if not rec.asset_ids or not rec.target_asset_ids: raise ValidationError(_('Soure or target assets not filled!')) inactive_assets = rec.asset_ids.filtered(lambda l: not l.active) if inactive_assets: names = ', '.join(inactive_assets.mapped('code')) raise ValidationError( _('Following assets are not active!\n%s') % names) if float_compare(rec.source_asset_value, rec.target_asset_value, 2) != 0: raise ValidationError( _('To transfer, source and target value must equal!'))
def test_qif_file_import(self): from openerp.tools import float_compare qif_file_path = get_module_resource( 'account_bank_statement_import_qif', 'test_qif_file', 'test_qif.qif') qif_file = open(qif_file_path, 'rb').read().encode('base64') bank_statement_improt = self.statement_import_model.with_context( journal_id=self.ref('account.bank_journal')).create( dict(data_file=qif_file)) bank_statement_improt.import_file() bank_statement = self.statement_line_model.search( [('name', '=', 'YOUR LOCAL SUPERMARKET')], limit=1)[0].statement_id assert float_compare(bank_statement.balance_end_real, -1896.09, 2) == 0
def check_min_max_qty(self): precision = self.env['decimal.precision'].precision_get( 'Product Unit of Measure') for product in self: if (not float_is_zero(product.max_qty, precision_digits=precision) and float_compare(product.max_qty, product.min_qty, precision_digits=precision) != 1): raise ValidationError( _("On product '%s', the maximum quantity (%s) is lower " "than the minimum quantity (%s).") % (product.name_get()[0][1], product.max_qty, product.min_qty))
def wkf_validate_vs_requisition(self): """ Amount should not exceed that in Requisition """ decimal_prec = self.env['decimal.precision'] precision = decimal_prec.precision_get('Account') for po in self: # Quotation or Purchase Order requisition = po.requisition_id or po.quote_id.requisition_id if requisition: if float_compare(po.amount_total, requisition.amount_total, precision) == 1: raise ValidationError( _('Confirmed amount exceed Call for Bid amount')) return True
def _quants_get_order(self, location, product, quantity, domain=[], orderby='in_date'): """ Implementation of removal strategies If it can not reserve, it will return a tuple (None, qty) """ domain += location and [('location_id', 'child_of', location.id)] or [] domain += [('product_id', '=', product.id)] if self.env.context.get('force_company'): domain += [('company_id', '=', self.env.context.get('force_company'))] 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) if not quants: res.append((None, quantity)) break for quant in quants: rounding = product.uom_id.rounding if float_compare(quantity, abs(quant.qty), precision_rounding=rounding) >= 0: res += [(quant, abs(quant.qty))] quantity -= abs(quant.qty) elif float_compare(quantity, 0.0, precision_rounding=rounding) != 0: res += [(quant, quantity)] quantity = 0 break offset += 10 return res
def action_done(self): for breakdown in self: if float_compare(breakdown.new_policy_amount, breakdown.policy_amount, 2) != 0: raise ValidationError(_('Overall policy amount mismatch!')) if not breakdown.line_ids: raise ValidationError( _('Before you proceed, please click button to ' '"Generate Breakdown Lines".')) # if not breakdown._validate_breakdown(): # continue breakdown.generate_budget_control() breakdown.write({'state': 'done'})
def compare_amounts(self, amount1, amount2): """ Compare `amount1` and `amount2` after rounding them according to `self`'s precision. An amount is considered lower/greater than another amount if their rounded value is different. This is not the same as having a non-zero difference! For example 1.432 and 1.431 are equal at 2 digits precision, so this method would return 0. However 0.006 and 0.002 are considered different (returns 1) because they respectively round to 0.01 and 0.0, even though 0.006-0.002 = 0.004 which would be considered zero at 2 digits precision. """ return float_compare(amount1, amount2, precision_rounding=self.rounding)
def reconcile(self, writeoff_acc_id=False, writeoff_journal_id=False): res = super(AccountMoveLine, self).reconcile(writeoff_acc_id=writeoff_acc_id, writeoff_journal_id=writeoff_journal_id) account_move_ids = [ l.move_id.id for l in self if float_compare( l.move_id.matched_percentage, 1, precision_digits=5) == 0 ] if account_move_ids: expenses = self.env['hr.expense'].search([('account_move_id', 'in', account_move_ids)]) expenses.paid_expenses() return res
def _create_link_for_product(self, prod2move_ids, operation_id, product_id, qty): '''method that creates the link between a given operation and move(s) of given product, for the given quantity. Returns True if it was possible to create links for the requested quantity (False if there was not enough quantity on stock moves)''' op_sol_id = self.env['stock.pack.operation'].search([('id', '=', operation_id)]) \ .read(['sale_line_id'], load=False)[0]['sale_line_id'] if not op_sol_id: return super(ExpeditionByOrderLinePicking, self)._create_link_for_product( prod2move_ids, operation_id, product_id, qty) qty_to_assign = qty product = self.env['product.product'].browse([product_id]) rounding = product.uom_id.rounding qtyassign_cmp = float_compare(qty_to_assign, 0.0, precision_rounding=rounding) if prod2move_ids.get(product_id): while prod2move_ids[product_id] and qtyassign_cmp > 0: i = 0 for move_data in prod2move_ids[product_id]: if move_data['move']['sale_line_id'] == op_sol_id: qty_on_link, prod2move_ids = self._create_link_for_index( prod2move_ids, operation_id, i, product_id, qty_to_assign, quant_id=False) qty_to_assign -= qty_on_link qtyassign_cmp = float_compare( qty_to_assign, 0.0, precision_rounding=rounding) break else: i += 1 else: break result_comp = qtyassign_cmp == 0 return result_comp, prod2move_ids
def form_feedback(self, cr, uid, data, acquirer_name, context=None): """ Override to confirm the sale order, if defined, and if the transaction is done. """ tx = None res = super(PaymentTransaction, self).form_feedback(cr, uid, data, acquirer_name, context=context) # fetch the tx, check its state, confirm the potential SO tx_find_method_name = '_%s_form_get_tx_from_data' % acquirer_name if hasattr(self, tx_find_method_name): tx = getattr(self, tx_find_method_name)(cr, uid, data, context=context) if tx and tx.sale_order_id and tx.sale_order_id.state in [ 'draft', 'sent' ]: amount_matches = float_compare(tx.amount, tx.sale_order_id.amount_total, 2) == 0 if amount_matches: if tx.state == 'done' and tx.acquirer_id.auto_confirm == 'at_pay_confirm': _logger.info( '<%s> transaction completed, auto-confirming order %s (ID %s)', acquirer_name, tx.sale_order_id.name, tx.sale_order_id.id) self.pool['sale.order'].action_confirm( cr, SUPERUSER_ID, [tx.sale_order_id.id], context=dict(context, send_email=True)) elif tx and tx.state not in [ 'cancel', 'error' ] and tx.sale_order_id and tx.sale_order_id.state in ['draft']: _logger.info( '<%s> transaction pending/to confirm manually, sending quote email for order %s (ID %s)', acquirer_name, tx.sale_order_id.name, tx.sale_order_id.id) self.pool['sale.order'].force_quotation_send( cr, SUPERUSER_ID, [tx.sale_order_id.id], context=context) else: _logger.warning( '<%s> transaction MISMATCH for order %s (ID %s)', acquirer_name, tx.sale_order_id.name, tx.sale_order_id.id) return res
def _po_info(self, cr, uid, ids, field_names=None, arg=False, context=None): """ Finds the requisition related PO info. @return: Dictionary of values """ if not field_names: field_names = [] if context is None: context = {} res = {} for id in ids: res[id] = {}.fromkeys(field_names, 0.0) for req_line in self.browse(cr, uid, ids, context=context): generated_po = False req_qty = req_line.product_qty po_qty = 0 product_qty_remain = req_line.product_qty po_qty_str = '' if req_line.po_lines_ids: uom_obj = self.pool.get('product.uom') for po_line in req_line.po_lines_ids: if po_line.state != 'cancel': ctx_uom = context.copy() ctx_uom['raise-exception'] = False uom_po_qty = uom_obj._compute_qty_obj(cr, uid, po_line.product_uom, po_line.product_qty, \ req_line.product_uom_id, context=ctx_uom) po_qty += uom_po_qty po_qty_str += ( (po_qty_str or '') and '; ' ) + '%s(%s)@%s' % (po_line.product_qty, uom_po_qty, po_line.order_id.name) # po_finished = float_compare(po_qty, req_qty, precision_rounding=req_line.product_uom_id.rounding) po_finished = float_compare(req_qty, po_qty, precision_rounding=1) generated_po = (po_finished <= 0) if generated_po: product_qty_remain = 0 else: product_qty_remain = req_qty - po_qty res[req_line.id]['generated_po'] = generated_po res[req_line.id]['product_qty_remain'] = product_qty_remain res[req_line.id]['po_info'] = po_qty_str return res
def form_feedback(self, data, acquirer_name): """ Override to confirm the sale order, if defined, and if the transaction is done. """ tx = None res = super(PaymentTransaction, self).form_feedback(data, acquirer_name) # fetch the tx, check its state, confirm the potential SO try: tx_find_method_name = '_%s_form_get_tx_from_data' % acquirer_name if hasattr(self, tx_find_method_name): tx = getattr(self, tx_find_method_name)(data) _logger.info( '<%s> transaction processed: tx ref:%s, tx amount: %s', acquirer_name, tx.reference if tx else 'n/a', tx.amount if tx else 'n/a') if tx and tx.sale_order_id and tx.sale_order_id.state in [ 'draft', 'sent' ]: # verify SO/TX match, excluding tx.fees which are currently not included in SO amount_matches = float_compare(tx.amount, tx.sale_order_id.amount_total, 2) == 0 if amount_matches: if tx.state == 'done' and tx.acquirer_id.auto_confirm == 'at_pay_confirm': _logger.info( '<%s> transaction completed, auto-confirming order %s (ID %s)', acquirer_name, tx.sale_order_id.name, tx.sale_order_id.id) tx.sale_order_id.with_context( send_email=True).action_confirm() elif tx.state not in [ 'cancel', 'error' ] and tx.sale_order_id.state == 'draft': _logger.info( '<%s> transaction pending/to confirm manually, sending quote email for order %s (ID %s)', acquirer_name, tx.sale_order_id.name, tx.sale_order_id.id) tx.sale_order_id.force_quotation_send() else: _logger.warning( '<%s> transaction MISMATCH for order %s (ID %s)', acquirer_name, tx.sale_order_id.name, tx.sale_order_id.id) except Exception: _logger.exception( 'Fail to confirm the order or send the confirmation email%s', tx and ' for the transaction %s' % tx.reference or '') return res
def product_id_change(self, cr, uid, ids, pricelist, product, qty=0, uom=False, qty_uos=0, uos=False, name='', partner_id=False, lang=False, update_tax=True, date_order=False, packaging=False, fiscal_position=False, flag=False, context=None): context = context or {} product_uom_obj = self.pool.get('product.uom') partner_obj = self.pool.get('res.partner') product_obj = self.pool.get('product.product') warning = {} res = super(sale_order_line, self).product_id_change(cr, uid, ids, pricelist, product, qty=qty, uom=uom, qty_uos=qty_uos, uos=uos, name=name, partner_id=partner_id, lang=lang, update_tax=update_tax, date_order=date_order, packaging=packaging, fiscal_position=fiscal_position, flag=flag, context=context) if not product: res['value'].update({'product_packaging': False}) return res #update of result obtained in super function product_obj = product_obj.browse(cr, uid, product, context=context) res['value']['delay'] = (product_obj.sale_delay or 0.0) res['value']['type'] = product_obj.procure_method #check if product is available, and if not: raise an error uom2 = False if uom: uom2 = product_uom_obj.browse(cr, uid, uom, context=context) if product_obj.uom_id.category_id.id != uom2.category_id.id: uom = False if not uom2: uom2 = product_obj.uom_id # Calling product_packaging_change function after updating UoM res_packing = self.product_packaging_change(cr, uid, ids, pricelist, product, qty, uom, partner_id, packaging, context=context) res['value'].update(res_packing.get('value', {})) warning_msgs = res_packing.get('warning') and res_packing['warning']['message'] or '' compare_qty = float_compare(product_obj.virtual_available, qty, precision_rounding=uom2.rounding) if (product_obj.type=='product') and int(compare_qty) == -1 \ and (product_obj.procure_method=='make_to_stock'): warn_msg = _('You plan to sell %.2f %s but you only have %.2f %s available !\nThe real stock is %.2f %s. (without reservations)') % \ (qty, uom2.name, max(0,product_obj.virtual_available), uom2.name, max(0,product_obj.qty_available), uom2.name) warning_msgs += _("Not enough stock ! : ") + warn_msg + "\n\n" #update of warning messages if warning_msgs: warning = { 'title': _('Configuration Error!'), 'message' : warning_msgs } res.update({'warning': warning}) return res
def _compute_procure_recommended(self): procurement_model = self.env['procurement.order'] for op in self: op.procure_recommended_date = \ procurement_model._get_orderpoint_date_planned( op, datetime.today()) procure_recommended_qty = 0.0 prods = procurement_model._product_virtual_get(op) if prods is None: continue if float_compare(prods, op.product_min_qty, precision_rounding=op.product_uom.rounding) < 0: qty = max(op.product_min_qty, op.product_max_qty) - prods reste = op.qty_multiple > 0 and qty % op.qty_multiple or 0.0 if float_compare( reste, 0.0, precision_rounding=op.product_uom.rounding) > 0: qty += op.qty_multiple - reste if float_compare( qty, 0.0, precision_rounding=op.product_uom.rounding) <= 0: continue qty -= op.subtract_procurements(op) qty_rounded = float_round( qty, precision_rounding=op.product_uom.rounding) if qty_rounded > 0: procure_recommended_qty = qty_rounded if op.procure_uom_id: product_qty = op.procure_uom_id._compute_qty( op.product_id.uom_id.id, procure_recommended_qty, op.procure_uom_id.id) else: product_qty = procure_recommended_qty op.procure_recommended_qty = product_qty
def _check_sum(self, cr, uid, landed_cost, context=None): """ Will check if each cost line its valuation lines sum to the correct amount and if the overall total amount is correct also """ if context is None: context = {} currency_obj = self.pool.get('res.currency') costcor = {} tot = 0 for valuation_line in landed_cost.valuation_adjustment_lines: if costcor.get(valuation_line.cost_line_id): costcor[valuation_line. cost_line_id] += valuation_line.additional_landed_cost else: costcor[valuation_line. cost_line_id] = valuation_line.additional_landed_cost tot += valuation_line.additional_landed_cost prec = self.pool['decimal.precision'].precision_get(cr, uid, 'Account') local_context = context.copy() # float_compare returns 0 for equal amounts res = not bool( float_compare(tot, landed_cost.amount_total, precision_digits=prec)) for costl in costcor.keys(): local_context['date'] = landed_cost.date price_unit = currency_obj.compute( cr, uid, costl.currency_id.id, landed_cost.company_id.currency_id.id, costl.price_unit, context=local_context) if float_compare(costcor[costl], price_unit, precision_digits=prec): res = False return res
def action_consume(self, cr, uid, ids, product_qty, location_id=False, restrict_lot_id=False, restrict_partner_id=False, consumed_for=False, context=None): """ Consumed product with specific quantity from specific source location. @param product_qty: Consumed/produced product quantity (= in quantity of UoM of product) @param location_id: Source location @param restrict_lot_id: optionnal parameter that allows to restrict the choice of quants on this specific lot @param restrict_partner_id: optionnal parameter that allows to restrict the choice of quants to this specific partner @param consumed_for: optionnal parameter given to this function to make the link between raw material consumed and produced product, for a better traceability @return: New lines created if not everything was consumed for this line """ if context is None: context = {} res = [] production_obj = self.pool.get('mrp.production') if product_qty <= 0: raise UserError(_('Please provide proper quantity.')) #because of the action_confirm that can create extra moves in case of phantom bom, we need to make 2 loops ids2 = [] for move in self.browse(cr, uid, ids, context=context): if move.state == 'draft': ids2.extend(self.action_confirm(cr, uid, [move.id], context=context)) else: ids2.append(move.id) prod_orders = set() for move in self.browse(cr, uid, ids2, context=context): prod_orders.add(move.raw_material_production_id.id or move.production_id.id) move_qty = move.product_qty if move_qty <= 0: raise UserError(_('Cannot consume a move with negative or zero quantity.')) quantity_rest = move_qty - product_qty # Compare with numbers of move uom as we want to avoid a split with 0 qty quantity_rest_uom = move.product_uom_qty - self.pool.get("product.uom")._compute_qty_obj(cr, uid, move.product_id.uom_id, product_qty, move.product_uom) if float_compare(quantity_rest_uom, 0, precision_rounding=move.product_uom.rounding) != 0: new_mov = self.split(cr, uid, move, quantity_rest, context=context) res.append(new_mov) vals = {'restrict_lot_id': restrict_lot_id, 'restrict_partner_id': restrict_partner_id, 'consumed_for': consumed_for} if location_id: vals.update({'location_id': location_id}) self.write(cr, uid, [move.id], vals, context=context) # Original moves will be the quantities consumed, so they need to be done self.action_done(cr, uid, ids2, context=context) if res: self.action_assign(cr, uid, res, context=context) if prod_orders: production_obj.signal_workflow(cr, uid, list(prod_orders), 'button_produce') return res
def write(self, values): lines = False if 'product_uom_qty' in values: precision = self.env['decimal.precision'].precision_get( 'Product Unit of Measure') lines = self.filtered( lambda r: r.state == 'sale' and float_compare( r.product_uom_qty, values['product_uom_qty'], precision_digits=precision) == -1) result = super(SaleOrderLine, self).write(values) if lines: lines._action_procurement_create() return result
def wkf_validate_vs_quotation(self): """ Case Central Purchase, quotation amount should not exceed """ decimal_prec = self.env['decimal.precision'] precision = decimal_prec.precision_get('Account') for requisition in self: if not requisition.is_central_purchase: continue total = sum([o.amount_total for o in requisition.purchase_ids]) if float_compare(total, requisition.amount_total, precision) == 1: raise ValidationError( _('Total quotation amount exceed Call for Bid amount') ) return True
def create(self, vals): uom_id = vals.get('product_uom') product_id = vals.get('product_id') move_qty = float(vals.get('product_uom_qty', 0)) uom = uom_id and self.env['product.uom'].search([('id', '=', uom_id)]) or \ self.env['product.product'].search([('id', '=', product_id)]).uom_id or False negative_move = uom and float_compare( move_qty, 0.0, precision_rounding=uom.rounding) <= 0 or move_qty <= 0 if negative_move: raise exceptions.except_orm( _(u"Error!"), _(u"You are not allowed to create a negative or null move.")) return super(StockQuant, self).create(vals)
def _amount_currency_change(self): if (self.currency_id and self.amount_currency and not self.credit and not self.debit): date = self.date or None amount_company_currency = self.currency_id.with_context( date=date).compute(self.amount_currency, self.env.user.company_id.currency_id) precision = self.env['decimal.precision'].precision_get('Account') if float_compare(amount_company_currency, 0, precision_digits=precision) == -1: self.debit = amount_company_currency * -1 else: self.credit = amount_company_currency
def proforma_voucher(self): for rec in self: if rec.type != 'payment': continue if rec.force_pay: continue # A payment, and not force pay, check for balance and throw error if float_compare(rec.amount, rec.journal_balance, 2) > 0: raise ValidationError( _('Pay amount (%s) exceed balance (%s)!\n') % ('{:,.2f}'.format(rec.amount), '{:,.2f}'.format(rec.journal_balance))) res = super(AccountVoucher, self).proforma_voucher() return res
def split_not_totally_consumed_moves(self, dict_reservation_target): for move in dict_reservation_target.keys(): prec = move.product_id.uom_id.rounding required_qty = sum([ quant_tuple[1] for quant_tuple in dict_reservation_target[move] ['reservations'] ]) if float_compare(move.product_qty, required_qty, precision_rounding=prec) > 0: move.split( move, float_round(move.product_qty - required_qty, precision_rounding=prec))
def check_product_uom_qty(self, product_id): if self.product_uom_qty: if self.product_id.type == 'product': if self.product_id.virtual_available_days < 5 or self.product_id.consumption_per_day < self.product_uom_qty: compare_qty = float_compare( self.product_id.virtual_available_netto, self.product_uom_qty, precision_rounding=self.product_uom.rounding) if compare_qty == -1: raise Warning(_('Missing stock:\n%s: You plan to sell %.2f %s but you only have %.2f %s available!\nThe real stock is %.2f %s. (without reservations)') % \ (self.product_id.name_get()[0][1], self.product_uom_qty, self.product_uom.name, max(0,self.product_id.virtual_available_netto), self.product_uom.name, max(0,self.product_id.qty_available), self.product_uom.name))
def test_coda_file_import(self): self.bank_statement_import.import_file() bank_st_record = self.bank_statement_model.search([ ('name', '=', 'TBNK/2012/135') ])[0] self.assertEqual( float_compare(bank_st_record.balance_start, 11812.70, precision_digits=2), 0) self.assertEqual( float_compare(bank_st_record.balance_end_real, 13646.05, precision_digits=2), 0) # check line name self.assertEqual( 'MEDEDELING', bank_st_record.line_ids[0].name, 'Name should be the communication if no structured communication ' 'found') self.assertEqual( '+++240/2838/42818+++', bank_st_record.line_ids[1].name, 'Name should be the structured communication id provided') for line in bank_st_record.line_ids[2:5]: self.assertEqual( 'KBC-INVESTERINGSKREDIET 737-6543210-21', line.name, 'Name should be the communication of the related ' 'globalisation line for details line') # check the note self.assertEqual( 'Counter Party: PARTNER 2\n' 'Counter Party Account: BE61310126985517\n' 'Communication: +++240/2838/42818+++ ' '001PARTNER 2MOLENSTRAAT 60 9340 LEDE', bank_st_record.line_ids[1].note, 'The note should contain informations on the counter part ' 'but also the communication for the information records that ' 'refer the movement record')
def parse_csv_order(self, order_file, partner): assert partner, 'missing partner' fileobj = TemporaryFile('w+') fileobj.write(order_file) fileobj.seek(0) reader = unicodecsv.reader(fileobj, delimiter=';', quoting=unicodecsv.QUOTE_MINIMAL, encoding='utf8') i = 0 parsed_order = { 'partner': { 'recordset': partner }, 'lines': [], } precision = self.env['decimal.precision'].precision_get('Product UoS') for line in reader: logger.debug('csv line %d: %s', i, line) i += 1 if len(line) < 2: raise UserError( _("Error on line %d of the CSV file: this line should have " "a product code and a quantity, separated by a " "semi-colon.") % i) if not line[0]: raise UserError( _("Error on line %d of the CSV file: the line should start " "with a product code") % i) try: qty = float(line[1]) except: raise UserError( _("Error on line %d of the CSV file: the second column " "should contain a quantity. The quantity should use dot " "as decimal separator and shouldn't have any thousand " "separator") % i) if float_compare(qty, 0, precision_digits=precision) != 1: raise UserError( _("Error on line %d of the CSV file: the quantity should " "be strictly positive") % i) parsed_order['lines'].append({ 'qty': qty, 'product': { 'code': line[0] }, }) fileobj.close() return parsed_order
def _prepare_suggest_line(self, product_id, qty_dict): porderline_id = False porderlines = self.env['purchase.order.line'].search( [('state', 'not in', ('draft', 'cancel')), ('product_id', '=', product_id)], order='id desc', limit=1) # I cannot filter on 'date_order' because it is not a stored field porderline_id = porderlines and porderlines[0].id or False future_qty = qty_dict['virtual_available'] + qty_dict['draft_po_qty'] if float_compare( qty_dict['max_qty'], qty_dict['min_qty'], precision_rounding=qty_dict['product'].uom_id.rounding) == 1: # order to go up to qty_max qty_to_order = qty_dict['max_qty'] - future_qty else: # order to go up to qty_min qty_to_order = qty_dict['min_qty'] - future_qty sline = { 'company_id': qty_dict['orderpoint'] and qty_dict['orderpoint'].company_id.id, 'product_id': product_id, 'seller_id': qty_dict['product'].seller_id.id or False, 'qty_available': qty_dict['qty_available'], 'incoming_qty': qty_dict['incoming_qty'], 'outgoing_qty': qty_dict['outgoing_qty'], 'draft_po_qty': qty_dict['draft_po_qty'], 'orderpoint_id': qty_dict['orderpoint'] and qty_dict['orderpoint'].id, 'location_id': self.location_id.id, 'min_qty': qty_dict['min_qty'], 'max_qty': qty_dict['max_qty'], 'last_po_line_id': porderline_id, 'qty_to_order': qty_to_order, } return sline
def move_products(self): self.ensure_one() lines = self.quant_line_ids + self.package_line_ids lines.check_quantities() lines.check_data_active() is_manual_op = self.is_manual_op or lines.force_is_manual_op() packages = self.env['stock.quant.package'] for package_line in self.package_line_ids: packages |= package_line.package_id quants_to_move = packages.get_content() quants_to_move = self.env['stock.quant'].browse(quants_to_move) move_items = {} # Let's add package quants for quant in quants_to_move: move_items = quant.partial_move(move_items, quant.product_id, quant.qty) # Let's move quant lines for quant_line in self.quant_line_ids: domain = [ ('product_id', '=', quant_line.product_id.id), ('location_id', '=', quant_line.location_id.id), ('package_id', '=', quant_line.package_id and quant_line.package_id.id or False), ('lot_id', '=', quant_line.lot_id and quant_line.lot_id.id or False), ('product_id.uom_id', '=', quant_line.uom_id and quant_line.uom_id.id or False), ('id', 'not in', quants_to_move.ids) ] quants = self.env['stock.quant'].search(domain, order='in_date, qty') if quants: move_items = quants.partial_move(move_items, quant_line.product_id, quant_line.qty) quants_to_move |= quants if any([ float_compare( quant.qty, 0, precision_rounding=quant.product_id.uom_id.rounding) < 0 for quant in quants_to_move ]): raise exceptions.except_orm( _("error"), _("Impossible to move a negative quant")) new_picking = quants_to_move.with_context(mail_notrack=True). \ move_to(self.global_dest_loc, self.picking_type_id, move_items=move_items, is_manual_op=is_manual_op, filling_method=self.filling_method) return new_picking.open_picking_form(is_manual_op)
def action_consume_cancel(self, cr, uid, ids, context=None): """ Cancels the moves and if all moves are cancelled it cancels the picking. @return: True """ if not ids: return True new_move = self.browse(cr, uid, ids, context)[0] sm_ids = self.search(cr, uid, [('move_dest_id', '=', new_move.id)], context=context) sp_picking = False if sm_ids: for move in self.browse(cr, uid, sm_ids): sp_picking = move.picking_id if move.state == 'done': self.write(cr, uid, [move.id], {'state': 'cancel'}) else: self.action_cancel(cr, uid, [move.id], context=context) if sp_picking: mrp_obj = self.pool.get('mrp.production') mo_ids = mrp_obj.search(cr, uid, [('picking_id', '=', sp_picking.id)], context=context) if mo_ids: prod_line_obj = self.pool.get('mrp.production.product.line') ml_ids = prod_line_obj.search( cr, uid, [('production_id', '=', mo_ids[0]), ('product_id', '=', new_move.product_id.id)], context=context) if ml_ids: prod_line = prod_line_obj.browse(cr, uid, ml_ids)[0] compare = float_compare(prod_line.product_qty, new_move.product_qty, precision_rounding=4) if compare == 0: prod_line_obj.unlink(cr, uid, [prod_line.id], context=context) elif compare > 0: prod_line_obj.write( cr, uid, [prod_line.id], { 'product_qty': prod_line.product_qty - new_move.product_qty }) self.action_cancel(cr, uid, [new_move.id], context=context)
def post_import_validation(self): for rec in self: # 1) total_budget and planned_amount should equal total_budget = sum(rec.plan_line_ids.mapped('total_budget')) planned_amount = sum(rec.plan_line_ids.mapped('planned_amount')) if float_compare(total_budget, planned_amount, 2) != 0: raise ValidationError( _("Excel's total budget not equal planned amount")) # 2) If external line has income_section_id, remove it. if rec.plan_line_ids.\ filtered(lambda l: l.charge_type == 'external').\ mapped('income_section_id'): raise ValidationError( _('For line with external charge type, ' 'income section is not allowed'))