diff --git a/easy_my_coop_connector/demo/demo.xml b/easy_my_coop_connector/demo/demo.xml
index eae18ae..e240535 100644
--- a/easy_my_coop_connector/demo/demo.xml
+++ b/easy_my_coop_connector/demo/demo.xml
@@ -4,6 +4,11 @@
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
+
+
+ emc_api
+
+
IWP backend
http://localhost:9876
@@ -22,9 +27,4 @@
31
-
- emc_api
-
-
-
diff --git a/easy_my_coop_connector/models/__init__.py b/easy_my_coop_connector/models/__init__.py
index e786407..ae6fedf 100644
--- a/easy_my_coop_connector/models/__init__.py
+++ b/easy_my_coop_connector/models/__init__.py
@@ -1,4 +1,6 @@
from . import emc_backend
from . import emc_bindings
from . import account_invoice
+from . import account_journal
+from . import account_payment
from . import subscription_request
diff --git a/easy_my_coop_connector/models/account_journal.py b/easy_my_coop_connector/models/account_journal.py
new file mode 100644
index 0000000..855d16e
--- /dev/null
+++ b/easy_my_coop_connector/models/account_journal.py
@@ -0,0 +1,16 @@
+# Copyright 2020 Coop IT Easy SCRL fs
+# Robin Keunen
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
+
+from odoo import fields, models
+
+
+class AccountJournal(models.Model):
+ _inherit = "account.journal"
+
+ binding_id = fields.One2many(
+ comodel_name="emc.binding.account.journal",
+ inverse_name="internal_id",
+ string="Binding ID",
+ required=False,
+ )
diff --git a/easy_my_coop_connector/models/account_payment.py b/easy_my_coop_connector/models/account_payment.py
new file mode 100644
index 0000000..abac561
--- /dev/null
+++ b/easy_my_coop_connector/models/account_payment.py
@@ -0,0 +1,48 @@
+# Copyright 2020 Coop IT Easy SCRL fs
+# Robin Keunen
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
+
+from odoo import _, api, fields, models
+from odoo.exceptions import ValidationError
+
+from .emc_adapters import AccountPaymentAdapter
+
+
+class AccountPayment(models.Model):
+ _inherit = "account.payment"
+
+ binding_id = fields.One2many(
+ comodel_name="emc.binding.account.payment",
+ inverse_name="internal_id",
+ string="Binding ID",
+ required=False,
+ )
+
+ @api.multi
+ def post(self):
+ res = super(AccountPayment, self).post()
+ for payment in self:
+ if any(payment.invoice_ids.mapped("release_capital_request")):
+ invoice_id = payment.invoice_ids
+ if len(invoice_id) > 1:
+ raise ValidationError(
+ _(
+ "This version of easy my coop connector "
+ "can't handle several invoice per"
+ "payment. Please contact your "
+ "system administrator"
+ )
+ )
+
+ backend = self.env["emc.backend"].get_backend()
+ adapter = AccountPaymentAdapter(backend=backend)
+ external_id, external_record = adapter.create(payment)
+ self.env["emc.binding.account.payment"].create(
+ {
+ "backend": backend.id,
+ "internal_id": payment.id,
+ "external_id": external_id,
+ }
+ )
+
+ return res
diff --git a/easy_my_coop_connector/models/emc_adapters.py b/easy_my_coop_connector/models/emc_adapters.py
index 654339c..2fb4fcc 100644
--- a/easy_my_coop_connector/models/emc_adapters.py
+++ b/easy_my_coop_connector/models/emc_adapters.py
@@ -19,10 +19,12 @@ class AbstractEMCAdapter:
def __init__(self, backend):
self.backend = backend
- def _get_url(self, args):
+ def _get_url(self, args=None):
"""args is a list of path elements
:return the complete route to the service
"""
+ if args is None:
+ args = []
return join("/", self._root, self._service, *args)
def search(self, **params):
@@ -34,9 +36,13 @@ class AbstractEMCAdapter:
api_dict = self.backend.http_get_content(url)
return self.to_write_values(api_dict)
- def create(self):
+ def create(self, record):
# pylint: disable=method-required-super
- raise NotImplementedError
+ url = self._get_url()
+ api_dict = self.to_api_dict(record)
+ external_record = self.backend.http_post_content(url, api_dict)
+ external_id, writeable_dict = self.to_write_values(external_record)
+ return external_id, writeable_dict
def update(self):
raise NotImplementedError
@@ -53,13 +59,16 @@ class AbstractEMCAdapter:
"""
raise NotImplementedError
+ def to_api_dict(self, record):
+ raise NotImplementedError
+
class SubscriptionRequestAdapter(AbstractEMCAdapter):
_model = "subscription.request"
_service = "subscription-request"
def search(self, date_from=None, date_to=None):
- url = self._get_url([])
+ url = self._get_url()
params = {}
if date_from:
params.update({"date_from": Date.to_string(date_from)})
@@ -137,3 +146,44 @@ class AccountInvoiceAdapter(AbstractEMCAdapter):
external_id = api_dict.pop("id")
writable_dict = api_dict
return external_id, writable_dict
+
+
+class AccountPaymentAdapter(AbstractEMCAdapter):
+ _model = "account.payment"
+ _service = "payment"
+
+ def to_write_values(self, api_dict):
+ api_dict = api_dict.copy()
+ external_id = api_dict.pop("id")
+ writable_dict = api_dict
+ return external_id, writable_dict
+
+ def to_api_dict(self, record):
+
+ if not record.journal_id.binding_id:
+ raise ValidationError(
+ _(
+ "Journal %s is not bound to a journal on the platform. "
+ "Please contact system administrator."
+ )
+ % record.journal_id.name
+ )
+
+ if not record.invoice_ids.binding_id:
+ raise ValidationError(
+ _(
+ "Invoice %s is not bound to a journal on the platform. "
+ "Please contact system administrator."
+ )
+ % record.invoice_ids.name
+ )
+
+ return {
+ "journal": record.journal_id.binding_id.external_id,
+ "invoice": record.invoice_ids.binding_id.external_id,
+ "payment_date": Date.to_string(record.payment_date),
+ "amount": record.amount,
+ "communication": record.communication,
+ "payment_type": record.payment_type,
+ "payment_method": record.payment_method_id.code,
+ }
diff --git a/easy_my_coop_connector/models/emc_backend.py b/easy_my_coop_connector/models/emc_backend.py
index 61743cb..ee9924d 100644
--- a/easy_my_coop_connector/models/emc_backend.py
+++ b/easy_my_coop_connector/models/emc_backend.py
@@ -24,6 +24,19 @@ class EMCBackend(models.Model):
description = fields.Text(string="Description", required=False)
active = fields.Boolean(string="active", default=True)
+ @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.multi
def http_get(self, url, params=None, headers=None):
self.ensure_one()
diff --git a/easy_my_coop_connector/models/emc_bindings.py b/easy_my_coop_connector/models/emc_bindings.py
index 39dec2e..0077cdc 100644
--- a/easy_my_coop_connector/models/emc_bindings.py
+++ b/easy_my_coop_connector/models/emc_bindings.py
@@ -55,3 +55,21 @@ class AccountInvoiceBinding(models.Model):
internal_id = fields.Many2one(
comodel_name="account.invoice", string="Internal ID", required=True
)
+
+
+class AccountPaymentBinding(models.Model):
+ _name = "emc.binding.account.payment"
+ _inherit = "emc.binding"
+
+ internal_id = fields.Many2one(
+ comodel_name="account.payment", string="Internal ID", required=True
+ )
+
+
+class AccountJournalBinding(models.Model):
+ _name = "emc.binding.account.journal"
+ _inherit = "emc.binding"
+
+ internal_id = fields.Many2one(
+ comodel_name="account.journal", 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 6c5c31d..214315e 100644
--- a/easy_my_coop_connector/models/subscription_request.py
+++ b/easy_my_coop_connector/models/subscription_request.py
@@ -23,24 +23,11 @@ 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._get_backend()
+ backend = self.env["emc.backend"].get_backend()
adapter = SubscriptionRequestAdapter(backend=backend)
requests_dict = adapter.search(date_from=date_from, date_to=date_to)
for external_id, request_dict in requests_dict["rows"]:
@@ -73,7 +60,7 @@ class SubscriptionRequest(models.Model):
def backend_read(self, external_id):
SRBinding = self.env["emc.binding.subscription.request"]
- backend = self._get_backend()
+ backend = self.env["emc.backend"].get_backend()
adapter = SubscriptionRequestAdapter(backend)
_, request_values = adapter.read(external_id)
@@ -95,7 +82,7 @@ class SubscriptionRequest(models.Model):
@api.model
def fetch_subscription_requests_cron(self):
- backend = self._get_backend()
+ backend = self.env["emc.backend"].get_backend()
date_to = date.today()
date_from = date_to - timedelta(days=1)
@@ -116,7 +103,7 @@ class SubscriptionRequest(models.Model):
).validate_subscription_request()
if self.source == "emc_api":
- backend = self._get_backend()
+ backend = self.env["emc.backend"].get_backend()
sr_adapter = SubscriptionRequestAdapter(backend=backend)
external_id, invoice_dict = sr_adapter.validate(
self.binding_id.external_id
diff --git a/easy_my_coop_connector/security/ir.model.access.csv b/easy_my_coop_connector/security/ir.model.access.csv
index 9ce8ad3..96d779b 100644
--- a/easy_my_coop_connector/security/ir.model.access.csv
+++ b/easy_my_coop_connector/security/ir.model.access.csv
@@ -3,3 +3,5 @@ access_emc_backend_administrator,access_emc_backend_administrator,model_emc_back
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
+access_emc_binding_account_payment_administrator,access_emc_binding_account_payment_administrator,model_emc_binding_account_payment,base.group_system,1,1,1,1
+access_emc_binding_account_journal_administrator,access_emc_binding_account_journal_administrator,model_emc_binding_account_journal,base.group_system,1,1,1,1
diff --git a/easy_my_coop_connector/tests/__init__.py b/easy_my_coop_connector/tests/__init__.py
index 4346b2f..9098a70 100644
--- a/easy_my_coop_connector/tests/__init__.py
+++ b/easy_my_coop_connector/tests/__init__.py
@@ -1 +1,2 @@
from . import test_subscription_request
+from . import test_payment
diff --git a/easy_my_coop_connector/tests/test_data.py b/easy_my_coop_connector/tests/test_data.py
new file mode 100644
index 0000000..e9b153e
--- /dev/null
+++ b/easy_my_coop_connector/tests/test_data.py
@@ -0,0 +1,92 @@
+# Copyright 2020 Coop IT Easy SCRL fs
+# Robin Keunen
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
+
+
+import json
+
+from odoo.fields import Date
+
+
+def dict_to_dump(content):
+ return json.dumps(content).encode("utf-8")
+
+
+NOT_FOUND_ERROR = {"name": "Not Found", "code": 404}
+FORBIDDEN_ERROR = {"name": "Forbidden", "code": 403}
+SERVER_ERROR = {"name": "Server Error", "code": 500}
+NO_RESULT = {"count": 0, "rows": []}
+
+SR_SEARCH_RESULT = {
+ "count": 1,
+ "rows": [
+ {
+ "id": 1,
+ "date": "2020-05-14",
+ "email": "manuel@demo.net",
+ "address": {
+ "city": "Brussels",
+ "street": "schaerbeekstraat",
+ "zip_code": "1111",
+ "country": "BE",
+ },
+ "lang": "en_US",
+ "ordered_parts": 3,
+ "name": "Manuel Dublues",
+ "share_product": {"name": "Part B - Worker", "id": 31},
+ "state": "draft",
+ }
+ ],
+}
+
+SR_GET_RESULT = {
+ "id": 1,
+ "name": "Robin Des Bois",
+ "date": "2020-05-14",
+ "email": "manuel@demo.net",
+ "address": {
+ "city": "Brussels",
+ "street": "schaerbeekstraat",
+ "zip_code": "1111",
+ "country": "BE",
+ },
+ "lang": "en_US",
+ "ordered_parts": 3,
+ "share_product": {"name": "Part B - Worker", "id": 31},
+ "state": "draft",
+}
+
+SR_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},
+}
+
+AP_CREATE_RESULT = {
+ "id": 9876,
+ "journal": {"id": 1, "name": "bank"},
+ "invoice": {
+ "id": SR_VALIDATE_RESULT["id"],
+ "name": SR_VALIDATE_RESULT["number"],
+ },
+ "payment_date": Date.to_string(Date.today()),
+ "amount": 75.0,
+ "communication": SR_VALIDATE_RESULT["number"],
+}
diff --git a/easy_my_coop_connector/tests/test_payment.py b/easy_my_coop_connector/tests/test_payment.py
new file mode 100644
index 0000000..827f785
--- /dev/null
+++ b/easy_my_coop_connector/tests/test_payment.py
@@ -0,0 +1,72 @@
+# Copyright 2020 Coop IT Easy SCRL fs
+# Robin Keunen
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
+
+
+from unittest.mock import Mock, patch
+
+import requests
+
+from odoo.fields import Date
+
+from odoo.addons.easy_my_coop.tests.test_base import EMCBaseCase
+
+from .test_data import AP_CREATE_RESULT, SR_VALIDATE_RESULT, dict_to_dump
+
+
+class EMCPaymentConnectorCase(EMCBaseCase):
+ def setUp(self):
+ super().setUp()
+ self.backend = self.browse_ref(
+ "easy_my_coop_connector.emc_backend_demo"
+ )
+ self.env["emc.binding.account.journal"].create(
+ {
+ "backend_id": self.backend.id,
+ "internal_id": self.bank_journal.id,
+ "external_id": 1,
+ }
+ )
+
+ def test_post_payment_sends_and_binds_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(SR_VALIDATE_RESULT)
+
+ srequest.validate_subscription_request()
+
+ capital_release_request = srequest.capital_release_request
+
+ payment_method_manual_in = self.env.ref(
+ "account.account_payment_method_manual_in"
+ )
+ ctx = {
+ "active_model": "account.invoice",
+ "active_ids": [capital_release_request.id],
+ }
+ register_payments = (
+ self.env["account.register.payments"]
+ .with_context(ctx)
+ .create(
+ {
+ "payment_date": Date.today(),
+ "journal_id": self.bank_journal.id,
+ "payment_method_id": payment_method_manual_in.id,
+ }
+ )
+ )
+
+ 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(AP_CREATE_RESULT)
+
+ register_payments.create_payments()
+
+ self.assertEquals(capital_release_request.state, "paid")
+
+ payment = capital_release_request.payment_ids
+ self.assertEquals(payment.state, "posted")
+ self.assertEquals(payment.binding_id.external_id, 9876)
diff --git a/easy_my_coop_connector/tests/test_subscription_request.py b/easy_my_coop_connector/tests/test_subscription_request.py
index c13f788..8f6f6f3 100644
--- a/easy_my_coop_connector/tests/test_subscription_request.py
+++ b/easy_my_coop_connector/tests/test_subscription_request.py
@@ -4,84 +4,21 @@
import datetime
-import json
from unittest.mock import Mock, patch
import requests
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}
-SERVER_ERROR = {"name": "Server Error", "code": 500}
-NO_RESULT = {"count": 0, "rows": []}
-SEARCH_RESULT = {
- "count": 1,
- "rows": [
- {
- "id": 1,
- "date": "2020-05-14",
- "email": "manuel@demo.net",
- "address": {
- "city": "Brussels",
- "street": "schaerbeekstraat",
- "zip_code": "1111",
- "country": "BE",
- },
- "lang": "en_US",
- "ordered_parts": 3,
- "name": "Manuel Dublues",
- "share_product": {"name": "Part B - Worker", "id": 31},
- "state": "draft",
- }
- ],
-}
-GET_RESULT = {
- "id": 1,
- "name": "Robin Des Bois",
- "date": "2020-05-14",
- "email": "manuel@demo.net",
- "address": {
- "city": "Brussels",
- "street": "schaerbeekstraat",
- "zip_code": "1111",
- "country": "BE",
- },
- "lang": "en_US",
- "ordered_parts": 3,
- "share_product": {"name": "Part B - Worker", "id": 31},
- "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 EMCConnectorCase(EMCBaseCase):
+from .test_data import (
+ SR_GET_RESULT,
+ SR_SEARCH_RESULT,
+ SR_VALIDATE_RESULT,
+ dict_to_dump,
+)
+
+
+class EMCSRConnectorCase(EMCBaseCase):
def setUp(self):
super().setUp()
self.backend = self.browse_ref(
@@ -102,7 +39,7 @@ class EMCConnectorCase(EMCBaseCase):
with patch.object(requests, "get") as mock_get:
mock_get.return_value = mock_response = Mock()
mock_response.status_code = 200
- mock_response.content = dict_to_dump(SEARCH_RESULT)
+ mock_response.content = dict_to_dump(SR_SEARCH_RESULT)
SubscriptionRequest.fetch_subscription_requests(
date_from=date_from, date_to=date_to
@@ -127,7 +64,7 @@ class EMCConnectorCase(EMCBaseCase):
with patch.object(requests, "get") as mock_get:
mock_get.return_value = mock_response = Mock()
mock_response.status_code = 200
- mock_response.content = dict_to_dump(GET_RESULT)
+ mock_response.content = dict_to_dump(SR_GET_RESULT)
SubscriptionRequest.backend_read(external_id)
@@ -138,7 +75,7 @@ class EMCConnectorCase(EMCBaseCase):
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)
+ mock_response.content = dict_to_dump(SR_VALIDATE_RESULT)
srequest.validate_subscription_request()
@@ -149,7 +86,7 @@ class EMCConnectorCase(EMCBaseCase):
# local invoice linked to external invoice
self.assertEquals(
srequest.capital_release_request.binding_id.external_id,
- VALIDATE_RESULT["id"],
+ SR_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 6790a5c..0a437ad 100644
--- a/easy_my_coop_connector/views/actions.xml
+++ b/easy_my_coop_connector/views/actions.xml
@@ -28,4 +28,16 @@
emc.binding.account.invoice
tree,form
+
+
+ payment Bindings
+ emc.binding.account.payment
+ tree,form
+
+
+
+ Journal Bindings
+ emc.binding.account.journal
+ tree,form
+
diff --git a/easy_my_coop_connector/views/emc_bindings.xml b/easy_my_coop_connector/views/emc_bindings.xml
index 8d20980..8951737 100644
--- a/easy_my_coop_connector/views/emc_bindings.xml
+++ b/easy_my_coop_connector/views/emc_bindings.xml
@@ -87,4 +87,60 @@
+
+
+ emc_binding_account_payment_view_form
+ emc.binding.account.payment
+
+
+
+
+
+
+ emc_binding_account_payment_view_tree
+ emc.binding.account.payment
+
+
+
+
+
+
+
+
+
+
+ emc_binding_account_journal_view_form
+ emc.binding.account.journal
+
+
+
+
+
+
+ emc_binding_account_journal_view_tree
+ emc.binding.account.journal
+
+
+
+
+
+
+
+
diff --git a/easy_my_coop_connector/views/menus.xml b/easy_my_coop_connector/views/menus.xml
index caac917..3ce45f8 100644
--- a/easy_my_coop_connector/views/menus.xml
+++ b/easy_my_coop_connector/views/menus.xml
@@ -40,6 +40,20 @@
groups="base.group_user"
sequence="1040"/>
+
+
+
+