[FIX] *: pre-commit fixes

Due to latest copier template with all the checks.
pull/654/head
Pedro M. Baeza 2024-05-01 19:31:30 +02:00
parent 3f8a1a8ef9
commit 90ca34afc2
7 changed files with 105 additions and 78 deletions

View File

@ -155,8 +155,8 @@ class MassReconcileAdvanced(models.AbstractModel):
mkey, mvalue = matcher mkey, mvalue = matcher
omkey, omvalue = opposite_matcher omkey, omvalue = opposite_matcher
assert mkey == omkey, _( assert mkey == omkey, _(
"A matcher %(mkey)s is compared with a matcher %(omkey)s, the _matchers and " "A matcher %(mkey)s is compared with a matcher %(omkey)s, the _matchers "
"_opposite_matchers are probably wrong" "and _opposite_matchers are probably wrong"
) % {"mkey": mkey, "omkey": omkey} ) % {"mkey": mkey, "omkey": omkey}
if not isinstance(mvalue, list | tuple): if not isinstance(mvalue, list | tuple):
mvalue = (mvalue,) mvalue = (mvalue,)

View File

@ -63,12 +63,14 @@ class AccountBankStatementLine(models.Model):
continue continue
# Find a partner having a name contained inside the statement line values. # Find a partner having a name contained inside the statement line values.
# Take care a partner could contain some special characters in its name that needs to be escaped. # Take care a partner could contain some special characters in its name that
# needs to be escaped.
sub_queries.append( sub_queries.append(
rf""" rf"""
{unaccent("%s")} ~* ('^' || ( {unaccent("%s")} ~* ('^' || (
SELECT STRING_AGG(CONCAT('(?=.*\m', chunk[1], '\M)'), '') SELECT STRING_AGG(CONCAT('(?=.*\m', chunk[1], '\M)'), '')
FROM regexp_matches({unaccent('partner.name')}, '\w{{3,}}', 'g') AS chunk FROM regexp_matches({unaccent('partner.name')}, '\w{{3,}}', 'g')
AS chunk
)) ))
""" """
) )
@ -100,7 +102,8 @@ class AccountBankStatementLine(models.Model):
return self.env["res.partner"] return self.env["res.partner"]
def _get_st_line_strings_for_matching(self, allowed_fields=None): def _get_st_line_strings_for_matching(self, allowed_fields=None):
"""Collect the strings that could be used on the statement line to perform some matching. """Collect the strings that could be used on the statement line to perform some
matching.
:param allowed_fields: A explicit list of fields to consider. :param allowed_fields: A explicit list of fields to consider.
:return: A list of strings. :return: A list of strings.
""" """

View File

@ -14,13 +14,14 @@ class AccountReconcileModel(models.Model):
#################################################### ####################################################
def _apply_lines_for_bank_widget(self, residual_amount_currency, partner, st_line): def _apply_lines_for_bank_widget(self, residual_amount_currency, partner, st_line):
"""Apply the reconciliation model lines to the statement line passed as parameter. """Apply the reconciliation model lines to the statement line passed as
:param residual_amount_currency: The open balance of the statement line in the bank reconciliation widget parameter.
expressed in the statement line currency. :param residual_amount_currency: The open balance of the statement line in the
bank reconciliation widget expressed in the statement line currency.
:param partner: The partner set on the wizard. :param partner: The partner set on the wizard.
:param st_line: The statement line processed by the bank reconciliation widget. :param st_line: The statement line processed by the bank reconciliation widget.
:return: A list of python dictionaries (one per reconcile model line) representing :return: A list of python dictionaries (one per reconcile model line)
the journal items to be created by the current reconcile model. representing the journal items to be created by the current reconcile model.
""" """
self.ensure_one() self.ensure_one()
currency = ( currency = (
@ -49,8 +50,10 @@ class AccountReconcileModel(models.Model):
def _get_taxes_move_lines_dict(self, tax, base_line_dict): def _get_taxes_move_lines_dict(self, tax, base_line_dict):
"""Get move.lines dict (to be passed to the create()) corresponding to a tax. """Get move.lines dict (to be passed to the create()) corresponding to a tax.
:param tax: An account.tax record. :param tax: An account.tax record.
:param base_line_dict: A dict representing the move.line containing the base amount. :param base_line_dict: A dict representing the move.line containing the base
:return: A list of dict representing move.lines to be created corresponding to the tax. amount.
:return: A list of dict representing move.lines to be created corresponding to
the tax.
""" """
self.ensure_one() self.ensure_one()
balance = base_line_dict["balance"] balance = base_line_dict["balance"]
@ -103,9 +106,12 @@ class AccountReconcileModel(models.Model):
return new_aml_dicts return new_aml_dicts
def _get_write_off_move_lines_dict(self, residual_balance, partner_id): def _get_write_off_move_lines_dict(self, residual_balance, partner_id):
"""Get move.lines dict corresponding to the reconciliation model's write-off lines. """Get move.lines dict corresponding to the reconciliation model's write-off
:param residual_balance: The residual balance of the account on the manual reconciliation widget. lines.
:return: A list of dict representing move.lines to be created corresponding to the write-off lines. :param residual_balance: The residual balance of the account on the manual
reconciliation widget.
:return: A list of dict representing move.lines to be created corresponding to
the write-off lines.
""" """
self.ensure_one() self.ensure_one()
@ -153,8 +159,9 @@ class AccountReconcileModel(models.Model):
if detected_fiscal_position: if detected_fiscal_position:
taxes = detected_fiscal_position.map_tax(taxes) taxes = detected_fiscal_position.map_tax(taxes)
writeoff_line["tax_ids"] += [Command.set(taxes.ids)] writeoff_line["tax_ids"] += [Command.set(taxes.ids)]
# Multiple taxes with force_tax_included results in wrong computation, so we # Multiple taxes with force_tax_included results in wrong computation,
# only allow to set the force_tax_included field if we have one tax selected # so we only allow to set the force_tax_included field if we have one
# tax selected
if line.force_tax_included: if line.force_tax_included:
taxes = taxes[0].with_context(force_price_include=True) taxes = taxes[0].with_context(force_price_include=True)
tax_vals_list = self._get_taxes_move_lines_dict(taxes, writeoff_line) tax_vals_list = self._get_taxes_move_lines_dict(taxes, writeoff_line)
@ -172,15 +179,17 @@ class AccountReconcileModel(models.Model):
def _apply_rules(self, st_line, partner): def _apply_rules(self, st_line, partner):
"""Apply criteria to get candidates for all reconciliation models. """Apply criteria to get candidates for all reconciliation models.
This function is called in enterprise by the reconciliation widget to match This function is called in enterprise by the reconciliation widget to match
the statement line with the available candidates (using the reconciliation models). the statement line with the available candidates (using the reconciliation
models).
:param st_line: The statement line to match. :param st_line: The statement line to match.
:param partner: The partner to consider. :param partner: The partner to consider.
:return: A dict mapping each statement line id with: :return: A dict mapping each statement line id with:
* aml_ids: A list of account.move.line ids. * aml_ids: A list of account.move.line ids.
* model: An account.reconcile.model record (optional). * model: An account.reconcile.model record (optional).
* status: 'reconciled' if the lines has been already reconciled, 'write_off' if the write-off * status: 'reconciled' if the lines has been already reconciled, 'write_off'
must be applied on the statement line. if the write-off must be applied on the statement line.
* auto_reconcile: A flag indicating if the match is enough significant to auto reconcile the candidates. * auto_reconcile: A flag indicating if the match is enough significant to
auto reconcile the candidates.
""" """
available_models = self.filtered( available_models = self.filtered(
lambda m: m.rule_type != "writeoff_button" lambda m: m.rule_type != "writeoff_button"
@ -318,7 +327,8 @@ class AccountReconcileModel(models.Model):
def _get_invoice_matching_st_line_tokens(self, st_line): def _get_invoice_matching_st_line_tokens(self, st_line):
"""Parse the textual information from the statement line passed as parameter """Parse the textual information from the statement line passed as parameter
in order to extract from it the meaningful information in order to perform the matching. in order to extract from it the meaningful information in order to perform the
matching.
:param st_line: A statement line. :param st_line: A statement line.
:return: A list of tokens, each one being a string. :return: A list of tokens, each one being a string.
""" """
@ -347,7 +357,8 @@ class AccountReconcileModel(models.Model):
return tokens return tokens
def _get_invoice_matching_amls_candidates(self, st_line, partner): def _get_invoice_matching_amls_candidates(self, st_line, partner):
"""Returns the match candidates for the 'invoice_matching' rule, with respect to the provided parameters. """Returns the match candidates for the 'invoice_matching' rule, with respect to
the provided parameters.
: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.
""" """
@ -381,14 +392,17 @@ class AccountReconcileModel(models.Model):
UNNEST( UNNEST(
REGEXP_SPLIT_TO_ARRAY( REGEXP_SPLIT_TO_ARRAY(
SUBSTRING( SUBSTRING(
REGEXP_REPLACE({table_alias}.{field}, '[^0-9\s]', '', 'g'), REGEXP_REPLACE(
{table_alias}.{field}, '[^0-9\s]', '', 'g'
),
'\S(?:.*\S)*' '\S(?:.*\S)*'
), ),
'\s+' '\s+'
) )
) AS token ) AS token
FROM {tables} FROM {tables}
JOIN account_move account_move_line__move_id ON account_move_line__move_id.id = account_move_line.move_id JOIN account_move account_move_line__move_id
ON account_move_line__move_id.id = account_move_line.move_id
WHERE {where_clause} AND {table_alias}.{field} IS NOT NULL WHERE {where_clause} AND {table_alias}.{field} IS NOT NULL
""" """
) )
@ -432,11 +446,14 @@ class AccountReconcileModel(models.Model):
} }
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 modules. """Get a mapping <priority_order, rule> that could be overridden in others
modules.
:return: a mapping <priority_order, rule> where: :return: a mapping <priority_order, rule> where:
* priority_order: Defines in which order the rules will be evaluated, the lowest comes first. * priority_order: Defines in which order the rules will be evaluated, the
This is extremely important since the algorithm stops when a rule returns some candidates. lowest comes first. This is extremely important since the algorithm stops
* rule: Method taking <st_line, partner> as parameters and returning the candidates journal items found. when a rule returns some candidates.
* rule: Method taking <st_line, partner> as parameters and returning the
candidates journal items found.
""" """
rules_map = defaultdict(list) rules_map = defaultdict(list)
rules_map[10].append(self._get_invoice_matching_amls_candidates) rules_map[10].append(self._get_invoice_matching_amls_candidates)
@ -563,9 +580,11 @@ class AccountReconcileModel(models.Model):
) )
> 0 > 0
): ):
# Here, we still have room for other candidates ; so we add the current one to the list we keep. # Here, we still have room for other candidates ; so we add the
# Then, we continue iterating, even if there is no room anymore, just in case one of the following candidates # current one to the list we keep. Then, we continue iterating, even
# is an exact match, which would then be preferred on the current candidates. # if there is no room anymore, just in case one of the following
# candidates is an exact match, which would then be preferred on the
# current candidates.
kepts_amls_values_list.append(aml_values) kepts_amls_values_list.append(aml_values)
sum_amount_residual_currency += aml_values[ sum_amount_residual_currency += aml_values[
"amount_residual_currency" "amount_residual_currency"
@ -580,7 +599,8 @@ class AccountReconcileModel(models.Model):
else: else:
return None, [] return None, []
# Try to match a batch with the early payment feature. Only a perfect match is allowed. # Try to match a batch with the early payment feature. Only a perfect match is
# allowed.
match_type, kepts_amls_values_list = match_batch_amls(amls_with_epd_values_list) match_type, kepts_amls_values_list = match_batch_amls(amls_with_epd_values_list)
if match_type != "perfect": if match_type != "perfect":
kepts_amls_values_list = [] kepts_amls_values_list = []
@ -607,11 +627,14 @@ class AccountReconcileModel(models.Model):
:param amls_values_list: The candidates account.move.line as a list of dict: :param amls_values_list: The candidates account.move.line as a list of dict:
* aml: The record. * aml: The record.
* amount_residual: The amount residual to consider. * amount_residual: The amount residual to consider.
* amount_residual_currency: The amount residual in foreign currency to consider. * amount_residual_currency: The amount residual in foreign currency to
consider.
:return: A string representing what to do with the candidates: :return: A string representing what to do with the candidates:
* rejected: Reject candidates. * rejected: Reject candidates.
* allow_write_off: Allow to generate the write-off from the reconcile model lines if specified. * allow_write_off: Allow to generate the write-off from the reconcile model
* allow_auto_reconcile: Allow to automatically reconcile entries if 'auto_validate' is enabled. lines if specified.
* allow_auto_reconcile: Allow to automatically reconcile entries if
'auto_validate' is enabled.
""" """
self.ensure_one() self.ensure_one()
@ -637,8 +660,8 @@ class AccountReconcileModel(models.Model):
if st_line_currency.is_zero(amount_curr_after_rec): if st_line_currency.is_zero(amount_curr_after_rec):
return {"allow_auto_reconcile"} return {"allow_auto_reconcile"}
# The payment amount is higher than the sum of invoices. # The payment amount is higher than the sum of invoices. In that case, don't
# In that case, don't check the tolerance and don't try to generate any write-off. # check the tolerance and don't try to generate any write-off.
if amount_curr_after_rec > 0.0: if amount_curr_after_rec > 0.0:
return {"allow_auto_reconcile"} return {"allow_auto_reconcile"}
@ -646,8 +669,8 @@ class AccountReconcileModel(models.Model):
if self.payment_tolerance_param == 0: if self.payment_tolerance_param == 0:
return {"rejected"} return {"rejected"}
# If the tolerance is expressed as a fixed amount, check the residual payment amount doesn't exceed the # If the tolerance is expressed as a fixed amount, check the residual payment
# tolerance. # amount doesn't exceed the tolerance.
if ( if (
self.payment_tolerance_type == "fixed_amount" self.payment_tolerance_type == "fixed_amount"
and -amount_curr_after_rec <= self.payment_tolerance_param and -amount_curr_after_rec <= self.payment_tolerance_param

View File

@ -9,8 +9,8 @@ class TestAccountReconciliationCommon(AccountTestInvoicingCommon):
"""Tests for reconciliation (account.tax) """Tests for reconciliation (account.tax)
Test used to check that when doing a sale or purchase invoice in a different currency, Test used to check that when doing a sale or purchase invoice in a different
the result will be balanced. currency, the result will be balanced.
""" """
@classmethod @classmethod

View File

@ -402,7 +402,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
}, },
) )
# Test matching with the partner name (reinitializing the statement line first) # Test matching with the partner name (resetting the statement line first)
self.bank_line_1.write( self.bank_line_1.write(
{**st_line_initial_vals, st_line_field: self.partner_1.name} {**st_line_initial_vals, st_line_field: self.partner_1.name}
) )
@ -920,7 +920,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
) )
def test_auto_reconcile_with_tax(self): def test_auto_reconcile_with_tax(self):
"""Test auto reconciliation with a tax amount included in the bank statement line""" """Test auto reconciliation with a tax amount included in the bank stat. line"""
self.rule_1.write( self.rule_1.write(
{ {
"auto_reconcile": True, "auto_reconcile": True,
@ -968,7 +968,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
) )
def test_auto_reconcile_with_tax_fpos(self): def test_auto_reconcile_with_tax_fpos(self):
"""Test the fiscal positions are applied by reconcile models when using taxes.""" """Test the fiscal positions are applied by reconcile models when using taxes"""
self.rule_1.write( self.rule_1.write(
{ {
"auto_reconcile": True, "auto_reconcile": True,
@ -1031,6 +1031,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
def test_reverted_move_matching(self): def test_reverted_move_matching(self):
partner = self.partner_1 partner = self.partner_1
AccountMove = self.env["account.move"] AccountMove = self.env["account.move"]
account = self.bank_journal.company_id.account_journal_payment_credit_account_id
move = AccountMove.create( move = AccountMove.create(
{ {
"journal_id": self.bank_journal.id, "journal_id": self.bank_journal.id,
@ -1049,7 +1050,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
0, 0,
0, 0,
{ {
"account_id": self.bank_journal.company_id.account_journal_payment_credit_account_id.id, "account_id": account.id,
"partner_id": partner.id, "partner_id": partner.id,
"name": "I'm gonna cut you into little pieces", "name": "I'm gonna cut you into little pieces",
"credit": 10, "credit": 10,
@ -1140,7 +1141,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
"line_ids": [(5, 0, 0)], "line_ids": [(5, 0, 0)],
"match_partner": False, "match_partner": False,
"match_label": "contains", "match_label": "contains",
"match_label_param": "Tournicoti", # So that we only match what we want to test "match_label_param": "Tournicoti", # match what we want to test
} }
) )
@ -1165,7 +1166,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
{ {
"match_partner": False, "match_partner": False,
"match_label": "contains", "match_label": "contains",
"match_label_param": "doudlidou", # So that we only match what we want to test "match_label_param": "doudlidou", # match what we want to test
"payment_tolerance_param": 10.0, "payment_tolerance_param": 10.0,
"auto_reconcile": True, "auto_reconcile": True,
} }
@ -1219,7 +1220,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
self.bank_line_2.write({"partner_id": None}) self.bank_line_2.write({"partner_id": None})
self.rule_1.write({"match_partner": False}) self.rule_1.write({"match_partner": False})
# bank_line_1 should match, as its communication contains the invoice's partner name # bank_line_1 should match, as its communic. contains the invoice's partner name
self._check_statement_matching( self._check_statement_matching(
self.rule_1, self.rule_1,
{ {
@ -1246,13 +1247,13 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
) )
def test_match_multi_currencies(self): def test_match_multi_currencies(self):
"""Ensure the matching of candidates is made using the right statement line currency. """Ensure the matching of candidates is made using the right statement line
In this test, the value of the statement line is 100 USD = 300 GOL = 900 DAR and we want to match two journal currency. In this test, the value of the statement line is 100 USD = 300
items of: GOL = 900 DAR and we want to match two journal items of:
- 100 USD = 200 GOL (= 600 DAR from the statement line point of view) - 100 USD = 200 GOL (= 600 DAR from the statement line point of view)
- 14 USD = 280 DAR - 14 USD = 280 DAR
Both journal items should be suggested to the user because they represents 98% of the statement line amount Both journal items should be suggested to the user because they represents 98%
(DAR). of the statement line amount (DAR).
""" """
partner = self.env["res.partner"].create({"name": "Bernard Perdant"}) partner = self.env["res.partner"].create({"name": "Bernard Perdant"})
@ -1280,9 +1281,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
} }
) )
statement_line = self.env[ statement_line = self.env["account.bank.statement.line"].create(
"account.bank.statement.line"
].create(
{ {
"journal_id": journal.id, "journal_id": journal.id,
"date": "2016-01-01", "date": "2016-01-01",
@ -1290,7 +1289,8 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
"partner_id": partner.id, "partner_id": partner.id,
"foreign_currency_id": self.currency_data_2["currency"].id, "foreign_currency_id": self.currency_data_2["currency"].id,
"amount": 300.0, # Rate is 3 GOL = 1 USD in 2016. "amount": 300.0, # Rate is 3 GOL = 1 USD in 2016.
"amount_currency": 900.0, # Rate is 10 DAR = 1 USD in 2016 but the rate used by the bank is 9:1. # Rate is 10 DAR = 1 USD in 2016 but the rate used by the bank is 9:1.
"amount_currency": 900.0,
} }
) )
@ -1447,10 +1447,11 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
) )
def test_no_amount_check_keep_first(self): def test_no_amount_check_keep_first(self):
"""In case the reconciliation model doesn't check the total amount of the candidates, """In case the reconciliation model doesn't check the total amount of the
we still don't want to suggest more than are necessary to match the statement. candidates, we still don't want to suggest more than are necessary to match the
For example, if a statement line amounts to 250 and is to be matched with three invoices statement. For example, if a statement line amounts to 250 and is to be matched
of 100, 200 and 300 (retrieved in this order), only 100 and 200 should be proposed. with three invoices of 100, 200 and 300 (retrieved in this order), only 100 and
200 should be proposed.
""" """
self.rule_1.allow_payment_tolerance = False self.rule_1.allow_payment_tolerance = False
self.bank_line_2.amount = 250 self.bank_line_2.amount = 250

View File

@ -122,7 +122,6 @@ class AccountBankStatementLine(models.Model):
or record.company_id.reconcile_aggregate or record.company_id.reconcile_aggregate
) )
record.reconcile_aggregate = reconcile_aggregate record.reconcile_aggregate = reconcile_aggregate
print(record.date, reconcile_aggregate_map[reconcile_aggregate](record))
record.aggregate_id, record.aggregate_name = reconcile_aggregate_map[ record.aggregate_id, record.aggregate_name = reconcile_aggregate_map[
reconcile_aggregate reconcile_aggregate
](record) ](record)
@ -825,9 +824,10 @@ class AccountBankStatementLine(models.Model):
], ],
limit=1, limit=1,
) )
balance = previous_line_with_statement.statement_id.balance_end_real
action["context"] = { action["context"] = {
"default_journal_id": self.journal_id.id, "default_journal_id": self.journal_id.id,
"default_balance_start": previous_line_with_statement.statement_id.balance_end_real, "default_balance_start": balance,
"split_line_id": self.id, "split_line_id": self.id,
} }
return action return action

View File

@ -711,8 +711,8 @@ class TestReconciliationWidget(TestAccountReconciliationCommon):
def test_widget_invoice_unselect(self): def test_widget_invoice_unselect(self):
""" """
We want to test how selection and unselection of an account move lines is managed We want to test how selection and unselection of an account move lines is
by the system. managed by the system.
""" """
inv1 = self.create_invoice( inv1 = self.create_invoice(
currency_id=self.currency_euro_id, invoice_amount=100 currency_id=self.currency_euro_id, invoice_amount=100
@ -786,7 +786,7 @@ class TestReconciliationWidget(TestAccountReconciliationCommon):
f.manual_partner_id = inv1.partner_id f.manual_partner_id = inv1.partner_id
self.assertEqual(f.partner_id, inv1.partner_id) self.assertEqual(f.partner_id, inv1.partner_id)
bank_stmt_line.clean_reconcile() bank_stmt_line.clean_reconcile()
# As we have a set a partner, the cleaning should assign the invoice automatically # As we have set a partner, the cleaning should assign the invoice automatically
self.assertTrue(bank_stmt_line.can_reconcile) self.assertTrue(bank_stmt_line.can_reconcile)
def test_widget_model_clean(self): def test_widget_model_clean(self):