[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" help="Mark this check if you want to control recurrrence at line level instead"
" of all together for the whole contract.", " 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") @api.onchange("contract_type")
def _onchange_contract_type(self): def _onchange_contract_type(self):

View File

@@ -6,12 +6,16 @@
# Copyright 2018 ACSONE SA/NV # Copyright 2018 ACSONE SA/NV
# Copyright 2021 Tecnativa - Víctor Martínez # Copyright 2021 Tecnativa - Víctor Martínez
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import logging
from odoo import api, fields, models from odoo import api, fields, models
from odoo.exceptions import UserError, ValidationError from odoo.exceptions import UserError, ValidationError
from odoo.osv import expression
from odoo.tests import Form from odoo.tests import Form
from odoo.tools.translate import _ from odoo.tools.translate import _
_logger = logging.getLogger(__name__)
class ContractContract(models.Model): class ContractContract(models.Model):
_name = "contract.contract" _name = "contract.contract"
@@ -607,23 +611,46 @@ class ContractContract(models.Model):
return moves return moves
@api.model @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: if not date_ref:
date_ref = fields.Date.context_today(self) date_ref = fields.Date.context_today(self)
domain = self._get_contracts_to_invoice_domain(date_ref) 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 # Invoice by companies, so assignation emails get correct context
companies_to_invoice = self.read_group(domain, ["company_id"], ["company_id"]) for company in companies:
for row in companies_to_invoice: contracts_to_invoice = contracts.filtered(
contracts_to_invoice = ( lambda c: c.company_id == company
self.search(row["__domain"]) and (not c.date_end or c.recurring_next_date <= c.date_end)
.with_context(allowed_company_ids=[row["company_id"][0]]) ).with_company(company)
.filtered( _recurring_create_func(contracts_to_invoice, date_ref)
lambda a: not a.date_end or a.recurring_next_date <= a.date_end return True
)
) @api.model
invoices |= contracts_to_invoice._recurring_create_invoice(date_ref) def cron_recurring_create_invoice(self, date_ref=None):
return invoices return self._cron_recurring_create(date_ref, create_type="invoice")
def action_terminate_contract(self): def action_terminate_contract(self):
self.ensure_one() 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
from . import test_contract_manually_create_invoice from . import test_contract_manually_create_invoice
from . import test_portal 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 <button
name="recurring_create_invoice" name="recurring_create_invoice"
type="object" type="object"
attrs="{'invisible': [('create_invoice_visibility', '=', False)]}" attrs="{'invisible': ['|', ('create_invoice_visibility', '=', False),('generation_type','!=','invoice')]}"
string="Create invoices" string="Create invoices"
groups="base.group_no_one" groups="base.group_no_one"
/> />
@@ -82,6 +82,7 @@
type="object" type="object"
icon="fa-list" icon="fa-list"
class="oe_stat_button" class="oe_stat_button"
attrs="{'invisible': [('generation_type','!=','invoice')]}"
> >
<field <field
string="Invoices" string="Invoices"
@@ -175,6 +176,9 @@
<field name="recurring_next_date" /> <field name="recurring_next_date" />
</group> </group>
</group> </group>
<group col="4" name="recurring_type" string="Recurring Type">
<field name="generation_type" />
</group>
<notebook> <notebook>
<page name="recurring_invoice_line" string="Recurring Invoices"> <page name="recurring_invoice_line" string="Recurring Invoices">
<field <field