Skip to main content

xsdata Odoo abstract model generator plugin

Project description

Odoo Abstract Model Generator from XSD Schemas

codecov PyPI pyversions PyPI version

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

$ 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

xsdata_odoo-1.1.0.tar.gz (27.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

xsdata_odoo-1.1.0-py3-none-any.whl (25.0 kB view details)

Uploaded Python 3

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

Hashes for xsdata_odoo-1.1.0.tar.gz
Algorithm Hash digest
SHA256 0f15420fc29956ff3fbba96f76836d3d420c69fdffeeec2720d96e0501b36588
MD5 6ca56d1861bcdc09d812406a5c58a657
BLAKE2b-256 598d3ca082631a33b45ca94fb17b5a5c570785c6bd2134c28979c05c1be087df

See more details on using hashes here.

Provenance

The following attestation bundles were made for xsdata_odoo-1.1.0.tar.gz:

Publisher: publish.yml on akretion/xsdata-odoo

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for xsdata_odoo-1.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 829d61af9883aac910f859268a5c7e0ec6f1093f96f0d5956e08af22d103bc29
MD5 092c874f23ba7542fdda048f4facbb83
BLAKE2b-256 2714abb43505fd434826943c048371b03a01a1fe72b4f25c9816b7187beb351e

See more details on using hashes here.

Provenance

The following attestation bundles were made for xsdata_odoo-1.1.0-py3-none-any.whl:

Publisher: publish.yml on akretion/xsdata-odoo

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page