Merge pull request #1 from TDu/14-procurement_create_group_by_product-add-lock

[IMP] proc_auto_create_grp_by_product: add lock
This commit is contained in:
Jacques-Etienne Baudoux
2024-04-05 17:38:49 +02:00
committed by GitHub
2 changed files with 52 additions and 0 deletions

View File

@@ -1,7 +1,11 @@
# Copyright 2023 Jacques-Etienne Baudoux (BCIM) <je@bcim.be>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
import hashlib
import struct
from odoo import fields, models
from odoo.exceptions import UserError
class StockRule(models.Model):
@@ -13,6 +17,21 @@ class StockRule(models.Model):
if self.auto_create_group_by_product:
if product.auto_create_procurement_group_ids:
return fields.first(product.auto_create_procurement_group_ids)
else:
# Make sure that two transactions can not create a procurement group
# For the same product at the same time.
lock_name = f"product.product,{product.id}-auto-proc-group"
hasher = hashlib.sha1(str(lock_name).encode())
int_lock = struct.unpack("q", hasher.digest()[:8])
self.env.cr.execute(
"SELECT pg_try_advisory_xact_lock(%s);", (int_lock,)
)
lock_acquired = self.env.cr.fetchone()[0]
if not lock_acquired:
raise UserError(
f"The auto procurement group for product {product.name} "
"is already being created by someone else."
)
return super()._get_auto_procurement_group(product)
def _prepare_auto_procurement_group_data(self, product):

View File

@@ -1,6 +1,9 @@
# Copyright 2023 Jacques-Etienne Baudoux (BCIM) <je@bcim.be>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo import api, registry
from odoo.exceptions import UserError
from odoo.addons.procurement_auto_create_group.tests.test_auto_create import (
TestProcurementAutoCreateGroup,
)
@@ -108,3 +111,33 @@ class TestProcurementAutoCreateGroupByProduct(TestProcurementAutoCreateGroup):
self.prod_auto_push,
"Procurement Group product missing.",
)
def test_concurrent_procurement_group_creation(self):
"""Check for the same product, no multiple procurement groups are created."""
rule = self.pull_push_rule_auto
rule.auto_create_group_by_product = True
product = self.prod_auto_pull_push
# Check that no procurement group exist for the product
self.assertFalse(product.auto_create_procurement_group_ids)
# So create one and an adisory lock will be created
rule._get_auto_procurement_group(product)
self.assertTrue(product.auto_create_procurement_group_ids)
# Use another transaction to test the advisory lock
with registry(self.env.cr.dbname).cursor() as new_cr:
new_env = api.Environment(new_cr, self.env.uid, self.env.context)
new_env["product.product"].invalidate_cache(
["auto_create_procurement_group_ids"],
[
product.id,
],
)
rule2 = new_env["stock.rule"].browse(rule.id)
rule2.auto_create_group_by_product = True
product2 = new_env["product.product"].browse(product.id)
self.assertFalse(product2.auto_create_procurement_group_ids)
exception_msg = (
f"The auto procurement group for product {product2.name} "
"is already being created by someone else."
)
with self.assertRaisesRegex(UserError, exception_msg):
rule2._get_auto_procurement_group(product2)