diff --git a/addons/stock/models/stock_move_line.py b/addons/stock/models/stock_move_line.py index 990fa4f146e..2b2e1b95c71 100644 --- a/addons/stock/models/stock_move_line.py +++ b/addons/stock/models/stock_move_line.py @@ -37,7 +37,7 @@ class StockMoveLine(models.Model): compute='_compute_reserved_qty', inverse='_set_reserved_qty', store=True) reserved_uom_qty = fields.Float( 'Reserved', default=0.0, digits='Product Unit of Measure', required=True, copy=False) - qty_done = fields.Float('Done', default=0.0, digits='Product Unit of Measure', copy=False, + qty_done = fields.Float('Done', digits='Product Unit of Measure', copy=False, compute='_compute_qty_done', store=True, readonly=False) package_id = fields.Many2one( 'stock.quant.package', 'Source Package', ondelete='restrict', @@ -137,7 +137,11 @@ class StockMoveLine(models.Model): def _compute_qty_done(self): for record in self: if not record.qty_done: - record.qty_done = min(record.quant_id.quantity, record.move_id.product_qty) + if (record.move_id.product_qty - record.move_id.quantity_done): + record.qty_done = min(record.quant_id.available_quantity, max(record.move_id.product_qty - record.move_id.quantity_done, 0)) + else: + record.qty_done = record.quant_id.quantity + @api.constrains('lot_id', 'product_id') def _check_lot_product(self): diff --git a/addons/stock/static/src/fields/stock_move_line_x2_many_field.js b/addons/stock/static/src/fields/stock_move_line_x2_many_field.js index 6c6c8d3f6a6..137e2558da7 100644 --- a/addons/stock/static/src/fields/stock_move_line_x2_many_field.js +++ b/addons/stock/static/src/fields/stock_move_line_x2_many_field.js @@ -36,10 +36,14 @@ export class SMLX2ManyField extends X2ManyField { }; const productName = this.props.record.data.product_id[1]; const title = sprintf(this.env._t("Add line: %s"), productName); + const alreadySelected = this.props.record.data.move_line_ids.records.filter((line) => line.data.quant_id?.[0]); const domain = [ ["product_id", "=", this.props.record.data.product_id[0]], ["location_id", "child_of", this.props.context.default_location_id], ]; + if (alreadySelected.length) { + domain.push(["id", "not in", alreadySelected.map((line) => line.data.quant_id[0])]); + } return this.selectCreate({ domain, context, title }); } diff --git a/addons/stock/tests/test_move_lines.py b/addons/stock/tests/test_move_lines.py new file mode 100644 index 00000000000..d5c54dd590b --- /dev/null +++ b/addons/stock/tests/test_move_lines.py @@ -0,0 +1,84 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo.addons.stock.tests.common import TestStockCommon + + +class StockMoveLine(TestStockCommon): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env.user.groups_id += cls.env.ref("stock.group_tracking_owner") + cls.env.user.groups_id += cls.env.ref("stock.group_tracking_lot") + cls.env.user.groups_id += cls.env.ref("stock.group_production_lot") + cls.env.user.groups_id += cls.env.ref('stock.group_stock_multi_locations') + cls.product = cls.env['product.product'].create({ + 'name': 'Product A', + 'type': 'product', + 'tracking': 'lot', + 'categ_id': cls.env.ref('product.product_category_all').id, + }) + cls.shelf1 = cls.env['stock.location'].create({ + 'name': 'Shelf 1', + 'usage': 'internal', + 'location_id': cls.stock_location, + }) + cls.pack = cls.env['stock.quant.package'].create({ + 'name': 'Pack A', + }) + cls.lot = cls.env['stock.lot'].create({ + 'product_id': cls.product.id, + 'name': 'Lot 1', + 'company_id': cls.env.company.id, + }) + cls.partner = cls.env['res.partner'].create({ + 'name': 'The Owner', + 'email': 'owner@example.com', + }) + + cls.quant = cls.env['stock.quant'].create({ + 'product_id': cls.product.id, + 'location_id': cls.shelf1.id, + 'quantity': 10, + 'lot_id': cls.lot.id, + 'package_id': cls.pack.id, + 'owner_id': cls.partner.id, + }) + + def test_pick_from_1(self): + """ test quant name_get """ + self.assertEqual(self.quant.name_get()[0][1], 'WH/Stock/Shelf 1 - Lot 1 - Pack A - The Owner') + + def test_pick_from_2(self): + """ Create a move line from a quant""" + move = self.env['stock.move'].create({ + 'name': 'Test move', + 'product_id': self.product.id, + 'product_uom': self.product.uom_id.id, + 'location_id': self.stock_location, + 'location_dest_id': self.stock_location, + 'move_line_ids': [(0, 0, {'quant_id': self.quant.id})] + }) + + self.assertEqual(move.move_line_ids.lot_id, self.lot) + self.assertEqual(move.move_line_ids.package_id, self.pack) + self.assertEqual(move.move_line_ids.owner_id, self.partner) + self.assertEqual(move.move_line_ids.location_id, self.shelf1) + self.assertEqual(move.move_line_ids.qty_done, 10) + + def test_pick_from_3(self): + """ check the quantity done is added up to the initial demand""" + move = self.env['stock.move'].create({ + 'name': 'Test move', + 'product_id': self.product.id, + 'product_uom': self.product.uom_id.id, + 'location_id': self.stock_location, + 'location_dest_id': self.stock_location, + 'state': 'draft', + 'product_uom_qty': 5, + }) + move._action_confirm() + move._action_assign() + self.assertEqual(move.move_line_ids.qty_done, 0) + move.move_line_ids.write({'quant_id': self.quant.id}) + self.assertEqual(move.move_line_ids.qty_done, 5)