115 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			115 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
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 or employee.is_deleted:
 | 
						||
            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 or employee.is_deleted:
 | 
						||
            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="Запись о смене успешно удалена")
 |