brewman/brewman/routers/employee_benefit.py

247 lines
8.6 KiB
Python

import uuid
from math import ceil
from typing import List
from datetime import datetime, date
from fastapi import (
APIRouter,
HTTPException,
status,
Depends,
Security,
File,
Request,
)
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session
from .db_image import save_files, update_files
from .voucher import (
voucher_info,
check_voucher_lock_info,
check_voucher_edit_allowed,
blank_voucher,
)
from ..core.session import set_date, get_date, get_last_day
from ..models import AccountBase, Employee
from ..schemas.auth import UserToken
from ..core.security import get_current_active_user as get_user
from ..db.session import SessionLocal
from ..models.voucher import (
Voucher,
VoucherType,
Journal,
EmployeeBenefit,
)
import brewman.schemas.voucher as output
import brewman.schemas.input as schema_in
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.post("", response_model=output.Voucher)
def save_route(
request: Request,
data: schema_in.EmployeeBenefitIn = Depends(schema_in.EmployeeBenefitIn.load_form),
db: Session = Depends(get_db),
i: List[bytes] = File(None),
t: List[bytes] = File(None),
user: UserToken = Security(get_user, scopes=["employee-benefit"]),
):
try:
dt = get_last_day(data.date_)
days_in_month = dt.day
item: Voucher = save(data, dt, user, db)
exp, total = save_employee_benefits(item, data.employee_benefits, days_in_month, db)
save_journals(item, exp, total, db)
save_files(item.id, i, t, db)
db.commit()
set_date(data.date_.strftime("%d-%b-%Y"), request.session)
info = voucher_info(item, db)
return info
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e),
)
except Exception:
db.rollback()
raise
def save(data: schema_in.EmployeeBenefitIn, date_: date, user: UserToken, db: Session) -> Voucher:
check_voucher_lock_info(None, date_, db)
voucher = Voucher(
date=date_,
narration=data.narration,
is_starred=data.is_starred,
user_id=user.id_,
type_=VoucherType.by_name(data.type_),
)
db.add(voucher)
return voucher
def save_employee_benefits(
voucher: Voucher, employee_benefits: List[schema_in.EmployeeBenefit], days_in_month: int, db: Session,
):
total_exp, total_total = 0, 0
for item in employee_benefits:
account = db.query(Employee).filter(Employee.id == item.employee.id_).first()
gross_salary = item.gross_salary
days_worked = item.days_worked
esi_ee, esi_er, esi_both = esi_contribution(gross_salary, days_worked, days_in_month)
pf_ee, pf_er, pf_both = pf_contribution(gross_salary, days_worked, days_in_month)
journal = Journal(amount=esi_ee + pf_ee, debit=1, account_id=account.id, cost_centre_id=account.cost_centre_id,)
sd = EmployeeBenefit(
journal=journal,
gross_salary=gross_salary,
days_worked=days_worked,
esi_ee=esi_ee,
pf_ee=pf_ee,
esi_er=esi_er,
pf_er=pf_er,
)
voucher.journals.append(journal)
voucher.employee_benefits.append(sd)
db.add(journal)
db.add(sd)
total_exp += esi_er + pf_er
total_total += esi_both + pf_both
return total_exp, total_total
def save_journals(voucher: Voucher, exp: int, total: int, db: Session):
account = db.query(AccountBase).filter(AccountBase.id == AccountBase.esi_pf_expense()).first()
journal = Journal(amount=exp, debit=1, account_id=account.id, cost_centre_id=account.cost_centre_id,)
db.add(journal)
voucher.journals.append(journal)
account = db.query(AccountBase).filter(AccountBase.id == AccountBase.esi_pf_payable()).first()
journal = Journal(amount=total, debit=-1, account_id=account.id, cost_centre_id=account.cost_centre_id,)
db.add(journal)
voucher.journals.append(journal)
@router.put("/{id_}", response_model=output.Voucher)
def update_route(
id_: uuid.UUID,
request: Request,
data: schema_in.EmployeeBenefitIn = Depends(schema_in.EmployeeBenefitIn.load_form),
db: Session = Depends(get_db),
i: List[bytes] = File(None),
t: List[bytes] = File(None),
user: UserToken = Security(get_user, scopes=["employee-benefit"]),
):
try:
dt = get_last_day(data.date_)
days_in_month = dt.day
item: Voucher = update(id_, data, user, db)
if dt != item.date:
raise ValueError("Date Cannot be changed for Employee Benefit voucher!")
exp, total = update_employee_benefits(item, data.employee_benefits, days_in_month, db)
update_journals(item, exp, total)
# journals_valid(voucher)
update_files(item.id, data.files, i, t, db)
db.commit()
set_date(data.date_.strftime("%d-%b-%Y"), request.session)
return voucher_info(item, db)
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e),
)
except Exception:
db.rollback()
raise
def update(id_: uuid.UUID, data: schema_in.EmployeeBenefitIn, user: UserToken, db: Session) -> Voucher:
voucher: Voucher = db.query(Voucher).filter(Voucher.id == id_).first()
check_voucher_lock_info(voucher.date, data.date_, db)
check_voucher_edit_allowed(voucher, user)
voucher.is_starred = data.is_starred
voucher.narration = data.narration
voucher.user_id = user.id_
voucher.posted = False
voucher.last_edit_date = datetime.utcnow()
return voucher
def update_employee_benefits(
voucher: Voucher, employee_benefits: List[schema_in.EmployeeBenefit], days_in_month: int, db: Session,
):
exp, total = 0, 0
for i in range(len(voucher.employee_benefits), 0, -1):
item = voucher.employee_benefits[i - 1]
found = False
for j in range(len(employee_benefits), 0, -1):
new_item = employee_benefits[j - 1]
if new_item.id_ == item.id:
exp += item.esi_er + item.pf_er
total += item.esi_ee + item.pf_ee + item.esi_er + item.pf_er
employee_benefits.remove(new_item)
break
if not found:
voucher.employee_benefits.remove(item)
voucher.journals.remove(item.journal)
new_exp, new_total = save_employee_benefits(voucher, employee_benefits, days_in_month, db)
return exp + new_exp, total + new_total
def update_journals(voucher: Voucher, exp: int, total: int):
journal = next(i for i in voucher.journals if i.account_id == AccountBase.esi_pf_expense())
journal.amount = exp
journal = next(i for i in voucher.journals if i.account_id == AccountBase.esi_pf_payable())
journal.amount = total
@router.get("/{id_}", response_model=output.Voucher)
def get_id(
id_: uuid.UUID, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["employee-benefit"]),
):
try:
item: Voucher = db.query(Voucher).filter(Voucher.id == id_).first()
return voucher_info(item, db)
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e),
)
except Exception:
db.rollback()
raise
@router.get("", response_model=output.Voucher)
def show_blank(
request: Request, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["employee-benefit"]),
):
additional_info = {"date": get_date(request.session), "type": "Employee Benefit"}
return blank_voucher(additional_info, db)
def esi_contribution(gross_salary, days_worked, days_in_month):
limit = 21000
employee_rate = 0.0175
employer_rate = 0.0475
employee = 0 if gross_salary > limit else ceil(employee_rate * gross_salary * days_worked / days_in_month)
employer = 0 if gross_salary > limit else ceil(employer_rate * gross_salary * days_worked / days_in_month)
return employee, employer, employee + employer
def pf_contribution(gross_salary, days_worked, days_in_month):
limit = 15000
employee_rate = 0.12
employer_rate = 0.12 + 0.011 + 0.005 + 0.0001
employee = 0 if gross_salary > limit else ceil(employee_rate * gross_salary * days_worked / days_in_month)
employer = 0 if gross_salary > limit else ceil(employer_rate * gross_salary * days_worked / days_in_month)
return employee, employer, employee + employer