From 1ac5d620b613f2f96a83ba5496caab5f014480a2 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Sun, 15 Dec 2024 18:03:32 +0100 Subject: [PATCH 1/2] edi_purchase: use edi.configuration --- edi_purchase_oca/__init__.py | 1 + edi_purchase_oca/__manifest__.py | 15 ++++-- edi_purchase_oca/components/__init__.py | 1 + edi_purchase_oca/components/listeners.py | 27 +++++++++++ edi_purchase_oca/data/edi_configuration.xml | 13 +++++ edi_purchase_oca/demo/edi_backend.xml | 11 +++++ edi_purchase_oca/demo/edi_configuration.xml | 23 +++++++++ edi_purchase_oca/demo/edi_exchange_type.xml | 14 ++++++ edi_purchase_oca/models/__init__.py | 1 + edi_purchase_oca/models/purchase_order.py | 15 ++++-- edi_purchase_oca/models/res_partner.py | 18 +++++++ edi_purchase_oca/readme/CONTRIBUTORS.rst | 1 + edi_purchase_oca/readme/DESCRIPTION.rst | 9 ++++ edi_purchase_oca/tests/__init__.py | 1 + edi_purchase_oca/tests/test_edi_conf.py | 53 +++++++++++++++++++++ edi_purchase_oca/views/res_partner_view.xml | 22 +++++++++ 16 files changed, 218 insertions(+), 7 deletions(-) create mode 100644 edi_purchase_oca/components/__init__.py create mode 100644 edi_purchase_oca/components/listeners.py create mode 100644 edi_purchase_oca/data/edi_configuration.xml create mode 100644 edi_purchase_oca/demo/edi_backend.xml create mode 100644 edi_purchase_oca/demo/edi_configuration.xml create mode 100644 edi_purchase_oca/demo/edi_exchange_type.xml create mode 100644 edi_purchase_oca/models/res_partner.py create mode 100644 edi_purchase_oca/tests/__init__.py create mode 100644 edi_purchase_oca/tests/test_edi_conf.py create mode 100644 edi_purchase_oca/views/res_partner_view.xml diff --git a/edi_purchase_oca/__init__.py b/edi_purchase_oca/__init__.py index 0650744f6b..0f00a6730d 100644 --- a/edi_purchase_oca/__init__.py +++ b/edi_purchase_oca/__init__.py @@ -1 +1,2 @@ from . import models +from . import components diff --git a/edi_purchase_oca/__manifest__.py b/edi_purchase_oca/__manifest__.py index be446cd404..69107e9447 100644 --- a/edi_purchase_oca/__manifest__.py +++ b/edi_purchase_oca/__manifest__.py @@ -7,9 +7,18 @@ Define EDI Configuration for Purchase Orders""", "version": "14.0.1.0.0", "license": "LGPL-3", - "author": "ForgeFlow, Odoo Community Association (OCA)", + "author": "ForgeFlow, Camptocamp, Odoo Community Association (OCA)", "website": "https://github.com/OCA/edi", "depends": ["purchase", "edi_oca", "component_event"], - "data": ["views/purchase_order_views.xml", "views/edi_exchange_record_views.xml"], - "demo": [], + "data": [ + "views/purchase_order_views.xml", + "views/edi_exchange_record_views.xml", + "views/res_partner_view.xml", + "data/edi_configuration.xml", + ], + "demo": [ + "demo/edi_backend.xml", + "demo/edi_exchange_type.xml", + "demo/edi_configuration.xml", + ], } diff --git a/edi_purchase_oca/components/__init__.py b/edi_purchase_oca/components/__init__.py new file mode 100644 index 0000000000..a441d8bd1f --- /dev/null +++ b/edi_purchase_oca/components/__init__.py @@ -0,0 +1 @@ +from . import listeners diff --git a/edi_purchase_oca/components/listeners.py b/edi_purchase_oca/components/listeners.py new file mode 100644 index 0000000000..a29013ee48 --- /dev/null +++ b/edi_purchase_oca/components/listeners.py @@ -0,0 +1,27 @@ +# Copyright 2024 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo.addons.component.core import Component + + +class EDIConfigPOListener(Component): + _name = "edi.listener.config.purchase.order" + _inherit = "base.event.listener" + _apply_on = ["purchase.order"] + + def on_record_create(self, record, fields=None): + trigger = "on_record_create" + return self._exec_conf(record, trigger) + + def on_record_write(self, record, fields=None): + trigger = "on_record_write" + return self._exec_conf(record, trigger) + + def on_edi_purchase_order_state_change(self, record, state=None): + trigger = "on_edi_purchase_order_state_change" + return self._exec_conf(record, trigger) + + def _exec_conf(self, record, trigger, conf_field="edi_purchase_conf_ids"): + confs = record.partner_id[conf_field].edi_get_conf(trigger) + for conf in confs: + conf.edi_exec_snippet_do(record) diff --git a/edi_purchase_oca/data/edi_configuration.xml b/edi_purchase_oca/data/edi_configuration.xml new file mode 100644 index 0000000000..edaf5da8f2 --- /dev/null +++ b/edi_purchase_oca/data/edi_configuration.xml @@ -0,0 +1,13 @@ + + + + + On PO state change + on_edi_purchase_order_state_change + Trigger when a purchase order state changes + + + diff --git a/edi_purchase_oca/demo/edi_backend.xml b/edi_purchase_oca/demo/edi_backend.xml new file mode 100644 index 0000000000..3410efd878 --- /dev/null +++ b/edi_purchase_oca/demo/edi_backend.xml @@ -0,0 +1,11 @@ + + + + Purchase DEMO + purchase_demo + + + purchase DEMO + + + diff --git a/edi_purchase_oca/demo/edi_configuration.xml b/edi_purchase_oca/demo/edi_configuration.xml new file mode 100644 index 0000000000..b22172f813 --- /dev/null +++ b/edi_purchase_oca/demo/edi_configuration.xml @@ -0,0 +1,23 @@ + + + + Demo PO send + Show case how you can send out an order automatically + + + + + +# ('draft', 'RFQ'), +# ('sent', 'RFQ Sent'), +# ('to approve', 'To Approve'), +# ('purchase', 'Purchase Order'), +# ('done', 'Locked'), +# ('cancel', 'Cancelled') +if record.state == 'purchase': + record._edi_send_via_edi(conf.type_id) + + + diff --git a/edi_purchase_oca/demo/edi_exchange_type.xml b/edi_purchase_oca/demo/edi_exchange_type.xml new file mode 100644 index 0000000000..00f26a101a --- /dev/null +++ b/edi_purchase_oca/demo/edi_exchange_type.xml @@ -0,0 +1,14 @@ + + + + + + + Demo Purchase Order out + demo_PurchaseOrder_out + output + {record_name}-{type.code}-{dt} + xml + + + diff --git a/edi_purchase_oca/models/__init__.py b/edi_purchase_oca/models/__init__.py index 9f03530643..abb5aa08ab 100644 --- a/edi_purchase_oca/models/__init__.py +++ b/edi_purchase_oca/models/__init__.py @@ -1 +1,2 @@ from . import purchase_order +from . import res_partner diff --git a/edi_purchase_oca/models/purchase_order.py b/edi_purchase_oca/models/purchase_order.py index 6ac69723e7..56d76dbafd 100644 --- a/edi_purchase_oca/models/purchase_order.py +++ b/edi_purchase_oca/models/purchase_order.py @@ -6,16 +6,23 @@ class PurchaseOrder(models.Model): _name = "purchase.order" - _inherit = ["purchase.order", "edi.exchange.consumer.mixin"] + _inherit = [ + "purchase.order", + "edi.exchange.consumer.mixin", + ] def button_confirm(self): - result = super().button_confirm() - if self: + res = super().button_confirm() + # DEPRECATED: rely on `on_edi_purchase_order_state_change` + # provided automatically by edi.consumer.mixin + if self: # TODO: is this check necessary? self._event("on_button_confirm_purchase_order").notify(self) - return result + return res def button_cancel(self): result = super().button_cancel() + # DEPRECATED: rely on `on_edi_purchase_order_state_change` + # provided automatically by edi.consumer.mixin if self: self._event("on_button_cancel_purchase_order").notify(self) return result diff --git a/edi_purchase_oca/models/res_partner.py b/edi_purchase_oca/models/res_partner.py new file mode 100644 index 0000000000..7c38d54b4c --- /dev/null +++ b/edi_purchase_oca/models/res_partner.py @@ -0,0 +1,18 @@ +# Copyright 2024 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + + +from odoo import fields, models + + +class ResPartner(models.Model): + _inherit = "res.partner" + + edi_purchase_conf_ids = fields.Many2many( + string="EDI purchase configuration", + comodel_name="edi.configuration", + relation="res_partner_edi_purchase_configuration_rel", + column1="partner_id", + column2="conf_id", + domain=[("model_name", "=", "purchase.order")], + ) diff --git a/edi_purchase_oca/readme/CONTRIBUTORS.rst b/edi_purchase_oca/readme/CONTRIBUTORS.rst index 77dfbe89e9..0da6e10f63 100644 --- a/edi_purchase_oca/readme/CONTRIBUTORS.rst +++ b/edi_purchase_oca/readme/CONTRIBUTORS.rst @@ -1 +1,2 @@ * Lois Rilo +* Simone Orsi diff --git a/edi_purchase_oca/readme/DESCRIPTION.rst b/edi_purchase_oca/readme/DESCRIPTION.rst index 9c5b5932c4..0b6c2b49da 100644 --- a/edi_purchase_oca/readme/DESCRIPTION.rst +++ b/edi_purchase_oca/readme/DESCRIPTION.rst @@ -1,6 +1,15 @@ This module intends to create a base to be extended by local edi rules for purchase. + +Configuration +~~~~~~~~~~~~~ + +TODO + +Deprecated mode +~~~~~~~~~~~~~~~ + In order to add a new integration, you need to create a listener: .. code-block:: python diff --git a/edi_purchase_oca/tests/__init__.py b/edi_purchase_oca/tests/__init__.py new file mode 100644 index 0000000000..2c35aec6f7 --- /dev/null +++ b/edi_purchase_oca/tests/__init__.py @@ -0,0 +1 @@ +from . import test_edi_conf diff --git a/edi_purchase_oca/tests/test_edi_conf.py b/edi_purchase_oca/tests/test_edi_conf.py new file mode 100644 index 0000000000..0f0642b4e8 --- /dev/null +++ b/edi_purchase_oca/tests/test_edi_conf.py @@ -0,0 +1,53 @@ +# Copyright 2024 CamptoCamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from unittest import mock + +from odoo.addons.edi_oca.tests.common import EDIBackendCommonComponentTestCase + + +class TestsPurchaseEDIConfiguration(EDIBackendCommonComponentTestCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.purchase_order = cls.env["purchase.order"] + cls.product = cls.env["product.product"].create( + { + "name": "Product 1", + "default_code": "1234567", + } + ) + cls.exc_type_out = cls.env.ref("edi_purchase_oca.demo_edi_exc_type_order_out") + cls.edi_conf = cls.env.ref("edi_purchase_oca.demo_edi_configuration_confirmed") + cls.partner.edi_purchase_conf_ids = cls.edi_conf + + @mock.patch("odoo.addons.edi_oca.models.edi_backend.EDIBackend._validate_data") + @mock.patch("odoo.addons.edi_oca.models.edi_backend.EDIBackend._exchange_generate") + @mock.patch("odoo.addons.edi_oca.models.edi_backend.EDIBackend._exchange_send") + def test_order_confirm(self, mock_send, mock_generate, mock_validate): + mock_generate.return_value = "TEST PO OUT" + order = self.purchase_order.create( + { + "partner_id": self.partner.id, + "order_line": [ + ( + 0, + 0, + { + "product_id": self.product.id, + "product_qty": 10, + "price_unit": 100.0, + }, + ) + ], + } + ) + self.assertEqual(order.state, "draft") + self.assertEqual(len(order.exchange_record_ids), 0) + order.button_confirm() + self.assertEqual(order.state, "purchase") + self.assertEqual(len(order.exchange_record_ids), 1) + self.assertEqual(order.exchange_record_ids[0].type_id, self.exc_type_out) + self.assertEqual( + order.exchange_record_ids[0]._get_file_content(), "TEST PO OUT" + ) diff --git a/edi_purchase_oca/views/res_partner_view.xml b/edi_purchase_oca/views/res_partner_view.xml new file mode 100644 index 0000000000..353a3a7698 --- /dev/null +++ b/edi_purchase_oca/views/res_partner_view.xml @@ -0,0 +1,22 @@ + + + + res.partner.edi.purchase + res.partner + + + + + + + + + + + + + + + + + From 5e9cbdffffed7d89b54c06a5a71e1ec4e5b1e1a7 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Fri, 20 Dec 2024 11:35:09 +0100 Subject: [PATCH 2/2] Add edi_purchase_ubl_output_oca --- edi_purchase_ubl_output_oca/README.rst | 1 + edi_purchase_ubl_output_oca/__init__.py | 1 + edi_purchase_ubl_output_oca/__manifest__.py | 18 +++++++++++++ .../components/__init__.py | 1 + .../components/generate.py | 27 +++++++++++++++++++ .../demo/edi_exchange_type.xml | 20 ++++++++++++++ .../readme/CONFIGURATION.rst | 7 +++++ .../readme/CONTRIBUTORS.rst | 1 + .../readme/DESCRIPTION.rst | 5 ++++ .../odoo/addons/edi_purchase_ubl_output_oca | 1 + setup/edi_purchase_ubl_output_oca/setup.py | 6 +++++ 11 files changed, 88 insertions(+) create mode 100644 edi_purchase_ubl_output_oca/README.rst create mode 100644 edi_purchase_ubl_output_oca/__init__.py create mode 100644 edi_purchase_ubl_output_oca/__manifest__.py create mode 100644 edi_purchase_ubl_output_oca/components/__init__.py create mode 100644 edi_purchase_ubl_output_oca/components/generate.py create mode 100644 edi_purchase_ubl_output_oca/demo/edi_exchange_type.xml create mode 100644 edi_purchase_ubl_output_oca/readme/CONFIGURATION.rst create mode 100644 edi_purchase_ubl_output_oca/readme/CONTRIBUTORS.rst create mode 100644 edi_purchase_ubl_output_oca/readme/DESCRIPTION.rst create mode 120000 setup/edi_purchase_ubl_output_oca/odoo/addons/edi_purchase_ubl_output_oca create mode 100644 setup/edi_purchase_ubl_output_oca/setup.py diff --git a/edi_purchase_ubl_output_oca/README.rst b/edi_purchase_ubl_output_oca/README.rst new file mode 100644 index 0000000000..a74415d005 --- /dev/null +++ b/edi_purchase_ubl_output_oca/README.rst @@ -0,0 +1 @@ +wait for the bot diff --git a/edi_purchase_ubl_output_oca/__init__.py b/edi_purchase_ubl_output_oca/__init__.py new file mode 100644 index 0000000000..1377f57f5a --- /dev/null +++ b/edi_purchase_ubl_output_oca/__init__.py @@ -0,0 +1 @@ +from . import components diff --git a/edi_purchase_ubl_output_oca/__manifest__.py b/edi_purchase_ubl_output_oca/__manifest__.py new file mode 100644 index 0000000000..0b2a232c2b --- /dev/null +++ b/edi_purchase_ubl_output_oca/__manifest__.py @@ -0,0 +1,18 @@ +# Copyright 2021 Camptocamp SA +# @author: Simone Orsi +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "EDI UBL Purchase", + "summary": """Handle outbound exchanges for purchases.""", + "version": "14.0.1.0.0", + "development_status": "Alpha", + "license": "AGPL-3", + "website": "https://github.com/OCA/edi", + "author": "Camptocamp, Odoo Community Association (OCA)", + "maintainers": ["simahawk"], + "depends": ["edi_purchase_oca", "edi_ubl_oca", "purchase_order_ubl"], + "demo": [ + "demo/edi_exchange_type.xml", + ], +} diff --git a/edi_purchase_ubl_output_oca/components/__init__.py b/edi_purchase_ubl_output_oca/components/__init__.py new file mode 100644 index 0000000000..f839b0d3b3 --- /dev/null +++ b/edi_purchase_ubl_output_oca/components/__init__.py @@ -0,0 +1 @@ +from . import generate diff --git a/edi_purchase_ubl_output_oca/components/generate.py b/edi_purchase_ubl_output_oca/components/generate.py new file mode 100644 index 0000000000..557344312d --- /dev/null +++ b/edi_purchase_ubl_output_oca/components/generate.py @@ -0,0 +1,27 @@ +# Copyright 2021 Camptocamp SA +# @author: Simone Orsi +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + + +from odoo.addons.component.core import Component + + +class EDIExchangePOGenerate(Component): + """Generate purchase orders.""" + + _name = "edi.output.ubl.purchase.order" + _inherit = "edi.component.output.mixin" + _usage = "output.generate.purchase.order" + + def generate(self): + return self._generate_ubl_xml() + + # TODO: add tests + def _generate_ubl_xml(self): + order = self.record + doc_type = order.get_ubl_purchase_order_doc_type() + if not doc_type: + raise NotImplementedError("TODO: handle no doc type") + version = order.get_ubl_version() + xml_string = order.generate_ubl_xml_string(doc_type, version=version) + return xml_string diff --git a/edi_purchase_ubl_output_oca/demo/edi_exchange_type.xml b/edi_purchase_ubl_output_oca/demo/edi_exchange_type.xml new file mode 100644 index 0000000000..5c0428ba7d --- /dev/null +++ b/edi_purchase_ubl_output_oca/demo/edi_exchange_type.xml @@ -0,0 +1,20 @@ + + + + + Demo UBL PO out + demo_UBL_PO_out + output + {record_name}-{type.code}-{dt} + xml + + +components: + generate: + usage: output.generate.purchase.order + + + diff --git a/edi_purchase_ubl_output_oca/readme/CONFIGURATION.rst b/edi_purchase_ubl_output_oca/readme/CONFIGURATION.rst new file mode 100644 index 0000000000..df94d54c51 --- /dev/null +++ b/edi_purchase_ubl_output_oca/readme/CONFIGURATION.rst @@ -0,0 +1,7 @@ +On your exchange type configured for UBL outbound exchanges use this conf in `advanced_settings_edit`:: + + components: + generate: + usage: output.generate.purchase.order + +That's it. diff --git a/edi_purchase_ubl_output_oca/readme/CONTRIBUTORS.rst b/edi_purchase_ubl_output_oca/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000000..f1c71bce18 --- /dev/null +++ b/edi_purchase_ubl_output_oca/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Simone Orsi diff --git a/edi_purchase_ubl_output_oca/readme/DESCRIPTION.rst b/edi_purchase_ubl_output_oca/readme/DESCRIPTION.rst new file mode 100644 index 0000000000..41f35c7a49 --- /dev/null +++ b/edi_purchase_ubl_output_oca/readme/DESCRIPTION.rst @@ -0,0 +1,5 @@ +Handle purchase exchanges with the EDI framework. + +This module is mostly a glue module for `purchase_order_ubl` with `edi_oca`. + +Allows you to generate and send purchase orders as UBL XML files. diff --git a/setup/edi_purchase_ubl_output_oca/odoo/addons/edi_purchase_ubl_output_oca b/setup/edi_purchase_ubl_output_oca/odoo/addons/edi_purchase_ubl_output_oca new file mode 120000 index 0000000000..d298c5b1de --- /dev/null +++ b/setup/edi_purchase_ubl_output_oca/odoo/addons/edi_purchase_ubl_output_oca @@ -0,0 +1 @@ +../../../../edi_purchase_ubl_output_oca \ No newline at end of file diff --git a/setup/edi_purchase_ubl_output_oca/setup.py b/setup/edi_purchase_ubl_output_oca/setup.py new file mode 100644 index 0000000000..28c57bb640 --- /dev/null +++ b/setup/edi_purchase_ubl_output_oca/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)