Skip to content

Commit

Permalink
base_upflow: add upflow_type and refactor reconcile payload
Browse files Browse the repository at this point in the history
  • Loading branch information
petrus-v committed Feb 6, 2024
1 parent a855fe7 commit b917953
Show file tree
Hide file tree
Showing 8 changed files with 273 additions and 101 deletions.
2 changes: 1 addition & 1 deletion base_upflow/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Base Upflow.io
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:bd21d053b40a9912cbd5c2ebd07de0691a56ce1d8f3d68a9bb47ce3e7451c062
!! source digest: sha256:d7c3cc9b72154534c57c4158ea7aef658511c0609a2631b2b5d2680065011595
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png
Expand Down
2 changes: 1 addition & 1 deletion base_upflow/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
{
"name": "Base Upflow.io",
"summary": "Base module to generate Upflow.io API payloads format from odoo object",
"version": "14.0.1.1.0",
"version": "14.0.2.0.0",
"development_status": "Alpha",
"category": "EDI",
"website": "https://github.com/OCA/credit-control",
Expand Down
103 changes: 79 additions & 24 deletions base_upflow/models/account_move.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@
# @author Pierre Verkest <pierreverkest84@gmail.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import base64
import logging

from odoo import _, fields, models
from odoo.exceptions import UserError
from odoo.tools.float_utils import float_compare

_logger = logging.getLogger(__name__)


class AccountMove(models.Model):
Expand All @@ -17,6 +21,81 @@ class AccountMove(models.Model):
help="Technical field to get the upflow customer to use on account move",
)

upflow_type = fields.Selection(
selection=[
("none", "Not concerned"),
("invoices", "Invoice"),
("payments", "Invoice payment"),
("creditNotes", "Refund"),
("refunds", "Refund payment"),
],
compute="_compute_upflow_type",
help=(
"Technical fields to make sure consistency "
"while sending Journal entry and reconcile "
"payloads. Key values are current payload "
"keys while sending reconcile. While creating "
"malicious entries it can be hard to automatically "
"choose proper type"
),
)

def _compute_upflow_type(self):
for move in self:
if move.move_type.startswith("in_") or move.state != "posted":
move.upflow_type = "none"
continue
if move.move_type == "out_invoice":
move.upflow_type = "invoices"
elif move.move_type == "out_refund":
move.upflow_type = "creditNotes"
else:
receivables_lines = move.line_ids.filtered(
lambda line: line.account_id.user_type_id.type == "receivable"
)
if not receivables_lines:
move.upflow_type = "none"
continue
debit = sum(receivables_lines.mapped("debit"))
credit = sum(receivables_lines.mapped("credit"))
if (
float_compare(
debit,
0,
precision_rounding=move.currency_id.rounding,
)
== 0
and float_compare(
credit,
0,
precision_rounding=move.currency_id.rounding,
)
!= 0
):
move.upflow_type = "payments"
elif (
float_compare(
debit,
0,
precision_rounding=move.currency_id.rounding,
)
!= 0
and float_compare(
credit,
0,
precision_rounding=move.currency_id.rounding,
)
== 0
):
move.upflow_type = "refunds"
else:
_logger.error(
"Sum of receivable move lines on %s have credit (%d) and debit(%d). "
"Which sounds suspicious and can't set upflow type",
move.name,
)
move.upflow_type = "none"

def _compute_upflow_commercial_partner_id(self):
# while using OD as counter part or bank statement
# there are chance that partner_id is not set on account.move
Expand Down Expand Up @@ -56,35 +135,11 @@ def _prepare_upflow_api_payload(self):
def get_upflow_api_post_invoice_payload(self):
"""An upflow invoice match with account.move out_invoice odoo type"""
self.ensure_one()
if self.move_type != "out_invoice":
raise UserError(
_(
"You try to get upflow invoice payload "
"on account entry %s with an other type %s "
"(expected out_invoice)"
)
% (
self.name,
self.move_type,
)
)
return self._prepare_upflow_api_payload()

def get_upflow_api_post_credit_note_payload(self):
"""An upflow credit note match with account.move out_refund odoo type"""
self.ensure_one()
if self.move_type != "out_refund":
raise UserError(
_(
"You try to get upflow refund payload "
"on account entry %s with an other type %s "
"(expected out_refund)"
)
% (
self.name,
self.move_type,
)
)
return self._prepare_upflow_api_payload()

def get_upflow_api_post_payment_payload(self):
Expand Down
52 changes: 22 additions & 30 deletions base_upflow/models/account_partial_reconcile.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,32 @@


class AccountPartialReconcile(models.Model):

_inherit = ["account.partial.reconcile"]
_name = "account.partial.reconcile"
_inherit = ["account.partial.reconcile", "upflow.mixin"]

def _prepare_reconcile_payload(self):
payload = {
"externalId": str(self.id),
"externalId": "partial-" + str(self.id),
"invoices": [],
"payments": [],
"creditNotes": [],
"refunds": [],
}
return payload

def _get_part_payload(self, move_line):
data = {
"externalId": str(move_line.move_id.id),
"amountLinked": self.company_currency_id.to_lowest_division(self.amount),
}
if move_line.move_id.upflow_uuid:
data["id"] = move_line.move_id.upflow_uuid

if move_line.move_id.upflow_type in ["invoices", "creditNotes"]:
data["customId"] = move_line.move_id.name

return data

def get_upflow_api_post_reconcile_payload(self):
"""expect to be called from account move type:
Expand All @@ -28,32 +41,11 @@ def get_upflow_api_post_reconcile_payload(self):
"""
payload = self._prepare_reconcile_payload()

data = {
"externalId": str(self.debit_move_id.move_id.id),
"amountLinked": self.company_currency_id.to_lowest_division(self.amount),
}
if self.debit_move_id.move_id.upflow_uuid:
data["id"] = self.debit_move_id.move_id.upflow_uuid
if self.debit_move_id.move_id.move_type == "out_invoice":
kind = "invoices"
data["customId"] = self.debit_move_id.move_id.name
else:
kind = "refunds"

payload[kind].append(data)

data = {
"externalId": str(self.credit_move_id.move_id.id),
"amountLinked": self.company_currency_id.to_lowest_division(self.amount),
}
if self.credit_move_id.move_id.upflow_uuid:
data["id"] = self.credit_move_id.move_id.upflow_uuid
if self.credit_move_id.move_id.move_type == "out_refund":
kind = "creditNotes"
data["customId"] = self.credit_move_id.move_id.name
else:
kind = "payments"

payload[kind].append(data)
payload[self.debit_move_id.move_id.upflow_type].append(
self._get_part_payload(self.debit_move_id)
)
payload[self.credit_move_id.move_id.upflow_type].append(
self._get_part_payload(self.credit_move_id)
)

return payload
2 changes: 1 addition & 1 deletion base_upflow/static/description/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ <h1 class="title">Base Upflow.io</h1>
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:bd21d053b40a9912cbd5c2ebd07de0691a56ce1d8f3d68a9bb47ce3e7451c062
!! source digest: sha256:d7c3cc9b72154534c57c4158ea7aef658511c0609a2631b2b5d2680065011595
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Alpha" src="https://img.shields.io/badge/maturity-Alpha-red.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/credit-control/tree/14.0/base_upflow"><img alt="OCA/credit-control" src="https://img.shields.io/badge/github-OCA%2Fcredit--control-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/credit-control-14-0/credit-control-14-0-base_upflow"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/credit-control&amp;target_branch=14.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>This modules provide methods to generate <a class="reference external" href="https://upflow.docs.apiary.io/">upflow.io</a>
Expand Down
151 changes: 151 additions & 0 deletions base_upflow/tests/test_account_move.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
from odoo.tests.common import SavepointCase
from odoo.tools import mute_logger

from .common import AccountingCommonCase


class TestAccountMove(SavepointCase):
Expand Down Expand Up @@ -67,3 +70,151 @@ def test_compute_upflow_commercial_partner_id_invoice(self):
self.account_move.upflow_commercial_partner_id,
self.partner.commercial_partner_id,
)


class TestAccountMoveUpflowType(SavepointCase, AccountingCommonCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls._setup_accounting()
cls.partner = cls.env["res.partner"].create(
{
"name": "My customer company",
"is_company": True,
"vat": "FR23334175221",
"street": "Street 1",
"street2": "and more",
"zip": "45500",
"city": "Customer city",
}
)
cls.invoice = cls._create_invoice()
cls.refund = cls._create_invoice(move_type="out_refund")
cls.purchase_invoice = cls._create_invoice(move_type="in_invoice")
cls.payment_bnk_stmt = cls._make_credit_transfer_payment_reconciled(cls.invoice)

def test_compute_upflow_type_draft(self):
self.assertEqual(self.invoice.upflow_type, "none")
self.assertEqual(self.refund.upflow_type, "none")
self.assertEqual(self.purchase_invoice.upflow_type, "none")

def test_compute_upflow_type_invoices(self):
self.invoice.action_post()
self.assertEqual(self.invoice.upflow_type, "invoices")

def test_compute_upflow_type_credit_notes(self):
self.refund.action_post()
self.assertEqual(self.refund.upflow_type, "creditNotes")

def test_payment_by_bank_statment(self):
self.assertEqual(self.payment_bnk_stmt.upflow_type, "payments")

def test_customer_payment_from_bank_statement(self):
self.refund.action_post()
payment_bnk_stmt = self._make_credit_transfer_payment_reconciled(
self.refund,
amount=-self.refund.amount_residual,
reconcile_param=[
{
"id": self.refund.line_ids.filtered(
lambda line: line.account_internal_type
in ("receivable", "payable")
).id
}
],
)
self.assertEqual(payment_bnk_stmt.upflow_type, "refunds")

def test_customer_payment_manual_payment(self):
self.refund.action_post()
move = self._register_manual_payment_reconciled(self.refund)
self.assertEqual(move.upflow_type, "refunds")

def test_bank_statment_not_reconciled(self):
# at that time we don't know yet if payment entry is related to
# payment refunds or paid purchase invoice

(
bank_journal,
_method,
payment_date,
amount,
_currency,
) = self._payment_params(
self.invoice,
)
bank_stmt = self.env["account.bank.statement"].create(
{
"journal_id": bank_journal.id,
"date": payment_date,
"name": "payment",
"line_ids": [
(
0,
0,
{
"payment_ref": "payment",
"partner_id": self.partner.id,
"amount": -amount,
},
)
],
}
)
bank_stmt.button_post()
self.assertEqual(bank_stmt.line_ids[0].move_id.upflow_type, "none")

def test_no_receivable_lines(self):
self.purchase_invoice.action_post()
self.assertEqual(self.purchase_invoice.upflow_type, "none")

@mute_logger("odoo.addons.base_upflow.models.account_move")
def test_receivables_null(self):

account_user_type = self.env["account.account.type"].create(
{
"name": "Test account type",
"type": "receivable",
"internal_group": "asset",
}
)
account = self.env["account.account"].create(
{
"name": "Test account",
"code": "TEST",
"user_type_id": account_user_type.id,
"reconcile": True,
}
)
entry_move = self.env["account.move"].create(
{
"journal_id": self.invoice.journal_id.id,
"move_type": "entry",
"line_ids": [
(
0,
0,
{
"account_id": account.id,
"partner_id": self.partner.id,
"name": "Test entry",
"debit": 0,
"credit": 0,
},
),
(
0,
0,
{
"account_id": account.id,
"partner_id": self.partner.id,
"name": "Test entry",
"debit": 0,
"credit": 0,
},
),
],
}
)
entry_move.action_post()
self.assertEquals(entry_move.upflow_type, "none")
Loading

0 comments on commit b917953

Please sign in to comment.