From e3286c87ba100a80c60337ac36bc0ba4beb61c09 Mon Sep 17 00:00:00 2001 From: tanshu Date: Sat, 23 May 2020 09:45:02 +0530 Subject: [PATCH] Incentive Done!! Employee Benefit Done!! --- .../eed0b382c287_lowercase_fastapi.py | 9 +- brewman/main.py | 36 ++- brewman/models/voucher.py | 16 +- brewman/routers/employee_benefit.py | 10 +- brewman/routers/incentive.py | 276 +++++++++++++++--- brewman/routers/voucher/__init__.py | 177 +++++------ brewman/routers/voucher/incentive.py | 178 ----------- brewman/schemas/input.py | 76 +++-- brewman/schemas/master.py | 61 ++-- brewman/schemas/voucher.py | 50 ++-- overlord/src/app/core/voucher.service.ts | 4 +- overlord/src/app/core/voucher.ts | 3 +- .../employee-benefits-resolver.service.ts | 4 +- .../employee-benefits-routing.module.ts | 4 +- .../employee-benefits.component.html | 6 +- .../employee-benefits.component.ts | 6 +- .../employee-functions-routing.module.ts | 2 +- .../app/incentive/incentive-routing.module.ts | 4 +- .../app/incentive/incentive.component.html | 2 +- .../src/app/incentive/incentive.component.ts | 6 +- 20 files changed, 515 insertions(+), 415 deletions(-) delete mode 100644 brewman/routers/voucher/incentive.py diff --git a/alembic/versions/eed0b382c287_lowercase_fastapi.py b/alembic/versions/eed0b382c287_lowercase_fastapi.py index 9b81c5b9..8f5a1126 100644 --- a/alembic/versions/eed0b382c287_lowercase_fastapi.py +++ b/alembic/versions/eed0b382c287_lowercase_fastapi.py @@ -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 ### diff --git a/brewman/main.py b/brewman/main.py index 2c666910..dc1740f3 100644 --- a/brewman/main.py +++ b/brewman/main.py @@ -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"]) diff --git a/brewman/models/voucher.py b/brewman/models/voucher.py index fd7b7020..49dac44f 100644 --- a/brewman/models/voucher.py +++ b/brewman/models/voucher.py @@ -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 diff --git a/brewman/routers/employee_benefit.py b/brewman/routers/employee_benefit.py index 8b53cabe..b521d3a5 100644 --- a/brewman/routers/employee_benefit.py +++ b/brewman/routers/employee_benefit.py @@ -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() diff --git a/brewman/routers/incentive.py b/brewman/routers/incentive.py index b07eded6..4c64a535 100644 --- a/brewman/routers/incentive.py +++ b/brewman/routers/incentive.py @@ -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") diff --git a/brewman/routers/voucher/__init__.py b/brewman/routers/voucher/__init__.py index 45d7701f..6dd46ede 100644 --- a/brewman/routers/voucher/__init__.py +++ b/brewman/routers/voucher/__init__.py @@ -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", diff --git a/brewman/routers/voucher/incentive.py b/brewman/routers/voucher/incentive.py deleted file mode 100644 index 63881d41..00000000 --- a/brewman/routers/voucher/incentive.py +++ /dev/null @@ -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") diff --git a/brewman/schemas/input.py b/brewman/schemas/input.py index 2b2f7505..219793b9 100644 --- a/brewman/schemas/input.py +++ b/brewman/schemas/input.py @@ -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) diff --git a/brewman/schemas/master.py b/brewman/schemas/master.py index 31d72094..f678696f 100644 --- a/brewman/schemas/master.py +++ b/brewman/schemas/master.py @@ -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"} diff --git a/brewman/schemas/voucher.py b/brewman/schemas/voucher.py index cfee5d30..8a150ec9 100644 --- a/brewman/schemas/voucher.py +++ b/brewman/schemas/voucher.py @@ -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 diff --git a/overlord/src/app/core/voucher.service.ts b/overlord/src/app/core/voucher.service.ts index 0e3586f5..2f92351b 100644 --- a/overlord/src/app/core/voucher.service.ts +++ b/overlord/src/app/core/voucher.service.ts @@ -41,8 +41,8 @@ export class VoucherService { } getIncentive(date: string): Observable { - const options = {params: new HttpParams().set('t', 'Incentive').set('d', date)}; - return >this.http.get(url, options) + const options = {params: new HttpParams().set('d', date)}; + return >this.http.get(`${url}/incentive`, options) .pipe( catchError(this.log.handleError(serviceName, 'list')) ); diff --git a/overlord/src/app/core/voucher.ts b/overlord/src/app/core/voucher.ts index e610345e..e726b80d 100644 --- a/overlord/src/app/core/voucher.ts +++ b/overlord/src/app/core/voucher.ts @@ -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 { diff --git a/overlord/src/app/employee-benefits/employee-benefits-resolver.service.ts b/overlord/src/app/employee-benefits/employee-benefits-resolver.service.ts index bc486020..928c7bec 100644 --- a/overlord/src/app/employee-benefits/employee-benefits-resolver.service.ts +++ b/overlord/src/app/employee-benefits/employee-benefits-resolver.service.ts @@ -15,9 +15,9 @@ export class EmployeeBenefitsResolver implements Resolve { resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { 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'); } } } diff --git a/overlord/src/app/employee-benefits/employee-benefits-routing.module.ts b/overlord/src/app/employee-benefits/employee-benefits-routing.module.ts index e3c6b23f..8a49b0fc 100644 --- a/overlord/src/app/employee-benefits/employee-benefits-routing.module.ts +++ b/overlord/src/app/employee-benefits/employee-benefits-routing.module.ts @@ -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 diff --git a/overlord/src/app/employee-benefits/employee-benefits.component.html b/overlord/src/app/employee-benefits/employee-benefits.component.html index 8f19ad52..3d56ab5e 100644 --- a/overlord/src/app/employee-benefits/employee-benefits.component.html +++ b/overlord/src/app/employee-benefits/employee-benefits.component.html @@ -37,19 +37,19 @@ Name - {{row.journal.account.name}} + {{row.employee.name}} Designation - {{row.journal.account.designation}} + {{row.employee.designation}} Department - {{row.journal.account.costCentre.name}} + {{row.employee.costCentre.name}} diff --git a/overlord/src/app/employee-benefits/employee-benefits.component.ts b/overlord/src/app/employee-benefits/employee-benefits.component.ts index 446185c9..8592d3ee 100644 --- a/overlord/src/app/employee-benefits/employee-benefits.component.ts +++ b/overlord/src/app/employee-benefits/employee-benefits.component.ts @@ -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, diff --git a/overlord/src/app/employee-functions/employee-functions-routing.module.ts b/overlord/src/app/employee-functions/employee-functions-routing.module.ts index fdae333a..a761f34b 100644 --- a/overlord/src/app/employee-functions/employee-functions-routing.module.ts +++ b/overlord/src/app/employee-functions/employee-functions-routing.module.ts @@ -10,7 +10,7 @@ const employeeFunctionsRoutes: Routes = [ component: EmployeeFunctionsComponent, canActivate: [AuthGuard], data: { - permission: 'Salary Deduction' + permission: 'employee-benefit' }, runGuardsAndResolvers: 'always' } diff --git a/overlord/src/app/incentive/incentive-routing.module.ts b/overlord/src/app/incentive/incentive-routing.module.ts index cca745d2..7551706d 100644 --- a/overlord/src/app/incentive/incentive-routing.module.ts +++ b/overlord/src/app/incentive/incentive-routing.module.ts @@ -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 diff --git a/overlord/src/app/incentive/incentive.component.html b/overlord/src/app/incentive/incentive.component.html index dfeead7c..b662ef49 100644 --- a/overlord/src/app/incentive/incentive.component.html +++ b/overlord/src/app/incentive/incentive.component.html @@ -51,7 +51,7 @@ - + diff --git a/overlord/src/app/incentive/incentive.component.ts b/overlord/src/app/incentive/incentive.component.ts index cefa2573..6d9726de 100644 --- a/overlord/src/app/incentive/incentive.component.ts +++ b/overlord/src/app/incentive/incentive.component.ts @@ -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; }