[FIX] stock: prevent opening SM when picking is dirty

Steps to reproduce
==================

- Create two new storable products tracked by USN
- Create a new RFQ with one of the created product
- Confirm the order
- Open the receipt
- Add a new line with the other product
- Click on the open move button in the new line
- Add a new SN
- Save & close

=> Cannot read properties of undefined (reading 'resId')

Cause of the issue
==================

When calling openRecord, if the record is dirty, it is saved before
proceeding.

After saving, we call super.openRecord with the old record.
Since that record is no longer linked to the root record (the
stock.picking), when we try to save it, it won't match an existing id.

Solution
========

If the record is new, we don't save as there would be no way of knowing
which of the returned line would come from this one.

If we are opening an existing record, we find the new datapoint by
matching it's ID.

opw-3777615

closes odoo/odoo#162425

Signed-off-by: William Henrotin (whe) <whe@odoo.com>
This commit is contained in:
Hubert Van De Walle
2024-04-19 11:09:27 +00:00
parent a17d30e5db
commit f7e6523bd7
3 changed files with 118 additions and 1 deletions
@@ -46,10 +46,14 @@ export class StockMoveX2ManyField extends X2ManyField {
async openRecord(record) {
if (this.canOpenRecord) {
if (this.canOpenRecord && !record.isNew) {
const dirty = await record.isDirty();
if (dirty && 'quantity' in record._changes) {
await record.model.root.save({ reload: true });
record = record.model.root.data[this.props.name].records.find(e => e.resId === record.resId);
if (!record) {
return;
}
}
}
return super.openRecord(record);
@@ -104,3 +104,55 @@ registry.category("web_tour.tours").add('test_generate_serial_1', { test: true,
isCheck: true,
},
]});
registry.category("web_tour.tours").add('test_add_new_line', {
test: true,
steps: () => [
{
extra_trigger: '.o_form_editable',
trigger: '.o_field_x2many_list_row_add > a'
},
{
trigger: ".o_field_widget[name=product_id] input",
run: 'text two',
},
{ trigger: ".ui-menu-item > a:contains('Product two')" },
{ trigger: ".fa-list:eq(1)" },
{ trigger: "h4:contains('Stock move')" },
{ trigger: '.o_field_x2many_list_row_add > a' },
{
trigger: ".o_field_widget[name=lot_name] input",
run: 'text two',
},
{ trigger: ".o_form_view.modal-content .o_form_button_save" },
{ trigger: ".o_form_view:not(.modal-content) .o_form_button_save" },
{
trigger: ".o_form_renderer.o_form_saved",
isCheck: true,
},
]
});
registry.category("web_tour.tours").add('test_edit_existing_line', {
test: true,
steps: () => [
{ trigger: ".o_data_cell[name=quantity]" },
{
trigger: ".o_field_widget[name=quantity] input",
run: 'text 2',
},
{ trigger: ".fa-list" },
{ trigger: "h4:contains('Stock move')" },
{ trigger: ".o_data_cell[name=quantity]:eq(1)" },
{
trigger: ".o_field_widget[name=lot_name] input",
run: 'text two',
},
{ trigger: ".o_form_view.modal-content .o_form_button_save" },
{ trigger: ".o_form_view:not(.modal-content) .o_form_button_save" },
{
trigger: ".o_form_renderer.o_form_saved",
isCheck: true,
},
]
});
+61
View File
@@ -1,4 +1,5 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import Command
from odoo.tests import HttpCase, tagged
@@ -57,3 +58,63 @@ class TestStockPickingTour(HttpCase):
('product_id', '=', product_serial.id),
])
self.assertEqual(len(serial), 5)
def test_add_new_line(self):
product_one, _ = self.env["product.product"].create([
{
'name': 'Product one',
'type': 'product',
'tracking': 'serial',
},
{
'name': 'Product two',
'type': 'product',
'tracking': 'serial',
}
])
self.receipt.write({
"move_ids": [Command.create({
"name": "test",
"product_uom_qty": 1,
"product_id": product_one.id,
"location_id": self.receipt.location_id.id,
"location_dest_id": self.receipt.location_dest_id.id,
})]
})
self.receipt.action_confirm()
self.receipt.move_ids.move_line_ids.lot_name = "one"
url = self._get_picking_url(self.receipt.id)
self.start_tour(url, 'test_add_new_line', login='admin', timeout=60)
names = self.receipt.move_ids.move_line_ids.mapped('lot_name')
self.assertEqual(names, ["two", "one"])
def test_edit_existing_line(self):
self.uom_unit = self.env.ref('uom.product_uom_unit')
product_one = self.env['product.product'].create({
'name': 'Product one',
'type': 'product',
'tracking': 'serial',
})
self.receipt.write({
"move_ids": [Command.create({
"name": "test",
"product_id": product_one.id,
"location_id": self.receipt.location_id.id,
"location_dest_id": self.receipt.location_dest_id.id,
"product_uom_qty": 1,
})]
})
self.receipt.action_confirm()
self.receipt.move_ids.move_line_ids.lot_name = "one"
url = self._get_picking_url(self.receipt.id)
self.start_tour(url, 'test_edit_existing_line', login='admin', timeout=60)
names = self.receipt.move_ids.move_line_ids.mapped('lot_name')
self.assertEqual(names, ["one", "two"])