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 cec15193..7c0eca9d 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 @@ -94,6 +94,44 @@ class FpDirectOrderWizard(models.TransientModel): notes = fields.Text(string='Internal Notes') + # ---- Header additions (Phase A) ---- + partner_invoice_id = fields.Many2one( + 'res.partner', string='Invoice Address', + domain="['|', ('id', '=', partner_id), " + "('parent_id', '=', partner_id)]", + ) + partner_shipping_id = fields.Many2one( + 'res.partner', string='Delivery Address', + domain="['|', ('id', '=', partner_id), " + "('parent_id', '=', partner_id)]", + ) + customer_job_number = fields.Char( + string='Customer Job #', + help="Customer's internal job number for cross-referencing. " + "Appears on work orders and invoices.", + ) + planned_start_date = fields.Date( + string='Planned Start', default=fields.Date.context_today, + ) + internal_deadline = fields.Date(string='Internal Deadline') + customer_deadline = fields.Date(string='Customer Deadline') + + # ---- Lines (Phase A) ---- + line_ids = fields.One2many( + 'fp.direct.order.line', 'wizard_id', string='Order Lines', + ) + total_amount = fields.Monetary( + string='Order Total', + compute='_compute_totals', currency_field='currency_id', + ) + total_qty = fields.Integer(string='Total Qty', compute='_compute_totals') + total_line_count = fields.Integer( + string='Line Count', compute='_compute_totals', + ) + + # ---- Missing info banner ---- + missing_info_msg = fields.Char(compute='_compute_missing_info_msg') + # Description template picker — the domain is dynamically narrowed to # this part's canned descriptions first. When no part is chosen it # falls through to generic templates. @@ -120,13 +158,46 @@ class FpDirectOrderWizard(models.TransientModel): for rec in self: rec.line_subtotal = (rec.quantity or 0) * (rec.unit_price or 0.0) + @api.depends('line_ids.line_subtotal', 'line_ids.quantity') + def _compute_totals(self): + for rec in self: + rec.total_amount = sum(rec.line_ids.mapped('line_subtotal')) + rec.total_qty = sum(rec.line_ids.mapped('quantity')) + rec.total_line_count = len(rec.line_ids) + + @api.depends('line_ids.part_catalog_id', 'line_ids.coating_config_id', + 'line_ids.unit_price', 'line_ids.quantity') + def _compute_missing_info_msg(self): + for rec in self: + has_missing = False + for line in rec.line_ids: + if (not line.part_catalog_id + or not line.coating_config_id + or not line.unit_price + or not line.quantity): + has_missing = True + break + rec.missing_info_msg = ( + 'Some lines are missing quote information ' + '(part / treatment / price / qty). ' + 'Verify before confirming the order.' + if has_missing else False + ) + @api.onchange('partner_id') def _onchange_partner_id(self): - """Reset part selection when customer changes + pull invoice defaults.""" + """Reset part selection when customer changes + pull invoice defaults + addresses.""" self.part_catalog_id = False if self.partner_id and 'x_fc_default_invoice_strategy' in self.partner_id._fields: self.invoice_strategy = self.partner_id.x_fc_default_invoice_strategy or False self.deposit_percent = self.partner_id.x_fc_default_deposit_percent or 0.0 + if self.partner_id: + addrs = self.partner_id.address_get(['invoice', 'delivery']) + self.partner_invoice_id = addrs.get('invoice') or self.partner_id.id + self.partner_shipping_id = addrs.get('delivery') or self.partner_id.id + else: + self.partner_invoice_id = False + self.partner_shipping_id = False @api.onchange('description_template_id') def _onchange_description_template(self):