# -*- coding: utf-8 -*- # Copyright 2026 Nexa Systems Inc. # License OPL-1 (Odoo Proprietary License v1.0) import logging from odoo import models, fields, api _logger = logging.getLogger(__name__) class ProductProduct(models.Model): _inherit = 'product.product' x_fi_price_offset = fields.Float( string='Price Offset', default=0.0, help='Margin-calculated price adjustment, kept separate from ' 'manual attribute price extras.') x_fi_variant_margin_pct = fields.Float( string='Margin (%)', compute='_compute_variant_margin', inverse='_inverse_variant_margin', store=True, readonly=False, help='Profit margin based on (cost + shipping + extra cost). ' 'Extra Price from attributes is added on top, outside margin.') x_fi_margin_override = fields.Boolean( string='Margin Override', default=False, help='When set, the template "Apply Margin" button ' 'and automatic propagation skip this variant.') x_fi_variant_profit = fields.Float( string='Profit', compute='_compute_variant_profit', help='Total sale price minus total cost.') x_fi_shipping_cost = fields.Float( string='Shipping Cost', default=0.0, help='Per-unit shipping cost.') x_fi_cost_extra = fields.Float( string='Attribute Extra Cost', compute='_compute_cost_extra', store=True, help='Sum of Extra Cost from all attribute values for this variant.') @api.depends('product_template_attribute_value_ids.x_fi_extra_cost') def _compute_cost_extra(self): for product in self: product.x_fi_cost_extra = sum( product.product_template_attribute_value_ids.mapped( 'x_fi_extra_cost')) # ── Include price offset in pricelist / SO pricing ── def _get_attributes_extra_price(self): extra = super()._get_attributes_extra_price() return extra + (self.x_fi_price_offset or 0.0) # ── Override lst_price to include our price offset ── @api.depends('list_price', 'price_extra', 'x_fi_price_offset') @api.depends_context('uom') def _compute_product_lst_price(self): to_uom = None if 'uom' in self._context: to_uom = self.env['uom.uom'].browse(self._context['uom']) for product in self: if to_uom: list_price = product.uom_id._compute_price( product.list_price, to_uom) else: list_price = product.list_price product.lst_price = ( list_price + product.price_extra + (product.x_fi_price_offset or 0.0)) def _set_product_lst_price(self): for product in self: if self._context.get('uom'): value = ( float(product.lst_price) * product.uom_id.factor / self.env['uom.uom'].browse( self._context['uom']).factor) else: value = product.lst_price value -= product.price_extra value -= (product.x_fi_price_offset or 0.0) product.write({'list_price': value}) # ── Margin / Profit ── # effective_cost = standard_price + shipping + extra_cost # base_sale_price = list_price + x_fi_price_offset (margin applies here) # final_price = base_sale_price + price_extra (surcharge on top) # margin = (base_sale_price - effective_cost) / base_sale_price @api.depends('list_price', 'price_extra', 'x_fi_price_offset', 'standard_price', 'x_fi_shipping_cost', 'x_fi_cost_extra') def _compute_variant_margin(self): for var in self: base = var.list_price + (var.x_fi_price_offset or 0.0) eff = (var.standard_price + (var.x_fi_shipping_cost or 0.0) + (var.x_fi_cost_extra or 0.0)) if base > 0 and eff > 0: var.x_fi_variant_margin_pct = round( ((base - eff) / base) * 100, 2) else: var.x_fi_variant_margin_pct = 0.0 def _inverse_variant_margin(self): for var in self: margin = var.x_fi_variant_margin_pct or 0.0 eff = (var.standard_price + (var.x_fi_shipping_cost or 0.0) + (var.x_fi_cost_extra or 0.0)) if eff > 0 and 0 < margin < 100: base = round(eff / (1 - margin / 100), 2) var.x_fi_price_offset = round(base - var.list_price, 2) @api.depends('list_price', 'price_extra', 'x_fi_price_offset', 'standard_price', 'x_fi_shipping_cost', 'x_fi_cost_extra') def _compute_variant_profit(self): for var in self: lst = (var.list_price + var.price_extra + (var.x_fi_price_offset or 0.0)) eff = (var.standard_price + (var.x_fi_shipping_cost or 0.0) + (var.x_fi_cost_extra or 0.0)) var.x_fi_variant_profit = lst - eff # ── Onchange handlers ── @api.onchange('x_fi_variant_margin_pct') def _onchange_variant_margin(self): for var in self: margin = var.x_fi_variant_margin_pct or 0.0 eff = (var.standard_price + (var.x_fi_shipping_cost or 0.0) + (var.x_fi_cost_extra or 0.0)) if eff > 0 and 0 < margin < 100: base = round(eff / (1 - margin / 100), 2) var.x_fi_price_offset = round(base - var.list_price, 2) @api.onchange('standard_price', 'x_fi_shipping_cost') def _onchange_variant_cost(self): """When cost or shipping changes, recalculate price to keep margin.""" for var in self: margin = var.x_fi_variant_margin_pct or 0.0 eff = (var.standard_price + (var.x_fi_shipping_cost or 0.0) + (var.x_fi_cost_extra or 0.0)) if eff > 0 and 0 < margin < 100: base = round(eff / (1 - margin / 100), 2) var.x_fi_price_offset = round(base - var.list_price, 2) def _apply_margin_to_variant(self, margin_pct): """Set this variant's price offset to achieve the given margin.""" self.ensure_one() eff = (self.standard_price + (self.x_fi_shipping_cost or 0.0) + (self.x_fi_cost_extra or 0.0)) if eff <= 0 or margin_pct <= 0 or margin_pct >= 100: return base = round(eff / (1 - margin_pct / 100), 2) new_offset = round(base - self.list_price, 2) if abs((self.x_fi_price_offset or 0.0) - new_offset) > 0.001: self.x_fi_price_offset = new_offset