Carlos Dauden
5 years ago
18 changed files with 234 additions and 373 deletions
-
34contract_price_revision/README.rst
-
13contract_price_revision/__manifest__.py
-
88contract_price_revision/i18n/contract_price_revision.pot
-
111contract_price_revision/i18n/es.po
-
3contract_price_revision/models/__init__.py
-
19contract_price_revision/models/account_analytic_account.py
-
39contract_price_revision/models/account_analytic_invoice_line.py
-
31contract_price_revision/models/contract_line.py
-
1contract_price_revision/readme/CONTRIBUTORS.rst
-
2contract_price_revision/readme/USAGE.rst
-
11contract_price_revision/static/description/index.html
-
74contract_price_revision/tests/test_contract_price_revision.py
-
43contract_price_revision/views/account_analytic_account_views.xml
-
26contract_price_revision/views/contract_line.xml
-
2contract_price_revision/wizards/__init__.py
-
54contract_price_revision/wizards/contract_price_revision.py
-
6contract_price_revision/wizards/contract_price_revision_views.xml
-
50contract_price_revision/wizards/create_revision_line.py
@ -1,2 +1 @@ |
|||
from . import account_analytic_account |
|||
from . import account_analytic_invoice_line |
|||
from . import contract_line |
@ -1,19 +0,0 @@ |
|||
# Copyright 2019 Tecnativa <vicent.cubells@tecnativa.com> |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). |
|||
from odoo import api, models |
|||
|
|||
|
|||
class AccountAnalyticAccount(models.Model): |
|||
_inherit = "account.analytic.account" |
|||
|
|||
@api.model |
|||
def _prepare_invoice_line(self, line, invoice_id): |
|||
line_obj = self.env['account.invoice.line'] |
|||
invoice = self.env['account.invoice'].browse( |
|||
invoice_id, prefetch=self._prefetch, |
|||
) |
|||
# Line with automatic price are not taken into account |
|||
if (line.date_start and invoice.date_invoice < line.date_start) or \ |
|||
(line.date_end and invoice.date_invoice > line.date_end): |
|||
return line_obj |
|||
return super()._prepare_invoice_line(line, invoice_id) |
@ -1,39 +0,0 @@ |
|||
# Copyright 2019 Tecnativa <vicent.cubells@tecnativa.com> |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). |
|||
from odoo import api, fields, models |
|||
import odoo.addons.decimal_precision as dp |
|||
|
|||
|
|||
class AccountAnalyticINvoiceLine(models.Model): |
|||
_inherit = "account.analytic.invoice.line" |
|||
|
|||
date_start = fields.Date( |
|||
string='Start Date', |
|||
) |
|||
date_end = fields.Date( |
|||
string='End Date', |
|||
) |
|||
previous_revision_id = fields.Many2one( |
|||
comodel_name='account.analytic.invoice.line', |
|||
string='Previous revision', |
|||
help='Relation with previous revision', |
|||
) |
|||
previous_price = fields.Float( |
|||
related='previous_revision_id.price_unit', |
|||
readonly=True, |
|||
) |
|||
variation_percent = fields.Float( |
|||
compute='_compute_variation_percent', |
|||
store=True, |
|||
digits=dp.get_precision('Product Price'), |
|||
string='Variation %', |
|||
) |
|||
|
|||
@api.multi |
|||
@api.depends('price_unit', 'previous_revision_id.price_unit') |
|||
def _compute_variation_percent(self): |
|||
for line in self: |
|||
if not (line.price_unit and line.previous_price): |
|||
continue |
|||
line.variation_percent = ( |
|||
(line.price_unit / line.previous_price - 1) * 100) |
@ -0,0 +1,31 @@ |
|||
# Copyright 2019 Tecnativa - Vicent Cubells |
|||
# Copyright 2019 Tecnativa - Carlos Dauden |
|||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). |
|||
|
|||
from odoo import api, fields, models |
|||
import odoo.addons.decimal_precision as dp |
|||
|
|||
|
|||
class ContractLine(models.Model): |
|||
_inherit = 'contract.line' |
|||
|
|||
previous_price = fields.Float( |
|||
string='Previous price', |
|||
related='predecessor_contract_line_id.price_unit', |
|||
readonly=True, |
|||
) |
|||
variation_percent = fields.Float( |
|||
compute='_compute_variation_percent', |
|||
store=True, |
|||
digits=dp.get_precision('Product Price'), |
|||
string='Variation %', |
|||
) |
|||
|
|||
@api.depends('price_unit', 'predecessor_contract_line_id.price_unit') |
|||
def _compute_variation_percent(self): |
|||
for line in self: |
|||
if line.price_unit and line.previous_price: |
|||
line.variation_percent = ( |
|||
(line.price_unit / line.previous_price - 1) * 100) |
|||
else: |
|||
line.variation_percent = 0.0 |
@ -1,3 +1,4 @@ |
|||
* `Tecnativa <https://www.tecnativa.com>`_: |
|||
|
|||
* Vicent Cubells |
|||
* Carlos Dauden |
@ -1,71 +1,43 @@ |
|||
# Copyright 2019 Tecnativa - Vicent Cubells <vicent.cubells@tecnativa.com> |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
# Copyright 2019 Tecnativa - Vicent Cubells |
|||
# Copyright 2019 Tecnativa - Carlos Dauden |
|||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). |
|||
|
|||
from dateutil.relativedelta import relativedelta |
|||
from odoo.addons.contract.tests.test_contract import TestContractBase |
|||
|
|||
from odoo.tests import common |
|||
from odoo import fields |
|||
|
|||
|
|||
class TestContractPriceRevision(common.SavepointCase): |
|||
@classmethod |
|||
def setUpClass(cls): |
|||
super(TestContractPriceRevision, cls).setUpClass() |
|||
partner = cls.env['res.partner'].create({ |
|||
'name': 'Partner test', |
|||
}) |
|||
product = cls.env['product.product'].create({ |
|||
'name': 'Test Product', |
|||
}) |
|||
cls.contract = cls.env['account.analytic.account'].create({ |
|||
'name': 'Contract test', |
|||
'partner_id': partner.id, |
|||
'date_start': fields.Date.today(), |
|||
'recurring_next_date': fields.Date.to_string( |
|||
fields.date.today() + relativedelta(days=7)), |
|||
'recurring_rule_type': 'monthly', |
|||
'recurring_invoice_line_ids': [(0, 0, { |
|||
'product_id': product.id, |
|||
'quantity': 1.0, |
|||
'uom_id': product.uom_id.id, |
|||
'name': product.name, |
|||
'price_unit': 33.0, |
|||
'automatic_price': True, |
|||
}), (0, 0, { |
|||
'product_id': product.id, |
|||
'quantity': 1.0, |
|||
'uom_id': product.uom_id.id, |
|||
'name': product.name, |
|||
'price_unit': 25.0, |
|||
'automatic_price': False, |
|||
})] |
|||
}) |
|||
class TestContractPriceRevision(TestContractBase): |
|||
|
|||
def execute_wizard(self): |
|||
wizard = self.env['create.revision.line.wizard'].create({ |
|||
'date_start': fields.Date.today(), |
|||
'date_end': fields.Date.to_string( |
|||
fields.date.today() + relativedelta(years=1)), |
|||
wizard = self.env['contract.price.revision.wizard'].create({ |
|||
'date_start': '2018-02-15', |
|||
'variation_percent': 100.0, |
|||
}) |
|||
wizard.with_context( |
|||
{'active_ids': [self.contract.id]}).action_apply() |
|||
|
|||
def test_contract_price_revision_wizard(self): |
|||
self.assertEqual(len(self.contract.recurring_invoice_line_ids.ids), 2) |
|||
self.acct_line.copy({'automatic_price': True}) |
|||
self.assertEqual(len(self.contract.contract_line_ids.ids), 2) |
|||
self.execute_wizard() |
|||
self.assertEqual(len(self.contract.recurring_invoice_line_ids.ids), 3) |
|||
lines = self.contract.mapped('recurring_invoice_line_ids').filtered( |
|||
lambda x: x.price_unit == 50.0) |
|||
self.assertEqual(len(self.contract.contract_line_ids.ids), 3) |
|||
lines = self.contract.contract_line_ids.filtered( |
|||
lambda x: x.price_unit == 200.0) |
|||
self.assertEqual(len(lines), 1) |
|||
|
|||
def test_contract_price_revision_invoicing(self): |
|||
self.acct_line.copy({'automatic_price': True}) |
|||
self.execute_wizard() |
|||
self.contract.recurring_create_invoice() |
|||
invoice = self.contract.recurring_create_invoice() |
|||
invoices = self.env['account.invoice'].search([ |
|||
('contract_id', '=', self.contract.id)]) |
|||
('invoice_line_ids.contract_line_id', 'in', |
|||
self.contract.contract_line_ids.ids)]) |
|||
self.assertEqual(len(invoices), 1) |
|||
lines = invoices.mapped('invoice_line_ids') |
|||
lines = invoice.invoice_line_ids |
|||
self.assertEqual(len(lines), 2) |
|||
lines = lines.filtered(lambda x: x.price_unit == 100.0) |
|||
self.assertEqual(len(lines), 1) |
|||
invoice = self.contract.recurring_create_invoice() |
|||
lines = invoice.invoice_line_ids |
|||
self.assertEqual(len(lines), 2) |
|||
lines = lines.filtered(lambda x: x.price_unit == 50.0) |
|||
lines = lines.filtered(lambda x: x.price_unit == 200.0) |
|||
self.assertEqual(len(lines), 1) |
@ -1,43 +0,0 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<odoo> |
|||
|
|||
<record id="account_analytic_invoice_line_form_view" model="ir.ui.view"> |
|||
<field name="name">account.analytic.invoice.line.view</field> |
|||
<field name="model">account.analytic.invoice.line</field> |
|||
<field name="arch" type="xml"> |
|||
<form string="Line Information"> |
|||
<group> |
|||
<group> |
|||
<field name="analytic_account_id"/> |
|||
<field name="automatic_price"/> |
|||
</group> |
|||
<group> |
|||
<field name="product_id"/> |
|||
<field name="name"/> |
|||
<field name="quantity"/> |
|||
<field name="uom_id"/> |
|||
<field name="price_unit"/> |
|||
<field name="variation_percent"/> |
|||
<label for="date_start" string="Validity"/> |
|||
<div><field name="date_start" class="oe_inline"/> to <field name="date_end" class="oe_inline"/></div> |
|||
<field name="discount"/> |
|||
<field name="price_subtotal"/> |
|||
</group> |
|||
</group> |
|||
</form> |
|||
</field> |
|||
</record> |
|||
<record id="account_analytic_account_recurring_form_form" model="ir.ui.view"> |
|||
<field name="name">Contract form price revision</field> |
|||
<field name="model">account.analytic.account</field> |
|||
<field name="inherit_id" ref="contract.account_analytic_account_recurring_form_form"/> |
|||
<field name="arch" type="xml"> |
|||
<field name="price_unit" position="after"> |
|||
<field name="variation_percent" attrs="{'readonly': [('automatic_price', '=', True)]}"/> |
|||
<field name="date_start" attrs="{'readonly': [('automatic_price', '=', True)]}"/> |
|||
<field name="date_end" attrs="{'readonly': [('automatic_price', '=', True)]}"/> |
|||
</field> |
|||
</field> |
|||
</record> |
|||
|
|||
</odoo> |
@ -0,0 +1,26 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<odoo> |
|||
|
|||
<record id="contract_line_tree_view" model="ir.ui.view"> |
|||
<field name="model">contract.line</field> |
|||
<field name="inherit_id" ref="contract.contract_line_tree_view"/> |
|||
<field name="arch" type="xml"> |
|||
<field name="last_date_invoiced" position="after"> |
|||
<field name="variation_percent" groups="base.group_no_one"/> |
|||
</field> |
|||
</field> |
|||
</record> |
|||
|
|||
<record id="contract_line_form_view" model="ir.ui.view"> |
|||
<field name="model">contract.line</field> |
|||
<field name="inherit_id" ref="contract.contract_line_form_view"/> |
|||
<field name="arch" type="xml"> |
|||
<field name="discount" position="after"> |
|||
<field name="variation_percent" |
|||
groups="base.group_no_one" |
|||
attrs="{'invisible': [('predecessor_contract_line_id', '=', False)]}"/> |
|||
</field> |
|||
</field> |
|||
</record> |
|||
|
|||
</odoo> |
@ -1 +1 @@ |
|||
from . import create_revision_line |
|||
from . import contract_price_revision |
@ -0,0 +1,54 @@ |
|||
# Copyright 2019 Tecnativa - Vicent Cubells |
|||
# Copyright 2019 Tecnativa - Carlos Dauden |
|||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). |
|||
|
|||
from dateutil.relativedelta import relativedelta |
|||
|
|||
from odoo import fields, models |
|||
import odoo.addons.decimal_precision as dp |
|||
|
|||
|
|||
class ContractPriceRevisionWizard(models.TransientModel): |
|||
""" Update contract price based on percentage variation """ |
|||
_name = 'contract.price.revision.wizard' |
|||
_description = "Wizard to update price based on percentage variation" |
|||
|
|||
date_start = fields.Date( |
|||
required=True, |
|||
) |
|||
date_end = fields.Date() |
|||
variation_percent = fields.Float( |
|||
digits=dp.get_precision('Product Price'), |
|||
required=True, |
|||
string='Variation %', |
|||
) |
|||
|
|||
def action_apply(self): |
|||
ContractLine = self.env['contract.line'] |
|||
active_ids = self.env.context['active_ids'] |
|||
for line in self.env['contract.contract'].browse(active_ids).mapped( |
|||
'contract_line_ids'): |
|||
if (line.automatic_price or line.successor_contract_line_id or |
|||
not line.recurring_next_date): |
|||
continue |
|||
line.update({ |
|||
'date_end': self.date_start - relativedelta(days=1), |
|||
}) |
|||
new_vals = line.copy_data({ |
|||
'date_start': self.date_start, |
|||
'date_end': self.date_end, |
|||
'predecessor_contract_line_id': line.id, |
|||
'price_unit': line.price_unit * ( |
|||
1.0 + self.variation_percent / 100.0), |
|||
})[0] |
|||
tmp_line = ContractLine.new(new_vals) |
|||
tmp_line._onchange_date_start() |
|||
new_line = ContractLine.create( |
|||
tmp_line._convert_to_write(tmp_line._cache)) |
|||
line.update({ |
|||
'successor_contract_line_id': new_line.id, |
|||
}) |
|||
action = self.env['ir.actions.act_window'].for_xml_id( |
|||
'contract', 'action_customer_contract') |
|||
action['domain'] = [('id', 'in', active_ids)] |
|||
return action |
@ -1,50 +0,0 @@ |
|||
# Copyright 2019 Tecnativa <vicent.cubells@tecnativa.com> |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). |
|||
|
|||
from dateutil.relativedelta import relativedelta |
|||
|
|||
from odoo import api, fields, models |
|||
import odoo.addons.decimal_precision as dp |
|||
|
|||
|
|||
class CreateRevisionLineWizard(models.TransientModel): |
|||
_name = 'create.revision.line.wizard' |
|||
|
|||
date_start = fields.Date( |
|||
required=True, |
|||
) |
|||
date_end = fields.Date() |
|||
variation_percent = fields.Float( |
|||
digits=dp.get_precision('Product Price'), |
|||
required=True, |
|||
string='Variation %', |
|||
) |
|||
|
|||
@api.multi |
|||
def action_apply(self): |
|||
contract_obj = self.env['account.analytic.account'] |
|||
line_obj = self.env['account.analytic.invoice.line'] |
|||
active_ids = self.env.context['active_ids'] |
|||
line_news = line_obj |
|||
for item in contract_obj.browse(active_ids).mapped( |
|||
'recurring_invoice_line_ids').filtered( |
|||
lambda x: not x.automatic_price): |
|||
line_news |= item.copy({ |
|||
'date_start': self.date_start, |
|||
'date_end': self.date_end, |
|||
'previous_revision_id': item.id, |
|||
'price_unit': item.price_unit * ( |
|||
1.0 + self.variation_percent / 100.0), |
|||
}) |
|||
item.date_end = (fields.Date.from_string(self.date_start) - |
|||
relativedelta(days=1)) |
|||
action = self.env.ref( |
|||
'contract.action_account_analytic_sale_overdue_all').read()[0] |
|||
if len(active_ids) > 1: # pragma: no cover |
|||
action['domain'] = [('id', 'in', active_ids)] |
|||
elif active_ids: |
|||
action['views'] = [( |
|||
self.env.ref('contract.account_analytic_account_sale_form').id, |
|||
'form')] |
|||
action['res_id'] = active_ids[0] |
|||
return action |
Write
Preview
Loading…
Cancel
Save
Reference in new issue