diff --git a/brewman/.flake8 b/brewman/.flake8 index cbe41b29..9bfe5802 100644 --- a/brewman/.flake8 +++ b/brewman/.flake8 @@ -1,4 +1,4 @@ [flake8] -ignore = E203, W503 +ignore = E203, W503, E501 max-line-length = 120 exclude = .git,__pycache__,__init__.py,.mypy_cache,.pytest_cache diff --git a/brewman/brewman/core/security.py b/brewman/brewman/core/security.py index 648b0bd0..a24931d8 100644 --- a/brewman/brewman/core/security.py +++ b/brewman/brewman/core/security.py @@ -66,7 +66,7 @@ def authenticate_user(username: str, password: str, db: Session) -> Optional[Use def client_allowed(user: UserModel, client_id: int, otp: Optional[int], db: Session) -> Tuple[bool, Client]: - client: Client = ( + client: Optional[Client] = ( db.execute(select(Client).where(Client.code == client_id)).scalar_one_or_none() if client_id else None ) if client is None: diff --git a/brewman/brewman/core/session.py b/brewman/brewman/core/session.py index 7e272963..48caaa1f 100644 --- a/brewman/brewman/core/session.py +++ b/brewman/brewman/core/session.py @@ -1,30 +1,30 @@ from datetime import date, timedelta +from typing import Union def get_date(session: dict) -> str: if "date" not in session: session["date"] = date.today().strftime("%d-%b-%Y") - return session["date"] + return session["date"] # type: ignore[no-any-return] -def set_date(date_: str, session: dict) -> str: - session["date"] = date_ - return session["date"] +def set_date(date_: Union[str, date], session: dict) -> None: + session["date"] = date_ if isinstance(date_, str) else date_.strftime("%d-%b-%Y") def get_start_date(session: dict) -> str: if "start" not in session: session["start"] = get_first_day(date.today()).strftime("%d-%b-%Y") - return session["start"] + return session["start"] # type: ignore[no-any-return] def get_finish_date(session: dict) -> str: if "finish" not in session: session["finish"] = get_last_day(date.today()).strftime("%d-%b-%Y") - return session["finish"] + return session["finish"] # type: ignore[no-any-return] -def set_period(start, finish, session: dict): +def set_period(start: Union[str, date], finish: Union[str, date], session: dict) -> None: if start is not None: session["start"] = start if isinstance(start, str) else start.strftime("%d-%b-%Y") if finish is not None: diff --git a/brewman/brewman/db/session.py b/brewman/brewman/db/session.py index 330a2a74..c584b81d 100644 --- a/brewman/brewman/db/session.py +++ b/brewman/brewman/db/session.py @@ -8,5 +8,5 @@ from ..core.config import settings logging.basicConfig() logging.getLogger("sqlalchemy.engine").setLevel(settings.LOG_LEVEL) -engine = create_engine(settings.SQLALCHEMY_DATABASE_URI, pool_pre_ping=True, future=True) +engine = create_engine(settings.SQLALCHEMY_DATABASE_URI, pool_pre_ping=True, future=True) # type: ignore[arg-type] SessionFuture = sessionmaker(autoflush=False, bind=engine, future=True) diff --git a/brewman/brewman/routers/__init__.py b/brewman/brewman/routers/__init__.py index 356e5f0d..67485a32 100644 --- a/brewman/brewman/routers/__init__.py +++ b/brewman/brewman/routers/__init__.py @@ -1,8 +1,4 @@ -import re -import uuid - from datetime import date, timedelta -from decimal import Decimal from typing import List, Optional, Tuple from sqlalchemy import or_, select @@ -42,7 +38,7 @@ def get_lock_info( .scalars() .all() ) - data = sorted(data, key=lambda d: d["index"], reverse=True) + data = sorted(data, key=lambda d: d["index"], reverse=True) # type: ignore[no-any-return] for it in data: li: LockInformation = LockInformation( voucherTypes=[VoucherTypesSelected(id=vt["id_"], name=vt["name"]) for vt in it["voucher_types"]], @@ -77,30 +73,3 @@ def get_lock_info( return True, "Voucher allowed" return False, "Default Fallthrough" - - -def to_uuid(value): - p = re.compile("^[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}$") - return uuid.UUID(value) if p.match(value) else None - - -def to_decimal(value, default=0): - import re - - _parser = re.compile( - r""" # A numeric string consists of: - (?P[-+])? # an optional sign, followed by either... - ( - (?=\d|\.\d) # ...a number (with at least one digit) - (?P\d*) # having a (possibly empty) integer part - (\.(?P\d*))? # followed by an optional fractional part - | - (?Ps)? # ...an (optionally signaling) - NaN # NaN - (?P\d*) # with (possibly empty) diagnostic info. - ) - \Z - """, - re.VERBOSE | re.IGNORECASE, - ).match - return Decimal(value) if _parser(value.strip()) is not None else default diff --git a/brewman/brewman/routers/account.py b/brewman/brewman/routers/account.py index 8422042a..94627d5e 100644 --- a/brewman/brewman/routers/account.py +++ b/brewman/brewman/routers/account.py @@ -1,8 +1,8 @@ import uuid -from datetime import datetime +from datetime import date, datetime from decimal import Decimal -from typing import List +from typing import List, Optional import brewman.schemas.account as schemas @@ -20,6 +20,7 @@ from ..models.cost_centre import CostCentre from ..models.journal import Journal from ..models.voucher import Voucher from ..models.voucher_type import VoucherType +from ..schemas.balance import AccountBalance from ..schemas.user import UserToken @@ -121,7 +122,7 @@ async def show_list(user: UserToken = Depends(get_user)) -> List[schemas.Account ] -@router.get("/query") +@router.get("/query", response_model=List[schemas.AccountLink]) async def show_term( q: str, t: int = None, @@ -129,26 +130,26 @@ async def show_term( a: bool = None, c: int = None, current_user: UserToken = Depends(get_user), -): +) -> List[schemas.AccountLink]: count = c - list_ = [] + list_: List[schemas.AccountLink] = [] with SessionFuture() as db: for index, item in enumerate(AccountBase.query(q, t, r, a, db)): - list_.append({"id": item.id, "name": item.name}) + list_.append(schemas.AccountLink(id=item.id, name=item.name)) if count is not None and index == count - 1: break return list_ -@router.get("/{id_}/balance") +@router.get("/{id_}/balance", response_model=AccountBalance) async def show_balance( id_: uuid.UUID, d: str = None, user: UserToken = Depends(get_user), -): - date = None if d is None or d == "" else datetime.strptime(d, "%d-%b-%Y") +) -> AccountBalance: + date_ = None if d is None or d == "" else datetime.strptime(d, "%d-%b-%Y") with SessionFuture() as db: - return {"date": balance(id_, date, db), "total": balance(id_, None, db)} + return AccountBalance(date=balance(id_, date_, db), total=balance(id_, None, db)) @router.get("/{id_}", response_model=schemas.Account) @@ -161,17 +162,17 @@ def show_id( return account_info(item) -def balance(id_: uuid.UUID, date, db: Session) -> Decimal: +def balance(id_: uuid.UUID, date_: Optional[date], db: Session) -> Decimal: account: AccountBase = db.execute(select(AccountBase).where(AccountBase.id == id_)).scalar_one() if not account.type_.balance_sheet: return Decimal(0) bal = select(func.sum(Journal.amount * Journal.debit)).join(Journal.voucher) - if date is not None: - bal = bal.where(Voucher.date <= date) + if date_ is not None: + bal = bal.where(Voucher.date <= date_) bal = bal.where(Voucher.voucher_type != VoucherType.ISSUE).where(Journal.account_id == id_) - result: Decimal = db.execute(bal).scalar_one_or_none() + result: Optional[Decimal] = db.execute(bal).scalar_one_or_none() return Decimal(0) if result is None else result diff --git a/brewman/brewman/routers/attendance.py b/brewman/brewman/routers/attendance.py index d878a5db..340b1116 100644 --- a/brewman/brewman/routers/attendance.py +++ b/brewman/brewman/routers/attendance.py @@ -1,4 +1,5 @@ from datetime import date, datetime, timedelta +from typing import Generator, List import brewman.schemas.attendance as schemas @@ -20,8 +21,10 @@ router = APIRouter() @router.get("", response_model=schemas.Attendance) -def attendance_blank(request: Request, user: UserToken = Security(get_user, scopes=["attendance"])): - return {"date": get_date(request.session), "body": []} +def attendance_blank( + request: Request, user: UserToken = Security(get_user, scopes=["attendance"]) +) -> schemas.Attendance: + return schemas.Attendance(date=get_date(request.session), body=[]) @router.get("/{date_}", response_model=schemas.Attendance) @@ -29,17 +32,17 @@ def attendance_date( date_: str, request: Request, user: UserToken = Security(get_user, scopes=["attendance"]), -): +) -> schemas.Attendance: set_date(date_, request.session) with SessionFuture() as db: - return { - "date": date_, - "body": attendance_date_report(datetime.strptime(date_, "%d-%b-%Y"), db), - } + return schemas.Attendance( + date=date_, + body=attendance_date_report(datetime.strptime(date_, "%d-%b-%Y"), db), + ) -def attendance_date_report(date_: date, db: Session): - body = [] +def attendance_date_report(date_: date, db: Session) -> List[schemas.AttendanceItem]: + body: List[schemas.AttendanceItem] = [] employees = ( db.execute( select(Employee) @@ -93,7 +96,7 @@ def save( date_: str, data: schemas.Attendance, user: UserToken = Security(get_user, scopes=["attendance"]), -): +) -> schemas.Attendance: try: att_date = datetime.strptime(date_, "%d-%b-%Y").date() with SessionFuture() as db: @@ -107,7 +110,7 @@ def save( ) attendance.create(db) db.commit() - return {"date": date_, "body": attendance_date_report(att_date, db)} + return schemas.Attendance(date=date_, body=attendance_date_report(att_date, db)) except SQLAlchemyError as e: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, @@ -115,7 +118,9 @@ def save( ) -def date_range(start: date, stop: date, step=timedelta(days=1), inclusive=False): +def date_range( + start: date, stop: date, step: timedelta = timedelta(days=1), inclusive: bool = False +) -> Generator[date, None, None]: # inclusive=False to behave like range by default if step.days > 0: while start < stop: diff --git a/brewman/brewman/routers/attendance_report.py b/brewman/brewman/routers/attendance_report.py index 7bf735dc..b302be9c 100644 --- a/brewman/brewman/routers/attendance_report.py +++ b/brewman/brewman/routers/attendance_report.py @@ -18,12 +18,12 @@ from .attendance import date_range router = APIRouter() -@router.get("") +@router.get("", response_class=StreamingResponse) def get_report( s: str, f: str, # user: UserToken = Security(get_user, scopes=["attendance"]) ## removed as jwt headers are a pain in the ass -): +) -> StreamingResponse: try: with SessionFuture() as db: output = io.StringIO() @@ -41,7 +41,7 @@ def get_report( # output.close() -def attendance_record(start_date: date, finish_date: date, output, db: Session): +def attendance_record(start_date: date, finish_date: date, output: io.StringIO, db: Session) -> None: header = ["Code", "Name", "Designation", "Department", "Salary", "Points"] for date_ in date_range(start_date, finish_date, inclusive=True): header.append(date_.strftime("%d-%b")) diff --git a/brewman/brewman/routers/attendance_types.py b/brewman/brewman/routers/attendance_types.py index 8596f784..240ead13 100644 --- a/brewman/brewman/routers/attendance_types.py +++ b/brewman/brewman/routers/attendance_types.py @@ -1,3 +1,7 @@ +from typing import List + +import brewman.schemas.attendance_type as schemas + from fastapi import APIRouter, Depends from ..core.security import get_current_active_user as get_user @@ -8,10 +12,10 @@ from ..schemas.user import UserToken router = APIRouter() -@router.get("") -async def show_list(user: UserToken = Depends(get_user)): +@router.get("", response_model=List[schemas.AttendanceType]) +async def show_list(user: UserToken = Depends(get_user)) -> List[schemas.AttendanceType]: list_ = AttendanceType.list() - attendance_types = [] + attendance_types: List[schemas.AttendanceType] = [] for item in list_: - attendance_types.append({"id": item.id, "name": item.name, "value": item.value}) + attendance_types.append(schemas.AttendanceType(id=item.id, name=item.name, value=item.value)) return attendance_types diff --git a/brewman/brewman/routers/client.py b/brewman/brewman/routers/client.py index b2650efe..95756de6 100644 --- a/brewman/brewman/routers/client.py +++ b/brewman/brewman/routers/client.py @@ -18,12 +18,12 @@ from ..schemas.user import UserToken router = APIRouter() -@router.put("/{id_}") +@router.put("/{id_}", response_model=None) def update_route( id_: uuid.UUID, data: schemas.ClientIn, user: UserToken = Security(get_user, scopes=["clients"]), -): +) -> None: try: with SessionFuture() as db: item: Client = db.execute(select(Client).where(Client.id == id_)).scalar_one() @@ -32,7 +32,6 @@ def update_route( item.otp = None item.name = data.name db.commit() - return {} except SQLAlchemyError as e: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, @@ -40,18 +39,17 @@ def update_route( ) -@router.delete("/{id_}") +@router.delete("/{id_}", response_model=None) def delete_route( id_: uuid.UUID, user: UserToken = Security(get_user, scopes=["clients"]), -): +) -> None: try: with SessionFuture() as db: item: Client = db.execute(select(Client).where(Client.id == id_)).scalar_one() db.execute(delete(LoginHistory).where(LoginHistory.client_id == item.id)) db.delete(item) db.commit() - return {} except SQLAlchemyError as e: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, @@ -82,17 +80,17 @@ async def show_list( return clients -@router.get("/{id_}") +@router.get("/{id_}", response_model=schemas.Client) def show_id( id_: uuid.UUID, user: UserToken = Security(get_user, scopes=["clients"]), -): +) -> schemas.Client: with SessionFuture() as db: item: Client = db.execute(select(Client).where(Client.id == id_)).scalar_one() - return { - "id": item.id, - "code": item.code, - "name": item.name, - "enabled": item.enabled, - "otp": item.otp, - } + return schemas.Client( + id=item.id, + code=item.code, + name=item.name, + enabled=item.enabled, + otp=item.otp, + ) diff --git a/brewman/brewman/routers/cost_centre.py b/brewman/brewman/routers/cost_centre.py index dfbfc69d..bb11ddb4 100644 --- a/brewman/brewman/routers/cost_centre.py +++ b/brewman/brewman/routers/cost_centre.py @@ -61,11 +61,11 @@ def update_route( ) -@router.delete("/{id_}", response_model=schemas.CostCentreBlank) +@router.delete("/{id_}", response_model=None) def delete_route( id_: uuid.UUID, user: UserToken = Security(get_user, scopes=["cost-centres"]), -): +) -> None: with SessionFuture() as db: item: CostCentre = db.execute(select(CostCentre).where(CostCentre.id == id_)).scalar_one() if item.is_fixture: diff --git a/brewman/brewman/routers/credit_salary.py b/brewman/brewman/routers/credit_salary.py index 76a2ba5e..f5cce1d3 100644 --- a/brewman/brewman/routers/credit_salary.py +++ b/brewman/brewman/routers/credit_salary.py @@ -1,5 +1,7 @@ from calendar import monthrange from datetime import date, datetime +from decimal import Decimal +from typing import List from fastapi import APIRouter, Body, HTTPException, Security, status from sqlalchemy import or_, select @@ -15,17 +17,18 @@ from ..models.employee import Employee from ..models.journal import Journal from ..models.voucher import Voucher from ..models.voucher_type import VoucherType +from ..schemas.message import Message from ..schemas.user import UserToken router = APIRouter() -@router.post("") +@router.post("", response_model=Message) def credit_salary( month: str = Body(..., embed=True), user: UserToken = Security(get_user, scopes=["attendance"]), -): +) -> Message: month_ = datetime.strptime(month, "%d-%b-%Y").date() start_date = get_first_day(month_) finish_date = get_last_day(month_) @@ -43,13 +46,13 @@ def credit_salary( voucher.journals.append(item) db.add(item) db.commit() - return {"message": "Salary Entry created"} + return Message(message="Salary Entry created") -def salary_journals(start_date: date, finish_date: date, db: Session): +def salary_journals(start_date: date, finish_date: date, db: Session) -> List[Journal]: days = monthrange(start_date.year, start_date.month)[1] - amount = 0 - journals = [] + amount = Decimal(0) + journals: List[Journal] = [] employees = ( db.execute( select(Employee) diff --git a/brewman/brewman/routers/db_image.py b/brewman/brewman/routers/db_image.py index c210cba8..ce80643e 100644 --- a/brewman/brewman/routers/db_image.py +++ b/brewman/brewman/routers/db_image.py @@ -17,8 +17,8 @@ from ..models.db_image import DbImage router = APIRouter() -@router.get("/{id_}/{type_}") -def db_image(id_: uuid.UUID, type_: str): +@router.get("/{id_}/{type_}", response_class=StreamingResponse) +def db_image(id_: uuid.UUID, type_: str) -> StreamingResponse: with SessionFuture() as db: item = db.execute(select(DbImage).where(DbImage.id == id_)).scalar_one() if type_ == "thumbnail": @@ -28,7 +28,7 @@ def db_image(id_: uuid.UUID, type_: str): return StreamingResponse(item, media_type="image/jpeg") -def save_files(voucher_id: uuid.UUID, i: List[bytes], t: List[bytes], db: Session): +def save_files(voucher_id: uuid.UUID, i: List[bytes], t: List[bytes], db: Session) -> None: i = i or [] t = t or [] for index, value in enumerate(i): @@ -41,7 +41,7 @@ def update_files( i: List[bytes], t: List[bytes], db: Session, -): +) -> None: i = i or [] t = t or [] old = [f.id_ for f in data if f.id_] diff --git a/brewman/brewman/routers/db_integrity.py b/brewman/brewman/routers/db_integrity.py index 364ce1ab..45ab911d 100644 --- a/brewman/brewman/routers/db_integrity.py +++ b/brewman/brewman/routers/db_integrity.py @@ -5,23 +5,21 @@ from sqlalchemy.orm import Session from ..core.security import get_current_active_user as get_user from ..db.session import SessionFuture from ..models.attendance import Attendance +from ..schemas.settings import AttendanceCount from ..schemas.user import UserToken router = APIRouter() -@router.post("") -def post_check_db(user: UserToken = Security(get_user)): - info = {} - +@router.post("", response_model=AttendanceCount) +def post_check_db(user: UserToken = Security(get_user)) -> AttendanceCount: with SessionFuture() as db: duplicate_attendances = get_duplicate_attendances(db) if duplicate_attendances > 0: fix_duplicate_attendances(db) - info["attendanceCount"] = duplicate_attendances db.commit() - return info + return AttendanceCount(attendanceCount=duplicate_attendances) def get_duplicate_attendances(db: Session) -> int: @@ -37,7 +35,7 @@ def get_duplicate_attendances(db: Session) -> int: select(func.count(Attendance.id)).where( ~Attendance.id.in_(sub_query), Attendance.is_valid == True # noqa: E712 ) - ).scalar() + ).scalar_one() return query diff --git a/brewman/brewman/routers/employee.py b/brewman/brewman/routers/employee.py index b9c69a39..25af6f1b 100644 --- a/brewman/brewman/routers/employee.py +++ b/brewman/brewman/routers/employee.py @@ -111,7 +111,7 @@ def show_blank( @router.get("/list", response_model=List[schemas.Employee]) -async def show_list(user: UserToken = Depends(get_user)): +async def show_list(user: UserToken = Depends(get_user)) -> List[schemas.Employee]: with SessionFuture() as db: return [ employee_info(item) @@ -127,25 +127,25 @@ async def show_list(user: UserToken = Depends(get_user)): ] -@router.get("/query") +@router.get("/query", response_model=List[schemas.EmployeeLink]) async def show_term( q: str, c: int = None, current_user: UserToken = Depends(get_user), -): - list_ = [] +) -> List[schemas.EmployeeLink]: + list_: List[schemas.EmployeeLink] = [] with SessionFuture() as db: for index, item in enumerate(Employee.query(q=q, type_=10, reconcilable=None, active=None, db=db)): list_.append( - { - "id": item.id, - "name": item.name, - "designation": item.designation, - "costCentre": { - "id": item.cost_centre.id, - "name": item.cost_centre.name, - }, - } + schemas.EmployeeLink( + id=item.id, + name=item.name, + designation=item.designation, + costCentre=schemas.CostCentreLink( + id=item.cost_centre.id, + name=item.cost_centre.name, + ), + ) ) if c is not None and index == c - 1: break @@ -196,7 +196,7 @@ def employee_blank() -> schemas.EmployeeBlank: ) -def delete_with_data(employee: Employee, db: Session): +def delete_with_data(employee: Employee, db: Session) -> None: suspense_account = db.execute(select(Account).where(Account.id == Account.suspense())).scalar_one() query = ( db.execute( diff --git a/brewman/brewman/routers/employee_attendance.py b/brewman/brewman/routers/employee_attendance.py index d81a50fa..2eaf2917 100644 --- a/brewman/brewman/routers/employee_attendance.py +++ b/brewman/brewman/routers/employee_attendance.py @@ -49,7 +49,7 @@ def employee_attendance_report( finish_date = datetime.strptime(f or get_finish_date(request.session), "%d-%b-%Y").date() start_date = employee.joining_date if employee.joining_date > start_date else start_date finish_date = ( - employee.leaving_date if not employee.is_active and employee.leaving_date < finish_date else finish_date + employee.leaving_date if not employee.is_active and employee.leaving_date < finish_date else finish_date # type: ignore[assignment] ) info = schemas.EmployeeAttendance( startDate=start_date, diff --git a/brewman/brewman/routers/employee_benefit.py b/brewman/brewman/routers/employee_benefit.py index 831eee2c..468d6c3d 100644 --- a/brewman/brewman/routers/employee_benefit.py +++ b/brewman/brewman/routers/employee_benefit.py @@ -2,7 +2,7 @@ import uuid from datetime import date, datetime from math import ceil -from typing import List +from typing import List, Tuple import brewman.schemas.input as schema_in import brewman.schemas.voucher as output @@ -93,7 +93,7 @@ def save_employee_benefits( employee_benefits: List[EmployeeBenefitSchema], days_in_month: int, db: Session, -): +) -> Tuple[int, int]: total_exp, total_total = 0, 0 for item in employee_benefits: account = db.execute(select(Employee).where(Employee.id == item.employee.id_)).scalar_one() @@ -125,7 +125,7 @@ def save_employee_benefits( return total_exp, total_total -def save_journals(voucher: Voucher, exp: int, total: int, db: Session): +def save_journals(voucher: Voucher, exp: int, total: int, db: Session) -> None: account = db.execute(select(AccountBase).where(AccountBase.id == AccountBase.esi_pf_expense())).scalar_one() journal = Journal( amount=exp, @@ -154,7 +154,7 @@ def update_route( i: List[bytes] = File(None), t: List[bytes] = File(None), user: UserToken = Security(get_user, scopes=["employee-benefit"]), -): +) -> output.Voucher: try: dt = get_last_day(data.date_) days_in_month = dt.day @@ -212,7 +212,7 @@ def update_employee_benefits( employee_benefits: List[EmployeeBenefitSchema], days_in_month: int, db: Session, -): +) -> Tuple[int, int]: exp, total = 0, 0 for i in range(len(voucher.employee_benefits), 0, -1): item = voucher.employee_benefits[i - 1] @@ -228,7 +228,7 @@ def update_employee_benefits( return exp + new_exp, total + new_total -def update_journals(voucher: Voucher, exp: int, total: int): +def update_journals(voucher: Voucher, exp: int, total: int) -> None: journal = next(i for i in voucher.journals if i.account_id == AccountBase.esi_pf_expense()) journal.amount = exp journal = next(i for i in voucher.journals if i.account_id == AccountBase.esi_pf_payable()) @@ -239,7 +239,7 @@ def update_journals(voucher: Voucher, exp: int, total: int): def get_id( id_: uuid.UUID, user: UserToken = Security(get_user, scopes=["employee-benefit"]), -): +) -> output.Voucher: try: with SessionFuture() as db: item: Voucher = db.execute(select(Voucher).where(Voucher.id == id_)).scalar_one() @@ -255,13 +255,13 @@ def get_id( def show_blank( request: Request, user: UserToken = Security(get_user, scopes=["employee-benefit"]), -): +) -> output.Voucher: additional_info = BlankVoucherInfo(date=get_date(request.session), type=VoucherType.EMPLOYEE_BENEFIT) with SessionFuture() as db: return blank_voucher(additional_info, db) -def esi_contribution(gross_salary, days_worked, days_in_month): +def esi_contribution(gross_salary: int, days_worked: int, days_in_month: int) -> Tuple[int, int, int]: limit = 21000 employee_rate = 0.0175 employer_rate = 0.0475 @@ -270,7 +270,7 @@ def esi_contribution(gross_salary, days_worked, days_in_month): return employee, employer, employee + employer -def pf_contribution(gross_salary, days_worked, days_in_month): +def pf_contribution(gross_salary: int, days_worked: int, days_in_month: int) -> Tuple[int, int, int]: limit = 15000 employee_rate = 0.12 employer_rate = 0.12 + 0.011 + 0.005 + 0.0001 diff --git a/brewman/brewman/routers/fingerprint.py b/brewman/brewman/routers/fingerprint.py index 472615cc..e73629ba 100644 --- a/brewman/brewman/routers/fingerprint.py +++ b/brewman/brewman/routers/fingerprint.py @@ -1,8 +1,12 @@ import csv +import io import uuid from datetime import date, datetime, time, timedelta from io import StringIO +from typing import Dict, List, Tuple + +import brewman.schemas.fingerprint as schemas from fastapi import APIRouter, Depends, File, HTTPException, UploadFile, status from sqlalchemy import bindparam, select @@ -20,20 +24,20 @@ from ..schemas.user import UserToken router = APIRouter() -@router.post("") +@router.post("", response_model=None) def upload_prints( fingerprints: UploadFile = File(None), user: UserToken = Depends(get_user), -): +) -> None: try: with SessionFuture() as db: start = date.today() - timedelta(days=90) finish = date.today() + timedelta(days=7) - employees = {} + employees: Dict[int, uuid.UUID] = {} for id_, code in db.execute(select(Employee.id, Employee.code)).all(): employees[code] = id_ file_data = read_file(fingerprints) - prints = [d for d in fp(file_data, employees) if start <= d["date"].date() <= finish] + prints = [d for d in fp(file_data, employees) if start <= d.date <= finish] paged_data = [prints[i : i + 100] for i in range(0, len(prints), 100)] for i, page in enumerate(paged_data): print(f"Processing page {i} of {len(paged_data)}") @@ -47,10 +51,9 @@ def upload_prints( } ) .on_conflict_do_nothing(), - page, + [p.dict() for p in page], ) db.commit() - return {} except SQLAlchemyError as e: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, @@ -58,7 +61,7 @@ def upload_prints( ) -def read_file(input_file: UploadFile): +def read_file(input_file: UploadFile) -> io.StringIO: input_file.file.seek(0) output = bytearray() while 1: @@ -77,24 +80,24 @@ def read_file(input_file: UploadFile): return StringIO(output.decode(encoding)) -def fp(file_data, employees): - fingerprints = [] +def fp(file_data: StringIO, employees: Dict[int, uuid.UUID]) -> List[schemas.Fingerprint]: + fingerprints: List[schemas.Fingerprint] = [] reader = csv.reader(file_data, delimiter="\t") - header = next(reader, None) + header = next(reader) employee_column = 2 date_column = len(header) - 1 date_format = "%Y/%m/%d %H:%M:%S" if date_column == 6 else "%Y-%m-%d %H:%M:%S" for row in reader: try: employee_code = int(row[employee_column]) # EnNo - date_ = datetime.strptime(row[date_column], date_format) + date_ = datetime.strptime(row[date_column], date_format).date() if employee_code in employees.keys(): fingerprints.append( - { - "id": uuid.uuid4(), - "employee_id": employees[employee_code], - "date": date_, - } + schemas.Fingerprint( + id=uuid.uuid4(), + employee_id=employees[employee_code], + date=date_, + ) ) except ValueError: continue @@ -112,7 +115,7 @@ def fp(file_data, employees): # return Fingerprint.__table__.insert().from_select([Fingerprint.id, Fingerprint.employee_id, Fingerprint.date], sel) -def get_prints(employee_id: uuid.UUID, date_: date, db: Session): +def get_prints(employee_id: uuid.UUID, date_: date, db: Session) -> Tuple[str, str, bool]: prints = ( db.execute( select(Fingerprint) @@ -136,7 +139,7 @@ def get_prints(employee_id: uuid.UUID, date_: date, db: Session): last = item if len(prints) == 0: - hours_worked, full_day = "", None + hours_worked, full_day = "", False elif len(prints) == 2: time_worked = prints[1].date - prints[0].date hours_worked, full_day = working_hours(time_worked) @@ -152,7 +155,7 @@ def get_prints(employee_id: uuid.UUID, date_: date, db: Session): ) -def working_hours(delta: timedelta): +def working_hours(delta: timedelta) -> Tuple[str, bool]: minutes = (delta.seconds // 60) % 60 minutes = int(5 * round(float(minutes) / 5)) hours = delta.seconds // 3600 diff --git a/brewman/brewman/routers/incentive.py b/brewman/brewman/routers/incentive.py index 7b3ffb57..e192d422 100644 --- a/brewman/brewman/routers/incentive.py +++ b/brewman/brewman/routers/incentive.py @@ -40,7 +40,7 @@ def save_route( request: Request, data: schema_in.IncentiveIn = Depends(schema_in.IncentiveIn.load_form), user: UserToken = Security(get_user, scopes=["incentive"]), -): +) -> output.Voucher: try: with SessionFuture() as db: item: Voucher = save(data, user, db) @@ -91,7 +91,7 @@ def save_incentives( employees: List[schema_in.IncentiveEmployee], point_value: Decimal, db: Session, -): +) -> None: total_amount = 0 for item in employees: item_amount = round(item.points * item.days_worked * point_value) @@ -114,13 +114,13 @@ def save_incentives( db.add(journal) -@router.put("/{id_}") +@router.put("/{id_}", response_model=output.Voucher) def update_route( id_: uuid.UUID, request: Request, data: schema_in.IncentiveIn = Depends(schema_in.IncentiveIn.load_form), user: UserToken = Security(get_user, scopes=["incentive"]), -): +) -> output.Voucher: try: with SessionFuture() as db: item: Voucher = update_voucher(id_, data, user, db) @@ -173,7 +173,7 @@ def update_incentives( employees: List[schema_in.IncentiveEmployee], point_value: Decimal, db: Session, -): +) -> None: total_amount = 0 for item in voucher.incentives: employee = next(e for e in employees if e.employee_id == item.journal.account_id) @@ -191,7 +191,7 @@ def update_incentives( def get_id( id_: uuid.UUID, user: UserToken = Security(get_user, scopes=["incentive"]), -): +) -> output.Voucher: try: with SessionFuture() as db: item: Voucher = db.execute(select(Voucher).where(Voucher.id == id_)).scalar_one() @@ -208,7 +208,7 @@ def show_blank( request: Request, d: str = None, user: UserToken = Security(get_user, scopes=["incentive"]), -): +) -> output.Voucher: additional_info = BlankVoucherInfo(date=d or get_date(request.session), type=VoucherType.INCENTIVE) with SessionFuture() as db: return blank_voucher(additional_info, db) @@ -260,7 +260,7 @@ def get_employees( return details -def balance(date_: date, voucher_id: Optional[uuid.UUID], db: Session): +def balance(date_: date, voucher_id: Optional[uuid.UUID], db: Session) -> Decimal: amount = ( select(func.sum(Journal.amount * Journal.debit)) .join(Journal.voucher) @@ -279,14 +279,14 @@ def balance(date_: date, voucher_id: Optional[uuid.UUID], db: Session): if voucher_id is not None: amount = amount.where(Voucher.id != voucher_id) result: Optional[Decimal] = db.execute(amount).scalar_one_or_none() - return 0 if result is None else result * -1 + return Decimal(0) if result is None else result * -1 def check_if_employees_changed( json: List[IncentiveSchema], db: List[Employee], voucher: Optional[List[Journal]], -): +) -> None: json_ids = set(x.employee_id for x in json) db_ids = set(x.id for x in db) voucher_ids = ( diff --git a/brewman/brewman/routers/issue.py b/brewman/brewman/routers/issue.py index dc8f0245..d4139e2d 100644 --- a/brewman/brewman/routers/issue.py +++ b/brewman/brewman/routers/issue.py @@ -49,7 +49,7 @@ def save_route( i: List[bytes] = File(None), t: List[bytes] = File(None), user: UserToken = Security(get_user, scopes=["issue"]), -): +) -> output.Voucher: try: with SessionFuture() as db: item, batch_consumed = save(data, user, db) @@ -155,7 +155,7 @@ def save_journals( destination: schema_in.CostCentreLink, amount: Decimal, db: Session, -): +) -> None: s = Journal( debit=-1, account_id=AccountBase.all_purchases(), @@ -182,7 +182,7 @@ def update_route( i: List[bytes] = File(None), t: List[bytes] = File(None), user: UserToken = Security(get_user, scopes=["issue"]), -): +) -> output.Voucher: try: with SessionFuture() as db: item, batch_consumed = update_voucher(id_, data, user, db) @@ -272,7 +272,7 @@ def update_inventories( inventories: List[InventorySchema], batch_consumed: Optional[bool], db: Session, -): +) -> Decimal: amount: Decimal = Decimal(0) for it in range(len(voucher.inventories), 0, -1): item = voucher.inventories[it - 1] @@ -331,7 +331,7 @@ def update_journals( source: schema_in.CostCentreLink, destination: schema_in.CostCentreLink, amount: Decimal, -): +) -> None: for i in range(len(voucher.journals), 0, -1): item = voucher.journals[i - 1] if item.debit == -1: @@ -366,7 +366,7 @@ def refresh_voucher(id_: uuid.UUID, batch_id: uuid.UUID, db: Session) -> None: def get_id( id_: uuid.UUID, user: UserToken = Security(get_user, scopes=["issue"]), -): +) -> output.Voucher: try: with SessionFuture() as db: item: Voucher = db.execute(select(Voucher).where(Voucher.id == id_)).scalar_one() diff --git a/brewman/brewman/routers/journal.py b/brewman/brewman/routers/journal.py index 1f32c2c5..0f41eaf1 100644 --- a/brewman/brewman/routers/journal.py +++ b/brewman/brewman/routers/journal.py @@ -37,7 +37,7 @@ def save_route( i: List[bytes] = File(None), t: List[bytes] = File(None), user: UserToken = Security(get_user, scopes=["journal"]), -): +) -> output.Voucher: try: with SessionFuture() as db: item: Voucher = save(data, user, db) @@ -98,7 +98,7 @@ def update_route( i: List[bytes] = File(None), t: List[bytes] = File(None), user: UserToken = Security(get_user, scopes=["journal"]), -): +) -> output.Voucher: try: with SessionFuture() as db: item: Voucher = update_voucher(id_, data, user, db) @@ -175,7 +175,7 @@ def update_voucher(id_: uuid.UUID, data: schema_in.JournalIn, user: UserToken, d def get_id( id_: uuid.UUID, user: UserToken = Security(get_user, scopes=["journal"]), -): +) -> output.Voucher: try: with SessionFuture() as db: item: Voucher = db.execute(select(Voucher).where(Voucher.id == id_)).scalar_one() diff --git a/brewman/brewman/routers/lock_information.py b/brewman/brewman/routers/lock_information.py index 66518794..a075d581 100644 --- a/brewman/brewman/routers/lock_information.py +++ b/brewman/brewman/routers/lock_information.py @@ -53,7 +53,7 @@ def post( def delete_route( id_: uuid.UUID, user: UserToken = Security(get_user, scopes=["lock-date"]), -): +) -> List[LockInformation]: with SessionFuture() as db: db.execute(delete(DbSetting).where(DbSetting.id == id_)) db.commit() @@ -93,5 +93,5 @@ def get_info(db: Session) -> List[LockInformation]: finish=LockDate(days=item["finish"]["days"], date=item["finish"]["date_"]), index=item["index"], ) - for item in sorted(data, key=lambda d: d["index"], reverse=True) + for item in sorted(data, key=lambda d: d["index"], reverse=True) # type: ignore[no-any-return] ] diff --git a/brewman/brewman/routers/login.py b/brewman/brewman/routers/login.py index 7147a476..f1983f3a 100644 --- a/brewman/brewman/routers/login.py +++ b/brewman/brewman/routers/login.py @@ -1,4 +1,5 @@ from datetime import datetime, timedelta +from typing import Union from fastapi import ( APIRouter, @@ -38,7 +39,7 @@ async def login_for_access_token( form_data: OAuth2PasswordRequestForm = Depends(), client_id: int = Cookie(None), otp: int = Form(None), -): +) -> Union[Token, JSONResponse]: with SessionFuture() as db: user = authenticate_user(form_data.username, form_data.password, db) if not user: @@ -99,11 +100,11 @@ async def login_for_access_token( }, expires_delta=access_token_expires, ) - return {"access_token": access_token, "token_type": "bearer"} + return Token(access_token=access_token, token_type="bearer") @router.post("/refresh", response_model=Token) -async def refresh_token(user: UserToken = Security(get_current_active_user)): +async def refresh_token(user: UserToken = Security(get_current_active_user)) -> Token: access_token_expires = timedelta(minutes=settings.JWT_TOKEN_EXPIRE_MINUTES) access_token = create_access_token( data={ @@ -115,4 +116,4 @@ async def refresh_token(user: UserToken = Security(get_current_active_user)): }, expires_delta=access_token_expires, ) - return {"access_token": access_token, "token_type": "bearer"} + return Token(access_token=access_token, token_type="bearer") diff --git a/brewman/brewman/routers/maintenance.py b/brewman/brewman/routers/maintenance.py index 4736bce9..3d606275 100644 --- a/brewman/brewman/routers/maintenance.py +++ b/brewman/brewman/routers/maintenance.py @@ -1,3 +1,5 @@ +from typing import Optional + from fastapi import APIRouter, Security from sqlalchemy import select from sqlalchemy.orm import Session @@ -16,7 +18,7 @@ router = APIRouter() @router.get("", response_model=Maintenance) def get_maintenance( user: UserToken = Security(get_user), -): +) -> Maintenance: with SessionFuture() as db: data = db.execute(select(DbSetting).where(DbSetting.name == "Maintenance")).scalars().one_or_none() return info(data, db) @@ -26,9 +28,9 @@ def get_maintenance( def set_maintenance( data: Maintenance, user: UserToken = Security(get_user, scopes=["maintenance"]), -): +) -> Maintenance: with SessionFuture() as db: - maintenance = db.execute(select(DbSetting).where(DbSetting.name == "Maintenance")).scalars().one_or_none() + maintenance: Optional[DbSetting] = db.execute(select(DbSetting).where(DbSetting.name == "Maintenance")).scalar_one_or_none() if data.enabled is False and maintenance is not None: db.delete(maintenance) maintenance = None @@ -41,8 +43,8 @@ def set_maintenance( return info(maintenance, db) -def info(data, db: Session): +def info(data: Optional[DbSetting], db: Session) -> Maintenance: if data is None: - return {"enabled": False, "user": ""} + return Maintenance(enabled=False, user="") user = db.execute(select(User).where(User.id == data.data)).scalar_one() - return {"enabled": True, "user": user.name} + return Maintenance(enabled=True, user=user.name) diff --git a/brewman/brewman/routers/purchase.py b/brewman/brewman/routers/purchase.py index 91e565e9..3d074abb 100644 --- a/brewman/brewman/routers/purchase.py +++ b/brewman/brewman/routers/purchase.py @@ -50,7 +50,7 @@ def save_route( i: List[bytes] = File(None), t: List[bytes] = File(None), user: UserToken = Security(get_user, scopes=["purchase"]), -): +) -> output.Voucher: try: with SessionFuture() as db: item: Voucher = save(data, user, db) @@ -105,7 +105,7 @@ def save(data: schema_in.PurchaseIn, user: UserToken, db: Session) -> Voucher: return voucher -def save_inventories(voucher: Voucher, vendor_id: uuid.UUID, inventories: List[InventorySchema], db: Session): +def save_inventories(voucher: Voucher, vendor_id: uuid.UUID, inventories: List[InventorySchema], db: Session) -> None: for item in inventories: sku: StockKeepingUnit = db.execute( select(StockKeepingUnit).where(StockKeepingUnit.id == item.batch.sku.id_) @@ -141,7 +141,7 @@ def save_inventories(voucher: Voucher, vendor_id: uuid.UUID, inventories: List[I db.add(inventory) -def save_journals(voucher: Voucher, ven: schema_in.AccountLink, db: Session): +def save_journals(voucher: Voucher, ven: schema_in.AccountLink, db: Session) -> None: vendor = db.execute(select(AccountBase).where(AccountBase.id == ven.id_)).scalar_one() journals: Dict[uuid.UUID, Journal] = {} amount = 0 @@ -181,7 +181,7 @@ def update_route( i: List[bytes] = File(None), t: List[bytes] = File(None), user: UserToken = Security(get_user, scopes=["purchase"]), -): +) -> output.Voucher: try: with SessionFuture() as db: item: Voucher = update_voucher(id_, data, user, db) @@ -236,7 +236,7 @@ def update_voucher(id_: uuid.UUID, data: schema_in.PurchaseIn, user: UserToken, return voucher -def update_inventory(voucher: Voucher, vendor_id: uuid.UUID, inventories: List[InventorySchema], db: Session): +def update_inventory(voucher: Voucher, vendor_id: uuid.UUID, inventories: List[InventorySchema], db: Session) -> None: for it in range(len(voucher.inventories), 0, -1): item = voucher.inventories[it - 1] quantity_consumed = -1 * get_batch_quantity(item.batch_id, voucher.id, db) @@ -296,7 +296,7 @@ def update_inventory(voucher: Voucher, vendor_id: uuid.UUID, inventories: List[I save_inventories(voucher, vendor_id, inventories, db) -def update_journals(voucher: Voucher, ven: schema_in.AccountLink, db: Session): +def update_journals(voucher: Voucher, ven: schema_in.AccountLink, db: Session) -> None: vendor = db.execute(select(AccountBase).where(AccountBase.id == ven.id_)).scalar_one() journals: Dict[uuid.UUID, Journal] = {} amount = Decimal(0) @@ -343,7 +343,7 @@ def update_journals(voucher: Voucher, ven: schema_in.AccountLink, db: Session): def get_id( id_: uuid.UUID, user: UserToken = Security(get_user, scopes=["purchase"]), -): +) -> output.Voucher: try: with SessionFuture() as db: item: Voucher = db.execute(select(Voucher).where(Voucher.id == id_)).scalar_one() diff --git a/brewman/brewman/routers/purchase_return.py b/brewman/brewman/routers/purchase_return.py index 879c3e36..2868d888 100644 --- a/brewman/brewman/routers/purchase_return.py +++ b/brewman/brewman/routers/purchase_return.py @@ -46,7 +46,7 @@ def save_route( i: List[bytes] = File(None), t: List[bytes] = File(None), user: UserToken = Security(get_user, scopes=["purchase-return"]), -): +) -> output.Voucher: try: with SessionFuture() as db: item: Voucher = save(data, user, db) @@ -101,7 +101,7 @@ def save(data: schema_in.PurchaseIn, user: UserToken, db: Session) -> Voucher: return voucher -def save_inventories(voucher: Voucher, inventories: List[InventorySchema], db: Session): +def save_inventories(voucher: Voucher, inventories: List[InventorySchema], db: Session) -> None: for d_item in inventories: batch = db.execute(select(Batch).where(Batch.id == d_item.batch.id_)).scalar_one() @@ -130,7 +130,7 @@ def save_inventories(voucher: Voucher, inventories: List[InventorySchema], db: S db.add(item) -def save_journals(voucher: Voucher, ven: schema_in.AccountLink, db: Session): +def save_journals(voucher: Voucher, ven: schema_in.AccountLink, db: Session) -> None: vendor = db.execute(select(AccountBase).where(AccountBase.id == ven.id_)).scalar_one() journals: Dict[uuid.UUID, Journal] = {} amount = 0 @@ -170,7 +170,7 @@ def update_route( i: List[bytes] = File(None), t: List[bytes] = File(None), user: UserToken = Security(get_user, scopes=["purchase-return"]), -): +) -> output.Voucher: try: with SessionFuture() as db: item: Voucher = update_voucher(id_, data, user, db) @@ -228,7 +228,7 @@ def update_voucher(id_: uuid.UUID, data: schema_in.PurchaseIn, user: UserToken, return voucher -def update_inventory(voucher: Voucher, new_inventories: List[InventorySchema], db: Session): +def update_inventory(voucher: Voucher, new_inventories: List[InventorySchema], db: Session) -> None: for it in range(len(voucher.inventories), 0, -1): item = voucher.inventories[it - 1] batch = db.execute(select(Batch).where(Batch.id == item.batch_id)).scalar_one() @@ -261,7 +261,7 @@ def update_inventory(voucher: Voucher, new_inventories: List[InventorySchema], d save_inventories(voucher, new_inventories, db) -def update_journals(voucher: Voucher, ven: schema_in.AccountLink, db): +def update_journals(voucher: Voucher, ven: schema_in.AccountLink, db: Session) -> None: vendor = db.execute(select(AccountBase).where(AccountBase.id == ven.id_)).scalar_one() journals: Dict[uuid.UUID, Journal] = {} amount = 0 @@ -308,7 +308,7 @@ def update_journals(voucher: Voucher, ven: schema_in.AccountLink, db): def get_id( id_: uuid.UUID, user: UserToken = Security(get_user, scopes=["purchase-return"]), -): +) -> output.Voucher: try: with SessionFuture() as db: item: Voucher = db.execute(select(Voucher).where(Voucher.id == id_)).scalar_one() diff --git a/brewman/brewman/routers/rate_contract.py b/brewman/brewman/routers/rate_contract.py index 571c6e73..f870e5fe 100644 --- a/brewman/brewman/routers/rate_contract.py +++ b/brewman/brewman/routers/rate_contract.py @@ -94,7 +94,7 @@ async def update_route( ) -def update_items(rate_contract: RateContract, items: List[RateContractItemSchema], db: Session): +def update_items(rate_contract: RateContract, items: List[RateContractItemSchema], db: Session) -> None: for it in range(len(rate_contract.items), 0, -1): item = rate_contract.items[it - 1] index = next((idx for (idx, d) in enumerate(items) if d.id_ == item.id), None) diff --git a/brewman/brewman/routers/rebase.py b/brewman/brewman/routers/rebase.py index 03bf2137..b9ffc258 100644 --- a/brewman/brewman/routers/rebase.py +++ b/brewman/brewman/routers/rebase.py @@ -30,18 +30,18 @@ from ..schemas.user import UserToken router = APIRouter() -@router.post("/{date_}") +@router.post("/{date_}", response_model=None) def rebase( date_: str, user: UserToken = Security(get_user, scopes=["rebase"]), -): +) -> None: with SessionFuture() as db: - date_ = datetime.strptime(date_, "%d-%b-%Y") - voucher_l = opening_accounts(date_, user.id_, db) - voucher_b = opening_batches(date_, user.id_, db) - starred_vouchers = save_starred(date_, db) + date_obj = datetime.strptime(date_, "%d-%b-%Y") + voucher_l = opening_accounts(date_obj, user.id_, db) + voucher_b = opening_batches(date_obj, user.id_, db) + starred_vouchers = save_starred(date_obj, db) db.flush() - delete_data(date_, starred_vouchers, db) + delete_data(date_obj, starred_vouchers, db) db.add(voucher_l) for j in voucher_l.journals: db.add(j) @@ -52,13 +52,12 @@ def rebase( for i in voucher_b.inventories: db.add(i) db.flush() - cleanup_lint(date_, db) + cleanup_lint(date_obj, db) # request.dbsession.execute('RESET statement_timeout;') db.commit() - return {} -def save_starred(date_: date, db: Session): +def save_starred(date_: date, db: Session) -> List[Voucher]: accounts = [ i.id for i in db.execute(select(AccountBase.id).where(AccountBase.is_starred == True)).all() # noqa: E712 ] @@ -105,7 +104,7 @@ def save_starred(date_: date, db: Session): return vouchers -def opening_accounts(date_: date, user_id: uuid.UUID, db: Session): +def opening_accounts(date_: date, user_id: uuid.UUID, db: Session) -> Voucher: running_total = 0 sum_func = func.sum(Journal.signed_amount) query = db.execute( @@ -152,7 +151,7 @@ def opening_accounts(date_: date, user_id: uuid.UUID, db: Session): return voucher -def opening_batches(date_: date, user_id: uuid.UUID, db: Session): +def opening_batches(date_: date, user_id: uuid.UUID, db: Session) -> Voucher: total = 0 sum_func = func.sum(Journal.debit * Inventory.quantity) query = db.execute( @@ -205,7 +204,7 @@ def opening_batches(date_: date, user_id: uuid.UUID, db: Session): return voucher -def delete_data(date_: date, vouchers: List[Voucher], db: Session): +def delete_data(date_: date, vouchers: List[Voucher], db: Session) -> None: sub_voucher = aliased(Voucher) sub_query = select(sub_voucher.id).where(sub_voucher.date < date_).subquery() @@ -225,7 +224,7 @@ def delete_data(date_: date, vouchers: List[Voucher], db: Session): db.execute(delete(Voucher).where(Voucher.date < date_, ~Voucher.id.in_(vouchers))) -def cleanup_lint(date_: date, db: Session): +def cleanup_lint(date_: date, db: Session) -> None: # Insert executes on the end so keep list of batches and journals db.execute(delete(Batch).where(~Batch.id.in_(select(distinct(Inventory.batch_id)).subquery()))) db.execute(delete(Fingerprint).where(Fingerprint.date < date_)) diff --git a/brewman/brewman/routers/recipe.py b/brewman/brewman/routers/recipe.py index 9ad1192d..ce1ae886 100644 --- a/brewman/brewman/routers/recipe.py +++ b/brewman/brewman/routers/recipe.py @@ -30,7 +30,6 @@ from .reports import report_finish_date, report_start_date router = APIRouter() -# @router.post("/new") # "Recipes" @router.post("", response_model=schemas.Recipe) def save( data: schemas.RecipeIn, @@ -90,15 +89,15 @@ def save( recipe_cost += round(ingredient_cost * quantity, 2) recipe.items.append(RecipeItem(None, product.id, quantity, ingredient_cost)) - recipe.cost_price = round(recipe_cost, 2) + recipe.cost_price = round(recipe_cost / recipe.recipe_yield, 2) recipe_sku.cost_price = round(recipe.cost_price / recipe.recipe_yield, 2) if recipe_sku.product.is_sold: recipe_sku.sale_price = recipe.sale_price db.add(recipe) - for item in recipe.items: - item.recipe_id = recipe.id - db.add(item) + for r_item in recipe.items: + r_item.recipe_id = recipe.id + db.add(r_item) seen_recipes: Set[uuid.UUID] = set() check_recursion(recipe, seen_recipes, db) @@ -177,9 +176,9 @@ async def update_route( else: recipe.items.remove(item) - for item in data.items: - product = db.execute(select(Product).where(Product.id == item.product.id_)).scalar_one() - quantity = round(item.quantity, 2) + for d_item in data.items: + product = db.execute(select(Product).where(Product.id == d_item.product.id_)).scalar_one() + quantity = round(d_item.quantity, 2) if product.is_purchased: ingredient_cost = get_purchased_product_cost(product, data.valid_from, data.valid_till, db) else: @@ -189,7 +188,7 @@ async def update_route( recipe_cost += round(ingredient_cost * quantity, 2) recipe.items.append(RecipeItem(None, product.id, quantity, ingredient_cost)) - recipe.cost_price = recipe_cost + recipe.cost_price = round(recipe_cost / recipe.recipe_yield, 2) seen_recipes: Set[uuid.UUID] = set() check_recursion(recipe, seen_recipes, db) db.commit() @@ -249,11 +248,11 @@ def get_purchased_product_cost(product: Product, start_date: date, finish_date: else: for sku in skus: price.append(sku.cost_price / sku.fraction / sku.product_yield) - return round(sum(price) / len(price), 5) + return round(sum(price) / len(price), 5) # type: ignore[return-value] -def get_sub_product_cost(product: Product, start_date: date, finish_date: date, db: Session): - recipe: Recipe = ( +def get_sub_product_cost(product: Product, start_date: date, finish_date: date, db: Session) -> Decimal: + recipe: Optional[Recipe] = ( db.execute( select(Recipe) .join(Recipe.sku) @@ -273,57 +272,57 @@ def get_sub_product_cost(product: Product, start_date: date, finish_date: date, detail=f"Ingredient {product.name} is not bought and also does not have a recipe for the period.", ) - return recipe.cost_price + return round(recipe.cost_price / recipe.sku.fraction / recipe.sku.product_yield, 5) -def update_old_rows(product_id, valid_from: date, valid_till: date, db: Session): - old = ( +def update_old_rows(sku_id: uuid.UUID, valid_from: date, valid_till: date, db: Session) -> None: + old: List[Recipe] = ( db.execute( select(Recipe) .where( - Recipe.product_id == product_id, + Recipe.sku_id == sku_id, Recipe.valid_from < valid_till, Recipe.valid_till > valid_from, ) - .order_by(Recipe.effective_from) + .order_by(Recipe.valid_from) ) .scalars() .all() ) - new_recipes = [] + new_recipes: List[Recipe] = [] for item in old: if item.valid_from < valid_from: recipe = Recipe( - product_id=item.product_id, - quantity=item.quantity, + sku_id=item.sku_id, + recipe_yield=item.recipe_yield, cost_price=item.cost_price, sale_price=item.sale_price, valid_from=item.valid_from, valid_till=valid_from - timedelta(days=1), ) - for ri in item.recipe_items: - recipe.recipe_items.append(RecipeItem(None, ri.product_id, ri.quantity, ri.price)) + for ri in item.items: + recipe.items.append(RecipeItem(None, ri.product_id, ri.quantity, ri.price)) new_recipes.append(recipe) if item.valid_till > valid_till: recipe = Recipe( - product_id=item.product_id, - quantity=item.quantity, + sku_id=item.sku_id, + recipe_yield=item.recipe_yield, cost_price=item.cost_price, sale_price=item.sale_price, valid_from=valid_till + timedelta(days=1), valid_till=item.valid_till, ) - for ri in item.recipe_items: - recipe.recipe_items.append(RecipeItem(None, ri.product_id, ri.quantity, ri.price)) + for ri in item.items: + recipe.items.append(RecipeItem(None, ri.product_id, ri.quantity, ri.price)) new_recipes.append(recipe) for recipe in new_recipes: db.add(recipe) - for item in recipe.recipe_items: - item.recipe_id = recipe.id - db.add(item) + for r_item in recipe.items: + r_item.recipe_id = recipe.id + db.add(r_item) @router.delete("/{id_}", response_model=schemas.RecipeBlank) diff --git a/brewman/brewman/routers/reports/cash_flow.py b/brewman/brewman/routers/reports/cash_flow.py index e05e83f5..58a1c07f 100644 --- a/brewman/brewman/routers/reports/cash_flow.py +++ b/brewman/brewman/routers/reports/cash_flow.py @@ -1,5 +1,5 @@ from datetime import date, datetime -from typing import Dict, List +from typing import List, Tuple import brewman.schemas.cash_flow as schemas @@ -31,52 +31,54 @@ def report_blank( startDate=get_start_date(request.session), finishDate=get_finish_date(request.session), body=schemas.CashFlowBody(operating=[], investing=[], financing=[], details=[]), - footer=None, + footer=[], ) -@router.get("/data") +@router.get("/data", response_model=schemas.CashFlow) def report_data( request: Request, s: str, f: str, user: UserToken = Security(get_user, scopes=["cash-flow"]), -): +) -> schemas.CashFlow: with SessionFuture() as db: start_date = datetime.strptime(s, "%d-%b-%Y") finish_date = datetime.strptime(f, "%d-%b-%Y") body, footer = build_report(start_date, finish_date, db) set_period(s, f, request.session) - return { - "startDate": s, - "finishDate": f, - "body": body, - "footer": footer, - } + return schemas.CashFlow( + startDate=s, + finishDate=f, + body=body, + footer=footer, + ) -@router.get("/{id_}") +@router.get("/{id_}", response_model=schemas.CashFlow) def report_id( id_: int, request: Request, s: str, f: str, user: UserToken = Security(get_user, scopes=["cash-flow"]), -): +) -> schemas.CashFlow: with SessionFuture() as db: start_date = datetime.strptime(s, "%d-%b-%Y") finish_date = datetime.strptime(f, "%d-%b-%Y") - details, footer = build_report_id(id_, start_date, finish_date, db) + body, footer = build_report_id(id_, start_date, finish_date, db) set_period(s, f, request.session) - return { - "startDate": s, - "finishDate": f, - "body": {"details": details}, - "footer": footer, - } + return schemas.CashFlow( + startDate=s, + finishDate=f, + body=body, + footer=footer, + ) -def build_report(start_date: date, finish_date: date, db: Session): +def build_report( + start_date: date, finish_date: date, db: Session +) -> Tuple[schemas.CashFlowBody, List[schemas.CashFlowItem]]: sub_query = ( select(Voucher.id) .join(Voucher.journals) @@ -102,15 +104,16 @@ def build_report(start_date: date, finish_date: date, db: Session): ).all() total_amount = 0 - cf: Dict[str, List] = {"operating": [], "investing": [], "financing": []} + cf: schemas.CashFlowBody = schemas.CashFlowBody(operating=[], investing=[], financing=[], details=[]) + dict_ = {"operating": cf.operating, "investing": cf.investing, "financing": cf.investing, "details": cf.details} for account_type, amount in query: total_amount += amount * -1 - cf[account_type.cash_flow_classification.lower()].append( - { - "name": account_type.name, - "url": ["/", "cash-flow", str(account_type.id)], - "amount": amount * -1, - } + dict_[account_type.cash_flow_classification.lower()].append( + schemas.CashFlowItem( + name=account_type.name, + url=["/", "cash-flow", str(account_type.id)], + amount=amount * -1, + ) ) opening = db.execute( @@ -138,21 +141,26 @@ def build_report(start_date: date, finish_date: date, db: Session): return ( cf, [ - { - "name": "Net increase in cash and cash equivalents", - "amount": total_amount, - }, - { - "name": "Cash and cash equivalents at beginning of period", - "amount": opening, - }, - {"name": "Cash and cash equivalents at end of period", "amount": closing}, + schemas.CashFlowItem( + name="Net increase in cash and cash equivalents", + url=[], + amount=total_amount, + ), + schemas.CashFlowItem( + name="Cash and cash equivalents at beginning of period", + url=[], + amount=opening, + ), + schemas.CashFlowItem(name="Cash and cash equivalents at end of period", url=[], amount=closing), ], ) -def build_report_id(account_type: int, start_date: date, finish_date: date, db: Session): - details = [] +def build_report_id( + account_type: int, start_date: date, finish_date: date, db: Session +) -> Tuple[schemas.CashFlowBody, List[schemas.CashFlowItem]]: + cf: schemas.CashFlowBody = schemas.CashFlowBody(operating=[], investing=[], financing=[], details=[]) + details = cf.details sub_query = ( select(Voucher.id) .join(Voucher.journals) @@ -177,11 +185,11 @@ def build_report_id(account_type: int, start_date: date, finish_date: date, db: for account, amount in query: total_amount += amount * -1 details.append( - { - "name": account.name, - "url": ["/", "ledger", str(account.id)], - "amount": amount * -1, - } + schemas.CashFlowItem( + name=account.name, + url=["/", "ledger", str(account.id)], + amount=amount * -1, + ) ) - return details, [{"name": "total", "amount": total_amount}] + return cf, [schemas.CashFlowItem(name="total", url=[], amount=total_amount)] diff --git a/brewman/brewman/routers/reports/closing_stock.py b/brewman/brewman/routers/reports/closing_stock.py index 3f52f783..acbb54b2 100644 --- a/brewman/brewman/routers/reports/closing_stock.py +++ b/brewman/brewman/routers/reports/closing_stock.py @@ -63,7 +63,7 @@ def delete_voucher( date_: str, d: Optional[uuid.UUID] = None, user: UserToken = Security(get_user, scopes=["closing-stock"]), -): +) -> None: try: cost_centre_id = d or CostCentre.cost_centre_purchase() with SessionFuture() as db: @@ -210,7 +210,7 @@ def get_opening_stock(date_: date, db: Session) -> Decimal: return Decimal(0) if opening_stock is None else opening_stock -def get_closing_stock(date_, db: Session) -> Decimal: +def get_closing_stock(date_: date, db: Session) -> Decimal: closing_stock: Optional[Decimal] = db.execute( select(func.sum(Inventory.quantity * Inventory.rate * (1 + Inventory.tax) * Journal.debit)) .join(Journal.voucher) @@ -226,7 +226,7 @@ def save_route( request: Request, data: schemas.ClosingStock, user: UserToken = Security(get_user, scopes=["closing-stock"]), -): +) -> schemas.ClosingStock: try: departments = set(i.cost_centre.id_ for i in data.items if i.cost_centre is not None) if data.cost_centre.id_ in departments: @@ -265,7 +265,7 @@ def save_route( items = [d for d in data.items if d.cost_centre is not None and d.cost_centre.id_ == dep] item = save(data.date_, items, user, db) save_inventories(item, items, db) - amount: Decimal = sum(i.amount for i in item.inventories) + amount: Decimal = sum(i.amount for i in item.inventories) # type: ignore[assignment] save_journals(item, data.cost_centre.id_, dep, amount, db) check_journals_are_valid(item) db.commit() @@ -278,7 +278,7 @@ def save_route( ) -def save_cs(data: schemas.ClosingStock, db: Session): +def save_cs(data: schemas.ClosingStock, db: Session) -> None: for item in data.items: cs = ClosingStock( date_=data.date_, cost_centre_id=data.cost_centre.id_, sku_id=item.product.id_, quantity=item.physical @@ -344,7 +344,7 @@ def save_journals( destination: uuid.UUID, amount: Decimal, db: Session, -): +) -> None: s = Journal(debit=-1, account_id=AccountBase.all_purchases(), amount=amount, cost_centre_id=source) d = Journal(debit=1, account_id=AccountBase.all_purchases(), amount=amount, cost_centre_id=destination) voucher.journals.append(s) @@ -353,7 +353,7 @@ def save_journals( db.add(d) -def update_cs(date_: date, cost_centre_id: uuid.UUID, items: List[schemas.ClosingStockItem], db: Session): +def update_cs(date_: date, cost_centre_id: uuid.UUID, items: List[schemas.ClosingStockItem], db: Session) -> None: old = ( db.execute( select(ClosingStock).where(ClosingStock.date == date_, ClosingStock.cost_centre_id == cost_centre_id) @@ -380,7 +380,7 @@ def update_cs(date_: date, cost_centre_id: uuid.UUID, items: List[schemas.Closin db.add(cs) -def delete_old_vouchers(date_: date, cost_centre_id: uuid.UUID, db: Session): +def delete_old_vouchers(date_: date, cost_centre_id: uuid.UUID, db: Session) -> None: vouchers = ( db.execute(select(Voucher.id).where(Voucher.date == date_, Voucher.voucher_type == VoucherType.CLOSING_STOCK)) .scalars() diff --git a/brewman/brewman/routers/reports/product_ledger.py b/brewman/brewman/routers/reports/product_ledger.py index fbb2fcc2..861e8270 100644 --- a/brewman/brewman/routers/reports/product_ledger.py +++ b/brewman/brewman/routers/reports/product_ledger.py @@ -162,8 +162,8 @@ def opening_balance( .group_by(StockKeepingUnit.units) ).all() - quantity: Decimal = sum(r.quantity for r in row) - amount: Decimal = sum(r.amount for r in row) + quantity: Decimal = sum(r.quantity for r in row) # type: ignore[assignment] + amount: Decimal = sum(r.amount for r in row) # type: ignore[assignment] return ( quantity, diff --git a/brewman/brewman/routers/reports/reconcile.py b/brewman/brewman/routers/reports/reconcile.py index 4acbdcd5..8ea648be 100644 --- a/brewman/brewman/routers/reports/reconcile.py +++ b/brewman/brewman/routers/reports/reconcile.py @@ -1,7 +1,11 @@ -import datetime import uuid -from fastapi import APIRouter, Depends, Request, Security +from datetime import date, datetime +from typing import List + +import brewman.schemas.reconcile as schemas + +from fastapi import APIRouter, Request, Security from sqlalchemy import not_ from sqlalchemy.orm import Session, joinedload from sqlalchemy.sql.expression import and_, func, or_, select @@ -19,42 +23,44 @@ from ...schemas.user import UserToken router = APIRouter() -@router.get("") +@router.get("", response_model=schemas.Reconcile) def show_blank( request: Request, user: UserToken = Security(get_user, scopes=["reconcile"]), -): - return { - "startDate": get_start_date(request.session), - "finishDate": get_finish_date(request.session), - "account": None, - "body": [], - } +) -> schemas.Reconcile: + return schemas.Reconcile( + startDate=get_start_date(request.session), + finishDate=get_finish_date(request.session), + account=None, + body=[], + ) -@router.get("/{id_}") +@router.get("/{id_}", response_model=schemas.Reconcile) def show_data( id_: uuid.UUID, request: Request, s: str = None, f: str = None, user: UserToken = Security(get_user, scopes=["reconcile"]), -): +) -> schemas.Reconcile: with SessionFuture() as db: account = db.execute(select(AccountBase).where(AccountBase.id == id_)).scalar_one() - start_date = s if s is not None else get_start_date(request.session) - finish_date = f if f is not None else get_finish_date(request.session) + start_date = datetime.strptime(s or get_start_date(request.session), "%d-%b-%Y") + finish_date = datetime.strptime(f or get_finish_date(request.session), "%d-%b-%Y") body = build_report(account.id, start_date, finish_date, db) set_period(start_date, finish_date, request.session) - return { - "startDate": start_date, - "finishDate": finish_date, - "account": {"id": account.id, "name": account.name}, - "body": body, - } + return schemas.Reconcile( + startDate=start_date, + finishDate=finish_date, + account=schemas.AccountLink(id=account.id, name=account.name), + body=body, + ) -def build_report(account_id, start_date, finish_date, db): +def build_report( + account_id: uuid.UUID, start_date: date, finish_date: date, db: Session +) -> List[schemas.ReconcileItem]: opening = opening_balance(account_id, start_date, db) body = [opening] @@ -66,8 +72,8 @@ def build_report(account_id, start_date, finish_date, db): or_( Voucher.is_reconciled == False, # noqa: E712 and_( - Voucher.reconcile_date >= datetime.datetime.strptime(start_date, "%d-%b-%Y"), - Voucher.reconcile_date <= datetime.datetime.strptime(finish_date, "%d-%b-%Y"), + Voucher.reconcile_date >= start_date, + Voucher.reconcile_date <= finish_date, ), ), not_(Voucher.voucher_type.in_([VoucherType.ISSUE, VoucherType.CLOSING_STOCK])), @@ -94,27 +100,27 @@ def build_report(account_id, start_date, finish_date, db): name += "{0} / ".format(journal.account.name) name = name[:-3] body.append( - { - "id": voucher.id, - "date": voucher.date.strftime("%d-%b-%Y"), - "name": name, - "type": voucher.voucher_type.name.replace("_", " ").title(), - "narration": voucher.narration, - "debit": debit, - "credit": credit, - "isReconciled": voucher.is_reconciled, - "reconcileDate": voucher.reconcile_date.strftime("%d-%b-%Y"), - } + schemas.ReconcileItem( + id=voucher.id, + date=voucher.date, + name=name, + type=voucher.voucher_type.name.replace("_", " ").title(), + narration=voucher.narration, + debit=debit, + credit=credit, + isReconciled=voucher.is_reconciled, + reconcileDate=voucher.reconcile_date, + ) ) return body -def opening_balance(account_id: uuid.UUID, start_date, db: Session): +def opening_balance(account_id: uuid.UUID, start_date: date, db: Session) -> schemas.ReconcileItem: opening = db.execute( select(func.sum(Journal.amount * Journal.debit)) .join(Journal.voucher) .where( - Voucher.reconcile_date < datetime.datetime.strptime(start_date, "%d-%b-%Y"), + Voucher.reconcile_date < start_date, Voucher.is_reconciled == True, # noqa: E712 not_(Voucher.voucher_type.in_([VoucherType.ISSUE, VoucherType.CLOSING_STOCK])), Journal.account_id == account_id, @@ -127,51 +133,46 @@ def opening_balance(account_id: uuid.UUID, start_date, db: Session): else: debit = opening credit = 0 - return { - "date": start_date, - "id": None, - "name": "Opening Balance", - "type": "Opening Balance", - "narration": "", - "debit": debit, - "credit": credit, - "running": opening, - "isReconciled": True, - "reconcileDate": start_date, - } + return schemas.ReconcileItem( + date=start_date, + id=None, + name="Opening Balance", + type="Opening Balance", + narration="", + debit=debit, + credit=credit, + running=opening, + isReconciled=True, + reconcileDate=start_date, + ) -@router.post("/{id_}") +@router.post("/{id_}", response_model=schemas.Reconcile) def save( id_: uuid.UUID, + data: schemas.Reconcile, request: Request, s: str = None, f: str = None, user: UserToken = Security(get_user, scopes=["reconcile"]), -): +) -> schemas.Reconcile: with SessionFuture() as db: account = db.execute(select(AccountBase).where(AccountBase.id == id_)).scalar_one() - start_date = s if s is not None else get_start_date(request.session) - finish_date = f if f is not None else get_finish_date(request.session) - raise Exception("not fixed") - # for item in request.json_body["body"]: - # if "id" not in item or item["id"] is None: - # continue - # - # voucher = ( - # request.dbsession.query(Voucher) - # .where(Voucher.id == uuid.UUID(item["id"])) - # .first() - # ) - # is_reconciled = item["isReconciled"] - # reconcile_date = datetime.datetime.strptime(item["reconcileDate"], "%d-%b-%Y") - # voucher.is_reconciled = is_reconciled - # voucher.reconcile_date = reconcile_date - # transaction.commit() - # body = build_report(account.id, start_date, finish_date, request) - # return { - # "startDate": start_date, - # "finishDate": finish_date, - # "account": {"id": account.id, "name": account.name}, - # "body": body - # } + start_date = datetime.strptime(s or get_start_date(request.session), "%d-%b-%Y") + finish_date = datetime.strptime(f or get_finish_date(request.session), "%d-%b-%Y") + for item in data.body: + if item.id_ is None: + continue + + voucher: Voucher = db.execute(select(Voucher).where(Voucher.id == item.id_)).scalar_one() + voucher.is_reconciled = item.is_reconciled + if item.reconcile_date is not None: + voucher.reconcile_date = item.reconcile_date + db.commit() + body = build_report(account.id, start_date, finish_date, db) + return schemas.Reconcile( + startDate=start_date, + finishDate=finish_date, + account=schemas.AccountLink(id=account.id, name=account.name), + body=body, + ) diff --git a/brewman/brewman/routers/user.py b/brewman/brewman/routers/user.py index 6f6a8499..e8765d9d 100644 --- a/brewman/brewman/routers/user.py +++ b/brewman/brewman/routers/user.py @@ -106,11 +106,11 @@ def add_roles(user: User, roles: List[schemas.RoleItem], db: Session) -> None: user.roles.remove(ug) -@router.delete("/{id_}") +@router.delete("/{id_}", response_model=None) def delete_route( id_: uuid.UUID, user: UserToken = Security(get_user, scopes=["users"]), -): +) -> None: if id_ == user.id_: raise HTTPException( status_code=status.HTTP_501_NOT_IMPLEMENTED, @@ -154,11 +154,11 @@ async def show_list( ] -@router.get("/active") -async def show_active(user: UserToken = Depends(get_user)): +@router.get("/active", response_model=List[schemas.UserBlank]) +async def show_active(user: UserToken = Depends(get_user)) -> List[schemas.UserBlank]: with SessionFuture() as db: return [ - {"name": name} + schemas.UserBlank(name=name) for name in db.execute(select(User.name).where(User.locked_out == False).order_by(User.name)) # noqa: E712 .scalars() .all() diff --git a/brewman/brewman/routers/voucher.py b/brewman/brewman/routers/voucher.py index 01894de7..1e983e55 100644 --- a/brewman/brewman/routers/voucher.py +++ b/brewman/brewman/routers/voucher.py @@ -92,7 +92,7 @@ def check_delete_permissions(voucher: Voucher, user: UserToken) -> None: def delete_voucher( id_: uuid.UUID, user: UserToken = Security(get_user), -): +) -> output.Voucher: with SessionFuture() as db: voucher: Voucher = db.execute(select(Voucher).where(Voucher.id == id_)).scalar_one() images = db.execute(select(DbImage).where(DbImage.resource_id == voucher.id)).scalars().all() diff --git a/brewman/brewman/schemas/balance.py b/brewman/brewman/schemas/balance.py new file mode 100644 index 00000000..302ec60c --- /dev/null +++ b/brewman/brewman/schemas/balance.py @@ -0,0 +1,8 @@ +from decimal import Decimal + +from pydantic import BaseModel + + +class AccountBalance(BaseModel): + date: Decimal + total: Decimal diff --git a/brewman/brewman/schemas/blank_voucher_info.py b/brewman/brewman/schemas/blank_voucher_info.py index e1f552cd..b18199ea 100644 --- a/brewman/brewman/schemas/blank_voucher_info.py +++ b/brewman/brewman/schemas/blank_voucher_info.py @@ -21,7 +21,7 @@ class BlankVoucherInfo(BaseModel): json_encoders = {date: lambda v: v.strftime("%d-%b-%Y")} @validator("date_", pre=True) - def parse_date(cls, value): + def parse_date(cls, value) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() diff --git a/brewman/brewman/schemas/cash_flow.py b/brewman/brewman/schemas/cash_flow.py index 5fefc794..1befd2fb 100644 --- a/brewman/brewman/schemas/cash_flow.py +++ b/brewman/brewman/schemas/cash_flow.py @@ -33,7 +33,7 @@ class CashFlow(BaseModel): start_date: date finish_date: date body: CashFlowBody - footer: Optional[List[CashFlowItem]] + footer: List[CashFlowItem] class Config: anystr_strip_whitespace = True diff --git a/brewman/brewman/schemas/closing_stock.py b/brewman/brewman/schemas/closing_stock.py index 54de5485..df4ace35 100644 --- a/brewman/brewman/schemas/closing_stock.py +++ b/brewman/brewman/schemas/closing_stock.py @@ -43,13 +43,13 @@ class ClosingStock(BaseModel): json_encoders = {date: lambda v: v.strftime("%d-%b-%Y"), datetime: lambda v: v.strftime("%d-%b-%Y %H:%M")} @validator("date_", pre=True) - def parse_date(cls, value): + def parse_date(cls, value) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() @validator("creation_date", pre=True) - def parse_creation_date(cls, value): + def parse_creation_date(cls, value) -> Optional[datetime]: if value is None: return None if isinstance(value, datetime): @@ -57,7 +57,7 @@ class ClosingStock(BaseModel): return datetime.strptime(value, "%d-%b-%Y %H:%M") @validator("last_edit_date", pre=True) - def parse_last_edit_date(cls, value): + def parse_last_edit_date(cls, value) -> Optional[datetime]: if value is None: return None if isinstance(value, datetime): diff --git a/brewman/brewman/schemas/input.py b/brewman/brewman/schemas/input.py index fd535f8e..891b0700 100644 --- a/brewman/brewman/schemas/input.py +++ b/brewman/brewman/schemas/input.py @@ -30,13 +30,13 @@ class JournalIn(VoucherIn): } @validator("date_", pre=True) - def parse_date(cls, value): + def parse_date(cls, value) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() @classmethod - def load_form(cls, data: str = Form(...)): + def load_form(cls, data: str = Form(...)) -> BaseModel: json_data = json.loads(data) return cls.parse_obj(json_data) @@ -54,13 +54,13 @@ class PurchaseIn(VoucherIn): } @validator("date_", pre=True) - def parse_date(cls, value): + def parse_date(cls, value) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() @classmethod - def load_form(cls, data: str = Form(...)): + def load_form(cls, data: str = Form(...)) -> BaseModel: json_data = json.loads(data) return cls.parse_obj(json_data) @@ -79,19 +79,19 @@ class IssueIn(VoucherIn): } @validator("date_", pre=True) - def parse_date(cls, value): + def parse_date(cls, value) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() @validator("destination") # For Purchase, Issue and Return Vouchers - def source_destination_unique(cls, value: CostCentreLink, values): + def source_destination_unique(cls, value: CostCentreLink, values) -> CostCentreLink: if value.id_ == values["source"].id_: raise ValueError("Source and destination cannot be the same") return value @classmethod - def load_form(cls, data: str = Form(...)): + def load_form(cls, data: str = Form(...)) -> BaseModel: json_data = json.loads(data) return cls.parse_obj(json_data) @@ -108,13 +108,13 @@ class EmployeeBenefitIn(VoucherIn): } @validator("date_", pre=True) - def parse_date(cls, value): + def parse_date(cls, value) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() @classmethod - def load_form(cls, data: str = Form(...)): + def load_form(cls, data: str = Form(...)) -> BaseModel: json_data = json.loads(data) return cls.parse_obj(json_data) @@ -131,13 +131,13 @@ class IncentiveIn(VoucherIn): } @validator("date_", pre=True) - def parse_date(cls, value): + def parse_date(cls, value) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() @classmethod - def load_form(cls, data: str = Form(...)): + def load_form(cls, data: str = Form(...)) -> BaseModel: json_data = json.loads(data) return cls.parse_obj(json_data) diff --git a/brewman/brewman/schemas/message.py b/brewman/brewman/schemas/message.py new file mode 100644 index 00000000..1b78abd2 --- /dev/null +++ b/brewman/brewman/schemas/message.py @@ -0,0 +1,5 @@ +from pydantic.main import BaseModel + + +class Message(BaseModel): + message: str diff --git a/brewman/brewman/schemas/rate_contract.py b/brewman/brewman/schemas/rate_contract.py index 0770c964..da09c47c 100644 --- a/brewman/brewman/schemas/rate_contract.py +++ b/brewman/brewman/schemas/rate_contract.py @@ -28,29 +28,17 @@ class RateContractIn(BaseModel): } @validator("date_", pre=True) - def parse_date(cls, value): + def parse_date(cls, value) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() - @validator("valid_from", pre=True) - def parse_creation_date(cls, value): - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y") - - @validator("valid_till", pre=True) - def parse_last_edit_date(cls, value): - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y") - class RateContract(RateContractIn): id_: uuid.UUID - creation_date: Optional[datetime] - last_edit_date: Optional[datetime] - user: Optional[UserLink] + creation_date: datetime + last_edit_date: datetime + user: UserLink class Config: anystr_strip_whitespace = True @@ -60,32 +48,14 @@ class RateContract(RateContractIn): 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() - # - # @validator("valid_from", pre=True) - # def parse_creation_date(cls, value): - # if isinstance(value, datetime): - # return value - # return datetime.strptime(value, "%d-%b-%Y %H:%M") - # - # @validator("valid_till", pre=True) - # def parse_last_edit_date(cls, value): - # if isinstance(value, datetime): - # return value - # return datetime.strptime(value, "%d-%b-%Y %H:%M") - @validator("creation_date", pre=True) - def parse_creation_date(cls, value): + def parse_creation_date(cls, value) -> datetime: if isinstance(value, datetime): return value return datetime.strptime(value, "%d-%b-%Y %H:%M") @validator("last_edit_date", pre=True) - def parse_last_edit_date(cls, value): + def parse_last_edit_date(cls, value) -> datetime: if isinstance(value, datetime): return value return datetime.strptime(value, "%d-%b-%Y %H:%M") diff --git a/brewman/brewman/schemas/recipe.py b/brewman/brewman/schemas/recipe.py index de81b6d2..fc50f7da 100644 --- a/brewman/brewman/schemas/recipe.py +++ b/brewman/brewman/schemas/recipe.py @@ -30,13 +30,13 @@ class RecipeIn(BaseModel): json_encoders = {date: lambda v: v.strftime("%d-%b-%Y") if v is not None else None} @validator("valid_from", pre=True) - def parse_valid_from(cls, value): + def parse_valid_from(cls, value) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() @validator("valid_till", pre=True) - def parse_valid_till(cls, value): + def parse_valid_till(cls, value) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() diff --git a/brewman/brewman/schemas/reconcile.py b/brewman/brewman/schemas/reconcile.py new file mode 100644 index 00000000..2f9f60ba --- /dev/null +++ b/brewman/brewman/schemas/reconcile.py @@ -0,0 +1,68 @@ +import uuid + +from datetime import date, datetime +from decimal import Decimal +from typing import List, Optional + +from pydantic import validator +from pydantic.main import BaseModel + +from . import to_camel +from .account import AccountLink + + +class ReconcileItem(BaseModel): + id_: Optional[uuid.UUID] + date_: date + name: str + url: List[str] + type_: str + narration: str + debit: Decimal + credit: Decimal + posted: bool + is_reconciled: bool + reconcile_date: Optional[date] + + @validator("date_", pre=True) + def parse_date(cls, value): + if isinstance(value, date): + return value + return datetime.strptime(value, "%d-%b-%Y").date() + + @validator("reconcile_date", pre=True) + def parse_reconcile_date(cls, value): + if value is None: + return None + if isinstance(value, date): + return value + return datetime.strptime(value, "%d-%b-%Y").date() + + class Config: + anystr_strip_whitespace = True + alias_generator = to_camel + json_encoders = {date: lambda v: v.strftime("%d-%b-%Y")} + + +class Reconcile(BaseModel): + start_date: date + finish_date: date + account: Optional[AccountLink] + body: List[ReconcileItem] + + class Config: + anystr_strip_whitespace = True + alias_generator = to_camel + json_encoders = {date: lambda v: v.strftime("%d-%b-%Y")} + + @validator("start_date", pre=True) + def parse_start_date(cls, value): + if isinstance(value, date): + return value + return datetime.strptime(value, "%d-%b-%Y").date() + + @validator("finish_date", pre=True) + def parse_finish_date(cls, value): + if isinstance(value, date): + return value + return datetime.strptime(value, "%d-%b-%Y").date() diff --git a/brewman/brewman/schemas/settings.py b/brewman/brewman/schemas/settings.py index d457536d..d9733018 100644 --- a/brewman/brewman/schemas/settings.py +++ b/brewman/brewman/schemas/settings.py @@ -82,4 +82,11 @@ class LockInformation(BaseModel): class Maintenance(BaseModel): enabled: bool - user: Optional[str] + user: str + + +class AttendanceCount(BaseModel): + attendance_count: int + + class Config: + alias_generator = to_camel diff --git a/brewman/brewman/schemas/voucher.py b/brewman/brewman/schemas/voucher.py index 1c5969ac..6961262a 100644 --- a/brewman/brewman/schemas/voucher.py +++ b/brewman/brewman/schemas/voucher.py @@ -30,7 +30,7 @@ class VoucherIn(BaseModel): files: List[ImageUpload] @classmethod - def load_form(cls, data: str = Form(...)): + def load_form(cls, data: str = Form(...)) -> BaseModel: json_data = json.loads(data) return cls.parse_obj(json_data) @@ -63,13 +63,13 @@ class Voucher(VoucherIn): } @validator("date_", pre=True) - def parse_date(cls, value): + def parse_date(cls, value) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() @validator("creation_date", pre=True) - def parse_creation_date(cls, value): + def parse_creation_date(cls, value) -> Optional[date]: if value is None or value == "": return None if isinstance(value, datetime): @@ -77,7 +77,7 @@ class Voucher(VoucherIn): return datetime.strptime(value, "%d-%b-%Y %H:%M") @validator("last_edit_date", pre=True) - def parse_last_edit_date(cls, value): + def parse_last_edit_date(cls, value) -> Optional[date]: if value is None or value == "": return None if isinstance(value, datetime): @@ -85,7 +85,7 @@ class Voucher(VoucherIn): return datetime.strptime(value, "%d-%b-%Y %H:%M") @validator("reconcile_date", pre=True) - def parse_reconcile_date(cls, value): + def parse_reconcile_date(cls, value) -> Optional[date]: if value is None or value == "": return None elif isinstance(value, date): diff --git a/overlord/src/app/cash-flow/cash-flow.ts b/overlord/src/app/cash-flow/cash-flow.ts index 216f36d8..8086edec 100644 --- a/overlord/src/app/cash-flow/cash-flow.ts +++ b/overlord/src/app/cash-flow/cash-flow.ts @@ -10,7 +10,7 @@ export class CashFlow { details: CashFlowItem[]; }; - footer?: CashFlowItem[]; + footer: CashFlowItem[]; public constructor(init?: Partial) { this.startDate = ''; @@ -21,6 +21,7 @@ export class CashFlow { financing: [], details: [], }; + this.footer = []; Object.assign(this, init); }