Browse Source

[ADD] emcc: post payment for emc-api invoice sends the payment to platform

pull/115/head
robin.keunen 4 years ago
parent
commit
8f210401a6
  1. 10
      easy_my_coop_connector/demo/demo.xml
  2. 2
      easy_my_coop_connector/models/__init__.py
  3. 16
      easy_my_coop_connector/models/account_journal.py
  4. 48
      easy_my_coop_connector/models/account_payment.py
  5. 58
      easy_my_coop_connector/models/emc_adapters.py
  6. 13
      easy_my_coop_connector/models/emc_backend.py
  7. 18
      easy_my_coop_connector/models/emc_bindings.py
  8. 21
      easy_my_coop_connector/models/subscription_request.py
  9. 2
      easy_my_coop_connector/security/ir.model.access.csv
  10. 1
      easy_my_coop_connector/tests/__init__.py
  11. 92
      easy_my_coop_connector/tests/test_data.py
  12. 72
      easy_my_coop_connector/tests/test_payment.py
  13. 89
      easy_my_coop_connector/tests/test_subscription_request.py
  14. 12
      easy_my_coop_connector/views/actions.xml
  15. 56
      easy_my_coop_connector/views/emc_bindings.xml
  16. 14
      easy_my_coop_connector/views/menus.xml

10
easy_my_coop_connector/demo/demo.xml

@ -4,6 +4,11 @@
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="easy_my_coop.subscription_request_1_demo" model="subscription.request">
<field name="source">emc_api</field>
</record>
<record id="emc_backend_demo" model="emc.backend">
<field name="name">IWP backend</field>
<field name="location">http://localhost:9876</field>
@ -22,9 +27,4 @@
<field name="external_id">31</field>
</record>
<record id="easy_my_coop.subscription_request_1_demo" model="subscription.request">
<field name="source">emc_api</field>
</record>
</odoo>

2
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

16
easy_my_coop_connector/models/account_journal.py

@ -0,0 +1,16 @@
# Copyright 2020 Coop IT Easy SCRL fs
# Robin Keunen <robin@coopiteasy.be>
# 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,
)

48
easy_my_coop_connector/models/account_payment.py

@ -0,0 +1,48 @@
# Copyright 2020 Coop IT Easy SCRL fs
# Robin Keunen <robin@coopiteasy.be>
# 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

58
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,
}

13
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()

18
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
)

21
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

2
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

1
easy_my_coop_connector/tests/__init__.py

@ -1 +1,2 @@
from . import test_subscription_request
from . import test_payment

92
easy_my_coop_connector/tests/test_data.py

@ -0,0 +1,92 @@
# Copyright 2020 Coop IT Easy SCRL fs
# Robin Keunen <robin@coopiteasy.be>
# 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"],
}

72
easy_my_coop_connector/tests/test_payment.py

@ -0,0 +1,72 @@
# Copyright 2020 Coop IT Easy SCRL fs
# Robin Keunen <robin@coopiteasy.be>
# 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)

89
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

12
easy_my_coop_connector/views/actions.xml

@ -28,4 +28,16 @@
<field name="res_model">emc.binding.account.invoice</field>
<field name="view_mode">tree,form</field>
</record>
<record id="emc_binding_account_payment_action" model="ir.actions.act_window">
<field name="name">payment Bindings</field>
<field name="res_model">emc.binding.account.payment</field>
<field name="view_mode">tree,form</field>
</record>
<record id="emc_binding_account_journal_action" model="ir.actions.act_window">
<field name="name">Journal Bindings</field>
<field name="res_model">emc.binding.account.journal</field>
<field name="view_mode">tree,form</field>
</record>
</odoo>

56
easy_my_coop_connector/views/emc_bindings.xml

@ -87,4 +87,60 @@
</tree>
</field>
</record>
<record id="emc_binding_account_payment_view_form" model="ir.ui.view">
<field name="name">emc_binding_account_payment_view_form</field>
<field name="model">emc.binding.account.payment</field>
<field name="arch" type="xml">
<form string="emc_binding_account_payment_form">
<sheet>
<group>
<field name="backend_id"/>
<field name="internal_id"/>
<field name="external_id"/>
</group>
</sheet>
</form>
</field>
</record>
<record id="emc_binding_account_payment_view_tree" model="ir.ui.view">
<field name="name">emc_binding_account_payment_view_tree</field>
<field name="model">emc.binding.account.payment</field>
<field name="arch" type="xml">
<tree string="emc_binding_account_payment_tree">
<field name="backend_id"/>
<field name="internal_id"/>
<field name="external_id"/>
</tree>
</field>
</record>
<record id="emc_binding_account_journal_view_form" model="ir.ui.view">
<field name="name">emc_binding_account_journal_view_form</field>
<field name="model">emc.binding.account.journal</field>
<field name="arch" type="xml">
<form string="emc_binding_account_journal_form">
<sheet>
<group>
<field name="backend_id"/>
<field name="internal_id"/>
<field name="external_id"/>
</group>
</sheet>
</form>
</field>
</record>
<record id="emc_binding_account_journal_view_tree" model="ir.ui.view">
<field name="name">emc_binding_account_journal_view_tree</field>
<field name="model">emc.binding.account.journal</field>
<field name="arch" type="xml">
<tree string="emc_binding_account_journal_tree">
<field name="backend_id"/>
<field name="internal_id"/>
<field name="external_id"/>
</tree>
</field>
</record>
</odoo>

14
easy_my_coop_connector/views/menus.xml

@ -40,6 +40,20 @@
groups="base.group_user"
sequence="1040"/>
<menuitem id="emc_binding_account_payment_action_menu"
name="Payment Bindings"
parent="emc_connector_menu_menu"
action="emc_binding_account_payment_action"
groups="base.group_user"
sequence="1050"/>
<menuitem id="emc_binding_account_journal_action_menu"
name="Journal Bindings"
parent="emc_connector_menu_menu"
action="emc_binding_account_journal_action"
groups="base.group_user"
sequence="1060"/>
<menuitem id="emc_history_import_action_menu"
name="Import Subscription Request History"
parent="emc_connector_menu_menu"

Loading…
Cancel
Save