feat: deal status history table

This commit is contained in:
2025-09-20 10:07:56 +04:00
parent 44f315b4a0
commit 6b3d124adf
8 changed files with 45 additions and 7 deletions

View File

@ -10,6 +10,6 @@ from .built_in_module import ( # noqa: F401
) )
from .deal import Deal as Deal from .deal import Deal as Deal
from .project import Project as Project from .project import Project as Project
from .status import Status as Status, CardStatusHistory as CardStatusHistory from .status import Status as Status, DealStatusHistory as DealStatusHistory
configure_mappers() configure_mappers()

View File

@ -7,7 +7,7 @@ from models.base import BaseModel
from models.mixins import SoftDeleteMixin, CreatedAtMixin, IdMixin from models.mixins import SoftDeleteMixin, CreatedAtMixin, IdMixin
if TYPE_CHECKING: if TYPE_CHECKING:
from models import Status, Board, CardStatusHistory from models import Status, Board, DealStatusHistory
class Deal(BaseModel, IdMixin, SoftDeleteMixin, CreatedAtMixin): class Deal(BaseModel, IdMixin, SoftDeleteMixin, CreatedAtMixin):
@ -28,7 +28,7 @@ class Deal(BaseModel, IdMixin, SoftDeleteMixin, CreatedAtMixin):
) )
board: Mapped["Board"] = relationship(back_populates="deals") board: Mapped["Board"] = relationship(back_populates="deals")
status_history: Mapped[list["CardStatusHistory"]] = relationship( status_history: Mapped[list["DealStatusHistory"]] = relationship(
back_populates="deal", back_populates="deal",
cascade="all, delete-orphan", cascade="all, delete-orphan",
lazy="noload", lazy="noload",

View File

@ -20,7 +20,7 @@ class Status(BaseModel, IdMixin, SoftDeleteMixin):
board: Mapped["Board"] = relationship(back_populates="statuses") board: Mapped["Board"] = relationship(back_populates="statuses")
class CardStatusHistory(BaseModel, IdMixin, CreatedAtMixin): class DealStatusHistory(BaseModel, IdMixin, CreatedAtMixin):
__tablename__ = "status_history" __tablename__ = "status_history"
deal_id: Mapped[int] = mapped_column(ForeignKey("deals.id"), nullable=False) deal_id: Mapped[int] = mapped_column(ForeignKey("deals.id"), nullable=False)

View File

@ -1,6 +1,6 @@
from sqlalchemy.orm import joinedload from sqlalchemy.orm import joinedload
from models import Deal, CardStatusHistory, Board from models import Deal, Board, DealStatusHistory
from repositories.mixins import * from repositories.mixins import *
from schemas.base import SortDir from schemas.base import SortDir
from schemas.deal import UpdateDealSchema, CreateDealSchema from schemas.deal import UpdateDealSchema, CreateDealSchema
@ -68,7 +68,7 @@ class DealRepository(
if data.status_id and deal.status_id != data.status_id: if data.status_id and deal.status_id != data.status_id:
deal.status_history.append( deal.status_history.append(
CardStatusHistory( DealStatusHistory(
from_status_id=deal.status_id, from_status_id=deal.status_id,
to_status_id=data.status_id, to_status_id=data.status_id,
) )

View File

@ -1,6 +1,6 @@
from sqlalchemy import func from sqlalchemy import func
from models import Status, Deal from models import Status, Deal, DealStatusHistory
from repositories.mixins import * from repositories.mixins import *
from schemas.status import UpdateStatusSchema, CreateStatusSchema from schemas.status import UpdateStatusSchema, CreateStatusSchema
@ -29,3 +29,12 @@ class StatusRepository(RepCrudMixin[Status, CreateStatusSchema, UpdateStatusSche
async def update(self, status: Status, data: UpdateStatusSchema) -> Status: async def update(self, status: Status, data: UpdateStatusSchema) -> Status:
return await self._apply_update_data_to_model(status, data, True) return await self._apply_update_data_to_model(status, data, True)
async def get_status_history(self, deal_id: int) -> list[DealStatusHistory]:
stmt = (
select(DealStatusHistory)
.where(DealStatusHistory.deal_id == deal_id)
.order_by(DealStatusHistory.created_at)
)
result = await self.session.execute(stmt)
return list(result.scalars().all())

View File

@ -54,3 +54,15 @@ async def delete_status(
pk: int = Path(), pk: int = Path(),
): ):
return await StatusService(session).delete(pk) return await StatusService(session).delete(pk)
@router.get(
"/history/{dealId}",
response_model=GetStatusHistoryResponse,
operation_id="get_status_history",
)
async def get_status_history(
session: SessionDependency,
deal_id: int = Path(alias="dealId"),
):
return await StatusService(session).get_status_history(deal_id)

View File

@ -1,3 +1,4 @@
from datetime import datetime
from typing import Optional from typing import Optional
from schemas.base import BaseSchema, BaseResponse from schemas.base import BaseSchema, BaseResponse
@ -23,6 +24,14 @@ class UpdateStatusSchema(BaseSchema):
lexorank: Optional[str] = None lexorank: Optional[str] = None
class StatusHistorySchema(BaseSchema):
id: int
created_at: datetime
from_status: StatusSchema
to_status: StatusSchema
deal_id: int
# endregion # endregion
# region Requests # region Requests
@ -57,4 +66,8 @@ class DeleteStatusResponse(BaseResponse):
pass pass
class GetStatusHistoryResponse(BaseSchema):
items: list[StatusHistorySchema]
# endregion # endregion

View File

@ -19,3 +19,7 @@ class StatusService(
deals_count = await self.repository.get_deals_count(status.id) deals_count = await self.repository.get_deals_count(status.id)
is_soft_needed: bool = deals_count > 0 is_soft_needed: bool = deals_count > 0
return is_soft_needed return is_soft_needed
async def get_status_history(self, deal_id: int) -> GetStatusHistoryResponse:
items = await self.repository.get_status_history(deal_id)
return GetStatusHistoryResponse(items=items)