clientTierService = $clientTierService; } public function index(Request $request) { $query = Client::query(); if ($request->has('with')) { $relations = explode(',', $request->with); $query->with($relations); } if ($request->has('client_number') && $request->client_number) { $query->where('client_number', 'like', "%{$request->client_number}%"); } elseif ($request->has('q') && $request->q) { $query->where(function($q) use ($request) { $q->where('name', 'like', "%{$request->q}%") ->orWhere('email', 'like', "%{$request->q}%") ->orWhere('rfc', 'like', "%{$request->q}%"); }); } return ApiResponse::OK->response([ 'clients' => $query->paginate(config('app.pagination')), ]); } public function show(Client $client) { return ApiResponse::OK->response([ 'client' => $client ]); } public function store(Request $request) { $request->validate([ 'name' => 'nullable|string|max:255', 'email' => 'nullable|email|max:255', 'phone' => 'nullable|string|max:20', 'address' => 'nullable|string|max:500', 'rfc' => 'required|string|max:13', 'razon_social' => 'nullable|string|max:255', 'regimen_fiscal' => 'nullable|string|max:100', 'cp_fiscal' => 'nullable|string|max:5', 'uso_cfdi' => 'nullable|string|max:100', ],[ 'email.unique' => 'El correo electrónico ya está en uso por otro cliente.', 'phone.unique' => 'El teléfono ya está en uso por otro cliente.', 'rfc.unique' => 'El RFC ya está en uso por otro cliente.', 'rfc.required' => 'El RFC es obligatorio.', ]); try{ $data = $request->only([ 'name', 'email', 'phone', 'address', 'rfc', 'razon_social', 'regimen_fiscal', 'cp_fiscal', 'uso_cfdi', ]); // Usar RFC como client_number $data['client_number'] = $data['rfc'] ?? null; $client = Client::create($data); // Cargar relación tier $client->load('tier'); return ApiResponse::OK->response([ 'client' => $client, 'message' => 'Cliente creado correctamente.' ]); }catch(\Exception $e){ return ApiResponse::BAD_REQUEST->response([ 'message' => 'Error al crear el cliente.' ]); } } public function update(Request $request, Client $client) { $request->validate([ 'name' => 'nullable|string|max:255', 'email' => 'nullable|email|max:255', 'phone' => 'nullable|string|max:20', 'address' => 'nullable|string|max:500', 'rfc' => 'nullable|string|max:13', ],[ 'email.unique' => 'El correo electrónico ya está en uso por otro cliente.', 'phone.unique' => 'El teléfono ya está en uso por otro cliente.', 'rfc.unique' => 'El RFC ya está en uso por otro cliente.', ]); try{ $data = $request->only([ 'name', 'email', 'phone', 'address', 'rfc', 'razon_social', 'regimen_fiscal', 'cp_fiscal', 'uso_cfdi', ]); // Mantener client_number sincronizado con RFC cuando se actualiza if ($request->filled('rfc')) { $data['client_number'] = $data['rfc']; } $client->update($data); return ApiResponse::OK->response([ 'client' => $client, 'message' => 'Cliente actualizado correctamente.' ]); }catch(\Exception $e){ return ApiResponse::BAD_REQUEST->response([ 'message' => 'Error al actualizar el cliente.' ]); } } public function destroy(Client $client) { try{ $client->delete(); return ApiResponse::OK->response([ 'message' => 'Cliente eliminado correctamente.' ]); }catch(\Exception $e){ return ApiResponse::BAD_REQUEST->response([ 'message' => 'Error al eliminar el cliente.' ]); } } /** * Obtener estadísticas de compra del cliente */ public function stats(Client $client) { try { $stats = $this->clientTierService->getClientStats($client); return ApiResponse::OK->response([ 'stats' => $stats ]); } catch(\Exception $e) { return ApiResponse::BAD_REQUEST->response([ 'message' => 'Error al obtener estadísticas del cliente.' ]); } } /** * Obtener historial de cambios de tier del cliente */ public function tierHistory(Client $client) { try { $history = $client->tierHistory() ->with(['oldTier', 'newTier']) ->orderBy('changed_at', 'desc') ->get(); return ApiResponse::OK->response([ 'history' => $history ]); } catch(\Exception $e) { return ApiResponse::BAD_REQUEST->response([ 'message' => 'Error al obtener historial del cliente.' ]); } } /** * Obtener ventas con descuento de un cliente */ public function salesWithDiscounts(Client $client, Request $request) { try { $query = $client->sales() ->where('discount_amount', '>', 0) ->with(['details', 'user:id,name']) ->orderBy('created_at', 'desc'); // Filtro por rango de fechas if ($request->has('date_from')) { $query->where('created_at', '>=', $request->date_from); } if ($request->has('date_to')) { $query->where('created_at', '<=', $request->date_to); } $sales = $query->paginate($request->per_page ?? 15); // Calcular totales $totals = [ 'total_sales' => $client->sales()->where('discount_amount', '>', 0)->count(), 'total_amount' => $client->sales()->where('discount_amount', '>', 0)->sum('total'), 'total_discounts' => $client->sales()->where('discount_amount', '>', 0)->sum('discount_amount'), ]; return ApiResponse::OK->response([ 'sales' => $sales, 'totals' => $totals ]); } catch(\Exception $e) { return ApiResponse::BAD_REQUEST->response([ 'message' => 'Error al obtener ventas con descuentos.', 'error' => $e->getMessage() ]); } } }