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:
parent
e3286c87ba
commit
10dbe6663d
brewman
overlord/src/app
account/account-detail
attendance
client/client-detail
core
cost-centre/cost-centre-detail
employee-attendance
employee-benefits
employee-functions
employee/employee-detail
incentive
issue
journal
payment
product-group/product-group-detail
product/product-detail
purchase-return
purchase
receipt
role/role-detail
settings
user/user-detail
@ -10,6 +10,8 @@ from .routers import (
|
||||
attendance,
|
||||
batch,
|
||||
cost_centre,
|
||||
credit_salary,
|
||||
db_image,
|
||||
db_integrity,
|
||||
employee,
|
||||
employee_attendance,
|
||||
@ -61,7 +63,7 @@ app = FastAPI()
|
||||
|
||||
|
||||
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(account.router, prefix="/api/accounts", 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"]
|
||||
)
|
||||
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(
|
||||
lock_information.router, prefix="/api/lock-information", tags=["settings"]
|
||||
|
@ -366,6 +366,10 @@ class AccountBase(Base):
|
||||
def salary(cls):
|
||||
return {"id": "5c2b54d0-c174-004d-a0d5-92cdaadcefa7", "name": "Staff Salary"}
|
||||
|
||||
@classmethod
|
||||
def salary_id(cls):
|
||||
return uuid.UUID("5c2b54d0-c174-004d-a0d5-92cdaadcefa7")
|
||||
|
||||
@classmethod
|
||||
def incentive(cls):
|
||||
return {"id": "b7eff754-e8ba-e047-ab06-9132c15c7640", "name": "Incentives"}
|
||||
|
@ -14,7 +14,7 @@ from sqlalchemy import (
|
||||
)
|
||||
from sqlalchemy.dialects.postgresql import BYTEA
|
||||
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.master import Product
|
||||
@ -176,14 +176,14 @@ class Journal(Base):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
id=None,
|
||||
id_=None,
|
||||
debit=None,
|
||||
amount=None,
|
||||
voucher_id=None,
|
||||
account_id=None,
|
||||
cost_centre_id=None,
|
||||
):
|
||||
self.id = id
|
||||
self.id = id_
|
||||
self.debit = debit
|
||||
self.amount = amount
|
||||
self.voucher_id = voucher_id
|
||||
@ -357,14 +357,14 @@ class Batch(Base):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def list(cls, name, include_nil, date, dbsession):
|
||||
query = dbsession.query(Batch).join(Batch.product)
|
||||
def list(cls, name, include_nil, date, db: Session):
|
||||
query = db.query(Batch).join(Batch.product)
|
||||
if not include_nil:
|
||||
query = query.filter(cls.quantity_remaining > 0)
|
||||
if date is not None:
|
||||
query = query.filter(cls.name <= date)
|
||||
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()
|
||||
|
||||
@classmethod
|
||||
@ -388,7 +388,7 @@ class Attendance(Base):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
id=None,
|
||||
id_=None,
|
||||
employee_id=None,
|
||||
date=None,
|
||||
attendance_type=None,
|
||||
@ -397,20 +397,18 @@ class Attendance(Base):
|
||||
user_id=None,
|
||||
is_valid=None,
|
||||
):
|
||||
self.id = id
|
||||
self.id = id_
|
||||
self.employee_id = employee_id
|
||||
self.date = date
|
||||
self.attendance_type = attendance_type
|
||||
self.amount = amount if amount is not None else 0
|
||||
self.creation_date = (
|
||||
datetime.utcnow() if creation_date is None else creation_date
|
||||
)
|
||||
self.creation_date = creation_date or datetime.utcnow()
|
||||
self.user_id = user_id
|
||||
self.is_valid = is_valid if is_valid is not None else True
|
||||
|
||||
def create(self, dbsession):
|
||||
def create(self, db: Session):
|
||||
old = (
|
||||
dbsession.query(Attendance)
|
||||
db.query(Attendance)
|
||||
.filter(Attendance.date == self.date)
|
||||
.filter(Attendance.employee_id == self.employee_id)
|
||||
.filter(Attendance.is_valid == True)
|
||||
@ -453,13 +451,11 @@ class DbImage(Base):
|
||||
image=None,
|
||||
thumbnail=None,
|
||||
creation_date=None,
|
||||
id=None,
|
||||
id_=None,
|
||||
):
|
||||
self.resource_id = resource_id
|
||||
self.resource_type = resource_type
|
||||
self.image = image
|
||||
self.thumbnail = thumbnail
|
||||
self.creation_date = (
|
||||
datetime.utcnow() if creation_date is None else creation_date
|
||||
)
|
||||
self.id = id
|
||||
self.creation_date = creation_date or datetime.utcnow()
|
||||
self.id = id_
|
||||
|
@ -5,6 +5,8 @@ from decimal import Decimal
|
||||
from io import BytesIO
|
||||
from typing import Optional
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from brewman.models.master import DbSetting
|
||||
from brewman.models.voucher import DbImage
|
||||
|
||||
@ -13,26 +15,10 @@ from fastapi import APIRouter
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/{id}/{type}")
|
||||
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]):
|
||||
def get_lock_info(db: Session) -> (Optional[date], Optional[date]):
|
||||
start: 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:
|
||||
return None, None
|
||||
|
||||
|
101
brewman/routers/credit_salary.py
Normal file
101
brewman/routers/credit_salary.py
Normal file
@ -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
|
36
brewman/routers/db_image.py
Normal file
36
brewman/routers/db_image.py
Normal file
@ -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 ..core.security import get_current_active_user as get_user
|
||||
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.input as schema_in
|
||||
|
||||
@ -47,15 +47,14 @@ def save_route(
|
||||
request: Request,
|
||||
data: schema_in.JournalIn = Depends(schema_in.JournalIn.load_form),
|
||||
db: Session = Depends(get_db),
|
||||
i: List[UploadFile] = File(None),
|
||||
t: List[UploadFile] = File(None),
|
||||
i: List[bytes] = File(None),
|
||||
t: List[bytes] = File(None),
|
||||
user: UserToken = Security(get_user, scopes=["journal"]),
|
||||
):
|
||||
try:
|
||||
i = i or []
|
||||
t = t or []
|
||||
item: Voucher = save(data, user, db)
|
||||
save_files(i + t, db)
|
||||
db.flush()
|
||||
save_files(item.id, i, t, db)
|
||||
db.commit()
|
||||
set_date(data.date_.strftime("%d-%b-%Y"), request.session)
|
||||
info = voucher_info(item, db)
|
||||
@ -66,11 +65,7 @@ def save_route(
|
||||
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(),
|
||||
)
|
||||
raise
|
||||
|
||||
|
||||
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_
|
||||
).first()
|
||||
journal = Journal(
|
||||
id=item.id_,
|
||||
amount=item.amount,
|
||||
debit=item.debit,
|
||||
account_id=account.id,
|
||||
@ -99,10 +93,9 @@ def save(data: schema_in.JournalIn, user: UserToken, db: Session) -> Voucher:
|
||||
return voucher
|
||||
|
||||
|
||||
def save_files(files: List[UploadFile], db: Session):
|
||||
# for key, value in files.items():
|
||||
# db.add(DbImage(voucher.id, "voucher", value["f"], value["t"]))
|
||||
pass
|
||||
def save_files(voucher_id: uuid.UUID, i: List[bytes], t: List[bytes], db: Session):
|
||||
for index, value in enumerate(i):
|
||||
db.add(DbImage(voucher_id, "voucher", i[index], t[index]))
|
||||
|
||||
|
||||
@router.put("/{id_}", response_model=output.Voucher)
|
||||
@ -111,15 +104,13 @@ def update_route(
|
||||
request: Request,
|
||||
data: schema_in.JournalIn = Depends(schema_in.JournalIn.load_form),
|
||||
db: Session = Depends(get_db),
|
||||
i: List[UploadFile] = File(None),
|
||||
t: List[UploadFile] = File(None),
|
||||
i: List[bytes] = File(None),
|
||||
t: List[bytes] = File(None),
|
||||
user: UserToken = Security(get_user, scopes=["journal"]),
|
||||
):
|
||||
try:
|
||||
i = i or []
|
||||
t = t or []
|
||||
item: Voucher = update(id_, data, user, db)
|
||||
update_files(data, i + t, db)
|
||||
update_files(item.id, data.files, i, t, db)
|
||||
db.commit()
|
||||
set_date(data.date_.strftime("%d-%b-%Y"), request.session)
|
||||
return voucher_info(item, db)
|
||||
@ -174,7 +165,6 @@ def update(
|
||||
db.query(AccountBase).filter(AccountBase.id == new_item.account.id_).first()
|
||||
)
|
||||
journal = Journal(
|
||||
id=None,
|
||||
amount=new_item.amount,
|
||||
debit=new_item.debit,
|
||||
account_id=account.id,
|
||||
@ -185,16 +175,13 @@ def update(
|
||||
return voucher
|
||||
|
||||
|
||||
def update_files(data: schema_in.VoucherIn, files: List[UploadFile], db: Session):
|
||||
pass
|
||||
# old_files = [
|
||||
# uuid.UUID(f["id"]) for f in json["files"] if "id" in f and f["id"] is not None
|
||||
# ]
|
||||
# images = db.query(DbImage).filter(DbImage.resource_id == voucher.id).all()
|
||||
# for image in [i for i in images if i.id not in old_files]:
|
||||
# db.delete(image)
|
||||
# for key, value in files.items():
|
||||
# db.add(DbImage(voucher.id, "voucher", value["f"], value["t"]))
|
||||
def update_files(voucher_id: uuid.UUID, data: List[output.ImageUpload], i: List[bytes], t: List[bytes], db: Session):
|
||||
old = [f.id_ for f in data if f.id_]
|
||||
images = db.query(DbImage).filter(DbImage.resource_id == voucher_id).all()
|
||||
for image in [i for i in images if i.id not in old]:
|
||||
db.delete(image)
|
||||
for index, value in enumerate(i):
|
||||
db.add(DbImage(voucher_id, "voucher", i[index], t[index]))
|
||||
|
||||
|
||||
@router.get("/{id_}", response_model=output.Voucher)
|
||||
|
@ -7,9 +7,10 @@ from ..core.security import (
|
||||
Token,
|
||||
authenticate_user,
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES,
|
||||
create_access_token,
|
||||
create_access_token, get_user,
|
||||
)
|
||||
from ..db.session import SessionLocal
|
||||
from ..schemas.auth import UserToken
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@ -54,3 +55,21 @@ async def login_for_access_token(
|
||||
expires_delta=access_token_expires,
|
||||
)
|
||||
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()
|
||||
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
|
||||
|
||||
|
||||
@ -301,6 +301,7 @@ def blank_voucher(info, db):
|
||||
"narration": "",
|
||||
"journals": [],
|
||||
"inventories": [],
|
||||
"employeeBenefits": [],
|
||||
}
|
||||
if type_ == "Journal":
|
||||
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):
|
||||
id_: uuid.UUID
|
||||
name: str
|
||||
@ -94,6 +103,7 @@ class VoucherIn(BaseModel):
|
||||
narration: str
|
||||
is_starred: bool
|
||||
type_: str
|
||||
files: List[ImageUpload]
|
||||
|
||||
@classmethod
|
||||
def load_form(cls, data: str = Form(...)):
|
||||
@ -118,7 +128,7 @@ class Voucher(VoucherIn):
|
||||
incentives: Optional[List[Incentive]]
|
||||
incentive: Optional[Decimal]
|
||||
employee_benefits: List[EmployeeBenefit]
|
||||
files: List[Any]
|
||||
files: List[ImageUpload]
|
||||
|
||||
class Config:
|
||||
anystr_strip_whitespace = True
|
||||
|
@ -78,7 +78,7 @@ export class AccountDetailComponent implements OnInit, AfterViewInit {
|
||||
this.router.navigateByUrl('/accounts');
|
||||
},
|
||||
(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');
|
||||
},
|
||||
(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', '');
|
||||
},
|
||||
(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');
|
||||
},
|
||||
(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');
|
||||
},
|
||||
(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
|
||||
|
||||
// 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.
|
||||
// return of(result as T);
|
||||
|
@ -60,7 +60,7 @@ export class CostCentreDetailComponent implements OnInit, AfterViewInit {
|
||||
this.router.navigateByUrl('/cost-centres');
|
||||
},
|
||||
(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', '');
|
||||
},
|
||||
(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');
|
||||
},
|
||||
(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]);
|
||||
},
|
||||
(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});
|
||||
},
|
||||
(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');
|
||||
},
|
||||
(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');
|
||||
},
|
||||
(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');
|
||||
},
|
||||
(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');
|
||||
},
|
||||
(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');
|
||||
},
|
||||
(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]);
|
||||
},
|
||||
(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});
|
||||
},
|
||||
(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]);
|
||||
},
|
||||
(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});
|
||||
},
|
||||
(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');
|
||||
},
|
||||
(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]);
|
||||
},
|
||||
(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});
|
||||
},
|
||||
(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');
|
||||
},
|
||||
(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]);
|
||||
},
|
||||
(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});
|
||||
},
|
||||
(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');
|
||||
},
|
||||
(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');
|
||||
},
|
||||
(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');
|
||||
},
|
||||
(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');
|
||||
},
|
||||
(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]);
|
||||
},
|
||||
(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});
|
||||
},
|
||||
(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');
|
||||
},
|
||||
(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]);
|
||||
},
|
||||
(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});
|
||||
},
|
||||
(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');
|
||||
},
|
||||
(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]);
|
||||
},
|
||||
(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});
|
||||
},
|
||||
(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');
|
||||
},
|
||||
(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');
|
||||
},
|
||||
(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');
|
||||
},
|
||||
(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!');
|
||||
},
|
||||
(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!');
|
||||
},
|
||||
(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!');
|
||||
},
|
||||
(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');
|
||||
},
|
||||
(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');
|
||||
},
|
||||
(error) => {
|
||||
this.toaster.show('Danger', error.error);
|
||||
this.toaster.show('Danger', error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -4,7 +4,8 @@ fastapi
|
||||
environs
|
||||
python-jose[cryptography]
|
||||
passlib[bcrypt]
|
||||
psycopg2
|
||||
psycopg2-binary
|
||||
sqlalchemy
|
||||
python-multipart
|
||||
pyjwt
|
||||
alembic
|
||||
|
Loading…
x
Reference in New Issue
Block a user