From f798e57ea55ea80cb4e9d490a2411763f32d9dfe Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Thu, 9 Jul 2020 11:11:22 +0200 Subject: [PATCH 01/32] Add edi_gs1 --- edi_gs1/README.rst | 86 + edi_gs1/TODO | 13 + edi_gs1/__init__.py | 2 + edi_gs1/__manifest__.py | 21 + edi_gs1/components/__init__.py | 1 + edi_gs1/components/application_receipt_ack.py | 30 + .../data/business_header_output_template.xml | 17 + .../data/business_header_qweb_template.xml | 65 + edi_gs1/data/gs1_backend_data.xml | 12 + edi_gs1/models/__init__.py | 2 + edi_gs1/models/edi_backend.py | 22 + edi_gs1/models/res_partner.py | 12 + edi_gs1/readme/CONTRIBUTORS.rst | 1 + edi_gs1/readme/DESCRIPTION.rst | 9 + edi_gs1/security/gs1_backend_acl.xml | 13 + edi_gs1/static/description/icon.png | Bin 0 -> 9455 bytes edi_gs1/static/description/index.html | 434 ++++ .../ApplicationReceiptAcknowledgement.xsd | 111 + .../static/schemas/gs1/ecom/eComCommon.xsd | 1858 +++++++++++++++++ .../schemas/gs1/shared/SharedCommon.xsd | 1506 +++++++++++++ edi_gs1/static/schemas/sbdh/BasicTypes.xsd | 20 + edi_gs1/static/schemas/sbdh/BusinessScope.xsd | 55 + .../schemas/sbdh/DocumentIdentification.xsd | 14 + edi_gs1/static/schemas/sbdh/Manifest.xsd | 19 + edi_gs1/static/schemas/sbdh/Partner.xsd | 26 + .../sbdh/StandardBusinessDocumentHeader.xsd | 25 + edi_gs1/tests/_OLD_common.py | 69 + edi_gs1/tests/__init__.py | 2 + edi_gs1/tests/_test_business_header_OLD.py | 127 ++ edi_gs1/tests/common.py | 47 + ...icationReceiptAcknowledgement.example1.xml | 65 + edi_gs1/tests/test_application_receipt_ack.py | 20 + edi_gs1/tests/test_business_header.py | 104 + edi_gs1/xmler.py | 135 ++ 34 files changed, 4943 insertions(+) create mode 100644 edi_gs1/README.rst create mode 100644 edi_gs1/TODO create mode 100644 edi_gs1/__init__.py create mode 100644 edi_gs1/__manifest__.py create mode 100644 edi_gs1/components/__init__.py create mode 100644 edi_gs1/components/application_receipt_ack.py create mode 100644 edi_gs1/data/business_header_output_template.xml create mode 100644 edi_gs1/data/business_header_qweb_template.xml create mode 100644 edi_gs1/data/gs1_backend_data.xml create mode 100644 edi_gs1/models/__init__.py create mode 100644 edi_gs1/models/edi_backend.py create mode 100644 edi_gs1/models/res_partner.py create mode 100644 edi_gs1/readme/CONTRIBUTORS.rst create mode 100644 edi_gs1/readme/DESCRIPTION.rst create mode 100644 edi_gs1/security/gs1_backend_acl.xml create mode 100644 edi_gs1/static/description/icon.png create mode 100644 edi_gs1/static/description/index.html create mode 100644 edi_gs1/static/schemas/gs1/ecom/ApplicationReceiptAcknowledgement.xsd create mode 100644 edi_gs1/static/schemas/gs1/ecom/eComCommon.xsd create mode 100644 edi_gs1/static/schemas/gs1/shared/SharedCommon.xsd create mode 100644 edi_gs1/static/schemas/sbdh/BasicTypes.xsd create mode 100644 edi_gs1/static/schemas/sbdh/BusinessScope.xsd create mode 100644 edi_gs1/static/schemas/sbdh/DocumentIdentification.xsd create mode 100644 edi_gs1/static/schemas/sbdh/Manifest.xsd create mode 100644 edi_gs1/static/schemas/sbdh/Partner.xsd create mode 100644 edi_gs1/static/schemas/sbdh/StandardBusinessDocumentHeader.xsd create mode 100644 edi_gs1/tests/_OLD_common.py create mode 100644 edi_gs1/tests/__init__.py create mode 100644 edi_gs1/tests/_test_business_header_OLD.py create mode 100644 edi_gs1/tests/common.py create mode 100644 edi_gs1/tests/examples/ApplicationReceiptAcknowledgement.example1.xml create mode 100644 edi_gs1/tests/test_application_receipt_ack.py create mode 100644 edi_gs1/tests/test_business_header.py create mode 100644 edi_gs1/xmler.py diff --git a/edi_gs1/README.rst b/edi_gs1/README.rst new file mode 100644 index 000000000..14f2fee15 --- /dev/null +++ b/edi_gs1/README.rst @@ -0,0 +1,86 @@ +============ +Base GS1 EDI +============ + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png + :target: https://odoo-community.org/page/development-status + :alt: Alpha +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fedi-lightgray.png?logo=github + :target: https://github.com/OCA/edi/tree/13.0/edi_gs1 + :alt: OCA/edi +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/edi-13-0/edi-13-0-edi_gs1 + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/226/13.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This is the base module for the implementation of the +`GS1 -The Global Language of Business `_ standard. + +This module contains methods to generate and parse XML files compliant with this standard. + +Beside basic machinery it provides common components to generate common GS1 schema elements. +Namely: + + * `StandardBusinessDocumentHeader` + +.. IMPORTANT:: + This is an alpha version, the data model and design can change at any time without warning. + Only for development or testing purpose, do not use in production. + `More details on development status `_ + +**Table of contents** + +.. contents:: + :local: + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* ACSONE + +Contributors +~~~~~~~~~~~~ + +* Simone Orsi + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/edi `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/edi_gs1/TODO b/edi_gs1/TODO new file mode 100644 index 000000000..caf440dd3 --- /dev/null +++ b/edi_gs1/TODO @@ -0,0 +1,13 @@ +Inbound Instruction + +- [x] allow skipping keys / elements by returning none +- [x] check UOM code handling +- [x] finish mapping of what we know +- [] draft RF-EFR module w/ tests +- [] allow export from PO +- [] handle state of export +- [] use storage backend to store it +- [] add lookup of acknowledge message +- [] add new field code to UOM +- [] draft send ack +- [] draft Outbound Notification parse diff --git a/edi_gs1/__init__.py b/edi_gs1/__init__.py new file mode 100644 index 000000000..0f00a6730 --- /dev/null +++ b/edi_gs1/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import components diff --git a/edi_gs1/__manifest__.py b/edi_gs1/__manifest__.py new file mode 100644 index 000000000..d336f2f00 --- /dev/null +++ b/edi_gs1/__manifest__.py @@ -0,0 +1,21 @@ +# Copyright 2020 ACSONE +# @author: Simone Orsi +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Base GS1 EDI", + "summary": """ + Base module for GS1 standard EDI exchange""", + "version": "13.0.1.0.0", + "development_status": "Alpha", + "license": "AGPL-3", + "author": "ACSONE,Odoo Community Association (OCA)", + "depends": ["component", "edi_exchange_template"], + "external_dependencies": {"python": ["xmlschema"]}, + "data": [ + "security/gs1_backend_acl.xml", + "data/gs1_backend_data.xml", + "data/business_header_qweb_template.xml", + "data/business_header_output_template.xml", + ], +} diff --git a/edi_gs1/components/__init__.py b/edi_gs1/components/__init__.py new file mode 100644 index 000000000..2fa9d7532 --- /dev/null +++ b/edi_gs1/components/__init__.py @@ -0,0 +1 @@ +# from . import application_receipt_ack diff --git a/edi_gs1/components/application_receipt_ack.py b/edi_gs1/components/application_receipt_ack.py new file mode 100644 index 000000000..ad04c5eee --- /dev/null +++ b/edi_gs1/components/application_receipt_ack.py @@ -0,0 +1,30 @@ +# Copyright 2020 ACSONE +# @author: Simone Orsi +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import _, exceptions + +from odoo.addons.component.core import Component + + +class ApplicationReceiptAcknowledgement(Component): + """Parse GS1 applicationReceiptAcknowledgement. + """ + + _name = "gs1.input.applicationReceiptAcknowledgement" + _inherit = "gs1.input.mixin" + _usage = "gs1.in.ApplicationReceiptAcknowledgement" + _xsd_schema_path = "static/schemas/gs1/ecom/ApplicationReceiptAcknowledgement.xsd" + + def get_status(self, result): + try: + ack = result["applicationReceiptAcknowledgement"][0] + except IndexError: + raise exceptions.ValidationError( + _("applicationReceiptAcknowledgement element not found!") + ) + status_wrapper = ack["applicationResponseDocumentLevel"][0] + return status_wrapper["applicationResponseStatusCode"] + + def is_ok(self, result): + return self.get_status(result) == "RECEIVED" diff --git a/edi_gs1/data/business_header_output_template.xml b/edi_gs1/data/business_header_output_template.xml new file mode 100644 index 000000000..5a212615d --- /dev/null +++ b/edi_gs1/data/business_header_output_template.xml @@ -0,0 +1,17 @@ + + + + EDI Exchange - GS1 Business Header + gs1.BusinessHeader + + xml + + +# Default identifier to record name +result = {"instance_identifier": record.name} + + + diff --git a/edi_gs1/data/business_header_qweb_template.xml b/edi_gs1/data/business_header_qweb_template.xml new file mode 100644 index 000000000..2ab74ea3b --- /dev/null +++ b/edi_gs1/data/business_header_qweb_template.xml @@ -0,0 +1,65 @@ + + + + + diff --git a/edi_gs1/data/gs1_backend_data.xml b/edi_gs1/data/gs1_backend_data.xml new file mode 100644 index 000000000..8d9bfb0c6 --- /dev/null +++ b/edi_gs1/data/gs1_backend_data.xml @@ -0,0 +1,12 @@ + + + + default_gs1_backend_type + gs1 + + + GS1 + + + + diff --git a/edi_gs1/models/__init__.py b/edi_gs1/models/__init__.py new file mode 100644 index 000000000..fe5182755 --- /dev/null +++ b/edi_gs1/models/__init__.py @@ -0,0 +1,2 @@ +from . import edi_backend +from . import res_partner diff --git a/edi_gs1/models/edi_backend.py b/edi_gs1/models/edi_backend.py new file mode 100644 index 000000000..2dd74200d --- /dev/null +++ b/edi_gs1/models/edi_backend.py @@ -0,0 +1,22 @@ +# Copyright 2020 ACSONE SA +# @author Simone Orsi +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class EDIBackend(models.Model): + _inherit = "edi.backend" + + # TODO: find another way to configure this w/out polluting edi backend + # TODO: add to view + lsp_partner_id = fields.Many2one( + string="Logistic Services Provider (LSP)", + comodel_name="res.partner", + domain=[("is_lsp", "=", True)], + ) + lsc_partner_id = fields.Many2one( + string="Logistic Services Client (LSC)", + comodel_name="res.partner", + default=lambda self: self.env.ref("base.main_partner").id, + ) diff --git a/edi_gs1/models/res_partner.py b/edi_gs1/models/res_partner.py new file mode 100644 index 000000000..6bf612db7 --- /dev/null +++ b/edi_gs1/models/res_partner.py @@ -0,0 +1,12 @@ +# Copyright 2020 ACSONE SA/NV () +# @author: Simone Orsi +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from odoo import fields, models + + +class ResPartner(models.Model): + _inherit = "res.partner" + + # TODO: move to another module? Or, is there another module providing this? + gln_code = fields.Char() + is_lsp = fields.Boolean(string="Is Logistic Services Provider (LSP)") diff --git a/edi_gs1/readme/CONTRIBUTORS.rst b/edi_gs1/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..f583948be --- /dev/null +++ b/edi_gs1/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Simone Orsi diff --git a/edi_gs1/readme/DESCRIPTION.rst b/edi_gs1/readme/DESCRIPTION.rst new file mode 100644 index 000000000..491e592cf --- /dev/null +++ b/edi_gs1/readme/DESCRIPTION.rst @@ -0,0 +1,9 @@ +This is the base module for the implementation of the +`GS1 -The Global Language of Business `_ standard. + +This module contains methods to generate and parse XML files compliant with this standard. + +Beside basic machinery it provides common components to generate common GS1 schema elements. +Namely: + + * `StandardBusinessDocumentHeader` diff --git a/edi_gs1/security/gs1_backend_acl.xml b/edi_gs1/security/gs1_backend_acl.xml new file mode 100644 index 000000000..bef3f0f91 --- /dev/null +++ b/edi_gs1/security/gs1_backend_acl.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/edi_gs1/static/description/icon.png b/edi_gs1/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 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 z 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 Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 diff --git a/edi_gs1/static/description/index.html b/edi_gs1/static/description/index.html new file mode 100644 index 000000000..0ad160f14 --- /dev/null +++ b/edi_gs1/static/description/index.html @@ -0,0 +1,434 @@ + + + + + + +Base GS1 EDI + + + +
+

Base GS1 EDI

+ + +

Alpha License: AGPL-3 OCA/edi Translate me on Weblate Try me on Runbot

+

This is the base module for the implementation of the +GS1 -The Global Language of Business standard.

+

This module contains methods to generate and parse XML files compliant with this standard.

+

Beside basic machinery it provides common components to generate common GS1 schema elements. +Namely:

+
+
    +
  • StandardBusinessDocumentHeader
  • +
+
+
+

Important

+

This is an alpha version, the data model and design can change at any time without warning. +Only for development or testing purpose, do not use in production. +More details on development status

+
+

Table of contents

+ +
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • ACSONE
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/edi project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/edi_gs1/static/schemas/gs1/ecom/ApplicationReceiptAcknowledgement.xsd b/edi_gs1/static/schemas/gs1/ecom/ApplicationReceiptAcknowledgement.xsd new file mode 100644 index 000000000..22c9524b7 --- /dev/null +++ b/edi_gs1/static/schemas/gs1/ecom/ApplicationReceiptAcknowledgement.xsd @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/edi_gs1/static/schemas/gs1/ecom/eComCommon.xsd b/edi_gs1/static/schemas/gs1/ecom/eComCommon.xsd new file mode 100644 index 000000000..4ae5569dc --- /dev/null +++ b/edi_gs1/static/schemas/gs1/ecom/eComCommon.xsd @@ -0,0 +1,1858 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/edi_gs1/static/schemas/gs1/shared/SharedCommon.xsd b/edi_gs1/static/schemas/gs1/shared/SharedCommon.xsd new file mode 100644 index 000000000..d51b853df --- /dev/null +++ b/edi_gs1/static/schemas/gs1/shared/SharedCommon.xsd @@ -0,0 +1,1506 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/edi_gs1/static/schemas/sbdh/BasicTypes.xsd b/edi_gs1/static/schemas/sbdh/BasicTypes.xsd new file mode 100644 index 000000000..3718acfe5 --- /dev/null +++ b/edi_gs1/static/schemas/sbdh/BasicTypes.xsd @@ -0,0 +1,20 @@ + + + + + + The MIME type as defined by IANA. Please refer to + http://www.iana.org/assignments/media-types/ for a list of types. + + + + + + + + ISO 639-2; 1998 representation of Language name. Refer to http://www.loc.gov/standards/iso639-2/iso639jac.html to get the latest version of the standard. + + + + + diff --git a/edi_gs1/static/schemas/sbdh/BusinessScope.xsd b/edi_gs1/static/schemas/sbdh/BusinessScope.xsd new file mode 100644 index 000000000..19ddd7ce0 --- /dev/null +++ b/edi_gs1/static/schemas/sbdh/BusinessScope.xsd @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/edi_gs1/static/schemas/sbdh/DocumentIdentification.xsd b/edi_gs1/static/schemas/sbdh/DocumentIdentification.xsd new file mode 100644 index 000000000..5264a2e27 --- /dev/null +++ b/edi_gs1/static/schemas/sbdh/DocumentIdentification.xsd @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/edi_gs1/static/schemas/sbdh/Manifest.xsd b/edi_gs1/static/schemas/sbdh/Manifest.xsd new file mode 100644 index 000000000..59bed11f1 --- /dev/null +++ b/edi_gs1/static/schemas/sbdh/Manifest.xsd @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/edi_gs1/static/schemas/sbdh/Partner.xsd b/edi_gs1/static/schemas/sbdh/Partner.xsd new file mode 100644 index 000000000..ff4258da6 --- /dev/null +++ b/edi_gs1/static/schemas/sbdh/Partner.xsd @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/edi_gs1/static/schemas/sbdh/StandardBusinessDocumentHeader.xsd b/edi_gs1/static/schemas/sbdh/StandardBusinessDocumentHeader.xsd new file mode 100644 index 000000000..b97fb45f4 --- /dev/null +++ b/edi_gs1/static/schemas/sbdh/StandardBusinessDocumentHeader.xsd @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/edi_gs1/tests/_OLD_common.py b/edi_gs1/tests/_OLD_common.py new file mode 100644 index 000000000..38547cb8e --- /dev/null +++ b/edi_gs1/tests/_OLD_common.py @@ -0,0 +1,69 @@ +# Copyright 2020 ACSONE +# @author: Simone Orsi +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import os + +import xmlunittest + +from odoo.tests.common import SavepointCase + +from odoo.addons.component.tests.common import ComponentMixin + + +class BaseTestCase(SavepointCase, ComponentMixin): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) + cls.setUpComponent() + cls.backend = cls._get_backend() + # Logistic Services Provider (LSP) + cls.lsp_partner = cls.env.ref("base.res_partner_3") + # Logistic Services Client (LSC) + cls.lsc_partner = cls.env.ref("base.main_partner") + cls.backend.lsp_partner_id = cls.lsp_partner + + # set fake GLN codes + cls.lsp_partner.gln_code = "1".zfill(13) + cls.lsc_partner.gln_code = "2".zfill(13) + + @classmethod + def _get_backend(cls): + return cls.env.ref("base_gs1.default_gs1_backend") + + +class BaseXMLTestCase(BaseTestCase, xmlunittest.XmlTestMixin): + """Test XML files.""" + + def _dev_write_example_file(self, test_file, filename, content): + from pathlib import Path + + path = Path(test_file).parent / ("examples/test." + filename) + with open(path, "w") as out: + out.write(content) + + def flatten(self, txt): + return "".join([x.strip() for x in txt.splitlines()]) + + def read_test_file(self, filename): + path = os.path.join(os.path.dirname(__file__), "examples", filename) + with open(path, "r") as thefile: + return thefile.read() + + # @freeze_time("2018-08-10 17:10:00") + # def check_filename(self, name_template): + # """ + # Test the filename of the export, the name template can have a date + # parameter or a date and time parameter + # """ + # day = fields.Date.today().replace('-', '') + # time = fields.Datetime.now().split(' ')[1].replace(':', '') + # expected = name_template.format(day, time) + # with self.backend.work_on( + # self.model._name, timestamp=self.timestamp + # ) as work: + # writer = work.component(usage='local.xml.writer') + # self.assertEqual(writer.filename(), expected) + # writer = work.component(usage='sftp.xml.writer') + # self.assertEqual(writer.filename(), expected) diff --git a/edi_gs1/tests/__init__.py b/edi_gs1/tests/__init__.py new file mode 100644 index 000000000..a691b3870 --- /dev/null +++ b/edi_gs1/tests/__init__.py @@ -0,0 +1,2 @@ +from . import test_business_header +from . import test_application_receipt_ack diff --git a/edi_gs1/tests/_test_business_header_OLD.py b/edi_gs1/tests/_test_business_header_OLD.py new file mode 100644 index 000000000..15013e478 --- /dev/null +++ b/edi_gs1/tests/_test_business_header_OLD.py @@ -0,0 +1,127 @@ +# Copyright 2020 ACSONE +# @author: Simone Orsi +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from freezegun import freeze_time + +from .common import BaseXMLTestCase + + +class BusinessHeaderTestCase(BaseXMLTestCase): + def _get_handler(self, usage="gs1.StandardBusinessDocumentHeader", **kw): + # FIXME: use a real record + record = object() + return self.backend._get_component( + work_ctx=dict( + record=record, + sender=self.lsc_partner, + receiver=self.lsp_partner, + instance_identifier="TEST-DOC-XYZ", + ), + usage=usage, + **kw + ) + + def test_header_version_data(self): + handler = self._get_handler() + result = handler._header_version() + expected = {"HeaderVersion": {"@ns": "sh", "@value": "1.0"}} + self.assertEqual(result, expected) + + def _expected_contact(self, key, record): + ctype = "Buyer" if key == "Sender" else "Seller" + return { + key: { + "@ns": "sh", + "Identifier": {"@ns": "sh", "@attrs": {"Authority": "GS1"}}, + "ContactInformation": { + "@ns": "sh", + "Contact": {"@ns": "sh", "@value": record.name}, + "EmailAddress": {"@ns": "sh", "@value": record.email}, + "TelephoneNumber": {"@ns": "sh", "@value": record.phone}, + "ContactTypeIdentifier": {"@ns": "sh", "@value": ctype}, + }, + }, + } + + def test_sender_data(self): + handler = self._get_handler() + result = handler._sender() + expected = self._expected_contact("Sender", self.lsc_partner) + self.assertEqual(result, expected) + + def test_receiver_data(self): + handler = self._get_handler() + result = handler._receiver() + expected = self._expected_contact("Receiver", self.lsp_partner) + self.assertEqual(result, expected) + + @freeze_time("2020-07-08 07:30:00") + def test_document_id_data(self): + handler = self._get_handler() + result = handler._document_identification() + expected = { + "DocumentIdentification": { + "@ns": "sh", + "Standard": {"@ns": "sh", "@value": "GS1"}, + "TypeVersion": {"@ns": "sh", "@value": "3.4"}, + "InstanceIdentifier": {"@ns": "sh", "@value": "TEST-DOC-XYZ"}, + "Type": {"@ns": "sh", "@value": ""}, + "MultipleType": {"@ns": "sh", "@value": "false"}, + "CreationDateAndTime": {"@ns": "sh", "@value": "2020-07-08T07:30:00"}, + } + } + self.assertEqual(result, expected) + + @freeze_time("2020-07-08 07:30:00") + def test_xml(self): + handler = self._get_handler() + result = handler.generate_xml() + # namespace is include in main document root + # but we need it to parse the result which would fail w/out this + result = result.replace( + "", + "".format( + handler._xmlns["sh"] + ), + ) + expected = """ + + 1.0 + + + + {sender.name} + {sender.email} + {sender.phone} + Buyer + + + + + + {receiver.name} + {receiver.email} + {receiver.phone} + Seller + + + + GS1 + 3.4 + {identifier} + + false + {date} + + + """.format( + ns=handler._xmlns["sh"], + sender=self.lsc_partner, + receiver=self.lsp_partner, + identifier="TEST-DOC-XYZ", + date="2020-07-08T07:30:00", + ) + self.assertXmlEquivalentOutputs(self.flatten(result), self.flatten(expected)) + # when valid returns none + self.assertFalse(handler.validate_schema(result)) diff --git a/edi_gs1/tests/common.py b/edi_gs1/tests/common.py new file mode 100644 index 000000000..428a0719c --- /dev/null +++ b/edi_gs1/tests/common.py @@ -0,0 +1,47 @@ +# Copyright 2020 ACSONE +# @author: Simone Orsi +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import os + +import xmlunittest + +from odoo.tests.common import SavepointCase, tagged + + +@tagged("-at_install", "post_install") +class BaseTestCase(SavepointCase, xmlunittest.XmlTestMixin): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) + cls.backend = cls._get_backend() + # Logistic Services Provider (LSP) + cls.lsp_partner = cls.env.ref("base.res_partner_3") + # Logistic Services Client (LSC) + cls.lsc_partner = cls.env.ref("base.main_partner") + cls.backend.lsp_partner_id = cls.lsp_partner + cls.backend.lsc_partner_id = cls.lsc_partner + + # set fake GLN codes + cls.lsp_partner.gln_code = "1".zfill(13) + cls.lsc_partner.gln_code = "2".zfill(13) + + @classmethod + def _get_backend(cls): + return cls.env.ref("edi_gs1.default_gs1_backend") + + def _dev_write_example_file(self, test_file, filename, content): + from pathlib import Path + + path = Path(test_file).parent / ("examples/test." + filename) + with open(path, "w") as out: + out.write(content) + + def flatten(self, txt): + return "".join([x.strip() for x in txt.splitlines()]) + + def read_test_file(self, filename): + path = os.path.join(os.path.dirname(__file__), "examples", filename) + with open(path, "r") as thefile: + return thefile.read() diff --git a/edi_gs1/tests/examples/ApplicationReceiptAcknowledgement.example1.xml b/edi_gs1/tests/examples/ApplicationReceiptAcknowledgement.example1.xml new file mode 100644 index 000000000..8cd39ca3f --- /dev/null +++ b/edi_gs1/tests/examples/ApplicationReceiptAcknowledgement.example1.xml @@ -0,0 +1,65 @@ + + + + 1.0 + + + + John Doe + John_Doe@purchasing.XYZretailer.com + +1-212-555-1213 + +1-212-555-2122 + Buyer + + + + + + Mary Smith + Mary_Smith@widgets.com + +1-312-555-1214 + +1-312-555-2125 + Seller + + + + GS1 + 3.4 + 100002 + + false + 2006-01-10T12:00:01.000-05:00 + + + + 2011-03-11T11:43:00.000-05:00 + ORIGINAL + + RA0009 + + 4098765000010 + + + + ERROR + 2011-03-11T11:00:00.000-05:00 + 35 + + PO3352 + + 5412345000013 + + + + INCOMPLETE_MESSAGE + + + + diff --git a/edi_gs1/tests/test_application_receipt_ack.py b/edi_gs1/tests/test_application_receipt_ack.py new file mode 100644 index 000000000..ba9b3ff36 --- /dev/null +++ b/edi_gs1/tests/test_application_receipt_ack.py @@ -0,0 +1,20 @@ +# Copyright 2020 ACSONE +# @author: Simone Orsi +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from .common import BaseTestCase + + +class ApplicationReceiptTestCase(BaseTestCase): + def _get_handler(self, usage="gs1.in.ApplicationReceiptAcknowledgement", **kw): + return self.backend._get_component(work_ctx=dict(), usage=usage, **kw) + + def test_parse(self): + fname = "ApplicationReceiptAcknowledgement.example1.xml" + file_content = self.read_test_file(fname) + handler = self._get_handler() + result = handler.parse_xml(file_content) + # expected = {} + # TODO: improve it + self.assertTrue(result["applicationReceiptAcknowledgement"]) + self.assertFalse(handler.is_ok(result)) diff --git a/edi_gs1/tests/test_business_header.py b/edi_gs1/tests/test_business_header.py new file mode 100644 index 000000000..5b0edb3c3 --- /dev/null +++ b/edi_gs1/tests/test_business_header.py @@ -0,0 +1,104 @@ +# Copyright 2020 ACSONE +# @author: Simone Orsi +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from freezegun import freeze_time + +from .common import BaseTestCase + +BH_NS = "http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader" + + +class BusinessHeaderTestCase(BaseTestCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls._setup_records() + + @classmethod + def _setup_records(cls): + cls.bh_tmpl = cls.env.ref("edi_gs1.edi_exchange_template_business_header") + vals = { + "backend_id": cls.backend.id, + "backend_type_id": cls.backend.backend_type_id.id, + "name": "Template output 1", + "direction": "output", + "code": "test_type_out1", + "exchange_file_ext": "txt", + "exchange_filename_pattern": "{record.ref}-{type.code}-{dt}", + } + cls.exc_type = cls.env["edi.exchange.type"].create(vals) + cls.related_record = cls.env.ref("base.partner_demo") + vals = { + # Does not really matter which record we bind the exchange to + "model": cls.related_record._name, + "res_id": cls.related_record.id, + "type_id": cls.exc_type.id, + } + cls.exc_record = cls.backend.create_record("test_type_out1", vals) + + def test_template_render_values(self): + values = self.bh_tmpl._get_render_values(self.exc_record) + self.assertEqual( + values, + { + "backend": self.backend, + "date_to_string": self.bh_tmpl._date_to_string, + "exchange_record": self.exc_record, + "instance_identifier": self.related_record.name, + "record": self.related_record, + "template": self.bh_tmpl, + "utc_now": self.bh_tmpl._utc_now, + "render_edi_template": self.bh_tmpl._render_template, + }, + ) + + @freeze_time("2020-07-08 07:30:00") + def test_xml(self): + output = self.backend.generate_output( + self.exc_record, + template_code="gs1.BusinessHeader", + sender=self.lsc_partner, + receiver=self.lsp_partner, + ) + expected = """ + + 1.0 + + + + {sender.name} + {sender.email} + {sender.phone} + Buyer + + + + + + {receiver.name} + {receiver.email} + {receiver.phone} + Seller + + + + GS1 + 3.4 + {identifier} + + false + {date} + + + """.format( + ns=BH_NS, + sender=self.lsc_partner, + receiver=self.lsp_partner, + identifier=self.related_record.name, + date="2020-07-08T07:30:00", + ) + self.assertXmlEquivalentOutputs(self.flatten(output), self.flatten(expected)) + # when valid returns none + # TODO + # self.assertFalse(handler.validate_schema(result)) diff --git a/edi_gs1/xmler.py b/edi_gs1/xmler.py new file mode 100644 index 000000000..7c9ccfb47 --- /dev/null +++ b/edi_gs1/xmler.py @@ -0,0 +1,135 @@ +""" +Converts a Python dictionary to a valid XML string + +https://github.com/watzon/xmler Slightly modified and adapted for py3. + +TODO: project seems dead but it's very small and shall be easy to maintain. +Decide if we want to keep it here or contribute back. +""" + +from xml.dom import minidom +from xml.etree.ElementTree import Element, tostring + +__version__ = "0.2.0" +version = __version__ + + +def dict2xml(input_dict, encoding="utf-8", pretty=False): + """Converts a python dictionary into a valid XML string + + Args: + - encoding specifies the encoding to be included in the encoding + segment. If set to False no encoding segment will be displayed. + - customRoot defines the tag to wrap the returned output. Can be + a string, dictionary, or False if no custom root is to be used. + + Returns: + A XML formatted string representing the dictionary. + + Examples: + ``` + dic = { + "Envelope": { + "@ns": "soapenv", + "@attrs": { + "xmlns:soapenv": "http://schemas.xmlsoap.org/soap/envelope/", + "xmlns:urn": "urn:partner.soap.sforce.com" + }, + "Header": { + "@ns": "soapenv", + "SessionHeader": { + "@ns": "urn", + "sessionId": { + "@ns": "urn", + "@value": "00D36000000b28L!ARsAQMtHo4XD71VYRxoz" + } + } + }, + "Body": { + "@ns": "soapenv", + "query": { + "@ns": "urn", + "queryString": { + "@ns": "urn", + "@value": "SELECT Id, Name FROM Account LIMIT 2" + } + } + } + } + } + + xml = xml2dict(dict, customRoot=False) + print(xml) + ``` + + output: + ``` + + + + + {0} + + + + ``` + """ + + xml_string = tostring(parse(input_dict, pretty=pretty), encoding=encoding) + + if pretty: + xml_pretty_string = minidom.parseString(xml_string) + return xml_pretty_string.toprettyxml().decode(encoding) + else: + return xml_string.decode(encoding) + + +def parse(input_dict, parent=None, pretty=False): + parent = parent or {} + for key, value in input_dict.items(): + + if isinstance(value, (float, int)): + # Enfoce strings here + value = str(value) + + parent["name"] = key + parent["value"] = value + + if isinstance(value, dict): + if "@ns" in value: + parent["namespace"] = value.pop("@ns", None) + + if "@attrs" in value: + parent["attributes"] = value.pop("@attrs", None) + + if "@name" in value: + parent["name"] = value.pop("@name", None) + + if "@value" in value: + val = value["@value"] + if isinstance(val, (float, int)): + # Enfoce strings here + val = str(val) + parent["value"] = value = val + + if "namespace" in parent: + parent["name"] = "{}:{}".format(parent["namespace"], parent["name"]) + + if "attributes" in parent: + element = Element(parent["name"], parent["attributes"]) + else: + element = Element(parent["name"]) + + if isinstance(parent["value"], dict): + for child_key, child_value in parent["value"].items(): + element.append(parse({child_key: child_value}, parent={})) + + elif isinstance(parent["value"], (list, set, tuple)): + for child in parent["value"]: + element.append(parse(child, parent={})) + + else: + element.text = parent["value"] + + return element From b96ea4a10069408e9b8b181398838e8554f944e7 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Thu, 26 Nov 2020 08:45:52 +0100 Subject: [PATCH 02/32] edi_gs1: fix output tests --- .../data/business_header_output_template.xml | 2 +- edi_gs1/data/gs1_backend_data.xml | 11 ++++--- ... => _TODO_test_application_receipt_ack.py} | 0 edi_gs1/tests/__init__.py | 3 +- edi_gs1/tests/common.py | 8 +++-- edi_gs1/tests/test_business_header.py | 29 +++++++------------ 6 files changed, 24 insertions(+), 29 deletions(-) rename edi_gs1/tests/{test_application_receipt_ack.py => _TODO_test_application_receipt_ack.py} (100%) diff --git a/edi_gs1/data/business_header_output_template.xml b/edi_gs1/data/business_header_output_template.xml index 5a212615d..75939ce46 100644 --- a/edi_gs1/data/business_header_output_template.xml +++ b/edi_gs1/data/business_header_output_template.xml @@ -6,7 +6,7 @@ > EDI Exchange - GS1 Business Header gs1.BusinessHeader - + xml diff --git a/edi_gs1/data/gs1_backend_data.xml b/edi_gs1/data/gs1_backend_data.xml index 8d9bfb0c6..83f1a0b55 100644 --- a/edi_gs1/data/gs1_backend_data.xml +++ b/edi_gs1/data/gs1_backend_data.xml @@ -1,12 +1,11 @@ - - default_gs1_backend_type + + GS1 gs1 - - GS1 - - + + GS1 default + diff --git a/edi_gs1/tests/test_application_receipt_ack.py b/edi_gs1/tests/_TODO_test_application_receipt_ack.py similarity index 100% rename from edi_gs1/tests/test_application_receipt_ack.py rename to edi_gs1/tests/_TODO_test_application_receipt_ack.py diff --git a/edi_gs1/tests/__init__.py b/edi_gs1/tests/__init__.py index a691b3870..321fab57d 100644 --- a/edi_gs1/tests/__init__.py +++ b/edi_gs1/tests/__init__.py @@ -1,2 +1,3 @@ from . import test_business_header -from . import test_application_receipt_ack + +# from . import test_application_receipt_ack diff --git a/edi_gs1/tests/common.py b/edi_gs1/tests/common.py index 428a0719c..86c7022ab 100644 --- a/edi_gs1/tests/common.py +++ b/edi_gs1/tests/common.py @@ -6,11 +6,13 @@ import xmlunittest -from odoo.tests.common import SavepointCase, tagged +from odoo.tests.common import tagged + +from odoo.addons.component.tests.common import SavepointComponentCase @tagged("-at_install", "post_install") -class BaseTestCase(SavepointCase, xmlunittest.XmlTestMixin): +class BaseTestCase(SavepointComponentCase, xmlunittest.XmlTestMixin): @classmethod def setUpClass(cls): super().setUpClass() @@ -29,7 +31,7 @@ def setUpClass(cls): @classmethod def _get_backend(cls): - return cls.env.ref("edi_gs1.default_gs1_backend") + return cls.env.ref("edi_gs1.edi_backend_gs1_default") def _dev_write_example_file(self, test_file, filename, content): from pathlib import Path diff --git a/edi_gs1/tests/test_business_header.py b/edi_gs1/tests/test_business_header.py index 5b0edb3c3..534c42a89 100644 --- a/edi_gs1/tests/test_business_header.py +++ b/edi_gs1/tests/test_business_header.py @@ -39,27 +39,20 @@ def _setup_records(cls): def test_template_render_values(self): values = self.bh_tmpl._get_render_values(self.exc_record) - self.assertEqual( - values, - { - "backend": self.backend, - "date_to_string": self.bh_tmpl._date_to_string, - "exchange_record": self.exc_record, - "instance_identifier": self.related_record.name, - "record": self.related_record, - "template": self.bh_tmpl, - "utc_now": self.bh_tmpl._utc_now, - "render_edi_template": self.bh_tmpl._render_template, - }, - ) + expected = { + "backend": self.backend, + "exchange_record": self.exc_record, + "instance_identifier": self.related_record.name, + "record": self.related_record, + "template": self.bh_tmpl, + } + for k, v in expected.items(): + self.assertEqual(values[k], v) @freeze_time("2020-07-08 07:30:00") def test_xml(self): - output = self.backend.generate_output( - self.exc_record, - template_code="gs1.BusinessHeader", - sender=self.lsc_partner, - receiver=self.lsp_partner, + output = self.bh_tmpl.generate_output( + self.exc_record, sender=self.lsc_partner, receiver=self.lsp_partner, ) expected = """ From 2e20847316afeb28624aca760fbc1373bf2c6a85 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Wed, 2 Dec 2020 17:54:28 +0100 Subject: [PATCH 03/32] edi_gs1: BH instance identifier = record identifier --- edi_gs1/data/business_header_output_template.xml | 2 +- edi_gs1/tests/test_business_header.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/edi_gs1/data/business_header_output_template.xml b/edi_gs1/data/business_header_output_template.xml index 75939ce46..4ab29a20b 100644 --- a/edi_gs1/data/business_header_output_template.xml +++ b/edi_gs1/data/business_header_output_template.xml @@ -11,7 +11,7 @@ # Default identifier to record name -result = {"instance_identifier": record.name} +result = {"instance_identifier": exchange_record.identifier} diff --git a/edi_gs1/tests/test_business_header.py b/edi_gs1/tests/test_business_header.py index 534c42a89..e6d6bf9e9 100644 --- a/edi_gs1/tests/test_business_header.py +++ b/edi_gs1/tests/test_business_header.py @@ -42,7 +42,7 @@ def test_template_render_values(self): expected = { "backend": self.backend, "exchange_record": self.exc_record, - "instance_identifier": self.related_record.name, + "instance_identifier": self.exc_record.identifier, "record": self.related_record, "template": self.bh_tmpl, } @@ -88,7 +88,7 @@ def test_xml(self): ns=BH_NS, sender=self.lsc_partner, receiver=self.lsp_partner, - identifier=self.related_record.name, + identifier=self.exc_record.identifier, date="2020-07-08T07:30:00", ) self.assertXmlEquivalentOutputs(self.flatten(output), self.flatten(expected)) From bf71f88c15d504a2ed730d79edc95a2d8bd21103 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Wed, 2 Dec 2020 17:56:13 +0100 Subject: [PATCH 04/32] edi_gs1: add basic types for ack --- edi_gs1/__manifest__.py | 1 + edi_gs1/data/exchange_type_data.xml | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 edi_gs1/data/exchange_type_data.xml diff --git a/edi_gs1/__manifest__.py b/edi_gs1/__manifest__.py index d336f2f00..008162dc3 100644 --- a/edi_gs1/__manifest__.py +++ b/edi_gs1/__manifest__.py @@ -15,6 +15,7 @@ "data": [ "security/gs1_backend_acl.xml", "data/gs1_backend_data.xml", + "data/exchange_type_data.xml", "data/business_header_qweb_template.xml", "data/business_header_output_template.xml", ], diff --git a/edi_gs1/data/exchange_type_data.xml b/edi_gs1/data/exchange_type_data.xml new file mode 100644 index 000000000..a8ff5ec98 --- /dev/null +++ b/edi_gs1/data/exchange_type_data.xml @@ -0,0 +1,19 @@ + + + + + GS1 ApplicationReceiptAcknowledgement + applicationReceiptAcknowledgement_in + input + {record_name}-{type.code}-{dt} + xml + + + + GS1 ApplicationReceiptAcknowledgement + applicationReceiptAcknowledgement_out + output + {record_name}-{type.code}-{dt} + xml + + From 258cdbb2cf691dad0ffcb20ed4cf4ea0f4f47730 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Wed, 2 Dec 2020 17:56:52 +0100 Subject: [PATCH 05/32] edi_gs1: add basic component for output --- edi_gs1/components/__init__.py | 2 +- edi_gs1/components/output_mixin.py | 40 ++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 edi_gs1/components/output_mixin.py diff --git a/edi_gs1/components/__init__.py b/edi_gs1/components/__init__.py index 2fa9d7532..14ce72c88 100644 --- a/edi_gs1/components/__init__.py +++ b/edi_gs1/components/__init__.py @@ -1 +1 @@ -# from . import application_receipt_ack +from . import output_mixin diff --git a/edi_gs1/components/output_mixin.py b/edi_gs1/components/output_mixin.py new file mode 100644 index 000000000..f03910f44 --- /dev/null +++ b/edi_gs1/components/output_mixin.py @@ -0,0 +1,40 @@ +# Copyright 2020 ACSONE +# @author: Simone Orsi +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo.tools import DotDict + +from odoo.addons.component.core import AbstractComponent + + +class GS1OutputMessageMixin(AbstractComponent): + """Common gs1 output mixin. + """ + + _name = "edi.gs1.output.mixin" + _inherit = [ + "edi.output.mixin", + ] + _work_context_validate_attrs = [ + "record", + "sender", + "receiver", + ] + + def generate_info(self): + return DotDict(self._generate_info()) + + def _generate_info(self): + raise NotImplementedError("You must provide `_generate_info`") + + @property + def replace_existing(self): + """Pass `replace_existing` to work context to change this value.""" + return getattr(self.work, "replace_existing", False) + + def _document_action_code(self): + # Brand new file + code = "ADD" + if self.replace_existing: + code = "CHANGE_BY_REFRESH" + return code From 5cd932da7731cbcc8fee7f5b89c69da66c85e59a Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Wed, 2 Dec 2020 18:06:18 +0100 Subject: [PATCH 06/32] edi_gs1: add schemas for warehouse exchanges --- .../schemas/gs1/ecom/WarehousingCommon.xsd | 280 ++++++++++++++++++ .../ecom/WarehousingInboundInstruction.xsd | 77 +++++ .../ecom/WarehousingInboundNotification.xsd | 86 ++++++ .../ecom/WarehousingOutboundInstruction.xsd | 87 ++++++ .../ecom/WarehousingOutboundNotification.xsd | 96 ++++++ 5 files changed, 626 insertions(+) create mode 100644 edi_gs1/static/schemas/gs1/ecom/WarehousingCommon.xsd create mode 100644 edi_gs1/static/schemas/gs1/ecom/WarehousingInboundInstruction.xsd create mode 100644 edi_gs1/static/schemas/gs1/ecom/WarehousingInboundNotification.xsd create mode 100644 edi_gs1/static/schemas/gs1/ecom/WarehousingOutboundInstruction.xsd create mode 100644 edi_gs1/static/schemas/gs1/ecom/WarehousingOutboundNotification.xsd diff --git a/edi_gs1/static/schemas/gs1/ecom/WarehousingCommon.xsd b/edi_gs1/static/schemas/gs1/ecom/WarehousingCommon.xsd new file mode 100644 index 000000000..2b2add5fb --- /dev/null +++ b/edi_gs1/static/schemas/gs1/ecom/WarehousingCommon.xsd @@ -0,0 +1,280 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/edi_gs1/static/schemas/gs1/ecom/WarehousingInboundInstruction.xsd b/edi_gs1/static/schemas/gs1/ecom/WarehousingInboundInstruction.xsd new file mode 100644 index 000000000..eb4f90aaf --- /dev/null +++ b/edi_gs1/static/schemas/gs1/ecom/WarehousingInboundInstruction.xsd @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/edi_gs1/static/schemas/gs1/ecom/WarehousingInboundNotification.xsd b/edi_gs1/static/schemas/gs1/ecom/WarehousingInboundNotification.xsd new file mode 100644 index 000000000..ad3811cc6 --- /dev/null +++ b/edi_gs1/static/schemas/gs1/ecom/WarehousingInboundNotification.xsd @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/edi_gs1/static/schemas/gs1/ecom/WarehousingOutboundInstruction.xsd b/edi_gs1/static/schemas/gs1/ecom/WarehousingOutboundInstruction.xsd new file mode 100644 index 000000000..a03f46cb7 --- /dev/null +++ b/edi_gs1/static/schemas/gs1/ecom/WarehousingOutboundInstruction.xsd @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/edi_gs1/static/schemas/gs1/ecom/WarehousingOutboundNotification.xsd b/edi_gs1/static/schemas/gs1/ecom/WarehousingOutboundNotification.xsd new file mode 100644 index 000000000..d52717da7 --- /dev/null +++ b/edi_gs1/static/schemas/gs1/ecom/WarehousingOutboundNotification.xsd @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From fb925b4f6216286db3f197c5999854a7d746cd93 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Wed, 2 Dec 2020 23:06:24 +0100 Subject: [PATCH 07/32] edi_gs1: clean dead code --- edi_gs1/tests/_OLD_common.py | 69 ---------- .../_TODO_test_application_receipt_ack.py | 20 --- edi_gs1/tests/_test_business_header_OLD.py | 127 ------------------ 3 files changed, 216 deletions(-) delete mode 100644 edi_gs1/tests/_OLD_common.py delete mode 100644 edi_gs1/tests/_TODO_test_application_receipt_ack.py delete mode 100644 edi_gs1/tests/_test_business_header_OLD.py diff --git a/edi_gs1/tests/_OLD_common.py b/edi_gs1/tests/_OLD_common.py deleted file mode 100644 index 38547cb8e..000000000 --- a/edi_gs1/tests/_OLD_common.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright 2020 ACSONE -# @author: Simone Orsi -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -import os - -import xmlunittest - -from odoo.tests.common import SavepointCase - -from odoo.addons.component.tests.common import ComponentMixin - - -class BaseTestCase(SavepointCase, ComponentMixin): - @classmethod - def setUpClass(cls): - super().setUpClass() - cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) - cls.setUpComponent() - cls.backend = cls._get_backend() - # Logistic Services Provider (LSP) - cls.lsp_partner = cls.env.ref("base.res_partner_3") - # Logistic Services Client (LSC) - cls.lsc_partner = cls.env.ref("base.main_partner") - cls.backend.lsp_partner_id = cls.lsp_partner - - # set fake GLN codes - cls.lsp_partner.gln_code = "1".zfill(13) - cls.lsc_partner.gln_code = "2".zfill(13) - - @classmethod - def _get_backend(cls): - return cls.env.ref("base_gs1.default_gs1_backend") - - -class BaseXMLTestCase(BaseTestCase, xmlunittest.XmlTestMixin): - """Test XML files.""" - - def _dev_write_example_file(self, test_file, filename, content): - from pathlib import Path - - path = Path(test_file).parent / ("examples/test." + filename) - with open(path, "w") as out: - out.write(content) - - def flatten(self, txt): - return "".join([x.strip() for x in txt.splitlines()]) - - def read_test_file(self, filename): - path = os.path.join(os.path.dirname(__file__), "examples", filename) - with open(path, "r") as thefile: - return thefile.read() - - # @freeze_time("2018-08-10 17:10:00") - # def check_filename(self, name_template): - # """ - # Test the filename of the export, the name template can have a date - # parameter or a date and time parameter - # """ - # day = fields.Date.today().replace('-', '') - # time = fields.Datetime.now().split(' ')[1].replace(':', '') - # expected = name_template.format(day, time) - # with self.backend.work_on( - # self.model._name, timestamp=self.timestamp - # ) as work: - # writer = work.component(usage='local.xml.writer') - # self.assertEqual(writer.filename(), expected) - # writer = work.component(usage='sftp.xml.writer') - # self.assertEqual(writer.filename(), expected) diff --git a/edi_gs1/tests/_TODO_test_application_receipt_ack.py b/edi_gs1/tests/_TODO_test_application_receipt_ack.py deleted file mode 100644 index ba9b3ff36..000000000 --- a/edi_gs1/tests/_TODO_test_application_receipt_ack.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright 2020 ACSONE -# @author: Simone Orsi -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from .common import BaseTestCase - - -class ApplicationReceiptTestCase(BaseTestCase): - def _get_handler(self, usage="gs1.in.ApplicationReceiptAcknowledgement", **kw): - return self.backend._get_component(work_ctx=dict(), usage=usage, **kw) - - def test_parse(self): - fname = "ApplicationReceiptAcknowledgement.example1.xml" - file_content = self.read_test_file(fname) - handler = self._get_handler() - result = handler.parse_xml(file_content) - # expected = {} - # TODO: improve it - self.assertTrue(result["applicationReceiptAcknowledgement"]) - self.assertFalse(handler.is_ok(result)) diff --git a/edi_gs1/tests/_test_business_header_OLD.py b/edi_gs1/tests/_test_business_header_OLD.py deleted file mode 100644 index 15013e478..000000000 --- a/edi_gs1/tests/_test_business_header_OLD.py +++ /dev/null @@ -1,127 +0,0 @@ -# Copyright 2020 ACSONE -# @author: Simone Orsi -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from freezegun import freeze_time - -from .common import BaseXMLTestCase - - -class BusinessHeaderTestCase(BaseXMLTestCase): - def _get_handler(self, usage="gs1.StandardBusinessDocumentHeader", **kw): - # FIXME: use a real record - record = object() - return self.backend._get_component( - work_ctx=dict( - record=record, - sender=self.lsc_partner, - receiver=self.lsp_partner, - instance_identifier="TEST-DOC-XYZ", - ), - usage=usage, - **kw - ) - - def test_header_version_data(self): - handler = self._get_handler() - result = handler._header_version() - expected = {"HeaderVersion": {"@ns": "sh", "@value": "1.0"}} - self.assertEqual(result, expected) - - def _expected_contact(self, key, record): - ctype = "Buyer" if key == "Sender" else "Seller" - return { - key: { - "@ns": "sh", - "Identifier": {"@ns": "sh", "@attrs": {"Authority": "GS1"}}, - "ContactInformation": { - "@ns": "sh", - "Contact": {"@ns": "sh", "@value": record.name}, - "EmailAddress": {"@ns": "sh", "@value": record.email}, - "TelephoneNumber": {"@ns": "sh", "@value": record.phone}, - "ContactTypeIdentifier": {"@ns": "sh", "@value": ctype}, - }, - }, - } - - def test_sender_data(self): - handler = self._get_handler() - result = handler._sender() - expected = self._expected_contact("Sender", self.lsc_partner) - self.assertEqual(result, expected) - - def test_receiver_data(self): - handler = self._get_handler() - result = handler._receiver() - expected = self._expected_contact("Receiver", self.lsp_partner) - self.assertEqual(result, expected) - - @freeze_time("2020-07-08 07:30:00") - def test_document_id_data(self): - handler = self._get_handler() - result = handler._document_identification() - expected = { - "DocumentIdentification": { - "@ns": "sh", - "Standard": {"@ns": "sh", "@value": "GS1"}, - "TypeVersion": {"@ns": "sh", "@value": "3.4"}, - "InstanceIdentifier": {"@ns": "sh", "@value": "TEST-DOC-XYZ"}, - "Type": {"@ns": "sh", "@value": ""}, - "MultipleType": {"@ns": "sh", "@value": "false"}, - "CreationDateAndTime": {"@ns": "sh", "@value": "2020-07-08T07:30:00"}, - } - } - self.assertEqual(result, expected) - - @freeze_time("2020-07-08 07:30:00") - def test_xml(self): - handler = self._get_handler() - result = handler.generate_xml() - # namespace is include in main document root - # but we need it to parse the result which would fail w/out this - result = result.replace( - "", - "".format( - handler._xmlns["sh"] - ), - ) - expected = """ - - 1.0 - - - - {sender.name} - {sender.email} - {sender.phone} - Buyer - - - - - - {receiver.name} - {receiver.email} - {receiver.phone} - Seller - - - - GS1 - 3.4 - {identifier} - - false - {date} - - - """.format( - ns=handler._xmlns["sh"], - sender=self.lsc_partner, - receiver=self.lsp_partner, - identifier="TEST-DOC-XYZ", - date="2020-07-08T07:30:00", - ) - self.assertXmlEquivalentOutputs(self.flatten(result), self.flatten(expected)) - # when valid returns none - self.assertFalse(handler.validate_schema(result)) From b00b16e072d09dd899f1e01abd351f6afb14bb3c Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Thu, 3 Dec 2020 11:08:28 +0100 Subject: [PATCH 08/32] edi_gs1: depend on edi_xml --- edi_gs1/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edi_gs1/__manifest__.py b/edi_gs1/__manifest__.py index 008162dc3..b83cfc168 100644 --- a/edi_gs1/__manifest__.py +++ b/edi_gs1/__manifest__.py @@ -10,7 +10,7 @@ "development_status": "Alpha", "license": "AGPL-3", "author": "ACSONE,Odoo Community Association (OCA)", - "depends": ["component", "edi_exchange_template"], + "depends": ["component", "edi_exchange_template", "edi_xml"], "external_dependencies": {"python": ["xmlschema"]}, "data": [ "security/gs1_backend_acl.xml", From 95a00d49c0b042c9724c41791e5de3317f5fd2ae Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Thu, 3 Dec 2020 19:01:55 +0100 Subject: [PATCH 09/32] edi_gs1: component has only exc record in ctx --- edi_gs1/components/output_mixin.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/edi_gs1/components/output_mixin.py b/edi_gs1/components/output_mixin.py index f03910f44..9572b6c00 100644 --- a/edi_gs1/components/output_mixin.py +++ b/edi_gs1/components/output_mixin.py @@ -16,11 +16,15 @@ class GS1OutputMessageMixin(AbstractComponent): "edi.output.mixin", ] _work_context_validate_attrs = [ - "record", + "exchange_record", "sender", "receiver", ] + @property + def record(self): + return self.work.exchange_record.record + def generate_info(self): return DotDict(self._generate_info()) From 027bf329b99ee54ffa4ad7acd83101f72e24aa30 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Fri, 4 Dec 2020 08:07:42 +0100 Subject: [PATCH 10/32] edi_gs1: SBH tests validate xml --- edi_gs1/tests/common.py | 10 ++++++++++ edi_gs1/tests/test_business_header.py | 8 +++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/edi_gs1/tests/common.py b/edi_gs1/tests/common.py index 86c7022ab..d0fa2e574 100644 --- a/edi_gs1/tests/common.py +++ b/edi_gs1/tests/common.py @@ -13,6 +13,9 @@ @tagged("-at_install", "post_install") class BaseTestCase(SavepointComponentCase, xmlunittest.XmlTestMixin): + + _schema_path = "edi_gs1:static/schemas/sbdh/StandardBusinessDocumentHeader.xsd" + @classmethod def setUpClass(cls): super().setUpClass() @@ -47,3 +50,10 @@ def read_test_file(self, filename): path = os.path.join(os.path.dirname(__file__), "examples", filename) with open(path, "r") as thefile: return thefile.read() + + _schema_path = "" + + def _get_xml_handler(self): + return self.backend._find_component( + ["edi.xml"], work_ctx={"schema_path": self._schema_path}, safe=False, + ) diff --git a/edi_gs1/tests/test_business_header.py b/edi_gs1/tests/test_business_header.py index e6d6bf9e9..0896be8e6 100644 --- a/edi_gs1/tests/test_business_header.py +++ b/edi_gs1/tests/test_business_header.py @@ -10,6 +10,9 @@ class BusinessHeaderTestCase(BaseTestCase): + + _schema_path = "edi_gs1:static/schemas/sbdh/StandardBusinessDocumentHeader.xsd" + @classmethod def setUpClass(cls): super().setUpClass() @@ -92,6 +95,5 @@ def test_xml(self): date="2020-07-08T07:30:00", ) self.assertXmlEquivalentOutputs(self.flatten(output), self.flatten(expected)) - # when valid returns none - # TODO - # self.assertFalse(handler.validate_schema(result)) + handler = self._get_xml_handler() + self.assertEqual(handler.validate(output), None) From a15a5870c373b876a062c1474d366b0738796889 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Fri, 4 Dec 2020 09:20:56 +0100 Subject: [PATCH 11/32] edi: draft gs1 input mixin --- edi_gs1/components/__init__.py | 1 + edi_gs1/components/input_mixin.py | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 edi_gs1/components/input_mixin.py diff --git a/edi_gs1/components/__init__.py b/edi_gs1/components/__init__.py index 14ce72c88..b4bd9cc66 100644 --- a/edi_gs1/components/__init__.py +++ b/edi_gs1/components/__init__.py @@ -1 +1,2 @@ from . import output_mixin +from . import input_mixin diff --git a/edi_gs1/components/input_mixin.py b/edi_gs1/components/input_mixin.py new file mode 100644 index 000000000..13108ccda --- /dev/null +++ b/edi_gs1/components/input_mixin.py @@ -0,0 +1,24 @@ +# Copyright 2020 ACSONE +# @author: Simone Orsi +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo.addons.component.core import AbstractComponent + + +class GS1InputMessageMixin(AbstractComponent): + """Common gs1 input mixin. + """ + + _name = "edi.gs1.input.mixin" + _inherit = ["edi.component.input.mixin"] + + def process(self): + handler = self.backend._find_component( + ["edi.xml"], work_ctx={"schema_path": self.work.schema_path}, safe=False + ) + data = handler.parse_xml(self.exchage_record._get_file_content()) + self._process_data(data) + return True + + def _process_data(self, data): + NotImplementedError() From dc373060744c1093536bc201dc9393a9514e6a4f Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Thu, 11 Mar 2021 08:07:34 +0100 Subject: [PATCH 12/32] DROPME: add TODOs --- edi_gs1/data/exchange_type_data.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/edi_gs1/data/exchange_type_data.xml b/edi_gs1/data/exchange_type_data.xml index a8ff5ec98..5e2c3d316 100644 --- a/edi_gs1/data/exchange_type_data.xml +++ b/edi_gs1/data/exchange_type_data.xml @@ -7,6 +7,7 @@ input {record_name}-{type.code}-{dt} xml + From 9955d4affd2e76e4681b8730f8ff3425ba5c11b5 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Mon, 15 Mar 2021 10:01:21 +0100 Subject: [PATCH 13/32] edi_gs1: adapt tests to core changes --- edi_gs1/tests/common.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/edi_gs1/tests/common.py b/edi_gs1/tests/common.py index d0fa2e574..c3df549bb 100644 --- a/edi_gs1/tests/common.py +++ b/edi_gs1/tests/common.py @@ -55,5 +55,8 @@ def read_test_file(self, filename): def _get_xml_handler(self): return self.backend._find_component( - ["edi.xml"], work_ctx={"schema_path": self._schema_path}, safe=False, + self.backend._name, + ["edi.xml"], + work_ctx={"schema_path": self._schema_path}, + safe=False, ) From bacc68680a2dbc6310d0cc1bedc89ee7310b2666 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Honor=C3=A9?= Date: Wed, 17 Mar 2021 11:39:55 +0100 Subject: [PATCH 14/32] [ADD] edi_gs1: add gs1_code on UoM (+ auto init based on default values) + add a contact details qweb template --- edi_gs1/__init__.py | 1 + edi_gs1/__manifest__.py | 5 +- .../data/contact_details_qweb_template.xml | 44 ++++++++++++ edi_gs1/hooks.py | 14 ++++ edi_gs1/models/__init__.py | 1 + edi_gs1/models/uom_uom.py | 70 +++++++++++++++++++ edi_gs1/tests/common.py | 3 + edi_gs1/views/uom_uom.xml | 16 +++++ 8 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 edi_gs1/data/contact_details_qweb_template.xml create mode 100644 edi_gs1/hooks.py create mode 100644 edi_gs1/models/uom_uom.py create mode 100644 edi_gs1/views/uom_uom.xml diff --git a/edi_gs1/__init__.py b/edi_gs1/__init__.py index 0f00a6730..d20ee91b9 100644 --- a/edi_gs1/__init__.py +++ b/edi_gs1/__init__.py @@ -1,2 +1,3 @@ from . import models from . import components +from .hooks import post_init_hook diff --git a/edi_gs1/__manifest__.py b/edi_gs1/__manifest__.py index b83cfc168..478e8ff81 100644 --- a/edi_gs1/__manifest__.py +++ b/edi_gs1/__manifest__.py @@ -10,7 +10,7 @@ "development_status": "Alpha", "license": "AGPL-3", "author": "ACSONE,Odoo Community Association (OCA)", - "depends": ["component", "edi_exchange_template", "edi_xml"], + "depends": ["component", "edi_exchange_template", "edi_xml", "uom"], "external_dependencies": {"python": ["xmlschema"]}, "data": [ "security/gs1_backend_acl.xml", @@ -18,5 +18,8 @@ "data/exchange_type_data.xml", "data/business_header_qweb_template.xml", "data/business_header_output_template.xml", + "data/contact_details_qweb_template.xml", + "views/uom_uom.xml", ], + "post_init_hook": "post_init_hook", } diff --git a/edi_gs1/data/contact_details_qweb_template.xml b/edi_gs1/data/contact_details_qweb_template.xml new file mode 100644 index 000000000..8281408a9 --- /dev/null +++ b/edi_gs1/data/contact_details_qweb_template.xml @@ -0,0 +1,44 @@ + + + + diff --git a/edi_gs1/hooks.py b/edi_gs1/hooks.py new file mode 100644 index 000000000..b36b3f8b9 --- /dev/null +++ b/edi_gs1/hooks.py @@ -0,0 +1,14 @@ +# Copyright 2021 ACSONE SA/NV () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from odoo.api import SUPERUSER_ID, Environment + + +def post_init_hook(cr, registry): + """ + Hook used to init automatically some GS1 code on existing UoM + :param cr: database cursor + :param registry: + :return: + """ + env = Environment(cr, SUPERUSER_ID, {"tracking_disable": True}) + env["uom.uom"]._execute_gs1_map_code() diff --git a/edi_gs1/models/__init__.py b/edi_gs1/models/__init__.py index fe5182755..bf8796723 100644 --- a/edi_gs1/models/__init__.py +++ b/edi_gs1/models/__init__.py @@ -1,2 +1,3 @@ from . import edi_backend from . import res_partner +from . import uom_uom diff --git a/edi_gs1/models/uom_uom.py b/edi_gs1/models/uom_uom.py new file mode 100644 index 000000000..acc375296 --- /dev/null +++ b/edi_gs1/models/uom_uom.py @@ -0,0 +1,70 @@ +# Copyright 2020 ACSONE SA/NV () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from odoo import api, fields, models + + +class UomUom(models.Model): + _inherit = "uom.uom" + + gs1_code = fields.Char( + string="GS1 code", help="Code used to export with GS1 format" + ) + + @api.model + def _gs1_mapping_code(self): + """ + Get a dict with the mapping between the UoM code (from Odoo) and the GS1 code. + This mapping table is used into the init_hook of the module to automatically + init existing UoM. + key should be always in lower case! + Codes come from this url: + https://resources.gs1us.org/GS1-US-Data-Hub-Help-Center/ArtMID/3451/ + ArticleID/116/Unit-of-Measure-Codes + :return: dict + """ + map_code = { + "kg": "KGM", + "day": "DAY", + "days": "DAY", + "box": "BX", + "boxes": "BX", + "dozen": "DZN", + "dozens": "DZN", + "hours": "HUR", + "hour": "HUR", + "liters": "LTR", + "liter": "LTR", + "cm": "CMT", + "fl oz": "OZI", + "foot(ft)": "FOT", + "foot": "FOT", + "g": "GRM", + "gal": "GLI", + "gals": "GLI", + "inches": "INH", + "km": "KMT", + "m": "MTR", + } + return map_code + + @api.model + def _execute_gs1_map_code(self, overwrite_existing=False): + """ + Execute (or update) the gs1 mapping based on UoM name (lower case). + By default, it doesn't update UoM with a value into gs1_code. + But it can be forced by setting the overwrite_existing parameter to True. + :param overwrite_existing: bool + :return: bool + """ + domain = [("gs1_code", "=", False)] + if overwrite_existing: + domain = [] + uoms = self.env["uom.uom"].search(domain) + map_code = uoms._gs1_mapping_code() + for uom in uoms: + # Key always in lower case so the name too! + name = uom.name.lower() + gs1_code = map_code.get(name) + if gs1_code and uom.gs1_code != gs1_code: + uom.write({"gs1_code": gs1_code}) + return True diff --git a/edi_gs1/tests/common.py b/edi_gs1/tests/common.py index c3df549bb..cfa605f7c 100644 --- a/edi_gs1/tests/common.py +++ b/edi_gs1/tests/common.py @@ -31,6 +31,9 @@ def setUpClass(cls): # set fake GLN codes cls.lsp_partner.gln_code = "1".zfill(13) cls.lsc_partner.gln_code = "2".zfill(13) + # We have to trigger this gs1_code update manually in case of a submodule + # update them. + cls.env['uom.uom']._execute_gs1_map_code() @classmethod def _get_backend(cls): diff --git a/edi_gs1/views/uom_uom.xml b/edi_gs1/views/uom_uom.xml new file mode 100644 index 000000000..f6ef358ad --- /dev/null +++ b/edi_gs1/views/uom_uom.xml @@ -0,0 +1,16 @@ + + + + + uom.uom.form (in edi_gs1) + uom.uom + + + + + + + + + From 7d3a4f04e887c5f61a6ac5cd6aa08ba53f010fba Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Mon, 15 Mar 2021 10:06:54 +0100 Subject: [PATCH 15/32] edi_exchange_template: rework info provider mixins --- edi_gs1/components/output_mixin.py | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/edi_gs1/components/output_mixin.py b/edi_gs1/components/output_mixin.py index 9572b6c00..cd724d0e8 100644 --- a/edi_gs1/components/output_mixin.py +++ b/edi_gs1/components/output_mixin.py @@ -2,7 +2,6 @@ # @author: Simone Orsi # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo.tools import DotDict from odoo.addons.component.core import AbstractComponent @@ -12,30 +11,13 @@ class GS1OutputMessageMixin(AbstractComponent): """ _name = "edi.gs1.output.mixin" - _inherit = [ - "edi.output.mixin", - ] + _inherit = ["edi.output.info.mixin"] _work_context_validate_attrs = [ "exchange_record", "sender", "receiver", ] - @property - def record(self): - return self.work.exchange_record.record - - def generate_info(self): - return DotDict(self._generate_info()) - - def _generate_info(self): - raise NotImplementedError("You must provide `_generate_info`") - - @property - def replace_existing(self): - """Pass `replace_existing` to work context to change this value.""" - return getattr(self.work, "replace_existing", False) - def _document_action_code(self): # Brand new file code = "ADD" From 1bf575232c555a0aa22c9b50a56a699446ad1d16 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Thu, 18 Mar 2021 10:44:23 +0100 Subject: [PATCH 16/32] edi_gs1: add gs1_code to UoM GS1 standard requires UoM specific mapping. Here we add a specific field for it and provide mapping of values as well as auto compute for existing UoM. --- edi_gs1/tests/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edi_gs1/tests/common.py b/edi_gs1/tests/common.py index cfa605f7c..77690dfbf 100644 --- a/edi_gs1/tests/common.py +++ b/edi_gs1/tests/common.py @@ -33,7 +33,7 @@ def setUpClass(cls): cls.lsc_partner.gln_code = "2".zfill(13) # We have to trigger this gs1_code update manually in case of a submodule # update them. - cls.env['uom.uom']._execute_gs1_map_code() + cls.env["uom.uom"]._execute_gs1_map_code() @classmethod def _get_backend(cls): From e0849b5827b201f3063a83b8fbee7298a640658b Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Thu, 18 Mar 2021 10:46:48 +0100 Subject: [PATCH 17/32] edi_gs1_stock: improve outbound instruction * Add qweb tmpl for contact details * Fix fake res.partner record for shipper --- .../data/contact_details_qweb_template.xml | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/edi_gs1/data/contact_details_qweb_template.xml b/edi_gs1/data/contact_details_qweb_template.xml index 8281408a9..8cbe00ef1 100644 --- a/edi_gs1/data/contact_details_qweb_template.xml +++ b/edi_gs1/data/contact_details_qweb_template.xml @@ -9,35 +9,38 @@ t-esc="additionalPartyIdentification.value" />
- - - - - - - + + + + + + +
- + EMAIL - + TELEPHONE - + - + - - - - - - - + + + + + + + From 534a5034c1bae5e7a68b6a3899062f7bdc5a7f16 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Thu, 18 Mar 2021 11:51:19 +0100 Subject: [PATCH 18/32] edi_gs1: split ack data type + imp conf --- edi_gs1/__manifest__.py | 3 ++- edi_gs1/data/ack_in_exchange_type_data.xml | 27 +++++++++++++++++++++ edi_gs1/data/ack_out_exchange_type_data.xml | 20 +++++++++++++++ edi_gs1/data/exchange_type_data.xml | 20 --------------- 4 files changed, 49 insertions(+), 21 deletions(-) create mode 100644 edi_gs1/data/ack_in_exchange_type_data.xml create mode 100644 edi_gs1/data/ack_out_exchange_type_data.xml delete mode 100644 edi_gs1/data/exchange_type_data.xml diff --git a/edi_gs1/__manifest__.py b/edi_gs1/__manifest__.py index 478e8ff81..a1a34c5b8 100644 --- a/edi_gs1/__manifest__.py +++ b/edi_gs1/__manifest__.py @@ -15,7 +15,8 @@ "data": [ "security/gs1_backend_acl.xml", "data/gs1_backend_data.xml", - "data/exchange_type_data.xml", + "data/ack_in_exchange_type_data.xml", + "data/ack_out_exchange_type_data.xml", "data/business_header_qweb_template.xml", "data/business_header_output_template.xml", "data/contact_details_qweb_template.xml", diff --git a/edi_gs1/data/ack_in_exchange_type_data.xml b/edi_gs1/data/ack_in_exchange_type_data.xml new file mode 100644 index 000000000..d458c4f1d --- /dev/null +++ b/edi_gs1/data/ack_in_exchange_type_data.xml @@ -0,0 +1,27 @@ + + + + + GS1 ApplicationReceiptAcknowledgement + applicationReceiptAcknowledgement_in + input + + + xml + + components: + validate: + usage: edi.xml + work_ctx: + schema_path: edi_gs1:static/schemas/gs1/ecom/ApplicationReceiptAcknowledgement.xsd + process: + usage: gs1.in.ApplicationReceiptAcknowledgement + work_ctx: + schema_path: edi_gs1:static/schemas/gs1/ecom/ApplicationReceiptAcknowledgement.xsd + + + diff --git a/edi_gs1/data/ack_out_exchange_type_data.xml b/edi_gs1/data/ack_out_exchange_type_data.xml new file mode 100644 index 000000000..a22a5a276 --- /dev/null +++ b/edi_gs1/data/ack_out_exchange_type_data.xml @@ -0,0 +1,20 @@ + + + + + GS1 ApplicationReceiptAcknowledgement + applicationReceiptAcknowledgement_out + output + {record_name}-{type.code}-{dt} + xml + + + + components: + validate: + usage: edi.xml + work_ctx: + schema_path: edi_gs1:static/schemas/gs1/ecom/ApplicationReceiptAcknowledgement.xsd + + + diff --git a/edi_gs1/data/exchange_type_data.xml b/edi_gs1/data/exchange_type_data.xml deleted file mode 100644 index 5e2c3d316..000000000 --- a/edi_gs1/data/exchange_type_data.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - GS1 ApplicationReceiptAcknowledgement - applicationReceiptAcknowledgement_in - input - {record_name}-{type.code}-{dt} - xml - - - - - GS1 ApplicationReceiptAcknowledgement - applicationReceiptAcknowledgement_out - output - {record_name}-{type.code}-{dt} - xml - - From 906d007e78655e958c8a49c771b71e4db663135e Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Thu, 18 Mar 2021 11:54:16 +0100 Subject: [PATCH 19/32] edi_gs1: draft ack input process component --- edi_gs1/components/__init__.py | 1 + edi_gs1/components/application_receipt_ack.py | 20 +++++++++++++------ edi_gs1/components/input_mixin.py | 4 +++- edi_gs1/data/ack_in_exchange_type_data.xml | 2 -- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/edi_gs1/components/__init__.py b/edi_gs1/components/__init__.py index b4bd9cc66..ed79a5d61 100644 --- a/edi_gs1/components/__init__.py +++ b/edi_gs1/components/__init__.py @@ -1,2 +1,3 @@ from . import output_mixin from . import input_mixin +from . import application_receipt_ack diff --git a/edi_gs1/components/application_receipt_ack.py b/edi_gs1/components/application_receipt_ack.py index ad04c5eee..f97d68c4d 100644 --- a/edi_gs1/components/application_receipt_ack.py +++ b/edi_gs1/components/application_receipt_ack.py @@ -12,19 +12,27 @@ class ApplicationReceiptAcknowledgement(Component): """ _name = "gs1.input.applicationReceiptAcknowledgement" - _inherit = "gs1.input.mixin" + _inherit = "edi.gs1.input.mixin" _usage = "gs1.in.ApplicationReceiptAcknowledgement" _xsd_schema_path = "static/schemas/gs1/ecom/ApplicationReceiptAcknowledgement.xsd" - def get_status(self, result): + def _process_data(self, data): + if not self.is_ok(data): + # TODO: give a proper message based on the real state + # options: ERROR, RECEIVED, WARNING + raise exceptions.ValidationError(_("Exchange not received")) + + def is_ok(self, data): + return self._get_status(data) == "RECEIVED" + + def _get_status(self, data): try: - ack = result["applicationReceiptAcknowledgement"][0] + ack = data["applicationReceiptAcknowledgement"][0] except IndexError: + # Maybe raise an EDIValidationError? raise exceptions.ValidationError( _("applicationReceiptAcknowledgement element not found!") ) status_wrapper = ack["applicationResponseDocumentLevel"][0] + # options: ERROR, RECEIVED, WARNING return status_wrapper["applicationResponseStatusCode"] - - def is_ok(self, result): - return self.get_status(result) == "RECEIVED" diff --git a/edi_gs1/components/input_mixin.py b/edi_gs1/components/input_mixin.py index 13108ccda..c840dc7c9 100644 --- a/edi_gs1/components/input_mixin.py +++ b/edi_gs1/components/input_mixin.py @@ -11,10 +11,12 @@ class GS1InputMessageMixin(AbstractComponent): _name = "edi.gs1.input.mixin" _inherit = ["edi.component.input.mixin"] + _xsd_schema_path = None def process(self): + schema_path = self._xsd_schema_path or self.work.schema_path handler = self.backend._find_component( - ["edi.xml"], work_ctx={"schema_path": self.work.schema_path}, safe=False + ["edi.xml"], work_ctx={"schema_path": schema_path}, safe=False ) data = handler.parse_xml(self.exchage_record._get_file_content()) self._process_data(data) diff --git a/edi_gs1/data/ack_in_exchange_type_data.xml b/edi_gs1/data/ack_in_exchange_type_data.xml index d458c4f1d..572c905af 100644 --- a/edi_gs1/data/ack_in_exchange_type_data.xml +++ b/edi_gs1/data/ack_in_exchange_type_data.xml @@ -20,8 +20,6 @@ schema_path: edi_gs1:static/schemas/gs1/ecom/ApplicationReceiptAcknowledgement.xsd process: usage: gs1.in.ApplicationReceiptAcknowledgement - work_ctx: - schema_path: edi_gs1:static/schemas/gs1/ecom/ApplicationReceiptAcknowledgement.xsd
From 226c1f2cf1ad16787360096eac8ab4b865322d95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Honor=C3=A9?= Date: Wed, 31 Mar 2021 10:32:22 +0200 Subject: [PATCH 20/32] [ADD] edi_gs1: lsp and lsc partners on backend --- edi_gs1/__manifest__.py | 2 ++ edi_gs1/models/edi_backend.py | 1 - edi_gs1/views/edi_backend.xml | 19 +++++++++++++++++++ edi_gs1/views/res_partner.xml | 24 ++++++++++++++++++++++++ 4 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 edi_gs1/views/edi_backend.xml create mode 100644 edi_gs1/views/res_partner.xml diff --git a/edi_gs1/__manifest__.py b/edi_gs1/__manifest__.py index a1a34c5b8..b9ee275b8 100644 --- a/edi_gs1/__manifest__.py +++ b/edi_gs1/__manifest__.py @@ -20,6 +20,8 @@ "data/business_header_qweb_template.xml", "data/business_header_output_template.xml", "data/contact_details_qweb_template.xml", + "views/res_partner.xml", + "views/edi_backend.xml", "views/uom_uom.xml", ], "post_init_hook": "post_init_hook", diff --git a/edi_gs1/models/edi_backend.py b/edi_gs1/models/edi_backend.py index 2dd74200d..a642e07e7 100644 --- a/edi_gs1/models/edi_backend.py +++ b/edi_gs1/models/edi_backend.py @@ -9,7 +9,6 @@ class EDIBackend(models.Model): _inherit = "edi.backend" # TODO: find another way to configure this w/out polluting edi backend - # TODO: add to view lsp_partner_id = fields.Many2one( string="Logistic Services Provider (LSP)", comodel_name="res.partner", diff --git a/edi_gs1/views/edi_backend.xml b/edi_gs1/views/edi_backend.xml new file mode 100644 index 000000000..b988efeae --- /dev/null +++ b/edi_gs1/views/edi_backend.xml @@ -0,0 +1,19 @@ + + + + + edi.backend.form (in edi_gs1) + edi.backend + + + + + + + + + + + + diff --git a/edi_gs1/views/res_partner.xml b/edi_gs1/views/res_partner.xml new file mode 100644 index 000000000..5ba50adfc --- /dev/null +++ b/edi_gs1/views/res_partner.xml @@ -0,0 +1,24 @@ + + + + + res.partner.form (in edi_gs1) + res.partner + + + + + + + + + + + + + + + + + From a86ecdea4fe83318aedfcef99ab583a34fb59ccc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Honor=C3=A9?= Date: Wed, 31 Mar 2021 10:37:53 +0200 Subject: [PATCH 21/32] [FIX] edi_gs1: empty doc_type invalidate the document generation --- edi_gs1/data/business_header_qweb_template.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edi_gs1/data/business_header_qweb_template.xml b/edi_gs1/data/business_header_qweb_template.xml index 2ab74ea3b..033eaad66 100644 --- a/edi_gs1/data/business_header_qweb_template.xml +++ b/edi_gs1/data/business_header_qweb_template.xml @@ -53,7 +53,7 @@ 100002 - + false Date: Wed, 31 Mar 2021 10:44:22 +0200 Subject: [PATCH 22/32] [UPD] edi_gs1: update group on security --- edi_gs1/security/gs1_backend_acl.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/edi_gs1/security/gs1_backend_acl.xml b/edi_gs1/security/gs1_backend_acl.xml index bef3f0f91..eda64df05 100644 --- a/edi_gs1/security/gs1_backend_acl.xml +++ b/edi_gs1/security/gs1_backend_acl.xml @@ -5,6 +5,7 @@ - + xml components: diff --git a/edi_gs1/views/edi_backend.xml b/edi_gs1/views/edi_backend.xml index b988efeae..c3f91b523 100644 --- a/edi_gs1/views/edi_backend.xml +++ b/edi_gs1/views/edi_backend.xml @@ -1,17 +1,17 @@ - + edi.backend.form (in edi_gs1) edi.backend - - + + - - + + diff --git a/edi_gs1/views/uom_uom.xml b/edi_gs1/views/uom_uom.xml index f6ef358ad..f37265ae1 100644 --- a/edi_gs1/views/uom_uom.xml +++ b/edi_gs1/views/uom_uom.xml @@ -1,15 +1,15 @@ - + uom.uom.form (in edi_gs1) uom.uom - - + + - + From e5f8297846d95f2745d3ecec5ad63f315ea00285 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Honor=C3=A9?= Date: Fri, 16 Apr 2021 11:45:24 +0200 Subject: [PATCH 24/32] [FIX] Fix unit test about output generation + missing security rules --- edi_gs1/tests/test_business_header.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/edi_gs1/tests/test_business_header.py b/edi_gs1/tests/test_business_header.py index 0896be8e6..40c5309ef 100644 --- a/edi_gs1/tests/test_business_header.py +++ b/edi_gs1/tests/test_business_header.py @@ -54,8 +54,8 @@ def test_template_render_values(self): @freeze_time("2020-07-08 07:30:00") def test_xml(self): - output = self.bh_tmpl.generate_output( - self.exc_record, sender=self.lsc_partner, receiver=self.lsp_partner, + output = self.bh_tmpl.exchange_generate( + self.exc_record, sender=self.lsc_partner, receiver=self.lsp_partner ) expected = """ From af726762116f184bd3e359e273e504ae25667bbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Honor=C3=A9?= Date: Fri, 2 Jul 2021 14:07:07 +0200 Subject: [PATCH 25/32] [UPD] GS1 .xsd files update --- edi_gs1/static/schemas/gs1/ecom/WarehousingCommon.xsd | 6 +++--- .../schemas/gs1/ecom/WarehousingInboundInstruction.xsd | 6 +++--- .../schemas/gs1/ecom/WarehousingInboundNotification.xsd | 6 +++--- .../schemas/gs1/ecom/WarehousingOutboundInstruction.xsd | 6 +++--- .../schemas/gs1/ecom/WarehousingOutboundNotification.xsd | 6 +++--- edi_gs1/static/schemas/gs1/ecom/eComCommon.xsd | 6 +++--- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/edi_gs1/static/schemas/gs1/ecom/WarehousingCommon.xsd b/edi_gs1/static/schemas/gs1/ecom/WarehousingCommon.xsd index 2b2add5fb..3395efe4d 100644 --- a/edi_gs1/static/schemas/gs1/ecom/WarehousingCommon.xsd +++ b/edi_gs1/static/schemas/gs1/ecom/WarehousingCommon.xsd @@ -1,5 +1,5 @@ - + - + - + - + - + - + Date: Fri, 2 Jul 2021 14:08:45 +0200 Subject: [PATCH 26/32] [FIX] edi_gs1: fix typo and find_component. --- edi_gs1/__manifest__.py | 2 +- edi_gs1/components/input_mixin.py | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/edi_gs1/__manifest__.py b/edi_gs1/__manifest__.py index f0f134032..91bbbaa43 100644 --- a/edi_gs1/__manifest__.py +++ b/edi_gs1/__manifest__.py @@ -12,7 +12,7 @@ "website": "https://github.com/OCA/edi", "author": "ACSONE,Odoo Community Association (OCA)", "depends": ["component", "edi_exchange_template", "edi_xml", "uom"], - "external_dependencies": {"python": ["xmlschema"]}, + "external_dependencies": {"python": ["xmlschema", "xmlunittest"]}, "data": [ "security/gs1_backend_acl.xml", "data/gs1_backend_data.xml", diff --git a/edi_gs1/components/input_mixin.py b/edi_gs1/components/input_mixin.py index c840dc7c9..6ee3444aa 100644 --- a/edi_gs1/components/input_mixin.py +++ b/edi_gs1/components/input_mixin.py @@ -16,9 +16,12 @@ class GS1InputMessageMixin(AbstractComponent): def process(self): schema_path = self._xsd_schema_path or self.work.schema_path handler = self.backend._find_component( - ["edi.xml"], work_ctx={"schema_path": schema_path}, safe=False + self.backend._name, + ["edi.xml"], + work_ctx={"schema_path": schema_path}, + safe=False, ) - data = handler.parse_xml(self.exchage_record._get_file_content()) + data = handler.parse_xml(self.exchange_record._get_file_content()) self._process_data(data) return True From a900c94a9a0a11e282ab58854ce76b259ff4216f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Honor=C3=A9?= Date: Fri, 9 Jul 2021 15:03:24 +0200 Subject: [PATCH 27/32] [FIX] edi: fix due to refactor --- edi_gs1/views/edi_backend.xml | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/edi_gs1/views/edi_backend.xml b/edi_gs1/views/edi_backend.xml index c3f91b523..33aacd56a 100644 --- a/edi_gs1/views/edi_backend.xml +++ b/edi_gs1/views/edi_backend.xml @@ -8,12 +8,16 @@ - - - - - - + + + + + + + + + + From a8a2090fe3c1a15edca9abb2759fd0e9147de160 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Honor=C3=A9?= Date: Thu, 24 Nov 2022 17:31:01 +0100 Subject: [PATCH 28/32] [GS1] Generate the Warehouse Outbound Notification ACK --- edi_gs1/components/__init__.py | 1 + edi_gs1/components/application_send_ack.py | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 edi_gs1/components/application_send_ack.py diff --git a/edi_gs1/components/__init__.py b/edi_gs1/components/__init__.py index ed79a5d61..9ccb8dd7d 100644 --- a/edi_gs1/components/__init__.py +++ b/edi_gs1/components/__init__.py @@ -1,3 +1,4 @@ from . import output_mixin from . import input_mixin from . import application_receipt_ack +from . import application_send_ack diff --git a/edi_gs1/components/application_send_ack.py b/edi_gs1/components/application_send_ack.py new file mode 100644 index 000000000..e8fa69696 --- /dev/null +++ b/edi_gs1/components/application_send_ack.py @@ -0,0 +1,16 @@ +# Copyright 2020 ACSONE +# @author: Simone Orsi +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + + +from odoo.addons.component.core import Component + + +class ApplicationSendAcknowledgement(Component): + """Generate GS1 applicationSendAcknowledgement. + """ + + _name = "gs1.output.applicationSendAcknowledgement" + _inherit = "edi.gs1.output.mixin" + _usage = "gs1.out.ApplicationSendAcknowledgement" + _xsd_schema_path = "static/schemas/gs1/ecom/ApplicationReceiptAcknowledgement.xsd" From 6e2f956c93f2a9df3d289c50662fed076554e43b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Honor=C3=A9?= Date: Thu, 15 Dec 2022 16:07:19 +0100 Subject: [PATCH 29/32] [FIX] edi_gs1: remove useless _xsd_schema_path --- edi_gs1/components/application_receipt_ack.py | 1 - 1 file changed, 1 deletion(-) diff --git a/edi_gs1/components/application_receipt_ack.py b/edi_gs1/components/application_receipt_ack.py index f97d68c4d..e95a0fbb3 100644 --- a/edi_gs1/components/application_receipt_ack.py +++ b/edi_gs1/components/application_receipt_ack.py @@ -14,7 +14,6 @@ class ApplicationReceiptAcknowledgement(Component): _name = "gs1.input.applicationReceiptAcknowledgement" _inherit = "edi.gs1.input.mixin" _usage = "gs1.in.ApplicationReceiptAcknowledgement" - _xsd_schema_path = "static/schemas/gs1/ecom/ApplicationReceiptAcknowledgement.xsd" def _process_data(self, data): if not self.is_ok(data): From 3373ad4e3afcb4788c3dec7dbc6fe3935c4110bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Honor=C3=A9?= Date: Fri, 19 Jun 2026 15:08:05 +0200 Subject: [PATCH 30/32] [IMP] edi_gs1_oca: pre-commit auto fixes --- edi_gs1/readme/CONTRIBUTORS.rst | 1 - edi_gs1/readme/DESCRIPTION.rst | 9 --- {edi_gs1 => edi_gs1_oca}/README.rst | 56 ++++++++------ {edi_gs1 => edi_gs1_oca}/TODO | 0 {edi_gs1 => edi_gs1_oca}/__init__.py | 0 {edi_gs1 => edi_gs1_oca}/__manifest__.py | 2 +- .../components/__init__.py | 0 .../components/application_receipt_ack.py | 3 +- .../components/application_send_ack.py | 3 +- .../components/input_mixin.py | 3 +- .../components/output_mixin.py | 3 +- .../data/ack_in_exchange_type_data.xml | 0 .../data/ack_out_exchange_type_data.xml | 0 .../data/business_header_output_template.xml | 0 .../data/business_header_qweb_template.xml | 0 .../data/contact_details_qweb_template.xml | 0 .../data/gs1_backend_data.xml | 0 {edi_gs1 => edi_gs1_oca}/hooks.py | 0 {edi_gs1 => edi_gs1_oca}/models/__init__.py | 0 .../models/edi_backend.py | 0 .../models/res_partner.py | 0 {edi_gs1 => edi_gs1_oca}/models/uom_uom.py | 0 edi_gs1_oca/pyproject.toml | 3 + edi_gs1_oca/readme/CONTRIBUTORS.md | 1 + edi_gs1_oca/readme/DESCRIPTION.md | 10 +++ .../security/gs1_backend_acl.xml | 0 .../static/description/icon.png | Bin .../static/description/index.html | 73 ++++++++++-------- .../ApplicationReceiptAcknowledgement.xsd | 0 .../schemas/gs1/ecom/WarehousingCommon.xsd | 0 .../ecom/WarehousingInboundInstruction.xsd | 0 .../ecom/WarehousingInboundNotification.xsd | 0 .../ecom/WarehousingOutboundInstruction.xsd | 0 .../ecom/WarehousingOutboundNotification.xsd | 0 .../static/schemas/gs1/ecom/eComCommon.xsd | 0 .../schemas/gs1/shared/SharedCommon.xsd | 0 .../static/schemas/sbdh/BasicTypes.xsd | 0 .../static/schemas/sbdh/BusinessScope.xsd | 0 .../schemas/sbdh/DocumentIdentification.xsd | 0 .../static/schemas/sbdh/Manifest.xsd | 0 .../static/schemas/sbdh/Partner.xsd | 0 .../sbdh/StandardBusinessDocumentHeader.xsd | 0 {edi_gs1 => edi_gs1_oca}/tests/__init__.py | 0 {edi_gs1 => edi_gs1_oca}/tests/common.py | 3 +- ...icationReceiptAcknowledgement.example1.xml | 0 .../tests/test_business_header.py | 1 - .../views/edi_backend.xml | 0 .../views/res_partner.xml | 0 {edi_gs1 => edi_gs1_oca}/views/uom_uom.xml | 0 {edi_gs1 => edi_gs1_oca}/xmler.py | 1 - requirements.txt | 2 + 51 files changed, 96 insertions(+), 78 deletions(-) delete mode 100644 edi_gs1/readme/CONTRIBUTORS.rst delete mode 100644 edi_gs1/readme/DESCRIPTION.rst rename {edi_gs1 => edi_gs1_oca}/README.rst (53%) rename {edi_gs1 => edi_gs1_oca}/TODO (100%) rename {edi_gs1 => edi_gs1_oca}/__init__.py (100%) rename {edi_gs1 => edi_gs1_oca}/__manifest__.py (94%) rename {edi_gs1 => edi_gs1_oca}/components/__init__.py (100%) rename {edi_gs1 => edi_gs1_oca}/components/application_receipt_ack.py (95%) rename {edi_gs1 => edi_gs1_oca}/components/application_send_ack.py (88%) rename {edi_gs1 => edi_gs1_oca}/components/input_mixin.py (95%) rename {edi_gs1 => edi_gs1_oca}/components/output_mixin.py (93%) rename {edi_gs1 => edi_gs1_oca}/data/ack_in_exchange_type_data.xml (100%) rename {edi_gs1 => edi_gs1_oca}/data/ack_out_exchange_type_data.xml (100%) rename {edi_gs1 => edi_gs1_oca}/data/business_header_output_template.xml (100%) rename {edi_gs1 => edi_gs1_oca}/data/business_header_qweb_template.xml (100%) rename {edi_gs1 => edi_gs1_oca}/data/contact_details_qweb_template.xml (100%) rename {edi_gs1 => edi_gs1_oca}/data/gs1_backend_data.xml (100%) rename {edi_gs1 => edi_gs1_oca}/hooks.py (100%) rename {edi_gs1 => edi_gs1_oca}/models/__init__.py (100%) rename {edi_gs1 => edi_gs1_oca}/models/edi_backend.py (100%) rename {edi_gs1 => edi_gs1_oca}/models/res_partner.py (100%) rename {edi_gs1 => edi_gs1_oca}/models/uom_uom.py (100%) create mode 100644 edi_gs1_oca/pyproject.toml create mode 100644 edi_gs1_oca/readme/CONTRIBUTORS.md create mode 100644 edi_gs1_oca/readme/DESCRIPTION.md rename {edi_gs1 => edi_gs1_oca}/security/gs1_backend_acl.xml (100%) rename {edi_gs1 => edi_gs1_oca}/static/description/icon.png (100%) rename {edi_gs1 => edi_gs1_oca}/static/description/index.html (72%) rename {edi_gs1 => edi_gs1_oca}/static/schemas/gs1/ecom/ApplicationReceiptAcknowledgement.xsd (100%) rename {edi_gs1 => edi_gs1_oca}/static/schemas/gs1/ecom/WarehousingCommon.xsd (100%) rename {edi_gs1 => edi_gs1_oca}/static/schemas/gs1/ecom/WarehousingInboundInstruction.xsd (100%) rename {edi_gs1 => edi_gs1_oca}/static/schemas/gs1/ecom/WarehousingInboundNotification.xsd (100%) rename {edi_gs1 => edi_gs1_oca}/static/schemas/gs1/ecom/WarehousingOutboundInstruction.xsd (100%) rename {edi_gs1 => edi_gs1_oca}/static/schemas/gs1/ecom/WarehousingOutboundNotification.xsd (100%) rename {edi_gs1 => edi_gs1_oca}/static/schemas/gs1/ecom/eComCommon.xsd (100%) rename {edi_gs1 => edi_gs1_oca}/static/schemas/gs1/shared/SharedCommon.xsd (100%) rename {edi_gs1 => edi_gs1_oca}/static/schemas/sbdh/BasicTypes.xsd (100%) rename {edi_gs1 => edi_gs1_oca}/static/schemas/sbdh/BusinessScope.xsd (100%) rename {edi_gs1 => edi_gs1_oca}/static/schemas/sbdh/DocumentIdentification.xsd (100%) rename {edi_gs1 => edi_gs1_oca}/static/schemas/sbdh/Manifest.xsd (100%) rename {edi_gs1 => edi_gs1_oca}/static/schemas/sbdh/Partner.xsd (100%) rename {edi_gs1 => edi_gs1_oca}/static/schemas/sbdh/StandardBusinessDocumentHeader.xsd (100%) rename {edi_gs1 => edi_gs1_oca}/tests/__init__.py (100%) rename {edi_gs1 => edi_gs1_oca}/tests/common.py (98%) rename {edi_gs1 => edi_gs1_oca}/tests/examples/ApplicationReceiptAcknowledgement.example1.xml (100%) rename {edi_gs1 => edi_gs1_oca}/tests/test_business_header.py (99%) rename {edi_gs1 => edi_gs1_oca}/views/edi_backend.xml (100%) rename {edi_gs1 => edi_gs1_oca}/views/res_partner.xml (100%) rename {edi_gs1 => edi_gs1_oca}/views/uom_uom.xml (100%) rename {edi_gs1 => edi_gs1_oca}/xmler.py (99%) diff --git a/edi_gs1/readme/CONTRIBUTORS.rst b/edi_gs1/readme/CONTRIBUTORS.rst deleted file mode 100644 index f583948be..000000000 --- a/edi_gs1/readme/CONTRIBUTORS.rst +++ /dev/null @@ -1 +0,0 @@ -* Simone Orsi diff --git a/edi_gs1/readme/DESCRIPTION.rst b/edi_gs1/readme/DESCRIPTION.rst deleted file mode 100644 index 491e592cf..000000000 --- a/edi_gs1/readme/DESCRIPTION.rst +++ /dev/null @@ -1,9 +0,0 @@ -This is the base module for the implementation of the -`GS1 -The Global Language of Business `_ standard. - -This module contains methods to generate and parse XML files compliant with this standard. - -Beside basic machinery it provides common components to generate common GS1 schema elements. -Namely: - - * `StandardBusinessDocumentHeader` diff --git a/edi_gs1/README.rst b/edi_gs1_oca/README.rst similarity index 53% rename from edi_gs1/README.rst rename to edi_gs1_oca/README.rst index 14f2fee15..75a3541b5 100644 --- a/edi_gs1/README.rst +++ b/edi_gs1_oca/README.rst @@ -1,39 +1,47 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + ============ Base GS1 EDI ============ -.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:a5ab15a8b1dea2fd4da5955af650a8db9296c1a96cb53d4d7d98188d059a14da + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png :target: https://odoo-community.org/page/development-status :alt: Alpha -.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png +.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 -.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fedi-lightgray.png?logo=github - :target: https://github.com/OCA/edi/tree/13.0/edi_gs1 - :alt: OCA/edi +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fedi--framework-lightgray.png?logo=github + :target: https://github.com/OCA/edi-framework/tree/19.0/edi_gs1_oca + :alt: OCA/edi-framework .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/edi-13-0/edi-13-0-edi_gs1 + :target: https://translation.odoo-community.org/projects/edi-framework-19-0/edi-framework-19-0-edi_gs1_oca :alt: Translate me on Weblate -.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png - :target: https://runbot.odoo-community.org/runbot/226/13.0 - :alt: Try me on Runbot +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/edi-framework&target_branch=19.0 + :alt: Try me on Runboat -|badge1| |badge2| |badge3| |badge4| |badge5| +|badge1| |badge2| |badge3| |badge4| |badge5| -This is the base module for the implementation of the -`GS1 -The Global Language of Business `_ standard. +This is the base module for the implementation of the `GS1 -The Global +Language of Business `__ standard. -This module contains methods to generate and parse XML files compliant with this standard. +This module contains methods to generate and parse XML files compliant +with this standard. -Beside basic machinery it provides common components to generate common GS1 schema elements. -Namely: +Beside basic machinery it provides common components to generate common +GS1 schema elements. Namely: - * `StandardBusinessDocumentHeader` + - StandardBusinessDocumentHeader .. IMPORTANT:: This is an alpha version, the data model and design can change at any time without warning. @@ -48,10 +56,10 @@ Namely: Bug Tracker =========== -Bugs are tracked on `GitHub Issues `_. +Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. -If you spotted it first, help us smashing it by providing a detailed and welcomed -`feedback `_. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -59,17 +67,17 @@ Credits ======= Authors -~~~~~~~ +------- * ACSONE Contributors -~~~~~~~~~~~~ +------------ -* Simone Orsi +- Simone Orsi Maintainers -~~~~~~~~~~~ +----------- This module is maintained by the OCA. @@ -81,6 +89,6 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. -This module is part of the `OCA/edi `_ project on GitHub. +This module is part of the `OCA/edi-framework `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/edi_gs1/TODO b/edi_gs1_oca/TODO similarity index 100% rename from edi_gs1/TODO rename to edi_gs1_oca/TODO diff --git a/edi_gs1/__init__.py b/edi_gs1_oca/__init__.py similarity index 100% rename from edi_gs1/__init__.py rename to edi_gs1_oca/__init__.py diff --git a/edi_gs1/__manifest__.py b/edi_gs1_oca/__manifest__.py similarity index 94% rename from edi_gs1/__manifest__.py rename to edi_gs1_oca/__manifest__.py index 91bbbaa43..dcf600e9b 100644 --- a/edi_gs1/__manifest__.py +++ b/edi_gs1_oca/__manifest__.py @@ -9,7 +9,7 @@ "version": "13.0.1.0.0", "development_status": "Alpha", "license": "AGPL-3", - "website": "https://github.com/OCA/edi", + "website": "https://github.com/OCA/edi-framework", "author": "ACSONE,Odoo Community Association (OCA)", "depends": ["component", "edi_exchange_template", "edi_xml", "uom"], "external_dependencies": {"python": ["xmlschema", "xmlunittest"]}, diff --git a/edi_gs1/components/__init__.py b/edi_gs1_oca/components/__init__.py similarity index 100% rename from edi_gs1/components/__init__.py rename to edi_gs1_oca/components/__init__.py diff --git a/edi_gs1/components/application_receipt_ack.py b/edi_gs1_oca/components/application_receipt_ack.py similarity index 95% rename from edi_gs1/components/application_receipt_ack.py rename to edi_gs1_oca/components/application_receipt_ack.py index e95a0fbb3..f9033e023 100644 --- a/edi_gs1/components/application_receipt_ack.py +++ b/edi_gs1_oca/components/application_receipt_ack.py @@ -8,8 +8,7 @@ class ApplicationReceiptAcknowledgement(Component): - """Parse GS1 applicationReceiptAcknowledgement. - """ + """Parse GS1 applicationReceiptAcknowledgement.""" _name = "gs1.input.applicationReceiptAcknowledgement" _inherit = "edi.gs1.input.mixin" diff --git a/edi_gs1/components/application_send_ack.py b/edi_gs1_oca/components/application_send_ack.py similarity index 88% rename from edi_gs1/components/application_send_ack.py rename to edi_gs1_oca/components/application_send_ack.py index e8fa69696..b1df370d3 100644 --- a/edi_gs1/components/application_send_ack.py +++ b/edi_gs1_oca/components/application_send_ack.py @@ -7,8 +7,7 @@ class ApplicationSendAcknowledgement(Component): - """Generate GS1 applicationSendAcknowledgement. - """ + """Generate GS1 applicationSendAcknowledgement.""" _name = "gs1.output.applicationSendAcknowledgement" _inherit = "edi.gs1.output.mixin" diff --git a/edi_gs1/components/input_mixin.py b/edi_gs1_oca/components/input_mixin.py similarity index 95% rename from edi_gs1/components/input_mixin.py rename to edi_gs1_oca/components/input_mixin.py index 6ee3444aa..b09db3a03 100644 --- a/edi_gs1/components/input_mixin.py +++ b/edi_gs1_oca/components/input_mixin.py @@ -6,8 +6,7 @@ class GS1InputMessageMixin(AbstractComponent): - """Common gs1 input mixin. - """ + """Common gs1 input mixin.""" _name = "edi.gs1.input.mixin" _inherit = ["edi.component.input.mixin"] diff --git a/edi_gs1/components/output_mixin.py b/edi_gs1_oca/components/output_mixin.py similarity index 93% rename from edi_gs1/components/output_mixin.py rename to edi_gs1_oca/components/output_mixin.py index cd724d0e8..1f0895217 100644 --- a/edi_gs1/components/output_mixin.py +++ b/edi_gs1_oca/components/output_mixin.py @@ -7,8 +7,7 @@ class GS1OutputMessageMixin(AbstractComponent): - """Common gs1 output mixin. - """ + """Common gs1 output mixin.""" _name = "edi.gs1.output.mixin" _inherit = ["edi.output.info.mixin"] diff --git a/edi_gs1/data/ack_in_exchange_type_data.xml b/edi_gs1_oca/data/ack_in_exchange_type_data.xml similarity index 100% rename from edi_gs1/data/ack_in_exchange_type_data.xml rename to edi_gs1_oca/data/ack_in_exchange_type_data.xml diff --git a/edi_gs1/data/ack_out_exchange_type_data.xml b/edi_gs1_oca/data/ack_out_exchange_type_data.xml similarity index 100% rename from edi_gs1/data/ack_out_exchange_type_data.xml rename to edi_gs1_oca/data/ack_out_exchange_type_data.xml diff --git a/edi_gs1/data/business_header_output_template.xml b/edi_gs1_oca/data/business_header_output_template.xml similarity index 100% rename from edi_gs1/data/business_header_output_template.xml rename to edi_gs1_oca/data/business_header_output_template.xml diff --git a/edi_gs1/data/business_header_qweb_template.xml b/edi_gs1_oca/data/business_header_qweb_template.xml similarity index 100% rename from edi_gs1/data/business_header_qweb_template.xml rename to edi_gs1_oca/data/business_header_qweb_template.xml diff --git a/edi_gs1/data/contact_details_qweb_template.xml b/edi_gs1_oca/data/contact_details_qweb_template.xml similarity index 100% rename from edi_gs1/data/contact_details_qweb_template.xml rename to edi_gs1_oca/data/contact_details_qweb_template.xml diff --git a/edi_gs1/data/gs1_backend_data.xml b/edi_gs1_oca/data/gs1_backend_data.xml similarity index 100% rename from edi_gs1/data/gs1_backend_data.xml rename to edi_gs1_oca/data/gs1_backend_data.xml diff --git a/edi_gs1/hooks.py b/edi_gs1_oca/hooks.py similarity index 100% rename from edi_gs1/hooks.py rename to edi_gs1_oca/hooks.py diff --git a/edi_gs1/models/__init__.py b/edi_gs1_oca/models/__init__.py similarity index 100% rename from edi_gs1/models/__init__.py rename to edi_gs1_oca/models/__init__.py diff --git a/edi_gs1/models/edi_backend.py b/edi_gs1_oca/models/edi_backend.py similarity index 100% rename from edi_gs1/models/edi_backend.py rename to edi_gs1_oca/models/edi_backend.py diff --git a/edi_gs1/models/res_partner.py b/edi_gs1_oca/models/res_partner.py similarity index 100% rename from edi_gs1/models/res_partner.py rename to edi_gs1_oca/models/res_partner.py diff --git a/edi_gs1/models/uom_uom.py b/edi_gs1_oca/models/uom_uom.py similarity index 100% rename from edi_gs1/models/uom_uom.py rename to edi_gs1_oca/models/uom_uom.py diff --git a/edi_gs1_oca/pyproject.toml b/edi_gs1_oca/pyproject.toml new file mode 100644 index 000000000..4231d0ccc --- /dev/null +++ b/edi_gs1_oca/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/edi_gs1_oca/readme/CONTRIBUTORS.md b/edi_gs1_oca/readme/CONTRIBUTORS.md new file mode 100644 index 000000000..5792655b0 --- /dev/null +++ b/edi_gs1_oca/readme/CONTRIBUTORS.md @@ -0,0 +1 @@ +- Simone Orsi \<\> diff --git a/edi_gs1_oca/readme/DESCRIPTION.md b/edi_gs1_oca/readme/DESCRIPTION.md new file mode 100644 index 000000000..7f5b2ba48 --- /dev/null +++ b/edi_gs1_oca/readme/DESCRIPTION.md @@ -0,0 +1,10 @@ +This is the base module for the implementation of the [GS1 -The Global +Language of Business](http://gs1.org/) standard. + +This module contains methods to generate and parse XML files compliant +with this standard. + +Beside basic machinery it provides common components to generate common +GS1 schema elements. Namely: + +> - StandardBusinessDocumentHeader diff --git a/edi_gs1/security/gs1_backend_acl.xml b/edi_gs1_oca/security/gs1_backend_acl.xml similarity index 100% rename from edi_gs1/security/gs1_backend_acl.xml rename to edi_gs1_oca/security/gs1_backend_acl.xml diff --git a/edi_gs1/static/description/icon.png b/edi_gs1_oca/static/description/icon.png similarity index 100% rename from edi_gs1/static/description/icon.png rename to edi_gs1_oca/static/description/icon.png diff --git a/edi_gs1/static/description/index.html b/edi_gs1_oca/static/description/index.html similarity index 72% rename from edi_gs1/static/description/index.html rename to edi_gs1_oca/static/description/index.html index 0ad160f14..32db3d701 100644 --- a/edi_gs1/static/description/index.html +++ b/edi_gs1_oca/static/description/index.html @@ -1,20 +1,20 @@ - - -Base GS1 EDI + +README.rst -
-

Base GS1 EDI

+
+ + +Odoo Community Association + +
+

Base GS1 EDI

-

Alpha License: AGPL-3 OCA/edi Translate me on Weblate Try me on Runbot

-

This is the base module for the implementation of the -GS1 -The Global Language of Business standard.

-

This module contains methods to generate and parse XML files compliant with this standard.

-

Beside basic machinery it provides common components to generate common GS1 schema elements. -Namely:

+

Alpha License: AGPL-3 OCA/edi-framework Translate me on Weblate Try me on Runboat

+

This is the base module for the implementation of the GS1 -The Global +Language of Business standard.

+

This module contains methods to generate and parse XML files compliant +with this standard.

+

Beside basic machinery it provides common components to generate common +GS1 schema elements. Namely:

    -
  • StandardBusinessDocumentHeader
  • +
  • StandardBusinessDocumentHeader
@@ -387,48 +395,51 @@

Base GS1 EDI

Table of contents

-

Bug Tracker

-

Bugs are tracked on GitHub Issues. +

Bug Tracker

+

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. -If you spotted it first, help us smashing it by providing a detailed and welcomed -feedback.

+If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

Do not contact contributors directly about support or help with technical issues.

-

Credits

+

Credits

-

Authors

+

Authors

  • ACSONE
-

Maintainers

+

Maintainers

This module is maintained by the OCA.

-Odoo Community Association + +Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.

-

This module is part of the OCA/edi project on GitHub.

+

This module is part of the OCA/edi-framework project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
diff --git a/edi_gs1/static/schemas/gs1/ecom/ApplicationReceiptAcknowledgement.xsd b/edi_gs1_oca/static/schemas/gs1/ecom/ApplicationReceiptAcknowledgement.xsd similarity index 100% rename from edi_gs1/static/schemas/gs1/ecom/ApplicationReceiptAcknowledgement.xsd rename to edi_gs1_oca/static/schemas/gs1/ecom/ApplicationReceiptAcknowledgement.xsd diff --git a/edi_gs1/static/schemas/gs1/ecom/WarehousingCommon.xsd b/edi_gs1_oca/static/schemas/gs1/ecom/WarehousingCommon.xsd similarity index 100% rename from edi_gs1/static/schemas/gs1/ecom/WarehousingCommon.xsd rename to edi_gs1_oca/static/schemas/gs1/ecom/WarehousingCommon.xsd diff --git a/edi_gs1/static/schemas/gs1/ecom/WarehousingInboundInstruction.xsd b/edi_gs1_oca/static/schemas/gs1/ecom/WarehousingInboundInstruction.xsd similarity index 100% rename from edi_gs1/static/schemas/gs1/ecom/WarehousingInboundInstruction.xsd rename to edi_gs1_oca/static/schemas/gs1/ecom/WarehousingInboundInstruction.xsd diff --git a/edi_gs1/static/schemas/gs1/ecom/WarehousingInboundNotification.xsd b/edi_gs1_oca/static/schemas/gs1/ecom/WarehousingInboundNotification.xsd similarity index 100% rename from edi_gs1/static/schemas/gs1/ecom/WarehousingInboundNotification.xsd rename to edi_gs1_oca/static/schemas/gs1/ecom/WarehousingInboundNotification.xsd diff --git a/edi_gs1/static/schemas/gs1/ecom/WarehousingOutboundInstruction.xsd b/edi_gs1_oca/static/schemas/gs1/ecom/WarehousingOutboundInstruction.xsd similarity index 100% rename from edi_gs1/static/schemas/gs1/ecom/WarehousingOutboundInstruction.xsd rename to edi_gs1_oca/static/schemas/gs1/ecom/WarehousingOutboundInstruction.xsd diff --git a/edi_gs1/static/schemas/gs1/ecom/WarehousingOutboundNotification.xsd b/edi_gs1_oca/static/schemas/gs1/ecom/WarehousingOutboundNotification.xsd similarity index 100% rename from edi_gs1/static/schemas/gs1/ecom/WarehousingOutboundNotification.xsd rename to edi_gs1_oca/static/schemas/gs1/ecom/WarehousingOutboundNotification.xsd diff --git a/edi_gs1/static/schemas/gs1/ecom/eComCommon.xsd b/edi_gs1_oca/static/schemas/gs1/ecom/eComCommon.xsd similarity index 100% rename from edi_gs1/static/schemas/gs1/ecom/eComCommon.xsd rename to edi_gs1_oca/static/schemas/gs1/ecom/eComCommon.xsd diff --git a/edi_gs1/static/schemas/gs1/shared/SharedCommon.xsd b/edi_gs1_oca/static/schemas/gs1/shared/SharedCommon.xsd similarity index 100% rename from edi_gs1/static/schemas/gs1/shared/SharedCommon.xsd rename to edi_gs1_oca/static/schemas/gs1/shared/SharedCommon.xsd diff --git a/edi_gs1/static/schemas/sbdh/BasicTypes.xsd b/edi_gs1_oca/static/schemas/sbdh/BasicTypes.xsd similarity index 100% rename from edi_gs1/static/schemas/sbdh/BasicTypes.xsd rename to edi_gs1_oca/static/schemas/sbdh/BasicTypes.xsd diff --git a/edi_gs1/static/schemas/sbdh/BusinessScope.xsd b/edi_gs1_oca/static/schemas/sbdh/BusinessScope.xsd similarity index 100% rename from edi_gs1/static/schemas/sbdh/BusinessScope.xsd rename to edi_gs1_oca/static/schemas/sbdh/BusinessScope.xsd diff --git a/edi_gs1/static/schemas/sbdh/DocumentIdentification.xsd b/edi_gs1_oca/static/schemas/sbdh/DocumentIdentification.xsd similarity index 100% rename from edi_gs1/static/schemas/sbdh/DocumentIdentification.xsd rename to edi_gs1_oca/static/schemas/sbdh/DocumentIdentification.xsd diff --git a/edi_gs1/static/schemas/sbdh/Manifest.xsd b/edi_gs1_oca/static/schemas/sbdh/Manifest.xsd similarity index 100% rename from edi_gs1/static/schemas/sbdh/Manifest.xsd rename to edi_gs1_oca/static/schemas/sbdh/Manifest.xsd diff --git a/edi_gs1/static/schemas/sbdh/Partner.xsd b/edi_gs1_oca/static/schemas/sbdh/Partner.xsd similarity index 100% rename from edi_gs1/static/schemas/sbdh/Partner.xsd rename to edi_gs1_oca/static/schemas/sbdh/Partner.xsd diff --git a/edi_gs1/static/schemas/sbdh/StandardBusinessDocumentHeader.xsd b/edi_gs1_oca/static/schemas/sbdh/StandardBusinessDocumentHeader.xsd similarity index 100% rename from edi_gs1/static/schemas/sbdh/StandardBusinessDocumentHeader.xsd rename to edi_gs1_oca/static/schemas/sbdh/StandardBusinessDocumentHeader.xsd diff --git a/edi_gs1/tests/__init__.py b/edi_gs1_oca/tests/__init__.py similarity index 100% rename from edi_gs1/tests/__init__.py rename to edi_gs1_oca/tests/__init__.py diff --git a/edi_gs1/tests/common.py b/edi_gs1_oca/tests/common.py similarity index 98% rename from edi_gs1/tests/common.py rename to edi_gs1_oca/tests/common.py index 77690dfbf..255e89a36 100644 --- a/edi_gs1/tests/common.py +++ b/edi_gs1_oca/tests/common.py @@ -13,7 +13,6 @@ @tagged("-at_install", "post_install") class BaseTestCase(SavepointComponentCase, xmlunittest.XmlTestMixin): - _schema_path = "edi_gs1:static/schemas/sbdh/StandardBusinessDocumentHeader.xsd" @classmethod @@ -51,7 +50,7 @@ def flatten(self, txt): def read_test_file(self, filename): path = os.path.join(os.path.dirname(__file__), "examples", filename) - with open(path, "r") as thefile: + with open(path) as thefile: return thefile.read() _schema_path = "" diff --git a/edi_gs1/tests/examples/ApplicationReceiptAcknowledgement.example1.xml b/edi_gs1_oca/tests/examples/ApplicationReceiptAcknowledgement.example1.xml similarity index 100% rename from edi_gs1/tests/examples/ApplicationReceiptAcknowledgement.example1.xml rename to edi_gs1_oca/tests/examples/ApplicationReceiptAcknowledgement.example1.xml diff --git a/edi_gs1/tests/test_business_header.py b/edi_gs1_oca/tests/test_business_header.py similarity index 99% rename from edi_gs1/tests/test_business_header.py rename to edi_gs1_oca/tests/test_business_header.py index 40c5309ef..abee08ac1 100644 --- a/edi_gs1/tests/test_business_header.py +++ b/edi_gs1_oca/tests/test_business_header.py @@ -10,7 +10,6 @@ class BusinessHeaderTestCase(BaseTestCase): - _schema_path = "edi_gs1:static/schemas/sbdh/StandardBusinessDocumentHeader.xsd" @classmethod diff --git a/edi_gs1/views/edi_backend.xml b/edi_gs1_oca/views/edi_backend.xml similarity index 100% rename from edi_gs1/views/edi_backend.xml rename to edi_gs1_oca/views/edi_backend.xml diff --git a/edi_gs1/views/res_partner.xml b/edi_gs1_oca/views/res_partner.xml similarity index 100% rename from edi_gs1/views/res_partner.xml rename to edi_gs1_oca/views/res_partner.xml diff --git a/edi_gs1/views/uom_uom.xml b/edi_gs1_oca/views/uom_uom.xml similarity index 100% rename from edi_gs1/views/uom_uom.xml rename to edi_gs1_oca/views/uom_uom.xml diff --git a/edi_gs1/xmler.py b/edi_gs1_oca/xmler.py similarity index 99% rename from edi_gs1/xmler.py rename to edi_gs1_oca/xmler.py index 7c9ccfb47..24888ec63 100644 --- a/edi_gs1/xmler.py +++ b/edi_gs1_oca/xmler.py @@ -88,7 +88,6 @@ def dict2xml(input_dict, encoding="utf-8", pretty=False): def parse(input_dict, parent=None, pretty=False): parent = parent or {} for key, value in input_dict.items(): - if isinstance(value, (float, int)): # Enfoce strings here value = str(value) diff --git a/requirements.txt b/requirements.txt index 4ec60a70e..d950e810d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,5 @@ # generated from manifests external_dependencies PyYAML openupgradelib +xmlschema +xmlunittest From 44443af1c8d1b62313fbe9d2547b659badfc4601 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Honor=C3=A9?= Date: Mon, 29 Jun 2026 11:02:34 +0200 Subject: [PATCH 31/32] [MIG] edi_gs1_oca: migration to 19.0 Migrate from the legacy component-based EDI framework to the new edi_core_oca model-based architecture: - Replace components (AbstractComponent / _usage / _find_component) with model-based handlers inheriting edi.oca.handler.* abstract models. - Add edi.gs1.xml helper (XSD validate/parse), edi.gs1.input.mixin, edi.gs1.output.mixin and edi.gs1.handler.* (validate + process ack). - Wire handlers on exchange types via input/output_validate_model_id and process_model_id; move schema_path to advanced_settings (gs1: section). - Drop the edi.exchange.template.output record; render QWeb templates through the output mixin and purge nswrapper helpers (utils.py). - Update post_init_hook to the new env-based signature. - Point view inherit_id to edi_core_oca and fix module xmlids. - Adapt tests: create partners instead of removed demo xmlids and set contact details (Odoo 19 QWeb t-out uses the element body as fallback when the value is falsy). --- edi_gs1_oca/__init__.py | 1 - edi_gs1_oca/__manifest__.py | 5 +- edi_gs1_oca/components/__init__.py | 4 - .../components/application_receipt_ack.py | 36 --------- .../components/application_send_ack.py | 15 ---- edi_gs1_oca/components/input_mixin.py | 28 ------- edi_gs1_oca/components/output_mixin.py | 25 ------ .../data/ack_in_exchange_type_data.xml | 16 ++-- .../data/ack_out_exchange_type_data.xml | 13 ++- .../data/business_header_output_template.xml | 17 ---- .../data/business_header_qweb_template.xml | 4 +- edi_gs1_oca/hooks.py | 7 +- edi_gs1_oca/models/__init__.py | 4 + edi_gs1_oca/models/edi_gs1_handler.py | 81 +++++++++++++++++++ edi_gs1_oca/models/edi_gs1_input.py | 28 +++++++ edi_gs1_oca/models/edi_gs1_output.py | 74 +++++++++++++++++ edi_gs1_oca/models/edi_gs1_xml.py | 63 +++++++++++++++ edi_gs1_oca/tests/common.py | 37 ++++----- edi_gs1_oca/tests/test_business_header.py | 23 +++--- edi_gs1_oca/utils.py | 39 +++++++++ edi_gs1_oca/views/edi_backend.xml | 4 +- edi_gs1_oca/views/res_partner.xml | 2 +- edi_gs1_oca/views/uom_uom.xml | 4 +- 23 files changed, 341 insertions(+), 189 deletions(-) delete mode 100644 edi_gs1_oca/components/__init__.py delete mode 100644 edi_gs1_oca/components/application_receipt_ack.py delete mode 100644 edi_gs1_oca/components/application_send_ack.py delete mode 100644 edi_gs1_oca/components/input_mixin.py delete mode 100644 edi_gs1_oca/components/output_mixin.py delete mode 100644 edi_gs1_oca/data/business_header_output_template.xml create mode 100644 edi_gs1_oca/models/edi_gs1_handler.py create mode 100644 edi_gs1_oca/models/edi_gs1_input.py create mode 100644 edi_gs1_oca/models/edi_gs1_output.py create mode 100644 edi_gs1_oca/models/edi_gs1_xml.py create mode 100644 edi_gs1_oca/utils.py diff --git a/edi_gs1_oca/__init__.py b/edi_gs1_oca/__init__.py index d20ee91b9..cc6b6354a 100644 --- a/edi_gs1_oca/__init__.py +++ b/edi_gs1_oca/__init__.py @@ -1,3 +1,2 @@ from . import models -from . import components from .hooks import post_init_hook diff --git a/edi_gs1_oca/__manifest__.py b/edi_gs1_oca/__manifest__.py index dcf600e9b..375824a45 100644 --- a/edi_gs1_oca/__manifest__.py +++ b/edi_gs1_oca/__manifest__.py @@ -6,12 +6,12 @@ "name": "Base GS1 EDI", "summary": """ Base module for GS1 standard EDI exchange""", - "version": "13.0.1.0.0", + "version": "19.0.1.0.0", "development_status": "Alpha", "license": "AGPL-3", "website": "https://github.com/OCA/edi-framework", "author": "ACSONE,Odoo Community Association (OCA)", - "depends": ["component", "edi_exchange_template", "edi_xml", "uom"], + "depends": ["edi_core_oca", "uom"], "external_dependencies": {"python": ["xmlschema", "xmlunittest"]}, "data": [ "security/gs1_backend_acl.xml", @@ -19,7 +19,6 @@ "data/ack_in_exchange_type_data.xml", "data/ack_out_exchange_type_data.xml", "data/business_header_qweb_template.xml", - "data/business_header_output_template.xml", "data/contact_details_qweb_template.xml", "views/res_partner.xml", "views/edi_backend.xml", diff --git a/edi_gs1_oca/components/__init__.py b/edi_gs1_oca/components/__init__.py deleted file mode 100644 index 9ccb8dd7d..000000000 --- a/edi_gs1_oca/components/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from . import output_mixin -from . import input_mixin -from . import application_receipt_ack -from . import application_send_ack diff --git a/edi_gs1_oca/components/application_receipt_ack.py b/edi_gs1_oca/components/application_receipt_ack.py deleted file mode 100644 index f9033e023..000000000 --- a/edi_gs1_oca/components/application_receipt_ack.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright 2020 ACSONE -# @author: Simone Orsi -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo import _, exceptions - -from odoo.addons.component.core import Component - - -class ApplicationReceiptAcknowledgement(Component): - """Parse GS1 applicationReceiptAcknowledgement.""" - - _name = "gs1.input.applicationReceiptAcknowledgement" - _inherit = "edi.gs1.input.mixin" - _usage = "gs1.in.ApplicationReceiptAcknowledgement" - - def _process_data(self, data): - if not self.is_ok(data): - # TODO: give a proper message based on the real state - # options: ERROR, RECEIVED, WARNING - raise exceptions.ValidationError(_("Exchange not received")) - - def is_ok(self, data): - return self._get_status(data) == "RECEIVED" - - def _get_status(self, data): - try: - ack = data["applicationReceiptAcknowledgement"][0] - except IndexError: - # Maybe raise an EDIValidationError? - raise exceptions.ValidationError( - _("applicationReceiptAcknowledgement element not found!") - ) - status_wrapper = ack["applicationResponseDocumentLevel"][0] - # options: ERROR, RECEIVED, WARNING - return status_wrapper["applicationResponseStatusCode"] diff --git a/edi_gs1_oca/components/application_send_ack.py b/edi_gs1_oca/components/application_send_ack.py deleted file mode 100644 index b1df370d3..000000000 --- a/edi_gs1_oca/components/application_send_ack.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright 2020 ACSONE -# @author: Simone Orsi -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - - -from odoo.addons.component.core import Component - - -class ApplicationSendAcknowledgement(Component): - """Generate GS1 applicationSendAcknowledgement.""" - - _name = "gs1.output.applicationSendAcknowledgement" - _inherit = "edi.gs1.output.mixin" - _usage = "gs1.out.ApplicationSendAcknowledgement" - _xsd_schema_path = "static/schemas/gs1/ecom/ApplicationReceiptAcknowledgement.xsd" diff --git a/edi_gs1_oca/components/input_mixin.py b/edi_gs1_oca/components/input_mixin.py deleted file mode 100644 index b09db3a03..000000000 --- a/edi_gs1_oca/components/input_mixin.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright 2020 ACSONE -# @author: Simone Orsi -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo.addons.component.core import AbstractComponent - - -class GS1InputMessageMixin(AbstractComponent): - """Common gs1 input mixin.""" - - _name = "edi.gs1.input.mixin" - _inherit = ["edi.component.input.mixin"] - _xsd_schema_path = None - - def process(self): - schema_path = self._xsd_schema_path or self.work.schema_path - handler = self.backend._find_component( - self.backend._name, - ["edi.xml"], - work_ctx={"schema_path": schema_path}, - safe=False, - ) - data = handler.parse_xml(self.exchange_record._get_file_content()) - self._process_data(data) - return True - - def _process_data(self, data): - NotImplementedError() diff --git a/edi_gs1_oca/components/output_mixin.py b/edi_gs1_oca/components/output_mixin.py deleted file mode 100644 index 1f0895217..000000000 --- a/edi_gs1_oca/components/output_mixin.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright 2020 ACSONE -# @author: Simone Orsi -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - - -from odoo.addons.component.core import AbstractComponent - - -class GS1OutputMessageMixin(AbstractComponent): - """Common gs1 output mixin.""" - - _name = "edi.gs1.output.mixin" - _inherit = ["edi.output.info.mixin"] - _work_context_validate_attrs = [ - "exchange_record", - "sender", - "receiver", - ] - - def _document_action_code(self): - # Brand new file - code = "ADD" - if self.replace_existing: - code = "CHANGE_BY_REFRESH" - return code diff --git a/edi_gs1_oca/data/ack_in_exchange_type_data.xml b/edi_gs1_oca/data/ack_in_exchange_type_data.xml index 40fcc019f..e63b86fbe 100644 --- a/edi_gs1_oca/data/ack_in_exchange_type_data.xml +++ b/edi_gs1_oca/data/ack_in_exchange_type_data.xml @@ -1,7 +1,7 @@ - + GS1 ApplicationReceiptAcknowledgement applicationReceiptAcknowledgement_in input @@ -12,14 +12,14 @@ --> xml + + - components: - validate: - usage: edi.xml - work_ctx: - schema_path: edi_gs1:static/schemas/gs1/ecom/ApplicationReceiptAcknowledgement.xsd - process: - usage: gs1.in.ApplicationReceiptAcknowledgement +gs1: + schema_path: edi_gs1_oca:static/schemas/gs1/ecom/ApplicationReceiptAcknowledgement.xsd diff --git a/edi_gs1_oca/data/ack_out_exchange_type_data.xml b/edi_gs1_oca/data/ack_out_exchange_type_data.xml index a22a5a276..edc47991c 100644 --- a/edi_gs1_oca/data/ack_out_exchange_type_data.xml +++ b/edi_gs1_oca/data/ack_out_exchange_type_data.xml @@ -1,20 +1,17 @@ - + GS1 ApplicationReceiptAcknowledgement applicationReceiptAcknowledgement_out output + {record_name}-{type.code}-{dt} xml - - + - components: - validate: - usage: edi.xml - work_ctx: - schema_path: edi_gs1:static/schemas/gs1/ecom/ApplicationReceiptAcknowledgement.xsd +gs1: + schema_path: edi_gs1_oca:static/schemas/gs1/ecom/ApplicationReceiptAcknowledgement.xsd diff --git a/edi_gs1_oca/data/business_header_output_template.xml b/edi_gs1_oca/data/business_header_output_template.xml deleted file mode 100644 index 4ab29a20b..000000000 --- a/edi_gs1_oca/data/business_header_output_template.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - EDI Exchange - GS1 Business Header - gs1.BusinessHeader - - xml - - -# Default identifier to record name -result = {"instance_identifier": exchange_record.identifier} - - - diff --git a/edi_gs1_oca/data/business_header_qweb_template.xml b/edi_gs1_oca/data/business_header_qweb_template.xml index 033eaad66..30f104fc0 100644 --- a/edi_gs1_oca/data/business_header_qweb_template.xml +++ b/edi_gs1_oca/data/business_header_qweb_template.xml @@ -36,13 +36,13 @@ 1.0 - + - + diff --git a/edi_gs1_oca/hooks.py b/edi_gs1_oca/hooks.py index b36b3f8b9..c6dbb045f 100644 --- a/edi_gs1_oca/hooks.py +++ b/edi_gs1_oca/hooks.py @@ -1,14 +1,11 @@ # Copyright 2021 ACSONE SA/NV () # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from odoo.api import SUPERUSER_ID, Environment -def post_init_hook(cr, registry): +def post_init_hook(env): """ Hook used to init automatically some GS1 code on existing UoM - :param cr: database cursor - :param registry: + :param env: Odoo Environment :return: """ - env = Environment(cr, SUPERUSER_ID, {"tracking_disable": True}) env["uom.uom"]._execute_gs1_map_code() diff --git a/edi_gs1_oca/models/__init__.py b/edi_gs1_oca/models/__init__.py index bf8796723..a0074d0b1 100644 --- a/edi_gs1_oca/models/__init__.py +++ b/edi_gs1_oca/models/__init__.py @@ -1,3 +1,7 @@ +from . import edi_gs1_xml +from . import edi_gs1_output +from . import edi_gs1_input +from . import edi_gs1_handler from . import edi_backend from . import res_partner from . import uom_uom diff --git a/edi_gs1_oca/models/edi_gs1_handler.py b/edi_gs1_oca/models/edi_gs1_handler.py new file mode 100644 index 000000000..bff5164cc --- /dev/null +++ b/edi_gs1_oca/models/edi_gs1_handler.py @@ -0,0 +1,81 @@ +# Copyright 2020 ACSONE +# @author: Simone Orsi +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import exceptions, models + +from odoo.addons.edi_core_oca.exceptions import EDIValidationError + + +class EdiGs1HandlerValidate(models.AbstractModel): + """Validate GS1 exchange files against their XSD schema. + + Wired on exchange types via ``input_validate_model_id`` / + ``output_validate_model_id``. The schema is read from the exchange type + advanced settings:: + + gs1: + schema_path: edi_gs1_oca:static/schemas/.../Foo.xsd + """ + + _name = "edi.gs1.handler.validate" + _inherit = ["edi.oca.handler.input.validate", "edi.oca.handler.output.validate"] + _description = "GS1 EDI XSD validation handler" + + def _gs1_validate(self, exchange_record, value=None): + settings = exchange_record.type_id.get_settings() or {} + schema_path = settings.get("gs1", {}).get("schema_path") + if not schema_path: + return None + content = value + if content is None: + content = exchange_record._get_file_content() + if isinstance(content, bytes): + content = content.decode() + error = self.env["edi.gs1.xml"].validate(schema_path, content) + if error: + raise EDIValidationError(error) + return None + + def input_validate(self, exchange_record, value=None, **kw): + return self._gs1_validate(exchange_record, value=value) + + def output_validate(self, exchange_record, value=None, **kw): + return self._gs1_validate(exchange_record, value=value) + + +class EdiGs1HandlerApplicationReceiptAck(models.AbstractModel): + """Process GS1 applicationReceiptAcknowledgement. + + Wired on the input ack exchange type via ``process_model_id``. + """ + + _name = "edi.gs1.handler.application.receipt.ack" + _inherit = ["edi.oca.handler.process", "edi.gs1.input.mixin"] + _description = "GS1 process applicationReceiptAcknowledgement" + + def process(self, exchange_record, **kw): + data = self._parse(exchange_record) + self._process_data(data, exchange_record) + return self.env._("applicationReceiptAcknowledgement processed") + + def _process_data(self, data, exchange_record): + if not self.is_ok(data): + # TODO: give a proper message based on the real state + # options: ERROR, RECEIVED, WARNING + raise exceptions.ValidationError(self.env._("Exchange not received")) + + def is_ok(self, data): + return self._get_status(data) == "RECEIVED" + + def _get_status(self, data): + try: + ack = data["applicationReceiptAcknowledgement"][0] + except IndexError as exc: + # Maybe raise an EDIValidationError? + raise exceptions.ValidationError( + self.env._("applicationReceiptAcknowledgement element not found!") + ) from exc + status_wrapper = ack["applicationResponseDocumentLevel"][0] + # options: ERROR, RECEIVED, WARNING + return status_wrapper["applicationResponseStatusCode"] diff --git a/edi_gs1_oca/models/edi_gs1_input.py b/edi_gs1_oca/models/edi_gs1_input.py new file mode 100644 index 000000000..710c3de5d --- /dev/null +++ b/edi_gs1_oca/models/edi_gs1_input.py @@ -0,0 +1,28 @@ +# Copyright 2020 ACSONE +# @author: Simone Orsi +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models + + +class EdiGs1InputMixin(models.AbstractModel): + """Common GS1 input processing mixin. + + Replaces the old ``edi.gs1.input.mixin`` component. Concrete processors + inherit this mixin together with ``edi.oca.handler.process``. + """ + + _name = "edi.gs1.input.mixin" + _description = "GS1 EDI input mixin" + + def _get_schema_path(self, exchange_record): + settings = exchange_record.type_id.get_settings() or {} + return settings.get("gs1", {}).get("schema_path") + + def _parse(self, exchange_record): + schema_path = self._get_schema_path(exchange_record) + content = exchange_record._get_file_content() + return self.env["edi.gs1.xml"].parse_xml(schema_path, content) + + def _process_data(self, data, exchange_record): + raise NotImplementedError() diff --git a/edi_gs1_oca/models/edi_gs1_output.py b/edi_gs1_oca/models/edi_gs1_output.py new file mode 100644 index 000000000..61308416f --- /dev/null +++ b/edi_gs1_oca/models/edi_gs1_output.py @@ -0,0 +1,74 @@ +# Copyright 2020 ACSONE +# @author: Simone Orsi +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import datetime + +import pytz + +from odoo import fields, models +from odoo.tools import DotDict + +from ..utils import xml_purge_nswrapper + + +class EdiGs1OutputMixin(models.AbstractModel): + """Common GS1 output generation mixin. + + Replaces the old ``edi.output.info.mixin`` (info provider) and the + ``edi.exchange.template.output`` QWeb rendering, both dropped in the + new EDI OCA architecture. Concrete generators inherit this mixin + together with ``edi.oca.handler.generate``. + """ + + _name = "edi.gs1.output.mixin" + _description = "GS1 EDI output mixin" + + # --- info provider (ported from edi.info.provider.mixin) --- + def generate_info(self, exchange_record, **kw): + """Generate and return data for output info. + + :return: odoo.tools.DotDict + """ + return DotDict(self._generate_info(exchange_record, **kw)) + + def _generate_info(self, exchange_record, **kw): + raise NotImplementedError("You must provide `_generate_info`") + + @staticmethod + def _utc_now(): + return datetime.datetime.utcnow().isoformat() + + @staticmethod + def date_to_string(dt, utc=True): + if utc: + dt = dt.astimezone(pytz.UTC) + return fields.Date.to_string(dt) + + def _document_action_code(self, replace_existing=False): + # Brand new file -> "ADD", otherwise replacing an existing one + return "CHANGE_BY_REFRESH" if replace_existing else "ADD" + + # --- template rendering (ported from edi.exchange.template.output) --- + def _get_render_values(self, exchange_record, **kw): + """Collect values to render a GS1 QWeb template.""" + values = { + "exchange_record": exchange_record, + "record": exchange_record.record, + "backend": exchange_record.backend_id, + # Default identifier to the exchange record one + "instance_identifier": exchange_record.identifier, + "doc_type": None, + "utc_now": self._utc_now, + "date_to_string": self.date_to_string, + "render_edi_template": self._render_edi_template, + "info": {}, + } + values.update(kw) + return values + + def _render_edi_template(self, exchange_record, xmlid, **kw): + """Render a GS1 QWeb template and purge the `nswrapper` helpers.""" + values = self._get_render_values(exchange_record, **kw) + output = self.env["ir.qweb"]._render(xmlid, values) + return xml_purge_nswrapper(output) diff --git a/edi_gs1_oca/models/edi_gs1_xml.py b/edi_gs1_oca/models/edi_gs1_xml.py new file mode 100644 index 000000000..df244e593 --- /dev/null +++ b/edi_gs1_oca/models/edi_gs1_xml.py @@ -0,0 +1,63 @@ +# Copyright 2020 ACSONE +# @author: Simone Orsi +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import io +from contextlib import closing + +import xmlschema + +from odoo import models, tools +from odoo.tools import DotDict + + +class EdiGs1Xml(models.AbstractModel): + """Validate and parse XML against a bundled XSD schema. + + Replaces the ``edi.xml`` component from the old ``edi_xml`` module + (dropped in the new EDI OCA architecture). Schema paths use the + ``module:relative/path.xsd`` notation. + """ + + _name = "edi.gs1.xml" + _description = "GS1 EDI XML helper" + + @staticmethod + def _resolve_schema_path(schema_path): + try: + mod_name, path = schema_path.split(":") + except ValueError as exc: + raise ValueError("Path must be in the form `module:path`") from exc + return tools.file_path(f"{mod_name}/{path}") + + def _get_schema(self, schema_path): + return xmlschema.XMLSchema(self._resolve_schema_path(schema_path)) + + def parse_xml(self, schema_path, file_content, **kw): + """Read XML content and return a data dict. + + :param schema_path: schema path as ``module:path`` + :param file_content: str of XML file + :return: odoo.tools.DotDict with final data + """ + schema = self._get_schema(schema_path) + with closing(io.StringIO(file_content)) as fd: + return DotDict(schema.to_dict(fd, **kw)) + + def validate(self, schema_path, xml_content, raise_on_fail=False): + """Validate XML content against the XSD schema. + + :param schema_path: schema path as ``module:path`` + :param xml_content: str containing xml data to validate + :param raise_on_fail: turn on/off validation error exception on fail + :return: + * None if validation is ok + * error string if `raise_on_fail` is False + """ + schema = self._get_schema(schema_path) + try: + return schema.validate(xml_content) + except xmlschema.validators.exceptions.XMLSchemaValidationError as err: + if raise_on_fail: + raise + return str(err) diff --git a/edi_gs1_oca/tests/common.py b/edi_gs1_oca/tests/common.py index 255e89a36..7c0b2bae5 100644 --- a/edi_gs1_oca/tests/common.py +++ b/edi_gs1_oca/tests/common.py @@ -6,14 +6,12 @@ import xmlunittest -from odoo.tests.common import tagged - -from odoo.addons.component.tests.common import SavepointComponentCase +from odoo.tests.common import TransactionCase, tagged @tagged("-at_install", "post_install") -class BaseTestCase(SavepointComponentCase, xmlunittest.XmlTestMixin): - _schema_path = "edi_gs1:static/schemas/sbdh/StandardBusinessDocumentHeader.xsd" +class BaseTestCase(TransactionCase, xmlunittest.XmlTestMixin): + _schema_path = "edi_gs1_oca:static/schemas/sbdh/StandardBusinessDocumentHeader.xsd" @classmethod def setUpClass(cls): @@ -21,9 +19,16 @@ def setUpClass(cls): cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) cls.backend = cls._get_backend() # Logistic Services Provider (LSP) - cls.lsp_partner = cls.env.ref("base.res_partner_3") + cls.lsp_partner = cls.env["res.partner"].create( + { + "name": "Test LSP", + "email": "lsp@example.com", + "phone": "+1-212-555-0001", + } + ) # Logistic Services Client (LSC) cls.lsc_partner = cls.env.ref("base.main_partner") + cls.lsc_partner.write({"email": "lsc@example.com", "phone": "+1-212-555-0002"}) cls.backend.lsp_partner_id = cls.lsp_partner cls.backend.lsc_partner_id = cls.lsc_partner @@ -36,14 +41,7 @@ def setUpClass(cls): @classmethod def _get_backend(cls): - return cls.env.ref("edi_gs1.edi_backend_gs1_default") - - def _dev_write_example_file(self, test_file, filename, content): - from pathlib import Path - - path = Path(test_file).parent / ("examples/test." + filename) - with open(path, "w") as out: - out.write(content) + return cls.env.ref("edi_gs1_oca.edi_backend_gs1_default") def flatten(self, txt): return "".join([x.strip() for x in txt.splitlines()]) @@ -53,12 +51,7 @@ def read_test_file(self, filename): with open(path) as thefile: return thefile.read() - _schema_path = "" - - def _get_xml_handler(self): - return self.backend._find_component( - self.backend._name, - ["edi.xml"], - work_ctx={"schema_path": self._schema_path}, - safe=False, + def _validate_xml(self, content, schema_path=None): + return self.env["edi.gs1.xml"].validate( + schema_path or self._schema_path, content ) diff --git a/edi_gs1_oca/tests/test_business_header.py b/edi_gs1_oca/tests/test_business_header.py index abee08ac1..7bfcfc54b 100644 --- a/edi_gs1_oca/tests/test_business_header.py +++ b/edi_gs1_oca/tests/test_business_header.py @@ -10,7 +10,7 @@ class BusinessHeaderTestCase(BaseTestCase): - _schema_path = "edi_gs1:static/schemas/sbdh/StandardBusinessDocumentHeader.xsd" + _schema_path = "edi_gs1_oca:static/schemas/sbdh/StandardBusinessDocumentHeader.xsd" @classmethod def setUpClass(cls): @@ -19,7 +19,7 @@ def setUpClass(cls): @classmethod def _setup_records(cls): - cls.bh_tmpl = cls.env.ref("edi_gs1.edi_exchange_template_business_header") + cls.output_handler = cls.env["edi.gs1.output.mixin"] vals = { "backend_id": cls.backend.id, "backend_type_id": cls.backend.backend_type_id.id, @@ -28,11 +28,13 @@ def _setup_records(cls): "code": "test_type_out1", "exchange_file_ext": "txt", "exchange_filename_pattern": "{record.ref}-{type.code}-{dt}", + # Output types require a send handler (edi_core_oca constraint). + "send_model_id": cls.env.ref("edi_core_oca.model_edi_oca_handler_noop").id, } cls.exc_type = cls.env["edi.exchange.type"].create(vals) - cls.related_record = cls.env.ref("base.partner_demo") + # Does not really matter which record we bind the exchange to + cls.related_record = cls.env["res.partner"].create({"name": "Test Record"}) vals = { - # Does not really matter which record we bind the exchange to "model": cls.related_record._name, "res_id": cls.related_record.id, "type_id": cls.exc_type.id, @@ -40,21 +42,23 @@ def _setup_records(cls): cls.exc_record = cls.backend.create_record("test_type_out1", vals) def test_template_render_values(self): - values = self.bh_tmpl._get_render_values(self.exc_record) + values = self.output_handler._get_render_values(self.exc_record) expected = { "backend": self.backend, "exchange_record": self.exc_record, "instance_identifier": self.exc_record.identifier, "record": self.related_record, - "template": self.bh_tmpl, } for k, v in expected.items(): self.assertEqual(values[k], v) @freeze_time("2020-07-08 07:30:00") def test_xml(self): - output = self.bh_tmpl.exchange_generate( - self.exc_record, sender=self.lsc_partner, receiver=self.lsp_partner + output = self.output_handler._render_edi_template( + self.exc_record, + "edi_gs1_oca.edi_exchange_business_header", + sender=self.lsc_partner, + receiver=self.lsp_partner, ) expected = """ @@ -94,5 +98,4 @@ def test_xml(self): date="2020-07-08T07:30:00", ) self.assertXmlEquivalentOutputs(self.flatten(output), self.flatten(expected)) - handler = self._get_xml_handler() - self.assertEqual(handler.validate(output), None) + self.assertEqual(self._validate_xml(output), None) diff --git a/edi_gs1_oca/utils.py b/edi_gs1_oca/utils.py new file mode 100644 index 000000000..e22e20014 --- /dev/null +++ b/edi_gs1_oca/utils.py @@ -0,0 +1,39 @@ +# Copyright 2020 ACSONE +# @author: Simone Orsi +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from lxml import etree + + +def xml_purge_nswrapper(xml_content): + """Purge `nswrapper` elements. + + QWeb template does not allow parsing namespaced elements + without declaring namespaces on the root element. + + Hence, by default you cannot define smaller re-usable templates + if they have namespaced elements. + + The trick is to wrap your reusable template with `nswrapper` element + which holds the namespace for that particular sub template. + For instance: + + + + + + Then this method is going to purge these unwanted elements from the result. + """ + if not (xml_content and xml_content.strip()): + return xml_content + root = etree.XML(xml_content) + # deeper elements come after, keep the root element at the end (if any) + for nswrapper in reversed(root.xpath("//nswrapper")): + parent = nswrapper.getparent() + if parent is None: + return "".join( + etree.tostring(el, encoding="unicode") for el in nswrapper.getchildren() + ) + parent.extend(nswrapper.getchildren()) + parent.remove(nswrapper) + return etree.tostring(root, encoding="unicode") diff --git a/edi_gs1_oca/views/edi_backend.xml b/edi_gs1_oca/views/edi_backend.xml index 33aacd56a..528ceb46a 100644 --- a/edi_gs1_oca/views/edi_backend.xml +++ b/edi_gs1_oca/views/edi_backend.xml @@ -3,9 +3,9 @@ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). --> - edi.backend.form (in edi_gs1) + edi.backend.form (in edi_gs1_oca) edi.backend - + diff --git a/edi_gs1_oca/views/res_partner.xml b/edi_gs1_oca/views/res_partner.xml index 5ba50adfc..a0656ba4a 100644 --- a/edi_gs1_oca/views/res_partner.xml +++ b/edi_gs1_oca/views/res_partner.xml @@ -3,7 +3,7 @@ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). --> - res.partner.form (in edi_gs1) + res.partner.form (in edi_gs1_oca) res.partner diff --git a/edi_gs1_oca/views/uom_uom.xml b/edi_gs1_oca/views/uom_uom.xml index f37265ae1..460b4fd1b 100644 --- a/edi_gs1_oca/views/uom_uom.xml +++ b/edi_gs1_oca/views/uom_uom.xml @@ -3,12 +3,12 @@ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). --> - uom.uom.form (in edi_gs1) + uom.uom.form (in edi_gs1_oca) uom.uom - + From 87e5e91a4c5059a2990efa32a94de9b7f7e8812d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Honor=C3=A9?= Date: Thu, 2 Jul 2026 15:11:25 +0200 Subject: [PATCH 32/32] [IMP] edi_gs1_oca: move to Beta development status --- edi_gs1_oca/README.rst | 9 ++------- edi_gs1_oca/__manifest__.py | 2 +- edi_gs1_oca/static/description/index.html | 8 +------- 3 files changed, 4 insertions(+), 15 deletions(-) diff --git a/edi_gs1_oca/README.rst b/edi_gs1_oca/README.rst index 75a3541b5..deeb0aec8 100644 --- a/edi_gs1_oca/README.rst +++ b/edi_gs1_oca/README.rst @@ -14,9 +14,9 @@ Base GS1 EDI !! source digest: sha256:a5ab15a8b1dea2fd4da5955af650a8db9296c1a96cb53d4d7d98188d059a14da !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status - :alt: Alpha + :alt: Beta .. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 @@ -43,11 +43,6 @@ GS1 schema elements. Namely: - StandardBusinessDocumentHeader -.. IMPORTANT:: - This is an alpha version, the data model and design can change at any time without warning. - Only for development or testing purpose, do not use in production. - `More details on development status `_ - **Table of contents** .. contents:: diff --git a/edi_gs1_oca/__manifest__.py b/edi_gs1_oca/__manifest__.py index 375824a45..24ada98ef 100644 --- a/edi_gs1_oca/__manifest__.py +++ b/edi_gs1_oca/__manifest__.py @@ -7,7 +7,7 @@ "summary": """ Base module for GS1 standard EDI exchange""", "version": "19.0.1.0.0", - "development_status": "Alpha", + "development_status": "Beta", "license": "AGPL-3", "website": "https://github.com/OCA/edi-framework", "author": "ACSONE,Odoo Community Association (OCA)", diff --git a/edi_gs1_oca/static/description/index.html b/edi_gs1_oca/static/description/index.html index 32db3d701..f3d540eee 100644 --- a/edi_gs1_oca/static/description/index.html +++ b/edi_gs1_oca/static/description/index.html @@ -374,7 +374,7 @@

Base GS1 EDI

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! source digest: sha256:a5ab15a8b1dea2fd4da5955af650a8db9296c1a96cb53d4d7d98188d059a14da !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

Alpha License: AGPL-3 OCA/edi-framework Translate me on Weblate Try me on Runboat

+

Beta License: AGPL-3 OCA/edi-framework Translate me on Weblate Try me on Runboat

This is the base module for the implementation of the GS1 -The Global Language of Business standard.

This module contains methods to generate and parse XML files compliant @@ -386,12 +386,6 @@

Base GS1 EDI

  • StandardBusinessDocumentHeader
  • -
    -

    Important

    -

    This is an alpha version, the data model and design can change at any time without warning. -Only for development or testing purpose, do not use in production. -More details on development status

    -

    Table of contents