From 2a67c479d57ed0fa8f88f5dc5ea99a018e3e818d Mon Sep 17 00:00:00 2001
From: Zina Rasoamanana
Date: Mon, 5 Feb 2024 14:43:41 +0100
Subject: [PATCH] [16.0][MIG] account_cash_discount_write_off: migration to
v16.0
---
account_cash_discount_write_off/README.rst | 12 +-
.../__manifest__.py | 2 +-
.../static/description/index.html | 8 +-
.../test_account_cash_discount_write_off.py | 703 +++++++++++++++++-
test-requirements.txt | 2 +
5 files changed, 699 insertions(+), 28 deletions(-)
create mode 100644 test-requirements.txt
diff --git a/account_cash_discount_write_off/README.rst b/account_cash_discount_write_off/README.rst
index 45099ec99232..2ccbafa6d0b1 100644
--- a/account_cash_discount_write_off/README.rst
+++ b/account_cash_discount_write_off/README.rst
@@ -7,7 +7,7 @@ Account Cash Discount Write Off
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- !! source digest: sha256:3ae5bbdcc58337eb64bdf7024a5e2ab36e9e6f14bea534d1ee9fb55cc48dd2bd
+ !! source digest: sha256:61f70659fb0fd989c1c0715b2f373de6e43663c174eb0359143cea741e3c862c
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
@@ -17,13 +17,13 @@ Account Cash Discount Write Off
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Faccount--payment-lightgray.png?logo=github
- :target: https://github.com/OCA/account-payment/tree/14.0/account_cash_discount_write_off
+ :target: https://github.com/OCA/account-payment/tree/16.0/account_cash_discount_write_off
:alt: OCA/account-payment
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
- :target: https://translation.odoo-community.org/projects/account-payment-14-0/account-payment-14-0-account_cash_discount_write_off
+ :target: https://translation.odoo-community.org/projects/account-payment-16-0/account-payment-16-0-account_cash_discount_write_off
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
- :target: https://runboat.odoo-community.org/builds?repo=OCA/account-payment&target_branch=14.0
+ :target: https://runboat.odoo-community.org/builds?repo=OCA/account-payment&target_branch=16.0
:alt: Try me on Runboat
|badge1| |badge2| |badge3| |badge4| |badge5|
@@ -57,7 +57,7 @@ Bug Tracker
Bugs are tracked on `GitHub Issues `_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
-`feedback `_.
+`feedback `_.
Do not contact contributors directly about support or help with technical issues.
@@ -89,6 +89,6 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
-This module is part of the `OCA/account-payment `_ project on GitHub.
+This module is part of the `OCA/account-payment `_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
diff --git a/account_cash_discount_write_off/__manifest__.py b/account_cash_discount_write_off/__manifest__.py
index b1ad5106bd76..1468b5a9e6ce 100644
--- a/account_cash_discount_write_off/__manifest__.py
+++ b/account_cash_discount_write_off/__manifest__.py
@@ -6,7 +6,7 @@
"summary": """
Create an automatic writeoff for payment with discount on the payment
order confirmation""",
- "version": "14.0.1.0.0",
+ "version": "16.0.1.0.0",
"license": "AGPL-3",
"author": "ACSONE SA/NV, Odoo Community Association (OCA)",
"website": "https://github.com/OCA/account-payment",
diff --git a/account_cash_discount_write_off/static/description/index.html b/account_cash_discount_write_off/static/description/index.html
index ec845f9bb419..0a67f6b6792c 100644
--- a/account_cash_discount_write_off/static/description/index.html
+++ b/account_cash_discount_write_off/static/description/index.html
@@ -367,9 +367,9 @@ Account Cash Discount Write Off
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-!! source digest: sha256:3ae5bbdcc58337eb64bdf7024a5e2ab36e9e6f14bea534d1ee9fb55cc48dd2bd
+!! source digest: sha256:61f70659fb0fd989c1c0715b2f373de6e43663c174eb0359143cea741e3c862c
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
-
+
Create an automatic write-off for payment with discount on the payment order
confirmation. If the cash discount amount is computed based on the total
amount, the created write-off will also contains tax adjustments. This
@@ -405,7 +405,7 @@
Bugs are tracked on GitHub Issues.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
-feedback.
+feedback.
Do not contact contributors directly about support or help with technical issues.
@@ -431,7 +431,7 @@
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
-
This module is part of the OCA/account-payment project on GitHub.
+
This module is part of the OCA/account-payment project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
diff --git a/account_cash_discount_write_off/tests/test_account_cash_discount_write_off.py b/account_cash_discount_write_off/tests/test_account_cash_discount_write_off.py
index 5df9d2f83b88..0641a387f9cc 100644
--- a/account_cash_discount_write_off/tests/test_account_cash_discount_write_off.py
+++ b/account_cash_discount_write_off/tests/test_account_cash_discount_write_off.py
@@ -1,6 +1,7 @@
# Copyright 2018 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+from odoo import Command
from odoo.exceptions import UserError
from odoo.fields import Date
from odoo.tests.common import Form
@@ -11,14 +12,40 @@
class TestAccountCashDiscountWriteOff(TestAccountCashDiscountPaymentCommon):
+ def create_payment_order(self, invoice_date, invoice):
+ payment_order = self.PaymentOrder.create(
+ {
+ "payment_mode_id": self.payment_mode_out.id,
+ "payment_type": "outbound",
+ "journal_id": self.bank_ing_journal.id,
+ }
+ )
+ payment_line_wizard = self.PaymentLineCreate.with_context(
+ active_model=payment_order._name,
+ active_id=payment_order.id,
+ ).create(
+ {
+ "cash_discount_date": invoice_date,
+ "date_type": "discount_due_date",
+ "target_move": "posted",
+ "journal_ids": [(6, 0, [invoice.journal_id.id])],
+ }
+ )
+ self.assertEqual(payment_line_wizard.order_id, payment_order)
+
+ payment_line_wizard.populate()
+ payment_line_wizard.create_payment_lines()
+
+ return payment_order
+
@classmethod
def setUpClass(cls):
- super(TestAccountCashDiscountWriteOff, cls).setUpClass()
+ super().setUpClass()
cls.cash_discount_writeoff_account = cls.Account.create(
{
"name": "Cash Discount Write-Off",
- "code": "CD-W",
- "user_type_id": cls.inc_account_type.id,
+ "code": "CDW",
+ "account_type": "income_other",
}
)
@@ -29,11 +56,31 @@ def setUpClass(cls):
def test_cash_discount_with_write_off(self):
payment_mode = self.payment_mode_out
payment_mode.post_move = True
- discount_due_date = Date.today()
+ invoice_date = Date.today()
+
+ payment_term = self.env["account.payment.term"].create(
+ {
+ "name": "10% discount 5 days",
+ "line_ids": [
+ Command.create(
+ {
+ "value": "balance",
+ "value_amount": 0.0,
+ "months": 0,
+ "end_month": False,
+ "discount_percentage": 10,
+ "discount_days": 5,
+ }
+ )
+ ],
+ }
+ )
invoice = self.create_supplier_invoice(
- discount_due_date, payment_mode, 1000, 10, []
+ invoice_date, self.payment_mode_out, 1000, False, payment_term
)
+ invoice._onchange_discount_delay()
+ invoice.ref = "test ref"
invoice.action_post()
payment_order = self.PaymentOrder.create(
@@ -49,9 +96,9 @@ def test_cash_discount_with_write_off(self):
active_id=payment_order.id,
).create(
{
- "cash_discount_date": discount_due_date,
+ "cash_discount_date": invoice_date,
"date_type": "discount_due_date",
- "journal_ids": [(6, 0, [self.purchase_journal.id])],
+ "journal_ids": [(6, 0, [invoice.journal_id.id])],
}
)
@@ -123,11 +170,45 @@ def test_cash_discount_with_write_off_with_taxes(self):
payment_mode = self.payment_mode_out
payment_mode.post_move = True
- discount_due_date = Date.today()
+ invoice_date = Date.today()
+
+ payment_term = self.env["account.payment.term"].create(
+ {
+ "name": "10% discount 5 days",
+ "line_ids": [
+ Command.create(
+ {
+ "value": "balance",
+ "value_amount": 0.0,
+ "months": 0,
+ "end_month": False,
+ "discount_percentage": 10,
+ "discount_days": 5,
+ }
+ )
+ ],
+ }
+ )
invoice = self.create_supplier_invoice(
- discount_due_date, payment_mode, 1000, 10, [self.tax_10_p, self.tax_15_p]
+ invoice_date, self.payment_mode_out, 1000, self.tax_10_p, payment_term
)
+
+ # add one tax
+ inv_form = Form(
+ invoice.with_context(
+ default_move_type="in_invoice",
+ default_company_id=self.company.id,
+ default_payment_mode_id=payment_mode.id,
+ )
+ )
+ with inv_form.invoice_line_ids.edit(0) as line:
+ line.tax_ids.add(self.tax_15_p)
+
+ invoice = inv_form.save()
+
+ invoice._onchange_discount_delay()
+ invoice.ref = "test ref"
invoice.action_post()
payment_order = self.PaymentOrder.create(
@@ -143,9 +224,9 @@ def test_cash_discount_with_write_off_with_taxes(self):
active_id=payment_order.id,
).create(
{
- "cash_discount_date": discount_due_date,
+ "cash_discount_date": invoice_date,
"date_type": "discount_due_date",
- "journal_ids": [(6, 0, [self.purchase_journal.id])],
+ "journal_ids": [(6, 0, [invoice.journal_id.id])],
}
)
@@ -194,18 +275,44 @@ def test_cash_discount_with_refund(self):
payment_mode = self.payment_mode_out
payment_mode.post_move = True
- discount_due_date = Date.today()
+ invoice_date = Date.today()
+
+ payment_term = self.env["account.payment.term"].create(
+ {
+ "name": "2% discount 5 days",
+ "line_ids": [
+ Command.create(
+ {
+ "value": "balance",
+ "value_amount": 0.0,
+ "months": 0,
+ "end_month": False,
+ "discount_percentage": 2,
+ "discount_days": 5,
+ }
+ )
+ ],
+ }
+ )
invoice = self.create_supplier_invoice(
- discount_due_date, payment_mode, 100, 2, [self.tax_17_p]
+ invoice_date, self.payment_mode_out, 100, self.tax_17_p, payment_term
)
+ invoice._onchange_discount_delay()
+ invoice.ref = "test ref"
invoice.action_post()
self.assertAlmostEqual(invoice.amount_residual, 117)
self.assertAlmostEqual(invoice.residual_with_discount, 114.66)
move_reversal = self.AccountMoveReversal.with_context(
active_model=invoice._name, active_ids=invoice.ids
- ).create({"reason": "no reason", "refund_method": "refund"})
+ ).create(
+ {
+ "reason": "no reason",
+ "refund_method": "refund",
+ "journal_id": invoice.journal_id.id,
+ }
+ )
reversal = move_reversal.reverse_moves()
refund = self.env["account.move"].browse(reversal["res_id"])
@@ -215,7 +322,7 @@ def test_cash_discount_with_refund(self):
refund_line.price_unit = 10
refund_form.save()
- refund.write({"discount_due_date": discount_due_date, "discount_percent": 2})
+ refund.write({"discount_due_date": invoice_date, "discount_percent": 2})
refund.action_post()
credit_aml_id = self.AccountMoveLine.search(
[("move_id", "=", refund.id), ("debit", ">", 0)], limit=1
@@ -239,9 +346,9 @@ def test_cash_discount_with_refund(self):
active_id=payment_order.id,
).create(
{
- "cash_discount_date": discount_due_date,
+ "cash_discount_date": invoice_date,
"date_type": "discount_due_date",
- "journal_ids": [(6, 0, [self.purchase_journal.id])],
+ "journal_ids": [(6, 0, [invoice.journal_id.id])],
}
)
@@ -256,3 +363,565 @@ def test_cash_discount_with_refund(self):
payment_order.generated2uploaded()
self.assertEqual(invoice.payment_state, "paid")
+
+ def test_invoice_with_discount_1(self):
+ """
+ Discount: Tax included
+ Invoice:
+ Amount without Tax: 1500
+ Tax: 20% (300)
+ Total on invoice: 1800
+ Discount: 2% (36)
+ Total with discount: 1764
+ """
+ invoice_date = Date.today()
+ woff_account = self.cash_discount_writeoff_account
+ woff_journal = self.cash_discount_writeoff_journal
+ payment_mode = self.payment_mode_out
+ payment_mode.post_move = True
+ self.company.write(
+ {
+ "default_cash_discount_writeoff_account_id": woff_account.id,
+ "default_cash_discount_writeoff_journal_id": woff_journal.id,
+ "cash_discount_base_amount_type": "total",
+ }
+ )
+ invoice = self.create_supplier_invoice(
+ invoice_date,
+ self.payment_mode_out,
+ 1500,
+ self.tax_20_p,
+ self.payment_term_2_p_discount_7d,
+ )
+ invoice._onchange_discount_delay()
+ self.assertTrue(invoice.has_discount)
+ self.assertRecordValues(
+ invoice,
+ [
+ {
+ "amount_untaxed": 1500.0,
+ "amount_tax": 300.0,
+ "amount_total": 1800.0,
+ "discount_amount": 36.0,
+ "discount_percent": 2.0,
+ "amount_total_with_discount": 1764.0,
+ }
+ ],
+ )
+ invoice.ref = "test ref"
+ invoice.action_post()
+
+ payment_order = self.create_payment_order(invoice_date, invoice)
+
+ self.assertEqual(len(payment_order.payment_line_ids), 1)
+ payment_line = payment_order.payment_line_ids[0]
+ self.assertTrue(payment_line.pay_with_discount)
+ self.assertAlmostEqual(payment_line.amount_currency, 1764.0)
+ self.assertTrue(payment_line.pay_with_discount)
+
+ payment_order.draft2open()
+ payment_order.open2generated()
+ payment_order.generated2uploaded()
+
+ self.assertEqual(len(payment_order.move_ids), 1)
+
+ self.assertEqual(invoice.payment_state, "paid")
+
+ domain_reconlied_line = payment_order.move_ids.open_reconcile_view()["domain"]
+ reconciled_line_ids = self.env["account.move.line"].search(
+ domain_reconlied_line
+ )
+
+ self.assertRecordValues(
+ reconciled_line_ids.sorted(lambda r: r.balance),
+ [
+ {
+ "balance": -1800.0,
+ },
+ {
+ "balance": 36.0,
+ "name": "Cash Discount Write-Off",
+ },
+ {
+ "balance": 1764.0,
+ "name": "test ref",
+ },
+ ],
+ )
+
+ write_off_move = reconciled_line_ids.filtered(
+ lambda r: r.journal_id == woff_journal
+ ).move_id
+
+ self.assertRecordValues(
+ write_off_move.line_ids.sorted(lambda r: r.balance),
+ [
+ {
+ "balance": -30.0, # Not Tax
+ },
+ {
+ "balance": -6.0, # Tax
+ },
+ {
+ "balance": 36.0,
+ },
+ ],
+ )
+
+ def test_invoice_with_discount_2(self):
+ """
+ Discount: Tax excluded
+ Invoice:
+ Amount without Tax: 1500
+ Tax: 20% (300)
+ Total on invoice: 1800
+ Discount: 2% (30)
+ Total with discount: 1770
+ """
+ invoice_date = Date.today()
+ woff_account = self.cash_discount_writeoff_account
+ woff_journal = self.cash_discount_writeoff_journal
+ payment_mode = self.payment_mode_out
+ payment_mode.post_move = True
+ self.company.write(
+ {
+ "default_cash_discount_writeoff_account_id": woff_account.id,
+ "default_cash_discount_writeoff_journal_id": woff_journal.id,
+ "cash_discount_base_amount_type": "untaxed",
+ }
+ )
+ invoice = self.create_supplier_invoice(
+ invoice_date,
+ self.payment_mode_out,
+ 1500,
+ self.tax_20_p,
+ self.payment_term_2_p_discount_7d,
+ )
+ invoice._onchange_discount_delay()
+ self.assertTrue(invoice.has_discount)
+ self.assertRecordValues(
+ invoice,
+ [
+ {
+ "amount_untaxed": 1500.0,
+ "amount_tax": 300.0,
+ "amount_total": 1800.0,
+ "discount_amount": 30.0,
+ "discount_percent": 2.0,
+ "amount_total_with_discount": 1770.0,
+ }
+ ],
+ )
+ invoice.ref = "test ref"
+ invoice.action_post()
+
+ payment_order = self.create_payment_order(invoice_date, invoice)
+
+ self.assertEqual(len(payment_order.payment_line_ids), 1)
+ payment_line = payment_order.payment_line_ids[0]
+ self.assertTrue(payment_line.pay_with_discount)
+ self.assertAlmostEqual(payment_line.amount_currency, 1770.0)
+ self.assertTrue(payment_line.pay_with_discount)
+
+ payment_order.draft2open()
+ payment_order.open2generated()
+ payment_order.generated2uploaded()
+
+ self.assertEqual(len(payment_order.move_ids), 1)
+
+ self.assertEqual(invoice.payment_state, "paid")
+
+ domain_reconlied_line = payment_order.move_ids.open_reconcile_view()["domain"]
+ reconciled_line_ids = self.env["account.move.line"].search(
+ domain_reconlied_line
+ )
+
+ self.assertRecordValues(
+ reconciled_line_ids.sorted(lambda r: r.balance),
+ [
+ {
+ "balance": -1800.0,
+ },
+ {
+ "balance": 30.0,
+ "name": "Cash Discount Write-Off",
+ },
+ {
+ "balance": 1770.0,
+ "name": "test ref",
+ },
+ ],
+ )
+
+ write_off_move = reconciled_line_ids.filtered(
+ lambda r: r.journal_id == woff_journal
+ ).move_id
+
+ self.assertRecordValues(
+ write_off_move.line_ids.sorted(lambda r: r.balance),
+ [
+ {
+ "balance": -30.0, # Not Tax
+ },
+ {
+ "balance": 30.0,
+ },
+ ],
+ )
+
+ def test_invoice_with_discount_with_cn_1(self):
+ """
+ Discount: Tax included
+ Invoice:
+ Amount without Tax: 1500
+ Tax: 20% (300)
+ Total on invoice: 1800
+ Discount: 2% (36)
+
+ Credit Note:
+ Amount without Tax: 500
+ Tax: 20% (100)
+ Total on invoice: 600
+ Discount: 2% (12)
+
+ Total with discount: 1176
+ """
+ invoice_date = Date.today()
+ woff_account = self.cash_discount_writeoff_account
+ woff_journal = self.cash_discount_writeoff_journal
+ payment_mode = self.payment_mode_out
+ payment_mode.post_move = True
+ self.company.write(
+ {
+ "default_cash_discount_writeoff_account_id": woff_account.id,
+ "default_cash_discount_writeoff_journal_id": woff_journal.id,
+ "cash_discount_base_amount_type": "total",
+ }
+ )
+ invoice = self.create_supplier_invoice(
+ invoice_date,
+ self.payment_mode_out,
+ 1500,
+ self.tax_20_p,
+ self.payment_term_2_p_discount_7d,
+ )
+ invoice._onchange_discount_delay()
+ self.assertTrue(invoice.has_discount)
+ self.assertRecordValues(
+ invoice,
+ [
+ {
+ "amount_untaxed": 1500.0,
+ "amount_tax": 300.0,
+ "amount_total": 1800.0,
+ "discount_amount": 36.0,
+ "discount_percent": 2.0,
+ "amount_total_with_discount": 1764.0,
+ "real_discount_amount": 36.0,
+ "refunds_discount_amount": 0.0,
+ "residual_with_discount": 1764.0,
+ "invoice_payment_term_id": self.payment_term_2_p_discount_7d.id,
+ }
+ ],
+ )
+ invoice.ref = "test ref"
+ invoice.action_post()
+
+ move_reversal = (
+ self.env["account.move.reversal"]
+ .with_context(active_model="account.move", active_ids=invoice.ids)
+ .create(
+ {
+ "refund_method": "refund",
+ "journal_id": invoice.journal_id.id,
+ }
+ )
+ )
+ reversal = move_reversal.reverse_moves()
+ reversed_move = self.env["account.move"].browse(reversal["res_id"])
+ reversed_move.write(
+ {
+ "invoice_line_ids": [
+ Command.update(
+ reversed_move.invoice_line_ids.id, {"price_unit": 500}
+ ),
+ ],
+ }
+ )
+ reversed_move.action_post()
+ self.assertRecordValues(
+ reversed_move,
+ [
+ {
+ "amount_untaxed": 500.0,
+ "amount_tax": 100.0,
+ "amount_total": 600.0,
+ "discount_amount": 12.0,
+ "discount_percent": 2.0,
+ "amount_total_with_discount": 588.0,
+ "invoice_payment_term_id": self.payment_term_2_p_discount_7d.id,
+ }
+ ],
+ )
+
+ (invoice + reversed_move).line_ids.filtered(
+ lambda line: line.account_type in ("asset_receivable", "liability_payable")
+ ).reconcile()
+
+ self.assertRecordValues(
+ invoice,
+ [
+ {
+ "amount_untaxed": 1500.0,
+ "amount_tax": 300.0,
+ "amount_total": 1800.0,
+ "discount_amount": 36.0,
+ "discount_percent": 2.0,
+ "amount_total_with_discount": 1764.0,
+ "real_discount_amount": 24.0,
+ "refunds_discount_amount": 12.0,
+ "residual_with_discount": 1176.0,
+ "invoice_payment_term_id": self.payment_term_2_p_discount_7d.id,
+ }
+ ],
+ )
+
+ payment_order = self.create_payment_order(invoice_date, invoice)
+
+ self.assertEqual(len(payment_order.payment_line_ids), 1)
+ payment_line = payment_order.payment_line_ids[0]
+ self.assertTrue(payment_line.pay_with_discount)
+ self.assertAlmostEqual(payment_line.amount_currency, 1176.0)
+ self.assertTrue(payment_line.pay_with_discount)
+
+ payment_order.draft2open()
+ payment_order.open2generated()
+ payment_order.generated2uploaded()
+
+ self.assertEqual(len(payment_order.move_ids), 1)
+
+ self.assertEqual(invoice.payment_state, "paid")
+
+ domain_reconlied_line = payment_order.move_ids.open_reconcile_view()["domain"]
+ reconciled_line_ids = self.env["account.move.line"].search(
+ domain_reconlied_line
+ )
+
+ self.assertRecordValues(
+ reconciled_line_ids.sorted(lambda r: r.balance),
+ [
+ {
+ "balance": -1800.0,
+ },
+ {
+ "balance": 24.0,
+ "name": "Cash Discount Write-Off",
+ },
+ {
+ "balance": 600.0, # Credit note
+ },
+ {
+ "balance": 1176.0,
+ "name": "test ref",
+ },
+ ],
+ )
+
+ write_off_move = reconciled_line_ids.filtered(
+ lambda r: r.journal_id == woff_journal
+ ).move_id
+
+ self.assertRecordValues(
+ write_off_move.line_ids.sorted(lambda r: r.balance),
+ [
+ {
+ "balance": -30.0, # Not Tax
+ },
+ {
+ "balance": -6.0, # Tax
+ },
+ {
+ "balance": 2.0, # Tax Credit Note
+ },
+ {
+ "balance": 10.0, # Credit Note
+ },
+ {
+ "balance": 24.0,
+ },
+ ],
+ )
+
+ def test_invoice_with_discount_with_cn_2(self):
+ """
+ Discount: Tax excluded
+ Invoice:
+ Amount without Tax: 1500
+ Tax: 20% (300)
+ Total on invoice: 1800
+ Discount: 2% (20)
+
+ Credit Note:
+ Amount without Tax: 500
+ Tax: 20% (100)
+ Total on invoice: 600
+ Discount: 2% (10)
+
+ Total with discount: 1180
+ """
+ invoice_date = Date.today()
+ woff_account = self.cash_discount_writeoff_account
+ woff_journal = self.cash_discount_writeoff_journal
+ payment_mode = self.payment_mode_out
+ payment_mode.post_move = True
+ self.company.write(
+ {
+ "default_cash_discount_writeoff_account_id": woff_account.id,
+ "default_cash_discount_writeoff_journal_id": woff_journal.id,
+ "cash_discount_base_amount_type": "untaxed",
+ }
+ )
+ invoice = self.create_supplier_invoice(
+ invoice_date,
+ self.payment_mode_out,
+ 1500,
+ self.tax_20_p,
+ self.payment_term_2_p_discount_7d,
+ )
+ invoice._onchange_discount_delay()
+ self.assertTrue(invoice.has_discount)
+ self.assertRecordValues(
+ invoice,
+ [
+ {
+ "amount_untaxed": 1500.0,
+ "amount_tax": 300.0,
+ "amount_total": 1800.0,
+ "discount_amount": 30.0,
+ "discount_percent": 2.0,
+ "amount_total_with_discount": 1770.0,
+ "real_discount_amount": 30.0,
+ "refunds_discount_amount": 0.0,
+ "residual_with_discount": 1770.0,
+ "invoice_payment_term_id": self.payment_term_2_p_discount_7d.id,
+ }
+ ],
+ )
+ invoice.ref = "test ref"
+ invoice.action_post()
+
+ move_reversal = (
+ self.env["account.move.reversal"]
+ .with_context(active_model="account.move", active_ids=invoice.ids)
+ .create(
+ {
+ "refund_method": "refund",
+ "journal_id": invoice.journal_id.id,
+ }
+ )
+ )
+ reversal = move_reversal.reverse_moves()
+ reversed_move = self.env["account.move"].browse(reversal["res_id"])
+ reversed_move.write(
+ {
+ "invoice_line_ids": [
+ Command.update(
+ reversed_move.invoice_line_ids.id, {"price_unit": 500}
+ ),
+ ],
+ }
+ )
+ reversed_move.action_post()
+ self.assertRecordValues(
+ reversed_move,
+ [
+ {
+ "amount_untaxed": 500.0,
+ "amount_tax": 100.0,
+ "amount_total": 600.0,
+ "discount_amount": 10.0,
+ "discount_percent": 2.0,
+ "amount_total_with_discount": 590.0,
+ "invoice_payment_term_id": self.payment_term_2_p_discount_7d.id,
+ }
+ ],
+ )
+
+ (invoice + reversed_move).line_ids.filtered(
+ lambda line: line.account_type in ("asset_receivable", "liability_payable")
+ ).reconcile()
+
+ self.assertRecordValues(
+ invoice,
+ [
+ {
+ "amount_untaxed": 1500.0,
+ "amount_tax": 300.0,
+ "amount_total": 1800.0,
+ "discount_amount": 30.0,
+ "discount_percent": 2.0,
+ "amount_total_with_discount": 1770.0,
+ "real_discount_amount": 20.0,
+ "refunds_discount_amount": 10.0,
+ "residual_with_discount": 1180.0,
+ "invoice_payment_term_id": self.payment_term_2_p_discount_7d.id,
+ }
+ ],
+ )
+
+ payment_order = self.create_payment_order(invoice_date, invoice)
+
+ self.assertEqual(len(payment_order.payment_line_ids), 1)
+ payment_line = payment_order.payment_line_ids[0]
+ self.assertTrue(payment_line.pay_with_discount)
+ self.assertAlmostEqual(payment_line.amount_currency, 1180.0)
+ self.assertTrue(payment_line.pay_with_discount)
+
+ payment_order.draft2open()
+ payment_order.open2generated()
+ payment_order.generated2uploaded()
+
+ self.assertEqual(len(payment_order.move_ids), 1)
+
+ self.assertEqual(invoice.payment_state, "paid")
+
+ domain_reconlied_line = payment_order.move_ids.open_reconcile_view()["domain"]
+ reconciled_line_ids = self.env["account.move.line"].search(
+ domain_reconlied_line
+ )
+
+ self.assertRecordValues(
+ reconciled_line_ids.sorted(lambda r: r.balance),
+ [
+ {
+ "balance": -1800.0,
+ },
+ {
+ "balance": 20.0,
+ "name": "Cash Discount Write-Off",
+ },
+ {
+ "balance": 600.0, # Credit Note
+ },
+ {
+ "balance": 1180.0,
+ "name": "test ref",
+ },
+ ],
+ )
+
+ write_off_move = reconciled_line_ids.filtered(
+ lambda r: r.journal_id == woff_journal
+ ).move_id
+
+ self.assertRecordValues(
+ write_off_move.line_ids.sorted(lambda r: r.balance),
+ [
+ {
+ "balance": -20.0, # Not Tax
+ },
+ {
+ "balance": 20.0,
+ },
+ ],
+ )
diff --git a/test-requirements.txt b/test-requirements.txt
new file mode 100644
index 000000000000..7c50ec06848a
--- /dev/null
+++ b/test-requirements.txt
@@ -0,0 +1,2 @@
+odoo-addon-account-cash-discount-base @ git+https://github.com/OCA/account-payment.git@refs/pull/706/head#subdirectory=setup/account_cash_discount_base
+odoo-addon-account-cash-discount-payment @ git+https://github.com/OCA/account-payment.git@refs/pull/707/head#subdirectory=setup/account_cash_discount_payment