import uuid from datetime import date, datetime from math import ceil from typing import List import brewman.schemas.input as schema_in import brewman.schemas.voucher as output from fastapi import APIRouter, Depends, File, HTTPException, Request, Security, status from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.orm import Session from ..core.security import get_current_active_user as get_user from ..core.session import get_date, get_last_day, set_date from ..db.session import SessionLocal from ..models import AccountBase, Employee from ..models.voucher import EmployeeBenefit, Journal, Voucher, VoucherType from ..schemas.auth import UserToken from .db_image import save_files, update_files from .voucher import ( blank_voucher, check_voucher_edit_allowed, check_voucher_lock_info, voucher_info, ) 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 HTTPException( status_code=status.HTTP_423_LOCKED, detail="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