Files
Odoo-Modules/fusion_inventory/models/product_product.py
gsinghpal e9cf75ee48 changes
2026-03-14 12:04:20 -04:00

168 lines
6.7 KiB
Python

# -*- 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