Пример #1
0
    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 = self.product_id.with_context(
                warehouse=self.order_id.warehouse_id.id)
            product_qty = self.product_uom._compute_quantity(
                self.product_uom_qty, self.product_id.uom_id)
            if float_compare(product.virtual_available,
                             product_qty,
                             precision_digits=precision) == -1:
                is_available = self._check_routing()
                if not is_available:
                    message =  _('You plan to sell %s %s but you only have %s %s available in %s warehouse.') % \
                            (self.product_uom_qty, self.product_uom.name, product.virtual_available, product.uom_id.name, self.order_id.warehouse_id.name)
                    # We check if some products are available in other warehouses.
                    if float_compare(product.virtual_available,
                                     self.product_id.virtual_available,
                                     precision_digits=precision) == -1:
                        message += _('\nThere are %s %s available accross all warehouses.') % \
                                (self.product_id.virtual_available, product.uom_id.name)

                    warning_mess = {
                        'title': _('Not enough inventory!'),
                        'message': message
                    }
                    return {'warning': warning_mess}
        return {}
Пример #2
0
 def test_Clubbed_Discount_coupon_code(self):
     if not self.sale_order_6.coupon_flag:
         self.check_all_coupon_code(self.sale_order_6, 'CD15Per',
                                    self.pricelist_2)
     self.assertTrue(self.sale_order_6.have_coupon_code != '',
                     'Coupon Code: Please enter coupon code!')
     self.assertEqual(
         float_compare(self.sale_order_6.order_line[0].discount,
                       29.90,
                       precision_digits=2), 0,
         'Discount Line: the discount of first'
         'sale order line should be 29.90!')
     self.assertEqual(
         self.sale_order_6.discount, 3969.23,
         'Sale Discount: the discount for the '
         'sale order should be 3969.23!')
     self.assertEqual(
         float_compare(self.sale_order_6.order_line[1].discount,
                       29.90,
                       precision_digits=2), 0,
         'Discount Line: the discount of first'
         'sale order line should be 29.90!')
     self.assertEqual(
         float_compare(self.sale_order_6.amount_total,
                       9305.78,
                       precision_digits=2), 0, "Total not correct")
Пример #3
0
 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.'))
Пример #4
0
    def test_rounding_invalid(self):
        """ verify that invalid parameters are forbidden """
        with self.assertRaises(AssertionError):
            float_is_zero(0.01, precision_digits=3, precision_rounding=0.01)

        with self.assertRaises(AssertionError):
            float_compare(0.01,
                          0.02,
                          precision_digits=3,
                          precision_rounding=0.01)

        with self.assertRaises(AssertionError):
            float_round(0.01, precision_digits=3, precision_rounding=0.01)
Пример #5
0
    def _select_seller(self, partner_id=False, quantity=0.0, date=None, uom_id=False):
        self.ensure_one()
        if date is None:
            date = fields.Date.context_today(self)
        precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')

        res = self.env['product.supplierinfo']
        for seller in self.seller_ids:
            # Set quantity in UoM of seller
            quantity_uom_seller = quantity
            if quantity_uom_seller and uom_id and uom_id != seller.product_uom:
                quantity_uom_seller = uom_id._compute_quantity(quantity_uom_seller, seller.product_uom)

            if seller.date_start and seller.date_start > date:
                continue
            if seller.date_end and seller.date_end < date:
                continue
            if partner_id and seller.name not in [partner_id, partner_id.parent_id]:
                continue
            if float_compare(quantity_uom_seller, seller.min_qty, precision_digits=precision) == -1:
                continue
            if seller.product_id and seller.product_id != self:
                continue

            res |= seller
            break
        return res
Пример #6
0
 def action_validate(self):
     self.ensure_one()
     precision = self.env['decimal.precision'].precision_get(
         'Product Unit of Measure')
     available_qty = self.env['stock.quant']._get_available_quantity(
         self.product_id, self.location_id, self.lot_id, strict=True)
     if float_compare(available_qty,
                      self.product_qty,
                      precision_digits=precision) >= 0:
         return self.action_unbuild()
     else:
         return {
             'name':
             _('Insufficient Quantity'),
             'view_type':
             'form',
             'view_mode':
             'form',
             'res_model':
             'stock.warn.insufficient.qty.unbuild',
             'view_id':
             self.env.ref(
                 'mrp.stock_warn_insufficient_qty_unbuild_form_view').id,
             'type':
             'ir.actions.act_window',
             'context': {
                 'default_product_id': self.product_id.id,
                 'default_location_id': self.location_id.id,
                 'default_unbuild_id': self.id
             },
             'target':
             'new'
         }
Пример #7
0
 def _compute_consumed_less_than_planned(self):
     for order in self:
         order.consumed_less_than_planned = any(
             order.move_raw_ids.filtered(lambda move: float_compare(
                 move.quantity_done,
                 move.product_uom_qty,
                 precision_rounding=move.product_uom.rounding) == -1))
Пример #8
0
    def _confirm_so(self):
        """ Check tx state, confirm the potential SO """
        self.ensure_one()
        if self.sale_order_id.state not in ['draft', 'sent', 'sale']:
            _logger.warning(
                '<%s> transaction STATE INCORRECT for order %s (ID %s, state %s)',
                self.acquirer_id.provider, self.sale_order_id.name,
                self.sale_order_id.id, self.sale_order_id.state)
            return 'pay_sale_invalid_doc_state'
        if not float_compare(self.amount, self.sale_order_id.amount_total,
                             2) == 0:
            _logger.warning(
                '<%s> transaction AMOUNT MISMATCH for order %s (ID %s): expected %r, got %r',
                self.acquirer_id.provider,
                self.sale_order_id.name,
                self.sale_order_id.id,
                self.sale_order_id.amount_total,
                self.amount,
            )
            self.sale_order_id.message_post(
                subject=_("Amount Mismatch (%s)") % self.acquirer_id.provider,
                body=
                _("The sale order was not confirmed despite response from the acquirer (%s): SO amount is %r but acquirer replied with %r."
                  ) % (
                      self.acquirer_id.provider,
                      self.sale_order_id.amount_total,
                      self.amount,
                  ))
            return 'pay_sale_tx_amount'

        if self.state == 'authorized' and self.acquirer_id.capture_manually:
            _logger.info(
                '<%s> transaction authorized, auto-confirming order %s (ID %s)',
                self.acquirer_id.provider, self.sale_order_id.name,
                self.sale_order_id.id)
            if self.sale_order_id.state in ('draft', 'sent'):
                self.sale_order_id.with_context(
                    send_email=True).action_confirm()
        elif self.state == 'done':
            _logger.info(
                '<%s> transaction completed, auto-confirming order %s (ID %s)',
                self.acquirer_id.provider, self.sale_order_id.name,
                self.sale_order_id.id)
            if self.sale_order_id.state in ('draft', 'sent'):
                self.sale_order_id.with_context(
                    send_email=True).action_confirm()
            self._generate_and_pay_invoice()
        elif self.state not in ['cancel', 'error'
                                ] and self.sale_order_id.state == 'draft':
            _logger.info(
                '<%s> transaction pending/to confirm manually, sending quote email for order %s (ID %s)',
                self.acquirer_id.provider, self.sale_order_id.name,
                self.sale_order_id.id)
            self.sale_order_id.force_quotation_send()
        else:
            _logger.warning('<%s> transaction MISMATCH for order %s (ID %s)',
                            self.acquirer_id.provider, self.sale_order_id.name,
                            self.sale_order_id.id)
            return 'pay_sale_tx_state'
        return True
Пример #9
0
    def create_move(self, post_move=True):
        created_moves = self.env['account.move']
        prec = self.env['decimal.precision'].precision_get('Account')
        for line in self:
            if line.move_id:
                raise UserError(_('This depreciation is already linked to a journal entry! Please post or delete it.'))
            category_id = line.asset_id.category_id
            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.with_context(date=depreciation_date).compute(line.amount, company_currency)
            asset_name = line.asset_id.name + ' (%s/%s)' % (line.sequence, len(line.asset_id.depreciation_line_ids))
            move_line_1 = {
                'name': asset_name,
                'account_id': category_id.account_depreciation_id.id,
                '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': category_id.journal_id.id,
                'partner_id': line.asset_id.partner_id.id,
                'analytic_account_id': category_id.account_analytic_id.id if category_id.type == 'sale' else False,
                'currency_id': company_currency != current_currency and current_currency.id or False,
                'amount_currency': company_currency != current_currency and - 1.0 * line.amount or 0.0,
            }
            move_line_2 = {
                'name': asset_name,
                'account_id': category_id.account_depreciation_expense_id.id,
                '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': category_id.journal_id.id,
                'partner_id': line.asset_id.partner_id.id,
                'analytic_account_id': category_id.account_analytic_id.id if category_id.type == 'purchase' else False,
                'currency_id': company_currency != current_currency and current_currency.id or False,
                'amount_currency': company_currency != current_currency and line.amount or 0.0,
            }
            move_vals = {
                'ref': line.asset_id.code,
                'date': depreciation_date or False,
                'journal_id': category_id.journal_id.id,
                'line_ids': [(0, 0, move_line_1), (0, 0, move_line_2)],
            }
            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 m: any(m.asset_depreciation_ids.mapped('asset_id.category_id.open_asset'))).post()
        return [x.id for x in created_moves]
Пример #10
0
 def _check_main_currency_rounding(self):
     if any(precision.name == 'Account' and tools.float_compare(
             self.env.user.company_id.currency_id.rounding,
             10**-precision.digits,
             precision_digits=6) == -1 for precision in self):
         raise ValidationError(
             _("You cannot define the decimal precision of 'Account' as greater than the rounding factor of the company's main currency"
               ))
     return True
Пример #11
0
    def test_Fixed_coupon_code(self):
        if not self.sale_order_3.coupon_flag:
            self.check_all_coupon_code(self.sale_order_3, 'Get20off',
                                       self.pricelist_2)
        self.assertTrue(self.sale_order_3.have_coupon_code != '',
                        'Coupon Code: Please enter the coupon code!')
        self.assertEqual(
            float_compare(self.sale_order_3.order_line[0].discount,
                          17.16,
                          precision_digits=2), 0,
            'Discount Line: the discount of first'
            'sale order line should be 17.16!')
        self.assertEqual(
            float_compare(self.sale_order_3.order_line[1].discount,
                          15.58,
                          precision_digits=2), 0,
            'Discount Line: the discount of second'
            'sale order line should be 15.58!')
        self.assertEqual(
            self.sale_order_3.discount, 2138.16,
            'Sale Discount: the discount for the '
            'sale order should be 2138.16!')

        # Remove Coupon Code
        self.sale_order_3.apply_coupon_code()
        coupon_code_id = self.CouponCode.search([('coupon_code', '=',
                                                  'Get20off')])
        coupon_code_id.write({'discount_amount': 30})

        self.SaleOrderLine.create({
            'name': self.product_3.name,
            'product_id': self.product_3.id,
            'product_uom_qty': 3,
            'product_uom': self.product_3.uom_id.id,
            'price_unit': self.product_3.list_price,
            'order_id': self.sale_order_3.id,
        })
        self.sale_order_3._check_cart_rules()
        self.check_all_coupon_code(self.sale_order_3, 'Get20off',
                                   self.pricelist_2)
Пример #12
0
 def test_Buy_X_Product_Get_Percent_Free_coupon_code(self):
     if not self.sale_order_7.coupon_flag:
         self.check_all_coupon_code(self.sale_order_7, 'BXGPercentFree',
                                    self.pricelist_2)
     self.assertTrue(self.sale_order_7.have_coupon_code != '',
                     'Coupon Code: Please enter the coupon code!')
     self.assertEqual(
         float_compare(self.sale_order_7.order_line[0].discount,
                       22.65,
                       precision_digits=2), 0,
         'Discount Line: the discount of first'
         'sale order line should be 22.65!')
     self.assertEqual(
         float_compare(self.sale_order_7.order_line[1].discount,
                       14.90,
                       precision_digits=2), 0,
         'Discount Line: the discount of first'
         'sale order line should be 14.90!')
     self.assertEqual(
         self.sale_order_7.discount, 1881.36,
         'Sale Discount: the discount for the'
         ' sale order should be 1881.36!')
Пример #13
0
 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)
     res = super(SaleOrderLine, self).write(values)
     if lines:
         lines._action_launch_procurement_rule()
     return res
Пример #14
0
 def _generate_lot_ids(self):
     """ Generate stock move lines """
     self.ensure_one()
     MoveLine = self.env['stock.move.line']
     tracked_moves = self.move_raw_ids.filtered(
         lambda move: move.state not in
         ('done', 'cancel') and move.product_id.tracking != 'none' and move.
         product_id != self.production_id.product_id and move.bom_line_id)
     for move in tracked_moves:
         qty = move.unit_factor * self.qty_producing
         if move.product_id.tracking == 'serial':
             while float_compare(
                     qty, 0.0,
                     precision_rounding=move.product_uom.rounding) > 0:
                 MoveLine.create({
                     'move_id':
                     move.id,
                     'product_uom_qty':
                     0,
                     'product_uom_id':
                     move.product_uom.id,
                     'qty_done':
                     min(1, qty),
                     'production_id':
                     self.production_id.id,
                     'workorder_id':
                     self.id,
                     'product_id':
                     move.product_id.id,
                     'done_wo':
                     False,
                     'location_id':
                     move.location_id.id,
                     'location_dest_id':
                     move.location_dest_id.id,
                 })
                 qty -= 1
         else:
             MoveLine.create({
                 'move_id': move.id,
                 'product_uom_qty': 0,
                 'product_uom_id': move.product_uom.id,
                 'qty_done': qty,
                 'product_id': move.product_id.id,
                 'production_id': self.production_id.id,
                 'workorder_id': self.id,
                 'done_wo': False,
                 'location_id': move.location_id.id,
                 'location_dest_id': move.location_dest_id.id,
             })
Пример #15
0
    def _confirm_invoice(self):
        """ Check tx state, confirm and pay potential invoice """
        self.ensure_one()
        # check tx state, confirm the potential SO
        if self.account_invoice_id.state != 'open':
            _logger.warning(
                '<%s> transaction STATE INCORRECT for invoice %s (ID %s, state %s)',
                self.acquirer_id.provider, self.account_invoice_id.number,
                self.account_invoice_id.id, self.account_invoice_id.state)
            return 'pay_invoice_invalid_doc_state'
        if not float_compare(self.amount, self.account_invoice_id.amount_total,
                             2) == 0:
            _logger.warning(
                '<%s> transaction AMOUNT MISMATCH for invoice %s (ID %s): expected %r, got %r',
                self.acquirer_id.provider,
                self.account_invoice_id.number,
                self.account_invoice_id.id,
                self.account_invoice_id.amount_total,
                self.amount,
            )
            self.account_invoice_id.message_post(
                subject=_("Amount Mismatch (%s)") % self.acquirer_id.provider,
                body=
                _("The invoice was not confirmed despite response from the acquirer (%s): invoice amount is %r but acquirer replied with %r."
                  ) % (
                      self.acquirer_id.provider,
                      self.account_invoice_id.amount_total,
                      self.amount,
                  ))
            return 'pay_invoice_tx_amount'

        if self.state == 'authorized' and self.acquirer_id.capture_manually:
            _logger.info(
                '<%s> transaction authorized, nothing to do with invoice %s (ID %s)',
                self.acquirer_id.provider, self.account_invoice_id.number,
                self.account_invoice_id.id)
        elif self.state == 'done':
            _logger.info(
                '<%s> transaction completed, paying invoice %s (ID %s)',
                self.acquirer_id.provider, self.account_invoice_id.number,
                self.account_invoice_id.id)
            self._pay_invoice()
        else:
            _logger.warning('<%s> transaction MISMATCH for invoice %s (ID %s)',
                            self.acquirer_id.provider,
                            self.account_invoice_id.number,
                            self.account_invoice_id.id)
            return 'pay_invoice_tx_state'
        return True
Пример #16
0
 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:
         expense_sheets = self.env['hr.expense.sheet'].search([
             ('account_move_id', 'in', account_move_ids),
             ('state', '!=', 'done')
         ])
         expense_sheets.set_to_paid()
     return res
Пример #17
0
    def _check_sum(self):
        """ Check if each cost line its valuation lines sum to the correct amount
        and if the overall total amount is correct also """
        prec_digits = self.env['decimal.precision'].precision_get('Account')
        for landed_cost in self:
            total_amount = sum(
                landed_cost.valuation_adjustment_lines.mapped(
                    'additional_landed_cost'))
            if not tools.float_compare(total_amount,
                                       landed_cost.amount_total,
                                       precision_digits=prec_digits) == 0:
                return False

            val_to_cost_lines = defaultdict(lambda: 0.0)
            for val_line in landed_cost.valuation_adjustment_lines:
                val_to_cost_lines[
                    val_line.cost_line_id] += val_line.additional_landed_cost
            if any(
                    tools.float_compare(cost_line.price_unit,
                                        val_amount,
                                        precision_digits=prec_digits) != 0
                    for cost_line, val_amount in val_to_cost_lines.items()):
                return False
        return True
Пример #18
0
 def test_Percentage_coupon_code(self):
     if not self.sale_order_2.coupon_flag:
         self.check_all_coupon_code(self.sale_order_2, 'Get10Peroff',
                                    self.pricelist_2)
     self.assertTrue(self.sale_order_2.have_coupon_code != '',
                     'Coupon Code: Please enter the coupon code!')
     self.assertEqual(
         float_compare(self.sale_order_2.order_line[0].discount,
                       24.90,
                       precision_digits=2), 0,
         'Discount Line: the discount of first'
         'sale order line should be 24.90!')
     self.assertEqual(
         self.sale_order_2.discount, 3305.48,
         'Sale Discount: the discount for the '
         'sale order should be 3305.48!')
Пример #19
0
 def test_Buy_X_Product_Get_Y_Other_Product_Free_coupon_code(self):
     if not self.sale_order_5.coupon_flag:
         self.check_all_coupon_code(self.sale_order_5, 'BXGYOtherFree',
                                    self.pricelist_2)
     self.assertTrue(self.sale_order_5.have_coupon_code != '',
                     'Coupon Code: Please enter coupon code!')
     self.assertEqual(
         float_compare(self.sale_order_5.order_line[0].discount,
                       14.90,
                       precision_digits=2), 0,
         'Discount Line: the discount of first'
         'sale order line should be 14.90!')
     self.assertEqual(
         self.sale_order_5.discount, 1977.98,
         'Sale Discount: the discount for the '
         'sale order should be 1977.98!')
     self.assertTrue(
         len(self.sale_order_5.order_line) == 3,
         'Sale: Order Line is missing')
     self.assertEqual(self.sale_order_5.order_line[2].price_unit, 0.0,
                      'Price unit of the line should be 0.0!')
Пример #20
0
 def action_validate(self):
     self.ensure_one()
     precision = self.env['decimal.precision'].precision_get(
         'Product Unit of Measure')
     available_qty = sum(self.env['stock.quant']._gather(
         self.product_id,
         self.location_id,
         self.lot_id,
         self.package_id,
         self.owner_id,
         strict=True).mapped('quantity'))
     if float_compare(available_qty,
                      self.scrap_qty,
                      precision_digits=precision) >= 0:
         return self.do_scrap()
     else:
         return {
             'name':
             _('Insufficient Quantity'),
             'view_type':
             'form',
             'view_mode':
             'form',
             'res_model':
             'stock.warn.insufficient.qty.scrap',
             'view_id':
             self.env.ref(
                 'stock.stock_warn_insufficient_qty_scrap_form_view').id,
             'type':
             'ir.actions.act_window',
             'context': {
                 'default_product_id': self.product_id.id,
                 'default_location_id': self.location_id.id,
                 'default_scrap_id': self.id
             },
             'target':
             'new'
         }
Пример #21
0
    def compare_amounts(self, 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 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.

           With the new API, call it like: ``currency.compare_amounts(amount1, amount2)``.
        """
        return tools.float_compare(amount1,
                                   amount2,
                                   precision_rounding=self.rounding)
Пример #22
0
 def _onchange_qty_producing(self):
     """ Update stock.move.lot records, according to the new qty currently
     produced. """
     moves = self.move_raw_ids.filtered(
         lambda move: move.state not in
         ('done', 'cancel') and move.product_id.tracking != 'none' and move.
         product_id.id != self.production_id.product_id.id)
     for move in moves:
         move_lots = self.active_move_line_ids.filtered(
             lambda move_lot: move_lot.move_id == move)
         if not move_lots:
             continue
         rounding = move.product_uom.rounding
         new_qty = float_round(move.unit_factor * self.qty_producing,
                               precision_rounding=rounding)
         if move.product_id.tracking == 'lot':
             move_lots[0].product_qty = new_qty
             move_lots[0].qty_done = new_qty
         elif move.product_id.tracking == 'serial':
             # Create extra pseudo record
             qty_todo = float_round(new_qty -
                                    sum(move_lots.mapped('qty_done')),
                                    precision_rounding=rounding)
             if float_compare(qty_todo, 0.0,
                              precision_rounding=rounding) > 0:
                 while float_compare(
                         qty_todo, 0.0, precision_rounding=rounding) > 0:
                     self.active_move_line_ids += self.env[
                         'stock.move.line'].new({
                             'move_id':
                             move.id,
                             'product_id':
                             move.product_id.id,
                             'lot_id':
                             False,
                             'product_uom_qty':
                             0.0,
                             'product_uom_id':
                             move.product_uom.id,
                             'qty_done':
                             min(1.0, qty_todo),
                             'workorder_id':
                             self.id,
                             'done_wo':
                             False,
                             'location_id':
                             move.location_id.id,
                             'location_dest_id':
                             move.location_dest_id.id,
                         })
                     qty_todo -= 1
             elif float_compare(qty_todo, 0.0,
                                precision_rounding=rounding) < 0:
                 qty_todo = abs(qty_todo)
                 for move_lot in move_lots:
                     if float_compare(
                             qty_todo, 0, precision_rounding=rounding) <= 0:
                         break
                     if not move_lot.lot_id and float_compare(
                             qty_todo,
                             move_lot.qty_done,
                             precision_rounding=rounding) >= 0:
                         qty_todo = float_round(qty_todo -
                                                move_lot.qty_done,
                                                precision_rounding=rounding)
                         self.active_move_line_ids -= move_lot  # Difference operator
                     else:
                         #move_lot.product_qty = move_lot.product_qty - qty_todo
                         if float_compare(move_lot.qty_done - qty_todo,
                                          0,
                                          precision_rounding=rounding) == 1:
                             move_lot.qty_done = move_lot.qty_done - qty_todo
                         else:
                             move_lot.qty_done = 0
                         qty_todo = 0
Пример #23
0
    def _generate_consumed_move_line(self, qty_to_add, final_lot, lot=False):
        if lot:
            move_lines = self.move_line_ids.filtered(
                lambda ml: ml.lot_id == lot and not ml.lot_produced_id)
        else:
            move_lines = self.move_line_ids.filtered(
                lambda ml: not ml.lot_id and not ml.lot_produced_id)

        # Sanity check: if the product is a serial number and `lot` is already present in the other
        # consumed move lines, raise.
        if lot and self.product_id.tracking == 'serial' and lot in self.move_line_ids.filtered(
                lambda ml: ml.qty_done).mapped('lot_id'):
            raise UserError(
                _('You cannot consume the same serial number twice. Please correct the serial numbers encoded.'
                  ))

        for ml in move_lines:
            rounding = ml.product_uom_id.rounding
            if float_compare(qty_to_add, 0, precision_rounding=rounding) <= 0:
                break
            quantity_to_process = min(qty_to_add,
                                      ml.product_uom_qty - ml.qty_done)
            qty_to_add -= quantity_to_process

            new_quantity_done = (ml.qty_done + quantity_to_process)
            if float_compare(new_quantity_done,
                             ml.product_uom_qty,
                             precision_rounding=rounding) >= 0:
                ml.write({
                    'qty_done': new_quantity_done,
                    'lot_produced_id': final_lot.id
                })
            else:
                new_qty_reserved = ml.product_uom_qty - new_quantity_done
                default = {
                    'product_uom_qty': new_quantity_done,
                    'qty_done': new_quantity_done,
                    'lot_produced_id': final_lot.id
                }
                ml.copy(default=default)
                ml.with_context(bypass_reservation_update=True).write({
                    'product_uom_qty':
                    new_qty_reserved,
                    'qty_done':
                    0
                })

        if float_compare(qty_to_add,
                         0,
                         precision_rounding=self.product_uom.rounding) > 0:
            # Search for a sub-location where the product is available. This might not be perfectly
            # correct if the quantity available is spread in several sub-locations, but at least
            # we should be closer to the reality. Anyway, no reservation is made, so it is still
            # possible to change it afterwards.
            quants = self.env['stock.quant']._gather(self.product_id,
                                                     self.location_id,
                                                     lot_id=lot,
                                                     strict=False)
            available_quantity = self.product_id.uom_id._compute_quantity(
                self.env['stock.quant']._get_available_quantity(
                    self.product_id,
                    self.location_id,
                    lot_id=lot,
                    strict=False), self.product_uom)
            location_id = False
            if float_compare(qty_to_add,
                             available_quantity,
                             precision_rounding=self.product_uom.rounding) < 0:
                location_id = quants.filtered(
                    lambda r: r.quantity > 0)[-1:].location_id

            vals = {
                'move_id':
                self.id,
                'product_id':
                self.product_id.id,
                'location_id':
                location_id.id if location_id else self.location_id.id,
                'location_dest_id':
                self.location_dest_id.id,
                'product_uom_qty':
                0,
                'product_uom_id':
                self.product_uom.id,
                'qty_done':
                qty_to_add,
                'lot_produced_id':
                final_lot.id,
            }
            if lot:
                vals.update({'lot_id': lot.id})
            self.env['stock.move.line'].create(vals)
Пример #24
0
    def _action_launch_procurement_rule(self):
        """
        Launch procurement group run method with required/custom fields genrated by a
        sale order line. procurement group will launch '_run_move', '_run_buy' or '_run_manufacture'
        depending on the sale order line product rule.
        """
        precision = self.env['decimal.precision'].precision_get(
            'Product Unit of Measure')
        errors = []
        for line in self:
            if line.state != 'sale' or not line.product_id.type in ('consu',
                                                                    'product'):
                continue
            qty = 0.0
            for move in line.move_ids.filtered(lambda r: r.state != 'cancel'):
                qty += move.product_uom._compute_quantity(
                    move.product_uom_qty,
                    line.product_uom,
                    rounding_method='HALF-UP')
            if float_compare(qty,
                             line.product_uom_qty,
                             precision_digits=precision) >= 0:
                continue

            group_id = line.order_id.procurement_group_id
            if not group_id:
                group_id = self.env['procurement.group'].create({
                    'name':
                    line.order_id.name,
                    'move_type':
                    line.order_id.picking_policy,
                    'sale_id':
                    line.order_id.id,
                    'partner_id':
                    line.order_id.partner_shipping_id.id,
                })
                line.order_id.procurement_group_id = group_id
            else:
                # In case the procurement group is already created and the order was
                # cancelled, we need to update certain values of the group.
                updated_vals = {}
                if group_id.partner_id != line.order_id.partner_shipping_id:
                    updated_vals.update(
                        {'partner_id': line.order_id.partner_shipping_id.id})
                if group_id.move_type != line.order_id.picking_policy:
                    updated_vals.update(
                        {'move_type': line.order_id.picking_policy})
                if updated_vals:
                    group_id.write(updated_vals)

            values = line._prepare_procurement_values(group_id=group_id)
            product_qty = line.product_uom_qty - qty

            procurement_uom = line.product_uom
            quant_uom = line.product_id.uom_id
            get_param = self.env['ir.config_parameter'].sudo().get_param
            if procurement_uom.id != quant_uom.id and get_param(
                    'stock.propagate_uom') != '1':
                product_qty = line.product_uom._compute_quantity(
                    product_qty, quant_uom, rounding_method='HALF-UP')
                procurement_uom = quant_uom

            try:
                self.env['procurement.group'].run(
                    line.product_id, product_qty, procurement_uom,
                    line.order_id.partner_shipping_id.property_stock_customer,
                    line.name, line.order_id.name, values)
            except UserError as error:
                errors.append(error.name)
        if errors:
            raise UserError('\n'.join(errors))
        return True
Пример #25
0
    def test_10_calculation_price_of_products_pricelist(self):
        """Test calculation of product price based on pricelist"""
        # I check sale price of iPad Retina Display
        context = {}
        context.update({
            'pricelist': self.customer_pricelist.id,
            'quantity': 1
        })
        ipad_retina_display = self.ipad_retina_display.with_context(context)
        msg = "Wrong sale price: iPad Retina Display. should be %s instead of %s" % (
            ipad_retina_display.price,
            (ipad_retina_display.lst_price - ipad_retina_display.lst_price *
             (0.10)))
        self.assertEqual(
            float_compare(ipad_retina_display.price,
                          (ipad_retina_display.lst_price -
                           ipad_retina_display.lst_price * (0.10)),
                          precision_digits=2), 0, msg)

        # I check sale price of Laptop.
        laptop_E5023 = self.laptop_E5023.with_context(context)
        msg = "Wrong sale price: Laptop. should be %s instead of %s" % (
            laptop_E5023.price, (laptop_E5023.lst_price + 1))
        self.assertEqual(
            float_compare(laptop_E5023.price,
                          laptop_E5023.lst_price + 1,
                          precision_digits=2), 0, msg)

        # I check sale price of IT component.
        apple_headphones = self.apple_in_ear_headphones.with_context(context)
        msg = "Wrong sale price: IT component. should be %s instead of %s" % (
            apple_headphones.price, apple_headphones.lst_price)
        self.assertEqual(
            float_compare(apple_headphones.price,
                          apple_headphones.lst_price,
                          precision_digits=2), 0, msg)

        # I check sale price of IT component if more than 3 Unit.
        context.update({'quantity': 5})
        laptop_S3450 = self.laptop_S3450.with_context(context)
        msg = "Wrong sale price: IT component if more than 3 Unit. should be %s instead of %s" % (
            laptop_S3450.price,
            (laptop_S3450.lst_price - laptop_S3450.lst_price * (0.05)))
        self.assertEqual(
            float_compare(laptop_S3450.price,
                          laptop_S3450.lst_price - laptop_S3450.lst_price *
                          (0.05),
                          precision_digits=2), 0, msg)

        # I check sale price of LCD Monitor.
        context.update({'quantity': 1})
        ipad_mini = self.ipad_mini.with_context(context)
        msg = "Wrong sale price: LCD Monitor. should be %s instead of %s" % (
            ipad_mini.price, ipad_mini.lst_price)
        self.assertEqual(
            float_compare(ipad_mini.price,
                          ipad_mini.lst_price,
                          precision_digits=2), 0, msg)

        # I check sale price of LCD Monitor on end of year.
        context.update({'quantity': 1, 'date': '2011-12-31'})
        ipad_mini = self.ipad_mini.with_context(context)
        msg = "Wrong sale price: LCD Monitor on end of year. should be %s instead of %s" % (
            ipad_mini.price, ipad_mini.lst_price - ipad_mini.lst_price *
            (0.30))
        self.assertEqual(
            float_compare(ipad_mini.price,
                          ipad_mini.lst_price - ipad_mini.lst_price * (0.30),
                          precision_digits=2), 0, msg)

        # I check cost price of LCD Monitor.
        context.update({
            'quantity': 1,
            'date': False,
            'partner_id': self.res_partner_4.id
        })
        ipad_mini = self.ipad_mini.with_context(context)
        partner = self.res_partner_4.with_context(context)
        msg = "Wrong cost price: LCD Monitor. should be 790 instead of %s" % ipad_mini._select_seller(
            partner_id=partner, quantity=1.0).price
        self.assertEqual(
            float_compare(ipad_mini._select_seller(partner_id=partner,
                                                   quantity=1.0).price,
                          790,
                          precision_digits=2), 0, msg)

        # I check cost price of LCD Monitor if more than 3 Unit.
        context.update({'quantity': 3})
        ipad_mini = self.ipad_mini.with_context(context)
        partner = self.res_partner_4.with_context(context)
        msg = "Wrong cost price: LCD Monitor if more than 3 Unit.should be 785 instead of %s" % ipad_mini._select_seller(
            partner_id=partner, quantity=3.0).price
        self.assertEqual(
            float_compare(ipad_mini._select_seller(partner_id=partner,
                                                   quantity=3.0).price,
                          785,
                          precision_digits=2), 0, msg)

        # I print the sale prices report.
        ctx = {
            'active_model':
            'product.product',
            'date':
            '2011-12-30',
            'active_ids': [
                self.computer_SC234.id, self.ipad_retina_display.id,
                self.custom_computer_kit.id, self.ipad_mini.id
            ]
        }
        data_dict = {
            'qty1': 1,
            'qty2': 5,
            'qty3': 10,
            'qty4': 15,
            'qty5': 30,
            'price_list': self.customer_pricelist.id,
        }
        test_reports.try_report_action(self.cr,
                                       self.uid,
                                       'action_product_price_list',
                                       wiz_data=data_dict,
                                       context=ctx,
                                       our_module='product')
Пример #26
0
    def action_payslip_done(self):
        precision = self.env['decimal.precision'].precision_get('Payroll')

        for slip in self:
            line_ids = []
            debit_sum = 0.0
            credit_sum = 0.0
            date = slip.date or slip.date_to

            name = _('Payslip of %s') % (slip.employee_id.name)
            move_dict = {
                'narration': name,
                'ref': slip.number,
                'journal_id': slip.journal_id.id,
                'date': date,
            }
            for line in slip.details_by_salary_rule_category:
                amount = slip.credit_note and -line.total or line.total
                if float_is_zero(amount, 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': line._get_partner_id(credit_account=False),
                        'account_id': debit_account_id,
                        'journal_id': slip.journal_id.id,
                        'date': date,
                        'debit': amount > 0.0 and amount or 0.0,
                        'credit': amount < 0.0 and -amount or 0.0,
                        'analytic_account_id': line.salary_rule_id.analytic_account_id.id,
                        'tax_line_id': line.salary_rule_id.account_tax_id.id,
                    })
                    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': line._get_partner_id(credit_account=True),
                        'account_id': credit_account_id,
                        'journal_id': slip.journal_id.id,
                        'date': date,
                        'debit': amount < 0.0 and -amount or 0.0,
                        'credit': amount > 0.0 and amount or 0.0,
                        'analytic_account_id': line.salary_rule_id.analytic_account_id.id,
                        'tax_line_id': line.salary_rule_id.account_tax_id.id,
                    })
                    line_ids.append(credit_line)
                    credit_sum += credit_line[2]['credit'] - credit_line[2]['debit']

            if float_compare(credit_sum, debit_sum, precision_digits=precision) == -1:
                acc_id = slip.journal_id.default_credit_account_id.id
                if not acc_id:
                    raise UserError(_('The Expense Journal "%s" has not properly configured the Credit Account!') % (slip.journal_id.name))
                adjust_credit = (0, 0, {
                    'name': _('Adjustment Entry'),
                    'partner_id': False,
                    'account_id': acc_id,
                    'journal_id': slip.journal_id.id,
                    'date': date,
                    'debit': 0.0,
                    'credit': debit_sum - credit_sum,
                })
                line_ids.append(adjust_credit)

            elif float_compare(debit_sum, credit_sum, precision_digits=precision) == -1:
                acc_id = slip.journal_id.default_debit_account_id.id
                if not acc_id:
                    raise UserError(_('The Expense Journal "%s" has not properly configured the Debit Account!') % (slip.journal_id.name))
                adjust_debit = (0, 0, {
                    'name': _('Adjustment Entry'),
                    'partner_id': False,
                    'account_id': acc_id,
                    'journal_id': slip.journal_id.id,
                    'date': date,
                    'debit': credit_sum - debit_sum,
                    'credit': 0.0,
                })
                line_ids.append(adjust_debit)
            move_dict['line_ids'] = line_ids
            move = self.env['account.move'].create(move_dict)
            slip.write({'move_id': move.id, 'date': date})
            move.post()
        return super(HrPayslip, self).action_payslip_done()
Пример #27
0
    def test_order_to_invoice(self):

        # I create a new PoS order with 2 units of PC1 at 450 EUR (Tax Incl) and 3 units of PCSC349 at 300 EUR. (Tax Excl)
        self.pos_order_pos1 = self.PosOrder.create({
            'company_id':
            self.company_id,
            'partner_id':
            self.partner1.id,
            'pricelist_id':
            self.partner1.property_product_pricelist.id,
            'lines': [(0, 0, {
                'name': "OL/0001",
                'product_id': self.product3.id,
                'price_unit': 450,
                'discount': 5.0,
                'qty': 2.0,
                'tax_ids': [(6, 0, self.product3.taxes_id.ids)],
            }),
                      (0, 0, {
                          'name': "OL/0002",
                          'product_id': self.product4.id,
                          'price_unit': 300,
                          'discount': 5.0,
                          'qty': 3.0,
                          'tax_ids': [(6, 0, self.product4.taxes_id.ids)],
                      })]
        })

        # I click on the "Make Payment" wizard to pay the PoS order
        context_make_payment = {
            "active_ids": [self.pos_order_pos1.id],
            "active_id": self.pos_order_pos1.id
        }
        self.pos_make_payment = self.PosMakePayment.with_context(
            context_make_payment).create({
                'amount': (450 * 2 + 300 * 3 * 1.05) * 0.95,
            })
        # I click on the validate button to register the payment.
        context_payment = {'active_id': self.pos_order_pos1.id}
        self.pos_make_payment.with_context(context_payment).check()

        # I check that the order is marked as paid and there is no invoice
        # attached to it
        self.assertEqual(self.pos_order_pos1.state, 'paid',
                         "Order should be in paid state.")
        self.assertFalse(self.pos_order_pos1.invoice_id,
                         'Invoice should not be attached to order.')

        # I generate an invoice from the order
        res = self.pos_order_pos1.action_pos_order_invoice()
        self.assertIn('res_id', res, "No invoice created")

        # I test that the total of the attached invoice is correct
        invoice = self.env['account.invoice'].browse(res['res_id'])
        self.assertEqual(
            float_compare(invoice.amount_total, 1752.75, precision_digits=2),
            0, "Invoice not correct")
        """In order to test the reports on Bank Statement defined in point_of_sale module, I create a bank statement line, confirm it and print the reports"""

        # I select the period and journal for the bank statement

        context_journal = {'journal_type': 'bank'}
        self.assertTrue(
            self.AccountBankStatement.with_context(
                context_journal)._default_journal(),
            'Journal has not been selected')
        journal = self.env['account.journal'].create({
            'name':
            'Bank Test',
            'code':
            'BNKT',
            'type':
            'bank',
            'company_id':
            self.company_id,
        })
        # I create a bank statement with Opening and Closing balance 0.
        account_statement = self.AccountBankStatement.create({
            'balance_start':
            0.0,
            'balance_end_real':
            0.0,
            'date':
            time.strftime('%Y-%m-%d'),
            'journal_id':
            journal.id,
            'company_id':
            self.company_id,
            'name':
            'pos session test',
        })
        # I create bank statement line
        account_statement_line = self.AccountBankStatementLine.create({
            'amount':
            1000,
            'partner_id':
            self.partner4.id,
            'statement_id':
            account_statement.id,
            'name':
            'EXT001'
        })
        # I modify the bank statement and set the Closing Balance.
        account_statement.write({
            'balance_end_real': 1000.0,
        })

        # I reconcile the bank statement.
        new_aml_dicts = [{
            'account_id':
            self.partner4.property_account_receivable_id.id,
            'name':
            "EXT001",
            'credit':
            1000.0,
            'debit':
            0.0,
        }]

        account_statement_line.process_reconciliations([{
            'new_aml_dicts':
            new_aml_dicts
        }])

        # I confirm the bank statement using Confirm button

        self.AccountBankStatement.button_confirm_bank()
Пример #28
0
    def _procure_orderpoint_confirm(self,
                                    use_new_cursor=False,
                                    company_id=False):
        """ Create procurements based on orderpoints.
        :param bool use_new_cursor: if set, use a dedicated cursor and auto-commit after processing
            1000 orderpoints.
            This is appropriate for batch jobs only.
        """
        if company_id and self.env.user.company_id.id != company_id:
            # To ensure that the company_id is taken into account for
            # all the processes triggered by this method
            # i.e. If a PO is generated by the run of the procurements the
            # sequence to use is the one for the specified company not the
            # one of the user's company
            self = self.with_context(company_id=company_id,
                                     force_company=company_id)
        OrderPoint = self.env['stock.warehouse.orderpoint']
        domain = self._get_orderpoint_domain(company_id=company_id)
        orderpoints_noprefetch = OrderPoint.with_context(
            prefetch_fields=False).search(
                domain,
                order=self._procurement_from_orderpoint_get_order()).ids
        while orderpoints_noprefetch:
            if use_new_cursor:
                cr = registry(self._cr.dbname).cursor()
                self = self.with_env(self.env(cr=cr))
            OrderPoint = self.env['stock.warehouse.orderpoint']

            orderpoints = OrderPoint.browse(orderpoints_noprefetch[:1000])
            orderpoints_noprefetch = orderpoints_noprefetch[1000:]

            # Calculate groups that can be executed together
            location_data = defaultdict(lambda: dict(
                products=self.env['product.product'],
                orderpoints=self.env['stock.warehouse.orderpoint'],
                groups=list()))
            for orderpoint in orderpoints:
                key = self._procurement_from_orderpoint_get_grouping_key(
                    [orderpoint.id])
                location_data[key]['products'] += orderpoint.product_id
                location_data[key]['orderpoints'] += orderpoint
                location_data[key][
                    'groups'] = self._procurement_from_orderpoint_get_groups(
                        [orderpoint.id])

            for location_id, location_data in location_data.items():
                location_orderpoints = location_data['orderpoints']
                product_context = dict(
                    self._context,
                    location=location_orderpoints[0].location_id.id)
                substract_quantity = location_orderpoints._quantity_in_progress(
                )

                for group in location_data['groups']:
                    if group.get('from_date'):
                        product_context['from_date'] = group[
                            'from_date'].strftime(
                                DEFAULT_SERVER_DATETIME_FORMAT)
                    if group['to_date']:
                        product_context['to_date'] = group['to_date'].strftime(
                            DEFAULT_SERVER_DATETIME_FORMAT)
                    product_quantity = location_data['products'].with_context(
                        product_context)._product_available()
                    for orderpoint in location_orderpoints:
                        try:
                            op_product_virtual = product_quantity[
                                orderpoint.product_id.id]['virtual_available']
                            if op_product_virtual is None:
                                continue
                            if float_compare(op_product_virtual,
                                             orderpoint.product_min_qty,
                                             precision_rounding=orderpoint.
                                             product_uom.rounding) <= 0:
                                qty = max(orderpoint.product_min_qty,
                                          orderpoint.product_max_qty
                                          ) - op_product_virtual
                                remainder = orderpoint.qty_multiple > 0 and qty % orderpoint.qty_multiple or 0.0

                                if float_compare(remainder,
                                                 0.0,
                                                 precision_rounding=orderpoint.
                                                 product_uom.rounding) > 0:
                                    qty += orderpoint.qty_multiple - remainder

                                if float_compare(qty,
                                                 0.0,
                                                 precision_rounding=orderpoint.
                                                 product_uom.rounding) < 0:
                                    continue

                                qty -= substract_quantity[orderpoint.id]
                                qty_rounded = float_round(
                                    qty,
                                    precision_rounding=orderpoint.product_uom.
                                    rounding)
                                if qty_rounded > 0:
                                    values = orderpoint._prepare_procurement_values(
                                        qty_rounded,
                                        **group['procurement_values'])
                                    try:
                                        with self._cr.savepoint():
                                            self.env['procurement.group'].run(
                                                orderpoint.product_id,
                                                qty_rounded,
                                                orderpoint.product_uom,
                                                orderpoint.location_id,
                                                orderpoint.name,
                                                orderpoint.name, values)
                                    except UserError as error:
                                        self.env[
                                            'procurement.rule']._log_next_activity(
                                                orderpoint.product_id,
                                                error.name)
                                    self._procurement_from_orderpoint_post_process(
                                        [orderpoint.id])
                                if use_new_cursor:
                                    cr.commit()

                        except OperationalError:
                            if use_new_cursor:
                                orderpoints_noprefetch += [orderpoint.id]
                                cr.rollback()
                                continue
                            else:
                                raise

            try:
                if use_new_cursor:
                    cr.commit()
            except OperationalError:
                if use_new_cursor:
                    cr.rollback()
                    continue
                else:
                    raise

            if use_new_cursor:
                cr.commit()
                cr.close()

        return {}
Пример #29
0
 def _compute_is_produced(self):
     rounding = self.production_id.product_uom_id.rounding
     self.is_produced = float_compare(self.qty_produced,
                                      self.production_id.product_qty,
                                      precision_rounding=rounding) >= 0
Пример #30
0
    def record_production(self):
        self.ensure_one()
        if self.qty_producing <= 0:
            raise UserError(
                _('Please set the quantity you are currently producing. It should be different from zero.'
                  ))

        if (self.production_id.product_id.tracking !=
                'none') and not self.final_lot_id and self.move_raw_ids:
            raise UserError(
                _('You should provide a lot/serial number for the final product'
                  ))

        # Update quantities done on each raw material line
        # For each untracked component without any 'temporary' move lines,
        # (the new workorder tablet view allows registering consumed quantities for untracked components)
        # we assume that only the theoretical quantity was used
        for move in self.move_raw_ids:
            if move.has_tracking == 'none' and (move.state not in ('done', 'cancel')) and move.bom_line_id\
                        and move.unit_factor and not move.move_line_ids.filtered(lambda ml: not ml.done_wo):
                rounding = move.product_uom.rounding
                if self.product_id.tracking != 'none':
                    qty_to_add = float_round(self.qty_producing *
                                             move.unit_factor,
                                             precision_rounding=rounding)
                    move._generate_consumed_move_line(qty_to_add,
                                                      self.final_lot_id)
                else:
                    move.quantity_done += float_round(
                        self.qty_producing * move.unit_factor,
                        precision_rounding=rounding)

        # Transfer quantities from temporary to final move lots or make them final
        for move_line in self.active_move_line_ids:
            # Check if move_line already exists
            if move_line.qty_done <= 0:  # rounding...
                move_line.sudo().unlink()
                continue
            if move_line.product_id.tracking != 'none' and not move_line.lot_id:
                raise UserError(
                    _('You should provide a lot/serial number for a component')
                )
            # Search other move_line where it could be added:
            lots = self.move_line_ids.filtered(
                lambda x: (x.lot_id.id == move_line.lot_id.id) and
                (not x.lot_produced_id) and (not x.done_move) and
                (x.product_id == move_line.product_id))
            if lots:
                lots[0].qty_done += move_line.qty_done
                lots[0].lot_produced_id = self.final_lot_id.id
                move_line.sudo().unlink()
            else:
                move_line.lot_produced_id = self.final_lot_id.id
                move_line.done_wo = True

        # One a piece is produced, you can launch the next work order
        if self.next_work_order_id.state == 'pending':
            self.next_work_order_id.state = 'ready'

        self.move_line_ids.filtered(
            lambda move_line: not move_line.done_move and not move_line.
            lot_produced_id and move_line.qty_done > 0).write({
                'lot_produced_id':
                self.final_lot_id.id,
                'lot_produced_qty':
                self.qty_producing
            })

        # If last work order, then post lots used
        # TODO: should be same as checking if for every workorder something has been done?
        if not self.next_work_order_id:
            production_moves = self.production_id.move_finished_ids.filtered(
                lambda x: (x.state not in ('done', 'cancel')))
            for production_move in production_moves:
                if production_move.product_id.id == self.production_id.product_id.id and production_move.has_tracking != 'none':
                    move_line = production_move.move_line_ids.filtered(
                        lambda x: x.lot_id.id == self.final_lot_id.id)
                    if move_line:
                        move_line.product_uom_qty += self.qty_producing
                    else:
                        move_line.create({
                            'move_id':
                            production_move.id,
                            'product_id':
                            production_move.product_id.id,
                            'lot_id':
                            self.final_lot_id.id,
                            'product_uom_qty':
                            self.qty_producing,
                            'product_uom_id':
                            production_move.product_uom.id,
                            'qty_done':
                            self.qty_producing,
                            'workorder_id':
                            self.id,
                            'location_id':
                            production_move.location_id.id,
                            'location_dest_id':
                            production_move.location_dest_id.id,
                        })
                elif production_move.unit_factor:
                    rounding = production_move.product_uom.rounding
                    production_move.quantity_done += float_round(
                        self.qty_producing * production_move.unit_factor,
                        precision_rounding=rounding)
                else:
                    production_move.quantity_done += self.qty_producing

        if not self.next_work_order_id:
            for by_product_move in self.production_id.move_finished_ids.filtered(
                    lambda x: (x.product_id.id != self.production_id.product_id
                               .id) and (x.state not in ('done', 'cancel'))):
                if by_product_move.has_tracking == 'none':
                    by_product_move.quantity_done += self.qty_producing * by_product_move.unit_factor

        # Update workorder quantity produced
        self.qty_produced += self.qty_producing

        if self.final_lot_id:
            self.final_lot_id.use_next_on_work_order_id = self.next_work_order_id
            self.final_lot_id = False

        # Set a qty producing
        rounding = self.production_id.product_uom_id.rounding
        if float_compare(self.qty_produced,
                         self.production_id.product_qty,
                         precision_rounding=rounding) >= 0:
            self.qty_producing = 0
        elif self.production_id.product_id.tracking == 'serial':
            self._assign_default_final_lot_id()
            self.qty_producing = 1.0
            self._generate_lot_ids()
        else:
            self.qty_producing = float_round(self.production_id.product_qty -
                                             self.qty_produced,
                                             precision_rounding=rounding)
            self._generate_lot_ids()

        if self.next_work_order_id and self.production_id.product_id.tracking != 'none':
            self.next_work_order_id._assign_default_final_lot_id()

        if float_compare(self.qty_produced,
                         self.production_id.product_qty,
                         precision_rounding=rounding) >= 0:
            self.button_finish()
        return True