Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[17.0][IMP] purchase_advance_payment: auto reconcile adv payments #2409

Open
wants to merge 1 commit into
base: 17.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions purchase_advance_payment/data/ir_config_parameter.xml
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo noupdate="1">

<record id="auto_post_advance_payments" model="ir.config_parameter" forcecreate="0">
<field name="key">purchase_advance_payment.auto_post_advance_payments</field>
<field name="value">True</field>
</record>

<record
id="auto_reconcile_advance_payments"
model="ir.config_parameter"
forcecreate="0"
>
<field
name="key"
>purchase_advance_payment.auto_reconcile_advance_payments</field>
<field name="value">False</field>
</record>
</odoo>
1 change: 1 addition & 0 deletions purchase_advance_payment/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from . import account_move
from . import payment
from . import purchase_order
from . import res_config_settings
35 changes: 35 additions & 0 deletions purchase_advance_payment/models/account_move.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Copyright 2024 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo import models


class AccountMove(models.Model):
_inherit = "account.move"

def action_post(self):
# Automatic reconciliation of payment when bill confirmed.
res = super().action_post()
if bool(
self.env["ir.config_parameter"]
.sudo()
.get_param("purchase_advance_payment.auto_reconcile_advance_payments")
):
for move in self:
purchase_order = move.line_ids.purchase_order_id
if (
purchase_order
and move.invoice_outstanding_credits_debits_widget is not False
):
json_invoice_outstanding_data = (
move.invoice_outstanding_credits_debits_widget.get(
"content", []
)
)
for data in json_invoice_outstanding_data:
if (
data.get("move_id")
in purchase_order.account_payment_ids.move_id.ids
):
move.js_assign_outstanding_line(line_id=data.get("id"))
return res
3 changes: 3 additions & 0 deletions purchase_advance_payment/models/res_config_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@ class ResConfigSettings(models.TransientModel):
auto_post_purchase_advance_payments = fields.Boolean(
config_parameter="purchase_advance_payment.auto_post_advance_payments",
)
auto_reconcile_purchase_advance_payments = fields.Boolean(
config_parameter="purchase_advance_payment.auto_reconcile_advance_payments",
)
209 changes: 209 additions & 0 deletions purchase_advance_payment/tests/test_purchase_advance_payment.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,9 @@ def setUpClass(cls):
"currency_id": cls.currency_usd.id,
}
)
cls.env["ir.config_parameter"].sudo().set_param(
"purchase_advance_payment.auto_reconcile_advance_payments", False
)

def test_00_with_context_payment(self):
context_payment_2 = {
Expand Down Expand Up @@ -177,6 +180,13 @@ def test_00_with_context_payment(self):
)

def test_01_purchase_advance_payment(self):
self.assertFalse(
bool(
self.env["ir.config_parameter"]
.sudo()
.get_param("purchase_advance_payment.auto_reconcile_advance_payments")
)
)
self.assertEqual(
self.purchase_order_1.amount_residual,
3600,
Expand Down Expand Up @@ -253,6 +263,13 @@ def test_01_purchase_advance_payment(self):
self.assertEqual(self.purchase_order_1.amount_residual, 2580)

def test_02_residual_amount_with_bill(self):
self.assertFalse(
bool(
self.env["ir.config_parameter"]
.sudo()
.get_param("purchase_advance_payment.auto_reconcile_advance_payments")
)
)
self.assertEqual(
self.purchase_order_1.amount_residual,
3600,
Expand Down Expand Up @@ -319,6 +336,13 @@ def test_02_residual_amount_with_bill(self):
self.assertEqual(self.purchase_order_1.amount_residual, 2200)

def test_03_residual_amount_big_pre_payment(self):
self.assertFalse(
bool(
self.env["ir.config_parameter"]
.sudo()
.get_param("purchase_advance_payment.auto_reconcile_advance_payments")
)
)
self.assertEqual(
self.purchase_order_1.amount_residual,
3600,
Expand Down Expand Up @@ -398,6 +422,13 @@ def test_03_residual_amount_big_pre_payment(self):
self.assertEqual(self.purchase_order_1.amount_residual, 1300)

def test_04_residual_amount_with_no_amount_left(self):
self.assertFalse(
bool(
self.env["ir.config_parameter"]
.sudo()
.get_param("purchase_advance_payment.auto_reconcile_advance_payments")
)
)
self.assertEqual(
self.purchase_order_1.amount_residual,
3600,
Expand All @@ -423,6 +454,13 @@ def test_04_residual_amount_with_no_amount_left(self):
self.assertEqual(self.purchase_order_1.advance_payment_status, "paid")

def test_05_check_residual_amount_warning(self):
self.assertFalse(
bool(
self.env["ir.config_parameter"]
.sudo()
.get_param("purchase_advance_payment.auto_reconcile_advance_payments")
)
)
self.assertEqual(
self.purchase_order_1.amount_residual,
3600,
Expand Down Expand Up @@ -466,6 +504,13 @@ def test_05_check_residual_amount_warning(self):
)

def test_06_skip_payment_post(self):
self.assertFalse(
bool(
self.env["ir.config_parameter"]
.sudo()
.get_param("purchase_advance_payment.auto_reconcile_advance_payments")
)
)
self.assertEqual(
self.purchase_order_1.amount_residual,
3600,
Expand Down Expand Up @@ -510,3 +555,167 @@ def test_06_skip_payment_post(self):
payment_2 = self.purchase_order_1.account_payment_ids - payment_1
self.assertEqual(len(payment_2), 1)
self.assertEqual(payment_2.state, "draft")

def test_07_auto_reconcile_advance_payment_enabled(self):
# Set the config parameter to True
self.assertFalse(
bool(
self.env["ir.config_parameter"]
.sudo()
.get_param("purchase_advance_payment.auto_reconcile_advance_payments")
)
)
# Set the config parameter to True
self.env["ir.config_parameter"].sudo().set_param(
"purchase_advance_payment.auto_reconcile_advance_payments", True
)
self.assertTrue(
bool(
self.env["ir.config_parameter"]
.sudo()
.get_param("purchase_advance_payment.auto_reconcile_advance_payments")
)
)
self.assertEqual(
self.purchase_order_1.amount_residual,
3600,
)
self.assertEqual(
self.purchase_order_1.amount_residual,
self.purchase_order_1.amount_total,
)
context_payment = {
"active_ids": [self.purchase_order_1.id],
"active_id": self.purchase_order_1.id,
}
# Create Advance Payment - USD - cash
advance_payment_usd = (
self.env["account.voucher.wizard.purchase"]
.with_context(**context_payment)
.create(
{
"journal_id": self.journal_usd_cash.id,
"amount_advance": 200,
"order_id": self.purchase_order_1.id,
}
)
)
advance_payment_usd.make_advance_payment()
pre_payment = self.purchase_order_1.account_payment_ids
self.assertEqual(len(pre_payment), 1)
self.assertEqual(self.purchase_order_1.amount_residual, 3400)
# generate bill, pay bill, check amount residual.
self.purchase_order_1.button_confirm()
self.assertEqual(self.purchase_order_1.invoice_status, "to invoice")
self.purchase_order_1.action_create_invoice()
self.assertEqual(self.purchase_order_1.invoice_status, "invoiced")
self.assertEqual(self.purchase_order_1.amount_residual, 3400)
invoice = self.purchase_order_1.invoice_ids
invoice.invoice_date = fields.Date.today()
self.assertEqual(invoice.amount_residual, 3600)
invoice.action_post()
self.assertEqual(invoice.amount_residual, 3400)
self.assertNotEqual(
invoice.amount_residual,
invoice.amount_total,
)
self.assertTrue(
invoice.payment_state in ["partial"],
"Advance payment should be reconciled automatically.",
)

def test_08_auto_reconcile_advance_payment_disabled(self):
self.assertFalse(
bool(
self.env["ir.config_parameter"]
.sudo()
.get_param("purchase_advance_payment.auto_reconcile_advance_payments")
)
)

self.assertEqual(
self.purchase_order_1.amount_residual,
3600,
)
self.assertEqual(
self.purchase_order_1.amount_residual,
self.purchase_order_1.amount_total,
)
context_payment = {
"active_ids": [self.purchase_order_1.id],
"active_id": self.purchase_order_1.id,
}
# Create Advance Payment - USD - cash
advance_payment_usd = (
self.env["account.voucher.wizard.purchase"]
.with_context(**context_payment)
.create(
{
"journal_id": self.journal_usd_cash.id,
"amount_advance": 200,
"order_id": self.purchase_order_1.id,
}
)
)
advance_payment_usd.make_advance_payment()
pre_payment = self.purchase_order_1.account_payment_ids
self.assertEqual(len(pre_payment), 1)
self.assertEqual(self.purchase_order_1.amount_residual, 3400)
# generate bill, pay bill, check amount residual.
self.purchase_order_1.button_confirm()
self.assertEqual(self.purchase_order_1.invoice_status, "to invoice")
self.purchase_order_1.action_create_invoice()
self.assertEqual(self.purchase_order_1.invoice_status, "invoiced")
self.assertEqual(self.purchase_order_1.amount_residual, 3400)
invoice = self.purchase_order_1.invoice_ids
invoice.invoice_date = fields.Date.today()
self.assertEqual(invoice.amount_residual, 3600)
invoice.action_post()
self.assertEqual(invoice.amount_residual, 3600)
self.assertEqual(
invoice.amount_residual,
invoice.amount_total,
)
self.assertEqual(
invoice.payment_state,
"not_paid",
"Advance payment should not be automatically reconciled "
"when setting is disabled.",
)

def test_09_no_reconcile_when_no_matching_payment(self):
# Set the config parameter to True
self.env["ir.config_parameter"].sudo().set_param(
"purchase_advance_payment.auto_reconcile_advance_payments", True
)
self.assertEqual(
self.purchase_order_1.amount_residual,
3600,
)
self.assertEqual(
self.purchase_order_1.amount_residual,
self.purchase_order_1.amount_total,
)

# Confirm Purchase Order
self.purchase_order_1.button_confirm()
self.assertEqual(self.purchase_order_1.invoice_status, "to invoice")

# Create and post the invoice without making an advance payment
self.purchase_order_1.action_create_invoice()
self.assertEqual(self.purchase_order_1.invoice_status, "invoiced")
self.assertEqual(self.purchase_order_1.amount_residual, 3600)
invoice = self.purchase_order_1.invoice_ids
invoice.write({"invoice_date": fields.Date.today()})
invoice.action_post()
self.assertEqual(invoice.amount_residual, 3600)
self.assertEqual(
invoice.amount_residual,
invoice.amount_total,
)
# Ensure no reconciliation happens
self.assertEqual(
invoice.payment_state,
"not_paid",
"No reconciliation should happen without advance payments.",
)
7 changes: 7 additions & 0 deletions purchase_advance_payment/views/res_config_settings_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@
>
<field name="auto_post_purchase_advance_payments" />
</setting>
<setting
id="purchase_advance_payment_auto_reconcile_advance_payments"
title="Automatically reconcile payments upon bill confirmation."
help="If enabled, advance payments created from POs are automatically reconciled upon bill confirmation."
>
<field name="auto_reconcile_purchase_advance_payments" />
</setting>
</xpath>
</field>
</record>
Expand Down
Loading