feat: temp barcode templates
This commit is contained in:
1
main.py
1
main.py
@@ -31,6 +31,7 @@ routers_list = [
|
||||
routers.client_router,
|
||||
routers.service_router,
|
||||
routers.product_router,
|
||||
routers.barcode_router
|
||||
]
|
||||
for router in routers_list:
|
||||
app.include_router(router)
|
||||
|
||||
@@ -6,4 +6,5 @@ from .client import *
|
||||
from .service import *
|
||||
from .product import *
|
||||
from .secondary import *
|
||||
from .barcode import *
|
||||
configure_mappers()
|
||||
|
||||
22
models/barcode.py
Normal file
22
models/barcode.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from sqlalchemy import Integer, Column, String, Boolean
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from models import BaseModel
|
||||
|
||||
|
||||
class BarcodeTemplateAttribute(BaseModel):
|
||||
__tablename__ = 'barcode_template_attributes'
|
||||
id = Column(Integer, autoincrement=True, primary_key=True, index=True)
|
||||
name = Column(String, nullable=False, index=True, comment='Название атрибута')
|
||||
label = Column(String, nullable=False, index=True, comment='Метка атрибута')
|
||||
|
||||
|
||||
class BarcodeTemplate(BaseModel):
|
||||
__tablename__ = 'barcode_templates'
|
||||
id = Column(Integer, autoincrement=True, primary_key=True, index=True)
|
||||
name = Column(String, nullable=False, index=True, comment='Название шаблона')
|
||||
attributes = relationship('BarcodeTemplateAttributeLink',
|
||||
back_populates='barcode_template',
|
||||
cascade="all, delete-orphan",
|
||||
lazy='joined')
|
||||
is_default = Column(Boolean, nullable=False, default=False, comment='По умолчанию')
|
||||
@@ -11,9 +11,11 @@ class Client(BaseModel):
|
||||
created_at = Column(DateTime, nullable=False, comment='Дата создания')
|
||||
|
||||
products = relationship('Product', back_populates='client')
|
||||
|
||||
details = relationship('ClientDetails', uselist=False, back_populates='client', cascade='all, delete')
|
||||
|
||||
barcode_template_id = Column(Integer, ForeignKey('barcode_templates.id'), nullable=True)
|
||||
barcode_template = relationship('BarcodeTemplate')
|
||||
|
||||
|
||||
class ClientDetails(BaseModel):
|
||||
__tablename__ = 'client_details'
|
||||
|
||||
@@ -18,6 +18,9 @@ class Product(BaseModel):
|
||||
client = relationship('Client', back_populates='products')
|
||||
barcodes = relationship('ProductBarcode', back_populates='product', cascade="all, delete-orphan")
|
||||
|
||||
barcode_template_id = Column(Integer, ForeignKey('barcode_templates.id'), nullable=True)
|
||||
barcode_template = relationship('BarcodeTemplate')
|
||||
|
||||
|
||||
class ProductBarcode(BaseModel):
|
||||
__tablename__ = 'product_barcodes'
|
||||
|
||||
@@ -31,3 +31,20 @@ class DealProduct(BaseModel):
|
||||
product = relationship('Product')
|
||||
|
||||
quantity = Column(Integer, nullable=False, comment='Кол-во продукта')
|
||||
|
||||
|
||||
class BarcodeTemplateAttributeLink(BaseModel):
|
||||
__tablename__ = 'barcode_template_attributes_links'
|
||||
barcode_template_id = Column(Integer,
|
||||
ForeignKey('barcode_templates.id'),
|
||||
nullable=False,
|
||||
comment='ID Шаблона ШК',
|
||||
primary_key=True)
|
||||
barcode_template = relationship('BarcodeTemplate', back_populates='attributes')
|
||||
|
||||
attribute_id = Column(Integer,
|
||||
ForeignKey('barcode_template_attributes.id'),
|
||||
nullable=False,
|
||||
comment='ID Атрибута',
|
||||
primary_key=True)
|
||||
attribute = relationship('BarcodeTemplateAttribute')
|
||||
|
||||
@@ -3,3 +3,4 @@ from .deal import deal_router
|
||||
from .client import client_router
|
||||
from .service import service_router
|
||||
from .product import product_router
|
||||
from .barcode import barcode_router
|
||||
|
||||
82
routers/barcode.py
Normal file
82
routers/barcode.py
Normal file
@@ -0,0 +1,82 @@
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from backend.session import get_session
|
||||
from schemas.barcode import (GetBarcodeTemplateByIdResponse,
|
||||
GetBarcodeTemplateByIdRequest,
|
||||
BarcodeTemplateCreateResponse,
|
||||
BarcodeTemplateCreateRequest, GetAllBarcodeTemplateAttributesResponse,
|
||||
CreateBarcodeTemplateAttributeResponse, CreateBarcodeTemplateAttributeRequest,
|
||||
BarcodeTemplateUpdateResponse, BarcodeTemplateUpdateRequest)
|
||||
from services.barcode import BarcodeService
|
||||
|
||||
barcode_router = APIRouter(
|
||||
prefix='/barcode',
|
||||
tags=['barcode'],
|
||||
)
|
||||
|
||||
|
||||
# region Templates
|
||||
@barcode_router.get(
|
||||
'/template/get',
|
||||
response_model=GetBarcodeTemplateByIdResponse,
|
||||
operation_id='get_barcode_template_by_id'
|
||||
)
|
||||
async def get_barcode_template_by_id(
|
||||
request: GetBarcodeTemplateByIdRequest,
|
||||
session: Annotated[AsyncSession, Depends(get_session)]
|
||||
):
|
||||
return await BarcodeService(session).get_barcode_template_by_id(request)
|
||||
|
||||
|
||||
@barcode_router.post(
|
||||
'/template/create',
|
||||
response_model=BarcodeTemplateCreateResponse,
|
||||
operation_id='create_barcode_template'
|
||||
)
|
||||
async def create_barcode_template(
|
||||
request: BarcodeTemplateCreateRequest,
|
||||
session: Annotated[AsyncSession, Depends(get_session)]
|
||||
):
|
||||
return await BarcodeService(session).create_barcode_template(request)
|
||||
|
||||
|
||||
@barcode_router.post(
|
||||
'/template/update',
|
||||
response_model=BarcodeTemplateUpdateResponse,
|
||||
operation_id='update_barcode_template'
|
||||
)
|
||||
async def update_barcode_template(
|
||||
request: BarcodeTemplateUpdateRequest,
|
||||
session: Annotated[AsyncSession, Depends(get_session)]
|
||||
):
|
||||
return await BarcodeService(session).update_barcode_template(request)
|
||||
|
||||
|
||||
# endregion
|
||||
|
||||
# region Template attributes
|
||||
@barcode_router.get(
|
||||
'/template/attribute/get-all',
|
||||
response_model=GetAllBarcodeTemplateAttributesResponse,
|
||||
operation_id='get_all_barcode_template_attributes'
|
||||
)
|
||||
async def get_all_barcode_template_attributes(
|
||||
session: Annotated[AsyncSession, Depends(get_session)]
|
||||
):
|
||||
return await BarcodeService(session).get_all_barcode_template_attributes()
|
||||
|
||||
|
||||
@barcode_router.post(
|
||||
'/template/attribute/create',
|
||||
response_model=CreateBarcodeTemplateAttributeResponse,
|
||||
operation_id='create_barcode_template_attribute'
|
||||
)
|
||||
async def create_barcode_template_attribute(
|
||||
request: CreateBarcodeTemplateAttributeRequest,
|
||||
session: Annotated[AsyncSession, Depends(get_session)]
|
||||
):
|
||||
return await BarcodeService(session).create_barcode_template_attribute(request)
|
||||
# endregion
|
||||
64
schemas/barcode.py
Normal file
64
schemas/barcode.py
Normal file
@@ -0,0 +1,64 @@
|
||||
from schemas.base import CustomModelCamel, OkMessageSchema
|
||||
|
||||
|
||||
# region Entities
|
||||
class BarcodeTemplateAttribute(CustomModelCamel):
|
||||
id: int
|
||||
label: str
|
||||
name: str
|
||||
|
||||
|
||||
class BarcodeTemplate(CustomModelCamel):
|
||||
id: int
|
||||
name: str
|
||||
is_default: bool
|
||||
attributes: list[BarcodeTemplateAttribute]
|
||||
|
||||
|
||||
# endregion
|
||||
|
||||
# region Requests
|
||||
class GetBarcodeTemplateByIdRequest(CustomModelCamel):
|
||||
id: int
|
||||
|
||||
|
||||
class BarcodeTemplateCreateResponse(OkMessageSchema):
|
||||
id: int
|
||||
|
||||
|
||||
class BarcodeTemplateUpdateResponse(OkMessageSchema):
|
||||
pass
|
||||
|
||||
|
||||
class GetAllBarcodeTemplateAttributesResponse(CustomModelCamel):
|
||||
attributes: list[BarcodeTemplateAttribute]
|
||||
|
||||
|
||||
class CreateBarcodeTemplateAttributeRequest(CustomModelCamel):
|
||||
name: str
|
||||
label: str
|
||||
|
||||
|
||||
# endregion
|
||||
|
||||
# region Responses
|
||||
class GetBarcodeTemplateByIdResponse(CustomModelCamel):
|
||||
barcode_template: BarcodeTemplate
|
||||
|
||||
|
||||
class BarcodeTemplateCreateRequest(CustomModelCamel):
|
||||
name: str
|
||||
attribute_ids: list[int]
|
||||
is_default: bool
|
||||
|
||||
|
||||
class BarcodeTemplateUpdateRequest(CustomModelCamel):
|
||||
id: int
|
||||
name: str
|
||||
is_default: bool
|
||||
attribute_ids: list[int]
|
||||
|
||||
|
||||
class CreateBarcodeTemplateAttributeResponse(OkMessageSchema):
|
||||
id: int
|
||||
# endregion
|
||||
147
services/barcode.py
Normal file
147
services/barcode.py
Normal file
@@ -0,0 +1,147 @@
|
||||
from sqlalchemy import select, update
|
||||
|
||||
from models import BarcodeTemplate, BarcodeTemplateAttribute, BarcodeTemplateAttributeLink
|
||||
from schemas.barcode import (GetBarcodeTemplateByIdRequest,
|
||||
GetBarcodeTemplateByIdResponse,
|
||||
BarcodeTemplateCreateResponse,
|
||||
BarcodeTemplateCreateRequest, CreateBarcodeTemplateAttributeRequest,
|
||||
BarcodeTemplateUpdateResponse, BarcodeTemplateUpdateRequest)
|
||||
from services.base import BaseService
|
||||
|
||||
|
||||
class BarcodeService(BaseService):
|
||||
|
||||
# region Template
|
||||
async def get_barcode_template_by_id(self,
|
||||
request: GetBarcodeTemplateByIdRequest) -> GetBarcodeTemplateByIdResponse:
|
||||
stmt = select(BarcodeTemplate).filter(BarcodeTemplate.id == request.id)
|
||||
query = await self.session.execute(
|
||||
stmt
|
||||
)
|
||||
return query.scalar()
|
||||
|
||||
async def create_barcode_template(self, request: BarcodeTemplateCreateRequest) -> BarcodeTemplateCreateResponse:
|
||||
try:
|
||||
if request.is_default:
|
||||
stmt = select(BarcodeTemplate).filter(BarcodeTemplate.is_default == True)
|
||||
query = await self.session.execute(stmt)
|
||||
if query.scalar():
|
||||
raise ValueError('Стандартный шаблон уже существует')
|
||||
|
||||
# prevent duplicate template
|
||||
stmt = select(BarcodeTemplate).filter(BarcodeTemplate.name == request.name)
|
||||
query = await self.session.execute(stmt)
|
||||
if query.scalar():
|
||||
raise ValueError('Шаблон с таким именем уже существует')
|
||||
|
||||
# create template then add attributes
|
||||
template = BarcodeTemplate(name=request.name, is_default=request.is_default)
|
||||
self.session.add(template)
|
||||
await self.session.flush()
|
||||
|
||||
# get all attributes from database
|
||||
stmt = select(BarcodeTemplateAttribute).filter(
|
||||
BarcodeTemplateAttribute.id.in_(request.attribute_ids))
|
||||
query = await self.session.execute(stmt)
|
||||
attributes = query.scalars().all()
|
||||
|
||||
# add attributes to template
|
||||
for attribute in attributes:
|
||||
template_attribute_link = BarcodeTemplateAttributeLink(
|
||||
barcode_template_id=template.id,
|
||||
attribute_id=attribute.id
|
||||
)
|
||||
self.session.add(template_attribute_link)
|
||||
await self.session.flush()
|
||||
await self.session.commit()
|
||||
return BarcodeTemplateCreateResponse(message='Шаблон успешно создан',
|
||||
ok=True,
|
||||
id=template.id)
|
||||
except Exception as e:
|
||||
await self.session.rollback()
|
||||
return BarcodeTemplateCreateResponse(message=str(e),
|
||||
ok=False,
|
||||
id=-1)
|
||||
|
||||
async def update_barcode_template(self, request: BarcodeTemplateUpdateRequest) -> BarcodeTemplateUpdateResponse:
|
||||
try:
|
||||
stmt = select(BarcodeTemplate).filter(BarcodeTemplate.id == request.id)
|
||||
query = await self.session.execute(stmt)
|
||||
template = query.scalar()
|
||||
if not template:
|
||||
raise ValueError('Шаблон не найден')
|
||||
|
||||
if request.is_default:
|
||||
stmt = select(BarcodeTemplate).filter(BarcodeTemplate.is_default == True)
|
||||
query = await self.session.execute(stmt)
|
||||
default_template = query.scalar()
|
||||
if default_template and default_template.id != request.id:
|
||||
raise ValueError('Стандартный шаблон уже существует')
|
||||
|
||||
# update template then add attributes with dict and value syntax
|
||||
request_dict = request.dict()
|
||||
del request_dict['id']
|
||||
del request_dict['attribute_ids']
|
||||
await self.session.execute(
|
||||
update(BarcodeTemplate)
|
||||
.where(BarcodeTemplate.id == request.id)
|
||||
.values(**request_dict)
|
||||
)
|
||||
|
||||
# difference deleted and new
|
||||
template_attributes = set([attribute.attribute_id for attribute in template.attributes])
|
||||
request_attributes = set(request.attribute_ids)
|
||||
new_attributes = request_attributes.difference(template_attributes)
|
||||
deleted_attributes = template_attributes.difference(request_attributes)
|
||||
|
||||
# delete attributes
|
||||
for attribute in template.attributes:
|
||||
if attribute.attribute_id not in deleted_attributes:
|
||||
continue
|
||||
await self.session.delete(attribute)
|
||||
|
||||
# add new attributes
|
||||
for new_attribute in new_attributes:
|
||||
template_attribute_link = BarcodeTemplateAttributeLink(
|
||||
barcode_template_id=template.id,
|
||||
attribute_id=new_attribute
|
||||
)
|
||||
self.session.add(template_attribute_link)
|
||||
await self.session.flush()
|
||||
|
||||
await self.session.commit()
|
||||
return BarcodeTemplateUpdateResponse(message='Шаблон успешно обновлен',
|
||||
ok=True)
|
||||
except Exception as e:
|
||||
await self.session.rollback()
|
||||
return BarcodeTemplateUpdateResponse(message=str(e),
|
||||
ok=False)
|
||||
|
||||
# endregion
|
||||
|
||||
# region Template attributes
|
||||
async def get_all_barcode_template_attributes(self):
|
||||
stmt = select(BarcodeTemplateAttribute)
|
||||
query = await self.session.execute(stmt)
|
||||
return query.scalars().all()
|
||||
|
||||
async def create_barcode_template_attribute(self, request: CreateBarcodeTemplateAttributeRequest):
|
||||
try:
|
||||
# prevent duplicate attribute
|
||||
stmt = select(BarcodeTemplateAttribute).filter(BarcodeTemplateAttribute.name == request.name)
|
||||
query = await self.session.execute(stmt)
|
||||
if query.scalar():
|
||||
raise ValueError('Атрибут с таким именем уже существует')
|
||||
|
||||
attribute = BarcodeTemplateAttribute(**request.dict())
|
||||
self.session.add(attribute)
|
||||
await self.session.commit()
|
||||
return BarcodeTemplateCreateResponse(message='Атрибут успешно создан',
|
||||
ok=True,
|
||||
id=attribute.id)
|
||||
except Exception as e:
|
||||
await self.session.rollback()
|
||||
return BarcodeTemplateCreateResponse(message=str(e),
|
||||
ok=False,
|
||||
id=-1)
|
||||
# endregion
|
||||
Reference in New Issue
Block a user