diff --git a/barcodes/attributes/__init__.py b/barcodes/attributes/__init__.py index aa69432..5b2d20a 100644 --- a/barcodes/attributes/__init__.py +++ b/barcodes/attributes/__init__.py @@ -1,17 +1,8 @@ -from abc import abstractmethod, ABC - -from models import Product from .article_attribute_getter import ArticleAttributeGetter from .client_name_attribute_getter import ClientNameAttributeGetter from .name_attribute_getter import NameAttributeGetter -class BaseAttributeGetter(ABC): - @abstractmethod - def get_value(self, product: Product): - pass - - class AttributeWriterFactory: @staticmethod def get_writer(key: str): @@ -22,3 +13,5 @@ class AttributeWriterFactory: return ArticleAttributeGetter() case 'client.name': return ClientNameAttributeGetter() + case _: + return None diff --git a/barcodes/attributes/article_attribute_getter.py b/barcodes/attributes/article_attribute_getter.py index a103da9..3d0090c 100644 --- a/barcodes/attributes/article_attribute_getter.py +++ b/barcodes/attributes/article_attribute_getter.py @@ -1,4 +1,4 @@ -from barcodes.attributes import BaseAttributeGetter +from barcodes.attributes.base import BaseAttributeGetter from models import Product diff --git a/barcodes/attributes/base.py b/barcodes/attributes/base.py new file mode 100644 index 0000000..71a5be6 --- /dev/null +++ b/barcodes/attributes/base.py @@ -0,0 +1,9 @@ +from abc import ABC, abstractmethod + +from models import Product + + +class BaseAttributeGetter(ABC): + @abstractmethod + def get_value(self, product: Product): + pass diff --git a/barcodes/attributes/client_name_attribute_getter.py b/barcodes/attributes/client_name_attribute_getter.py index 826b763..cc9254d 100644 --- a/barcodes/attributes/client_name_attribute_getter.py +++ b/barcodes/attributes/client_name_attribute_getter.py @@ -1,4 +1,4 @@ -from barcodes.attributes import BaseAttributeGetter +from barcodes.attributes.base import BaseAttributeGetter from models import Product, Client diff --git a/barcodes/attributes/name_attribute_getter.py b/barcodes/attributes/name_attribute_getter.py index ce0232a..f91953a 100644 --- a/barcodes/attributes/name_attribute_getter.py +++ b/barcodes/attributes/name_attribute_getter.py @@ -1,4 +1,4 @@ -from barcodes.attributes import BaseAttributeGetter +from barcodes.attributes.base import BaseAttributeGetter from models import Product diff --git a/barcodes/generator/__init__.py b/barcodes/generator/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/barcodes/generator/base.py b/barcodes/generator/base.py new file mode 100644 index 0000000..3636c73 --- /dev/null +++ b/barcodes/generator/base.py @@ -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 diff --git a/barcodes/generator/default_generator.py b/barcodes/generator/default_generator.py new file mode 100644 index 0000000..4256e5d --- /dev/null +++ b/barcodes/generator/default_generator.py @@ -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 diff --git a/models/barcode.py b/models/barcode.py index d3448e6..d97081d 100644 --- a/models/barcode.py +++ b/models/barcode.py @@ -20,22 +20,31 @@ class BarcodeTemplateAdditionalField(BaseModel): 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): __tablename__ = 'barcode_templates' id = Column(Integer, autoincrement=True, primary_key=True, index=True) name = Column(String, nullable=False, index=True, comment='Название шаблона') attributes = relationship('BarcodeTemplateAttribute', secondary=barcode_template_attribute_link, - cascade="all", lazy='selectin' ) - additional_fields = relationship('BarcodeTemplateAdditionalField', - lazy='selectin', - back_populates='barcode_template', - cascade="all") + additional_attributes = relationship('BarcodeTemplateAdditionalField', + lazy='selectin', + back_populates='barcode_template', + cascade="all, delete") is_default = Column(Boolean, nullable=False, default=False, comment='По умолчанию') - # size - width = Column(Integer, nullable=False, comment='Ширина в мм') - height = Column(Integer, nullable=False, comment='Высота в мм') + size_id = Column(Integer, ForeignKey('barcode_template_sizes.id'), nullable=False) + size = relationship('BarcodeTemplateSize', lazy='joined') diff --git a/models/secondary.py b/models/secondary.py index 6e77a01..15536a9 100644 --- a/models/secondary.py +++ b/models/secondary.py @@ -33,23 +33,6 @@ class DealProduct(BaseModel): 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_links', BaseModel.metadata, diff --git a/routers/barcode.py b/routers/barcode.py index ba20101..79d3e3e 100644 --- a/routers/barcode.py +++ b/routers/barcode.py @@ -11,7 +11,7 @@ from schemas.barcode import (GetBarcodeTemplateByIdResponse, CreateBarcodeTemplateAttributeResponse, CreateBarcodeTemplateAttributeRequest, BarcodeTemplateUpdateResponse, BarcodeTemplateUpdateRequest, GetAllBarcodeTemplatesResponse, BarcodeTemplateDeleteRequest, - BarcodeTemplateDeleteResponse) + BarcodeTemplateDeleteResponse, GetAllBarcodeTemplateSizesResponse) from services.barcode import BarcodeService barcode_router = APIRouter( @@ -104,4 +104,18 @@ async def create_barcode_template_attribute( session: Annotated[AsyncSession, Depends(get_session)] ): 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 diff --git a/routers/product.py b/routers/product.py index ea6828e..8150214 100644 --- a/routers/product.py +++ b/routers/product.py @@ -5,9 +5,11 @@ from sqlalchemy.ext.asyncio import AsyncSession import utils.dependecies from backend.session import get_session +from schemas.barcode import GetProductBarcodeResponse, GetProductBarcodeRequest from schemas.base import PaginationSchema from schemas.product import * from services.auth import get_current_user +from services.barcode import BarcodeService from services.product import ProductService product_router = APIRouter( @@ -111,3 +113,15 @@ async def generate_product_barcode( session: Annotated[AsyncSession, Depends(get_session)] ): 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) diff --git a/schemas/barcode.py b/schemas/barcode.py index 0beec07..e3a5b02 100644 --- a/schemas/barcode.py +++ b/schemas/barcode.py @@ -10,6 +10,14 @@ class BarcodeTemplateAttributeSchema(CustomModelCamel): name: str +class BarcodeTemplateSizeSchema(CustomModelCamel): + id: int + name: str + key: str + width: int + height: int + + class BarcodeTemplateAdditionalAttributeSchema(CustomModelCamel): name: str value: str @@ -18,8 +26,7 @@ class BarcodeTemplateAdditionalAttributeSchema(CustomModelCamel): class BaseBarcodeTemplateSchema(CustomModelCamel): name: str is_default: bool - width: int - height: int + size: BarcodeTemplateSizeSchema additional_attributes: list[BarcodeTemplateAdditionalAttributeSchema] @@ -102,4 +109,8 @@ class BarcodeTemplateDeleteResponse(OkMessageSchema): class GetProductBarcodeResponse(CustomModelCamel): barcode: BarcodeSchema + + +class GetAllBarcodeTemplateSizesResponse(CustomModelCamel): + sizes: list[BarcodeTemplateSizeSchema] # endregion diff --git a/services/barcode.py b/services/barcode.py index 7bb57ad..a64a9cf 100644 --- a/services/barcode.py +++ b/services/barcode.py @@ -1,7 +1,9 @@ from sqlalchemy import select, update, insert 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 services.base import BaseService @@ -9,21 +11,7 @@ from services.base import BaseService class BarcodeService(BaseService): # region Barcode - async def get_barcode(self, request: GetProductBarcodeRequest) -> GetProductBarcodeResponse: - # 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 + async def _get_barcode_template(self, request: GetProductBarcodeRequest, product: Product) -> BarcodeTemplate: if request.barcode_template_id: stmt = select(BarcodeTemplate).filter(BarcodeTemplate.id == request.barcode_template_id) query = await self.session.execute(stmt) @@ -41,9 +29,46 @@ class BarcodeService(BaseService): if not barcode_template: raise ValueError('Стандартный шаблон не найден') barcode_template: BarcodeTemplate + return barcode_template - - # endregion + async def get_barcode(self, request: GetProductBarcodeRequest) -> GetProductBarcodeResponse: + # 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 @@ -84,6 +109,9 @@ class BarcodeService(BaseService): # create template then add attributes request_dict = request.dict() 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) self.session.add(template) await self.session.flush() @@ -97,6 +125,11 @@ class BarcodeService(BaseService): # add attributes to template for attribute in attributes: 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() return BarcodeTemplateCreateResponse(message='Шаблон успешно создан', ok=True, @@ -126,6 +159,10 @@ class BarcodeService(BaseService): request_dict = request.dict() del request_dict['id'] 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( update(BarcodeTemplate) .where(BarcodeTemplate.id == request.id) @@ -151,6 +188,24 @@ class BarcodeService(BaseService): await self.session.execute(stmt) 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() return BarcodeTemplateUpdateResponse(message='Шаблон успешно обновлен', ok=True) @@ -196,4 +251,13 @@ class BarcodeService(BaseService): return BarcodeTemplateCreateResponse(message=str(e), ok=False, 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