Files
Odoo-Modules/fusion_plating/fusion_plating_jobs/models/fp_job_step.py
gsinghpal d9f58b9851 changes
2026-04-26 15:05:17 -04:00

91 lines
3.4 KiB
Python

# -*- coding: utf-8 -*-
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
#
# Real implementations for the state-machine action stubs that
# fusion_plating core's fp.job.step shipped as NotImplementedError
# placeholders. Per spec §5.2 state machine.
from odoo import _, fields, models
from odoo.exceptions import UserError
class FpJobStep(models.Model):
_inherit = 'fp.job.step'
def button_start(self):
"""Override — soft gate when parts haven't been received yet.
Doesn't block (parts could be in-transit late, manager wants
the shop to start prep regardless), but posts a chatter warning
on the job so the audit trail captures premature starts.
"""
result = super().button_start()
for step in self:
so = step.job_id.sale_order_id
if not so:
continue
recv = so.x_fc_receiving_status if (
'x_fc_receiving_status' in so._fields
) else None
if recv in (False, None, 'not_received'):
step.job_id.message_post(body=_(
'Step "%(step)s" started before parts were received '
'(SO %(so)s — receiving status: %(status)s). '
'Confirm the parts are physically on the floor before '
'continuing.'
) % {
'step': step.name,
'so': so.name or '',
'status': recv or 'unknown',
})
return result
def button_pause(self):
"""Pause an in-progress step (operator break, end of shift).
Closes the open timelog row, sums duration_actual, transitions
state to 'paused'. button_start re-opens a fresh timelog when
the operator resumes.
"""
for step in self:
if step.state != 'in_progress':
raise UserError(_(
"Step '%s' is in state '%s' — only in-progress steps can pause."
) % (step.name, step.state))
now = fields.Datetime.now()
open_log = step.time_log_ids.filtered(lambda l: not l.date_finished)
if open_log:
open_log.write({'date_finished': now})
step.state = 'paused'
step.duration_actual = sum(step.time_log_ids.mapped('duration_minutes'))
return True
def button_skip(self):
"""Skip a pending/ready step (e.g. opt-in step the planner
decided not to activate for this job).
"""
for step in self:
if step.state not in ('pending', 'ready'):
raise UserError(_(
"Step '%s' is in state '%s' — only pending/ready steps can be skipped."
) % (step.name, step.state))
step.state = 'skipped'
return True
def button_cancel(self):
"""Cancel a single step. Use fp.job.action_cancel to cancel
the whole job.
"""
for step in self:
if step.state == 'done':
raise UserError(_(
"Step '%s' is done — cannot cancel."
) % step.name)
if step.state == 'cancelled':
raise UserError(_(
"Step '%s' is already cancelled."
) % step.name)
step.state = 'cancelled'
return True