Files
enterprise-addons/pos_settle_due/tests/test_receivable_account_reconciliation.py
T

260 lines
11 KiB
Python

from unittest.mock import patch
import odoo
from odoo.addons.point_of_sale.tests.common import TestPoSCommon
@odoo.tests.tagged('-at_install', 'post_install') # Only runs after installation, not at install time
class TestPOSCustomerAccountReconciliation(TestPoSCommon):
def setUp(self):
super().setUp()
self.config = self.basic_config
self.order_data = [{
"pos_order_lines_ui_args": [(self.product_a, 1)],
"payments": [(self.pay_later_pm, 1150)],
"customer": self.partner_a,
"is_invoiced": True,
}, {
"pos_order_lines_ui_args": [(self.product_a, 1)],
"payments": [(self.pay_later_pm, 1150)],
"customer": self.partner_a,
"is_invoiced": False,
}, {
"pos_order_lines_ui_args": [(self.product_a, 1)],
"payments": [(self.pay_later_pm, 1150)],
"customer": self.partner_a,
"is_invoiced": False,
}, {
"pos_order_lines_ui_args": [(self.product_a, 1)],
"payments": [(self.pay_later_pm, 1150)],
"customer": self.partner_b,
"is_invoiced": True,
}, {
"pos_order_lines_ui_args": [(self.product_a, 1)],
"payments": [(self.pay_later_pm, 1150)],
"customer": self.partner_b,
"is_invoiced": False,
}]
def _create_paid_orders(self):
orders_dict = self._create_orders(self.order_data)
orders = self.env["pos.order"]
for order in orders_dict.values():
order.action_pos_order_paid()
orders |= order
return orders
def _get_unreconciled_moves(self, partner):
move_lines = self.env['account.move.line'].search([
('partner_id', '=', partner.id),
('account_id', '=', partner.property_account_receivable_id.id),
('reconciled', '=', False)
])
return move_lines
def _create_settle_order_lines_data(self, due_orders):
lines = []
for order in due_orders:
lines.append({'product': self.config.settle_due_product_id,
'quantity': 1,
'settled_order_id': order.id if not order.is_invoiced else False,
'settled_invoice_id': order.account_move.id if order.is_invoiced else False,
"qty": 0.0,
"price_unit": order.amount_paid,
"price_subtotal": 0.0,
"price_subtotal_incl": 0.0,
"price_type": "manual",
"discount": 0.0,
"refunded_qty": 0.0,
"price_extra": 0.0
})
return lines
def _create_settle_order(self, due_orders, is_invoiced=False):
result = []
order_data = []
for partner_id in due_orders.mapped('partner_id'):
partner_due_orders = due_orders.filtered(lambda o: o.partner_id.id == partner_id.id)
total_due = sum(order.amount_paid for order in partner_due_orders)
payments = [(self.bank_pm1, total_due)]
payments.append((self.pay_later_pm, -total_due))
order_data.append({
"pos_order_lines_ui_args": self._create_settle_order_lines_data(
partner_due_orders
),
"payments": payments,
"customer": partner_id,
"is_invoiced": is_invoiced,
})
order_data = [self.create_ui_order_data(**params) for params in order_data]
for data in order_data:
data['state'] = 'paid'
data['amount_paid'] = 0
order_ids = [order['id'] for order in self.env['pos.order'].sync_from_ui(order_data)['pos.order']]
for order_id in self.env["pos.order"].browse(order_ids):
result += order_id
return result
def _perform_test_customer_account_payment_is_reconciled(self, is_invoiced):
session1 = self.open_new_session()
orders = self._create_paid_orders()
session1.close_session_from_ui()
self.assertTrue(self._get_unreconciled_moves(self.partner_a))
self.assertTrue(self._get_unreconciled_moves(self.partner_b))
session2 = self.open_new_session()
self._create_settle_order(orders, is_invoiced)
session2.close_session_from_ui()
self.assertFalse(self._get_unreconciled_moves(self.partner_a))
self.assertFalse(self._get_unreconciled_moves(self.partner_b))
def test_customer_account_payment_is_reconciled_when_settlement_order_invoiced(self):
self._perform_test_customer_account_payment_is_reconciled(True)
def test_customer_account_payment_is_reconciled_when_settlement_order_not_invoiced(self):
self._perform_test_customer_account_payment_is_reconciled(False)
def test_customer_account_payment_is_reconciled_when_settlement_on_same_session(self):
session1 = self.open_new_session()
orders = self._create_paid_orders()
self._create_settle_order(orders, False)
session1.close_session_from_ui()
self.assertFalse(self._get_unreconciled_moves(self.partner_a))
self.assertFalse(self._get_unreconciled_moves(self.partner_b))
def test_different_customer_invoices_settlement_same_session_reconciled_separately(self):
self.partner_a.property_account_receivable_id = self.partner_b.property_account_receivable_id
invoice_a = self._create_invoice_one_line(partner_id=self.partner_a, price_unit=1000)
invoice_b = self._create_invoice_one_line(partner_id=self.partner_b, price_unit=2000)
(invoice_a + invoice_b).action_post()
session1 = self.open_new_session()
order_data = [
{
"pos_order_lines_ui_args": [{
'product': self.config.settle_invoice_product_id,
'quantity': 1,
'settled_order_id': False,
'settled_invoice_id': invoice_a.id,
"qty": 0.0,
"price_unit": 1000,
"price_subtotal": 0.0,
"price_subtotal_incl": 0.0,
"price_type": "manual",
"discount": 0.0,
"refunded_qty": 0.0,
"price_extra": 0.0,
}],
"payments": [(self.bank_pm1, 500)],
"customer": self.partner_a,
"is_invoiced": True,
},
{
"pos_order_lines_ui_args": [{
'product': self.config.settle_invoice_product_id,
'quantity': 1,
'settled_order_id': False,
'settled_invoice_id': invoice_b.id,
"qty": 0.0,
"price_unit": 2000,
"price_subtotal": 0.0,
"price_subtotal_incl": 0.0,
"price_type": "manual",
"discount": 0.0,
"refunded_qty": 0.0,
"price_extra": 0.0
}],
"payments": [(self.bank_pm1, 1000)],
"customer": self.partner_b,
"is_invoiced": True,
},
]
order_data = [self.create_ui_order_data(**params) for params in order_data]
order_data[0].update({
'state': 'paid',
'amount_total': '500',
})
order_data[1].update({
'state': 'paid',
'amount_total': '1000',
})
self.env['pos.order'].sync_from_ui(order_data)
session1.close_session_from_ui()
invoice_lines_matching = self.env['account.move.line'].search([('matching_number', '=', invoice_a.line_ids[-1].matching_number)])
self.assertEqual(len(invoice_lines_matching.partner_id), 1, "Only aml from same partner should be reconciled together")
def test_pos_amount_unsettled_after_session_close(self):
""" Partially settling an invoice in the POS must leave a correct
pos_amount_unsettled once the session is closed.
While the session is being closed, the settle payment is reconciled
with the invoice (lowering amount_residual_signed and marking
pos_amount_unsettled for recomputation) before the session state is
set to 'closed'. If any flush executes the pending recomputation in
that window, the settle line is deducted a second time
(residual - settled instead of residual) and, as the session state
was not a dependency, closing the session did not correct the value.
The flush after the reconciliation simulates that recomputation.
"""
invoice = self._create_invoice_one_line(partner_id=self.partner_a, price_unit=1000)
invoice.action_post()
self.assertEqual(invoice.pos_amount_unsettled, 1000)
session = self.open_new_session()
order_data = self.create_ui_order_data(
pos_order_lines_ui_args=[{
'product': self.config.settle_invoice_product_id,
'quantity': 1,
'settled_order_id': False,
'settled_invoice_id': invoice.id,
"qty": 0.0,
"price_unit": 700,
"price_subtotal": 0.0,
"price_subtotal_incl": 0.0,
"price_type": "manual",
"discount": 0.0,
"refunded_qty": 0.0,
"price_extra": 0.0,
}],
payments=[(self.bank_pm1, 700), (self.pay_later_pm, -700)],
customer=self.partner_a,
)
order_data['state'] = 'paid'
order_data['amount_paid'] = 0
self.env['pos.order'].sync_from_ui([order_data])
self.assertEqual(invoice.pos_amount_unsettled, 300)
PosSession = self.env.registry['pos.session']
original_reconcile = PosSession._reconcile_account_move_lines
def reconcile_and_flush(session_self, data):
result = original_reconcile(session_self, data)
session_self.env['account.move'].flush_model()
return result
with patch.object(PosSession, '_reconcile_account_move_lines', reconcile_and_flush):
session.close_session_from_ui()
self.assertEqual(invoice.amount_residual_signed, 300)
self.assertEqual(invoice.pos_amount_unsettled, 300)
self.assertEqual(self.partner_a.invoices_amount_due, 300)
def test_settlement_invoiced_later(self):
session = self.open_new_session()
order_dict = self._create_orders([{
"pos_order_lines_ui_args": [(self.product_a, 1)],
"payments": [(self.pay_later_pm, 1150)],
"customer": self.partner_a,
"is_invoiced": False,
}])
order = next(iter(order_dict.values()))
order.action_pos_order_paid()
self._create_settle_order(order, False)
session.close_session_from_ui()
order._generate_pos_order_invoice()
self.assertEqual(order.state, 'done')
self.assertEqual(order.account_move.payment_state, 'paid')