From 9a9e35ac91e56c8a743fa8376d6a46a13b8556f4 Mon Sep 17 00:00:00 2001 From: "Pedro M. Baeza" Date: Sat, 23 Dec 2017 02:04:26 +0100 Subject: [PATCH] [IMP] contract: Automatic prices for lines --- contract/README.rst | 2 + contract/__manifest__.py | 2 +- contract/i18n/es.po | 72 +++++++++++-------- .../migrations/11.0.2.0.0/pre-migration.py | 13 ++++ contract/models/account_analytic_contract.py | 15 ++-- .../models/account_analytic_invoice_line.py | 58 ++++++++++++--- contract/tests/test_contract.py | 14 ++++ .../views/account_analytic_account_view.xml | 4 +- .../views/account_analytic_contract_view.xml | 6 +- 9 files changed, 142 insertions(+), 44 deletions(-) create mode 100644 contract/migrations/11.0.2.0.0/pre-migration.py diff --git a/contract/README.rst b/contract/README.rst index e2aa831e..267baaef 100644 --- a/contract/README.rst +++ b/contract/README.rst @@ -33,6 +33,8 @@ To use this module, you need to: * Invoicing type: pre-paid or post-paid. #. Add the lines to be invoiced with the product, description, quantity and price. +#. You can mark Auto-price? for having a price automatically obtained applying + the pricelist to the product price. #. You have the possibility to use the markers #START# or #END# in the description field to show the start and end date of the invoiced period. #. Choosing between pre-paid and post-paid, you modify the dates that are shown diff --git a/contract/__manifest__.py b/contract/__manifest__.py index eaa39d61..1512a6f9 100644 --- a/contract/__manifest__.py +++ b/contract/__manifest__.py @@ -8,7 +8,7 @@ { 'name': 'Contracts Management - Recurring', - 'version': '11.0.1.4.3', + 'version': '11.0.2.0.0', 'category': 'Contract Management', 'license': 'AGPL-3', 'author': "OpenERP SA, " diff --git a/contract/i18n/es.po b/contract/i18n/es.po index 8d9904a7..ab9d78dc 100644 --- a/contract/i18n/es.po +++ b/contract/i18n/es.po @@ -5,6 +5,8 @@ # Translators: # OCA Transbot , 2017 # Pedro M. Baeza , 2017 +# * contract +# msgid "" msgstr "" "Project-Id-Version: Odoo Server 11.0\n" @@ -16,13 +18,11 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: \n" -"Language: es\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Plural-Forms: \n" #. module: contract #: model:mail.template,body_html:contract.email_contract_template -msgid "" -"\n" +msgid "\n" "
\n" "

Hello ${object.partner_id.name or ''},

\n" "

A new contract has been created:

\n" @@ -70,8 +70,7 @@ msgid "" "
\n" "\n" " " -msgstr "" -"\n" +msgstr "\n" "
\n" "

Hola ${object.partner_id.name or ''},

\n" "

Se ha creado un nuevo contrato:

\n" @@ -197,6 +196,12 @@ msgstr "Ver líneas contables analíticas" msgid "Analytic Account" msgstr "Cuenta analítica" +#. module: contract +#: model:ir.model.fields,field_description:contract.field_account_analytic_contract_line_automatic_price +#: model:ir.model.fields,field_description:contract.field_account_analytic_invoice_line_automatic_price +msgid "Auto-price?" +msgstr "¿Precio automático?" + #. module: contract #: model:ir.actions.act_window,help:contract.account_analytic_contract_action msgid "Click to create a new contract template." @@ -235,8 +240,7 @@ msgstr "Contrato" #: code:addons/contract/models/account_analytic_account.py:138 #, python-format msgid "Contract '%s' start date can't be later than end date" -msgstr "" -"La fecha de inicio del contrato '%s' no puede ser superior a la fecha de fin" +msgstr "La fecha de inicio del contrato '%s' no puede ser superior a la fecha de fin" #. module: contract #: model:ir.model,name:contract.model_account_analytic_contract_line @@ -272,7 +276,7 @@ msgstr "Contratos" #: model:ir.model.fields,field_description:contract.field_account_analytic_account_create_invoice_visibility #: model:ir.model.fields,field_description:contract.field_project_project_create_invoice_visibility msgid "Create Invoice Visibility" -msgstr "" +msgstr "Visibilidad de crear factura" #. module: contract #: model:ir.ui.view,arch_db:contract.account_analytic_account_recurring_form_form @@ -362,12 +366,8 @@ msgstr "El descuento debería ser menor o igual a 100" #. module: contract #: model:ir.model.fields,help:contract.field_account_analytic_contract_line_discount #: model:ir.model.fields,help:contract.field_account_analytic_invoice_line_discount -msgid "" -"Discount that is applied in generated invoices. It should be less or equal " -"to 100" -msgstr "" -"Descuento que es aplicado en las facturas generadas. Debería ser menor o " -"igual a 100" +msgid "Discount that is applied in generated invoices. It should be less or equal to 100" +msgstr "Descuento que es aplicado en las facturas generadas. Debería ser menor o igual a 100" #. module: contract #: model:ir.model.fields,field_description:contract.field_account_analytic_contract_display_name @@ -406,6 +406,12 @@ msgstr "Agrupar por..." msgid "ID" msgstr "ID (identificación)" +#. module: contract +#: model:ir.model.fields,help:contract.field_account_analytic_contract_line_automatic_price +#: model:ir.model.fields,help:contract.field_account_analytic_invoice_line_automatic_price +msgid "If this is marked, the price will be obtained automatically applying the pricelist to the product. If not, you will be able to introduce a manual price" +msgstr "Si está marcado, el precio se obtendrá automáticamente aplicando la tarifa al producto. Si no, podrá introducir un precio manual" + #. module: contract #: model:ir.model,name:contract.model_account_invoice msgid "Invoice" @@ -465,8 +471,7 @@ msgstr "Última actualización en" #: model:ir.ui.view,arch_db:contract.account_analytic_account_recurring_form_form #: model:ir.ui.view,arch_db:contract.account_analytic_contract_view_form msgid "Legend (for the markers inside invoice lines description)" -msgstr "" -"Leyenda (para los marcadores dentro de descripción en lineas de factura)" +msgstr "Leyenda (para los marcadores dentro de descripción en lineas de factura)" #. module: contract #: selection:account.analytic.account,recurring_rule_type:0 @@ -490,6 +495,16 @@ msgstr "Nombre" msgid "Next Invoice" msgstr "Próxima factura" +#. module: contract +#: model:ir.model,name:contract.model_res_partner +msgid "Partner" +msgstr "Empresa" + +#. module: contract +#: model:ir.model.fields,field_description:contract.field_account_analytic_contract_partner_id +msgid "Partner (always False)" +msgstr "Empresa (siempre Falso)" + #. module: contract #: model:ir.ui.view,arch_db:contract.view_account_analytic_account_contract_search msgid "Partner and dependents" @@ -588,6 +603,12 @@ msgstr "Secuencia de la linea de contrato cuando se muestra en los contratos" msgid "Show the contracts for this partner" msgstr "Muestra los contratos para esta empresa/contacto" +#. module: contract +#: model:ir.model.fields,field_description:contract.field_account_analytic_contract_line_specific_price +#: model:ir.model.fields,field_description:contract.field_account_analytic_invoice_line_specific_price +msgid "Specific Price" +msgstr "Precio específico" + #. module: contract #: model:ir.model.fields,help:contract.field_account_analytic_account_recurring_rule_type #: model:ir.model.fields,help:contract.field_account_analytic_contract_recurring_rule_type @@ -600,8 +621,7 @@ msgstr "Especifica el intervalo para la generación de facturas automática." #: model:ir.model.fields,help:contract.field_account_analytic_contract_recurring_invoicing_type #: model:ir.model.fields,help:contract.field_project_project_recurring_invoicing_type msgid "Specify if process date is 'from' or 'to' invoicing date" -msgstr "" -"Especifica si la fecha de proceso es desde o hasta la fecha de facturación" +msgstr "Especifica si la fecha de proceso es desde o hasta la fecha de facturación" #. module: contract #: model:ir.model.fields,field_description:contract.field_account_analytic_contract_line_price_subtotal @@ -646,11 +666,8 @@ msgstr "Año(s)" #. module: contract #: code:addons/contract/models/account_analytic_account.py:111 #, python-format -msgid "" -"You can't have a next invoicing date before the start of the contract '%s'" -msgstr "" -"No puede tener una fecha de próxima factura anterior a la fecha de inicio " -"del contrato '%s'" +msgid "You can't have a next invoicing date before the start of the contract '%s'" +msgstr "No puede tener una fecha de próxima factura anterior a la fecha de inicio del contrato '%s'" #. module: contract #: code:addons/contract/models/account_analytic_account.py:206 @@ -661,11 +678,9 @@ msgstr "¡Seleccione un cliente para este contrato %s!" #. module: contract #: code:addons/contract/models/account_analytic_account.py:264 #, python-format -msgid "" -"You must review start and end dates!\n" +msgid "You must review start and end dates!\n" "%s" -msgstr "" -"Debe revisar las fechas de inicio y de fin\n" +msgstr "Debe revisar las fechas de inicio y de fin\n" "%s" #. module: contract @@ -700,3 +715,4 @@ msgstr "account.analytic.invoice.line" #: model:ir.ui.view,arch_db:contract.account_analytic_account_recurring_form_form msgid "⇒ Show recurring invoices" msgstr "⇒ Mostrar facturas recurrentes" + diff --git a/contract/migrations/11.0.2.0.0/pre-migration.py b/contract/migrations/11.0.2.0.0/pre-migration.py new file mode 100644 index 00000000..b97bb7a0 --- /dev/null +++ b/contract/migrations/11.0.2.0.0/pre-migration.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- +# Copyright 2015-2017 Tecnativa - Pedro M. Baeza +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + + +def migrate(cr, version): + """Rename column for specific price for keeping backwards compatibility.""" + if not version: + return + cr.execute( + "ALTER TABLE account_analytic_invoice_line " + "RENAME price_unit TO specific_price" + ) diff --git a/contract/models/account_analytic_contract.py b/contract/models/account_analytic_contract.py index 2753e017..6f04a9e5 100644 --- a/contract/models/account_analytic_contract.py +++ b/contract/models/account_analytic_contract.py @@ -1,8 +1,9 @@ -# © 2004-2010 OpenERP SA -# © 2014 Angel Moya -# © 2015 Pedro M. Baeza -# © 2016 Carlos Dauden +# -*- coding: utf-8 -*- +# Copyright 2004-2010 OpenERP SA +# Copyright 2014 Angel Moya +# Copyright 2016 Carlos Dauden # Copyright 2016-2017 LasLabs Inc. +# Copyright 2015-2017 Tecnativa - Pedro M. Baeza # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo import api, fields, models @@ -14,11 +15,17 @@ class AccountAnalyticContract(models.Model): # These fields will not be synced to the contract NO_SYNC = [ 'name', + 'partner_id', ] name = fields.Char( required=True, ) + # Needed for avoiding errors on several inherited behaviors + partner_id = fields.Many2one( + comodel_name="res.partner", + string="Partner (always False)", + ) pricelist_id = fields.Many2one( comodel_name='product.pricelist', string='Pricelist', diff --git a/contract/models/account_analytic_invoice_line.py b/contract/models/account_analytic_invoice_line.py index 188d4c81..75fd22df 100644 --- a/contract/models/account_analytic_invoice_line.py +++ b/contract/models/account_analytic_invoice_line.py @@ -1,8 +1,9 @@ -# © 2004-2010 OpenERP SA -# © 2014 Angel Moya -# © 2015 Pedro M. Baeza -# © 2016-2018 Carlos Dauden +# -*- coding: utf-8 -*- +# Copyright 2004-2010 OpenERP SA +# Copyright 2014 Angel Moya +# Copyright 2016 Carlos Dauden # Copyright 2016-2017 LasLabs Inc. +# Copyright 2015-2017 Tecnativa - Pedro M. Baeza # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from dateutil.relativedelta import relativedelta @@ -41,9 +42,19 @@ class AccountAnalyticInvoiceLine(models.Model): string='Unit of Measure', required=True, ) + automatic_price = fields.Boolean( + string="Auto-price?", + help="If this is marked, the price will be obtained automatically " + "applying the pricelist to the product. If not, you will be " + "able to introduce a manual price", + ) + specific_price = fields.Float( + string='Specific Price', + ) price_unit = fields.Float( - 'Unit Price', - required=True, + string='Unit Price', + compute="_compute_price_unit", + inverse="_inverse_price_unit", ) price_subtotal = fields.Float( compute='_compute_price_subtotal', @@ -72,6 +83,37 @@ class AccountAnalyticInvoiceLine(models.Model): help='Date to invoiced period', ) + @api.depends( + 'automatic_price', + 'specific_price', + 'product_id', + 'quantity', + 'analytic_account_id.pricelist_id', + 'analytic_account_id.partner_id', + ) + def _compute_price_unit(self): + """Get the specific price if no auto-price, and the price obtained + from the pricelist otherwise. + """ + for line in self: + if line.automatic_price: + product = line.product_id.with_context( + quantity=line.quantity, + pricelist=line.analytic_account_id.pricelist_id.id, + partner=line.analytic_account_id.partner_id.id, + date=line.env.context.get('old_date', fields.Date.today()), + ) + line.price_unit = product.price + else: + line.price_unit = line.specific_price + + # Tip in https://github.com/odoo/odoo/issues/23891#issuecomment-376910788 + @api.onchange('price_unit') + def _inverse_price_unit(self): + """Store the specific price in the no auto-price records.""" + for line in self.filtered(lambda x: not x.automatic_price): + line.specific_price = line.price_unit + @api.multi @api.depends('quantity', 'price_unit', 'discount') def _compute_price_subtotal(self): @@ -150,12 +192,12 @@ class AccountAnalyticInvoiceLine(models.Model): if self.analytic_account_id._name == 'account.analytic.account': date = ( self.analytic_account_id.recurring_next_date or - fields.Datetime.now() + fields.Date.today() ) partner = self.analytic_account_id.partner_id else: - date = fields.Datetime.now() + date = fields.Date.today() partner = self.env.user.partner_id product = self.product_id.with_context( diff --git a/contract/tests/test_contract.py b/contract/tests/test_contract.py index c82baf0b..3c11073b 100644 --- a/contract/tests/test_contract.py +++ b/contract/tests/test_contract.py @@ -59,6 +59,20 @@ class TestContract(TestContractBase): with self.assertRaises(ValidationError): self.acct_line.write({'discount': 120}) + def test_automatic_price(self): + self.acct_line.automatic_price = True + self.product.list_price = 1100 + self.assertEqual(self.acct_line.price_unit, 1100) + # Try to write other price + self.acct_line.price_unit = 10 + self.acct_line.refresh() + self.assertEqual(self.acct_line.price_unit, 1100) + # Now disable automatic price + self.acct_line.automatic_price = False + self.acct_line.price_unit = 10 + self.acct_line.refresh() + self.assertEqual(self.acct_line.price_unit, 10) + def test_contract(self): self.assertAlmostEqual(self.acct_line.price_subtotal, 50.0) res = self.acct_line._onchange_product_id() diff --git a/contract/views/account_analytic_account_view.xml b/contract/views/account_analytic_account_view.xml index b915b90b..c85a7d5c 100644 --- a/contract/views/account_analytic_account_view.xml +++ b/contract/views/account_analytic_account_view.xml @@ -78,7 +78,9 @@ - + + + diff --git a/contract/views/account_analytic_contract_view.xml b/contract/views/account_analytic_contract_view.xml index 2e3d9da4..d724b3db 100644 --- a/contract/views/account_analytic_contract_view.xml +++ b/contract/views/account_analytic_contract_view.xml @@ -30,14 +30,16 @@ - + - + + +