diff --git a/account_mass_reconcile/models/base_advanced_reconciliation.py b/account_mass_reconcile/models/base_advanced_reconciliation.py index 19bbe093..a5c49332 100644 --- a/account_mass_reconcile/models/base_advanced_reconciliation.py +++ b/account_mass_reconcile/models/base_advanced_reconciliation.py @@ -155,8 +155,8 @@ class MassReconcileAdvanced(models.AbstractModel): mkey, mvalue = matcher omkey, omvalue = opposite_matcher assert mkey == omkey, _( - "A matcher %(mkey)s is compared with a matcher %(omkey)s, the _matchers and " - "_opposite_matchers are probably wrong" + "A matcher %(mkey)s is compared with a matcher %(omkey)s, the _matchers " + "and _opposite_matchers are probably wrong" ) % {"mkey": mkey, "omkey": omkey} if not isinstance(mvalue, list | tuple): mvalue = (mvalue,) diff --git a/account_reconcile_model_oca/models/account_bank_statement_line.py b/account_reconcile_model_oca/models/account_bank_statement_line.py index 234f76bf..652a3aa6 100644 --- a/account_reconcile_model_oca/models/account_bank_statement_line.py +++ b/account_reconcile_model_oca/models/account_bank_statement_line.py @@ -63,12 +63,14 @@ class AccountBankStatementLine(models.Model): continue # 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( rf""" {unaccent("%s")} ~* ('^' || ( 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"] 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. :return: A list of strings. """ diff --git a/account_reconcile_model_oca/models/account_reconcile_model.py b/account_reconcile_model_oca/models/account_reconcile_model.py index 2ad17fa4..58e6a3c2 100644 --- a/account_reconcile_model_oca/models/account_reconcile_model.py +++ b/account_reconcile_model_oca/models/account_reconcile_model.py @@ -14,13 +14,14 @@ class AccountReconcileModel(models.Model): #################################################### 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. - :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 st_line: The statement line processed by the bank reconciliation widget. - :return: A list of python dictionaries (one per reconcile model line) representing - the journal items to be created by the current reconcile model. + """Apply the reconciliation model lines to the statement line passed as + parameter. + :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 st_line: The statement line processed by the bank reconciliation widget. + :return: A list of python dictionaries (one per reconcile model line) + representing the journal items to be created by the current reconcile model. """ self.ensure_one() currency = ( @@ -48,9 +49,11 @@ class AccountReconcileModel(models.Model): 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. - :param tax: An account.tax record. - :param base_line_dict: A dict representing the move.line containing the base amount. - :return: A list of dict representing move.lines to be created corresponding to the tax. + :param tax: An account.tax record. + :param base_line_dict: A dict representing the move.line containing the base + amount. + :return: A list of dict representing move.lines to be created corresponding to + the tax. """ self.ensure_one() balance = base_line_dict["balance"] @@ -103,9 +106,12 @@ class AccountReconcileModel(models.Model): return new_aml_dicts 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. - :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. + """Get move.lines dict corresponding to the reconciliation model's 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() @@ -153,8 +159,9 @@ class AccountReconcileModel(models.Model): if detected_fiscal_position: taxes = detected_fiscal_position.map_tax(taxes) writeoff_line["tax_ids"] += [Command.set(taxes.ids)] - # Multiple taxes with force_tax_included results in wrong computation, so we - # only allow to set the force_tax_included field if we have one tax selected + # Multiple taxes with force_tax_included results in wrong computation, + # so we only allow to set the force_tax_included field if we have one + # tax selected if line.force_tax_included: taxes = taxes[0].with_context(force_price_include=True) 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): """Apply criteria to get candidates for all reconciliation models. 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 partner: The partner to consider. - :return: A dict mapping each statement line id with: - * aml_ids: A list of account.move.line ids. - * model: An account.reconcile.model record (optional). - * status: 'reconciled' if the lines has been already reconciled, 'write_off' 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. + :return: A dict mapping each statement line id with: + * aml_ids: A list of account.move.line ids. + * model: An account.reconcile.model record (optional). + * status: 'reconciled' if the lines has been already reconciled, 'write_off' + 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. """ available_models = self.filtered( 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): """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. :return: A list of tokens, each one being a string. """ @@ -347,7 +357,8 @@ class AccountReconcileModel(models.Model): return tokens 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 partner: The partner associated to the statement line. """ @@ -381,14 +392,17 @@ class AccountReconcileModel(models.Model): UNNEST( REGEXP_SPLIT_TO_ARRAY( SUBSTRING( - REGEXP_REPLACE({table_alias}.{field}, '[^0-9\s]', '', 'g'), + REGEXP_REPLACE( + {table_alias}.{field}, '[^0-9\s]', '', 'g' + ), '\S(?:.*\S)*' ), '\s+' ) ) AS token 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 """ ) @@ -432,11 +446,14 @@ class AccountReconcileModel(models.Model): } def _get_invoice_matching_rules_map(self): - """Get a mapping that could be overridden in others modules. + """Get a mapping that could be overridden in others + modules. :return: a mapping where: - * priority_order: Defines in which order the rules will be evaluated, the lowest comes first. - This is extremely important since the algorithm stops when a rule returns some candidates. - * rule: Method taking as parameters and returning the candidates journal items found. + * priority_order: Defines in which order the rules will be evaluated, the + lowest comes first. This is extremely important since the algorithm stops + when a rule returns some candidates. + * rule: Method taking as parameters and returning the + candidates journal items found. """ rules_map = defaultdict(list) rules_map[10].append(self._get_invoice_matching_amls_candidates) @@ -563,9 +580,11 @@ class AccountReconcileModel(models.Model): ) > 0 ): - # Here, we still have room for other candidates ; so we add the current one to the list we keep. - # Then, we continue iterating, even 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. + # Here, we still have room for other candidates ; so we add the + # current one to the list we keep. Then, we continue iterating, even + # 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) sum_amount_residual_currency += aml_values[ "amount_residual_currency" @@ -580,7 +599,8 @@ class AccountReconcileModel(models.Model): else: 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) if match_type != "perfect": kepts_amls_values_list = [] @@ -603,15 +623,18 @@ class AccountReconcileModel(models.Model): def _check_rule_propositions(self, st_line, amls_values_list): """Check restrictions that can't be handled for each move.line separately. Note: Only used by models having a type equals to 'invoice_matching'. - :param st_line: The statement line. - :param amls_values_list: The candidates account.move.line as a list of dict: - * aml: The record. - * amount_residual: The amount residual to consider. - * amount_residual_currency: The amount residual in foreign currency to consider. + :param st_line: The statement line. + :param amls_values_list: The candidates account.move.line as a list of dict: + * aml: The record. + * amount_residual: The amount residual to consider. + * amount_residual_currency: The amount residual in foreign currency to + consider. :return: A string representing what to do with the candidates: - * rejected: Reject candidates. - * allow_write_off: Allow to generate the write-off from the reconcile model lines if specified. - * allow_auto_reconcile: Allow to automatically reconcile entries if 'auto_validate' is enabled. + * rejected: Reject candidates. + * allow_write_off: Allow to generate the write-off from the reconcile model + lines if specified. + * allow_auto_reconcile: Allow to automatically reconcile entries if + 'auto_validate' is enabled. """ self.ensure_one() @@ -637,8 +660,8 @@ class AccountReconcileModel(models.Model): if st_line_currency.is_zero(amount_curr_after_rec): return {"allow_auto_reconcile"} - # The payment amount is higher than the sum of invoices. - # In that case, don't check the tolerance and don't try to generate any write-off. + # The payment amount is higher than the sum of invoices. In that case, don't + # check the tolerance and don't try to generate any write-off. if amount_curr_after_rec > 0.0: return {"allow_auto_reconcile"} @@ -646,8 +669,8 @@ class AccountReconcileModel(models.Model): if self.payment_tolerance_param == 0: return {"rejected"} - # If the tolerance is expressed as a fixed amount, check the residual payment amount doesn't exceed the - # tolerance. + # If the tolerance is expressed as a fixed amount, check the residual payment + # amount doesn't exceed the tolerance. if ( self.payment_tolerance_type == "fixed_amount" and -amount_curr_after_rec <= self.payment_tolerance_param diff --git a/account_reconcile_model_oca/tests/common.py b/account_reconcile_model_oca/tests/common.py index 7b9a804c..7ddac2fb 100644 --- a/account_reconcile_model_oca/tests/common.py +++ b/account_reconcile_model_oca/tests/common.py @@ -9,8 +9,8 @@ class TestAccountReconciliationCommon(AccountTestInvoicingCommon): """Tests for reconciliation (account.tax) - Test used to check that when doing a sale or purchase invoice in a different currency, - the result will be balanced. + Test used to check that when doing a sale or purchase invoice in a different + currency, the result will be balanced. """ @classmethod diff --git a/account_reconcile_model_oca/tests/test_reconciliation_match.py b/account_reconcile_model_oca/tests/test_reconciliation_match.py index 26704d15..9c99f101 100644 --- a/account_reconcile_model_oca/tests/test_reconciliation_match.py +++ b/account_reconcile_model_oca/tests/test_reconciliation_match.py @@ -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( {**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): - """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( { "auto_reconcile": True, @@ -968,7 +968,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): ) 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( { "auto_reconcile": True, @@ -1031,6 +1031,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): def test_reverted_move_matching(self): partner = self.partner_1 AccountMove = self.env["account.move"] + account = self.bank_journal.company_id.account_journal_payment_credit_account_id move = AccountMove.create( { "journal_id": self.bank_journal.id, @@ -1049,7 +1050,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): 0, 0, { - "account_id": self.bank_journal.company_id.account_journal_payment_credit_account_id.id, + "account_id": account.id, "partner_id": partner.id, "name": "I'm gonna cut you into little pieces", "credit": 10, @@ -1140,7 +1141,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): "line_ids": [(5, 0, 0)], "match_partner": False, "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_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, "auto_reconcile": True, } @@ -1219,7 +1220,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): self.bank_line_2.write({"partner_id": None}) 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.rule_1, { @@ -1246,13 +1247,13 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): ) def test_match_multi_currencies(self): - """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 GOL = 900 DAR and we want to match two journal - items of: + """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 + 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) - 14 USD = 280 DAR - Both journal items should be suggested to the user because they represents 98% of the statement line amount - (DAR). + Both journal items should be suggested to the user because they represents 98% + of the statement line amount (DAR). """ partner = self.env["res.partner"].create({"name": "Bernard Perdant"}) @@ -1280,9 +1281,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): } ) - statement_line = self.env[ - "account.bank.statement.line" - ].create( + statement_line = self.env["account.bank.statement.line"].create( { "journal_id": journal.id, "date": "2016-01-01", @@ -1290,7 +1289,8 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): "partner_id": partner.id, "foreign_currency_id": self.currency_data_2["currency"].id, "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): - """In case the reconciliation model doesn't check the total amount of the candidates, - we still don't want to suggest more than are necessary to match the statement. - For example, if a statement line amounts to 250 and is to be matched with three invoices - of 100, 200 and 300 (retrieved in this order), only 100 and 200 should be proposed. + """In case the reconciliation model doesn't check the total amount of the + candidates, we still don't want to suggest more than are necessary to match the + statement. For example, if a statement line amounts to 250 and is to be matched + 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.bank_line_2.amount = 250 diff --git a/account_reconcile_oca/models/account_bank_statement_line.py b/account_reconcile_oca/models/account_bank_statement_line.py index d51f2bd7..98b3e558 100644 --- a/account_reconcile_oca/models/account_bank_statement_line.py +++ b/account_reconcile_oca/models/account_bank_statement_line.py @@ -122,7 +122,6 @@ class AccountBankStatementLine(models.Model): or record.company_id.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[ reconcile_aggregate ](record) @@ -825,9 +824,10 @@ class AccountBankStatementLine(models.Model): ], limit=1, ) + balance = previous_line_with_statement.statement_id.balance_end_real action["context"] = { "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, } return action diff --git a/account_reconcile_oca/tests/test_bank_account_reconcile.py b/account_reconcile_oca/tests/test_bank_account_reconcile.py index a94ba68b..bf8bdc24 100644 --- a/account_reconcile_oca/tests/test_bank_account_reconcile.py +++ b/account_reconcile_oca/tests/test_bank_account_reconcile.py @@ -711,8 +711,8 @@ class TestReconciliationWidget(TestAccountReconciliationCommon): def test_widget_invoice_unselect(self): """ - We want to test how selection and unselection of an account move lines is managed - by the system. + We want to test how selection and unselection of an account move lines is + managed by the system. """ inv1 = self.create_invoice( currency_id=self.currency_euro_id, invoice_amount=100 @@ -786,7 +786,7 @@ class TestReconciliationWidget(TestAccountReconciliationCommon): f.manual_partner_id = inv1.partner_id self.assertEqual(f.partner_id, inv1.partner_id) 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) def test_widget_model_clean(self):