From 5459aa785c2a042baa7161b7a3556c350f0e4161 Mon Sep 17 00:00:00 2001
From: "Christihan Laurel [Vauxoo]"
Date: Tue, 21 Jan 2025 08:14:48 +0000
Subject: [PATCH] [MIG] account_move_name_sequence: Migration to 18.0
- Rename the method `_fetch_duplicate_supplier_reference` to
`_fetch_duplicate_reference` to include both sale and purchase
documents.
- Refactor tests to use `SetUpClass`, update the creation of payments
with a related journal entry, and include the deletion of payments
when deleting the moves.
- Patch the `_validate_fields` and `_hash_moves` methods in tests to
simulate the same behavior as if the journal entries are unlocked.
This is done because, in the pipeline, the
`account_journal_restrict_mode` module is installed, and it locks
posted entries, preventing them from being set to draft or deleted
afterward.
- Add additional tests for the sequence prefix to increase coverage.
Co-authored-by: Bert Van Groenendael
Co-authored-by: oury.balde
---
account_move_name_sequence/README.rst | 48 ++++----
account_move_name_sequence/__manifest__.py | 2 +-
.../models/account_journal.py | 27 +++--
.../models/account_move.py | 9 +-
.../readme/CONTRIBUTORS.md | 2 +
.../static/description/index.html | 21 ++--
.../test_account_incoming_supplier_invoice.py | 23 +++-
.../tests/test_account_move_name_seq.py | 98 +++++++++++----
.../tests/test_sequence_concurrency.py | 114 +++++++++++-------
9 files changed, 230 insertions(+), 114 deletions(-)
diff --git a/account_move_name_sequence/README.rst b/account_move_name_sequence/README.rst
index 287e3bedbc1..b2125b86841 100644
--- a/account_move_name_sequence/README.rst
+++ b/account_move_name_sequence/README.rst
@@ -17,13 +17,13 @@ Account Move Number Sequence
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Faccount--financial--tools-lightgray.png?logo=github
- :target: https://github.com/OCA/account-financial-tools/tree/17.0/account_move_name_sequence
+ :target: https://github.com/OCA/account-financial-tools/tree/18.0/account_move_name_sequence
:alt: OCA/account-financial-tools
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
- :target: https://translation.odoo-community.org/projects/account-financial-tools-17-0/account-financial-tools-17-0-account_move_name_sequence
+ :target: https://translation.odoo-community.org/projects/account-financial-tools-18-0/account-financial-tools-18-0-account_move_name_sequence
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
- :target: https://runboat.odoo-community.org/builds?repo=OCA/account-financial-tools&target_branch=17.0
+ :target: https://runboat.odoo-community.org/builds?repo=OCA/account-financial-tools&target_branch=18.0
:alt: Try me on Runboat
|badge1| |badge2| |badge3| |badge4| |badge5|
@@ -37,16 +37,16 @@ the same journal is computed by a complex piece of code that guesses the
format of the journal entry number from the number of the journal entry
which was manually entered by the user. It has several drawbacks:
-- the available options for the sequence are limited,
-- it is not possible to configure the sequence in advance before the
- deployment in production,
-- as it is error-prone, they added a *Resequence* wizard to re-generate
- the journal entry numbers, which can be considered as illegal in many
- countries,
-- the `piece of
- code `__
- that handles this is not easy to understand and quite difficult to
- debug.
+- the available options for the sequence are limited,
+- it is not possible to configure the sequence in advance before the
+ deployment in production,
+- as it is error-prone, they added a *Resequence* wizard to re-generate
+ the journal entry numbers, which can be considered as illegal in many
+ countries,
+- the `piece of
+ code `__
+ that handles this is not easy to understand and quite difficult to
+ debug.
Using this module, you can configure what kind of documents the gap
sequence may be relaxed And even if you must use no-gap in your company
@@ -98,7 +98,7 @@ Bug Tracker
Bugs are tracked on `GitHub Issues `_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
-`feedback `_.
+`feedback `_.
Do not contact contributors directly about support or help with technical issues.
@@ -114,18 +114,22 @@ Authors
Contributors
------------
-- `Akretion `__:
+- `Akretion `__:
- - Alexis de Lattre
+ - Alexis de Lattre
-- `Vauxoo `__:
+- `Vauxoo `__:
- - Moisés López
- - Francisco Luna
+ - Moisés López
+ - Francisco Luna
-- `Factor Libre `__:
+- `Factor Libre `__:
- - Rodrigo Bonilla Martinez
+ - Rodrigo Bonilla Martinez
+
+- `Dynapps `__:
+
+ - Bert Van Groenendael
Maintainers
-----------
@@ -154,6 +158,6 @@ Current `maintainers `__:
|maintainer-alexis-via| |maintainer-moylop260| |maintainer-luisg123v|
-This module is part of the `OCA/account-financial-tools `_ project on GitHub.
+This module is part of the `OCA/account-financial-tools `_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
diff --git a/account_move_name_sequence/__manifest__.py b/account_move_name_sequence/__manifest__.py
index 1267e3e5ac6..59dba2dabe4 100644
--- a/account_move_name_sequence/__manifest__.py
+++ b/account_move_name_sequence/__manifest__.py
@@ -7,7 +7,7 @@
{
"name": "Account Move Number Sequence",
- "version": "17.0.1.0.5",
+ "version": "18.0.1.0.0",
"category": "Accounting",
"license": "AGPL-3",
"summary": "Generate journal entry number from sequence",
diff --git a/account_move_name_sequence/models/account_journal.py b/account_move_name_sequence/models/account_journal.py
index f90a5252838..0880175111f 100644
--- a/account_move_name_sequence/models/account_journal.py
+++ b/account_move_name_sequence/models/account_journal.py
@@ -7,7 +7,7 @@
import logging
-from odoo import _, api, fields, models
+from odoo import api, fields, models
from odoo.exceptions import ValidationError
_logger = logging.getLogger(__name__)
@@ -30,7 +30,8 @@ class AccountJournal(models.Model):
copy=False,
check_company=True,
domain="[('company_id', '=', company_id)]",
- help="This sequence will be used to generate the journal entry number for refunds.",
+ help="This sequence will be used to generate "
+ "the journal entry number for refunds.",
)
# Redefine the default to True as <=v13.0
refund_sequence = fields.Boolean(default=True)
@@ -47,14 +48,14 @@ def _check_journal_sequence(self):
and journal.refund_sequence_id == journal.sequence_id
):
raise ValidationError(
- _(
+ self.env._(
"On journal '%s', the same sequence is used as "
"Entry Sequence and Credit Note Entry Sequence.",
journal.display_name,
)
)
if journal.sequence_id and not journal.sequence_id.company_id:
- msg = _(
+ msg = self.env._(
"The company is not set on sequence '%(sequence)s' configured on "
"journal '%(journal)s'.",
sequence=journal.sequence_id.display_name,
@@ -62,7 +63,7 @@ def _check_journal_sequence(self):
)
raise ValidationError(msg)
if journal.refund_sequence_id and not journal.refund_sequence_id.company_id:
- msg = _(
+ msg = self.env._(
"The company is not set on sequence '%(sequence)s' configured as "
"credit note sequence of journal '%(journal)s'.",
sequence=journal.refund_sequence_id.display_name,
@@ -89,7 +90,8 @@ def _prepare_sequence(self, vals, refund=False):
prefix = "{}{}/%(range_year)s/".format(refund and "R" or "", code)
seq_vals = {
"name": "{}{}".format(
- vals.get("name", _("Sequence")), refund and " " + _("Refund") or ""
+ vals.get("name", self.env._("Sequence")),
+ refund and " " + self.env._("Refund") or "",
),
"company_id": vals.get("company_id") or self.env.company.id,
"implementation": "no_gap",
@@ -129,7 +131,7 @@ def _prepare_sequence_current_moves(self, refund=False):
"Using default values.".format(self.id, refund and "refund" or "")
)
if not last_move:
- _logger.warning("%s %s", msg_err, "No moves found")
+ _logger.debug("%s %s", msg_err, "No moves found")
return {}
try:
with self.env.cr.savepoint():
@@ -188,10 +190,15 @@ def _prepare_sequence_current_moves(self, refund=False):
if prefix3
else "''"
)
- select_max_number = f"MAX(split_part(name, '{prefixes[-1]}', {prefixes.count(prefixes[-1]) + 1})::INTEGER) AS max_number"
+ select_max_number = (
+ f"MAX(split_part(name, '{prefixes[-1]}', "
+ f"{prefixes.count(prefixes[-1]) + 1}):"
+ f":INTEGER) AS max_number"
+ )
query = (
- f"SELECT {select_year}, {select_month}, {select_max_number} FROM account_move "
- "WHERE name LIKE %s AND journal_id=%s GROUP BY 1,2"
+ f"SELECT {select_year}, {select_month}, "
+ f"{select_max_number} FROM account_move "
+ f"WHERE name LIKE %s AND journal_id=%s GROUP BY 1,2"
)
# It is not using user input
diff --git a/account_move_name_sequence/models/account_move.py b/account_move_name_sequence/models/account_move.py
index 1514b6cb892..db0d669dd5c 100644
--- a/account_move_name_sequence/models/account_move.py
+++ b/account_move_name_sequence/models/account_move.py
@@ -69,11 +69,13 @@ def _is_end_of_seq_chain(self):
return False
return super(AccountMove, invoices_other_sequences)._is_end_of_seq_chain()
- def _fetch_duplicate_supplier_reference(self, only_posted=False):
- moves = self.filtered(lambda m: m.is_purchase_document() and m.ref)
+ def _fetch_duplicate_reference(self, matching_states=("draft", "posted")):
+ moves = self.filtered(
+ lambda m: m.is_sale_document() or m.is_purchase_document() and m.ref
+ )
if moves:
self.flush_model(["name", "journal_id", "move_type", "state"])
- return super()._fetch_duplicate_supplier_reference(only_posted=only_posted)
+ return super()._fetch_duplicate_reference(matching_states=matching_states)
def _get_last_sequence(self, relaxed=False, with_prefix=None):
return super()._get_last_sequence(relaxed, None)
@@ -88,6 +90,7 @@ def _post(self, soft=True):
self.flush_recordset()
return super()._post(soft=soft)
+ @api.depends()
def _compute_name(self):
"""Overwrite account module method in order to
avoid side effect if legacy code call it directly
diff --git a/account_move_name_sequence/readme/CONTRIBUTORS.md b/account_move_name_sequence/readme/CONTRIBUTORS.md
index 588d502ea68..0b7337f04ff 100644
--- a/account_move_name_sequence/readme/CONTRIBUTORS.md
+++ b/account_move_name_sequence/readme/CONTRIBUTORS.md
@@ -5,3 +5,5 @@
- Francisco Luna \<\>
- [Factor Libre](https://www.factorlibre.com):
- Rodrigo Bonilla Martinez \<\>
+- [Dynapps](https://www.dynapps.eu):
+ - Bert Van Groenendael \<\>
diff --git a/account_move_name_sequence/static/description/index.html b/account_move_name_sequence/static/description/index.html
index ae898e4d968..dc889670624 100644
--- a/account_move_name_sequence/static/description/index.html
+++ b/account_move_name_sequence/static/description/index.html
@@ -8,10 +8,11 @@
/*
:Author: David Goodger (goodger@python.org)
-:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $
+:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
+Despite the name, some widely supported CSS2 features are used.
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
customize this style sheet.
@@ -274,7 +275,7 @@
margin-left: 2em ;
margin-right: 2em }
-pre.code .ln { color: grey; } /* line numbers */
+pre.code .ln { color: gray; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
@@ -300,7 +301,7 @@
span.pre {
white-space: pre }
-span.problematic {
+span.problematic, pre.problematic {
color: red }
span.section-subtitle {
@@ -368,7 +369,7 @@ Account Move Number Sequence
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:03b876b8607b5c34b0f49870637439187af2b3913e3af50d384838d28ef870ee
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
-
+
In Odoo version 13.0 and previous versions, the number of journal
entries was generated from a sequence configured on the journal.
In Odoo version 14.0, the number of journal entries can be manually set
@@ -438,7 +439,7 @@
Bugs are tracked on GitHub Issues.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
-feedback.
+feedback.
Do not contact contributors directly about support or help with technical issues.
This module is maintained by the OCA.
-
+
+
+
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
Current maintainers:
-
This module is part of the OCA/account-financial-tools project on GitHub.
+
This module is part of the OCA/account-financial-tools project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
diff --git a/account_move_name_sequence/tests/test_account_incoming_supplier_invoice.py b/account_move_name_sequence/tests/test_account_incoming_supplier_invoice.py
index 7545ac0bed8..bfadfa51032 100644
--- a/account_move_name_sequence/tests/test_account_incoming_supplier_invoice.py
+++ b/account_move_name_sequence/tests/test_account_incoming_supplier_invoice.py
@@ -10,8 +10,8 @@ class TestAccountIncomingSupplierInvoice(AccountTestInvoicingCommon):
"""Testing creating account move fetching mail.alias"""
@classmethod
- def setUpClass(cls, chart_template_ref=None):
- super().setUpClass(chart_template_ref=chart_template_ref)
+ def setUpClass(cls):
+ super().setUpClass()
cls.env["ir.config_parameter"].sudo().set_param(
"mail.catchall.domain", "test-company.odoo.com"
@@ -59,14 +59,25 @@ def test_supplier_invoice_mailed_from_supplier(self):
message_parsed = {
"message_id": "message-id-dead-beef",
"subject": "Incoming bill",
- "from": f"{self.supplier_partner.name} <{self.supplier_partner.email}>",
- "to": f"{self.journal.alias_id.alias_name}@{self.journal.alias_id.alias_domain}",
+ "from": f"{self.supplier_partner.name} " f"<{self.supplier_partner.email}>",
+ "to": f"{self.journal.alias_id.alias_name}@"
+ f"{self.journal.alias_id.alias_domain}",
"body": "You know, that thing that you bought.",
"attachments": [b"Hello, invoice"],
}
- invoice = self.env["account.move"].message_new(
- message_parsed, {"move_type": "in_invoice", "journal_id": self.journal.id}
+ invoice = (
+ self.env["account.move"]
+ .with_context(
+ tracking_disable=False,
+ mail_create_nolog=False,
+ mail_create_nosubscribe=False,
+ mail_notrack=False,
+ )
+ .message_new(
+ message_parsed,
+ {"move_type": "in_invoice", "journal_id": self.journal.id},
+ )
)
message_ids = invoice.message_ids
diff --git a/account_move_name_sequence/tests/test_account_move_name_seq.py b/account_move_name_sequence/tests/test_account_move_name_seq.py
index c1e074f544b..b414e14c484 100644
--- a/account_move_name_sequence/tests/test_account_move_name_seq.py
+++ b/account_move_name_sequence/tests/test_account_move_name_seq.py
@@ -5,30 +5,31 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from datetime import datetime
+from unittest.mock import patch
from freezegun import freeze_time
from odoo import fields
from odoo.exceptions import UserError, ValidationError
-from odoo.tests import tagged
-from odoo.tests.common import Form, TransactionCase
+from odoo.tests import Form, TransactionCase, tagged
@tagged("post_install", "-at_install")
class TestAccountMoveNameSequence(TransactionCase):
- def setUp(self):
- super().setUp()
- self.company = self.env.ref("base.main_company")
- self.partner = self.env.ref("base.res_partner_3")
- self.misc_journal = self.env["account.journal"].create(
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
+ cls.company = cls.env.ref("base.main_company")
+ cls.partner = cls.env.ref("base.res_partner_3")
+ cls.misc_journal = cls.env["account.journal"].create(
{
"name": "Test Journal Move name seq",
"code": "ADLM",
"type": "general",
- "company_id": self.company.id,
+ "company_id": cls.company.id,
}
)
- self.sales_seq = self.env["ir.sequence"].create(
+ cls.sales_seq = cls.env["ir.sequence"].create(
{
"name": "TB2C",
"implementation": "no_gap",
@@ -36,34 +37,38 @@ def setUp(self):
"use_date_range": True,
"number_increment": 1,
"padding": 4,
- "company_id": self.company.id,
+ "company_id": cls.company.id,
}
)
- self.sales_journal = self.env["account.journal"].create(
+ cls.sales_journal = cls.env["account.journal"].create(
{
"name": "TB2C",
"code": "TB2C",
"type": "sale",
- "company_id": self.company.id,
+ "company_id": cls.company.id,
"refund_sequence": True,
- "sequence_id": self.sales_seq.id,
+ "sequence_id": cls.sales_seq.id,
}
)
- self.purchase_journal = self.env["account.journal"].create(
+ cls.purchase_journal = cls.env["account.journal"].create(
{
"name": "Test Purchase Journal Move name seq",
"code": "ADLP",
"type": "purchase",
- "company_id": self.company.id,
+ "company_id": cls.company.id,
"refund_sequence": True,
}
)
- self.accounts = self.env["account.account"].search(
- [("company_id", "=", self.company.id)], limit=2
+ cls.accounts = cls.env["account.account"].search(
+ [("company_ids", "=", cls.company.id)], limit=2
)
- self.account1 = self.accounts[0]
- self.account2 = self.accounts[1]
- self.date = datetime.now()
+ cls.account1 = cls.accounts[0]
+ cls.account2 = cls.accounts[1]
+ cls.date = datetime.now()
+
+ cls.journals = cls.misc_journal | cls.purchase_journal | cls.sales_journal
+ with patch("odoo.models.BaseModel._validate_fields"):
+ cls.journals.restrict_mode_hash_table = False
def test_seq_creation(self):
self.assertTrue(self.misc_journal.sequence_id)
@@ -160,6 +165,40 @@ def test_prefix_move_name_use_move_date(self):
move.action_post()
self.assertEqual(move.name, "TEST-2022-07-0001")
+ def test_prefix_move_name_use_move_date_2(self):
+ seq = self.misc_journal.sequence_id
+ seq.prefix = "TEST-%(range_month)s-"
+ with freeze_time("2022-01-01"):
+ move = self.env["account.move"].create(
+ {
+ "date": "2022-06-30",
+ "journal_id": self.misc_journal.id,
+ "line_ids": [
+ (0, 0, {"account_id": self.account1.id, "debit": 10}),
+ (0, 0, {"account_id": self.account2.id, "credit": 10}),
+ ],
+ }
+ )
+ move.action_post()
+ self.assertEqual(move.name, "TEST-06-0001")
+
+ def test_prefix_move_name_use_move_date_3(self):
+ seq = self.misc_journal.sequence_id
+ seq.prefix = "TEST-%(range_day)s-"
+ with freeze_time("2022-01-01"):
+ move = self.env["account.move"].create(
+ {
+ "date": "2022-01-01",
+ "journal_id": self.misc_journal.id,
+ "line_ids": [
+ (0, 0, {"account_id": self.account1.id, "debit": 10}),
+ (0, 0, {"account_id": self.account2.id, "credit": 10}),
+ ],
+ }
+ )
+ move.action_post()
+ self.assertEqual(move.name, "TEST-01-0001")
+
def test_in_invoice_and_refund(self):
in_invoice = self.env["account.move"].create(
{
@@ -275,7 +314,11 @@ def test_remove_invoice_error_secuence_no_grap(self):
)
self.assertEqual(invoice.name, "/")
invoice.action_post()
- error_msg = "You can't delete a posted journal item. Don’t play games with your accounting records; reset the journal entry to draft before deleting it."
+ error_msg = (
+ "You can't delete a posted journal item. "
+ "Don’t play games with your accounting records; "
+ "reset the journal entry to draft before deleting it."
+ )
with self.assertRaisesRegex(UserError, error_msg):
invoice.unlink()
invoice.button_draft()
@@ -307,7 +350,11 @@ def test_remove_invoice_error_secuence_standard(self):
)
self.assertEqual(in_refund_invoice.name, "/")
in_refund_invoice.action_post()
- error_msg = "You can't delete a posted journal item. Don’t play games with your accounting records; reset the journal entry to draft before deleting it."
+ error_msg = (
+ "You can't delete a posted journal item. "
+ "Don’t play games with your accounting records; "
+ "reset the journal entry to draft before deleting it."
+ )
with self.assertRaisesRegex(UserError, error_msg):
in_refund_invoice.unlink()
in_refund_invoice.button_draft()
@@ -315,7 +362,12 @@ def test_remove_invoice_error_secuence_standard(self):
self.assertTrue(in_refund_invoice.unlink())
def test_journal_check_journal_sequence(self):
- new_journal = self.purchase_journal.copy()
+ new_journal = self.purchase_journal.copy({"restrict_mode_hash_table": True})
+ self.env.cr.execute(
+ "UPDATE account_journal SET restrict_mode_hash_table = False WHERE id = %s",
+ (new_journal.id,),
+ )
+ self.env.invalidate_all()
# same sequence_id and refund_sequence_id
with self.assertRaises(ValidationError):
new_journal.write({"refund_sequence_id": new_journal.sequence_id})
diff --git a/account_move_name_sequence/tests/test_sequence_concurrency.py b/account_move_name_sequence/tests/test_sequence_concurrency.py
index 79e83e84f86..0885b69234e 100644
--- a/account_move_name_sequence/tests/test_sequence_concurrency.py
+++ b/account_move_name_sequence/tests/test_sequence_concurrency.py
@@ -1,13 +1,13 @@
import logging
import threading
import time
+from unittest.mock import patch
import psycopg2
-import odoo
from odoo import SUPERUSER_ID, api, fields, tools
-from odoo.tests import tagged
-from odoo.tests.common import Form, TransactionCase
+from odoo.modules.registry import Registry
+from odoo.tests import Form, TransactionCase, tagged
_logger = logging.getLogger(__name__)
@@ -36,18 +36,23 @@ def join(self, *args, **kwargs):
@tagged("post_install", "-at_install", "test_move_sequence")
class TestSequenceConcurrency(TransactionCase):
- def setUp(self):
- super().setUp()
- self.product = self.env.ref("product.product_delivery_01")
- self.partner = self.env.ref("base.res_partner_12")
- self.partner2 = self.env.ref("base.res_partner_1")
- self.date = fields.Date.to_date("1985-04-14")
- self.journal_sale_std = self.env.ref(
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
+ cls.product = cls.env.ref("product.product_delivery_01")
+ cls.partner = cls.env.ref("base.res_partner_12")
+ cls.partner2 = cls.env.ref("base.res_partner_1")
+ cls.date = fields.Date.to_date("1985-04-14")
+ cls.journal_sale_std = cls.env.ref(
"account_move_name_sequence.journal_sale_std_demo"
)
- self.journal_cash_std = self.env.ref(
+ cls.journal_cash_std = cls.env.ref(
"account_move_name_sequence.journal_cash_std_demo"
)
+ cls.bank_journal = cls.env.ref("account.1_bank")
+ cls.bank_journal.inbound_payment_method_line_ids.payment_account_id = (
+ cls.env.ref("account.1_account_journal_suspense_account_id")
+ )
def _new_cr(self):
return self.env.registry.cursor()
@@ -55,6 +60,7 @@ def _new_cr(self):
def _create_invoice_form(
self, env, post=True, partner=None, ir_sequence_standard=False
):
+ env.invalidate_all()
if partner is None:
# Use another partner to bypass "increase_rank" lock error
partner = self.partner
@@ -62,7 +68,6 @@ def _create_invoice_form(
with Form(env["account.move"].with_context(**ctx)) as invoice_form:
invoice_form.partner_id = partner
invoice_form.invoice_date = self.date
-
with invoice_form.invoice_line_ids.new() as line_form:
line_form.product_id = self.product
line_form.price_unit = 100.0
@@ -71,10 +76,14 @@ def _create_invoice_form(
if ir_sequence_standard:
invoice.journal_id = self.journal_sale_std
if post:
- invoice.action_post()
+ with patch(
+ "odoo.addons.account.models.account_move.AccountMove._hash_moves"
+ ):
+ invoice.action_post()
return invoice
def _create_payment_form(self, env, ir_sequence_standard=False):
+ env.invalidate_all()
with Form(
env["account.payment"].with_context(
default_payment_type="inbound",
@@ -85,31 +94,38 @@ def _create_payment_form(self, env, ir_sequence_standard=False):
payment_form.partner_id = env.ref("base.res_partner_12")
payment_form.amount = 100
payment_form.date = self.date
-
+ if ir_sequence_standard:
+ payment_form.journal_id = self.journal_cash_std
payment = payment_form.save()
- if ir_sequence_standard:
- payment.move_id.journal_id = self.journal_cash_std
- payment.action_post()
+ with patch("odoo.addons.account.models.account_move.AccountMove._hash_moves"):
+ payment.action_post()
return payment
- def _clean_moves(self, move_ids, payment=None):
- """Delete moves created after finish unittest using
+ def _clean_moves_and_payments(self, move_ids):
+ """Delete moves and payments created after finish unittest using
self.addCleanup(
- self._clean_moves, self.env, (invoices | payments.mapped('move_id')).ids
- )"""
+ self._clean_moves_and_payments,
+ self.env,
+ (invoices | payments.mapped('move_id')).ids,
+ )
+ """
with self._new_cr() as cr:
env = api.Environment(cr, SUPERUSER_ID, {})
- moves = env["account.move"].browse(move_ids)
- moves.button_draft()
- moves = moves.with_context(force_delete=True)
- moves.unlink()
- # TODO: Delete payment and journal
+ moves = env["account.move"].with_context(force_delete=True).browse(move_ids)
+ payments = moves.payment_ids
+ moves_without_payments = moves - payments.mapped("move_id")
+ if payments:
+ payments.action_draft()
+ payments.unlink()
+ if moves_without_payments:
+ moves_without_payments.button_draft()
+ moves_without_payments.unlink()
env.cr.commit()
def _create_invoice_payment(
self, deadlock_timeout, payment_first=False, ir_sequence_standard=False
):
- odoo.registry(self.env.cr.dbname)
+ Registry(self.env.cr.dbname)
with self._new_cr() as cr, cr.savepoint():
env = api.Environment(cr, SUPERUSER_ID, {})
cr_pid = cr.connection.get_backend_pid()
@@ -146,12 +162,13 @@ def test_sequence_concurrency_10_draft_invoices(self):
env1 = api.Environment(cr1, SUPERUSER_ID, {})
env2 = api.Environment(cr2, SUPERUSER_ID, {})
for cr in [cr0, cr1, cr2]:
- # Set 10s timeout in order to avoid waiting for release locks a long time
+ # Set 10s timeout in order to avoid
+ # waiting for release locks a long time
cr.execute("SET LOCAL statement_timeout = '10s'")
# Create "last move" to lock
invoice = self._create_invoice_form(env0)
- self.addCleanup(self._clean_moves, invoice.ids)
+ self.addCleanup(self._clean_moves_and_payments, invoice.ids)
env0.cr.commit()
with env1.cr.savepoint(), env2.cr.savepoint():
invoice1 = self._create_invoice_form(env1, post=False)
@@ -166,13 +183,14 @@ def test_sequence_concurrency_20_editing_last_invoice(self):
env0 = api.Environment(cr0, SUPERUSER_ID, {})
env1 = api.Environment(cr1, SUPERUSER_ID, {})
for cr in [cr0, cr1]:
- # Set 10s timeout in order to avoid waiting for release locks a long time
+ # Set 10s timeout in order to avoid
+ # waiting for release locks a long time
cr.execute("SET LOCAL statement_timeout = '10s'")
# Create "last move" to lock
invoice = self._create_invoice_form(env0)
- self.addCleanup(self._clean_moves, invoice.ids)
+ self.addCleanup(self._clean_moves_and_payments, invoice.ids)
env0.cr.commit()
with env0.cr.savepoint(), env1.cr.savepoint():
# Edit something in "last move"
@@ -187,13 +205,14 @@ def test_sequence_concurrency_30_editing_last_payment(self):
env0 = api.Environment(cr0, SUPERUSER_ID, {})
env1 = api.Environment(cr1, SUPERUSER_ID, {})
for cr in [cr0, cr1]:
- # Set 10s timeout in order to avoid waiting for release locks a long time
+ # Set 10s timeout in order to avoid
+ # waiting for release locks a long time
cr.execute("SET LOCAL statement_timeout = '10s'")
# Create "last move" to lock
payment = self._create_payment_form(env0)
payment_move = payment.move_id
- self.addCleanup(self._clean_moves, payment_move.ids)
+ self.addCleanup(self._clean_moves_and_payments, payment_move.ids)
env0.cr.commit()
with env0.cr.savepoint(), env1.cr.savepoint():
# Edit something in "last move"
@@ -209,14 +228,17 @@ def test_sequence_concurrency_40_reconciling_last_invoice(self):
env0 = api.Environment(cr0, SUPERUSER_ID, {})
env1 = api.Environment(cr1, SUPERUSER_ID, {})
for cr in [cr0, cr1]:
- # Set 10s timeout in order to avoid waiting for release locks a long time
+ # Set 10s timeout in order to avoid
+ # waiting for release locks a long time
cr.execute("SET LOCAL statement_timeout = '10s'")
# Create "last move" to lock
invoice = self._create_invoice_form(env0)
payment = self._create_payment_form(env0)
payment_move = payment.move_id
- self.addCleanup(self._clean_moves, invoice.ids + payment_move.ids)
+ self.addCleanup(
+ self._clean_moves_and_payments, invoice.ids + payment_move.ids
+ )
env0.cr.commit()
lines2reconcile = (
(payment_move | invoice)
@@ -241,14 +263,17 @@ def test_sequence_concurrency_50_reconciling_last_payment(self):
env0 = api.Environment(cr0, SUPERUSER_ID, {})
env1 = api.Environment(cr1, SUPERUSER_ID, {})
for cr in [cr0, cr1]:
- # Set 10s timeout in order to avoid waiting for release locks a long time
+ # Set 10s timeout in order to avoid
+ # waiting for release locks a long time
cr.execute("SET LOCAL statement_timeout = '10s'")
# Create "last move" to lock
invoice = self._create_invoice_form(env0)
payment = self._create_payment_form(env0)
payment_move = payment.move_id
- self.addCleanup(self._clean_moves, invoice.ids + payment_move.ids)
+ self.addCleanup(
+ self._clean_moves_and_payments, invoice.ids + payment_move.ids
+ )
env0.cr.commit()
lines2reconcile = (
(payment_move | invoice)
@@ -273,13 +298,14 @@ def test_sequence_concurrency_90_payments(self):
env1 = api.Environment(cr1, SUPERUSER_ID, {})
env2 = api.Environment(cr2, SUPERUSER_ID, {})
for cr in [cr0, cr1, cr2]:
- # Set 10s timeout in order to avoid waiting for release locks a long time
+ # Set 10s timeout in order to avoid
+ # waiting for release locks a long time
cr.execute("SET LOCAL statement_timeout = '10s'")
# Create "last move" to lock
payment = self._create_payment_form(env0, ir_sequence_standard=True)
payment_move_ids = payment.move_id.ids
- self.addCleanup(self._clean_moves, payment_move_ids)
+ self.addCleanup(self._clean_moves_and_payments, payment_move_ids)
env0.cr.commit()
with env1.cr.savepoint(), env2.cr.savepoint():
self._create_payment_form(env1, ir_sequence_standard=True)
@@ -292,12 +318,13 @@ def test_sequence_concurrency_92_invoices(self):
env1 = api.Environment(cr1, SUPERUSER_ID, {})
env2 = api.Environment(cr2, SUPERUSER_ID, {})
for cr in [cr0, cr1, cr2]:
- # Set 10s timeout in order to avoid waiting for release locks a long time
+ # Set 10s timeout in order to avoid
+ # waiting for release locks a long time
cr.execute("SET LOCAL statement_timeout = '10s'")
# Create "last move" to lock
invoice = self._create_invoice_form(env0, ir_sequence_standard=True)
- self.addCleanup(self._clean_moves, invoice.ids)
+ self.addCleanup(self._clean_moves_and_payments, invoice.ids)
env0.cr.commit()
with env1.cr.savepoint(), env2.cr.savepoint():
self._create_invoice_form(env1, ir_sequence_standard=True)
@@ -320,7 +347,9 @@ def test_sequence_concurrency_95_pay2inv_inv2pay(self):
# Create "last move" to lock
payment = self._create_payment_form(env0)
payment_move_ids = payment.move_id.ids
- self.addCleanup(self._clean_moves, invoice.ids + payment_move_ids)
+ self.addCleanup(
+ self._clean_moves_and_payments, invoice.ids + payment_move_ids
+ )
env0.cr.commit()
env0.cr.execute(
"SELECT setting FROM pg_settings WHERE name = 'deadlock_timeout'"
@@ -344,6 +373,7 @@ def test_sequence_concurrency_95_pay2inv_inv2pay(self):
args=(deadlock_timeout, False, True),
name="Thread invoice payment",
)
+ self.env.registry.enter_test_mode(self.env.registry.cursor())
t_pay_inv.start()
t_inv_pay.start()
# the thread could raise the error before to wait for it so disable coverage