diff options
| author | Nicolas Galler <ngl@odoo.com> | 2020-11-12 17:47:53 +0000 |
|---|---|---|
| committer | Nicolas Galler <ngl@odoo.com> | 2020-11-20 16:00:26 +0000 |
| commit | 2abc90263fead40223a98f0d2e20b9cd80bd9572 (patch) | |
| tree | 3a43a7b9bda8a475550b912460f8f61d8c88cb4c | |
| parent | 909b66883c1085c3eba59452fd89f1328948acab (diff) | |
[FIX] stock: shipping entire package does not update delivered quantity
Behavior prior to this commit:
In the following scenario:
- order of 10 widgets
- delivery using an entire package of 15 widgets (by adding the package
under the "Packages" list and checking the "Done" checkbox)
The delivery, when validated, does not update the delivered quantity on
the sales order, and generates a warning about not shipping enough
product. We expect instead the sales order to be updated with the
actual quantity that was shipped (i.e., 15), and a warning about
shipping too many products.
Additionally, when adding the package, the picking reverts to a "Draft"
state and we have to click "Mark as Todo" again.
Behavior after the commit:
When adding one (or more) entire package in the packages list, and
marking them as Done, their entire quantity will be marked as Delivered
on the Sales Order, and no warning about not shipping enough product
will be shown. Of note, if the package also contains products that are
not on the SO, they will be marked as shipped as well (in that case the
picking will indeed revert to draft since we will have added a new
product). However they will not be automatically added to the SO.
Technical details:
When adding a package under the picking it needs to, eventually, reflect
on a stock move. Prior to the commit, the system would add a new stock
move. This had 2 problems:
- the generated move did not carry back to the SO,
- it would flip the picking back to draft (under certain circumstances
it was possible to circumvent that and have a package level, and a move
line, that was not tied to a stock move and thus would automatically
reattach itself when the picking was validated, and sort of work, but
it was more of a happy accident and relying on the steps being followed
in a very precise order).
With this commit, we do not generate the stock move as soon as the
package is added. Instead, that is done when the picking is marked as
done, either by relating it to an existing stock move, or by generating
a new one at this time.
opw-2367893
closes odoo/odoo#62108
X-original-commit: 173bfe6764a554d601d2c7edac2a4c79e2faaa83
Signed-off-by: Nicolas Galler <ngaller@users.noreply.github.com>
| -rw-r--r-- | addons/stock/models/stock_package_level.py | 2 | ||||
| -rw-r--r-- | addons/stock/tests/test_packing.py | 72 |
2 files changed, 71 insertions, 3 deletions
diff --git a/addons/stock/models/stock_package_level.py b/addons/stock/models/stock_package_level.py index d5d79ef81ef..0db3d8cd2a1 100644 --- a/addons/stock/models/stock_package_level.py +++ b/addons/stock/models/stock_package_level.py @@ -147,8 +147,6 @@ class StockPackageLevel(models.Model): if vals.get('location_dest_id'): result.mapped('move_line_ids').write({'location_dest_id': vals['location_dest_id']}) result.mapped('move_ids').write({'location_dest_id': vals['location_dest_id']}) - if result.picking_id.state != 'draft' and result.location_id and result.location_dest_id and not result.move_ids and not result.move_line_ids: - result._generate_moves() return result def write(self, vals): diff --git a/addons/stock/tests/test_packing.py b/addons/stock/tests/test_packing.py index 1e03411041b..ff06a5f444b 100644 --- a/addons/stock/tests/test_packing.py +++ b/addons/stock/tests/test_packing.py @@ -8,7 +8,6 @@ from odoo.exceptions import UserError class TestPackingCommon(SavepointCase): - @classmethod def setUpClass(cls): super(TestPackingCommon, cls).setUpClass() @@ -730,3 +729,74 @@ class TestPacking(TestPackingCommon): {"qty_done": 0, "result_package_id": new_package.id}, ], ) + + def test_entire_pack_overship(self): + """ + Test the scenario of overshipping: we send the customer an entire package, even though it might be more than + what they initially ordered, and update the quantity on the sales order to reflect what was actually sent. + """ + self.warehouse.delivery_steps = 'ship_only' + package = self.env["stock.quant.package"].create({"name": "Src Pack"}) + self.env['stock.quant']._update_available_quantity(self.productA, self.stock_location, 100, package_id=package) + self.warehouse.out_type_id.show_entire_packs = True + picking = self.env['stock.picking'].create({ + 'location_id': self.stock_location.id, + 'location_dest_id': self.customer_location.id, + 'picking_type_id': self.warehouse.out_type_id.id, + }) + with Form(picking) as picking_form: + with picking_form.move_ids_without_package.new() as move: + move.product_id = self.productA + move.product_uom_qty = 75 + picking.action_confirm() + picking.action_assign() + with Form(picking) as picking_form: + with picking_form.package_level_ids_details.new() as package_level: + package_level.package_id = package + self.assertEqual(len(picking.move_lines), 1, 'Should have only 1 stock move') + self.assertEqual(len(picking.move_lines), 1, 'Should have only 1 stock move') + with Form(picking) as picking_form: + with picking_form.package_level_ids_details.edit(0) as package_level: + package_level.is_done = True + action = picking.button_validate() + print(action) + + self.assertEqual(action, True, 'Should not open wizard') + + for ml in picking.move_line_ids: + self.assertEqual(ml.package_id, package, 'move_line.package') + self.assertEqual(ml.result_package_id, package, 'move_line.result_package') + self.assertEqual(ml.state, 'done', 'move_line.state') + quant = package.quant_ids.filtered(lambda q: q.location_id == self.customer_location) + self.assertEqual(len(quant), 1, 'Should have quant at customer location') + self.assertEqual(quant.reserved_quantity, 0, 'quant.reserved_quantity should = 0') + self.assertEqual(quant.quantity, 100.0, 'quant.quantity should = 100') + self.assertEqual(sum(ml.qty_done for ml in picking.move_line_ids), 100.0, 'total move_line.qty_done should = 100') + backorders = self.env['stock.picking'].search([('backorder_id', '=', picking.id)]) + self.assertEqual(len(backorders), 0, 'Should not create a backorder') + + def test_remove_package(self): + """ + In the overshipping scenario, if I remove the package after adding it, we should not remove the associated + stock move. + """ + self.warehouse.delivery_steps = 'ship_only' + package = self.env["stock.quant.package"].create({"name": "Src Pack"}) + self.env['stock.quant']._update_available_quantity(self.productA, self.stock_location, 100, package_id=package) + self.warehouse.out_type_id.show_entire_packs = True + picking = self.env['stock.picking'].create({ + 'location_id': self.stock_location.id, + 'location_dest_id': self.customer_location.id, + 'picking_type_id': self.warehouse.out_type_id.id, + }) + with Form(picking) as picking_form: + with picking_form.move_ids_without_package.new() as move: + move.product_id = self.productA + move.product_uom_qty = 75 + picking.action_assign() + with Form(picking) as picking_form: + with picking_form.package_level_ids_details.new() as package_level: + package_level.package_id = package + with Form(picking) as picking_form: + picking_form.package_level_ids.remove(0) + self.assertEqual(len(picking.move_lines), 1, 'Should have only 1 stock move') |
