feat(configurator): add header fields + line O2M to direct order wizard
Task A2 of the direct-order-wizard rewrite. Adds SO-header fields for customer job #, three deadlines (planned start / internal / customer), bill-to / ship-to address pickers, the line_ids O2M linking to fp.direct.order.line, computed order totals, and a missing-info warning banner. Partner onchange now also seeds default addresses. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user