diff --git a/fusion_plating/fusion_plating_jobs/models/fp_job.py b/fusion_plating/fusion_plating_jobs/models/fp_job.py index 7c350fc8..5e6f5730 100644 --- a/fusion_plating/fusion_plating_jobs/models/fp_job.py +++ b/fusion_plating/fusion_plating_jobs/models/fp_job.py @@ -599,17 +599,33 @@ class FpJob(models.Model): job.next_milestone_action = False job.next_milestone_label = '' continue - if job.state != 'done': + # New state machine (spec 2026-05-25). The auto-advance + # helper normally fires button_finish post-super, so we + # rarely see state='in_progress' here. When we do (e.g. + # historical jobs caught mid-migration, or jobs whose + # cert/delivery infra failed mid-transition), surface + # mark_done as a manual fallback. + if job.state == 'in_progress': job.next_milestone_action = 'mark_done' - elif job._fp_has_draft_required_certs(): + elif job.state == 'awaiting_cert': job.next_milestone_action = 'issue_certs' - elif (not job.delivery_id - or job.delivery_id.state == 'draft'): - job.next_milestone_action = 'schedule_delivery' - elif job.delivery_id.state in ('scheduled', 'in_transit'): + elif job.state == 'awaiting_ship': job.next_milestone_action = 'mark_shipped' + elif job.state == 'done': + # Legacy path — historical jobs that closed before the + # new state machine landed. Preserve the old cascade + # so their milestone buttons keep working. + if job._fp_has_draft_required_certs(): + job.next_milestone_action = 'issue_certs' + elif (not job.delivery_id + or job.delivery_id.state == 'draft'): + job.next_milestone_action = 'schedule_delivery' + elif job.delivery_id.state in ('scheduled', 'in_transit'): + job.next_milestone_action = 'mark_shipped' + else: + job.next_milestone_action = 'closed' else: - job.next_milestone_action = 'closed' + job.next_milestone_action = False job.next_milestone_label = labels.get( job.next_milestone_action, '' ) @@ -646,7 +662,10 @@ class FpJob(models.Model): 'mark_done': self.button_mark_done, 'issue_certs': self._action_open_draft_certs, 'schedule_delivery': self._action_open_draft_delivery, - 'mark_shipped': self._action_mark_active_delivery_delivered, + # Spec 2026-05-25: dispatch between the new state-machine + # path (state=awaiting_ship → button_mark_shipped) and the + # legacy delivery path (state=done + scheduled delivery). + 'mark_shipped': self._action_mark_shipped_dispatch, } fn = action_map.get(self.next_milestone_action) if not fn: @@ -734,6 +753,18 @@ class FpJob(models.Model): ) % self.delivery_id.name) return True + def _action_mark_shipped_dispatch(self): + """Dispatch the milestone-cascade 'Mark Shipped' button to the + right handler based on job state. Spec 2026-05-25: + - awaiting_ship → button_mark_shipped (new state machine) + - done + active delivery → _action_mark_active_delivery_delivered + (legacy historical path) + """ + self.ensure_one() + if self.state == 'awaiting_ship': + return self.button_mark_shipped() + return self._action_mark_active_delivery_delivered() + @api.depends( 'sale_order_id', 'delivery_id', 'portal_job_id', 'step_ids', 'step_ids.time_log_ids', 'origin', 'partner_id', @@ -1997,6 +2028,32 @@ class FpJob(models.Model): job._fp_fire_notification('job_complete') return True + def button_mark_shipped(self): + """Manual transition awaiting_ship → done. Operator-facing + button on the job form; restricted to Manager / Owner via + groups= on the view button. + + Does NOT re-run the bake/qty/QC gates — those passed when the + job first transitioned out of in_progress. This is just the + "yes, shipped" stamp. + + Future hook: delivery.action_mark_delivered will call this + automatically — out of scope for this iteration (spec 2026-05-25). + """ + for job in self: + if job.state != 'awaiting_ship': + raise UserError(_( + 'Job %s cannot be marked Shipped — state is "%s" ' + '(expected "awaiting_ship").' + ) % (job.name, job.state)) + job.state = 'done' + job.date_finished = fields.Datetime.now() + job._fp_fire_notification('job_shipped') + job.message_post(body=_( + 'Marked shipped by %s.' + ) % self.env.user.name) + return True + # ------------------------------------------------------------------ # Notifications dispatch (Phase 4) # diff --git a/fusion_plating/fusion_plating_jobs/views/fp_job_form_inherit.xml b/fusion_plating/fusion_plating_jobs/views/fp_job_form_inherit.xml index 63946950..c3f06464 100644 --- a/fusion_plating/fusion_plating_jobs/views/fp_job_form_inherit.xml +++ b/fusion_plating/fusion_plating_jobs/views/fp_job_form_inherit.xml @@ -68,7 +68,8 @@ string="Mark Shipped" class="btn-success" icon="fa-paper-plane" - invisible="next_milestone_action != 'mark_shipped'"/> + invisible="next_milestone_action != 'mark_shipped'" + groups="fusion_plating.group_fp_manager,fusion_plating.group_fp_owner"/>