[$sometimes, 'exists:maintenance_equipment_parts,id'], 'spare_part_id' => ['nullable', 'exists:maintenance_spare_parts,id'], 'part_catalog_id' => ['nullable', 'exists:maintenance_part_catalogs,id'], 'part_name' => [$partial ? 'sometimes' : 'required_without:spare_part_id', 'nullable', 'string', 'max:255'], 'part_code' => ['nullable', 'string', 'max:100'], 'quantity' => ['nullable', 'numeric', 'min:0'], 'unit' => ['nullable', 'string', 'max:50'], 'part_type' => ['sometimes', 'string', 'in:mechanical,electronic,consumable,hydraulic,pneumatic'], 'is_consumable' => ['sometimes', 'boolean'], 'is_critical' => ['sometimes', 'boolean'], 'responsibility' => ['sometimes', 'string', 'in:ours,vendor,shared'], 'responsibility_party_name' => ['nullable', 'string', 'max:255'], 'warranty_status' => ['sometimes', 'string', 'in:none,active,expired'], 'warranty_provider' => ['nullable', 'string', 'max:255'], 'warranty_expires_at' => ['nullable', 'date'], 'installed_by' => ['sometimes', 'string', 'in:ours,vendor,third_party'], 'installer_name' => ['nullable', 'string', 'max:255'], 'expected_replacement_date' => ['nullable', 'date'], 'replacement_interval_days' => ['nullable', 'integer', 'min:0'], 'last_replaced_at' => ['nullable', 'date'], 'replacement_quality_grade' => ['nullable', 'string', 'max:50'], 'damage_action' => ['nullable', 'string', 'in:replace,service,both'], 'damage_service_list' => ['nullable', 'array'], 'multi_year_service_plan' => ['nullable', 'array'], 'health_percentage' => ['nullable', 'numeric', 'min:0', 'max:100'], 'notes' => ['nullable', 'string'], ]; } public function index(Request $request, Equipment $equipment) { if (! $this->ensureBusinessOwnership($request, $equipment->business_id)) { return $this->jsonError('دسترسی ندارید.', 403); } $parts = EquipmentPart::where('equipment_id', $equipment->id) ->with(['sparePart:id,part_number,name,quantity,unit', 'partCatalog:id,catalog_code,name', 'children']) ->orderBy('part_name') ->get(); $tree = $this->buildTree($parts); return $this->jsonSuccess(['items' => $parts, 'tree' => $tree]); } public function store(Request $request, Equipment $equipment) { if (! $this->ensureBusinessOwnership($request, $equipment->business_id)) { return $this->jsonError('دسترسی ندارید.', 403); } $data = $request->validate($this->partRules()); if (! empty($data['spare_part_id'])) { $spare = \Modules\Maintenance\Models\SparePart::find($data['spare_part_id']); if ($spare) { $data['part_name'] = $data['part_name'] ?? $spare->name; $data['part_code'] = $data['part_code'] ?? $spare->part_number; $data['unit'] = $data['unit'] ?? $spare->unit; } } if (! empty($data['part_catalog_id'])) { $catalog = \Modules\Maintenance\Models\PartCatalog::find($data['part_catalog_id']); if ($catalog) { $data['part_name'] = $data['part_name'] ?? $catalog->name; $data['part_code'] = $data['part_code'] ?? $catalog->catalog_code; $data['part_type'] = $data['part_type'] ?? $catalog->part_type; $data['is_consumable'] = $data['is_consumable'] ?? $catalog->is_consumable; } } $part = EquipmentPart::create(array_merge($data, [ 'business_id' => $equipment->business_id, 'equipment_id' => $equipment->id, 'quantity' => $data['quantity'] ?? 1, ])); return $this->jsonSuccess($part->load(['sparePart', 'partCatalog']), 'قطعه به تجهیز اضافه شد.', 201); } public function update(Request $request, Equipment $equipment, EquipmentPart $part) { if (! $this->ensureBusinessOwnership($request, $equipment->business_id) || $part->equipment_id !== $equipment->id) { return $this->jsonError('دسترسی ندارید.', 403); } $data = $request->validate($this->partRules(true)); $part->update($data); return $this->jsonSuccess($part->fresh()->load(['sparePart', 'partCatalog']), 'قطعه به‌روزرسانی شد.'); } public function destroy(Request $request, Equipment $equipment, EquipmentPart $part) { if (! $this->ensureBusinessOwnership($request, $equipment->business_id) || $part->equipment_id !== $equipment->id) { return $this->jsonError('دسترسی ندارید.', 403); } $part->delete(); return $this->jsonSuccess(null, 'قطعه حذف شد.'); } private function buildTree($parts, ?int $parentId = null): array { $branch = []; foreach ($parts as $part) { if ($part->parent_id === $parentId) { $branch[] = [ 'id' => $part->id, 'part_name' => $part->part_name, 'part_code' => $part->part_code, 'health_percentage' => $part->health_percentage, 'children' => $this->buildTree($parts, $part->id), ]; } } return $branch; } }