feat: work shifts planning

This commit is contained in:
2025-01-22 18:01:24 +04:00
parent 2c4037c1b5
commit de4c2677aa
6 changed files with 193 additions and 3 deletions

View File

@@ -47,6 +47,7 @@ routers_list = [
routers.task_router,
routers.statistics_router,
routers.work_shifts_router,
routers.work_shifts_planning_router,
routers.transaction_router,
routers.shipping_router,
routers.department_router,

View File

@@ -1,14 +1,14 @@
from datetime import datetime
from datetime import datetime, date
from typing import TYPE_CHECKING
from sqlalchemy import ForeignKey
from sqlalchemy import ForeignKey, Table, Column
from sqlalchemy.sql import expression
from sqlalchemy.orm import Mapped, mapped_column, relationship
from models.base import BaseModel
if TYPE_CHECKING:
from models import User
from models import User, Position
class WorkShift(BaseModel):
@@ -63,3 +63,29 @@ class WorkShiftPause(BaseModel):
back_populates="pauses",
lazy="selectin",
)
work_shifts_positions = Table(
'work_shifts_positions',
BaseModel.metadata,
Column('position_key', ForeignKey('positions.key'), primary_key=True),
Column('work_shift_id', ForeignKey('planned_work_shifts.id'), primary_key=True),
)
class PlannedWorkShift(BaseModel):
__tablename__ = "planned_work_shifts"
id: Mapped[int] = mapped_column(primary_key=True)
shift_date: Mapped[date] = mapped_column(nullable=False, index=True)
created_at: Mapped[datetime] = mapped_column(nullable=False)
user_id: Mapped[int] = mapped_column(ForeignKey('users.id'), nullable=False, index=True)
user: Mapped["User"] = relationship(lazy="selectin", backref="planned_work_shifts")
positions: Mapped[list["Position"]] = relationship(
"Position",
uselist=True,
secondary=work_shifts_positions,
lazy="selectin",
)

View File

@@ -14,6 +14,7 @@ from .time_tracking import time_tracking_router
from .billing import billing_router
from .task import task_router
from .work_shifts import work_shifts_router
from .work_shifts_planning import work_shifts_planning_router
from .statistics import statistics_router
from .transaction import transaction_router
from .shipping import shipping_router

View File

@@ -0,0 +1,34 @@
from fastapi import APIRouter
from backend.dependecies import SessionDependency
from schemas.work_shifts_planning import *
from services.work_shifts_planning import WorkShiftsPlanningService
work_shifts_planning_router = APIRouter(
prefix="/work-shifts-planning",
tags=["work-shifts-planning"],
)
@work_shifts_planning_router.post(
"/",
response_model=GetPlannedWorkShiftsResponse,
operation_id="get_work_shifts",
)
async def get_work_shifts(
session: SessionDependency,
request: GetWorkShiftsPlanningDataRequest,
):
return await WorkShiftsPlanningService(session).get_work_shifts(request)
@work_shifts_planning_router.post(
"/update",
response_model=UpdatePlanningWorkShiftResponse,
operation_id="update_work_shift",
)
async def update_work_shift(
session: SessionDependency,
request: UpdatePlanningWorkShiftRequest,
):
return await WorkShiftsPlanningService(session).update_work_shift(request)

View File

@@ -0,0 +1,47 @@
from datetime import datetime, date
from schemas.base import BaseSchema, OkMessageSchema
from schemas.position import PositionSchema
from schemas.user import UserSchema
# region Entities
class PlannedWorkShiftSchema(BaseSchema):
id: int
shift_date: datetime
positions: list[PositionSchema]
class PlanningTableRow(BaseSchema):
user: UserSchema
shifts: list[PlannedWorkShiftSchema]
# endregion
# region Requests
class GetWorkShiftsPlanningDataRequest(BaseSchema):
date_from: date
date_to: date
class UpdatePlanningWorkShiftRequest(BaseSchema):
shift_date: date
position_keys: list[str]
user_id: int
# endregion
# region Responses
class GetPlannedWorkShiftsResponse(BaseSchema):
shifts: list[PlanningTableRow]
class UpdatePlanningWorkShiftResponse(OkMessageSchema):
pass
# endregion

View File

@@ -0,0 +1,81 @@
from collections import defaultdict
from typing import Optional
from sqlalchemy import select, and_
from sqlalchemy.orm import joinedload
from models import Position
from models.work_shifts import PlannedWorkShift
from schemas.work_shifts_planning import *
from services.base import BaseService
class WorkShiftsPlanningService(BaseService):
async def get_work_shifts(self, request: GetWorkShiftsPlanningDataRequest) -> GetPlannedWorkShiftsResponse:
users_shifts_ids = (
select(PlannedWorkShift)
.options(
joinedload(PlannedWorkShift.user),
)
.where(PlannedWorkShift.shift_date.between(request.date_from, request.date_to))
.order_by(PlannedWorkShift.shift_date)
)
shifts = (await self.session.scalars(users_shifts_ids)).all()
user_shifts_dict = defaultdict(list)
for shift in shifts:
user_shifts_dict[shift.user].append(shift)
result_shifts = []
for user, shifts in user_shifts_dict.items():
user_schema = UserSchema.model_validate(user)
shifts_schema = [PlannedWorkShiftSchema.model_validate(shift) for shift in shifts]
result_shifts.append(
PlanningTableRow(
user=user_schema,
shifts=shifts_schema,
)
)
return GetPlannedWorkShiftsResponse(shifts=result_shifts)
async def _get_work_shift(self, user_id: int, shift_date: date) -> Optional[PlannedWorkShift]:
stmt = (
select(PlannedWorkShift)
.where(
and_(
PlannedWorkShift.user_id == user_id,
PlannedWorkShift.shift_date == shift_date,
)
)
)
work_shift = await self.session.execute(stmt)
work_shift = work_shift.one_or_none()
return work_shift[0] if work_shift else None
async def update_work_shift(self, request: UpdatePlanningWorkShiftRequest) -> UpdatePlanningWorkShiftResponse:
work_shift = await self._get_work_shift(request.user_id, request.shift_date)
positions: list[Position] = []
for position_key in request.position_keys:
position: Optional[Position] = await self.session.get(Position, position_key)
if position:
positions.append(position)
else:
return UpdatePlanningWorkShiftResponse(ok=False, message=f"Должность с ID: {position_key} не найдена")
if not work_shift:
work_shift = PlannedWorkShift(
user_id=request.user_id,
shift_date=request.shift_date,
positions=positions,
created_at=datetime.now(),
)
self.session.add(work_shift)
else:
work_shift.positions = positions
await self.session.commit()
return UpdatePlanningWorkShiftResponse(ok=True, message="Данные о смене сохранены")