feat(sub12b): fp.job.step + fp.job — rack/move/traveller fields
fp.job.step:
+ requires_rack_assignment (related from recipe_node_id)
+ requires_transition_form (related)
+ move_ids (O2M from_step_id), incoming_move_ids (O2M to_step_id)
+ is_racked (compute, stored, depends rack_id) — drives tablet
rack-vs-parts greyed-button guard
+ qty_at_step_start, qty_at_step_finish (advanced by move commits)
NOTE: existing 'rack_id' field is reused as the 'current rack' pointer
(already there on line 95). Adding requires_rack_assignment as a
related from recipe_node_id for runtime gate evaluation.
fp.job:
+ qty_received, qty_visual_inspection_rejects, qty_rework
+ special_requirements (Text — paper traveller header)
+ active_timer_ids (filtered O2M, depends on Task 7's state field)
+ move_ids (O2M to fp.job.step.move)
All additive. No removed fields. Existing battle tests unaffected.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -186,6 +186,40 @@ class FpJob(models.Model):
|
||||
'job_id',
|
||||
string='Steps',
|
||||
)
|
||||
|
||||
# ===== Sub 12b — traveller header + active timer ========================
|
||||
# Header counters mirror the paper traveller's "Qty Rec." / "VIS INSP."
|
||||
# / "Rework" columns (screens 16-18). Sub 12c's traveller report pulls
|
||||
# these into the printed header.
|
||||
qty_received = fields.Integer(
|
||||
string='Qty Received',
|
||||
help='Paper traveller "Qty Rec." column.',
|
||||
)
|
||||
qty_visual_inspection_rejects = fields.Integer(
|
||||
string='Visual Insp Rejects',
|
||||
help='Paper traveller "VIS INSP." column.',
|
||||
)
|
||||
qty_rework = fields.Integer(
|
||||
string='Qty Sent to Rework',
|
||||
help='Paper traveller "Rework" column.',
|
||||
)
|
||||
special_requirements = fields.Text(
|
||||
string='Special Requirements',
|
||||
help='Long free-form spec text from customer; printed on the '
|
||||
'traveller header (Sub 12c).',
|
||||
)
|
||||
active_timer_ids = fields.One2many(
|
||||
'fp.job.step.timelog',
|
||||
'job_id',
|
||||
string='Active Timers',
|
||||
domain=[('state', 'in', ('running', 'paused'))],
|
||||
help='Sub 12b — used by tablet for live timer badges. Filtered '
|
||||
'on state by Task 7\'s state field.',
|
||||
)
|
||||
move_ids = fields.One2many(
|
||||
'fp.job.step.move', 'job_id',
|
||||
string='Move Log',
|
||||
)
|
||||
# step_count + step_done_count are stored (drive list views / stat
|
||||
# buttons in Task 1.8). step_progress_pct stays non-stored — it's a
|
||||
# cheap derivative. Odoo flags as inconsistent when stored and
|
||||
|
||||
@@ -139,6 +139,41 @@ class FpJobStep(models.Model):
|
||||
'step in this job is done/skipped/cancelled.',
|
||||
)
|
||||
|
||||
# ===== Sub 12b — chain-of-custody + rack awareness =====================
|
||||
# Note: rack_id (line 95 above) already exists — reused as the "current
|
||||
# rack on this step" pointer. Sub 12b builds the runtime guards on top.
|
||||
requires_rack_assignment = fields.Boolean(
|
||||
related='recipe_node_id.requires_rack_assignment',
|
||||
store=True,
|
||||
help='If True, the Move Parts dialog requires a rack to be '
|
||||
'assigned to the parts before the move commits. Snapshot '
|
||||
'from the recipe step at job creation.',
|
||||
)
|
||||
requires_transition_form = fields.Boolean(
|
||||
related='recipe_node_id.requires_transition_form',
|
||||
store=True,
|
||||
)
|
||||
move_ids = fields.One2many(
|
||||
'fp.job.step.move', 'from_step_id',
|
||||
string='Outgoing Moves',
|
||||
)
|
||||
incoming_move_ids = fields.One2many(
|
||||
'fp.job.step.move', 'to_step_id',
|
||||
string='Incoming Moves',
|
||||
)
|
||||
is_racked = fields.Boolean(
|
||||
string='Racked', compute='_compute_is_racked', store=True,
|
||||
help='True when rack_id is set — drives the tablet rack-vs-parts '
|
||||
'button-state guard (Move Parts greys out).',
|
||||
)
|
||||
qty_at_step_start = fields.Integer(string='Qty at Step Start')
|
||||
qty_at_step_finish = fields.Integer(string='Qty at Step Finish')
|
||||
|
||||
@api.depends('rack_id')
|
||||
def _compute_is_racked(self):
|
||||
for rec in self:
|
||||
rec.is_racked = bool(rec.rack_id)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Cost rollup (Task 1.6)
|
||||
# cost_per_hour comes from fp.work.centre (Task 1.2 added it there).
|
||||
|
||||
Reference in New Issue
Block a user