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. +
+ options="{'currency_field': 'currency_id'}" + invisible="not x_fc_margin_available"/> + widget="percentage" + invisible="not x_fc_margin_available"/> +
@@ -188,6 +198,13 @@ +
+
diff --git a/fusion_plating/fusion_plating_configurator/wizard/fp_direct_order_wizard_views.xml b/fusion_plating/fusion_plating_configurator/wizard/fp_direct_order_wizard_views.xml index cd3bd630..fa848c42 100644 --- a/fusion_plating/fusion_plating_configurator/wizard/fp_direct_order_wizard_views.xml +++ b/fusion_plating/fusion_plating_configurator/wizard/fp_direct_order_wizard_views.xml @@ -6,6 +6,13 @@ fp.direct.order.wizard
+