resolveBusiness($request); if (! $business) { return $this->jsonError('کسب‌وکار یافت نشد.', 404); } $query = Equipment::where('business_id', $business->id) ->with(['category', 'customer:id,name,type', 'project:id,name', 'productionLine.project']) ->orderByDesc('created_at'); if ($status = $request->get('status')) { $query->where('status', $status); } if ($categoryId = $request->get('category_id')) { $query->where('category_id', $categoryId); } if ($departmentId = $request->get('department_id')) { $query->where('department_id', $departmentId); } if ($productionLineId = $request->get('production_line_id')) { $query->where('production_line_id', $productionLineId); } if ($projectId = $request->get('project_id')) { $query->whereHas('productionLine', fn ($q) => $q->where('project_id', $projectId)); } if ($search = $request->get('q')) { $query->where(function ($q) use ($search) { $q->where('name', 'like', "%{$search}%") ->orWhere('code', 'like', "%{$search}%") ->orWhere('asset_number', 'like', "%{$search}%") ->orWhere('serial_number', 'like', "%{$search}%"); }); } $paginator = $query->paginate((int) $request->get('per_page', 15)); return $this->jsonSuccess([ 'items' => $paginator->items(), 'pagination' => [ 'total' => $paginator->total(), 'per_page' => $paginator->perPage(), 'current_page' => $paginator->currentPage(), 'last_page' => $paginator->lastPage(), ], ]); } public function store(Request $request) { $business = $this->resolveBusiness($request); if (! $business) { return $this->jsonError('کسب‌وکار یافت نشد.', 404); } $data = $request->validate([ 'code' => ['required', 'string', 'max:50'], 'name' => ['required', 'string', 'max:255'], 'category_id' => ['nullable', 'exists:maintenance_equipment_categories,id'], 'project_id' => ['required', 'exists:pjt_projects,id'], 'production_line_id' => ['required', 'exists:maintenance_production_lines,id'], 'client_type' => ['required', 'string', 'in:person,factory,project'], 'customer_id' => ['nullable', 'required_if:client_type,person,factory', 'exists:contacts,id'], 'asset_number' => ['nullable', 'string'], 'manufacturer' => ['nullable', 'string'], 'model' => ['nullable', 'string'], 'serial_number' => ['nullable', 'string'], 'capacity' => ['nullable', 'string'], 'installation_date' => ['nullable', 'date'], 'warranty_expires_at' => ['nullable', 'date'], 'department_id' => ['nullable', 'integer'], 'branch_id' => ['nullable', 'integer'], 'location' => ['nullable', 'string'], 'status' => ['sometimes', 'string', 'in:active,inactive,under_maintenance,under_overhaul,retired'], 'priority' => ['sometimes', 'string', 'in:low,medium,high,critical'], 'criticality' => ['sometimes', 'string', 'in:low,medium,high'], 'risk_level' => ['sometimes', 'string', 'in:low,medium,high,critical'], 'working_hours' => ['nullable', 'numeric'], 'last_service_date' => ['nullable', 'date'], 'next_service_date' => ['nullable', 'date'], 'last_overhaul_date' => ['nullable', 'date'], 'next_overhaul_date' => ['nullable', 'date'], 'health_percentage' => ['nullable', 'numeric', 'min:0', 'max:100'], 'fixed_asset_id' => ['nullable', 'integer', 'exists:assets,id'], 'notes' => ['nullable', 'string'], ]); try { $data = $this->syncScopeFromLine($data); } catch (\InvalidArgumentException $e) { return $this->jsonError($e->getMessage(), 422); } if ($data['client_type'] === 'project') { $data['customer_id'] = null; } $line = ProductionLine::where('business_id', $business->id)->find($data['production_line_id']); if (! $line) { return $this->jsonError('خط تولید یافت نشد.', 404); } $equipment = Equipment::create(array_merge($data, [ 'business_id' => $business->id, 'created_by' => $request->user()?->id, 'status' => $data['status'] ?? 'active', ])); $this->preventiveService->generateEquipmentQrCode($business, $equipment); MaintenanceActivityService::log( $business, 'created', 'equipment', $equipment->id, "تجهیز «{$equipment->name}» ایجاد شد.", $request->user() ); MaintenanceActivityService::logEquipmentHistory( $business, $equipment->id, 'installation', 'ثبت تجهیز', "تجهیز «{$equipment->name}» در سیستم ثبت شد.", $request->user() ); return $this->jsonSuccess($equipment->load(['category', 'customer', 'project', 'productionLine.project']), 'تجهیز ایجاد شد.', 201); } public function show(Request $request, Equipment $equipment) { if (! $this->ensureBusinessOwnership($request, $equipment->business_id)) { return $this->jsonError('دسترسی ندارید.', 403); } return $this->jsonSuccess($equipment->load([ 'category', 'customer', 'project', 'fixedAsset', 'productionLine.project', 'preventiveSchedules', 'overhauls.project', 'historyEntries.performer', 'equipmentParts.sparePart', 'equipmentParts.partCatalog', 'equipmentParts.children', 'checklistTemplates.items', 'documents', 'inspectionVisits' => fn ($q) => $q->limit(10), 'serviceRecords' => fn ($q) => $q->limit(10), 'partReplacements' => fn ($q) => $q->limit(10), ])); } public function update(Request $request, Equipment $equipment) { if (! $this->ensureBusinessOwnership($request, $equipment->business_id)) { return $this->jsonError('دسترسی ندارید.', 403); } $data = $request->validate([ 'code' => ['sometimes', 'string', 'max:50'], 'name' => ['sometimes', 'string', 'max:255'], 'category_id' => ['nullable', 'exists:maintenance_equipment_categories,id'], 'project_id' => ['sometimes', 'required', 'exists:pjt_projects,id'], 'production_line_id' => ['sometimes', 'required', 'exists:maintenance_production_lines,id'], 'client_type' => ['sometimes', 'string', 'in:person,factory,project'], 'customer_id' => ['nullable', 'exists:contacts,id'], 'asset_number' => ['nullable', 'string'], 'manufacturer' => ['nullable', 'string'], 'model' => ['nullable', 'string'], 'serial_number' => ['nullable', 'string'], 'capacity' => ['nullable', 'string'], 'installation_date' => ['nullable', 'date'], 'warranty_expires_at' => ['nullable', 'date'], 'department_id' => ['nullable', 'integer'], 'branch_id' => ['nullable', 'integer'], 'location' => ['nullable', 'string'], 'status' => ['sometimes', 'string', 'in:active,inactive,under_maintenance,under_overhaul,retired'], 'priority' => ['sometimes', 'string', 'in:low,medium,high,critical'], 'criticality' => ['sometimes', 'string', 'in:low,medium,high'], 'risk_level' => ['sometimes', 'string', 'in:low,medium,high,critical'], 'working_hours' => ['nullable', 'numeric'], 'last_service_date' => ['nullable', 'date'], 'next_service_date' => ['nullable', 'date'], 'last_overhaul_date' => ['nullable', 'date'], 'next_overhaul_date' => ['nullable', 'date'], 'health_percentage' => ['nullable', 'numeric', 'min:0', 'max:100'], 'notes' => ['nullable', 'string'], 'technical_documents' => ['nullable', 'array'], 'images' => ['nullable', 'array'], 'videos' => ['nullable', 'array'], 'user_manual' => ['nullable', 'array'], ]); if (isset($data['production_line_id']) || isset($data['project_id'])) { $merged = array_merge([ 'project_id' => $equipment->project_id, 'production_line_id' => $equipment->production_line_id, ], $data); try { $data = array_merge($data, $this->syncScopeFromLine($merged)); } catch (\InvalidArgumentException $e) { return $this->jsonError($e->getMessage(), 422); } } if (($data['client_type'] ?? $equipment->client_type) === 'project') { $data['customer_id'] = null; } $equipment->update(array_merge($data, ['updated_by' => $request->user()?->id])); MaintenanceActivityService::log( $business = $this->resolveBusiness($request), 'updated', 'equipment', $equipment->id, "تجهیز «{$equipment->name}» ویرایش شد.", $request->user() ); return $this->jsonSuccess($equipment->fresh()->load(['category', 'customer', 'project', 'productionLine.project']), 'تجهیز به‌روزرسانی شد.'); } public function destroy(Request $request, Equipment $equipment) { if (! $this->ensureBusinessOwnership($request, $equipment->business_id)) { return $this->jsonError('دسترسی ندارید.', 403); } $equipment->delete(); return $this->jsonSuccess(null, 'تجهیز حذف شد.'); } public function scan(Request $request, string $qrCode) { $business = $this->resolveBusiness($request); if (! $business) { return $this->jsonError('کسب‌وکار یافت نشد.', 404); } $equipment = Equipment::where('business_id', $business->id) ->where('qr_code', $qrCode) ->with(['category', 'historyEntries', 'workOrders' => fn ($q) => $q->whereIn('status', ['open', 'in_progress'])->limit(5)]) ->first(); if (! $equipment) { return $this->jsonError('تجهیز یافت نشد.', 404); } return $this->jsonSuccess($equipment); } }