summaryrefslogtreecommitdiff
path: root/addons/account/tests/test_sequence_mixin.py
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/account/tests/test_sequence_mixin.py
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/account/tests/test_sequence_mixin.py')
-rw-r--r--addons/account/tests/test_sequence_mixin.py396
1 files changed, 396 insertions, 0 deletions
diff --git a/addons/account/tests/test_sequence_mixin.py b/addons/account/tests/test_sequence_mixin.py
new file mode 100644
index 00000000..6280533b
--- /dev/null
+++ b/addons/account/tests/test_sequence_mixin.py
@@ -0,0 +1,396 @@
+# -*- coding: utf-8 -*-
+from odoo.addons.account.tests.common import AccountTestInvoicingCommon
+from odoo.tests import tagged
+from odoo.tests.common import Form
+from odoo import fields, api, SUPERUSER_ID
+from odoo.exceptions import ValidationError
+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 TestSequenceMixin(AccountTestInvoicingCommon):
+
+ @classmethod
+ def setUpClass(cls, chart_template_ref=None):
+ super().setUpClass(chart_template_ref=chart_template_ref)
+ cls.test_move = cls.create_move()
+
+ @classmethod
+ def create_move(cls, move_type=None, date=None, journal=None, name=None, post=False):
+ move = cls.env['account.move'].create({
+ 'move_type': move_type or 'entry',
+ 'date': date or '2016-01-01',
+ 'line_ids': [
+ (0, None, {
+ 'name': 'line',
+ 'account_id': cls.company_data['default_account_revenue'].id,
+ }),
+ ]
+ })
+ if journal:
+ move.name = False
+ move.journal_id = journal
+ if name:
+ move.name = name
+ if post:
+ move.action_post()
+ return move
+
+ def test_sequence_change_date(self):
+ """Change the sequence when we change the date iff it has never been posted."""
+ # 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.create_move(date=self.test_move.date)
+ self.assertEqual(copy1.name, '/')
+ copy1.action_post()
+ self.assertEqual(copy1.name, 'MISC/2016/01/0002')
+
+ copy2 = self.create_move(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
+ with mute_logger('odoo.tests.common.onchange'):
+ move_form.name = 'MyMISC/2016/0001'
+ self.assertIn(
+ 'The sequence will restart at 1 at the start of every year',
+ move_form._perform_onchange(['name'])['warning']['message'],
+ )
+ move_form.journal_id = self.test_move.journal_id
+ self.assertEqual(move_form.name, '/')
+ move_form.journal_id = new_journal
+ self.assertEqual(move_form.name, 'MISC2/2016/01/0001')
+ with mute_logger('odoo.tests.common.onchange'):
+ move_form.name = 'MyMISC/2016/0001'
+ self.assertIn(
+ 'The sequence will restart at 1 at the start of every year',
+ move_form._perform_onchange(['name'])['warning']['message'],
+ )
+ copy2.action_post()
+ self.assertEqual(copy2.name, 'MyMISC/2016/0001')
+
+ copy3 = self.create_move(date=copy2.date, journal=new_journal)
+ 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 = self.create_move(date=copy2.date, journal=new_journal)
+ copy4.action_post()
+ self.assertEqual(copy4.name, 'MISC2/2016/00003')
+
+ copy5 = self.create_move(date=copy2.date, journal=new_journal)
+ copy5.date = '2021-02-02'
+ copy5.action_post()
+ self.assertEqual(copy5.name, 'MISC2/2021/00001')
+ copy5.name = 'N\'importe quoi?'
+
+ copy6 = self.create_move(date=copy5.date, journal=new_journal)
+ 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'),
+ ]
+
+ init_move = self.create_move(date='2016-03-12')
+ next_move = self.create_move(date='2016-03-12')
+ next_move_month = self.create_move(date='2016-04-12')
+ next_move_year = self.create_move(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):
+ """Sequences behave correctly even when there is not enough padding."""
+ prefix = "TEST_ORDER/2016/"
+ self.test_move.name = f"{prefix}1"
+ for c in range(2, 25):
+ copy = self.create_move(date=self.test_move.date)
+ copy.name = "/"
+ copy.action_post()
+ self.assertEqual(copy.name, f"{prefix}{c}")
+
+ def test_journal_sequence_multiple_type(self):
+ """Domain is computed accordingly to different types."""
+ entry, entry2, invoice, invoice2, refund, refund2 = (
+ self.create_move(date='2016-01-01')
+ 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_sequence_groupby_compute(self):
+ """The grouping optimization is correctly done."""
+ # Setup two journals with a sequence that resets yearly
+ journals = self.env['account.journal'].create([{
+ 'name': f'Journal{i}',
+ 'code': f'J{i}',
+ 'type': 'general',
+ } for i in range(2)])
+ account = self.env['account.account'].search([], limit=1)
+ moves = self.env['account.move'].create([{
+ 'journal_id': journals[i].id,
+ 'line_ids': [(0, 0, {'account_id': account.id, 'name': 'line'})],
+ 'date': '2010-01-01',
+ } for i in range(2)])._post()
+ for i in range(2):
+ moves[i].name = f'J{i}/2010/00001'
+
+ # Check that the moves are correctly batched
+ moves = self.env['account.move'].create([{
+ 'journal_id': journals[journal_index].id,
+ 'line_ids': [(0, 0, {'account_id': account.id, 'name': 'line'})],
+ 'date': f'2010-{month}-01',
+ } for journal_index, month in [(1, 1), (0, 1), (1, 2), (1, 1)]])._post()
+ self.assertEqual(
+ moves.mapped('name'),
+ ['J1/2010/00002', 'J0/2010/00002', 'J1/2010/00004', 'J1/2010/00003'],
+ )
+
+ journals[0].code = 'OLD'
+ journals.flush()
+ journal_same_code = self.env['account.journal'].create([{
+ 'name': 'Journal0',
+ 'code': 'J0',
+ 'type': 'general',
+ }])
+ moves = (
+ self.create_move(date='2010-01-01', journal=journal_same_code, name='J0/2010/00001')
+ + self.create_move(date='2010-01-01', journal=journal_same_code)
+ + self.create_move(date='2010-01-01', journal=journal_same_code)
+ + self.create_move(date='2010-01-01', journal=journals[0])
+ )._post()
+ self.assertEqual(
+ moves.mapped('name'),
+ ['J0/2010/00001', 'J0/2010/00002', 'J0/2010/00003', 'J0/2010/00003'],
+ )
+
+ def test_journal_override_sequence_regex(self):
+ """There is a possibility to override the regex and change the order of the paramters."""
+ self.create_move(date='2020-01-01', name='00000876-G 0002/2020')
+ next = self.create_move(date='2020-01-01')
+ next.action_post()
+ self.assertEqual(next.name, '00000876-G 0002/2021') # Wait, I didn't want this!
+
+ next.button_draft()
+ next.name = False
+ next.journal_id.sequence_override_regex = r'^(?P<seq>\d*)(?P<suffix1>.*?)(?P<year>(\d{4})?)(?P<suffix2>)$'
+ next.action_post()
+ self.assertEqual(next.name, '00000877-G 0002/2020') # Pfew, better!
+ next = self.create_move(date='2020-01-01')
+ next.action_post()
+ self.assertEqual(next.name, '00000878-G 0002/2020')
+
+ next = self.create_move(date='2017-05-02')
+ next.action_post()
+ self.assertEqual(next.name, '00000001-G 0002/2017')
+
+ def test_journal_sequence_ordering(self):
+ """Entries are correctly sorted when posting multiple at once."""
+ self.test_move.name = 'XMISC/2016/00001'
+ copies = reduce((lambda x, y: x+y), [
+ self.create_move(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_get_more_specific(self):
+ """There is the ability to change the format (i.e. from yearly to montlhy)."""
+ def test_date(date, name):
+ test = self.create_move(date=date)
+ test.action_post()
+ self.assertEqual(test.name, name)
+
+ def set_sequence(date, name):
+ return self.create_move(date=date, name=name)._post()
+
+ # Start with a continuous sequence
+ self.test_move.name = 'MISC/00001'
+
+ # Change the prefix to reset every year starting in 2017
+ new_year = set_sequence(self.test_move.date + relativedelta(years=1), 'MISC/2017/00001')
+
+ # Change the prefix to reset every month starting in February 2017
+ new_month = set_sequence(new_year.date + relativedelta(months=1), 'MISC/2017/02/00001')
+
+ test_date(self.test_move.date, 'MISC/00002') # Keep the old prefix in 2016
+ test_date(new_year.date, 'MISC/2017/00002') # Keep the new prefix in 2017
+ test_date(new_month.date, 'MISC/2017/02/00002') # Keep the new prefix in February 2017
+
+ # Change the prefix to never reset (again) year starting in 2018 (Please don't do that)
+ reset_never = set_sequence(self.test_move.date + relativedelta(years=2), 'MISC/00100')
+ test_date(reset_never.date, 'MISC/00101') # Keep the new prefix in 2018
+
+ def test_sequence_concurency(self):
+ """Computing the same name in concurent transactions is not allowed."""
+ 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()