From b2af3a22b04f40c9a39f94fb53d0339eb9b32d66 Mon Sep 17 00:00:00 2001 From: "robin.keunen" Date: Fri, 5 Jun 2020 16:46:35 +0200 Subject: [PATCH] [ADD] emcc: send validate request to platform --- easy_my_coop_connector/demo/demo.xml | 6 +++ easy_my_coop_connector/models/__init__.py | 1 + .../models/account_invoice.py | 20 +++++++ easy_my_coop_connector/models/emc_backend.py | 44 +++++++++------ easy_my_coop_connector/models/emc_bindings.py | 11 +++- .../models/subscription_request.py | 53 ++++++++++++++----- .../models/subscription_request_adapter.py | 19 ++++++- .../security/ir.model.access.csv | 1 + .../tests/test_subscription_request.py | 48 ++++++++++++++++- easy_my_coop_connector/views/actions.xml | 6 +++ easy_my_coop_connector/views/emc_bindings.xml | 28 ++++++++++ easy_my_coop_connector/views/menus.xml | 9 +++- 12 files changed, 212 insertions(+), 34 deletions(-) create mode 100644 easy_my_coop_connector/models/account_invoice.py diff --git a/easy_my_coop_connector/demo/demo.xml b/easy_my_coop_connector/demo/demo.xml index 5a3a1c7..eae18ae 100644 --- a/easy_my_coop_connector/demo/demo.xml +++ b/easy_my_coop_connector/demo/demo.xml @@ -21,4 +21,10 @@ 31 + + + emc_api + + + diff --git a/easy_my_coop_connector/models/__init__.py b/easy_my_coop_connector/models/__init__.py index f5c72d9..e786407 100644 --- a/easy_my_coop_connector/models/__init__.py +++ b/easy_my_coop_connector/models/__init__.py @@ -1,3 +1,4 @@ from . import emc_backend from . import emc_bindings +from . import account_invoice from . import subscription_request diff --git a/easy_my_coop_connector/models/account_invoice.py b/easy_my_coop_connector/models/account_invoice.py new file mode 100644 index 0000000..91fa6e7 --- /dev/null +++ b/easy_my_coop_connector/models/account_invoice.py @@ -0,0 +1,20 @@ +# Copyright 2020 Coop IT Easy SCRL fs +# Robin Keunen +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +import logging + +from odoo import fields, models + +_logger = logging.getLogger(__name__) + + +class AccountInvoice(models.Model): + _inherit = "account.invoice" + + binding_id = fields.One2many( + comodel_name="emc.binding.account.invoice", + inverse_name="internal_id", + string="Binding ID", + required=False, + ) diff --git a/easy_my_coop_connector/models/emc_backend.py b/easy_my_coop_connector/models/emc_backend.py index 3079df6..61743cb 100644 --- a/easy_my_coop_connector/models/emc_backend.py +++ b/easy_my_coop_connector/models/emc_backend.py @@ -6,15 +6,10 @@ import json import logging import requests -from werkzeug.exceptions import ( - InternalServerError, - NotFound, -) +from werkzeug.exceptions import BadRequest, InternalServerError, NotFound from odoo import _, api, fields, models -from odoo.exceptions import ( - AccessDenied, -) +from odoo.exceptions import AccessDenied _logger = logging.getLogger(__name__) @@ -38,16 +33,22 @@ class EMCBackend(models.Model): return requests.get(url, params=params, headers=headers) - @api.multi - def http_get_content(self, url, params=None, headers=None): - self.ensure_one() - response = self.http_get(url, params=params, headers=headers) - + def _process_response(self, response): if response.status_code == 200: content = response.content.decode("utf-8") return json.loads(content) + elif response.status_code == 400: + content = response.content.decode("utf-8") + raise BadRequest( + _( + "request returned status code %s with message %s" + % (response.status_code, content) + ) + ) elif response.status_code == 403: - raise AccessDenied(_("You are not allowed to access this resource")) + raise AccessDenied( + _("You are not allowed to access this resource") + ) elif response.status_code == 404: raise NotFound( _("Resource not found %s on server" % response.status_code) @@ -55,10 +56,18 @@ class EMCBackend(models.Model): else: # 500 et al. content = response.content.decode("utf-8") raise InternalServerError( - _("request returned status code %s with message %s" % ( - response.status_code, content)) + _( + "request returned status code %s with message %s" + % (response.status_code, content) + ) ) + @api.multi + def http_get_content(self, url, params=None, headers=None): + self.ensure_one() + response = self.http_get(url, params=params, headers=headers) + return self._process_response(response) + @api.multi def http_post(self, url, data, headers=None): self.ensure_one() @@ -68,6 +77,11 @@ class EMCBackend(models.Model): return requests.post(url, json=data, headers=headers) + def http_post_content(self, url, data, headers=None): + self.ensure_one() + response = self.http_post(url, data, headers=headers) + return self._process_response(response) + @api.multi def _add_api_key(self, headers): self.ensure_one() diff --git a/easy_my_coop_connector/models/emc_bindings.py b/easy_my_coop_connector/models/emc_bindings.py index c1e9981..39dec2e 100644 --- a/easy_my_coop_connector/models/emc_bindings.py +++ b/easy_my_coop_connector/models/emc_bindings.py @@ -13,7 +13,7 @@ class EMCBinding(models.AbstractModel): comodel_name="emc.backend", string="EMC Backend", ondelete="restrict" ) external_id = fields.Integer(string="ID in Platform", index=True) - # odoo_id = fields.Many2one # implement in concrete class + # internal_id = fields.Many2one # implement in concrete class @api.model def search_binding(self, backend, external_id): @@ -46,3 +46,12 @@ class ProductTemplateBinding(models.Model): domain="[('is_share', '=', True)]", required=True, ) + + +class AccountInvoiceBinding(models.Model): + _name = "emc.binding.account.invoice" + _inherit = "emc.binding" + + internal_id = fields.Many2one( + comodel_name="account.invoice", string="Internal ID", required=True + ) diff --git a/easy_my_coop_connector/models/subscription_request.py b/easy_my_coop_connector/models/subscription_request.py index b09c22d..2662b9c 100644 --- a/easy_my_coop_connector/models/subscription_request.py +++ b/easy_my_coop_connector/models/subscription_request.py @@ -23,13 +23,24 @@ class SubscriptionRequest(models.Model): required=False, ) + @api.model + def _get_backend(self): + backend = self.env["emc.backend"].search([("active", "=", True)]) + try: + backend.ensure_one() + except ValueError as e: + _logger.error( + "One and only one backend is allowed for the Easy My Coop " + "connector." + ) + raise e + return backend + @api.model def fetch_subscription_requests(self, date_from=None, date_to=None): SRBinding = self.env["emc.binding.subscription.request"] - backend = self.env["emc.backend"].search([("active", "=", True)]) - backend.ensure_one() - + backend = self._get_backend() adapter = SubscriptionRequestAdapter(backend=backend) requests_dict = adapter.search(date_from=date_from, date_to=date_to) for request_dict in requests_dict["rows"]: @@ -62,8 +73,7 @@ class SubscriptionRequest(models.Model): def backend_read(self, external_id): SRBinding = self.env["emc.binding.subscription.request"] - backend = self.env["emc.backend"].search([("active", "=", True)]) - backend.ensure_one() + backend = self._get_backend() adapter = SubscriptionRequestAdapter(backend) sr_data = adapter.read(external_id) @@ -87,15 +97,7 @@ class SubscriptionRequest(models.Model): @api.model def fetch_subscription_requests_cron(self): - backend = self.env["emc.backend"].search([("active", "=", True)]) - try: - backend.ensure_one() - except ValueError as e: - _logger.error( - "One and only one backend is allowed for the Easy My Coop " - "connector " - ) - raise e + backend = self._get_backend() date_to = date.today() date_from = date_to - timedelta(days=1) @@ -107,3 +109,26 @@ class SubscriptionRequest(models.Model): ) self.fetch_subscription_requests(date_from=date_from, date_to=date_to) _logger.info("fetch done.") + + @api.multi + def validate_subscription_request(self): + self.ensure_one() + invoice = super( + SubscriptionRequest, self + ).validate_subscription_request() + + if self.source == "emc_api": + backend = self._get_backend() + sr_adapter = SubscriptionRequestAdapter(backend=backend) + invoice_dict = sr_adapter.validate(self.binding_id.external_id) + + InvoiceBinding = self.env["emc.binding.account.invoice"] + InvoiceBinding.create( + { + "backend_id": backend.id, + "external_id": invoice_dict["id"], + "internal_id": invoice.id, + } + ) + + return invoice diff --git a/easy_my_coop_connector/models/subscription_request_adapter.py b/easy_my_coop_connector/models/subscription_request_adapter.py index d28a182..d312563 100644 --- a/easy_my_coop_connector/models/subscription_request_adapter.py +++ b/easy_my_coop_connector/models/subscription_request_adapter.py @@ -4,8 +4,10 @@ from os.path import join +from werkzeug.exceptions import BadRequest + from odoo import _ -from odoo.exceptions import UserError +from odoo.exceptions import UserError, ValidationError from odoo.fields import Date @@ -50,6 +52,21 @@ class SubscriptionRequestAdapter: def delete(self): raise NotImplementedError + def validate(self, id_): + url = self.get_url([str(id_), "validate"]) + data = {} + try: + invoice_dict = self.backend.http_post_content(url, data) + except BadRequest: + raise ValidationError( + _( + "The request was already validated on the " + "platform. Please check data consistency " + "with your system administrator." + ) + ) + return invoice_dict + def to_write_values(self, request): """ :return a writable dictionary of values from the dictionary diff --git a/easy_my_coop_connector/security/ir.model.access.csv b/easy_my_coop_connector/security/ir.model.access.csv index 5dc68a2..9ce8ad3 100644 --- a/easy_my_coop_connector/security/ir.model.access.csv +++ b/easy_my_coop_connector/security/ir.model.access.csv @@ -2,3 +2,4 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_emc_backend_administrator,access_emc_backend_administrator,model_emc_backend,base.group_system,1,1,1,1 access_emc_binding_subscription_request_administrator,access_emc_binding_subscription_request_administrator,model_emc_binding_subscription_request,base.group_system,1,1,1,1 access_emc_binding_product_template_administrator,access_emc_binding_product_template_administrator,model_emc_binding_product_template,base.group_system,1,1,1,1 +access_emc_binding_account_invoice_administrator,access_emc_binding_account_invoice_administrator,model_emc_binding_account_invoice,base.group_system,1,1,1,1 diff --git a/easy_my_coop_connector/tests/test_subscription_request.py b/easy_my_coop_connector/tests/test_subscription_request.py index 65f4931..c13f788 100644 --- a/easy_my_coop_connector/tests/test_subscription_request.py +++ b/easy_my_coop_connector/tests/test_subscription_request.py @@ -9,7 +9,7 @@ from unittest.mock import Mock, patch import requests -from odoo.tests.common import TransactionCase +from odoo.addons.easy_my_coop.tests.test_base import EMCBaseCase NOT_FOUND_ERROR = {"name": "Not Found", "code": 404} FORBIDDEN_ERROR = {"name": "Forbidden", "code": 403} @@ -53,12 +53,35 @@ GET_RESULT = { "state": "draft", } +VALIDATE_RESULT = { + "id": 9999, + "number": "SUBJ/2020/001", + "date_due": "2020-08-12", + "state": "open", + "date_invoice": "2020-08-12", + "date": "2020-08-12", + "type": "out_invoice", + "subscription_request": {"name": "Manuel Dublues", "id": 1}, + "partner": {"name": "Manuel Dublues", "id": 1}, + "invoice_lines": [ + { + "price_unit": 25.0, + "quantity": 3.0, + "account": {"name": "Product Sales", "id": 2}, + "name": "Part B - Worker", + "product": {"name": "Part B - Worker", "id": 2}, + } + ], + "journal": {"name": "Subscription Journal", "id": 1}, + "account": {"name": "Cooperators", "id": 1}, +} + def dict_to_dump(content): return json.dumps(content).encode("utf-8") -class TestCase(TransactionCase): +class EMCConnectorCase(EMCBaseCase): def setUp(self): super().setUp() self.backend = self.browse_ref( @@ -109,3 +132,24 @@ class TestCase(TransactionCase): SubscriptionRequest.backend_read(external_id) self.assertEquals(srequest.name, "Robin Des Bois") + + def test_validate_request(self): + srequest = self.browse_ref("easy_my_coop.subscription_request_1_demo") + with patch.object(requests, "post") as mock_get: + mock_get.return_value = mock_response = Mock() + mock_response.status_code = 200 + mock_response.content = dict_to_dump(VALIDATE_RESULT) + + srequest.validate_subscription_request() + + self.assertEquals(srequest.state, "done") + + # local invoice created + self.assertTrue(len(srequest.capital_release_request) > 0) + # local invoice linked to external invoice + self.assertEquals( + srequest.capital_release_request.binding_id.external_id, + VALIDATE_RESULT["id"], + ) + + # todo test 400 diff --git a/easy_my_coop_connector/views/actions.xml b/easy_my_coop_connector/views/actions.xml index 97e2390..6790a5c 100644 --- a/easy_my_coop_connector/views/actions.xml +++ b/easy_my_coop_connector/views/actions.xml @@ -22,4 +22,10 @@ emc.binding.product.template tree,form + + + Invoice Bindings + emc.binding.account.invoice + tree,form + diff --git a/easy_my_coop_connector/views/emc_bindings.xml b/easy_my_coop_connector/views/emc_bindings.xml index 7d92357..8d20980 100644 --- a/easy_my_coop_connector/views/emc_bindings.xml +++ b/easy_my_coop_connector/views/emc_bindings.xml @@ -59,4 +59,32 @@ + + + emc_binding_account_invoice_view_form + emc.binding.account.invoice + +
+ + + + + + + +
+
+
+ + + emc_binding_account_invoice_view_tree + emc.binding.account.invoice + + + + + + + + diff --git a/easy_my_coop_connector/views/menus.xml b/easy_my_coop_connector/views/menus.xml index 47c5a00..caac917 100644 --- a/easy_my_coop_connector/views/menus.xml +++ b/easy_my_coop_connector/views/menus.xml @@ -33,10 +33,17 @@ groups="base.group_user" sequence="1030"/> + + + sequence="1100"/>