Incentive Done!!
Employee Benefit Done!!
This commit is contained in:
parent
814d289758
commit
e3286c87ba
alembic/versions
brewman
overlord/src/app
core
employee-benefits
employee-benefits-resolver.service.tsemployee-benefits-routing.module.tsemployee-benefits.component.htmlemployee-benefits.component.ts
employee-functions
incentive
@ -1,4 +1,4 @@
|
||||
"""my test
|
||||
"""lowercase and fastapi
|
||||
|
||||
Revision ID: eed0b382c287
|
||||
Revises: 0bf3d70ee7de
|
||||
@ -8,7 +8,6 @@ Create Date: 2020-05-10 19:52:58.163810
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy import table, column
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'eed0b382c287'
|
||||
@ -193,9 +192,9 @@ def upgrade():
|
||||
op.drop_constraint('entities_salarydeductions_JournalID_fkey', 'employee_benefit', type_='foreignkey')
|
||||
op.drop_constraint('salary_deductions_VoucherID_fkey', 'employee_benefit', type_='foreignkey')
|
||||
|
||||
role = table('auth_roles', column('name', sa.String))
|
||||
op.execute(role.update().where(role.c.name == op.inline_literal('Service Charge')).values({'name': op.inline_literal('Incentive')}))
|
||||
op.execute(role.update().where(role.c.name == op.inline_literal('Salary Deduction')).values({'name': op.inline_literal('Employee Benefit')}))
|
||||
permission = table('auth_permissions', column('name', sa.String))
|
||||
op.execute(permission.update().where(permission.c.name == op.inline_literal('Service Charge')).values({'name': op.inline_literal('Incentive')}))
|
||||
op.execute(permission.update().where(permission.c.name == op.inline_literal('Salary Deduction')).values({'name': op.inline_literal('Employee Benefit')}))
|
||||
account = table('accounts', column('name', sa.String))
|
||||
op.execute(account.update().where(account.c.name == op.inline_literal('Service Charges')).values({'name': op.inline_literal('Incentives')}))
|
||||
### end Alembic commands ###
|
||||
|
@ -15,6 +15,7 @@ from .routers import (
|
||||
employee_attendance,
|
||||
employee_benefit,
|
||||
fingerprint,
|
||||
incentive,
|
||||
issue,
|
||||
issue_grid,
|
||||
lock_information,
|
||||
@ -29,7 +30,7 @@ from .routers import (
|
||||
login,
|
||||
journal,
|
||||
purchase,
|
||||
purchase_return
|
||||
purchase_return,
|
||||
)
|
||||
from .routers.auth import client, user, role
|
||||
from .routers.reports import (
|
||||
@ -99,13 +100,23 @@ app.include_router(profit_loss.router, prefix="/api/profit-loss", tags=["reports
|
||||
app.include_router(closing_stock.router, prefix="/api/closing-stock", tags=["reports"])
|
||||
app.include_router(cash_flow.router, prefix="/api/cash-flow", tags=["reports"])
|
||||
app.include_router(daybook.router, prefix="/api/daybook", tags=["reports"])
|
||||
app.include_router(net_transactions.router, prefix="/api/net-transactions", tags=["reports"])
|
||||
app.include_router(product_ledger.router, prefix="/api/product-ledger", tags=["reports"])
|
||||
app.include_router(purchase_entries.router, prefix="/api/purchase-entries", tags=["reports"])
|
||||
app.include_router(
|
||||
net_transactions.router, prefix="/api/net-transactions", tags=["reports"]
|
||||
)
|
||||
app.include_router(
|
||||
product_ledger.router, prefix="/api/product-ledger", tags=["reports"]
|
||||
)
|
||||
app.include_router(
|
||||
purchase_entries.router, prefix="/api/purchase-entries", tags=["reports"]
|
||||
)
|
||||
app.include_router(purchases.router, prefix="/api/purchases", tags=["reports"])
|
||||
app.include_router(raw_material_cost.router, prefix="/api/raw-material-cost", tags=["reports"])
|
||||
app.include_router(
|
||||
raw_material_cost.router, prefix="/api/raw-material-cost", tags=["reports"]
|
||||
)
|
||||
app.include_router(reconcile.router, prefix="/api/reconcile", tags=["reports"])
|
||||
app.include_router(stock_movement.router, prefix="/api/stock-movement", tags=["reports"])
|
||||
app.include_router(
|
||||
stock_movement.router, prefix="/api/stock-movement", tags=["reports"]
|
||||
)
|
||||
app.include_router(trial_balance.router, prefix="/api/trial-balance", tags=["reports"])
|
||||
app.include_router(unposted.router, prefix="/api/unposted", tags=["reports"])
|
||||
|
||||
@ -115,11 +126,18 @@ app.include_router(journal.router, prefix="/api/journal", tags=["vouchers"])
|
||||
app.include_router(payment.router, prefix="/api/payment", tags=["vouchers"])
|
||||
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(
|
||||
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(
|
||||
employee_benefit.router, prefix="/api/employee-benefit", tags=["vouchers"]
|
||||
)
|
||||
app.include_router(incentive.router, prefix="/api/incentive", tags=["vouchers"])
|
||||
|
||||
app.include_router(lock_information.router, prefix="/api/lock-information", tags=["settings"])
|
||||
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"])
|
||||
|
@ -241,7 +241,7 @@ class Incentive(Base):
|
||||
id = Column("id", GUID(), primary_key=True, default=uuid.uuid4)
|
||||
voucher_id = Column("voucher_id", GUID(), ForeignKey("vouchers.id"), nullable=False)
|
||||
journal_id = Column("journal_id", GUID(), ForeignKey("journals.id"), nullable=False)
|
||||
days_worked = Column("days_worked", Integer, nullable=False)
|
||||
days_worked = Column("days_worked", Numeric(precision=5, scale=1), nullable=False)
|
||||
points = Column("points", Numeric(precision=5, scale=2), nullable=False)
|
||||
|
||||
journal = relationship(
|
||||
@ -253,20 +253,20 @@ class Incentive(Base):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
id=None,
|
||||
id_=None,
|
||||
voucher_id=None,
|
||||
journal_id=None,
|
||||
journal=None,
|
||||
days_worked=None,
|
||||
points=None,
|
||||
):
|
||||
self.id = id
|
||||
self.id = id_
|
||||
self.voucher_id = voucher_id
|
||||
self.journal_id = journal_id
|
||||
if journal is None:
|
||||
self.journal_id = journal_id
|
||||
self.days_worked = days_worked
|
||||
self.points = points
|
||||
if journal_id is None and journal is not None:
|
||||
self.journal = journal
|
||||
self.journal = journal
|
||||
|
||||
|
||||
class Inventory(Base):
|
||||
@ -296,7 +296,7 @@ class Inventory(Base):
|
||||
tax=None,
|
||||
discount=None,
|
||||
batch=None,
|
||||
product=None
|
||||
product=None,
|
||||
):
|
||||
self.id = id_
|
||||
self.voucher_id = voucher_id
|
||||
@ -339,7 +339,7 @@ class Batch(Base):
|
||||
rate=None,
|
||||
tax=None,
|
||||
discount=None,
|
||||
product=None
|
||||
product=None,
|
||||
):
|
||||
self.name = name
|
||||
self.product_id = product_id
|
||||
|
@ -1,6 +1,5 @@
|
||||
import traceback
|
||||
import uuid
|
||||
from decimal import Decimal
|
||||
from math import ceil
|
||||
from typing import List
|
||||
from datetime import datetime, date
|
||||
@ -14,7 +13,6 @@ from fastapi import (
|
||||
File,
|
||||
Request,
|
||||
)
|
||||
from sqlalchemy import func
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
@ -25,15 +23,13 @@ from .voucher import (
|
||||
blank_voucher,
|
||||
)
|
||||
from ..core.session import set_date, get_date, get_last_day
|
||||
from ..models import Product, AccountBase, Employee
|
||||
from ..models import 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,
|
||||
VoucherType,
|
||||
Batch,
|
||||
Inventory,
|
||||
Journal,
|
||||
EmployeeBenefit,
|
||||
)
|
||||
@ -112,7 +108,7 @@ def save_employee_benefits(
|
||||
):
|
||||
total_exp, total_total = 0, 0
|
||||
for item in employee_benefits:
|
||||
account = db.query(Employee).filter(Employee.id == item.employee_id).first()
|
||||
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(
|
||||
@ -201,7 +197,7 @@ def update_route(
|
||||
exp, total = update_employee_benefits(
|
||||
item, data.employee_benefits, days_in_month, db
|
||||
)
|
||||
update_journals(item, exp, total, db)
|
||||
update_journals(item, exp, total)
|
||||
# journals_valid(voucher)
|
||||
update_files(data, i + t, db)
|
||||
db.commit()
|
||||
|
@ -1,18 +1,40 @@
|
||||
import traceback
|
||||
import uuid
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter, HTTPException, status, Depends, Security, UploadFile, File, Request
|
||||
from decimal import Decimal
|
||||
from typing import List, Optional
|
||||
from datetime import date, datetime
|
||||
from fastapi import (
|
||||
APIRouter,
|
||||
HTTPException,
|
||||
status,
|
||||
Depends,
|
||||
Security,
|
||||
Request,
|
||||
)
|
||||
from sqlalchemy import or_, 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_first_day
|
||||
from ..models import Employee, AttendanceType, Account
|
||||
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,
|
||||
Journal,
|
||||
Incentive,
|
||||
Attendance,
|
||||
)
|
||||
import brewman.schemas.voucher as output
|
||||
import brewman.schemas.input as schema_in
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@ -26,25 +48,30 @@ 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.IncentiveIn = Depends(schema_in.IncentiveIn.load_form),
|
||||
db: Session = Depends(get_db),
|
||||
files: List[UploadFile] = File(...),
|
||||
user: UserToken = Security(get_user, scopes=["journal"]),
|
||||
user: UserToken = Security(get_user, scopes=["incentive"]),
|
||||
):
|
||||
try:
|
||||
item: Voucher = save(data, files, user, 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)
|
||||
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,33 +81,75 @@ def save_route(
|
||||
)
|
||||
|
||||
|
||||
def save(data: schemas.Voucher, files: List[UploadFile], user: UserToken, db: Session) -> Voucher:
|
||||
def save(data: schema_in.IncentiveIn, 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)
|
||||
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
|
||||
|
||||
|
||||
@router.get("/{id_}")
|
||||
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.query(Account).filter(Account.id == Account.incentive_id()).first()
|
||||
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: schemas.Voucher,
|
||||
data: schema_in.IncentiveIn = Depends(schema_in.IncentiveIn.load_form),
|
||||
db: Session = Depends(get_db),
|
||||
files: List[UploadFile] = File(...),
|
||||
user: UserToken = Security(get_user, scopes=["journal"]),
|
||||
user: UserToken = Security(get_user, scopes=["incentive"]),
|
||||
):
|
||||
try:
|
||||
item: Voucher = update(id_, data, files, user, db)
|
||||
item: Voucher = update(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)
|
||||
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),
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e),
|
||||
)
|
||||
except Exception:
|
||||
db.rollback()
|
||||
@ -90,10 +159,149 @@ 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 ["Incentive"]:
|
||||
voucher = incentive_update_voucher(item, data, files, user, db)
|
||||
def update(
|
||||
id_: uuid.UUID, data: schema_in.IncentiveIn, 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_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,
|
||||
db: Session = Depends(get_db),
|
||||
user: UserToken = Security(get_user, scopes=["incentive"]),
|
||||
):
|
||||
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,
|
||||
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()
|
||||
)
|
||||
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.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(
|
||||
json: List[schema_in.Incentive],
|
||||
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 ValueError("Employee missing in json data")
|
||||
else:
|
||||
if len(json ^ db) != 0 or len(db ^ voucher) != 0:
|
||||
raise ValueError("Employee missing in json data")
|
||||
|
@ -22,8 +22,6 @@ from brewman.models.voucher import (
|
||||
Journal,
|
||||
)
|
||||
from brewman.routers import get_lock_info
|
||||
from .incentive import incentive_create_voucher, incentive_update_voucher
|
||||
from .employee_benefit import employee_benefit_create_voucher, employee_benefit_update_voucher
|
||||
from ...core.session import get_first_day
|
||||
|
||||
from fastapi import APIRouter, Depends, Security, Request, HTTPException, status
|
||||
@ -37,19 +35,23 @@ router = APIRouter()
|
||||
def voucher_post(request):
|
||||
user = (
|
||||
request.dbsession.query(User)
|
||||
.filter(User.id == uuid.UUID(request.authenticated_userid))
|
||||
.one()
|
||||
.filter(User.id == uuid.UUID(request.authenticated_userid))
|
||||
.one()
|
||||
)
|
||||
voucher = (
|
||||
request.dbsession.query(Voucher)
|
||||
.filter(Voucher.id == uuid.UUID(request.matchdict["id"]))
|
||||
.first()
|
||||
.filter(Voucher.id == uuid.UUID(request.matchdict["id"]))
|
||||
.first()
|
||||
)
|
||||
start, finish = get_lock_info(request.dbsession)
|
||||
if start is not None and start > voucher.date:
|
||||
raise ValidationError(f"Vouchers before {start.strftime('%d-%b-%Y')} have been locked.")
|
||||
raise ValidationError(
|
||||
f"Vouchers before {start.strftime('%d-%b-%Y')} have been locked."
|
||||
)
|
||||
elif finish is not None and finish < voucher.date:
|
||||
raise ValidationError(f"Vouchers after {finish.strftime('%d-%b-%Y')} have been locked.")
|
||||
raise ValidationError(
|
||||
f"Vouchers after {finish.strftime('%d-%b-%Y')} have been locked."
|
||||
)
|
||||
voucher.posted = True
|
||||
voucher.poster_id = user.id
|
||||
transaction.commit()
|
||||
@ -62,8 +64,8 @@ def voucher_post(request):
|
||||
def check_delete_permissions(request, voucher):
|
||||
user = (
|
||||
request.dbsession.query(User)
|
||||
.filter(User.id == uuid.UUID(request.authenticated_userid))
|
||||
.one()
|
||||
.filter(User.id == uuid.UUID(request.authenticated_userid))
|
||||
.one()
|
||||
)
|
||||
|
||||
if voucher.posted and not request.has_permission("Edit Posted Vouchers"):
|
||||
@ -71,22 +73,28 @@ def check_delete_permissions(request, voucher):
|
||||
response.status_int = 403
|
||||
return response
|
||||
elif voucher.user_id != user.id and not request.has_permission(
|
||||
"Edit Other User's Vouchers"
|
||||
"Edit Other User's Vouchers"
|
||||
):
|
||||
response = Response("You are not allowed to edit other user's vouchers")
|
||||
response.status_int = 403
|
||||
return response
|
||||
elif not request.has_permission(VoucherType.by_id(voucher.type).name):
|
||||
response = Response(f"You are not allowed ({VoucherType.by_id(voucher.type).name}) vouchers")
|
||||
response = Response(
|
||||
f"You are not allowed ({VoucherType.by_id(voucher.type).name}) vouchers"
|
||||
)
|
||||
response.status_int = 403
|
||||
return response
|
||||
start, finish = get_lock_info(request.dbsession)
|
||||
if start is not None and start > voucher.date:
|
||||
response = Response(f"Vouchers before {start.strftime('%d-%b-%Y')} have been locked.")
|
||||
response = Response(
|
||||
f"Vouchers before {start.strftime('%d-%b-%Y')} have been locked."
|
||||
)
|
||||
response.status_int = 403
|
||||
return response
|
||||
elif finish is not None and finish < voucher.date:
|
||||
response = Response(f"Vouchers after {finish.strftime('%d-%b-%Y')} have been locked.")
|
||||
response = Response(
|
||||
f"Vouchers after {finish.strftime('%d-%b-%Y')} have been locked."
|
||||
)
|
||||
response.status_int = 403
|
||||
return response
|
||||
|
||||
@ -95,8 +103,8 @@ def check_delete_permissions(request, voucher):
|
||||
def delete(request):
|
||||
voucher = (
|
||||
request.dbsession.query(Voucher)
|
||||
.filter(Voucher.id == uuid.UUID(request.matchdict["id"]))
|
||||
.first()
|
||||
.filter(Voucher.id == uuid.UUID(request.matchdict["id"]))
|
||||
.first()
|
||||
)
|
||||
images = (
|
||||
request.dbsession.query(DbImage).filter(DbImage.resource_id == voucher.id).all()
|
||||
@ -128,24 +136,20 @@ def delete(request):
|
||||
for item in voucher.inventories:
|
||||
if item.batch.quantity_remaining < item.batch.quantity_remaining:
|
||||
raise ValueError(
|
||||
"Quantity remaining for {0} is less than issued, hence cannot be deleted".format(
|
||||
item.product.name
|
||||
)
|
||||
f"Quantity remaining for {item.product.name} is less than issued, hence cannot be deleted"
|
||||
)
|
||||
item.batch.quantity_remaining -= item.quantity
|
||||
elif voucher.type == VoucherType.by_name("Purchase").id:
|
||||
for item in voucher.inventories:
|
||||
uses = (
|
||||
request.dbsession.query(func.count(Inventory.id))
|
||||
.filter(Inventory.batch_id == item.batch.id)
|
||||
.filter(Inventory.id != item.id)
|
||||
.scalar()
|
||||
.filter(Inventory.batch_id == item.batch.id)
|
||||
.filter(Inventory.id != item.id)
|
||||
.scalar()
|
||||
)
|
||||
if uses > 0:
|
||||
raise ValueError(
|
||||
"{0} has been issued and cannot be deleted".format(
|
||||
item.product.name
|
||||
)
|
||||
f"{item.product.name} has been issued and cannot be deleted"
|
||||
)
|
||||
batches_to_delete.append(item.batch)
|
||||
elif voucher.type == VoucherType.by_name("Purchase Return").id:
|
||||
@ -181,6 +185,7 @@ def voucher_info(voucher, db):
|
||||
"inventories": [],
|
||||
"employeeBenefits": [],
|
||||
"incentives": [],
|
||||
"incentive": 0,
|
||||
"files": [],
|
||||
"creationDate": voucher.creation_date.strftime("%d-%b-%Y %H:%M"),
|
||||
"lastEditDate": voucher.last_edit_date.strftime("%d-%b-%Y %H:%M"),
|
||||
@ -188,9 +193,7 @@ def voucher_info(voucher, db):
|
||||
"poster": voucher.poster.name if voucher.posted else "",
|
||||
}
|
||||
if voucher.reconcile_date is not None:
|
||||
json_voucher["reconcileDate"] = voucher.reconcile_date.strftime(
|
||||
"%d-%b-%Y"
|
||||
)
|
||||
json_voucher["reconcileDate"] = voucher.reconcile_date.strftime("%d-%b-%Y")
|
||||
if voucher.type == 2: # "Purchase"
|
||||
item = [j for j in voucher.journals if j.debit == -1][0]
|
||||
json_voucher["vendor"] = {"id": item.account.id, "name": item.account.name}
|
||||
@ -219,29 +222,24 @@ def voucher_info(voucher, db):
|
||||
"pfEmployee": item.pf_ee,
|
||||
"esiEmployer": item.esi_er,
|
||||
"pfEmployer": item.pf_er,
|
||||
"journal": {
|
||||
"id": item.journal.id,
|
||||
"account": {
|
||||
"id": item.journal.account.id,
|
||||
"name": item.journal.account.name,
|
||||
"designation": item.journal.account.designation,
|
||||
"costCentre": {
|
||||
"id": item.journal.account.cost_centre.id,
|
||||
"name": item.journal.account.cost_centre.name,
|
||||
},
|
||||
"employee": {
|
||||
"id": item.journal.account.id,
|
||||
"name": item.journal.account.name,
|
||||
"designation": item.journal.account.designation,
|
||||
"costCentre": {
|
||||
"id": item.journal.account.cost_centre.id,
|
||||
"name": item.journal.account.cost_centre.name,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
for item in voucher.incentives:
|
||||
employee = (
|
||||
db.query(Employee)
|
||||
.filter(Employee.id == item.journal.account_id)
|
||||
.first()
|
||||
db.query(Employee).filter(Employee.id == item.journal.account_id).first()
|
||||
)
|
||||
json_voucher["incentives"].append(
|
||||
{
|
||||
"id": item.journal.account_id,
|
||||
"employeeId": item.journal.account_id,
|
||||
"name": item.journal.account.name,
|
||||
"designation": employee.designation,
|
||||
"department": item.journal.account.cost_centre.name,
|
||||
@ -250,11 +248,9 @@ def voucher_info(voucher, db):
|
||||
}
|
||||
)
|
||||
if len(json_voucher["incentives"]) > 0:
|
||||
json_voucher["incentive"] = [
|
||||
x.amount
|
||||
for x in voucher.journals
|
||||
if x.account_id == Account.incentive_id()
|
||||
][0]
|
||||
json_voucher["incentive"] = next(
|
||||
x.amount for x in voucher.journals if x.account_id == Account.incentive_id()
|
||||
)
|
||||
for item in voucher.inventories:
|
||||
text = f"{item.product.name} ({item.product.units}) {item.batch.quantity_remaining:.2f}@{item.batch.rate:.2f} from {item.batch.name.strftime('%d-%b-%Y')}"
|
||||
json_voucher["inventories"].append(
|
||||
@ -285,9 +281,7 @@ def voucher_info(voucher, db):
|
||||
},
|
||||
}
|
||||
)
|
||||
images = (
|
||||
db.query(DbImage).filter(DbImage.resource_id == voucher.id).all()
|
||||
)
|
||||
images = db.query(DbImage).filter(DbImage.resource_id == voucher.id).all()
|
||||
for image in images:
|
||||
json_voucher["files"].append({"id": image.id, "resized": "", "thumbnail": ""})
|
||||
return json_voucher
|
||||
@ -315,8 +309,8 @@ def blank_voucher(info, db):
|
||||
if info and "account" in info and info["account"]:
|
||||
account = (
|
||||
db.query(AccountBase)
|
||||
.filter(AccountBase.id == uuid.UUID(info["account"]))
|
||||
.first()
|
||||
.filter(AccountBase.id == uuid.UUID(info["account"]))
|
||||
.first()
|
||||
)
|
||||
if account is not None:
|
||||
account = {"id": account.id, "name": account.name}
|
||||
@ -328,8 +322,8 @@ def blank_voucher(info, db):
|
||||
if info and "account" in info and info["account"]:
|
||||
account = (
|
||||
db.query(AccountBase)
|
||||
.filter(AccountBase.id == uuid.UUID(info["account"]))
|
||||
.first()
|
||||
.filter(AccountBase.id == uuid.UUID(info["account"]))
|
||||
.first()
|
||||
)
|
||||
if account is not None:
|
||||
account = {"id": account.id, "name": account.name}
|
||||
@ -355,42 +349,42 @@ def blank_voucher(info, db):
|
||||
elif type_ == "Employee Benefit":
|
||||
json_voucher["employeeBenefits"] = []
|
||||
elif type_ == "Incentive":
|
||||
json_voucher["incentives"], json_voucher[
|
||||
"incentive"
|
||||
] = incentive_employees(info["date"], dbsession)
|
||||
json_voucher["incentives"], json_voucher["incentive"] = incentive_employees(
|
||||
info["date"], db
|
||||
)
|
||||
else:
|
||||
raise ValidationError(f'Voucher of type "{type_}" does not exist.')
|
||||
raise ValueError(f'Voucher of type "{type_}" does not exist.')
|
||||
json_voucher["files"] = []
|
||||
return json_voucher
|
||||
|
||||
|
||||
def incentive_employees(date, dbsession):
|
||||
date = datetime.datetime.strptime(date, "%d-%b-%Y")
|
||||
start_date = get_first_day(date)
|
||||
finish_date = date + datetime.timedelta(1)
|
||||
def incentive_employees(date_, db: Session):
|
||||
date_ = datetime.strptime(date_, "%d-%b-%Y")
|
||||
start_date = get_first_day(date_)
|
||||
finish_date = date_
|
||||
details = []
|
||||
employees = (
|
||||
dbsession.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()
|
||||
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()
|
||||
)
|
||||
for employee in employees:
|
||||
att = (
|
||||
dbsession.query(Attendance)
|
||||
.filter(Attendance.employee_id == employee.id)
|
||||
.filter(Attendance.date >= start_date)
|
||||
.filter(Attendance.date < finish_date)
|
||||
.filter(Attendance.is_valid == True)
|
||||
.all()
|
||||
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()
|
||||
)
|
||||
att = sum(map(lambda x: AttendanceType.by_id(x.attendance_type).value, att))
|
||||
details.append(
|
||||
{
|
||||
"id": employee.id,
|
||||
"employeeId": employee.id,
|
||||
"name": employee.name,
|
||||
"designation": employee.designation,
|
||||
"department": employee.cost_centre.name,
|
||||
@ -400,12 +394,12 @@ def incentive_employees(date, dbsession):
|
||||
)
|
||||
|
||||
amount = (
|
||||
dbsession.query(func.sum(Journal.amount * Journal.debit))
|
||||
.join(Journal.voucher)
|
||||
.filter(Voucher.date < finish_date)
|
||||
.filter(Voucher.type != VoucherType.by_name("Issue").id)
|
||||
.filter(Journal.account_id == Account.incentive_id())
|
||||
.scalar()
|
||||
db.query(func.sum(Journal.amount * Journal.debit))
|
||||
.join(Journal.voucher)
|
||||
.filter(Voucher.date < finish_date)
|
||||
.filter(Voucher.type != VoucherType.by_name("Issue").id)
|
||||
.filter(Journal.account_id == Account.incentive_id())
|
||||
.scalar()
|
||||
)
|
||||
amount = 0 if amount is None else amount * Decimal(0.9) * -1
|
||||
return details, amount
|
||||
@ -413,12 +407,22 @@ def incentive_employees(date, dbsession):
|
||||
|
||||
def check_voucher_lock_info(voucher_date: Optional[date], data_date: date, db: Session):
|
||||
start, finish = get_lock_info(db)
|
||||
if start is not None and start > data_date or voucher_date is not None and start > voucher_date:
|
||||
if (
|
||||
start is not None
|
||||
and start > data_date
|
||||
or voucher_date is not None
|
||||
and start > voucher_date
|
||||
):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_423_LOCKED,
|
||||
detail=f"Vouchers before {start.strftime('%d-%b-%Y')} have been locked.",
|
||||
)
|
||||
elif finish is not None and finish < data_date or voucher_date is not None and finish < voucher_date:
|
||||
elif (
|
||||
finish is not None
|
||||
and finish < data_date
|
||||
or voucher_date is not None
|
||||
and finish < voucher_date
|
||||
):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_423_LOCKED,
|
||||
detail=f"Vouchers after {finish.strftime('%d-%b-%Y')} have been locked.",
|
||||
@ -441,7 +445,10 @@ def check_voucher_edit_allowed(voucher: Voucher, user: UserToken):
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="You are not allowed to edit posted vouchers",
|
||||
)
|
||||
elif voucher.user_id != user.id_ and "edit-other-user's-vouchers" not in user.permissions:
|
||||
elif (
|
||||
voucher.user_id != user.id_
|
||||
and "edit-other-user's-vouchers" not in user.permissions
|
||||
):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="You are not allowed to edit other user's vouchers",
|
||||
|
@ -1,178 +0,0 @@
|
||||
import datetime
|
||||
import uuid
|
||||
from decimal import Decimal
|
||||
|
||||
from sqlalchemy import or_, func
|
||||
|
||||
from brewman.models.master import Employee, AttendanceType, Account
|
||||
from brewman.models.voucher import (
|
||||
Journal,
|
||||
Voucher,
|
||||
VoucherType,
|
||||
Attendance,
|
||||
Incentive,
|
||||
)
|
||||
from brewman.core.session import get_first_day
|
||||
|
||||
|
||||
def incentive_create_voucher(json, user, dbsession):
|
||||
date = datetime.datetime.strptime(json["date"], "%d-%b-%Y")
|
||||
|
||||
voucher = Voucher(date=date, user_id=user.id, type=VoucherType.by_id(13))
|
||||
dbsession.add(voucher)
|
||||
|
||||
employees = get_employees(
|
||||
get_first_day(date),
|
||||
date + datetime.timedelta(1),
|
||||
json["incentives"],
|
||||
dbsession=dbsession,
|
||||
)
|
||||
amount = balance(date, dbsession=dbsession) * Decimal(0.9)
|
||||
|
||||
total_points = 0
|
||||
for item in employees:
|
||||
total_points += item["points"] * item["daysWorked"]
|
||||
point_value = round(amount / total_points, 2)
|
||||
total_amount = 0
|
||||
|
||||
for item in employees:
|
||||
item_amount = round(item["points"] * item["daysWorked"] * point_value)
|
||||
employee = item["employee"]
|
||||
journal = Journal(
|
||||
amount=item_amount,
|
||||
debit=-1,
|
||||
account_id=employee.id,
|
||||
cost_centre_id=employee.cost_centre_id,
|
||||
)
|
||||
inc = Incentive(
|
||||
journal=journal, days_worked=item["daysWorked"], points=item["points"]
|
||||
)
|
||||
voucher.journals.append(journal)
|
||||
voucher.incentives.append(inc)
|
||||
dbsession.add(journal)
|
||||
dbsession.add(inc)
|
||||
total_amount += item_amount
|
||||
|
||||
sc = (
|
||||
dbsession.query(Account)
|
||||
.filter(Account.id == Account.incentive_id())
|
||||
.first()
|
||||
)
|
||||
journal = Journal(
|
||||
amount=total_amount, debit=1, account_id=sc.id, cost_centre_id=sc.cost_centre_id
|
||||
)
|
||||
voucher.journals.append(journal)
|
||||
dbsession.add(journal)
|
||||
journals_valid(voucher)
|
||||
return voucher
|
||||
|
||||
|
||||
def incentive_update_voucher(voucher, json, user, dbsession):
|
||||
date = datetime.datetime.strptime(json["date"], "%d-%b-%Y")
|
||||
voucher.date = date
|
||||
voucher.user_id = user.id
|
||||
voucher.posted = False
|
||||
voucher.last_edit_date = datetime.datetime.utcnow()
|
||||
|
||||
employees = get_employees(
|
||||
get_first_day(date),
|
||||
date + datetime.timedelta(1),
|
||||
json["incentives"],
|
||||
voucher.journals,
|
||||
dbsession=dbsession,
|
||||
)
|
||||
amount = balance(date, voucher.id, dbsession) * Decimal(0.9)
|
||||
|
||||
total_points = 0
|
||||
for item in employees:
|
||||
total_points += item["points"] * item["daysWorked"]
|
||||
point_value = round(amount / total_points, 2)
|
||||
total_amount = 0
|
||||
|
||||
for item in voucher.incentives:
|
||||
employee = [
|
||||
e for e in employees if e["employee"].id == item.journal.account_id
|
||||
][0]
|
||||
item_amount = round(employee["points"] * employee["daysWorked"] * point_value)
|
||||
item.days_worked = employee["daysWorked"]
|
||||
item.points = employee["points"]
|
||||
item.journal.amount = item_amount
|
||||
total_amount += item_amount
|
||||
|
||||
journal = [
|
||||
j for j in voucher.journals if j.account_id == Account.incentive_id()
|
||||
][0]
|
||||
journal.amount = total_amount
|
||||
|
||||
journals_valid(voucher)
|
||||
return voucher
|
||||
|
||||
|
||||
def get_employees(
|
||||
start_date, finish_date, json, voucher_employees=None, dbsession=None
|
||||
):
|
||||
details = []
|
||||
employees = (
|
||||
dbsession.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(json, employees, voucher_employees)
|
||||
|
||||
for employee in employees:
|
||||
att = (
|
||||
dbsession.query(Attendance)
|
||||
.filter(Attendance.employee_id == employee.id)
|
||||
.filter(Attendance.date >= start_date)
|
||||
.filter(Attendance.date < finish_date)
|
||||
.filter(Attendance.is_valid == True)
|
||||
.all()
|
||||
)
|
||||
att = sum(map(lambda x: AttendanceType.by_id(x.attendance_type).value, att))
|
||||
details.append({"employee": employee, "daysWorked": round(Decimal(att), 2)})
|
||||
for item in details:
|
||||
j = [x for x in json if uuid.UUID(x["id"]) == item["employee"].id][0]
|
||||
item["points"] = round(Decimal(j["points"]), 2)
|
||||
json.remove(j)
|
||||
return details
|
||||
|
||||
|
||||
def balance(date, voucher_id=None, dbsession=None):
|
||||
amount = (
|
||||
dbsession.query(func.sum(Journal.amount * Journal.debit))
|
||||
.join(Journal.voucher)
|
||||
.filter(Voucher.date < date + datetime.timedelta(1))
|
||||
.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(json, db, voucher):
|
||||
json = set([uuid.UUID(x["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 ValueError("Employee missing in json data")
|
||||
else:
|
||||
if len(json ^ db) != 0 or len(db ^ voucher) != 0:
|
||||
raise ValueError("Employee missing in json data")
|
@ -5,12 +5,18 @@ from decimal import Decimal
|
||||
from typing import List, Optional
|
||||
|
||||
from fastapi import Form
|
||||
from pydantic import validator
|
||||
from pydantic import validator, BaseModel, Field
|
||||
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, EmployeeBenefit
|
||||
from brewman.schemas.voucher import (
|
||||
VoucherIn,
|
||||
Journal,
|
||||
Inventory,
|
||||
EmployeeBenefit,
|
||||
Incentive,
|
||||
)
|
||||
|
||||
|
||||
class JournalIn(VoucherIn):
|
||||
@ -21,7 +27,7 @@ class JournalIn(VoucherIn):
|
||||
alias_generator = to_camel
|
||||
json_encoders = {
|
||||
date: lambda v: v.strftime("%d-%b-%Y"),
|
||||
datetime: lambda v: v.strftime("%d-%b-%Y %H:%I")
|
||||
datetime: lambda v: v.strftime("%d-%b-%Y %H:%I"),
|
||||
}
|
||||
|
||||
@validator("date_", pre=True)
|
||||
@ -38,7 +44,11 @@ class JournalIn(VoucherIn):
|
||||
|
||||
@validator("journals")
|
||||
def is_distinct(cls, value: List[Journal]):
|
||||
journal_set = set(hash(x.account.id_) ^ hash(None if x.cost_centre is None else x.cost_centre.id_) for x in value)
|
||||
journal_set = set(
|
||||
hash(x.account.id_)
|
||||
^ hash(None if x.cost_centre is None else x.cost_centre.id_)
|
||||
for x in value
|
||||
)
|
||||
if len(value) != len(journal_set):
|
||||
raise ValueError("Duplicate journals")
|
||||
return value
|
||||
@ -58,7 +68,7 @@ class PurchaseIn(VoucherIn):
|
||||
alias_generator = to_camel
|
||||
json_encoders = {
|
||||
date: lambda v: v.strftime("%d-%b-%Y"),
|
||||
datetime: lambda v: v.strftime("%d-%b-%Y %H:%I")
|
||||
datetime: lambda v: v.strftime("%d-%b-%Y %H:%I"),
|
||||
}
|
||||
|
||||
@validator("date_", pre=True)
|
||||
@ -67,13 +77,13 @@ class PurchaseIn(VoucherIn):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@validator("inventories") # For Purchase, Issue and Return Vouchers
|
||||
@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
|
||||
@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")
|
||||
@ -95,7 +105,7 @@ class IssueIn(VoucherIn):
|
||||
alias_generator = to_camel
|
||||
json_encoders = {
|
||||
date: lambda v: v.strftime("%d-%b-%Y"),
|
||||
datetime: lambda v: v.strftime("%d-%b-%Y %H:%I")
|
||||
datetime: lambda v: v.strftime("%d-%b-%Y %H:%I"),
|
||||
}
|
||||
|
||||
@validator("date_", pre=True)
|
||||
@ -104,21 +114,21 @@ class IssueIn(VoucherIn):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@validator("inventories") # For Purchase, Issue and Return Vouchers
|
||||
@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
|
||||
@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
|
||||
|
||||
@validator("destination") # For Purchase, Issue and Return Vouchers
|
||||
@validator("destination") # For Purchase, Issue and Return Vouchers
|
||||
def source_destination_unique(cls, value: CostCentreLink, values):
|
||||
if value.id_ == values['source'].id_:
|
||||
if value.id_ == values["source"].id_:
|
||||
raise ValueError("Source and destination cannot be the same")
|
||||
return value
|
||||
|
||||
@ -136,7 +146,7 @@ class EmployeeBenefitIn(VoucherIn):
|
||||
alias_generator = to_camel
|
||||
json_encoders = {
|
||||
date: lambda v: v.strftime("%d-%b-%Y"),
|
||||
datetime: lambda v: v.strftime("%d-%b-%Y %H:%I")
|
||||
datetime: lambda v: v.strftime("%d-%b-%Y %H:%I"),
|
||||
}
|
||||
|
||||
@validator("date_", pre=True)
|
||||
@ -145,19 +155,39 @@ class EmployeeBenefitIn(VoucherIn):
|
||||
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
|
||||
@classmethod
|
||||
def load_form(cls, data: str = Form(...)):
|
||||
json_data = json.loads(data)
|
||||
return cls.parse_obj(json_data)
|
||||
|
||||
@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
|
||||
|
||||
class IncentiveIn(VoucherIn):
|
||||
incentives: List[Incentive]
|
||||
|
||||
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()
|
||||
|
||||
@classmethod
|
||||
def load_form(cls, data: str = Form(...)):
|
||||
json_data = json.loads(data)
|
||||
return cls.parse_obj(json_data)
|
||||
|
||||
|
||||
class IncentiveEmployee(BaseModel):
|
||||
id_: Optional[uuid.UUID]
|
||||
employee_id: uuid.UUID
|
||||
cost_centre_id: uuid.UUID
|
||||
journal: Optional[Journal]
|
||||
days_worked: Decimal = Field(ge=0, multiple_of=0.5)
|
||||
points: Decimal = Field(ge=0, multiple_of=0.01)
|
||||
|
@ -13,7 +13,25 @@ class AccountLink(BaseModel):
|
||||
name: Optional[str]
|
||||
|
||||
class Config:
|
||||
fields = {'id_': 'id'}
|
||||
fields = {"id_": "id"}
|
||||
|
||||
|
||||
class CostCentreLink(BaseModel):
|
||||
id_: uuid.UUID = Field(...)
|
||||
name: Optional[str]
|
||||
|
||||
class Config:
|
||||
fields = {"id_": "id"}
|
||||
|
||||
|
||||
class EmployeeLink(BaseModel):
|
||||
id_: uuid.UUID = Field(...)
|
||||
name: Optional[str]
|
||||
designation: Optional[str]
|
||||
cost_centre: CostCentreLink
|
||||
|
||||
class Config:
|
||||
alias_generator = to_camel
|
||||
|
||||
|
||||
class ProductGroupIn(BaseModel):
|
||||
@ -25,7 +43,7 @@ class ProductGroup(ProductGroupIn):
|
||||
is_fixture: bool
|
||||
|
||||
class Config:
|
||||
fields = {'id_': 'id'}
|
||||
fields = {"id_": "id"}
|
||||
anystr_strip_whitespace = True
|
||||
alias_generator = to_camel
|
||||
|
||||
@ -34,7 +52,7 @@ class ProductGroupLink(BaseModel):
|
||||
id_: uuid.UUID = Field(...)
|
||||
|
||||
class Config:
|
||||
fields = {'id_': 'id'}
|
||||
fields = {"id_": "id"}
|
||||
|
||||
|
||||
class ProductLink(BaseModel):
|
||||
@ -42,7 +60,7 @@ class ProductLink(BaseModel):
|
||||
name: Optional[str]
|
||||
|
||||
class Config:
|
||||
fields = {'id_': 'id'}
|
||||
fields = {"id_": "id"}
|
||||
|
||||
|
||||
class ProductIn(BaseModel):
|
||||
@ -59,7 +77,7 @@ class ProductIn(BaseModel):
|
||||
is_sold: bool
|
||||
|
||||
class Config:
|
||||
fields = {'id_': 'id'}
|
||||
fields = {"id_": "id"}
|
||||
anystr_strip_whitespace = True
|
||||
alias_generator = to_camel
|
||||
|
||||
@ -95,13 +113,6 @@ class RecipeItem(BaseModel):
|
||||
price: int
|
||||
|
||||
|
||||
class CostCentreLink(BaseModel):
|
||||
id_: uuid.UUID = Field(...)
|
||||
|
||||
class Config:
|
||||
fields = {'id_': 'id'}
|
||||
|
||||
|
||||
class CostCentreIn(BaseModel):
|
||||
name: str = Field(..., min_length=1)
|
||||
|
||||
@ -111,7 +122,7 @@ class CostCentre(CostCentreIn):
|
||||
is_fixture: bool
|
||||
|
||||
class Config:
|
||||
fields = {'id_': 'id'}
|
||||
fields = {"id_": "id"}
|
||||
anystr_strip_whitespace = True
|
||||
alias_generator = to_camel
|
||||
|
||||
@ -123,7 +134,7 @@ class AccountBase(BaseModel):
|
||||
cost_centre: CostCentreLink
|
||||
|
||||
class Config:
|
||||
fields = {'id_': 'id'}
|
||||
fields = {"id_": "id"}
|
||||
anystr_strip_whitespace = True
|
||||
alias_generator = to_camel
|
||||
|
||||
@ -148,27 +159,21 @@ class EmployeeIn(AccountBase):
|
||||
|
||||
@validator("joining_date", pre=True)
|
||||
def parse_joining_date(cls, value):
|
||||
return datetime.strptime(
|
||||
value,
|
||||
"%d-%b-%Y"
|
||||
).date()
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@validator("leaving_date", pre=True)
|
||||
def parse_leaving_date(cls, value):
|
||||
if value is None or value == "":
|
||||
return None
|
||||
else:
|
||||
return datetime.strptime(
|
||||
value,
|
||||
"%d-%b-%Y"
|
||||
).date()
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@validator('leaving_date')
|
||||
@validator("leaving_date")
|
||||
def leaving_date_more_than_joining_date(cls, v, values, **kwargs):
|
||||
if values['is_active']:
|
||||
if values["is_active"]:
|
||||
return None
|
||||
if v < values['joining_date']:
|
||||
raise ValueError('Leaving Date cannot be less than Joining Date')
|
||||
if v < values["joining_date"]:
|
||||
raise ValueError("Leaving Date cannot be less than Joining Date")
|
||||
return v
|
||||
|
||||
|
||||
@ -189,6 +194,4 @@ class AccountType(BaseModel):
|
||||
name: str
|
||||
|
||||
class Config:
|
||||
fields = {'id_': 'id'}
|
||||
|
||||
|
||||
fields = {"id_": "id"}
|
||||
|
@ -8,7 +8,12 @@ from fastapi import Form
|
||||
from pydantic import BaseModel, validator, Field
|
||||
|
||||
from brewman.schemas import to_camel
|
||||
from brewman.schemas.master import AccountLink, CostCentreLink, ProductLink
|
||||
from brewman.schemas.master import (
|
||||
AccountLink,
|
||||
CostCentreLink,
|
||||
ProductLink,
|
||||
EmployeeLink,
|
||||
)
|
||||
|
||||
|
||||
class UserLink(BaseModel):
|
||||
@ -60,25 +65,28 @@ class Inventory(BaseModel):
|
||||
|
||||
class EmployeeBenefit(BaseModel):
|
||||
id_: Optional[uuid.UUID]
|
||||
journal_id: Optional[uuid.UUID]
|
||||
employee_id: uuid.UUID
|
||||
employee: Optional[EmployeeLink]
|
||||
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)
|
||||
esi_employee: Optional[int] = Field(ge=0)
|
||||
pf_employee: Optional[int] = Field(ge=0)
|
||||
esi_employer: Optional[int] = Field(ge=0)
|
||||
pf_employer: Optional[int] = Field(ge=0)
|
||||
|
||||
class Config:
|
||||
alias_generator = to_camel
|
||||
|
||||
|
||||
class Incentive(BaseModel):
|
||||
id: uuid.UUID
|
||||
voucher_id: uuid.UUID
|
||||
journal_id: uuid.UUID
|
||||
days_worked: int
|
||||
points: Decimal
|
||||
employee_id: uuid.UUID
|
||||
name: str
|
||||
designation: str
|
||||
department: str
|
||||
days_worked: Decimal = Field(ge=0, multiple_of=0.5)
|
||||
points: Decimal = Field(ge=0, multiple_of=0.01)
|
||||
|
||||
class Config:
|
||||
alias_generator = to_camel
|
||||
|
||||
|
||||
class VoucherIn(BaseModel):
|
||||
@ -99,7 +107,7 @@ class Voucher(VoucherIn):
|
||||
reconcile_date: Optional[date]
|
||||
creation_date: Optional[datetime]
|
||||
last_edit_date: Optional[datetime]
|
||||
user: UserLink
|
||||
user: Optional[UserLink]
|
||||
posted: Optional[bool]
|
||||
poster_id: Optional[uuid.UUID]
|
||||
journals: List[Journal]
|
||||
@ -107,6 +115,9 @@ class Voucher(VoucherIn):
|
||||
vendor: Optional[AccountLink]
|
||||
source: Optional[CostCentreLink]
|
||||
destination: Optional[CostCentreLink]
|
||||
incentives: Optional[List[Incentive]]
|
||||
incentive: Optional[Decimal]
|
||||
employee_benefits: List[EmployeeBenefit]
|
||||
files: List[Any]
|
||||
|
||||
class Config:
|
||||
@ -114,7 +125,7 @@ class Voucher(VoucherIn):
|
||||
alias_generator = to_camel
|
||||
json_encoders = {
|
||||
date: lambda v: v.strftime("%d-%b-%Y"),
|
||||
datetime: lambda v: v.strftime("%d-%b-%Y %H:%I")
|
||||
datetime: lambda v: v.strftime("%d-%b-%Y %H:%I"),
|
||||
}
|
||||
|
||||
@validator("date_", pre=True)
|
||||
@ -141,10 +152,7 @@ class Voucher(VoucherIn):
|
||||
return None
|
||||
elif isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(
|
||||
value,
|
||||
"%d-%b-%Y"
|
||||
).date()
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@validator("journals")
|
||||
def validate_enough_journals(cls, value: List[Journal]):
|
||||
@ -160,7 +168,11 @@ class Voucher(VoucherIn):
|
||||
|
||||
@validator("journals")
|
||||
def is_distinct(cls, value: List[Journal]):
|
||||
journal_set = set(hash(x.account.id_) ^ hash(None if x.cost_centre is None else x.cost_centre.id_) for x in value)
|
||||
journal_set = set(
|
||||
hash(x.account.id_)
|
||||
^ hash(None if x.cost_centre is None else x.cost_centre.id_)
|
||||
for x in value
|
||||
)
|
||||
if len(value) != len(journal_set):
|
||||
raise ValueError("Duplicate journals")
|
||||
return value
|
||||
|
@ -41,8 +41,8 @@ export class VoucherService {
|
||||
}
|
||||
|
||||
getIncentive(date: string): Observable<Voucher> {
|
||||
const options = {params: new HttpParams().set('t', 'Incentive').set('d', date)};
|
||||
return <Observable<Voucher>>this.http.get<Voucher>(url, options)
|
||||
const options = {params: new HttpParams().set('d', date)};
|
||||
return <Observable<Voucher>>this.http.get<Voucher>(`${url}/incentive`, options)
|
||||
.pipe(
|
||||
catchError(this.log.handleError(serviceName, 'list'))
|
||||
);
|
||||
|
@ -2,6 +2,7 @@ import {Account} from './account';
|
||||
import {User} from './user';
|
||||
import {CostCentre} from './cost-centre';
|
||||
import {Product} from './product';
|
||||
import {Employee} from "../employee/employee";
|
||||
|
||||
export class Voucher {
|
||||
id: string;
|
||||
@ -45,7 +46,7 @@ export class EmployeeBenefit {
|
||||
pfEmployee: number;
|
||||
esiEmployer: number;
|
||||
pfEmployer: number;
|
||||
journal: Journal; // Should be employee as we need designation
|
||||
employee: Employee;
|
||||
}
|
||||
|
||||
export class Incentive {
|
||||
|
@ -15,9 +15,9 @@ export class EmployeeBenefitsResolver implements Resolve<Voucher> {
|
||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Voucher> {
|
||||
const id = route.paramMap.get('id');
|
||||
if (id === null) {
|
||||
return this.ser.getOfType('Salary Deduction');
|
||||
return this.ser.getOfType('Employee Benefit');
|
||||
} else {
|
||||
return this.ser.get(id, 'Employee-Benefits');
|
||||
return this.ser.get(id, 'Employee Benefit');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ const employeeBenefitsRoutes: Routes = [
|
||||
component: EmployeeBenefitsComponent,
|
||||
canActivate: [AuthGuard],
|
||||
data: {
|
||||
permission: 'Salary Deduction'
|
||||
permission: 'employee-benefit'
|
||||
},
|
||||
resolve: {
|
||||
voucher: EmployeeBenefitsResolver
|
||||
@ -23,7 +23,7 @@ const employeeBenefitsRoutes: Routes = [
|
||||
component: EmployeeBenefitsComponent,
|
||||
canActivate: [AuthGuard],
|
||||
data: {
|
||||
permission: 'Salary Deduction'
|
||||
permission: 'employee-benefit'
|
||||
},
|
||||
resolve: {
|
||||
voucher: EmployeeBenefitsResolver
|
||||
|
@ -37,19 +37,19 @@
|
||||
<!-- Name Column -->
|
||||
<ng-container matColumnDef="name">
|
||||
<mat-header-cell *matHeaderCellDef>Name</mat-header-cell>
|
||||
<mat-cell *matCellDef="let row">{{row.journal.account.name}}</mat-cell>
|
||||
<mat-cell *matCellDef="let row">{{row.employee.name}}</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Designation Column -->
|
||||
<ng-container matColumnDef="designation">
|
||||
<mat-header-cell *matHeaderCellDef>Designation</mat-header-cell>
|
||||
<mat-cell *matCellDef="let row">{{row.journal.account.designation}}</mat-cell>
|
||||
<mat-cell *matCellDef="let row">{{row.employee.designation}}</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Department Column -->
|
||||
<ng-container matColumnDef="department">
|
||||
<mat-header-cell *matHeaderCellDef>Department</mat-header-cell>
|
||||
<mat-cell *matCellDef="let row">{{row.journal.account.costCentre.name}}</mat-cell>
|
||||
<mat-cell *matCellDef="let row">{{row.employee.costCentre.name}}</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- GrossSalary Column -->
|
||||
|
@ -6,7 +6,7 @@ import {ActivatedRoute, Router} from '@angular/router';
|
||||
import {BehaviorSubject, Observable, of as observableOf} from 'rxjs';
|
||||
import {EmployeeBenefitsDataSource} from './employee-benefits-datasource';
|
||||
import {VoucherService} from '../core/voucher.service';
|
||||
import {EmployeeBenefit, Journal, Voucher} from '../core/voucher';
|
||||
import {EmployeeBenefit, Voucher} from '../core/voucher';
|
||||
import * as moment from 'moment';
|
||||
import {AuthService} from '../auth/auth.service';
|
||||
import {ConfirmDialogComponent} from '../shared/confirm-dialog/confirm-dialog.component';
|
||||
@ -106,7 +106,7 @@ export class EmployeeBenefitsComponent implements OnInit, AfterViewInit {
|
||||
}
|
||||
|
||||
addRow() {
|
||||
const oldFiltered = this.voucher.employeeBenefits.filter((x) => x.journal.account.id === this.employee.id);
|
||||
const oldFiltered = this.voucher.employeeBenefits.filter((x) => x.employee.id === this.employee.id);
|
||||
if (oldFiltered.length) {
|
||||
this.toaster.show('Danger', 'Employee has already been added');
|
||||
return;
|
||||
@ -121,7 +121,7 @@ export class EmployeeBenefitsComponent implements OnInit, AfterViewInit {
|
||||
const pf = this.getPf(grossSalary, daysWorked, daysInMonth);
|
||||
|
||||
this.voucher.employeeBenefits.push({
|
||||
journal: new Journal({account: new Account(this.employee)}),
|
||||
employee: this.employee,
|
||||
grossSalary: grossSalary,
|
||||
daysWorked: daysWorked,
|
||||
esiEmployee: esi.ee,
|
||||
|
@ -10,7 +10,7 @@ const employeeFunctionsRoutes: Routes = [
|
||||
component: EmployeeFunctionsComponent,
|
||||
canActivate: [AuthGuard],
|
||||
data: {
|
||||
permission: 'Salary Deduction'
|
||||
permission: 'employee-benefit'
|
||||
},
|
||||
runGuardsAndResolvers: 'always'
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ const incentiveRoutes: Routes = [
|
||||
component: IncentiveComponent,
|
||||
canActivate: [AuthGuard],
|
||||
data: {
|
||||
permission: 'Incentive'
|
||||
permission: 'incentive'
|
||||
},
|
||||
resolve: {
|
||||
voucher: IncentiveResolver
|
||||
@ -23,7 +23,7 @@ const incentiveRoutes: Routes = [
|
||||
component: IncentiveComponent,
|
||||
canActivate: [AuthGuard],
|
||||
data: {
|
||||
permission: 'Incentive'
|
||||
permission: 'incentive'
|
||||
},
|
||||
resolve: {
|
||||
voucher: IncentiveResolver
|
||||
|
@ -51,7 +51,7 @@
|
||||
<button matPrefix (click)="less(row, i)">
|
||||
<mat-icon>remove</mat-icon>
|
||||
</button>
|
||||
<input matInput formControlName="points" autocomplete="off">
|
||||
<input matInput formControlName="points" autocomplete="off" (change)="change(row, i)">
|
||||
<button matSuffix color="warn" (click)="more(row, i)">
|
||||
<mat-icon>add</mat-icon>
|
||||
</button>
|
||||
|
@ -109,6 +109,11 @@ export class IncentiveComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
change(row: Incentive, i: number) {
|
||||
row.points = +(this.form.get('incentives').get('' + i).get('points').value);
|
||||
this.form.get('incentives').get('' + i).setValue({'points': '' + row.points});
|
||||
}
|
||||
|
||||
more(row: Incentive, i: number) {
|
||||
row.points += 1;
|
||||
this.form.get('incentives').get('' + i).setValue({'points': '' + row.points});
|
||||
@ -160,7 +165,6 @@ export class IncentiveComponent implements OnInit {
|
||||
this.voucher.incentives.forEach((item, index) => {
|
||||
item.points = array.controls[index].value.points;
|
||||
});
|
||||
this.voucher.narration = formModel.narration;
|
||||
return this.voucher;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user