This commit is contained in:
2024-05-09 01:32:37 +03:00
parent 3e83cf6f92
commit 61d27d2389
16 changed files with 179 additions and 115 deletions

View File

@@ -1,21 +1,21 @@
from abc import abstractmethod, ABC # from abc import abstractmethod, ABC
#
from .article_attribute_writer import ArticleAttributeWriter # from .article_attribute_writer import ArticleAttributeWriter
from .name_attribute_writer import NameAttributeWriter # from .name_attribute_writer import NameAttributeWriter
from ..barcode import Barcode # from ..barcode import Barcode
#
#
class BaseAttributeWriter(ABC): # class BaseAttributeWriter(ABC):
@abstractmethod # @abstractmethod
def write(self, barcode: Barcode): # def write(self, barcode: Barcode):
pass # pass
#
#
class AttributeWriterFactory: # class AttributeWriterFactory:
@staticmethod # @staticmethod
def get_writer(key: str): # def get_writer(key: str):
match key: # match key:
case 'name': # case 'name':
return NameAttributeWriter() # return NameAttributeWriter()
case 'article': # case 'article':
return ArticleAttributeWriter() # return ArticleAttributeWriter()

View File

@@ -1,7 +1,7 @@
from barcodes.attributes import BaseAttributeWriter # from barcodes.attributes import BaseAttributeWriter
from barcodes.barcode import Barcode # from barcodes.barcode import Barcode
#
#
class ArticleAttributeWriter(BaseAttributeWriter): # class ArticleAttributeWriter(BaseAttributeWriter):
def write(self, barcode: Barcode): # def write(self, barcode: Barcode):
pass # pass

View File

@@ -1,7 +1,7 @@
from barcodes.attributes import BaseAttributeWriter # from barcodes.attributes import BaseAttributeWriter
from barcodes.barcode import Barcode # from barcodes.barcode import Barcode
#
#
class NameAttributeWriter(BaseAttributeWriter): # class NameAttributeWriter(BaseAttributeWriter):
def write(self, barcode: Barcode): # def write(self, barcode: Barcode):
pass # pass

View File

@@ -1,15 +1,15 @@
from .attributes import AttributeWriterFactory # from .attributes import AttributeWriterFactory
from models import ProductBarcode, BarcodeTemplate, BarcodeTemplateAttribute # from models import ProductBarcode, BarcodeTemplate, BarcodeTemplateAttribute
#
#
class Barcode: # class Barcode:
def __init__(self, session, barcode: ProductBarcode): # def __init__(self, session, barcode: ProductBarcode):
self.session = session # self.session = session
self.barcode = barcode # self.barcode = barcode
#
def render(self, template: BarcodeTemplate): # def render(self, template: BarcodeTemplate):
for attribute in template.attributes: # for attribute in template.attributes:
attribute: BarcodeTemplateAttribute # attribute: BarcodeTemplateAttribute
writer = AttributeWriterFactory.get_writer(attribute.key) # writer = AttributeWriterFactory.get_writer(attribute.key)
writer.write(self) # writer.write(self)
#

View File

@@ -1,6 +1,6 @@
from models import ProductBarcode # from models import ProductBarcode
#
#
class BarcodeGenerator: # class BarcodeGenerator:
def __init__(self, barcode: ProductBarcode): # def __init__(self, barcode: ProductBarcode):
self.barcode = barcode # self.barcode = barcode

View File

@@ -1,7 +1,7 @@
from sqlalchemy import Integer, Column, String, Boolean from sqlalchemy import Integer, Column, String, Boolean, ForeignKey
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from models import BaseModel from models import BaseModel, barcode_template_attribute_link
class BarcodeTemplateAttribute(BaseModel): class BarcodeTemplateAttribute(BaseModel):
@@ -11,14 +11,31 @@ class BarcodeTemplateAttribute(BaseModel):
name = Column(String, nullable=False, index=True, comment='Метка атрибута') name = Column(String, nullable=False, index=True, comment='Метка атрибута')
class BarcodeTemplateAdditionalField(BaseModel):
__tablename__ = 'barcode_template_additional_fields'
id = Column(Integer, autoincrement=True, primary_key=True, index=True)
name = Column(String, nullable=False)
value = Column(String, nullable=False)
barcode_template_id = Column(Integer, ForeignKey('barcode_templates.id'), nullable=False)
barcode_template = relationship('BarcodeTemplate', back_populates='additional_fields')
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('BarcodeTemplateAttributeLink', attributes = relationship('BarcodeTemplateAttribute',
back_populates='barcode_template', secondary=barcode_template_attribute_link,
cascade="all, delete-orphan", # back_populates='barcode_template',
lazy='joined') # cascade="all, delete-orphan",
lazy='selectin'
)
additional_fields = relationship('BarcodeTemplateAdditionalField',
back_populates='barcode_template',
lazy='selectin'
)
is_default = Column(Boolean, nullable=False, default=False, comment='По умолчанию') is_default = Column(Boolean, nullable=False, default=False, comment='По умолчанию')
# size # size

View File

@@ -14,7 +14,7 @@ class Client(BaseModel):
details = relationship('ClientDetails', uselist=False, back_populates='client', cascade='all, delete') details = relationship('ClientDetails', uselist=False, back_populates='client', cascade='all, delete')
barcode_template_id = Column(Integer, ForeignKey('barcode_templates.id'), nullable=True) barcode_template_id = Column(Integer, ForeignKey('barcode_templates.id'), nullable=True)
barcode_template = relationship('BarcodeTemplate') barcode_template = relationship('BarcodeTemplate', lazy='selectin')
class ClientDetails(BaseModel): class ClientDetails(BaseModel):

View File

@@ -1,10 +1,9 @@
from enum import IntEnum, unique from enum import IntEnum, unique
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Boolean, Sequence from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Boolean
from sqlalchemy.orm import relationship, backref from sqlalchemy.orm import relationship, backref
from models.base import BaseModel, metadata from models.base import BaseModel
from models.utils import add_sequence_to_model
@unique @unique

View File

@@ -1,11 +1,7 @@
from sqlalchemy import Column, Integer, String, ForeignKey, Sequence from sqlalchemy import Column, Integer, String, ForeignKey, Sequence
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from models import BaseModel, metadata from models import BaseModel
deal_rank_seq = Sequence('test_ochko', start=1, increment=1, metadata=metadata)
sequence = Sequence('my_sequence_name')
class Product(BaseModel): class Product(BaseModel):

View File

@@ -33,18 +33,26 @@ class DealProduct(BaseModel):
quantity = Column(Integer, nullable=False, comment='Кол-во продукта') quantity = Column(Integer, nullable=False, comment='Кол-во продукта')
class BarcodeTemplateAttributeLink(BaseModel): # class BarcodeTemplateAttributeLink(BaseModel):
__tablename__ = 'barcode_template_attributes_links' # __tablename__ = 'barcode_template_attributes_links'
barcode_template_id = Column(Integer, # barcode_template_id = Column(Integer,
ForeignKey('barcode_templates.id'), # ForeignKey('barcode_templates.id'),
nullable=False, # nullable=False,
comment='ID Шаблона ШК', # comment='ID Шаблона ШК',
primary_key=True) # primary_key=True)
barcode_template = relationship('BarcodeTemplate', back_populates='attributes') # barcode_template = relationship('BarcodeTemplate',
# # back_populates='attributes'
attribute_id = Column(Integer, # )
ForeignKey('barcode_template_attributes.id'), #
nullable=False, # attribute_id = Column(Integer,
comment='ID Атрибута', # ForeignKey('barcode_template_attributes.id'),
primary_key=True) # nullable=False,
attribute = relationship('BarcodeTemplateAttribute') # comment='ID Атрибута',
# primary_key=True)
# attribute = relationship('BarcodeTemplateAttribute')
barcode_template_attribute_link = Table(
'barcode_template_attribute_links',
BaseModel.metadata,
Column('barcode_template_id', ForeignKey('barcode_templates.id')),
Column('attribute_id', ForeignKey('barcode_template_attributes.id'))
)

View File

@@ -10,7 +10,8 @@ from schemas.barcode import (GetBarcodeTemplateByIdResponse,
BarcodeTemplateCreateRequest, GetAllBarcodeTemplateAttributesResponse, BarcodeTemplateCreateRequest, GetAllBarcodeTemplateAttributesResponse,
CreateBarcodeTemplateAttributeResponse, CreateBarcodeTemplateAttributeRequest, CreateBarcodeTemplateAttributeResponse, CreateBarcodeTemplateAttributeRequest,
BarcodeTemplateUpdateResponse, BarcodeTemplateUpdateRequest, BarcodeTemplateUpdateResponse, BarcodeTemplateUpdateRequest,
GetAllBarcodeTemplatesResponse) GetAllBarcodeTemplatesResponse, BarcodeTemplateDeleteRequest,
BarcodeTemplateDeleteResponse)
from services.barcode import BarcodeService from services.barcode import BarcodeService
barcode_router = APIRouter( barcode_router = APIRouter(
@@ -67,6 +68,18 @@ async def update_barcode_template(
return await BarcodeService(session).update_barcode_template(request) return await BarcodeService(session).update_barcode_template(request)
@barcode_router.post(
'/template/delete',
response_model=BarcodeTemplateDeleteResponse,
operation_id='delete_barcode_template'
)
async def delete_barcode_template(
request: BarcodeTemplateDeleteRequest,
session: Annotated[AsyncSession, Depends(get_session)]
):
return await BarcodeService(session).delete_template(request)
# endregion # endregion
# region Template attributes # region Template attributes

View File

@@ -40,6 +40,10 @@ class CreateBarcodeTemplateAttributeRequest(CustomModelCamel):
label: str label: str
class BarcodeTemplateDeleteRequest(CustomModelCamel):
id: int
# endregion # endregion
# region Responses # region Responses
@@ -66,4 +70,8 @@ class GetAllBarcodeTemplatesResponse(CustomModelCamel):
class GetAllBarcodeTemplateAttributesResponse(CustomModelCamel): class GetAllBarcodeTemplateAttributesResponse(CustomModelCamel):
attributes: list[BarcodeTemplateAttributeSchema] attributes: list[BarcodeTemplateAttributeSchema]
class BarcodeTemplateDeleteResponse(OkMessageSchema):
pass
# endregion # endregion

View File

@@ -2,6 +2,7 @@ from typing import List
from pydantic import validator, field_validator from pydantic import validator, field_validator
from schemas.barcode import BarcodeTemplateSchema
from schemas.base import CustomModelCamel, OkMessageSchema from schemas.base import CustomModelCamel, OkMessageSchema
@@ -20,6 +21,7 @@ class ClientDetailsSchema(CustomModelCamel):
class ClientSchema(CustomModelCamel): class ClientSchema(CustomModelCamel):
id: int id: int
name: str name: str
barcode_template: BarcodeTemplateSchema | None = None
details: ClientDetailsSchema | None = None details: ClientDetailsSchema | None = None

View File

@@ -1,13 +1,15 @@
from sqlalchemy import select, update from sqlalchemy import select, update, insert
from sqlalchemy.orm import selectinload
from models import BarcodeTemplate, BarcodeTemplateAttribute, BarcodeTemplateAttributeLink from models import BarcodeTemplate, BarcodeTemplateAttribute, barcode_template_attribute_link
from schemas.barcode import (GetBarcodeTemplateByIdRequest, from schemas.barcode import (GetBarcodeTemplateByIdRequest,
GetBarcodeTemplateByIdResponse, GetBarcodeTemplateByIdResponse,
BarcodeTemplateCreateResponse, BarcodeTemplateCreateResponse,
BarcodeTemplateCreateRequest, CreateBarcodeTemplateAttributeRequest, BarcodeTemplateCreateRequest, CreateBarcodeTemplateAttributeRequest,
BarcodeTemplateUpdateResponse, BarcodeTemplateUpdateRequest, BarcodeTemplateUpdateResponse, BarcodeTemplateUpdateRequest,
BarcodeTemplateAttributeSchema, GetAllBarcodeTemplateAttributesResponse, BarcodeTemplateAttributeSchema, GetAllBarcodeTemplateAttributesResponse,
GetAllBarcodeTemplatesResponse) GetAllBarcodeTemplatesResponse, BarcodeTemplateDeleteRequest,
BarcodeTemplateDeleteResponse)
from services.base import BaseService from services.base import BaseService
@@ -23,7 +25,12 @@ class BarcodeService(BaseService):
return query.scalar() return query.scalar()
async def get_all_barcode_templates(self) -> GetAllBarcodeTemplatesResponse: async def get_all_barcode_templates(self) -> GetAllBarcodeTemplatesResponse:
stmt = select(BarcodeTemplate).order_by(BarcodeTemplate.id) stmt = (
select(BarcodeTemplate)
.options(
selectinload(BarcodeTemplate.attributes)
)
.order_by(BarcodeTemplate.id))
query = await self.session.execute(stmt) query = await self.session.execute(stmt)
templates = query.scalars().all() templates = query.scalars().all()
return GetAllBarcodeTemplatesResponse(templates=templates) return GetAllBarcodeTemplatesResponse(templates=templates)
@@ -43,10 +50,12 @@ class BarcodeService(BaseService):
raise ValueError('Шаблон с таким именем уже существует') raise ValueError('Шаблон с таким именем уже существует')
# create template then add attributes # create template then add attributes
template = BarcodeTemplate(name=request.name, is_default=request.is_default) request_dict = request.dict()
del request_dict['attribute_ids']
template = BarcodeTemplate(**request_dict)
self.session.add(template) self.session.add(template)
await self.session.flush() await self.session.flush()
await self.session.refresh(template)
# get all attributes from database # get all attributes from database
stmt = select(BarcodeTemplateAttribute).filter( stmt = select(BarcodeTemplateAttribute).filter(
BarcodeTemplateAttribute.id.in_(request.attribute_ids)) BarcodeTemplateAttribute.id.in_(request.attribute_ids))
@@ -55,12 +64,7 @@ class BarcodeService(BaseService):
# add attributes to template # add attributes to template
for attribute in attributes: for attribute in attributes:
template_attribute_link = BarcodeTemplateAttributeLink( template.attributes.append(attribute)
barcode_template_id=template.id,
attribute_id=attribute.id
)
self.session.add(template_attribute_link)
await self.session.flush()
await self.session.commit() await self.session.commit()
return BarcodeTemplateCreateResponse(message='Шаблон успешно создан', return BarcodeTemplateCreateResponse(message='Шаблон успешно создан',
ok=True, ok=True,
@@ -97,24 +101,22 @@ class BarcodeService(BaseService):
) )
# difference deleted and new # difference deleted and new
template_attributes = set([attribute.attribute_id for attribute in template.attributes]) template_attributes = set([attribute.id for attribute in template.attributes])
request_attributes = set(request.attribute_ids) request_attributes = set(request.attribute_ids)
new_attributes = request_attributes.difference(template_attributes) new_attribute_ids = request_attributes.difference(template_attributes)
deleted_attributes = template_attributes.difference(request_attributes) deleted_attribute_ids = template_attributes.difference(request_attributes)
# delete attributes # delete attributes
for attribute in template.attributes: for attribute in template.attributes:
if attribute.attribute_id not in deleted_attributes: if attribute.id not in deleted_attribute_ids:
continue continue
await self.session.delete(attribute) template.attributes.remove(attribute)
for new_attribute_id in new_attribute_ids:
# add new attributes stmt = insert(barcode_template_attribute_link).values({
for new_attribute in new_attributes: 'barcode_template_id': template.id,
template_attribute_link = BarcodeTemplateAttributeLink( 'attribute_id': new_attribute_id
barcode_template_id=template.id, })
attribute_id=new_attribute await self.session.execute(stmt)
)
self.session.add(template_attribute_link)
await self.session.flush() await self.session.flush()
await self.session.commit() await self.session.commit()
@@ -125,6 +127,15 @@ class BarcodeService(BaseService):
return BarcodeTemplateUpdateResponse(message=str(e), return BarcodeTemplateUpdateResponse(message=str(e),
ok=False) ok=False)
async def delete_template(self, request: BarcodeTemplateDeleteRequest) -> BarcodeTemplateDeleteResponse:
try:
template = await self.session.get(BarcodeTemplate, request.id)
await self.session.delete(template)
await self.session.commit()
return BarcodeTemplateDeleteResponse(ok=True, message='Шаблон успешно удален')
except Exception as e:
return BarcodeTemplateDeleteResponse(ok=False, message=str(e))
# endregion # endregion
# region Template attributes # region Template attributes

View File

@@ -91,7 +91,17 @@ class ClientService(BaseService):
client = await self.get_by_id(request.data.id) client = await self.get_by_id(request.data.id)
if not client: if not client:
return ClientUpdateResponse(ok=False, message='Клиент не найден') return ClientUpdateResponse(ok=False, message='Клиент не найден')
await self.session.execute(update(Client).where(Client.id == client.id).values(name=request.data.name)) request_dict = request.data.dict()
del request_dict['id']
del request_dict['details']
del request_dict['barcode_template']
request_dict['barcode_template_id'] = request.data.barcode_template.id
stmt = (
update(Client)
.where(Client.id == client.id)
.values(**request_dict)
)
await self.session.execute(stmt)
await self.update_details(user, client, request.data.details) await self.update_details(user, client, request.data.details)
await self.session.commit() await self.session.commit()
return ClientUpdateResponse(ok=True, message='Клиент обновлен') return ClientUpdateResponse(ok=True, message='Клиент обновлен')

View File

@@ -21,7 +21,7 @@ class DealService(BaseService):
async def _get_deal_by_id(self, deal_id) -> Union[Deal, None]: async def _get_deal_by_id(self, deal_id) -> Union[Deal, None]:
return await self.session.get(Deal, deal_id) return await self.session.get(Deal, deal_id)
async def _get_rank_for_deal(self, deal_status: DealStatus) -> int: async def _get_rank_for_deal(self, deal_status: DealStatus) -> str:
deal_query = await self.session.execute( deal_query = await self.session.execute(
select(Deal).where(Deal.current_status == deal_status).order_by(Deal.lexorank.desc()).limit(1)) select(Deal).where(Deal.current_status == deal_status).order_by(Deal.lexorank.desc()).limit(1))
deal = deal_query.scalar_one_or_none() deal = deal_query.scalar_one_or_none()
@@ -35,7 +35,7 @@ class DealService(BaseService):
user: User, user: User,
deadline: datetime.datetime = None, deadline: datetime.datetime = None,
rank=None, rank=None,
comment: str = '') -> DealStatusHistory: comment: str = ''):
if not deal.current_status == status: if not deal.current_status == status:
deadline = deadline deadline = deadline
status_change = DealStatusHistory( status_change = DealStatusHistory(