[FIX] account: reconciliation models: properly match statement line fields when no partner is set
When no partner is set on a statement line, the reconciliation models try to find candidates using the payment reference or the partner name.
This was not working well when using reconciliation models configured to match on notes and/or reference. Only the default match on label was working.
As an example, consider the following case:
1) setup an invoice-matcing reconciliation model as such:
- Partner Is Set and Matches = False
- Match Invoice/bill with = Reference
2) Create an invoice with payment reference 123, for 100€
3) Create a statement line of 100€, with reference ('ref' field, inherited from account.move) = 123, label='test', and no partner set.
4) Try to reconcile the statement line
=> not match is found
OPW 2701729
X-original-commit: 74894b0da82f5f71cd22a3c9bb405696f908ef5c
Part-of: odoo/odoo#81730
This commit is contained in:
@@ -634,30 +634,42 @@ class AccountReconcileModel(models.Model):
|
||||
if partner:
|
||||
st_line_subquery += r" AND aml.partner_id = %s" % partner.id
|
||||
else:
|
||||
st_line_subquery += r"""
|
||||
AND
|
||||
(
|
||||
substring(REGEXP_REPLACE(st_line.payment_ref, '[^0-9\s]', '', 'g'), '\S(?:.*\S)*') != ''
|
||||
AND
|
||||
(
|
||||
(""" + self._get_select_communication_flag() + """)
|
||||
OR
|
||||
(""" + self._get_select_payment_reference_flag() + """)
|
||||
)
|
||||
)
|
||||
OR
|
||||
(
|
||||
/* We also match statement lines without partners with amls
|
||||
whose partner's name's parts (splitting on space) are all present
|
||||
within the payment_ref, in any order, with any characters between them. */
|
||||
st_line_fields_consideration = [
|
||||
(self.match_text_location_label, 'st_line.payment_ref'),
|
||||
(self.match_text_location_note, 'st_line_move.narration'),
|
||||
(self.match_text_location_reference, 'st_line_move.ref'),
|
||||
]
|
||||
|
||||
aml_partner.name IS NOT NULL
|
||||
AND """ + unaccent("st_line.payment_ref") + r""" ~* ('^' || (
|
||||
SELECT string_agg(concat('(?=.*\m', chunk[1], '\M)'), '')
|
||||
FROM regexp_matches(""" + unaccent("aml_partner.name") + r""", '\w{3,}', 'g') AS chunk
|
||||
))
|
||||
)
|
||||
"""
|
||||
no_partner_query = " OR ".join([
|
||||
r"""
|
||||
(
|
||||
substring(REGEXP_REPLACE(""" + sql_field + r""", '[^0-9\s]', '', 'g'), '\S(?:.*\S)*') != ''
|
||||
AND
|
||||
(
|
||||
(""" + self._get_select_communication_flag() + r""")
|
||||
OR
|
||||
(""" + self._get_select_payment_reference_flag() + r""")
|
||||
)
|
||||
)
|
||||
OR
|
||||
(
|
||||
/* We also match statement lines without partners with amls
|
||||
whose partner's name's parts (splitting on space) are all present
|
||||
within the payment_ref, in any order, with any characters between them. */
|
||||
|
||||
aml_partner.name IS NOT NULL
|
||||
AND """ + unaccent(sql_field) + r""" ~* ('^' || (
|
||||
SELECT string_agg(concat('(?=.*\m', chunk[1], '\M)'), '')
|
||||
FROM regexp_matches(""" + unaccent("aml_partner.name") + r""", '\w{3,}', 'g') AS chunk
|
||||
))
|
||||
)
|
||||
"""
|
||||
for consider_field, sql_field in st_line_fields_consideration
|
||||
if consider_field
|
||||
])
|
||||
|
||||
if no_partner_query:
|
||||
st_line_subquery += " AND " + no_partner_query
|
||||
|
||||
st_lines_queries.append(r"st_line.id = %s AND (%s)" % (st_line.id, st_line_subquery))
|
||||
|
||||
|
||||
@@ -298,6 +298,51 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
|
||||
self.bank_line_5.id: {'aml_ids': [self.invoice_line_6.id], 'model': self.rule_1, 'partner': self.bank_line_5.partner_id},
|
||||
}, statements=self.bank_st_2)
|
||||
|
||||
def test_matching_fields_match_text_location_no_partner(self):
|
||||
self.bank_line_2.unlink() # One line is enough for this test
|
||||
self.bank_line_1.partner_id = None
|
||||
|
||||
self.partner_1.name = "Bernard Gagnant"
|
||||
|
||||
self.rule_1.write({
|
||||
'match_partner': False,
|
||||
'match_partner_ids': [(5, 0, 0)],
|
||||
'line_ids': [(5, 0, 0)],
|
||||
})
|
||||
|
||||
st_line_initial_vals = {'ref': None, 'payment_ref': 'nothing', 'narration': None}
|
||||
recmod_initial_vals = {'match_text_location_label': False, 'match_text_location_note': False, 'match_text_location_reference': False}
|
||||
|
||||
rec_mod_options_to_fields = {
|
||||
'match_text_location_label': 'payment_ref',
|
||||
'match_text_location_note': 'narration',
|
||||
'match_text_location_reference': 'ref',
|
||||
}
|
||||
|
||||
for rec_mod_field, st_line_field in rec_mod_options_to_fields.items():
|
||||
self.rule_1.write({**recmod_initial_vals, rec_mod_field: True})
|
||||
# Fully reinitialize the statement line
|
||||
self.bank_line_1.write(st_line_initial_vals)
|
||||
|
||||
# Nothing should match
|
||||
self._check_statement_matching(self.rule_1, {
|
||||
self.bank_line_1.id: {'aml_ids': []},
|
||||
}, statements=self.bank_st)
|
||||
|
||||
# Test matching with the invoice ref
|
||||
self.bank_line_1.write({st_line_field: self.invoice_line_1.move_id.payment_reference})
|
||||
|
||||
self._check_statement_matching(self.rule_1, {
|
||||
self.bank_line_1.id: {'aml_ids': self.invoice_line_1.ids, 'model': self.rule_1, 'partner': self.env['res.partner']},
|
||||
}, statements=self.bank_st)
|
||||
|
||||
# Test matching with the partner name (reinitializing the statement line first)
|
||||
self.bank_line_1.write({**st_line_initial_vals, st_line_field: self.partner_1.name})
|
||||
|
||||
self._check_statement_matching(self.rule_1, {
|
||||
self.bank_line_1.id: {'aml_ids': self.invoice_line_1.ids, 'model': self.rule_1, 'partner': self.env['res.partner']},
|
||||
}, statements=self.bank_st)
|
||||
|
||||
def test_matching_fields_match_journal_ids(self):
|
||||
self.rule_1.match_journal_ids |= self.cash_st.journal_id
|
||||
self._check_statement_matching(self.rule_1, {
|
||||
|
||||
Reference in New Issue
Block a user