feat: barcode templates
This commit is contained in:
@@ -1,17 +1,8 @@
|
|||||||
from abc import abstractmethod, ABC
|
|
||||||
|
|
||||||
from models import Product
|
|
||||||
from .article_attribute_getter import ArticleAttributeGetter
|
from .article_attribute_getter import ArticleAttributeGetter
|
||||||
from .client_name_attribute_getter import ClientNameAttributeGetter
|
from .client_name_attribute_getter import ClientNameAttributeGetter
|
||||||
from .name_attribute_getter import NameAttributeGetter
|
from .name_attribute_getter import NameAttributeGetter
|
||||||
|
|
||||||
|
|
||||||
class BaseAttributeGetter(ABC):
|
|
||||||
@abstractmethod
|
|
||||||
def get_value(self, product: Product):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class AttributeWriterFactory:
|
class AttributeWriterFactory:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_writer(key: str):
|
def get_writer(key: str):
|
||||||
@@ -22,3 +13,5 @@ class AttributeWriterFactory:
|
|||||||
return ArticleAttributeGetter()
|
return ArticleAttributeGetter()
|
||||||
case 'client.name':
|
case 'client.name':
|
||||||
return ClientNameAttributeGetter()
|
return ClientNameAttributeGetter()
|
||||||
|
case _:
|
||||||
|
return None
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from barcodes.attributes import BaseAttributeGetter
|
from barcodes.attributes.base import BaseAttributeGetter
|
||||||
from models import Product
|
from models import Product
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
9
barcodes/attributes/base.py
Normal file
9
barcodes/attributes/base.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
|
from models import Product
|
||||||
|
|
||||||
|
|
||||||
|
class BaseAttributeGetter(ABC):
|
||||||
|
@abstractmethod
|
||||||
|
def get_value(self, product: Product):
|
||||||
|
pass
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
from barcodes.attributes import BaseAttributeGetter
|
from barcodes.attributes.base import BaseAttributeGetter
|
||||||
from models import Product, Client
|
from models import Product, Client
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from barcodes.attributes import BaseAttributeGetter
|
from barcodes.attributes.base import BaseAttributeGetter
|
||||||
from models import Product
|
from models import Product
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
0
barcodes/generator/__init__.py
Normal file
0
barcodes/generator/__init__.py
Normal file
13
barcodes/generator/base.py
Normal file
13
barcodes/generator/base.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
from abc import abstractmethod
|
||||||
|
|
||||||
|
from models import ProductBarcode, Product, BarcodeTemplate
|
||||||
|
|
||||||
|
|
||||||
|
class BaseBarcodeGenerator:
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def generate(self,
|
||||||
|
barcode: ProductBarcode,
|
||||||
|
product: Product,
|
||||||
|
template: BarcodeTemplate):
|
||||||
|
pass
|
||||||
10
barcodes/generator/default_generator.py
Normal file
10
barcodes/generator/default_generator.py
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
from barcodes.generator.base import BaseBarcodeGenerator
|
||||||
|
from models import ProductBarcode, Product, BarcodeTemplate
|
||||||
|
|
||||||
|
|
||||||
|
class DefaultBarcodeGenerator(BaseBarcodeGenerator):
|
||||||
|
def generate(self,
|
||||||
|
barcode: ProductBarcode,
|
||||||
|
product: Product,
|
||||||
|
template: BarcodeTemplate):
|
||||||
|
pass
|
||||||
@@ -20,22 +20,31 @@ class BarcodeTemplateAdditionalField(BaseModel):
|
|||||||
barcode_template = relationship('BarcodeTemplate')
|
barcode_template = relationship('BarcodeTemplate')
|
||||||
|
|
||||||
|
|
||||||
|
class BarcodeTemplateSize(BaseModel):
|
||||||
|
__tablename__ = 'barcode_template_sizes'
|
||||||
|
id = Column(Integer, autoincrement=True, primary_key=True, index=True)
|
||||||
|
|
||||||
|
name = Column(String, nullable=False, index=True, comment='Название размера')
|
||||||
|
key = Column(String, nullable=False, index=True, comment='Ключ размера')
|
||||||
|
|
||||||
|
width = Column(Integer, nullable=False, comment='Ширина в мм')
|
||||||
|
height = Column(Integer, nullable=False, comment='Высота в мм')
|
||||||
|
|
||||||
|
|
||||||
class BarcodeTemplate(BaseModel):
|
class BarcodeTemplate(BaseModel):
|
||||||
__tablename__ = 'barcode_templates'
|
__tablename__ = 'barcode_templates'
|
||||||
id = Column(Integer, autoincrement=True, primary_key=True, index=True)
|
id = Column(Integer, autoincrement=True, primary_key=True, index=True)
|
||||||
name = Column(String, nullable=False, index=True, comment='Название шаблона')
|
name = Column(String, nullable=False, index=True, comment='Название шаблона')
|
||||||
attributes = relationship('BarcodeTemplateAttribute',
|
attributes = relationship('BarcodeTemplateAttribute',
|
||||||
secondary=barcode_template_attribute_link,
|
secondary=barcode_template_attribute_link,
|
||||||
cascade="all",
|
|
||||||
lazy='selectin'
|
lazy='selectin'
|
||||||
)
|
)
|
||||||
additional_fields = relationship('BarcodeTemplateAdditionalField',
|
additional_attributes = relationship('BarcodeTemplateAdditionalField',
|
||||||
lazy='selectin',
|
lazy='selectin',
|
||||||
back_populates='barcode_template',
|
back_populates='barcode_template',
|
||||||
cascade="all")
|
cascade="all, delete")
|
||||||
|
|
||||||
is_default = Column(Boolean, nullable=False, default=False, comment='По умолчанию')
|
is_default = Column(Boolean, nullable=False, default=False, comment='По умолчанию')
|
||||||
|
|
||||||
# size
|
size_id = Column(Integer, ForeignKey('barcode_template_sizes.id'), nullable=False)
|
||||||
width = Column(Integer, nullable=False, comment='Ширина в мм')
|
size = relationship('BarcodeTemplateSize', lazy='joined')
|
||||||
height = Column(Integer, nullable=False, comment='Высота в мм')
|
|
||||||
|
|||||||
@@ -33,23 +33,6 @@ class DealProduct(BaseModel):
|
|||||||
quantity = Column(Integer, nullable=False, comment='Кол-во продукта')
|
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')
|
|
||||||
barcode_template_attribute_link = Table(
|
barcode_template_attribute_link = Table(
|
||||||
'barcode_template_attribute_links',
|
'barcode_template_attribute_links',
|
||||||
BaseModel.metadata,
|
BaseModel.metadata,
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from schemas.barcode import (GetBarcodeTemplateByIdResponse,
|
|||||||
CreateBarcodeTemplateAttributeResponse, CreateBarcodeTemplateAttributeRequest,
|
CreateBarcodeTemplateAttributeResponse, CreateBarcodeTemplateAttributeRequest,
|
||||||
BarcodeTemplateUpdateResponse, BarcodeTemplateUpdateRequest,
|
BarcodeTemplateUpdateResponse, BarcodeTemplateUpdateRequest,
|
||||||
GetAllBarcodeTemplatesResponse, BarcodeTemplateDeleteRequest,
|
GetAllBarcodeTemplatesResponse, BarcodeTemplateDeleteRequest,
|
||||||
BarcodeTemplateDeleteResponse)
|
BarcodeTemplateDeleteResponse, GetAllBarcodeTemplateSizesResponse)
|
||||||
from services.barcode import BarcodeService
|
from services.barcode import BarcodeService
|
||||||
|
|
||||||
barcode_router = APIRouter(
|
barcode_router = APIRouter(
|
||||||
@@ -104,4 +104,18 @@ async def create_barcode_template_attribute(
|
|||||||
session: Annotated[AsyncSession, Depends(get_session)]
|
session: Annotated[AsyncSession, Depends(get_session)]
|
||||||
):
|
):
|
||||||
return await BarcodeService(session).create_barcode_template_attribute(request)
|
return await BarcodeService(session).create_barcode_template_attribute(request)
|
||||||
|
|
||||||
|
|
||||||
|
# endregion
|
||||||
|
|
||||||
|
# region Template size
|
||||||
|
@barcode_router.get(
|
||||||
|
'/template/size/get-all',
|
||||||
|
response_model=GetAllBarcodeTemplateSizesResponse,
|
||||||
|
operation_id='get_all_barcode_template_sizes'
|
||||||
|
)
|
||||||
|
async def get_all_barcode_template_sizes(
|
||||||
|
session: Annotated[AsyncSession, Depends(get_session)]
|
||||||
|
):
|
||||||
|
return await BarcodeService(session).get_all_barcode_template_sizes()
|
||||||
# endregion
|
# endregion
|
||||||
|
|||||||
@@ -5,9 +5,11 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
|||||||
|
|
||||||
import utils.dependecies
|
import utils.dependecies
|
||||||
from backend.session import get_session
|
from backend.session import get_session
|
||||||
|
from schemas.barcode import GetProductBarcodeResponse, GetProductBarcodeRequest
|
||||||
from schemas.base import PaginationSchema
|
from schemas.base import PaginationSchema
|
||||||
from schemas.product import *
|
from schemas.product import *
|
||||||
from services.auth import get_current_user
|
from services.auth import get_current_user
|
||||||
|
from services.barcode import BarcodeService
|
||||||
from services.product import ProductService
|
from services.product import ProductService
|
||||||
|
|
||||||
product_router = APIRouter(
|
product_router = APIRouter(
|
||||||
@@ -111,3 +113,15 @@ async def generate_product_barcode(
|
|||||||
session: Annotated[AsyncSession, Depends(get_session)]
|
session: Annotated[AsyncSession, Depends(get_session)]
|
||||||
):
|
):
|
||||||
return await ProductService(session).generate_barcode(request)
|
return await ProductService(session).generate_barcode(request)
|
||||||
|
|
||||||
|
|
||||||
|
@product_router.post(
|
||||||
|
'/barcode/get',
|
||||||
|
response_model=GetProductBarcodeResponse,
|
||||||
|
operation_id='get_product_barcode'
|
||||||
|
)
|
||||||
|
async def get_product_barcode(
|
||||||
|
request: GetProductBarcodeRequest,
|
||||||
|
session: Annotated[AsyncSession, Depends(get_session)]
|
||||||
|
):
|
||||||
|
return await BarcodeService(session).get_barcode(request)
|
||||||
|
|||||||
@@ -10,6 +10,14 @@ class BarcodeTemplateAttributeSchema(CustomModelCamel):
|
|||||||
name: str
|
name: str
|
||||||
|
|
||||||
|
|
||||||
|
class BarcodeTemplateSizeSchema(CustomModelCamel):
|
||||||
|
id: int
|
||||||
|
name: str
|
||||||
|
key: str
|
||||||
|
width: int
|
||||||
|
height: int
|
||||||
|
|
||||||
|
|
||||||
class BarcodeTemplateAdditionalAttributeSchema(CustomModelCamel):
|
class BarcodeTemplateAdditionalAttributeSchema(CustomModelCamel):
|
||||||
name: str
|
name: str
|
||||||
value: str
|
value: str
|
||||||
@@ -18,8 +26,7 @@ class BarcodeTemplateAdditionalAttributeSchema(CustomModelCamel):
|
|||||||
class BaseBarcodeTemplateSchema(CustomModelCamel):
|
class BaseBarcodeTemplateSchema(CustomModelCamel):
|
||||||
name: str
|
name: str
|
||||||
is_default: bool
|
is_default: bool
|
||||||
width: int
|
size: BarcodeTemplateSizeSchema
|
||||||
height: int
|
|
||||||
additional_attributes: list[BarcodeTemplateAdditionalAttributeSchema]
|
additional_attributes: list[BarcodeTemplateAdditionalAttributeSchema]
|
||||||
|
|
||||||
|
|
||||||
@@ -102,4 +109,8 @@ class BarcodeTemplateDeleteResponse(OkMessageSchema):
|
|||||||
|
|
||||||
class GetProductBarcodeResponse(CustomModelCamel):
|
class GetProductBarcodeResponse(CustomModelCamel):
|
||||||
barcode: BarcodeSchema
|
barcode: BarcodeSchema
|
||||||
|
|
||||||
|
|
||||||
|
class GetAllBarcodeTemplateSizesResponse(CustomModelCamel):
|
||||||
|
sizes: list[BarcodeTemplateSizeSchema]
|
||||||
# endregion
|
# endregion
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
from sqlalchemy import select, update, insert
|
from sqlalchemy import select, update, insert
|
||||||
from sqlalchemy.orm import selectinload, joinedload
|
from sqlalchemy.orm import selectinload, joinedload
|
||||||
|
|
||||||
from models import BarcodeTemplate, BarcodeTemplateAttribute, barcode_template_attribute_link, Product
|
from barcodes.attributes import AttributeWriterFactory
|
||||||
|
from models import BarcodeTemplate, BarcodeTemplateAttribute, barcode_template_attribute_link, Product, \
|
||||||
|
BarcodeTemplateAdditionalField, BarcodeTemplateSize
|
||||||
from schemas.barcode import *
|
from schemas.barcode import *
|
||||||
from services.base import BaseService
|
from services.base import BaseService
|
||||||
|
|
||||||
@@ -9,21 +11,7 @@ from services.base import BaseService
|
|||||||
class BarcodeService(BaseService):
|
class BarcodeService(BaseService):
|
||||||
|
|
||||||
# region Barcode
|
# region Barcode
|
||||||
async def get_barcode(self, request: GetProductBarcodeRequest) -> GetProductBarcodeResponse:
|
async def _get_barcode_template(self, request: GetProductBarcodeRequest, product: Product) -> BarcodeTemplate:
|
||||||
# get product by id
|
|
||||||
stmt = (
|
|
||||||
select(Product)
|
|
||||||
.options(
|
|
||||||
joinedload(Product.client)
|
|
||||||
)
|
|
||||||
.filter(Product.id == request.product_id)
|
|
||||||
)
|
|
||||||
query = await self.session.execute(stmt)
|
|
||||||
product: Product = query.scalar()
|
|
||||||
if not product:
|
|
||||||
raise ValueError('Товар не найден')
|
|
||||||
|
|
||||||
# get barcode template by id
|
|
||||||
if request.barcode_template_id:
|
if request.barcode_template_id:
|
||||||
stmt = select(BarcodeTemplate).filter(BarcodeTemplate.id == request.barcode_template_id)
|
stmt = select(BarcodeTemplate).filter(BarcodeTemplate.id == request.barcode_template_id)
|
||||||
query = await self.session.execute(stmt)
|
query = await self.session.execute(stmt)
|
||||||
@@ -41,9 +29,46 @@ class BarcodeService(BaseService):
|
|||||||
if not barcode_template:
|
if not barcode_template:
|
||||||
raise ValueError('Стандартный шаблон не найден')
|
raise ValueError('Стандартный шаблон не найден')
|
||||||
barcode_template: BarcodeTemplate
|
barcode_template: BarcodeTemplate
|
||||||
|
return barcode_template
|
||||||
|
|
||||||
|
async def get_barcode(self, request: GetProductBarcodeRequest) -> GetProductBarcodeResponse:
|
||||||
# endregion
|
# get product by id
|
||||||
|
stmt = (
|
||||||
|
select(Product)
|
||||||
|
.options(
|
||||||
|
joinedload(Product.client)
|
||||||
|
)
|
||||||
|
.filter(Product.id == request.product_id)
|
||||||
|
)
|
||||||
|
query = await self.session.execute(stmt)
|
||||||
|
product: Product = query.scalar()
|
||||||
|
if not product:
|
||||||
|
raise ValueError('Товар не найден')
|
||||||
|
barcode_template = await self._get_barcode_template(request, product)
|
||||||
|
attributes_list = []
|
||||||
|
for attribute in barcode_template.attributes:
|
||||||
|
attribute_getter = AttributeWriterFactory.get_writer(attribute.key)
|
||||||
|
if not attribute_getter:
|
||||||
|
continue
|
||||||
|
value = attribute_getter.get_value(product)
|
||||||
|
attributes_list.append(
|
||||||
|
BarcodeAttributeSchema(
|
||||||
|
name=attribute.name,
|
||||||
|
value=value
|
||||||
|
)
|
||||||
|
)
|
||||||
|
for additional_attribute in barcode_template.additional_attributes:
|
||||||
|
attributes_list.append(
|
||||||
|
BarcodeAttributeSchema(
|
||||||
|
name=additional_attribute.name,
|
||||||
|
value=additional_attribute.value
|
||||||
|
)
|
||||||
|
)
|
||||||
|
barcode = BarcodeSchema(
|
||||||
|
barcode=request.barcode,
|
||||||
|
attributes=attributes_list
|
||||||
|
)
|
||||||
|
return GetProductBarcodeResponse(barcode=barcode)
|
||||||
|
|
||||||
# endregion
|
# endregion
|
||||||
|
|
||||||
@@ -84,6 +109,9 @@ class BarcodeService(BaseService):
|
|||||||
# create template then add attributes
|
# create template then add attributes
|
||||||
request_dict = request.dict()
|
request_dict = request.dict()
|
||||||
del request_dict['attribute_ids']
|
del request_dict['attribute_ids']
|
||||||
|
del request_dict['additional_attributes']
|
||||||
|
del request_dict['size']
|
||||||
|
request_dict['size_id'] = request.size.id
|
||||||
template = BarcodeTemplate(**request_dict)
|
template = BarcodeTemplate(**request_dict)
|
||||||
self.session.add(template)
|
self.session.add(template)
|
||||||
await self.session.flush()
|
await self.session.flush()
|
||||||
@@ -97,6 +125,11 @@ class BarcodeService(BaseService):
|
|||||||
# add attributes to template
|
# add attributes to template
|
||||||
for attribute in attributes:
|
for attribute in attributes:
|
||||||
template.attributes.append(attribute)
|
template.attributes.append(attribute)
|
||||||
|
for additional_attribute in request.additional_attributes:
|
||||||
|
template.additional_attributes.append(
|
||||||
|
BarcodeTemplateAdditionalField(**additional_attribute.dict(),
|
||||||
|
barcode_template_id=template.id)
|
||||||
|
)
|
||||||
await self.session.commit()
|
await self.session.commit()
|
||||||
return BarcodeTemplateCreateResponse(message='Шаблон успешно создан',
|
return BarcodeTemplateCreateResponse(message='Шаблон успешно создан',
|
||||||
ok=True,
|
ok=True,
|
||||||
@@ -126,6 +159,10 @@ class BarcodeService(BaseService):
|
|||||||
request_dict = request.dict()
|
request_dict = request.dict()
|
||||||
del request_dict['id']
|
del request_dict['id']
|
||||||
del request_dict['attribute_ids']
|
del request_dict['attribute_ids']
|
||||||
|
del request_dict['additional_attributes']
|
||||||
|
del request_dict['size']
|
||||||
|
request_dict['size_id'] = request.size.id
|
||||||
|
|
||||||
await self.session.execute(
|
await self.session.execute(
|
||||||
update(BarcodeTemplate)
|
update(BarcodeTemplate)
|
||||||
.where(BarcodeTemplate.id == request.id)
|
.where(BarcodeTemplate.id == request.id)
|
||||||
@@ -151,6 +188,24 @@ class BarcodeService(BaseService):
|
|||||||
await self.session.execute(stmt)
|
await self.session.execute(stmt)
|
||||||
await self.session.flush()
|
await self.session.flush()
|
||||||
|
|
||||||
|
# working with additional attributes
|
||||||
|
# deleting additioanl attributes that is deleted
|
||||||
|
template_additional_attributes = set([attribute.name for attribute in template.additional_attributes])
|
||||||
|
request_additional_attributes = set([attribute.name for attribute in request.additional_attributes])
|
||||||
|
new_additional_attributes = request_additional_attributes.difference(template_additional_attributes)
|
||||||
|
deleted_additional_attributes = template_additional_attributes.difference(request_additional_attributes)
|
||||||
|
for attribute in template.additional_attributes:
|
||||||
|
if attribute.name not in deleted_additional_attributes:
|
||||||
|
continue
|
||||||
|
await self.session.delete(attribute)
|
||||||
|
for new_attribute in request.additional_attributes:
|
||||||
|
if new_attribute.name not in new_additional_attributes:
|
||||||
|
continue
|
||||||
|
template.additional_attributes.append(
|
||||||
|
BarcodeTemplateAdditionalField(**new_attribute.dict(),
|
||||||
|
barcode_template_id=template.id)
|
||||||
|
)
|
||||||
|
|
||||||
await self.session.commit()
|
await self.session.commit()
|
||||||
return BarcodeTemplateUpdateResponse(message='Шаблон успешно обновлен',
|
return BarcodeTemplateUpdateResponse(message='Шаблон успешно обновлен',
|
||||||
ok=True)
|
ok=True)
|
||||||
@@ -196,4 +251,13 @@ class BarcodeService(BaseService):
|
|||||||
return BarcodeTemplateCreateResponse(message=str(e),
|
return BarcodeTemplateCreateResponse(message=str(e),
|
||||||
ok=False,
|
ok=False,
|
||||||
id=-1)
|
id=-1)
|
||||||
|
|
||||||
|
# endregion
|
||||||
|
|
||||||
|
# region Template sizes
|
||||||
|
async def get_all_barcode_template_sizes(self) -> GetAllBarcodeTemplateSizesResponse:
|
||||||
|
stmt = select(BarcodeTemplateSize).order_by(BarcodeTemplateSize.id)
|
||||||
|
query = await self.session.execute(stmt)
|
||||||
|
sizes = query.scalars().all()
|
||||||
|
return GetAllBarcodeTemplateSizesResponse(sizes=sizes)
|
||||||
# endregion
|
# endregion
|
||||||
|
|||||||
Reference in New Issue
Block a user