[ADD] product_update_price_last_purchase

This commit is contained in:
Luis 2022-03-29 12:25:28 +02:00
parent 27f0791503
commit 5f4bb24894
18 changed files with 942 additions and 0 deletions

View file

@ -0,0 +1,3 @@
from . import models
from . import report

View file

@ -0,0 +1,31 @@
{
'name': 'Product Update Sale Price',
'version': '12.0.1.1.0',
'category': 'product',
'summary' : 'Allow updating list_price when the cost price changes in last purchase.',
'description' : """
* Setea los impuestos del proveedor al mismo tipo que el impuesto de venta.
* Actualiza el precio de venta según el precio de coste aplicado a una tarifa.
* Filtro para productos actualizados el precio de coste.
* Filtro para productos que necesitan etiqueta nueva.
* Campo que guarda el último precio de compra en vez de usar standard_price y no afectar a la valoración de inventario.
""",
'author': 'Criptomart',
'website': 'https://criptomart.net',
'license': 'AGPL-3',
'depends': [
'base',
'product',
'account',
'stock_account',
],
'data': [
'views/actions.xml',
'views/product_view.xml',
'data/report_paperformat.xml',
'report/report_product_shelf_tag.xml',
'report/report_product_barcode.xml',
'report/product_barcode.xml'
],
'installable': True,
}

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="paperformat_barcode" model="report.paperformat">
<field name="name">Barcodes stickers format</field>
<field name="default" eval="True" />
<field name="format">A4</field>
<field name="page_height">0</field>
<field name="page_width">0</field>
<field name="orientation">Portrait</field>
<field name="margin_top">10</field>
<field name="margin_bottom">5</field>
<field name="margin_left">8</field>
<field name="margin_right">8</field>
<field name="header_line" eval="False" />
<field name="header_spacing">0</field>
<field name="dpi">75</field>
</record>
</odoo>

View file

@ -0,0 +1,282 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * product_update_price_last_purchase
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 12.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-03-28 17:11+0000\n"
"PO-Revision-Date: 2022-03-28 17:11+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: product_update_price_last_purchase
#: model:ir.model.fields,field_description:product_update_price_last_purchase.field_product_product__list_price_automatic
#: model:ir.model.fields,field_description:product_update_price_last_purchase.field_product_template__list_price_automatic
msgid "Automatic Sale Price"
msgstr "Precio de venta automático"
#. module: product_update_price_last_purchase
#: model:ir.model.fields,help:product_update_price_last_purchase.field_product_product__list_price_automatic
#: model:ir.model.fields,help:product_update_price_last_purchase.field_product_template__list_price_automatic
msgid "Automatic computation of the PVP from the cost price and the rate defined in the configuration."
msgstr "Cálculo automático del PVP a partir del precio de coste y la tarifa definida en la configuración."
#. module: product_update_price_last_purchase
#: model:ir.actions.report,name:product_update_price_last_purchase.report_productbarcode
msgid "Barcode"
msgstr "Código de barras"
#. module: product_update_price_last_purchase
#: model:ir.model.fields,help:product_update_price_last_purchase.field_product_pricelist_item__base
msgid "Base price for computation.\n"
"Public Price: The base price will be the Sale/public Price.\n"
"Cost Price : The base price will be the cost price.\n"
"Other Pricelist : Computation of the base price based on another Pricelist."
msgstr "Precio base de cálculo.\n"
"Precio Público: El precio base será el Precio Público / Venta.\n"
"Precio de costo: El precio base será el precio de costo.\n"
"Otra lista de precios: cálculo del precio base basado en otra lista de precios."
#. module: product_update_price_last_purchase
#: model:ir.model.fields,field_description:product_update_price_last_purchase.field_product_pricelist_item__base
msgid "Based on"
msgstr "Basado en"
#. module: product_update_price_last_purchase
#: model:ir.model.fields,help:product_update_price_last_purchase.field_product_product__last_purchase_price_compute_type
#: model:ir.model.fields,help:product_update_price_last_purchase.field_product_template__last_purchase_price_compute_type
msgid "Choose whether discounts should influence the calculation of the last purchase price. Select Never update for manual configuration of cost and sale prices.\n"
"\n"
"* Without discounts: does not take into account discounts when updating the last purchase price.\n"
"* First discount: take into account only first discount when updating the last purchase price.\n"
"* Triple discount: take into account all discounts when updating the last purchase price. Needs \"Purchase Triple Discount\" OCA module.\n"
"* Manual update: Select this for manual configuration of cost and sale price. The sales price will not be calculated automatically."
msgstr "Elija si los descuentos deben influir en la actualización del último precio de compra. Seleccione No actualizar nunca para la configuración manual de los precios de coste y venta.\n"
"\n"
"* Sin descuentos: no tiene en cuenta ningún descuento al actualizar el último precio de compra.\n"
"* Primer descuento: tiene en cuenta sólo el primer descuento al actualizar el último precio de compra.\n"
"* Triple descuento: tiene en cuenta todos los descuentos al actualizar el último precio de compra. Necesita el módulo OCA \"Purchase Triple Discount\".\n"
"* Actualización manual: configuración manual del precio de coste y de venta. El precio de venta no se calculará automáticamente."
#. module: product_update_price_last_purchase
#: model:ir.model,name:product_update_price_last_purchase.model_res_company
msgid "Companies"
msgstr "Compañías"
#. module: product_update_price_last_purchase
#: model:ir.model,name:product_update_price_last_purchase.model_res_config_settings
msgid "Config Settings"
msgstr "Opciones de Configuración"
#. module: product_update_price_last_purchase
#: model:ir.model.fields,field_description:product_update_price_last_purchase.field_product_packaging__qty
msgid "Contained Quantity"
msgstr "Cantidad contenida"
#. module: product_update_price_last_purchase
#: selection:product.pricelist.item,base:0
msgid "Cost"
msgstr "Coste"
#. module: product_update_price_last_purchase
#: selection:product.pricelist.item,base:0
msgid "Cost Price Tax Included"
msgstr "Precio de coste con impuesto incluido"
#. module: product_update_price_last_purchase
#: model_terms:ir.ui.view,arch_db:product_update_price_last_purchase.res_config_settings_view_form_pricelists
msgid "Default pricelists for Coops"
msgstr "Tarifas por defecto para cooperativas"
#. module: product_update_price_last_purchase
#: selection:product.template,last_purchase_price_compute_type:0
msgid "First discount"
msgstr "Primer descuento"
#. module: product_update_price_last_purchase
#: model:ir.model.fields,field_description:product_update_price_last_purchase.field_product_product__last_purchase_price
#: model:ir.model.fields,field_description:product_update_price_last_purchase.field_product_template__last_purchase_price
#: model_terms:ir.ui.view,arch_db:product_update_price_last_purchase.product_view_inherit_list_price_auto
#: selection:product.pricelist.item,base:0
msgid "Last purchase price"
msgstr "Último precio de compra"
#. module: product_update_price_last_purchase
#: model:ir.model.fields,field_description:product_update_price_last_purchase.field_product_product__last_purchase_price_compute_type
#: model:ir.model.fields,field_description:product_update_price_last_purchase.field_product_template__last_purchase_price_compute_type
msgid "Last purchase price calculation type"
msgstr "Cálculo de último precio de compra"
#. module: product_update_price_last_purchase
#: model:ir.model.fields,field_description:product_update_price_last_purchase.field_product_product__list_price_updated
#: model:ir.model.fields,field_description:product_update_price_last_purchase.field_product_template__list_price_updated
msgid "Last purchase price updated"
msgstr "Último precio de compra actualizado"
#. module: product_update_price_last_purchase
#: selection:product.template,last_purchase_price_compute_type:0
msgid "Manual update"
msgstr "Actualización manual"
#. module: product_update_price_last_purchase
#: code:addons/product_update_price_last_purchase/models/product_template.py:38
#, python-format
msgid "Not found a valid pricelist to compute sale price. Check configuration in General Settings."
msgstr ""
#. module: product_update_price_last_purchase
#: selection:product.pricelist.item,base:0
msgid "Other Pricelist"
msgstr "Otra tarifa"
#. module: product_update_price_last_purchase
#: model:ir.model.fields,help:product_update_price_last_purchase.field_product_product__list_price
#: model:ir.model.fields,help:product_update_price_last_purchase.field_product_template__list_price
msgid "Price calculated according to the configured pricelist, including VAT."
msgstr "Precio calculado según la tarifa configurada, incluyendo el IVA."
#. module: product_update_price_last_purchase
#: model:ir.model,name:product_update_price_last_purchase.model_product_pricelist
msgid "Pricelist"
msgstr "Tarifa"
#. module: product_update_price_last_purchase
#: model:ir.model,name:product_update_price_last_purchase.model_product_pricelist_item
msgid "Pricelist Item"
msgstr "Elemento de tarifa"
#. module: product_update_price_last_purchase
#: model:ir.model.fields,field_description:product_update_price_last_purchase.field_res_config_settings__product_pricelist_automatic
msgid "Pricelist applied to automatic calculation of sales price"
msgstr "Tarifa aplicada al cálculo automático del precio de venta"
#. module: product_update_price_last_purchase
#: model:ir.model.fields,field_description:product_update_price_last_purchase.field_res_company__product_pricelist_automatic
msgid "Pricelist applied to the automatic selling price"
msgstr "Tarifa aplicada al PVP automáticamente"
#. module: product_update_price_last_purchase
#: model:ir.model,name:product_update_price_last_purchase.model_product_product
msgid "Product"
msgstr "Producto"
#. module: product_update_price_last_purchase
#: model:ir.actions.report,name:product_update_price_last_purchase.report_product_product_barcode
#: model:ir.actions.report,name:product_update_price_last_purchase.report_product_template_barcode
#: model:ir.actions.report,name:product_update_price_last_purchase.report_productbarcode_laosa
#: model:ir.actions.report,name:product_update_price_last_purchase.report_simple_barcode_laosa
msgid "Product Barcode (PDF)"
msgstr "Código de barras del producto (PDF)"
#. module: product_update_price_last_purchase
#: model:ir.model,name:product_update_price_last_purchase.model_product_packaging
msgid "Product Packaging"
msgstr "Empaquetado del producto"
#. module: product_update_price_last_purchase
#: model:ir.model,name:product_update_price_last_purchase.model_product_template
msgid "Product Template"
msgstr "Plantilla de producto"
#. module: product_update_price_last_purchase
#: model_terms:ir.ui.view,arch_db:product_update_price_last_purchase.view_product_search_form_inherit_tags
msgid "Products that have changed their sales price and no label has been printed."
msgstr "Productos que han cambiado su PVP y no se ha impreso su etiqueta."
#. module: product_update_price_last_purchase
#: model_terms:ir.ui.view,arch_db:product_update_price_last_purchase.view_product_search_form_inherit_updated
msgid "Products that have recently changed their cost price and have not updated their selling price."
msgstr "Productos que han cambiado recientemente su precio de coste y no han actualizado su PVP."
#. module: product_update_price_last_purchase
#: selection:product.pricelist.item,base:0
msgid "Public Price"
msgstr "Precio público"
#. module: product_update_price_last_purchase
#: model:ir.model.fields,help:product_update_price_last_purchase.field_res_company__product_pricelist_automatic
#: model:ir.model.fields,help:product_update_price_last_purchase.field_res_config_settings__product_pricelist_automatic
msgid "Rate that applies to all products that update the selling price automatically."
msgstr "Tarifa que se aplica a todos los productos que actualizan el precio de venta automáticamente."
#. module: product_update_price_last_purchase
#: model:ir.model,name:product_update_price_last_purchase.model_ir_actions_report
msgid "Report Action"
msgstr "Reportar la acción"
#. module: product_update_price_last_purchase
#: model:ir.model.fields,field_description:product_update_price_last_purchase.field_product_product__list_price
#: model:ir.model.fields,field_description:product_update_price_last_purchase.field_product_template__list_price
msgid "Sale price with VAT"
msgstr "PVP con impuestos"
#. module: product_update_price_last_purchase
#: model_terms:ir.ui.view,arch_db:product_update_price_last_purchase.product_view_inherit_list_price_auto
msgid "Sale price."
msgstr "PVP"
#. module: product_update_price_last_purchase
#: model:ir.model,name:product_update_price_last_purchase.model_stock_move
msgid "Stock Move"
msgstr "Movimiento de stock"
#. module: product_update_price_last_purchase
#: model:ir.model.fields,help:product_update_price_last_purchase.field_product_product__list_price_updated
#: model:ir.model.fields,help:product_update_price_last_purchase.field_product_template__list_price_updated
msgid "The last cost price has been updated and you need to update the selling price in the database, shelves and scales."
msgstr "Se ha actualizado el último precio de coste y hay que actualizar el PVP en la base de datos, lineales y las balanzas."
#. module: product_update_price_last_purchase
#: model:ir.model.fields,help:product_update_price_last_purchase.field_product_product__last_purchase_price
#: model:ir.model.fields,help:product_update_price_last_purchase.field_product_template__last_purchase_price
msgid "The last price at which the product was purchased. It is used as the base price field for calculating the product sale price."
msgstr "El último precio al que se compró el producto. Se utiliza como campo de precio base para calcular el precio de venta del producto."
#. module: product_update_price_last_purchase
#: model:ir.model.fields,help:product_update_price_last_purchase.field_product_product__to_print
#: model:ir.model.fields,help:product_update_price_last_purchase.field_product_template__to_print
msgid "The sale price has been updated on this product and needs to be updated on the shelf."
msgstr "El PVP ha sido actualizado en este producto y necesita ser actualizado en el lineal."
#. module: product_update_price_last_purchase
#: model:ir.model.fields,help:product_update_price_last_purchase.field_product_packaging__qty
msgid "The total number of products you can have per pallet or box."
msgstr "El numero total de productos que puede tener por palé o caja."
#. module: product_update_price_last_purchase
#: model_terms:ir.ui.view,arch_db:product_update_price_last_purchase.view_product_search_form_inherit_tags
msgid "To print"
msgstr "Para imprimir"
#. module: product_update_price_last_purchase
#: model:ir.model.fields,field_description:product_update_price_last_purchase.field_product_product__to_print
#: model:ir.model.fields,field_description:product_update_price_last_purchase.field_product_template__to_print
msgid "To print label"
msgstr "Pendiente de imprimir etiqueta"
#. module: product_update_price_last_purchase
#: model_terms:ir.ui.view,arch_db:product_update_price_last_purchase.view_product_search_form_inherit_updated
msgid "To update sales price"
msgstr "Pendiente de actualizar PVP"
#. module: product_update_price_last_purchase
#: selection:product.template,last_purchase_price_compute_type:0
msgid "Triple discount"
msgstr "Triple decuento"
#. module: product_update_price_last_purchase
#: model:ir.actions.server,name:product_update_price_last_purchase.action_product_calculate_theoritical_price
msgid "Update Sales Price"
msgstr "Actualizar PVP"
#. module: product_update_price_last_purchase
#: selection:product.template,last_purchase_price_compute_type:0
msgid "Without discounts"
msgstr "Sin descuentos"

View file

@ -0,0 +1,8 @@
from . import product_template
from . import product_product
from . import res_company
from . import res_config
from . import ir_actions_report
from . import stock_move
from . import product_pricelist
from . import product_pricelist_item

View file

@ -0,0 +1,22 @@
import logging
from odoo import api, fields, models
_logger = logging.getLogger(__name__)
class IrActionsReport(models.Model):
_inherit = 'ir.actions.report'
@api.multi
def render_qweb_pdf(self, res_ids=None, data=None):
_logger.debug("render_qweb_pdf : %s -- %s" %(res_ids, data))
if res_ids:
Model = self.env[self.model]
record_ids = Model.browse(res_ids)
wk_record_ids = Model
for record_id in record_ids:
if self.report_name == "product.report_producttemplatelabel":
record_id.to_print = False
return super(IrActionsReport, self).render_qweb_pdf(res_ids, data)

View file

@ -0,0 +1,25 @@
# Copyright (C) 2020: Criptomart (https://criptomart.net)
# @author Santi Noreña (<santi@criptomart.net>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import api, models
class ProductPricelist(models.Model):
_inherit = 'product.pricelist'
@api.multi
def _compute_price_rule(self, products_qty_partner, date=False, uom_id=False):
ProductPricelistItem = self.env['product.pricelist.item']
ProductProduct = self.env['product.product']
res = super()._compute_price_rule(products_qty_partner, date=date, uom_id=uom_id)
new_res = res.copy()
for product_id, values in res.items():
if values[1]:
item_id = values[1]
if item_id:
item = ProductPricelistItem.browse(item_id)
if item.base == 'last_purchase_price':
product = ProductProduct.browse(product_id)
new_res[product_id] = (product.last_purchase_price, item_id)
return new_res

View file

@ -0,0 +1,12 @@
# Copyright (C) 2020: Criptomart (https://criptomart.net)
# @author Santi Noreña (<santi@criptomart.net>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import fields, models
class ProductPricelistItem(models.Model):
_inherit = 'product.pricelist.item'
base = fields.Selection(selection_add=[
('last_purchase_price', 'Last purchase price')])

View file

@ -0,0 +1,26 @@
# Copyright (C) 2020: Criptomart (https://criptomart.net)
# @author Santi Noreña (<santi@criptomart.net>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import logging
from odoo import models, fields, api
from odoo.addons import decimal_precision as dp
_logger = logging.getLogger(__name__)
class ProductProduct(models.Model):
_inherit = "product.product"
@api.multi
def write(self, vals):
if vals.get('last_purchase_price'):
vals['list_price_updated'] = True
return super(ProductProduct, self).write(vals)
class ProductPackaging(models.Model):
_inherit = "product.packaging"
qty = fields.Float('Contained Quantity', help="The total number of products you can have per pallet or box.", digits=dp.get_precision('Product Unit of Measure'))

View file

@ -0,0 +1,144 @@
# Copyright (C) 2020: Criptomart (https://criptomart.net)
# @author Santi Noreña (<santi@criptomart.net>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import logging
import math
from odoo import exceptions, models, fields, api, _
from odoo.exceptions import UserError
from odoo.addons import decimal_precision as dp
_logger = logging.getLogger(__name__)
class ProductTemplate(models.Model):
_inherit = "product.template"
@api.multi
def _compute_theoritical_price(self):
partner = self.env['res.users'].browse(self.env.uid).partner_id
pricelist_obj = self.env['product.pricelist']
pricelist_id = self.env['ir.config_parameter'].sudo().get_param('product_laosa.product_pricelist_automatic') or False
pricelist = pricelist_obj.browse(int(pricelist_id))
_logger.debug("Calculating price with PriceList : %s" %pricelist.name)
if pricelist:
for template in self:
if template.name and template.id and template.last_purchase_price_compute_type != 'manual_update':
partial_price = pricelist.get_product_price(template.product_variant_id, "1", partner)
_logger.debug("partial_price : %s" %partial_price)
# Suma el IVA
for tax in template.taxes_id:
partial_price = partial_price * ( 1 + ( tax.amount / 100) )
_logger.debug("partial_price after taxes : %s" %partial_price)
# Redondea a 0,05 de precisión
template.list_price = round(round(partial_price / 0.05) * 0.05, -int(math.floor(math.log10(0.05))))
_logger.debug("final price : %s" %template.list_price)
template.list_price_updated = False
else:
raise UserError(_('Not found a valid pricelist to compute sale price. Check configuration in General Settings.'))
return False
list_price = fields.Float(
string="Sale price with VAT",
help="Price calculated according to the configured pricelist, including VAT.",
compute="_compute_theoritical_price",
store=True,
readonly=False,
digits=dp.get_precision('Product Price')
)
list_price_updated = fields.Boolean(
string="Last purchase price updated",
help="The last cost price has been updated and you need to update the selling price in the database, shelves and scales.",
default=False,
readonly=True
)
list_price_automatic = fields.Boolean(
string="Automatic Sale Price",
help="Automatic computation of the PVP from the cost price and the rate defined in the configuration.",
default=True
)
to_print = fields.Boolean(
string="To print label",
help="The sale price has been updated on this product and needs to be updated on the shelf.",
default=False,
readonly=True
)
last_purchase_price = fields.Float(
string="Last purchase price",
help="The last price at which the product was purchased. It is used as the base price field for calculating the product sale price.",
#readonly=True,
digits=dp.get_precision('Product Price')
)
last_purchase_price_compute_type = fields.Selection([
('without_discounts', 'Without discounts'),
('with_discount', 'First discount'),
('with_three_discounts', 'Triple discount'),
('manual_update', 'Manual update')],
string="Last purchase price calculation type",
help='Choose whether discounts should influence the calculation of the last purchase price. Select Never update for manual configuration of cost and sale prices.\n'
'\n* Without discounts: does not take into account discounts when updating the last purchase price.\n'
'* First discount: take into account only first discount when updating the last purchase price.\n'
'* Triple discount: take into account all discounts when updating the last purchase price. Needs "Purchase Triple Discount" OCA module.\n'
'* Manual update: Select this for manual configuration of cost and sale price. The sales price will not be calculated automatically.',
default='without_discounts',
required=True
)
@api.model
def create(self, vals):
# Set supplier tax same than customer tax
if vals.get('taxes_id'):
tax = self.get_supplier_tax(vals.get('taxes_id'))
vals['supplier_taxes_id'] = tax
vals['list_price_updated'] = True
return super(ProductTemplate, self).create(vals)
@api.multi
def write(self, vals):
# Set supplier tax same than customer tax
if vals.get('taxes_id'):
tax = self.get_supplier_tax(vals.get('taxes_id'))
vals['supplier_taxes_id'] = tax
# Mark if product needs list price update
if vals.get('last_purchase_price'):
vals['list_price_updated'] = True
# Mark if product needs update the shelf tag
if vals.get('list_price'):
vals['to_print'] = True
return super(ProductTemplate, self).write(vals)
def get_supplier_tax(self, taxes_id):
tax_obj = self.env['account.tax']
tax_res = []
if taxes_id:
if "6, False, []" not in taxes_id:
for tupla in taxes_id:
a, b, c = tupla
if c:
for i in c:
tax = tax_obj.browse(i)
val = tax.amount
if val == 21:
record_id = self.env.ref('l10n_es.1_account_tax_template_p_iva21_bc').id
elif val == 10:
record_id = self.env.ref('l10n_es.1_account_tax_template_p_iva10_bc').id
elif val == 4:
record_id = self.env.ref('l10n_es.1_account_tax_template_p_iva4_bc').id
else:
record_id = self.env['account.tax'].search([
('amount','=', val),
('active','=', True),
('type_tax_use','=','purchase')
],
limit=1).id
tax_res.append(record_id)
return [(6, 0, tax_res)]

View file

@ -0,0 +1,17 @@
#import logging
from odoo import models, fields, api
#_logger = logging.getLogger(__name__)
class ResCompany(models.Model):
_inherit = "res.company"
product_pricelist_automatic = fields.Many2one(
comodel_name='product.pricelist',
string='Pricelist applied to the automatic selling price',
help='Rate that applies to all products that update the selling price automatically.',
)

View file

@ -0,0 +1,35 @@
#import logging
from odoo import models, fields, api
#_logger = logging.getLogger(__name__)
class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
product_pricelist_automatic = fields.Many2one(
related='company_id.product_pricelist_automatic',
comodel_name='product.pricelist',
string='Pricelist applied to automatic calculation of sales price',
readonly=False,
)
@api.model
def get_values(self):
res = super(ResConfigSettings, self).get_values()
config_obj = self.env["ir.config_parameter"]
product_pricelist_automatic = config_obj.sudo().get_param(
"product_laosa.product_pricelist_automatic", default=0)
return res
@api.multi
def set_values(self):
super(ResConfigSettings, self).set_values()
config_obj = self.env["ir.config_parameter"]
config_obj.sudo().set_param(
"product_laosa.product_pricelist_automatic",
int(self.product_pricelist_automatic.id)
)

View file

@ -0,0 +1,33 @@
# Copyright 2016-2019 Akretion (http://www.akretion.com/)
# Copyright 2020 Criptomart <tech@criptomart.net>
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# @author Santi Noreña (<santi@criptomart.net>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import logging
import math
from odoo import exceptions, models, fields, api, _
from odoo.tools import float_is_zero, float_round, float_compare
_logger = logging.getLogger(__name__)
class StockMove(models.Model):
_inherit = 'stock.move'
@api.multi
def product_price_update_before_done(self):
super(StockMove, self).product_price_update_before_done()
for move in self.filtered(lambda move: move.location_id.usage == 'supplier'):
if move.product_id.last_purchase_price_compute_type == 'with_three_discounts':
price_updated = float_round(move.purchase_line_id.price_subtotal / move.purchase_line_id.product_qty, precision_digits=2)
elif move.product_id.last_purchase_price_compute_type == 'with_discount':
price_updated = float_round(move.purchase_line_id.price_unit * (1 - move.purchase_line_id.discount / 100), precision_digits=2)
else:
price_updated = move.purchase_line_id.price_unit
if float_compare(move.product_id.last_purchase_price, price_updated, precision_digits=2) and not float_is_zero(move.quantity_done, precision_digits=3):
_logger.info("Update last_purchase_price: %s for product %s Previous price: %s" % (price_updated, move.product_id.default_code, move.product_id.last_purchase_price))
# Write the last purchase price, as SUPERUSER_ID because a warehouse manager may not have the right to write on products
move.product_id.with_context(force_company=move.company_id.id).sudo().write({'last_purchase_price': price_updated})

View file

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<report
id="report_productbarcode"
string="Barcode"
model="product.product"
report_type="qweb-pdf"
name="product_laosa.report_productbarcode_laosa"
file="product_laosa.report_productbarcode_laosa"/>
<record id="report_productbarcode" model="ir.actions.report" >
<field name="paperformat_id" ref="product_laosa.paperformat_barcode"/>
</record>
<report
id="report_product_product_barcode"
string="Product Barcode (PDF)"
model="product.product"
report_type="qweb-pdf"
name="product.report_productbarcode"
file="product.report_productbarcode"
print_report_name="'Products barcode - %s' % (object.name)"
/>
<record id="report_product_product_barcode" model="ir.actions.report" >
<field name="paperformat_id" ref="product_laosa.paperformat_barcode"/>
</record>
<report
id="report_product_template_barcode"
string="Product Barcode (PDF)"
model="product.product"
report_type="qweb-pdf"
name="product.report_productbarcode"
file="product.report_productbarcode"
print_report_name="'Products barcode - %s' % (object.name)"
/>
<record id="report_product_template_barcode" model="ir.actions.report" >
<field name="paperformat_id" ref="product_laosa.paperformat_barcode"/>
</record>
<report
id="report_simple_barcode_laosa"
string="Product Barcode (PDF)"
model="product.product"
report_type="qweb-pdf"
name="product.report_productbarcode"
file="product.report_productbarcode"
print_report_name="'Products barcode - %s' % (object.name)"
/>
<record id="report_simple_barcode_laosa" model="ir.actions.report" >
<field name="paperformat_id" ref="product_laosa.paperformat_barcode"/>
</record>
<report
id="report_productbarcode_laosa"
string="Product Barcode (PDF)"
model="product.product"
report_type="qweb-pdf"
name="product.report_productbarcode"
file="product.report_productbarcode"
print_report_name="'Products barcode - %s' % (object.name)"
/>
<record id="report_productbarcode_laosa" model="ir.actions.report" >
<field name="paperformat_id" ref="product_laosa.paperformat_barcode"/>
</record>
</data>
</odoo>

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="report_simple_barcode_laosa" inherit_id="product.report_simple_barcode" >
<xpath expr="." position="replace">
<t t-name="product.report_simple_barcode">
<div style="height: 1.65cm; width: 3.5cm; margin-top: 0.2mm; padding: 0; text-align: center; position: relative; display: inline-table; border: 2px solid black;">
<t t-if="product.barcode">
<img alt="Barcode" t-if="len(product.barcode) == 13" t-att-src="'/report/barcode/?type=%s&amp;value=%s&amp;width=%s&amp;height=%s' % ('EAN13', quote_plus(product.barcode or ''), 600, 150)" style="width: 100%; height: 1.165cm;"/>
<img alt="Barcode" t-elif="len(product.barcode) == 8" t-att-src="'/report/barcode/?type=%s&amp;value=%s&amp;width=%s&amp;height=%s' % ('EAN8', quote_plus(product.barcode or ''), 600, 150)" style="width:100%;height:4rem;"/>
<img alt="Barcode" t-else="" t-att-src="'/report/barcode/?type=%s&amp;value=%s&amp;width=%s&amp;height=%s' % ('Code128', quote_plus(product.barcode or ''), 600, 150)" style="width:100%;height:4rem"/>
<span t-field="product.barcode" style="font-size: 0.6em;" />
</t>
<t t-else=""><span class="text-muted">No barcode</span></t>
</div>
</t>
</xpath>
</template>
<template id="report_productbarcode_laosa" inherit_id="product.report_productbarcode">
<xpath expr="." position="replace">
<t t-name="product.report_productbarcode">
<t t-call="web.html_container">
<div class="page" >
<t t-foreach="docs" t-as="product">
<t t-call="product.report_simple_barcode_laosa">
<t t-set="product" t-value="product"/>
</t>
</t>
</div>
</t>
</t>
</xpath>
</template>
</odoo>

View file

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<data>
<template id="report_simple_label_shelf" inherit_id="product.report_simple_label" >
<xpath expr="." position="replace">
<t t-name="product.report_simple_label">
<div style="height: 4cm; width: 6.7cm; border: 2px solid black; text-align: center; margin: 0; padding: 0; position: relative; display: inline-table;">
<div class="text-center" style="margin: 10px; font-size: 1em; text-align: center;">
<strong t-field="product.name"/>
</div>
<div style="font-size: 1.9em; vertical-align:bottom; position: absolute; bottom: 15; left: 10;">
<strong t-field="product.lst_price" t-options="{'widget': 'monetary', 'display_currency': product.company_id.currency_id}"/>
</div>
<t t-if="product.packaging_ids">
<div style="vertical-align: bottom; position: absolute; bottom: 20; right: 10; font-size: 0.7em; text-align: left;" >
<t t-set="unit_price" t-value="round(product.list_price / product.packaging_ids[0].qty, 1)" />
<strong ><t t-esc="unit_price" t-options="{'widget': 'monetary', 'display_currency': product.company_id.currency_id}"/></strong>
<strong >/</strong>
<strong t-field="product.packaging_ids[0].name"/>
</div>
</t>
</div>
</t>
</xpath>
</template>
<template id="report_producttemplatelabel_shelf" inherit_id="product.report_producttemplatelabel" >
<xpath expr="." position="replace">
<t t-name="product.report_producttemplatelabel">
<t t-call="web.html_container">
<div class="page" style="">
<t t-set="page" t-value="0" />
<t t-foreach="docs" t-as="product">
<t t-call="product.report_simple_label">
<t t-set="product" t-value="product"/>
</t>
</t>
</div>
</t>
</t>
</xpath>
</template>
</data>
</odoo>

View file

@ -0,0 +1,39 @@
<?xml version="1.0"?>
<!--
Copyright (C) 2020-Today: La Osa (<https://laosa.coop/>)
@author: Criptomart (https://criptomart.net)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-->
<odoo>
<!-- Add action entry in the Action Menu for calculate sale price in Products -->
<record id="action_product_calculate_theoritical_price" model="ir.actions.server">
<field name="name">Update Sales Price</field>
<field name="model_id" ref="product.model_product_template"/>
<field name="binding_model_id" ref="product.model_product_template"/>
<field name="state">code</field>
<field name="code">
records._compute_theoritical_price()
</field>
</record>
<!-- Open products window with filter
<record id="action_product_template_list_price_updated" model="ir.actions.act_window">
<field name="name">Productos con PVP actualizable</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">product.template</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="product.product_template_tree_view"/>
<field name="domain">[]</field>
<field name="context">{"search_default_products_updated_filter": 1}</field>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Todos los productos están actualizados.
</p>
</field>
</record>
-->
</odoo>

View file

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<data>
<!-- list automatic price tick in product sheet -->
<record model="ir.ui.view" id="product_view_inherit_list_price_auto">
<field name="name">product.list.price.automatic.form</field>
<field name="model">product.template</field>
<field name="type">form</field>
<field name="inherit_id" ref="product.product_template_form_view"/>
<field name="arch" type="xml">
<field name="list_price" position="replace">
<field name="list_price"
string="Sale price."
widget="monetary"
options="{'currency_field': 'currency_id', 'field_digits': True}"
attrs="{'readonly': [('last_purchase_price_compute_type','!=', 'manual_update')]}"
/>
<field name="last_purchase_price"
string="Last purchase price"
widget="monetary"
options="{'currency_field': 'currency_id', 'field_digits': True}"
attrs="{'readonly': 1}"
/>
<field name="last_purchase_price_compute_type"/>
<field name="list_price_updated"/>
<field name="to_print"/>
</field>
<field name="standard_price" position="replace">
<field name="standard_price" attrs="{'readonly': [('last_purchase_price_compute_type','!=', 'manual_update')]}" />
</field>
</field>
</record>
<!-- Settings Window -->
<record id="res_config_settings_view_form_pricelists" model="ir.ui.view">
<field name="name">product_update_price_last_purchase.res.config.settings.form</field>
<!-- <field name="name">res.config.settings.view.form.inherit.sale.pricelists</field>-->
<field name="model">res.config.settings</field>
<field name="priority" eval="100"/>
<field name="inherit_id" ref="stock.res_config_settings_view_form"/>
<field name="arch" type="xml">
<xpath expr="//div[@data-key='stock']" position="inside">
<h2>Default pricelists for Coops</h2>
<div class="row mt16 o_settings_container" name="coop_setting_container">
<div class="row mt16 o_settings_container">
<label for="product_pricelist_automatic"/>
<field name="product_pricelist_automatic"/>
</div>
</div>
</xpath>
</field>
</record>
<!--Filter for products that should update its sales price -->
<record id="view_product_search_form_inherit_updated" model="ir.ui.view">
<field name="name">view.product.search.form.inherit.updated</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_search_view"/>
<field name="arch" type="xml">
<xpath expr="//filter[@name='services']" position="before">
<separator/>
<filter string="To update sales price"
name="products_updated_filter"
domain="[('list_price_updated', '=', True), ('last_purchase_price_compute_type', '!=', 'manual_update')]"
help="Products that have recently changed their cost price and have not updated their selling price."
icon="terp-project"
/>
</xpath>
</field>
</record>
<!--Filter for products that should print its shelf label -->
<record id="view_product_search_form_inherit_tags" model="ir.ui.view">
<field name="name">product_update_price_last_purchase.product.search.form.inherit.tags</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_search_view"/>
<field name="arch" type="xml">
<xpath expr="//filter[@name='products_updated_filter']" position="after">
<filter string="To print"
name="products_ready_to_print_filter"
domain="[('to_print', '=', True)]"
help="Products that have changed their sales price and no label has been printed."
icon="terp-project"
/>
<separator/>
</xpath>
</field>
</record>
</data>
</odoo>