diff --git a/enums/__init__.py b/enums/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/enums/service.py b/enums/service.py new file mode 100644 index 0000000..e3af487 --- /dev/null +++ b/enums/service.py @@ -0,0 +1,13 @@ +from enum import unique, IntEnum + + +@unique +class ServiceType(IntEnum): + DEAL_SERVICE = 0 + PRODUCT_SERVICE = 1 + + +SERVICE_TYPE_LABELS = { + ServiceType.DEAL_SERVICE: 'Услуга для сделки', + ServiceType.PRODUCT_SERVICE: 'Услуга для товара', +} diff --git a/models/deal.py b/models/deal.py index 221e592..03a0261 100644 --- a/models/deal.py +++ b/models/deal.py @@ -33,6 +33,8 @@ class Deal(BaseModel): is_completed = Column(Boolean, nullable=False, server_default='0', default=False, comment='Завершена') services = relationship('DealService', back_populates='deal', cascade="all, delete-orphan") + product_services = relationship('DealProductService', back_populates='deal', cascade="all, delete-orphan") + products = relationship('DealProduct', back_populates='deal', cascade="all, delete-orphan") # TODO remake with sequence diff --git a/models/secondary.py b/models/secondary.py index 15536a9..c12bfcb 100644 --- a/models/secondary.py +++ b/models/secondary.py @@ -1,4 +1,4 @@ -from sqlalchemy import Table, Column, Integer, ForeignKey +from sqlalchemy import Table, Column, Integer, ForeignKey, Boolean from sqlalchemy.orm import relationship from models.base import metadata, BaseModel @@ -16,6 +16,7 @@ class DealService(BaseModel): service = relationship('Service') quantity = Column(Integer, nullable=False, comment='Кол-во услуги') + price = Column(Integer, nullable=False, comment='Цена услуги') class DealProduct(BaseModel): @@ -33,6 +34,27 @@ class DealProduct(BaseModel): quantity = Column(Integer, nullable=False, comment='Кол-во продукта') +class DealProductService(BaseModel): + __tablename__ = 'deal_product_services' + deal_id = Column(Integer, + ForeignKey('deals.id'), + nullable=False, + comment='ID Сделки', + primary_key=True) + deal = relationship('Deal', back_populates='product_services') + + product_id = Column(Integer, ForeignKey('products.id'), nullable=False, comment='ID Продукта', primary_key=True) + product = relationship('Product') + + service_id = Column(Integer, ForeignKey('services.id'), nullable=False, comment='ID Услуги', primary_key=True) + service = relationship('Service') + + quantity = Column(Integer, nullable=False, comment='Кол-во продукта') + price = Column(Integer, nullable=False, comment='Цена услуги') + + link_to_product_quantity = Column(Boolean, nullable=False, comment='Связь с количеством продукта') + + barcode_template_attribute_link = Table( 'barcode_template_attribute_links', BaseModel.metadata, diff --git a/models/service.py b/models/service.py index c23e59a..059b465 100644 --- a/models/service.py +++ b/models/service.py @@ -1,6 +1,7 @@ -from sqlalchemy import Column, Integer, String, ForeignKey, Double +from sqlalchemy import Column, Integer, String, ForeignKey, Double, asc from sqlalchemy.orm import relationship, mapped_column, Mapped +import enums.service from models import BaseModel @@ -10,9 +11,26 @@ class Service(BaseModel): name = Column(String, nullable=False, comment='Название услуги') category_id = Column(Integer, ForeignKey('service_categories.id'), nullable=False, comment='ID категории услуги') - category = relationship('ServiceCategory') + category = relationship('ServiceCategory', lazy='joined') price = Column(Double, nullable=False, comment='Стоимость услуги') + service_type = Column(Integer, + server_default=f'{enums.service.ServiceType.DEAL_SERVICE}', + nullable=False, + comment='Тип услуги') + + price_ranges = relationship('ServicePriceRange', back_populates='service', lazy='selectin', + order_by="asc(ServicePriceRange.from_quantity)") + + +class ServicePriceRange(BaseModel): + __tablename__ = 'service_price_ranges' + id = Column(Integer, autoincrement=True, primary_key=True, index=True) + service_id = Column(Integer, ForeignKey('services.id'), nullable=False, comment='ID услуги') + service = relationship('Service', back_populates='price_ranges') + from_quantity = Column(Integer, nullable=False, comment='От количества') + to_quantity = Column(Integer, nullable=False, comment='До количества') + price = Column(Double, nullable=False, comment='Цена') class ServiceCategory(BaseModel): diff --git a/routers/service.py b/routers/service.py index 5c979be..2249a42 100644 --- a/routers/service.py +++ b/routers/service.py @@ -3,7 +3,9 @@ from typing import Annotated from fastapi import APIRouter, Depends from sqlalchemy.ext.asyncio import AsyncSession +import enums.service from backend.session import get_session +from schemas.base import BaseEnumSchema, BaseEnumListSchema from schemas.service import * from services.auth import get_current_user from services.service import ServiceService @@ -83,3 +85,16 @@ async def create_category( request: ServiceCreateCategoryRequest ): return await ServiceService(session).create_category(request) + + +@service_router.get( + '/types/get-all', + response_model=BaseEnumListSchema, + operation_id="get_all_service_types" +) +async def get_all_service_types( +): + result = [] + for key, value in enums.service.SERVICE_TYPE_LABELS.items(): + result.append({"id": key, "name": value}) + return BaseEnumListSchema(items=result) diff --git a/schemas/base.py b/schemas/base.py index 62c6f5f..1ddfc12 100644 --- a/schemas/base.py +++ b/schemas/base.py @@ -38,3 +38,12 @@ class PaginationSchema(CustomModelCamel): class PaginationInfoSchema(CustomModelCamel): total_pages: int total_items: int + + +class BaseEnumSchema(CustomModelCamel): + id: int + name: str + + +class BaseEnumListSchema(CustomModelCamel): + items: list[BaseEnumSchema] diff --git a/schemas/deal.py b/schemas/deal.py index 84bb62e..f1be7f6 100644 --- a/schemas/deal.py +++ b/schemas/deal.py @@ -30,10 +30,18 @@ class DealSummary(CustomModelCamel): class DealServiceSchema(CustomModelCamel): service: ServiceSchema quantity: int + price: int + + +class DealProductServiceSchema(CustomModelCamel): + service: ServiceSchema + quantity: int + price: int class DealProductSchema(CustomModelCamel): product: ProductSchema + services: List[DealProductServiceSchema] quantity: int diff --git a/schemas/service.py b/schemas/service.py index b8a7314..52e7b81 100644 --- a/schemas/service.py +++ b/schemas/service.py @@ -1,9 +1,16 @@ -from typing import List +from typing import List, Optional -from schemas.base import CustomModelCamel, OkMessageSchema +from schemas.base import CustomModelCamel, OkMessageSchema, BaseEnumSchema # region Entities +class ServicePriceRangeSchema(CustomModelCamel): + id: int | None + from_quantity: int + to_quantity: int + price: float + + class ServiceCategorySchema(CustomModelCamel): id: int name: str @@ -14,6 +21,8 @@ class ServiceSchema(CustomModelCamel): name: str category: ServiceCategorySchema price: float + service_type: int + price_ranges: List[ServicePriceRangeSchema] # endregion diff --git a/services/service.py b/services/service.py index 2b5829c..75a065c 100644 --- a/services/service.py +++ b/services/service.py @@ -1,7 +1,7 @@ from sqlalchemy import select, update from sqlalchemy.orm import joinedload -from models import Service, ServiceCategory +from models import Service, ServiceCategory, ServicePriceRange from services.base import BaseService from schemas.service import ServiceGetAllResponse, ServiceSchema, ServiceGetAllCategoriesResponse, \ ServiceCategorySchema, ServiceCreateRequest, ServiceCreateResponse, ServiceCreateCategoryRequest, \ @@ -27,8 +27,18 @@ class ServiceService(BaseService): service_dict['category_id'] = raw_service.category.id del service_dict['id'] del service_dict['category'] + del service_dict['price_ranges'] service = Service(**service_dict) self.session.add(service) + await self.session.flush() + price_ranges = request.service.price_ranges + for price_range in price_ranges: + price_range_dict = price_range.model_dump() + price_range_dict['service_id'] = service.id + del price_range_dict['id'] + price_range_obj = ServicePriceRange(**price_range_dict) + self.session.add(price_range_obj) + await self.session.commit() return ServiceCreateResponse(ok=True, message="Услуга успешно создана") except Exception as e: @@ -37,21 +47,41 @@ class ServiceService(BaseService): async def update(self, request: ServiceUpdateRequest) -> ServiceUpdateResponse: try: raw_service = request.data - service = await (self.session - .scalars(select(Service) - .filter(Service.id == raw_service.id))) + service = await (self.session.get(Service, raw_service.id)) if not service: return ServiceUpdateResponse(ok=False, message="Услуга не найдена") service_dict = raw_service.dict() service_dict['category_id'] = raw_service.category.id del service_dict['category'] + del service_dict['price_ranges'] await self.session.execute( update(Service) .where(Service.id == raw_service.id) .values(**service_dict) ) - + # checking if old price ranges are still in the request + request_price_range_ids = [price_range.id for price_range in raw_service.price_ranges if price_range.id] + price_ranges_to_delete = [] + for price_range in service.price_ranges: + if price_range.id not in request_price_range_ids: + price_ranges_to_delete.append(price_range) + for price_range in price_ranges_to_delete: + await self.session.delete(price_range) + await self.session.flush() + for price_range in raw_service.price_ranges: + price_range_dict = price_range.dict() + price_range_dict['service_id'] = raw_service.id + if price_range.id: + await self.session.execute( + update(ServicePriceRange) + .where(ServicePriceRange.id == price_range.id) + .values(**price_range_dict) + ) + else: + del price_range_dict['id'] + price_range_obj = ServicePriceRange(**price_range_dict) + self.session.add(price_range_obj) await self.session.commit() return ServiceUpdateResponse(ok=True, message="Услуга успешно обновлена") except Exception as e: