diff --git a/contract_variable_quantity/models/__init__.py b/contract_variable_quantity/models/__init__.py index a51c4d1f..dc2b5f46 100644 --- a/contract_variable_quantity/models/__init__.py +++ b/contract_variable_quantity/models/__init__.py @@ -1,3 +1,6 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from . import abstract_contract_line from . import contract +from . import contract_line +from . import contract_line_formula diff --git a/contract_variable_quantity/models/abstract_contract_line.py b/contract_variable_quantity/models/abstract_contract_line.py new file mode 100644 index 00000000..9c95dfed --- /dev/null +++ b/contract_variable_quantity/models/abstract_contract_line.py @@ -0,0 +1,18 @@ +# Copyright 2016 Tecnativa - Pedro M. Baeza +# Copyright 2018 Tecnativa - Carlos Dauden +# Copyright 2018 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class AccountAbstractAnalyticContractLine(models.AbstractModel): + _inherit = 'account.abstract.analytic.contract.line' + + qty_type = fields.Selection( + selection=[ + ('fixed', 'Fixed quantity'), + ('variable', 'Variable quantity'), + ], required=True, default='fixed', string="Qty. type") + qty_formula_id = fields.Many2one( + comodel_name="contract.line.qty.formula", string="Qty. formula") diff --git a/contract_variable_quantity/models/contract.py b/contract_variable_quantity/models/contract.py index 26edf6d9..59b56ce8 100644 --- a/contract_variable_quantity/models/contract.py +++ b/contract_variable_quantity/models/contract.py @@ -1,10 +1,9 @@ # Copyright 2016 Tecnativa - Pedro M. Baeza # Copyright 2018 Tecnativa - Carlos Dauden +# Copyright 2018 ACSONE SA/NV # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import _, api, fields, models, exceptions -from odoo.tools import float_is_zero -from odoo.tools.safe_eval import safe_eval +from odoo import fields, models class AccountAnalyticAccount(models.Model): @@ -14,71 +13,3 @@ class AccountAnalyticAccount(models.Model): string='Skip Zero Qty Lines', help="If checked, contract lines with 0 qty don't create invoice line", ) - - @api.model - def _prepare_invoice_line(self, line, invoice_id): - vals = super(AccountAnalyticAccount, self)._prepare_invoice_line( - line, invoice_id) - if line.qty_type == 'variable': - eval_context = { - 'env': self.env, - 'context': self.env.context, - 'user': self.env.user, - 'line': line, - 'contract': line.analytic_account_id, - 'invoice': self.env['account.invoice'].browse(invoice_id), - } - safe_eval(line.qty_formula_id.code.strip(), eval_context, - mode="exec", nocopy=True) # nocopy for returning result - qty = eval_context.get('result', 0) - if self.skip_zero_qty and float_is_zero( - qty, self.env['decimal.precision'].precision_get( - 'Product Unit of Measure')): - # Return empty dict to skip line create - vals = {} - else: - vals['quantity'] = qty - # Re-evaluate price with this new quantity - vals['price_unit'] = line.with_context( - contract_line_qty=qty, - ).price_unit - return vals - - -class AccountAnalyticContractLine(models.Model): - _inherit = 'account.analytic.contract.line' - - qty_type = fields.Selection( - selection=[ - ('fixed', 'Fixed quantity'), - ('variable', 'Variable quantity'), - ], required=True, default='fixed', string="Qty. type") - qty_formula_id = fields.Many2one( - comodel_name="contract.line.qty.formula", string="Qty. formula") - - -class ContractLineFormula(models.Model): - _name = 'contract.line.qty.formula' - _description = 'Contract Line Formula' - - name = fields.Char(required=True, translate=True) - code = fields.Text(required=True, default="result = 0") - - @api.constrains('code') - def _check_code(self): - eval_context = { - 'env': self.env, - 'context': self.env.context, - 'user': self.env.user, - 'line': self.env['account.analytic.invoice.line'], - 'contract': self.env['account.analytic.account'], - 'invoice': self.env['account.invoice'], - } - try: - safe_eval( - self.code.strip(), eval_context, mode="exec", nocopy=True) - except Exception as e: - raise exceptions.ValidationError( - _('Error evaluating code.\nDetails: %s') % e) - if 'result' not in eval_context: - raise exceptions.ValidationError(_('No valid result returned.')) diff --git a/contract_variable_quantity/models/contract_line.py b/contract_variable_quantity/models/contract_line.py new file mode 100644 index 00000000..c5397e7c --- /dev/null +++ b/contract_variable_quantity/models/contract_line.py @@ -0,0 +1,41 @@ +# Copyright 2016 Tecnativa - Pedro M. Baeza +# Copyright 2018 Tecnativa - Carlos Dauden +# Copyright 2018 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, models +from odoo.tools import float_is_zero +from odoo.tools.safe_eval import safe_eval + + +class AccountAnalyticInvoiceLine(models.Model): + _inherit = 'account.analytic.invoice.line' + + @api.multi + def _prepare_invoice_line(self, invoice_id): + vals = super(AccountAnalyticInvoiceLine, self)._prepare_invoice_line( + invoice_id) + if self.qty_type == 'variable': + eval_context = { + 'env': self.env, + 'context': self.env.context, + 'user': self.env.user, + 'line': self, + 'contract': self.contract_id, + 'invoice': self.env['account.invoice'].browse(invoice_id), + } + safe_eval(self.qty_formula_id.code.strip(), eval_context, + mode="exec", nocopy=True) # nocopy for returning result + qty = eval_context.get('result', 0) + if self.contract_id.skip_zero_qty and float_is_zero( + qty, self.env['decimal.precision'].precision_get( + 'Product Unit of Measure')): + # Return empty dict to skip line create + vals = {} + else: + vals['quantity'] = qty + # Re-evaluate price with this new quantity + vals['price_unit'] = self.with_context( + contract_line_qty=qty, + ).price_unit + return vals diff --git a/contract_variable_quantity/models/contract_line_formula.py b/contract_variable_quantity/models/contract_line_formula.py new file mode 100644 index 00000000..12179f79 --- /dev/null +++ b/contract_variable_quantity/models/contract_line_formula.py @@ -0,0 +1,34 @@ +# Copyright 2016 Tecnativa - Pedro M. Baeza +# Copyright 2018 Tecnativa - Carlos Dauden +# Copyright 2018 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import _, api, fields, models, exceptions +from odoo.tools.safe_eval import safe_eval + + +class ContractLineFormula(models.Model): + _name = 'contract.line.qty.formula' + _description = 'Contract Line Formula' + + name = fields.Char(required=True, translate=True) + code = fields.Text(required=True, default="result = 0") + + @api.constrains('code') + def _check_code(self): + eval_context = { + 'env': self.env, + 'context': self.env.context, + 'user': self.env.user, + 'line': self.env['account.analytic.invoice.line'], + 'contract': self.env['account.analytic.account'], + 'invoice': self.env['account.invoice'], + } + try: + safe_eval( + self.code.strip(), eval_context, mode="exec", nocopy=True) + except Exception as e: + raise exceptions.ValidationError( + _('Error evaluating code.\nDetails: %s') % e) + if 'result' not in eval_context: + raise exceptions.ValidationError(_('No valid result returned.')) diff --git a/contract_variable_quantity/tests/test_contract_variable_quantity.py b/contract_variable_quantity/tests/test_contract_variable_quantity.py index c9702fb7..a0040fce 100644 --- a/contract_variable_quantity/tests/test_contract_variable_quantity.py +++ b/contract_variable_quantity/tests/test_contract_variable_quantity.py @@ -1,5 +1,6 @@ # Copyright 2016 Tecnativa - Pedro M. Baeza # Copyright 2018 Tecnativa - Carlos Dauden +# Copyright 2018 ACSONE SA/NV # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). import odoo.tests @@ -35,7 +36,7 @@ class TestContractVariableQuantity(odoo.tests.HttpCase): 'result = 12', }) self.contract_line = self.env['account.analytic.invoice.line'].create({ - 'analytic_account_id': self.contract.id, + 'contract_id': self.contract.id, 'product_id': self.product.id, 'name': 'Test', 'qty_type': 'variable', @@ -44,6 +45,10 @@ class TestContractVariableQuantity(odoo.tests.HttpCase): 'uom_id': self.product.uom_id.id, 'price_unit': 100, 'discount': 50, + 'recurring_rule_type': 'monthly', + 'recurring_interval': 1, + 'date_start': '2016-02-15', + 'recurring_next_date': '2016-02-29', }) def test_check_invalid_code(self): @@ -55,7 +60,7 @@ class TestContractVariableQuantity(odoo.tests.HttpCase): self.formula.code = "user.id" def test_check_variable_quantity(self): - self.contract._create_invoice() + self.contract.recurring_create_invoice() invoice = self.env['account.invoice'].search( [('contract_id', '=', self.contract.id)]) self.assertEqual(invoice.invoice_line_ids[0].quantity, 12) @@ -63,8 +68,8 @@ class TestContractVariableQuantity(odoo.tests.HttpCase): def test_check_skip_zero_qty(self): self.formula.code = 'result=0' self.contract.skip_zero_qty = True - invoice = self.contract._create_invoice() + invoice = self.contract.recurring_create_invoice() self.assertFalse(invoice.invoice_line_ids) self.contract.skip_zero_qty = False - invoice = self.contract._create_invoice() + invoice = self.contract.recurring_create_invoice() self.assertAlmostEqual(invoice.invoice_line_ids[0].quantity, 0.0)