docs(jobs): split fp.job §5.1 fields by module ownership (Task 1.4)
Originally Task 1.4 was to add all spec §5.1 extension fields to fp.job in core. The dependency-graph audit during implementation revealed that 6 of those fields point to models in dependent modules (configurator, quality, portal, logistics, bridge_mrp). Adding them in core would invert the dependency graph. Spec §5.1 now has a Module column. Core-safe fields stay in fusion_plating/models/fp_job.py; cross-module fields are deferred to their owning modules via _inherit = 'fp.job' in Phase 2. Plan Task 1.4 narrative updated to reflect the reduced scope. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -453,7 +453,10 @@ Create `fusion_plating/data/fp_job_sequences.xml`:
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<!-- noupdate="1" is REQUIRED — without it, every -u fusion_plating
|
||||
resets number_next back to 1, which corrupts the live sequence
|
||||
on every module update. Matches the convention in fp_sequence_data.xml. -->
|
||||
<odoo noupdate="1">
|
||||
<!-- Sequence for fp.job. Format: WH/JOB/00001 onwards.
|
||||
Migrated mrp.production records keep their WH/MO/... names. -->
|
||||
<record id="seq_fp_job" model="ir.sequence">
|
||||
@@ -513,11 +516,20 @@ Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>"
|
||||
|
||||
---
|
||||
|
||||
### Task 1.4: Add SO/origin/extension fields to `fp.job`
|
||||
### Task 1.4: Add core-safe extension fields to `fp.job`
|
||||
|
||||
The full field list from spec §5.1 — added in chunks so each commit is reviewable.
|
||||
**Scope reduction (2026-04-25):** Originally this task added all spec §5.1 fields.
|
||||
But the dependency audit during Task 1.4 implementation revealed that 6 of those
|
||||
fields point to models in modules that depend on `fusion_plating` core (configurator,
|
||||
quality, portal, logistics, bridge_mrp). Adding them in core would invert the
|
||||
dependency graph. **Per the updated spec §5.1**, those fields are deferred to their
|
||||
owning modules via `_inherit = 'fp.job'` and re-bundled by `fusion_plating_jobs` in
|
||||
Phase 2.
|
||||
|
||||
- [ ] **Step 1: Add SO + recipe + portal/delivery fields**
|
||||
This task now lands ONLY the fields whose target models are reachable from core's
|
||||
existing `depends` (sale_management → sale → account, and our own process.node):
|
||||
|
||||
- [ ] **Step 1: Add SO + recipe core-safe fields**
|
||||
|
||||
Modify `fusion_plating/models/fp_job.py` — add fields after `company_id`:
|
||||
|
||||
@@ -528,19 +540,6 @@ Modify `fusion_plating/models/fp_job.py` — add fields after `company_id`:
|
||||
'job_id', 'line_id',
|
||||
string='Source SO Lines',
|
||||
)
|
||||
part_catalog_id = fields.Many2one(
|
||||
'fp.part.catalog',
|
||||
string='Part',
|
||||
index=True,
|
||||
)
|
||||
coating_config_id = fields.Many2one(
|
||||
'fp.coating.config',
|
||||
string='Coating Configuration',
|
||||
)
|
||||
customer_spec_id = fields.Many2one(
|
||||
'fusion.plating.customer.spec',
|
||||
string='Customer Spec',
|
||||
)
|
||||
recipe_id = fields.Many2one(
|
||||
'fusion.plating.process.node',
|
||||
string='Recipe',
|
||||
@@ -551,26 +550,22 @@ Modify `fusion_plating/models/fp_job.py` — add fields after `company_id`:
|
||||
string='Start at Node',
|
||||
help='Rework: start the job at this recipe node (skip earlier).',
|
||||
)
|
||||
portal_job_id = fields.Many2one(
|
||||
'fusion.plating.portal.job',
|
||||
string='Portal Job',
|
||||
)
|
||||
delivery_id = fields.Many2one(
|
||||
'fusion.plating.delivery',
|
||||
string='Delivery',
|
||||
)
|
||||
invoice_ids = fields.Many2many(
|
||||
'account.move',
|
||||
'fp_job_account_move_rel',
|
||||
'job_id', 'move_id',
|
||||
string='Invoices',
|
||||
)
|
||||
qc_check_id = fields.Many2one(
|
||||
'fp.quality.check',
|
||||
string='Active QC Check',
|
||||
)
|
||||
```
|
||||
|
||||
**Deferred to bridge modules (DO NOT add in this task):**
|
||||
- `part_catalog_id`, `coating_config_id` → owned by `fusion_plating_configurator`
|
||||
- `customer_spec_id` → owned by `fusion_plating_quality`
|
||||
- `portal_job_id` → owned by `fusion_plating_portal`
|
||||
- `delivery_id` → owned by `fusion_plating_logistics`
|
||||
- `qc_check_id` → owned by `fusion_plating_jobs` (Phase 2; the underlying model
|
||||
`fusion.plating.quality.check` currently lives in `fusion_plating_bridge_mrp`)
|
||||
|
||||
- [ ] **Step 2: Add cost rollup fields (computed)**
|
||||
|
||||
Append:
|
||||
|
||||
@@ -96,50 +96,57 @@ process tree with cost/time aggregates.
|
||||
|
||||
Replaces `mrp.production` for plating jobs. One record per shop-floor job.
|
||||
|
||||
| Field | Type | Notes |
|
||||
|---|---|---|
|
||||
| `name` | Char | Sequence: `WH/JOB/00033`. The legacy "WH/MO/00033" labels stay only on migrated records (see §7). |
|
||||
| `state` | Selection | `draft`, `confirmed`, `in_progress`, `done`, `cancelled`, `on_hold` |
|
||||
| `partner_id` | Many2one(res.partner) | Customer; copied from SO |
|
||||
| `product_id` | Many2one(product.product) | Reference part product (for inventory only) |
|
||||
| `part_catalog_id` | Many2one(fp.part.catalog) | The actual part being plated; primary identifier |
|
||||
| `qty` | Float | Quantity to plate |
|
||||
| `qty_done` | Float | Quantity completed |
|
||||
| `qty_scrapped` | Float | Quantity scrapped (rolled up from holds) |
|
||||
| `date_deadline` | Datetime | Promised completion date |
|
||||
| `date_planned_start` | Datetime | Planned start |
|
||||
| `date_started` | Datetime | Actual start (first step start) |
|
||||
| `date_finished` | Datetime | Actual completion |
|
||||
| `origin` | Char | SO name for traceability |
|
||||
| `sale_order_id` | Many2one(sale.order) | Source SO |
|
||||
| `sale_order_line_ids` | Many2many(sale.order.line) | Lines that fed this job (group_tag collapse) |
|
||||
| `recipe_id` | Many2one(fusion.plating.process.node) | The recipe template used |
|
||||
| `step_ids` | One2many(fp.job.step, job_id) | The operations |
|
||||
| `step_count` | Integer | Computed |
|
||||
| `step_done_count` | Integer | Computed |
|
||||
| `step_progress_pct` | Float | Computed: `step_done_count / step_count * 100` |
|
||||
| `current_step_id` | Many2one(fp.job.step) | The operation currently in progress (or next ready) |
|
||||
| `coating_config_id` | Many2one(fp.coating.config) | The coating spec |
|
||||
| `facility_id` | Many2one(fp.facility) | Hard gate at confirm |
|
||||
| `manager_id` | Many2one(res.users) | Plating manager |
|
||||
| `priority` | Selection | `low`, `normal`, `high`, `rush` (operator-relevant ordering) |
|
||||
| `customer_spec_id` | Many2one(fp.customer.spec) | Optional spec |
|
||||
| `portal_job_id` | Many2one(fp.portal.job) | Customer portal binding (renamed from `x_fc_portal_job_id`) |
|
||||
| `delivery_id` | Many2one(fp.delivery) | The shipment |
|
||||
| `invoice_ids` | Many2many(account.move) | Linked invoices |
|
||||
| `certificate_ids` | One2many(fp.certificate, job_id) | Certs generated |
|
||||
| `batch_ids` | One2many(fp.batch, job_id) | Batches that ran through |
|
||||
| `quality_hold_ids` | One2many(fp.quality.hold, job_id) | Holds raised |
|
||||
| `consumption_ids` | One2many(fp.job.consumption, job_id) | Consumables |
|
||||
| `qc_check_id` | Many2one(fp.quality.check) | Active QC check |
|
||||
| `quoted_revenue` | Monetary | From SO |
|
||||
| `actual_cost` | Monetary | Computed from steps + consumables |
|
||||
| `margin` | Monetary | Computed |
|
||||
| `margin_pct` | Float | Computed |
|
||||
| `start_at_node_id` | Many2one(fusion.plating.process.node) | Rework: start at this recipe node |
|
||||
| `override_ids` | One2many(fp.job.node.override, job_id) | Per-job opt-in/out |
|
||||
| `current_location` | Char | Computed: "Queued: Bath 3" / "In progress: Oven A" / "Ready to ship" |
|
||||
| `mail.thread, mail.activity.mixin` | Inherits | Chatter |
|
||||
**Module ownership:** `fp.job` lives in `fusion_plating` core. Cross-module fields
|
||||
(referencing models from `fusion_plating_configurator`, `_portal`, `_logistics`,
|
||||
`_quality`, `_bridge_mrp`) **cannot** live in core without inverting the dependency
|
||||
graph. Each owning module extends `fp.job` via `_inherit` to add its field. The
|
||||
Phase 2 module `fusion_plating_jobs` becomes the umbrella that pulls all the
|
||||
extensions together. Ownership is called out in the **Module** column below.
|
||||
|
||||
| Field | Type | Module | Notes |
|
||||
|---|---|---|---|
|
||||
| `name` | Char | core | Sequence: `WH/JOB/00033`. The legacy "WH/MO/00033" labels stay only on migrated records (see §7). |
|
||||
| `state` | Selection | core | `draft`, `confirmed`, `in_progress`, `done`, `cancelled`, `on_hold` |
|
||||
| `partner_id` | Many2one(res.partner) | core | Customer; copied from SO |
|
||||
| `product_id` | Many2one(product.product) | core | Reference part product (for inventory only) |
|
||||
| `qty` | Float | core | Quantity to plate |
|
||||
| `qty_done` | Float | core | Quantity completed |
|
||||
| `qty_scrapped` | Float | core | Quantity scrapped (rolled up from holds) |
|
||||
| `date_deadline` | Datetime | core | Promised completion date |
|
||||
| `date_planned_start` | Datetime | core | Planned start |
|
||||
| `date_started` | Datetime | core | Actual start (first step start) |
|
||||
| `date_finished` | Datetime | core | Actual completion |
|
||||
| `origin` | Char | core | SO name for traceability |
|
||||
| `sale_order_id` | Many2one(sale.order) | core | Source SO (sale_management is in core depends) |
|
||||
| `sale_order_line_ids` | Many2many(sale.order.line) | core | Lines that fed this job (group_tag collapse) |
|
||||
| `recipe_id` | Many2one(fusion.plating.process.node) | core | The recipe template used |
|
||||
| `step_ids` | One2many(fp.job.step, job_id) | core | The operations |
|
||||
| `step_count` | Integer | core | Computed |
|
||||
| `step_done_count` | Integer | core | Computed |
|
||||
| `step_progress_pct` | Float | core | Computed: `step_done_count / step_count * 100` |
|
||||
| `current_step_id` | Many2one(fp.job.step) | core | The operation currently in progress (or next ready) |
|
||||
| `facility_id` | Many2one(fusion.plating.facility) | core | Hard gate at confirm |
|
||||
| `manager_id` | Many2one(res.users) | core | Plating manager |
|
||||
| `priority` | Selection | core | `low`, `normal`, `high`, `rush` (operator-relevant ordering) |
|
||||
| `invoice_ids` | Many2many(account.move) | core | Linked invoices (account is reachable via sale_management → sale → account) |
|
||||
| `quoted_revenue` | Monetary | core | From SO |
|
||||
| `actual_cost` | Monetary | core | Computed from steps + consumables |
|
||||
| `margin` | Monetary | core | Computed |
|
||||
| `margin_pct` | Float | core | Computed |
|
||||
| `start_at_node_id` | Many2one(fusion.plating.process.node) | core | Rework: start at this recipe node |
|
||||
| `current_location` | Char | core | Computed: "Queued: Bath 3" / "In progress: Oven A" / "Ready to ship" |
|
||||
| `mail.thread, mail.activity.mixin` | Inherits | core | Chatter |
|
||||
| `part_catalog_id` | Many2one(fp.part.catalog) | **`fusion_plating_configurator`** (`_inherit = 'fp.job'`) | The actual part being plated; primary identifier |
|
||||
| `coating_config_id` | Many2one(fp.coating.config) | **`fusion_plating_configurator`** | The coating spec |
|
||||
| `customer_spec_id` | Many2one(fusion.plating.customer.spec) | **`fusion_plating_quality`** | Optional spec |
|
||||
| `portal_job_id` | Many2one(fusion.plating.portal.job) | **`fusion_plating_portal`** | Customer portal binding |
|
||||
| `delivery_id` | Many2one(fusion.plating.delivery) | **`fusion_plating_logistics`** | The shipment |
|
||||
| `qc_check_id` | Many2one(fusion.plating.quality.check) | **`fusion_plating_jobs`** (Phase 2) | Active QC check; model lives in current bridge_mrp, will move to jobs module |
|
||||
| `certificate_ids` | One2many(fp.certificate, job_id) | **`fusion_plating_certificates`** | Certs generated |
|
||||
| `batch_ids` | One2many(fp.batch, job_id) | **`fusion_plating_batch`** | Batches that ran through |
|
||||
| `quality_hold_ids` | One2many(fp.quality.hold, job_id) | **`fusion_plating_quality`** | Holds raised |
|
||||
| `consumption_ids` | One2many(fp.job.consumption, job_id) | **`fusion_plating_jobs`** (Phase 2) | Consumables |
|
||||
| `override_ids` | One2many(fp.job.node.override, job_id) | **`fusion_plating_jobs`** (Phase 2) | Per-job opt-in/out |
|
||||
|
||||
**State machine:**
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user