Files
brewman/brewman/brewman/routers/incentive.py
T
tanshu ca352649f0 Fix: The lock was not working because:
1. The Account Types sent were sqlqueries and not integer literals
2. Voucher Types and Account Types were being compared to VoucherTypesSelected and AccountTypesSelected objects and not their ids.
2021-09-10 22:36:07 +05:30

303 lines
10 KiB
Python

import uuid
from datetime import date, datetime
from decimal import Decimal
from typing import List, Optional
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 and_, distinct, func, or_, select
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session
from ..core.security import get_current_active_user as get_user
from ..core.session import get_date, get_first_day, set_date
from ..db.session import SessionFuture
from ..models.account import Account
from ..models.account_base import AccountBase
from ..models.attendance import Attendance
from ..models.attendance_type import AttendanceType
from ..models.employee import Employee
from ..models.incentive import Incentive
from ..models.journal import Journal
from ..models.validations import check_journals_are_valid
from ..models.voucher import Voucher
from ..models.voucher_type import VoucherType
from ..schemas.incentive import Incentive as IncentiveSchema
from ..schemas.user import UserToken
from . import get_lock_info
from .voucher import blank_voucher, check_voucher_edit_allowed, voucher_info
router = APIRouter()
@router.post("", response_model=output.Voucher)
def save_route(
request: Request,
data: schema_in.IncentiveIn = Depends(schema_in.IncentiveIn.load_form),
user: UserToken = Security(get_user, scopes=["incentive"]),
):
try:
with SessionFuture() as db:
item: Voucher = save(data, user, db)
employees = get_employees(get_first_day(data.date_), data.date_, data.incentives, None, db)
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)
check_journals_are_valid(item)
db.commit()
set_date(data.date_.strftime("%d-%b-%Y"), request.session)
info = voucher_info(item, db)
return info
except SQLAlchemyError as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
def save(data: schema_in.IncentiveIn, user: UserToken, db: Session) -> Voucher:
account_types = (
db.execute(
select(distinct(AccountBase.type)).where(AccountBase.id.in_([dj.employee_id for dj in data.incentives]))
)
.scalars()
.all()
)
allowed, message = get_lock_info([data.date_], VoucherType.by_name(data.type_).id, account_types, db)
if not allowed:
raise HTTPException(
status_code=status.HTTP_423_LOCKED,
detail=message,
)
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)
return voucher
def save_incentives(
voucher: Voucher,
employees: List[schema_in.IncentiveEmployee],
point_value: Decimal,
db: Session,
):
total_amount = 0
for item in employees:
item_amount = round(item.points * item.days_worked * point_value)
journal = Journal(
amount=item_amount,
debit=-1,
account_id=item.employee_id,
cost_centre_id=item.cost_centre_id,
)
inc = Incentive(journal=journal, days_worked=item.days_worked, points=item.points)
voucher.journals.append(journal)
voucher.incentives.append(inc)
db.add(journal)
db.add(inc)
total_amount += item_amount
sc = db.execute(select(Account).where(Account.id == Account.incentive_id())).scalar_one()
journal = Journal(amount=total_amount, debit=1, account_id=sc.id, cost_centre_id=sc.cost_centre_id)
voucher.journals.append(journal)
db.add(journal)
@router.put("/{id_}")
def update_route(
id_: uuid.UUID,
request: Request,
data: schema_in.IncentiveIn = Depends(schema_in.IncentiveIn.load_form),
user: UserToken = Security(get_user, scopes=["incentive"]),
):
try:
with SessionFuture() as db:
item: Voucher = update_voucher(id_, data, user, db)
employees = get_employees(get_first_day(data.date_), data.date_, data.incentives, item.journals, db)
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)
check_journals_are_valid(item)
db.commit()
set_date(data.date_.strftime("%d-%b-%Y"), request.session)
return voucher_info(item, db)
except SQLAlchemyError as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
def update_voucher(id_: uuid.UUID, data: schema_in.IncentiveIn, user: UserToken, db: Session) -> Voucher:
voucher: Voucher = db.execute(select(Voucher).where(Voucher.id == id_)).scalar_one()
account_types = (
db.execute(
select(distinct(AccountBase.type)).where(
AccountBase.id.in_(
[dj.employee_id for dj in data.incentives] + [vj.account_id for vj in voucher.journals]
)
)
)
.scalars()
.all()
)
allowed, message = get_lock_info([voucher.date, data.date_], voucher.type, account_types, db)
if not allowed:
raise HTTPException(
status_code=status.HTTP_423_LOCKED,
detail=message,
)
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_incentives(
voucher: Voucher,
employees: List[schema_in.IncentiveEmployee],
point_value: Decimal,
db: Session,
):
total_amount = 0
for item in voucher.incentives:
employee = next(e for e in employees if e.employee_id == item.journal.account_id)
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
journal = next(j for j in voucher.journals if j.account_id == Account.incentive_id())
journal.amount = total_amount
@router.get("/{id_}", response_model=output.Voucher)
def get_id(
id_: uuid.UUID,
user: UserToken = Security(get_user, scopes=["incentive"]),
):
try:
with SessionFuture() as db:
item: Voucher = db.execute(select(Voucher).where(Voucher.id == id_)).scalar_one()
return voucher_info(item, db)
except SQLAlchemyError as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
@router.get("", response_model=output.Voucher)
def show_blank(
request: Request,
d: str = None,
user: UserToken = Security(get_user, scopes=["incentive"]),
):
additional_info = {"date": d or get_date(request.session), "type": "Incentive"}
with SessionFuture() as db:
return blank_voucher(additional_info, db)
def get_employees(
start_date: date,
finish_date: date,
incentives: List[IncentiveSchema],
journals: Optional[List[Journal]],
db: Session,
) -> List[schema_in.IncentiveEmployee]:
details = []
employees = db.execute(
select(Employee)
.where(Employee.joining_date <= finish_date, or_(Employee.is_active, Employee.leaving_date >= start_date))
.order_by(Employee.cost_centre_id, Employee.designation, Employee.name)
.all()
)
check_if_employees_changed(incentives, employees, journals)
for employee in employees:
att = (
db.execute(
select(Attendance).where(
Attendance.employee_id == employee.id,
Attendance.date >= start_date,
Attendance.date <= finish_date,
Attendance.is_valid == True, # noqa: E712
)
)
.scalars()
.all()
)
att = 0.5 * round(sum(map(lambda x: AttendanceType.by_id(x.attendance_type).value, att)) / 0.5)
points = next(x for x in incentives if x.employee_id == employee.id).points
details.append(
schema_in.IncentiveEmployee(
employee_id=employee.id,
cost_centre_id=employee.cost_centre_id,
days_worked=att,
points=points,
)
)
return details
def balance(date_: date, voucher_id: Optional[uuid.UUID], db: Session):
amount = db.execute(
select(func.sum(Journal.amount * Journal.debit))
.join(Journal.voucher)
.where(
Journal.account_id == Account.incentive_id(),
Voucher.type != VoucherType.by_name("Issue").id,
or_(
Voucher.date <= date_,
and_(
Voucher.date == date_,
Voucher.type != VoucherType.by_name("Incentive").id,
),
),
)
)
if voucher_id is not None:
amount = amount.where(Voucher.id != voucher_id)
result: Decimal = amount.scalar()
return 0 if result is None else result * -1
def check_if_employees_changed(
json: List[IncentiveSchema],
db: List[Employee],
voucher: Optional[List[Journal]],
):
json = set(x.employee_id for x in json)
db = set(x.id for x in db)
voucher = (
set(x.account_id for x in voucher if x.account_id != Account.incentive_id()) if voucher is not None else None
)
if voucher is None:
if len(json ^ db) != 0:
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail="Employee missing in json data",
)
else:
if len(json ^ db) != 0 or len(db ^ voucher) != 0:
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail="Employee missing in json data",
)