[IMP] account: journal items match improvements
This commit contains some misc improvements based on the 16.0 overall improvements regarding navigation and Journal Items
in account.full.reconcile, 'name' field has been removed, and the replacement for matching_number will be the SQL id instead (no more sequences)
For full reconciliations, the matching number now don't have an 'A' prefix
For partial reconciliations, the matching number will have a format of 'P{id}'
Community-pr: https://github.com/odoo/odoo/pull/138140
Enterprise-pr: https://github.com/odoo/enterprise/pull/48980
Task-id: 3530458
Part-of: odoo/odoo#138140
This commit is contained in:
@@ -97,18 +97,6 @@
|
||||
Command.create({'value': 'percent', 'value_amount': 100.0, 'nb_days': 30})]"/>
|
||||
</record>
|
||||
|
||||
<!--
|
||||
Account Statement Sequences
|
||||
-->
|
||||
<record id="sequence_reconcile_seq" model="ir.sequence">
|
||||
<field name="name">Account reconcile sequence</field>
|
||||
<field name="code">account.reconcile</field>
|
||||
<field name="prefix">A</field>
|
||||
<field eval="1" name="number_next"/>
|
||||
<field eval="1" name="number_increment"/>
|
||||
<field eval="False" name="company_id"/>
|
||||
</record>
|
||||
|
||||
<!-- Account-related subtypes for messaging / Chatter -->
|
||||
<record id="mt_invoice_validated" model="mail.message.subtype">
|
||||
<field name="name">Validated</field>
|
||||
|
||||
@@ -6,7 +6,6 @@ class AccountFullReconcile(models.Model):
|
||||
_name = "account.full.reconcile"
|
||||
_description = "Full Reconcile"
|
||||
|
||||
name = fields.Char(string='Number', required=True, copy=False, default=lambda self: self.env['ir.sequence'].next_by_code('account.reconcile'))
|
||||
partial_reconcile_ids = fields.One2many('account.partial.reconcile', 'full_reconcile_id', string='Reconciliation Parts')
|
||||
reconciled_line_ids = fields.One2many('account.move.line', 'full_reconcile_id', string='Matched Journal Items')
|
||||
exchange_move_id = fields.Many2one('account.move', index="btree_not_null")
|
||||
@@ -35,3 +34,10 @@ class AccountFullReconcile(models.Model):
|
||||
moves_to_reverse._reverse_moves(default_values_list, cancel=True)
|
||||
|
||||
return res
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
fulls = super().create(vals_list)
|
||||
for full in fulls:
|
||||
full.reconciled_line_ids.matching_number = str(full.id)
|
||||
return fulls
|
||||
|
||||
@@ -256,7 +256,7 @@ class AccountMoveLine(models.Model):
|
||||
)
|
||||
matching_number = fields.Char(
|
||||
string="Matching #",
|
||||
compute='_compute_matching_number', store=True,
|
||||
readonly=True,
|
||||
help="Matching number for this line, 'P' if it is only partially reconcile, or the name of "
|
||||
"the full reconcile if it exists.",
|
||||
)
|
||||
@@ -666,16 +666,6 @@ class AccountMoveLine(models.Model):
|
||||
if line.currency_id == line.company_id.currency_id:
|
||||
line.amount_currency = line.balance
|
||||
|
||||
@api.depends('full_reconcile_id.name', 'matched_debit_ids', 'matched_credit_ids')
|
||||
def _compute_matching_number(self):
|
||||
for record in self:
|
||||
if record.full_reconcile_id:
|
||||
record.matching_number = record.full_reconcile_id.name
|
||||
elif record.matched_debit_ids or record.matched_credit_ids:
|
||||
record.matching_number = 'P'
|
||||
else:
|
||||
record.matching_number = None
|
||||
|
||||
@api.depends_context('order_cumulated_balance', 'domain_cumulated_balance')
|
||||
def _compute_cumulated_balance(self):
|
||||
if not self.env.context.get('order_cumulated_balance'):
|
||||
@@ -2948,13 +2938,22 @@ class AccountMoveLine(models.Model):
|
||||
|
||||
def _all_reconciled_lines(self):
|
||||
reconciliation_lines = self.filtered(lambda x: x.account_id.reconcile or x.account_id.account_type in ('asset_cash', 'liability_credit_card'))
|
||||
current_lines = reconciliation_lines
|
||||
current_partials = self.env['account.partial.reconcile']
|
||||
while current_lines:
|
||||
current_partials = (current_lines.matched_debit_ids + current_lines.matched_credit_ids) - current_partials
|
||||
current_lines = (current_partials.debit_move_id + current_partials.credit_move_id) - current_lines
|
||||
reconciliation_lines += current_lines
|
||||
return reconciliation_lines
|
||||
self.env['account.partial.reconcile'].flush_model()
|
||||
self.env.cr.execute("""
|
||||
WITH RECURSIVE partials (current_id) AS (
|
||||
SELECT line.id
|
||||
FROM account_move_line line
|
||||
WHERE id = ANY(%s)
|
||||
|
||||
UNION
|
||||
|
||||
SELECT CASE WHEN partial.debit_move_id = p.current_id THEN partial.credit_move_id ELSE partial.debit_move_id END
|
||||
FROM partials p
|
||||
JOIN account_partial_reconcile partial ON partial.debit_move_id = p.current_id OR partial.credit_move_id = p.current_id
|
||||
)
|
||||
SELECT current_id FROM partials;
|
||||
""", [reconciliation_lines.ids])
|
||||
return self.browse(r[0] for r in self.env.cr.fetchall())
|
||||
|
||||
def _get_attachment_domains(self):
|
||||
self.ensure_one()
|
||||
|
||||
@@ -8,7 +8,6 @@ from datetime import date
|
||||
class AccountPartialReconcile(models.Model):
|
||||
_name = "account.partial.reconcile"
|
||||
_description = "Partial Reconcile"
|
||||
_rec_name = "id"
|
||||
|
||||
# ==== Reconciliation fields ====
|
||||
debit_move_id = fields.Many2one(
|
||||
@@ -106,6 +105,7 @@ class AccountPartialReconcile(models.Model):
|
||||
|
||||
# Retrieve the matching number to unlink.
|
||||
full_to_unlink = self.full_reconcile_id
|
||||
all_reconciled = self.debit_move_id + self.credit_move_id
|
||||
|
||||
# Retrieve the CABA entries to reverse.
|
||||
moves_to_reverse = self.env['account.move'].search([('tax_cash_basis_rec_id', 'in', self.ids)])
|
||||
@@ -126,8 +126,57 @@ class AccountPartialReconcile(models.Model):
|
||||
} for move in moves_to_reverse]
|
||||
moves_to_reverse._reverse_moves(default_values_list, cancel=True)
|
||||
|
||||
self._update_matching_number(all_reconciled)
|
||||
return res
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
partials = super().create(vals_list)
|
||||
self._update_matching_number(partials.debit_move_id + partials.credit_move_id)
|
||||
return partials
|
||||
|
||||
@api.model
|
||||
def _update_matching_number(self, amls):
|
||||
amls = amls._all_reconciled_lines()
|
||||
self.env['account.partial.reconcile'].flush_model()
|
||||
amls.flush_recordset(['full_reconcile_id'])
|
||||
self.env.cr.execute("""
|
||||
WITH RECURSIVE partials (line_id, current_id) AS (
|
||||
SELECT id, id
|
||||
FROM account_move_line
|
||||
WHERE id = ANY(%s)
|
||||
AND full_reconcile_id IS NULL
|
||||
|
||||
UNION
|
||||
|
||||
SELECT p.line_id,
|
||||
CASE WHEN partial.debit_move_id = p.current_id THEN partial.credit_move_id
|
||||
ELSE partial.debit_move_id
|
||||
END
|
||||
FROM partials p
|
||||
JOIN account_partial_reconcile partial ON p.current_id = partial.debit_move_id
|
||||
OR p.current_id = partial.credit_move_id
|
||||
)
|
||||
SELECT line_id, 'P' || MIN(partial.id) AS matching_number
|
||||
FROM partials
|
||||
JOIN account_partial_reconcile partial ON current_id = partial.debit_move_id
|
||||
OR current_id = partial.credit_move_id
|
||||
GROUP BY line_id
|
||||
""", [
|
||||
amls.ids,
|
||||
])
|
||||
|
||||
line_matching_number = dict(self.env.cr.fetchall())
|
||||
|
||||
for line in amls:
|
||||
if line.full_reconcile_id:
|
||||
line.matching_number = str(line.full_reconcile_id.id)
|
||||
elif line.matched_debit_ids or line.matched_credit_ids:
|
||||
line.matching_number = line_matching_number[line.id]
|
||||
else:
|
||||
line.matching_number = False
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# RECONCILIATION METHODS
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
@@ -4000,6 +4000,104 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
|
||||
{'debit': 0.0, 'credit': 0.01, 'tax_ids': [], 'tax_tag_ids': self.tax_tags[5].ids, 'account_id': self.tax_account_2.id},
|
||||
])
|
||||
|
||||
def test_matching_number_full_reconcile(self):
|
||||
currency = self.env.company.currency_id
|
||||
line_a = self.create_line_for_reconciliation(1000, 1000, currency, '2016-01-01')
|
||||
line_b = self.create_line_for_reconciliation(-1000, -1000, currency, '2016-01-01')
|
||||
(line_a + line_b).reconcile()
|
||||
self.assertFullReconcile(line_a.full_reconcile_id, (line_a + line_b))
|
||||
self.assertEqual(line_a.matching_number, str(line_a.full_reconcile_id.id))
|
||||
self.assertEqual(line_a.matching_number, line_b.matching_number)
|
||||
|
||||
def test_matching_number_partial_single_reconcile(self):
|
||||
currency = self.env.company.currency_id
|
||||
line_a = self.create_line_for_reconciliation(1000, 1000, currency, '2016-01-01')
|
||||
line_b = self.create_line_for_reconciliation(-500, -500, currency, '2016-01-01')
|
||||
(line_a + line_b).reconcile()
|
||||
self.assertEqual(line_a.matching_number, f'P{line_a.matched_credit_ids.id}')
|
||||
self.assertEqual(line_a.matching_number, line_b.matching_number)
|
||||
|
||||
def test_matching_number_partial_multi_reconcile(self):
|
||||
currency = self.env.company.currency_id
|
||||
line_a = self.create_line_for_reconciliation(1000, 1000, currency, '2016-01-01')
|
||||
line_b = self.create_line_for_reconciliation(-500, -500, currency, '2016-01-01')
|
||||
line_c = self.create_line_for_reconciliation(-1000, -1000, currency, '2016-01-01')
|
||||
line_d = self.create_line_for_reconciliation(1000, 1000, currency, '2016-01-01')
|
||||
(line_a + line_b).reconcile()
|
||||
(line_a + line_c).reconcile()
|
||||
(line_c + line_d).reconcile()
|
||||
self.assertEqual(line_a.matching_number, f'P{line_a.matched_credit_ids.ids[0]}')
|
||||
self.assertEqual(line_b.matching_number, line_a.matching_number)
|
||||
self.assertEqual(line_c.matching_number, line_a.matching_number)
|
||||
self.assertEqual(line_d.matching_number, line_a.matching_number)
|
||||
|
||||
line_b.remove_move_reconcile()
|
||||
self.assertEqual(line_a.matching_number, f'P{line_a.matched_credit_ids.ids[0]}')
|
||||
self.assertEqual(line_b.matching_number, False)
|
||||
self.assertEqual(line_c.matching_number, f'P{line_c.matched_debit_ids.ids[0]}')
|
||||
self.assertEqual(line_d.matching_number, line_c.matching_number)
|
||||
|
||||
(line_a + line_b).reconcile() # everything should be matched again
|
||||
self.assertEqual(line_a.matching_number, line_c.matching_number)
|
||||
self.assertEqual(line_b.matching_number, line_c.matching_number)
|
||||
self.assertEqual(line_c.matching_number, f'P{line_c.matched_debit_ids.ids[0]}')
|
||||
self.assertEqual(line_d.matching_number, line_c.matching_number)
|
||||
|
||||
def test_matching_number_partial_multi_separate_reconcile(self):
|
||||
currency = self.env.company.currency_id
|
||||
line_a = self.create_line_for_reconciliation(1000, 1000, currency, '2016-01-01')
|
||||
line_b = self.create_line_for_reconciliation(-500, -500, currency, '2016-01-01')
|
||||
(line_a + line_b).reconcile()
|
||||
self.assertEqual(line_a.matching_number, f'P{line_a.matched_credit_ids.id}')
|
||||
self.assertEqual(line_a.matching_number, line_b.matching_number)
|
||||
|
||||
line_c = self.create_line_for_reconciliation(-300, -300, currency, '2016-01-01')
|
||||
(line_a + line_c).reconcile()
|
||||
self.assertEqual(line_a.matching_number, f'P{line_a.matched_credit_ids.ids[0]}')
|
||||
self.assertEqual(line_a.matching_number, line_b.matching_number)
|
||||
self.assertEqual(line_a.matching_number, line_c.matching_number)
|
||||
|
||||
def test_matching_number_unreconcile_single(self):
|
||||
currency = self.env.company.currency_id
|
||||
full_line_a = self.create_line_for_reconciliation(200, 200, currency, '2016-01-01')
|
||||
full_line_b = self.create_line_for_reconciliation(-200, -200, currency, '2016-01-01')
|
||||
partial_line_a = self.create_line_for_reconciliation(1000, 1000, currency, '2016-01-01')
|
||||
partial_line_b = self.create_line_for_reconciliation(-500, -500, currency, '2016-01-01')
|
||||
(full_line_a + full_line_b).reconcile()
|
||||
(partial_line_a + partial_line_b).reconcile()
|
||||
(full_line_a + full_line_b + partial_line_a + partial_line_b).remove_move_reconcile()
|
||||
self.assertFalse(full_line_a.matching_number)
|
||||
self.assertFalse(full_line_b.matching_number)
|
||||
self.assertFalse(partial_line_a.matching_number)
|
||||
self.assertFalse(partial_line_b.matching_number)
|
||||
|
||||
def test_matching_number_unreconcile_multi(self):
|
||||
currency = self.env.company.currency_id
|
||||
line_a = self.create_line_for_reconciliation(-500, -500, currency, '2016-01-01')
|
||||
line_b = self.create_line_for_reconciliation(1000, 1000, currency, '2016-01-01')
|
||||
line_c = self.create_line_for_reconciliation(-1000, -1000, currency, '2016-01-01')
|
||||
line_d = self.create_line_for_reconciliation(300, 300, currency, '2016-01-01')
|
||||
(line_a + line_b).reconcile()
|
||||
(line_b + line_c).reconcile()
|
||||
(line_c + line_d).reconcile()
|
||||
|
||||
previous_matching_number = line_a.matching_number
|
||||
line_a.remove_move_reconcile()
|
||||
self.assertFalse(line_a.matching_number)
|
||||
self.assertNotEqual(previous_matching_number, line_b.matching_number)
|
||||
self.assertEqual(line_b.matching_number, line_c.matching_number)
|
||||
self.assertEqual(line_b.matching_number, line_d.matching_number)
|
||||
|
||||
previous_matching_number = line_b.matching_number
|
||||
line_b.remove_move_reconcile()
|
||||
self.assertFalse(line_b.matching_number)
|
||||
self.assertNotEqual(previous_matching_number, line_c.matching_number)
|
||||
self.assertEqual(line_c.matching_number, line_d.matching_number)
|
||||
|
||||
line_c.remove_move_reconcile()
|
||||
self.assertFalse(line_c.matching_number)
|
||||
self.assertFalse(line_d.matching_number)
|
||||
|
||||
def test_caba_mix_reconciliation(self):
|
||||
""" Test the reconciliation of tax lines (when using a reconcilable tax account)
|
||||
for cases mixing taxes exigible on payment and on invoices.
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<form string="Matching">
|
||||
<group>
|
||||
<div class="oe_title" colspan="4">
|
||||
<h1><field name="name" readonly="1"/></h1>
|
||||
<h1><field name="id" readonly="1"/></h1>
|
||||
</div>
|
||||
<separator string="Matched Journal Items" colspan="4"/>
|
||||
<field name="reconciled_line_ids" readonly="1" colspan="4" nolabel="1"/>
|
||||
|
||||
@@ -360,7 +360,7 @@
|
||||
<filter string="Invoice Date" name="groupby_invoice_date" domain="[]" context="{'group_by': 'invoice_date'}"/>
|
||||
<filter string="Taxes" name="group_by_taxes" domain="[]" context="{'group_by': 'tax_ids'}"/>
|
||||
<filter string="Tax Grid" name="group_by_tax_tags" domain="[]" context="{'group_by': 'tax_tag_ids'}"/>
|
||||
<filter string="Matching #" name="group_by_matching" domain="[]" context="{'group_by': 'full_reconcile_id'}"/>
|
||||
<filter string="Matching #" name="group_by_matching" domain="[]" context="{'group_by': 'matching_number'}"/>
|
||||
</group>
|
||||
<searchpanel class="account_root">
|
||||
<field name="account_root_id" icon="fa-filter" groupby="account_id" limit="0"/>
|
||||
|
||||
@@ -337,7 +337,7 @@ class AccountFrFec(models.TransientModel):
|
||||
ELSE REGEXP_REPLACE(replace(aml.name, '|', '/'), '[\\t\\n\\r]', ' ', 'g') END AS EcritureLib,
|
||||
replace(CASE WHEN aml.debit = 0 THEN '0,00' ELSE to_char(aml.debit, '000000000000000D99') END, '.', ',') AS Debit,
|
||||
replace(CASE WHEN aml.credit = 0 THEN '0,00' ELSE to_char(aml.credit, '000000000000000D99') END, '.', ',') AS Credit,
|
||||
CASE WHEN rec.name IS NULL THEN '' ELSE rec.name END AS EcritureLet,
|
||||
CASE WHEN rec.id IS NULL THEN ''::text ELSE rec.id::text END AS EcritureLet,
|
||||
CASE WHEN aml.full_reconcile_id IS NULL THEN '' ELSE TO_CHAR(rec.create_date, 'YYYYMMDD') END AS DateLet,
|
||||
TO_CHAR(am.date, 'YYYYMMDD') AS ValidDate,
|
||||
CASE
|
||||
|
||||
Reference in New Issue
Block a user