feat: temp barcode templates

This commit is contained in:
2024-05-03 17:07:36 +03:00
parent 4f62bb2468
commit 99f0308a8a
10 changed files with 341 additions and 1 deletions

View File

@@ -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)

View File

@@ -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
View 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='По умолчанию')

View File

@@ -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'

View File

@@ -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'

View File

@@ -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')

View File

@@ -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
View 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
View 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
View 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