feat(jobs): finish original plan — Job Margin, polish, legacy hide

Three batched changes that close out the original 10-phase
migration plan.

1. Phase 5 — Job Margin report bound to fp.job (replaces the
   mrp.production-bound report_wo_margin). Per-step labour cost
   table + margin summary using existing fp.job.step.cost_total
   from Phase 1.

2. Polish:
   - Real implementations for fp.job.step.button_pause,
     button_skip, button_cancel (was NotImplementedError stubs).
     button_pause closes the open timelog and sums duration_actual,
     mirroring button_finish; button_skip/cancel transition state
     with UserError guards.
   - Explicit ondelete= policies on fp.job's cross-module Many2ones
     (part_catalog/coating restrict, customer_spec/portal/delivery
     set null) — was implicit set null.
   - Standard Nexa Systems author/website/maintainer/support block
     on fusion_plating_jobs manifest, suppressing the install
     warning.

3. Legacy hide:
   - New 'Plating Legacy Menus' group (group_fusion_plating_legacy_menus)
     — nobody in it by default.
   - Old shopfloor Manager Desk + Plant Overview + Tablet Station
     menus restricted to that group, hiding them from operators
     now that the native equivalents under 'Plating Jobs (Native)'
     exist. (Note: ir.ui.menu uses group_ids in Odoo 19, not the
     deprecated groups_id alias.)

Manifest 19.0.2.4.0 → 19.0.3.0.0. fusion_plating_shopfloor added
to depends so the legacy menu xmlid references resolve at install
time.

Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-04-25 04:49:44 -04:00
parent f8ad224b1a
commit 7f84e66b72
8 changed files with 245 additions and 1 deletions

View File

@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="action_report_fp_job_margin" model="ir.actions.report">
<field name="name">Job Margin Report</field>
<field name="model">fp.job</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">fusion_plating_jobs.report_fp_job_margin_template</field>
<field name="report_file">fusion_plating_jobs.report_fp_job_margin_template</field>
<field name="print_report_name">'Job Margin - %s' % (object.name or '').replace('/', '-')</field>
<field name="binding_model_id" ref="fusion_plating.model_fp_job"/>
<field name="binding_type">report</field>
</record>
<template id="report_fp_job_margin_template">
<t t-call="web.html_container">
<t t-foreach="rows" t-as="row">
<t t-call="web.external_layout">
<div class="page">
<h2>Job Margin — <span t-esc="row['job'].name"/></h2>
<table class="table table-sm" style="margin-top: 1em; max-width: 600px;">
<tr><th>Customer</th><td><span t-esc="row['job'].partner_id.name"/></td></tr>
<tr><th>Recipe</th><td><span t-esc="row['job'].recipe_id.name or '-'"/></td></tr>
<tr><th>Quantity</th><td><span t-esc="row['job'].qty"/></td></tr>
<tr><th>Status</th><td><span t-esc="row['job'].state"/></td></tr>
</table>
<h3 style="margin-top: 1.5em;">Step Breakdown</h3>
<table class="table table-sm table-bordered">
<thead>
<tr>
<th>#</th>
<th>Step</th>
<th>Work Centre</th>
<th class="text-end">Expected (min)</th>
<th class="text-end">Actual (min)</th>
<th class="text-end">Rate / hr</th>
<th class="text-end">Cost</th>
</tr>
</thead>
<tbody>
<t t-foreach="row['steps']" t-as="step">
<tr>
<td><span t-esc="step['sequence']"/></td>
<td><span t-esc="step['name']"/></td>
<td><span t-esc="step['work_centre']"/></td>
<td class="text-end"><span t-esc="step['duration_expected']"/></td>
<td class="text-end"><span t-esc="step['duration_actual']"/></td>
<td class="text-end"><span t-field="step['rate']" t-options="{'widget': 'monetary', 'display_currency': row['job'].currency_id}"/></td>
<td class="text-end"><span t-field="step['cost']" t-options="{'widget': 'monetary', 'display_currency': row['job'].currency_id}"/></td>
</tr>
</t>
<tr style="font-weight: bold; background: #f3f3f3;">
<td colspan="3">Totals</td>
<td></td>
<td class="text-end"><span t-esc="row['total_minutes']"/></td>
<td></td>
<td class="text-end"><span t-field="row['total_labour']" t-options="{'widget': 'monetary', 'display_currency': row['job'].currency_id}"/></td>
</tr>
</tbody>
</table>
<h3 style="margin-top: 1.5em;">Margin Summary</h3>
<table class="table table-sm" style="max-width: 400px;">
<tr><th>Quoted Revenue</th><td class="text-end"><span t-field="row['quoted_revenue']" t-options="{'widget': 'monetary', 'display_currency': row['job'].currency_id}"/></td></tr>
<tr><th>Actual Cost</th><td class="text-end"><span t-field="row['actual_cost']" t-options="{'widget': 'monetary', 'display_currency': row['job'].currency_id}"/></td></tr>
<tr style="font-weight: bold;"><th>Margin</th><td class="text-end"><span t-field="row['margin']" t-options="{'widget': 'monetary', 'display_currency': row['job'].currency_id}"/></td></tr>
<tr><th>Margin %</th><td class="text-end"><span t-esc="round(row['margin_pct'], 1)"/>%</td></tr>
</table>
</div>
</t>
</t>
</t>
</template>
</odoo>