[FIX+IMP+MIG] contract: Several refinements:

* Remove incorrect oldname attributes.
* Add filter on partners for running contracts (+ a support o2m field for that).
* Cover more tables in model renaming + cleaner code using a loop.
* Don't copy contract lines, but rename table + copy contract records on pre.
* Contract code is now populated to "Reference/Description" field in invoice.
* Order on new contract model has been restored to the same as old analytic accounts.
This commit is contained in:
Pedro M. Baeza
2019-09-13 11:40:47 +02:00
committed by Francisco Ivan Anton Prieto
parent f55b537d08
commit a2cd6455ad
79 changed files with 63545 additions and 15851 deletions

View File

@@ -1,5 +1,5 @@
================================
Contracts Management - Recurring
Recurring - Contracts Management
================================
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
@@ -44,33 +44,31 @@ user access rights.
Usage
=====
To use this module, you need to:
#. Contracts are in Invoicing -> Customers -> Customer and Invoicing -> Vendors -> Supplier Contracts
#. When creating a contract, fill fields for selecting the invoicing parameters:
#. Go to Accounting -> Contracts and select or create a new contract.
#. Check *Generate recurring invoices automatically*.
#. Fill fields for selecting the recurrency and invoice parameters:
* a journal
* a price list (optional)
* Journal
* Pricelist
* Period. It can be any interval of days, weeks, months, months last day or
years.
* Start date and next invoice date.
* Invoicing type: pre-paid or post-paid.
#. And add the lines to be invoiced with:
#. Add the lines to be invoiced with the product, description, quantity and
price.
#. You can mark Auto-price? for having a price automatically obtained applying
the pricelist to the product price.
#. You have the possibility to use the markers #START# or #END# in the
description field to show the start and end date of the invoiced period.
#. Choosing between pre-paid and post-paid, you modify the dates that are shown
with the markers.
#. A cron is created with daily interval, but if you are in debug mode, you can
click on *Create invoices* to force this action.
#. Click *Show recurring invoices* link to show all invoices created by the
* the product with a description, a quantity and a price
* the recurrence parameters: interval (days, weeks, months, months last day or years),
start date, date of next invoice (automatically computed, can be modified) and end date (optional)
* auto-price, for having a price automatically obtained from the price list
* #START# or #END# in the description field to display the start/end date of
the invoiced period in the invoice line description
* pre-paid (invoice at period start) or post-paid (invoice at start of next period)
#. The "Generate Recurring Invoices from Contracts" cron runs daily to generate the invoices.
If you are in debug mode, you can click on the invoice creation button.
#. The *Show recurring invoices* shortcut on contracts shows all invoices created from the
contract.
#. Click on *Print > Contract* menu to print contract report.
#. Click on *Send by Email* button to send contract by email.
#. The contract report can be printed from the Print menu
#. The contract can be sent by email with the *Send by Email* button
#. Contract templates can be created from the Configuration -> Contracts -> Contract Templates menu.
They allow to define default journal, price list and lines when creating a contract.
To use it, just select the template on the contract and fields will be filled automatically.
Known issues / Roadmap
======================
@@ -108,6 +106,9 @@ Contributors
* Vicent Cubells <vicent.cubells@tecnativa.com>
* Miquel Raïch <miquel.raich@eficent.com>
* Souheil Bejaoui <souheil.bejaoui@acsone.eu>
* Thomas Binsfeld <thomas.binsfeld@acsone.eu>
* Rafael Blasco <rafael.blasco@tecnativa.com>
* Guillaume Vandamme <guillaume.vandamme@acsone.eu>
Maintainers
~~~~~~~~~~~

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -8,164 +8,6 @@ from odoo.tools import parse_version
_logger = logging.getLogger(__name__)
CONTRACT_FIELDS = [
'id',
'name',
'partner_id',
'pricelist_id',
'contract_type',
'journal_id',
'company_id',
'active',
'code',
'group_id',
'contract_template_id',
'user_id',
'recurring_next_date',
'date_end',
'message_main_attachment_id',
'create_uid',
'create_date',
'write_uid',
'write_date',
'payment_term_id',
'fiscal_position_id',
'invoice_partner_id',
]
CONTRACT_LINE_FIELDS = [
'id',
'product_id',
'name',
'quantity',
'uom_id',
'automatic_price',
'specific_price',
'discount',
'recurring_rule_type',
'recurring_invoicing_type',
'recurring_interval',
'sequence',
'date_start',
'date_end',
'recurring_next_date',
'last_date_invoiced',
'termination_notice_date',
'successor_contract_line_id',
'predecessor_contract_line_id',
'manual_renew_needed',
'create_uid',
'create_date',
'write_uid',
'write_date',
]
def _get_contract_field_name(cr):
"""
Contract field changed the name from analytic_account_id to contract_id
in 12.0.2.0.0. This method used to get the contract field name in
account_analytic_invoice_line"""
return (
'contract_id'
if openupgrade.column_exists(
cr, 'account_analytic_invoice_line', 'contract_id'
)
else 'analytic_account_id'
)
def _copy_contract_table(cr):
contract_fields = []
for field in CONTRACT_FIELDS:
if openupgrade.column_exists(cr, 'account_analytic_account', field):
contract_fields.append(field)
contract_field_name = _get_contract_field_name(cr)
openupgrade.logged_query(
cr,
"INSERT INTO contract_contract ("
+ ', '.join(contract_fields)
+ ") "
+ "SELECT "
+ ', '.join(contract_fields)
+ " FROM account_analytic_account "
+ "WHERE id in ( SELECT DISTINCT "
+ contract_field_name
+ " FROM "
+ "account_analytic_invoice_line)",
)
if openupgrade.column_exists(cr, 'account_invoice', 'old_contract_id_tmp'):
openupgrade.logged_query(
cr,
"""
UPDATE account_invoice
SET old_contract_id = old_contract_id_tmp
""",
)
# Move contract attachments
openupgrade.logged_query(
cr,
"UPDATE ir_attachment SET res_model='contract.contract'"
+ "WHERE res_model='account.analytic.account' "
+ "AND res_id IN ( SELECT DISTINCT "
+ contract_field_name
+ " FROM account_analytic_invoice_line)",
)
# Move contract messages
openupgrade.logged_query(
cr,
"UPDATE mail_message SET model='contract.contract'"
+ "WHERE model='account.analytic.account' "
+ "AND res_id IN ( SELECT DISTINCT "
+ contract_field_name
+ " FROM account_analytic_invoice_line)",
)
# Move contract followers
openupgrade.logged_query(
cr,
"UPDATE mail_followers SET res_model='contract.contract'"
+ "WHERE res_model='account.analytic.account' "
+ "AND res_id IN ( SELECT DISTINCT "
+ contract_field_name
+ " FROM account_analytic_invoice_line)",
)
def _copy_contract_line_table(cr):
contract_line_fields = []
contract_field_name = _get_contract_field_name(cr)
for field in CONTRACT_LINE_FIELDS:
if openupgrade.column_exists(
cr, 'account_analytic_invoice_line', field
):
contract_line_fields.append(field)
account_analytic_invoice_line_fields = contract_line_fields.copy()
contract_line_fields.append('contract_id')
account_analytic_invoice_line_fields.append(contract_field_name)
contract_line_fields.append('analytic_account_id')
account_analytic_invoice_line_fields.append(contract_field_name)
contract_line_fields.append('active')
account_analytic_invoice_line_fields.append('true')
openupgrade.logged_query(
cr,
"INSERT INTO contract_line ("
+ ', '.join(contract_line_fields)
+ ") "
+ "SELECT "
+ ', '.join(account_analytic_invoice_line_fields)
+ " FROM account_analytic_invoice_line ",
)
openupgrade.logged_query(
cr,
"""
UPDATE account_invoice_line
SET contract_line_id = contract_line_id_tmp
""",
)
def _update_no_update_ir_cron(env):
# Update ir.cron
@@ -196,12 +38,7 @@ def _init_invoicing_partner_id_on_contracts(env):
@openupgrade.migrate()
def migrate(env, version):
cr = env.cr
_copy_contract_table(cr)
_copy_contract_line_table(cr)
_update_no_update_ir_cron(env)
if parse_version(version) < parse_version('12.0.2.0.0'):
# We check the version here as this post-migration script was in
# 12.0.2.0.0 and already done for those who used the module when

View File

@@ -1,113 +1,113 @@
# Copyright 2019 ACSONE SA/NV
# Copyright 2019 Tecnativa 2019 - Pedro M. Baeza
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import logging
from openupgradelib import openupgrade
from psycopg2 import sql
_logger = logging.getLogger(__name__)
models_to_rename = [
# Contract Line Wizard
('account.analytic.invoice.line.wizard', 'contract.line.wizard'),
# Abstract Contract
('account.abstract.analytic.contract', 'contract.abstract.contract'),
# Abstract Contract Line
(
'account.abstract.analytic.contract.line',
'contract.abstract.contract.line',
),
# Contract Line
('account.analytic.invoice.line', 'contract.line'),
# Contract Template
('account.analytic.contract', 'contract.template'),
# Contract Template Line
('account.analytic.contract.line', 'contract.template.line'),
]
tables_to_rename = [
# Contract Line
('account_analytic_invoice_line', 'contract_line'),
# Contract Template
('account_analytic_contract', 'contract_template'),
# Contract Template Line
('account_analytic_contract_line', 'contract_template_line'),
]
columns_to_copy = {
'contract_line': [
('analytic_account_id', 'contract_id', None),
],
}
xmlids_to_rename = [
(
'contract.account_analytic_cron_for_invoice',
'contract.contract_cron_for_invoice',
),
(
'contract.account_analytic_contract_manager',
'contract.contract_template_manager',
),
(
'contract.account_analytic_contract_user',
'contract.contract_template_user',
),
(
'contract.account_analytic_invoice_line_manager',
'contract.contract_line_manager',
),
(
'contract.account_analytic_invoice_line_user',
'contract.contract_line_user',
),
(
'contract.account_analytic_contract_line_manager',
'contract.contract_template_line_manager',
),
(
'contract.account_analytic_contract_line_user',
'contract.contract_template_line_user',
),
]
def _get_contract_field_name(cr):
"""
Contract field changed the name from analytic_account_id to contract_id
in 12.0.2.0.0. This method used to get the contract field name in
account_analytic_invoice_line"""
return (
'contract_id'
if openupgrade.column_exists(
cr, 'account_analytic_invoice_line', 'contract_id'
)
else 'analytic_account_id'
)
def create_contract_records(cr):
contract_field_name = _get_contract_field_name(cr)
openupgrade.logged_query(
cr, """
CREATE TABLE contract_contract
(LIKE account_analytic_account INCLUDING ALL)""",
)
openupgrade.logged_query(
cr, sql.SQL("""
INSERT INTO contract_contract
SELECT * FROM account_analytic_account
WHERE id IN (SELECT DISTINCT {} FROM contract_line)
""").format(
sql.Identifier(contract_field_name),
),
)
@openupgrade.migrate()
def migrate(env, version):
_logger.info(">> Pre-Migration 12.0.4.0.0")
cr = env.cr
models_to_rename = [
# Contract Line Wizard
('account.analytic.invoice.line.wizard', 'contract.line.wizard'),
# Abstract Contract
('account.abstract.analytic.contract', 'contract.abstract.contract'),
# Abstract Contract Line
(
'account.abstract.analytic.contract.line',
'contract.abstract.contract.line',
),
# Contract Line
('account.analytic.invoice.line', 'contract.line'),
# Contract Template
('account.analytic.contract', 'contract.template'),
# Contract Template Line
('account.analytic.contract.line', 'contract.template.line'),
]
tables_to_rename = [
# Contract Template
('account_analytic_contract', 'contract_template'),
# Contract Template Line
('account_analytic_contract_line', 'contract_template_line'),
]
xmlids_to_rename = [
(
'contract.account_analytic_cron_for_invoice',
'contract.contract_cron_for_invoice',
),
(
'contract.account_analytic_contract_manager',
'contract.contract_template_manager',
),
(
'contract.account_analytic_contract_user',
'contract.contract_template_user',
),
(
'contract.account_analytic_invoice_line_manager',
'contract.contract_line_manager',
),
(
'contract.account_analytic_invoice_line_user',
'contract.contract_line_user',
),
(
'contract.account_analytic_contract_line_manager',
'contract.contract_template_line_manager',
),
(
'contract.account_analytic_contract_line_user',
'contract.contract_template_line_user',
),
]
openupgrade.rename_models(cr, models_to_rename)
openupgrade.rename_tables(cr, tables_to_rename)
openupgrade.rename_xmlids(cr, xmlids_to_rename)
# A temporary column is needed to avoid breaking the foreign key constraint
# The temporary column is dropped in the post-migration script
openupgrade.logged_query(
cr,
"""
ALTER TABLE account_invoice_line
ADD COLUMN contract_line_id_tmp INTEGER
""",
)
if openupgrade.column_exists(
cr, 'account_invoice_line', 'contract_line_id'
):
openupgrade.logged_query(
cr,
"""
UPDATE account_invoice_line
SET contract_line_id_tmp = contract_line_id
""",
)
openupgrade.logged_query(
cr,
"""
UPDATE account_invoice_line SET contract_line_id = NULL
""",
)
if not openupgrade.column_exists(
cr, 'account_invoice', 'old_contract_id'
):
openupgrade.logged_query(
cr,
"""
ALTER TABLE account_invoice
ADD COLUMN old_contract_id_tmp INTEGER
""",
)
openupgrade.logged_query(
cr,
"""
UPDATE account_invoice
SET old_contract_id_tmp = contract_id
""",
)
openupgrade.copy_columns(cr, columns_to_copy)
create_contract_records(cr)

View File

@@ -113,7 +113,6 @@ class ContractAbstractContractLine(models.AbstractModel):
comodel_name='contract.abstract.contract',
required=True,
ondelete='cascade',
oldname='analytic_account_id',
)
@api.depends(

View File

@@ -14,6 +14,7 @@ from odoo.tools.translate import _
class ContractContract(models.Model):
_name = 'contract.contract'
_description = "Contract"
_order = 'code, name asc'
_inherit = [
'mail.thread',
'mail.activity.mixin',
@@ -44,7 +45,6 @@ class ContractContract(models.Model):
comodel_name='contract.line',
inverse_name='contract_id',
copy=True,
oldnae='contract_line_ids',
)
user_id = fields.Many2one(
@@ -273,7 +273,7 @@ class ContractContract(models.Model):
if self.contract_type == 'purchase':
invoice_type = 'in_invoice'
return {
'reference': self.code,
'name': self.code,
'type': invoice_type,
'partner_id': self.invoice_partner_id.id,
'currency_id': currency.id,

View File

@@ -25,7 +25,6 @@ class ContractLine(models.Model):
required=True,
index=True,
ondelete='cascade',
oldname='analytic_account_id',
)
analytic_account_id = fields.Many2one(
string="Analytic account",

View File

@@ -19,5 +19,4 @@ class ContractTemplate(models.Model):
inverse_name='contract_id',
copy=True,
string='Contract template lines',
oldname='contract_line_ids',
)

View File

@@ -15,6 +15,11 @@ class ResPartner(models.Model):
string='Purchase Contracts',
compute='_compute_contract_count',
)
contract_ids = fields.One2many(
comodel_name='contract.contract',
inverse='partner_id',
string="Contracts",
)
def _compute_contract_count(self):
contract_model = self.env['contract.contract']

View File

@@ -4,7 +4,7 @@
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.15.1: http://docutils.sourceforge.net/" />
<title>Contracts Management - Recurring</title>
<title>Recurring - Contracts Management</title>
<style type="text/css">
/*
@@ -360,8 +360,8 @@ ul.auto-toc {
</style>
</head>
<body>
<div class="document" id="contracts-management-recurring">
<h1 class="title">Contracts Management - Recurring</h1>
<div class="document" id="recurring-contracts-management">
<h1 class="title">Recurring - Contracts Management</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
@@ -393,33 +393,32 @@ user access rights.</p>
</div>
<div class="section" id="usage">
<h1><a class="toc-backref" href="#id2">Usage</a></h1>
<p>To use this module, you need to:</p>
<ol class="arabic simple">
<li>Go to Accounting -&gt; Contracts and select or create a new contract.</li>
<li>Check <em>Generate recurring invoices automatically</em>.</li>
<li>Fill fields for selecting the recurrency and invoice parameters:<ul>
<li>Journal</li>
<li>Pricelist</li>
<li>Period. It can be any interval of days, weeks, months, months last day or
years.</li>
<li>Start date and next invoice date.</li>
<li>Invoicing type: pre-paid or post-paid.</li>
<li>Contracts are in Invoicing -&gt; Customers -&gt; Customer and Invoicing -&gt; Vendors -&gt; Supplier Contracts</li>
<li>When creating a contract, fill fields for selecting the invoicing parameters:<ul>
<li>a journal</li>
<li>a price list (optional)</li>
</ul>
</li>
<li>Add the lines to be invoiced with the product, description, quantity and
price.</li>
<li>You can mark Auto-price? for having a price automatically obtained applying
the pricelist to the product price.</li>
<li>You have the possibility to use the markers #START# or #END# in the
description field to show the start and end date of the invoiced period.</li>
<li>Choosing between pre-paid and post-paid, you modify the dates that are shown
with the markers.</li>
<li>A cron is created with daily interval, but if you are in debug mode, you can
click on <em>Create invoices</em> to force this action.</li>
<li>Click <em>Show recurring invoices</em> link to show all invoices created by the
<li>And add the lines to be invoiced with:<ul>
<li>the product with a description, a quantity and a price</li>
<li>the recurrence parameters: interval (days, weeks, months, months last day or years),
start date, date of next invoice (automatically computed, can be modified) and end date (optional)</li>
<li>auto-price, for having a price automatically obtained from the price list</li>
<li>#START# or #END# in the description field to display the start/end date of
the invoiced period in the invoice line description</li>
<li>pre-paid (invoice at period start) or post-paid (invoice at start of next period)</li>
</ul>
</li>
<li>The “Generate Recurring Invoices from Contracts” cron runs daily to generate the invoices.
If you are in debug mode, you can click on the invoice creation button.</li>
<li>The <em>Show recurring invoices</em> shortcut on contracts shows all invoices created from the
contract.</li>
<li>Click on <em>Print &gt; Contract</em> menu to print contract report.</li>
<li>Click on <em>Send by Email</em> button to send contract by email.</li>
<li>The contract report can be printed from the Print menu</li>
<li>The contract can be sent by email with the <em>Send by Email</em> button</li>
<li>Contract templates can be created from the Configuration -&gt; Contracts -&gt; Contract Templates menu.
They allow to define default journal, price list and lines when creating a contract.
To use it, just select the template on the contract and fields will be filled automatically.</li>
</ol>
</div>
<div class="section" id="known-issues-roadmap">
@@ -456,6 +455,9 @@ If you spotted it first, help us smashing it by providing a detailed and welcome
<li>Vicent Cubells &lt;<a class="reference external" href="mailto:vicent.cubells&#64;tecnativa.com">vicent.cubells&#64;tecnativa.com</a>&gt;</li>
<li>Miquel Raïch &lt;<a class="reference external" href="mailto:miquel.raich&#64;eficent.com">miquel.raich&#64;eficent.com</a>&gt;</li>
<li>Souheil Bejaoui &lt;<a class="reference external" href="mailto:souheil.bejaoui&#64;acsone.eu">souheil.bejaoui&#64;acsone.eu</a>&gt;</li>
<li>Thomas Binsfeld &lt;<a class="reference external" href="mailto:thomas.binsfeld&#64;acsone.eu">thomas.binsfeld&#64;acsone.eu</a>&gt;</li>
<li>Rafael Blasco &lt;<a class="reference external" href="mailto:rafael.blasco&#64;tecnativa.com">rafael.blasco&#64;tecnativa.com</a>&gt;</li>
<li>Guillaume Vandamme &lt;<a class="reference external" href="mailto:guillaume.vandamme&#64;acsone.eu">guillaume.vandamme&#64;acsone.eu</a>&gt;</li>
</ul>
</div>
<div class="section" id="maintainers">

View File

@@ -3,7 +3,7 @@
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo>
<record id="view_partner_form" model="ir.ui.view">
<record id="view_partner_form" model="ir.ui.view">
<field name="inherit_id" ref="base.view_partner_form" />
<field name="model">res.partner</field>
<field type="xml" name="arch">
@@ -21,7 +21,22 @@
<field name="purchase_contract_count" widget="statinfo" string="Purchase Contracts"/>
</button>
</xpath>
</field>
</record>
</field>
</record>
<record id="view_res_partner_filter" model="ir.ui.view">
<field name="inherit_id" ref="base.view_res_partner_filter" />
<field name="model">res.partner</field>
<field type="xml" name="arch">
<filter name="inactive" position="after">
<separator/>
<filter
name="filter_running_contract"
string="With running contracts"
domain="['|', ('contract_ids.date_end', '&gt;=', context_today().strftime('%Y-%m-%d')), ('contract_ids.date_end', '=', False)]"
/>
</filter>
</field>
</record>
</odoo>