authorizeMaintenanceAccess('maintenance.mission.view'); $business_id = $this->maintenanceUtil->getBusinessId(); if ($request->ajax()) { $query = FieldMission::where('business_id', $business_id) ->with(['leadUser:id,first_name,last_name', 'equipment:id,name']) ->orderByDesc('id'); if ($status = $request->get('status')) { $query->where('status', $status); } $statusLabels = $this->missionService->statusLabels(); $typeLabels = $this->missionService->missionTypeLabels(); return DataTables::of($query) ->addColumn('lead_name', fn ($row) => $row->leadUser ? trim($row->leadUser->first_name.' '.($row->leadUser->last_name ?? '')) : '—') ->addColumn('equipment_name', fn ($row) => $row->equipment?->name ?? '—') ->addColumn('status_label', fn ($row) => $statusLabels[$row->status] ?? $row->status) ->addColumn('type_label', fn ($row) => $typeLabels[$row->mission_type] ?? $row->mission_type) ->addColumn('action', fn ($row) => '') ->editColumn('planned_departure_at', fn ($row) => $this->maintenanceUtil->formatDateTime($row->planned_departure_at)) ->editColumn('total_cost', fn ($row) => $this->maintenanceUtil->formatMoney($row->total_cost)) ->rawColumns(['action']) ->make(true); } return view('maintenance::field_missions.index', [ 'statuses' => $this->missionService->statusLabels(), ]); } public function show($id) { $this->authorizeMaintenanceAccess('maintenance.mission.view'); $business_id = $this->maintenanceUtil->getBusinessId(); $mission = FieldMission::where('business_id', $business_id) ->with(['crewMembers.user', 'items.sparePart', 'items.tool', 'expenses', 'equipment', 'destinationLocation', 'leadUser']) ->findOrFail($id); return view('maintenance::field_missions.show', $this->showViewData($mission)); } public function createForSource(Request $request, string $sourceType, int $sourceId) { $this->authorizeMaintenanceAccess('maintenance.mission.manage'); $business = $this->maintenanceUtil->getBusiness(); try { $mission = $this->missionService->getOrCreateForSource($business, $sourceType, $sourceId, auth()->user()); } catch (\InvalidArgumentException $e) { return $this->maintenanceUtil->jsonOrRedirect($request, false, $e->getMessage()); } return $this->maintenanceUtil->jsonOrRedirect( $request, true, __('maintenance::lang.fm_created', ['ref' => $mission->mission_number]), action([self::class, 'show'], [$mission->id]) ); } public function storeCrew(Request $request, $id) { $this->authorizeMaintenanceAccess('maintenance.mission.manage'); $mission = $this->findMission($id); $data = $request->validate([ 'user_id' => 'nullable|exists:users,id', 'person_name' => 'nullable|string|max:255', 'role' => 'nullable|string|max:50', 'is_lead' => 'nullable|boolean', 'labor_hours' => 'nullable|numeric|min:0', 'hourly_rate' => 'nullable|integer|min:0', 'per_diem' => 'nullable|integer|min:0', 'notes' => 'nullable|string', ]); if (empty($data['user_id']) && empty($data['person_name'])) { return $this->maintenanceUtil->jsonOrRedirect($request, false, __('maintenance::lang.fm_crew_required')); } $this->missionService->addCrewMember($mission, $data); return $this->maintenanceUtil->jsonOrRedirect($request, true, __('maintenance::lang.fm_crew_added')); } public function updateCrew(Request $request, int $crewId) { $this->authorizeMaintenanceAccess('maintenance.mission.manage'); $member = MissionCrewMember::whereHas('mission', fn ($q) => $q->where('business_id', $this->maintenanceUtil->getBusinessId())) ->findOrFail($crewId); $data = $request->validate([ 'user_id' => 'nullable|exists:users,id', 'person_name' => 'nullable|string|max:255', 'role' => 'nullable|string|max:50', 'is_lead' => 'nullable|boolean', 'labor_hours' => 'nullable|numeric|min:0', 'hourly_rate' => 'nullable|integer|min:0', 'per_diem' => 'nullable|integer|min:0', 'notes' => 'nullable|string', ]); $this->missionService->updateCrewMember($member, $data); return $this->maintenanceUtil->jsonOrRedirect($request, true, __('maintenance::lang.fm_crew_updated')); } public function destroyCrew(Request $request, int $crewId) { $this->authorizeMaintenanceAccess('maintenance.mission.manage'); $member = MissionCrewMember::whereHas('mission', fn ($q) => $q->where('business_id', $this->maintenanceUtil->getBusinessId())) ->findOrFail($crewId); $this->missionService->removeCrewMember($member); return $this->maintenanceUtil->jsonOrRedirect($request, true, __('maintenance::lang.fm_crew_removed')); } public function storeItem(Request $request, $id) { $this->authorizeMaintenanceAccess('maintenance.mission.issue'); $mission = $this->findMission($id); $data = $request->validate([ 'item_type' => 'required|in:spare_part,product,tool,other', 'spare_part_id' => 'nullable|exists:maintenance_spare_parts,id', 'tool_id' => 'nullable|exists:maintenance_tools,id', 'part_name' => 'nullable|string|max:255', 'quantity_issued' => 'required|numeric|min:0.001', 'location_id' => 'nullable|integer|exists:business_locations,id', 'notes' => 'nullable|string', ]); if (in_array($mission->status, ['closed', 'cancelled'], true)) { return $this->maintenanceUtil->jsonOrRedirect($request, false, __('maintenance::lang.fm_mission_closed')); } $this->missionService->addItem($mission, $data); return $this->maintenanceUtil->jsonOrRedirect($request, true, __('maintenance::lang.fm_item_added')); } public function destroyItem(Request $request, int $itemId) { $this->authorizeMaintenanceAccess('maintenance.mission.issue'); $item = MissionItem::whereHas('mission', fn ($q) => $q->where('business_id', $this->maintenanceUtil->getBusinessId())) ->findOrFail($itemId); try { $this->missionService->removeItem($item); } catch (\InvalidArgumentException $e) { return $this->maintenanceUtil->jsonOrRedirect($request, false, $e->getMessage()); } return $this->maintenanceUtil->jsonOrRedirect($request, true, __('maintenance::lang.fm_item_removed')); } public function dispatchMission(Request $request, $id) { $this->authorizeMaintenanceAccess('maintenance.mission.issue'); $mission = $this->findMission($id); try { $this->missionService->dispatch($mission, auth()->user()); } catch (\InvalidArgumentException $e) { return $this->maintenanceUtil->jsonOrRedirect($request, false, $e->getMessage()); } return $this->maintenanceUtil->jsonOrRedirect($request, true, __('maintenance::lang.fm_dispatched')); } public function recordReturns(Request $request, $id) { $this->authorizeMaintenanceAccess('maintenance.mission.issue'); $mission = $this->findMission($id); $data = $request->validate([ 'lines' => 'required|array|min:1', 'lines.*.item_id' => 'required|integer', 'lines.*.quantity_returned' => 'required|numeric|min:0', 'lines.*.quantity_lost' => 'nullable|numeric|min:0', ]); try { $this->missionService->recordReturns($mission, $data['lines']); } catch (\InvalidArgumentException $e) { return $this->maintenanceUtil->jsonOrRedirect($request, false, $e->getMessage()); } return $this->maintenanceUtil->jsonOrRedirect($request, true, __('maintenance::lang.fm_returns_saved')); } public function closeMission(Request $request, $id) { $this->authorizeMaintenanceAccess('maintenance.mission.manage'); $mission = $this->findMission($id); try { $this->missionService->closeMission($mission, auth()->user()); } catch (\InvalidArgumentException $e) { return $this->maintenanceUtil->jsonOrRedirect($request, false, $e->getMessage()); } return $this->maintenanceUtil->jsonOrRedirect($request, true, __('maintenance::lang.fm_closed')); } public function storeExpense(Request $request, $id) { $this->authorizeMaintenanceAccess('maintenance.mission.expense'); $mission = $this->findMission($id); $data = $request->validate([ 'expense_type' => 'required|string|max:50', 'description' => 'nullable|string|max:255', 'amount' => 'required|integer|min:0', 'expense_date' => 'nullable|string', 'receipt_ref' => 'nullable|string|max:100', 'vendor_name' => 'nullable|string|max:255', 'paid_by' => 'nullable|in:company,employee', 'crew_member_id' => 'nullable|integer', 'notes' => 'nullable|string', ]); if (! empty($data['expense_date'])) { $data['expense_date'] = $this->maintenanceUtil->parseInputDate($data['expense_date']); } $this->missionService->addExpense($mission, $data); return $this->maintenanceUtil->jsonOrRedirect($request, true, __('maintenance::lang.fm_expense_added')); } public function destroyExpense(Request $request, int $expenseId) { $this->authorizeMaintenanceAccess('maintenance.mission.expense'); $expense = MissionExpense::whereHas('mission', fn ($q) => $q->where('business_id', $this->maintenanceUtil->getBusinessId())) ->findOrFail($expenseId); $this->missionService->removeExpense($expense); return $this->maintenanceUtil->jsonOrRedirect($request, true, __('maintenance::lang.fm_expense_removed')); } protected function findMission($id): FieldMission { return FieldMission::where('business_id', $this->maintenanceUtil->getBusinessId())->findOrFail($id); } protected function showViewData(FieldMission $mission): array { $business_id = $mission->business_id; $mission->loadMissing('business'); $locations = $mission->source_id ? ($this->missionService->getPanelData($mission->business, $mission->source_type, (int) $mission->source_id)['locations'] ?? collect()) : $this->stockService->locationsForBusiness($business_id); return [ 'mission' => $mission, 'statuses' => $this->missionService->statusLabels(), 'mission_types' => $this->missionService->missionTypeLabels(), 'role_labels' => $this->missionService->roleLabels(), 'expense_types' => $this->missionService->expenseTypeLabels(), 'users' => $this->maintenanceUtil->getUsersDropdown($business_id), 'spare_parts' => $this->maintenanceUtil->getSparePartsDropdown($business_id), 'tools' => MaintenanceTool::where('business_id', $business_id)->where('status', 'available')->orderBy('name')->pluck('name', 'id'), 'locations' => $locations, 'can_manage' => auth()->user()->can('maintenance.mission.manage'), 'can_issue' => auth()->user()->can('maintenance.mission.issue'), 'can_expense' => auth()->user()->can('maintenance.mission.expense'), 'tms_enabled' => $this->isTmsEnabledForMission(), 'mission_shipment' => $this->findMissionShipment($mission), 'tms_statuses' => class_exists(\Modules\Tms\Utils\TmsUtil::class) ? app(\Modules\Tms\Utils\TmsUtil::class)->shipmentStatuses() : [], ]; } protected function isTmsEnabledForMission(): bool { if (! \Module::has('Tms') || ! \Module::isEnabled('Tms')) { return false; } $module_util = new \App\Utils\ModuleUtil(); $business_id = $this->maintenanceUtil->getBusinessId(); if (! $module_util->isModuleInstalled('Tms')) { return false; } if (! auth()->user()->can('tms.shipments.view') && ! auth()->user()->can('tms.shipments.create')) { return false; } if ($module_util->isSuperadminInstalled()) { return (bool) $module_util->hasThePermissionInSubscription($business_id, 'tms_module'); } return true; } protected function findMissionShipment(FieldMission $mission) { if (! class_exists(\Modules\Tms\Services\TmsMissionSyncService::class)) { return null; } return app(\Modules\Tms\Services\TmsMissionSyncService::class)->findShipmentForMission($mission); } }