Employee Benefit Done!!
Db Integrity, Reset Stock and Rebase done!!
This commit is contained in:
1 parent
b2f2e742f5
commit
814d289758
18 files changed
+631
-358
No files matched your search
+11
-2
@@ -5,13 +5,15 @@ from starlette.middleware.sessions import SessionMiddleware
|
||||
from .routers import (
|
||||
account,
|
||||
account_types,
|
||||
attendance_report,
|
||||
attendance_types,
|
||||
attendance,
|
||||
batch,
|
||||
employee_attendance,
|
||||
attendance_report,
|
||||
cost_centre,
|
||||
db_integrity,
|
||||
employee,
|
||||
employee_attendance,
|
||||
employee_benefit,
|
||||
fingerprint,
|
||||
issue,
|
||||
issue_grid,
|
||||
@@ -21,6 +23,8 @@ from .routers import (
|
||||
product,
|
||||
product_group,
|
||||
receipt,
|
||||
rebase,
|
||||
reset_stock,
|
||||
recipe,
|
||||
login,
|
||||
journal,
|
||||
@@ -113,10 +117,15 @@ app.include_router(receipt.router, prefix="/api/receipt", tags=["vouchers"])
|
||||
app.include_router(purchase.router, prefix="/api/purchase", tags=["vouchers"])
|
||||
app.include_router(purchase_return.router, prefix="/api/purchase-return", tags=["vouchers"])
|
||||
app.include_router(issue.router, prefix="/api/issue", tags=["vouchers"])
|
||||
app.include_router(employee_benefit.router, prefix="/api/employee-benefit", tags=["vouchers"])
|
||||
|
||||
app.include_router(lock_information.router, prefix="/api/lock-information", tags=["settings"])
|
||||
app.include_router(maintenance.router, prefix="/api/maintenance", tags=["settings"])
|
||||
|
||||
app.include_router(db_integrity.router, prefix="/api/db-integrity", tags=["management"])
|
||||
app.include_router(reset_stock.router, prefix="/api/reset-stock", tags=["management"])
|
||||
app.include_router(rebase.router, prefix="/api/rebase", tags=["management"])
|
||||
|
||||
|
||||
def init():
|
||||
uvicorn.run(app, host=Settings.host, port=Settings.port)
|
||||
@@ -1,22 +1,43 @@
|
||||
from sqlalchemy import func, distinct, over, desc
|
||||
from brewman.models.voucher import Attendance
|
||||
from fastapi import (
|
||||
APIRouter,
|
||||
Depends,
|
||||
Security,
|
||||
)
|
||||
from sqlalchemy import distinct, func, over, desc
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from ..models import Attendance
|
||||
from ..schemas.auth import UserToken
|
||||
from ..core.security import get_current_active_user as get_user
|
||||
from ..db.session import SessionLocal
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.post("/api/db-integrity") # "Authenticated"
|
||||
def post_check_db(request):
|
||||
# Dependency
|
||||
def get_db() -> Session:
|
||||
try:
|
||||
db = SessionLocal()
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@router.post("")
|
||||
def post_check_db(db: Session = Depends(get_db), user: UserToken = Security(get_user)):
|
||||
info = {}
|
||||
|
||||
duplicate_attendances = get_duplicate_attendances(request.dbsession)
|
||||
duplicate_attendances = get_duplicate_attendances(db)
|
||||
if duplicate_attendances > 0:
|
||||
fix_duplicate_attendances(request.dbsession)
|
||||
fix_duplicate_attendances(db)
|
||||
info["Duplicate Attendances Fixed"] = duplicate_attendances
|
||||
transaction.commit()
|
||||
db.commit()
|
||||
return info
|
||||
|
||||
|
||||
def get_duplicate_attendances(dbsession):
|
||||
def get_duplicate_attendances(db):
|
||||
sub_query = (
|
||||
dbsession.query(
|
||||
db.query(
|
||||
over(
|
||||
distinct(func.first_value(Attendance.id)),
|
||||
partition_by=[Attendance.employee_id, Attendance.date],
|
||||
@@ -26,16 +47,16 @@ def get_duplicate_attendances(dbsession):
|
||||
.subquery()
|
||||
)
|
||||
query = (
|
||||
dbsession.query(func.count(Attendance.id))
|
||||
db.query(func.count(Attendance.id))
|
||||
.filter(~Attendance.id.in_(sub_query))
|
||||
.filter(Attendance.is_valid == True)
|
||||
)
|
||||
return query.scalar()
|
||||
|
||||
|
||||
def fix_duplicate_attendances(dbsession):
|
||||
def fix_duplicate_attendances(db):
|
||||
sub = (
|
||||
dbsession.query(
|
||||
db.query(
|
||||
over(
|
||||
distinct(func.first_value(Attendance.id)),
|
||||
partition_by=[Attendance.employee_id, Attendance.date],
|
||||
@@ -45,6 +66,6 @@ def fix_duplicate_attendances(dbsession):
|
||||
.filter(Attendance.is_valid == True)
|
||||
.subquery()
|
||||
)
|
||||
dbsession.query(Attendance).filter(~Attendance.id.in_(sub)).filter(
|
||||
db.query(Attendance).filter(~Attendance.id.in_(sub)).filter(
|
||||
Attendance.is_valid == True
|
||||
).delete(False)
|
||||
@@ -1,18 +1,44 @@
|
||||
import traceback
|
||||
import uuid
|
||||
from decimal import Decimal
|
||||
from math import ceil
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter, HTTPException, status, Depends, Security, UploadFile, File, Request
|
||||
from datetime import datetime, date
|
||||
from fastapi import (
|
||||
APIRouter,
|
||||
HTTPException,
|
||||
status,
|
||||
Depends,
|
||||
Security,
|
||||
UploadFile,
|
||||
File,
|
||||
Request,
|
||||
)
|
||||
from sqlalchemy import func
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from .voucher import incentive_create_voucher, employee_benefit_create_voucher, voucher_info, check_voucher_lock_info, check_voucher_edit_allowed
|
||||
from ..core.session import set_date
|
||||
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 Product, 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
|
||||
import brewman.schemas.voucher as schemas
|
||||
from ..models.voucher import (
|
||||
Voucher,
|
||||
VoucherType,
|
||||
Batch,
|
||||
Inventory,
|
||||
Journal,
|
||||
EmployeeBenefit,
|
||||
)
|
||||
import brewman.schemas.voucher as output
|
||||
import brewman.schemas.input as schema_in
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@@ -26,25 +52,34 @@ def get_db() -> Session:
|
||||
db.close()
|
||||
|
||||
|
||||
@router.post("", response_model=schemas.Voucher)
|
||||
@router.post("", response_model=output.Voucher)
|
||||
def save_route(
|
||||
request: Request,
|
||||
data: schemas.Voucher,
|
||||
data: schema_in.EmployeeBenefitIn = Depends(schema_in.EmployeeBenefitIn.load_form),
|
||||
db: Session = Depends(get_db),
|
||||
files: List[UploadFile] = File(...),
|
||||
user: UserToken = Security(get_user, scopes=["journal"]),
|
||||
i: List[UploadFile] = File(None),
|
||||
t: List[UploadFile] = File(None),
|
||||
user: UserToken = Security(get_user, scopes=["employee-benefit"]),
|
||||
):
|
||||
try:
|
||||
item: Voucher = save(data, files, user, db)
|
||||
i = i or []
|
||||
t = t or []
|
||||
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(i + t, db)
|
||||
db.commit()
|
||||
set_date(request.session, data.date_)
|
||||
# item: Voucher = db.query(Voucher).filter(Voucher.id == item.id).first()
|
||||
return voucher_info(item, db)
|
||||
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),
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e),
|
||||
)
|
||||
except Exception:
|
||||
db.rollback()
|
||||
@@ -54,35 +89,128 @@ def save_route(
|
||||
)
|
||||
|
||||
|
||||
def save(data: schemas.Voucher, files: List[UploadFile], user: UserToken, db: Session) -> Voucher:
|
||||
check_voucher_lock_info(None, data.date_, db)
|
||||
if data.type_ in ["Employee Benefit"]:
|
||||
voucher = employee_benefit_create_voucher(data, files, user, db)
|
||||
elif data.type_ in ["Incentive"]:
|
||||
voucher = incentive_create_voucher(data, files, user, db)
|
||||
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
|
||||
|
||||
|
||||
@router.get("/{id_}")
|
||||
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)
|
||||
|
||||
|
||||
def save_files(files: List[UploadFile], db: Session):
|
||||
# for key, value in files.items():
|
||||
# db.add(DbImage(voucher.id, "voucher", value["f"], value["t"]))
|
||||
pass
|
||||
|
||||
|
||||
@router.put("/{id_}", response_model=output.Voucher)
|
||||
def update_route(
|
||||
id_: uuid.UUID,
|
||||
request: Request,
|
||||
data: schemas.Voucher,
|
||||
data: schema_in.EmployeeBenefitIn = Depends(schema_in.EmployeeBenefitIn.load_form),
|
||||
db: Session = Depends(get_db),
|
||||
files: List[UploadFile] = File(...),
|
||||
user: UserToken = Security(get_user, scopes=["journal"]),
|
||||
i: List[UploadFile] = File(None),
|
||||
t: List[UploadFile] = File(None),
|
||||
user: UserToken = Security(get_user, scopes=["employee-benefit"]),
|
||||
):
|
||||
try:
|
||||
item: Voucher = update(id_, data, files, user, db)
|
||||
i = i or []
|
||||
t = t or []
|
||||
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, db)
|
||||
# journals_valid(voucher)
|
||||
update_files(data, i + t, db)
|
||||
db.commit()
|
||||
set_date(request.session, data.date_)
|
||||
# item: Voucher = db.query(Voucher).filter(Voucher.id == item.id).first()
|
||||
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),
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e),
|
||||
)
|
||||
except Exception:
|
||||
db.rollback()
|
||||
@@ -92,12 +220,130 @@ def update_route(
|
||||
)
|
||||
|
||||
|
||||
def update(id_: uuid.UUID, data: schemas.Voucher, files: List[UploadFile], user: UserToken, db: Session) -> Voucher:
|
||||
item: Voucher = db.query(Voucher).filter(Voucher.id == id_).first()
|
||||
check_voucher_lock_info(item.date, data.date_, db)
|
||||
check_voucher_edit_allowed(item, user)
|
||||
if data.type_ in ["Employee Benefit"]:
|
||||
voucher = employee_benefit_update_voucher(item, data, files, user, db)
|
||||
elif data.type_ in ["Incentive"]:
|
||||
voucher = incentive_update_voucher(item, data, files, user, db)
|
||||
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
|
||||
|
||||
|
||||
def update_files(data: schema_in.VoucherIn, files: List[UploadFile], db: Session):
|
||||
pass
|
||||
# old_files = [
|
||||
# uuid.UUID(f["id"]) for f in json["files"] if "id" in f and f["id"] is not None
|
||||
# ]
|
||||
# images = db.query(DbImage).filter(DbImage.resource_id == voucher.id).all()
|
||||
# for image in [i for i in images if i.id not in old_files]:
|
||||
# db.delete(image)
|
||||
# for key, value in files.items():
|
||||
# db.add(DbImage(voucher.id, "voucher", value["f"], value["t"]))
|
||||
|
||||
|
||||
@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 HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=traceback.format_exc(),
|
||||
)
|
||||
|
||||
|
||||
@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
|
||||
@@ -0,0 +1,99 @@
|
||||
import traceback
|
||||
import uuid
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter, HTTPException, status, Depends, Security, UploadFile, File, Request
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from .voucher import incentive_create_voucher, employee_benefit_create_voucher, voucher_info, check_voucher_lock_info, check_voucher_edit_allowed
|
||||
from ..core.session import set_date
|
||||
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
|
||||
import brewman.schemas.voucher as schemas
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
# Dependency
|
||||
def get_db() -> Session:
|
||||
try:
|
||||
db = SessionLocal()
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@router.post("", response_model=schemas.Voucher)
|
||||
def save_route(
|
||||
request: Request,
|
||||
data: schemas.Voucher,
|
||||
db: Session = Depends(get_db),
|
||||
files: List[UploadFile] = File(...),
|
||||
user: UserToken = Security(get_user, scopes=["journal"]),
|
||||
):
|
||||
try:
|
||||
item: Voucher = save(data, files, user, db)
|
||||
db.commit()
|
||||
set_date(request.session, data.date_)
|
||||
# item: Voucher = db.query(Voucher).filter(Voucher.id == item.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 HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=traceback.format_exc(),
|
||||
)
|
||||
|
||||
|
||||
def save(data: schemas.Voucher, files: List[UploadFile], user: UserToken, db: Session) -> Voucher:
|
||||
check_voucher_lock_info(None, data.date_, db)
|
||||
if data.type_ in ["Incentive"]:
|
||||
voucher = incentive_create_voucher(data, files, user, db)
|
||||
return voucher
|
||||
|
||||
|
||||
@router.get("/{id_}")
|
||||
def update_route(
|
||||
id_: uuid.UUID,
|
||||
request: Request,
|
||||
data: schemas.Voucher,
|
||||
db: Session = Depends(get_db),
|
||||
files: List[UploadFile] = File(...),
|
||||
user: UserToken = Security(get_user, scopes=["journal"]),
|
||||
):
|
||||
try:
|
||||
item: Voucher = update(id_, data, files, user, db)
|
||||
db.commit()
|
||||
set_date(request.session, data.date_)
|
||||
# item: Voucher = db.query(Voucher).filter(Voucher.id == item.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 HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=traceback.format_exc(),
|
||||
)
|
||||
|
||||
|
||||
def update(id_: uuid.UUID, data: schemas.Voucher, files: List[UploadFile], user: UserToken, db: Session) -> Voucher:
|
||||
item: Voucher = db.query(Voucher).filter(Voucher.id == id_).first()
|
||||
check_voucher_lock_info(item.date, data.date_, db)
|
||||
check_voucher_edit_allowed(item, user)
|
||||
if data.type_ in ["Incentive"]:
|
||||
voucher = incentive_update_voucher(item, data, files, user, db)
|
||||
return voucher
|
||||
@@ -367,7 +367,7 @@ def get_id(
|
||||
)
|
||||
|
||||
|
||||
@router.get("")
|
||||
@router.get("", response_model=output.Voucher)
|
||||
def show_blank(
|
||||
request: Request,
|
||||
date: str = None,
|
||||
|
||||
@@ -219,7 +219,7 @@ def get_id(
|
||||
)
|
||||
|
||||
|
||||
@router.get("")
|
||||
@router.get("", response_model=output.Voucher)
|
||||
def show_blank(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
|
||||
Whitespace-only changes.
@@ -120,7 +120,7 @@ def get_id(
|
||||
)
|
||||
|
||||
|
||||
@router.get("")
|
||||
@router.get("", response_model=output.Voucher)
|
||||
def show_blank(
|
||||
request: Request,
|
||||
a: str = None,
|
||||
|
||||
@@ -364,7 +364,7 @@ def get_id(
|
||||
)
|
||||
|
||||
|
||||
@router.get("")
|
||||
@router.get("", response_model=output.Voucher)
|
||||
def show_blank(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
|
||||
@@ -307,7 +307,7 @@ def get_id(
|
||||
)
|
||||
|
||||
|
||||
@router.get("")
|
||||
@router.get("", response_model=output.Voucher)
|
||||
def show_blank(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
|
||||
@@ -1,62 +1,75 @@
|
||||
import datetime
|
||||
import uuid
|
||||
from datetime import datetime, timedelta, date
|
||||
from decimal import Decimal
|
||||
from sqlalchemy import func, and_, distinct
|
||||
from sqlalchemy.orm import aliased, joinedload_all
|
||||
from typing import List
|
||||
|
||||
from brewman.models.master import AccountBase, CostCentre, Account, Employee
|
||||
from brewman.models.voucher import (
|
||||
Journal,
|
||||
Voucher,
|
||||
VoucherType,
|
||||
Batch,
|
||||
Inventory,
|
||||
EmployeeBenefit,
|
||||
Fingerprint,
|
||||
Attendance,
|
||||
DbImage,
|
||||
Incentive,
|
||||
from fastapi import (
|
||||
APIRouter,
|
||||
Depends,
|
||||
Security,
|
||||
)
|
||||
from sqlalchemy import func, and_, distinct
|
||||
from sqlalchemy.orm import Session, joinedload_all, aliased
|
||||
|
||||
from ..models import Voucher, Journal, VoucherType, Inventory, Batch, EmployeeBenefit
|
||||
from ..models.master import AccountBase, CostCentre, Employee, Account
|
||||
from ..models.voucher import Incentive, DbImage, Fingerprint, Attendance
|
||||
from ..schemas.auth import UserToken
|
||||
from ..core.security import get_current_active_user as get_user
|
||||
from ..db.session import SessionLocal
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.post("/api/rebase/{date}") # "Rebase"
|
||||
def rebase(request):
|
||||
# Dependency
|
||||
def get_db() -> Session:
|
||||
try:
|
||||
db = SessionLocal()
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@router.post("/{date_}")
|
||||
def rebase(
|
||||
date_: str,
|
||||
db: Session = Depends(get_db),
|
||||
user: UserToken = Security(get_user, scopes=["rebase"]),
|
||||
):
|
||||
# request.dbsession.execute('SET statement_timeout TO 300000;') # 5 minutes
|
||||
date = request.matchdict.get("date", None)
|
||||
date = datetime.datetime.strptime(date, "%d-%b-%Y")
|
||||
user_id = uuid.UUID(request.authenticated_userid)
|
||||
voucher_l = opening_accounts(date, user_id, request.dbsession)
|
||||
voucher_b = opening_batches(date, user_id, request.dbsession)
|
||||
starred_vouchers = save_starred(date, request.dbsession)
|
||||
request.dbsession.flush()
|
||||
delete_data(date, starred_vouchers, request.dbsession)
|
||||
request.dbsession.add(voucher_l)
|
||||
date_ = datetime.strptime(date_, "%d-%b-%Y")
|
||||
voucher_l = opening_accounts(date_, user.id_, db)
|
||||
voucher_b = opening_batches(date_, user.id_, db)
|
||||
starred_vouchers = save_starred(date_, db)
|
||||
db.flush()
|
||||
delete_data(date_, starred_vouchers, db)
|
||||
db.add(voucher_l)
|
||||
for j in voucher_l.journals:
|
||||
request.dbsession.add(j)
|
||||
request.dbsession.add(voucher_b)
|
||||
db.add(j)
|
||||
db.add(voucher_b)
|
||||
for j in voucher_b.journals:
|
||||
request.dbsession.add(j)
|
||||
db.add(j)
|
||||
for i in voucher_b.inventories:
|
||||
request.dbsession.add(i)
|
||||
request.dbsession.flush()
|
||||
cleanup_lint(date, request.dbsession)
|
||||
db.add(i)
|
||||
db.flush()
|
||||
cleanup_lint(date_, db)
|
||||
# request.dbsession.execute('RESET statement_timeout;')
|
||||
transaction.commit()
|
||||
db.commit()
|
||||
return {}
|
||||
|
||||
|
||||
def save_starred(date, dbsession):
|
||||
def save_starred(date_: date, db: Session):
|
||||
accounts = [
|
||||
i.id
|
||||
for i in dbsession.query(AccountBase.id)
|
||||
for i in db.query(AccountBase.id)
|
||||
.filter(AccountBase.is_starred == True)
|
||||
.all()
|
||||
]
|
||||
vouchers = []
|
||||
query = (
|
||||
dbsession.query(Voucher)
|
||||
db.query(Voucher)
|
||||
.options(joinedload_all(Voucher.journals, Journal.account, innerjoin=True))
|
||||
.filter(Voucher.date < date)
|
||||
.filter(Voucher.date < date_)
|
||||
.filter(Voucher.journals.any(Journal.account_id.in_(accounts)))
|
||||
.all()
|
||||
)
|
||||
@@ -79,43 +92,41 @@ def save_starred(date, dbsession):
|
||||
cost_centre_id=CostCentre.cost_centre_overall(),
|
||||
)
|
||||
voucher.journals.append(journal)
|
||||
dbsession.add(journal)
|
||||
db.add(journal)
|
||||
|
||||
for other in others:
|
||||
if voucher.type != VoucherType.by_name("Opening Accounts").id:
|
||||
voucher.narration += "\nSuspense \u20B9{0:,.2f} is {1}".format(
|
||||
other.amount, other.account.name
|
||||
)
|
||||
dbsession.delete(other)
|
||||
voucher.narration += f"\nSuspense \u20B9{other.amount:,.2f} is {other.account.name}"
|
||||
db.delete(other)
|
||||
voucher.type = VoucherType.by_name("Journal")
|
||||
if len(voucher.narration) >= 1000:
|
||||
voucher.narration = voucher.narration[:1000]
|
||||
return vouchers
|
||||
|
||||
|
||||
def opening_accounts(date, user_id, dbsession):
|
||||
def opening_accounts(date_: date, user_id: uuid.UUID, db: Session):
|
||||
running_total = 0
|
||||
sum_func = func.sum(Journal.signed_amount)
|
||||
query = (
|
||||
dbsession.query(AccountBase, sum_func)
|
||||
db.query(AccountBase, sum_func)
|
||||
.join(Journal, Voucher.journals)
|
||||
.join(AccountBase, Journal.account)
|
||||
.filter(AccountBase.is_starred == False)
|
||||
.filter(AccountBase.id != AccountBase.suspense())
|
||||
.filter(Voucher.date < date)
|
||||
.filter(Voucher.date < date_)
|
||||
.filter(Voucher.type != VoucherType.by_name("Issue").id)
|
||||
.having(sum_func != 0)
|
||||
.group_by(AccountBase)
|
||||
.all()
|
||||
)
|
||||
|
||||
dt = date - datetime.timedelta(days=1)
|
||||
dt = date_ - timedelta(days=1)
|
||||
voucher = Voucher(
|
||||
date=dt,
|
||||
narration="Opening Accounts",
|
||||
user_id=user_id,
|
||||
posted=True,
|
||||
type=VoucherType.by_name("Opening Accounts"),
|
||||
type_=VoucherType.by_name("Opening Accounts"),
|
||||
)
|
||||
for account, amount in query:
|
||||
amount = round(Decimal(amount), 2)
|
||||
@@ -139,28 +150,28 @@ def opening_accounts(date, user_id, dbsession):
|
||||
return voucher
|
||||
|
||||
|
||||
def opening_batches(date, user_id, dbsession):
|
||||
def opening_batches(date_: date, user_id: uuid.UUID, db: Session):
|
||||
total = 0
|
||||
sum_func = func.sum(Journal.debit * Inventory.quantity)
|
||||
query = (
|
||||
dbsession.query(Batch, sum_func)
|
||||
db.query(Batch, sum_func)
|
||||
.join(Journal, Voucher.journals)
|
||||
.join(Inventory, Voucher.inventories)
|
||||
.join(Batch, Inventory.batch)
|
||||
.filter(Voucher.date < date)
|
||||
.filter(Voucher.date < date_)
|
||||
.filter(Journal.cost_centre_id == CostCentre.cost_centre_purchase())
|
||||
.having(sum_func != 0)
|
||||
.group_by(Batch)
|
||||
.all()
|
||||
)
|
||||
|
||||
dt = date - datetime.timedelta(days=1)
|
||||
dt = date_ - timedelta(days=1)
|
||||
voucher = Voucher(
|
||||
date=dt,
|
||||
narration="Opening Batches",
|
||||
user_id=user_id,
|
||||
posted=True,
|
||||
type=VoucherType.by_name("Opening Batches"),
|
||||
type_=VoucherType.by_name("Opening Batches"),
|
||||
)
|
||||
|
||||
for batch, quantity in query:
|
||||
@@ -195,25 +206,25 @@ def opening_batches(date, user_id, dbsession):
|
||||
return voucher
|
||||
|
||||
|
||||
def delete_data(date, vouchers, dbsession):
|
||||
def delete_data(date_: date, vouchers: List[Voucher], db: Session):
|
||||
sub_voucher = aliased(Voucher)
|
||||
sub_query = (
|
||||
dbsession.query(sub_voucher.id).filter(sub_voucher.date < date).subquery()
|
||||
db.query(sub_voucher.id).filter(sub_voucher.date < date_).subquery()
|
||||
)
|
||||
|
||||
dbsession.execute(Inventory.__table__.delete(Inventory.voucher_id.in_(sub_query)))
|
||||
dbsession.execute(
|
||||
db.execute(Inventory.__table__.delete(Inventory.voucher_id.in_(sub_query)))
|
||||
db.execute(
|
||||
EmployeeBenefit.__table__.delete(EmployeeBenefit.voucher_id.in_(sub_query))
|
||||
)
|
||||
dbsession.execute(
|
||||
db.execute(
|
||||
Incentive.__table__.delete(Incentive.voucher_id.in_(sub_query))
|
||||
)
|
||||
dbsession.execute(
|
||||
db.execute(
|
||||
Journal.__table__.delete(
|
||||
and_(Journal.voucher_id.in_(sub_query), ~Journal.voucher_id.in_(vouchers))
|
||||
)
|
||||
)
|
||||
dbsession.execute(
|
||||
db.execute(
|
||||
DbImage.__table__.delete(
|
||||
and_(
|
||||
DbImage.resource_type == "voucher",
|
||||
@@ -222,57 +233,57 @@ def delete_data(date, vouchers, dbsession):
|
||||
)
|
||||
)
|
||||
)
|
||||
dbsession.execute(
|
||||
Voucher.__table__.delete(and_(Voucher.date < date, ~Voucher.id.in_(vouchers)))
|
||||
db.execute(
|
||||
Voucher.__table__.delete(and_(Voucher.date < date_, ~Voucher.id.in_(vouchers)))
|
||||
)
|
||||
|
||||
|
||||
def cleanup_lint(date, dbsession):
|
||||
def cleanup_lint(date_: date, db: Session):
|
||||
# Insert executes on the end so keep list of batches and journals
|
||||
dbsession.execute(
|
||||
db.execute(
|
||||
Batch.__table__.delete(
|
||||
~Batch.id.in_(dbsession.query(distinct(Inventory.batch_id)).subquery())
|
||||
~Batch.id.in_(db.query(distinct(Inventory.batch_id)).subquery())
|
||||
)
|
||||
)
|
||||
dbsession.execute(Fingerprint.__table__.delete(Fingerprint.date < date))
|
||||
dbsession.execute(Attendance.__table__.delete(Attendance.date < date))
|
||||
dbsession.execute(
|
||||
db.execute(Fingerprint.__table__.delete(Fingerprint.date < date_))
|
||||
db.execute(Attendance.__table__.delete(Attendance.date < date_))
|
||||
db.execute(
|
||||
Employee.__table__.delete(
|
||||
and_(
|
||||
~Employee.id.in_(
|
||||
dbsession.query(distinct(Journal.account_id)).subquery()
|
||||
db.query(distinct(Journal.account_id)).subquery()
|
||||
),
|
||||
~Employee.id.in_(
|
||||
dbsession.query(distinct(Fingerprint.employee_id)).subquery()
|
||||
db.query(distinct(Fingerprint.employee_id)).subquery()
|
||||
),
|
||||
~Employee.id.in_(
|
||||
dbsession.query(distinct(Attendance.employee_id)).subquery()
|
||||
db.query(distinct(Attendance.employee_id)).subquery()
|
||||
),
|
||||
Employee.id.in_(
|
||||
dbsession.query(AccountBase.id)
|
||||
db.query(AccountBase.id)
|
||||
.filter(AccountBase.is_fixture == False)
|
||||
.filter(AccountBase.is_active == False)
|
||||
.filter(AccountBase.is_starred == False)
|
||||
.subquery()
|
||||
),
|
||||
Employee.leaving_date < date,
|
||||
Employee.leaving_date < date_,
|
||||
)
|
||||
)
|
||||
)
|
||||
dbsession.execute(
|
||||
db.execute(
|
||||
AccountBase.__table__.delete(
|
||||
and_(
|
||||
~AccountBase.id.in_(dbsession.query(Employee.id).subquery()),
|
||||
~AccountBase.id.in_(db.query(Employee.id).subquery()),
|
||||
AccountBase.account_type
|
||||
== Employee.__mapper_args__["polymorphic_identity"],
|
||||
)
|
||||
)
|
||||
)
|
||||
dbsession.execute(
|
||||
db.execute(
|
||||
Account.__table__.delete(
|
||||
and_(
|
||||
~Account.id.in_(
|
||||
dbsession.query(distinct(Journal.account_id)).subquery()
|
||||
db.query(distinct(Journal.account_id)).subquery()
|
||||
),
|
||||
Account.is_fixture == False,
|
||||
Account.is_starred == False,
|
||||
@@ -120,7 +120,7 @@ def get_id(
|
||||
)
|
||||
|
||||
|
||||
@router.get("")
|
||||
@router.get("", response_model=output.Voucher)
|
||||
def show_blank(
|
||||
request: Request,
|
||||
a: str = None,
|
||||
|
||||
@@ -1,49 +1,71 @@
|
||||
import datetime
|
||||
from decimal import Decimal
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
|
||||
from fastapi import (
|
||||
APIRouter,
|
||||
Depends,
|
||||
Security,
|
||||
)
|
||||
from sqlalchemy import func
|
||||
from brewman.models.master import AccountBase, CostCentre, Product
|
||||
from brewman.models.validation_exception import ValidationError
|
||||
from brewman.models.voucher import Journal, Voucher, VoucherType, Batch, Inventory
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from ..models import Inventory, Journal, Voucher, Batch, VoucherType
|
||||
from ..models.master import Product, CostCentre, AccountBase
|
||||
from ..schemas.auth import UserToken
|
||||
from ..core.security import get_current_active_user as get_user
|
||||
from ..db.session import SessionLocal
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.post("/api/reset-stock/{id}") # "Reset Stock"
|
||||
def rebase(request):
|
||||
user_id = uuid.UUID(request.authenticated_userid)
|
||||
product = (
|
||||
request.dbsession.query(Product)
|
||||
.filter(Product.id == uuid.UUID(request.matchdict["id"]))
|
||||
.first()
|
||||
)
|
||||
# Dependency
|
||||
def get_db() -> Session:
|
||||
try:
|
||||
db = SessionLocal()
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
quantity = round(Decimal(request.GET["Quantity"]), 2)
|
||||
stock_date = datetime.datetime.strptime(request.GET["StockDate"], "%d-%b-%Y")
|
||||
reset_date = datetime.datetime.strptime(request.GET["ResetDate"], "%d-%b-%Y")
|
||||
|
||||
@router.post("/{id_}")
|
||||
def rebase(
|
||||
id_: uuid.UUID,
|
||||
quantity: Decimal,
|
||||
stockDate: str,
|
||||
resetDate: str,
|
||||
db: Session = Depends(get_db),
|
||||
user: UserToken = Security(get_user, scopes=["reset-stock"]),
|
||||
):
|
||||
product: Product = db.query(Product).filter(Product.id == id_).first()
|
||||
|
||||
quantity = round(quantity, 2)
|
||||
stock_date = datetime.strptime(stockDate, "%d-%b-%Y").date()
|
||||
reset_date = datetime.strptime(resetDate, "%d-%b-%Y").date()
|
||||
|
||||
if reset_date > stock_date:
|
||||
raise ValidationError("Reset cannot be after the stock date")
|
||||
raise ValueError("Reset cannot be after the stock date")
|
||||
|
||||
change = quantity - get_closing_stock(
|
||||
product, stock_date, dbsession=request.dbsession
|
||||
product, stock_date, db=db
|
||||
)
|
||||
if change == 0:
|
||||
return {"No Change Needed"}
|
||||
final = get_closing_stock(product, dbsession=request.dbsession)
|
||||
final = get_closing_stock(product, db=db)
|
||||
if final + change < 0:
|
||||
raise ValidationError("Current Quantity will get negative. Cannot proceed")
|
||||
raise ValueError("Current Quantity will get negative. Cannot proceed")
|
||||
|
||||
batch = get_last_batch(product, request.dbsession)
|
||||
set_batches(batch, final + change, request.dbsession)
|
||||
batch = get_last_batch(product, db)
|
||||
set_batches(batch, final + change, db)
|
||||
|
||||
create_voucher(batch, change, reset_date, user_id, request.dbsession)
|
||||
transaction.commit()
|
||||
create_voucher(batch, change, reset_date, user.id_, db)
|
||||
db.commit()
|
||||
return {}
|
||||
|
||||
|
||||
def get_closing_stock(product, finish_date=None, dbsession=None):
|
||||
def get_closing_stock(product, finish_date=None, db=None):
|
||||
query = (
|
||||
dbsession.query(func.sum(Inventory.quantity * Journal.debit))
|
||||
db.query(func.sum(Inventory.quantity * Journal.debit))
|
||||
.join(Voucher)
|
||||
.filter(Voucher.id == Inventory.voucher_id)
|
||||
.filter(Voucher.id == Journal.voucher_id)
|
||||
@@ -56,24 +78,24 @@ def get_closing_stock(product, finish_date=None, dbsession=None):
|
||||
return 0 if query is None else query[0]
|
||||
|
||||
|
||||
def get_last_batch(product, dbsession):
|
||||
def get_last_batch(product, db):
|
||||
batch = (
|
||||
dbsession.query(Batch)
|
||||
db.query(Batch)
|
||||
.filter(Batch.product_id == product.id)
|
||||
.order_by(Batch.name.desc())
|
||||
.first()
|
||||
)
|
||||
if batch is None:
|
||||
raise ValidationError(
|
||||
raise ValueError(
|
||||
"Details for the product exist. Just add a purchase entry"
|
||||
)
|
||||
return batch
|
||||
|
||||
|
||||
def set_batches(batch, quantity, dbsession):
|
||||
def set_batches(batch, quantity, db):
|
||||
batch.quantity_remaining = quantity
|
||||
batches = (
|
||||
dbsession.query(Batch)
|
||||
db.query(Batch)
|
||||
.filter(Batch.id != batch.id)
|
||||
.filter(Batch.product_id == batch.product_id)
|
||||
)
|
||||
@@ -82,14 +104,14 @@ def set_batches(batch, quantity, dbsession):
|
||||
pass
|
||||
|
||||
|
||||
def create_voucher(batch, quantity, date, user_id, dbsession):
|
||||
def create_voucher(batch, quantity, date_, user_id, db):
|
||||
voucher = Voucher(
|
||||
date=date,
|
||||
date=date_,
|
||||
narration="Product Reset",
|
||||
user_id=user_id,
|
||||
type=VoucherType.by_name("Issue"),
|
||||
type_=VoucherType.by_name("Issue"),
|
||||
)
|
||||
dbsession.add(voucher)
|
||||
db.add(voucher)
|
||||
|
||||
if quantity > 0:
|
||||
source = CostCentre.cost_centre_overall()
|
||||
@@ -107,7 +129,7 @@ def create_voucher(batch, quantity, date, user_id, dbsession):
|
||||
batch=batch,
|
||||
)
|
||||
voucher.inventories.append(inventory)
|
||||
dbsession.add(inventory)
|
||||
db.add(inventory)
|
||||
|
||||
amount = round(inventory.amount, 2)
|
||||
source = Journal(
|
||||
@@ -117,7 +139,7 @@ def create_voucher(batch, quantity, date, user_id, dbsession):
|
||||
cost_centre_id=source,
|
||||
)
|
||||
voucher.journals.append(source)
|
||||
dbsession.add(source)
|
||||
db.add(source)
|
||||
destination = Journal(
|
||||
debit=1,
|
||||
account_id=AccountBase.all_purchases(),
|
||||
@@ -125,4 +147,4 @@ def create_voucher(batch, quantity, date, user_id, dbsession):
|
||||
cost_centre_id=destination,
|
||||
)
|
||||
voucher.journals.append(destination)
|
||||
dbsession.add(destination)
|
||||
db.add(destination)
|
||||
@@ -359,7 +359,7 @@ def blank_voucher(info, db):
|
||||
"incentive"
|
||||
] = incentive_employees(info["date"], dbsession)
|
||||
else:
|
||||
raise ValidationError('Voucher of type "{0}" does not exist.'.format(type_))
|
||||
raise ValidationError(f'Voucher of type "{type_}" does not exist.')
|
||||
json_voucher["files"] = []
|
||||
return json_voucher
|
||||
|
||||
|
||||
@@ -1,169 +0,0 @@
|
||||
import datetime
|
||||
from math import ceil
|
||||
import uuid
|
||||
|
||||
from brewman.core.session import get_last_day
|
||||
from brewman.models.master import AccountBase, Employee
|
||||
from brewman.models.voucher import Journal, Voucher, VoucherType, EmployeeBenefit
|
||||
|
||||
|
||||
def employee_benefit_create_voucher(json, user, dbsession):
|
||||
dt = get_last_day(datetime.datetime.strptime(json["date"], "%d-%b-%Y"))
|
||||
days_in_month = dt.day
|
||||
voucher = Voucher(date=dt, user_id=user.id, type=VoucherType.by_id(12))
|
||||
dbsession.add(voucher)
|
||||
exp, total = 0, 0
|
||||
for item in json["employeeBenefits"]:
|
||||
item_exp, item_total = add_employee_benefit(
|
||||
item, days_in_month, voucher, dbsession
|
||||
)
|
||||
exp += item_exp
|
||||
total += item_total
|
||||
account = (
|
||||
dbsession.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,
|
||||
)
|
||||
dbsession.add(journal)
|
||||
voucher.journals.append(journal)
|
||||
account = (
|
||||
dbsession.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,
|
||||
)
|
||||
dbsession.add(journal)
|
||||
voucher.journals.append(journal)
|
||||
|
||||
journals_valid(voucher)
|
||||
return voucher
|
||||
|
||||
|
||||
def employee_benefit_update_voucher(voucher, json, user, dbsession):
|
||||
dt = get_last_day(datetime.datetime.strptime(json["date"], "%d-%b-%Y"))
|
||||
if dt != voucher.date.date():
|
||||
raise ValidationError("Date Cannot be changed for Employee Benefit voucher!")
|
||||
days_in_month = voucher.date.day
|
||||
voucher.user_id = user.id
|
||||
voucher.posted = False
|
||||
voucher.last_edit_date = datetime.datetime.utcnow()
|
||||
|
||||
new_deductions = json["employeeBenefits"]
|
||||
exp, total, journals = 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(new_deductions), 0, -1):
|
||||
new_item = new_deductions[j - 1]
|
||||
if (
|
||||
"id" in new_item
|
||||
and new_item["id"] is not None
|
||||
and item.id == uuid.UUID(new_item["id"])
|
||||
):
|
||||
journals.append(item.journal.id)
|
||||
exp += item.esi_er + item.pf_er
|
||||
total += item.esi_ee + item.pf_ee + item.esi_er + item.pf_er
|
||||
new_deductions.remove(new_item)
|
||||
break
|
||||
if not found:
|
||||
voucher.employee_benefits.remove(item)
|
||||
voucher.journals.remove(item.journal)
|
||||
for new_item in new_deductions:
|
||||
item_exp, item_total = add_employee_benefit(
|
||||
new_item, days_in_month, voucher, dbsession
|
||||
)
|
||||
exp += item_exp
|
||||
total += item_total
|
||||
|
||||
journal = [
|
||||
i for i in voucher.journals if i.account_id == AccountBase.esi_pf_expense()
|
||||
]
|
||||
journal = journal[0]
|
||||
journal.amount = exp
|
||||
journal = [
|
||||
i for i in voucher.journals if i.account_id == AccountBase.esi_pf_payable()
|
||||
]
|
||||
journal = journal[0]
|
||||
journal.amount = total
|
||||
|
||||
journals_valid(voucher)
|
||||
return voucher
|
||||
|
||||
|
||||
def add_employee_benefit(item, days_in_month, voucher, dbsession):
|
||||
account = (
|
||||
dbsession.query(Employee)
|
||||
.filter(Employee.id == uuid.UUID(item["journal"]["account"]["id"]))
|
||||
.first()
|
||||
)
|
||||
gross_salary = int(item["grossSalary"])
|
||||
days_worked = int(item["daysWorked"])
|
||||
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)
|
||||
dbsession.add(journal)
|
||||
dbsession.add(sd)
|
||||
return esi_er + pf_er, esi_both + pf_both
|
||||
|
||||
|
||||
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
|
||||
@@ -6,10 +6,6 @@ class EmptyVoucher(object):
|
||||
def __init__(self, request):
|
||||
self.request = request
|
||||
|
||||
@router.get("/api/voucher", request_param="t=Employee Benefit") # "Purchase Return"
|
||||
def purchase_return(self):
|
||||
return self.get_blank()
|
||||
|
||||
@router.get("/api/voucher", request_param="t=Incentive") # "Incentive"
|
||||
def incentive(self):
|
||||
voucher_type = self.request.GET.get("t", None)
|
||||
|
||||
@@ -10,7 +10,7 @@ from sqlalchemy.orm import Session
|
||||
|
||||
from brewman.schemas import to_camel
|
||||
from brewman.schemas.master import AccountLink, CostCentreLink
|
||||
from brewman.schemas.voucher import VoucherIn, Journal, Inventory
|
||||
from brewman.schemas.voucher import VoucherIn, Journal, Inventory, EmployeeBenefit
|
||||
|
||||
|
||||
class JournalIn(VoucherIn):
|
||||
@@ -126,3 +126,38 @@ class IssueIn(VoucherIn):
|
||||
def load_form(cls, data: str = Form(...)):
|
||||
json_data = json.loads(data)
|
||||
return cls.parse_obj(json_data)
|
||||
|
||||
|
||||
class EmployeeBenefitIn(VoucherIn):
|
||||
employee_benefits: List[EmployeeBenefit]
|
||||
|
||||
class Config:
|
||||
anystr_strip_whitespace = True
|
||||
alias_generator = to_camel
|
||||
json_encoders = {
|
||||
date: lambda v: v.strftime("%d-%b-%Y"),
|
||||
datetime: lambda v: v.strftime("%d-%b-%Y %H:%I")
|
||||
}
|
||||
|
||||
@validator("date_", pre=True)
|
||||
def parse_date(cls, value):
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@validator("inventories") # For Purchase, Issue and Return Vouchers
|
||||
def validate_enough_inventories(cls, value: List[Inventory]):
|
||||
if len(value) < 1:
|
||||
raise ValueError("Not enough inventories")
|
||||
return value
|
||||
|
||||
@validator("inventories") # For Purchase, Issue and Return Vouchers
|
||||
def validate_inventories_unique(cls, value: List[Inventory]):
|
||||
if len(set(x.product.id_ for x in value)) != len(value):
|
||||
raise ValueError("Duplicate products")
|
||||
return value
|
||||
|
||||
@classmethod
|
||||
def load_form(cls, data: str = Form(...)):
|
||||
json_data = json.loads(data)
|
||||
return cls.parse_obj(json_data)
|
||||
@@ -59,15 +59,18 @@ class Inventory(BaseModel):
|
||||
|
||||
|
||||
class EmployeeBenefit(BaseModel):
|
||||
id: uuid.UUID
|
||||
voucher_id: uuid.UUID
|
||||
journal_id: uuid.UUID
|
||||
gross_salary: int
|
||||
days_worked: int
|
||||
esi_ee: int
|
||||
pf_ee: int
|
||||
esi_er: int
|
||||
pf_er: int
|
||||
id_: Optional[uuid.UUID]
|
||||
journal_id: Optional[uuid.UUID]
|
||||
employee_id: uuid.UUID
|
||||
gross_salary: int = Field(ge=0)
|
||||
days_worked: int = Field(ge=0)
|
||||
esi_ee: int = Field(ge=0)
|
||||
pf_ee: int = Field(ge=0)
|
||||
esi_er: int = Field(ge=0)
|
||||
pf_er: int = Field(ge=0)
|
||||
|
||||
class Config:
|
||||
alias_generator = to_camel
|
||||
|
||||
|
||||
class Incentive(BaseModel):
|
||||
|
||||
Reference in new issue
Block a user