Merge branch 'OCA:17.0' into 17.0

pull/800/head
Antonio A. 2025-04-10 21:47:40 +02:00 committed by GitHub
commit 36cefd3ee6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 518 additions and 255 deletions

View File

@ -23,8 +23,8 @@ addon | version | maintainers | summary
--- | --- | --- | --- --- | --- | --- | ---
[account_in_payment](account_in_payment/) | 17.0.1.0.0 | | This module enables in-payment mode for your accounting [account_in_payment](account_in_payment/) | 17.0.1.0.0 | | This module enables in-payment mode for your accounting
[account_mass_reconcile](account_mass_reconcile/) | 17.0.1.0.1 | | Account Mass Reconcile [account_mass_reconcile](account_mass_reconcile/) | 17.0.1.0.1 | | Account Mass Reconcile
[account_reconcile_model_oca](account_reconcile_model_oca/) | 17.0.1.0.2 | | This includes the logic moved from Odoo Community to Odoo Enterprise [account_reconcile_model_oca](account_reconcile_model_oca/) | 17.0.1.0.4 | | This includes the logic moved from Odoo Community to Odoo Enterprise
[account_reconcile_oca](account_reconcile_oca/) | 17.0.1.5.5 | [![etobella](https://github.com/etobella.png?size=30px)](https://github.com/etobella) | Reconcile addons for Odoo CE accounting [account_reconcile_oca](account_reconcile_oca/) | 17.0.1.5.8 | [![etobella](https://github.com/etobella.png?size=30px)](https://github.com/etobella) | Reconcile addons for Odoo CE accounting
[account_statement_base](account_statement_base/) | 17.0.1.5.0 | [![alexis-via](https://github.com/alexis-via.png?size=30px)](https://github.com/alexis-via) | Base module for Bank Statements [account_statement_base](account_statement_base/) | 17.0.1.5.0 | [![alexis-via](https://github.com/alexis-via.png?size=30px)](https://github.com/alexis-via) | Base module for Bank Statements
[//]: # (end addons) [//]: # (end addons)

View File

@ -7,7 +7,7 @@ Account Reconcile Model Oca
!! This file is generated by oca-gen-addon-readme !! !! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !! !! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:80bb08dc3058116c364563a7014c16787db3ac0b12afadbde03716f7277fa298 !! source digest: sha256:f0554ce70e9ac90badf0a4082aecd5af7011cf1461fe0cc6678577d2b7f87e21
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png

View File

@ -5,11 +5,12 @@
"name": "Account Reconcile Model Oca", "name": "Account Reconcile Model Oca",
"summary": """ "summary": """
This includes the logic moved from Odoo Community to Odoo Enterprise""", This includes the logic moved from Odoo Community to Odoo Enterprise""",
"version": "17.0.1.0.2", "version": "17.0.1.0.4",
"license": "LGPL-3", "license": "LGPL-3",
"author": "Dixmit,Odoo,Odoo Community Association (OCA)", "author": "Dixmit,Odoo,Odoo Community Association (OCA)",
"website": "https://github.com/OCA/account-reconcile", "website": "https://github.com/OCA/account-reconcile",
"depends": ["account"], "depends": ["account"],
"excludes": ["account_accountant"],
"data": [], "data": [],
"demo": [], "demo": [],
} }

View File

@ -109,23 +109,15 @@ class AccountBankStatementLine(models.Model):
""" """
self.ensure_one() self.ensure_one()
def _get_text_value(field_name):
if self._fields[field_name].type == "html":
return self[field_name] and html2plaintext(self[field_name])
else:
return self[field_name]
st_line_text_values = [] st_line_text_values = []
if allowed_fields is None or "payment_ref" in allowed_fields: if not allowed_fields or "payment_ref" in allowed_fields:
value = _get_text_value("payment_ref") if self.payment_ref:
if value: st_line_text_values.append(self.payment_ref)
st_line_text_values.append(value) if not allowed_fields or "narration" in allowed_fields:
if allowed_fields is None or "narration" in allowed_fields: value = html2plaintext(self.narration or "")
value = _get_text_value("narration")
if value:
st_line_text_values.append(value)
if allowed_fields is None or "ref" in allowed_fields:
value = _get_text_value("ref")
if value: if value:
st_line_text_values.append(value) st_line_text_values.append(value)
if not allowed_fields or "ref" in allowed_fields:
if self.ref:
st_line_text_values.append(self.ref)
return st_line_text_values return st_line_text_values

View File

@ -131,6 +131,8 @@ class AccountReconcileModel(models.Model):
balance = currency.round( balance = currency.round(
line.amount * (1 if residual_balance > 0.0 else -1) line.amount * (1 if residual_balance > 0.0 else -1)
) )
else:
balance = 0.0
if currency.is_zero(balance): if currency.is_zero(balance):
continue continue
@ -260,7 +262,7 @@ class AccountReconcileModel(models.Model):
or ( or (
self.match_partner self.match_partner
and self.match_partner_category_ids and self.match_partner_category_ids
and partner.category_id not in self.match_partner_category_ids and not (partner.category_id & self.match_partner_category_ids)
) )
): ):
return False return False
@ -316,36 +318,63 @@ class AccountReconcileModel(models.Model):
return aml_domain return aml_domain
def _get_st_line_text_values_for_matching(self, st_line):
"""Collect the strings that could be used on the statement line to perform
some matching.
:param st_line: The current statement line.
:return: A list of strings.
"""
self.ensure_one()
allowed_fields = []
if self.match_text_location_label:
allowed_fields.append("payment_ref")
if self.match_text_location_note:
allowed_fields.append("narration")
if self.match_text_location_reference:
allowed_fields.append("ref")
return st_line._get_st_line_strings_for_matching(allowed_fields=allowed_fields)
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 in order to extract from it the meaningful information in order to perform the
matching. 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 tuple of list of tokens, each one being a string.
The first element is a list of tokens you may match on
numerical information.
The second element is a list of tokens you may match exactly.
""" """
st_line_text_values = st_line._get_st_line_strings_for_matching( st_line_text_values = self._get_st_line_text_values_for_matching(st_line)
allowed_fields=(
"payment_ref" if self.match_text_location_label else None,
"narration" if self.match_text_location_note else None,
"ref" if self.match_text_location_reference else None,
)
)
significant_token_size = 4 significant_token_size = 4
tokens = [] numerical_tokens = []
exact_tokens = []
text_tokens = []
for text_value in st_line_text_values: for text_value in st_line_text_values:
for token in (text_value or "").split(): tokens = [
"".join(x for x in token if re.match(r"[0-9a-zA-Z\s]", x))
for token in (text_value or "").split()
]
# Numerical tokens
for token in tokens:
# The token is too short to be significant. # The token is too short to be significant.
if len(token) < significant_token_size: if len(token) < significant_token_size:
continue continue
text_tokens.append(token)
formatted_token = "".join(x for x in token if x.isdecimal()) formatted_token = "".join(x for x in token if x.isdecimal())
# The token is too short after formatting to be significant. # The token is too short after formatting to be significant.
if len(formatted_token) < significant_token_size: if len(formatted_token) < significant_token_size:
continue continue
tokens.append(formatted_token) numerical_tokens.append(formatted_token)
return tokens
# Exact tokens.
if len(tokens) == 1:
exact_tokens.append(text_value)
return numerical_tokens, exact_tokens, text_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 """Returns the match candidates for the 'invoice_matching' rule, with respect to
@ -353,53 +382,97 @@ 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)
tables, where_clause, where_params = query.get_sql() tables, where_clause, where_params = query.get_sql()
tokens = self._get_invoice_matching_st_line_tokens(st_line) sub_queries = []
if tokens: all_params = []
sub_queries = [] aml_cte = ""
for table_alias, field in ( (
("account_move_line", "name"), numerical_tokens,
("account_move_line__move_id", "name"), exact_tokens,
("account_move_line__move_id", "ref"), _text_tokens,
): ) = self._get_invoice_matching_st_line_tokens(st_line)
if numerical_tokens or exact_tokens:
aml_cte = rf"""
WITH aml_cte AS (
SELECT
account_move_line.id as account_move_line_id,
account_move_line.date as account_move_line_date,
account_move_line.date_maturity as account_move_line_date_maturity,
account_move_line.name as account_move_line_name,
account_move_line__move_id.name as account_move_line__move_id_name,
account_move_line__move_id.ref as account_move_line__move_id_ref
FROM {tables}
JOIN account_move account_move_line__move_id
ON account_move_line__move_id.id = account_move_line.move_id
WHERE {where_clause}
)
""" # noqa: E501
all_params += where_params
enabled_matches = []
if self.match_text_location_label:
enabled_matches.append(("account_move_line", "name"))
if self.match_text_location_note:
enabled_matches.append(("account_move_line__move_id", "name"))
if self.match_text_location_reference:
enabled_matches.append(("account_move_line__move_id", "ref"))
if numerical_tokens:
for table_alias, field in enabled_matches:
sub_queries.append( sub_queries.append(
rf""" rf"""
SELECT SELECT
account_move_line.id, account_move_line_id as id,
account_move_line.date, account_move_line_date as date,
account_move_line.date_maturity, account_move_line_date_maturity as date_maturity,
UNNEST( UNNEST(
REGEXP_SPLIT_TO_ARRAY( REGEXP_SPLIT_TO_ARRAY(
SUBSTRING( SUBSTRING(
REGEXP_REPLACE( REGEXP_REPLACE(
{table_alias}.{field}, '[^0-9\s]', '', 'g' {table_alias}_{field}, '[^0-9\s]', '', 'g'
), ),
'\S(?:.*\S)*' '\S(?:.*\S)*'
), ),
'\s+' '\s+'
) )
) AS token ) AS token
FROM {tables} FROM aml_cte
JOIN account_move account_move_line__move_id WHERE {table_alias}_{field} IS NOT NULL
ON account_move_line__move_id.id = account_move_line.move_id
WHERE {where_clause} AND {table_alias}.{field} IS NOT NULL
""" """
) )
self._cr.execute( if exact_tokens:
for table_alias, field in enabled_matches:
sub_queries.append(
rf"""
SELECT
account_move_line_id as id,
account_move_line_date as date,
account_move_line_date_maturity as date_maturity,
{table_alias}_{field} AS token
FROM aml_cte
WHERE COALESCE({table_alias}_{field}, '') != ''
""" """
)
if sub_queries:
order_by = get_order_by_clause(alias="sub")
self._cr.execute(
aml_cte
+ """
SELECT SELECT
sub.id, sub.id,
COUNT(*) AS nb_match COUNT(*) AS nb_match
@ -413,7 +486,7 @@ class AccountReconcileModel(models.Model):
+ order_by + order_by
+ """ + """
""", """,
(where_params * 3) + [tuple(tokens)], all_params + [tuple(numerical_tokens + exact_tokens)],
) )
candidate_ids = [r[0] for r in self._cr.fetchall()] candidate_ids = [r[0] for r in self._cr.fetchall()]
if candidate_ids: if candidate_ids:
@ -421,20 +494,58 @@ class AccountReconcileModel(models.Model):
"allow_auto_reconcile": True, "allow_auto_reconcile": True,
"amls": self.env["account.move.line"].browse(candidate_ids), "amls": self.env["account.move.line"].browse(candidate_ids),
} }
elif (
self.match_text_location_label
or self.match_text_location_note
or self.match_text_location_reference
):
# In the case any of the Label, Note or Reference matching rule has been
# toggled, and the query didn't return
# any candidates, the model should not try to mount another aml instead.
return
# 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 {tables}
} 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
@ -645,7 +756,9 @@ class AccountReconcileModel(models.Model):
for aml_values in amls_values_list for aml_values in amls_values_list
) )
sign = 1 if st_line_amount_curr > 0.0 else -1 sign = 1 if st_line_amount_curr > 0.0 else -1
amount_curr_after_rec = sign * (amls_amount_curr + st_line_amount_curr) amount_curr_after_rec = st_line_currency.round(
sign * (amls_amount_curr + st_line_amount_curr)
)
# The statement line will be fully reconciled. # The statement line will be fully reconciled.
if st_line_currency.is_zero(amount_curr_after_rec): if st_line_currency.is_zero(amount_curr_after_rec):
@ -664,7 +777,10 @@ class AccountReconcileModel(models.Model):
# amount doesn't exceed the 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 st_line_currency.compare_amounts(
-amount_curr_after_rec, self.payment_tolerance_param
)
<= 0
): ):
return {"allow_write_off", "allow_auto_reconcile"} return {"allow_write_off", "allow_auto_reconcile"}
@ -674,7 +790,10 @@ class AccountReconcileModel(models.Model):
) * 100.0 ) * 100.0
if ( if (
self.payment_tolerance_type == "percentage" self.payment_tolerance_type == "percentage"
and reconciled_percentage_left <= self.payment_tolerance_param and st_line_currency.compare_amounts(
reconciled_percentage_left, self.payment_tolerance_param
)
<= 0
): ):
return {"allow_write_off", "allow_auto_reconcile"} return {"allow_write_off", "allow_auto_reconcile"}

View File

@ -367,7 +367,7 @@ ul.auto-toc {
!! This file is generated by oca-gen-addon-readme !! !! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !! !! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:80bb08dc3058116c364563a7014c16787db3ac0b12afadbde03716f7277fa298 !! source digest: sha256:f0554ce70e9ac90badf0a4082aecd5af7011cf1461fe0cc6678577d2b7f87e21
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/lgpl-3.0-standalone.html"><img alt="License: LGPL-3" src="https://img.shields.io/badge/licence-LGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/account-reconcile/tree/17.0/account_reconcile_model_oca"><img alt="OCA/account-reconcile" src="https://img.shields.io/badge/github-OCA%2Faccount--reconcile-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/account-reconcile-17-0/account-reconcile-17-0-account_reconcile_model_oca"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/account-reconcile&amp;target_branch=17.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p> <p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/lgpl-3.0-standalone.html"><img alt="License: LGPL-3" src="https://img.shields.io/badge/licence-LGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/account-reconcile/tree/17.0/account_reconcile_model_oca"><img alt="OCA/account-reconcile" src="https://img.shields.io/badge/github-OCA%2Faccount--reconcile-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/account-reconcile-17-0/account-reconcile-17-0-account_reconcile_model_oca"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/account-reconcile&amp;target_branch=17.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>This module restores account reconciliation models functions moved from <p>This module restores account reconciliation models functions moved from

View File

@ -1,3 +1,5 @@
from contextlib import contextmanager
from freezegun import freeze_time from freezegun import freeze_time
from odoo import Command from odoo import Command
@ -83,6 +85,8 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
"match_nature": "both", "match_nature": "both",
"match_same_currency": True, "match_same_currency": True,
"allow_payment_tolerance": True, "allow_payment_tolerance": True,
"match_text_location_note": True,
"match_text_location_reference": True,
"payment_tolerance_type": "percentage", "payment_tolerance_type": "percentage",
"payment_tolerance_param": 0.0, "payment_tolerance_param": 0.0,
"match_partner": True, "match_partner": True,
@ -287,6 +291,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
def test_matching_fields(self): def test_matching_fields(self):
# Check without restriction. # Check without restriction.
self.rule_1.match_text_location_label = False
self._check_statement_matching( self._check_statement_matching(
self.rule_1, self.rule_1,
{ {
@ -301,123 +306,8 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
}, },
) )
@freeze_time("2020-01-01")
def test_matching_fields_match_text_location(self):
st_line = self._create_st_line(
payment_ref="1111", ref="2222 3333", narration="4444 5555 6666"
)
inv1 = self._create_invoice_line(
1000, self.partner_a, "out_invoice", pay_reference="bernard 1111 gagnant"
)
inv2 = self._create_invoice_line(
1000, self.partner_a, "out_invoice", pay_reference="2222 turlututu 3333"
)
inv3 = self._create_invoice_line(
1000,
self.partner_a,
"out_invoice",
pay_reference="4444 tsoin 5555 tsoin 6666",
)
rule = self._create_reconcile_model(
allow_payment_tolerance=False,
match_text_location_label=True,
match_text_location_reference=False,
match_text_location_note=False,
)
self.assertDictEqual(
rule._apply_rules(st_line, st_line._retrieve_partner()),
{"amls": inv1, "model": rule},
)
rule.match_text_location_reference = True
self.assertDictEqual(
rule._apply_rules(st_line, st_line._retrieve_partner()),
{"amls": inv2, "model": rule},
)
rule.match_text_location_note = True
self.assertDictEqual(
rule._apply_rules(st_line, st_line._retrieve_partner()),
{"amls": inv3, "model": rule},
)
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: {},
},
)
# 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: {
"amls": self.invoice_line_1,
"model": self.rule_1,
},
},
)
# 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}
)
self._check_statement_matching(
self.rule_1,
{
self.bank_line_1: {
"amls": self.invoice_line_1,
"model": self.rule_1,
},
},
)
def test_matching_fields_match_journal_ids(self): def test_matching_fields_match_journal_ids(self):
self.rule_1.match_text_location_label = False
self.rule_1.match_journal_ids |= self.cash_line_1.journal_id self.rule_1.match_journal_ids |= self.cash_line_1.journal_id
self._check_statement_matching( self._check_statement_matching(
self.rule_1, self.rule_1,
@ -429,6 +319,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
) )
def test_matching_fields_match_nature(self): def test_matching_fields_match_nature(self):
self.rule_1.match_text_location_label = False
self.rule_1.match_nature = "amount_received" self.rule_1.match_nature = "amount_received"
self._check_statement_matching( self._check_statement_matching(
self.rule_1, self.rule_1,
@ -454,6 +345,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
) )
def test_matching_fields_match_amount(self): def test_matching_fields_match_amount(self):
self.rule_1.match_text_location_label = False
self.rule_1.match_amount = "lower" self.rule_1.match_amount = "lower"
self.rule_1.match_amount_max = 150 self.rule_1.match_amount_max = 150
self._check_statement_matching( self._check_statement_matching(
@ -497,6 +389,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
) )
def test_matching_fields_match_label(self): def test_matching_fields_match_label(self):
self.rule_1.match_text_location_label = False
self.rule_1.match_label = "contains" self.rule_1.match_label = "contains"
self.rule_1.match_label_param = "yyyyy" self.rule_1.match_label_param = "yyyyy"
self._check_statement_matching( self._check_statement_matching(
@ -535,7 +428,11 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
@freeze_time("2019-01-01") @freeze_time("2019-01-01")
def test_zero_payment_tolerance(self): def test_zero_payment_tolerance(self):
rule = self._create_reconcile_model(line_ids=[{}]) rule = self._create_reconcile_model(
line_ids=[{}],
match_text_location_reference=True,
match_text_location_note=True,
)
for inv_type, bsl_sign in (("out_invoice", 1), ("in_invoice", -1)): for inv_type, bsl_sign in (("out_invoice", 1), ("in_invoice", -1)):
invl = self._create_invoice_line( invl = self._create_invoice_line(
@ -543,21 +440,27 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
) )
# Exact matching. # Exact matching.
st_line = self._create_st_line(amount=bsl_sign * 1000.0) st_line = self._create_st_line(
amount=bsl_sign * 1000.0, payment_ref=invl.name
)
self._check_statement_matching( self._check_statement_matching(
rule, rule,
{st_line: {"amls": invl, "model": rule}}, {st_line: {"amls": invl, "model": rule}},
) )
# No matching because there is no tolerance. # No matching because there is no tolerance.
st_line = self._create_st_line(amount=bsl_sign * 990.0) st_line = self._create_st_line(
amount=bsl_sign * 990.0, payment_ref=invl.name
)
self._check_statement_matching( self._check_statement_matching(
rule, rule,
{st_line: {}}, {st_line: {}},
) )
# The payment amount is higher than the invoice one. # The payment amount is higher than the invoice one.
st_line = self._create_st_line(amount=bsl_sign * 1010.0) st_line = self._create_st_line(
amount=bsl_sign * 1010.0, payment_ref=invl.name
)
self._check_statement_matching( self._check_statement_matching(
rule, rule,
{st_line: {"amls": invl, "model": rule}}, {st_line: {"amls": invl, "model": rule}},
@ -580,7 +483,9 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
) )
# No matching because there is no tolerance. # No matching because there is no tolerance.
st_line = self._create_st_line(amount=bsl_sign * 990.0) st_line = self._create_st_line(
amount=bsl_sign * 990.0, payment_ref="123456"
)
self._check_statement_matching( self._check_statement_matching(
rule, rule,
{st_line: {}}, {st_line: {}},
@ -609,7 +514,9 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
) )
# No matching because there is no enough tolerance. # No matching because there is no enough tolerance.
st_line = self._create_st_line(amount=bsl_sign * 990.0) st_line = self._create_st_line(
amount=bsl_sign * 990.0, payment_ref=invl.name
)
self._check_statement_matching( self._check_statement_matching(
rule, rule,
{st_line: {}}, {st_line: {}},
@ -618,7 +525,9 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
# The payment amount is higher than the invoice one. # The payment amount is higher than the invoice one.
# However, since the invoice amount is lower than the payment amount, # However, since the invoice amount is lower than the payment amount,
# the tolerance is not checked and the invoice line is matched. # the tolerance is not checked and the invoice line is matched.
st_line = self._create_st_line(amount=bsl_sign * 1010.0) st_line = self._create_st_line(
amount=bsl_sign * 1010.0, payment_ref=invl.name
)
self._check_statement_matching( self._check_statement_matching(
rule, rule,
{st_line: {"amls": invl, "model": rule}}, {st_line: {"amls": invl, "model": rule}},
@ -627,17 +536,19 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
@freeze_time("2019-01-01") @freeze_time("2019-01-01")
def test_enough_payment_tolerance(self): def test_enough_payment_tolerance(self):
rule = self._create_reconcile_model( rule = self._create_reconcile_model(
payment_tolerance_param=1.0, payment_tolerance_param=2.0,
line_ids=[{}], line_ids=[{}],
) )
for inv_type, bsl_sign in (("out_invoice", 1), ("in_invoice", -1)): for inv_type, bsl_sign in (("out_invoice", 1), ("in_invoice", -1)):
invl = self._create_invoice_line( invl = self._create_invoice_line(
1000.0, self.partner_a, inv_type, inv_date="2019-01-01" 1210.0, self.partner_a, inv_type, inv_date="2019-01-01"
) )
# Enough tolerance to match the invoice line. # Enough tolerance to match the invoice line.
st_line = self._create_st_line(amount=bsl_sign * 990.0) st_line = self._create_st_line(
amount=bsl_sign * 1185.80, payment_ref=invl.name
)
self._check_statement_matching( self._check_statement_matching(
rule, rule,
{st_line: {"amls": invl, "model": rule, "status": "write_off"}}, {st_line: {"amls": invl, "model": rule, "status": "write_off"}},
@ -646,7 +557,9 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
# The payment amount is higher than the invoice one. # The payment amount is higher than the invoice one.
# However, since the invoice amount is lower than the payment amount, # However, since the invoice amount is lower than the payment amount,
# the tolerance is not checked and the invoice line is matched. # the tolerance is not checked and the invoice line is matched.
st_line = self._create_st_line(amount=bsl_sign * 1010.0) st_line = self._create_st_line(
amount=bsl_sign * 1234.20, payment_ref=invl.name
)
self._check_statement_matching( self._check_statement_matching(
rule, rule,
{st_line: {"amls": invl, "model": rule}}, {st_line: {"amls": invl, "model": rule}},
@ -695,7 +608,9 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
invl = self._create_invoice_line( invl = self._create_invoice_line(
990.0, self.partner_a, inv_type, inv_date="2019-01-01" 990.0, self.partner_a, inv_type, inv_date="2019-01-01"
) )
st_line = self._create_st_line(amount=bsl_sign * 1000) st_line = self._create_st_line(
amount=bsl_sign * 1000, payment_ref=invl.name
)
# Partial reconciliation. # Partial reconciliation.
self._check_statement_matching( self._check_statement_matching(
@ -775,10 +690,14 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
) )
def test_matching_fields_match_partner_category_ids(self): def test_matching_fields_match_partner_category_ids(self):
self.rule_1.match_text_location_label = False
test_category = self.env["res.partner.category"].create( test_category = self.env["res.partner.category"].create(
{"name": "Consulting Services"} {"name": "Consulting Services"}
) )
self.partner_2.category_id = test_category test_category2 = self.env["res.partner.category"].create(
{"name": "Consulting Services2"}
)
self.partner_2.category_id = test_category + test_category2
self.rule_1.match_partner_category_ids |= test_category self.rule_1.match_partner_category_ids |= test_category
self._check_statement_matching( self._check_statement_matching(
self.rule_1, self.rule_1,
@ -792,6 +711,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
def test_mixin_rules(self): def test_mixin_rules(self):
"""Test usage of rules together.""" """Test usage of rules together."""
self.rule_1.match_text_location_label = False
# rule_1 is used before rule_2. # rule_1 is used before rule_2.
self.rule_1.sequence = 1 self.rule_1.sequence = 1
self.rule_2.sequence = 2 self.rule_2.sequence = 2
@ -870,18 +790,24 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
self.rule_2.auto_reconcile = True self.rule_2.auto_reconcile = True
self._check_statement_matching( self._check_statement_matching(
self.rule_1 + self.rule_2, self.rule_1,
{ {
self.bank_line_1: { self.bank_line_1: {
"amls": self.invoice_line_1, "amls": self.invoice_line_1,
"model": self.rule_1, "model": self.rule_1,
"auto_reconcile": True, "auto_reconcile": True,
}, },
},
)
rule_3 = self.rule_1.copy({"match_text_location_label": False})
self._check_statement_matching(
self.rule_2 + rule_3,
{
self.bank_line_2: { self.bank_line_2: {
"amls": self.invoice_line_1 "amls": self.invoice_line_1
+ self.invoice_line_2 + self.invoice_line_2
+ self.invoice_line_3, + self.invoice_line_3,
"model": self.rule_1, "model": rule_3,
}, },
self.cash_line_1: { self.cash_line_1: {
"model": self.rule_2, "model": self.rule_2,
@ -910,11 +836,17 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
"model": self.rule_1, "model": self.rule_1,
"auto_reconcile": True, "auto_reconcile": True,
}, },
},
)
rule_3 = self.rule_1.copy({"match_text_location_label": False})
self._check_statement_matching(
rule_3,
{
self.bank_line_2: { self.bank_line_2: {
"amls": self.invoice_line_1 "amls": self.invoice_line_1
+ self.invoice_line_2 + self.invoice_line_2
+ self.invoice_line_3, + self.invoice_line_3,
"model": self.rule_1, "model": rule_3,
}, },
}, },
) )
@ -1069,6 +1001,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
move_reversed = move._reverse_moves() move_reversed = move._reverse_moves()
self.assertTrue(move_reversed.exists()) self.assertTrue(move_reversed.exists())
self.rule_1.match_text_location_label = False
self.bank_line_1.write( self.bank_line_1.write(
{ {
"payment_ref": "8", "payment_ref": "8",
@ -1110,7 +1043,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
"partner_id": partner.id, "partner_id": partner.id,
"foreign_currency_id": currency_statement.id, "foreign_currency_id": currency_statement.id,
"amount_currency": 100, "amount_currency": 100,
"payment_ref": "test", "payment_ref": invoice_line.name,
} }
) )
self._check_statement_matching( self._check_statement_matching(
@ -1212,40 +1145,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
@ -1278,6 +1177,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
"match_same_currency": False, "match_same_currency": False,
"company_id": self.company_data["company"].id, "company_id": self.company_data["company"].id,
"past_months_limit": False, "past_months_limit": False,
"match_text_location_label": False,
} }
) )
@ -1454,6 +1354,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
200 should be proposed. 200 should be proposed.
""" """
self.rule_1.allow_payment_tolerance = False self.rule_1.allow_payment_tolerance = False
self.rule_1.match_text_location_label = False
self.bank_line_2.amount = 250 self.bank_line_2.amount = 250
self.bank_line_1.partner_id = None self.bank_line_1.partner_id = None
@ -1476,6 +1377,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
other ones are disregarded. other ones are disregarded.
""" """
self.rule_1.allow_payment_tolerance = False self.rule_1.allow_payment_tolerance = False
self.rule_1.match_text_location_label = False
self.bank_line_2.amount = 300 self.bank_line_2.amount = 300
self.bank_line_1.partner_id = None self.bank_line_1.partner_id = None
@ -1490,3 +1392,238 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
}, },
}, },
) )
@freeze_time("2019-01-01")
def test_invoice_matching_using_match_text_location(self):
@contextmanager
def rollback():
savepoint = self.cr.savepoint()
yield
savepoint.rollback()
rule = self._create_reconcile_model(
match_partner=False,
allow_payment_tolerance=False,
match_text_location_reference=True,
match_text_location_note=True,
)
st_line = self._create_st_line(amount=1000, partner_id=False)
invoice = self.env["account.move"].create(
{
"move_type": "out_invoice",
"partner_id": self.partner_a.id,
"invoice_date": "2019-01-01",
"invoice_line_ids": [
Command.create(
{
"product_id": self.product_a.id,
"price_unit": 100,
}
)
],
}
)
invoice.action_post()
term_line = invoice.line_ids.filtered(
lambda x: x.display_type == "payment_term"
)
# No match at all.
self.assertDictEqual(
rule._apply_rules(st_line, None),
{},
)
with rollback():
term_line.name = "1234"
st_line.payment_ref = "1234"
# Matching if no checkbox checked.
self.assertDictEqual(
rule._apply_rules(st_line, None),
{"amls": term_line, "model": rule},
)
# No matching if checkbox is unchecked.
rule.match_text_location_label = False
rule.match_text_location_reference = False
rule.match_text_location_note = False
self.assertDictEqual(
rule._apply_rules(st_line, None),
{},
)
with rollback():
# Test Matching on exact_token.
term_line.name = "PAY-123"
st_line.payment_ref = "PAY-123"
# Matching if no checkbox checked.
self.assertDictEqual(
rule._apply_rules(st_line, None),
{"amls": term_line, "model": rule},
)
with self.subTest(
rule_field="match_text_location_label", st_line_field="payment_ref"
):
with rollback():
term_line.name = ""
st_line.payment_ref = "/?"
# No exact matching when the term line name is an empty string
self.assertDictEqual(
rule._apply_rules(st_line, None),
{},
)
for rule_field, st_line_field in (
("match_text_location_label", "payment_ref"),
("match_text_location_reference", "ref"),
("match_text_location_note", "narration"),
):
with self.subTest(rule_field=rule_field, st_line_field=st_line_field):
with rollback():
rule[rule_field] = True
st_line[st_line_field] = "123456"
term_line.name = "123456"
# Matching if the corresponding flag is enabled.
self.assertDictEqual(
rule._apply_rules(st_line, None),
{"amls": term_line, "model": rule},
)
# It works also if the statement line contains the word.
st_line[st_line_field] = "payment for 123456 urgent!"
self.assertDictEqual(
rule._apply_rules(st_line, None),
{"amls": term_line, "model": rule},
)
# Not if the invoice has nothing in common even if numerical.
term_line.name = "78910"
self.assertDictEqual(
rule._apply_rules(st_line, None),
{},
)
# Exact matching on a single word.
st_line[st_line_field] = "TURLUTUTU21"
term_line.name = "TURLUTUTU21"
self.assertDictEqual(
rule._apply_rules(st_line, None),
{"amls": term_line, "model": rule},
)
# No matching if not enough numerical values.
st_line[st_line_field] = "12"
term_line.name = "selling 3 apples, 2 tomatoes and 12kg of potatoes"
self.assertDictEqual(
rule._apply_rules(st_line, None),
{},
)
invoice2 = self.env["account.move"].create(
{
"move_type": "out_invoice",
"partner_id": self.partner_a.id,
"invoice_date": "2019-01-01",
"invoice_line_ids": [
Command.create(
{
"product_id": self.product_a.id,
"price_unit": 100,
}
)
],
}
)
invoice2.action_post()
term_lines = (invoice + invoice2).line_ids.filtered(
lambda x: x.display_type == "payment_term"
)
# Matching multiple invoices.
rule.match_text_location_label = True
st_line.payment_ref = "paying invoices 1234 & 5678"
term_lines[0].name = "INV/1234"
term_lines[1].name = "INV/5678"
self.assertDictEqual(
rule._apply_rules(st_line, None),
{"amls": term_lines, "model": rule},
)
# Matching multiple invoices sharing the same reference.
term_lines[1].name = "INV/1234"
self.assertDictEqual(
rule._apply_rules(st_line, None),
{"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.currency_data_2["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,
},
},
)

View File

@ -7,7 +7,7 @@ Account Reconcile Oca
!! This file is generated by oca-gen-addon-readme !! !! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !! !! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:cf0d778067ac722c5a6d7f65f8fa4b0766076829ba9c9c147cc3718782cc85b0 !! source digest: sha256:a1d19f7cb36b1b53954b505f964b766f7237615fc66c01368256cc9c5f390fd2
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png

View File

@ -5,7 +5,7 @@
"name": "Account Reconcile Oca", "name": "Account Reconcile Oca",
"summary": """ "summary": """
Reconcile addons for Odoo CE accounting""", Reconcile addons for Odoo CE accounting""",
"version": "17.0.1.5.5", "version": "17.0.1.5.8",
"license": "AGPL-3", "license": "AGPL-3",
"author": "CreuBlanca,Dixmit,Odoo Community Association (OCA)", "author": "CreuBlanca,Dixmit,Odoo Community Association (OCA)",
"maintainers": ["etobella"], "maintainers": ["etobella"],

View File

@ -613,7 +613,10 @@ class AccountBankStatementLine(models.Model):
self.env["res.partner"].browse(line["partner_id"]).display_name, self.env["res.partner"].browse(line["partner_id"]).display_name,
) )
elif self.partner_id: elif self.partner_id:
new_line["partner_id"] = self.partner_id.name_get()[0] new_line["partner_id"] = (
self.partner_id.id,
self.partner_id.display_name,
)
new_data.append(new_line) new_data.append(new_line)
return new_data, reconcile_auxiliary_id return new_data, reconcile_auxiliary_id
@ -681,13 +684,16 @@ class AccountBankStatementLine(models.Model):
reconciled_line.move_id.journal_id reconciled_line.move_id.journal_id
== self.company_id.currency_exchange_journal_id == self.company_id.currency_exchange_journal_id
): ):
reconcile_auxiliary_id, lines = self._get_reconcile_line( for rl_item in (
reconciled_line.move_id.line_ids - reconciled_line, reconciled_line.move_id.line_ids - reconciled_line
"other", ):
from_unreconcile=False, reconcile_auxiliary_id, lines = self._get_reconcile_line(
move=True, rl_item,
) "other",
data += lines from_unreconcile=False,
move=True,
)
data += lines
continue continue
partial = partial_lines.filtered( partial = partial_lines.filtered(
lambda r, line=reconciled_line: r.debit_move_id == line lambda r, line=reconciled_line: r.debit_move_id == line
@ -1017,7 +1023,7 @@ class AccountBankStatementLine(models.Model):
suspense_lines, suspense_lines,
_other_lines, _other_lines,
) = st_line._seek_for_lines() ) = st_line._seek_for_lines()
line_vals = {"partner_id": st_line.partner_id} line_vals = {"partner_id": st_line.partner_id.id}
line_ids_commands = [(1, liquidity_lines.id, line_vals)] line_ids_commands = [(1, liquidity_lines.id, line_vals)]
if suspense_lines: if suspense_lines:
line_ids_commands.append((1, suspense_lines.id, line_vals)) line_ids_commands.append((1, suspense_lines.id, line_vals))
@ -1270,3 +1276,11 @@ class AccountBankStatementLine(models.Model):
for line in lines: for line in lines:
self._add_account_move_line(line, keep_current=True) self._add_account_move_line(line, keep_current=True)
return res return res
def _retrieve_partner(self):
if self.env.context.get("skip_retrieve_partner"):
# This hook can be used, for example, when importing files.
# With large databases, we already have the information, moreover,
# the data might be preloaded, so it has no sense to import it again
return self.partner_id
return super()._retrieve_partner()

View File

@ -367,7 +367,7 @@ ul.auto-toc {
!! This file is generated by oca-gen-addon-readme !! !! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !! !! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:cf0d778067ac722c5a6d7f65f8fa4b0766076829ba9c9c147cc3718782cc85b0 !! source digest: sha256:a1d19f7cb36b1b53954b505f964b766f7237615fc66c01368256cc9c5f390fd2
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/account-reconcile/tree/17.0/account_reconcile_oca"><img alt="OCA/account-reconcile" src="https://img.shields.io/badge/github-OCA%2Faccount--reconcile-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/account-reconcile-17-0/account-reconcile-17-0-account_reconcile_oca"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/account-reconcile&amp;target_branch=17.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p> <p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/account-reconcile/tree/17.0/account_reconcile_oca"><img alt="OCA/account-reconcile" src="https://img.shields.io/badge/github-OCA%2Faccount--reconcile-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/account-reconcile-17-0/account-reconcile-17-0-account_reconcile_oca"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/account-reconcile&amp;target_branch=17.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>This addon allows to reconcile bank statements and account marked as <p>This addon allows to reconcile bank statements and account marked as

View File

@ -6,15 +6,15 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Odoo Server 16.0\n" "Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2023-07-29 12:10+0000\n" "PO-Revision-Date: 2025-02-13 20:35+0000\n"
"Last-Translator: Ivorra78 <informatica@totmaterial.es>\n" "Last-Translator: \"Pedro M. Baeza\" <pedro.baeza@tecnativa.com>\n"
"Language-Team: none\n" "Language-Team: none\n"
"Language: es\n" "Language: es\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n" "Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n" "Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.17\n" "X-Generator: Weblate 5.6.2\n"
#. module: account_statement_base #. module: account_statement_base
#: model_terms:ir.ui.view,arch_db:account_statement_base.view_bank_statement_form #: model_terms:ir.ui.view,arch_db:account_statement_base.view_bank_statement_form
@ -63,7 +63,7 @@ msgstr "Etiqueta, referencia o notas"
#. module: account_statement_base #. module: account_statement_base
#: model_terms:ir.ui.view,arch_db:account_statement_base.account_bank_statement_line_search #: model_terms:ir.ui.view,arch_db:account_statement_base.account_bank_statement_line_search
msgid "Not Reconciled" msgid "Not Reconciled"
msgstr "No reconciliado" msgstr "No conciliado"
#. module: account_statement_base #. module: account_statement_base
#: model_terms:ir.ui.view,arch_db:account_statement_base.account_bank_statement_line_form #: model_terms:ir.ui.view,arch_db:account_statement_base.account_bank_statement_line_form
@ -89,7 +89,7 @@ msgstr "Socio"
#. module: account_statement_base #. module: account_statement_base
#: model_terms:ir.ui.view,arch_db:account_statement_base.account_bank_statement_line_search #: model_terms:ir.ui.view,arch_db:account_statement_base.account_bank_statement_line_search
msgid "Reconciled" msgid "Reconciled"
msgstr "Reconciliado" msgstr "Conciliado"
#. module: account_statement_base #. module: account_statement_base
#: model_terms:ir.ui.view,arch_db:account_statement_base.account_bank_statement_line_tree #: model_terms:ir.ui.view,arch_db:account_statement_base.account_bank_statement_line_tree