import datetime from typing import Union from fastapi import HTTPException from sqlalchemy import select, update from sqlalchemy.orm import joinedload, selectinload, noload from models import Client, ClientDetails, User, ResidualPallet, ResidualBox, ResidualProduct, Product, Card from schemas.client import * from services.auth import AuthService from services.base import BaseService class ClientService(BaseService): async def get_by_name(self, name: str) -> Union[Client, None]: client = await self.session.scalar(select(Client).where(Client.name == name)) return client async def _get_by_id(self, client_id: int) -> Union[Client, None]: return await self.session.get(Client, client_id) async def get_by_id(self, client_id: int) -> ClientGetResponse: stmt = ( select(Client) .options( selectinload(Client.pallets) .selectinload(ResidualPallet.residual_products) .selectinload(ResidualProduct.product) .noload(Product.barcodes), selectinload(Client.pallets) .selectinload(ResidualPallet.boxes) .selectinload(ResidualBox.residual_products) .selectinload(ResidualProduct.product) .noload(Product.barcodes), selectinload(Client.boxes) .selectinload(ResidualBox.residual_products) .selectinload(ResidualProduct.product) .noload(Product.barcodes), ) .where(Client.id == client_id) ) client = (await self.session.execute(stmt)).one_or_none() client = client[0] if client else None if not client: raise HTTPException(status_code=404, detail="Клиент не найден") return ClientGetResponse(client=client) async def get_details_by_client_id(self, client_id: int) -> Union[ClientDetails, None]: details = await self.session.scalar(select(ClientDetails).where(ClientDetails.client_id == client_id)) return details async def get_all(self) -> ClientGetAllResponse: clients_query = await self.session.scalars( select(Client) .options( joinedload(Client.details), noload(Client.products), noload(Client.pallets), noload(Client.boxes), noload(Client.chat) ) ) clients = clients_query.all() result = [] for client in clients: result.append(ClientSchema.model_validate(client)) return ClientGetAllResponse(clients=result) async def create_details(self, user, client: Client, request: ClientDetailsSchema): dict_data = request.dict() dict_data['client_id'] = client.id dict_data['last_modified_at'] = datetime.datetime.now() dict_data['modified_by_user_id'] = user.id details = ClientDetails(**dict_data) self.session.add(details) await self.session.flush() return details async def update_details(self, user: User, client: Client, request: ClientDetailsSchema) -> ClientDetails: details = await self.get_details_by_client_id(client_id=client.id) if not details: details = await self.create_details(user, client, request) dict_data = request.dict() dict_data['last_modified_at'] = datetime.datetime.now() dict_data['modified_by_user_id'] = user.id await self.session.execute(update(ClientDetails).where(ClientDetails.id == details.id).values(**dict_data)) await self.session.flush() async def create_client_raw(self, user: User, client_name: str, details_schema: ClientDetailsSchema) -> Client: client = Client(name=client_name, created_at=datetime.datetime.now()) self.session.add(client) await self.session.flush() await self.update_details(user, client, details_schema) return client async def search_clients(self, request: ClientSearchRequest) -> ClientSearchResponse: query = await self.session.scalars( select(Client) .where( Client.name.ilike(f'%{request.name}%'), Client.is_deleted == False, ) .options(joinedload(Client.details)) ) clients = [] for client in query.all(): clients.append(ClientSchema.model_validate(client)) return ClientSearchResponse(clients=clients) async def create(self, request: ClientCreateRequest, user: User) -> ClientCreateResponse: try: client = await self.get_by_name(request.data.name) if client: return ClientCreateResponse(ok=False, message='Клиент с таким именем уже существует') client_dict = request.data.dict() del client_dict['id'] del client_dict['details'] del client_dict['barcode_template'] if request.data.barcode_template: client_dict['barcode_template_id'] = request.data.barcode_template.id client_dict['created_at'] = datetime.datetime.now() client = Client(**client_dict) self.session.add(client) await self.session.flush() await self.create_details(user, client, request.data.details) # await self.create_client_raw(user, request.data.name, request.data.details) await self.session.commit() return ClientCreateResponse(ok=True, message='Client created') except Exception as e: return ClientCreateResponse(ok=False, message=str(e)) async def update(self, request: ClientUpdateRequest, user: User) -> ClientUpdateResponse: try: client = await self._get_by_id(request.data.id) if not client: return ClientUpdateResponse(ok=False, message='Клиент не найден') request_dict = request.data.dict() del request_dict['id'] del request_dict['details'] del request_dict['barcode_template'] del request_dict['chat'] if request.data.barcode_template: request_dict['barcode_template_id'] = request.data.barcode_template.id stmt = ( update(Client) .where(Client.id == client.id) .values(**request_dict) ) await self.session.execute(stmt) await self.update_details(user, client, request.data.details) await self.session.commit() return ClientUpdateResponse(ok=True, message='Клиент обновлен') except Exception as e: return ClientUpdateResponse(ok=False, message=str(e)) async def delete(self, request: ClientDeleteRequest) -> ClientDeleteResponse: try: client = await self._get_by_id(request.client_id) if not client: return ClientDeleteResponse(ok=False, message='Клиент не найден') client.is_deleted = True await self.session.commit() return ClientDeleteResponse(ok=True, message='Клиент удален') except Exception as e: return ClientDeleteResponse(ok=False, message=str(e)) async def create_guest_url(self, client_id: int) -> CreateGuestUrlResponse: access_token = AuthService(self.session).create_client_guest_token(client_id) url = f"leads?accessToken={access_token}" return CreateGuestUrlResponse(ok=True, message='Ссылка успешно создана!', url=url)