[FIX] account_reconcile_model_oca: Fix matching rules with no partner
The orm doesn't match monetary amounts using the related currency.
It means, -208.73 != 208.730000000000002.
Let's match the amount using an sql query instead.
Related to ae848e9981
pull/819/head
parent
0510276a36
commit
13f438d320
|
@ -380,15 +380,16 @@ class AccountReconcileModel(models.Model):
|
||||||
:param st_line: A statement line.
|
:param st_line: A statement line.
|
||||||
:param partner: The partner associated to the statement line.
|
:param partner: The partner associated to the statement line.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def get_order_by_clause(alias=None):
|
||||||
|
direction = "DESC" if self.matching_order == "new_first" else "ASC"
|
||||||
|
dotted_alias = f"{alias}." if alias else ""
|
||||||
|
return f"{dotted_alias}date_maturity {direction}, {dotted_alias}date {direction}, {dotted_alias}id {direction}" # noqa: E501
|
||||||
|
|
||||||
assert self.rule_type == "invoice_matching"
|
assert self.rule_type == "invoice_matching"
|
||||||
self.env["account.move"].flush_model()
|
self.env["account.move"].flush_model()
|
||||||
self.env["account.move.line"].flush_model()
|
self.env["account.move.line"].flush_model()
|
||||||
|
|
||||||
if self.matching_order == "new_first":
|
|
||||||
order_by = "sub.date_maturity DESC, sub.date DESC, sub.id DESC"
|
|
||||||
else:
|
|
||||||
order_by = "sub.date_maturity ASC, sub.date ASC, sub.id ASC"
|
|
||||||
|
|
||||||
aml_domain = self._get_invoice_matching_amls_domain(st_line, partner)
|
aml_domain = self._get_invoice_matching_amls_domain(st_line, partner)
|
||||||
query = self.env["account.move.line"]._where_calc(aml_domain)
|
query = self.env["account.move.line"]._where_calc(aml_domain)
|
||||||
from_string, from_params = query.from_clause
|
from_string, from_params = query.from_clause
|
||||||
|
@ -456,6 +457,7 @@ class AccountReconcileModel(models.Model):
|
||||||
all_params += where_params
|
all_params += where_params
|
||||||
|
|
||||||
if sub_queries:
|
if sub_queries:
|
||||||
|
order_by = get_order_by_clause(alias="sub")
|
||||||
self._cr.execute(
|
self._cr.execute(
|
||||||
"""
|
"""
|
||||||
SELECT
|
SELECT
|
||||||
|
@ -480,19 +482,48 @@ class AccountReconcileModel(models.Model):
|
||||||
"amls": self.env["account.move.line"].browse(candidate_ids),
|
"amls": self.env["account.move.line"].browse(candidate_ids),
|
||||||
}
|
}
|
||||||
|
|
||||||
# Search without any matching based on textual information.
|
if not partner:
|
||||||
if partner:
|
st_line_currency = (
|
||||||
if self.matching_order == "new_first":
|
st_line.foreign_currency_id
|
||||||
order = "date_maturity DESC, date DESC, id DESC"
|
or st_line.journal_id.currency_id
|
||||||
|
or st_line.company_currency_id
|
||||||
|
)
|
||||||
|
if st_line_currency == self.company_id.currency_id:
|
||||||
|
aml_amount_field = "amount_residual"
|
||||||
else:
|
else:
|
||||||
order = "date_maturity ASC, date ASC, id ASC"
|
aml_amount_field = "amount_residual_currency"
|
||||||
|
|
||||||
amls = self.env["account.move.line"].search(aml_domain, order=order)
|
order_by = get_order_by_clause(alias="account_move_line")
|
||||||
if amls:
|
self._cr.execute(
|
||||||
return {
|
f"""
|
||||||
"allow_auto_reconcile": False,
|
SELECT account_move_line.id
|
||||||
"amls": amls,
|
FROM {from_clause}
|
||||||
}
|
WHERE
|
||||||
|
{where_clause}
|
||||||
|
AND account_move_line.currency_id = %s
|
||||||
|
AND ROUND(account_move_line.{aml_amount_field}, %s) = ROUND(%s, %s)
|
||||||
|
ORDER BY {order_by}
|
||||||
|
""", # noqa: E501
|
||||||
|
where_params
|
||||||
|
+ [
|
||||||
|
st_line_currency.id,
|
||||||
|
st_line_currency.decimal_places,
|
||||||
|
-st_line.amount_residual,
|
||||||
|
st_line_currency.decimal_places,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
amls = self.env["account.move.line"].browse(
|
||||||
|
[row[0] for row in self._cr.fetchall()]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
amls = self.env["account.move.line"].search(
|
||||||
|
aml_domain, order=get_order_by_clause()
|
||||||
|
)
|
||||||
|
if amls:
|
||||||
|
return {
|
||||||
|
"allow_auto_reconcile": False,
|
||||||
|
"amls": amls,
|
||||||
|
}
|
||||||
|
|
||||||
def _get_invoice_matching_rules_map(self):
|
def _get_invoice_matching_rules_map(self):
|
||||||
"""Get a mapping <priority_order, rule> that could be overridden in others
|
"""Get a mapping <priority_order, rule> that could be overridden in others
|
||||||
|
|
|
@ -1095,40 +1095,6 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
|
||||||
# Matching is back thanks to "coincoin".
|
# Matching is back thanks to "coincoin".
|
||||||
self.assertEqual(st_line._retrieve_partner(), self.partner_1)
|
self.assertEqual(st_line._retrieve_partner(), self.partner_1)
|
||||||
|
|
||||||
def test_partner_name_in_communication(self):
|
|
||||||
self.invoice_line_1.partner_id.write({"name": "Archibald Haddock"})
|
|
||||||
self.bank_line_1.write(
|
|
||||||
{"partner_id": None, "payment_ref": "1234//HADDOCK-Archibald"}
|
|
||||||
)
|
|
||||||
self.bank_line_2.write({"partner_id": None})
|
|
||||||
self.rule_1.write({"match_partner": False})
|
|
||||||
|
|
||||||
# bank_line_1 should match, as its communic. contains the invoice's partner name
|
|
||||||
self._check_statement_matching(
|
|
||||||
self.rule_1,
|
|
||||||
{
|
|
||||||
self.bank_line_1: {"amls": self.invoice_line_1, "model": self.rule_1},
|
|
||||||
self.bank_line_2: {},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_partner_name_with_regexp_chars(self):
|
|
||||||
self.invoice_line_1.partner_id.write({"name": "Archibald + Haddock"})
|
|
||||||
self.bank_line_1.write(
|
|
||||||
{"partner_id": None, "payment_ref": "1234//HADDOCK+Archibald"}
|
|
||||||
)
|
|
||||||
self.bank_line_2.write({"partner_id": None})
|
|
||||||
self.rule_1.write({"match_partner": False})
|
|
||||||
|
|
||||||
# The query should still work
|
|
||||||
self._check_statement_matching(
|
|
||||||
self.rule_1,
|
|
||||||
{
|
|
||||||
self.bank_line_1: {"amls": self.invoice_line_1, "model": self.rule_1},
|
|
||||||
self.bank_line_2: {},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_match_multi_currencies(self):
|
def test_match_multi_currencies(self):
|
||||||
"""Ensure the matching of candidates is made using the right statement line
|
"""Ensure the matching of candidates is made using the right statement line
|
||||||
currency. In this test, the value of the statement line is 100 USD = 300
|
currency. In this test, the value of the statement line is 100 USD = 300
|
||||||
|
@ -1540,3 +1506,70 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
|
||||||
rule._apply_rules(st_line, None),
|
rule._apply_rules(st_line, None),
|
||||||
{"amls": term_lines, "model": rule},
|
{"amls": term_lines, "model": rule},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@freeze_time("2019-01-01")
|
||||||
|
def test_matching_exact_amount_no_partner(self):
|
||||||
|
"""In case the reconciliation model can't match via text or partner matching
|
||||||
|
we do a last check to find amls with the exact amount.
|
||||||
|
"""
|
||||||
|
self.rule_1.write(
|
||||||
|
{
|
||||||
|
"match_text_location_label": False,
|
||||||
|
"match_partner": False,
|
||||||
|
"match_partner_ids": [Command.clear()],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.bank_line_1.partner_id = None
|
||||||
|
self.bank_line_1.payment_ref = False
|
||||||
|
|
||||||
|
with self.subTest(test="single_currency"):
|
||||||
|
st_line = self._create_st_line(
|
||||||
|
amount=100, payment_ref=None, partner_id=None
|
||||||
|
)
|
||||||
|
invl = self._create_invoice_line(100, self.partner_1, "out_invoice")
|
||||||
|
self._check_statement_matching(
|
||||||
|
self.rule_1,
|
||||||
|
{
|
||||||
|
st_line: {
|
||||||
|
"amls": invl,
|
||||||
|
"model": self.rule_1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
with self.subTest(test="rounding"):
|
||||||
|
st_line = self._create_st_line(
|
||||||
|
amount=-208.73, payment_ref=None, partner_id=None
|
||||||
|
)
|
||||||
|
invl = self._create_invoice_line(208.73, self.partner_1, "in_invoice")
|
||||||
|
self._check_statement_matching(
|
||||||
|
self.rule_1,
|
||||||
|
{
|
||||||
|
st_line: {
|
||||||
|
"amls": invl,
|
||||||
|
"model": self.rule_1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
with self.subTest(test="multi_currencies"):
|
||||||
|
foreign_curr = self.other_currency
|
||||||
|
invl = self._create_invoice_line(
|
||||||
|
300, self.partner_1, "out_invoice", currency=foreign_curr
|
||||||
|
)
|
||||||
|
st_line = self._create_st_line(
|
||||||
|
amount=15.0,
|
||||||
|
foreign_currency_id=foreign_curr.id,
|
||||||
|
amount_currency=300.0,
|
||||||
|
payment_ref=None,
|
||||||
|
partner_id=None,
|
||||||
|
)
|
||||||
|
self._check_statement_matching(
|
||||||
|
self.rule_1,
|
||||||
|
{
|
||||||
|
st_line: {
|
||||||
|
"amls": invl,
|
||||||
|
"model": self.rule_1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
Loading…
Reference in New Issue