[FIX+IMP] contract: lots of things

- Improve invoice generation through cron
  Avoid too much sql queries by iterating on first search
  Avoid performances problems through invoices ids isntead
  of recordset
- Improve tests
  Adds multi company tests
- Add a generation type on contract
  Add a generation type on contract that allows to generate other
  document than invoice (e.g. sale order)
- Allows to get several functions to create recurring documents
- Set visibility on button to show invoices
- Add generation_type field
- Update button visibility
- Simplify test flow
- Use Odoo conventions for methods
- Add explicit cron create type for invoices
- Improve function call for cron recurring creates
- Improve multi-company tests / don't break former tests structure
- Fix forwardport from 14.0 #741
This commit is contained in:
Denis Roussel
2021-11-03 18:20:54 +01:00
committed by Pedro M. Baeza
parent e230ba9f0f
commit 5261c621d5
5 changed files with 169 additions and 16 deletions

View File

@@ -48,6 +48,19 @@ class ContractAbstractContract(models.AbstractModel):
help="Mark this check if you want to control recurrrence at line level instead"
" of all together for the whole contract.",
)
generation_type = fields.Selection(
selection=lambda self: self._selection_generation_type(),
default=lambda self: self._default_generation_type(),
help="Choose the document that will be automatically generated by cron.",
)
@api.model
def _selection_generation_type(self):
return [("invoice", "Invoice")]
@api.model
def _default_generation_type(self):
return "invoice"
@api.onchange("contract_type")
def _onchange_contract_type(self):

View File

@@ -6,12 +6,16 @@
# Copyright 2018 ACSONE SA/NV
# Copyright 2021 Tecnativa - Víctor Martínez
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import logging
from odoo import api, fields, models
from odoo.exceptions import UserError, ValidationError
from odoo.osv import expression
from odoo.tests import Form
from odoo.tools.translate import _
_logger = logging.getLogger(__name__)
class ContractContract(models.Model):
_name = "contract.contract"
@@ -607,23 +611,46 @@ class ContractContract(models.Model):
return moves
@api.model
def cron_recurring_create_invoice(self, date_ref=None):
def _get_recurring_create_func(self, create_type="invoice"):
"""
Allows to retrieve the recurring create function depending
on generate_type attribute
"""
if create_type == "invoice":
return self.__class__._recurring_create_invoice
@api.model
def _cron_recurring_create(self, date_ref=False, create_type="invoice"):
"""
The cron function in order to create recurrent documents
from contracts.
"""
_recurring_create_func = self._get_recurring_create_func(
create_type=create_type
)
if not date_ref:
date_ref = fields.Date.context_today(self)
domain = self._get_contracts_to_invoice_domain(date_ref)
invoices = self.env["account.move"]
domain = expression.AND(
[
domain,
[("generation_type", "=", create_type)],
]
)
contracts = self.search(domain)
companies = set(contracts.mapped("company_id"))
# Invoice by companies, so assignation emails get correct context
companies_to_invoice = self.read_group(domain, ["company_id"], ["company_id"])
for row in companies_to_invoice:
contracts_to_invoice = (
self.search(row["__domain"])
.with_context(allowed_company_ids=[row["company_id"][0]])
.filtered(
lambda a: not a.date_end or a.recurring_next_date <= a.date_end
)
)
invoices |= contracts_to_invoice._recurring_create_invoice(date_ref)
return invoices
for company in companies:
contracts_to_invoice = contracts.filtered(
lambda c: c.company_id == company
and (not c.date_end or c.recurring_next_date <= c.date_end)
).with_company(company)
_recurring_create_func(contracts_to_invoice, date_ref)
return True
@api.model
def cron_recurring_create_invoice(self, date_ref=None):
return self._cron_recurring_create(date_ref, create_type="invoice")
def action_terminate_contract(self):
self.ensure_one()

View File

@@ -1,5 +1,4 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from . import test_contract
from . import test_contract_manually_create_invoice
from . import test_portal
from . import test_multicompany

View File

@@ -0,0 +1,110 @@
# Copyright 2021 ACSONE SA/NV (<http://acsone.eu>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from .test_contract import TestContractBase
class ContractMulticompanyCase(TestContractBase):
@classmethod
def setUpClass(cls):
super().setUpClass()
chart_template = cls.env.ref("l10n_generic_coa.configurable_chart_template")
cls.company_obj = cls.env["res.company"]
cls.company_1 = cls.env.ref("base.main_company")
vals = {"name": "Company 2"}
cls.company_2 = cls.company_obj.create(vals)
chart_template.try_loading(company=cls.company_2)
cls.env.user.company_ids |= cls.company_2
cls.contract_mc = (
cls.env["contract.contract"]
.with_company(cls.company_2)
.create(
{
"name": "Test Contract MC",
"partner_id": cls.partner.id,
"pricelist_id": cls.partner.property_product_pricelist.id,
"line_recurrence": True,
"contract_type": "purchase",
"contract_line_ids": [
(
0,
0,
{
"product_id": cls.product_1.id,
"name": "Services from #START# to #END#",
"quantity": 1,
"uom_id": cls.product_1.uom_id.id,
"price_unit": 100,
"discount": 50,
"recurring_rule_type": "monthly",
"recurring_interval": 1,
"date_start": "2018-02-15",
"recurring_next_date": "2018-02-22",
},
)
],
}
)
)
cls.line_vals = {
"contract_id": cls.contract_mc.id,
"product_id": cls.product_1.id,
"name": "Services from #START# to #END#",
"quantity": 1,
"uom_id": cls.product_1.uom_id.id,
"price_unit": 100,
"discount": 50,
"recurring_rule_type": "monthly",
"recurring_interval": 1,
"date_start": "2018-01-01",
"recurring_next_date": "2018-01-15",
"is_auto_renew": False,
}
cls.acct_line_mc = (
cls.env["contract.line"].with_company(cls.company_2).create(cls.line_vals)
)
def test_cron_recurring_create_invoice_multi_company(self):
self.acct_line.date_start = "2018-01-01"
self.acct_line.recurring_invoicing_type = "post-paid"
self.acct_line.date_end = "2018-03-15"
self.acct_line_mc.date_start = "2018-01-01"
self.acct_line_mc.recurring_invoicing_type = "post-paid"
self.acct_line_mc.date_end = "2018-03-15"
contracts = self.contract2
contracts_company_2 = self.env["contract.contract"].browse()
for _i in range(10):
contracts |= self.contract.copy()
for _i in range(10):
vals = (
self.contract_mc.with_company(company=self.company_2)
.with_context(active_test=False)
.copy_data({"company_id": self.company_2.id})
)
contracts_company_2 |= self.contract_mc.with_company(
company=self.company_2
).create(vals)
self.env["contract.contract"].cron_recurring_create_invoice()
# Check company 1
invoice_lines_company_1 = self.env["account.move.line"].search(
[("contract_line_id", "in", contracts.mapped("contract_line_ids").ids)]
)
invoice_lines_company_2 = self.env["account.move.line"].search(
[
(
"contract_line_id",
"in",
contracts_company_2.mapped("contract_line_ids").ids,
)
]
)
self.assertEqual(
len(contracts.mapped("contract_line_ids")), len(invoice_lines_company_1)
)
self.assertEqual(
len(contracts_company_2.mapped("contract_line_ids")),
len(invoice_lines_company_2),
)

View File

@@ -40,7 +40,7 @@
<button
name="recurring_create_invoice"
type="object"
attrs="{'invisible': [('create_invoice_visibility', '=', False)]}"
attrs="{'invisible': ['|', ('create_invoice_visibility', '=', False),('generation_type','!=','invoice')]}"
string="Create invoices"
groups="base.group_no_one"
/>
@@ -82,6 +82,7 @@
type="object"
icon="fa-list"
class="oe_stat_button"
attrs="{'invisible': [('generation_type','!=','invoice')]}"
>
<field
string="Invoices"
@@ -175,6 +176,9 @@
<field name="recurring_next_date" />
</group>
</group>
<group col="4" name="recurring_type" string="Recurring Type">
<field name="generation_type" />
</group>
<notebook>
<page name="recurring_invoice_line" string="Recurring Invoices">
<field