mirror of
https://github.com/OCA/contract.git
synced 2025-02-13 17:57:24 +02:00
[MIG] contract: Migration to 13.0
- Standard procedure - Adapt invoice creation to new account.move - Tests adapted
This commit is contained in:
@@ -14,13 +14,13 @@ Recurring - Contracts Management
|
||||
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
|
||||
:alt: License: AGPL-3
|
||||
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fcontract-lightgray.png?logo=github
|
||||
:target: https://github.com/OCA/contract/tree/12.0/contract
|
||||
:target: https://github.com/OCA/contract/tree/13.0/contract
|
||||
:alt: OCA/contract
|
||||
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
|
||||
:target: https://translation.odoo-community.org/projects/contract-12-0/contract-12-0-contract
|
||||
:target: https://translation.odoo-community.org/projects/contract-13-0/contract-13-0-contract
|
||||
:alt: Translate me on Weblate
|
||||
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
|
||||
:target: https://runbot.odoo-community.org/runbot/110/12.0
|
||||
:target: https://runbot.odoo-community.org/runbot/110/13.0
|
||||
:alt: Try me on Runbot
|
||||
|
||||
|badge1| |badge2| |badge3| |badge4| |badge5|
|
||||
@@ -74,7 +74,6 @@ Known issues / Roadmap
|
||||
======================
|
||||
|
||||
* Recover states and others functional fields in Contracts.
|
||||
* Remove ``models/ir_ui_view.py`` in v13, where the workaround it contains is supported upstream.
|
||||
|
||||
Bug Tracker
|
||||
===========
|
||||
@@ -82,7 +81,7 @@ Bug Tracker
|
||||
Bugs are tracked on `GitHub Issues <https://github.com/OCA/contract/issues>`_.
|
||||
In case of trouble, please check there if your issue has already been reported.
|
||||
If you spotted it first, help us smashing it by providing a detailed and welcomed
|
||||
`feedback <https://github.com/OCA/contract/issues/new?body=module:%20contract%0Aversion:%2012.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
|
||||
`feedback <https://github.com/OCA/contract/issues/new?body=module:%20contract%0Aversion:%2013.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
|
||||
|
||||
Do not contact contributors directly about support or help with technical issues.
|
||||
|
||||
@@ -92,9 +91,8 @@ Credits
|
||||
Authors
|
||||
~~~~~~~
|
||||
|
||||
* OpenERP SA
|
||||
* Tecnativa
|
||||
* LasLabs
|
||||
* ACSONE SA/NV
|
||||
|
||||
Contributors
|
||||
~~~~~~~~~~~~
|
||||
@@ -124,6 +122,6 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose
|
||||
mission is to support the collaborative development of Odoo features and
|
||||
promote its widespread use.
|
||||
|
||||
This module is part of the `OCA/contract <https://github.com/OCA/contract/tree/12.0/contract>`_ project on GitHub.
|
||||
This module is part of the `OCA/contract <https://github.com/OCA/contract/tree/13.0/contract>`_ project on GitHub.
|
||||
|
||||
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
|
||||
|
||||
@@ -5,17 +5,15 @@
|
||||
# Copyright 2017 Tecnativa - Vicent Cubells
|
||||
# Copyright 2016-2017 LasLabs Inc.
|
||||
# Copyright 2018-2019 ACSONE SA/NV
|
||||
# Copyright 2020 Tecnativa - Pedro M. Baeza
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
{
|
||||
"name": "Recurring - Contracts Management",
|
||||
"version": "12.0.7.2.2",
|
||||
"version": "13.0.1.0.0",
|
||||
"category": "Contract Management",
|
||||
"license": "AGPL-3",
|
||||
"author": "OpenERP SA, "
|
||||
"Tecnativa, "
|
||||
"LasLabs, "
|
||||
"Odoo Community Association (OCA)",
|
||||
"author": "Tecnativa, ACSONE SA/NV, Odoo Community Association (OCA)",
|
||||
"website": "https://github.com/oca/contract",
|
||||
"depends": ["base", "account", "product"],
|
||||
"external_dependencies": {"python": ["dateutil"]},
|
||||
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -1,144 +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)
|
||||
@@ -1,10 +0,0 @@
|
||||
def migrate(cr, version):
|
||||
# pre-paid/post-paid becomes significant for monthlylastday too,
|
||||
# make sure it has the value that was implied for previous versions.
|
||||
cr.execute(
|
||||
"""\
|
||||
UPDATE contract_line
|
||||
SET recurring_invoicing_type = 'post-paid'
|
||||
WHERE recurring_rule_type = 'monthlylastday'
|
||||
"""
|
||||
)
|
||||
@@ -1,7 +0,0 @@
|
||||
def migrate(cr, version):
|
||||
cr.execute(
|
||||
"""\
|
||||
UPDATE res_company
|
||||
SET create_new_line_at_contract_line_renew = true
|
||||
"""
|
||||
)
|
||||
28
contract/migrations/13.0.1.0.0/noupdate_changes.xml
Normal file
28
contract/migrations/13.0.1.0.0/noupdate_changes.xml
Normal file
@@ -0,0 +1,28 @@
|
||||
<?xml version='1.0' encoding='utf-8' ?>
|
||||
<odoo>
|
||||
<record id="rule_contract_contract_multi_company" model="ir.rule">
|
||||
<field
|
||||
name="domain_force"
|
||||
>['|',('company_id','=',False),('company_id','in',company_ids)]</field>
|
||||
</record>
|
||||
<record id="rule_contract_line_multi_company" model="ir.rule">
|
||||
<field
|
||||
name="domain_force"
|
||||
>['|',('company_id','=',False),('company_id','in',company_ids)]</field>
|
||||
</record>
|
||||
<record id="rule_contract_template_multi_company" model="ir.rule">
|
||||
<field
|
||||
name="domain_force"
|
||||
>['|',('company_id','=',False),('company_id','in',company_ids)]</field>
|
||||
</record>
|
||||
<record id="rule_contract_template_line_multi_company" model="ir.rule">
|
||||
<field
|
||||
name="domain_force"
|
||||
>['|',('company_id','=',False),('company_id','in',company_ids)]</field>
|
||||
</record>
|
||||
<record id="contract_tag_multi_company_rule" model="ir.rule">
|
||||
<field
|
||||
name="domain_force"
|
||||
>['|',('company_id','=',False),('company_id','in',company_ids)]</field>
|
||||
</record>
|
||||
</odoo>
|
||||
11
contract/migrations/13.0.1.0.0/post-migration.py
Normal file
11
contract/migrations/13.0.1.0.0/post-migration.py
Normal file
@@ -0,0 +1,11 @@
|
||||
# Copyright 2020 Tecnativa - Pedro M. Baeza
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from openupgradelib import openupgrade # pylint: disable=W7936
|
||||
|
||||
|
||||
@openupgrade.migrate()
|
||||
def migrate(env, version):
|
||||
openupgrade.load_data(
|
||||
env.cr, "contract", "migrations/13.0.1.0.0/noupdate_changes.xml"
|
||||
)
|
||||
@@ -6,11 +6,9 @@ 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
|
||||
from . import res_partner
|
||||
from . import contract_tag
|
||||
from . import res_company
|
||||
from . import res_config_settings
|
||||
from . import contract_terminate_reason
|
||||
from . import ir_ui_view
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Copyright 2004-2010 OpenERP SA
|
||||
# Copyright 2014 Angel Moya <angel.moya@domatix.com>
|
||||
# Copyright 2015 Pedro M. Baeza <pedro.baeza@tecnativa.com>
|
||||
# Copyright 2015-2020 Tecnativa - Pedro M. Baeza
|
||||
# Copyright 2016-2018 Carlos Dauden <carlos.dauden@tecnativa.com>
|
||||
# Copyright 2016-2017 LasLabs Inc.
|
||||
# Copyright 2018 ACSONE SA/NV
|
||||
@@ -39,7 +39,7 @@ class ContractAbstractContract(models.AbstractModel):
|
||||
"res.company",
|
||||
string="Company",
|
||||
required=True,
|
||||
default=lambda self: self.env["res.company"]._company_default_get(self._name),
|
||||
default=lambda self: self.env.company.id,
|
||||
)
|
||||
|
||||
@api.onchange("contract_type")
|
||||
|
||||
@@ -10,8 +10,6 @@ from odoo import api, fields, models
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.tools.translate import _
|
||||
|
||||
from odoo.addons import decimal_precision as dp
|
||||
|
||||
|
||||
class ContractAbstractContractLine(models.AbstractModel):
|
||||
_name = "contract.abstract.contract.line"
|
||||
@@ -35,13 +33,11 @@ class ContractAbstractContractLine(models.AbstractModel):
|
||||
inverse="_inverse_price_unit",
|
||||
)
|
||||
price_subtotal = fields.Float(
|
||||
compute="_compute_price_subtotal",
|
||||
digits=dp.get_precision("Account"),
|
||||
string="Sub Total",
|
||||
compute="_compute_price_subtotal", digits="Account", string="Sub Total",
|
||||
)
|
||||
discount = fields.Float(
|
||||
string="Discount (%)",
|
||||
digits=dp.get_precision("Discount"),
|
||||
digits="Discount",
|
||||
help="Discount that is applied in generated invoices."
|
||||
" It should be less or equal to 100",
|
||||
)
|
||||
@@ -123,7 +119,7 @@ class ContractAbstractContractLine(models.AbstractModel):
|
||||
ondelete="cascade",
|
||||
)
|
||||
display_type = fields.Selection(
|
||||
selection=[("line_section", "Section"), ("line_note", "Note"),],
|
||||
selection=[("line_section", "Section"), ("line_note", "Note")],
|
||||
default=False,
|
||||
help="Technical field for UX purpose.",
|
||||
)
|
||||
@@ -140,6 +136,7 @@ class ContractAbstractContractLine(models.AbstractModel):
|
||||
"- Custom: Depending on the recurrence to be define.",
|
||||
)
|
||||
is_recurring_note = fields.Boolean(compute="_compute_is_recurring_note")
|
||||
company_id = fields.Many2one(related="contract_id.company_id", store=True)
|
||||
|
||||
@api.model
|
||||
def _get_default_recurring_invoicing_offset(
|
||||
@@ -153,7 +150,8 @@ class ContractAbstractContractLine(models.AbstractModel):
|
||||
else:
|
||||
return 1
|
||||
|
||||
def is_recurring_note(self):
|
||||
@api.depends("display_type", "note_invoicing_mode")
|
||||
def _compute_is_recurring_note(self):
|
||||
for record in self:
|
||||
record.is_recurring_note = (
|
||||
record.display_type == "line_note"
|
||||
@@ -163,7 +161,8 @@ class ContractAbstractContractLine(models.AbstractModel):
|
||||
@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(
|
||||
method = self._get_default_recurring_invoicing_offset
|
||||
rec.recurring_invoicing_offset = method(
|
||||
rec.recurring_invoicing_type, rec.recurring_rule_type
|
||||
)
|
||||
|
||||
@@ -206,7 +205,6 @@ 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):
|
||||
for line in self:
|
||||
@@ -219,14 +217,12 @@ class ContractAbstractContractLine(models.AbstractModel):
|
||||
else:
|
||||
line.price_subtotal = subtotal
|
||||
|
||||
@api.multi
|
||||
@api.constrains("discount")
|
||||
def _check_discount(self):
|
||||
for line in self:
|
||||
if line.discount > 100:
|
||||
raise ValidationError(_("Discount should be less or equal to 100"))
|
||||
|
||||
@api.multi
|
||||
@api.onchange("product_id")
|
||||
def _onchange_product_id(self):
|
||||
if not self.product_id:
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
# © 2016 Carlos Dauden <carlos.dauden@tecnativa.com>
|
||||
# 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")
|
||||
@@ -1,12 +0,0 @@
|
||||
# Copyright 2018 ACSONE SA/NV.
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class AccountInvoiceLine(models.Model):
|
||||
_inherit = "account.invoice.line"
|
||||
|
||||
contract_line_id = fields.Many2one(
|
||||
"contract.line", string="Contract Line", index=True
|
||||
)
|
||||
21
contract/models/account_move.py
Normal file
21
contract/models/account_move.py
Normal file
@@ -0,0 +1,21 @@
|
||||
# Copyright 2016 Tecnativa - Carlos Dauden
|
||||
# Copyright 2018 ACSONE SA/NV.
|
||||
# Copyright 2020 Tecnativa - Pedro M. Baeza
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class AccountInvoice(models.Model):
|
||||
_inherit = "account.move"
|
||||
|
||||
# We keep this field for migration purpose
|
||||
old_contract_id = fields.Many2one("contract.contract")
|
||||
|
||||
|
||||
class AccountMoveLine(models.Model):
|
||||
_inherit = "account.move.line"
|
||||
|
||||
contract_line_id = fields.Many2one(
|
||||
"contract.line", string="Contract Line", index=True
|
||||
)
|
||||
@@ -1,6 +1,6 @@
|
||||
# Copyright 2004-2010 OpenERP SA
|
||||
# Copyright 2014 Angel Moya <angel.moya@domatix.com>
|
||||
# Copyright 2015 Pedro M. Baeza <pedro.baeza@tecnativa.com>
|
||||
# Copyright 2015-2020 Tecnativa - Pedro M. Baeza
|
||||
# Copyright 2016-2018 Carlos Dauden <carlos.dauden@tecnativa.com>
|
||||
# Copyright 2016-2017 LasLabs Inc.
|
||||
# Copyright 2018 ACSONE SA/NV
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
from odoo import api, fields, models
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
from odoo.tests import Form
|
||||
from odoo.tools.translate import _
|
||||
|
||||
|
||||
@@ -106,7 +107,6 @@ class ContractContract(models.Model):
|
||||
track_visibility="onchange",
|
||||
)
|
||||
|
||||
@api.multi
|
||||
def _inverse_partner_id(self):
|
||||
for rec in self:
|
||||
if not rec.invoice_partner_id:
|
||||
@@ -114,18 +114,15 @@ class ContractContract(models.Model):
|
||||
"invoice"
|
||||
]
|
||||
|
||||
@api.multi
|
||||
def _get_related_invoices(self):
|
||||
self.ensure_one()
|
||||
|
||||
invoices = (
|
||||
self.env["account.invoice.line"]
|
||||
self.env["account.move.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)]
|
||||
.mapped("move_id")
|
||||
)
|
||||
invoices |= self.env["account.move"].search([("old_contract_id", "=", self.id)])
|
||||
return invoices
|
||||
|
||||
def _get_computed_currency(self):
|
||||
@@ -162,31 +159,18 @@ class ContractContract(models.Model):
|
||||
else:
|
||||
rec.manual_currency_id = False
|
||||
|
||||
@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"
|
||||
)
|
||||
form_view_ref = (
|
||||
"account.invoice_supplier_form"
|
||||
if self.contract_type == "purchase"
|
||||
else "account.invoice_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)
|
||||
tree_view = self.env.ref("account.view_invoice_tree", raise_if_not_found=False)
|
||||
form_view = self.env.ref("account.view_move_form", raise_if_not_found=False)
|
||||
action = {
|
||||
"type": "ir.actions.act_window",
|
||||
"name": "Invoices",
|
||||
"res_model": "account.invoice",
|
||||
"view_type": "form",
|
||||
"res_model": "account.move",
|
||||
"view_mode": "tree,kanban,form,calendar,pivot,graph,activity",
|
||||
"domain": [("id", "in", self._get_related_invoices().ids)],
|
||||
}
|
||||
@@ -216,6 +200,8 @@ class ContractContract(models.Model):
|
||||
).mapped("recurring_next_date")
|
||||
if recurring_next_date:
|
||||
contract.recurring_next_date = min(recurring_next_date)
|
||||
else:
|
||||
contract.recurring_next_date = False
|
||||
|
||||
@api.depends("contract_line_ids.create_invoice_visibility")
|
||||
def _compute_create_invoice_visibility(self):
|
||||
@@ -279,7 +265,6 @@ class ContractContract(models.Model):
|
||||
}
|
||||
}
|
||||
|
||||
@api.multi
|
||||
def _convert_contract_lines(self, contract):
|
||||
self.ensure_one()
|
||||
new_lines = self.env["contract.line"]
|
||||
@@ -295,8 +280,12 @@ class ContractContract(models.Model):
|
||||
new_lines._onchange_is_auto_renew()
|
||||
return new_lines
|
||||
|
||||
@api.multi
|
||||
def _prepare_invoice(self, date_invoice, journal=None):
|
||||
"""Prepare in a Form the values for the generated invoice record.
|
||||
|
||||
:return: A tuple with the vals dictionary and the Form with the
|
||||
preloaded values for being used in lines.
|
||||
"""
|
||||
self.ensure_one()
|
||||
if not journal:
|
||||
journal = (
|
||||
@@ -318,36 +307,30 @@ class ContractContract(models.Model):
|
||||
invoice_type = "out_invoice"
|
||||
if self.contract_type == "purchase":
|
||||
invoice_type = "in_invoice"
|
||||
vinvoice = (
|
||||
self.env["account.invoice"]
|
||||
.with_context(force_company=self.company_id.id,)
|
||||
.new(
|
||||
{
|
||||
"company_id": self.company_id.id,
|
||||
"partner_id": self.invoice_partner_id.id,
|
||||
"type": invoice_type,
|
||||
}
|
||||
move_form = Form(
|
||||
self.env["account.move"].with_context(
|
||||
force_company=self.company_id.id, default_type=invoice_type
|
||||
)
|
||||
)
|
||||
vinvoice._onchange_partner_id()
|
||||
invoice_vals = vinvoice._convert_to_write(vinvoice._cache)
|
||||
move_form.partner_id = self.invoice_partner_id
|
||||
if self.payment_term_id:
|
||||
move_form.invoice_payment_term_id = self.payment_term_id
|
||||
if self.fiscal_position_id:
|
||||
move_form.fiscal_position_id = self.fiscal_position_id
|
||||
invoice_vals = move_form._values_to_save(all_fields=True)
|
||||
invoice_vals.update(
|
||||
{
|
||||
"name": self.code,
|
||||
"ref": self.code,
|
||||
"company_id": self.company_id.id,
|
||||
"currency_id": self.currency_id.id,
|
||||
"date_invoice": date_invoice,
|
||||
"invoice_date": date_invoice,
|
||||
"journal_id": journal.id,
|
||||
"origin": self.name,
|
||||
"invoice_origin": self.name,
|
||||
"user_id": self.user_id.id,
|
||||
}
|
||||
)
|
||||
if self.payment_term_id:
|
||||
invoice_vals["payment_term_id"] = self.payment_term_id.id
|
||||
if self.fiscal_position_id:
|
||||
invoice_vals["fiscal_position_id"] = self.fiscal_position_id.id
|
||||
return invoice_vals
|
||||
return invoice_vals, move_form
|
||||
|
||||
@api.multi
|
||||
def action_contract_send(self):
|
||||
self.ensure_one()
|
||||
template = self.env.ref("contract.email_contract_template", False)
|
||||
@@ -362,7 +345,6 @@ class ContractContract(models.Model):
|
||||
return {
|
||||
"name": _("Compose Email"),
|
||||
"type": "ir.actions.act_window",
|
||||
"view_type": "form",
|
||||
"view_mode": "form",
|
||||
"res_model": "mail.compose.message",
|
||||
"views": [(compose_form.id, "form")],
|
||||
@@ -371,40 +353,6 @@ class ContractContract(models.Model):
|
||||
"context": ctx,
|
||||
}
|
||||
|
||||
@api.model
|
||||
def _finalize_invoice_values(self, invoice_values):
|
||||
"""Provided for keeping compatibility in this version."""
|
||||
# TODO: Must be removed in >=13.0
|
||||
return invoice_values
|
||||
|
||||
@api.model
|
||||
def _finalize_invoice_creation(self, invoices):
|
||||
"""This method is called right after the creation of the invoices.
|
||||
|
||||
Override it when you need to do something after the records are created
|
||||
in the DB. If you need to modify any value, better to do it on the
|
||||
_prepare_* methods on contract or contract line.
|
||||
"""
|
||||
invoices.compute_taxes()
|
||||
|
||||
@api.model
|
||||
def _finalize_and_create_invoices(self, invoices_values):
|
||||
"""This method:
|
||||
|
||||
- creates the invoices
|
||||
- finalizes the created invoices (tax computation...)
|
||||
|
||||
:param invoices_values: list of dictionaries (invoices values)
|
||||
:return: created invoices (account.invoice)
|
||||
"""
|
||||
final_invoices_values = []
|
||||
# TODO: This call must be removed in >=13.0
|
||||
for invoice_values in invoices_values:
|
||||
final_invoices_values.append(self._finalize_invoice_values(invoice_values))
|
||||
invoices = self.env["account.invoice"].create(final_invoices_values)
|
||||
self._finalize_invoice_creation(invoices)
|
||||
return invoices
|
||||
|
||||
@api.model
|
||||
def _get_contracts_to_invoice_domain(self, date_ref=None):
|
||||
"""
|
||||
@@ -419,7 +367,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
|
||||
@@ -460,7 +407,6 @@ class ContractContract(models.Model):
|
||||
previous = line
|
||||
return lines2invoice.sorted()
|
||||
|
||||
@api.multi
|
||||
def _prepare_recurring_invoices_values(self, date_ref=False):
|
||||
"""
|
||||
This method builds the list of invoices values to create, based on
|
||||
@@ -479,21 +425,15 @@ class ContractContract(models.Model):
|
||||
contract_lines = contract._get_lines_to_invoice(date_ref)
|
||||
if not contract_lines:
|
||||
continue
|
||||
invoice_values = contract._prepare_invoice(date_ref)
|
||||
invoice_vals, move_form = contract._prepare_invoice(date_ref)
|
||||
invoice_vals["invoice_line_ids"] = []
|
||||
for line in contract_lines:
|
||||
invoice_values.setdefault("invoice_line_ids", [])
|
||||
invoice_line_values = line._prepare_invoice_line(
|
||||
invoice_values=invoice_values,
|
||||
)
|
||||
if invoice_line_values:
|
||||
invoice_values["invoice_line_ids"].append(
|
||||
(0, 0, invoice_line_values)
|
||||
)
|
||||
invoices_values.append(invoice_values)
|
||||
invoice_line_vals = line._prepare_invoice_line(move_form=move_form)
|
||||
invoice_vals["invoice_line_ids"].append((0, 0, invoice_line_vals))
|
||||
invoices_values.append(invoice_vals)
|
||||
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
|
||||
@@ -511,17 +451,16 @@ class ContractContract(models.Model):
|
||||
)
|
||||
return 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)
|
||||
return self.env["account.move"].create(invoices_values)
|
||||
|
||||
@api.model
|
||||
def cron_recurring_create_invoice(self, date_ref=None):
|
||||
if not date_ref:
|
||||
date_ref = fields.Date.context_today(self)
|
||||
domain = self._get_contracts_to_invoice_domain(date_ref)
|
||||
invoices = self.env["account.invoice"]
|
||||
invoices = self.env["account.move"]
|
||||
# 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:
|
||||
@@ -531,7 +470,6 @@ class ContractContract(models.Model):
|
||||
invoices |= contracts_to_invoice._recurring_create_invoice(date_ref)
|
||||
return invoices
|
||||
|
||||
@api.multi
|
||||
def action_terminate_contract(self):
|
||||
self.ensure_one()
|
||||
context = {"default_contract_id": self.id}
|
||||
@@ -539,13 +477,11 @@ class ContractContract(models.Model):
|
||||
"type": "ir.actions.act_window",
|
||||
"name": _("Terminate Contract"),
|
||||
"res_model": "contract.contract.terminate",
|
||||
"view_type": "form",
|
||||
"view_mode": "form",
|
||||
"target": "new",
|
||||
"context": context,
|
||||
}
|
||||
|
||||
@api.multi
|
||||
def _terminate_contract(
|
||||
self, terminate_reason_id, terminate_comment, terminate_date
|
||||
):
|
||||
@@ -563,7 +499,6 @@ class ContractContract(models.Model):
|
||||
)
|
||||
return True
|
||||
|
||||
@api.multi
|
||||
def action_cancel_contract_termination(self):
|
||||
self.ensure_one()
|
||||
self.write(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# Copyright 2017 LasLabs Inc.
|
||||
# Copyright 2018 ACSONE SA/NV.
|
||||
# Copyright 2020 Tecnativa - Pedro M. Baeza
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from datetime import timedelta
|
||||
@@ -118,7 +119,6 @@ class ContractLine(models.Model):
|
||||
default=True,
|
||||
)
|
||||
|
||||
@api.multi
|
||||
@api.depends(
|
||||
"date_end", "termination_notice_rule_type", "termination_notice_interval",
|
||||
)
|
||||
@@ -128,14 +128,15 @@ class ContractLine(models.Model):
|
||||
rec.termination_notice_date = rec.date_end - self.get_relative_delta(
|
||||
rec.termination_notice_rule_type, rec.termination_notice_interval,
|
||||
)
|
||||
else:
|
||||
rec.termination_notice_date = False
|
||||
|
||||
@api.multi
|
||||
@api.depends("is_canceled", "date_start", "date_end", "is_auto_renew")
|
||||
def _compute_state(self):
|
||||
today = fields.Date.context_today(self)
|
||||
for rec in self:
|
||||
rec.state = False
|
||||
if rec.display_type:
|
||||
rec.state = False
|
||||
continue
|
||||
if rec.is_canceled:
|
||||
rec.state = "canceled"
|
||||
@@ -192,8 +193,8 @@ class ContractLine(models.Model):
|
||||
("date_end", ">=", today),
|
||||
("date_end", "=", False),
|
||||
"|",
|
||||
"&",
|
||||
("is_auto_renew", "=", True),
|
||||
"&",
|
||||
("is_auto_renew", "=", False),
|
||||
("termination_notice_date", ">", today),
|
||||
]
|
||||
@@ -290,16 +291,16 @@ class ContractLine(models.Model):
|
||||
)
|
||||
def _compute_allowed(self):
|
||||
for rec in self:
|
||||
rec.update(
|
||||
{
|
||||
"is_plan_successor_allowed": False,
|
||||
"is_stop_plan_successor_allowed": False,
|
||||
"is_stop_allowed": False,
|
||||
"is_cancel_allowed": False,
|
||||
"is_un_cancel_allowed": False,
|
||||
}
|
||||
)
|
||||
if rec.contract_id.is_terminated:
|
||||
rec.update(
|
||||
{
|
||||
"is_plan_successor_allowed": False,
|
||||
"is_stop_plan_successor_allowed": False,
|
||||
"is_stop_allowed": False,
|
||||
"is_cancel_allowed": False,
|
||||
"is_un_cancel_allowed": False,
|
||||
}
|
||||
)
|
||||
continue
|
||||
if rec.date_start:
|
||||
allowed = get_allowed(
|
||||
@@ -315,7 +316,9 @@ class ContractLine(models.Model):
|
||||
rec.update(
|
||||
{
|
||||
"is_plan_successor_allowed": allowed.plan_successor,
|
||||
"is_stop_plan_successor_allowed": allowed.stop_plan_successor,
|
||||
"is_stop_plan_successor_allowed": (
|
||||
allowed.stop_plan_successor
|
||||
),
|
||||
"is_stop_allowed": allowed.stop,
|
||||
"is_cancel_allowed": allowed.cancel,
|
||||
"is_un_cancel_allowed": allowed.uncancel,
|
||||
@@ -618,7 +621,13 @@ class ContractLine(models.Model):
|
||||
% line.name
|
||||
)
|
||||
|
||||
@api.depends("recurring_next_date", "date_start", "date_end")
|
||||
@api.depends(
|
||||
"display_type",
|
||||
"is_recurring_note",
|
||||
"recurring_next_date",
|
||||
"date_start",
|
||||
"date_end",
|
||||
)
|
||||
def _compute_create_invoice_visibility(self):
|
||||
# TODO: depending on the lines, and their order, some sections
|
||||
# have no meaning in certain invoices
|
||||
@@ -633,51 +642,31 @@ class ContractLine(models.Model):
|
||||
else:
|
||||
rec.create_invoice_visibility = False
|
||||
|
||||
@api.multi
|
||||
def _prepare_invoice_line(self, invoice_id=False, invoice_values=False):
|
||||
def _prepare_invoice_line(self, move_form):
|
||||
self.ensure_one()
|
||||
dates = self._get_period_to_invoice(
|
||||
self.last_date_invoiced, self.recurring_next_date
|
||||
)
|
||||
invoice_line_vals = {
|
||||
"display_type": self.display_type,
|
||||
"product_id": self.product_id.id,
|
||||
"quantity": self._get_quantity_to_invoice(*dates),
|
||||
"uom_id": self.uom_id.id,
|
||||
"discount": self.discount,
|
||||
"contract_line_id": self.id,
|
||||
}
|
||||
if invoice_id:
|
||||
invoice_line_vals["invoice_id"] = invoice_id.id
|
||||
invoice_line = (
|
||||
self.env["account.invoice.line"]
|
||||
.with_context(force_company=self.contract_id.company_id.id,)
|
||||
.new(invoice_line_vals)
|
||||
)
|
||||
if invoice_values and not invoice_id:
|
||||
invoice = (
|
||||
self.env["account.invoice"]
|
||||
.with_context(force_company=self.contract_id.company_id.id,)
|
||||
.new(invoice_values)
|
||||
)
|
||||
invoice_line.invoice_id = invoice
|
||||
# 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
|
||||
line_form = move_form.invoice_line_ids.new()
|
||||
line_form.display_type = self.display_type
|
||||
line_form.product_id = self.product_id
|
||||
invoice_line_vals = line_form._values_to_save(all_fields=True)
|
||||
name = self._insert_markers(dates[0], dates[1])
|
||||
invoice_line_vals.update(
|
||||
{
|
||||
"quantity": self._get_quantity_to_invoice(*dates),
|
||||
"product_uom_id": self.uom_id.id,
|
||||
"discount": self.discount,
|
||||
"contract_line_id": self.id,
|
||||
"sequence": self.sequence,
|
||||
"name": name,
|
||||
"account_analytic_id": self.analytic_account_id.id,
|
||||
"analytic_account_id": self.analytic_account_id.id,
|
||||
"analytic_tag_ids": [(6, 0, self.analytic_tag_ids.ids)],
|
||||
"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
|
||||
):
|
||||
@@ -702,7 +691,6 @@ class ContractLine(models.Model):
|
||||
)
|
||||
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"]
|
||||
@@ -713,7 +701,6 @@ 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:
|
||||
last_date_invoiced = rec.next_period_date_end
|
||||
@@ -732,7 +719,6 @@ class ContractLine(models.Model):
|
||||
}
|
||||
)
|
||||
|
||||
@api.multi
|
||||
def _init_last_date_invoiced(self):
|
||||
"""Used to init last_date_invoiced for migration purpose"""
|
||||
for rec in self:
|
||||
@@ -778,7 +764,6 @@ class ContractLine(models.Model):
|
||||
else:
|
||||
return relativedelta(years=interval)
|
||||
|
||||
@api.multi
|
||||
def _delay(self, delay_delta):
|
||||
"""
|
||||
Delay a contract line
|
||||
@@ -811,7 +796,6 @@ class ContractLine(models.Model):
|
||||
}
|
||||
)
|
||||
|
||||
@api.multi
|
||||
def _prepare_value_for_stop(self, date_end, manual_renew_needed):
|
||||
self.ensure_one()
|
||||
return {
|
||||
@@ -828,7 +812,6 @@ class ContractLine(models.Model):
|
||||
),
|
||||
}
|
||||
|
||||
@api.multi
|
||||
def stop(self, date_end, manual_renew_needed=False, post_message=True):
|
||||
"""
|
||||
Put date_end on contract line
|
||||
@@ -868,7 +851,6 @@ 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
|
||||
):
|
||||
@@ -893,7 +875,6 @@ class ContractLine(models.Model):
|
||||
values["predecessor_contract_line_id"] = self.id
|
||||
return values
|
||||
|
||||
@api.multi
|
||||
def plan_successor(
|
||||
self,
|
||||
date_start,
|
||||
@@ -939,7 +920,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
|
||||
@@ -1035,7 +1015,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"))
|
||||
@@ -1053,7 +1032,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"))
|
||||
@@ -1075,7 +1053,6 @@ class ContractLine(models.Model):
|
||||
rec.recurring_next_date = recurring_next_date
|
||||
return True
|
||||
|
||||
@api.multi
|
||||
def action_uncancel(self):
|
||||
self.ensure_one()
|
||||
context = {
|
||||
@@ -1088,14 +1065,12 @@ class ContractLine(models.Model):
|
||||
"type": "ir.actions.act_window",
|
||||
"name": "Un-Cancel Contract Line",
|
||||
"res_model": "contract.line.wizard",
|
||||
"view_type": "form",
|
||||
"view_mode": "form",
|
||||
"views": [(view_id, "form")],
|
||||
"target": "new",
|
||||
"context": context,
|
||||
}
|
||||
|
||||
@api.multi
|
||||
def action_plan_successor(self):
|
||||
self.ensure_one()
|
||||
context = {
|
||||
@@ -1110,14 +1085,12 @@ class ContractLine(models.Model):
|
||||
"type": "ir.actions.act_window",
|
||||
"name": "Plan contract line successor",
|
||||
"res_model": "contract.line.wizard",
|
||||
"view_type": "form",
|
||||
"view_mode": "form",
|
||||
"views": [(view_id, "form")],
|
||||
"target": "new",
|
||||
"context": context,
|
||||
}
|
||||
|
||||
@api.multi
|
||||
def action_stop(self):
|
||||
self.ensure_one()
|
||||
context = {
|
||||
@@ -1130,14 +1103,12 @@ class ContractLine(models.Model):
|
||||
"type": "ir.actions.act_window",
|
||||
"name": "Terminate contract line",
|
||||
"res_model": "contract.line.wizard",
|
||||
"view_type": "form",
|
||||
"view_mode": "form",
|
||||
"views": [(view_id, "form")],
|
||||
"target": "new",
|
||||
"context": context,
|
||||
}
|
||||
|
||||
@api.multi
|
||||
def action_stop_plan_successor(self):
|
||||
self.ensure_one()
|
||||
context = {
|
||||
@@ -1152,14 +1123,12 @@ class ContractLine(models.Model):
|
||||
"type": "ir.actions.act_window",
|
||||
"name": "Suspend contract line",
|
||||
"res_model": "contract.line.wizard",
|
||||
"view_type": "form",
|
||||
"view_mode": "form",
|
||||
"views": [(view_id, "form")],
|
||||
"target": "new",
|
||||
"context": context,
|
||||
}
|
||||
|
||||
@api.multi
|
||||
def _get_renewal_new_date_end(self):
|
||||
self.ensure_one()
|
||||
date_start = self.date_end + relativedelta(days=1)
|
||||
@@ -1168,7 +1137,6 @@ class ContractLine(models.Model):
|
||||
)
|
||||
return date_end
|
||||
|
||||
@api.multi
|
||||
def _renew_create_line(self, date_end):
|
||||
self.ensure_one()
|
||||
date_start = self.date_end + relativedelta(days=1)
|
||||
@@ -1180,13 +1148,11 @@ class ContractLine(models.Model):
|
||||
new_line._onchange_date_start()
|
||||
return new_line
|
||||
|
||||
@api.multi
|
||||
def _renew_extend_line(self, date_end):
|
||||
self.ensure_one()
|
||||
self.date_end = date_end
|
||||
return self
|
||||
|
||||
@api.multi
|
||||
def renew(self):
|
||||
res = self.env["contract.line"]
|
||||
for rec in self:
|
||||
@@ -1240,7 +1206,6 @@ class ContractLine(models.Model):
|
||||
view_id = self.env.ref("contract.contract_line_customer_form_view").id
|
||||
return super().fields_view_get(view_id, view_type, toolbar, submenu)
|
||||
|
||||
@api.multi
|
||||
def unlink(self):
|
||||
"""stop unlink uncnacled lines"""
|
||||
for record in self:
|
||||
@@ -1248,7 +1213,6 @@ class ContractLine(models.Model):
|
||||
raise ValidationError(_("Contract line must be canceled before delete"))
|
||||
return super().unlink()
|
||||
|
||||
@api.multi
|
||||
def _get_quantity_to_invoice(
|
||||
self, period_first_date, period_last_date, invoice_date
|
||||
):
|
||||
|
||||
@@ -11,5 +11,5 @@ class ContractTag(models.Model):
|
||||
|
||||
name = fields.Char(requirment=True)
|
||||
company_id = fields.Many2one(
|
||||
"res.company", string="Company", default=lambda self: self.env.user.company_id,
|
||||
"res.company", string="Company", default=lambda self: self.env.company.id,
|
||||
)
|
||||
|
||||
@@ -20,5 +20,4 @@ class ContractTemplateLine(models.Model):
|
||||
comodel_name="contract.template",
|
||||
required=True,
|
||||
ondelete="cascade",
|
||||
oldname="analytic_account_id",
|
||||
)
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
# Copyright 2020 Tecnativa - Jairo Llopis
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import api, models
|
||||
|
||||
|
||||
class IrUiView(models.Model):
|
||||
_inherit = "ir.ui.view"
|
||||
|
||||
@api.model
|
||||
def _prepare_qcontext(self):
|
||||
"""Patch context to use fw-compatible v13 company."""
|
||||
# TODO Delete this method in v13; it's upstream there
|
||||
result = super()._prepare_qcontext()
|
||||
if self.env.context.get("allowed_company_ids"):
|
||||
result["res_company"] = (
|
||||
self.env["res.company"]
|
||||
.browse(self.env.context["allowed_company_ids"][0])
|
||||
.sudo()
|
||||
)
|
||||
return result
|
||||
@@ -14,7 +14,7 @@ class ResPartner(models.Model):
|
||||
string="Purchase Contracts", compute="_compute_contract_count",
|
||||
)
|
||||
contract_ids = fields.One2many(
|
||||
comodel_name="contract.contract", inverse="partner_id", string="Contracts",
|
||||
comodel_name="contract.contract", inverse_name="partner_id", string="Contracts",
|
||||
)
|
||||
|
||||
def _compute_contract_count(self):
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
* Recover states and others functional fields in Contracts.
|
||||
* Remove ``models/ir_ui_view.py`` in v13, where the workaround it contains is supported upstream.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<?xml version='1.0' encoding='utf-8' ?>
|
||||
<odoo noupdate="1">
|
||||
<record id="rule_contract_contract_multi_company" model="ir.rule">
|
||||
<field name="name">Contract contract multi-company</field>
|
||||
@@ -5,7 +6,7 @@
|
||||
<field name="global" eval="True" />
|
||||
<field
|
||||
name="domain_force"
|
||||
>['|', ('company_id', '=', False), ('company_id', 'child_of', [user.company_id.id])]</field>
|
||||
>['|',('company_id','=',False),('company_id','in',company_ids)]</field>
|
||||
</record>
|
||||
<record id="rule_contract_line_multi_company" model="ir.rule">
|
||||
<field name="name">Contract line multi-company</field>
|
||||
@@ -13,7 +14,7 @@
|
||||
<field name="global" eval="True" />
|
||||
<field
|
||||
name="domain_force"
|
||||
>['|', ('contract_id.company_id', '=', False), ('contract_id.company_id', 'child_of', [user.company_id.id])]</field>
|
||||
>['|',('company_id','=',False),('company_id','in',company_ids)]</field>
|
||||
</record>
|
||||
<record id="rule_contract_template_multi_company" model="ir.rule">
|
||||
<field name="name">Contract template multi-company</field>
|
||||
@@ -21,7 +22,7 @@
|
||||
<field name="global" eval="True" />
|
||||
<field
|
||||
name="domain_force"
|
||||
>['|', ('company_id', '=', False), ('company_id', 'child_of', [user.company_id.id])]</field>
|
||||
>['|',('company_id','=',False),('company_id','in',company_ids)]</field>
|
||||
</record>
|
||||
<record id="rule_contract_template_line_multi_company" model="ir.rule">
|
||||
<field name="name">Contract template line multi-company</field>
|
||||
@@ -29,6 +30,6 @@
|
||||
<field name="global" eval="True" />
|
||||
<field
|
||||
name="domain_force"
|
||||
>['|', ('contract_id.company_id', '=', False), ('contract_id.company_id', 'child_of', [user.company_id.id])]</field>
|
||||
>['|',('company_id','=',False),('company_id','in',company_ids)]</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
||||
@@ -16,6 +16,6 @@
|
||||
<field name="model_id" ref="model_contract_tag" />
|
||||
<field
|
||||
name="domain_force"
|
||||
>['|', ('company_id', '=', False), ('company_id', 'child_of', [user.company_id.id])]</field>
|
||||
>['|',('company_id','=',False),('company_id','in',company_ids)]</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
||||
@@ -367,7 +367,7 @@ ul.auto-toc {
|
||||
!! This file is generated by oca-gen-addon-readme !!
|
||||
!! changes will be overwritten. !!
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
||||
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/contract/tree/12.0/contract"><img alt="OCA/contract" src="https://img.shields.io/badge/github-OCA%2Fcontract-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/contract-12-0/contract-12-0-contract"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/110/12.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
|
||||
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/contract/tree/13.0/contract"><img alt="OCA/contract" src="https://img.shields.io/badge/github-OCA%2Fcontract-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/contract-13-0/contract-13-0-contract"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/110/13.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
|
||||
<p>This module enables contracts management with recurring
|
||||
invoicing functions. Also you can print and send by email contract report.</p>
|
||||
<p>It works for customer contract and supplier contracts.</p>
|
||||
@@ -425,7 +425,6 @@ To use it, just select the template on the contract and fields will be filled au
|
||||
<h1><a class="toc-backref" href="#id3">Known issues / Roadmap</a></h1>
|
||||
<ul class="simple">
|
||||
<li>Recover states and others functional fields in Contracts.</li>
|
||||
<li>Remove <tt class="docutils literal">models/ir_ui_view.py</tt> in v13, where the workaround it contains is supported upstream.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="bug-tracker">
|
||||
@@ -433,7 +432,7 @@ To use it, just select the template on the contract and fields will be filled au
|
||||
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/contract/issues">GitHub Issues</a>.
|
||||
In case of trouble, please check there if your issue has already been reported.
|
||||
If you spotted it first, help us smashing it by providing a detailed and welcomed
|
||||
<a class="reference external" href="https://github.com/OCA/contract/issues/new?body=module:%20contract%0Aversion:%2012.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
|
||||
<a class="reference external" href="https://github.com/OCA/contract/issues/new?body=module:%20contract%0Aversion:%2013.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
|
||||
<p>Do not contact contributors directly about support or help with technical issues.</p>
|
||||
</div>
|
||||
<div class="section" id="credits">
|
||||
@@ -441,9 +440,8 @@ If you spotted it first, help us smashing it by providing a detailed and welcome
|
||||
<div class="section" id="authors">
|
||||
<h2><a class="toc-backref" href="#id6">Authors</a></h2>
|
||||
<ul class="simple">
|
||||
<li>OpenERP SA</li>
|
||||
<li>Tecnativa</li>
|
||||
<li>LasLabs</li>
|
||||
<li>ACSONE SA/NV</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="contributors">
|
||||
@@ -469,7 +467,7 @@ If you spotted it first, help us smashing it by providing a detailed and welcome
|
||||
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
|
||||
mission is to support the collaborative development of Odoo features and
|
||||
promote its widespread use.</p>
|
||||
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/contract/tree/12.0/contract">OCA/contract</a> project on GitHub.</p>
|
||||
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/contract/tree/13.0/contract">OCA/contract</a> project on GitHub.</p>
|
||||
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -19,7 +19,7 @@ odoo.define("contract.section_and_note_backend", function(require) {
|
||||
var result = this._super.apply(this, arguments);
|
||||
if (this.view.arch.tag === "tree") {
|
||||
result.include({
|
||||
_renderBodyCell: function(record, node, index, options) {
|
||||
_renderBodyCell: function(record) {
|
||||
var $cell = this._super.apply(this, arguments);
|
||||
|
||||
var isSection = record.data.display_type === "line_section";
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# © 2016 Carlos Dauden <carlos.dauden@tecnativa.com>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from . import test_contract
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Copyright 2018 Tecnativa - Carlos Dauden
|
||||
# Copyright 2018 Tecnativa - Pedro M. Baeza
|
||||
# Copyright 2018-2020 Tecnativa - Pedro M. Baeza
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from collections import namedtuple
|
||||
@@ -22,7 +22,7 @@ class TestContractBase(common.SavepointCase):
|
||||
super().setUpClass()
|
||||
cls.today = fields.Date.today()
|
||||
cls.pricelist = cls.env["product.pricelist"].create(
|
||||
{"name": "pricelist for contract test",}
|
||||
{"name": "pricelist for contract test"}
|
||||
)
|
||||
cls.partner = cls.env["res.partner"].create(
|
||||
{
|
||||
@@ -163,7 +163,7 @@ class TestContract(TestContractBase):
|
||||
self.assertTrue(self.invoice_monthly)
|
||||
self.assertEqual(self.acct_line.recurring_next_date, recurring_next_date)
|
||||
self.inv_line = self.invoice_monthly.invoice_line_ids[0]
|
||||
self.assertTrue(self.inv_line.invoice_line_tax_ids)
|
||||
self.assertTrue(self.inv_line.tax_ids)
|
||||
self.assertAlmostEqual(self.inv_line.price_subtotal, 50.0)
|
||||
self.assertEqual(self.contract.user_id, self.invoice_monthly.user_id)
|
||||
|
||||
@@ -398,7 +398,7 @@ class TestContract(TestContractBase):
|
||||
def test_check_recurring_next_date_start_date(self):
|
||||
with self.assertRaises(ValidationError):
|
||||
self.acct_line.write(
|
||||
{"date_start": "2018-01-01", "recurring_next_date": "2017-01-01",}
|
||||
{"date_start": "2018-01-01", "recurring_next_date": "2017-01-01"}
|
||||
)
|
||||
|
||||
def test_onchange_contract_template_id(self):
|
||||
@@ -409,7 +409,7 @@ class TestContract(TestContractBase):
|
||||
self.contract._onchange_contract_template_id()
|
||||
res = {
|
||||
"contract_line_ids": [
|
||||
(0, 0, {"display_type": "line_section", "name": "Test section",}),
|
||||
(0, 0, {"display_type": "line_section", "name": "Test section"}),
|
||||
(
|
||||
0,
|
||||
0,
|
||||
@@ -536,7 +536,6 @@ class TestContract(TestContractBase):
|
||||
{
|
||||
"name": "Customer Contracts",
|
||||
"type": "ir.actions.act_window",
|
||||
"view_type": "form",
|
||||
"res_model": "contract.contract",
|
||||
"xml_id": "contract.action_customer_contract",
|
||||
},
|
||||
@@ -731,7 +730,6 @@ class TestContract(TestContractBase):
|
||||
"recurring_invoicing_type": recurring_invoicing_type,
|
||||
"recurring_rule_type": recurring_rule_type,
|
||||
"recurring_interval": recurring_interval,
|
||||
"max_date_end": max_date_end,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -777,7 +775,7 @@ class TestContract(TestContractBase):
|
||||
|
||||
Result = namedtuple(
|
||||
"Result",
|
||||
["recurring_next_date", "next_period_date_start", "next_period_date_end",],
|
||||
["recurring_next_date", "next_period_date_start", "next_period_date_end"],
|
||||
)
|
||||
Combination = namedtuple(
|
||||
"Combination",
|
||||
@@ -1045,7 +1043,7 @@ class TestContract(TestContractBase):
|
||||
def test_stop_past_contract_line(self):
|
||||
"""Past contract line are ignored on stop"""
|
||||
self.acct_line.write(
|
||||
{"date_end": self.today + relativedelta(months=5), "is_auto_renew": True,}
|
||||
{"date_end": self.today + relativedelta(months=5), "is_auto_renew": True}
|
||||
)
|
||||
self.acct_line.stop(self.today + relativedelta(months=7))
|
||||
self.assertEqual(self.acct_line.date_end, self.today + relativedelta(months=5))
|
||||
@@ -1479,7 +1477,7 @@ class TestContract(TestContractBase):
|
||||
|
||||
def test_cancel(self):
|
||||
self.acct_line.write(
|
||||
{"date_end": self.today + relativedelta(months=5), "is_auto_renew": True,}
|
||||
{"date_end": self.today + relativedelta(months=5), "is_auto_renew": True}
|
||||
)
|
||||
self.acct_line.cancel()
|
||||
self.assertTrue(self.acct_line.is_canceled)
|
||||
@@ -1493,7 +1491,7 @@ class TestContract(TestContractBase):
|
||||
self.acct_line.cancel()
|
||||
self.assertTrue(self.acct_line.is_canceled)
|
||||
wizard = self.env["contract.line.wizard"].create(
|
||||
{"recurring_next_date": self.today, "contract_line_id": self.acct_line.id,}
|
||||
{"recurring_next_date": self.today, "contract_line_id": self.acct_line.id}
|
||||
)
|
||||
wizard.uncancel()
|
||||
self.assertFalse(self.acct_line.is_canceled)
|
||||
@@ -1630,10 +1628,10 @@ class TestContract(TestContractBase):
|
||||
self.acct_line.date_end = "2018-03-15"
|
||||
self.acct_line._onchange_date_start()
|
||||
contracts = self.contract2
|
||||
for i in range(10):
|
||||
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)]
|
||||
)
|
||||
self.assertEqual(
|
||||
@@ -1647,28 +1645,28 @@ class TestContract(TestContractBase):
|
||||
self.acct_line._onchange_date_start()
|
||||
self.contract2.unlink()
|
||||
contracts = self.contract
|
||||
for i in range(10):
|
||||
for _i in range(10):
|
||||
contracts |= self.contract.copy()
|
||||
wizard = self.env["contract.manually.create.invoice"].create(
|
||||
{"invoice_date": self.today}
|
||||
)
|
||||
wizard.action_show_contract_to_invoice()
|
||||
contract_to_invoice_count = wizard.contract_to_invoice_count
|
||||
self.assertEqual(
|
||||
contracts,
|
||||
self.env["contract.contract"].search(
|
||||
self.assertFalse(
|
||||
contracts
|
||||
- self.env["contract.contract"].search(
|
||||
wizard.action_show_contract_to_invoice()["domain"]
|
||||
),
|
||||
)
|
||||
action = wizard.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)]
|
||||
)
|
||||
self.assertEqual(
|
||||
len(contracts.mapped("contract_line_ids")), len(invoice_lines),
|
||||
)
|
||||
invoices = self.env["account.invoice"].search(action["domain"])
|
||||
self.assertEqual(invoice_lines.mapped("invoice_id"), invoices)
|
||||
invoices = self.env["account.move"].search(action["domain"])
|
||||
self.assertFalse(invoice_lines.mapped("move_id") - invoices)
|
||||
self.assertEqual(len(invoices), contract_to_invoice_count)
|
||||
|
||||
def test_get_period_to_invoice_monthlylastday_postpaid(self):
|
||||
@@ -1957,6 +1955,7 @@ class TestContract(TestContractBase):
|
||||
]
|
||||
self.assertEqual(set(lines.mapped("state")), set(states))
|
||||
# Test search method
|
||||
lines.flush() # Needed for computed stored fields like termination_notice_date
|
||||
for state in states:
|
||||
lines = self.env["contract.line"].search([("state", "=", state)])
|
||||
self.assertTrue(lines, state)
|
||||
@@ -2036,10 +2035,10 @@ class TestContract(TestContractBase):
|
||||
}
|
||||
)
|
||||
line_prepaid = self.acct_line.copy(
|
||||
{"recurring_invoicing_type": "pre-paid", "recurring_rule_type": "monthly",}
|
||||
{"recurring_invoicing_type": "pre-paid", "recurring_rule_type": "monthly"}
|
||||
)
|
||||
line_postpaid = self.acct_line.copy(
|
||||
{"recurring_invoicing_type": "post-paid", "recurring_rule_type": "monthly",}
|
||||
{"recurring_invoicing_type": "post-paid", "recurring_rule_type": "monthly"}
|
||||
)
|
||||
lines = line_monthlylastday | line_prepaid | line_postpaid
|
||||
lines.write({"last_date_invoiced": False})
|
||||
@@ -2101,7 +2100,7 @@ class TestContract(TestContractBase):
|
||||
|
||||
def test_multicompany_partner_edited(self):
|
||||
"""Editing a partner with contracts in several companies works."""
|
||||
company2 = self.env["res.company"].create({"name": "Company 2",})
|
||||
company2 = self.env["res.company"].create({"name": "Company 2"})
|
||||
unprivileged_user = self.env["res.users"].create(
|
||||
{
|
||||
"name": "unprivileged test user",
|
||||
@@ -2111,16 +2110,14 @@ class TestContract(TestContractBase):
|
||||
}
|
||||
)
|
||||
parent_partner = self.env["res.partner"].create(
|
||||
{"name": "parent partner", "is_company": True,}
|
||||
{"name": "parent partner", "is_company": True}
|
||||
)
|
||||
# Assume contract 2 is for company 2
|
||||
self.contract2.company_id = company2
|
||||
# Update the partner attached to both contracts
|
||||
self.partner.sudo(unprivileged_user).with_context(
|
||||
self.partner.with_user(unprivileged_user).with_context(
|
||||
company_id=company2.id, force_company=company2.id
|
||||
).write(
|
||||
{"is_company": False, "parent_id": parent_partner.id,}
|
||||
)
|
||||
).write({"is_company": False, "parent_id": parent_partner.id})
|
||||
|
||||
def test_sale_fields_view_get(self):
|
||||
sale_form_view = self.env.ref("contract.contract_line_customer_form_view")
|
||||
@@ -2137,7 +2134,7 @@ class TestContract(TestContractBase):
|
||||
self.assertEqual(self.contract.invoice_count, 3)
|
||||
|
||||
def test_contract_count_invoice_2(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()
|
||||
@@ -2260,7 +2257,7 @@ class TestContract(TestContractBase):
|
||||
self.assertEqual(self.contract2.currency_id, currency_cad)
|
||||
# Get currency from contract pricelist
|
||||
pricelist = self.env["product.pricelist"].create(
|
||||
{"name": "Test pricelist", "currency_id": currency_eur.id,}
|
||||
{"name": "Test pricelist", "currency_id": currency_eur.id}
|
||||
)
|
||||
self.contract2.pricelist_id = pricelist.id
|
||||
self.contract2.contract_line_ids.automatic_price = True
|
||||
|
||||
@@ -68,19 +68,14 @@
|
||||
/>
|
||||
</header>
|
||||
<sheet string="Contract">
|
||||
<field name="active" invisible="1" />
|
||||
<widget
|
||||
name="web_ribbon"
|
||||
text="Archived"
|
||||
bg_color="bg-danger"
|
||||
attrs="{'invisible': [('active', '=', True)]}"
|
||||
/>
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<button
|
||||
class="oe_stat_button"
|
||||
type="object"
|
||||
name="toggle_active"
|
||||
icon="fa-archive"
|
||||
>
|
||||
<field
|
||||
name="active"
|
||||
widget="boolean_button"
|
||||
options="{"terminology": "archive"}"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
name="action_show_invoices"
|
||||
type="object"
|
||||
@@ -348,10 +343,9 @@
|
||||
<field name="arch" type="xml">
|
||||
<field name="partner_id" position="attributes">
|
||||
<attribute name="string">Customer</attribute>
|
||||
<attribute name="domain">[('customer', '=', True)]</attribute>
|
||||
<attribute
|
||||
name="context"
|
||||
>{'default_customer': True, 'default_supplier': False}</attribute>
|
||||
>{'default_customer_rank': 1, 'default_supplier_rank': 0, 'res_partner_search_mode': 'customer', 'show_vat': True}</attribute>
|
||||
</field>
|
||||
<field name="journal_id" position="attributes">
|
||||
<attribute
|
||||
@@ -370,10 +364,9 @@
|
||||
<field name="arch" type="xml">
|
||||
<field name="partner_id" position="attributes">
|
||||
<attribute name="string">Supplier</attribute>
|
||||
<attribute name="domain">[('supplier', '=', True)]</attribute>
|
||||
<attribute
|
||||
name="context"
|
||||
>{'default_customer': False, 'default_supplier': True}</attribute>
|
||||
>{'default_customer_rank': False, 'default_supplier_rank': 1, 'res_partner_search_mode': 'supplier', 'show_vat': True}</attribute>
|
||||
</field>
|
||||
<field name="journal_id" position="attributes">
|
||||
<attribute
|
||||
@@ -460,7 +453,6 @@
|
||||
<record id="action_customer_contract" model="ir.actions.act_window">
|
||||
<field name="name">Customer Contracts</field>
|
||||
<field name="res_model">contract.contract</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain">[('contract_type', '=', 'sale')]</field>
|
||||
<field name="context">
|
||||
@@ -497,7 +489,6 @@
|
||||
<record id="action_supplier_contract" model="ir.actions.act_window">
|
||||
<field name="name">Supplier Contracts</field>
|
||||
<field name="res_model">contract.contract</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain">[('contract_type', '=', 'purchase')]</field>
|
||||
<field name="context">
|
||||
|
||||
@@ -116,7 +116,6 @@
|
||||
<record id="contract_template_action" model="ir.actions.act_window">
|
||||
<field name="name">Contract Templates</field>
|
||||
<field name="res_model">contract.template</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="search_view_id" ref="contract_template_search_view" />
|
||||
<field name="help" type="html">
|
||||
|
||||
@@ -8,13 +8,14 @@
|
||||
<field name="groups_id" eval="[(4, ref('account.group_account_invoice'))]" />
|
||||
<field type="xml" name="arch">
|
||||
<xpath expr="//div[@name='button_box']" position="inside">
|
||||
<field name="customer_rank" invisible="1" />
|
||||
<button
|
||||
name="act_show_contract"
|
||||
type="object"
|
||||
class="oe_stat_button"
|
||||
icon="fa-book"
|
||||
context="{'default_contract_type': 'sale', 'contract_type': 'sale'}"
|
||||
attrs="{'invisible': [('customer','=',False)]}"
|
||||
attrs="{'invisible': [('customer_rank','=',0)]}"
|
||||
help="Show the sale contracts for this partner"
|
||||
>
|
||||
<field
|
||||
@@ -23,13 +24,14 @@
|
||||
string="Sale Contracts"
|
||||
/>
|
||||
</button>
|
||||
<field name="supplier_rank" invisible="1" />
|
||||
<button
|
||||
name="act_show_contract"
|
||||
type="object"
|
||||
class="oe_stat_button"
|
||||
icon="fa-book"
|
||||
context="{'default_contract_type': 'purchase', 'contract_type': 'purchase'}"
|
||||
attrs="{'invisible': [('supplier','=',False)]}"
|
||||
attrs="{'invisible': [('supplier_rank','=',0)]}"
|
||||
help="Show the purchase contracts for this partner"
|
||||
>
|
||||
<field
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Copyright 2020 ACSONE SA/NV
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import api, fields, models
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class ContractContractTerminate(models.TransientModel):
|
||||
@@ -27,7 +27,6 @@ class ContractContractTerminate(models.TransientModel):
|
||||
related="terminate_reason_id.terminate_comment_required"
|
||||
)
|
||||
|
||||
@api.multi
|
||||
def terminate_contract(self):
|
||||
for wizard in self:
|
||||
wizard.contract_id._terminate_contract(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Copyright 2018 ACSONE SA/NV
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import api, fields, models
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class ContractLineWizard(models.TransientModel):
|
||||
@@ -28,7 +28,6 @@ class ContractLineWizard(models.TransientModel):
|
||||
ondelete="cascade",
|
||||
)
|
||||
|
||||
@api.multi
|
||||
def stop(self):
|
||||
for wizard in self:
|
||||
wizard.contract_line_id.stop(
|
||||
@@ -36,7 +35,6 @@ class ContractLineWizard(models.TransientModel):
|
||||
)
|
||||
return True
|
||||
|
||||
@api.multi
|
||||
def plan_successor(self):
|
||||
for wizard in self:
|
||||
wizard.contract_line_id.plan_successor(
|
||||
@@ -44,7 +42,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(
|
||||
@@ -52,7 +49,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)
|
||||
|
||||
@@ -36,7 +36,6 @@ class ContractManuallyCreateInvoice(models.TransientModel):
|
||||
)
|
||||
self.contract_to_invoice_count = len(self.contract_to_invoice_ids)
|
||||
|
||||
@api.multi
|
||||
def action_show_contract_to_invoice(self):
|
||||
self.ensure_one()
|
||||
return {
|
||||
@@ -48,16 +47,15 @@ class ContractManuallyCreateInvoice(models.TransientModel):
|
||||
"context": self.env.context,
|
||||
}
|
||||
|
||||
@api.multi
|
||||
def create_invoice(self):
|
||||
self.ensure_one()
|
||||
invoices = self.env["account.invoice"]
|
||||
invoices = self.env["account.move"]
|
||||
for contract in self.contract_to_invoice_ids:
|
||||
invoices |= contract.recurring_create_invoice()
|
||||
return {
|
||||
"type": "ir.actions.act_window",
|
||||
"name": _("Invoices"),
|
||||
"res_model": "account.invoice",
|
||||
"res_model": "account.move",
|
||||
"domain": [("id", "in", invoices.ids)],
|
||||
"view_mode": "tree,form",
|
||||
"context": self.env.context,
|
||||
|
||||
Reference in New Issue
Block a user