withCount('deals'); if ($request->search) $query->where('name', 'like', "%{$request->search}%"); if ($request->filled('is_active')) $query->where('is_active', filter_var($request->is_active, FILTER_VALIDATE_BOOLEAN)); return response()->json($query->latest()->paginate(min((int) $request->get('per_page', 15), 100))); } public function store(Request $request): JsonResponse { $this->authorizeManage(); $product = Product::create($this->validated($request) + ['created_by' => auth()->id()]); ActivityLogger::log('product_created', "Product {$product->name} created", $product); return response()->json($product->load('salesScript:id,title'), 201); } public function show(Product $product): JsonResponse { return response()->json($product->load('salesScript.sections', 'deals:id,title,product_id,status')); } public function update(Request $request, Product $product): JsonResponse { $this->authorizeManage(); $product->update($this->validated($request, true)); ActivityLogger::log('product_updated', "Product {$product->name} updated", $product); return response()->json($product->fresh('salesScript:id,title')); } public function destroy(Product $product): JsonResponse { $this->authorizeManage(); $product->delete(); ActivityLogger::log('product_deleted', "Product {$product->id} deleted"); return response()->json(['message' => 'محصول/خدمت حذف شد']); } private function validated(Request $request, bool $partial = false): array { $sometimes = $partial ? 'sometimes|' : ''; return $request->validate([ 'name' => $sometimes . 'required|string|max:255', 'category' => 'nullable|string|max:120', 'base_price' => 'nullable|numeric|min:0', 'description' => 'nullable|string', 'is_active' => 'nullable|boolean', 'sales_script_id' => 'nullable|exists:sales_scripts,id', 'faq' => 'nullable|array', 'objection_handling' => 'nullable|array', ]); } private function authorizeManage(): void { abort_unless(auth()->user()?->hasRole('admin') || auth()->user()?->hasRole('supervisor') || auth()->user()?->can('manage_products'), 403, 'شما مجوز مدیریت محصولات را ندارید.'); } }