From 3169811e6f5d35cc1769fb162672f828057f13f5 Mon Sep 17 00:00:00 2001 From: Administrator Date: Thu, 19 Dec 2019 22:06:41 +0200 Subject: [PATCH] {MIG} contract: Migration to 13.0 from 12.0.5.0 --- contract/__manifest__.py | 4 +- .../migrations/12.0.2.0.0/pre-migration.py | 94 ----- .../migrations/12.0.4.0.0/post-migration.py | 63 ---- .../migrations/12.0.4.0.0/pre-migration.py | 147 -------- contract/models/__init__.py | 4 +- contract/models/abstract_contract.py | 21 +- contract/models/abstract_contract_line.py | 106 ++++-- contract/models/account_invoice.py | 12 - contract/models/account_move.py | 12 + ...t_invoice_line.py => account_move_line.py} | 4 +- contract/models/contract.py | 152 +++----- contract/models/contract_line.py | 334 ++++++++++++------ contract/models/contract_template_line.py | 1 - contract/tests/test_contract.py | 4 +- contract/views/abstract_contract_line.xml | 6 +- contract/views/account_move.xml | 28 ++ contract/views/contract.xml | 28 +- contract/views/contract_line.xml | 10 +- contract/views/contract_template.xml | 1 - contract/views/res_partner_view.xml | 6 +- contract/wizards/contract_line_wizard.py | 4 - 21 files changed, 453 insertions(+), 588 deletions(-) delete mode 100644 contract/migrations/12.0.2.0.0/pre-migration.py delete mode 100644 contract/migrations/12.0.4.0.0/post-migration.py delete mode 100644 contract/migrations/12.0.4.0.0/pre-migration.py delete mode 100644 contract/models/account_invoice.py create mode 100644 contract/models/account_move.py rename contract/models/{account_invoice_line.py => account_move_line.py} (75%) create mode 100644 contract/views/account_move.xml diff --git a/contract/__manifest__.py b/contract/__manifest__.py index 274ab732..1c452aca 100644 --- a/contract/__manifest__.py +++ b/contract/__manifest__.py @@ -4,12 +4,12 @@ # Copyright 2016-2018 Tecnativa - Carlos Dauden # Copyright 2017 Tecnativa - Vicent Cubells # Copyright 2016-2017 LasLabs Inc. -# Copyright 2018 ACSONE SA/NV +# Copyright 2018-2019 ACSONE SA/NV # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { 'name': 'Recurring - Contracts Management', - 'version': '12.0.4.2.5', + 'version': '13.0.1.0.0', #from 12.0.5.0.0 'category': 'Contract Management', 'license': 'AGPL-3', 'author': "OpenERP SA, " diff --git a/contract/migrations/12.0.2.0.0/pre-migration.py b/contract/migrations/12.0.2.0.0/pre-migration.py deleted file mode 100644 index 87de6704..00000000 --- a/contract/migrations/12.0.2.0.0/pre-migration.py +++ /dev/null @@ -1,94 +0,0 @@ -# Copyright 2018 ACSONE SA/NV -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -import logging - -from openupgradelib import openupgrade - -_logger = logging.getLogger(__name__) - - -def _set_finished_contract(cr): - _logger.info("set recurring_next_date to false for finished contract") - openupgrade.logged_query( - cr, - """ - UPDATE account_analytic_account - SET recurring_next_date=NULL - WHERE recurring_next_date > date_end - """, - ) - - -def _move_contract_recurrence_info_to_contract_line(cr): - _logger.info("Move contract data to line level") - openupgrade.logged_query( - cr, - """ - ALTER TABLE account_analytic_invoice_line - ADD COLUMN IF NOT EXISTS recurring_rule_type VARCHAR(255), - ADD COLUMN IF NOT EXISTS recurring_invoicing_type VARCHAR(255), - ADD COLUMN IF NOT EXISTS recurring_interval INTEGER, - ADD COLUMN IF NOT EXISTS recurring_next_date DATE, - ADD COLUMN IF NOT EXISTS date_start DATE, - ADD COLUMN IF NOT EXISTS date_end DATE - """, - ) - - openupgrade.logged_query( - cr, - """ - UPDATE account_analytic_invoice_line AS contract_line - SET - recurring_rule_type=contract.recurring_rule_type, - recurring_invoicing_type=contract.recurring_invoicing_type, - recurring_interval=contract.recurring_interval, - recurring_next_date=contract.recurring_next_date, - date_start=contract.date_start, - date_end=contract.date_end - FROM - account_analytic_account AS contract - WHERE - contract.id=contract_line.analytic_account_id - """, - ) - - -def _move_contract_template_recurrence_info_to_contract_template_line(cr): - _logger.info("Move contract template data to line level") - openupgrade.logged_query( - cr, - """ - ALTER TABLE account_analytic_contract_line - ADD COLUMN IF NOT EXISTS recurring_rule_type VARCHAR(255), - ADD COLUMN IF NOT EXISTS recurring_invoicing_type VARCHAR(255), - ADD COLUMN IF NOT EXISTS recurring_interval INTEGER - """, - ) - - openupgrade.logged_query( - cr, - """ - UPDATE account_analytic_contract_line AS contract_template_line - SET - recurring_rule_type=contract_template.recurring_rule_type, - recurring_invoicing_type=contract_template.recurring_invoicing_type, - recurring_interval=contract_template.recurring_interval - FROM - account_analytic_contract AS contract_template - WHERE - contract_template.id=contract_template_line.analytic_account_id - """, - ) - - -@openupgrade.migrate() -def migrate(env, version): - """ - set recurring_next_date to false for finished contract - """ - _logger.info(">> Pre-Migration 12.0.2.0.0") - cr = env.cr - _set_finished_contract(cr) - _move_contract_recurrence_info_to_contract_line(cr) - _move_contract_template_recurrence_info_to_contract_template_line(cr) diff --git a/contract/migrations/12.0.4.0.0/post-migration.py b/contract/migrations/12.0.4.0.0/post-migration.py deleted file mode 100644 index 57d48ae8..00000000 --- a/contract/migrations/12.0.4.0.0/post-migration.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright 2019 ACSONE SA/NV -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -import logging - -from openupgradelib import openupgrade -from odoo.tools import parse_version - -_logger = logging.getLogger(__name__) - - -def _update_no_update_ir_cron(env): - # Update ir.cron - env.ref('contract.contract_cron_for_invoice').model_id = env.ref( - 'contract.model_contract_contract' - ) - env.ref('contract.contract_line_cron_for_renew').model_id = env.ref( - 'contract.model_contract_line' - ) - env.ref('contract.email_contract_template').model_id = env.ref( - 'contract.model_contract_contract' - ) - - -def _init_last_date_invoiced_on_contract_lines(env): - _logger.info("init last_date_invoiced field for contract lines") - contract_lines = env["contract.line"].search( - [("recurring_next_date", "!=", False)] - ) - contract_lines._init_last_date_invoiced() - - -def _init_invoicing_partner_id_on_contracts(env): - _logger.info("Populate invoicing partner field on contracts") - contracts = env["contract.contract"].search([]) - contracts._inverse_partner_id() - - -def assign_salesman(env): - """As v11 takes salesman from linked partner and now the salesman is a - field in the contract that is initialized to current user, we need - to assign to the recently converted contracts following old logic, or they - will have admin as responsible. - """ - openupgrade.logged_query( - env.cr, """ - UPDATE contract_contract cc - SET user_id = rp.user_id - FROM res_partner rp - WHERE rp.id = cc.partner_id""", - ) - - -@openupgrade.migrate() -def migrate(env, version): - _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 - # it was a PR - _init_last_date_invoiced_on_contract_lines(env) - _init_invoicing_partner_id_on_contracts(env) - assign_salesman(env) diff --git a/contract/migrations/12.0.4.0.0/pre-migration.py b/contract/migrations/12.0.4.0.0/pre-migration.py deleted file mode 100644 index a99aa2ab..00000000 --- a/contract/migrations/12.0.4.0.0/pre-migration.py +++ /dev/null @@ -1,147 +0,0 @@ -# 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), - ), - ) - # Deactivate disabled contracts - openupgrade.logged_query( - cr, """UPDATE contract_contract cc - SET active = False - FROM account_analytic_account aaa - WHERE aaa.id = cc.id - AND NOT aaa.recurring_invoices""", - ) - # Handle id sequence - cr.execute("CREATE SEQUENCE IF NOT EXISTS contract_contract_id_seq") - cr.execute("SELECT setval('contract_contract_id_seq', " - "(SELECT MAX(id) FROM contract_contract))") - cr.execute("ALTER TABLE contract_contract ALTER id " - "SET DEFAULT NEXTVAL('contract_contract_id_seq')") - # Move common stuff from one table to the other - mapping = [ - ('ir_attachment', 'res_model', 'res_id'), - ('mail_message', 'model', 'res_id'), - ('mail_activity', 'res_model', 'res_id'), - ('mail_followers', 'res_model', 'res_id'), - ] - for table, model_column, id_column in mapping: - openupgrade.logged_query( - cr, sql.SQL(""" - UPDATE {table} SET {model_column}='contract.contract' - WHERE {model_column}='account.analytic.account' - AND {id_column} IN (SELECT DISTINCT {col} FROM contract_line) - """).format( - table=sql.Identifier(table), - model_column=sql.Identifier(model_column), - id_column=sql.Identifier(id_column), - col=sql.Identifier(contract_field_name), - ), - ) - - -@openupgrade.migrate() -def migrate(env, version): - cr = env.cr - openupgrade.rename_models(cr, models_to_rename) - openupgrade.rename_tables(cr, tables_to_rename) - openupgrade.rename_xmlids(cr, xmlids_to_rename) - openupgrade.copy_columns(cr, columns_to_copy) - create_contract_records(cr) diff --git a/contract/models/__init__.py b/contract/models/__init__.py index 28dc9922..f7652267 100644 --- a/contract/models/__init__.py +++ b/contract/models/__init__.py @@ -6,6 +6,6 @@ from . import contract_template from . import contract from . import contract_template_line from . import contract_line -from . import account_invoice -from . import account_invoice_line +from . import account_move_line +from . import account_move from . import res_partner diff --git a/contract/models/abstract_contract.py b/contract/models/abstract_contract.py index 398d79d4..0552eb02 100644 --- a/contract/models/abstract_contract.py +++ b/contract/models/abstract_contract.py @@ -16,14 +16,27 @@ class ContractAbstractContract(models.AbstractModel): # These fields will not be synced to the contract NO_SYNC = ['name', 'partner_id', 'company_id'] + @api.model + def _get_default_invoice_incoterm(self): + ''' Get the default incoterm for invoice. ''' + return self.env.company.incoterm_id + + incoterm_id = fields.Many2one('account.incoterms', string='Incoterm', + default=_get_default_invoice_incoterm, + help='International Commercial Terms are a series of predefined commercial terms used in international transactions.') + name = fields.Char(required=True) + date = fields.Date( string='Signed Date', default=lambda self: fields.Date.today()) + # Needed for avoiding errors on several inherited behaviors partner_id = fields.Many2one( comodel_name="res.partner", string="Partner", index=True ) - pricelist_id = fields.Many2one( - comodel_name='product.pricelist', string='Pricelist' - ) + pricelist_id = fields.Many2one('product.pricelist', string='Pricelist') + currency_id = fields.Many2one( + related="pricelist_id.currency_id", + string="Pricelist currency", + store=True, ) contract_type = fields.Selection( selection=[('sale', 'Customer'), ('purchase', 'Supplier')], default='sale', @@ -47,6 +60,8 @@ class ContractAbstractContract(models.AbstractModel): ), ) + + @api.onchange('contract_type') def _onchange_contract_type(self): if self.contract_type == 'purchase': diff --git a/contract/models/abstract_contract_line.py b/contract/models/abstract_contract_line.py index c1320208..ff7dcb07 100644 --- a/contract/models/abstract_contract_line.py +++ b/contract/models/abstract_contract_line.py @@ -16,43 +16,43 @@ class ContractAbstractContractLine(models.AbstractModel): _name = 'contract.abstract.contract.line' _description = 'Abstract Recurring Contract Line' - product_id = fields.Many2one( - 'product.product', string='Product', required=True - ) + product_id = fields.Many2one('product.product', string='Product', required=True) name = fields.Text(string='Description', required=True) quantity = fields.Float(default=1.0, required=True) - uom_id = fields.Many2one( - 'uom.uom', string='Unit of Measure', required=True - ) - automatic_price = fields.Boolean( - string="Auto-price?", + uom_id = fields.Many2one('uom.uom', string='Unit of Measure', required=True) + automatic_price = fields.Boolean(string="Auto-price", help="If this is marked, the price will be obtained automatically " "applying the pricelist to the product. If not, you will be " "able to introduce a manual price", ) specific_price = fields.Float(string='Specific Price') - price_unit = fields.Float( - string='Unit Price', + price_unit = fields.Float(string='Unit Price', compute="_compute_price_unit", inverse="_inverse_price_unit", ) - price_subtotal = fields.Float( - compute='_compute_price_subtotal', - digits=dp.get_precision('Account'), - string='Sub Total', - ) - discount = fields.Float( - string='Discount (%)', - digits=dp.get_precision('Discount'), - help='Discount that is applied in generated invoices.' - ' It should be less or equal to 100', - ) + tax_id = fields.Many2many('account.tax', string='Taxes', domain=['|', ('active', '=', False), ('active', '=', True)]) + + contract_id = fields.Many2one(string='Contract', comodel_name='contract.contract', required=True,ondelete='cascade',) + + pricelist_id = fields.Many2one(related='contract_id.pricelist_id', depends=['contract_id'], string='Pricelist',store=True) + currency_id = fields.Many2one(related='contract_id.currency_id', depends=['contract_id'], store=True, string='Currency') + company_id = fields.Many2one(related='contract_id.company_id', string='Company', readonly=True, index=True) + + + + price_subtotal = fields.Monetary(compute='_compute_amount', string='Subtotal', readonly=True, store=True,currency_field='currency_id') + price_tax = fields.Float(compute='_compute_amount', string='Total Tax', readonly=True, store=True,currency_field='currency_id') + price_total = fields.Monetary(compute='_compute_amount', string='Total', readonly=True, store=True,currency_field='currency_id') + + # discount does not have any sense because can be a discounted pricelist or directly in price + discount = fields.Float(string='Discount (%)',digits=dp.get_precision('Discount'),help='Discount that is applied in generated invoices.'' It should be less or equal to 100',) sequence = fields.Integer( string="Sequence", default=10, help="Sequence of the contract line when displaying contracts", ) + recurring_rule_type = fields.Selection( [ ('daily', 'Day(s)'), @@ -70,9 +70,17 @@ class ContractAbstractContractLine(models.AbstractModel): [('pre-paid', 'Pre-paid'), ('post-paid', 'Post-paid')], default='pre-paid', string='Invoicing type', - help="Specify if process date is 'from' or 'to' invoicing date", + help="Specify if the invoice must be generated at the beginning (pre-paid) or end (post-paid) of the period.", required=True, ) + recurring_invoicing_offset = fields.Integer( + compute="_compute_recurring_invoicing_offset", + string="Invoicing offset", + help=( + "Number of days to offset the invoice from the period end " + "date (in post-paid mode) or start date (in pre-paid mode)." + ) + ) recurring_interval = fields.Integer( default=1, string='Invoice Every', @@ -108,12 +116,27 @@ class ContractAbstractContractLine(models.AbstractModel): default='monthly', string='Termination Notice type', ) - contract_id = fields.Many2one( - string='Contract', - comodel_name='contract.abstract.contract', - required=True, - ondelete='cascade', - ) + + @api.model + def _get_default_recurring_invoicing_offset( + self, recurring_invoicing_type, recurring_rule_type + ): + if ( + recurring_invoicing_type == 'pre-paid' + or recurring_rule_type == 'monthlylastday' + ): + return 0 + else: + return 1 + + @api.depends('recurring_invoicing_type', 'recurring_rule_type') + def _compute_recurring_invoicing_offset(self): + for rec in self: + rec.recurring_invoicing_offset = ( + self._get_default_recurring_invoicing_offset( + rec.recurring_invoicing_type, rec.recurring_rule_type + ) + ) @api.depends( 'automatic_price', @@ -151,20 +174,32 @@ class ContractAbstractContractLine(models.AbstractModel): for line in self.filtered(lambda x: not x.automatic_price): line.specific_price = line.price_unit - @api.multi - @api.depends('quantity', 'price_unit', 'discount') - def _compute_price_subtotal(self): + @api.depends('quantity', 'price_unit', 'discount','tax_id') + def _compute_amount(self): + """ + Compute the amounts of the contract line. + """ for line in self: + price = line.price_unit * (1 - (line.discount or 0.0) / 100.0) + taxes = line.tax_id.compute_all(price, line.contract_id.currency_id, line.quantity, product=line.product_id, partner=line.contract_id.partner_id) +# print(f"{line}") + line.update({ + 'price_tax': sum(t.get('amount', 0.0) for t in taxes.get('taxes', [])), + 'price_total': taxes['total_included'], + 'price_subtotal': taxes['total_excluded'], + }) + subtotal = line.quantity * line.price_unit discount = line.discount / 100 subtotal *= 1 - discount - if line.contract_id.pricelist_id: + if line.contract_id.pricelist_id: cur = line.contract_id.pricelist_id.currency_id + line.price_subtotal = subtotal line.price_subtotal = cur.round(subtotal) else: line.price_subtotal = subtotal - @api.multi + @api.constrains('discount') def _check_discount(self): for line in self: @@ -173,7 +208,6 @@ class ContractAbstractContractLine(models.AbstractModel): _("Discount should be less or equal to 100") ) - @api.multi @api.onchange('product_id') def _onchange_product_id(self): if not self.product_id: @@ -201,6 +235,8 @@ class ContractAbstractContractLine(models.AbstractModel): uom=self.uom_id.id, ) vals['name'] = self.product_id.get_product_multiline_description_sale() - vals['price_unit'] = product.price + vals['price_unit'] = product.lst_price # or .price? + vals['tax_id'] = product.taxes_id if self.contract_id.contract_type == 'sale' else product.supplier_taxes_id + self.update(vals) return {'domain': domain} diff --git a/contract/models/account_invoice.py b/contract/models/account_invoice.py deleted file mode 100644 index 3510c354..00000000 --- a/contract/models/account_invoice.py +++ /dev/null @@ -1,12 +0,0 @@ -# © 2016 Carlos Dauden -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo import fields, models - - -class AccountInvoice(models.Model): - _inherit = 'account.invoice' - - # We keep this field for migration purpose - old_contract_id = fields.Many2one( - 'contract.contract', oldname='contract_id') diff --git a/contract/models/account_move.py b/contract/models/account_move.py new file mode 100644 index 00000000..c3e8a7a7 --- /dev/null +++ b/contract/models/account_move.py @@ -0,0 +1,12 @@ +# Copyright 2018 ACSONE SA/NV. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class AccountMove(models.Model): + _inherit = 'account.move' + + contract_id = fields.Many2one('contract.contract', string='Generated by contract', index=True, readonly=True) + + diff --git a/contract/models/account_invoice_line.py b/contract/models/account_move_line.py similarity index 75% rename from contract/models/account_invoice_line.py rename to contract/models/account_move_line.py index 31454e41..f894752e 100644 --- a/contract/models/account_invoice_line.py +++ b/contract/models/account_move_line.py @@ -4,8 +4,8 @@ from odoo import fields, models -class AccountInvoiceLine(models.Model): - _inherit = 'account.invoice.line' +class AccountMoveLine(models.Model): + _inherit = 'account.move.line' contract_line_id = fields.Many2one( 'contract.line', string='Contract Line', index=True diff --git a/contract/models/contract.py b/contract/models/contract.py index 481a64c9..f8a3d98f 100644 --- a/contract/models/contract.py +++ b/contract/models/contract.py @@ -7,7 +7,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo import api, fields, models -from odoo.exceptions import ValidationError +from odoo.exceptions import ValidationError,UserError from odoo.tools.translate import _ @@ -21,9 +21,7 @@ class ContractContract(models.Model): 'contract.abstract.contract', ] - active = fields.Boolean( - default=True, - ) + active = fields.Boolean( default=True,) code = fields.Char( string="Reference", ) @@ -32,11 +30,7 @@ class ContractContract(models.Model): comodel_name='account.analytic.account', ondelete='restrict', ) - currency_id = fields.Many2one( - related="company_id.currency_id", - string="Currency", - readonly=True, - ) + contract_template_id = fields.Many2one( string='Contract Template', comodel_name='contract.template' ) @@ -92,7 +86,6 @@ class ContractContract(models.Model): index=True ) - @api.multi def _inverse_partner_id(self): for rec in self: if not rec.invoice_partner_id: @@ -100,54 +93,32 @@ class ContractContract(models.Model): ['invoice'] )['invoice'] - @api.multi def _get_related_invoices(self): self.ensure_one() - invoices = ( - self.env['account.invoice.line'] - .search( - [ - ( - 'contract_line_id', - 'in', - self.contract_line_ids.ids, - ) - ] - ) - .mapped('invoice_id') - ) - invoices |= self.env['account.invoice'].search( - [('old_contract_id', '=', self.id)] - ) + self.env['account.move'].search([('contract_id','=',self.id),]) ) return invoices - @api.multi def _compute_invoice_count(self): for rec in self: rec.invoice_count = len(rec._get_related_invoices()) - @api.multi def action_show_invoices(self): self.ensure_one() tree_view_ref = ( - 'account.invoice_supplier_tree' - if self.contract_type == 'purchase' - else 'account.invoice_tree_with_onboarding' + 'account.move_supplier_tree' if self.contract_type == 'purchase' else 'account.move_tree_with_onboarding' ) form_view_ref = ( - 'account.invoice_supplier_form' - if self.contract_type == 'purchase' - else 'account.invoice_form' + 'account.move_supplier_form' if self.contract_type == 'purchase' else 'account.move_form' ) tree_view = self.env.ref(tree_view_ref, raise_if_not_found=False) form_view = self.env.ref(form_view_ref, raise_if_not_found=False) action = { 'type': 'ir.actions.act_window', 'name': 'Invoices', - 'res_model': 'account.invoice', + 'res_model': 'account.move', 'view_type': 'form', - 'view_mode': 'tree,kanban,form,calendar,pivot,graph,activity', + 'view_mode': 'tree,form,activity', 'domain': [('id', 'in', self._get_related_invoices().ids)], } if tree_view and form_view: @@ -173,7 +144,7 @@ class ContractContract(models.Model): ).mapped('recurring_next_date') if recurring_next_date: contract.recurring_next_date = min(recurring_next_date) - + @api.depends('contract_line_ids.create_invoice_visibility') def _compute_create_invoice_visibility(self): for contract in self: @@ -234,7 +205,6 @@ class ContractContract(models.Model): } } - @api.multi def _convert_contract_lines(self, contract): self.ensure_one() new_lines = self.env['contract.line'] @@ -252,49 +222,55 @@ class ContractContract(models.Model): new_lines._onchange_is_auto_renew() return new_lines - @api.multi + def _prepare_invoice(self, date_invoice, journal=None): + """ + Taken from sale/models/sale.py + Prepare the dict of values to create the new invoice for a sales order. This method may be + overridden to implement custom invoice generation (making sure to call super() to establish + a clean extension chain). + """ self.ensure_one() + if self.contract_type == 'sale': + invoice_type = 'out_invoice' + else: #purchase + invoice_type = 'in_invoice' if not journal: - journal = ( - self.journal_id - if self.journal_id.type == self.contract_type - else self.env['account.journal'].search( - [ - ('type', '=', self.contract_type), - ('company_id', '=', self.company_id.id), - ], - limit=1, - ) - ) - if not journal: - raise ValidationError( - _("Please define a %s journal for the company '%s'.") - % (self.contract_type, self.company_id.name or '') - ) + if self.journal_id: + journal = self.journal_id + else: + journal = self.env['account.move'].with_context(force_company=self.company_id.id, default_type=invoice_type)._get_default_journal() + if not journal: + raise UserError(_('Please define an accounting %s journal for the company %s (%s).') % (self.contract_type, self.company_id.name, self.company_id.id)) + currency = ( self.pricelist_id.currency_id or self.partner_id.property_product_pricelist.currency_id or self.company_id.currency_id ) - invoice_type = 'out_invoice' - if self.contract_type == 'purchase': - invoice_type = 'in_invoice' - return { - 'name': self.code, + + invoice_vals = { + 'name' : '/', + 'date': date_invoice, + 'ref': '%s contract id=%s'%(self.contract_type,self.id), 'type': invoice_type, - 'partner_id': self.invoice_partner_id.id, - 'currency_id': currency.id, - 'date_invoice': date_invoice, 'journal_id': journal.id, - 'origin': self.name, + 'invoice_origin': 'Contract'+self.name+'id='+str(self.id), 'company_id': self.company_id.id, + 'currency_id': currency.id, + 'invoice_user_id': self.user_id and self.user_id.id, + 'partner_id': self.invoice_partner_id.id, +# 'partner_shipping_id': self.partner_id.id, + 'fiscal_position_id': self.fiscal_position_id.id or self.invoice_partner_id.property_account_position_id.id, + 'invoice_incoterm_id': self.incoterm_id.id, + 'invoice_payment_term_id': self.payment_term_id.id, + 'invoice_line_ids': [], 'user_id': self.user_id.id, - 'payment_term_id': self.payment_term_id.id, - 'fiscal_position_id': self.fiscal_position_id.id, + 'contract_id':self.id, } + return invoice_vals + - @api.multi def action_contract_send(self): self.ensure_one() template = self.env.ref('contract.email_contract_template', False) @@ -335,31 +311,24 @@ class ContractContract(models.Model): # taken from the invoice's journal in _onchange_product_id # This code is not in finalize_creation_from_contract because it's # not possible to create an invoice line with no account - new_invoice = self.env['account.invoice'].new(invoice_values) - for invoice_line in new_invoice.invoice_line_ids: + new_invoice = self.env['account.move'].new(invoice_values) + + + for invoice_line in new_invoice.line_ids: name = invoice_line.name - account_analytic_id = invoice_line.account_analytic_id + analytic_account_id = invoice_line.analytic_account_id price_unit = invoice_line.price_unit invoice_line.invoice_id = new_invoice invoice_line._onchange_product_id() invoice_line.update( { - 'name': name, - 'account_analytic_id': account_analytic_id, - 'price_unit': price_unit, - } - ) + 'name': name, + 'analytic_account_id': analytic_account_id, + 'price_unit': price_unit, + } ) + return new_invoice._convert_to_record(new_invoice._cache) return new_invoice._convert_to_write(new_invoice._cache) - @api.model - def _finalize_invoice_creation(self, invoices): - for invoice in invoices: - payment_term = invoice.payment_term_id - fiscal_position = invoice.fiscal_position_id - invoice._onchange_partner_id() - invoice.payment_term_id = payment_term - invoice.fiscal_position_id = fiscal_position - invoices.compute_taxes() @api.model def _finalize_and_create_invoices(self, invoices_values): @@ -369,7 +338,7 @@ class ContractContract(models.Model): - creates the invoices - finalizes the created invoices (onchange's, tax computation...) :param invoices_values: list of dictionaries (invoices values) - :return: created invoices (account.invoice) + :return: created invoices (account.move) """ if isinstance(invoices_values, dict): invoices_values = [invoices_values] @@ -378,8 +347,7 @@ class ContractContract(models.Model): final_invoices_values.append( self._finalize_invoice_values(invoice_values) ) - invoices = self.env['account.invoice'].create(final_invoices_values) - self._finalize_invoice_creation(invoices) + invoices = self.env['account.move'].create(final_invoices_values) return invoices @api.model @@ -396,7 +364,6 @@ class ContractContract(models.Model): domain.extend([('recurring_next_date', '<=', date_ref)]) return domain - @api.multi def _get_lines_to_invoice(self, date_ref): """ This method fetches and returns the lines to invoice on the contract @@ -411,7 +378,6 @@ class ContractContract(models.Model): and l.recurring_next_date <= date_ref ) - @api.multi def _prepare_recurring_invoices_values(self, date_ref=False): """ This method builds the list of invoices values to create, based on @@ -433,9 +399,7 @@ class ContractContract(models.Model): invoice_values = contract._prepare_invoice(date_ref) for line in contract_lines: invoice_values.setdefault('invoice_line_ids', []) - invoice_line_values = line._prepare_invoice_line( - invoice_id=False - ) + invoice_line_values = line._prepare_invoice_line() # function in contact_line if invoice_line_values: invoice_values['invoice_line_ids'].append( (0, 0, invoice_line_values) @@ -444,7 +408,6 @@ class ContractContract(models.Model): contract_lines._update_recurring_next_date() return invoices_values - @api.multi def recurring_create_invoice(self): """ This method triggers the creation of the next invoices of the contracts @@ -452,7 +415,6 @@ class ContractContract(models.Model): """ return self._recurring_create_invoice() - @api.multi def _recurring_create_invoice(self, date_ref=False): invoices_values = self._prepare_recurring_invoices_values(date_ref) return self._finalize_and_create_invoices(invoices_values) diff --git a/contract/models/contract_line.py b/contract/models/contract_line.py index 3178f240..854720ec 100644 --- a/contract/models/contract_line.py +++ b/contract/models/contract_line.py @@ -16,21 +16,15 @@ class ContractLine(models.Model): _description = "Contract Line" _inherit = 'contract.abstract.contract.line' - sequence = fields.Integer( - string="Sequence", - ) - contract_id = fields.Many2one( - comodel_name='contract.contract', - string='Contract', - required=True, - index=True, - auto_join=True, - ondelete='cascade', - ) + display_type = fields.Selection([ + ('line_section', "Section"), + ('line_note', "Note")], default=False, help="Technical field for UX purpose.") + analytic_account_id = fields.Many2one( string="Analytic account", comodel_name='account.analytic.account', ) + date_start = fields.Date( string='Date Start', required=True, @@ -41,6 +35,14 @@ class ContractLine(models.Model): last_date_invoiced = fields.Date( string='Last Date Invoiced', readonly=True, copy=False ) + next_period_date_start = fields.Date( + string='Next Period Start', + compute='_compute_next_period_date_start', + ) + next_period_date_end = fields.Date( + string='Next Period End', + compute='_compute_next_period_date_end', + ) termination_notice_date = fields.Date( string='Termination notice date', compute="_compute_termination_notice_date", @@ -112,7 +114,6 @@ class ContractLine(models.Model): default=True, ) - @api.multi @api.depends( 'date_end', 'termination_notice_rule_type', @@ -129,7 +130,6 @@ class ContractLine(models.Model): ) ) - @api.multi @api.depends('is_canceled', 'date_start', 'date_end', 'is_auto_renew') def _compute_state(self): today = fields.Date.context_today(self) @@ -361,20 +361,140 @@ class ContractLine(models.Model): date_start, recurring_invoicing_type, recurring_rule_type, + recurring_interval + ): + # deprecated method for backward compatibility + return self.get_next_invoice_date( + date_start, + recurring_invoicing_type, + self._get_default_recurring_invoicing_offset( + recurring_invoicing_type, recurring_rule_type + ), + recurring_rule_type, + recurring_interval, + max_date_end=False, + ) + @api.model + def get_next_invoice_date( + self, + next_period_date_start, + recurring_invoicing_type, + recurring_invoicing_offset, + recurring_rule_type, recurring_interval, + max_date_end, ): - if recurring_rule_type == 'monthlylastday': - return date_start + self.get_relative_delta( - recurring_rule_type, recurring_interval - 1 - ) - if recurring_invoicing_type == 'pre-paid': - return date_start - return date_start + self.get_relative_delta( - recurring_rule_type, recurring_interval + next_period_date_end = self.get_next_period_date_end( + next_period_date_start, + recurring_rule_type, + recurring_interval, + max_date_end=max_date_end, ) + if not next_period_date_end: + return False + if recurring_invoicing_type == 'pre-paid': + recurring_next_date = ( + next_period_date_start + + relativedelta(days=recurring_invoicing_offset) + ) + else: # post-paid + recurring_next_date = ( + next_period_date_end + + relativedelta(days=recurring_invoicing_offset) + ) + return recurring_next_date + + @api.model + def get_next_period_date_end( + self, + next_period_date_start, + recurring_rule_type, + recurring_interval, + max_date_end, + next_invoice_date=False, + recurring_invoicing_type=False, + recurring_invoicing_offset=False, + ): + """Compute the end date for the next period. + + The next period normally depends on recurrence options only. + It is however possible to provide it a next invoice date, in + which case this method can adjust the next period based on that + too. In that scenario it required the invoicing type and offset + arguments. + """ + if not next_period_date_start: + return False + if max_date_end and next_period_date_start > max_date_end: + # start is past max date end: there is no next period + return False + if not next_invoice_date: + # regular algorithm + next_period_date_end = ( + next_period_date_start + + self.get_relative_delta( + recurring_rule_type, recurring_interval + ) + - relativedelta(days=1) + ) + else: + # special algorithm when the next invoice date is forced + if recurring_invoicing_type == 'pre-paid': + next_period_date_end = ( + next_invoice_date + - relativedelta(days=recurring_invoicing_offset) + + self.get_relative_delta( + recurring_rule_type, recurring_interval + ) + - relativedelta(days=1) + ) + else: # post-paid + next_period_date_end = ( + next_invoice_date + - relativedelta(days=recurring_invoicing_offset) + ) + if max_date_end and next_period_date_end > max_date_end: + # end date is past max_date_end: trim it + next_period_date_end = max_date_end + return next_period_date_end + + @api.depends('last_date_invoiced', 'date_start', 'date_end') + def _compute_next_period_date_start(self): + for rec in self: + if rec.last_date_invoiced: + next_period_date_start = ( + rec.last_date_invoiced + relativedelta(days=1) + ) + else: + next_period_date_start = rec.date_start + if rec.date_end and next_period_date_start > rec.date_end: + next_period_date_start = False + rec.next_period_date_start = next_period_date_start + + + @api.depends( + 'next_period_date_start', + 'recurring_invoicing_type', + 'recurring_invoicing_offset', + 'recurring_rule_type', + 'recurring_interval', + 'date_end', + 'recurring_next_date', + ) + def _compute_next_period_date_end(self): + for rec in self: + rec.next_period_date_end = self.get_next_period_date_end( + rec.next_period_date_start, + rec.recurring_rule_type, + rec.recurring_interval, + max_date_end=rec.date_end, + next_invoice_date=rec.recurring_next_date, + recurring_invoicing_type=rec.recurring_invoicing_type, + recurring_invoicing_offset=rec.recurring_invoicing_offset, + ) @api.model - def compute_first_date_end( + def _get_first_date_end( self, date_start, auto_renew_rule_type, auto_renew_interval ): return ( @@ -404,17 +524,20 @@ class ContractLine(models.Model): @api.onchange( 'date_start', + 'date_end', 'recurring_invoicing_type', 'recurring_rule_type', 'recurring_interval', ) def _onchange_date_start(self): for rec in self.filtered('date_start'): - rec.recurring_next_date = self._compute_first_recurring_next_date( + rec.recurring_next_date = self.get_next_invoice_date( rec.date_start, rec.recurring_invoicing_type, + rec.recurring_invoicing_offset, rec.recurring_rule_type, rec.recurring_interval, + max_date_end=rec.date_end, ) @api.constrains('is_canceled', 'is_auto_renew') @@ -499,70 +622,61 @@ class ContractLine(models.Model): rec.recurring_next_date ) - @api.multi - def _prepare_invoice_line(self, invoice_id=False): + def _prepare_invoice_line(self): + """ + Prepare the dict of values to create the new invoice line for a contact ( taken from sale/model/sale.py. + """ self.ensure_one() dates = self._get_period_to_invoice( self.last_date_invoiced, self.recurring_next_date ) - invoice_line_vals = { + if self.contract_id.contract_type =='sale': + account_type = 'income' + else: + account_type = 'expense' + + + return { + 'sequence': self.sequence, + 'name': self._insert_markers(dates[0], dates[1]), + 'account_id':self.product_id.product_tmpl_id._get_product_accounts()[account_type].id, + 'product_id': self.product_id.id, - 'quantity': self._get_quantity_to_invoice(*dates), - 'uom_id': self.uom_id.id, + 'product_uom_id': self.uom_id.id, + 'quantity': self._get_quantity_to_invoice(*dates),#self.quantity 'discount': self.discount, - 'contract_line_id': self.id, + 'price_unit': self.price_unit, + 'tax_ids': [(6, 0, self.tax_id.ids)], + 'analytic_account_id': self.analytic_account_id.id, +# 'analytic_tag_ids': [(6, 0, self.product_id.analytic_tag_ids.ids)], + 'contract_line_id': [(4, self.id)], } - if invoice_id: - invoice_line_vals['invoice_id'] = invoice_id.id - invoice_line = self.env['account.invoice.line'].new(invoice_line_vals) - # Get other invoice line values from product onchange - invoice_line._onchange_product_id() - invoice_line_vals = invoice_line._convert_to_write(invoice_line._cache) - # Insert markers - name = self._insert_markers(dates[0], dates[1]) - invoice_line_vals.update( - { - 'name': name, - 'account_analytic_id': self.analytic_account_id.id, - 'price_unit': self.price_unit, - } - ) - return invoice_line_vals - @api.multi + def _get_period_to_invoice( self, last_date_invoiced, recurring_next_date, stop_at_date_end=True ): + # TODO this method can now be removed, since + # TODO self.next_period_date_start/end have the same values self.ensure_one() - first_date_invoiced = False if not recurring_next_date: - return first_date_invoiced, last_date_invoiced, recurring_next_date + return False, False, False first_date_invoiced = ( last_date_invoiced + relativedelta(days=1) if last_date_invoiced else self.date_start ) - if self.recurring_rule_type == 'monthlylastday': - last_date_invoiced = recurring_next_date - else: - if self.recurring_invoicing_type == 'pre-paid': - last_date_invoiced = ( - recurring_next_date - + self.get_relative_delta( - self.recurring_rule_type, self.recurring_interval - ) - - relativedelta(days=1) - ) - else: - last_date_invoiced = recurring_next_date - relativedelta( - days=1 - ) - if stop_at_date_end: - if self.date_end and self.date_end < last_date_invoiced: - last_date_invoiced = self.date_end + last_date_invoiced = self.get_next_period_date_end( + first_date_invoiced, + self.recurring_rule_type, + self.recurring_interval, + max_date_end=(self.date_end if stop_at_date_end else False), + next_invoice_date=recurring_next_date, + recurring_invoicing_type=self.recurring_invoicing_type, + recurring_invoicing_offset=self.recurring_invoicing_offset, + ) return first_date_invoiced, last_date_invoiced, recurring_next_date - @api.multi def _insert_markers(self, first_date_invoiced, last_date_invoiced): self.ensure_one() lang_obj = self.env['res.lang'] @@ -577,28 +691,22 @@ class ContractLine(models.Model): name = name.replace('#END#', last_date_invoiced.strftime(date_format)) return name - @api.multi def _update_recurring_next_date(self): for rec in self: - old_date = rec.recurring_next_date - new_date = old_date + self.get_relative_delta( - rec.recurring_rule_type, rec.recurring_interval + last_date_invoiced = rec.next_period_date_end + recurring_next_date = rec.get_next_invoice_date( + last_date_invoiced + relativedelta(days=1), + rec.recurring_invoicing_type, + rec.recurring_invoicing_offset, + rec.recurring_rule_type, + rec.recurring_interval, + max_date_end=rec.date_end, ) - if rec.recurring_rule_type == 'monthlylastday': - last_date_invoiced = old_date - elif rec.recurring_invoicing_type == 'post-paid': - last_date_invoiced = old_date - relativedelta(days=1) - elif rec.recurring_invoicing_type == 'pre-paid': - last_date_invoiced = new_date - relativedelta(days=1) - - if rec.date_end and last_date_invoiced >= rec.date_end: - rec.last_date_invoiced = rec.date_end - rec.recurring_next_date = False - else: - rec.last_date_invoiced = last_date_invoiced - rec.recurring_next_date = new_date + rec.write({ + "recurring_next_date": recurring_next_date, + "last_date_invoiced": last_date_invoiced, + }) - @api.multi def _init_last_date_invoiced(self): """Used to init last_date_invoiced for migration purpose""" for rec in self: @@ -609,8 +717,9 @@ class ContractLine(models.Model): last_date_invoiced = ( rec.recurring_next_date - self.get_relative_delta( - rec.recurring_rule_type, rec.recurring_interval + rec.recurring_rule_type, rec.recurring_interval - 1 ) + - relativedelta(days=1) ) elif rec.recurring_invoicing_type == 'post-paid': last_date_invoiced = ( @@ -618,12 +727,19 @@ class ContractLine(models.Model): - self.get_relative_delta( rec.recurring_rule_type, rec.recurring_interval ) - ) - relativedelta(days=1) + - relativedelta(days=1) + ) if last_date_invoiced > rec.date_start: rec.last_date_invoiced = last_date_invoiced + @api.model @api.model def get_relative_delta(self, recurring_rule_type, interval): + """Return a relativedelta for one period. + + When added to the first day of the period, + it gives the first day of the next period. + """ if recurring_rule_type == 'daily': return relativedelta(days=interval) elif recurring_rule_type == 'weekly': @@ -631,11 +747,11 @@ class ContractLine(models.Model): elif recurring_rule_type == 'monthly': return relativedelta(months=interval) elif recurring_rule_type == 'monthlylastday': - return relativedelta(months=interval, day=31) + return relativedelta(months=interval, day=1) else: return relativedelta(years=interval) - @api.multi + def _delay(self, delay_delta): """ Delay a contract line @@ -651,17 +767,24 @@ class ContractLine(models.Model): ) ) new_date_start = rec.date_start + delay_delta - rec.recurring_next_date = self._compute_first_recurring_next_date( + if rec.date_end: + new_date_end = rec.date_end + delay_delta + else: + new_date_end = False + new_recurring_next_date = self.get_next_invoice_date( new_date_start, rec.recurring_invoicing_type, + rec.recurring_invoicing_offset, rec.recurring_rule_type, rec.recurring_interval, + max_date_end=new_date_end ) - if rec.date_end: - rec.date_end += delay_delta - rec.date_start = new_date_start + rec.write({ + "date_start": new_date_start, + "date_end": new_date_end, + "recurring_next_date": new_recurring_next_date, + }) - @api.multi def stop(self, date_end, manual_renew_needed=False, post_message=True): """ Put date_end on contract line @@ -706,17 +829,18 @@ class ContractLine(models.Model): ) return True - @api.multi def _prepare_value_for_plan_successor( self, date_start, date_end, is_auto_renew, recurring_next_date=False ): self.ensure_one() if not recurring_next_date: - recurring_next_date = self._compute_first_recurring_next_date( + recurring_next_date = self.get_next_invoice_date( date_start, self.recurring_invoicing_type, + self.recurring_invoicing_offset, self.recurring_rule_type, self.recurring_interval, + max_date_end=date_end, ) new_vals = self.read()[0] new_vals.pop("id", None) @@ -729,7 +853,6 @@ class ContractLine(models.Model): values['predecessor_contract_line_id'] = self.id return values - @api.multi def plan_successor( self, date_start, @@ -777,7 +900,6 @@ class ContractLine(models.Model): rec.contract_id.message_post(body=msg) return contract_line - @api.multi def stop_plan_successor(self, date_start, date_end, is_auto_renew): """ Stop a contract line for a defined period and start it later @@ -885,7 +1007,6 @@ class ContractLine(models.Model): rec.contract_id.message_post(body=msg) return contract_line - @api.multi def cancel(self): if not all(self.mapped('is_cancel_allowed')): raise ValidationError(_('Cancel not allowed for this line')) @@ -906,7 +1027,6 @@ class ContractLine(models.Model): ) return self.write({'is_canceled': True, 'is_auto_renew': False}) - @api.multi def uncancel(self, recurring_next_date): if not all(self.mapped('is_un_cancel_allowed')): raise ValidationError(_('Un-cancel not allowed for this line')) @@ -931,7 +1051,6 @@ class ContractLine(models.Model): rec.recurring_next_date = recurring_next_date return True - @api.multi def action_uncancel(self): self.ensure_one() context = { @@ -953,7 +1072,6 @@ class ContractLine(models.Model): 'context': context, } - @api.multi def action_plan_successor(self): self.ensure_one() context = { @@ -975,7 +1093,6 @@ class ContractLine(models.Model): 'context': context, } - @api.multi def action_stop(self): self.ensure_one() context = { @@ -997,7 +1114,6 @@ class ContractLine(models.Model): 'context': context, } - @api.multi def action_stop_plan_successor(self): self.ensure_one() context = { @@ -1019,16 +1135,14 @@ class ContractLine(models.Model): 'context': context, } - @api.multi def _get_renewal_dates(self): self.ensure_one() date_start = self.date_end + relativedelta(days=1) - date_end = self.compute_first_date_end( + date_end = self._get_first_date_end( date_start, self.auto_renew_rule_type, self.auto_renew_interval ) return date_start, date_end - @api.multi def renew(self): res = self.env['contract.line'] for rec in self: @@ -1055,7 +1169,6 @@ class ContractLine(models.Model): rec.contract_id.message_post(body=msg) return res - @api.model def _contract_line_to_renew_domain(self): return [ ('is_auto_renew', '=', True), @@ -1063,13 +1176,11 @@ class ContractLine(models.Model): ('termination_notice_date', '<=', fields.Date.context_today(self)), ] - @api.model def cron_renew_contract_line(self): domain = self._contract_line_to_renew_domain() to_renew = self.search(domain) to_renew.renew() - @api.model def fields_view_get( self, view_id=None, view_type='form', toolbar=False, submenu=False ): @@ -1091,7 +1202,6 @@ class ContractLine(models.Model): view_id, view_type, toolbar, submenu ) - @api.multi def unlink(self): """stop unlink uncnacled lines""" if not all(self.mapped('is_canceled')): @@ -1100,9 +1210,9 @@ class ContractLine(models.Model): ) return super().unlink() - @api.multi def _get_quantity_to_invoice( self, period_first_date, period_last_date, invoice_date ): self.ensure_one() return self.quantity + diff --git a/contract/models/contract_template_line.py b/contract/models/contract_template_line.py index 25f0cd52..d14b70b8 100644 --- a/contract/models/contract_template_line.py +++ b/contract/models/contract_template_line.py @@ -20,5 +20,4 @@ class ContractTemplateLine(models.Model): comodel_name='contract.template', required=True, ondelete='cascade', - oldname='analytic_account_id', ) diff --git a/contract/tests/test_contract.py b/contract/tests/test_contract.py index 74adae51..8932baa0 100644 --- a/contract/tests/test_contract.py +++ b/contract/tests/test_contract.py @@ -1322,7 +1322,7 @@ class TestContract(TestContractBase): for i in range(10): contracts |= self.contract.copy() self.env['contract.contract'].cron_recurring_create_invoice() - invoice_lines = self.env['account.invoice.line'].search( + invoice_lines = self.env['account.move.line'].search( [('contract_line_id', 'in', contracts.mapped('contract_line_ids').ids)] ) @@ -1809,7 +1809,7 @@ class TestContract(TestContractBase): self.assertEqual(self.contract.invoice_count, 3) def test_contract_count_invoice(self): - invoices = self.env['account.invoice'] + invoices = self.env['account.move'] invoices |= self.contract.recurring_create_invoice() invoices |= self.contract.recurring_create_invoice() invoices |= self.contract.recurring_create_invoice() diff --git a/contract/views/abstract_contract_line.xml b/contract/views/abstract_contract_line.xml index b66077ee..f0c292c3 100644 --- a/contract/views/abstract_contract_line.xml +++ b/contract/views/abstract_contract_line.xml @@ -18,6 +18,8 @@ + + @@ -59,8 +61,8 @@ - + + diff --git a/contract/views/account_move.xml b/contract/views/account_move.xml new file mode 100644 index 00000000..a8bcfbf6 --- /dev/null +++ b/contract/views/account_move.xml @@ -0,0 +1,28 @@ + + + + + + + account.move + + + + + + + + + + + account.move + + + + + + + + + diff --git a/contract/views/contract.xml b/contract/views/contract.xml index 5145abc5..ee823b2c 100644 --- a/contract/views/contract.xml +++ b/contract/views/contract.xml @@ -33,13 +33,20 @@ widget="statinfo"/> -
-
@@ -55,6 +62,9 @@ + + + @@ -65,6 +75,7 @@ + @@ -125,8 +136,8 @@ Customer - [('customer', '=', True)] - {'default_customer': True, 'default_supplier': False} + [('customer_rank','>=','1')] + {'default_customer_rank': 1,'res_partner_search_mode': 'customer'} [('type', '=', 'sale'),('company_id', '=', company_id)] @@ -144,8 +155,8 @@ Supplier - [('supplier', '=', True)] - {'default_customer': False, 'default_supplier': True} + [('supplier_rank','>=','1')] + {'default_supplier_rank': 1,'res_partner_search_mode': 'supplier'} [('type', '=', 'purchase'),('company_id', '=', company_id)] @@ -212,6 +223,9 @@ domain="[]" context="{'group_by':'date_end'}" /> + + + @@ -221,7 +235,6 @@ Customer Contracts contract.contract - form tree,form [('contract_type', '=', 'sale')] {'is_contract':1, @@ -260,7 +273,6 @@ Supplier Contracts contract.contract - form tree,form [('contract_type', '=', 'purchase')] {'is_contract':1, diff --git a/contract/views/contract_line.xml b/contract/views/contract_line.xml index 2d14a81f..4342a7f1 100644 --- a/contract/views/contract_line.xml +++ b/contract/views/contract_line.xml @@ -10,11 +10,16 @@
+
+ + + + @@ -47,7 +52,7 @@ contract.line customer form view (in contract) contract.line - + primary @@ -81,6 +86,7 @@ + @@ -90,6 +96,8 @@ + + Contract Templates contract.template - form tree,form diff --git a/contract/views/res_partner_view.xml b/contract/views/res_partner_view.xml index cda22f4a..bd3e9875 100644 --- a/contract/views/res_partner_view.xml +++ b/contract/views/res_partner_view.xml @@ -9,15 +9,17 @@ + + diff --git a/contract/wizards/contract_line_wizard.py b/contract/wizards/contract_line_wizard.py index f136d36d..2864935e 100644 --- a/contract/wizards/contract_line_wizard.py +++ b/contract/wizards/contract_line_wizard.py @@ -27,7 +27,6 @@ class ContractLineWizard(models.TransientModel): index=True, ) - @api.multi def stop(self): for wizard in self: wizard.contract_line_id.stop( @@ -35,7 +34,6 @@ class ContractLineWizard(models.TransientModel): ) return True - @api.multi def plan_successor(self): for wizard in self: wizard.contract_line_id.plan_successor( @@ -43,7 +41,6 @@ class ContractLineWizard(models.TransientModel): ) return True - @api.multi def stop_plan_successor(self): for wizard in self: wizard.contract_line_id.stop_plan_successor( @@ -51,7 +48,6 @@ class ContractLineWizard(models.TransientModel): ) return True - @api.multi def uncancel(self): for wizard in self: wizard.contract_line_id.uncancel(wizard.recurring_next_date)