feat: вфыв
This commit is contained in:
293
services/payroll.py
Normal file
293
services/payroll.py
Normal file
@@ -0,0 +1,293 @@
|
||||
import datetime
|
||||
import math
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import HTTPException
|
||||
from sqlalchemy import select, insert, update, delete, func
|
||||
from sqlalchemy.orm import joinedload
|
||||
from starlette import status
|
||||
|
||||
import enums.payroll
|
||||
from models import PayrollScheme, PayRate, user_pay_rate, PaymentRecord, User
|
||||
from schemas.base import PaginationSchema, PaginationInfoSchema
|
||||
from schemas.finances import CreatePayRateRequest, UpdatePayRateRequest, DeletePayRateRequest, \
|
||||
GetAllPayrollSchemeResponse, GetAllPayRatesResponse, CreatePayRateResponse, UpdatePayRateResponse, \
|
||||
DeletePayRateResponse
|
||||
from schemas.payment_record import GetPaymentRecordsResponse, PaymentRecordGetSchema, CreatePaymentRecordRequest, \
|
||||
CreatePaymentRecordResponse, PaymentRecordCreateSchema, DeletePaymentRecordRequest, DeletePaymentRecordResponse
|
||||
from services.base import BaseService
|
||||
from schemas.payroll import *
|
||||
from utils.dependecies import is_valid_pagination
|
||||
|
||||
|
||||
class PayrollService(BaseService):
|
||||
async def get_all_schemas(self) -> GetAllPayrollSchemeResponse:
|
||||
stmt = (select(PayrollScheme).order_by(PayrollScheme.key))
|
||||
payroll_schemas = (await self.session.scalars(stmt)).all()
|
||||
return GetAllPayrollSchemeResponse(
|
||||
payroll_schemas=PayrollSchemeSchema.from_orm_list(payroll_schemas)
|
||||
)
|
||||
|
||||
async def get_all_pay_rates(self) -> GetAllPayRatesResponse:
|
||||
stmt = (
|
||||
select(
|
||||
PayRate
|
||||
).order_by(
|
||||
PayRate.id
|
||||
)
|
||||
.options(
|
||||
joinedload(PayRate.payroll_scheme)
|
||||
)
|
||||
)
|
||||
pay_rates = (await self.session.scalars(stmt)).all()
|
||||
return GetAllPayRatesResponse(
|
||||
pay_rates=pay_rates
|
||||
)
|
||||
|
||||
async def create_pay_rate(self, request: CreatePayRateRequest) -> CreatePayRateResponse:
|
||||
try:
|
||||
# Preventing duplicate by name
|
||||
if await self.session.scalar(select(PayRate).where(PayRate.name == request.data.name)):
|
||||
return CreatePayRateResponse(ok=False, message="Тариф с таким названием уже существует")
|
||||
pay_rate_dict = request.data.model_dump()
|
||||
del pay_rate_dict['payroll_scheme']
|
||||
pay_rate_dict['payroll_scheme_key'] = request.data.payroll_scheme.key
|
||||
|
||||
stmt = (
|
||||
insert(PayRate)
|
||||
.values(**pay_rate_dict)
|
||||
)
|
||||
await self.session.execute(stmt)
|
||||
await self.session.commit()
|
||||
|
||||
return CreatePayRateResponse(ok=True, message='Тариф успешно создан')
|
||||
except Exception as e:
|
||||
return CreatePayRateResponse(ok=False, message=str(e))
|
||||
|
||||
async def update_pay_rate(self, request: UpdatePayRateRequest) -> UpdatePayRateResponse:
|
||||
try:
|
||||
# Preventing duplicate by name
|
||||
stmt = (
|
||||
select(
|
||||
PayRate
|
||||
).where(
|
||||
PayRate.id == request.data.id
|
||||
)
|
||||
)
|
||||
pay_rate = await self.session.scalar(stmt)
|
||||
if not pay_rate:
|
||||
return CreatePayRateResponse(ok=False, message="Указанный тариф несуществует")
|
||||
pay_rate_dict = request.data.model_dump()
|
||||
del pay_rate_dict['payroll_scheme']
|
||||
pay_rate_dict['payroll_scheme_key'] = request.data.payroll_scheme.key
|
||||
|
||||
stmt = (
|
||||
update(PayRate)
|
||||
.values(**pay_rate_dict).where(
|
||||
PayRate.id == request.data.id
|
||||
)
|
||||
)
|
||||
await self.session.execute(stmt)
|
||||
await self.session.commit()
|
||||
|
||||
return CreatePayRateResponse(ok=True, message='Тариф успешно обновлен')
|
||||
except Exception as e:
|
||||
return CreatePayRateResponse(ok=False, message=str(e))
|
||||
|
||||
async def delete_pay_rate(self, request: DeletePayRateRequest) -> DeletePayRateResponse:
|
||||
try:
|
||||
user_pay_rate_record = await (
|
||||
self.session.scalar(
|
||||
select(
|
||||
user_pay_rate
|
||||
)
|
||||
.where(
|
||||
user_pay_rate.c.pay_rate_id == request.pay_rate_id
|
||||
)
|
||||
)
|
||||
)
|
||||
if user_pay_rate_record:
|
||||
return DeletePayRateResponse(ok=False, message="Указанный тариф привязан к пользователю")
|
||||
stmt = (
|
||||
delete(
|
||||
PayRate
|
||||
)
|
||||
.where(
|
||||
PayRate.id == request.pay_rate_id
|
||||
)
|
||||
)
|
||||
await self.session.execute(stmt)
|
||||
await self.session.commit()
|
||||
return DeletePayRateResponse(ok=True, message="Тариф успешно удален")
|
||||
except Exception as e:
|
||||
return DeletePayRateResponse(ok=False, message=str(e))
|
||||
|
||||
async def get_payment_records(self, pagination: PaginationSchema) -> GetPaymentRecordsResponse:
|
||||
if not is_valid_pagination(pagination):
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail='Invalid pagination')
|
||||
page = max(0, pagination.page - 1)
|
||||
stmt = (
|
||||
select(
|
||||
PaymentRecord
|
||||
)
|
||||
.options(
|
||||
joinedload(PaymentRecord.payroll_scheme),
|
||||
joinedload(PaymentRecord.user).noload(User.payment_records),
|
||||
joinedload(PaymentRecord.created_by_user),
|
||||
)
|
||||
.order_by(
|
||||
PaymentRecord.created_at.desc()
|
||||
)
|
||||
.offset(
|
||||
page * pagination.items_per_page
|
||||
)
|
||||
.limit(
|
||||
pagination.items_per_page
|
||||
)
|
||||
)
|
||||
total_records = await self.session.scalar(select(func.count()).select_from(PaymentRecord))
|
||||
if not total_records:
|
||||
return GetPaymentRecordsResponse(
|
||||
payment_records=[],
|
||||
pagination_info=PaginationInfoSchema(
|
||||
total_pages=0,
|
||||
total_items=0
|
||||
)
|
||||
)
|
||||
total_items = total_records
|
||||
total_pages = math.ceil(total_records / pagination.items_per_page)
|
||||
|
||||
payment_records = (await self.session.scalars(stmt)).all()
|
||||
response = GetPaymentRecordsResponse(
|
||||
payment_records=PaymentRecordGetSchema.from_orm_list(payment_records),
|
||||
pagination_info=PaginationInfoSchema(
|
||||
total_items=total_items,
|
||||
total_pages=total_pages
|
||||
)
|
||||
)
|
||||
return response
|
||||
|
||||
async def _create_payment_record_hourly(
|
||||
self,
|
||||
creator: User,
|
||||
user: User,
|
||||
record_schema: PaymentRecordCreateSchema
|
||||
):
|
||||
pay_rate: PayRate = user.pay_rate
|
||||
overtime_threshold = 0
|
||||
overtime_rate = 0
|
||||
|
||||
if pay_rate.overtime_threshold:
|
||||
overtime_threshold = pay_rate.overtime_threshold
|
||||
if pay_rate.overtime_rate:
|
||||
overtime_rate = pay_rate.overtime_rate
|
||||
|
||||
if overtime_threshold == 0 or overtime_rate == 0:
|
||||
base_units = record_schema.work_units
|
||||
overtime_units = 0
|
||||
else:
|
||||
overtime_units = max(0, record_schema.work_units - overtime_threshold)
|
||||
base_units = record_schema.work_units - overtime_units
|
||||
|
||||
amount = pay_rate.base_rate * base_units + overtime_rate * overtime_units
|
||||
payment_record_dict = record_schema.model_dump()
|
||||
del payment_record_dict['user']
|
||||
payment_record_dict.update({
|
||||
'created_by_user_id': creator.id,
|
||||
'created_at': datetime.datetime.now(),
|
||||
'payroll_scheme_key': pay_rate.payroll_scheme.key,
|
||||
'amount': amount,
|
||||
'user_id': record_schema.user.id
|
||||
})
|
||||
stmt = (
|
||||
insert(
|
||||
PaymentRecord
|
||||
).values(
|
||||
**payment_record_dict
|
||||
)
|
||||
)
|
||||
await self.session.execute(stmt)
|
||||
await self.session.commit()
|
||||
|
||||
async def _create_payment_record_daily(
|
||||
self,
|
||||
creator: User,
|
||||
user: User,
|
||||
record_schema: PaymentRecordCreateSchema
|
||||
):
|
||||
pay_rate: PayRate = user.pay_rate
|
||||
amount = pay_rate.base_rate * record_schema.work_units
|
||||
payment_record_dict = record_schema.model_dump()
|
||||
del payment_record_dict['user']
|
||||
|
||||
payment_record_dict.update({
|
||||
'created_by_user_id': creator.id,
|
||||
'created_at': datetime.datetime.now(),
|
||||
'payroll_scheme_key': pay_rate.payroll_scheme.key,
|
||||
'amount': amount,
|
||||
'user_id': record_schema.user.id
|
||||
|
||||
})
|
||||
|
||||
async def _create_payment_record_monthly(
|
||||
self,
|
||||
creator: User,
|
||||
|
||||
user: User,
|
||||
record_schema: PaymentRecordCreateSchema
|
||||
) -> CreatePaymentRecordResponse:
|
||||
pay_rate: PayRate = user.pay_rate
|
||||
amount = pay_rate.base_rate * record_schema.work_units
|
||||
payment_record_dict = record_schema.model_dump()
|
||||
del payment_record_dict['user']
|
||||
|
||||
payment_record_dict.update({
|
||||
'created_by_user_id': creator.id,
|
||||
'created_at': datetime.datetime.now(),
|
||||
'payroll_scheme_key': pay_rate.payroll_scheme.key,
|
||||
'amount': amount,
|
||||
'user_id': record_schema.user.id
|
||||
|
||||
})
|
||||
|
||||
async def create_payment_record(
|
||||
self,
|
||||
request: CreatePaymentRecordRequest,
|
||||
creator: User
|
||||
) -> CreatePaymentRecordResponse:
|
||||
try:
|
||||
user: Optional[User] = await self.session.scalar(select(User).where(User.id == request.data.user.id))
|
||||
if not user:
|
||||
return CreatePaymentRecordResponse(ok=False, message='Указанный пользователь не найден')
|
||||
if not user.pay_rate:
|
||||
return CreatePaymentRecordResponse(ok=False, message='У пользователя не указан тариф')
|
||||
user_payroll_scheme = user.pay_rate.payroll_scheme.key
|
||||
|
||||
if user_payroll_scheme == enums.payroll.PaySchemeType.hourly:
|
||||
await self._create_payment_record_hourly(creator, user, request.data)
|
||||
if user_payroll_scheme == enums.payroll.PaySchemeType.daily:
|
||||
await self._create_payment_record_hourly(creator, user, request.data)
|
||||
if user_payroll_scheme == enums.payroll.PaySchemeType.monthly:
|
||||
await self._create_payment_record_hourly(creator, user, request.data)
|
||||
|
||||
return CreatePaymentRecordResponse(ok=True, message='Запись успешно добавлена')
|
||||
except Exception as e:
|
||||
return CreatePaymentRecordResponse(ok=False, message=str(e))
|
||||
|
||||
async def delete_payment_record(
|
||||
self,
|
||||
request: DeletePaymentRecordRequest
|
||||
) -> DeletePaymentRecordResponse:
|
||||
try:
|
||||
stmt = (
|
||||
delete(
|
||||
PaymentRecord
|
||||
).where(
|
||||
PaymentRecord.id == request.payment_record_id
|
||||
)
|
||||
)
|
||||
await self.session.execute(stmt)
|
||||
await self.session.commit()
|
||||
return DeletePayRateResponse(ok=True, message="Начисление успешно удалено")
|
||||
except Exception as e:
|
||||
return DeletePayRateResponse(ok=False, message=str(e))
|
||||
Reference in New Issue
Block a user