DbImage works!!

Credit Salary works!!

Refresh router created, now need to use it in angular

Errors should now show up in the frontend.
This commit is contained in:
Amritanshu Agrawal 2020-05-30 01:58:17 +05:30
parent e3286c87ba
commit 10dbe6663d
34 changed files with 264 additions and 233 deletions

@ -10,6 +10,8 @@ from .routers import (
attendance, attendance,
batch, batch,
cost_centre, cost_centre,
credit_salary,
db_image,
db_integrity, db_integrity,
employee, employee,
employee_attendance, employee_attendance,
@ -61,7 +63,7 @@ app = FastAPI()
app.add_middleware(SessionMiddleware, secret_key="c982367648") app.add_middleware(SessionMiddleware, secret_key="c982367648")
# app.include_router(brewman.routers, prefix="/api/db-image", tags=["db-image"]) app.include_router(db_image.router, prefix="/db-image", tags=["db-image"])
app.include_router(login.router, tags=["login"]) app.include_router(login.router, tags=["login"])
app.include_router(account.router, prefix="/api/accounts", tags=["accounts"]) app.include_router(account.router, prefix="/api/accounts", tags=["accounts"])
app.include_router(account_types.router, prefix="/api/account-types", tags=["accounts"]) app.include_router(account_types.router, prefix="/api/account-types", tags=["accounts"])
@ -134,6 +136,7 @@ app.include_router(
employee_benefit.router, prefix="/api/employee-benefit", tags=["vouchers"] employee_benefit.router, prefix="/api/employee-benefit", tags=["vouchers"]
) )
app.include_router(incentive.router, prefix="/api/incentive", tags=["vouchers"]) app.include_router(incentive.router, prefix="/api/incentive", tags=["vouchers"])
app.include_router(credit_salary.router, prefix="/api/credit-salary", tags=["vouchers"])
app.include_router( app.include_router(
lock_information.router, prefix="/api/lock-information", tags=["settings"] lock_information.router, prefix="/api/lock-information", tags=["settings"]

@ -366,6 +366,10 @@ class AccountBase(Base):
def salary(cls): def salary(cls):
return {"id": "5c2b54d0-c174-004d-a0d5-92cdaadcefa7", "name": "Staff Salary"} return {"id": "5c2b54d0-c174-004d-a0d5-92cdaadcefa7", "name": "Staff Salary"}
@classmethod
def salary_id(cls):
return uuid.UUID("5c2b54d0-c174-004d-a0d5-92cdaadcefa7")
@classmethod @classmethod
def incentive(cls): def incentive(cls):
return {"id": "b7eff754-e8ba-e047-ab06-9132c15c7640", "name": "Incentives"} return {"id": "b7eff754-e8ba-e047-ab06-9132c15c7640", "name": "Incentives"}

@ -14,7 +14,7 @@ from sqlalchemy import (
) )
from sqlalchemy.dialects.postgresql import BYTEA from sqlalchemy.dialects.postgresql import BYTEA
from sqlalchemy.ext.hybrid import hybrid_property from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import relationship, synonym, backref from sqlalchemy.orm import relationship, synonym, backref, Session
from brewman.models.guidtype import GUID from brewman.models.guidtype import GUID
from brewman.models.master import Product from brewman.models.master import Product
@ -176,14 +176,14 @@ class Journal(Base):
def __init__( def __init__(
self, self,
id=None, id_=None,
debit=None, debit=None,
amount=None, amount=None,
voucher_id=None, voucher_id=None,
account_id=None, account_id=None,
cost_centre_id=None, cost_centre_id=None,
): ):
self.id = id self.id = id_
self.debit = debit self.debit = debit
self.amount = amount self.amount = amount
self.voucher_id = voucher_id self.voucher_id = voucher_id
@ -357,14 +357,14 @@ class Batch(Base):
) )
@classmethod @classmethod
def list(cls, name, include_nil, date, dbsession): def list(cls, name, include_nil, date, db: Session):
query = dbsession.query(Batch).join(Batch.product) query = db.query(Batch).join(Batch.product)
if not include_nil: if not include_nil:
query = query.filter(cls.quantity_remaining > 0) query = query.filter(cls.quantity_remaining > 0)
if date is not None: if date is not None:
query = query.filter(cls.name <= date) query = query.filter(cls.name <= date)
for item in name.split(): for item in name.split():
query = query.filter(Product.name.ilike("%" + item + "%")) query = query.filter(Product.name.ilike(f"%{item}%"))
return query.order_by(Product.name).order_by(cls.name).all() return query.order_by(Product.name).order_by(cls.name).all()
@classmethod @classmethod
@ -388,7 +388,7 @@ class Attendance(Base):
def __init__( def __init__(
self, self,
id=None, id_=None,
employee_id=None, employee_id=None,
date=None, date=None,
attendance_type=None, attendance_type=None,
@ -397,20 +397,18 @@ class Attendance(Base):
user_id=None, user_id=None,
is_valid=None, is_valid=None,
): ):
self.id = id self.id = id_
self.employee_id = employee_id self.employee_id = employee_id
self.date = date self.date = date
self.attendance_type = attendance_type self.attendance_type = attendance_type
self.amount = amount if amount is not None else 0 self.amount = amount if amount is not None else 0
self.creation_date = ( self.creation_date = creation_date or datetime.utcnow()
datetime.utcnow() if creation_date is None else creation_date
)
self.user_id = user_id self.user_id = user_id
self.is_valid = is_valid if is_valid is not None else True self.is_valid = is_valid if is_valid is not None else True
def create(self, dbsession): def create(self, db: Session):
old = ( old = (
dbsession.query(Attendance) db.query(Attendance)
.filter(Attendance.date == self.date) .filter(Attendance.date == self.date)
.filter(Attendance.employee_id == self.employee_id) .filter(Attendance.employee_id == self.employee_id)
.filter(Attendance.is_valid == True) .filter(Attendance.is_valid == True)
@ -453,13 +451,11 @@ class DbImage(Base):
image=None, image=None,
thumbnail=None, thumbnail=None,
creation_date=None, creation_date=None,
id=None, id_=None,
): ):
self.resource_id = resource_id self.resource_id = resource_id
self.resource_type = resource_type self.resource_type = resource_type
self.image = image self.image = image
self.thumbnail = thumbnail self.thumbnail = thumbnail
self.creation_date = ( self.creation_date = creation_date or datetime.utcnow()
datetime.utcnow() if creation_date is None else creation_date self.id = id_
)
self.id = id

@ -5,6 +5,8 @@ from decimal import Decimal
from io import BytesIO from io import BytesIO
from typing import Optional from typing import Optional
from sqlalchemy.orm import Session
from brewman.models.master import DbSetting from brewman.models.master import DbSetting
from brewman.models.voucher import DbImage from brewman.models.voucher import DbImage
@ -13,26 +15,10 @@ from fastapi import APIRouter
router = APIRouter() router = APIRouter()
@router.get("/{id}/{type}") def get_lock_info(db: Session) -> (Optional[date], Optional[date]):
def db_image(request):
item = (
request.dbsession.query(DbImage)
.filter(DbImage.id == uuid.UUID(request.matchdict["id"]))
.first()
)
if request.matchdict["type"] == "thumbnail":
item = BytesIO(item.thumbnail)
else:
item = BytesIO(item.image)
response = Response(content_type="image/jpeg")
response.app_iter = item
return response
def get_lock_info(dbsession) -> (Optional[date], Optional[date]):
start: Optional[date] start: Optional[date]
finish: Optional[date] finish: Optional[date]
data = dbsession.query(DbSetting).filter(DbSetting.name == "Lock Info").first() data = db.query(DbSetting).filter(DbSetting.name == "Lock Info").first()
if data is None: if data is None:
return None, None return None, None

@ -0,0 +1,101 @@
from calendar import monthrange
from datetime import datetime, date
from fastapi import APIRouter, Depends, Security, Body, HTTPException, status
from sqlalchemy import or_
from sqlalchemy.orm import Session
from ..core.session import get_first_day, get_last_day
from ..schemas.auth import UserToken
from ..core.security import get_current_active_user as get_user
from ..db.session import SessionLocal
from ..models.master import Account, Employee, AttendanceType
from ..models.voucher import Voucher, Journal, VoucherType, Attendance
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.post("")
def credit_salary(
month: str = Body(..., embed=True),
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["attendance"]),
):
month = datetime.strptime(month, "%d-%b-%Y").date()
start_date = get_first_day(month)
finish_date = get_last_day(month)
voucher = Voucher(
date=finish_date,
narration="Auto Generated Salary Entry",
user_id=user.id_,
type_=VoucherType.by_name("Journal"),
posted=True,
poster_id=user.id_,
)
db.add(voucher)
for item in salary_journals(start_date, finish_date, db):
voucher.journals.append(item)
db.add(item)
db.commit()
return {"message": "Salary Entry created"}
def salary_journals(start_date: date, finish_date: date, db: Session):
days = monthrange(start_date.year, start_date.month)[1]
amount = 0
journals = []
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()
)
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 = sum(map(lambda x: AttendanceType.by_id(x.attendance_type).value, att))
att = round(att * employee.salary / days)
if att != 0:
amount += att
journals.append(
Journal(
amount=att,
debit=-1,
account_id=employee.id,
cost_centre_id=employee.cost_centre_id,
)
)
salary = db.query(Account).filter(Account.id == Account.salary_id()).first()
if amount == 0:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="No salaries to credit",
)
journals.append(
Journal(
amount=amount,
debit=1,
account_id=salary.id,
cost_centre_id=salary.cost_centre_id,
)
)
return journals

@ -0,0 +1,36 @@
import uuid
from io import BytesIO
from fastapi import APIRouter, Depends, Security
from fastapi.responses import StreamingResponse
from sqlalchemy.orm import Session
from ..core.security import get_user
from ..models.voucher import DbImage
from ..db.session import SessionLocal
from ..schemas.auth import UserToken
router = APIRouter()
# Dependency
def get_db() -> Session:
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.get("/{id_}/{type_}")
def db_image(
id_: uuid.UUID,
type_: str,
db: Session = Depends(get_db)
):
item = db.query(DbImage).filter(DbImage.id == id_).first()
if type_ == "thumbnail":
item = BytesIO(item.thumbnail)
else:
item = BytesIO(item.image)
return StreamingResponse(item, media_type="image/jpeg")

@ -26,7 +26,7 @@ from ..models import AccountBase
from ..schemas.auth import UserToken from ..schemas.auth import UserToken
from ..core.security import get_current_active_user as get_user from ..core.security import get_current_active_user as get_user
from ..db.session import SessionLocal from ..db.session import SessionLocal
from ..models.voucher import Voucher, VoucherType, Journal from ..models.voucher import Voucher, VoucherType, Journal, DbImage
import brewman.schemas.voucher as output import brewman.schemas.voucher as output
import brewman.schemas.input as schema_in import brewman.schemas.input as schema_in
@ -47,15 +47,14 @@ def save_route(
request: Request, request: Request,
data: schema_in.JournalIn = Depends(schema_in.JournalIn.load_form), data: schema_in.JournalIn = Depends(schema_in.JournalIn.load_form),
db: Session = Depends(get_db), db: Session = Depends(get_db),
i: List[UploadFile] = File(None), i: List[bytes] = File(None),
t: List[UploadFile] = File(None), t: List[bytes] = File(None),
user: UserToken = Security(get_user, scopes=["journal"]), user: UserToken = Security(get_user, scopes=["journal"]),
): ):
try: try:
i = i or []
t = t or []
item: Voucher = save(data, user, db) item: Voucher = save(data, user, db)
save_files(i + t, db) db.flush()
save_files(item.id, i, t, db)
db.commit() db.commit()
set_date(data.date_.strftime("%d-%b-%Y"), request.session) set_date(data.date_.strftime("%d-%b-%Y"), request.session)
info = voucher_info(item, db) info = voucher_info(item, db)
@ -66,11 +65,7 @@ def save_route(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e), status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e),
) )
except Exception: except Exception:
db.rollback() raise
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=traceback.format_exc(),
)
def save(data: schema_in.JournalIn, user: UserToken, db: Session) -> Voucher: def save(data: schema_in.JournalIn, user: UserToken, db: Session) -> Voucher:
@ -88,7 +83,6 @@ def save(data: schema_in.JournalIn, user: UserToken, db: Session) -> Voucher:
AccountBase.id == item.account.id_ AccountBase.id == item.account.id_
).first() ).first()
journal = Journal( journal = Journal(
id=item.id_,
amount=item.amount, amount=item.amount,
debit=item.debit, debit=item.debit,
account_id=account.id, account_id=account.id,
@ -99,10 +93,9 @@ def save(data: schema_in.JournalIn, user: UserToken, db: Session) -> Voucher:
return voucher return voucher
def save_files(files: List[UploadFile], db: Session): def save_files(voucher_id: uuid.UUID, i: List[bytes], t: List[bytes], db: Session):
# for key, value in files.items(): for index, value in enumerate(i):
# db.add(DbImage(voucher.id, "voucher", value["f"], value["t"])) db.add(DbImage(voucher_id, "voucher", i[index], t[index]))
pass
@router.put("/{id_}", response_model=output.Voucher) @router.put("/{id_}", response_model=output.Voucher)
@ -111,15 +104,13 @@ def update_route(
request: Request, request: Request,
data: schema_in.JournalIn = Depends(schema_in.JournalIn.load_form), data: schema_in.JournalIn = Depends(schema_in.JournalIn.load_form),
db: Session = Depends(get_db), db: Session = Depends(get_db),
i: List[UploadFile] = File(None), i: List[bytes] = File(None),
t: List[UploadFile] = File(None), t: List[bytes] = File(None),
user: UserToken = Security(get_user, scopes=["journal"]), user: UserToken = Security(get_user, scopes=["journal"]),
): ):
try: try:
i = i or []
t = t or []
item: Voucher = update(id_, data, user, db) item: Voucher = update(id_, data, user, db)
update_files(data, i + t, db) update_files(item.id, data.files, i, t, db)
db.commit() db.commit()
set_date(data.date_.strftime("%d-%b-%Y"), request.session) set_date(data.date_.strftime("%d-%b-%Y"), request.session)
return voucher_info(item, db) return voucher_info(item, db)
@ -174,7 +165,6 @@ def update(
db.query(AccountBase).filter(AccountBase.id == new_item.account.id_).first() db.query(AccountBase).filter(AccountBase.id == new_item.account.id_).first()
) )
journal = Journal( journal = Journal(
id=None,
amount=new_item.amount, amount=new_item.amount,
debit=new_item.debit, debit=new_item.debit,
account_id=account.id, account_id=account.id,
@ -185,16 +175,13 @@ def update(
return voucher return voucher
def update_files(data: schema_in.VoucherIn, files: List[UploadFile], db: Session): def update_files(voucher_id: uuid.UUID, data: List[output.ImageUpload], i: List[bytes], t: List[bytes], db: Session):
pass old = [f.id_ for f in data if f.id_]
# old_files = [ images = db.query(DbImage).filter(DbImage.resource_id == voucher_id).all()
# uuid.UUID(f["id"]) for f in json["files"] if "id" in f and f["id"] is not None for image in [i for i in images if i.id not in old]:
# ] db.delete(image)
# images = db.query(DbImage).filter(DbImage.resource_id == voucher.id).all() for index, value in enumerate(i):
# for image in [i for i in images if i.id not in old_files]: db.add(DbImage(voucher_id, "voucher", i[index], t[index]))
# db.delete(image)
# for key, value in files.items():
# db.add(DbImage(voucher.id, "voucher", value["f"], value["t"]))
@router.get("/{id_}", response_model=output.Voucher) @router.get("/{id_}", response_model=output.Voucher)

@ -7,9 +7,10 @@ from ..core.security import (
Token, Token,
authenticate_user, authenticate_user,
ACCESS_TOKEN_EXPIRE_MINUTES, ACCESS_TOKEN_EXPIRE_MINUTES,
create_access_token, create_access_token, get_user,
) )
from ..db.session import SessionLocal from ..db.session import SessionLocal
from ..schemas.auth import UserToken
router = APIRouter() router = APIRouter()
@ -54,3 +55,21 @@ async def login_for_access_token(
expires_delta=access_token_expires, expires_delta=access_token_expires,
) )
return {"access_token": access_token, "token_type": "bearer"} return {"access_token": access_token, "token_type": "bearer"}
@router.post("/refresh", response_model=Token)
async def refresh_token(
db: Session = Depends(get_db),
user: UserToken = Depends(get_user)
):
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={
"sub": user.name,
"scopes": user.permissions,
"userId": str(user.id_),
"lockedOut": user.locked_out,
},
expires_delta=access_token_expires,
)
return {"access_token": access_token, "token_type": "bearer"}

@ -283,7 +283,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: for image in images:
json_voucher["files"].append({"id": image.id, "resized": "", "thumbnail": ""}) json_voucher["files"].append({"id": image.id, "resized": f"/db-image/{image.id}/resized", "thumbnail": f"/db-image/{image.id}/thumbnail"})
return json_voucher return json_voucher
@ -301,6 +301,7 @@ def blank_voucher(info, db):
"narration": "", "narration": "",
"journals": [], "journals": [],
"inventories": [], "inventories": [],
"employeeBenefits": [],
} }
if type_ == "Journal": if type_ == "Journal":
pass pass

@ -1,87 +0,0 @@
import calendar
import datetime
import uuid
from sqlalchemy import or_
from ....models.auth import User
from ....models.master import Employee, AttendanceType, Account
from ....models.voucher import Voucher, VoucherType, Attendance, Journal
from ..session import get_first_day, get_last_day
@router.post("/api/credit-salary") # "Attendance"
def credit_salary(request):
user = (
request.dbsession.query(User)
.filter(User.id == uuid.UUID(request.authenticated_userid))
.one()
)
month = datetime.datetime.strptime(request.json_body["month"], "%d-%b-%Y")
start_date = get_first_day(month)
finish_date = get_last_day(month)
voucher = Voucher(
date=finish_date,
narration="Auto Generated Salary Entry",
user_id=user.id,
type=VoucherType.by_name("Journal"),
posted=True,
poster_id=user.id,
)
request.dbsession.add(voucher)
for item in salary_journals(start_date, finish_date, request.dbsession):
voucher.journals.append(item)
request.dbsession.add(item)
transaction.commit()
return {"message": "Salary Entry created"}
def salary_journals(start_date, finish_date, dbsession):
days = calendar.monthrange(start_date.year, start_date.month)[1]
finish_date = finish_date + datetime.timedelta(1)
amount = 0
journals = []
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()
)
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))
att = round(att * employee.salary / days)
if att != 0:
amount += att
journals.append(
Journal(
amount=att,
debit=-1,
account_id=employee.id,
cost_centre_id=employee.cost_centre_id,
)
)
salary = (
dbsession.query(Account)
.filter(Account.id == uuid.UUID(Account.salary()["id"]))
.first()
)
journals.append(
Journal(
amount=amount,
debit=1,
account_id=salary.id,
cost_centre_id=salary.cost_centre_id,
)
)
return journals

@ -1,27 +0,0 @@
from ....core.session import get_date
from brewman.routers.services.voucher import blank_voucher
class EmptyVoucher(object):
def __init__(self, request):
self.request = request
@router.get("/api/voucher", request_param="t=Incentive") # "Incentive"
def incentive(self):
voucher_type = self.request.GET.get("t", None)
date = self.request.GET.get("d", None)
if date is not None:
return blank_voucher(
{"type": voucher_type, "date": date}, self.request.dbsession
)
else:
return self.get_blank()
def get_blank(self, additional_info=None):
voucher_type = self.request.GET.get("t", None)
if additional_info is None:
additional_info = {}
additional_info["date"] = get_date(self.request)
additional_info["type"] = voucher_type
return blank_voucher(additional_info, self.request.dbsession)

@ -16,6 +16,15 @@ from brewman.schemas.master import (
) )
class ImageUpload(BaseModel):
id_: Optional[uuid.UUID]
resized: Optional[str]
thumbnail: Optional[str]
class Config:
alias_generator = to_camel
class UserLink(BaseModel): class UserLink(BaseModel):
id_: uuid.UUID id_: uuid.UUID
name: str name: str
@ -94,6 +103,7 @@ class VoucherIn(BaseModel):
narration: str narration: str
is_starred: bool is_starred: bool
type_: str type_: str
files: List[ImageUpload]
@classmethod @classmethod
def load_form(cls, data: str = Form(...)): def load_form(cls, data: str = Form(...)):
@ -118,7 +128,7 @@ class Voucher(VoucherIn):
incentives: Optional[List[Incentive]] incentives: Optional[List[Incentive]]
incentive: Optional[Decimal] incentive: Optional[Decimal]
employee_benefits: List[EmployeeBenefit] employee_benefits: List[EmployeeBenefit]
files: List[Any] files: List[ImageUpload]
class Config: class Config:
anystr_strip_whitespace = True anystr_strip_whitespace = True

@ -78,7 +78,7 @@ export class AccountDetailComponent implements OnInit, AfterViewInit {
this.router.navigateByUrl('/accounts'); this.router.navigateByUrl('/accounts');
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }
@ -91,7 +91,7 @@ export class AccountDetailComponent implements OnInit, AfterViewInit {
this.router.navigateByUrl('/accounts'); this.router.navigateByUrl('/accounts');
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }

@ -82,7 +82,7 @@ export class AttendanceComponent implements OnInit {
this.toaster.show('Success', ''); this.toaster.show('Success', '');
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }

@ -69,7 +69,7 @@ export class ClientDetailComponent implements OnInit, AfterViewInit {
this.router.navigateByUrl('/clients'); this.router.navigateByUrl('/clients');
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }
@ -82,7 +82,7 @@ export class ClientDetailComponent implements OnInit, AfterViewInit {
this.router.navigateByUrl('/clients'); this.router.navigateByUrl('/clients');
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }

@ -22,7 +22,7 @@ export class ErrorLoggerService {
console.error(error); // log to console instead console.error(error); // log to console instead
// TODO: better job of transforming error for user consumption // TODO: better job of transforming error for user consumption
this.log(serviceName, `${operation} failed: ${error.message}`); this.log(serviceName, `${operation} failed: ${error}`);
// // Let the app keep running by returning an empty result. // // Let the app keep running by returning an empty result.
// return of(result as T); // return of(result as T);

@ -60,7 +60,7 @@ export class CostCentreDetailComponent implements OnInit, AfterViewInit {
this.router.navigateByUrl('/cost-centres'); this.router.navigateByUrl('/cost-centres');
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }

@ -129,7 +129,7 @@ export class EmployeeAttendanceComponent implements OnInit, AfterViewInit {
this.toaster.show('Success', ''); this.toaster.show('Success', '');
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }

@ -185,7 +185,7 @@ export class EmployeeBenefitsComponent implements OnInit, AfterViewInit {
this.toaster.show('Success', 'Voucher Posted'); this.toaster.show('Success', 'Voucher Posted');
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }
@ -198,7 +198,7 @@ export class EmployeeBenefitsComponent implements OnInit, AfterViewInit {
this.router.navigate(['/employee-benefits', result.id]); this.router.navigate(['/employee-benefits', result.id]);
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }
@ -217,7 +217,7 @@ export class EmployeeBenefitsComponent implements OnInit, AfterViewInit {
this.router.navigate(['/employee-benefits'], {replaceUrl: true}); this.router.navigate(['/employee-benefits'], {replaceUrl: true});
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }

@ -79,7 +79,8 @@ export class EmployeeFunctionsComponent implements OnInit {
this.toaster.show('Success', 'Salaries Credited'); this.toaster.show('Success', 'Salaries Credited');
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); console.log('Error:', error);
this.toaster.show('Danger', error);
} }
); );
} }
@ -109,7 +110,7 @@ export class EmployeeFunctionsComponent implements OnInit {
this.toaster.show('Success', 'Fingerprints uploaded'); this.toaster.show('Success', 'Fingerprints uploaded');
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }

@ -90,7 +90,7 @@ export class EmployeeDetailComponent implements OnInit, AfterViewInit {
this.router.navigateByUrl('/employees'); this.router.navigateByUrl('/employees');
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }
@ -103,7 +103,7 @@ export class EmployeeDetailComponent implements OnInit, AfterViewInit {
this.router.navigateByUrl('/employees'); this.router.navigateByUrl('/employees');
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }

@ -140,7 +140,7 @@ export class IncentiveComponent implements OnInit {
this.toaster.show('Success', 'Voucher Posted'); this.toaster.show('Success', 'Voucher Posted');
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }
@ -153,7 +153,7 @@ export class IncentiveComponent implements OnInit {
this.router.navigate(['/incentive', result.id]); this.router.navigate(['/incentive', result.id]);
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }
@ -176,7 +176,7 @@ export class IncentiveComponent implements OnInit {
this.router.navigate(['/incentive'], {replaceUrl: true}); this.router.navigate(['/incentive'], {replaceUrl: true});
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }

@ -223,7 +223,7 @@ export class IssueComponent implements OnInit, AfterViewInit, OnDestroy {
this.router.navigate(['/issue', result.id]); this.router.navigate(['/issue', result.id]);
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }
@ -249,7 +249,7 @@ export class IssueComponent implements OnInit, AfterViewInit, OnDestroy {
this.router.navigate(['/issue'], {replaceUrl: true}); this.router.navigate(['/issue'], {replaceUrl: true});
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }

@ -218,7 +218,7 @@ export class JournalComponent implements OnInit, AfterViewInit, OnDestroy {
this.toaster.show('Success', 'Voucher Posted'); this.toaster.show('Success', 'Voucher Posted');
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }
@ -232,7 +232,7 @@ export class JournalComponent implements OnInit, AfterViewInit, OnDestroy {
this.router.navigate(['/journal', result.id]); this.router.navigate(['/journal', result.id]);
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }
@ -252,7 +252,7 @@ export class JournalComponent implements OnInit, AfterViewInit, OnDestroy {
this.router.navigate(['/journal'], {replaceUrl: true}); this.router.navigate(['/journal'], {replaceUrl: true});
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }

@ -222,7 +222,7 @@ export class PaymentComponent implements OnInit, AfterViewInit, OnDestroy {
this.toaster.show('Success', 'Voucher Posted'); this.toaster.show('Success', 'Voucher Posted');
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }
@ -236,7 +236,7 @@ export class PaymentComponent implements OnInit, AfterViewInit, OnDestroy {
this.router.navigate(['/payment', result.id]); this.router.navigate(['/payment', result.id]);
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }
@ -257,7 +257,7 @@ export class PaymentComponent implements OnInit, AfterViewInit, OnDestroy {
this.router.navigate(['/payment'], {replaceUrl: true}); this.router.navigate(['/payment'], {replaceUrl: true});
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }

@ -60,7 +60,7 @@ export class ProductGroupDetailComponent implements OnInit, AfterViewInit {
this.router.navigateByUrl('/product-groups'); this.router.navigateByUrl('/product-groups');
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }

@ -87,7 +87,7 @@ export class ProductDetailComponent implements OnInit, AfterViewInit {
this.router.navigateByUrl('/products'); this.router.navigateByUrl('/products');
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }
@ -100,7 +100,7 @@ export class ProductDetailComponent implements OnInit, AfterViewInit {
this.router.navigateByUrl('/products'); this.router.navigateByUrl('/products');
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }

@ -209,7 +209,7 @@ export class PurchaseReturnComponent implements OnInit, AfterViewInit, OnDestroy
this.toaster.show('Success', 'Voucher Posted'); this.toaster.show('Success', 'Voucher Posted');
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }
@ -223,7 +223,7 @@ export class PurchaseReturnComponent implements OnInit, AfterViewInit, OnDestroy
this.router.navigate(['/purchase-return', result.id]); this.router.navigate(['/purchase-return', result.id]);
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }
@ -244,7 +244,7 @@ export class PurchaseReturnComponent implements OnInit, AfterViewInit, OnDestroy
this.router.navigate(['/purchase-return'], {replaceUrl: true}); this.router.navigate(['/purchase-return'], {replaceUrl: true});
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }

@ -222,7 +222,7 @@ export class PurchaseComponent implements OnInit, AfterViewInit, OnDestroy {
this.toaster.show('Success', 'Voucher Posted'); this.toaster.show('Success', 'Voucher Posted');
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }
@ -236,7 +236,7 @@ export class PurchaseComponent implements OnInit, AfterViewInit, OnDestroy {
this.router.navigate(['/purchase', result.id]); this.router.navigate(['/purchase', result.id]);
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }
@ -257,7 +257,7 @@ export class PurchaseComponent implements OnInit, AfterViewInit, OnDestroy {
this.router.navigate(['/purchase'], {replaceUrl: true}); this.router.navigate(['/purchase'], {replaceUrl: true});
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }

@ -221,7 +221,7 @@ export class ReceiptComponent implements OnInit, AfterViewInit, OnDestroy {
this.toaster.show('Success', 'Voucher Posted'); this.toaster.show('Success', 'Voucher Posted');
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }
@ -235,7 +235,7 @@ export class ReceiptComponent implements OnInit, AfterViewInit, OnDestroy {
this.router.navigate(['/receipt', result.id]); this.router.navigate(['/receipt', result.id]);
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }
@ -256,7 +256,7 @@ export class ReceiptComponent implements OnInit, AfterViewInit, OnDestroy {
this.router.navigate(['/receipt'], {replaceUrl: true}); this.router.navigate(['/receipt'], {replaceUrl: true});
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }

@ -65,7 +65,7 @@ export class RoleDetailComponent implements OnInit, AfterViewInit {
this.router.navigateByUrl('/roles'); this.router.navigateByUrl('/roles');
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }
@ -78,7 +78,7 @@ export class RoleDetailComponent implements OnInit, AfterViewInit {
this.router.navigateByUrl('/roles'); this.router.navigateByUrl('/roles');
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }

@ -132,7 +132,7 @@ export class SettingsComponent implements OnInit {
this.toaster.show('Success', 'Lock information Updated'); this.toaster.show('Success', 'Lock information Updated');
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }
@ -162,7 +162,7 @@ export class SettingsComponent implements OnInit {
this.toaster.show('Success', 'Data has been rebased!'); this.toaster.show('Success', 'Data has been rebased!');
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }
@ -213,7 +213,7 @@ export class SettingsComponent implements OnInit {
this.toaster.show('Success', 'Stock has been reset!'); this.toaster.show('Success', 'Stock has been reset!');
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }
@ -227,7 +227,7 @@ export class SettingsComponent implements OnInit {
this.toaster.show('Success', 'Database checked, it is fine!'); this.toaster.show('Success', 'Database checked, it is fine!');
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }

@ -76,7 +76,7 @@ export class UserDetailComponent implements OnInit, AfterViewInit {
this.router.navigateByUrl('/users'); this.router.navigateByUrl('/users');
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }
@ -89,7 +89,7 @@ export class UserDetailComponent implements OnInit, AfterViewInit {
this.router.navigateByUrl('/users'); this.router.navigateByUrl('/users');
}, },
(error) => { (error) => {
this.toaster.show('Danger', error.error); this.toaster.show('Danger', error);
} }
); );
} }

@ -4,7 +4,8 @@ fastapi
environs environs
python-jose[cryptography] python-jose[cryptography]
passlib[bcrypt] passlib[bcrypt]
psycopg2 psycopg2-binary
sqlalchemy
python-multipart python-multipart
pyjwt pyjwt
alembic alembic