feat: deal attributes editing

This commit is contained in:
2025-10-27 10:02:02 +04:00
parent a579ae4145
commit 759a8d6478
6 changed files with 183 additions and 5 deletions

View File

@ -72,9 +72,14 @@ class Attribute(BaseModel, IdMixin, SoftDeleteMixin):
) )
class AttributeValue(BaseModel, IdMixin): class AttributeValue(BaseModel):
__tablename__ = "attribute_values" __tablename__ = "attribute_values"
id: Mapped[int] = mapped_column(
primary_key=True,
autoincrement=True,
)
value: Mapped[Optional[dict[str, any]]] = mapped_column(JSONB) value: Mapped[Optional[dict[str, any]]] = mapped_column(JSONB)
deal_id: Mapped[int] = mapped_column( deal_id: Mapped[int] = mapped_column(

View File

@ -1,8 +1,15 @@
from sqlalchemy import and_
from sqlalchemy.orm import joinedload from sqlalchemy.orm import joinedload
from models import Attribute, AttributeLabel, AttributeType from models import (
Attribute,
AttributeLabel,
AttributeType,
AttributeValue,
module_attribute,
)
from repositories.mixins import * from repositories.mixins import *
from schemas.attribute import CreateAttributeSchema, UpdateAttributeSchema from schemas.attribute import CreateAttributeSchema, UpdateAttributeSchema, UpdateDealModuleAttributeSchema
from utils.exceptions import ForbiddenException from utils.exceptions import ForbiddenException
@ -71,3 +78,75 @@ class AttributeRepository(
stmt = select(AttributeType).where(AttributeType.is_deleted.is_(False)) stmt = select(AttributeType).where(AttributeType.is_deleted.is_(False))
result = await self.session.execute(stmt) result = await self.session.execute(stmt)
return list(result.scalars().all()) return list(result.scalars().all())
async def get_deal_module_attributes(
self, deal_id: int, module_id: int
) -> list[tuple[Attribute, AttributeValue, AttributeLabel]]:
stmt = (
select(Attribute, AttributeValue, AttributeLabel)
.outerjoin(
AttributeValue,
and_(
AttributeValue.attribute_id == Attribute.id,
AttributeValue.module_id == module_id,
AttributeValue.deal_id == deal_id,
),
)
.outerjoin(
AttributeLabel,
and_(
AttributeLabel.attribute_id == Attribute.id,
AttributeLabel.module_id == module_id,
),
)
.join(
module_attribute,
and_(
module_attribute.c.attribute_id == Attribute.id,
module_attribute.c.module_id == module_id,
),
)
)
result = await self.session.execute(stmt)
return list(result.all())
async def _get_deal_attribute_values(
self, deal_id: int, module_id: int
) -> list[AttributeValue]:
stmt = (
select(AttributeValue)
.join(Attribute, AttributeValue.attribute_id == Attribute.id)
.where(
AttributeValue.deal_id == deal_id,
AttributeValue.module_id == module_id,
Attribute.is_deleted.is_(False),
)
)
result = await self.session.execute(stmt)
return list(result.scalars().all())
async def update_or_create_deal_attribute_values(
self,
deal_id: int,
module_id: int,
attributes: list[UpdateDealModuleAttributeSchema],
):
old_deal_attribute_values: list[AttributeValue] = await self._get_deal_attribute_values(deal_id, module_id)
dict_old_attrs: dict[int, AttributeValue] = {obj.attribute_id: obj for obj in old_deal_attribute_values}
for attribute in attributes:
if attribute.attribute_id in dict_old_attrs:
# update
attribute_value = dict_old_attrs[attribute.attribute_id]
attribute_value.value = attribute.value
else:
# create
attribute_value = AttributeValue(
attribute_id=attribute.attribute_id,
deal_id=deal_id,
module_id=module_id,
value=attribute.value,
)
self.session.add(attribute_value)
await self.session.commit()

View File

@ -46,7 +46,7 @@ class ModuleRepository(
Module.is_deleted.is_(False), Module.is_deleted.is_(False),
or_(Attribute.id.is_(None), Attribute.is_deleted.is_(False)), or_(Attribute.id.is_(None), Attribute.is_deleted.is_(False)),
) )
.order_by(Attribute.id) .order_by(Module.id, Attribute.id)
) )
async def get_with_attributes_as_tuples( async def get_with_attributes_as_tuples(

View File

@ -76,3 +76,34 @@ async def get_attribute_types(
session: SessionDependency, session: SessionDependency,
): ):
return await AttributeService(session).get_attribute_types() return await AttributeService(session).get_attribute_types()
@router.get(
"/deal/{dealId}/module/{moduleId}",
response_model=GetDealModuleAttributesResponse,
operation_id="get_deal_module_attributes",
)
async def get_deal_module_attributes(
session: SessionDependency,
deal_id: int = Path(alias="dealId"),
module_id: int = Path(alias="moduleId"),
):
return await AttributeService(session).get_deal_module_attributes(
deal_id, module_id
)
@router.post(
"/deal/{dealId}/module/{moduleId}",
response_model=UpdateDealModuleAttributesResponse,
operation_id="update_deal_module_attributes",
)
async def update_deal_module_attributes(
session: SessionDependency,
request: UpdateDealModuleAttributesRequest,
deal_id: int = Path(alias="dealId"),
module_id: int = Path(alias="moduleId"),
):
return await AttributeService(session).update_deal_module_attributes(
deal_id, module_id, request
)

View File

@ -44,6 +44,25 @@ class ModuleAttributeSchema(AttributeSchema):
original_label: str original_label: str
class DealModuleAttributeSchema(BaseSchema):
attribute_id: int
label: str
original_label: str
value: Optional[dict[str, Any]]
type: AttributeTypeSchema
default_value: dict[str, Any]
description: str
is_applicable_to_group: bool
is_shown_on_dashboard: bool
is_highlight_if_expired: bool
is_nullable: bool
class UpdateDealModuleAttributeSchema(BaseSchema):
attribute_id: int
value: Optional[dict[str, Any]]
# endregion # endregion
# region Request # region Request
@ -63,6 +82,9 @@ class UpdateAttributeLabelRequest(BaseSchema):
label: str label: str
class UpdateDealModuleAttributesRequest(BaseSchema):
attributes: list[UpdateDealModuleAttributeSchema]
# endregion # endregion
# region Response # region Response
@ -92,4 +114,11 @@ class GetAllAttributeTypesResponse(BaseSchema):
items: list[AttributeTypeSchema] items: list[AttributeTypeSchema]
class GetDealModuleAttributesResponse(BaseSchema):
attributes: list[DealModuleAttributeSchema]
class UpdateDealModuleAttributesResponse(BaseResponse):
pass
# endregion # endregion

View File

@ -1,4 +1,4 @@
from models import Attribute from models import Attribute, AttributeValue, AttributeLabel
from repositories import AttributeRepository from repositories import AttributeRepository
from schemas.attribute import * from schemas.attribute import *
from services.mixins import * from services.mixins import *
@ -29,3 +29,37 @@ class AttributeService(
return GetAllAttributeTypesResponse( return GetAllAttributeTypesResponse(
items=[AttributeTypeSchema.model_validate(t) for t in types] items=[AttributeTypeSchema.model_validate(t) for t in types]
) )
async def get_deal_module_attributes(
self, deal_id: int, module_id: int
) -> GetDealModuleAttributesResponse:
deal_attributes: list[
tuple[Attribute, AttributeValue, AttributeLabel]
] = await self.repository.get_deal_module_attributes(deal_id, module_id)
attributes = []
for attr, attr_value, attr_label in deal_attributes:
attribute = DealModuleAttributeSchema(
attribute_id=attr.id,
label=attr_label.label if attr_label else attr.label,
original_label=attr.label,
value=attr_value.value if attr_value else attr.default_value,
type=AttributeTypeSchema.model_validate(attr.type),
default_value=attr.default_value,
description=attr.description,
is_applicable_to_group=attr.is_applicable_to_group,
is_shown_on_dashboard=attr.is_shown_on_dashboard,
is_highlight_if_expired=attr.is_highlight_if_expired,
is_nullable=attr.is_nullable,
)
attributes.append(attribute)
return GetDealModuleAttributesResponse(attributes=attributes)
async def update_deal_module_attributes(
self, deal_id: int, module_id: int, request: UpdateDealModuleAttributesRequest
) -> UpdateDealModuleAttributesResponse:
await self.repository.update_or_create_deal_attribute_values(
deal_id, module_id, request.attributes
)
return UpdateDealModuleAttributesResponse(message="Успешно сохранено")