routingOperations()->with('workCenter')->get()->map(fn ($op) => [ 'operation_code' => $op->operation_code, 'name' => $op->name, 'sequence' => $op->sequence, 'work_center' => $op->workCenter?->name, 'setup_hours' => (float) $op->setup_time_hours, 'run_hours' => (float) $op->run_time_hours, ])->values()->all(); return ReleasePackage::create([ 'business_id' => $businessId, 'line_revision_id' => $revision->id, 'mbom_id' => $mbom->id, 'cost_scenario_id' => $costScenario?->id, 'package_code' => $packageCode, 'name' => $name, 'status' => 'draft', 'mbom_snapshot' => $this->bomService->snapshot($mbom), 'routing_snapshot' => $routing, 'cost_snapshot' => $costScenario ? [ 'scenario_id' => $costScenario->id, 'total_cost' => (float) $costScenario->total_cost, 'material' => (float) $costScenario->total_material_cost, 'labor' => (float) $costScenario->total_labor_cost, 'machine' => (float) $costScenario->total_machine_cost, ] : null, ]); } public function requestSignoffs(ReleasePackage $package, array $roles = ['engineering', 'manufacturing', 'quality']): void { foreach ($roles as $role) { ReleaseSignoff::firstOrCreate( ['release_package_id' => $package->id, 'signoff_role' => $role], ['business_id' => $package->business_id, 'status' => 'pending'] ); } $package->update(['status' => 'pending_approval']); } public function signoff(ReleasePackage $package, string $role, int $userId, string $status = 'approved', ?string $notes = null): void { ReleaseSignoff::where('release_package_id', $package->id) ->where('signoff_role', $role) ->update([ 'signer_id' => $userId, 'signer_name' => auth()->user()?->user_full_name ?? auth()->user()?->username, 'status' => $status, 'signed_at' => now(), 'notes' => $notes, ]); } public function release(ReleasePackage $package, int $userId): ReleasePackage { $pending = ReleaseSignoff::where('release_package_id', $package->id) ->where('status', '!=', 'approved') ->exists(); if ($pending) { throw new \RuntimeException('All signoffs must be approved before release.'); } return DB::transaction(function () use ($package, $userId) { $package->update([ 'status' => 'released', 'released_by' => $userId, 'released_at' => now(), ]); $package->lineRevision->update([ 'status' => 'released', 'released_by' => $userId, 'released_at' => now(), ]); if ($package->mbom) { $package->mbom->update([ 'status' => 'released', 'released_by' => $userId, 'released_at' => now(), ]); } return $package->fresh(); }); } public function createWorkOrder( ReleasePackage $package, string $workOrderNumber, float $plannedQty = 1, ?int $locationId = null ): ManufacturingWorkOrder { if ($package->status !== 'released') { throw new \RuntimeException('Release package must be released before creating work orders.'); } return ManufacturingWorkOrder::create([ 'business_id' => $package->business_id, 'release_package_id' => $package->id, 'location_id' => $locationId, 'work_order_number' => $workOrderNumber, 'planned_quantity' => $plannedQty, 'status' => 'planned', 'bom_snapshot' => $package->mbom_snapshot, 'routing_snapshot' => $package->routing_snapshot, ]); } }