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:
committed by
Francisco Ivan Anton Prieto
parent
a2f71595bf
commit
fc4eb98c74
@@ -14,13 +14,13 @@ Recurring - Contracts Management
|
|||||||
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
|
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
|
||||||
:alt: License: AGPL-3
|
:alt: License: AGPL-3
|
||||||
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fcontract-lightgray.png?logo=github
|
.. |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
|
:alt: OCA/contract
|
||||||
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
|
.. |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
|
:alt: Translate me on Weblate
|
||||||
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
|
.. |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
|
:alt: Try me on Runbot
|
||||||
|
|
||||||
|badge1| |badge2| |badge3| |badge4| |badge5|
|
|badge1| |badge2| |badge3| |badge4| |badge5|
|
||||||
@@ -74,7 +74,6 @@ Known issues / Roadmap
|
|||||||
======================
|
======================
|
||||||
|
|
||||||
* Recover states and others functional fields in Contracts.
|
* 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
|
Bug Tracker
|
||||||
===========
|
===========
|
||||||
@@ -82,7 +81,7 @@ Bug Tracker
|
|||||||
Bugs are tracked on `GitHub Issues <https://github.com/OCA/contract/issues>`_.
|
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.
|
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
|
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.
|
Do not contact contributors directly about support or help with technical issues.
|
||||||
|
|
||||||
@@ -92,9 +91,8 @@ Credits
|
|||||||
Authors
|
Authors
|
||||||
~~~~~~~
|
~~~~~~~
|
||||||
|
|
||||||
* OpenERP SA
|
|
||||||
* Tecnativa
|
* Tecnativa
|
||||||
* LasLabs
|
* ACSONE SA/NV
|
||||||
|
|
||||||
Contributors
|
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
|
mission is to support the collaborative development of Odoo features and
|
||||||
promote its widespread use.
|
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.
|
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 2017 Tecnativa - Vicent Cubells
|
||||||
# Copyright 2016-2017 LasLabs Inc.
|
# Copyright 2016-2017 LasLabs Inc.
|
||||||
# Copyright 2018-2019 ACSONE SA/NV
|
# Copyright 2018-2019 ACSONE SA/NV
|
||||||
|
# Copyright 2020 Tecnativa - Pedro M. Baeza
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": "Recurring - Contracts Management",
|
"name": "Recurring - Contracts Management",
|
||||||
"version": "12.0.7.2.2",
|
"version": "13.0.1.0.0",
|
||||||
"category": "Contract Management",
|
"category": "Contract Management",
|
||||||
"license": "AGPL-3",
|
"license": "AGPL-3",
|
||||||
"author": "OpenERP SA, "
|
"author": "Tecnativa, ACSONE SA/NV, Odoo Community Association (OCA)",
|
||||||
"Tecnativa, "
|
|
||||||
"LasLabs, "
|
|
||||||
"Odoo Community Association (OCA)",
|
|
||||||
"website": "https://github.com/oca/contract",
|
"website": "https://github.com/oca/contract",
|
||||||
"depends": ["base", "account", "product"],
|
"depends": ["base", "account", "product"],
|
||||||
"external_dependencies": {"python": ["dateutil"]},
|
"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
|
||||||
from . import contract_template_line
|
from . import contract_template_line
|
||||||
from . import contract_line
|
from . import contract_line
|
||||||
from . import account_invoice
|
from . import account_move
|
||||||
from . import account_invoice_line
|
|
||||||
from . import res_partner
|
from . import res_partner
|
||||||
from . import contract_tag
|
from . import contract_tag
|
||||||
from . import res_company
|
from . import res_company
|
||||||
from . import res_config_settings
|
from . import res_config_settings
|
||||||
from . import contract_terminate_reason
|
from . import contract_terminate_reason
|
||||||
from . import ir_ui_view
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Copyright 2004-2010 OpenERP SA
|
# Copyright 2004-2010 OpenERP SA
|
||||||
# Copyright 2014 Angel Moya <angel.moya@domatix.com>
|
# 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-2018 Carlos Dauden <carlos.dauden@tecnativa.com>
|
||||||
# Copyright 2016-2017 LasLabs Inc.
|
# Copyright 2016-2017 LasLabs Inc.
|
||||||
# Copyright 2018 ACSONE SA/NV
|
# Copyright 2018 ACSONE SA/NV
|
||||||
@@ -39,7 +39,7 @@ class ContractAbstractContract(models.AbstractModel):
|
|||||||
"res.company",
|
"res.company",
|
||||||
string="Company",
|
string="Company",
|
||||||
required=True,
|
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")
|
@api.onchange("contract_type")
|
||||||
|
|||||||
@@ -10,8 +10,6 @@ from odoo import api, fields, models
|
|||||||
from odoo.exceptions import ValidationError
|
from odoo.exceptions import ValidationError
|
||||||
from odoo.tools.translate import _
|
from odoo.tools.translate import _
|
||||||
|
|
||||||
from odoo.addons import decimal_precision as dp
|
|
||||||
|
|
||||||
|
|
||||||
class ContractAbstractContractLine(models.AbstractModel):
|
class ContractAbstractContractLine(models.AbstractModel):
|
||||||
_name = "contract.abstract.contract.line"
|
_name = "contract.abstract.contract.line"
|
||||||
@@ -35,13 +33,11 @@ class ContractAbstractContractLine(models.AbstractModel):
|
|||||||
inverse="_inverse_price_unit",
|
inverse="_inverse_price_unit",
|
||||||
)
|
)
|
||||||
price_subtotal = fields.Float(
|
price_subtotal = fields.Float(
|
||||||
compute="_compute_price_subtotal",
|
compute="_compute_price_subtotal", digits="Account", string="Sub Total",
|
||||||
digits=dp.get_precision("Account"),
|
|
||||||
string="Sub Total",
|
|
||||||
)
|
)
|
||||||
discount = fields.Float(
|
discount = fields.Float(
|
||||||
string="Discount (%)",
|
string="Discount (%)",
|
||||||
digits=dp.get_precision("Discount"),
|
digits="Discount",
|
||||||
help="Discount that is applied in generated invoices."
|
help="Discount that is applied in generated invoices."
|
||||||
" It should be less or equal to 100",
|
" It should be less or equal to 100",
|
||||||
)
|
)
|
||||||
@@ -123,7 +119,7 @@ class ContractAbstractContractLine(models.AbstractModel):
|
|||||||
ondelete="cascade",
|
ondelete="cascade",
|
||||||
)
|
)
|
||||||
display_type = fields.Selection(
|
display_type = fields.Selection(
|
||||||
selection=[("line_section", "Section"), ("line_note", "Note"),],
|
selection=[("line_section", "Section"), ("line_note", "Note")],
|
||||||
default=False,
|
default=False,
|
||||||
help="Technical field for UX purpose.",
|
help="Technical field for UX purpose.",
|
||||||
)
|
)
|
||||||
@@ -140,6 +136,7 @@ class ContractAbstractContractLine(models.AbstractModel):
|
|||||||
"- Custom: Depending on the recurrence to be define.",
|
"- Custom: Depending on the recurrence to be define.",
|
||||||
)
|
)
|
||||||
is_recurring_note = fields.Boolean(compute="_compute_is_recurring_note")
|
is_recurring_note = fields.Boolean(compute="_compute_is_recurring_note")
|
||||||
|
company_id = fields.Many2one(related="contract_id.company_id", store=True)
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def _get_default_recurring_invoicing_offset(
|
def _get_default_recurring_invoicing_offset(
|
||||||
@@ -153,7 +150,8 @@ class ContractAbstractContractLine(models.AbstractModel):
|
|||||||
else:
|
else:
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
def is_recurring_note(self):
|
@api.depends("display_type", "note_invoicing_mode")
|
||||||
|
def _compute_is_recurring_note(self):
|
||||||
for record in self:
|
for record in self:
|
||||||
record.is_recurring_note = (
|
record.is_recurring_note = (
|
||||||
record.display_type == "line_note"
|
record.display_type == "line_note"
|
||||||
@@ -163,7 +161,8 @@ class ContractAbstractContractLine(models.AbstractModel):
|
|||||||
@api.depends("recurring_invoicing_type", "recurring_rule_type")
|
@api.depends("recurring_invoicing_type", "recurring_rule_type")
|
||||||
def _compute_recurring_invoicing_offset(self):
|
def _compute_recurring_invoicing_offset(self):
|
||||||
for rec in 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
|
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):
|
for line in self.filtered(lambda x: not x.automatic_price):
|
||||||
line.specific_price = line.price_unit
|
line.specific_price = line.price_unit
|
||||||
|
|
||||||
@api.multi
|
|
||||||
@api.depends("quantity", "price_unit", "discount")
|
@api.depends("quantity", "price_unit", "discount")
|
||||||
def _compute_price_subtotal(self):
|
def _compute_price_subtotal(self):
|
||||||
for line in self:
|
for line in self:
|
||||||
@@ -219,14 +217,12 @@ class ContractAbstractContractLine(models.AbstractModel):
|
|||||||
else:
|
else:
|
||||||
line.price_subtotal = subtotal
|
line.price_subtotal = subtotal
|
||||||
|
|
||||||
@api.multi
|
|
||||||
@api.constrains("discount")
|
@api.constrains("discount")
|
||||||
def _check_discount(self):
|
def _check_discount(self):
|
||||||
for line in self:
|
for line in self:
|
||||||
if line.discount > 100:
|
if line.discount > 100:
|
||||||
raise ValidationError(_("Discount should be less or equal to 100"))
|
raise ValidationError(_("Discount should be less or equal to 100"))
|
||||||
|
|
||||||
@api.multi
|
|
||||||
@api.onchange("product_id")
|
@api.onchange("product_id")
|
||||||
def _onchange_product_id(self):
|
def _onchange_product_id(self):
|
||||||
if not self.product_id:
|
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 2004-2010 OpenERP SA
|
||||||
# Copyright 2014 Angel Moya <angel.moya@domatix.com>
|
# 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-2018 Carlos Dauden <carlos.dauden@tecnativa.com>
|
||||||
# Copyright 2016-2017 LasLabs Inc.
|
# Copyright 2016-2017 LasLabs Inc.
|
||||||
# Copyright 2018 ACSONE SA/NV
|
# Copyright 2018 ACSONE SA/NV
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
from odoo import api, fields, models
|
from odoo import api, fields, models
|
||||||
from odoo.exceptions import UserError, ValidationError
|
from odoo.exceptions import UserError, ValidationError
|
||||||
|
from odoo.tests import Form
|
||||||
from odoo.tools.translate import _
|
from odoo.tools.translate import _
|
||||||
|
|
||||||
|
|
||||||
@@ -106,7 +107,6 @@ class ContractContract(models.Model):
|
|||||||
track_visibility="onchange",
|
track_visibility="onchange",
|
||||||
)
|
)
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def _inverse_partner_id(self):
|
def _inverse_partner_id(self):
|
||||||
for rec in self:
|
for rec in self:
|
||||||
if not rec.invoice_partner_id:
|
if not rec.invoice_partner_id:
|
||||||
@@ -114,18 +114,15 @@ class ContractContract(models.Model):
|
|||||||
"invoice"
|
"invoice"
|
||||||
]
|
]
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def _get_related_invoices(self):
|
def _get_related_invoices(self):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
|
|
||||||
invoices = (
|
invoices = (
|
||||||
self.env["account.invoice.line"]
|
self.env["account.move.line"]
|
||||||
.search([("contract_line_id", "in", self.contract_line_ids.ids,)])
|
.search([("contract_line_id", "in", self.contract_line_ids.ids,)])
|
||||||
.mapped("invoice_id")
|
.mapped("move_id")
|
||||||
)
|
|
||||||
invoices |= self.env["account.invoice"].search(
|
|
||||||
[("old_contract_id", "=", self.id)]
|
|
||||||
)
|
)
|
||||||
|
invoices |= self.env["account.move"].search([("old_contract_id", "=", self.id)])
|
||||||
return invoices
|
return invoices
|
||||||
|
|
||||||
def _get_computed_currency(self):
|
def _get_computed_currency(self):
|
||||||
@@ -162,31 +159,18 @@ class ContractContract(models.Model):
|
|||||||
else:
|
else:
|
||||||
rec.manual_currency_id = False
|
rec.manual_currency_id = False
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def _compute_invoice_count(self):
|
def _compute_invoice_count(self):
|
||||||
for rec in self:
|
for rec in self:
|
||||||
rec.invoice_count = len(rec._get_related_invoices())
|
rec.invoice_count = len(rec._get_related_invoices())
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def action_show_invoices(self):
|
def action_show_invoices(self):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
tree_view_ref = (
|
tree_view = self.env.ref("account.view_invoice_tree", raise_if_not_found=False)
|
||||||
"account.invoice_supplier_tree"
|
form_view = self.env.ref("account.view_move_form", raise_if_not_found=False)
|
||||||
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)
|
|
||||||
action = {
|
action = {
|
||||||
"type": "ir.actions.act_window",
|
"type": "ir.actions.act_window",
|
||||||
"name": "Invoices",
|
"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,kanban,form,calendar,pivot,graph,activity",
|
||||||
"domain": [("id", "in", self._get_related_invoices().ids)],
|
"domain": [("id", "in", self._get_related_invoices().ids)],
|
||||||
}
|
}
|
||||||
@@ -216,6 +200,8 @@ class ContractContract(models.Model):
|
|||||||
).mapped("recurring_next_date")
|
).mapped("recurring_next_date")
|
||||||
if recurring_next_date:
|
if recurring_next_date:
|
||||||
contract.recurring_next_date = min(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")
|
@api.depends("contract_line_ids.create_invoice_visibility")
|
||||||
def _compute_create_invoice_visibility(self):
|
def _compute_create_invoice_visibility(self):
|
||||||
@@ -279,7 +265,6 @@ class ContractContract(models.Model):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def _convert_contract_lines(self, contract):
|
def _convert_contract_lines(self, contract):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
new_lines = self.env["contract.line"]
|
new_lines = self.env["contract.line"]
|
||||||
@@ -295,8 +280,12 @@ class ContractContract(models.Model):
|
|||||||
new_lines._onchange_is_auto_renew()
|
new_lines._onchange_is_auto_renew()
|
||||||
return new_lines
|
return new_lines
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def _prepare_invoice(self, date_invoice, journal=None):
|
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()
|
self.ensure_one()
|
||||||
if not journal:
|
if not journal:
|
||||||
journal = (
|
journal = (
|
||||||
@@ -318,36 +307,30 @@ class ContractContract(models.Model):
|
|||||||
invoice_type = "out_invoice"
|
invoice_type = "out_invoice"
|
||||||
if self.contract_type == "purchase":
|
if self.contract_type == "purchase":
|
||||||
invoice_type = "in_invoice"
|
invoice_type = "in_invoice"
|
||||||
vinvoice = (
|
move_form = Form(
|
||||||
self.env["account.invoice"]
|
self.env["account.move"].with_context(
|
||||||
.with_context(force_company=self.company_id.id,)
|
force_company=self.company_id.id, default_type=invoice_type
|
||||||
.new(
|
|
||||||
{
|
|
||||||
"company_id": self.company_id.id,
|
|
||||||
"partner_id": self.invoice_partner_id.id,
|
|
||||||
"type": invoice_type,
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
vinvoice._onchange_partner_id()
|
move_form.partner_id = self.invoice_partner_id
|
||||||
invoice_vals = vinvoice._convert_to_write(vinvoice._cache)
|
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(
|
invoice_vals.update(
|
||||||
{
|
{
|
||||||
"name": self.code,
|
"ref": self.code,
|
||||||
|
"company_id": self.company_id.id,
|
||||||
"currency_id": self.currency_id.id,
|
"currency_id": self.currency_id.id,
|
||||||
"date_invoice": date_invoice,
|
"invoice_date": date_invoice,
|
||||||
"journal_id": journal.id,
|
"journal_id": journal.id,
|
||||||
"origin": self.name,
|
"invoice_origin": self.name,
|
||||||
"user_id": self.user_id.id,
|
"user_id": self.user_id.id,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
if self.payment_term_id:
|
return invoice_vals, move_form
|
||||||
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
|
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def action_contract_send(self):
|
def action_contract_send(self):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
template = self.env.ref("contract.email_contract_template", False)
|
template = self.env.ref("contract.email_contract_template", False)
|
||||||
@@ -362,7 +345,6 @@ class ContractContract(models.Model):
|
|||||||
return {
|
return {
|
||||||
"name": _("Compose Email"),
|
"name": _("Compose Email"),
|
||||||
"type": "ir.actions.act_window",
|
"type": "ir.actions.act_window",
|
||||||
"view_type": "form",
|
|
||||||
"view_mode": "form",
|
"view_mode": "form",
|
||||||
"res_model": "mail.compose.message",
|
"res_model": "mail.compose.message",
|
||||||
"views": [(compose_form.id, "form")],
|
"views": [(compose_form.id, "form")],
|
||||||
@@ -371,40 +353,6 @@ class ContractContract(models.Model):
|
|||||||
"context": ctx,
|
"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
|
@api.model
|
||||||
def _get_contracts_to_invoice_domain(self, date_ref=None):
|
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)])
|
domain.extend([("recurring_next_date", "<=", date_ref)])
|
||||||
return domain
|
return domain
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def _get_lines_to_invoice(self, date_ref):
|
def _get_lines_to_invoice(self, date_ref):
|
||||||
"""
|
"""
|
||||||
This method fetches and returns the lines to invoice on the contract
|
This method fetches and returns the lines to invoice on the contract
|
||||||
@@ -460,7 +407,6 @@ class ContractContract(models.Model):
|
|||||||
previous = line
|
previous = line
|
||||||
return lines2invoice.sorted()
|
return lines2invoice.sorted()
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def _prepare_recurring_invoices_values(self, date_ref=False):
|
def _prepare_recurring_invoices_values(self, date_ref=False):
|
||||||
"""
|
"""
|
||||||
This method builds the list of invoices values to create, based on
|
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)
|
contract_lines = contract._get_lines_to_invoice(date_ref)
|
||||||
if not contract_lines:
|
if not contract_lines:
|
||||||
continue
|
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:
|
for line in contract_lines:
|
||||||
invoice_values.setdefault("invoice_line_ids", [])
|
invoice_line_vals = line._prepare_invoice_line(move_form=move_form)
|
||||||
invoice_line_values = line._prepare_invoice_line(
|
invoice_vals["invoice_line_ids"].append((0, 0, invoice_line_vals))
|
||||||
invoice_values=invoice_values,
|
invoices_values.append(invoice_vals)
|
||||||
)
|
|
||||||
if invoice_line_values:
|
|
||||||
invoice_values["invoice_line_ids"].append(
|
|
||||||
(0, 0, invoice_line_values)
|
|
||||||
)
|
|
||||||
invoices_values.append(invoice_values)
|
|
||||||
contract_lines._update_recurring_next_date()
|
contract_lines._update_recurring_next_date()
|
||||||
return invoices_values
|
return invoices_values
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def recurring_create_invoice(self):
|
def recurring_create_invoice(self):
|
||||||
"""
|
"""
|
||||||
This method triggers the creation of the next invoices of the contracts
|
This method triggers the creation of the next invoices of the contracts
|
||||||
@@ -511,17 +451,16 @@ class ContractContract(models.Model):
|
|||||||
)
|
)
|
||||||
return invoice
|
return invoice
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def _recurring_create_invoice(self, date_ref=False):
|
def _recurring_create_invoice(self, date_ref=False):
|
||||||
invoices_values = self._prepare_recurring_invoices_values(date_ref)
|
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
|
@api.model
|
||||||
def cron_recurring_create_invoice(self, date_ref=None):
|
def cron_recurring_create_invoice(self, date_ref=None):
|
||||||
if not date_ref:
|
if not date_ref:
|
||||||
date_ref = fields.Date.context_today(self)
|
date_ref = fields.Date.context_today(self)
|
||||||
domain = self._get_contracts_to_invoice_domain(date_ref)
|
domain = self._get_contracts_to_invoice_domain(date_ref)
|
||||||
invoices = self.env["account.invoice"]
|
invoices = self.env["account.move"]
|
||||||
# Invoice by companies, so assignation emails get correct context
|
# Invoice by companies, so assignation emails get correct context
|
||||||
companies_to_invoice = self.read_group(domain, ["company_id"], ["company_id"])
|
companies_to_invoice = self.read_group(domain, ["company_id"], ["company_id"])
|
||||||
for row in companies_to_invoice:
|
for row in companies_to_invoice:
|
||||||
@@ -531,7 +470,6 @@ class ContractContract(models.Model):
|
|||||||
invoices |= contracts_to_invoice._recurring_create_invoice(date_ref)
|
invoices |= contracts_to_invoice._recurring_create_invoice(date_ref)
|
||||||
return invoices
|
return invoices
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def action_terminate_contract(self):
|
def action_terminate_contract(self):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
context = {"default_contract_id": self.id}
|
context = {"default_contract_id": self.id}
|
||||||
@@ -539,13 +477,11 @@ class ContractContract(models.Model):
|
|||||||
"type": "ir.actions.act_window",
|
"type": "ir.actions.act_window",
|
||||||
"name": _("Terminate Contract"),
|
"name": _("Terminate Contract"),
|
||||||
"res_model": "contract.contract.terminate",
|
"res_model": "contract.contract.terminate",
|
||||||
"view_type": "form",
|
|
||||||
"view_mode": "form",
|
"view_mode": "form",
|
||||||
"target": "new",
|
"target": "new",
|
||||||
"context": context,
|
"context": context,
|
||||||
}
|
}
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def _terminate_contract(
|
def _terminate_contract(
|
||||||
self, terminate_reason_id, terminate_comment, terminate_date
|
self, terminate_reason_id, terminate_comment, terminate_date
|
||||||
):
|
):
|
||||||
@@ -563,7 +499,6 @@ class ContractContract(models.Model):
|
|||||||
)
|
)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def action_cancel_contract_termination(self):
|
def action_cancel_contract_termination(self):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
self.write(
|
self.write(
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
# Copyright 2017 LasLabs Inc.
|
# Copyright 2017 LasLabs Inc.
|
||||||
# Copyright 2018 ACSONE SA/NV.
|
# Copyright 2018 ACSONE SA/NV.
|
||||||
|
# Copyright 2020 Tecnativa - Pedro M. Baeza
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
@@ -118,7 +119,6 @@ class ContractLine(models.Model):
|
|||||||
default=True,
|
default=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
@api.multi
|
|
||||||
@api.depends(
|
@api.depends(
|
||||||
"date_end", "termination_notice_rule_type", "termination_notice_interval",
|
"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_date = rec.date_end - self.get_relative_delta(
|
||||||
rec.termination_notice_rule_type, rec.termination_notice_interval,
|
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")
|
@api.depends("is_canceled", "date_start", "date_end", "is_auto_renew")
|
||||||
def _compute_state(self):
|
def _compute_state(self):
|
||||||
today = fields.Date.context_today(self)
|
today = fields.Date.context_today(self)
|
||||||
for rec in self:
|
for rec in self:
|
||||||
if rec.display_type:
|
|
||||||
rec.state = False
|
rec.state = False
|
||||||
|
if rec.display_type:
|
||||||
continue
|
continue
|
||||||
if rec.is_canceled:
|
if rec.is_canceled:
|
||||||
rec.state = "canceled"
|
rec.state = "canceled"
|
||||||
@@ -192,8 +193,8 @@ class ContractLine(models.Model):
|
|||||||
("date_end", ">=", today),
|
("date_end", ">=", today),
|
||||||
("date_end", "=", False),
|
("date_end", "=", False),
|
||||||
"|",
|
"|",
|
||||||
"&",
|
|
||||||
("is_auto_renew", "=", True),
|
("is_auto_renew", "=", True),
|
||||||
|
"&",
|
||||||
("is_auto_renew", "=", False),
|
("is_auto_renew", "=", False),
|
||||||
("termination_notice_date", ">", today),
|
("termination_notice_date", ">", today),
|
||||||
]
|
]
|
||||||
@@ -290,7 +291,6 @@ class ContractLine(models.Model):
|
|||||||
)
|
)
|
||||||
def _compute_allowed(self):
|
def _compute_allowed(self):
|
||||||
for rec in self:
|
for rec in self:
|
||||||
if rec.contract_id.is_terminated:
|
|
||||||
rec.update(
|
rec.update(
|
||||||
{
|
{
|
||||||
"is_plan_successor_allowed": False,
|
"is_plan_successor_allowed": False,
|
||||||
@@ -300,6 +300,7 @@ class ContractLine(models.Model):
|
|||||||
"is_un_cancel_allowed": False,
|
"is_un_cancel_allowed": False,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
if rec.contract_id.is_terminated:
|
||||||
continue
|
continue
|
||||||
if rec.date_start:
|
if rec.date_start:
|
||||||
allowed = get_allowed(
|
allowed = get_allowed(
|
||||||
@@ -315,7 +316,9 @@ class ContractLine(models.Model):
|
|||||||
rec.update(
|
rec.update(
|
||||||
{
|
{
|
||||||
"is_plan_successor_allowed": allowed.plan_successor,
|
"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_stop_allowed": allowed.stop,
|
||||||
"is_cancel_allowed": allowed.cancel,
|
"is_cancel_allowed": allowed.cancel,
|
||||||
"is_un_cancel_allowed": allowed.uncancel,
|
"is_un_cancel_allowed": allowed.uncancel,
|
||||||
@@ -618,7 +621,13 @@ class ContractLine(models.Model):
|
|||||||
% line.name
|
% 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):
|
def _compute_create_invoice_visibility(self):
|
||||||
# TODO: depending on the lines, and their order, some sections
|
# TODO: depending on the lines, and their order, some sections
|
||||||
# have no meaning in certain invoices
|
# have no meaning in certain invoices
|
||||||
@@ -633,51 +642,31 @@ class ContractLine(models.Model):
|
|||||||
else:
|
else:
|
||||||
rec.create_invoice_visibility = False
|
rec.create_invoice_visibility = False
|
||||||
|
|
||||||
@api.multi
|
def _prepare_invoice_line(self, move_form):
|
||||||
def _prepare_invoice_line(self, invoice_id=False, invoice_values=False):
|
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
dates = self._get_period_to_invoice(
|
dates = self._get_period_to_invoice(
|
||||||
self.last_date_invoiced, self.recurring_next_date
|
self.last_date_invoiced, self.recurring_next_date
|
||||||
)
|
)
|
||||||
invoice_line_vals = {
|
line_form = move_form.invoice_line_ids.new()
|
||||||
"display_type": self.display_type,
|
line_form.display_type = self.display_type
|
||||||
"product_id": self.product_id.id,
|
line_form.product_id = self.product_id
|
||||||
"quantity": self._get_quantity_to_invoice(*dates),
|
invoice_line_vals = line_form._values_to_save(all_fields=True)
|
||||||
"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
|
|
||||||
name = self._insert_markers(dates[0], dates[1])
|
name = self._insert_markers(dates[0], dates[1])
|
||||||
invoice_line_vals.update(
|
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,
|
"sequence": self.sequence,
|
||||||
"name": name,
|
"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)],
|
"analytic_tag_ids": [(6, 0, self.analytic_tag_ids.ids)],
|
||||||
"price_unit": self.price_unit,
|
"price_unit": self.price_unit,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return invoice_line_vals
|
return invoice_line_vals
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def _get_period_to_invoice(
|
def _get_period_to_invoice(
|
||||||
self, last_date_invoiced, recurring_next_date, stop_at_date_end=True
|
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
|
return first_date_invoiced, last_date_invoiced, recurring_next_date
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def _insert_markers(self, first_date_invoiced, last_date_invoiced):
|
def _insert_markers(self, first_date_invoiced, last_date_invoiced):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
lang_obj = self.env["res.lang"]
|
lang_obj = self.env["res.lang"]
|
||||||
@@ -713,7 +701,6 @@ class ContractLine(models.Model):
|
|||||||
name = name.replace("#END#", last_date_invoiced.strftime(date_format))
|
name = name.replace("#END#", last_date_invoiced.strftime(date_format))
|
||||||
return name
|
return name
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def _update_recurring_next_date(self):
|
def _update_recurring_next_date(self):
|
||||||
for rec in self:
|
for rec in self:
|
||||||
last_date_invoiced = rec.next_period_date_end
|
last_date_invoiced = rec.next_period_date_end
|
||||||
@@ -732,7 +719,6 @@ class ContractLine(models.Model):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def _init_last_date_invoiced(self):
|
def _init_last_date_invoiced(self):
|
||||||
"""Used to init last_date_invoiced for migration purpose"""
|
"""Used to init last_date_invoiced for migration purpose"""
|
||||||
for rec in self:
|
for rec in self:
|
||||||
@@ -778,7 +764,6 @@ class ContractLine(models.Model):
|
|||||||
else:
|
else:
|
||||||
return relativedelta(years=interval)
|
return relativedelta(years=interval)
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def _delay(self, delay_delta):
|
def _delay(self, delay_delta):
|
||||||
"""
|
"""
|
||||||
Delay a contract line
|
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):
|
def _prepare_value_for_stop(self, date_end, manual_renew_needed):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
return {
|
return {
|
||||||
@@ -828,7 +812,6 @@ class ContractLine(models.Model):
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def stop(self, date_end, manual_renew_needed=False, post_message=True):
|
def stop(self, date_end, manual_renew_needed=False, post_message=True):
|
||||||
"""
|
"""
|
||||||
Put date_end on contract line
|
Put date_end on contract line
|
||||||
@@ -868,7 +851,6 @@ class ContractLine(models.Model):
|
|||||||
)
|
)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def _prepare_value_for_plan_successor(
|
def _prepare_value_for_plan_successor(
|
||||||
self, date_start, date_end, is_auto_renew, recurring_next_date=False
|
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
|
values["predecessor_contract_line_id"] = self.id
|
||||||
return values
|
return values
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def plan_successor(
|
def plan_successor(
|
||||||
self,
|
self,
|
||||||
date_start,
|
date_start,
|
||||||
@@ -939,7 +920,6 @@ class ContractLine(models.Model):
|
|||||||
rec.contract_id.message_post(body=msg)
|
rec.contract_id.message_post(body=msg)
|
||||||
return contract_line
|
return contract_line
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def stop_plan_successor(self, date_start, date_end, is_auto_renew):
|
def stop_plan_successor(self, date_start, date_end, is_auto_renew):
|
||||||
"""
|
"""
|
||||||
Stop a contract line for a defined period and start it later
|
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)
|
rec.contract_id.message_post(body=msg)
|
||||||
return contract_line
|
return contract_line
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def cancel(self):
|
def cancel(self):
|
||||||
if not all(self.mapped("is_cancel_allowed")):
|
if not all(self.mapped("is_cancel_allowed")):
|
||||||
raise ValidationError(_("Cancel not allowed for this line"))
|
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})
|
return self.write({"is_canceled": True, "is_auto_renew": False})
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def uncancel(self, recurring_next_date):
|
def uncancel(self, recurring_next_date):
|
||||||
if not all(self.mapped("is_un_cancel_allowed")):
|
if not all(self.mapped("is_un_cancel_allowed")):
|
||||||
raise ValidationError(_("Un-cancel not allowed for this line"))
|
raise ValidationError(_("Un-cancel not allowed for this line"))
|
||||||
@@ -1075,7 +1053,6 @@ class ContractLine(models.Model):
|
|||||||
rec.recurring_next_date = recurring_next_date
|
rec.recurring_next_date = recurring_next_date
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def action_uncancel(self):
|
def action_uncancel(self):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
context = {
|
context = {
|
||||||
@@ -1088,14 +1065,12 @@ class ContractLine(models.Model):
|
|||||||
"type": "ir.actions.act_window",
|
"type": "ir.actions.act_window",
|
||||||
"name": "Un-Cancel Contract Line",
|
"name": "Un-Cancel Contract Line",
|
||||||
"res_model": "contract.line.wizard",
|
"res_model": "contract.line.wizard",
|
||||||
"view_type": "form",
|
|
||||||
"view_mode": "form",
|
"view_mode": "form",
|
||||||
"views": [(view_id, "form")],
|
"views": [(view_id, "form")],
|
||||||
"target": "new",
|
"target": "new",
|
||||||
"context": context,
|
"context": context,
|
||||||
}
|
}
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def action_plan_successor(self):
|
def action_plan_successor(self):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
context = {
|
context = {
|
||||||
@@ -1110,14 +1085,12 @@ class ContractLine(models.Model):
|
|||||||
"type": "ir.actions.act_window",
|
"type": "ir.actions.act_window",
|
||||||
"name": "Plan contract line successor",
|
"name": "Plan contract line successor",
|
||||||
"res_model": "contract.line.wizard",
|
"res_model": "contract.line.wizard",
|
||||||
"view_type": "form",
|
|
||||||
"view_mode": "form",
|
"view_mode": "form",
|
||||||
"views": [(view_id, "form")],
|
"views": [(view_id, "form")],
|
||||||
"target": "new",
|
"target": "new",
|
||||||
"context": context,
|
"context": context,
|
||||||
}
|
}
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def action_stop(self):
|
def action_stop(self):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
context = {
|
context = {
|
||||||
@@ -1130,14 +1103,12 @@ class ContractLine(models.Model):
|
|||||||
"type": "ir.actions.act_window",
|
"type": "ir.actions.act_window",
|
||||||
"name": "Terminate contract line",
|
"name": "Terminate contract line",
|
||||||
"res_model": "contract.line.wizard",
|
"res_model": "contract.line.wizard",
|
||||||
"view_type": "form",
|
|
||||||
"view_mode": "form",
|
"view_mode": "form",
|
||||||
"views": [(view_id, "form")],
|
"views": [(view_id, "form")],
|
||||||
"target": "new",
|
"target": "new",
|
||||||
"context": context,
|
"context": context,
|
||||||
}
|
}
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def action_stop_plan_successor(self):
|
def action_stop_plan_successor(self):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
context = {
|
context = {
|
||||||
@@ -1152,14 +1123,12 @@ class ContractLine(models.Model):
|
|||||||
"type": "ir.actions.act_window",
|
"type": "ir.actions.act_window",
|
||||||
"name": "Suspend contract line",
|
"name": "Suspend contract line",
|
||||||
"res_model": "contract.line.wizard",
|
"res_model": "contract.line.wizard",
|
||||||
"view_type": "form",
|
|
||||||
"view_mode": "form",
|
"view_mode": "form",
|
||||||
"views": [(view_id, "form")],
|
"views": [(view_id, "form")],
|
||||||
"target": "new",
|
"target": "new",
|
||||||
"context": context,
|
"context": context,
|
||||||
}
|
}
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def _get_renewal_new_date_end(self):
|
def _get_renewal_new_date_end(self):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
date_start = self.date_end + relativedelta(days=1)
|
date_start = self.date_end + relativedelta(days=1)
|
||||||
@@ -1168,7 +1137,6 @@ class ContractLine(models.Model):
|
|||||||
)
|
)
|
||||||
return date_end
|
return date_end
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def _renew_create_line(self, date_end):
|
def _renew_create_line(self, date_end):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
date_start = self.date_end + relativedelta(days=1)
|
date_start = self.date_end + relativedelta(days=1)
|
||||||
@@ -1180,13 +1148,11 @@ class ContractLine(models.Model):
|
|||||||
new_line._onchange_date_start()
|
new_line._onchange_date_start()
|
||||||
return new_line
|
return new_line
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def _renew_extend_line(self, date_end):
|
def _renew_extend_line(self, date_end):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
self.date_end = date_end
|
self.date_end = date_end
|
||||||
return self
|
return self
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def renew(self):
|
def renew(self):
|
||||||
res = self.env["contract.line"]
|
res = self.env["contract.line"]
|
||||||
for rec in self:
|
for rec in self:
|
||||||
@@ -1240,7 +1206,6 @@ class ContractLine(models.Model):
|
|||||||
view_id = self.env.ref("contract.contract_line_customer_form_view").id
|
view_id = self.env.ref("contract.contract_line_customer_form_view").id
|
||||||
return super().fields_view_get(view_id, view_type, toolbar, submenu)
|
return super().fields_view_get(view_id, view_type, toolbar, submenu)
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def unlink(self):
|
def unlink(self):
|
||||||
"""stop unlink uncnacled lines"""
|
"""stop unlink uncnacled lines"""
|
||||||
for record in self:
|
for record in self:
|
||||||
@@ -1248,7 +1213,6 @@ class ContractLine(models.Model):
|
|||||||
raise ValidationError(_("Contract line must be canceled before delete"))
|
raise ValidationError(_("Contract line must be canceled before delete"))
|
||||||
return super().unlink()
|
return super().unlink()
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def _get_quantity_to_invoice(
|
def _get_quantity_to_invoice(
|
||||||
self, period_first_date, period_last_date, invoice_date
|
self, period_first_date, period_last_date, invoice_date
|
||||||
):
|
):
|
||||||
|
|||||||
@@ -11,5 +11,5 @@ class ContractTag(models.Model):
|
|||||||
|
|
||||||
name = fields.Char(requirment=True)
|
name = fields.Char(requirment=True)
|
||||||
company_id = fields.Many2one(
|
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",
|
comodel_name="contract.template",
|
||||||
required=True,
|
required=True,
|
||||||
ondelete="cascade",
|
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",
|
string="Purchase Contracts", compute="_compute_contract_count",
|
||||||
)
|
)
|
||||||
contract_ids = fields.One2many(
|
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):
|
def _compute_contract_count(self):
|
||||||
|
|||||||
@@ -1,2 +1 @@
|
|||||||
* Recover states and others functional fields in Contracts.
|
* 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">
|
<odoo noupdate="1">
|
||||||
<record id="rule_contract_contract_multi_company" model="ir.rule">
|
<record id="rule_contract_contract_multi_company" model="ir.rule">
|
||||||
<field name="name">Contract contract multi-company</field>
|
<field name="name">Contract contract multi-company</field>
|
||||||
@@ -5,7 +6,7 @@
|
|||||||
<field name="global" eval="True" />
|
<field name="global" eval="True" />
|
||||||
<field
|
<field
|
||||||
name="domain_force"
|
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>
|
||||||
<record id="rule_contract_line_multi_company" model="ir.rule">
|
<record id="rule_contract_line_multi_company" model="ir.rule">
|
||||||
<field name="name">Contract line multi-company</field>
|
<field name="name">Contract line multi-company</field>
|
||||||
@@ -13,7 +14,7 @@
|
|||||||
<field name="global" eval="True" />
|
<field name="global" eval="True" />
|
||||||
<field
|
<field
|
||||||
name="domain_force"
|
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>
|
||||||
<record id="rule_contract_template_multi_company" model="ir.rule">
|
<record id="rule_contract_template_multi_company" model="ir.rule">
|
||||||
<field name="name">Contract template multi-company</field>
|
<field name="name">Contract template multi-company</field>
|
||||||
@@ -21,7 +22,7 @@
|
|||||||
<field name="global" eval="True" />
|
<field name="global" eval="True" />
|
||||||
<field
|
<field
|
||||||
name="domain_force"
|
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>
|
||||||
<record id="rule_contract_template_line_multi_company" model="ir.rule">
|
<record id="rule_contract_template_line_multi_company" model="ir.rule">
|
||||||
<field name="name">Contract template line multi-company</field>
|
<field name="name">Contract template line multi-company</field>
|
||||||
@@ -29,6 +30,6 @@
|
|||||||
<field name="global" eval="True" />
|
<field name="global" eval="True" />
|
||||||
<field
|
<field
|
||||||
name="domain_force"
|
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>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|||||||
@@ -16,6 +16,6 @@
|
|||||||
<field name="model_id" ref="model_contract_tag" />
|
<field name="model_id" ref="model_contract_tag" />
|
||||||
<field
|
<field
|
||||||
name="domain_force"
|
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>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|||||||
@@ -367,7 +367,7 @@ ul.auto-toc {
|
|||||||
!! This file is generated by oca-gen-addon-readme !!
|
!! This file is generated by oca-gen-addon-readme !!
|
||||||
!! changes will be overwritten. !!
|
!! 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
|
<p>This module enables contracts management with recurring
|
||||||
invoicing functions. Also you can print and send by email contract report.</p>
|
invoicing functions. Also you can print and send by email contract report.</p>
|
||||||
<p>It works for customer contract and supplier contracts.</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>
|
<h1><a class="toc-backref" href="#id3">Known issues / Roadmap</a></h1>
|
||||||
<ul class="simple">
|
<ul class="simple">
|
||||||
<li>Recover states and others functional fields in Contracts.</li>
|
<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>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="bug-tracker">
|
<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>.
|
<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.
|
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
|
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>
|
<p>Do not contact contributors directly about support or help with technical issues.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="credits">
|
<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">
|
<div class="section" id="authors">
|
||||||
<h2><a class="toc-backref" href="#id6">Authors</a></h2>
|
<h2><a class="toc-backref" href="#id6">Authors</a></h2>
|
||||||
<ul class="simple">
|
<ul class="simple">
|
||||||
<li>OpenERP SA</li>
|
|
||||||
<li>Tecnativa</li>
|
<li>Tecnativa</li>
|
||||||
<li>LasLabs</li>
|
<li>ACSONE SA/NV</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="contributors">
|
<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
|
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
|
||||||
mission is to support the collaborative development of Odoo features and
|
mission is to support the collaborative development of Odoo features and
|
||||||
promote its widespread use.</p>
|
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>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ odoo.define("contract.section_and_note_backend", function(require) {
|
|||||||
var result = this._super.apply(this, arguments);
|
var result = this._super.apply(this, arguments);
|
||||||
if (this.view.arch.tag === "tree") {
|
if (this.view.arch.tag === "tree") {
|
||||||
result.include({
|
result.include({
|
||||||
_renderBodyCell: function(record, node, index, options) {
|
_renderBodyCell: function(record) {
|
||||||
var $cell = this._super.apply(this, arguments);
|
var $cell = this._super.apply(this, arguments);
|
||||||
|
|
||||||
var isSection = record.data.display_type === "line_section";
|
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).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
from . import test_contract
|
from . import test_contract
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Copyright 2018 Tecnativa - Carlos Dauden
|
# 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).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
@@ -22,7 +22,7 @@ class TestContractBase(common.SavepointCase):
|
|||||||
super().setUpClass()
|
super().setUpClass()
|
||||||
cls.today = fields.Date.today()
|
cls.today = fields.Date.today()
|
||||||
cls.pricelist = cls.env["product.pricelist"].create(
|
cls.pricelist = cls.env["product.pricelist"].create(
|
||||||
{"name": "pricelist for contract test",}
|
{"name": "pricelist for contract test"}
|
||||||
)
|
)
|
||||||
cls.partner = cls.env["res.partner"].create(
|
cls.partner = cls.env["res.partner"].create(
|
||||||
{
|
{
|
||||||
@@ -163,7 +163,7 @@ class TestContract(TestContractBase):
|
|||||||
self.assertTrue(self.invoice_monthly)
|
self.assertTrue(self.invoice_monthly)
|
||||||
self.assertEqual(self.acct_line.recurring_next_date, recurring_next_date)
|
self.assertEqual(self.acct_line.recurring_next_date, recurring_next_date)
|
||||||
self.inv_line = self.invoice_monthly.invoice_line_ids[0]
|
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.assertAlmostEqual(self.inv_line.price_subtotal, 50.0)
|
||||||
self.assertEqual(self.contract.user_id, self.invoice_monthly.user_id)
|
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):
|
def test_check_recurring_next_date_start_date(self):
|
||||||
with self.assertRaises(ValidationError):
|
with self.assertRaises(ValidationError):
|
||||||
self.acct_line.write(
|
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):
|
def test_onchange_contract_template_id(self):
|
||||||
@@ -409,7 +409,7 @@ class TestContract(TestContractBase):
|
|||||||
self.contract._onchange_contract_template_id()
|
self.contract._onchange_contract_template_id()
|
||||||
res = {
|
res = {
|
||||||
"contract_line_ids": [
|
"contract_line_ids": [
|
||||||
(0, 0, {"display_type": "line_section", "name": "Test section",}),
|
(0, 0, {"display_type": "line_section", "name": "Test section"}),
|
||||||
(
|
(
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
@@ -536,7 +536,6 @@ class TestContract(TestContractBase):
|
|||||||
{
|
{
|
||||||
"name": "Customer Contracts",
|
"name": "Customer Contracts",
|
||||||
"type": "ir.actions.act_window",
|
"type": "ir.actions.act_window",
|
||||||
"view_type": "form",
|
|
||||||
"res_model": "contract.contract",
|
"res_model": "contract.contract",
|
||||||
"xml_id": "contract.action_customer_contract",
|
"xml_id": "contract.action_customer_contract",
|
||||||
},
|
},
|
||||||
@@ -731,7 +730,6 @@ class TestContract(TestContractBase):
|
|||||||
"recurring_invoicing_type": recurring_invoicing_type,
|
"recurring_invoicing_type": recurring_invoicing_type,
|
||||||
"recurring_rule_type": recurring_rule_type,
|
"recurring_rule_type": recurring_rule_type,
|
||||||
"recurring_interval": recurring_interval,
|
"recurring_interval": recurring_interval,
|
||||||
"max_date_end": max_date_end,
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -777,7 +775,7 @@ class TestContract(TestContractBase):
|
|||||||
|
|
||||||
Result = namedtuple(
|
Result = namedtuple(
|
||||||
"Result",
|
"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 = namedtuple(
|
||||||
"Combination",
|
"Combination",
|
||||||
@@ -1045,7 +1043,7 @@ class TestContract(TestContractBase):
|
|||||||
def test_stop_past_contract_line(self):
|
def test_stop_past_contract_line(self):
|
||||||
"""Past contract line are ignored on stop"""
|
"""Past contract line are ignored on stop"""
|
||||||
self.acct_line.write(
|
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.acct_line.stop(self.today + relativedelta(months=7))
|
||||||
self.assertEqual(self.acct_line.date_end, self.today + relativedelta(months=5))
|
self.assertEqual(self.acct_line.date_end, self.today + relativedelta(months=5))
|
||||||
@@ -1479,7 +1477,7 @@ class TestContract(TestContractBase):
|
|||||||
|
|
||||||
def test_cancel(self):
|
def test_cancel(self):
|
||||||
self.acct_line.write(
|
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.acct_line.cancel()
|
||||||
self.assertTrue(self.acct_line.is_canceled)
|
self.assertTrue(self.acct_line.is_canceled)
|
||||||
@@ -1493,7 +1491,7 @@ class TestContract(TestContractBase):
|
|||||||
self.acct_line.cancel()
|
self.acct_line.cancel()
|
||||||
self.assertTrue(self.acct_line.is_canceled)
|
self.assertTrue(self.acct_line.is_canceled)
|
||||||
wizard = self.env["contract.line.wizard"].create(
|
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()
|
wizard.uncancel()
|
||||||
self.assertFalse(self.acct_line.is_canceled)
|
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.date_end = "2018-03-15"
|
||||||
self.acct_line._onchange_date_start()
|
self.acct_line._onchange_date_start()
|
||||||
contracts = self.contract2
|
contracts = self.contract2
|
||||||
for i in range(10):
|
for _i in range(10):
|
||||||
contracts |= self.contract.copy()
|
contracts |= self.contract.copy()
|
||||||
self.env["contract.contract"].cron_recurring_create_invoice()
|
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)]
|
[("contract_line_id", "in", contracts.mapped("contract_line_ids").ids)]
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
@@ -1647,28 +1645,28 @@ class TestContract(TestContractBase):
|
|||||||
self.acct_line._onchange_date_start()
|
self.acct_line._onchange_date_start()
|
||||||
self.contract2.unlink()
|
self.contract2.unlink()
|
||||||
contracts = self.contract
|
contracts = self.contract
|
||||||
for i in range(10):
|
for _i in range(10):
|
||||||
contracts |= self.contract.copy()
|
contracts |= self.contract.copy()
|
||||||
wizard = self.env["contract.manually.create.invoice"].create(
|
wizard = self.env["contract.manually.create.invoice"].create(
|
||||||
{"invoice_date": self.today}
|
{"invoice_date": self.today}
|
||||||
)
|
)
|
||||||
wizard.action_show_contract_to_invoice()
|
wizard.action_show_contract_to_invoice()
|
||||||
contract_to_invoice_count = wizard.contract_to_invoice_count
|
contract_to_invoice_count = wizard.contract_to_invoice_count
|
||||||
self.assertEqual(
|
self.assertFalse(
|
||||||
contracts,
|
contracts
|
||||||
self.env["contract.contract"].search(
|
- self.env["contract.contract"].search(
|
||||||
wizard.action_show_contract_to_invoice()["domain"]
|
wizard.action_show_contract_to_invoice()["domain"]
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
action = wizard.create_invoice()
|
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)]
|
[("contract_line_id", "in", contracts.mapped("contract_line_ids").ids)]
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
len(contracts.mapped("contract_line_ids")), len(invoice_lines),
|
len(contracts.mapped("contract_line_ids")), len(invoice_lines),
|
||||||
)
|
)
|
||||||
invoices = self.env["account.invoice"].search(action["domain"])
|
invoices = self.env["account.move"].search(action["domain"])
|
||||||
self.assertEqual(invoice_lines.mapped("invoice_id"), invoices)
|
self.assertFalse(invoice_lines.mapped("move_id") - invoices)
|
||||||
self.assertEqual(len(invoices), contract_to_invoice_count)
|
self.assertEqual(len(invoices), contract_to_invoice_count)
|
||||||
|
|
||||||
def test_get_period_to_invoice_monthlylastday_postpaid(self):
|
def test_get_period_to_invoice_monthlylastday_postpaid(self):
|
||||||
@@ -1957,6 +1955,7 @@ class TestContract(TestContractBase):
|
|||||||
]
|
]
|
||||||
self.assertEqual(set(lines.mapped("state")), set(states))
|
self.assertEqual(set(lines.mapped("state")), set(states))
|
||||||
# Test search method
|
# Test search method
|
||||||
|
lines.flush() # Needed for computed stored fields like termination_notice_date
|
||||||
for state in states:
|
for state in states:
|
||||||
lines = self.env["contract.line"].search([("state", "=", state)])
|
lines = self.env["contract.line"].search([("state", "=", state)])
|
||||||
self.assertTrue(lines, state)
|
self.assertTrue(lines, state)
|
||||||
@@ -2036,10 +2035,10 @@ class TestContract(TestContractBase):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
line_prepaid = self.acct_line.copy(
|
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(
|
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 = line_monthlylastday | line_prepaid | line_postpaid
|
||||||
lines.write({"last_date_invoiced": False})
|
lines.write({"last_date_invoiced": False})
|
||||||
@@ -2101,7 +2100,7 @@ class TestContract(TestContractBase):
|
|||||||
|
|
||||||
def test_multicompany_partner_edited(self):
|
def test_multicompany_partner_edited(self):
|
||||||
"""Editing a partner with contracts in several companies works."""
|
"""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(
|
unprivileged_user = self.env["res.users"].create(
|
||||||
{
|
{
|
||||||
"name": "unprivileged test user",
|
"name": "unprivileged test user",
|
||||||
@@ -2111,16 +2110,14 @@ class TestContract(TestContractBase):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
parent_partner = self.env["res.partner"].create(
|
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
|
# Assume contract 2 is for company 2
|
||||||
self.contract2.company_id = company2
|
self.contract2.company_id = company2
|
||||||
# Update the partner attached to both contracts
|
# 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
|
company_id=company2.id, force_company=company2.id
|
||||||
).write(
|
).write({"is_company": False, "parent_id": parent_partner.id})
|
||||||
{"is_company": False, "parent_id": parent_partner.id,}
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_sale_fields_view_get(self):
|
def test_sale_fields_view_get(self):
|
||||||
sale_form_view = self.env.ref("contract.contract_line_customer_form_view")
|
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)
|
self.assertEqual(self.contract.invoice_count, 3)
|
||||||
|
|
||||||
def test_contract_count_invoice_2(self):
|
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()
|
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)
|
self.assertEqual(self.contract2.currency_id, currency_cad)
|
||||||
# Get currency from contract pricelist
|
# Get currency from contract pricelist
|
||||||
pricelist = self.env["product.pricelist"].create(
|
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.pricelist_id = pricelist.id
|
||||||
self.contract2.contract_line_ids.automatic_price = True
|
self.contract2.contract_line_ids.automatic_price = True
|
||||||
|
|||||||
@@ -68,19 +68,14 @@
|
|||||||
/>
|
/>
|
||||||
</header>
|
</header>
|
||||||
<sheet string="Contract">
|
<sheet string="Contract">
|
||||||
<div class="oe_button_box" name="button_box">
|
<field name="active" invisible="1" />
|
||||||
<button
|
<widget
|
||||||
class="oe_stat_button"
|
name="web_ribbon"
|
||||||
type="object"
|
text="Archived"
|
||||||
name="toggle_active"
|
bg_color="bg-danger"
|
||||||
icon="fa-archive"
|
attrs="{'invisible': [('active', '=', True)]}"
|
||||||
>
|
|
||||||
<field
|
|
||||||
name="active"
|
|
||||||
widget="boolean_button"
|
|
||||||
options="{"terminology": "archive"}"
|
|
||||||
/>
|
/>
|
||||||
</button>
|
<div class="oe_button_box" name="button_box">
|
||||||
<button
|
<button
|
||||||
name="action_show_invoices"
|
name="action_show_invoices"
|
||||||
type="object"
|
type="object"
|
||||||
@@ -348,10 +343,9 @@
|
|||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<field name="partner_id" position="attributes">
|
<field name="partner_id" position="attributes">
|
||||||
<attribute name="string">Customer</attribute>
|
<attribute name="string">Customer</attribute>
|
||||||
<attribute name="domain">[('customer', '=', True)]</attribute>
|
|
||||||
<attribute
|
<attribute
|
||||||
name="context"
|
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>
|
||||||
<field name="journal_id" position="attributes">
|
<field name="journal_id" position="attributes">
|
||||||
<attribute
|
<attribute
|
||||||
@@ -370,10 +364,9 @@
|
|||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<field name="partner_id" position="attributes">
|
<field name="partner_id" position="attributes">
|
||||||
<attribute name="string">Supplier</attribute>
|
<attribute name="string">Supplier</attribute>
|
||||||
<attribute name="domain">[('supplier', '=', True)]</attribute>
|
|
||||||
<attribute
|
<attribute
|
||||||
name="context"
|
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>
|
||||||
<field name="journal_id" position="attributes">
|
<field name="journal_id" position="attributes">
|
||||||
<attribute
|
<attribute
|
||||||
@@ -460,7 +453,6 @@
|
|||||||
<record id="action_customer_contract" model="ir.actions.act_window">
|
<record id="action_customer_contract" model="ir.actions.act_window">
|
||||||
<field name="name">Customer Contracts</field>
|
<field name="name">Customer Contracts</field>
|
||||||
<field name="res_model">contract.contract</field>
|
<field name="res_model">contract.contract</field>
|
||||||
<field name="view_type">form</field>
|
|
||||||
<field name="view_mode">tree,form</field>
|
<field name="view_mode">tree,form</field>
|
||||||
<field name="domain">[('contract_type', '=', 'sale')]</field>
|
<field name="domain">[('contract_type', '=', 'sale')]</field>
|
||||||
<field name="context">
|
<field name="context">
|
||||||
@@ -497,7 +489,6 @@
|
|||||||
<record id="action_supplier_contract" model="ir.actions.act_window">
|
<record id="action_supplier_contract" model="ir.actions.act_window">
|
||||||
<field name="name">Supplier Contracts</field>
|
<field name="name">Supplier Contracts</field>
|
||||||
<field name="res_model">contract.contract</field>
|
<field name="res_model">contract.contract</field>
|
||||||
<field name="view_type">form</field>
|
|
||||||
<field name="view_mode">tree,form</field>
|
<field name="view_mode">tree,form</field>
|
||||||
<field name="domain">[('contract_type', '=', 'purchase')]</field>
|
<field name="domain">[('contract_type', '=', 'purchase')]</field>
|
||||||
<field name="context">
|
<field name="context">
|
||||||
|
|||||||
@@ -116,7 +116,6 @@
|
|||||||
<record id="contract_template_action" model="ir.actions.act_window">
|
<record id="contract_template_action" model="ir.actions.act_window">
|
||||||
<field name="name">Contract Templates</field>
|
<field name="name">Contract Templates</field>
|
||||||
<field name="res_model">contract.template</field>
|
<field name="res_model">contract.template</field>
|
||||||
<field name="view_type">form</field>
|
|
||||||
<field name="view_mode">tree,form</field>
|
<field name="view_mode">tree,form</field>
|
||||||
<field name="search_view_id" ref="contract_template_search_view" />
|
<field name="search_view_id" ref="contract_template_search_view" />
|
||||||
<field name="help" type="html">
|
<field name="help" type="html">
|
||||||
|
|||||||
@@ -8,13 +8,14 @@
|
|||||||
<field name="groups_id" eval="[(4, ref('account.group_account_invoice'))]" />
|
<field name="groups_id" eval="[(4, ref('account.group_account_invoice'))]" />
|
||||||
<field type="xml" name="arch">
|
<field type="xml" name="arch">
|
||||||
<xpath expr="//div[@name='button_box']" position="inside">
|
<xpath expr="//div[@name='button_box']" position="inside">
|
||||||
|
<field name="customer_rank" invisible="1" />
|
||||||
<button
|
<button
|
||||||
name="act_show_contract"
|
name="act_show_contract"
|
||||||
type="object"
|
type="object"
|
||||||
class="oe_stat_button"
|
class="oe_stat_button"
|
||||||
icon="fa-book"
|
icon="fa-book"
|
||||||
context="{'default_contract_type': 'sale', 'contract_type': 'sale'}"
|
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"
|
help="Show the sale contracts for this partner"
|
||||||
>
|
>
|
||||||
<field
|
<field
|
||||||
@@ -23,13 +24,14 @@
|
|||||||
string="Sale Contracts"
|
string="Sale Contracts"
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
|
<field name="supplier_rank" invisible="1" />
|
||||||
<button
|
<button
|
||||||
name="act_show_contract"
|
name="act_show_contract"
|
||||||
type="object"
|
type="object"
|
||||||
class="oe_stat_button"
|
class="oe_stat_button"
|
||||||
icon="fa-book"
|
icon="fa-book"
|
||||||
context="{'default_contract_type': 'purchase', 'contract_type': 'purchase'}"
|
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"
|
help="Show the purchase contracts for this partner"
|
||||||
>
|
>
|
||||||
<field
|
<field
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Copyright 2020 ACSONE SA/NV
|
# Copyright 2020 ACSONE SA/NV
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# 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):
|
class ContractContractTerminate(models.TransientModel):
|
||||||
@@ -27,7 +27,6 @@ class ContractContractTerminate(models.TransientModel):
|
|||||||
related="terminate_reason_id.terminate_comment_required"
|
related="terminate_reason_id.terminate_comment_required"
|
||||||
)
|
)
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def terminate_contract(self):
|
def terminate_contract(self):
|
||||||
for wizard in self:
|
for wizard in self:
|
||||||
wizard.contract_id._terminate_contract(
|
wizard.contract_id._terminate_contract(
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Copyright 2018 ACSONE SA/NV
|
# Copyright 2018 ACSONE SA/NV
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# 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):
|
class ContractLineWizard(models.TransientModel):
|
||||||
@@ -28,7 +28,6 @@ class ContractLineWizard(models.TransientModel):
|
|||||||
ondelete="cascade",
|
ondelete="cascade",
|
||||||
)
|
)
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
for wizard in self:
|
for wizard in self:
|
||||||
wizard.contract_line_id.stop(
|
wizard.contract_line_id.stop(
|
||||||
@@ -36,7 +35,6 @@ class ContractLineWizard(models.TransientModel):
|
|||||||
)
|
)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def plan_successor(self):
|
def plan_successor(self):
|
||||||
for wizard in self:
|
for wizard in self:
|
||||||
wizard.contract_line_id.plan_successor(
|
wizard.contract_line_id.plan_successor(
|
||||||
@@ -44,7 +42,6 @@ class ContractLineWizard(models.TransientModel):
|
|||||||
)
|
)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def stop_plan_successor(self):
|
def stop_plan_successor(self):
|
||||||
for wizard in self:
|
for wizard in self:
|
||||||
wizard.contract_line_id.stop_plan_successor(
|
wizard.contract_line_id.stop_plan_successor(
|
||||||
@@ -52,7 +49,6 @@ class ContractLineWizard(models.TransientModel):
|
|||||||
)
|
)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def uncancel(self):
|
def uncancel(self):
|
||||||
for wizard in self:
|
for wizard in self:
|
||||||
wizard.contract_line_id.uncancel(wizard.recurring_next_date)
|
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)
|
self.contract_to_invoice_count = len(self.contract_to_invoice_ids)
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def action_show_contract_to_invoice(self):
|
def action_show_contract_to_invoice(self):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
return {
|
return {
|
||||||
@@ -48,16 +47,15 @@ class ContractManuallyCreateInvoice(models.TransientModel):
|
|||||||
"context": self.env.context,
|
"context": self.env.context,
|
||||||
}
|
}
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def create_invoice(self):
|
def create_invoice(self):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
invoices = self.env["account.invoice"]
|
invoices = self.env["account.move"]
|
||||||
for contract in self.contract_to_invoice_ids:
|
for contract in self.contract_to_invoice_ids:
|
||||||
invoices |= contract.recurring_create_invoice()
|
invoices |= contract.recurring_create_invoice()
|
||||||
return {
|
return {
|
||||||
"type": "ir.actions.act_window",
|
"type": "ir.actions.act_window",
|
||||||
"name": _("Invoices"),
|
"name": _("Invoices"),
|
||||||
"res_model": "account.invoice",
|
"res_model": "account.move",
|
||||||
"domain": [("id", "in", invoices.ids)],
|
"domain": [("id", "in", invoices.ids)],
|
||||||
"view_mode": "tree,form",
|
"view_mode": "tree,form",
|
||||||
"context": self.env.context,
|
"context": self.env.context,
|
||||||
|
|||||||
Reference in New Issue
Block a user