Skip to content

Commit

Permalink
[ADD]product_harmonized_system_heading
Browse files Browse the repository at this point in the history
New module to have a better structure on H.S. Codes. If installed, H.S. Codes will require from a H.S. Heading to be created first.
  • Loading branch information
GuillemCForgeFlow committed Oct 3, 2023
1 parent e2716c1 commit f9b04ae
Show file tree
Hide file tree
Showing 17 changed files with 360 additions and 0 deletions.
2 changes: 2 additions & 0 deletions product_harmonized_system_heading/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import models
from .hooks import post_init_hook
24 changes: 24 additions & 0 deletions product_harmonized_system_heading/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright 2023 ForgeFlow S.L. (https://www.forgeflow.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
{
"name": "Product Harmonized System Code Heading",
"summary": """
Adds a heading model for the Product Harmonized System Codes (H.S. Codes).
""",
"version": "13.0.1.0.0",
"category": "Reporting",
"license": "AGPL-3",
"website": "https://github.com/OCA/intrastat-extrastat",
"author": "ForgeFlow, Odoo Community Association (OCA)",
"depends": ["product_harmonized_system"],
"data": [
"security/security.xml",
"security/ir.model.access.csv",
"views/hs_code_heading_views.xml",
"views/hs_code_views.xml",
],
"maintainers": ["GuillemCForgeFlow"],
"installable": True,
"application": False,
"post_init_hook": "post_init_hook",
}
33 changes: 33 additions & 0 deletions product_harmonized_system_heading/hooks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Copyright 2023 ForgeFlow S.L. (https://www.forgeflow.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
import logging

from odoo import SUPERUSER_ID, api

_logger = logging.getLogger(__name__)


def post_init_hook(cr, registry):
_logger.info("Creating default H.S. Code Headings for H.S. Codes in the System.")
env = api.Environment(cr, SUPERUSER_ID, {})
HSCode = env["hs.code"].with_context(active_test=False)
HSCodeHeading = env["hs.code.heading"].with_context(active_test=False)
existing_hs_code_heading_codes = HSCodeHeading.search([]).mapped("code")
to_create_dict = dict()
hs_code_ids = HSCode.search([])
for hs_code in hs_code_ids:
vals = False
heading_code = hs_code.local_code[:4]
if heading_code in existing_hs_code_heading_codes:
continue
if heading_code not in to_create_dict.keys():
vals = {"code": heading_code}
to_create_dict[heading_code] = [hs_code.id]
else:
to_create_dict[heading_code].append(hs_code.id)
vals_list = list()
for code, rel_hs_code_ids in to_create_dict.items():
vals = {"code": code, "hs_code_ids": [(6, 0, rel_hs_code_ids)]}
vals_list.append(vals)
_logger.info("Creating %s H.S. Code Headings" % len(vals_list))
HSCodeHeading.create(vals_list)
2 changes: 2 additions & 0 deletions product_harmonized_system_heading/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import hs_code_heading
from . import hs_code
41 changes: 41 additions & 0 deletions product_harmonized_system_heading/models/hs_code.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Copyright 2023 ForgeFlow S.L. (https://www.forgeflow.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
from odoo.tools import config


class HSCode(models.Model):
_inherit = "hs.code"

hs_code_heading_id = fields.Many2one(comodel_name="hs.code.heading", readonly=True)

@api.model_create_multi
def create(self, vals_list):
if not config["test_enable"]:
HSCodeHeading = self.env["hs.code.heading"]
for vals in vals_list:
local_code = vals.get("local_code", "")
heading_code = local_code[:4]
heading = HSCodeHeading.search([("code", "=", heading_code)], limit=1)
if not heading:
msg = (
"There is no H.S. Code Heading with the following Code: %s."
"\nYou first need to create the Heading." % heading_code
)
raise ValidationError(_(msg))
vals["hs_code_heading_id"] = heading.id
return super().create(vals_list)

def _get_name(self):
"""
Overwrite method from the `product_harmonized_system` module in order to set the
Heading Description in the H.S. Code name
"""
self.ensure_one()
name = self.local_code
if self.hs_code_heading_id.description:
name += " " + self.hs_code_heading_id.description
if self.description:
name += ": " + self.description
return len(name) > 55 and name[:55] + "..." or name
48 changes: 48 additions & 0 deletions product_harmonized_system_heading/models/hs_code_heading.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Copyright 2023 ForgeFlow S.L. (https://www.forgeflow.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo import fields, models


class HSCodeHeading(models.Model):
_name = "hs.code.heading"
_description = "H.S. Code Heading"

# General fields
active = fields.Boolean(default=True)
code = fields.Char(
required=True,
size=4,
help="The H.S. Code Heading can only have 4 digits, as this define the Heading.",
)
description = fields.Text()

# Relational fields
hs_code_ids = fields.One2many(
comodel_name="hs.code",
inverse_name="hs_code_heading_id",
string="H.S. Codes",
readonly=True,
help="The related H.S. Codes using the Heading.",
)

_sql_constraints = [
(
"code_uniq",
"UNIQUE(code)",
"There is already an existing H.S. Code Heading with this Code.",
)
]

def _get_name(self):
self.ensure_one()
name = self.code
if self.description:
name += " " + self.description
return len(name) > 55 and name[:55] + "..." or name

def name_get(self):
res = []
for rec in self:
name = rec._get_name()
res.append((rec.id, name))
return res
1 change: 1 addition & 0 deletions product_harmonized_system_heading/readme/CONTRIBUTORS.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* Guillem Casassas <[email protected]>
4 changes: 4 additions & 0 deletions product_harmonized_system_heading/readme/DESCRIPTION.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
This module adds the possibility to assign Headings to the H.S. Codes in order to have
a better structure.

The H.S. Code Heading has a unique Code and a possible Description.
6 changes: 6 additions & 0 deletions product_harmonized_system_heading/readme/USAGE.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
In order to assign the H.S. Code Heading to a new H.S. Code, we first need to have the
Heading created, if there is none when creating the Code, the user will see an error
demanding to create the Heading first.

The relation is not editable from the User prespective as we just to need to ensure that
the Headings are created before creating the H.S. Codes.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_hs_code_heading_user,access_hs_code_heading_user,model_hs_code_heading,,1,0,0,0
access_hs_code_heading_manager,access_hs_code_heading_manager,model_hs_code_heading,product_harmonized_system_heading.group_product_harmonized_system_heading_manager,1,1,1,1
22 changes: 22 additions & 0 deletions product_harmonized_system_heading/security/security.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo noupdate="1">
<record model="ir.module.category" id="module_category_hs_code">
<field name="name">H.S. Code</field>
<field name="sequence">20</field>
</record>
<record id="group_product_harmonized_system_heading_manager" model="res.groups">
<field name="name">H.S. Code Heading Manager</field>
<field
name="comment"
>The user will have manager access to H.S. Code Heading.</field>
<field
name="category_id"
ref="product_harmonized_system_heading.module_category_hs_code"
/>
<field name="implied_ids" eval="[(4, ref('base.group_user'))]" />
<field
name="users"
eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"
/>
</record>
</odoo>
1 change: 1 addition & 0 deletions product_harmonized_system_heading/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import test_product_harmonized_system_heading
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Copyright 2023 ForgeFlow S.L. (https://www.forgeflow.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo.exceptions import ValidationError
from odoo.tests.common import SavepointCase


class TestProductHarmonizedSystemHeading(SavepointCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
# Models
cls.hs_code_model = cls.env["hs.code"]
cls.hs_code_heading_model = cls.env["hs.code.heading"]

@classmethod
def _create_hs_code_heading(cls, code="1234", description=False):
return cls.hs_code_heading_model.create(
{"code": code, "description": description}
)

@classmethod
def _create_hs_code(cls, local_code="123456789", heading=False, description=False):
return cls.hs_code_model.create(
{
"local_code": local_code,
"hs_code_heading_id": heading.id if heading else heading,
"description": description,
}
)

def test_01_check_validation_error_if_no_heading(self):
"""
Check that a Validation Error is raised when trying to create a new H.S. Code
with a Heading which is still not created in the system.
"""
with self.assertRaises(ValidationError):
self._create_hs_code()

def test_02_check_hs_code_creation(self):
"""
Check that the H.S. Code is correctly created if there is a related Heading.
"""
heading = self._create_hs_code_heading(code="5678")
code = self._create_hs_code(local_code="56789123", heading=heading)
self.assertTrue(code)

def test_03_check_correct_hs_code_name_get_value(self):
"""
Check that the H.S. Code display name is correctly set based on the changes.
"""
heading_description = "Heading Description"
code_description = "Code Description"
heading = self._create_hs_code_heading(description=heading_description)
code = self._create_hs_code(heading=heading, description=code_description)
self.assertEqual(
code.name_get()[0][1],
"123456789 %s: %s" % (heading_description, code_description),
)

def test_04_check_correct_hs_code_heading_name_get_value(self):
"""
Check that the H.S. Code display name is correctly set based on the changes.
"""
heading_description = "Heading Description 2"
heading = self._create_hs_code_heading(description=heading_description)
self.assertEqual(heading.name_get()[0][1], "1234 %s" % heading_description)
51 changes: 51 additions & 0 deletions product_harmonized_system_heading/views/hs_code_heading_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--
Copyright 2023 ForgeFlow S.L. (https://www.forgeflow.com)
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
-->
<odoo>
<record id="hs_code_heading_form_view" model="ir.ui.view">
<field name="name">hs.code.heading.form</field>
<field name="model">hs.code.heading</field>
<field name="arch" type="xml">
<form>
<sheet>
<group name="general" string="General">
<field name="code" />
<field name="description" />
</group>
<group name="hs_codes" string="Related H.S. Codes">
<field name="hs_code_ids" widget="many2many_tags" />
</group>
</sheet>
</form>
</field>
</record>
<record id="hs_code_heading_tree_view" model="ir.ui.view">
<field name="name">hs.code.heading.tree</field>
<field name="model">hs.code.heading</field>
<field name="arch" type="xml">
<tree string="H.S. Code Heading">
<field name="code" />
<field name="description" />
<field name="hs_code_ids" widget="many2many_tags" />
</tree>
</field>
</record>
<record id="hs_code_heading_search_view" model="ir.ui.view">
<field name="name">hs.code.heading.search.view</field>
<field name="model">hs.code.heading</field>
<field name="arch" type="xml">
<search string="H.S. Code Heading">
<field name="code" />
<field name="hs_code_ids" />
</search>
</field>
</record>
<record id="hs_code_heading_action" model="ir.actions.act_window">
<field name="name">H.S. Code Heading</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">hs.code.heading</field>
<field name="view_mode">tree,form</field>
</record>
</odoo>
49 changes: 49 additions & 0 deletions product_harmonized_system_heading/views/hs_code_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--
Copyright 2023 ForgeFlow S.L. (https://www.forgeflow.com)
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
-->
<odoo>
<record id="hs_code_view_search" model="ir.ui.view">
<field name="name">hs.code.search - product_harmonized_system_heading</field>
<field name="model">hs.code</field>
<field name="inherit_id" ref="product_harmonized_system.hs_code_view_search" />
<field name="arch" type="xml">
<field name="local_code" position="after">
<field name="hs_code_heading_id" />
</field>
<filter name="inactive" position="after">
<separator />
<filter
name="hs_code_heading_groupby"
string="H.S. Code Heading"
domain="[]"
context="{'group_by': 'hs_code_heading_id'}"
/>
</filter>
</field>
</record>
<record id="hs_code_view_tree" model="ir.ui.view">
<field name="name">hs.code.tree - product_harmonized_system_heading</field>
<field name="model">hs.code</field>
<field name="inherit_id" ref="product_harmonized_system.hs_code_view_tree" />
<field name="arch" type="xml">
<field name="hs_code" position="before">
<field name="hs_code_heading_id" />
</field>
</field>
</record>
<record id="hs_code_view_form" model="ir.ui.view">
<field name="name">hs.code.form - product_harmonized_system_heading</field>
<field name="model">hs.code</field>
<field name="inherit_id" ref="product_harmonized_system.hs_code_view_form" />
<field name="arch" type="xml">
<xpath
expr="//group[@name='main']/field[@name='local_code']"
position="before"
>
<field name="hs_code_heading_id" />
</xpath>
</field>
</record>
</odoo>
6 changes: 6 additions & 0 deletions setup/product_harmonized_system_heading/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import setuptools

setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)

0 comments on commit f9b04ae

Please sign in to comment.