feat: deal product services

This commit is contained in:
2024-05-13 07:46:13 +03:00
parent e9aec10feb
commit be650aca74
10 changed files with 136 additions and 10 deletions

0
enums/__init__.py Normal file
View File

13
enums/service.py Normal file
View File

@@ -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: 'Услуга для товара',
}

View File

@@ -33,6 +33,8 @@ class Deal(BaseModel):
is_completed = Column(Boolean, nullable=False, server_default='0', default=False, comment='Завершена') is_completed = Column(Boolean, nullable=False, server_default='0', default=False, comment='Завершена')
services = relationship('DealService', back_populates='deal', cascade="all, delete-orphan") 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") products = relationship('DealProduct', back_populates='deal', cascade="all, delete-orphan")
# TODO remake with sequence # TODO remake with sequence

View File

@@ -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 sqlalchemy.orm import relationship
from models.base import metadata, BaseModel from models.base import metadata, BaseModel
@@ -16,6 +16,7 @@ class DealService(BaseModel):
service = relationship('Service') service = relationship('Service')
quantity = Column(Integer, nullable=False, comment='Кол-во услуги') quantity = Column(Integer, nullable=False, comment='Кол-во услуги')
price = Column(Integer, nullable=False, comment='Цена услуги')
class DealProduct(BaseModel): class DealProduct(BaseModel):
@@ -33,6 +34,27 @@ class DealProduct(BaseModel):
quantity = Column(Integer, nullable=False, comment='Кол-во продукта') 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_link = Table(
'barcode_template_attribute_links', 'barcode_template_attribute_links',
BaseModel.metadata, BaseModel.metadata,

View File

@@ -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 from sqlalchemy.orm import relationship, mapped_column, Mapped
import enums.service
from models import BaseModel from models import BaseModel
@@ -10,9 +11,26 @@ class Service(BaseModel):
name = Column(String, nullable=False, comment='Название услуги') name = Column(String, nullable=False, comment='Название услуги')
category_id = Column(Integer, ForeignKey('service_categories.id'), nullable=False, comment='ID категории услуги') 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='Стоимость услуги') 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): class ServiceCategory(BaseModel):

View File

@@ -3,7 +3,9 @@ from typing import Annotated
from fastapi import APIRouter, Depends from fastapi import APIRouter, Depends
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
import enums.service
from backend.session import get_session from backend.session import get_session
from schemas.base import BaseEnumSchema, BaseEnumListSchema
from schemas.service import * from schemas.service import *
from services.auth import get_current_user from services.auth import get_current_user
from services.service import ServiceService from services.service import ServiceService
@@ -83,3 +85,16 @@ async def create_category(
request: ServiceCreateCategoryRequest request: ServiceCreateCategoryRequest
): ):
return await ServiceService(session).create_category(request) 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)

View File

@@ -38,3 +38,12 @@ class PaginationSchema(CustomModelCamel):
class PaginationInfoSchema(CustomModelCamel): class PaginationInfoSchema(CustomModelCamel):
total_pages: int total_pages: int
total_items: int total_items: int
class BaseEnumSchema(CustomModelCamel):
id: int
name: str
class BaseEnumListSchema(CustomModelCamel):
items: list[BaseEnumSchema]

View File

@@ -30,10 +30,18 @@ class DealSummary(CustomModelCamel):
class DealServiceSchema(CustomModelCamel): class DealServiceSchema(CustomModelCamel):
service: ServiceSchema service: ServiceSchema
quantity: int quantity: int
price: int
class DealProductServiceSchema(CustomModelCamel):
service: ServiceSchema
quantity: int
price: int
class DealProductSchema(CustomModelCamel): class DealProductSchema(CustomModelCamel):
product: ProductSchema product: ProductSchema
services: List[DealProductServiceSchema]
quantity: int quantity: int

View File

@@ -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 # region Entities
class ServicePriceRangeSchema(CustomModelCamel):
id: int | None
from_quantity: int
to_quantity: int
price: float
class ServiceCategorySchema(CustomModelCamel): class ServiceCategorySchema(CustomModelCamel):
id: int id: int
name: str name: str
@@ -14,6 +21,8 @@ class ServiceSchema(CustomModelCamel):
name: str name: str
category: ServiceCategorySchema category: ServiceCategorySchema
price: float price: float
service_type: int
price_ranges: List[ServicePriceRangeSchema]
# endregion # endregion

View File

@@ -1,7 +1,7 @@
from sqlalchemy import select, update from sqlalchemy import select, update
from sqlalchemy.orm import joinedload from sqlalchemy.orm import joinedload
from models import Service, ServiceCategory from models import Service, ServiceCategory, ServicePriceRange
from services.base import BaseService from services.base import BaseService
from schemas.service import ServiceGetAllResponse, ServiceSchema, ServiceGetAllCategoriesResponse, \ from schemas.service import ServiceGetAllResponse, ServiceSchema, ServiceGetAllCategoriesResponse, \
ServiceCategorySchema, ServiceCreateRequest, ServiceCreateResponse, ServiceCreateCategoryRequest, \ ServiceCategorySchema, ServiceCreateRequest, ServiceCreateResponse, ServiceCreateCategoryRequest, \
@@ -27,8 +27,18 @@ class ServiceService(BaseService):
service_dict['category_id'] = raw_service.category.id service_dict['category_id'] = raw_service.category.id
del service_dict['id'] del service_dict['id']
del service_dict['category'] del service_dict['category']
del service_dict['price_ranges']
service = Service(**service_dict) service = Service(**service_dict)
self.session.add(service) 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() await self.session.commit()
return ServiceCreateResponse(ok=True, message="Услуга успешно создана") return ServiceCreateResponse(ok=True, message="Услуга успешно создана")
except Exception as e: except Exception as e:
@@ -37,21 +47,41 @@ class ServiceService(BaseService):
async def update(self, request: ServiceUpdateRequest) -> ServiceUpdateResponse: async def update(self, request: ServiceUpdateRequest) -> ServiceUpdateResponse:
try: try:
raw_service = request.data raw_service = request.data
service = await (self.session service = await (self.session.get(Service, raw_service.id))
.scalars(select(Service)
.filter(Service.id == raw_service.id)))
if not service: if not service:
return ServiceUpdateResponse(ok=False, message="Услуга не найдена") return ServiceUpdateResponse(ok=False, message="Услуга не найдена")
service_dict = raw_service.dict() service_dict = raw_service.dict()
service_dict['category_id'] = raw_service.category.id service_dict['category_id'] = raw_service.category.id
del service_dict['category'] del service_dict['category']
del service_dict['price_ranges']
await self.session.execute( await self.session.execute(
update(Service) update(Service)
.where(Service.id == raw_service.id) .where(Service.id == raw_service.id)
.values(**service_dict) .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() await self.session.commit()
return ServiceUpdateResponse(ok=True, message="Услуга успешно обновлена") return ServiceUpdateResponse(ok=True, message="Услуга успешно обновлена")
except Exception as e: except Exception as e: