diff --git a/addons/mrp_account/tests/test_analytic_account.py b/addons/mrp_account/tests/test_analytic_account.py index 3ab6e3559ce..cd7bd5411cc 100644 --- a/addons/mrp_account/tests/test_analytic_account.py +++ b/addons/mrp_account/tests/test_analytic_account.py @@ -392,3 +392,43 @@ class TestAnalyticAccount(TestMrpAnalyticAccount): self.assertAlmostEqual(line_with_A_accounts.amount, -100/3, delta=0.02) self.assertAlmostEqual(line_with_B_accounts.amount, -100/3, delta=0.02) self.assertAlmostEqual(line_with_B_accounts.amount, -100/3, delta=0.02) + + def test_mo_qty_analytics(self): + """ + This test tests multiple behaviours and edge cases. First off, when + an analytic distribution with an MO, the analytic entries should only + be generated when the MO's components are consumed/reserved (i.e. + picked). Second, when changing the produced quantity in a confirmed MO, + it should appropriately adjust the amount of picked components. Third, + analytic entries should at all times reflect the current situation, and + thus must be regenerated every time there's a change in components' + picked status. + """ + # refill components + location = self.env.ref('stock.stock_location_stock') + self.env['stock.quant']._update_available_quantity(self.component, location, 10) + + # create a mo + mo_form = Form(self.env['mrp.production']) + mo_form.product_id = self.product + mo_form.bom_id = self.bom + mo_form.product_qty = 10.0 + mo_form.analytic_distribution = {str(self.analytic_account.id): 100.0} + mo = mo_form.save() + mo.action_confirm() + self.assertEqual(mo.state, 'confirmed') + self.assertEqual(self.analytic_account.balance, 0.0) + + # increase qty_producing to 5.0 + mo_form = Form(mo) + mo_form.qty_producing = 5.0 + mo_form.save() + self.assertEqual(mo.state, 'progress') + self.assertEqual(self.analytic_account.balance, -50.0) + + # decrease qty_producing to 0.0 + mo_form = Form(mo) + mo_form.qty_producing = 0.0 + mo_form.save() + self.assertEqual(mo.state, 'progress') + self.assertEqual(self.analytic_account.balance, 0.0) diff --git a/addons/stock_account/models/stock_move.py b/addons/stock_account/models/stock_move.py index c906ecbf54e..7d6c5d2167a 100644 --- a/addons/stock_account/models/stock_move.py +++ b/addons/stock_account/models/stock_move.py @@ -20,6 +20,10 @@ class StockMove(models.Model): stock_valuation_layer_ids = fields.One2many('stock.valuation.layer', 'stock_move_id') analytic_account_line_ids = fields.Many2many('account.analytic.line', copy=False) + def _inverse_picked(self): + super()._inverse_picked() + self._account_analytic_entry_move() + def _filter_anglo_saxon_moves(self, product): return self.filtered(lambda m: m.product_id.id == product.id) @@ -420,11 +424,12 @@ class StockMove(models.Model): amount, unit_amount = 0, 0 if self.state != 'done': - unit_amount = self.product_uom._compute_quantity( - self.quantity, self.product_id.uom_id) - # Falsy in FIFO but since it's an estimation we don't require exact correct cost. Otherwise - # we would have to recompute all the analytic estimation at each out. - amount = - unit_amount * self.product_id.standard_price + if self.picked: + unit_amount = self.product_uom._compute_quantity( + self.quantity, self.product_id.uom_id) + # Falsy in FIFO but since it's an estimation we don't require exact correct cost. Otherwise + # we would have to recompute all the analytic estimation at each out. + amount = - unit_amount * self.product_id.standard_price elif self.product_id.valuation == 'real_time' and not self._ignore_automatic_valuation(): accounts_data = self.product_id.product_tmpl_id.get_product_accounts() account_valuation = accounts_data.get('stock_valuation', False) diff --git a/addons/stock_account/models/stock_move_line.py b/addons/stock_account/models/stock_move_line.py index a9bffa7b97a..9b4c15340d1 100644 --- a/addons/stock_account/models/stock_move_line.py +++ b/addons/stock_account/models/stock_move_line.py @@ -53,6 +53,12 @@ class StockMoveLine(models.Model): self.env['stock.move'].browse(analytic_move_to_recompute)._account_analytic_entry_move() return res + def unlink(self): + analytic_move_to_recompute = self.move_id + res = super().unlink() + analytic_move_to_recompute._account_analytic_entry_move() + return res + # ------------------------------------------------------------------------- # SVL creation helpers # -------------------------------------------------------------------------