feat(configurator): 'Add From Prior SO' sub-wizard for repeat orders
Task B4. New fp.add.from.so.wizard transient model: given the current direct-order wizard + customer, lists the customer's prior confirmed sale orders, lets the estimator tick source lines, and clones them into fp.direct.order.line rows (part, coating, treatments, qty, price, deadline, rush, WO group, description). Button "+ Add From Prior SO" lives on the Lines tab of the main wizard, visible once the customer is picked. Sub-wizard rejects source lines that predate the new plating fields (no x_fc_part_catalog_id). Smoke-tested on odoo-entech: copying all 3 lines of S00066 onto a fresh wizard reproduces part/coating/qty/price correctly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -50,6 +50,7 @@ Provides:
|
||||
'views/fp_configurator_menu.xml',
|
||||
'views/fp_sale_description_template_views.xml',
|
||||
'wizard/fp_direct_order_wizard_views.xml',
|
||||
'wizard/fp_add_from_so_wizard_views.xml',
|
||||
'wizard/fp_part_catalog_import_wizard_views.xml',
|
||||
'data/fp_sale_description_template_data.xml',
|
||||
],
|
||||
|
||||
@@ -21,6 +21,8 @@ access_fp_direct_order_wizard_estimator,fp.direct.order.wizard.estimator,model_f
|
||||
access_fp_direct_order_wizard_manager,fp.direct.order.wizard.manager,model_fp_direct_order_wizard,fusion_plating.group_fusion_plating_manager,1,1,1,1
|
||||
access_fp_direct_order_line_estimator,fp.direct.order.line.estimator,model_fp_direct_order_line,fusion_plating_configurator.group_fp_estimator,1,1,1,1
|
||||
access_fp_direct_order_line_manager,fp.direct.order.line.manager,model_fp_direct_order_line,fusion_plating.group_fusion_plating_manager,1,1,1,1
|
||||
access_fp_add_from_so_wizard_estimator,fp.add.from.so.wizard.estimator,model_fp_add_from_so_wizard,fusion_plating_configurator.group_fp_estimator,1,1,1,1
|
||||
access_fp_add_from_so_wizard_manager,fp.add.from.so.wizard.manager,model_fp_add_from_so_wizard,fusion_plating.group_fusion_plating_manager,1,1,1,1
|
||||
access_fp_part_import_wizard_estimator,fp.part.catalog.import.wizard.estimator,model_fp_part_catalog_import_wizard,fusion_plating_configurator.group_fp_estimator,1,1,1,1
|
||||
access_fp_part_import_wizard_manager,fp.part.catalog.import.wizard.manager,model_fp_part_catalog_import_wizard,fusion_plating.group_fusion_plating_manager,1,1,1,1
|
||||
access_fp_customer_price_list_operator,fp.customer.price.list.operator,model_fp_customer_price_list,fusion_plating.group_fusion_plating_operator,1,0,0,0
|
||||
|
||||
|
@@ -4,4 +4,5 @@
|
||||
|
||||
from . import fp_direct_order_wizard
|
||||
from . import fp_direct_order_line
|
||||
from . import fp_add_from_so_wizard
|
||||
from . import fp_part_catalog_import_wizard
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2026 Nexa Systems Inc.
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
# Part of the Fusion Plating product family.
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class FpAddFromSoWizard(models.TransientModel):
|
||||
"""Pick lines from a prior sale.order and clone them onto the direct-order wizard.
|
||||
|
||||
Entry: a button on the direct-order wizard. The source SO list is
|
||||
filtered to the wizard's current customer. Selected SO lines are
|
||||
mapped back to fp.direct.order.line rows (part, coating, qty, price,
|
||||
treatments, description).
|
||||
"""
|
||||
_name = 'fp.add.from.so.wizard'
|
||||
_description = 'Fusion Plating - Add Lines From Prior SO'
|
||||
|
||||
direct_order_wizard_id = fields.Many2one(
|
||||
'fp.direct.order.wizard',
|
||||
required=True,
|
||||
ondelete='cascade',
|
||||
)
|
||||
partner_id = fields.Many2one(
|
||||
related='direct_order_wizard_id.partner_id', readonly=True,
|
||||
)
|
||||
source_order_id = fields.Many2one(
|
||||
'sale.order', string='Source Sales Order',
|
||||
domain="[('partner_id', '=', partner_id), ('state', 'in', ('sale', 'done'))]",
|
||||
help='Pick a prior confirmed order for this customer.',
|
||||
)
|
||||
source_line_ids = fields.Many2many(
|
||||
'sale.order.line',
|
||||
string='Lines to Copy',
|
||||
domain="[('order_id', '=', source_order_id)]",
|
||||
help='Tick the lines you want to replicate on the new direct order.',
|
||||
)
|
||||
|
||||
@api.onchange('source_order_id')
|
||||
def _onchange_source_order_id(self):
|
||||
self.source_line_ids = False
|
||||
|
||||
def action_copy_lines(self):
|
||||
self.ensure_one()
|
||||
if not self.source_order_id:
|
||||
raise UserError(_('Pick a source sales order.'))
|
||||
if not self.source_line_ids:
|
||||
raise UserError(_('Pick at least one line to copy.'))
|
||||
|
||||
Line = self.env['fp.direct.order.line']
|
||||
wizard = self.direct_order_wizard_id
|
||||
copied = 0
|
||||
for src in self.source_line_ids:
|
||||
if not src.x_fc_part_catalog_id or not src.x_fc_coating_config_id:
|
||||
# Skip SO lines that predate the plating fields
|
||||
continue
|
||||
Line.create({
|
||||
'wizard_id': wizard.id,
|
||||
'part_catalog_id': src.x_fc_part_catalog_id.id,
|
||||
'coating_config_id': src.x_fc_coating_config_id.id,
|
||||
'treatment_ids': [(6, 0, src.x_fc_treatment_ids.ids)],
|
||||
'quantity': int(src.product_uom_qty) or 1,
|
||||
'unit_price': src.price_unit or 0.0,
|
||||
'part_deadline': src.x_fc_part_deadline,
|
||||
'rush_order': src.x_fc_rush_order,
|
||||
'wo_group_tag': src.x_fc_wo_group_tag or False,
|
||||
'line_description': src.name,
|
||||
})
|
||||
copied += 1
|
||||
|
||||
if not copied:
|
||||
raise UserError(_(
|
||||
'None of the selected lines carry plating part / coating '
|
||||
'fields, so there was nothing to copy. Pick lines from a '
|
||||
'direct order created with the new wizard.'
|
||||
))
|
||||
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'fp.direct.order.wizard',
|
||||
'res_id': wizard.id,
|
||||
'view_mode': 'form',
|
||||
'target': 'new',
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="view_fp_add_from_so_wizard_form" model="ir.ui.view">
|
||||
<field name="name">fp.add.from.so.wizard.form</field>
|
||||
<field name="model">fp.add.from.so.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Add Lines From Prior SO">
|
||||
<sheet>
|
||||
<div class="oe_title">
|
||||
<h1>Copy Lines From Prior Order</h1>
|
||||
<p class="text-muted">
|
||||
Pick one of this customer's previous confirmed orders,
|
||||
then tick the lines you want replicated on the new
|
||||
direct order. Each copied row can still be edited
|
||||
before confirming.
|
||||
</p>
|
||||
</div>
|
||||
<group>
|
||||
<field name="direct_order_wizard_id" invisible="1"/>
|
||||
<field name="partner_id" readonly="1"/>
|
||||
<field name="source_order_id"
|
||||
options="{'no_create': True, 'no_open': True}"/>
|
||||
</group>
|
||||
<separator string="Lines on Source Order"/>
|
||||
<field name="source_line_ids"
|
||||
invisible="not source_order_id">
|
||||
<list>
|
||||
<field name="name"/>
|
||||
<field name="x_fc_part_catalog_id"/>
|
||||
<field name="x_fc_coating_config_id"/>
|
||||
<field name="product_uom_qty"/>
|
||||
<field name="price_unit"/>
|
||||
<field name="x_fc_part_deadline"/>
|
||||
</list>
|
||||
</field>
|
||||
</sheet>
|
||||
<footer>
|
||||
<button name="action_copy_lines"
|
||||
type="object"
|
||||
string="Copy Selected Lines"
|
||||
class="btn-primary"/>
|
||||
<button string="Cancel" special="cancel" class="btn-secondary"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -145,6 +145,23 @@ class FpDirectOrderWizard(models.TransientModel):
|
||||
self.partner_shipping_id = False
|
||||
|
||||
# ---- Actions ----
|
||||
def action_add_from_prior_so(self):
|
||||
"""Open a sub-wizard to copy lines from a prior sale.order."""
|
||||
self.ensure_one()
|
||||
if not self.partner_id:
|
||||
raise UserError(_('Pick a customer first.'))
|
||||
sub = self.env['fp.add.from.so.wizard'].create({
|
||||
'direct_order_wizard_id': self.id,
|
||||
})
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Add Lines From Prior SO'),
|
||||
'res_model': 'fp.add.from.so.wizard',
|
||||
'res_id': sub.id,
|
||||
'view_mode': 'form',
|
||||
'target': 'new',
|
||||
}
|
||||
|
||||
def action_create_order(self):
|
||||
"""Create and confirm the sale order with one SO line per wizard line."""
|
||||
self.ensure_one()
|
||||
|
||||
@@ -70,6 +70,13 @@
|
||||
|
||||
<notebook>
|
||||
<page string="Lines" name="lines">
|
||||
<div class="mb-2">
|
||||
<button name="action_add_from_prior_so"
|
||||
type="object"
|
||||
string="+ Add From Prior SO"
|
||||
class="btn-secondary"
|
||||
invisible="not partner_id"/>
|
||||
</div>
|
||||
<field name="line_ids">
|
||||
<list editable="bottom"
|
||||
decoration-warning="is_missing_info">
|
||||
|
||||
Reference in New Issue
Block a user