[FIX] purchase_stock: on_time_rate in multi companies
Because of the weird behavior of the ORM regarding related field pointing to computed fields, `on_time_rate` was taking account of all companies when asking it through `purchase.order.on_time_rate` but it was single-company when asked through `res.partner.on_time_rate`. It was observed during an upgrade request issue analysis, and in that case the problem was not even the fact it was miscomputed, but the fact this computed field, computed as sudo, leaded to fill the cache for `purchase.order.line.move_ids` with `stock.move` belonging to other companies than the user one, and then when attempting to read values from these stock moves a multi-company access error was raised. upg-8106 closes odoo/odoo#66882 X-original-commit: 1b729171c5deac4fadebcff013aa061a96860b09 Signed-off-by: Denis Ledoux (dle) <dle@odoo.com>
This commit is contained in:
@@ -29,7 +29,7 @@ class PurchaseOrder(models.Model):
|
||||
is_shipped = fields.Boolean(compute="_compute_is_shipped")
|
||||
effective_date = fields.Datetime("Effective Date", compute='_compute_effective_date', store=True, copy=False,
|
||||
help="Completion date of the first receipt order.")
|
||||
on_time_rate = fields.Float(related='partner_id.on_time_rate')
|
||||
on_time_rate = fields.Float(related='partner_id.on_time_rate', compute_sudo=False)
|
||||
|
||||
@api.depends('order_line.move_ids.picking_id')
|
||||
def _compute_picking(self):
|
||||
|
||||
@@ -263,3 +263,68 @@ class TestPurchaseOrder(ValuationReconciliationTestCommon):
|
||||
self.assertEqual(po.company_id, company_a)
|
||||
self.assertEqual(po.picking_type_id.warehouse_id.company_id, company_a)
|
||||
self.assertEqual(po.currency_id, po.company_id.currency_id)
|
||||
|
||||
def test_06_on_time_rate(self):
|
||||
company_a = self.env.user.company_id
|
||||
company_b = self.env['res.company'].create({
|
||||
"name": "Test Company",
|
||||
"currency_id": self.env['res.currency'].with_context(active_test=False).search([
|
||||
('id', '!=', company_a.currency_id.id),
|
||||
], limit=1).id
|
||||
})
|
||||
|
||||
# Create a purchase order with 90% qty received for company A
|
||||
self.env.user.write({
|
||||
'company_id': company_a.id,
|
||||
'company_ids': [(6, 0, [company_a.id])],
|
||||
})
|
||||
po = self.env['purchase.order'].create(self.po_vals)
|
||||
po.order_line.write({'product_qty': 10})
|
||||
po.button_confirm()
|
||||
picking = po.picking_ids[0]
|
||||
# Process 9.0 out of the 10.0 ordered qty
|
||||
picking.move_line_ids.write({'qty_done': 9.0})
|
||||
res_dict = picking.button_validate()
|
||||
# No backorder
|
||||
self.env['stock.backorder.confirmation'].with_context(res_dict['context']).process_cancel_backorder()
|
||||
# `on_time_rate` should be equals to the ratio of quantity received against quantity ordered
|
||||
expected_rate = sum(picking.move_line_ids.mapped("qty_done")) / sum(po.order_line.mapped("product_qty")) * 100
|
||||
self.assertEqual(expected_rate, po.on_time_rate)
|
||||
|
||||
# Create a purchase order with 80% qty received for company B
|
||||
# The On-Time Delivery Rate shouldn't be shared accross multiple companies
|
||||
self.env.user.write({
|
||||
'company_id': company_b.id,
|
||||
'company_ids': [(6, 0, [company_b.id])],
|
||||
})
|
||||
po = self.env['purchase.order'].create(self.po_vals)
|
||||
po.order_line.write({'product_qty': 10})
|
||||
po.button_confirm()
|
||||
picking = po.picking_ids[0]
|
||||
# Process 8.0 out of the 10.0 ordered qty
|
||||
picking.move_line_ids.write({'qty_done': 8.0})
|
||||
res_dict = picking.button_validate()
|
||||
# No backorder
|
||||
self.env['stock.backorder.confirmation'].with_context(res_dict['context']).process_cancel_backorder()
|
||||
# `on_time_rate` should be equal to the ratio of quantity received against quantity ordered
|
||||
expected_rate = sum(picking.move_line_ids.mapped("qty_done")) / sum(po.order_line.mapped("product_qty")) * 100
|
||||
self.assertEqual(expected_rate, po.on_time_rate)
|
||||
|
||||
# Tricky corner case
|
||||
# As `purchase.order.on_time_rate` is a related to `partner_id.on_time_rate`
|
||||
# `on_time_rate` on the PO should equals `on_time_rate` on the partner.
|
||||
# Related fields are by default computed as sudo
|
||||
# while non-stored computed fields are not computed as sudo by default
|
||||
# If the computation of the related field (`purchase.order.on_time_rate`) was asked
|
||||
# and `res.partner.on_time_rate` was not yet in the cache
|
||||
# the `sudo` requested for the computation of the related `purchase.order.on_time_rate`
|
||||
# was propagated to the computation of `res.partner.on_time_rate`
|
||||
# and therefore the multi-company record rules were ignored.
|
||||
# 1. Compute `res.partner.on_time_rate` regular non-stored comptued field
|
||||
partner_on_time_rate = po.partner_id.on_time_rate
|
||||
# 2. Invalidate the cache for that record and field, so it's not reused in the next step.
|
||||
po.partner_id.invalidate_cache(fnames=["on_time_rate"], ids=po.partner_id.ids)
|
||||
# 3. Compute the related field `purchase.order.on_time_rate`
|
||||
po_on_time_rate = po.on_time_rate
|
||||
# 4. Check both are equals.
|
||||
self.assertEqual(partner_on_time_rate, po_on_time_rate)
|
||||
|
||||
Reference in New Issue
Block a user