diff --git a/fusion_plating/fusion_plating_bridge_mrp/models/sale_order.py b/fusion_plating/fusion_plating_bridge_mrp/models/sale_order.py
index 6bcc1487..b1844bad 100644
--- a/fusion_plating/fusion_plating_bridge_mrp/models/sale_order.py
+++ b/fusion_plating/fusion_plating_bridge_mrp/models/sale_order.py
@@ -229,35 +229,38 @@ class SaleOrder(models.Model):
self.env.cr.execute('RELEASE SAVEPOINT %s' % savepoint_name)
except Exception as exc:
self.env.cr.execute('ROLLBACK TO SAVEPOINT %s' % savepoint_name)
- self.message_post(body=_(
+ self.message_post(body=(
'Auto-MO group %s failed: %s'
) % (tag or 'single-line', exc))
continue
if created or adopted:
+ # _() needs a lang in env.context; in shell/cron this may be
+ # unset. Compose the message with plain format strings — this
+ # text is an internal chatter log, not user-facing UI.
msg_parts = []
if created:
lines_html = ' '.join([
- _('MO %s '
- '(%s, %d source line%s)') % (
- mo.id, mo.name, tag or 'untagged',
- n, 's' if n != 1 else ''
- )
+ 'MO %s '
+ '(%s, %d source line%s)' % (
+ mo.id, mo.name, tag or 'untagged',
+ n, 's' if n != 1 else ''
+ )
for mo, tag, n in created
])
msg_parts.append(
- _('%d draft MO(s) auto-created: %s') % (
+ '%d draft MO(s) auto-created: %s' % (
len(created), lines_html,
)
)
if adopted:
adopted_html = ' '.join([
- _('MO %s '
- '(legacy, now line-linked)') % (mo.id, mo.name)
+ 'MO %s '
+ '(legacy, now line-linked)' % (mo.id, mo.name)
for mo in adopted
])
msg_parts.append(
- _('%d legacy MO(s) adopted: %s') % (
+ '%d legacy MO(s) adopted: %s' % (
len(adopted), adopted_html,
)
)
diff --git a/fusion_plating/fusion_plating_configurator/models/sale_order.py b/fusion_plating/fusion_plating_configurator/models/sale_order.py
index 939255b5..1f620ef9 100644
--- a/fusion_plating/fusion_plating_configurator/models/sale_order.py
+++ b/fusion_plating/fusion_plating_configurator/models/sale_order.py
@@ -113,6 +113,12 @@ class SaleOrder(models.Model):
string='Margin %',
compute='_compute_margin',
)
+ x_fc_margin_available = fields.Boolean(
+ string='Margin Available',
+ compute='_compute_margin',
+ help='False when no order line has a costed coating — the '
+ 'margin fields should render "n/a" in the UI.',
+ )
x_fc_workorder_count = fields.Integer(
string='Active WOs',
@@ -486,24 +492,34 @@ class SaleOrder(models.Model):
@api.depends('order_line.price_subtotal', 'amount_untaxed')
def _compute_margin(self):
- """Simple margin: untaxed total minus rolled-up cost from coating configs.
+ """Margin = untaxed total − rolled-up cost from coating configs.
x_fc_margin_percent is stored as a fraction (0.0 - 1.0) so the
- widget='percentage' formats it correctly (a 100% margin reads
- as 100%, not 10000%).
+ widget='percentage' formats 100% as 100%, not 10000%.
+
+ x_fc_margin_available is False when NO line has a costed coating
+ (i.e. fp.coating.config.unit_cost isn't populated anywhere). The
+ UI should render margin fields as "n/a" in that case rather than
+ showing a misleading 100%.
"""
for rec in self:
+ has_cost_data = False
cost = 0.0
for line in rec.order_line:
- if line.x_fc_coating_config_id:
- cost_per_unit = getattr(
- line.x_fc_coating_config_id, 'unit_cost', 0.0,
- ) or 0.0
- cost += cost_per_unit * (line.product_uom_qty or 0)
+ cc = line.x_fc_coating_config_id
+ if not cc:
+ continue
+ if 'unit_cost' not in cc._fields:
+ continue
+ if cc.unit_cost:
+ has_cost_data = True
+ cost_per_unit = cc.unit_cost or 0.0
+ cost += cost_per_unit * (line.product_uom_qty or 0)
+ rec.x_fc_margin_available = has_cost_data
rec.x_fc_margin_amount = (rec.amount_untaxed or 0) - cost
rec.x_fc_margin_percent = (
(rec.x_fc_margin_amount / rec.amount_untaxed)
- if rec.amount_untaxed else 0.0
+ if (rec.amount_untaxed and has_cost_data) else 0.0
)
@api.onchange('upload_rfq_file')
diff --git a/fusion_plating/fusion_plating_configurator/views/sale_order_views.xml b/fusion_plating/fusion_plating_configurator/views/sale_order_views.xml
index 19f29706..3248c4d0 100644
--- a/fusion_plating/fusion_plating_configurator/views/sale_order_views.xml
+++ b/fusion_plating/fusion_plating_configurator/views/sale_order_views.xml
@@ -148,11 +148,21 @@
+
+
+ Margin n/a — coating cost rollup not yet
+ populated on any line's treatment.
+