Skip to content

Commit 00c9ed5

Browse files
committed
[ADD] product_barcode_sequence: generate product barcodes (fixup)
1 parent 1697bc0 commit 00c9ed5

File tree

4 files changed

+99
-58
lines changed

4 files changed

+99
-58
lines changed

product_barcode_sequence/__manifest__.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,10 @@
99
"license": "AGPL-3",
1010
"category": "Product",
1111
"summary": "Automatically assign EAN barcodes to products by category",
12-
"depends": ["product"],
12+
"depends": ["product", "stock"],
1313
"data": [
1414
"views/product_category.xml",
1515
"data/ir_actions_server.xml",
1616
],
1717
"installable": True,
18-
"auto_install": False,
19-
"application": False,
20-
"demo": [],
2118
}

product_barcode_sequence/models/product_category.py

Lines changed: 38 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,23 @@ class ProductCategory(models.Model):
1111
barcode_prefix = fields.Char(
1212
help="Prefix used to generate EAN barcodes for products "
1313
"created with this category. Should be 1-12 digits.",
14+
compute="_compute_barcode_fields",
15+
inverse="_inverse_barcode_prefix",
1416
size=12,
17+
store=True,
1518
)
1619
barcode_sequence_id = fields.Many2one(
1720
comodel_name="ir.sequence",
1821
help="Sequence used to generate unique barcode numbers for products "
1922
"in this category.",
23+
compute="_compute_barcode_fields",
24+
inverse="_inverse_barcode_sequence_prefix",
25+
store=True,
2026
copy=False,
21-
readonly=True,
2227
)
2328
auto_generate_barcode = fields.Boolean(
2429
help="Automatically generate EAN barcode when creating products "
2530
"in this category.",
26-
default=False,
2731
)
2832

2933
@api.constrains("barcode_prefix")
@@ -37,51 +41,45 @@ def _check_barcode_prefix(self):
3741
if len(category.barcode_prefix) == 0:
3842
raise UserError(_("Barcode prefix cannot be empty"))
3943

44+
@api.depends("barcode_sequence_id.prefix")
45+
def _compute_barcode_fields(self):
46+
"""Compute barcode_prefix from barcode_sequence_id and vice versa."""
47+
for category in self:
48+
category.barcode_prefix = category.barcode_sequence_id.prefix
49+
50+
@api.depends("barcode_prefix")
51+
def _inverse_barcode_prefix(self):
52+
"""When barcode_prefix is set, find or create corresponding sequence."""
53+
for category in self:
54+
if category.barcode_prefix:
55+
category.barcode_sequence_id = (
56+
category._create_or_update_barcode_sequence()
57+
)
58+
59+
def _create_or_update_barcode_sequence(self):
60+
if not self.prefix:
61+
return False
62+
existing_sequence = self.env["ir.sequence"].search(
63+
[
64+
("prefix", "=", self.prefix),
65+
("code", "like", "product.barcode"),
66+
("company_id", "=", False),
67+
],
68+
limit=1,
69+
)
70+
if existing_sequence:
71+
return existing_sequence
72+
else:
73+
seq_vals = self._prepare_barcode_sequence(self.prefix)
74+
return self.env["ir.sequence"].create(seq_vals)
75+
4076
@api.model
4177
def _prepare_barcode_sequence(self, prefix):
42-
"""Prepare the vals for creating the barcode sequence
43-
:param prefix: a string with the prefix of the barcode.
44-
:return: a dict with the values.
45-
"""
4678
vals = {
4779
"name": "Barcode " + prefix,
4880
"code": "product.barcode - " + prefix,
4981
"padding": 12 - len(prefix), # Total 13 digits for EAN-13
5082
"prefix": prefix,
5183
"company_id": False,
52-
"implementation": "no_gap",
5384
}
5485
return vals
55-
56-
def _create_or_update_barcode_sequence(self, prefix):
57-
"""Create or update barcode sequence for given prefix"""
58-
seq_vals = self._prepare_barcode_sequence(prefix)
59-
if self.barcode_sequence_id:
60-
self.sudo().barcode_sequence_id.write(
61-
{
62-
"prefix": prefix,
63-
"padding": 12 - len(prefix),
64-
}
65-
)
66-
else:
67-
self.barcode_sequence_id = self.env["ir.sequence"].create(seq_vals)
68-
69-
def write(self, vals):
70-
prefix = vals.get("barcode_prefix", False)
71-
if prefix:
72-
for rec in self:
73-
rec._create_or_update_barcode_sequence(prefix)
74-
return super().write(vals)
75-
76-
@api.model_create_multi
77-
def create(self, vals_list):
78-
vals_list_updated = []
79-
for vals in vals_list:
80-
prefix = vals.get("barcode_prefix", False)
81-
if prefix:
82-
seq_vals = self._prepare_barcode_sequence(prefix)
83-
sequence = self.env["ir.sequence"].create(seq_vals)
84-
vals_list_updated.append(dict(vals, barcode_sequence_id=sequence.id))
85-
else:
86-
vals_list_updated.append(vals)
87-
return super().create(vals_list_updated)

product_barcode_sequence/tests/test_product_barcode_sequence.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,3 +331,56 @@ def test_workflow_10_force_barcode_regeneration(self):
331331
self.assertIn(
332332
product, generated_products, "Product should be in generated products"
333333
)
334+
335+
def test_workflow_11_bidirectional_sync_sequence_to_prefix(self):
336+
"""Workflow: Barcode prefix updates when sequence is set."""
337+
# Step 1: Create category without prefix
338+
category = self._create_category_with_barcode_config("Sync Test", "", False)
339+
340+
# Step 2: Create a sequence manually
341+
sequence = self.env["ir.sequence"].create(
342+
{
343+
"name": "Test Sequence",
344+
"code": "product.barcode - 333333",
345+
"prefix": "333333",
346+
"padding": 6,
347+
"company_id": False,
348+
}
349+
)
350+
351+
# Step 3: Set the sequence on category
352+
category.write({"barcode_sequence_id": sequence.id})
353+
354+
# Step 4: Verify prefix was updated from sequence
355+
self.assertEqual(category.barcode_prefix, "333333")
356+
self.assertEqual(category.barcode_sequence_id, sequence)
357+
358+
def test_workflow_12_sequence_reuse_across_categories(self):
359+
"""Workflow: Multiple categories reuse the same sequence when prefix matches."""
360+
# Step 1: Create first category with prefix
361+
category1 = self._create_category_with_barcode_config("Category 1", "444444")
362+
sequence1_id = category1.barcode_sequence_id.id
363+
364+
# Step 2: Create second category with same prefix
365+
category2 = self._create_category_with_barcode_config("Category 2", "444444")
366+
367+
# Step 3: Verify same sequence is reused
368+
self.assertEqual(
369+
category1.barcode_sequence_id.id,
370+
category2.barcode_sequence_id.id,
371+
"Same sequence should be reused for same prefix",
372+
)
373+
self.assertEqual(sequence1_id, category2.barcode_sequence_id.id)
374+
375+
def test_workflow_13_clear_barcode_configuration(self):
376+
"""Workflow: Clear barcode prefix and sequence."""
377+
# Step 1: Create category with barcode configuration
378+
category = self._create_category_with_barcode_config("Clear Test", "555555")
379+
self.assertTrue(category.barcode_sequence_id, "Sequence should exist")
380+
381+
# Step 2: Clear the prefix
382+
category.write({"barcode_prefix": ""})
383+
384+
# Step 3: Verify both fields are cleared
385+
self.assertFalse(category.barcode_prefix, "Prefix should be cleared")
386+
self.assertFalse(category.barcode_sequence_id, "Sequence should be cleared")

product_barcode_sequence/views/product_category.xml

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,15 @@
44
<record id="product_category_form_view" model="ir.ui.view">
55
<field name="name">product.category.form - product_barcode_sequence</field>
66
<field name="model">product.category</field>
7-
<field name="inherit_id" ref="product.product_category_form_view" />
7+
<field name="inherit_id" ref="stock.product_category_form_view_inherit" />
88
<field name="arch" type="xml">
9-
<field name="parent_id" position="after">
10-
<group string="Barcode Configuration">
9+
10+
<group name="logistics" position="inside">
1111
<field name="auto_generate_barcode" />
12-
<label for="barcode_prefix" />
13-
<div>
14-
<field
15-
name="barcode_prefix"
16-
style="padding-right: 1.0em"
17-
class="oe_inline"
18-
/>
19-
<field name="barcode_sequence_id" groups="base.group_no_one" />
20-
</div>
21-
</group>
22-
</field>
12+
<field name="barcode_prefix" />
13+
<field name="barcode_sequence_id" groups="base.group_no_one" />
14+
</group>
15+
2316
</field>
2417
</record>
2518
</odoo>

0 commit comments

Comments
 (0)