167 lines
7.3 KiB
Python
167 lines
7.3 KiB
Python
import logging
|
|
|
|
from odoo import models, fields, api
|
|
from odoo.tools.float_utils import float_repr
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
PRICER_RELATED_FIELDS = [
|
|
'additional_product_tag_ids',
|
|
'barcode',
|
|
'currency_id',
|
|
'list_price',
|
|
'lst_price',
|
|
'name',
|
|
'on_sale_price',
|
|
'pricer_sale_pricelist_id',
|
|
'pricer_tag_ids',
|
|
'pricer_store_id',
|
|
'res.partner',
|
|
'seller_ids',
|
|
'stock',
|
|
'taxes_id',
|
|
'to_weight',
|
|
'weight',
|
|
]
|
|
|
|
|
|
class ProductProduct(models.Model):
|
|
""" Adding the necessary fields to products to use with Pricer electronic tags """
|
|
_inherit = 'product.product'
|
|
|
|
pricer_store_id = fields.Many2one(
|
|
comodel_name='pricer.store',
|
|
string='Pricer Store',
|
|
index='btree_not_null',
|
|
help='This product will be linked to and displayed on the Pricer tags of the store selected here'
|
|
)
|
|
pricer_tag_ids = fields.One2many(
|
|
comodel_name='pricer.tag',
|
|
inverse_name='product_id',
|
|
string='Pricer tags ids',
|
|
help='This product will be linked to and displayed on the Pricer tags with ids listed here. It is recommended to use a barcode scanner'
|
|
)
|
|
|
|
# Boolean to checker whether we need to create/update this product in Pricer db
|
|
pricer_product_to_create_or_update = fields.Boolean(default=False)
|
|
|
|
# String representing price including taxes
|
|
# Used in requests sent to the Pricer API and displayed on tags
|
|
pricer_display_price = fields.Char()
|
|
|
|
pricer_sale_pricelist_id = fields.Many2one(
|
|
'product.pricelist',
|
|
string='Pricer Sales Pricelist',
|
|
help='This pricelist will be used to set sales on Pricer tags for this product',
|
|
domain=lambda self: [
|
|
'&',
|
|
('item_ids.product_id', '=', self.id),
|
|
'&',
|
|
('item_ids.min_quantity', '<=', 1),
|
|
'|',
|
|
('item_ids.compute_price', 'in', ['percentage', 'fixed']),
|
|
('item_ids.base', 'in', ['list_price', 'standard_price']),
|
|
],
|
|
# for now, we don't handle pricelists available for quantities > 1, neither those based on 'formulas based on
|
|
# other pricelists'
|
|
)
|
|
|
|
on_sale_price = fields.Float(help="Price after setting a Pricer Sales Pricelist", store=True)
|
|
|
|
def compute_prices(self, on_sale=False):
|
|
currency = self.currency_id
|
|
price = self.on_sale_price if on_sale else self.lst_price
|
|
if not self.taxes_id:
|
|
return float_repr(price, currency.decimal_places)
|
|
else:
|
|
res = self.taxes_id.compute_all(price, product=self, partner=self.env['res.partner'])
|
|
rounded_including = float_repr(res['total_included'], currency.decimal_places)
|
|
rounded_excluding = float_repr(res['total_excluded'], currency.decimal_places)
|
|
result = rounded_including if currency.compare_amounts(res['total_included'], price) else rounded_excluding
|
|
return result
|
|
|
|
def _get_create_or_update_body(self):
|
|
"""
|
|
If the product related to a pricer tag needs to be updated:
|
|
- we need to add its data to the JSON body used in create/update request
|
|
"""
|
|
variants = [
|
|
','.join(record.product_attribute_value_id.mapped('name'))
|
|
for record in self.product_template_variant_value_ids
|
|
if record.price_extra and record.price_extra != 0
|
|
]
|
|
variant = ','.join(variants)
|
|
|
|
variants_tags = [
|
|
','.join(record.mapped('name'))
|
|
for record in self.additional_product_tag_ids
|
|
]
|
|
variant_tag = ','.join(variants_tags)
|
|
|
|
self.pricer_display_price = self.compute_prices(on_sale=self.pricer_sale_pricelist_id)
|
|
|
|
# If multiple suppliers / taxes are set, we only send the first one to Pricer
|
|
supplier_id = self.seller_ids[0] if self.seller_ids else None
|
|
taxes_id = self.product_tmpl_id.taxes_id[0] if self.product_tmpl_id.taxes_id else None
|
|
|
|
data_to_send = {
|
|
"itemId": self.id,
|
|
"itemName": self.name,
|
|
"price": self.pricer_display_price, # price including taxes
|
|
"presentation": "PROMO" if self.pricer_sale_pricelist_id else "NORMAL", # template name used on pricer tags
|
|
"properties": {
|
|
"barcode": self.barcode or "", # product barcode
|
|
"currency": self.currency_id.symbol, # product currency symbol (Ex: '$')
|
|
"price_before_discount": self.compute_prices(on_sale=False), # product price before pricelist discount (if any)
|
|
"price_excl_tax": self.lst_price, # product price excluding taxes
|
|
"stock_qty_available": self.qty_available or "", # stock_qty_available
|
|
"supplier_reference": supplier_id.partner_id.ref if supplier_id and supplier_id.partner_id else "", # reference identifying the supplier
|
|
"supplier_product_code": supplier_id.product_code if supplier_id else "", # reference identifying the product for the supplier
|
|
"tax_name": taxes_id.name if taxes_id else "", # the name of the tax rule applied to the product (Ex: VAT 21%)
|
|
"to_weight": self.to_weight or "", # boolean indicating if the product is sold by weight
|
|
"unit_of_measure": self.uom_id.name if self.uom_id else "", # units used to sell the product (Ex: 1.5 eur per kg)
|
|
"variant": variant or "", # product variant names separated by ','
|
|
"variant_tag": variant_tag or "", # product additional tags separated by ','
|
|
"weight": self.weight or "", # product weight as used in inventory
|
|
}
|
|
}
|
|
_logger.debug("Data to send to Pricer API for product [%s] %s: %s", self.id, self.name, data_to_send)
|
|
|
|
return data_to_send
|
|
|
|
def write(self, vals):
|
|
"""
|
|
Called whenever we update a product variant and click "save"
|
|
If Pricer related fields are changed,
|
|
We need to send the new information to Pricer API to display it
|
|
"""
|
|
if any(val in PRICER_RELATED_FIELDS for val in vals):
|
|
vals['pricer_product_to_create_or_update'] = True
|
|
|
|
result = super().write(vals)
|
|
if 'pricer_store_id' in vals:
|
|
self.pricer_tag_ids.pricer_product_to_link = True
|
|
|
|
return result
|
|
|
|
@api.onchange('pricer_sale_pricelist_id', 'lst_price', 'standard_price')
|
|
def _onchange_compute_pricing(self):
|
|
# We use '._origin' to avoid getting a NewId (as the record is in a transient state) instead of id
|
|
for product in self:
|
|
if product.pricer_sale_pricelist_id:
|
|
# temporarily patch _origin with dirty values so _get_product_price
|
|
# uses the latest unsaved values
|
|
original_lst_price = product._origin.lst_price
|
|
original_standard_price = product._origin.standard_price
|
|
|
|
product._origin.lst_price = product.lst_price
|
|
product._origin.standard_price = product.standard_price
|
|
computed_price = product.pricer_sale_pricelist_id._get_product_price(product._origin or product, quantity=1.0)
|
|
|
|
product._origin.lst_price = original_lst_price
|
|
product._origin.standard_price = original_standard_price
|
|
|
|
product.on_sale_price = product._origin.on_sale_price = computed_price
|
|
else:
|
|
product.on_sale_price = 0.0
|