2020-05-22 04:40:45 +00:00
|
|
|
import uuid
|
2020-10-07 15:18:43 +00:00
|
|
|
|
|
|
|
from datetime import date, datetime
|
2020-05-23 04:15:02 +00:00
|
|
|
from decimal import Decimal
|
|
|
|
from typing import List, Optional
|
2020-10-07 15:18:43 +00:00
|
|
|
|
|
|
|
import brewman.schemas.input as schema_in
|
|
|
|
import brewman.schemas.voucher as output
|
|
|
|
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Request, Security, status
|
|
|
|
from sqlalchemy import func, or_
|
2020-05-22 04:40:45 +00:00
|
|
|
from sqlalchemy.exc import SQLAlchemyError
|
|
|
|
from sqlalchemy.orm import Session
|
|
|
|
|
|
|
|
from ..core.security import get_current_active_user as get_user
|
2020-10-07 15:18:43 +00:00
|
|
|
from ..core.session import get_date, get_first_day, set_date
|
2020-05-22 04:40:45 +00:00
|
|
|
from ..db.session import SessionLocal
|
2020-10-07 15:18:43 +00:00
|
|
|
from ..models import Account, AttendanceType, Employee
|
|
|
|
from ..models.voucher import Attendance, Incentive, Journal, Voucher, VoucherType
|
|
|
|
from ..schemas.auth import UserToken
|
|
|
|
from .voucher import (
|
|
|
|
blank_voucher,
|
|
|
|
check_voucher_edit_allowed,
|
|
|
|
check_voucher_lock_info,
|
|
|
|
voucher_info,
|
2020-05-23 04:15:02 +00:00
|
|
|
)
|
2020-10-07 15:18:43 +00:00
|
|
|
|
2020-05-22 04:40:45 +00:00
|
|
|
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
|
|
|
|
|
|
# Dependency
|
|
|
|
def get_db() -> Session:
|
|
|
|
try:
|
|
|
|
db = SessionLocal()
|
|
|
|
yield db
|
|
|
|
finally:
|
|
|
|
db.close()
|
|
|
|
|
|
|
|
|
2020-05-23 04:15:02 +00:00
|
|
|
@router.post("", response_model=output.Voucher)
|
2020-05-22 04:40:45 +00:00
|
|
|
def save_route(
|
|
|
|
request: Request,
|
2020-05-23 04:15:02 +00:00
|
|
|
data: schema_in.IncentiveIn = Depends(schema_in.IncentiveIn.load_form),
|
2020-05-22 04:40:45 +00:00
|
|
|
db: Session = Depends(get_db),
|
2020-05-23 04:15:02 +00:00
|
|
|
user: UserToken = Security(get_user, scopes=["incentive"]),
|
2020-05-22 04:40:45 +00:00
|
|
|
):
|
|
|
|
try:
|
2020-05-23 04:15:02 +00:00
|
|
|
item: Voucher = save(data, user, db)
|
2020-10-07 16:59:24 +00:00
|
|
|
employees = get_employees(
|
|
|
|
get_first_day(data.date_), data.date_, data.incentives, None, db
|
|
|
|
)
|
2020-05-23 04:15:02 +00:00
|
|
|
amount = balance(data.date_, None, db) * Decimal(0.9) # 10% for Deb Dip
|
|
|
|
total_points = sum(e.points * e.days_worked for e in employees)
|
|
|
|
point_value = round(amount / total_points, 2)
|
|
|
|
save_incentives(item, employees, point_value, db)
|
2020-05-22 04:40:45 +00:00
|
|
|
db.commit()
|
2020-05-23 04:15:02 +00:00
|
|
|
set_date(data.date_.strftime("%d-%b-%Y"), request.session)
|
|
|
|
info = voucher_info(item, db)
|
|
|
|
return info
|
2020-05-22 04:40:45 +00:00
|
|
|
except SQLAlchemyError as e:
|
|
|
|
db.rollback()
|
|
|
|
raise HTTPException(
|
2020-10-07 15:18:43 +00:00
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
detail=str(e),
|
2020-05-22 04:40:45 +00:00
|
|
|
)
|
|
|
|
except Exception:
|
|
|
|
db.rollback()
|
2020-06-01 03:31:31 +00:00
|
|
|
raise
|
2020-05-22 04:40:45 +00:00
|
|
|
|
|
|
|
|
2020-05-23 04:15:02 +00:00
|
|
|
def save(data: schema_in.IncentiveIn, user: UserToken, db: Session) -> Voucher:
|
2020-05-22 04:40:45 +00:00
|
|
|
check_voucher_lock_info(None, data.date_, db)
|
2020-05-23 04:15:02 +00:00
|
|
|
voucher = Voucher(
|
|
|
|
date=data.date_,
|
|
|
|
narration=data.narration,
|
|
|
|
is_starred=data.is_starred,
|
|
|
|
user_id=user.id_,
|
|
|
|
type_=VoucherType.by_name(data.type_),
|
|
|
|
)
|
|
|
|
db.add(voucher)
|
2020-05-22 04:40:45 +00:00
|
|
|
return voucher
|
|
|
|
|
|
|
|
|
2020-05-23 04:15:02 +00:00
|
|
|
def save_incentives(
|
2020-10-07 15:18:43 +00:00
|
|
|
voucher: Voucher,
|
|
|
|
employees: List[schema_in.IncentiveEmployee],
|
|
|
|
point_value: Decimal,
|
|
|
|
db: Session,
|
2020-05-23 04:15:02 +00:00
|
|
|
):
|
|
|
|
total_amount = 0
|
|
|
|
for item in employees:
|
|
|
|
item_amount = round(item.points * item.days_worked * point_value)
|
|
|
|
journal = Journal(
|
2020-10-07 15:18:43 +00:00
|
|
|
amount=item_amount,
|
|
|
|
debit=-1,
|
|
|
|
account_id=item.employee_id,
|
|
|
|
cost_centre_id=item.cost_centre_id,
|
2020-05-23 04:15:02 +00:00
|
|
|
)
|
2020-10-07 16:59:24 +00:00
|
|
|
inc = Incentive(
|
|
|
|
journal=journal, days_worked=item.days_worked, points=item.points
|
|
|
|
)
|
2020-05-23 04:15:02 +00:00
|
|
|
voucher.journals.append(journal)
|
|
|
|
voucher.incentives.append(inc)
|
|
|
|
db.add(journal)
|
|
|
|
db.add(inc)
|
|
|
|
total_amount += item_amount
|
|
|
|
|
|
|
|
sc = db.query(Account).filter(Account.id == Account.incentive_id()).first()
|
2020-10-07 16:59:24 +00:00
|
|
|
journal = Journal(
|
|
|
|
amount=total_amount, debit=1, account_id=sc.id, cost_centre_id=sc.cost_centre_id
|
|
|
|
)
|
2020-05-23 04:15:02 +00:00
|
|
|
voucher.journals.append(journal)
|
|
|
|
db.add(journal)
|
|
|
|
|
|
|
|
|
|
|
|
@router.put("/{id_}")
|
2020-05-22 04:40:45 +00:00
|
|
|
def update_route(
|
|
|
|
id_: uuid.UUID,
|
|
|
|
request: Request,
|
2020-05-23 04:15:02 +00:00
|
|
|
data: schema_in.IncentiveIn = Depends(schema_in.IncentiveIn.load_form),
|
2020-05-22 04:40:45 +00:00
|
|
|
db: Session = Depends(get_db),
|
2020-05-23 04:15:02 +00:00
|
|
|
user: UserToken = Security(get_user, scopes=["incentive"]),
|
2020-05-22 04:40:45 +00:00
|
|
|
):
|
|
|
|
try:
|
2020-05-23 04:15:02 +00:00
|
|
|
item: Voucher = update(id_, data, user, db)
|
2020-10-07 16:59:24 +00:00
|
|
|
employees = get_employees(
|
|
|
|
get_first_day(data.date_), data.date_, data.incentives, item.journals, db
|
|
|
|
)
|
2020-05-23 04:15:02 +00:00
|
|
|
amount = balance(data.date_, item.id, db) * Decimal(0.9) # 10% for Deb Dip
|
|
|
|
total_points = sum(e.points * e.days_worked for e in employees)
|
|
|
|
point_value = round(amount / total_points, 2)
|
|
|
|
update_incentives(item, employees, point_value, db)
|
2020-05-22 04:40:45 +00:00
|
|
|
db.commit()
|
|
|
|
set_date(request.session, data.date_)
|
|
|
|
return voucher_info(item, db)
|
|
|
|
except SQLAlchemyError as e:
|
|
|
|
db.rollback()
|
|
|
|
raise HTTPException(
|
2020-10-07 15:18:43 +00:00
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
detail=str(e),
|
2020-05-22 04:40:45 +00:00
|
|
|
)
|
|
|
|
except Exception:
|
|
|
|
db.rollback()
|
2020-06-01 03:31:31 +00:00
|
|
|
raise
|
2020-05-22 04:40:45 +00:00
|
|
|
|
|
|
|
|
2020-10-07 16:59:24 +00:00
|
|
|
def update(
|
|
|
|
id_: uuid.UUID, data: schema_in.IncentiveIn, user: UserToken, db: Session
|
|
|
|
) -> Voucher:
|
2020-05-23 04:15:02 +00:00
|
|
|
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()
|
2020-05-22 04:40:45 +00:00
|
|
|
return voucher
|
2020-05-23 04:15:02 +00:00
|
|
|
|
|
|
|
|
|
|
|
def update_incentives(
|
2020-10-07 15:18:43 +00:00
|
|
|
voucher: Voucher,
|
|
|
|
employees: List[schema_in.IncentiveEmployee],
|
|
|
|
point_value: Decimal,
|
|
|
|
db: Session,
|
2020-05-23 04:15:02 +00:00
|
|
|
):
|
|
|
|
total_amount = 0
|
|
|
|
for item in voucher.incentives:
|
2020-10-07 16:59:24 +00:00
|
|
|
employee = next(
|
|
|
|
e for e in employees if e.employee_id == item.journal.account_id
|
|
|
|
)
|
2020-05-23 04:15:02 +00:00
|
|
|
item_amount = round(employee.points * employee.days_worked * point_value)
|
|
|
|
item.days_worked = employee.days_worked
|
|
|
|
item.points = employee.points
|
|
|
|
item.journal.amount = item_amount
|
|
|
|
total_amount += item_amount
|
|
|
|
|
2020-10-07 16:59:24 +00:00
|
|
|
journal = next(
|
|
|
|
j for j in voucher.journals if j.account_id == Account.incentive_id()
|
|
|
|
)
|
2020-05-23 04:15:02 +00:00
|
|
|
journal.amount = total_amount
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/{id_}", response_model=output.Voucher)
|
|
|
|
def get_id(
|
2020-10-07 15:18:43 +00:00
|
|
|
id_: uuid.UUID,
|
|
|
|
db: Session = Depends(get_db),
|
|
|
|
user: UserToken = Security(get_user, scopes=["incentive"]),
|
2020-05-23 04:15:02 +00:00
|
|
|
):
|
|
|
|
try:
|
|
|
|
item: Voucher = db.query(Voucher).filter(Voucher.id == id_).first()
|
|
|
|
return voucher_info(item, db)
|
|
|
|
except SQLAlchemyError as e:
|
|
|
|
db.rollback()
|
|
|
|
raise HTTPException(
|
2020-10-07 15:18:43 +00:00
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
detail=str(e),
|
2020-05-23 04:15:02 +00:00
|
|
|
)
|
|
|
|
except Exception:
|
|
|
|
db.rollback()
|
2020-06-01 03:31:31 +00:00
|
|
|
raise
|
2020-05-23 04:15:02 +00:00
|
|
|
|
|
|
|
|
|
|
|
@router.get("", response_model=output.Voucher)
|
|
|
|
def show_blank(
|
|
|
|
request: Request,
|
|
|
|
d: str = None,
|
|
|
|
db: Session = Depends(get_db),
|
|
|
|
user: UserToken = Security(get_user, scopes=["incentive"]),
|
|
|
|
):
|
|
|
|
additional_info = {"date": d or get_date(request.session), "type": "Incentive"}
|
|
|
|
return blank_voucher(additional_info, db)
|
|
|
|
|
|
|
|
|
|
|
|
def get_employees(
|
|
|
|
start_date: date,
|
|
|
|
finish_date: date,
|
|
|
|
incentives: List[schema_in.Incentive],
|
|
|
|
journals: Optional[List[Journal]],
|
|
|
|
db: Session,
|
|
|
|
) -> List[schema_in.IncentiveEmployee]:
|
|
|
|
details = []
|
|
|
|
employees = (
|
|
|
|
db.query(Employee)
|
|
|
|
.filter(Employee.joining_date <= finish_date)
|
|
|
|
.filter(or_(Employee.is_active, Employee.leaving_date >= start_date))
|
|
|
|
.order_by(Employee.cost_centre_id)
|
|
|
|
.order_by(Employee.designation)
|
|
|
|
.order_by(Employee.name)
|
|
|
|
.all()
|
|
|
|
)
|
|
|
|
|
|
|
|
check_if_employees_changed(incentives, employees, journals)
|
|
|
|
|
|
|
|
for employee in employees:
|
|
|
|
att = (
|
|
|
|
db.query(Attendance)
|
|
|
|
.filter(Attendance.employee_id == employee.id)
|
|
|
|
.filter(Attendance.date >= start_date)
|
|
|
|
.filter(Attendance.date <= finish_date)
|
|
|
|
.filter(Attendance.is_valid == True)
|
|
|
|
.all()
|
|
|
|
)
|
2020-10-07 16:59:24 +00:00
|
|
|
att = 0.5 * round(
|
|
|
|
sum(map(lambda x: AttendanceType.by_id(x.attendance_type).value, att)) / 0.5
|
|
|
|
)
|
2020-05-23 04:15:02 +00:00
|
|
|
points = next(x for x in incentives if x.employee_id == employee.id).points
|
|
|
|
details.append(
|
|
|
|
schema_in.IncentiveEmployee(
|
2020-10-07 15:18:43 +00:00
|
|
|
employee_id=employee.id,
|
|
|
|
cost_centre_id=employee.cost_centre_id,
|
|
|
|
days_worked=att,
|
|
|
|
points=points,
|
2020-05-23 04:15:02 +00:00
|
|
|
)
|
|
|
|
)
|
|
|
|
return details
|
|
|
|
|
|
|
|
|
|
|
|
def balance(date_: date, voucher_id: Optional[uuid.UUID], db: Session):
|
|
|
|
amount = (
|
|
|
|
db.query(func.sum(Journal.amount * Journal.debit))
|
|
|
|
.join(Journal.voucher)
|
|
|
|
.filter(Voucher.date <= date_)
|
|
|
|
.filter(Voucher.type != VoucherType.by_name("Issue").id)
|
|
|
|
.filter(Journal.account_id == Account.incentive_id())
|
|
|
|
)
|
|
|
|
if voucher_id is not None:
|
|
|
|
amount = amount.filter(Voucher.id != voucher_id)
|
|
|
|
amount = amount.scalar()
|
|
|
|
return 0 if amount is None else amount * -1
|
|
|
|
|
|
|
|
|
|
|
|
def check_if_employees_changed(
|
2020-10-07 15:18:43 +00:00
|
|
|
json: List[schema_in.Incentive],
|
|
|
|
db: List[Employee],
|
|
|
|
voucher: Optional[List[Journal]],
|
2020-05-23 04:15:02 +00:00
|
|
|
):
|
|
|
|
json = set(x.employee_id for x in json)
|
|
|
|
db = set(x.id for x in db)
|
|
|
|
voucher = (
|
2020-10-07 16:59:24 +00:00
|
|
|
set(x.account_id for x in voucher if x.account_id != Account.incentive_id())
|
|
|
|
if voucher is not None
|
|
|
|
else None
|
2020-05-23 04:15:02 +00:00
|
|
|
)
|
|
|
|
if voucher is None:
|
|
|
|
if len(json ^ db) != 0:
|
2020-10-07 14:07:28 +00:00
|
|
|
raise HTTPException(
|
2020-10-07 15:18:43 +00:00
|
|
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
|
|
|
detail="Employee missing in json data",
|
2020-10-07 14:07:28 +00:00
|
|
|
)
|
2020-05-23 04:15:02 +00:00
|
|
|
else:
|
|
|
|
if len(json ^ db) != 0 or len(db ^ voucher) != 0:
|
2020-10-07 14:07:28 +00:00
|
|
|
raise HTTPException(
|
2020-10-07 15:18:43 +00:00
|
|
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
|
|
|
detail="Employee missing in json data",
|
2020-10-07 14:07:28 +00:00
|
|
|
)
|