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 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 _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 bom_delivered[bom.id] = False bom_exploded = self.env['mrp.bom']._bom_explode( bom, self.product_id, self.product_uom_qty)[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_obj( move.product_uom, move.product_uom_qty, self.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 _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: 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 = {} for bom in self.product_id.product_tmpl_id.bom_ids: if bom.type != 'phantom': continue bom_delivered[bom.id] = False bom_exploded = self.env['mrp.bom']._bom_explode(bom, self.product_id, self.product_uom_qty)[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_obj(move.product_uom, move.product_uom_qty, self.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 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 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 _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 """ 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') # 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(): if float_compare(costcor[costl], costl.price_unit, precision_digits=prec): res = False return res
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 get_reconciliation_proposition(self, excluded_ids=None): """ Returns move lines that constitute the best guess to reconcile a statement line Note: it only looks for move lines in the same currency as the statement line. """ # Look for structured communication match if self.name: overlook_partner = not self.partner_id # If the transaction has no partner, look for match in payable and receivable account anyway domain = [('ref', '=', self.name)] match_recs = self.get_move_lines_for_reconciliation(excluded_ids=excluded_ids, limit=2, additional_domain=domain, overlook_partner=overlook_partner) if match_recs and len(match_recs) == 1: return match_recs elif len(match_recs) == 0: move = self.env['account.move'].search([('name', '=', self.name)], limit=1) if move: domain = [('move_id', '=', move.id)] match_recs = self.get_move_lines_for_reconciliation(excluded_ids=excluded_ids, limit=2, additional_domain=domain, overlook_partner=overlook_partner) if match_recs and len(match_recs) == 1: return match_recs # How to compare statement line amount and move lines amount amount_domain_maker = self._get_domain_maker_move_line_amount() amount = self.amount_currency or self.amount # Look for a single move line with the same amount match_recs = self.get_move_lines_for_reconciliation(excluded_ids=excluded_ids, limit=1, additional_domain=amount_domain_maker('=', amount)) if match_recs: return match_recs if not self.partner_id: return self.env['account.move.line'] # Select move lines until their total amount is greater than the statement line amount domain = [('reconciled', '=', False)] domain += [('account_id.user_type_id.type', '=', amount > 0 and 'receivable' or 'payable')] # Make sure we can't mix receivable and payable domain += amount_domain_maker('<', amount) # Will also enforce > 0 mv_lines = self.get_move_lines_for_reconciliation(excluded_ids=excluded_ids, limit=5, additional_domain=domain) st_line_currency = self.currency_id or self.journal_id.currency_id or self.journal_id.company_id.currency_id ret = self.env['account.move.line'] total = 0 for line in mv_lines: total += line.currency_id and line.amount_residual_currency or line.amount_residual if float_compare(total, abs(amount), precision_digits=st_line_currency.rounding) != -1: break ret = (ret | line) return ret
def compare_amounts(self, cr, uid, currency, amount1, amount2): """Compare ``amount1`` and ``amount2`` after rounding them according to the given currency'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. :param Record currency: currency for which we are rounding :param float amount1: first amount to compare :param float amount2: second amount to compare :return: (resp.) -1, 0 or 1, if ``amount1`` is (resp.) lower than, equal to, or greater than ``amount2``, according to ``currency``'s rounding. """ return float_compare(amount1, amount2, precision_rounding=currency.rounding)
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 _procure_orderpoint_confirm(self, cr, uid, use_new_cursor=False, company_id=False, context=None): ''' Create procurement based on Orderpoint :param bool use_new_cursor: if set, use a dedicated cursor and auto-commit after processing each procurement. This is appropriate for batch jobs only. ''' if context is None: context = {} if use_new_cursor: cr = ecore.registry(cr.dbname).cursor() orderpoint_obj = self.pool.get('stock.warehouse.orderpoint') procurement_obj = self.pool.get('procurement.order') product_obj = self.pool.get('product.product') dom = company_id and [('company_id', '=', company_id)] or [] orderpoint_ids = orderpoint_obj.search(cr, uid, dom, order="location_id") prev_ids = [] tot_procs = [] while orderpoint_ids: ids = orderpoint_ids[:1000] del orderpoint_ids[:1000] product_dict = {} ops_dict = {} ops = orderpoint_obj.browse(cr, uid, ids, context=context) #Calculate groups that can be executed together for op in ops: key = (op.location_id.id,) if not product_dict.get(key): product_dict[key] = [op.product_id] ops_dict[key] = [op] else: product_dict[key] += [op.product_id] ops_dict[key] += [op] for key in product_dict.keys(): ctx = context.copy() ctx.update({'location': ops_dict[key][0].location_id.id}) prod_qty = product_obj._product_available(cr, uid, [x.id for x in product_dict[key]], context=ctx) subtract_qty = orderpoint_obj.subtract_procurements_from_orderpoints(cr, uid, [x.id for x in ops_dict[key]], context=context) for op in ops_dict[key]: try: prods = prod_qty[op.product_id.id]['virtual_available'] 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 -= subtract_qty[op.id] qty_rounded = float_round(qty, precision_rounding=op.product_uom.rounding) if qty_rounded > 0: proc_id = procurement_obj.create(cr, uid, self._prepare_orderpoint_procurement(cr, uid, op, qty_rounded, context=context), context=context) tot_procs.append(proc_id) if use_new_cursor: cr.commit() except OperationalError: if use_new_cursor: orderpoint_ids.append(op.id) cr.rollback() continue else: raise try: tot_procs.reverse() self.run(cr, uid, tot_procs, context=context) tot_procs = [] if use_new_cursor: cr.commit() except OperationalError: if use_new_cursor: cr.rollback() continue else: raise if use_new_cursor: cr.commit() if prev_ids == ids: break else: prev_ids = ids if use_new_cursor: cr.commit() cr.close() return {}
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) if move.production_id: self.write(cr, uid, [new_mov], {'production_id': move.production_id.id}, 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 _procure_orderpoint_confirm(self, cr, uid, use_new_cursor=False, company_id=False, context=None): ''' Create procurement based on Orderpoint :param bool use_new_cursor: if set, use a dedicated cursor and auto-commit after processing each procurement. This is appropriate for batch jobs only. ''' if context is None: context = {} if use_new_cursor: cr = ecore.registry(cr.dbname).cursor() orderpoint_obj = self.pool.get('stock.warehouse.orderpoint') procurement_obj = self.pool.get('procurement.order') product_obj = self.pool.get('product.product') dom = company_id and [('company_id', '=', company_id)] or [] orderpoint_ids = orderpoint_obj.search(cr, uid, dom, order="location_id") prev_ids = [] tot_procs = [] while orderpoint_ids: ids = orderpoint_ids[:1000] del orderpoint_ids[:1000] product_dict = {} ops_dict = {} ops = orderpoint_obj.browse(cr, uid, ids, context=context) #Calculate groups that can be executed together for op in ops: key = (op.location_id.id, ) if not product_dict.get(key): product_dict[key] = [op.product_id] ops_dict[key] = [op] else: product_dict[key] += [op.product_id] ops_dict[key] += [op] for key in product_dict.keys(): ctx = context.copy() ctx.update({'location': ops_dict[key][0].location_id.id}) prod_qty = product_obj._product_available( cr, uid, [x.id for x in product_dict[key]], context=ctx) subtract_qty = orderpoint_obj.subtract_procurements_from_orderpoints( cr, uid, [x.id for x in ops_dict[key]], context=context) for op in ops_dict[key]: try: prods = prod_qty[op.product_id.id]['virtual_available'] 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 -= subtract_qty[op.id] qty_rounded = float_round( qty, precision_rounding=op.product_uom.rounding) if qty_rounded > 0: proc_id = procurement_obj.create( cr, uid, self._prepare_orderpoint_procurement( cr, uid, op, qty_rounded, context=context), context=context) tot_procs.append(proc_id) if use_new_cursor: cr.commit() except OperationalError: if use_new_cursor: orderpoint_ids.append(op.id) cr.rollback() continue else: raise try: tot_procs.reverse() self.run(cr, uid, tot_procs, context=context) tot_procs = [] if use_new_cursor: cr.commit() except OperationalError: if use_new_cursor: cr.rollback() continue else: raise if use_new_cursor: cr.commit() if prev_ids == ids: break else: prev_ids = ids if use_new_cursor: cr.commit() cr.close() return {}
def test_00_delivery_cost(self): # In order to test Carrier Cost # Create sale order with Normal Delivery Charges self.sale_normal_delivery_charges = self.SaleOrder.create({ 'partner_id': self.partner_18.id, 'partner_invoice_id': self.partner_18.id, 'partner_shipping_id': self.partner_18.id, 'pricelist_id': self.pricelist.id, 'order_line': [(0, 0, { 'name': 'PC Assamble + 2GB RAM', 'product_id': self.product_4.id, 'product_uom_qty': 1, 'product_uom': self.product_uom_unit.id, 'price_unit': 750.00, })], 'carrier_id': self.normal_delivery.id }) # I add delivery cost in Sale order self.a_sale = self.AccountAccount.create({ 'code': 'X2020', 'name': 'Product Sales - (test)', 'user_type_id': self.account_data.id, 'tag_ids': [(6, 0, { self.account_tag_operating.id })] }) self.product_consultant = self.Product.create({ 'sale_ok': True, 'list_price': 75.0, 'standard_price': 30.0, 'uom_id': self.product_uom_hour.id, 'uom_po_id': self.product_uom_hour.id, 'name': 'Service', 'categ_id': self.product_category.id, 'type': 'service' }) # I add delivery cost in Sale order self.sale_normal_delivery_charges.delivery_set() # I check sale order after added delivery cost line = self.SaleOrderLine.search([('order_id', '=', self.sale_normal_delivery_charges.id), ('product_id', '=', self.sale_normal_delivery_charges.carrier_id.product_id.id)]) self.assertEqual(len(line), 1, "Delivery cost is not Added") self.assertEqual(float_compare(line.price_subtotal, 10, precision_digits=2), 0, "Delivey cost is not correspond.") # I confirm the sale order self.sale_normal_delivery_charges.action_confirm() # Create one more sale order with Free Delivery Charges self.delivery_sale_order_cost = self.SaleOrder.create({ 'partner_id': self.partner_4.id, 'partner_invoice_id': self.partner_address_13.id, 'partner_shipping_id': self.partner_address_13.id, 'pricelist_id': self.pricelist.id, 'order_line': [(0, 0, { 'name': 'Service on demand', 'product_id': self.product_consultant.id, 'product_uom_qty': 24, 'product_uom': self.product_uom_hour.id, 'price_unit': 75.00, }), (0, 0, { 'name': 'On Site Assistance', 'product_id': self.product_2.id, 'product_uom_qty': 30, 'product_uom': self.product_uom_hour.id, 'price_unit': 38.25, })], 'carrier_id': self.free_delivery.id }) # I add free delivery cost in Sale order self.delivery_sale_order_cost.delivery_set() # I check sale order after adding delivery cost line = self.SaleOrderLine.search([('order_id', '=', self.delivery_sale_order_cost.id), ('product_id', '=', self.delivery_sale_order_cost.carrier_id.product_id.id)]) self.assertEqual(len(line), 1, "Delivery cost is not Added") self.assertEqual(float_compare(line.price_subtotal, 0, precision_digits=2), 0, "Delivey cost is not correspond.") # I set default delivery policy self.default_delivery_policy = self.SaleConfigSetting.create({}) self.default_delivery_policy.execute()
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') 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 date = timenow 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 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, 'partner_id': hr_payslip_line_pool._get_partner_id(cr, uid, line, credit_account=False, context=context), '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), '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'), 'date': timenow, '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}, context=context) move_pool.post(cr, uid, [move_id], context=context) return super(hr_payslip, self).process_sheet(cr, uid, [slip.id], context=context)
def _procure_orderpoint_confirm(self, cr, uid, use_new_cursor=False, company_id=False, context=None): ''' Create procurement based on Orderpoint :param bool use_new_cursor: if set, use a dedicated cursor and auto-commit after processing each procurement. This is appropriate for batch jobs only. ''' if context is None: context = {} if use_new_cursor: cr = ecore.registry(cr.dbname).cursor() orderpoint_obj = self.pool.get('stock.warehouse.orderpoint') procurement_obj = self.pool.get('procurement.order') product_obj = self.pool.get('product.product') dom = company_id and [('company_id', '=', company_id)] or [] orderpoint_ids = orderpoint_obj.search( cr, uid, dom, order="location_id, purchase_calendar_id, calendar_id") prev_ids = [] tot_procs = [] while orderpoint_ids: ids = orderpoint_ids[:1000] del orderpoint_ids[:1000] dates_dict = {} product_dict = {} ops_dict = {} ops = orderpoint_obj.browse(cr, uid, ids, context=context) #Calculate groups that can be executed together for op in ops: key = (op.location_id.id, op.purchase_calendar_id.id, op.calendar_id.id) res_groups = [] if not dates_dict.get(key): date_groups = self._get_group(cr, uid, op, context=context) for date, group in date_groups: if op.calendar_id and op.calendar_id.attendance_ids: date1, date2 = self._get_next_dates( cr, uid, op, date, group, context=context) res_groups += [ (group, date1, date2, date) ] #date1/date2 as deliveries and date as purchase confirmation date else: res_groups += [(group, date, False, date)] dates_dict[key] = res_groups product_dict[key] = [op.product_id] ops_dict[key] = [op] else: product_dict[key] += [op.product_id] ops_dict[key] += [op] for key in product_dict.keys(): for res_group in dates_dict[key]: ctx = context.copy() ctx.update({'location': ops_dict[key][0].location_id.id}) if res_group[2]: ctx.update({ 'to_date': res_group[2].strftime( DEFAULT_SERVER_DATETIME_FORMAT) }) prod_qty = product_obj._product_available( cr, uid, [x.id for x in product_dict[key]], context=ctx) group = res_group[0] date = res_group[1] subtract_qty = orderpoint_obj.subtract_procurements_from_orderpoints( cr, uid, [x.id for x in ops_dict[key]], context=context) first_op = True ndelivery = date and self._convert_to_UTC( cr, uid, date, context=context) or False npurchase = res_group[3] and self._convert_to_UTC( cr, uid, res_group[3], context=context) or False for op in ops_dict[key]: try: prods = prod_qty[ op.product_id.id]['virtual_available'] 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 -= subtract_qty[op.id] qty_rounded = float_round( qty, precision_rounding=op.product_uom.rounding) if qty_rounded > 0: proc_id = procurement_obj.create( cr, uid, self._prepare_orderpoint_procurement( cr, uid, op, qty_rounded, date=ndelivery, purchase_date=npurchase, group=group, context=context), context=context) tot_procs.append(proc_id) orderpoint_obj.write( cr, uid, [op.id], { 'last_execution_date': datetime.utcnow().strftime( DEFAULT_SERVER_DATETIME_FORMAT) }, context=context) if use_new_cursor: cr.commit() except OperationalError: if use_new_cursor: orderpoint_ids.append(op.id) cr.rollback() continue else: raise try: tot_procs.reverse() self.run(cr, uid, tot_procs, context=context) tot_procs = [] if use_new_cursor: cr.commit() except OperationalError: if use_new_cursor: cr.rollback() continue else: raise if use_new_cursor: cr.commit() if prev_ids == ids: break else: prev_ids = ids if use_new_cursor: cr.commit() cr.close() return {}
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') 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 date = timenow 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 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, 'partner_id': hr_payslip_line_pool._get_partner_id( cr, uid, line, credit_account=False, context=context), '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), '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'), 'date': timenow, '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 }, context=context) move_pool.post(cr, uid, [move_id], context=context) return super(hr_payslip, self).process_sheet(cr, uid, [slip.id], context=context)