1207 lines
51 KiB
Python
1207 lines
51 KiB
Python
# -*- coding: utf-8 -*-
|
|
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
|
|
from odoo.tests import tagged, new_test_user
|
|
from odoo.tests.common import Form
|
|
from odoo import Command, fields
|
|
from odoo.exceptions import UserError, RedirectWarning
|
|
|
|
from dateutil.relativedelta import relativedelta
|
|
from freezegun import freeze_time
|
|
from collections import defaultdict
|
|
|
|
@tagged('post_install', '-at_install')
|
|
class TestAccountMove(AccountTestInvoicingCommon):
|
|
|
|
@classmethod
|
|
def setUpClass(cls, chart_template_ref=None):
|
|
super().setUpClass(chart_template_ref=chart_template_ref)
|
|
|
|
tax_repartition_line = cls.company_data['default_tax_sale'].refund_repartition_line_ids\
|
|
.filtered(lambda line: line.repartition_type == 'tax')
|
|
cls.test_move = cls.env['account.move'].create({
|
|
'move_type': 'entry',
|
|
'date': fields.Date.from_string('2016-01-01'),
|
|
'line_ids': [
|
|
(0, None, {
|
|
'name': 'revenue line 1',
|
|
'account_id': cls.company_data['default_account_revenue'].id,
|
|
'debit': 500.0,
|
|
'credit': 0.0,
|
|
}),
|
|
(0, None, {
|
|
'name': 'revenue line 2',
|
|
'account_id': cls.company_data['default_account_revenue'].id,
|
|
'debit': 1000.0,
|
|
'credit': 0.0,
|
|
'tax_ids': [(6, 0, cls.company_data['default_tax_sale'].ids)],
|
|
}),
|
|
(0, None, {
|
|
'name': 'tax line',
|
|
'account_id': cls.company_data['default_account_tax_sale'].id,
|
|
'debit': 150.0,
|
|
'credit': 0.0,
|
|
'tax_repartition_line_id': tax_repartition_line.id,
|
|
}),
|
|
(0, None, {
|
|
'name': 'counterpart line',
|
|
'account_id': cls.company_data['default_account_expense'].id,
|
|
'debit': 0.0,
|
|
'credit': 1650.0,
|
|
}),
|
|
]
|
|
})
|
|
cls.entry_line_vals_1 = {
|
|
'name': 'Line 1',
|
|
'account_id': cls.company_data['default_account_revenue'].id,
|
|
'debit': 500.0,
|
|
'credit': 0.0,
|
|
}
|
|
cls.entry_line_vals_2 = {
|
|
'name': 'Line 2',
|
|
'account_id': cls.company_data['default_account_expense'].id,
|
|
'debit': 0.0,
|
|
'credit': 500.0,
|
|
}
|
|
|
|
def test_out_invoice_auto_post_at_date(self):
|
|
# Create auto-posted (but not recurring) entry
|
|
nb_invoices = self.env['account.move'].search_count(domain=[])
|
|
self.test_move.auto_post = 'at_date'
|
|
self.test_move.date = fields.Date.today()
|
|
with freeze_time(self.test_move.date - relativedelta(days=1)):
|
|
self.env.ref('account.ir_cron_auto_post_draft_entry').method_direct_trigger()
|
|
self.assertEqual(self.test_move.state, 'draft') # can't be posted before its date
|
|
with freeze_time(self.test_move.date + relativedelta(days=1)):
|
|
self.env.ref('account.ir_cron_auto_post_draft_entry').method_direct_trigger()
|
|
self.assertEqual(self.test_move.state, 'posted') # can be posted after its date
|
|
self.assertEqual(nb_invoices, self.env['account.move'].search_count(domain=[]))
|
|
|
|
def test_posting_future_invoice_fails(self):
|
|
# Create auto-posted, recurring entry, attempt manually posting it
|
|
self.test_move.date = fields.Date.today() + relativedelta(days=1)
|
|
self.test_move.auto_post = 'quarterly'
|
|
self.test_move._post() # default soft=True parameter filters out future moves
|
|
self.assertEqual(self.test_move.state, 'draft')
|
|
with self.assertRaisesRegex(UserError, "This move is configured to be auto-posted"):
|
|
self.test_move._post(soft=False)
|
|
|
|
def test_out_invoice_auto_post_monthly(self):
|
|
# Create auto-posted entry, recurring monthly until two months later
|
|
prev_invoices = self.env['account.move'].search(domain=[])
|
|
self.test_move.auto_post = 'monthly'
|
|
self.test_move.auto_post_until = fields.Date.from_string('2022-02-28')
|
|
date = fields.Date.from_string('2021-12-30')
|
|
self.test_move.invoice_date = date
|
|
self.test_move.date = date # invoice_date's onchange does not trigger from code
|
|
self.test_move.invoice_date_due = date + relativedelta(days=1)
|
|
|
|
self.env.ref('account.ir_cron_auto_post_draft_entry').method_direct_trigger() # first recurrence
|
|
new_invoices_1 = self.env['account.move'].search(domain=[]) - prev_invoices
|
|
new_date_1 = fields.Date.from_string('2022-01-30')
|
|
self.assertEqual(self.test_move.state, 'posted')
|
|
self.assertEqual(1, len(new_invoices_1)) # following entry is created
|
|
self.assertEqual('monthly', new_invoices_1.auto_post)
|
|
self.assertEqual(new_date_1, new_invoices_1.date)
|
|
self.assertEqual(new_date_1 + relativedelta(days=1), new_invoices_1.invoice_date_due) # due date maintains delta with date
|
|
|
|
self.env.ref('account.ir_cron_auto_post_draft_entry').method_direct_trigger() # second recurrence
|
|
new_invoices_2 = self.env['account.move'].search(domain=[]) - prev_invoices - new_invoices_1
|
|
new_date_2 = fields.Date.from_string('2022-02-28')
|
|
self.assertEqual(new_invoices_1.state, 'posted')
|
|
self.assertEqual(1, len(new_invoices_2))
|
|
self.assertEqual('monthly', new_invoices_2.auto_post)
|
|
self.assertEqual(new_date_2, new_invoices_2.date) # date does not overflow because of shorter month
|
|
self.assertEqual(new_date_2 + relativedelta(days=1), new_invoices_2.invoice_date_due)
|
|
self.assertEqual(new_invoices_2.invoice_user_id, self.test_move.invoice_user_id)
|
|
|
|
self.env.ref('account.ir_cron_auto_post_draft_entry').method_direct_trigger() # no more recurrences
|
|
new_invoices_3 = self.env['account.move'].search(domain=[]) - prev_invoices - new_invoices_1 - new_invoices_2
|
|
self.assertEqual(0, len(new_invoices_3))
|
|
|
|
def test_custom_currency_on_account_1(self):
|
|
custom_account = self.company_data['default_account_revenue'].copy()
|
|
|
|
# The currency set on the account is not the same as the one set on the company.
|
|
# It should raise an error.
|
|
custom_account.currency_id = self.currency_data['currency']
|
|
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
self.test_move.line_ids[0].account_id = custom_account
|
|
|
|
# The currency set on the account is the same as the one set on the company.
|
|
# It should not raise an error.
|
|
custom_account.currency_id = self.company_data['currency']
|
|
|
|
self.test_move.line_ids[0].account_id = custom_account
|
|
|
|
def test_fiscal_position_multicompany(self):
|
|
"""A move is assigned a fiscal position that matches its own company."""
|
|
company1 = self.company_data["company"]
|
|
company2 = self.company_data_2["company"]
|
|
partner = self.env['res.partner'].create({'name': 'Belouga'})
|
|
fpos1 = self.env["account.fiscal.position"].create(
|
|
{
|
|
"name": company1.name,
|
|
"company_id": company1.id,
|
|
}
|
|
)
|
|
fpos2 = self.env["account.fiscal.position"].create(
|
|
{
|
|
"name": company2.name,
|
|
"company_id": company2.id,
|
|
}
|
|
)
|
|
partner.with_company(company1).property_account_position_id = fpos1
|
|
partner.with_company(company2).property_account_position_id = fpos2
|
|
self.test_move.sudo().with_company(company2).partner_id = partner
|
|
self.assertEqual(self.test_move.fiscal_position_id, fpos1)
|
|
|
|
def test_misc_fiscalyear_lock_date_1(self):
|
|
self.test_move.action_post()
|
|
|
|
# Set the lock date after the journal entry date.
|
|
self.test_move.company_id.fiscalyear_lock_date = fields.Date.from_string('2017-01-01')
|
|
|
|
# lines[0] = 'counterpart line'
|
|
# lines[1] = 'tax line'
|
|
# lines[2] = 'revenue line 1'
|
|
# lines[3] = 'revenue line 2'
|
|
lines = self.test_move.line_ids.sorted('debit')
|
|
|
|
# Editing the reference should be allowed.
|
|
self.test_move.ref = 'whatever'
|
|
|
|
# Try to edit a line into a locked fiscal year.
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
self.test_move.write({
|
|
'line_ids': [
|
|
(1, lines[0].id, {'credit': lines[0].credit + 100.0}),
|
|
(1, lines[2].id, {'debit': lines[2].debit + 100.0}),
|
|
],
|
|
})
|
|
|
|
# Try to edit the account of a line.
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
self.test_move.line_ids[0].write({'account_id': self.test_move.line_ids[0].account_id.copy().id})
|
|
|
|
# Try to edit a line.
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
self.test_move.write({
|
|
'line_ids': [
|
|
(1, lines[0].id, {'credit': lines[0].credit + 100.0}),
|
|
(1, lines[3].id, {'debit': lines[3].debit + 100.0}),
|
|
],
|
|
})
|
|
|
|
# Try to add a new tax on a line.
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
self.test_move.write({
|
|
'line_ids': [
|
|
(1, lines[2].id, {'tax_ids': [(6, 0, self.company_data['default_tax_purchase'].ids)]}),
|
|
],
|
|
})
|
|
|
|
# Try to create a new line.
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
self.test_move.write({
|
|
'line_ids': [
|
|
(1, lines[0].id, {'credit': lines[0].credit + 100.0}),
|
|
(0, None, {
|
|
'name': 'revenue line 1',
|
|
'account_id': self.company_data['default_account_revenue'].id,
|
|
'debit': 100.0,
|
|
'credit': 0.0,
|
|
}),
|
|
],
|
|
})
|
|
|
|
# You can't remove the journal entry from a locked period.
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
self.test_move.date = fields.Date.from_string('2018-01-01')
|
|
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
self.test_move.name = "Othername"
|
|
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
self.test_move.unlink()
|
|
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
self.test_move.button_draft()
|
|
|
|
# Try to add a new journal entry prior to the lock date.
|
|
copy_move = self.test_move.copy({'date': '2017-01-01'})
|
|
# The date has been changed to the first valid date.
|
|
self.assertEqual(copy_move.date, copy_move.company_id.fiscalyear_lock_date + relativedelta(days=1))
|
|
|
|
def test_misc_fiscalyear_lock_date_2(self):
|
|
self.test_move.action_post()
|
|
|
|
# Create a bank statement to get a balance in the suspense account.
|
|
self.env['account.bank.statement.line'].create({
|
|
'journal_id': self.company_data['default_journal_bank'].id,
|
|
'date': '2016-01-01',
|
|
'payment_ref': 'test',
|
|
'amount': 10.0,
|
|
})
|
|
|
|
# You can't lock the fiscal year if there is some unreconciled statement.
|
|
with self.assertRaises(RedirectWarning), self.cr.savepoint():
|
|
self.test_move.company_id.fiscalyear_lock_date = fields.Date.from_string('2017-01-01')
|
|
|
|
def test_misc_tax_lock_date_1(self):
|
|
self.test_move.action_post()
|
|
|
|
# Set the tax lock date after the journal entry date.
|
|
self.test_move.company_id.tax_lock_date = fields.Date.from_string('2017-01-01')
|
|
|
|
# lines[0] = 'counterpart line'
|
|
# lines[1] = 'tax line'
|
|
# lines[2] = 'revenue line 1'
|
|
# lines[3] = 'revenue line 2'
|
|
lines = self.test_move.line_ids.sorted('debit')
|
|
|
|
# Try to edit a line not affecting the taxes.
|
|
self.test_move.write({
|
|
'line_ids': [
|
|
(1, lines[0].id, {'credit': lines[0].credit + 100.0}),
|
|
(1, lines[2].id, {'debit': lines[2].debit + 100.0}),
|
|
],
|
|
})
|
|
|
|
# Try to edit the account of a line.
|
|
self.test_move.line_ids[0].write({'account_id': self.test_move.line_ids[0].account_id.copy().id})
|
|
|
|
# Try to edit a line having some taxes.
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
self.test_move.write({
|
|
'line_ids': [
|
|
(1, lines[0].id, {'credit': lines[0].credit + 100.0}),
|
|
(1, lines[3].id, {'debit': lines[3].debit + 100.0}),
|
|
],
|
|
})
|
|
|
|
# Try to add a new tax on a line.
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
self.test_move.write({
|
|
'line_ids': [
|
|
(1, lines[2].id, {'tax_ids': [(6, 0, self.company_data['default_tax_purchase'].ids)]}),
|
|
],
|
|
})
|
|
|
|
# Try to edit a tax line.
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
self.test_move.write({
|
|
'line_ids': [
|
|
(1, lines[0].id, {'credit': lines[0].credit + 100.0}),
|
|
(1, lines[1].id, {'debit': lines[1].debit + 100.0}),
|
|
],
|
|
})
|
|
|
|
# Try to create a line not affecting the taxes.
|
|
self.test_move.write({
|
|
'line_ids': [
|
|
(1, lines[0].id, {'credit': lines[0].credit + 100.0}),
|
|
(0, None, {
|
|
'name': 'revenue line 1',
|
|
'account_id': self.company_data['default_account_revenue'].id,
|
|
'debit': 100.0,
|
|
'credit': 0.0,
|
|
}),
|
|
],
|
|
})
|
|
|
|
# Try to create a line affecting the taxes.
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
self.test_move.write({
|
|
'line_ids': [
|
|
(1, lines[0].id, {'credit': lines[0].credit + 100.0}),
|
|
(0, None, {
|
|
'name': 'revenue line 2',
|
|
'account_id': self.company_data['default_account_revenue'].id,
|
|
'debit': 100.0,
|
|
'credit': 0.0,
|
|
'tax_ids': [(6, 0, self.company_data['default_tax_sale'].ids)],
|
|
}),
|
|
],
|
|
})
|
|
|
|
# You can't remove the journal entry from a locked period.
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
self.test_move.date = fields.Date.from_string('2018-01-01')
|
|
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
self.test_move.name = "Othername"
|
|
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
self.test_move.unlink()
|
|
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
self.test_move.button_draft()
|
|
|
|
copy_move = self.test_move.copy({'date': self.test_move.date})
|
|
|
|
# /!\ The date is changed automatically to the next available one during the post.
|
|
copy_move.action_post()
|
|
|
|
# You can't change the date to one being in a locked period.
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
copy_move.date = fields.Date.from_string('2017-01-01')
|
|
|
|
def test_misc_draft_reconciled_entries_1(self):
|
|
draft_moves = self.env['account.move'].create([
|
|
{
|
|
'move_type': 'entry',
|
|
'line_ids': [
|
|
(0, None, {
|
|
'name': 'move 1 receivable line',
|
|
'account_id': self.company_data['default_account_receivable'].id,
|
|
'debit': 1000.0,
|
|
'credit': 0.0,
|
|
}),
|
|
(0, None, {
|
|
'name': 'move 1 counterpart line',
|
|
'account_id': self.company_data['default_account_expense'].id,
|
|
'debit': 0.0,
|
|
'credit': 1000.0,
|
|
}),
|
|
]
|
|
},
|
|
{
|
|
'move_type': 'entry',
|
|
'line_ids': [
|
|
(0, None, {
|
|
'name': 'move 2 receivable line',
|
|
'account_id': self.company_data['default_account_receivable'].id,
|
|
'debit': 0.0,
|
|
'credit': 2000.0,
|
|
}),
|
|
(0, None, {
|
|
'name': 'move 2 counterpart line',
|
|
'account_id': self.company_data['default_account_expense'].id,
|
|
'debit': 2000.0,
|
|
'credit': 0.0,
|
|
}),
|
|
]
|
|
},
|
|
])
|
|
|
|
# lines[0] = 'move 2 receivable line'
|
|
# lines[1] = 'move 1 counterpart line'
|
|
# lines[2] = 'move 1 receivable line'
|
|
# lines[3] = 'move 2 counterpart line'
|
|
draft_moves.action_post()
|
|
lines = draft_moves.mapped('line_ids').sorted('balance')
|
|
|
|
(lines[0] + lines[2]).reconcile()
|
|
|
|
# You can't write something impacting the reconciliation on an already reconciled line.
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
draft_moves[0].write({
|
|
'line_ids': [
|
|
(1, lines[1].id, {'credit': lines[1].credit + 100.0}),
|
|
(1, lines[2].id, {'debit': lines[2].debit + 100.0}),
|
|
]
|
|
})
|
|
|
|
# The write must not raise anything because the rounding of the monetary field should ignore such tiny amount.
|
|
draft_moves[0].write({
|
|
'line_ids': [
|
|
(1, lines[1].id, {'credit': lines[1].credit + 0.0000001}),
|
|
(1, lines[2].id, {'debit': lines[2].debit + 0.0000001}),
|
|
]
|
|
})
|
|
|
|
# You can't unlink an already reconciled line.
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
draft_moves.unlink()
|
|
|
|
def test_add_followers_on_post(self):
|
|
# Add some existing partners, some from another company
|
|
company = self.env['res.company'].create({'name': 'Oopo'})
|
|
company.flush_recordset()
|
|
existing_partners = self.env['res.partner'].create([{
|
|
'name': 'Jean',
|
|
'company_id': company.id,
|
|
},{
|
|
'name': 'Paulus',
|
|
}])
|
|
self.test_move.message_subscribe(existing_partners.ids)
|
|
|
|
user = new_test_user(self.env, login='jag', groups='account.group_account_invoice')
|
|
|
|
move = self.test_move.with_user(user)
|
|
partner = self.env['res.partner'].create({'name': 'Belouga'})
|
|
move.partner_id = partner
|
|
|
|
move.action_post()
|
|
self.assertEqual(move.message_partner_ids, self.env.user.partner_id | existing_partners | partner)
|
|
|
|
def test_misc_move_onchange(self):
|
|
''' Test the behavior on onchanges for account.move having 'entry' as type. '''
|
|
|
|
move_form = Form(self.env['account.move'])
|
|
# Rate 1:3
|
|
move_form.date = fields.Date.from_string('2016-01-01')
|
|
|
|
# New line that should get 400.0 as debit.
|
|
with move_form.line_ids.new() as line_form:
|
|
line_form.name = 'debit_line'
|
|
line_form.account_id = self.company_data['default_account_revenue']
|
|
line_form.currency_id = self.currency_data['currency']
|
|
line_form.amount_currency = 1200.0
|
|
|
|
# New line that should get 400.0 as credit.
|
|
with move_form.line_ids.new() as line_form:
|
|
line_form.name = 'credit_line'
|
|
line_form.account_id = self.company_data['default_account_revenue']
|
|
line_form.currency_id = self.currency_data['currency']
|
|
line_form.amount_currency = -1200.0
|
|
move = move_form.save()
|
|
|
|
self.assertRecordValues(
|
|
move.line_ids.sorted('debit'),
|
|
[
|
|
{
|
|
'currency_id': self.currency_data['currency'].id,
|
|
'amount_currency': -1200.0,
|
|
'debit': 0.0,
|
|
'credit': 400.0,
|
|
},
|
|
{
|
|
'currency_id': self.currency_data['currency'].id,
|
|
'amount_currency': 1200.0,
|
|
'debit': 400.0,
|
|
'credit': 0.0,
|
|
},
|
|
],
|
|
)
|
|
|
|
# Change the date to change the currency conversion's rate
|
|
with Form(move) as move_form:
|
|
move_form.date = fields.Date.from_string('2017-01-01')
|
|
|
|
self.assertRecordValues(
|
|
move.line_ids.sorted('debit'),
|
|
[
|
|
{
|
|
'currency_id': self.currency_data['currency'].id,
|
|
'amount_currency': -1200.0,
|
|
'debit': 0.0,
|
|
'credit': 600.0,
|
|
},
|
|
{
|
|
'currency_id': self.currency_data['currency'].id,
|
|
'amount_currency': 1200.0,
|
|
'debit': 600.0,
|
|
'credit': 0.0,
|
|
},
|
|
],
|
|
)
|
|
# You can change the balance manually without changing the currency amount
|
|
with Form(move) as move_form:
|
|
with move_form.line_ids.edit(0) as line_form:
|
|
line_form.debit = 200
|
|
with move_form.line_ids.edit(1) as line_form:
|
|
line_form.credit = 200
|
|
|
|
self.assertRecordValues(
|
|
move.line_ids.sorted('debit'),
|
|
[
|
|
{
|
|
'currency_id': self.currency_data['currency'].id,
|
|
'amount_currency': -1200.0,
|
|
'debit': 0.0,
|
|
'credit': 200.0,
|
|
},
|
|
{
|
|
'currency_id': self.currency_data['currency'].id,
|
|
'amount_currency': 1200.0,
|
|
'debit': 200.0,
|
|
'credit': 0.0,
|
|
},
|
|
],
|
|
)
|
|
|
|
def test_included_tax(self):
|
|
'''
|
|
Test an account.move.line is created automatically when adding a tax.
|
|
This test uses the following scenario:
|
|
- Create manually a debit line of 1000 having an included tax.
|
|
- Assume a line containing the tax amount is created automatically.
|
|
- Create manually a credit line to balance the two previous lines.
|
|
- Save the move.
|
|
|
|
included tax = 20%
|
|
|
|
Name | Debit | Credit | Tax_ids | Tax_line_id's name
|
|
-----------------------|-----------|-----------|---------------|-------------------
|
|
debit_line_1 | 1000 | | tax |
|
|
included_tax_line | 200 | | | included_tax_line
|
|
credit_line_1 | | 1200 | |
|
|
'''
|
|
|
|
self.included_percent_tax = self.env['account.tax'].create({
|
|
'name': 'included_tax_line',
|
|
'amount_type': 'percent',
|
|
'amount': 20,
|
|
'price_include': True,
|
|
'include_base_amount': False,
|
|
})
|
|
self.account = self.company_data['default_account_revenue']
|
|
|
|
move_form = Form(self.env['account.move'].with_context(default_move_type='entry'))
|
|
|
|
# Create a new account.move.line with debit amount.
|
|
with move_form.line_ids.new() as debit_line:
|
|
debit_line.name = 'debit_line_1'
|
|
debit_line.account_id = self.account
|
|
debit_line.debit = 1000
|
|
debit_line.tax_ids.clear()
|
|
debit_line.tax_ids.add(self.included_percent_tax)
|
|
|
|
# Create a third account.move.line with credit amount.
|
|
with move_form.line_ids.new() as credit_line:
|
|
credit_line.name = 'credit_line_1'
|
|
credit_line.account_id = self.account
|
|
credit_line.credit = 1200
|
|
|
|
move = move_form.save()
|
|
|
|
self.assertRecordValues(move.line_ids.sorted(lambda x: -x.balance), [
|
|
{'name': 'debit_line_1', 'debit': 1000.0, 'credit': 0.0, 'tax_ids': [self.included_percent_tax.id], 'tax_line_id': False},
|
|
{'name': 'included_tax_line', 'debit': 200.0, 'credit': 0.0, 'tax_ids': [], 'tax_line_id': self.included_percent_tax.id},
|
|
{'name': 'credit_line_1', 'debit': 0.0, 'credit': 1200.0, 'tax_ids': [], 'tax_line_id': False},
|
|
])
|
|
|
|
def test_misc_prevent_unlink_posted_items(self):
|
|
def unlink_posted_items():
|
|
self.test_move.line_ids.filtered(lambda l: not l.tax_repartition_line_id).balance = 0
|
|
self.test_move.line_ids[0].unlink()
|
|
|
|
# You cannot remove journal items if the related journal entry is posted.
|
|
self.test_move.action_post()
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
unlink_posted_items()
|
|
|
|
# You can remove journal items if the related journal entry is draft.
|
|
self.test_move.button_draft()
|
|
unlink_posted_items()
|
|
|
|
def test_account_move_inactive_currency_raise_error_on_post(self):
|
|
""" Ensure a move cannot be posted when using an inactive currency """
|
|
move = self.env['account.move'].create({
|
|
'move_type': 'entry',
|
|
'partner_id': self.partner_a.id,
|
|
'date': fields.Date.from_string('2019-01-01'),
|
|
'currency_id': self.currency_data['currency'].id,
|
|
'line_ids': [
|
|
(0, None, self.entry_line_vals_1),
|
|
(0, None, self.entry_line_vals_2),
|
|
],
|
|
})
|
|
|
|
move.currency_id.active = False
|
|
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
move.action_post()
|
|
|
|
# Make sure that the invoice can still be posted when the currency is active
|
|
move.action_activate_currency()
|
|
move.action_post()
|
|
|
|
self.assertEqual(move.state, 'posted')
|
|
|
|
def test_entry_reverse_storno(self):
|
|
# Test creating journal entries and reverting them
|
|
# while in Storno accounting
|
|
self.env.company.account_storno = True
|
|
|
|
move = self.env['account.move'].create({
|
|
'move_type': 'entry',
|
|
'date': fields.Date.from_string('2021-01-01'),
|
|
'line_ids': [
|
|
(0, None, self.entry_line_vals_1),
|
|
(0, None, self.entry_line_vals_2),
|
|
]
|
|
})
|
|
move.action_post()
|
|
|
|
move_reversal = self.env['account.move.reversal'].with_context(active_model="account.move", active_ids=move.ids).create({
|
|
'date': fields.Date.from_string('2021-02-01'),
|
|
'journal_id': move.journal_id.id,
|
|
})
|
|
reversal = move_reversal.refund_moves()
|
|
reversed_move = self.env['account.move'].browse(reversal['res_id'])
|
|
self.assertRecordValues(reversed_move.line_ids, [
|
|
{
|
|
**self.entry_line_vals_1,
|
|
'debit': 0.0,
|
|
'credit': 500.0,
|
|
}, {
|
|
**self.entry_line_vals_2,
|
|
'debit': 500.0,
|
|
'credit': 0.0,
|
|
}
|
|
])
|
|
|
|
reversed_move.is_storno = True
|
|
|
|
self.assertRecordValues(reversed_move.line_ids, [
|
|
{
|
|
**self.entry_line_vals_1,
|
|
'debit': -500.0,
|
|
'credit': 0.0,
|
|
}, {
|
|
**self.entry_line_vals_2,
|
|
'debit': 0.0,
|
|
'credit': -500.0,
|
|
}
|
|
])
|
|
|
|
def test_invoice_like_entry_reverse_caba(self):
|
|
tax_waiting_account = self.env['account.account'].create({
|
|
'name': 'TAX_WAIT',
|
|
'code': 'TWAIT',
|
|
'account_type': 'liability_current',
|
|
'reconcile': True,
|
|
'company_id': self.company_data['company'].id,
|
|
})
|
|
tax_final_account = self.env['account.account'].create({
|
|
'name': 'TAX_TO_DEDUCT',
|
|
'code': 'TDEDUCT',
|
|
'account_type': 'asset_current',
|
|
'company_id': self.company_data['company'].id,
|
|
})
|
|
tax_base_amount_account = self.env['account.account'].create({
|
|
'name': 'TAX_BASE',
|
|
'code': 'TBASE',
|
|
'account_type': 'asset_current',
|
|
'company_id': self.company_data['company'].id,
|
|
})
|
|
self.env.company.account_cash_basis_base_account_id = tax_base_amount_account
|
|
self.env.company.tax_exigibility = True
|
|
tax_tags = defaultdict(dict)
|
|
for line_type, repartition_type in [(l, r) for l in ('invoice', 'refund') for r in ('base', 'tax')]:
|
|
tax_tags[line_type][repartition_type] = self.env['account.account.tag'].create({
|
|
'name': '%s %s tag' % (line_type, repartition_type),
|
|
'applicability': 'taxes',
|
|
'country_id': self.env.ref('base.us').id,
|
|
})
|
|
tax = self.env['account.tax'].create({
|
|
'name': 'cash basis 10%',
|
|
'type_tax_use': 'sale',
|
|
'amount': 10,
|
|
'tax_exigibility': 'on_payment',
|
|
'cash_basis_transition_account_id': tax_waiting_account.id,
|
|
'invoice_repartition_line_ids': [
|
|
(0, 0, {
|
|
'repartition_type': 'base',
|
|
'tag_ids': [(6, 0, tax_tags['invoice']['base'].ids)],
|
|
}),
|
|
(0, 0, {
|
|
'repartition_type': 'tax',
|
|
'account_id': tax_final_account.id,
|
|
'tag_ids': [(6, 0, tax_tags['invoice']['tax'].ids)],
|
|
}),
|
|
],
|
|
'refund_repartition_line_ids': [
|
|
(0, 0, {
|
|
'repartition_type': 'base',
|
|
'tag_ids': [(6, 0, tax_tags['refund']['base'].ids)],
|
|
}),
|
|
(0, 0, {
|
|
'repartition_type': 'tax',
|
|
'account_id': tax_final_account.id,
|
|
'tag_ids': [(6, 0, tax_tags['refund']['tax'].ids)],
|
|
}),
|
|
],
|
|
})
|
|
move = self.env['account.move'].create({
|
|
'move_type': 'entry',
|
|
'date': fields.Date.from_string('2016-01-01'),
|
|
'line_ids': [
|
|
(0, None, {
|
|
'name': 'revenue line',
|
|
'account_id': self.company_data['default_account_revenue'].id,
|
|
'debit': 0.0,
|
|
'credit': 1000.0,
|
|
'tax_ids': [(6, 0, tax.ids)],
|
|
'tax_tag_ids': [(6, 0, tax_tags['invoice']['base'].ids)],
|
|
}),
|
|
(0, None, {
|
|
'name': 'tax line 1',
|
|
'account_id': tax_waiting_account.id,
|
|
'debit': 0.0,
|
|
'credit': 100.0,
|
|
'tax_tag_ids': [(6, 0, tax_tags['invoice']['tax'].ids)],
|
|
'tax_repartition_line_id': tax.invoice_repartition_line_ids.filtered(lambda x: x.repartition_type == 'tax').id,
|
|
}),
|
|
(0, None, {
|
|
'name': 'counterpart line',
|
|
'account_id': self.company_data['default_account_receivable'].id,
|
|
'debit': 1100.0,
|
|
'credit': 0.0,
|
|
}),
|
|
]
|
|
})
|
|
move.action_post()
|
|
# make payment
|
|
payment = self.env['account.payment'].create({
|
|
'payment_type': 'inbound',
|
|
'payment_method_id': self.env.ref('account.account_payment_method_manual_in').id,
|
|
'partner_type': 'customer',
|
|
'partner_id': self.partner_a.id,
|
|
'amount': 1100,
|
|
'date': move.date,
|
|
'journal_id': self.company_data['default_journal_bank'].id,
|
|
})
|
|
payment.action_post()
|
|
(payment.move_id + move).line_ids.filtered(lambda x: x.account_id == self.company_data['default_account_receivable']).reconcile()
|
|
# check caba move
|
|
partial_rec = move.mapped('line_ids.matched_credit_ids')
|
|
caba_move = self.env['account.move'].search([('tax_cash_basis_rec_id', '=', partial_rec.id)])
|
|
expected_values = [
|
|
{
|
|
'tax_line_id': False,
|
|
'tax_repartition_line_id': False,
|
|
'tax_ids': [],
|
|
'tax_tag_ids': [],
|
|
'account_id': tax_base_amount_account.id,
|
|
'debit': 1000.0,
|
|
'credit': 0.0,
|
|
},
|
|
{
|
|
'tax_line_id': False,
|
|
'tax_repartition_line_id': False,
|
|
'tax_ids': tax.ids,
|
|
'tax_tag_ids': tax_tags['invoice']['base'].ids,
|
|
'account_id': tax_base_amount_account.id,
|
|
'debit': 0.0,
|
|
'credit': 1000.0,
|
|
},
|
|
|
|
{
|
|
'tax_line_id': False,
|
|
'tax_repartition_line_id': False,
|
|
'tax_ids': [],
|
|
'tax_tag_ids': [],
|
|
'account_id': tax_waiting_account.id,
|
|
'debit': 100.0,
|
|
'credit': 0.0,
|
|
},
|
|
{
|
|
'tax_line_id': tax.id,
|
|
'tax_repartition_line_id': tax.invoice_repartition_line_ids.filtered(lambda x: x.repartition_type == 'tax').id,
|
|
'tax_ids': [],
|
|
'tax_tag_ids': tax_tags['invoice']['tax'].ids,
|
|
'account_id': tax_final_account.id,
|
|
'debit': 0.0,
|
|
'credit': 100.0,
|
|
},
|
|
]
|
|
self.assertRecordValues(caba_move.line_ids, expected_values)
|
|
# unreconcile
|
|
debit_aml = move.line_ids.filtered('debit')
|
|
debit_aml.remove_move_reconcile()
|
|
# check caba move reverse is same as caba move with only debit/credit inverted
|
|
reversed_caba_move = self.env['account.move'].search([('reversed_entry_id', '=', caba_move.id)])
|
|
for value in expected_values:
|
|
value.update({
|
|
'debit': value['credit'],
|
|
'credit': value['debit'],
|
|
})
|
|
self.assertRecordValues(reversed_caba_move.line_ids, expected_values)
|
|
|
|
def _get_cache_count(self, model_name='account.move', field_name='name'):
|
|
model = self.env[model_name]
|
|
field = model._fields[field_name]
|
|
return len(self.env.cache.get_records(model, field))
|
|
|
|
def test_cache_invalidation(self):
|
|
self.env.invalidate_all()
|
|
lines = self.test_move.line_ids
|
|
# prefetch
|
|
lines.mapped('move_id.name')
|
|
# check account.move cache
|
|
self.assertEqual(self._get_cache_count(), 1)
|
|
lines.invalidate_recordset()
|
|
self.assertEqual(self._get_cache_count(), 0)
|
|
|
|
def test_misc_prevent_edit_tax_on_posted_moves(self):
|
|
# You cannot remove journal items if the related journal entry is posted.
|
|
def edit_tax_on_posted_moves():
|
|
self.test_move.line_ids.filtered(lambda l: l.tax_ids).write({
|
|
'balance': 1000.0,
|
|
'tax_ids': False,
|
|
})
|
|
|
|
self.test_move.action_post()
|
|
with self.assertRaisesRegex(UserError, "You cannot modify the taxes related to a posted journal item"),\
|
|
self.cr.savepoint():
|
|
edit_tax_on_posted_moves()
|
|
|
|
with self.assertRaisesRegex(UserError, "You cannot modify the taxes related to a posted journal item"),\
|
|
self.cr.savepoint():
|
|
self.test_move.line_ids.filtered(lambda l: l.tax_line_id).tax_line_id = False
|
|
|
|
# You can remove journal items if the related journal entry is draft.
|
|
self.test_move.button_draft()
|
|
edit_tax_on_posted_moves()
|
|
|
|
def test_misc_tax_autobalance(self):
|
|
# Saving an unbalanced entry isn't something desired but we need this piece of code to work in order to support
|
|
# the tax auto-calculation on miscellaneous move. Indeed, the JS class `AutosaveMany2ManyTagsField` triggers the
|
|
# saving of the record as soon as a tax base_line is modified.
|
|
move = self.env["account.move"].create({
|
|
"move_type": "entry",
|
|
"line_ids": [
|
|
Command.create({
|
|
"name": "revenue line",
|
|
"account_id": self.company_data["default_account_revenue"].id,
|
|
'tax_ids': [Command.set(self.company_data['default_tax_sale'].ids)],
|
|
"balance": -10.0,
|
|
}),
|
|
]
|
|
})
|
|
tax_line = move.line_ids.filtered("tax_ids")
|
|
tax_line.unlink()
|
|
|
|
# But creating unbalanced misc entry shouldn't be allowed otherwise
|
|
with self.assertRaisesRegex(UserError, r"The move \(.*\) is not balanced\."):
|
|
self.env["account.move"].create({
|
|
"move_type": "entry",
|
|
"line_ids": [
|
|
Command.create({
|
|
"name": "revenue line",
|
|
"account_id": self.company_data["default_account_revenue"].id,
|
|
"balance": -10.0,
|
|
}),
|
|
]
|
|
})
|
|
|
|
def test_reset_draft_exchange_move(self):
|
|
""" Ensure you can't reset to draft an exchange journal entry """
|
|
moves = self.env['account.move'].create([
|
|
{
|
|
'date': '2016-01-01',
|
|
'line_ids': [
|
|
Command.create({
|
|
'name': "line1",
|
|
'account_id': self.company_data['default_account_receivable'].id,
|
|
'currency_id': self.currency_data['currency'].id,
|
|
'balance': 400.0,
|
|
'amount_currency': 1200.0,
|
|
}),
|
|
Command.create({
|
|
'name': "line2",
|
|
'account_id': self.company_data['default_account_expense'].id,
|
|
'balance': -400.0,
|
|
}),
|
|
]
|
|
},
|
|
{
|
|
'date': '2017-01-01',
|
|
'line_ids': [
|
|
Command.create({
|
|
'name': "line1",
|
|
'account_id': self.company_data['default_account_receivable'].id,
|
|
'currency_id': self.currency_data['currency'].id,
|
|
'balance': -600.0,
|
|
'amount_currency': -1200.0,
|
|
}),
|
|
Command.create({
|
|
'name': "line2",
|
|
'account_id': self.company_data['default_account_expense'].id,
|
|
'balance': 600.0,
|
|
}),
|
|
]
|
|
},
|
|
])
|
|
moves.action_post()
|
|
|
|
moves.line_ids\
|
|
.filtered(lambda x: x.account_id == self.company_data['default_account_receivable'])\
|
|
.reconcile()
|
|
|
|
exchange_diff = moves.line_ids.matched_debit_ids.exchange_move_id
|
|
self.assertTrue(exchange_diff)
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
exchange_diff.button_draft()
|
|
|
|
def test_always_exigible_caba_account(self):
|
|
""" Always exigible misc operations (so, the ones without payable/receivable line) with cash basis
|
|
taxes should see their tax lines use the final tax account, not the transition account.
|
|
"""
|
|
tax_account = self.company_data['default_account_tax_sale']
|
|
|
|
caba_tax = self.env['account.tax'].create({
|
|
'name': "CABA",
|
|
'amount_type': 'percent',
|
|
'amount': 20.0,
|
|
'tax_exigibility': 'on_payment',
|
|
'cash_basis_transition_account_id': self.safe_copy(tax_account).id,
|
|
'invoice_repartition_line_ids': [
|
|
(0, 0, {
|
|
'repartition_type': 'base',
|
|
}),
|
|
(0, 0, {
|
|
'repartition_type': 'tax',
|
|
'account_id': tax_account.id,
|
|
}),
|
|
],
|
|
'refund_repartition_line_ids': [
|
|
(0, 0, {
|
|
'repartition_type': 'base',
|
|
}),
|
|
(0, 0, {
|
|
'repartition_type': 'tax',
|
|
'account_id': tax_account.id,
|
|
}),
|
|
],
|
|
})
|
|
|
|
move_form = Form(self.env['account.move'].with_context(default_move_type='entry'))
|
|
|
|
# Create a new account.move.line with debit amount.
|
|
income_account = self.company_data['default_account_revenue']
|
|
with move_form.line_ids.new() as debit_line:
|
|
debit_line.name = 'debit'
|
|
debit_line.account_id = income_account
|
|
debit_line.debit = 120
|
|
|
|
with move_form.line_ids.new() as credit_line:
|
|
credit_line.name = 'credit'
|
|
credit_line.account_id = income_account
|
|
credit_line.credit = 100
|
|
credit_line.tax_ids.clear()
|
|
credit_line.tax_ids.add(caba_tax)
|
|
|
|
move = move_form.save()
|
|
|
|
self.assertTrue(move.always_tax_exigible)
|
|
|
|
self.assertRecordValues(move.line_ids.sorted(lambda x: -x.balance), [
|
|
# pylint: disable=C0326
|
|
{'name': 'debit', 'debit': 120.0, 'credit': 0.0, 'account_id': income_account.id, 'tax_ids': [], 'tax_line_id': False},
|
|
{'name': 'CABA', 'debit': 0.0, 'credit': 20.0, 'account_id': tax_account.id, 'tax_ids': [], 'tax_line_id': caba_tax.id},
|
|
{'name': 'credit', 'debit': 0.0, 'credit': 100.0, 'account_id': income_account.id, 'tax_ids': caba_tax.ids, 'tax_line_id': False},
|
|
])
|
|
|
|
def test_misc_with_taxes_reverse(self):
|
|
test_account = self.company_data['default_account_revenue']
|
|
|
|
# With a sale tax
|
|
sale_tax = self.company_data['default_tax_sale']
|
|
|
|
move_form = Form(self.env['account.move'])
|
|
|
|
with move_form.line_ids.new() as debit_line_form:
|
|
debit_line_form.name = 'debit'
|
|
debit_line_form.account_id = test_account
|
|
debit_line_form.debit = 115
|
|
|
|
with move_form.line_ids.new() as credit_line_form:
|
|
credit_line_form.name = 'credit'
|
|
credit_line_form.account_id = test_account
|
|
credit_line_form.credit = 100
|
|
credit_line_form.tax_ids.clear()
|
|
credit_line_form.tax_ids.add(sale_tax)
|
|
|
|
sale_move = move_form.save()
|
|
|
|
sale_invoice_rep_line = sale_tax.invoice_repartition_line_ids.filtered(lambda x: x.repartition_type == 'tax')
|
|
|
|
self.assertRecordValues(sale_move.line_ids.sorted(lambda x: -x.balance), [
|
|
# pylint: disable=C0326
|
|
{'name': 'debit', 'debit': 115.0, 'credit': 0.0, 'account_id': test_account.id, 'tax_ids': [], 'tax_base_amount': 0, 'tax_tag_invert': False, 'tax_repartition_line_id': False},
|
|
{'name': '15%', 'debit': 0.0, 'credit': 15.0, 'account_id': self.company_data['default_account_tax_sale'].id, 'tax_ids': [], 'tax_base_amount': 100, 'tax_tag_invert': True, 'tax_repartition_line_id': sale_invoice_rep_line.id},
|
|
{'name': 'credit', 'debit': 0.0, 'credit': 100.0, 'account_id': test_account.id, 'tax_ids': sale_tax.ids, 'tax_base_amount': 0, 'tax_tag_invert': True, 'tax_repartition_line_id': False},
|
|
])
|
|
|
|
# Same with a purchase tax
|
|
purchase_tax = self.company_data['default_tax_purchase']
|
|
|
|
move_form = Form(self.env['account.move'])
|
|
|
|
with move_form.line_ids.new() as credit_line_form:
|
|
credit_line_form.name = 'credit'
|
|
credit_line_form.account_id = test_account
|
|
credit_line_form.credit = 115
|
|
|
|
with move_form.line_ids.new() as debit_line_form:
|
|
debit_line_form.name = 'debit'
|
|
debit_line_form.account_id = test_account
|
|
debit_line_form.debit = 100
|
|
debit_line_form.tax_ids.clear()
|
|
debit_line_form.tax_ids.add(purchase_tax)
|
|
|
|
purchase_move = move_form.save()
|
|
|
|
purchase_invoice_rep_line = purchase_tax.invoice_repartition_line_ids.filtered(lambda x: x.repartition_type == 'tax')
|
|
self.assertRecordValues(purchase_move.line_ids.sorted(lambda x: x.balance), [
|
|
# pylint: disable=C0326
|
|
{'name': 'credit', 'credit': 115.0, 'debit': 0.0, 'account_id': test_account.id, 'tax_ids': [], 'tax_base_amount': 0, 'tax_tag_invert': False, 'tax_repartition_line_id': False},
|
|
{'name': '15%', 'credit': 0.0, 'debit': 15.0, 'account_id': self.company_data['default_account_tax_purchase'].id, 'tax_ids': [], 'tax_base_amount': 100, 'tax_tag_invert': False, 'tax_repartition_line_id': purchase_invoice_rep_line.id},
|
|
{'name': 'debit', 'credit': 0.0, 'debit': 100.0, 'account_id': test_account.id, 'tax_ids': purchase_tax.ids, 'tax_base_amount': 0, 'tax_tag_invert': False, 'tax_repartition_line_id': False},
|
|
])
|
|
|
|
@freeze_time('2021-10-01 00:00:00')
|
|
def test_change_journal_account_move(self):
|
|
"""Changing the journal should change the name of the move"""
|
|
journal = self.env['account.journal'].create({
|
|
'name': 'awesome journal',
|
|
'type': 'general',
|
|
'code': 'AJ',
|
|
})
|
|
move = self.env['account.move'].with_context(default_move_type='entry')
|
|
with Form(move) as move_form:
|
|
self.assertEqual(move_form.name, 'MISC/2021/10/0001')
|
|
move_form.journal_id, journal = journal, move_form.journal_id
|
|
self.assertEqual(move_form.name, 'AJ/2021/10/0001')
|
|
# ensure we aren't burning any sequence by switching journal
|
|
move_form.journal_id, journal = journal, move_form.journal_id
|
|
self.assertEqual(move_form.name, 'MISC/2021/10/0001')
|
|
move_form.journal_id, journal = journal, move_form.journal_id
|
|
self.assertEqual(move_form.name, 'AJ/2021/10/0001')
|
|
|
|
def test_manually_modifying_taxes(self):
|
|
"""Manually modifying taxes on a move should not automatically recompute them"""
|
|
move = self.env['account.move'].create({
|
|
'move_type': 'entry',
|
|
'line_ids': [
|
|
Command.create({
|
|
'name': 'Receivable',
|
|
'account_id': self.company_data['default_account_receivable'].id,
|
|
'debit': 0.0,
|
|
'credit': 5531.04,
|
|
}),
|
|
Command.create({
|
|
'name': 'Revenue',
|
|
'account_id': self.company_data['default_account_revenue'].id,
|
|
'tax_ids': [Command.set(self.company_data['default_tax_sale'].ids)],
|
|
'debit': 4809.61,
|
|
'credit': 0.0,
|
|
}),
|
|
]
|
|
})
|
|
tax_line = move.line_ids.filtered('tax_repartition_line_id')
|
|
self.assertEqual(tax_line.debit, 721.44)
|
|
with Form(move) as move_form:
|
|
with move_form.line_ids.edit(2) as line_form:
|
|
line_form.debit = 721.43
|
|
move_form.line_ids.remove(3)
|
|
move = move_form.save()
|
|
tax_line = move.line_ids.filtered('tax_repartition_line_id')
|
|
self.assertEqual(tax_line.debit, 721.43)
|
|
|
|
def test_account_root_multiple_companies(self):
|
|
account = self.env['account.account'].create({
|
|
'name': 'account',
|
|
'code': 'ZZ',
|
|
'account_type': 'asset_current',
|
|
'company_id': self.env.company.id,
|
|
})
|
|
other_company = self.env['res.company'].create({'name': 'other company'})
|
|
self.env['account.account'].create({
|
|
'name': 'other account',
|
|
'code': 'ZZ',
|
|
'account_type': 'asset_current',
|
|
'company_id': other_company.id,
|
|
})
|
|
self.env['account.move'].create({
|
|
'move_type': 'entry',
|
|
'date': fields.Date.from_string('2016-01-01'),
|
|
'line_ids': [
|
|
(0, None, {
|
|
'name': 'revenue line 1',
|
|
'account_id': account.id,
|
|
'debit': 500.0,
|
|
'credit': 0.0,
|
|
}),
|
|
(0, None, {
|
|
'name': 'revenue line 1',
|
|
'account_id': self.company_data['default_account_tax_sale'].id,
|
|
'debit': 0.0,
|
|
'credit': 500.0,
|
|
}),
|
|
]
|
|
})
|
|
balance = self.env["account.move.line"].read_group(
|
|
[("account_id", "=", account.id)], ["balance:sum"], ["account_root_id"]
|
|
)[0]["balance"]
|
|
self.assertEqual(balance, 500)
|
|
|
|
def test_line_steal(self):
|
|
honest_move = self.env['account.move'].create({
|
|
'line_ids': [
|
|
Command.create({
|
|
'name': 'receivable',
|
|
'account_id': self.company_data['default_account_receivable'].id,
|
|
'balance': 500.0,
|
|
}),
|
|
Command.create({
|
|
'name': 'tax',
|
|
'account_id': self.company_data['default_account_tax_sale'].id,
|
|
'balance': -500.0,
|
|
}),
|
|
]
|
|
})
|
|
honest_move.action_post()
|
|
|
|
with self.assertRaisesRegex(UserError, 'not balanced'), self.env.cr.savepoint():
|
|
self.env['account.move'].create({'line_ids': [Command.set(honest_move.line_ids[0].ids)]})
|
|
|
|
with self.assertRaisesRegex(UserError, 'not balanced'), self.env.cr.savepoint():
|
|
self.env['account.move'].create({'line_ids': [Command.link(honest_move.line_ids[0].id)]})
|
|
|
|
stealer_move = self.env['account.move'].create({})
|
|
with self.assertRaisesRegex(UserError, 'not balanced'), self.env.cr.savepoint():
|
|
stealer_move.write({'line_ids': [Command.set(honest_move.line_ids[0].ids)]})
|
|
|
|
with self.assertRaisesRegex(UserError, 'not balanced'), self.env.cr.savepoint():
|
|
stealer_move.write({'line_ids': [Command.link(honest_move.line_ids[0].id)]})
|
|
|
|
def test_validate_move_wizard_with_auto_post_entry(self):
|
|
""" Test that the wizard to validate a move with auto_post is working fine. """
|
|
self.test_move.date = fields.Date.today() + relativedelta(months=3)
|
|
self.test_move.auto_post = 'at_date'
|
|
wizard = self.env['validate.account.move'].with_context(active_model='account.move', active_ids=self.test_move.ids).create({})
|
|
wizard.force_post = True
|
|
wizard.validate_move()
|
|
self.assertTrue(self.test_move.state == 'posted')
|
|
|
|
def test_cumulated_balance(self):
|
|
move = self.env['account.move'].create({
|
|
'line_ids': [Command.create({
|
|
'balance': 100,
|
|
'account_id': self.company_data['default_account_receivable'].id,
|
|
}), Command.create({
|
|
'balance': 100,
|
|
'account_id': self.company_data['default_account_tax_sale'].id,
|
|
}), Command.create({
|
|
'balance': -200,
|
|
'account_id': self.company_data['default_account_revenue'].id,
|
|
})]
|
|
})
|
|
|
|
for order, expected in [
|
|
('balance DESC', [
|
|
(100, 0),
|
|
(100, -100),
|
|
(-200, -200),
|
|
]),
|
|
('balance ASC', [
|
|
(-200, 0),
|
|
(100, 200),
|
|
(100, 100),
|
|
]),
|
|
]:
|
|
read_results = self.env['account.move.line'].search_read(
|
|
domain=[('move_id', '=', move.id)],
|
|
fields=['balance', 'cumulated_balance'],
|
|
order=order,
|
|
)
|
|
for (balance, cumulated_balance), read_result in zip(expected, read_results):
|
|
self.assertAlmostEqual(balance, read_result['balance'])
|
|
self.assertAlmostEqual(cumulated_balance, read_result['cumulated_balance'])
|