feat(configurator): Phase E — SO list view uplift

E1/E2/E3/E4: list view gets new togglable columns for
- x_fc_wo_completion (e.g. '3/5'): count of completed vs total WOs
- x_fc_invoiced_amount (Monetary): sum of posted customer invoices
  minus credit notes
- x_fc_margin_amount + x_fc_margin_percent: reuses Phase D8 computes
- x_fc_is_blanket_order toggle

New sale.order.search view (sale.order.search.fp) with preset
filters: My Orders / Open / Confirmed / Done / Blanket / Has Rush /
Overdue, plus group-bys for Customer / Status / Customer Deadline.

Bumped to 19.0.7.1.0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-04-19 21:18:52 -04:00
parent 3f807d0152
commit 94eb7ef415
3 changed files with 90 additions and 1 deletions

View File

@@ -5,7 +5,7 @@
{
'name': 'Fusion Plating — Configurator',
'version': '19.0.7.0.0',
'version': '19.0.7.1.0',
'category': 'Manufacturing/Plating',
'summary': 'Quotation configurator with part catalog, coating configs, and formula-based pricing engine.',
'description': """

View File

@@ -119,6 +119,46 @@ class SaleOrder(models.Model):
compute='_compute_workorder_count',
)
# ---- Phase E: list view helpers ----
x_fc_wo_completion = fields.Char(
string='WO Progress',
compute='_compute_wo_completion',
help='Ratio of completed work orders, shown as "3/5 done".',
)
x_fc_invoiced_amount = fields.Monetary(
string='Invoiced',
compute='_compute_invoiced_amount',
currency_field='currency_id',
)
def _compute_wo_completion(self):
WO = self.env['mrp.workorder'].sudo()
for rec in self:
if not rec.name:
rec.x_fc_wo_completion = '0/0'
continue
total = WO.search_count([('production_id.origin', '=', rec.name)])
done = WO.search_count([
('production_id.origin', '=', rec.name),
('state', '=', 'done'),
])
rec.x_fc_wo_completion = '%d/%d' % (done, total) if total else '0/0'
@api.depends('invoice_ids.amount_total', 'invoice_ids.state',
'invoice_ids.move_type')
def _compute_invoiced_amount(self):
for rec in self:
posted = rec.invoice_ids.filtered(
lambda m: m.state == 'posted' and m.move_type == 'out_invoice'
)
refunds = rec.invoice_ids.filtered(
lambda m: m.state == 'posted' and m.move_type == 'out_refund'
)
rec.x_fc_invoiced_amount = (
sum(posted.mapped('amount_total'))
- sum(refunds.mapped('amount_total'))
)
def _compute_workorder_count(self):
WO = self.env['mrp.workorder'].sudo()
for rec in self:

View File

@@ -153,19 +153,68 @@
<field name="x_fc_internal_deadline" optional="show"/>
<field name="commitment_date" string="Customer Deadline" optional="show"/>
<field name="x_fc_deadline_countdown" optional="show"/>
<field name="x_fc_wo_completion" optional="show"/>
<field name="x_fc_planned_start_date" optional="hide"/>
<field name="x_fc_part_catalog_id" optional="hide"/>
<field name="x_fc_coating_config_id" optional="hide"/>
<field name="amount_total" sum="Total"/>
<field name="x_fc_invoiced_amount" sum="Invoiced" optional="hide"
widget="monetary"
options="{'currency_field': 'currency_id'}"/>
<field name="x_fc_margin_amount" sum="Margin" optional="hide"
widget="monetary"
options="{'currency_field': 'currency_id'}"/>
<field name="x_fc_margin_percent" optional="hide"
widget="percentage"/>
<field name="x_fc_is_blanket_order" optional="hide"/>
<field name="x_fc_receiving_status" widget="badge"
decoration-warning="x_fc_receiving_status == 'not_received'"
decoration-success="x_fc_receiving_status in ('received','inspected')"/>
<field name="x_fc_delivery_method" optional="hide"/>
<field name="currency_id" column_invisible="1"/>
<field name="state" widget="badge"/>
</list>
</field>
</record>
<!-- ===== Search view for Fusion Plating SO list ===== -->
<record id="view_sale_order_search_fp" model="ir.ui.view">
<field name="name">sale.order.search.fp</field>
<field name="model">sale.order</field>
<field name="arch" type="xml">
<search string="Sales Orders">
<field name="name"/>
<field name="partner_id"/>
<field name="x_fc_po_number" string="Customer PO #"/>
<field name="x_fc_customer_job_number" string="Customer Job #"/>
<filter name="my_orders" string="My Orders"
domain="[('user_id', '=', uid)]"/>
<separator/>
<filter name="open_orders" string="Open"
domain="[('state', 'in', ('draft', 'sent', 'sale'))]"/>
<filter name="confirmed" string="Confirmed"
domain="[('state', '=', 'sale')]"/>
<filter name="done" string="Done"
domain="[('state', '=', 'done')]"/>
<separator/>
<filter name="blanket_orders" string="Blanket Orders"
domain="[('x_fc_is_blanket_order', '=', True)]"/>
<filter name="rush_lines" string="Has Rush Line"
domain="[('order_line.x_fc_rush_order', '=', True)]"/>
<filter name="overdue" string="Overdue"
domain="[('commitment_date', '&lt;', context_today()), ('state', 'in', ('sale',))]"/>
<group>
<filter string="Customer" name="group_partner"
context="{'group_by': 'partner_id'}"/>
<filter string="Status" name="group_state"
context="{'group_by': 'state'}"/>
<filter string="Customer Deadline" name="group_deadline"
context="{'group_by': 'commitment_date'}"/>
</group>
</search>
</field>
</record>
<!-- ===== Window Action — Quotations (for Fusion Plating menu) ===== -->
<record id="action_fp_quotations" model="ir.actions.act_window">
<field name="name">Quotations</field>