when a Vendor bill in an inactive currency is created (via an an EDI or the OCR, or when the user create the invoice in an active foreign currency, then desactivate it) it should not be possible to validate it. Task ID: #2289537 closes odoo/odoo#54096 Signed-off-by: Laurent Smet <smetl@users.noreply.github.com>
770 lines
32 KiB
Python
770 lines
32 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 fields, api, SUPERUSER_ID
|
|
from odoo.exceptions import ValidationError, UserError, RedirectWarning
|
|
from odoo.tools import mute_logger
|
|
|
|
from dateutil.relativedelta import relativedelta
|
|
from functools import reduce
|
|
import json
|
|
import psycopg2
|
|
|
|
|
|
@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,
|
|
}),
|
|
]
|
|
})
|
|
|
|
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_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.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.
|
|
statement = self.env['account.bank.statement'].create({
|
|
'journal_id': self.company_data['default_journal_bank'].id,
|
|
'date': '2016-01-01',
|
|
'line_ids': [
|
|
(0, 0, {'payment_ref': 'test', 'amount': 10.0})
|
|
],
|
|
})
|
|
statement.button_post()
|
|
|
|
# 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': 1000.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.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_misc_always_balanced_move(self):
|
|
''' Ensure there is no way to make '''
|
|
# You can't remove a journal item making the journal entry unbalanced.
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
self.test_move.line_ids[0].unlink()
|
|
|
|
# Same check using write instead of unlink.
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
balance = self.test_move.line_ids[0].balance + 5
|
|
self.test_move.line_ids[0].write({
|
|
'debit': balance if balance > 0.0 else 0.0,
|
|
'credit': -balance if balance < 0.0 else 0.0,
|
|
})
|
|
|
|
# You can remove journal items if the related journal entry is still balanced.
|
|
self.test_move.line_ids.unlink()
|
|
|
|
def test_sequence_change_date(self):
|
|
# Check setup
|
|
self.assertEqual(self.test_move.state, 'draft')
|
|
self.assertEqual(self.test_move.name, 'MISC/2016/01/0001')
|
|
self.assertEqual(fields.Date.to_string(self.test_move.date), '2016-01-01')
|
|
|
|
# Never posetd, the number must change if we change the date
|
|
self.test_move.date = '2020-02-02'
|
|
self.assertEqual(self.test_move.name, 'MISC/2020/02/0001')
|
|
|
|
# We don't recompute user's input when posting
|
|
self.test_move.name = 'MyMISC/2020/0000001'
|
|
self.test_move.action_post()
|
|
self.assertEqual(self.test_move.name, 'MyMISC/2020/0000001')
|
|
|
|
# Has been posted, and it doesn't change anymore
|
|
self.test_move.button_draft()
|
|
self.test_move.date = '2020-01-02'
|
|
self.test_move.action_post()
|
|
self.assertEqual(self.test_move.name, 'MyMISC/2020/0000001')
|
|
|
|
def test_journal_sequence(self):
|
|
self.assertEqual(self.test_move.name, 'MISC/2016/01/0001')
|
|
self.test_move.action_post()
|
|
self.assertEqual(self.test_move.name, 'MISC/2016/01/0001')
|
|
|
|
copy1 = self.test_move.copy({'date': self.test_move.date})
|
|
self.assertEqual(copy1.name, '/')
|
|
copy1.action_post()
|
|
self.assertEqual(copy1.name, 'MISC/2016/01/0002')
|
|
|
|
copy2 = self.test_move.copy({'date': self.test_move.date})
|
|
new_journal = self.test_move.journal_id.copy()
|
|
new_journal.code = "MISC2"
|
|
copy2.journal_id = new_journal
|
|
self.assertEqual(copy2.name, 'MISC2/2016/01/0001')
|
|
with Form(copy2) as move_form: # It is editable in the form
|
|
move_form.name = 'MyMISC/2016/0001'
|
|
copy2.action_post()
|
|
self.assertEqual(copy2.name, 'MyMISC/2016/0001')
|
|
|
|
copy3 = copy2.copy({'date': copy2.date})
|
|
self.assertEqual(copy3.name, '/')
|
|
with self.assertRaises(AssertionError):
|
|
with Form(copy2) as move_form: # It is not editable in the form
|
|
move_form.name = 'MyMISC/2016/0002'
|
|
copy3.action_post()
|
|
self.assertEqual(copy3.name, 'MyMISC/2016/0002')
|
|
copy3.name = 'MISC2/2016/00002'
|
|
|
|
copy4 = copy2.copy({'date': copy2.date})
|
|
copy4.action_post()
|
|
self.assertEqual(copy4.name, 'MISC2/2016/00003')
|
|
|
|
copy5 = copy2.copy({'date': copy2.date})
|
|
copy5.date = '2021-02-02'
|
|
copy5.action_post()
|
|
self.assertEqual(copy5.name, 'MISC2/2021/00001')
|
|
copy5.name = 'N\'importe quoi?'
|
|
|
|
copy6 = copy5.copy({'date': copy5.date})
|
|
copy6.action_post()
|
|
self.assertEqual(copy6.name, 'N\'importe quoi?1')
|
|
|
|
def test_journal_sequence_format(self):
|
|
"""Test different format of sequences and what it becomes on another period"""
|
|
sequences = [
|
|
('JRNL/2016/00001', 'JRNL/2016/00002', 'JRNL/2016/00003', 'JRNL/2017/00001'),
|
|
('1234567', '1234568', '1234569', '1234570'),
|
|
('20190910', '20190911', '20190912', '20190913'),
|
|
('2016-0910', '2016-0911', '2016-0912', '2017-0001'),
|
|
('201603-10', '201603-11', '201604-01', '201703-01'),
|
|
('16-03-10', '16-03-11', '16-04-01', '17-03-01'),
|
|
('2016-10', '2016-11', '2016-12', '2017-01'),
|
|
('045-001-000002', '045-001-000003', '045-001-000004', '045-001-000005'),
|
|
('JRNL/2016/00001suffix', 'JRNL/2016/00002suffix', 'JRNL/2016/00003suffix', 'JRNL/2017/00001suffix'),
|
|
]
|
|
other_moves = self.env['account.move'].search([('journal_id', '=', self.test_move.journal_id.id)]) - self.test_move
|
|
other_moves.unlink() # Do not interfere when trying to get the highest name for new periods
|
|
|
|
init_move = self.test_move
|
|
next_move = init_move.copy()
|
|
next_move_month = init_move.copy()
|
|
next_move_year = init_move.copy()
|
|
init_move.date = '2016-03-12'
|
|
next_move.date = '2016-03-12'
|
|
next_move_month.date = '2016-04-12'
|
|
next_move_year.date = '2017-03-12'
|
|
next_moves = (next_move + next_move_month + next_move_year)
|
|
next_moves.action_post()
|
|
|
|
for sequence_init, sequence_next, sequence_next_month, sequence_next_year in sequences:
|
|
init_move.name = sequence_init
|
|
next_moves.name = False
|
|
next_moves._compute_name()
|
|
self.assertEqual(
|
|
[next_move.name, next_move_month.name, next_move_year.name],
|
|
[sequence_next, sequence_next_month, sequence_next_year],
|
|
)
|
|
|
|
def test_journal_next_sequence(self):
|
|
prefix = "TEST_ORDER/2016/"
|
|
self.test_move.name = f"{prefix}1"
|
|
for c in range(2, 25):
|
|
copy = self.test_move.copy({'date': self.test_move.date})
|
|
copy.name = "/"
|
|
copy.action_post()
|
|
self.assertEqual(copy.name, f"{prefix}{c}")
|
|
|
|
def test_journal_sequence_multiple_type(self):
|
|
entry, entry2, invoice, invoice2, refund, refund2 = (self.test_move.copy({'date': self.test_move.date}) for i in range(6))
|
|
(invoice + invoice2 + refund + refund2).write({
|
|
'journal_id': self.company_data['default_journal_sale'],
|
|
'partner_id': 1,
|
|
'invoice_date': '2016-01-01',
|
|
})
|
|
(invoice + invoice2).move_type = 'out_invoice'
|
|
(refund + refund2).move_type = 'out_refund'
|
|
all = (entry + entry2 + invoice + invoice2 + refund + refund2)
|
|
all.name = False
|
|
all.action_post()
|
|
self.assertEqual(entry.name, 'MISC/2016/01/0002')
|
|
self.assertEqual(entry2.name, 'MISC/2016/01/0003')
|
|
self.assertEqual(invoice.name, 'INV/2016/01/0001')
|
|
self.assertEqual(invoice2.name, 'INV/2016/01/0002')
|
|
self.assertEqual(refund.name, 'RINV/2016/01/0001')
|
|
self.assertEqual(refund2.name, 'RINV/2016/01/0002')
|
|
|
|
def test_journal_override_sequence_regex(self):
|
|
other_moves = self.env['account.move'].search([('journal_id', '=', self.test_move.journal_id.id)]) - self.test_move
|
|
other_moves.unlink() # Do not interfere when trying to get the highest name for new periods
|
|
self.test_move.date = '2020-01-01'
|
|
self.test_move.name = '00000876-G 0002/2020'
|
|
next = self.test_move.copy({'date': self.test_move.date})
|
|
next.action_post()
|
|
self.assertEqual(next.name, '00000876-G 0002/2021') # Wait, I didn't want this!
|
|
|
|
next.journal_id.sequence_override_regex = r'^(?P<seq>\d*)(?P<suffix1>.*?)(?P<year>(\d{4})?)(?P<suffix2>)$'
|
|
next.name = '/'
|
|
next._compute_name()
|
|
self.assertEqual(next.name, '00000877-G 0002/2020') # Pfew, better!
|
|
|
|
next = next = self.test_move.copy({'date': self.test_move.date})
|
|
next.date = "2017-05-02"
|
|
next.action_post()
|
|
self.assertEqual(next.name, '00000001-G 0002/2017')
|
|
|
|
def test_journal_sequence_ordering(self):
|
|
self.test_move.name = 'XMISC/2016/00001'
|
|
copies = reduce((lambda x, y: x+y), [self.test_move.copy({'date': self.test_move.date}) for i in range(6)])
|
|
|
|
copies[0].date = '2019-03-05'
|
|
copies[1].date = '2019-03-06'
|
|
copies[2].date = '2019-03-07'
|
|
copies[3].date = '2019-03-04'
|
|
copies[4].date = '2019-03-05'
|
|
copies[5].date = '2019-03-05'
|
|
# that entry is actualy the first one of the period, so it already has a name
|
|
# set it to '/' so that it is recomputed at post to be ordered correctly.
|
|
copies[0].name = '/'
|
|
copies.action_post()
|
|
|
|
# Ordered by date
|
|
self.assertEqual(copies[0].name, 'XMISC/2019/00002')
|
|
self.assertEqual(copies[1].name, 'XMISC/2019/00005')
|
|
self.assertEqual(copies[2].name, 'XMISC/2019/00006')
|
|
self.assertEqual(copies[3].name, 'XMISC/2019/00001')
|
|
self.assertEqual(copies[4].name, 'XMISC/2019/00003')
|
|
self.assertEqual(copies[5].name, 'XMISC/2019/00004')
|
|
|
|
# Can't have twice the same name
|
|
with self.assertRaises(ValidationError):
|
|
copies[0].name = 'XMISC/2019/00001'
|
|
|
|
# Lets remove the order by date
|
|
copies[0].name = 'XMISC/2019/10001'
|
|
copies[1].name = 'XMISC/2019/10002'
|
|
copies[2].name = 'XMISC/2019/10003'
|
|
copies[3].name = 'XMISC/2019/10004'
|
|
copies[4].name = 'XMISC/2019/10005'
|
|
copies[5].name = 'XMISC/2019/10006'
|
|
|
|
copies[4].button_draft()
|
|
copies[4].with_context(force_delete=True).unlink()
|
|
copies[5].button_draft()
|
|
|
|
wizard = Form(self.env['account.resequence.wizard'].with_context(active_ids=set(copies.ids) - set(copies[4].ids), active_model='account.move'))
|
|
|
|
new_values = json.loads(wizard.new_values)
|
|
self.assertEqual(new_values[str(copies[0].id)]['new_by_date'], 'XMISC/2019/10002')
|
|
self.assertEqual(new_values[str(copies[0].id)]['new_by_name'], 'XMISC/2019/10001')
|
|
|
|
self.assertEqual(new_values[str(copies[1].id)]['new_by_date'], 'XMISC/2019/10004')
|
|
self.assertEqual(new_values[str(copies[1].id)]['new_by_name'], 'XMISC/2019/10002')
|
|
|
|
self.assertEqual(new_values[str(copies[2].id)]['new_by_date'], 'XMISC/2019/10005')
|
|
self.assertEqual(new_values[str(copies[2].id)]['new_by_name'], 'XMISC/2019/10003')
|
|
|
|
self.assertEqual(new_values[str(copies[3].id)]['new_by_date'], 'XMISC/2019/10001')
|
|
self.assertEqual(new_values[str(copies[3].id)]['new_by_name'], 'XMISC/2019/10004')
|
|
|
|
self.assertEqual(new_values[str(copies[5].id)]['new_by_date'], 'XMISC/2019/10003')
|
|
self.assertEqual(new_values[str(copies[5].id)]['new_by_name'], 'XMISC/2019/10005')
|
|
|
|
wizard.save().resequence()
|
|
|
|
self.assertEqual(copies[3].state, 'posted')
|
|
self.assertEqual(copies[5].name, 'XMISC/2019/10005')
|
|
self.assertEqual(copies[5].state, 'draft')
|
|
|
|
def test_sequence_concurency(self):
|
|
with self.env.registry.cursor() as cr0,\
|
|
self.env.registry.cursor() as cr1,\
|
|
self.env.registry.cursor() as cr2:
|
|
env0 = api.Environment(cr0, SUPERUSER_ID, {})
|
|
env1 = api.Environment(cr1, SUPERUSER_ID, {})
|
|
env2 = api.Environment(cr2, SUPERUSER_ID, {})
|
|
|
|
journal = env0['account.journal'].create({
|
|
'name': 'concurency_test',
|
|
'code': 'CT',
|
|
'type': 'general',
|
|
})
|
|
account = env0['account.account'].create({
|
|
'code': 'CT',
|
|
'name': 'CT',
|
|
'user_type_id': env0.ref('account.data_account_type_fixed_assets').id,
|
|
})
|
|
moves = env0['account.move'].create([{
|
|
'journal_id': journal.id,
|
|
'date': fields.Date.from_string('2016-01-01'),
|
|
'line_ids': [(0, 0, {'name': 'name', 'account_id': account.id})]
|
|
}] * 3)
|
|
moves.name = '/'
|
|
moves[0].action_post()
|
|
self.assertEqual(moves.mapped('name'), ['CT/2016/01/0001', '/', '/'])
|
|
env0.cr.commit()
|
|
|
|
# start the transactions here on cr2 to simulate concurency with cr1
|
|
env2.cr.execute('SELECT 1')
|
|
|
|
move = env1['account.move'].browse(moves[1].id)
|
|
move.action_post()
|
|
env1.cr.commit()
|
|
|
|
move = env2['account.move'].browse(moves[2].id)
|
|
with self.assertRaises(psycopg2.OperationalError), env2.cr.savepoint(), mute_logger('odoo.sql_db'):
|
|
move.action_post()
|
|
|
|
self.assertEqual(moves.mapped('name'), ['CT/2016/01/0001', 'CT/2016/01/0002', '/'])
|
|
moves.button_draft()
|
|
moves.posted_before = False
|
|
moves.unlink()
|
|
journal.unlink()
|
|
account.unlink()
|
|
env0.cr.commit()
|
|
|
|
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()
|
|
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,
|
|
},
|
|
],
|
|
)
|
|
|
|
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)
|
|
|
|
self.assertTrue(debit_line.recompute_tax_line)
|
|
|
|
# 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, [
|
|
{'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):
|
|
# You cannot remove journal items if the related journal entry is posted.
|
|
self.test_move.action_post()
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
self.test_move.line_ids.unlink()
|
|
|
|
# You can remove journal items if the related journal entry is draft.
|
|
self.test_move.button_draft()
|
|
self.test_move.line_ids.unlink()
|
|
|
|
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': 'in_invoice',
|
|
'partner_id': self.partner_a.id,
|
|
'invoice_date': fields.Date.from_string('2019-01-01'),
|
|
'currency_id': self.currency_data['currency'].id,
|
|
'invoice_payment_term_id': self.pay_terms_a.id,
|
|
'invoice_line_ids': [{}]
|
|
})
|
|
|
|
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')
|