Employee Benefit Done!!

Db Integrity, Reset Stock and Rebase done!!
This commit is contained in:
tanshu committed 2020-05-22 10:10:45 +05:30
1 parent b2f2e742f5
commit 814d289758
18 files changed
+631 -358

No files matched your search

+11 -2
View File
@@ -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)
+285 -39
View File
@@ -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
+99
View File
@@ -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
+1 -1
View File
@@ -367,7 +367,7 @@ def get_id(
)
@router.get("")
@router.get("", response_model=output.Voucher)
def show_blank(
request: Request,
date: str = None,
+1 -1
View File
@@ -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.
+1 -1
View File
@@ -120,7 +120,7 @@ def get_id(
)
@router.get("")
@router.get("", response_model=output.Voucher)
def show_blank(
request: Request,
a: str = None,
+1 -1
View File
@@ -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),
+1 -1
View File
@@ -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,
+1 -1
View File
@@ -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)
+1 -1
View File
@@ -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
-169
View File
@@ -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
-4
View File
@@ -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)
+36 -1
View File
@@ -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)
+12 -9
View File
@@ -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):