feat: assignment of employees to deals
This commit is contained in:
@@ -9,7 +9,7 @@ from models.work_shifts import WorkShift
|
|||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from models.payroll import PayRate, PaymentRecord
|
from models.payroll import PayRate, PaymentRecord
|
||||||
from models import Deal
|
from models import Deal, Assignment
|
||||||
|
|
||||||
role_permissions = Table(
|
role_permissions = Table(
|
||||||
'role_permissions',
|
'role_permissions',
|
||||||
@@ -121,6 +121,11 @@ class User(BaseModel):
|
|||||||
cascade="all, delete-orphan"
|
cascade="all, delete-orphan"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
assignments: Mapped[list['Assignment']] = relationship(
|
||||||
|
back_populates='user',
|
||||||
|
lazy='selectin'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Position(BaseModel):
|
class Position(BaseModel):
|
||||||
__tablename__ = 'positions'
|
__tablename__ = 'positions'
|
||||||
|
|||||||
@@ -108,6 +108,8 @@ class Deal(BaseModel):
|
|||||||
pallets: Mapped[list[Pallet]] = relationship(back_populates='deal', lazy='selectin')
|
pallets: Mapped[list[Pallet]] = relationship(back_populates='deal', lazy='selectin')
|
||||||
boxes: Mapped[list[Box]] = relationship(back_populates='deal', lazy='selectin')
|
boxes: Mapped[list[Box]] = relationship(back_populates='deal', lazy='selectin')
|
||||||
|
|
||||||
|
assignments: Mapped[list['Assignment']] = relationship(back_populates='deal', lazy='selectin')
|
||||||
|
|
||||||
|
|
||||||
class DealStatusHistory(BaseModel):
|
class DealStatusHistory(BaseModel):
|
||||||
__tablename__ = 'deals_status_history'
|
__tablename__ = 'deals_status_history'
|
||||||
@@ -126,3 +128,14 @@ class DealStatusHistory(BaseModel):
|
|||||||
next_status_deadline = Column(DateTime,
|
next_status_deadline = Column(DateTime,
|
||||||
comment='Дедлайн до которого сделку нужно перевести на следующий этап')
|
comment='Дедлайн до которого сделку нужно перевести на следующий этап')
|
||||||
comment = Column(String, nullable=False, comment='Коментарий', server_default='')
|
comment = Column(String, nullable=False, comment='Коментарий', server_default='')
|
||||||
|
|
||||||
|
|
||||||
|
class Assignment(BaseModel):
|
||||||
|
__tablename__ = 'assignments'
|
||||||
|
user_id: Mapped[int] = mapped_column(ForeignKey('users.id'), primary_key=True)
|
||||||
|
user: Mapped['User'] = relationship('User', back_populates='assignments', lazy='selectin')
|
||||||
|
|
||||||
|
deal_id: Mapped[int] = mapped_column(ForeignKey('deals.id'), primary_key=True)
|
||||||
|
deal: Mapped[Deal] = relationship('Deal', back_populates='assignments', lazy='selectin')
|
||||||
|
|
||||||
|
created_at: Mapped[datetime] = mapped_column()
|
||||||
|
|||||||
@@ -234,6 +234,30 @@ async def recalculate_deal_price(
|
|||||||
return await DealService(session).recalculate_price(request)
|
return await DealService(session).recalculate_price(request)
|
||||||
|
|
||||||
|
|
||||||
|
@deal_router.post(
|
||||||
|
'/employee',
|
||||||
|
response_model=ManageEmployeeResponse,
|
||||||
|
operation_id='manage_employee',
|
||||||
|
)
|
||||||
|
async def manage_employee(
|
||||||
|
session: SessionDependency,
|
||||||
|
request: ManageEmployeeRequest,
|
||||||
|
):
|
||||||
|
return await DealService(session).manage_employee(request)
|
||||||
|
|
||||||
|
|
||||||
|
@deal_router.get(
|
||||||
|
'/employee/available/{deal_id}',
|
||||||
|
response_model=GetAvailableEmployeesToAssignResponse,
|
||||||
|
operation_id='get_available_employees_to_assign',
|
||||||
|
)
|
||||||
|
async def get_available_employees_to_assign(
|
||||||
|
session: Annotated[AsyncSession, Depends(get_session)],
|
||||||
|
deal_id: int,
|
||||||
|
):
|
||||||
|
return await DealService(session).get_available_employees_to_assign(deal_id)
|
||||||
|
|
||||||
|
|
||||||
# endregion
|
# endregion
|
||||||
|
|
||||||
# region Deal services
|
# region Deal services
|
||||||
|
|||||||
@@ -84,6 +84,11 @@ class DealStatusHistorySchema(BaseSchema):
|
|||||||
comment: str | None = None
|
comment: str | None = None
|
||||||
|
|
||||||
|
|
||||||
|
class AssignmentSchema(BaseSchema):
|
||||||
|
user: UserSchema
|
||||||
|
created_at: datetime.datetime
|
||||||
|
|
||||||
|
|
||||||
class DealSchema(BaseSchema):
|
class DealSchema(BaseSchema):
|
||||||
id: int
|
id: int
|
||||||
name: str
|
name: str
|
||||||
@@ -105,6 +110,7 @@ class DealSchema(BaseSchema):
|
|||||||
manager: Optional[UserSchema] = None
|
manager: Optional[UserSchema] = None
|
||||||
pallets: List[PalletSchema] = []
|
pallets: List[PalletSchema] = []
|
||||||
boxes: List[BoxSchema] = []
|
boxes: List[BoxSchema] = []
|
||||||
|
assignments: List[AssignmentSchema] = []
|
||||||
|
|
||||||
delivery_date: Optional[datetime.datetime] = None
|
delivery_date: Optional[datetime.datetime] = None
|
||||||
receiving_slot_date: Optional[datetime.datetime] = None
|
receiving_slot_date: Optional[datetime.datetime] = None
|
||||||
@@ -257,6 +263,12 @@ class DealRecalculatePriceRequest(BaseSchema):
|
|||||||
deal_id: int
|
deal_id: int
|
||||||
|
|
||||||
|
|
||||||
|
class ManageEmployeeRequest(BaseSchema):
|
||||||
|
deal_id: int
|
||||||
|
user_id: int
|
||||||
|
is_assign: bool
|
||||||
|
|
||||||
|
|
||||||
class DealAddToGroupRequest(BaseSchema):
|
class DealAddToGroupRequest(BaseSchema):
|
||||||
deal_id: int
|
deal_id: int
|
||||||
group_id: int
|
group_id: int
|
||||||
@@ -389,6 +401,14 @@ class DealRecalculatePriceResponse(OkMessageSchema):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ManageEmployeeResponse(OkMessageSchema):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class GetAvailableEmployeesToAssignResponse(BaseSchema):
|
||||||
|
employees: list[UserSchema]
|
||||||
|
|
||||||
|
|
||||||
class DealAddToGroupResponse(OkMessageSchema):
|
class DealAddToGroupResponse(OkMessageSchema):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import lexorank
|
import lexorank
|
||||||
from attr import dataclass
|
from attr import dataclass
|
||||||
from fastapi import HTTPException
|
from fastapi import HTTPException
|
||||||
from sqlalchemy import select, func, update, delete, insert
|
from sqlalchemy import select, func, update, delete, insert, and_
|
||||||
from sqlalchemy.orm import joinedload, selectinload
|
from sqlalchemy.orm import joinedload, selectinload
|
||||||
from starlette import status
|
from starlette import status
|
||||||
|
|
||||||
@@ -339,6 +339,8 @@ class DealService(BaseService):
|
|||||||
selectinload(Deal.boxes)
|
selectinload(Deal.boxes)
|
||||||
.selectinload(Box.product)
|
.selectinload(Box.product)
|
||||||
.noload(Product.barcodes),
|
.noload(Product.barcodes),
|
||||||
|
selectinload(Deal.assignments)
|
||||||
|
.joinedload(Assignment.user),
|
||||||
)
|
)
|
||||||
.where(Deal.id == deal_id)
|
.where(Deal.id == deal_id)
|
||||||
)
|
)
|
||||||
@@ -1136,7 +1138,6 @@ class DealService(BaseService):
|
|||||||
for deal in deals:
|
for deal in deals:
|
||||||
await self._recalculate_price_single(deal, services_quantity)
|
await self._recalculate_price_single(deal, services_quantity)
|
||||||
|
|
||||||
|
|
||||||
async def recalculate_price(self, request: DealRecalculatePriceRequest) -> DealRecalculatePriceResponse:
|
async def recalculate_price(self, request: DealRecalculatePriceRequest) -> DealRecalculatePriceResponse:
|
||||||
try:
|
try:
|
||||||
deal_stmt = (
|
deal_stmt = (
|
||||||
@@ -1164,6 +1165,59 @@ class DealService(BaseService):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
return DealRecalculatePriceResponse(ok=False, message=str(e))
|
return DealRecalculatePriceResponse(ok=False, message=str(e))
|
||||||
|
|
||||||
|
async def _assign_employee(self, deal: Deal, user: User) -> tuple[bool, str]:
|
||||||
|
assigned_employee_ids = [assignment.user_id for assignment in deal.assignments]
|
||||||
|
if user.id in assigned_employee_ids:
|
||||||
|
return False, "Работник уже назначен"
|
||||||
|
|
||||||
|
assignment = Assignment(user_id=user.id, deal_id=deal.id, created_at=datetime.datetime.now())
|
||||||
|
self.session.add(assignment)
|
||||||
|
await self.session.commit()
|
||||||
|
|
||||||
|
return True, "Работник успешно назначен"
|
||||||
|
|
||||||
|
async def _unassign_employee(self, deal: Deal, user: User) -> tuple[bool, str]:
|
||||||
|
assigned_employee_ids = [assignment.user_id for assignment in deal.assignments]
|
||||||
|
if user.id not in assigned_employee_ids:
|
||||||
|
return False, "Работник еще не назначен"
|
||||||
|
|
||||||
|
stmt = delete(Assignment).where(and_(Assignment.user_id == user.id, Assignment.deal_id == deal.id))
|
||||||
|
await self.session.execute(stmt)
|
||||||
|
await self.session.commit()
|
||||||
|
|
||||||
|
return True, "Работник успешно удален"
|
||||||
|
|
||||||
|
async def manage_employee(self, request: ManageEmployeeRequest) -> ManageEmployeeResponse:
|
||||||
|
deal: Optional[Deal] = await self._get_deal_by_id(request.deal_id)
|
||||||
|
if not deal:
|
||||||
|
return ManageEmployeeResponse(ok=False, message=f"Сделка с ID {request.deal_id} не найдена")
|
||||||
|
|
||||||
|
user: Optional[User] = await self.session.get(User, request.user_id)
|
||||||
|
if not user:
|
||||||
|
return ManageEmployeeResponse(ok=False, message=f"Пользователь с ID {request.user_id} не найден")
|
||||||
|
|
||||||
|
if request.is_assign:
|
||||||
|
ok, message = await self._assign_employee(deal, user)
|
||||||
|
else:
|
||||||
|
ok, message = await self._unassign_employee(deal, user)
|
||||||
|
|
||||||
|
return ManageEmployeeResponse(ok=ok, message=message)
|
||||||
|
|
||||||
|
async def get_available_employees_to_assign(self, deal_id: int) -> GetAvailableEmployeesToAssignResponse:
|
||||||
|
assigned_users = select(Assignment.user_id).where(Assignment.deal_id == deal_id)
|
||||||
|
|
||||||
|
stmt_free_employees = (
|
||||||
|
select(User)
|
||||||
|
.where(and_(
|
||||||
|
User.is_deleted == False,
|
||||||
|
User.role_key == "employee",
|
||||||
|
User.id.not_in(assigned_users),
|
||||||
|
))
|
||||||
|
)
|
||||||
|
|
||||||
|
free_employees = (await self.session.execute(stmt_free_employees)).scalars().all()
|
||||||
|
return GetAvailableEmployeesToAssignResponse(employees=free_employees)
|
||||||
|
|
||||||
async def add_to_group(self, user: User, request: DealAddToGroupRequest) -> DealAddToGroupResponse:
|
async def add_to_group(self, user: User, request: DealAddToGroupRequest) -> DealAddToGroupResponse:
|
||||||
try:
|
try:
|
||||||
group_bill_request = await self.session.get(GroupBillRequest, request.group_id)
|
group_bill_request = await self.session.get(GroupBillRequest, request.group_id)
|
||||||
|
|||||||
Reference in New Issue
Block a user