Browse Source
[ADD] product_contract: Create module
[ADD] product_contract: Create module
* Add contract functionality to `product.templates` * Add logic to create contracts from `sale.order` that contains contract products.pull/49/head
Ted Salmon
8 years ago
committed by
Dave Lasley
11 changed files with 265 additions and 0 deletions
-
66product_contract/README.rst
-
5product_contract/__init__.py
-
23product_contract/__manifest__.py
-
7product_contract/models/__init__.py
-
23product_contract/models/product_template.py
-
27product_contract/models/sale_order.py
-
14product_contract/models/sale_order_line.py
-
6product_contract/tests/__init__.py
-
31product_contract/tests/test_product_template.py
-
34product_contract/tests/test_sale_order.py
-
29product_contract/views/product_template_view.xml
@ -0,0 +1,66 @@ |
|||
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg |
|||
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html |
|||
:alt: License: AGPL-3 |
|||
|
|||
================ |
|||
Product Contract |
|||
================ |
|||
|
|||
This module adds support for products to be linked to contract templates. |
|||
It also adds functionality to automatically create a contract, from the template, |
|||
when a ``sale.order`` contains a product that implements a contract. |
|||
|
|||
Usage |
|||
===== |
|||
|
|||
To use this module, you need to: |
|||
|
|||
#. Go to Sales -> Products and select or create a product. |
|||
#. Check "Is a contract" and select the contract template related to the |
|||
product |
|||
|
|||
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas |
|||
:alt: Try me on Runbot |
|||
:target: https://runbot.odoo-community.org/runbot/110/10.0 |
|||
|
|||
Known issues / Roadmap |
|||
====================== |
|||
|
|||
* None |
|||
|
|||
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 smash it by providing detailed and welcomed feedback. |
|||
|
|||
Credits |
|||
======= |
|||
|
|||
Images |
|||
------ |
|||
|
|||
* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_. |
|||
|
|||
Contributors |
|||
------------ |
|||
|
|||
* Ted Salmon <tsalmon@laslabs.com> |
|||
|
|||
|
|||
Maintainer |
|||
---------- |
|||
|
|||
.. image:: https://odoo-community.org/logo.png |
|||
:alt: Odoo Community Association |
|||
:target: https://odoo-community.org |
|||
|
|||
This module is maintained by the OCA. |
|||
|
|||
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. |
|||
|
|||
To contribute to this module, please visit https://odoo-community.org. |
@ -0,0 +1,5 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2017 LasLabs Inc. |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
from . import models |
@ -0,0 +1,23 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2017 LasLabs Inc. |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
{ |
|||
'name': 'Product Contract', |
|||
'version': '10.0.1.0.0', |
|||
'category': 'Contract Management', |
|||
'license': 'AGPL-3', |
|||
'author': "LasLabs, " |
|||
"Odoo Community Association (OCA)", |
|||
'website': 'https://laslabs.com', |
|||
'depends': [ |
|||
'contract', |
|||
'product', |
|||
'sale', |
|||
], |
|||
'data': [ |
|||
'views/product_template_view.xml', |
|||
], |
|||
'installable': True, |
|||
'application': False, |
|||
} |
@ -0,0 +1,7 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2017 LasLabs Inc. |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
from . import product_template |
|||
from . import sale_order |
|||
from . import sale_order_line |
@ -0,0 +1,23 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2017 LasLabs Inc. |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
from odoo import api, fields, models |
|||
|
|||
|
|||
class ProductTemplate(models.Model): |
|||
_inherit = 'product.template' |
|||
|
|||
is_contract = fields.Boolean('Is a contract') |
|||
contract_template_id = fields.Many2one( |
|||
comodel_name='account.analytic.contract', |
|||
string='Contract Template', |
|||
) |
|||
|
|||
@api.onchange('is_contract') |
|||
def _change_is_contract(self): |
|||
""" Clear the relation to contract_template_id when downgrading |
|||
product from contract |
|||
""" |
|||
if not self.is_contract: |
|||
self.contract_template_id = False |
@ -0,0 +1,27 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2017 LasLabs Inc. |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
from odoo import api, models |
|||
|
|||
|
|||
class SaleOrder(models.Model): |
|||
_inherit = 'sale.order' |
|||
|
|||
@api.multi |
|||
def action_confirm(self): |
|||
""" If we have a contract in the order, set it up """ |
|||
for rec in self: |
|||
order_lines = self.mapped('order_line').filtered( |
|||
lambda r: r.product_id.is_contract |
|||
) |
|||
for line in order_lines: |
|||
contract_tmpl = line.product_id.contract_template_id |
|||
contract = self.env['account.analytic.account'].create({ |
|||
'name': '%s Contract' % rec.name, |
|||
'partner_id': rec.partner_id.id, |
|||
'contract_template_id': contract_tmpl.id, |
|||
}) |
|||
line.contract_id = contract.id |
|||
contract.recurring_create_invoice() |
|||
return super(SaleOrder, self).action_confirm() |
@ -0,0 +1,14 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2017 LasLabs Inc. |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
from odoo import fields, models |
|||
|
|||
|
|||
class SaleOrderLine(models.Model): |
|||
_inherit = 'sale.order.line' |
|||
|
|||
contract_id = fields.Many2one( |
|||
comodel_name='account.analytic.account', |
|||
string='Contract' |
|||
) |
@ -0,0 +1,6 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2017 LasLabs Inc. |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
from . import test_product_template |
|||
from . import test_sale_order |
@ -0,0 +1,31 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2017 LasLabs Inc. |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
from odoo.tests.common import TransactionCase |
|||
|
|||
|
|||
class TestProductTemplate(TransactionCase): |
|||
|
|||
def setUp(self): |
|||
super(TestProductTemplate, self).setUp() |
|||
self.product = self.env.ref( |
|||
'product.product_product_4_product_template' |
|||
) |
|||
self.contract = self.env['account.analytic.contract'].create({ |
|||
'name': 'Test', |
|||
'recurring_rule_type': 'yearly', |
|||
'recurring_interval': 12345, |
|||
}) |
|||
|
|||
def test_change_is_contract(self): |
|||
""" It should verify that the contract_template_id is removed |
|||
when is_contract is False """ |
|||
self.product.is_contract = True |
|||
self.product.contract_template_id = self.contract.id |
|||
self.product.is_contract = False |
|||
self.product._change_is_contract() |
|||
self.assertEquals( |
|||
len(self.product.contract_template_id), |
|||
0 |
|||
) |
@ -0,0 +1,34 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2017 LasLabs Inc. |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
from mock import MagicMock |
|||
from odoo.tests.common import TransactionCase |
|||
|
|||
|
|||
class TestSaleOrder(TransactionCase): |
|||
|
|||
def setUp(self): |
|||
super(TestSaleOrder, self).setUp() |
|||
self.product = self.env.ref('product.product_product_1') |
|||
self.sale = self.env.ref('sale.sale_order_2') |
|||
self.contract = self.env['account.analytic.contract'].create({ |
|||
'name': 'Test', |
|||
'recurring_rule_type': 'yearly', |
|||
'recurring_interval': 12345, |
|||
}) |
|||
self.product.product_tmpl_id.is_contract = True |
|||
self.product.product_tmpl_id.contract_template_id = self.contract.id |
|||
|
|||
def test_action_done(self): |
|||
""" It should create a contract when the sale for a contract is set |
|||
to done for the first time """ |
|||
self.env['account.analytic.account']._patch_method( |
|||
'create', MagicMock() |
|||
) |
|||
self.sale.action_confirm() |
|||
self.env['account.analytic.account'].create.assert_called_once_with({ |
|||
'name': '%s Contract' % self.sale.name, |
|||
'partner_id': self.sale.partner_id.id, |
|||
'contract_template_id': self.contract.id, |
|||
}) |
@ -0,0 +1,29 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
|
|||
<!-- |
|||
Copyright 2017 LasLabs Inc. |
|||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
--> |
|||
|
|||
<odoo> |
|||
|
|||
<record id="product_template_form_contract_view" model="ir.ui.view"> |
|||
<field name="name">account.invoice.select.contract</field> |
|||
<field name="model">product.template</field> |
|||
<field name="inherit_id" ref="product.product_template_form_view"/> |
|||
<field name="arch" type="xml"> |
|||
<xpath expr="//div[@name='options']" position="inside"> |
|||
<div> |
|||
<field name="is_contract" /> |
|||
<label for="is_contract" /> |
|||
</div> |
|||
</xpath> |
|||
<xpath expr="//group[@name='group_standard_price']" position="inside"> |
|||
<field name="contract_template_id" |
|||
attrs="{'invisible': [('is_contract', '=', False)], |
|||
'required':[('is_contract', '=', True)]}" /> |
|||
</xpath> |
|||
</field> |
|||
</record> |
|||
|
|||
</odoo> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue