Skip to content
Health
60/100
fair
  • Security25/25
    No findings
  • Activity25/25
    Human commit this month
  • Required by5/25
    0 modules depend on it
  • Maturity5/25
    In 1 Odoo series since 19.0

Heuristic over the facts below, not an audit · How it's computed

Security findings
Grave / minor (static analysis)
Required by
0
Modules depending on it
External deps
4 pip · 0 bin
Python packages / system binaries
License
LGPL-3
No conflicts with its dependencies
Folder size
0.13 MB
Module directory on disk
Code analysis 0 migration notes 6 models 6 views 0 HTTP endpoints

Description

EDI integration with SPS Commerce via their canonical RSX XML format (namespace http://www.spscommerce.com/RSX, schema v7.7.0), on top of the OCA EDI framework (edi_core_oca), targeting Odoo 19.0.

RSX is SPS's proprietary normalized schema: one document type covers every trading partner — SPS maps each partner's native X12/EDIFACT/API behind it, so we integrate once instead of per-partner segment maps.

Documents covered:

  • Orders inbound — RSX Orders → draft sale.order
  • Shipments outbound (ASN) — stock.picking → RSX Shipments
  • Invoices outbound — account.move → RSX Invoices

All three share one group vocabulary (Address, Dates, References, Notes, PaymentTerms, Taxes, ChargesAllowances, CarrierInformation, part-number id block). Inbound payloads are parsed by services/rsx.py (xmltodict) into schema-shaped dicts; outbound payloads are rendered by QWeb edi.exchange.template.output templates that consume the business records directly.

Transport is pluggable per exchange record:

  • SPS-hosted SFTP (sftp.spscommerce.com) via edi_storage_oca — inbound files polled from out/testout, outbound uploaded to in/testin;
  • Manual drop (fallback) — import wizard for inbound, local directory for outbound.

This module is a POC in active development: the mappings cover the core fields and defer the long schema tail (carrier detail, pack levels, charges/allowances, taxes detail) to the partner implementation guide.

Not affiliated with or endorsed by SPS Commerce.

Dependencies

Odoo dependencies
Python dependencies
xmltodict, PyYAML, openupgradelib, fsspec>=2024.5.0
System dependencies
None
Required by
None

Code analysis

Extracted from the source by best-effort static analysis (no code is executed); details may be incomplete.

Views touched 6

XML IDNameModelTypeStatus
qweb_sps_address qweb New
qweb_sps_invoices qweb New
qweb_sps_item_ids qweb New
qweb_sps_meta qweb New
qweb_sps_shipments qweb New
view_edi_sps_import_wizard_form edi.sps.import.wizard.form edi.sps.import.wizard form New

HTTP endpoints 0

No HTTP endpoints found for this module.

Models touched 6

New fields (0)

No new fields.

Public methods (0)

No public methods.

New fields (0)

No new fields.

Public methods (0)

No public methods.

New fields (0)

No new fields.

Public methods (7)
  • check(self, exchange_record)
    Poll remote done/error dirs — only meaningful with a storage.
  • generate(self, exchange_record)
    Render the outbound payload via the type's QWeb output template. Document structure *and* the RSX -> Odoo field mapping live in the ``edi.exchange.template.output`` QWeb document: the template consumes the business record directly (``record`` / ``exchange_record`` / ``backend`` are already in the render context). This method only injects ``meta`` (envelope ids from the exchange-type settings) — the template's customization surface needs no code.
  • input_validate(self, exchange_record, value=None, **kw)
    Validate the received payload is RSX XML of the expected document.
  • output_validate(self, exchange_record, value=None, **kw)
  • process(self, exchange_record)
    Parse the received payload and create business documents.
  • receive(self, exchange_record)
    Fetch the payload: attached content first, storage second. Attached content wins so the import wizard and manual exchange records keep working even when the backend has a storage configured. Records created by the storage poller carry only a filename, so they fall through to the remote fetch.
  • send(self, exchange_record)
    Deliver the payload: storage transport first, local dir fallback. With ``backend_id.storage_id`` set, delegates to ``edi.oca.storage.handler.send`` which uploads to ``output_dir_pending`` on the remote filesystem. Otherwise writes the payload to ``rsx.outbound_dir`` (manual fallback).

New fields (3)
  • exchange_file Binary
    required=True string='RSX file'
  • exchange_filename Char
    string='Filename'
  • exchange_type_id Many2one → edi.exchange.type
    comodel_name='edi.exchange.type' domain=[('direction', '=', 'input'), ('backend_type_id.code', '=', 'sps')] required=True string='Exchange type'
Public methods (1)
  • action_import(self)

New fields (0)

No new fields.

Public methods (0)

No public methods.

New fields (0)

No new fields.

Public methods (1)
  • button_validate(self)