feat: work shifts by QR codes
This commit is contained in:
114
services/work_shifts.py
Normal file
114
services/work_shifts.py
Normal file
@@ -0,0 +1,114 @@
|
||||
from datetime import datetime, date, timedelta
|
||||
|
||||
from sqlalchemy import select, delete
|
||||
from sqlalchemy.orm import joinedload
|
||||
|
||||
from models import WorkShift, User
|
||||
from schemas.time_tracking import UpdateTimeTrackingRecordRequest
|
||||
from schemas.user import *
|
||||
from schemas.work_shifts import StartShiftResponse, FinishShiftResponse, ActiveWorkShiftsResponse, DeleteShiftResponse, \
|
||||
FinishShiftByIdResponse
|
||||
from services.base import BaseService
|
||||
from services.time_tracking import TimeTrackingService
|
||||
|
||||
|
||||
class WorkShiftsService(BaseService):
|
||||
async def _get_last_work_shift_for_today(self, user_id: int) -> Optional[WorkShift]:
|
||||
stmt = (
|
||||
select(WorkShift)
|
||||
.where(WorkShift.user_id == user_id)
|
||||
.order_by(WorkShift.started_at.desc())
|
||||
.limit(1)
|
||||
)
|
||||
|
||||
work_shift = await self.session.execute(stmt)
|
||||
work_shift = work_shift.one_or_none()
|
||||
work_shift = work_shift[0] if work_shift else None
|
||||
|
||||
if work_shift and work_shift.started_at.date() == date.today():
|
||||
return work_shift
|
||||
return None
|
||||
|
||||
async def start_shift(self, user_id: int) -> StartShiftResponse:
|
||||
employee = await self.session.get(User, user_id)
|
||||
if not employee:
|
||||
return StartShiftResponse(ok=False, message=f"Пользователь с ID {user_id} не найден")
|
||||
|
||||
work_shift = await self._get_last_work_shift_for_today(user_id)
|
||||
if work_shift:
|
||||
if not work_shift.finished_at:
|
||||
return StartShiftResponse(ok=False, message="Предыдущая смена еще не закончена")
|
||||
return StartShiftResponse(ok=False, message="Смена для сотрудника на сегодня уже закончена")
|
||||
|
||||
work_shift = WorkShift(user_id=user_id, started_at=datetime.now())
|
||||
self.session.add(work_shift)
|
||||
await self.session.commit()
|
||||
return StartShiftResponse(ok=True, message="Смена начата")
|
||||
|
||||
async def finish_shift(self, user: User, user_id: int) -> FinishShiftResponse:
|
||||
employee = await self.session.get(User, user_id)
|
||||
if not employee:
|
||||
return FinishShiftResponse(ok=False, message=f"Пользователь с ID {user_id} не найден")
|
||||
|
||||
work_shift = await self._get_last_work_shift_for_today(user_id)
|
||||
|
||||
if not work_shift or work_shift.finished_at:
|
||||
return FinishShiftResponse(ok=False, message="Смена для сотрудника еще не начата")
|
||||
|
||||
work_shift.finished_at = datetime.now()
|
||||
await self.session.commit()
|
||||
|
||||
diff: timedelta = work_shift.finished_at - work_shift.started_at
|
||||
hours = int(diff.total_seconds() // 3600)
|
||||
|
||||
data = UpdateTimeTrackingRecordRequest(
|
||||
user_id=user_id,
|
||||
date=work_shift.started_at.date(),
|
||||
hours=hours,
|
||||
)
|
||||
await TimeTrackingService(self.session).update_record(user, data)
|
||||
|
||||
return FinishShiftResponse(ok=True, message=f"Смена закончена. Отработано {hours} ч.")
|
||||
|
||||
async def finish_shift_by_id(self, user: User, shift_id: int) -> FinishShiftByIdResponse:
|
||||
work_shift = await self.session.get(WorkShift, shift_id)
|
||||
|
||||
if not work_shift or work_shift.finished_at:
|
||||
return FinishShiftByIdResponse(ok=False, message="Смена для сотрудника еще не начата")
|
||||
|
||||
work_shift.finished_at = datetime.now()
|
||||
await self.session.commit()
|
||||
|
||||
diff: timedelta = work_shift.finished_at - work_shift.started_at
|
||||
hours = int(diff.total_seconds() // 3600)
|
||||
|
||||
data = UpdateTimeTrackingRecordRequest(
|
||||
user_id=work_shift.user_id,
|
||||
date=work_shift.started_at.date(),
|
||||
hours=hours,
|
||||
)
|
||||
await TimeTrackingService(self.session).update_record(user, data)
|
||||
|
||||
return FinishShiftByIdResponse(ok=True, message=f"Смена закончена. Отработано {hours} ч.")
|
||||
|
||||
async def get_active_shifts(self) -> ActiveWorkShiftsResponse:
|
||||
stmt = (
|
||||
select(WorkShift)
|
||||
.options(joinedload(WorkShift.user))
|
||||
.where(WorkShift.finished_at == None)
|
||||
.order_by(WorkShift.started_at.desc())
|
||||
)
|
||||
|
||||
shifts = await self.session.execute(stmt)
|
||||
shifts = shifts.scalars().all()
|
||||
response = ActiveWorkShiftsResponse(shifts=shifts)
|
||||
return response
|
||||
|
||||
async def delete_shift(self, shift_id: int) -> DeleteShiftResponse:
|
||||
stmt = (
|
||||
delete(WorkShift)
|
||||
.where(WorkShift.id == shift_id)
|
||||
)
|
||||
await self.session.execute(stmt)
|
||||
await self.session.commit()
|
||||
return DeleteShiftResponse(ok=True, message="Запись о смене успешно удалена")
|
||||
Reference in New Issue
Block a user