Skip to content

Commit

Permalink
[RFR] l10n_eu_product_adr: allow different adr goods per variant
Browse files Browse the repository at this point in the history
  • Loading branch information
StefanRijnhart committed Nov 16, 2021
1 parent 60a4ffb commit ee881f5
Show file tree
Hide file tree
Showing 10 changed files with 371 additions and 17 deletions.
28 changes: 28 additions & 0 deletions l10n_eu_product_adr/14.0.1.1.0/pre-migration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Copyright 2021 Opener B.V.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
try:
from openupgradelib import openupgrade
except ImportError:
openupgrade = None


def migrate(cr, version):
"""Move adr data from product_template to product_product"""
if (
not openupgrade
or not openupgrade.column_exists(cr, "product_template", "adr_goods_id")
or openupgrade.column_exists(cr, "product_product", "adr_goods_id")
):
return
cr.execute(
"""
ALTER TABLE product_product
ADD COLUMN adr_goods_id INTEGER,
ADD COLUMN is_dangerous BOOLEAN;
UPDATE product_product pp
SET adr_goods_id = pt.adr_goods_id,
is_dangerous = pt.is_dangerous
FROM product_template pt
WHERE pt.id = pp.product_tmpl_id
"""
)
4 changes: 3 additions & 1 deletion l10n_eu_product_adr/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
{
"name": "ADR Dangerous Goods",
"summary": "Allows to set appropriate danger class and components",
"version": "14.0.1.0.0",
"version": "14.0.1.1.0",
"category": "Inventory/Delivery",
"website": "https://github.com/OCA/community-data-files",
"author": "Opener B.V., Camptocamp, Odoo Community Association (OCA)",
Expand All @@ -24,7 +24,9 @@
"views/adr_label_views.xml",
"views/adr_packing_instruction_views.xml",
"views/menu.xml",
# NB. product template views need to come before product product views
"views/product_template_views.xml",
"views/product_product_views.xml",
"views/stock_picking_views.xml",
"security/ir.model.access.csv",
],
Expand Down
28 changes: 28 additions & 0 deletions l10n_eu_product_adr/migrations/14.0.1.1.0/pre-migration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Copyright 2021 Opener B.V.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
try:
from openupgradelib import openupgrade
except ImportError:
openupgrade = None


def migrate(cr, version):
"""Move adr data from product_template to product_product"""
if (
not openupgrade
or not openupgrade.column_exists(cr, "product_template", "adr_goods_id")
or openupgrade.column_exists(cr, "product_product", "adr_goods_id")
):
return
cr.execute(
"""
ALTER TABLE product_product
ADD COLUMN adr_goods_id INTEGER,
ADD COLUMN is_dangerous BOOLEAN;
UPDATE product_product pp
SET adr_goods_id = pt.adr_goods_id,
is_dangerous = pt.is_dangerous
FROM product_template pt
WHERE pt.id = pp.product_tmpl_id
"""
)
1 change: 1 addition & 0 deletions l10n_eu_product_adr/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from . import adr_label
from . import adr_packing_instruction
from . import common
from . import product_product
from . import product_template
from . import stock_move
from . import stock_picking
66 changes: 66 additions & 0 deletions l10n_eu_product_adr/models/product_product.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Copyright 2019 Iryna Vyshnevska (Camptocamp)
# Copyright 2021 Opener B.V. <stefan@opener.amsterdam>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
from odoo import api, fields, models


class ProductProduct(models.Model):
_inherit = "product.product"

is_dangerous = fields.Boolean(help="This product belongs to a dangerous class")
adr_goods_id = fields.Many2one("adr.goods", "Dangerous Goods")
adr_class_id = fields.Many2one(
"adr.class", related="adr_goods_id.class_id", readonly=True
)
adr_classification_code = fields.Char(
related="adr_goods_id.classification_code", readonly=True
)
adr_label_ids = fields.Many2many(
"adr.label", related="adr_goods_id.label_ids", readonly=True
)
adr_limited_quantity = fields.Float(
related="adr_goods_id.limited_quantity",
readonly=True,
)
adr_limited_quantity_uom_id = fields.Many2one(
related="adr_goods_id.limited_quantity_uom_id",
readonly=True,
)
adr_packing_instruction_ids = fields.Many2many(
"adr.packing.instruction",
related="adr_goods_id.packing_instruction_ids",
readonly=True,
)
adr_transport_category = fields.Selection(
related="adr_goods_id.transport_category", readonly=True
)
adr_tunnel_restriction_code = fields.Selection(
related="adr_goods_id.tunnel_restriction_code", readonly=True
)

@api.onchange("is_dangerous")
def onchange_is_dangerous(self):
"""Remove the dangerous goods attribute from the product
(when is_dangerous is deselected)
"""
if not self.is_dangerous and self.adr_goods_id:
self.adr_goods_id = False

@api.model_create_multi
def create(self, vals_list):
"""Propagate the template's adr settings when creating variants"""
for vals in vals_list:
if (
"product_tmpl_id" in vals
and "adr_goods_id" not in vals
and "is_dangerous" not in vals
):
template = self.env["product.template"].browse(vals["product_tmpl_id"])
vals.update(
{
"adr_goods_id": template.adr_goods_id.id,
"is_dangerous": template.is_dangerous,
}
)
return super().create(vals_list)
76 changes: 66 additions & 10 deletions l10n_eu_product_adr/models/product_template.py
Original file line number Diff line number Diff line change
@@ -1,43 +1,86 @@
# Copyright 2019 Iryna Vyshnevska (Camptocamp)
# Copyright 2021 Opener B.V. <stefan@opener.amsterdam>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
from odoo import api, fields, models
from odoo import _, api, fields, models
from odoo.exceptions import UserError


class ProductTemplate(models.Model):
_inherit = "product.template"

is_dangerous = fields.Boolean(help="This product belongs to a dangerous class")
adr_goods_id = fields.Many2one("adr.goods", "Dangerous Goods")
adr_goods_on_variants = fields.Boolean(
compute="_compute_adr_goods_on_variants",
help="Indicates whether the adr configuration is different for each variant.",
)
is_dangerous = fields.Boolean(
related="product_variant_ids.is_dangerous",
help="This product belongs to a dangerous class",
)
adr_goods_id = fields.Many2one(
"adr.goods", "Dangerous Goods", related="product_variant_ids.adr_goods_id"
)
adr_class_id = fields.Many2one(
"adr.class", related="adr_goods_id.class_id", readonly=True
"adr.class", related="product_variant_ids.adr_goods_id.class_id", readonly=True
)
adr_classification_code = fields.Char(
related="adr_goods_id.classification_code", readonly=True
)
adr_label_ids = fields.Many2many(
"adr.label", related="adr_goods_id.label_ids", readonly=True
"adr.label", related="product_variant_ids.adr_goods_id.label_ids", readonly=True
)
adr_limited_quantity = fields.Float(
related="adr_goods_id.limited_quantity",
related="product_variant_ids.adr_goods_id.limited_quantity",
readonly=True,
)
adr_limited_quantity_uom_id = fields.Many2one(
related="adr_goods_id.limited_quantity_uom_id",
related="product_variant_ids.adr_goods_id.limited_quantity_uom_id",
readonly=True,
)
adr_packing_instruction_ids = fields.Many2many(
"adr.packing.instruction",
related="adr_goods_id.packing_instruction_ids",
related="product_variant_ids.adr_goods_id.packing_instruction_ids",
readonly=True,
)
adr_transport_category = fields.Selection(
related="adr_goods_id.transport_category", readonly=True
related="product_variant_ids.adr_goods_id.transport_category", readonly=True
)
adr_tunnel_restriction_code = fields.Selection(
related="adr_goods_id.tunnel_restriction_code", readonly=True
related="product_variant_ids.adr_goods_id.tunnel_restriction_code",
readonly=True,
)

@api.depends("product_variant_ids.adr_goods_id")
def _compute_adr_goods_on_variants(self):
for template in self:
template.adr_goods_on_variants = not all(
product.adr_goods_id == (template.product_variant_ids[0].adr_goods_id)
for product in template.product_variant_ids[1:]
)

def write(self, values):
"""Delegate dangerous goods fields to variants
while preventing a sweeping change over variants with different settings
"""
values = values.copy()
variant_vals = {}
for field in ("is_dangerous", "adr_goods_id"):
if field in values:
variant_vals[field] = values.pop(field)
res = super().write(values)
if variant_vals:
if any(template.adr_goods_on_variants for template in self):
raise UserError(
_(
"There are different dangerous goods configured on "
"this product's variant, so you cannot update the "
"dangerous goods from here. Please reconfigure each "
"variant separately."
)
)
self.mapped("product_variant_ids").write(variant_vals)
return res

@api.onchange("is_dangerous")
def onchange_is_dangerous(self):
"""Remove the dangerous goods attribute from the product
Expand All @@ -46,3 +89,16 @@ def onchange_is_dangerous(self):
"""
if not self.is_dangerous and self.adr_goods_id:
self.adr_goods_id = False

@api.model_create_multi
def create(self, vals_list):
"""Propagate the template's adr settings on the created variants"""
res = super().create(vals_list)
for template, vals in zip(res, vals_list):
variant_vals = {}
for field in ("is_dangerous", "adr_goods_id"):
if field in vals:
variant_vals[field] = vals[field]
if variant_vals:
template.product_variant_ids.write(variant_vals)
return res
2 changes: 2 additions & 0 deletions l10n_eu_product_adr/readme/CONFIGURE.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ goods that applies to this particular product, out of a list of dangerous
goods that represents Table A from chapter 3 of the ADR specifications
document.

It is possible to specify a different, or no goods per product variant.

The data in this module is generated based on a spreadsheet from
https://cepa.be. This spreadsheet, just like the specifications themselves
sometimes contain multiple options for each dangerous good, or additional
Expand Down
Loading

0 comments on commit ee881f5

Please sign in to comment.