feat(fp.job): migration 19.0.11.0.0 — backfill new states (Task 21)
Idempotent post-migrate that moves mid-flight in_progress jobs whose recipe steps are all terminal into the appropriate new state: - draft cert exists → awaiting_cert - no cert required → awaiting_ship done jobs left alone (historically completed, already shipped). Card_state + mini_timeline_json recomputed for affected rows so the plant kanban renders correctly on first page load. Version bump 19.0.10.31.0 → 19.0.11.0.0 triggers the migration on -u. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||||
{
|
{
|
||||||
'name': 'Fusion Plating — Native Jobs',
|
'name': 'Fusion Plating — Native Jobs',
|
||||||
'version': '19.0.10.31.0',
|
'version': '19.0.11.0.0',
|
||||||
'category': 'Manufacturing/Plating',
|
'category': 'Manufacturing/Plating',
|
||||||
'summary': 'Native plating job model — replaces mrp.production / mrp.workorder bridge.',
|
'summary': 'Native plating job model — replaces mrp.production / mrp.workorder bridge.',
|
||||||
'author': 'Nexa Systems Inc.',
|
'author': 'Nexa Systems Inc.',
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2026 Nexa Systems Inc.
|
||||||
|
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||||
|
"""Backfill new awaiting_cert / awaiting_ship states for mid-flight jobs.
|
||||||
|
|
||||||
|
Spec: docs/superpowers/specs/2026-05-25-post-shop-cert-shipping-job-states-design.md
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
- in_progress + all steps terminal + draft cert exists → awaiting_cert
|
||||||
|
- in_progress + all steps terminal + no cert required → awaiting_ship
|
||||||
|
- done jobs LEFT ALONE — historically completed (already shipped)
|
||||||
|
|
||||||
|
Idempotent: re-running on a fresh upgrade is a no-op because no
|
||||||
|
in_progress job will match the all-terminal predicate after the first
|
||||||
|
run. Pass 1 and Pass 2 are mutually exclusive (the cert-existence
|
||||||
|
sub-queries are inverses).
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from odoo import api, SUPERUSER_ID
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def migrate(cr, version):
|
||||||
|
"""Post-migrate entrypoint — called by Odoo after the module's
|
||||||
|
XML/Python loads on -u of fusion_plating_jobs."""
|
||||||
|
|
||||||
|
# ---- Pass 1: in_progress + all-terminal + draft cert → awaiting_cert
|
||||||
|
cr.execute("""
|
||||||
|
UPDATE fp_job
|
||||||
|
SET state = 'awaiting_cert'
|
||||||
|
WHERE id IN (
|
||||||
|
SELECT j.id
|
||||||
|
FROM fp_job j
|
||||||
|
JOIN fp_job_step s ON s.job_id = j.id
|
||||||
|
WHERE j.state = 'in_progress'
|
||||||
|
GROUP BY j.id
|
||||||
|
HAVING count(*) FILTER (
|
||||||
|
WHERE s.state NOT IN ('done','skipped','cancelled')
|
||||||
|
) = 0
|
||||||
|
)
|
||||||
|
AND EXISTS (
|
||||||
|
SELECT 1 FROM fp_certificate c
|
||||||
|
WHERE c.x_fc_job_id = fp_job.id AND c.state = 'draft'
|
||||||
|
);
|
||||||
|
""")
|
||||||
|
n_cert = cr.rowcount
|
||||||
|
_logger.info(
|
||||||
|
"post-migrate 19.0.11.0.0: %d jobs migrated to awaiting_cert", n_cert,
|
||||||
|
)
|
||||||
|
|
||||||
|
# ---- Pass 2: in_progress + all-terminal + no cert → awaiting_ship
|
||||||
|
cr.execute("""
|
||||||
|
UPDATE fp_job
|
||||||
|
SET state = 'awaiting_ship'
|
||||||
|
WHERE id IN (
|
||||||
|
SELECT j.id
|
||||||
|
FROM fp_job j
|
||||||
|
JOIN fp_job_step s ON s.job_id = j.id
|
||||||
|
WHERE j.state = 'in_progress'
|
||||||
|
GROUP BY j.id
|
||||||
|
HAVING count(*) FILTER (
|
||||||
|
WHERE s.state NOT IN ('done','skipped','cancelled')
|
||||||
|
) = 0
|
||||||
|
)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM fp_certificate c
|
||||||
|
WHERE c.x_fc_job_id = fp_job.id
|
||||||
|
AND c.state IN ('draft', 'issued')
|
||||||
|
);
|
||||||
|
""")
|
||||||
|
n_ship = cr.rowcount
|
||||||
|
_logger.info(
|
||||||
|
"post-migrate 19.0.11.0.0: %d jobs migrated to awaiting_ship", n_ship,
|
||||||
|
)
|
||||||
|
|
||||||
|
# ---- Card_state recompute for affected rows (stored compute) ----
|
||||||
|
if n_cert or n_ship:
|
||||||
|
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||||
|
affected = env['fp.job'].search([
|
||||||
|
('state', 'in', ('awaiting_cert', 'awaiting_ship')),
|
||||||
|
])
|
||||||
|
# Bust cache then read-to-recompute via @api.depends.
|
||||||
|
affected.invalidate_recordset(['card_state', 'mini_timeline_json'])
|
||||||
|
affected.mapped('card_state')
|
||||||
|
affected.mapped('mini_timeline_json')
|
||||||
|
_logger.info(
|
||||||
|
"post-migrate 19.0.11.0.0: card_state recomputed on %d jobs",
|
||||||
|
len(affected),
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user