[19.0][ADD] product_category_hs_mapping#2292
Open
bosd wants to merge 1 commit into
Open
Conversation
b5f84ce to
4411034
Compare
Map customs HS codes to Odoo product categories. Used by EDI / supplier integrations to auto-categorise newly imported products. - product.category.hs.mapping model: HS-code-pattern → product.category rules. Longest literal prefix wins (a rule for 84312000 matches both 84312000 and 8431200080); optional trailing * wildcard is purely visual; bare * accepted as a catch-all (specificity 0, last-resort match). Per-company scope (rules without company_id apply globally; rules with company_id match only that company). Standard security ACL. - intrastat_description computed Char on each rule: 3-tier lookup against account.intrastat.code records (exact → shortest extending → parent prefix). Lets buyers eyeball whether 8539299* mapped to 'Hydraulics' is wrong (it is — it's 'Filament lamps') without consulting a tariff manual. Cheap when account_intrastat (OCA) isn't installed: env reference catches KeyError, returns empty. - Apply HS Code → Category Mapping server action on product.template (Action menu, form + list). Re-runs the matcher against the product's hs_code and writes the result to categ_id. Single-product invocations raise a specific UserError per 'nothing happened' reason (no HS code / no rule / already in matched category). Multi-product invocations show a summary toast with the breakdown plus a soft_reload to refresh the list. - No bootstrap data shipped — each customer's mapping table reflects their own catalogue scope (different HS chapters, different category trees). Adoption pattern documented in CONTEXT.md: install the matcher from OCA, then ship a small private module with your own category tree + HS rules. - Translations: nl + es + canonical .pot (45 messages). Closes the gap between OCA account_intrastat (which manages HS codes themselves) and product categorisation — neither account_intrastat nor product_category_active maps HS to product.category. Pre-OCA development was driven by a real-world deployment with a multi-thousand-SKU parts catalogue; the matcher graduated to OCA-shape after several rounds of buyer testing.
4411034 to
41f9c66
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A small bridge module between HS codes (managed by OCA
account_intrastat) and Odoo product categories. When an integration setsproduct.template.hs_codefrom an EDI feed / cXML cart / OCI punchout / REST enrichment, the matcher resolves the HS code to aproduct.categoryso the product lands in the right tree.What's in the box
product.category.hs.mappingmodel: HS-code-pattern →product.categoryrules8431200080matches a rule for84312000)*wildcard (purely visual;84312000and84312000*behave identically)*accepted as a catch-all (specificity 0, last-resort match)company_idset match only for that company; rules without apply globally)intrastat_descriptioncomputed Char on each rule: 3-tier lookup againstaccount.intrastat.coderecords (exact → shortest extending → parent prefix). Lets buyers eyeball whether8539299*mapped to "Hydraulics" is wrong (it is — it's "Filament lamps") without consulting a tariff manual. Cheap whenaccount_intrastatisn't installed (env reference catchesKeyError).Apply HS Code → Category Mappingserver action onproduct.template(Action menu, form + list views). Re-runs the matcher against the product'shs_codeand writes the result tocateg_id. Single-product invocations raise a specificUserErrorper "nothing happened" reason (no HS code / no rule / already in matched category). Multi-product invocations show a summary toast with the breakdown.What's not in the box
No bootstrap data. Each customer's mapping table reflects their own catalogue scope (different HS chapters, different category trees, different language preferences). The adoption pattern is documented in
CONTEXT.md: install this matcher module from OCA, then ship a tiny private module with your own category tree + HS rules.Why this is distinct from existing OCA work
account_intrastat— the source of HS codes themselves. Managesaccount.intrastat.coderecords mapped toproduct.template.hs_code. Doesn't categorise.account_intrastat_ossand friends — declaration-level consumers of those codes.product_category_active— manages category lifecycle but doesn't classify products by HS.This module fills the gap between "we have a customs code on the product" and "we want the product in the right Odoo category."
Use cases that drove the design
hs_codefrom the supplier's cXML / OCI payload; punchout-purchase glue calls_get_category_for_hs_code(hs_code)on the freshly createdproduct.templateand writes the result tocateg_id.commodityCodefromGET /items-style responses populates the category at the same time.hs_codefirst, category after, using the matcher.Tests
tests/test_product_category_hs_mapping.pycovers:*, bare*, no internal wildcards, no non-digits)UNIQUE— handles NULLcompany_idcorrectly)8431and8431*match the same set)*last-resort behaviourDeployment notes
product.templateonly (notproduct.product) —hs_codelives on the template; per-variant tariff codes are rare in OEM-parts catalogues. The variant binding is on the ROADMAP.Roadmap items (in
readme/ROADMAP.md)product.product)