[FIX] account: propose values when creating new journal items

The new lines should contain the current balance, and the account of the
last 2 lines if it is the same.
* The computation of the balance was reading on `debit` and `credit`,
  leading to a cycle in the compute method. It is useless to try to read
  those fields because of `_inverse_debit_credit`
* The computation of the account was simply forgotten during the
  refactoring
Also move a constraint from `write` in a more rigorous `api.constrains`,
which revealed a bug for to find the account of payment terms of receipts.

Followup of d8d47f9ff8
task-2956509

closes odoo/odoo#101994

X-original-commit: f04f83b42bb1fbcacbb6bf92a2156ff2be1bf1b1
Signed-off-by: Laurent Smet <las@odoo.com>
Signed-off-by: William André (wan) <wan@odoo.com>
This commit is contained in:
william
2022-10-03 21:23:00 +02:00
parent df1e344391
commit 3ae786465e
2 changed files with 52 additions and 30 deletions
+47 -27
View File
@@ -89,12 +89,12 @@ class AccountMoveLine(models.Model):
)
debit = fields.Monetary(
string='Debit',
compute='_compute_debit_credit', inverse='_inverse_debit_credit', store=True,
compute='_compute_debit_credit', inverse='_inverse_debit', store=True, precompute=True,
currency_field='company_currency_id',
)
credit = fields.Monetary(
string='Credit',
compute='_compute_debit_credit', inverse='_inverse_debit_credit', store=True,
compute='_compute_debit_credit', inverse='_inverse_credit', store=True, precompute=True,
currency_field='company_currency_id',
)
balance = fields.Monetary(
@@ -545,7 +545,7 @@ class AccountMoveLine(models.Model):
for model, id, account_type, account_id in self.env.cr.fetchall()
}
for line in term_lines:
account_type = 'asset_receivable' if line.move_id.is_sale_document() else 'liability_payable'
account_type = 'asset_receivable' if line.move_id.is_sale_document(include_receipts=True) else 'liability_payable'
move = line.move_id
account_id = (
accounts.get(('account.move', move.id, None))
@@ -576,19 +576,25 @@ class AccountMoveLine(models.Model):
)[0]
for line in self:
if not line.account_id and line.display_type not in ('line_section', 'line_note'):
line.account_id = line.company_id.account_journal_suspense_account_id
previous_two_accounts = line.move_id.line_ids.filtered(
lambda l: l.account_id and l.display_type == line.display_type
)[-2:].account_id
if len(previous_two_accounts) == 1:
line.account_id = previous_two_accounts
else:
line.account_id = line.company_id.account_journal_suspense_account_id
@api.depends('move_id')
def _compute_balance(self):
for line in self:
if line.display_type not in ('line_section', 'line_note'):
line.balance = (
line.balance
or line.debit - line.credit
or -sum((line.move_id.line_ids - line).mapped('balance'))
)
else:
if line.display_type in ('line_section', 'line_note'):
line.balance = False
elif not line.move_id.is_invoice(include_receipts=True):
# Only act as a default value when none of balance/debit/credit is specified
# balance is always the written field because of `_sanitize_vals`
line.balance = -sum((line.move_id.line_ids - line).mapped('balance'))
else:
line.balance = 0
@api.depends('balance', 'move_id.is_storno')
def _compute_debit_credit(self):
@@ -1101,9 +1107,18 @@ class AccountMoveLine(models.Model):
if line.currency_id == line.company_id.currency_id and line.balance != line.amount_currency:
line.balance = line.amount_currency
@api.onchange('debit', 'credit')
def _inverse_debit_credit(self):
@api.onchange('debit')
def _inverse_debit(self):
for line in self:
if line.debit:
line.credit = 0
line.balance = line.debit - line.credit
@api.onchange('credit')
def _inverse_credit(self):
for line in self:
if line.credit:
line.debit = 0
line.balance = line.debit - line.credit
@api.onchange('analytic_distribution')
@@ -1171,6 +1186,17 @@ class AccountMoveLine(models.Model):
if line.reconciled:
raise UserError(_('Lines from "Off-Balance Sheet" accounts cannot be reconciled'))
@api.constrains('account_id', 'display_type')
def _check_payable_receivable(self):
for line in self:
account_type = line.account_id.account_type
if line.move_id.is_sale_document(include_receipts=True):
if (line.display_type == 'payment_term') ^ (account_type == 'asset_receivable'):
raise UserError(_("Any journal item on a receivable account must have a due date and vice versa."))
if line.move_id.is_purchase_document(include_receipts=True):
if (line.display_type == 'payment_term') ^ (account_type == 'liability_payable'):
raise UserError(_("Any journal item on a payable account must have a due date and vice versa."))
def _affect_tax_report(self):
self.ensure_one()
return self.tax_ids or self.tax_line_id or self.tax_tag_ids.filtered(lambda x: x.applicability == "taxes")
@@ -1277,6 +1303,7 @@ class AccountMoveLine(models.Model):
def _sanitize_vals(self, vals):
if 'debit' in vals or 'credit' in vals:
vals = vals.copy()
if 'balance' in vals:
vals.pop('debit', None)
vals.pop('credit', None)
@@ -1329,6 +1356,11 @@ class AccountMoveLine(models.Model):
and (not changed('balance') or (line not in before and not line.balance))
):
line.balance = balance
# Since this method is called during the sync, inside of `create`/`write`, these fields
# already have been computed and marked as so. But this method should re-trigger it since
# it changes the dependencies.
self.env.add_to_compute(self._fields['debit'], container['records'])
self.env.add_to_compute(self._fields['credit'], container['records'])
@api.model_create_multi
def create(self, vals_list):
@@ -1349,7 +1381,7 @@ class AccountMoveLine(models.Model):
return lines
def new(self, values=None, origin=None, ref=None):
record = super().new(values, origin, ref)
record = super().new(self._sanitize_vals(values), origin, ref)
if record.move_id.quick_edit_total_amount and record.move_id.quick_edit_mode:
record.move_id._check_total_amount(record.move_id.quick_edit_total_amount)
return record
@@ -1365,7 +1397,7 @@ class AccountMoveLine(models.Model):
raise UserError(_('You cannot use a deprecated account.'))
line_to_write = self
self._sanitize_vals(vals)
vals = self._sanitize_vals(vals)
for line in self:
if not any(self.env['account.move']._field_will_change(line, vals, field_name) for field_name in vals):
line_to_write -= line
@@ -1389,18 +1421,6 @@ class AccountMoveLine(models.Model):
if any(self.env['account.move']._field_will_change(line, vals, field_name) for field_name in protected_fields['reconciliation']):
line._check_reconciliation()
# Check switching receivable / payable accounts.
if account_to_write:
account_type = line.account_id.account_type
if line.move_id.is_sale_document(include_receipts=True):
if (account_type == 'asset_receivable' and account_to_write.account_type != account_type) \
or (account_type != 'asset_receivable' and account_to_write.account_type == 'asset_receivable'):
raise UserError(_("You can only set an account having the receivable type on payment terms lines for customer invoice."))
if line.move_id.is_purchase_document(include_receipts=True):
if (account_type == 'liability_payable' and account_to_write.account_type != account_type) \
or (account_type != 'liability_payable' and account_to_write.account_type == 'liability_payable'):
raise UserError(_("You can only set an account having the payable type on payment terms lines for vendor bill."))
move_container = {'records': self.move_id, 'self': self, 'line_to_write': line_to_write}
with self.move_id._check_balanced(move_container),\
self.move_id._sync_dynamic_lines(move_container),\
+5 -3
View File
@@ -592,12 +592,13 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
self.category = self.env.ref('product.product_category_1').copy({'name': 'Test category','property_valuation': 'real_time', 'property_cost_method': 'fifo'})
self.account_receiv = self.env['account.account'].create({'name': 'Receivable', 'code': 'RCV00', 'account_type': 'asset_receivable', 'reconcile': True})
account_expense = self.env['account.account'].create({'name': 'Expense', 'code': 'EXP00', 'account_type': 'liability_current', 'reconcile': True})
account_income = self.env['account.account'].create({'name': 'Income', 'code': 'INC00', 'account_type': 'asset_current', 'reconcile': True})
account_output = self.env['account.account'].create({'name': 'Output', 'code': 'OUT00', 'account_type': 'liability_current', 'reconcile': True})
account_valuation = self.env['account.account'].create({'name': 'Valuation', 'code': 'STV00', 'account_type': 'asset_receivable', 'reconcile': True})
self.partner.property_account_receivable_id = self.account_receiv
self.category.property_account_income_categ_id = self.account_receiv
self.category.property_account_income_categ_id = account_income
self.category.property_account_expense_categ_id = account_expense
self.category.property_stock_account_input_categ_id = self.account_receiv
self.category.property_stock_account_input_categ_id = account_income
self.category.property_stock_account_output_categ_id = account_output
self.category.property_stock_valuation_account_id = account_valuation
self.category.property_stock_journal = self.env['account.journal'].create({'name': 'Stock journal', 'type': 'sale', 'code': 'STK00'})
@@ -1800,11 +1801,12 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
self.category = self.env.ref('product.product_category_1').copy({'name': 'Test category', 'property_valuation': 'real_time', 'property_cost_method': 'fifo'})
account_receiv = self.env['account.account'].create({'name': 'Receivable', 'code': 'RCV00', 'account_type': 'asset_receivable', 'reconcile': True})
account_expense = self.env['account.account'].create({'name': 'Expense', 'code': 'EXP00', 'account_type': 'liability_current', 'reconcile': True})
account_income = self.env['account.account'].create({'name': 'Income', 'code': 'INC00', 'account_type': 'asset_current', 'reconcile': True})
account_output = self.env['account.account'].create({'name': 'Output', 'code': 'OUT00', 'account_type': 'liability_current', 'reconcile': True})
account_valuation = self.env['account.account'].create({'name': 'Valuation', 'code': 'STV00', 'account_type': 'asset_receivable', 'reconcile': True})
self.stock_location = self.company_data['default_warehouse'].lot_stock_id
self.partner.property_account_receivable_id = account_receiv
self.category.property_account_income_categ_id = account_receiv
self.category.property_account_income_categ_id = account_income
self.category.property_account_expense_categ_id = account_expense
self.category.property_stock_account_input_categ_id = account_receiv
self.category.property_stock_account_output_categ_id = account_output