[MIG] product_contract: Migration to 16.0

This commit is contained in:
Rad0van
2023-06-08 18:22:40 +02:00
parent b3f1fa3a39
commit a41cafe605
11 changed files with 109 additions and 90 deletions

View File

@@ -2,10 +2,13 @@
Recurring - Product Contract
============================
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:ad8724fbb6c54e3f450ed7f3b3aa16384ed79b097065f0de96cc56e87eef2e71
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
@@ -14,14 +17,14 @@ Recurring - Product Contract
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fcontract-lightgray.png?logo=github
:target: https://github.com/OCA/contract/tree/14.0/product_contract
:target: https://github.com/OCA/contract/tree/16.0/product_contract
:alt: OCA/contract
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/contract-14-0/contract-14-0-product_contract
:target: https://translation.odoo-community.org/projects/contract-16-0/contract-16-0-product_contract
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
:target: https://runbot.odoo-community.org/runbot/110/14.0
:alt: Try me on Runbot
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/contract&target_branch=16.0
:alt: Try me on Runboat
|badge1| |badge2| |badge3| |badge4| |badge5|
@@ -51,8 +54,8 @@ Bug Tracker
Bugs are tracked on `GitHub Issues <https://github.com/OCA/contract/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
`feedback <https://github.com/OCA/contract/issues/new?body=module:%20product_contract%0Aversion:%2014.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/contract/issues/new?body=module:%20product_contract%0Aversion:%2016.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.
@@ -96,6 +99,6 @@ Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:
|maintainer-sbejaoui|
This module is part of the `OCA/contract <https://github.com/OCA/contract/tree/14.0/product_contract>`_ project on GitHub.
This module is part of the `OCA/contract <https://github.com/OCA/contract/tree/16.0/product_contract>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View File

@@ -4,7 +4,7 @@
{
"name": "Recurring - Product Contract",
"version": "14.0.1.0.1",
"version": "16.0.1.0.0",
"category": "Contract Management",
"license": "AGPL-3",
"author": "LasLabs, " "ACSONE SA/NV, " "Odoo Community Association (OCA)",

View File

@@ -3,7 +3,7 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api, fields, models
from odoo import Command, api, fields, models
class ContractLine(models.Model):
@@ -17,10 +17,10 @@ class ContractLine(models.Model):
copy=False,
)
def _prepare_invoice_line(self, move_form):
res = super(ContractLine, self)._prepare_invoice_line(move_form)
def _prepare_invoice_line(self):
res = super()._prepare_invoice_line()
if self.sale_order_line_id and res:
res["sale_line_ids"] = [(6, 0, [self.sale_order_line_id.id])]
res["sale_line_ids"] = [Command.set([self.sale_order_line_id.id])]
return res
def _get_auto_renew_rule_type(self):
@@ -37,9 +37,6 @@ class ContractLine(models.Model):
if rec.product_id.is_contract:
rec.update(
{
"recurring_rule_type": rec.product_id.recurring_rule_type,
"recurring_invoicing_type": rec.product_id.recurring_invoicing_type,
"recurring_interval": 1,
"is_auto_renew": rec.product_id.is_auto_renew,
"auto_renew_interval": rec.product_id.auto_renew_interval,
"auto_renew_rule_type": rec.product_id.auto_renew_rule_type,
@@ -51,3 +48,30 @@ class ContractLine(models.Model):
),
}
)
def _set_recurrence_field(self, field):
res = super()._set_recurrence_field(field)
for record in self:
if record.product_id.is_contract and field in record.product_id:
record[field] = record.product_id[field]
return res
@api.depends(
"contract_id.recurring_rule_type", "contract_id.line_recurrence", "product_id"
)
def _compute_recurring_rule_type(self):
return super()._compute_recurring_rule_type()
@api.depends(
"contract_id.recurring_invoicing_type",
"contract_id.line_recurrence",
"product_id",
)
def _compute_recurring_invoicing_type(self):
return super()._compute_recurring_invoicing_type()
@api.depends(
"contract_id.recurring_interval", "contract_id.line_recurrence", "product_id"
)
def _compute_recurring_interval(self):
return super()._compute_recurring_interval()

View File

@@ -68,12 +68,12 @@ class ProductTemplate(models.Model):
self.with_company(company).write(
{"property_contract_template_id": False}
)
super().write(vals)
return super().write(vals)
@api.constrains("is_contract", "type")
def _check_contract_product_type(self):
"""
Contract product should be service type
"""
if self.is_contract and self.type != "service":
if any([product.is_contract and product.type != "service" for product in self]):
raise ValidationError(_("Contract product should be service type"))

View File

@@ -14,7 +14,7 @@ class SaleOrder(models.Model):
need_contract_creation = fields.Boolean(compute="_compute_need_contract_creation")
@api.constrains("state")
def _check_contact_is_not_terminated(self):
def _check_contract_is_not_terminated(self):
for rec in self:
if rec.state not in (
"sale",
@@ -49,9 +49,7 @@ class SaleOrder(models.Model):
def _prepare_contract_value(self, contract_template):
self.ensure_one()
return {
"name": "{template_name}: {sale_name}".format(
template_name=contract_template.name, sale_name=self.name
),
"name": f"{contract_template.name}: {self.name}",
"partner_id": self.partner_id.id,
"company_id": self.company_id.id,
"contract_template_id": contract_template.id,
@@ -59,7 +57,7 @@ class SaleOrder(models.Model):
"payment_term_id": self.payment_term_id.id,
"fiscal_position_id": self.fiscal_position_id.id,
"invoice_partner_id": self.partner_invoice_id.id,
"line_recurrence": self.partner_invoice_id.id,
"line_recurrence": True,
}
def action_create_contract(self):
@@ -84,8 +82,13 @@ class SaleOrder(models.Model):
raise ValidationError(
_(
"You must specify a contract "
"template for '{}' product in '{}' company."
).format(order_line.product_id.name, rec.company_id.name)
"template for '%(product_name)s' product "
"in '%(company_name)s' company."
)
% {
"product_name": order_line.product_id.name,
"company_name": rec.company_id.name,
}
)
contract_templates |= contract_template
for contract_template in contract_templates:
@@ -98,7 +101,7 @@ class SaleOrder(models.Model):
contract = contract_model.create(
rec._prepare_contract_value(contract_template)
)
contracts.append(contract)
contracts.append(contract.id)
contract._onchange_contract_template_id()
contract._onchange_contract_type()
order_lines.create_contract_line(contract)
@@ -112,7 +115,7 @@ class SaleOrder(models.Model):
self.filtered(
lambda order: (order.company_id.create_contract_at_sale_order_confirmation)
).action_create_contract()
return super(SaleOrder, self).action_confirm()
return super().action_confirm()
@api.depends("order_line")
def _compute_contract_count(self):

View File

@@ -43,8 +43,8 @@ class SaleOrderLine(models.Model):
help="Specify if process date is 'from' or 'to' invoicing date",
copy=False,
)
date_start = fields.Date(string="Date Start")
date_end = fields.Date(string="Date End")
date_start = fields.Date()
date_end = fields.Date()
contract_line_id = fields.Many2one(
comodel_name="contract.line",
@@ -93,7 +93,7 @@ class SaleOrderLine(models.Model):
_("You can't upsell or downsell a terminated contract")
)
@api.depends("product_id")
@api.depends("product_id", "order_id.company_id")
def _compute_contract_template_id(self):
for rec in self:
rec.contract_template_id = rec.product_id.with_company(
@@ -141,21 +141,13 @@ class SaleOrderLine(models.Model):
rec.date_end = rec._get_date_end() if rec.date_start else False
def _get_contract_line_qty(self):
"""Returns the quantity to be put on new contract lines."""
"""Returns the amount that will be placed in new contract lines."""
self.ensure_one()
# The quantity on the generated contract line is 1, as it
# correspond to the most common use cases:
# - quantity on the SO line = number of periods sold and unit
# price the price of one period, so the
# total amount of the SO corresponds to the planned value
# of the contract; in this case the quantity on the contract
# line must be 1
# - quantity on the SO line = number of hours sold,
# automatic invoicing of the actual hours through a variable
# quantity formula, in which case the quantity on the contract
# line is not used
# The quantity in the generated contract line is the quantity of
# product requested in the order, since they correspond to the most common
# use cases.
# Other use cases are easy to implement by overriding this method.
return 1.0
return self.product_uom_qty
def _prepare_contract_line_values(
self, contract, predecessor_contract_line_id=False
@@ -198,7 +190,7 @@ class SaleOrderLine(models.Model):
"contract_id": contract.id,
"sale_order_line_id": self.id,
"predecessor_contract_line_id": predecessor_contract_line_id,
"analytic_account_id": self.order_id.analytic_account_id.id,
"analytic_distribution": self.analytic_distribution,
}
def create_contract_line(self, contract):
@@ -269,17 +261,11 @@ class SaleOrderLine(models.Model):
SaleOrderLine, self.filtered(lambda l: not l.contract_id)
).invoice_line_create(invoice_id, qty)
@api.depends(
"qty_invoiced",
"qty_delivered",
"product_uom_qty",
"order_id.state",
"product_id.is_contract",
)
def _get_to_invoice_qty(self):
@api.depends("qty_invoiced", "qty_delivered", "product_uom_qty", "state")
def _compute_qty_to_invoice(self):
"""
sale line linked to contracts must not be invoiced from sale order
"""
res = super()._get_to_invoice_qty()
res = super()._compute_qty_to_invoice()
self.filtered("product_id.is_contract").update({"qty_to_invoice": 0.0})
return res

View File

@@ -3,18 +3,18 @@
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.15.1: http://docutils.sourceforge.net/" />
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
<title>Recurring - Product Contract</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 7952 2016-07-26 18:15:59Z milde $
:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
@@ -366,26 +366,28 @@ ul.auto-toc {
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:ad8724fbb6c54e3f450ed7f3b3aa16384ed79b097065f0de96cc56e87eef2e71
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<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/14.0/product_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-14-0/contract-14-0-product_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/14.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 image-reference" 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 image-reference" 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 image-reference" href="https://github.com/OCA/contract/tree/16.0/product_contract"><img alt="OCA/contract" src="https://img.shields.io/badge/github-OCA%2Fcontract-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/contract-16-0/contract-16-0-product_contract"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/contract&amp;target_branch=16.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>This module adds support for products to be linked to contract templates.</p>
<p>A contract is created on <tt class="docutils literal">sale.order</tt> confirmation for each different template used in sale order line where recurrence details are set too.</p>
<p>Contract product are ignored on invoicing process and pass to nothing to invoice directly.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#usage" id="id1">Usage</a></li>
<li><a class="reference internal" href="#bug-tracker" id="id2">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="id3">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="id4">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="id5">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="id6">Maintainers</a></li>
<li><a class="reference internal" href="#usage" id="toc-entry-1">Usage</a></li>
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-2">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="toc-entry-3">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="toc-entry-4">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="toc-entry-5">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="toc-entry-6">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="usage">
<h1><a class="toc-backref" href="#id1">Usage</a></h1>
<h1><a class="toc-backref" href="#toc-entry-1">Usage</a></h1>
<p>To use this module, you need to:</p>
<ol class="arabic simple">
<li>Go to Sales -&gt; Products and select or create a product.</li>
@@ -395,24 +397,24 @@ product</li>
</ol>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#id2">Bug Tracker</a></h1>
<h1><a class="toc-backref" href="#toc-entry-2">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/contract/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/contract/issues/new?body=module:%20product_contract%0Aversion:%2014.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
If you spotted it first, help us to smash it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/contract/issues/new?body=module:%20product_contract%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h1><a class="toc-backref" href="#id3">Credits</a></h1>
<h1><a class="toc-backref" href="#toc-entry-3">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#id4">Authors</a></h2>
<h2><a class="toc-backref" href="#toc-entry-4">Authors</a></h2>
<ul class="simple">
<li>LasLabs</li>
<li>ACSONE SA/NV</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#id5">Contributors</a></h2>
<h2><a class="toc-backref" href="#toc-entry-5">Contributors</a></h2>
<ul class="simple">
<li>Ted Salmon &lt;<a class="reference external" href="mailto:tsalmon&#64;laslabs.com">tsalmon&#64;laslabs.com</a>&gt;</li>
<li>Souheil Bejaoui &lt;<a class="reference external" href="mailto:souheil.bejaoui&#64;acsone.eu">souheil.bejaoui&#64;acsone.eu</a>&gt;</li>
@@ -424,15 +426,15 @@ If you spotted it first, help us smashing it by providing a detailed and welcome
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#id6">Maintainers</a></h2>
<h2><a class="toc-backref" href="#toc-entry-6">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.</p>
<p>Current <a class="reference external" href="https://odoo-community.org/page/maintainer-role">maintainer</a>:</p>
<p><a class="reference external" href="https://github.com/sbejaoui"><img alt="sbejaoui" src="https://github.com/sbejaoui.png?size=40px" /></a></p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/contract/tree/14.0/product_contract">OCA/contract</a> project on GitHub.</p>
<p><a class="reference external image-reference" href="https://github.com/sbejaoui"><img alt="sbejaoui" src="https://github.com/sbejaoui.png?size=40px" /></a></p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/contract/tree/16.0/product_contract">OCA/contract</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>
</div>

View File

@@ -3,10 +3,10 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo.exceptions import ValidationError
from odoo.tests.common import SavepointCase
from odoo.tests.common import TransactionCase
class TestProductTemplate(SavepointCase):
class TestProductTemplate(TransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()

View File

@@ -6,10 +6,10 @@ from dateutil.relativedelta import relativedelta
from odoo.exceptions import UserError, ValidationError
from odoo.fields import Date
from odoo.tests.common import SavepointCase
from odoo.tests.common import TransactionCase
class TestSaleOrder(SavepointCase):
class TestSaleOrder(TransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
@@ -143,7 +143,9 @@ class TestSaleOrder(SavepointCase):
self.assertEqual(self.order_line1.invoice_status, "no")
invoice = self.order_line1.contract_id.recurring_create_invoice()
self.assertTrue(invoice)
self.assertEqual(self.order_line1.qty_invoiced, 1)
self.assertEqual(
self.order_line1.qty_invoiced, self.order_line1.product_uom_qty
)
self.assertEqual(self.order_line1.qty_to_invoice, 0)
def test_action_confirm_without_contract_creation(self):
@@ -342,7 +344,7 @@ class TestSaleOrder(SavepointCase):
self.order_line1.contract_id = self.contract
self.sale.action_confirm()
self.contract.is_terminated = True
self.sale.action_cancel()
self.sale._action_cancel()
with self.assertRaises(ValidationError):
self.sale.action_draft()
self.contract.is_terminated = False

View File

@@ -10,7 +10,7 @@
<field name="inherit_id" ref="sale.view_order_form" />
<field name="arch" type="xml">
<xpath expr="//header" position="inside">
<field name="need_contract_creation" attrs="{'invisible': []}" />
<field name="need_contract_creation" invisible="1" />
<button
name="action_create_contract"
string="Create Contracts"
@@ -31,21 +31,18 @@
</button>
</xpath>
<xpath expr="//field[@name='order_line']" position="before">
<field name="is_contract" attrs="{'invisible': []}" />
<field name="is_contract" invisible="1" />
</xpath>
<xpath
expr="//field[@name='order_line']/form//field[@name='product_id']"
position="after"
>
<field name="contract_template_id" attrs="{'invisible': []}" />
<field name="contract_template_id" invisible="1" />
<field
name="contract_id"
options='{"no_create": True}'
attrs="{'invisible': [('is_contract', '=', False)]}"
domain="['|',('contract_template_id','=',contract_template_id),
('contract_template_id','=',False),
('partner_id','=',parent.partner_id),
('is_terminated','=',False),
domain="['|',('contract_template_id','=',contract_template_id), ('contract_template_id','=',False), ('partner_id','=',parent.partner_id), ('is_terminated','=',False),
]"
/>
<field
@@ -58,7 +55,7 @@
expr="//field[@name='order_line']/form//field[@name='tax_id']/parent::group"
position="after"
>
<field name="is_contract" attrs="{'invisible': []}" />
<field name="is_contract" invisible="1" />
<separator
colspan="4"
string="Recurrence Invoicing"

2
requirements.txt Normal file
View File

@@ -0,0 +1,2 @@
# generated from manifests external_dependencies
python-dateutil