diff --git a/fusion_plating/fusion_plating_configurator/models/sale_order_line.py b/fusion_plating/fusion_plating_configurator/models/sale_order_line.py index f641c47e..092fa9c2 100644 --- a/fusion_plating/fusion_plating_configurator/models/sale_order_line.py +++ b/fusion_plating/fusion_plating_configurator/models/sale_order_line.py @@ -326,6 +326,9 @@ class SaleOrderLine(models.Model): 'pair, falling back to the part\'s default range. Prints ' 'verbatim on the cert, packing slip, and invoice.', ) + x_fc_is_lot_priced = fields.Boolean(string='Lot Priced') + x_fc_lot_total = fields.Monetary( + string='Lot Total', currency_field='currency_id') # ---- Express Orders per-line flags (2026-05-26) ---- # Mirror fp.direct.order.line.{customer_line_ref, masking_enabled, bake_instructions} diff --git a/fusion_plating/fusion_plating_configurator/wizard/fp_direct_order_wizard.py b/fusion_plating/fusion_plating_configurator/wizard/fp_direct_order_wizard.py index bef86e7a..c56f5358 100644 --- a/fusion_plating/fusion_plating_configurator/wizard/fp_direct_order_wizard.py +++ b/fusion_plating/fusion_plating_configurator/wizard/fp_direct_order_wizard.py @@ -813,7 +813,7 @@ class FpDirectOrderWizard(models.Model): 'x_fc_internal_notes': self.internal_notes or False, # material_process is a Many2One since 19.0.22.1.0 — pass .id 'x_fc_material_process': self.material_process.id if self.material_process else False, - 'x_fc_tooling_charge': self.tooling_charge or 0.0, + 'x_fc_tooling_charge': self.charge_amount or self.tooling_charge or 0.0, 'pricelist_id': self.pricelist_id.id if self.pricelist_id else False, 'validity_date': self.validity_date or False, 'order_line': [], @@ -895,32 +895,29 @@ class FpDirectOrderWizard(models.Model): # When blank, Odoo will compute taxes from the product # defaults at SO-line save time (the standard behaviour). # NB. Odoo 19 renamed the SO line field to tax_ids. - 'tax_ids': ([(6, 0, line.tax_ids.ids)] - if line.tax_ids else False), + 'tax_ids': ([(6, 0, self.tax_id.ids)] + if self.tax_id else False), + 'x_fc_is_lot_priced': line.is_lot_priced, + 'x_fc_lot_total': line.lot_total or 0.0, })) - # 4b. Tooling charge — surface as a real sale.order.line so it - # carries through SO.amount_total + invoice naturally, instead of - # sitting orphaned on the SO header. Tax: inherit from the first - # part line that has taxes set (best proxy for "the customer's - # standard tax rate"); falls back to product defaults if no line - # has taxes yet. - if self.tooling_charge: - tooling_taxes = False - first_taxed = next( - (l for l in self.line_ids if l.tax_ids), False, - ) - if first_taxed: - tooling_taxes = [(6, 0, first_taxed.tax_ids.ids)] + # 4b. Additional charge — one typed line, taxed by the order-level + # tax. The charge type's name labels the line; charge_amount with + # legacy tooling_charge fallback for in-flight drafts. + charge_amt = self.charge_amount or self.tooling_charge or 0.0 + if charge_amt: + charge_name = (self.charge_type_id.name + if self.charge_type_id else _('Additional Charge')) so_vals['order_line'].append((0, 0, { 'product_id': product.id, - 'name': _('Tooling Charge'), + 'name': charge_name, 'product_uom_qty': 1.0, - 'price_unit': self.tooling_charge, + 'price_unit': charge_amt, 'x_fc_internal_description': _( - 'One-time tooling fee added via Express Orders.' + 'Additional charge added via Express Orders.' ), - 'tax_ids': tooling_taxes, + 'tax_ids': ([(6, 0, self.tax_id.ids)] + if self.tax_id else False), })) # 5. Create — stays in draft / quotation. Sub 1: user reviews