[FIX] purchase/sale: filter on company in package computation
Versions: --------- - 16.0+ Steps to reproduce: ------------------- 1. install stock & purchase; 2. in Inventory settings, enable product packaging; 3. create a packaging for a product; 4. assign the packaging to the current company; 5. switch to a different company; 6. create a purchase order with the product. Issue: ------ The packaging that gets selected automatically shouldn't be accessible to the current company (and isn't in the drop-down). Trying to save the form will return an access error. Similar issues arise in `sale`. Cause: ------726179af78andccdd113260updated product packaging onchange methods to computed methods, changing the environment these fields exist to a `sudo` one. As a consequence, it includes records that are normally inaccessible. Solution: --------- Use the follwing filter before calling `_find_suitable_packaging`: ```python lambda p: p.product_id.company_id <= p.company_id <= line.company_id ``` - if `p.product_id.company_id` is set, `p.company_id` has to be set too; - else `p.company_id` has to be `False` or equal to `line.company_id`. opw-3457782 closes odoo/odoo#140025 X-original-commit: 05f6e91cd9babd8d65e2526a17439a7d81c65fc9 Signed-off-by: William Henrotin (whe) <whe@odoo.com> Signed-off-by: Levi Siuzdak <sile@odoo.com>
This commit is contained in:
@@ -377,9 +377,12 @@ class PurchaseOrderLine(models.Model):
|
||||
# remove packaging if not match the product
|
||||
if line.product_packaging_id.product_id != line.product_id:
|
||||
line.product_packaging_id = False
|
||||
# suggest biggest suitable packaging
|
||||
# suggest biggest suitable packaging matching the PO's company
|
||||
if line.product_id and line.product_qty and line.product_uom:
|
||||
line.product_packaging_id = line.product_id.packaging_ids.filtered('purchase')._find_suitable_product_packaging(line.product_qty, line.product_uom) or line.product_packaging_id
|
||||
suggested_packaging = line.product_id.packaging_ids\
|
||||
.filtered(lambda p: p.purchase and (p.product_id.company_id <= p.company_id <= line.company_id))\
|
||||
._find_suitable_product_packaging(line.product_qty, line.product_uom)
|
||||
line.product_packaging_id = suggested_packaging or line.product_packaging_id
|
||||
|
||||
@api.onchange('product_packaging_id')
|
||||
def _onchange_product_packaging_id(self):
|
||||
|
||||
@@ -174,7 +174,7 @@ class TestPurchase(AccountTestInvoicingCommon):
|
||||
activity.note,
|
||||
)
|
||||
|
||||
def test_onchange_packaging_00(self):
|
||||
def test_compute_packaging_00(self):
|
||||
"""Create a PO and use packaging. Check we suggested suitable packaging
|
||||
according to the product_qty. Also check product_qty or product_packaging
|
||||
are correctly calculated when one of them changed.
|
||||
@@ -219,7 +219,7 @@ class TestPurchase(AccountTestInvoicingCommon):
|
||||
self.assertEqual(po.order_line.product_qty, 12)
|
||||
|
||||
# Do the same test but without form, to check the `product_packaging_id` and `product_packaging_qty` are set
|
||||
# without manual call to onchanges
|
||||
# without manual call to compute
|
||||
po = self.env['purchase.order'].create({
|
||||
'partner_id': self.partner_a.id,
|
||||
'order_line': [
|
||||
@@ -237,6 +237,43 @@ class TestPurchase(AccountTestInvoicingCommon):
|
||||
po.order_line.product_packaging_qty = 1.0
|
||||
self.assertEqual(po.order_line.product_qty, 12)
|
||||
|
||||
def test_compute_packaging_01(self):
|
||||
"""Create a PO and use packaging in a multicompany environment.
|
||||
Ensure any suggested packaging matches the PO's.
|
||||
"""
|
||||
company1 = self.company_data['company']
|
||||
company2 = self.company_data_2['company']
|
||||
generic_single_pack = self.env['product.packaging'].create({
|
||||
'name': "single pack",
|
||||
'product_id': self.product_a.id,
|
||||
'qty': 1.0,
|
||||
'company_id': False,
|
||||
})
|
||||
company2_pack_of_10 = self.env['product.packaging'].create({
|
||||
'name': "pack of 10 by Company 2",
|
||||
'product_id': self.product_a.id,
|
||||
'qty': 10.0,
|
||||
'company_id': company2.id,
|
||||
})
|
||||
|
||||
po1 = self.env['purchase.order'].with_company(company1).create({
|
||||
'partner_id': self.partner_a.id,
|
||||
'order_line': [
|
||||
Command.create({'product_id': self.product_a.id, 'product_qty': 10.0}),
|
||||
]
|
||||
})
|
||||
self.assertEqual(po1.order_line.product_packaging_id, generic_single_pack)
|
||||
self.assertEqual(po1.order_line.product_packaging_qty, 10.0)
|
||||
|
||||
# verify that with the right company, we can get the other packaging
|
||||
po2 = self.env['purchase.order'].with_company(company2).create({
|
||||
'partner_id': self.partner_a.id,
|
||||
'order_line': [
|
||||
Command.create({'product_id': self.product_a.id, 'product_qty': 10.0}),
|
||||
]
|
||||
})
|
||||
self.assertEqual(po2.order_line.product_packaging_id, company2_pack_of_10)
|
||||
self.assertEqual(po2.order_line.product_packaging_qty, 1.0)
|
||||
|
||||
def test_with_different_uom(self):
|
||||
""" This test ensures that the unit price is correctly computed"""
|
||||
|
||||
@@ -642,10 +642,12 @@ class SaleOrderLine(models.Model):
|
||||
# remove packaging if not match the product
|
||||
if line.product_packaging_id.product_id != line.product_id:
|
||||
line.product_packaging_id = False
|
||||
# Find biggest suitable packaging
|
||||
# suggest biggest suitable packaging matching the SO's company
|
||||
if line.product_id and line.product_uom_qty and line.product_uom:
|
||||
line.product_packaging_id = line.product_id.packaging_ids.filtered(
|
||||
'sales')._find_suitable_product_packaging(line.product_uom_qty, line.product_uom) or line.product_packaging_id
|
||||
suggested_packaging = line.product_id.packaging_ids\
|
||||
.filtered(lambda p: p.sales and (p.product_id.company_id <= p.company_id <= line.company_id))\
|
||||
._find_suitable_product_packaging(line.product_uom_qty, line.product_uom)
|
||||
line.product_packaging_id = suggested_packaging or line.product_packaging_id
|
||||
|
||||
@api.depends('product_packaging_id', 'product_uom', 'product_uom_qty')
|
||||
def _compute_product_packaging_qty(self):
|
||||
|
||||
@@ -172,7 +172,7 @@ class TestSaleOrder(SaleCommon):
|
||||
with self.assertRaises(UserError):
|
||||
self.sale_order.unlink()
|
||||
|
||||
def test_onchange_packaging_00(self):
|
||||
def test_compute_packaging_00(self):
|
||||
"""Create a SO and use packaging. Check we suggested suitable packaging
|
||||
according to the product_qty. Also check product_qty or product_packaging
|
||||
are correctly calculated when one of them changed.
|
||||
@@ -253,6 +253,44 @@ class TestSaleOrder(SaleCommon):
|
||||
self.assertEqual(so2.order_line.product_packaging_qty, 1)
|
||||
self.assertEqual(so2.order_line.product_packaging_id.id, packaging_pack_of_20.id)
|
||||
|
||||
def test_compute_packaging_01(self):
|
||||
"""Create a SO and use packaging in a multicompany environment.
|
||||
Ensure any suggested packaging matches the SO's.
|
||||
"""
|
||||
company2 = self.env['res.company'].create([{'name': 'Company 2'}])
|
||||
generic_single_pack = self.env['product.packaging'].create({
|
||||
'name': "single pack",
|
||||
'product_id': self.product.id,
|
||||
'qty': 1.0,
|
||||
'company_id': False,
|
||||
})
|
||||
company2_pack_of_10 = self.env['product.packaging'].create({
|
||||
'name': "pack of 10 by Company 2",
|
||||
'product_id': self.product.id,
|
||||
'qty': 10.0,
|
||||
'company_id': company2.id,
|
||||
})
|
||||
|
||||
so1 = self.empty_order
|
||||
so1_form = Form(so1)
|
||||
with so1_form.order_line.new() as line:
|
||||
line.product_id = self.product
|
||||
line.product_uom_qty = 10.0
|
||||
so1_form.save()
|
||||
self.assertEqual(so1.order_line.product_packaging_id, generic_single_pack)
|
||||
self.assertEqual(so1.order_line.product_packaging_qty, 10.0)
|
||||
|
||||
so2 = self.env['sale.order'].with_company(company2).create({
|
||||
'partner_id': self.partner.id,
|
||||
})
|
||||
so2_form = Form(so2)
|
||||
with so2_form.order_line.new() as line:
|
||||
line.product_id = self.product
|
||||
line.product_uom_qty = 10.0
|
||||
so2_form.save()
|
||||
self.assertEqual(so2.order_line.product_packaging_id, company2_pack_of_10)
|
||||
self.assertEqual(so2.order_line.product_packaging_qty, 1.0)
|
||||
|
||||
def _create_sale_order(self):
|
||||
"""Create dummy sale order (without lines)"""
|
||||
return self.env['sale.order'].with_context(
|
||||
|
||||
Reference in New Issue
Block a user