Browse Source

Merge pull request #152 from Tecnativa/11.0-contract-automatic_prices

[IMP] contract: Automatic prices for lines
pull/148/merge
Pedro M. Baeza 7 years ago
committed by GitHub
parent
commit
39128de36f
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      contract/README.rst
  2. 2
      contract/__manifest__.py
  3. 72
      contract/i18n/es.po
  4. 13
      contract/migrations/11.0.2.0.0/pre-migration.py
  5. 15
      contract/models/account_analytic_contract.py
  6. 58
      contract/models/account_analytic_invoice_line.py
  7. 14
      contract/tests/test_contract.py
  8. 4
      contract/views/account_analytic_account_view.xml
  9. 6
      contract/views/account_analytic_contract_view.xml

2
contract/README.rst

@ -33,6 +33,8 @@ To use this module, you need to:
* Invoicing type: pre-paid or post-paid. * Invoicing type: pre-paid or post-paid.
#. Add the lines to be invoiced with the product, description, quantity and #. Add the lines to be invoiced with the product, description, quantity and
price. 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 #. 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. 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 #. Choosing between pre-paid and post-paid, you modify the dates that are shown

2
contract/__manifest__.py

@ -8,7 +8,7 @@
{ {
'name': 'Contracts Management - Recurring', 'name': 'Contracts Management - Recurring',
'version': '11.0.1.4.3',
'version': '11.0.2.0.0',
'category': 'Contract Management', 'category': 'Contract Management',
'license': 'AGPL-3', 'license': 'AGPL-3',
'author': "OpenERP SA, " 'author': "OpenERP SA, "

72
contract/i18n/es.po

@ -5,6 +5,8 @@
# Translators: # Translators:
# OCA Transbot <transbot@odoo-community.org>, 2017 # OCA Transbot <transbot@odoo-community.org>, 2017
# Pedro M. Baeza <pedro.baeza@gmail.com>, 2017 # Pedro M. Baeza <pedro.baeza@gmail.com>, 2017
# * contract
#
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Odoo Server 11.0\n" "Project-Id-Version: Odoo Server 11.0\n"
@ -16,13 +18,11 @@ msgstr ""
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n" "Content-Transfer-Encoding: \n"
"Language: es\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"Plural-Forms: \n"
#. module: contract #. module: contract
#: model:mail.template,body_html:contract.email_contract_template #: model:mail.template,body_html:contract.email_contract_template
msgid ""
"\n"
msgid "\n"
"<div style=\"font-family: 'Lucida Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: #FFF; \">\n" "<div style=\"font-family: 'Lucida Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: #FFF; \">\n"
" <p>Hello ${object.partner_id.name or ''},</p>\n" " <p>Hello ${object.partner_id.name or ''},</p>\n"
" <p>A new contract has been created: </p>\n" " <p>A new contract has been created: </p>\n"
@ -70,8 +70,7 @@ msgid ""
" </div>\n" " </div>\n"
"</div>\n" "</div>\n"
" " " "
msgstr ""
"\n"
msgstr "\n"
"<div style=\"font-family: 'Lucida Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: #FFF; \">\n" "<div style=\"font-family: 'Lucida Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: #FFF; \">\n"
" <p>Hola ${object.partner_id.name or ''},</p>\n" " <p>Hola ${object.partner_id.name or ''},</p>\n"
" <p>Se ha creado un nuevo contrato: </p>\n" " <p>Se ha creado un nuevo contrato: </p>\n"
@ -197,6 +196,12 @@ msgstr "Ver líneas contables analíticas"
msgid "Analytic Account" msgid "Analytic Account"
msgstr "Cuenta analítica" 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 #. module: contract
#: model:ir.actions.act_window,help:contract.account_analytic_contract_action #: model:ir.actions.act_window,help:contract.account_analytic_contract_action
msgid "Click to create a new contract template." msgid "Click to create a new contract template."
@ -235,8 +240,7 @@ msgstr "Contrato"
#: code:addons/contract/models/account_analytic_account.py:138 #: code:addons/contract/models/account_analytic_account.py:138
#, python-format #, python-format
msgid "Contract '%s' start date can't be later than end date" 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 #. module: contract
#: model:ir.model,name:contract.model_account_analytic_contract_line #: 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_account_analytic_account_create_invoice_visibility
#: model:ir.model.fields,field_description:contract.field_project_project_create_invoice_visibility #: model:ir.model.fields,field_description:contract.field_project_project_create_invoice_visibility
msgid "Create Invoice Visibility" msgid "Create Invoice Visibility"
msgstr ""
msgstr "Visibilidad de crear factura"
#. module: contract #. module: contract
#: model:ir.ui.view,arch_db:contract.account_analytic_account_recurring_form_form #: 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 #. module: contract
#: model:ir.model.fields,help:contract.field_account_analytic_contract_line_discount #: model:ir.model.fields,help:contract.field_account_analytic_contract_line_discount
#: model:ir.model.fields,help:contract.field_account_analytic_invoice_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 #. module: contract
#: model:ir.model.fields,field_description:contract.field_account_analytic_contract_display_name #: model:ir.model.fields,field_description:contract.field_account_analytic_contract_display_name
@ -406,6 +406,12 @@ msgstr "Agrupar por..."
msgid "ID" msgid "ID"
msgstr "ID (identificación)" 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 #. module: contract
#: model:ir.model,name:contract.model_account_invoice #: model:ir.model,name:contract.model_account_invoice
msgid "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_account_recurring_form_form
#: model:ir.ui.view,arch_db:contract.account_analytic_contract_view_form #: model:ir.ui.view,arch_db:contract.account_analytic_contract_view_form
msgid "Legend (for the markers inside invoice lines description)" 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 #. module: contract
#: selection:account.analytic.account,recurring_rule_type:0 #: selection:account.analytic.account,recurring_rule_type:0
@ -490,6 +495,16 @@ msgstr "Nombre"
msgid "Next Invoice" msgid "Next Invoice"
msgstr "Próxima factura" 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 #. module: contract
#: model:ir.ui.view,arch_db:contract.view_account_analytic_account_contract_search #: model:ir.ui.view,arch_db:contract.view_account_analytic_account_contract_search
msgid "Partner and dependents" 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" msgid "Show the contracts for this partner"
msgstr "Muestra los contratos para esta empresa/contacto" 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 #. module: contract
#: model:ir.model.fields,help:contract.field_account_analytic_account_recurring_rule_type #: 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 #: 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_account_analytic_contract_recurring_invoicing_type
#: model:ir.model.fields,help:contract.field_project_project_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" 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 #. module: contract
#: model:ir.model.fields,field_description:contract.field_account_analytic_contract_line_price_subtotal #: model:ir.model.fields,field_description:contract.field_account_analytic_contract_line_price_subtotal
@ -646,11 +666,8 @@ msgstr "Año(s)"
#. module: contract #. module: contract
#: code:addons/contract/models/account_analytic_account.py:111 #: code:addons/contract/models/account_analytic_account.py:111
#, python-format #, 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 #. module: contract
#: code:addons/contract/models/account_analytic_account.py:206 #: code:addons/contract/models/account_analytic_account.py:206
@ -661,11 +678,9 @@ msgstr "¡Seleccione un cliente para este contrato %s!"
#. module: contract #. module: contract
#: code:addons/contract/models/account_analytic_account.py:264 #: code:addons/contract/models/account_analytic_account.py:264
#, python-format #, python-format
msgid ""
"You must review start and end dates!\n"
msgid "You must review start and end dates!\n"
"%s" "%s"
msgstr ""
"Debe revisar las fechas de inicio y de fin\n"
msgstr "Debe revisar las fechas de inicio y de fin\n"
"%s" "%s"
#. module: contract #. module: contract
@ -700,3 +715,4 @@ msgstr "account.analytic.invoice.line"
#: model:ir.ui.view,arch_db:contract.account_analytic_account_recurring_form_form #: model:ir.ui.view,arch_db:contract.account_analytic_account_recurring_form_form
msgid "⇒ Show recurring invoices" msgid "⇒ Show recurring invoices"
msgstr "⇒ Mostrar facturas recurrentes" msgstr "⇒ Mostrar facturas recurrentes"

13
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"
)

15
contract/models/account_analytic_contract.py

@ -1,8 +1,9 @@
# © 2004-2010 OpenERP SA
# © 2014 Angel Moya <angel.moya@domatix.com>
# © 2015 Pedro M. Baeza <pedro.baeza@tecnativa.com>
# © 2016 Carlos Dauden <carlos.dauden@tecnativa.com>
# -*- coding: utf-8 -*-
# Copyright 2004-2010 OpenERP SA
# Copyright 2014 Angel Moya <angel.moya@domatix.com>
# Copyright 2016 Carlos Dauden <carlos.dauden@tecnativa.com>
# Copyright 2016-2017 LasLabs Inc. # Copyright 2016-2017 LasLabs Inc.
# Copyright 2015-2017 Tecnativa - Pedro M. Baeza
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api, fields, models from odoo import api, fields, models
@ -14,11 +15,17 @@ class AccountAnalyticContract(models.Model):
# These fields will not be synced to the contract # These fields will not be synced to the contract
NO_SYNC = [ NO_SYNC = [
'name', 'name',
'partner_id',
] ]
name = fields.Char( name = fields.Char(
required=True, 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( pricelist_id = fields.Many2one(
comodel_name='product.pricelist', comodel_name='product.pricelist',
string='Pricelist', string='Pricelist',

58
contract/models/account_analytic_invoice_line.py

@ -1,8 +1,9 @@
# © 2004-2010 OpenERP SA
# © 2014 Angel Moya <angel.moya@domatix.com>
# © 2015 Pedro M. Baeza <pedro.baeza@tecnativa.com>
# © 2016-2018 Carlos Dauden <carlos.dauden@tecnativa.com>
# -*- coding: utf-8 -*-
# Copyright 2004-2010 OpenERP SA
# Copyright 2014 Angel Moya <angel.moya@domatix.com>
# Copyright 2016 Carlos Dauden <carlos.dauden@tecnativa.com>
# Copyright 2016-2017 LasLabs Inc. # Copyright 2016-2017 LasLabs Inc.
# Copyright 2015-2017 Tecnativa - Pedro M. Baeza
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
@ -41,9 +42,19 @@ class AccountAnalyticInvoiceLine(models.Model):
string='Unit of Measure', string='Unit of Measure',
required=True, 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( price_unit = fields.Float(
'Unit Price',
required=True,
string='Unit Price',
compute="_compute_price_unit",
inverse="_inverse_price_unit",
) )
price_subtotal = fields.Float( price_subtotal = fields.Float(
compute='_compute_price_subtotal', compute='_compute_price_subtotal',
@ -72,6 +83,37 @@ class AccountAnalyticInvoiceLine(models.Model):
help='Date to invoiced period', 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.multi
@api.depends('quantity', 'price_unit', 'discount') @api.depends('quantity', 'price_unit', 'discount')
def _compute_price_subtotal(self): def _compute_price_subtotal(self):
@ -150,12 +192,12 @@ class AccountAnalyticInvoiceLine(models.Model):
if self.analytic_account_id._name == 'account.analytic.account': if self.analytic_account_id._name == 'account.analytic.account':
date = ( date = (
self.analytic_account_id.recurring_next_date or self.analytic_account_id.recurring_next_date or
fields.Datetime.now()
fields.Date.today()
) )
partner = self.analytic_account_id.partner_id partner = self.analytic_account_id.partner_id
else: else:
date = fields.Datetime.now()
date = fields.Date.today()
partner = self.env.user.partner_id partner = self.env.user.partner_id
product = self.product_id.with_context( product = self.product_id.with_context(

14
contract/tests/test_contract.py

@ -59,6 +59,20 @@ class TestContract(TestContractBase):
with self.assertRaises(ValidationError): with self.assertRaises(ValidationError):
self.acct_line.write({'discount': 120}) 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): def test_contract(self):
self.assertAlmostEqual(self.acct_line.price_subtotal, 50.0) self.assertAlmostEqual(self.acct_line.price_subtotal, 50.0)
res = self.acct_line._onchange_product_id() res = self.acct_line._onchange_product_id()

4
contract/views/account_analytic_account_view.xml

@ -78,7 +78,9 @@
<field name="name"/> <field name="name"/>
<field name="quantity"/> <field name="quantity"/>
<field name="uom_id"/> <field name="uom_id"/>
<field name="price_unit"/>
<field name="automatic_price"/>
<field name="price_unit" attrs="{'readonly': [('automatic_price', '=', True)]}"/>
<field name="specific_price" invisible="1"/>
<field name="discount" groups="sale.group_discount_per_so_line" /> <field name="discount" groups="sale.group_discount_per_so_line" />
<field name="price_subtotal"/> <field name="price_subtotal"/>
</tree> </tree>

6
contract/views/account_analytic_contract_view.xml

@ -30,14 +30,16 @@
</group> </group>
</group> </group>
<group name="group_invoice_lines" string="Invoice Lines"> <group name="group_invoice_lines" string="Invoice Lines">
<field name="recurring_invoice_line_ids">
<field name="recurring_invoice_line_ids" nolabel="1">
<tree string="Account Analytic Lines" editable="bottom"> <tree string="Account Analytic Lines" editable="bottom">
<field name="sequence" widget="handle" /> <field name="sequence" widget="handle" />
<field name="product_id" /> <field name="product_id" />
<field name="name" /> <field name="name" />
<field name="quantity" /> <field name="quantity" />
<field name="uom_id" /> <field name="uom_id" />
<field name="price_unit" />
<field name="automatic_price"/>
<field name="price_unit" attrs="{'readonly': [('automatic_price', '=', True)]}"/>
<field name="specific_price" invisible="1"/>
<field name="discount" groups="sale.group_discount_per_so_line" /> <field name="discount" groups="sale.group_discount_per_so_line" />
<field name="price_subtotal" /> <field name="price_subtotal" />
</tree> </tree>

Loading…
Cancel
Save