xsdata Odoo abstract model generator plugin
Project description
Odoo Abstract Model Generator from XSD Schemas
Generate Odoo abstract models (mixins) from XSD schemas using xsdata. Heavily inspired by xsdata-plantuml.
Video Tutorial: YouTube - xsdata-odoo Overview
Table of Contents
- Install
- Quick Start
- Real-World Usage
- Architecture Pattern
- Configuration
- Advanced Examples
- Field Prefixing Strategy
- Custom Filters
- Troubleshooting
Install
$ pip install xsdata[cli]
$ pip install git+https://github.com/akretion/xsdata-odoo
Quick Start
Microsoft Purchase Order Demo
$ xsdata generate tests/fixtures/po/po.xsd --output=odoo
Parsing schema po.xsd
Compiling schema po.xsd
Builder: 6 main and 1 inner classes
Analyzer input: 6 main and 1 inner classes
Analyzer output: 5 main and 1 inner classes
Generating package: generated.po
Brazilian NF-e (Electronic Invoice)
$ export XSDATA_SCHEMA=nfe
$ export XSDATA_VERSION=40
$ export XSDATA_SKIP="^ICMS.ICMS\d+|^ICMS.ICMSSN\d+"
$ export XSDATA_LANG="portuguese"
$ xsdata generate nfelib/nfe/schemas/v4_0 \
--package nfelib.nfe.odoo.v4_0 \
--output=odoo
Real-World Usage
xsdata-odoo is used in production by the OCA Brazilian Localization (l10n-brazil) to generate models for complex fiscal documents:
Brazilian Electronic Fiscal Documents
| Document | Fields | Model | Version | OCA Module |
|---|---|---|---|---|
| NF-e (Nota Fiscal Eletrônica) | 800+ | 55/65 | 4.0 | l10n_br_nfe_spec |
| CT-e (Transport Document) | 1000+ | 57 | 4.0 | l10n_br_cte_spec |
| MDF-e (Manifest) | 500+ | 58 | 3.0 | l10n_br_mdfe_spec |
| SPED (Fiscal Reports) | Variable | - | - | l10n_br_sped_* |
- NF-e: 800+ fields across multiple hierarchical structures (identification, items, taxes, transport, payment)
- CT-e: Multiple transport modes (road, air, waterway, rail, pipeline, multimodal) with specific fields each
- MDF-e: Aggregation of multiple NF-e/CT-e documents with municipal grouping
- SPED: Complex register hierarchies with parent-child relationships
Integration with nfelib: xsdata-odoo generates Odoo models, while nfelib handles XML serialization. Both use the same XSD schemas for consistency.
Architecture Pattern
Two-Layer Architecture
The OCA l10n-brazil uses a proven two-layer architecture:
1. Spec Modules (l10n_br_*_spec)
- 100% generated from XSD schemas
- Abstract models (mixins) with field definitions
- No business logic
- Versioned per schema version (nfe40, cte40, mdfe30)
2. Implementation Modules (l10n_br_nfe, etc.)
- Inherits from spec modules
- Maps to Odoo fiscal documents (
l10n_br_fiscal.document) - Business rules and validations
- Web service communication (SEFAZ)
- User interface
Benefits
- Schema Updates: Regenerate spec modules without touching business logic
- Version Migration: Different schema versions coexist (nfe40 vs nfe50)
- Testing: Business logic tested separately from generated code
- Maintainability: Clear separation of concerns
Configuration
Environment Variables
| Variable | Description | Default |
|---|---|---|
XSDATA_SCHEMA |
Schema name | spec |
XSDATA_VERSION |
Schema version | 10 |
XSDATA_SKIP |
Regex patterns to skip (pipe-separated) | [] |
XSDATA_LANG |
Language for text processing | "" |
XSDATA_MONETARY_TYPE |
XSD type to force fields.Monetary |
"" |
XSDATA_NUM_TYPE |
XSD type for numeric detection | TDec_[5:7.7:9] |
XSDATA_CURRENCY_FIELD |
Currency field name | currency_id |
XSDATA_GENDS |
GenerateDS compatibility mode | false |
Python API
from xsdata_odoo import get_config
config = get_config()
print(config.schema) # "spec"
print(config.version) # "10"
print(config.field_safe_prefix) # "spec10_"
print(config.inherit_model) # "spec.mixin.spec"
Advanced Examples
Brazilian NF-e with ICMS Handling
The NF-e schema defines ICMS taxes with multiple groups (ICMS00, ICMS10, ICMS40, etc.) that share field names. Skip them to avoid conflicts:
export XSDATA_SCHEMA=nfe
export XSDATA_VERSION=40
export XSDATA_SKIP="^ICMS.ICMS\d+|^ICMS.ICMSSN\d+"
export XSDATA_LANG="portuguese"
export XSDATA_CURRENCY_FIELD="brl_currency_id"
xsdata generate nfelib/nfe/schemas/v4_0 \
--package nfelib.nfe.odoo.v4_0 \
--output=odoo
# Move generated files to your module
mv nfelib/odoo/nfe/v4_0/* your_addon/models/v4_0/
CT-e (Transport Document)
export XSDATA_SCHEMA=cte
export XSDATA_VERSION=40
export XSDATA_SKIP="^ICMS\d+|^ICMSSN+|ICMSOutraUF|ICMSUFFim|INFESPECIE_TPESPECIE"
export XSDATA_LANG="portuguese"
xsdata generate nfelib/cte/schemas/v4_0 \
--package nfelib.cte.odoo.v4_0 \
--output=odoo
MDF-e (Manifest Document)
export XSDATA_SCHEMA=mdfe
export XSDATA_VERSION=30
export XSDATA_LANG="portuguese"
xsdata generate nfelib/mdfe/schemas/v3_0 \
--package nfelib.mdfe.odoo.v3_0 \
--output=odoo
SPED Fiscal Reports (Advanced)
For complex non-XML fiscal reports with register hierarchies:
from xsdata_odoo.generator import OdooGenerator
from xsdata_odoo.filters import OdooFilters
class SpedFilters(OdooFilters):
def registry_name(self, name="", parents=[], type_names=[]):
# Custom naming: schema.version.register_code
name = self.class_name(name)
return f"{self.schema}.{self.version}.{name[-4:].lower()}"
def class_properties(self, obj, parents):
# Add custom metadata
register = lookup_register(obj.name)
return f"_sped_level = {register['level']}"
Field Prefixing Strategy
Problem
With 800+ fields in NF-e alone, plus thousands of OCA modules, field name collisions are a real risk. Additionally, schemas evolve (3.0 → 4.0 → 5.0).
Solution
Each field gets a prefix combining schema name + version digits:
- NF-e v4.0:
nfe40_prefix →nfe40_vBC,nfe40_vICMS - CT-e v4.0:
cte40_prefix →cte40_vTPrest - MDF-e v3.0:
mdfe30_prefix →mdfe30_qNFe
Versioning Strategy
| Scenario | Example | Database Impact |
|---|---|---|
| Minor update (4.00 → 4.01) | Same nfe40_ prefix |
Fields updated in place, --update sufficient |
| Major update (3.0 → 4.0) | nfe30_ → nfe40_ prefix |
New fields/tables, data migration needed |
Automatic Prefix Generation
from xsdata_odoo import get_config
config = get_config()
config.schema = "nfe"
config.version = "40"
print(config.field_safe_prefix) # "nfe40_"
Custom Filters
Extend OdooFilters for specialized use cases:
from xsdata_odoo.filters import OdooFilters
from collections import OrderedDict
class MyCustomFilters(OdooFilters):
def registry_name(self, name="", parents=[], type_names=[]):
# Custom model naming logic
return f"my_module.{self.version}.{name.lower()}"
def _extract_field_attributes(self, parents, attr):
# Add custom field attributes
kwargs = super()._extract_field_attributes(parents, attr)
# Add custom metadata
kwargs["my_custom_attr"] = True
return kwargs
# Use custom filters
generator = OdooGenerator(config)
generator.filters = MyCustomFilters(
config,
all_simple_types=[],
all_complex_types=[],
registry_names={},
implicit_many2ones={},
)
generator.filters.register(generator.env)
Field Name Conflicts
Problem: XSD defines multiple groups with same field names (e.g., ICMS00.vBC, ICMS10.vBC).
Solution: Use XSDATA_SKIP to skip conflicting classes:
export XSDATA_SKIP="^ICMS.ICMS\d+|^ICMS.ICMSSN\d+"
Then implement business logic in your implementation module to handle the different ICMS groups.
Currency Fields
Problem: Monetary fields reference non-existent currency field.
Solution: Set XSDATA_CURRENCY_FIELD to match your Odoo module's currency field:
export XSDATA_CURRENCY_FIELD="company_currency_id" # or "currency_id", "brl_currency_id"
Language-Specific Text Processing
Problem: Field labels not extracted properly from XSD documentation.
Solution: Set XSDATA_LANG for stopword processing:
export XSDATA_LANG="portuguese" # or "english", "spanish", etc.
Links
License
MIT License - See LICENSE file for details
Copyright
2025 Akretion - Raphaël Valyi raphael.valyi@akretion.com
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file xsdata_odoo-1.1.0.tar.gz.
File metadata
- Download URL: xsdata_odoo-1.1.0.tar.gz
- Upload date:
- Size: 27.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0f15420fc29956ff3fbba96f76836d3d420c69fdffeeec2720d96e0501b36588
|
|
| MD5 |
6ca56d1861bcdc09d812406a5c58a657
|
|
| BLAKE2b-256 |
598d3ca082631a33b45ca94fb17b5a5c570785c6bd2134c28979c05c1be087df
|
Provenance
The following attestation bundles were made for xsdata_odoo-1.1.0.tar.gz:
Publisher:
publish.yml on akretion/xsdata-odoo
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
xsdata_odoo-1.1.0.tar.gz -
Subject digest:
0f15420fc29956ff3fbba96f76836d3d420c69fdffeeec2720d96e0501b36588 - Sigstore transparency entry: 1392524187
- Sigstore integration time:
-
Permalink:
akretion/xsdata-odoo@7198f8b973cdee185fc292ab97dc97abdec70eac -
Branch / Tag:
refs/tags/1.1.0 - Owner: https://github.com/akretion
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@7198f8b973cdee185fc292ab97dc97abdec70eac -
Trigger Event:
push
-
Statement type:
File details
Details for the file xsdata_odoo-1.1.0-py3-none-any.whl.
File metadata
- Download URL: xsdata_odoo-1.1.0-py3-none-any.whl
- Upload date:
- Size: 25.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
829d61af9883aac910f859268a5c7e0ec6f1093f96f0d5956e08af22d103bc29
|
|
| MD5 |
092c874f23ba7542fdda048f4facbb83
|
|
| BLAKE2b-256 |
2714abb43505fd434826943c048371b03a01a1fe72b4f25c9816b7187beb351e
|
Provenance
The following attestation bundles were made for xsdata_odoo-1.1.0-py3-none-any.whl:
Publisher:
publish.yml on akretion/xsdata-odoo
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
xsdata_odoo-1.1.0-py3-none-any.whl -
Subject digest:
829d61af9883aac910f859268a5c7e0ec6f1093f96f0d5956e08af22d103bc29 - Sigstore transparency entry: 1392524190
- Sigstore integration time:
-
Permalink:
akretion/xsdata-odoo@7198f8b973cdee185fc292ab97dc97abdec70eac -
Branch / Tag:
refs/tags/1.1.0 - Owner: https://github.com/akretion
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@7198f8b973cdee185fc292ab97dc97abdec70eac -
Trigger Event:
push
-
Statement type: