From 2d81f80c63f0bb100f16613552ac7695b8e29638 Mon Sep 17 00:00:00 2001 From: tanshu Date: Fri, 4 Dec 2020 13:02:13 +0530 Subject: [PATCH] Moved all the schemas into their own logical files. --- brewman/brewman/core/security.py | 25 +- brewman/brewman/core/session.py | 12 +- brewman/brewman/models/auth.py | 6 +- brewman/brewman/routers/account.py | 19 +- brewman/brewman/routers/account_types.py | 6 +- brewman/brewman/routers/attendance.py | 2 +- brewman/brewman/routers/attendance_types.py | 2 +- brewman/brewman/routers/auth/client.py | 4 +- brewman/brewman/routers/auth/role.py | 81 +-- brewman/brewman/routers/auth/user.py | 83 +-- brewman/brewman/routers/batch.py | 2 +- brewman/brewman/routers/cost_centre.py | 63 +- brewman/brewman/routers/credit_salary.py | 2 +- brewman/brewman/routers/db_integrity.py | 2 +- brewman/brewman/routers/employee.py | 6 +- .../brewman/routers/employee_attendance.py | 2 +- brewman/brewman/routers/employee_benefit.py | 2 +- brewman/brewman/routers/fingerprint.py | 2 +- brewman/brewman/routers/incentive.py | 2 +- brewman/brewman/routers/issue.py | 2 +- brewman/brewman/routers/issue_grid.py | 2 +- brewman/brewman/routers/journal.py | 2 +- brewman/brewman/routers/lock_information.py | 2 +- brewman/brewman/routers/login.py | 25 +- brewman/brewman/routers/maintenance.py | 2 +- brewman/brewman/routers/product.py | 123 ++-- brewman/brewman/routers/product_group.py | 60 +- brewman/brewman/routers/purchase.py | 2 +- brewman/brewman/routers/purchase_return.py | 2 +- brewman/brewman/routers/rebase.py | 2 +- .../brewman/routers/reports/balance_sheet.py | 17 +- brewman/brewman/routers/reports/cash_flow.py | 18 +- .../brewman/routers/reports/closing_stock.py | 38 +- brewman/brewman/routers/reports/daybook.py | 4 +- brewman/brewman/routers/reports/ledger.py | 82 +-- .../routers/reports/net_transactions.py | 39 +- .../brewman/routers/reports/product_ledger.py | 102 ++-- .../brewman/routers/reports/profit_loss.py | 90 +-- .../routers/reports/purchase_entries.py | 44 +- brewman/brewman/routers/reports/purchases.py | 14 +- .../routers/reports/raw_material_cost.py | 107 ++-- brewman/brewman/routers/reports/reconcile.py | 2 +- .../brewman/routers/reports/stock_movement.py | 84 +-- .../brewman/routers/reports/trial_balance.py | 28 +- brewman/brewman/routers/reports/unposted.py | 4 +- brewman/brewman/routers/reset_stock.py | 2 +- brewman/brewman/routers/voucher.py | 2 +- brewman/brewman/schemas/account.py | 46 ++ brewman/brewman/schemas/account_type.py | 9 + brewman/brewman/schemas/auth.py | 106 ---- brewman/brewman/schemas/balance_sheet.py | 36 ++ brewman/brewman/schemas/cash_flow.py | 52 ++ brewman/brewman/schemas/client.py | 18 + brewman/brewman/schemas/closing_stock.py | 34 ++ brewman/brewman/schemas/cost_centre.py | 39 ++ brewman/brewman/schemas/daybook.py | 56 ++ brewman/brewman/schemas/db_setting.py | 9 + brewman/brewman/schemas/employee.py | 17 +- brewman/brewman/schemas/input.py | 3 +- brewman/brewman/schemas/ledger.py | 57 ++ brewman/brewman/schemas/login_history.py | 18 + brewman/brewman/schemas/master.py | 164 ------ brewman/brewman/schemas/net_transactions.py | 42 ++ brewman/brewman/schemas/permission.py | 12 + brewman/brewman/schemas/product.py | 51 ++ brewman/brewman/schemas/product_group.py | 36 ++ brewman/brewman/schemas/product_ledger.py | 61 ++ brewman/brewman/schemas/profit_loss.py | 44 ++ brewman/brewman/schemas/purchase_entries.py | 52 ++ brewman/brewman/schemas/purchases.py | 43 ++ brewman/brewman/schemas/raw_material_cost.py | 53 ++ brewman/brewman/schemas/recipe.py | 30 + brewman/brewman/schemas/reports.py | 540 ------------------ brewman/brewman/schemas/role.py | 50 ++ brewman/brewman/schemas/stock_movement.py | 47 ++ brewman/brewman/schemas/trial_balance.py | 34 ++ brewman/brewman/schemas/unposted.py | 31 + brewman/brewman/schemas/user.py | 53 ++ brewman/brewman/schemas/voucher.py | 10 +- overlord/src/app/core/product.ts | 6 +- .../product-detail.component.ts | 5 +- .../product-list/product-list-datasource.ts | 7 +- .../product-list/product-list.component.html | 4 +- 83 files changed, 1669 insertions(+), 1430 deletions(-) create mode 100644 brewman/brewman/schemas/account.py create mode 100644 brewman/brewman/schemas/account_type.py delete mode 100644 brewman/brewman/schemas/auth.py create mode 100644 brewman/brewman/schemas/balance_sheet.py create mode 100644 brewman/brewman/schemas/cash_flow.py create mode 100644 brewman/brewman/schemas/client.py create mode 100644 brewman/brewman/schemas/closing_stock.py create mode 100644 brewman/brewman/schemas/cost_centre.py create mode 100644 brewman/brewman/schemas/daybook.py create mode 100644 brewman/brewman/schemas/db_setting.py create mode 100644 brewman/brewman/schemas/ledger.py create mode 100644 brewman/brewman/schemas/login_history.py delete mode 100644 brewman/brewman/schemas/master.py create mode 100644 brewman/brewman/schemas/net_transactions.py create mode 100644 brewman/brewman/schemas/permission.py create mode 100644 brewman/brewman/schemas/product.py create mode 100644 brewman/brewman/schemas/product_group.py create mode 100644 brewman/brewman/schemas/product_ledger.py create mode 100644 brewman/brewman/schemas/profit_loss.py create mode 100644 brewman/brewman/schemas/purchase_entries.py create mode 100644 brewman/brewman/schemas/purchases.py create mode 100644 brewman/brewman/schemas/raw_material_cost.py create mode 100644 brewman/brewman/schemas/recipe.py delete mode 100644 brewman/brewman/schemas/reports.py create mode 100644 brewman/brewman/schemas/role.py create mode 100644 brewman/brewman/schemas/stock_movement.py create mode 100644 brewman/brewman/schemas/trial_balance.py create mode 100644 brewman/brewman/schemas/unposted.py create mode 100644 brewman/brewman/schemas/user.py diff --git a/brewman/brewman/core/security.py b/brewman/brewman/core/security.py index afb45a41..fc846dad 100644 --- a/brewman/brewman/core/security.py +++ b/brewman/brewman/core/security.py @@ -17,7 +17,7 @@ from ..models.auth import Client from ..models.auth import User as UserModel # to get a string like this run: -from ..schemas.auth import UserToken +from ..schemas.user import UserToken oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token", scopes={}) @@ -69,29 +69,20 @@ def get_user(username: str, id_: str, locked_out: bool, scopes: List[str]) -> Us ) -def authenticate_user( - username: str, password: str, client_id: Optional[int], otp: int, db: Session -) -> Optional[UserModel]: +def authenticate_user(username: str, password: str, db: Session) -> Optional[UserModel]: user = UserModel.auth(username, password, db) return user -def client_allowed(user: UserModel, client_id: int, otp: Optional[int] = None, db: Session = None) -> (bool, int): +def client_allowed(user: UserModel, client_id: int, otp: Optional[int] = None, db: Session = None) -> (bool, Client): client = db.query(Client).filter(Client.code == client_id).first() if client_id else None - allowed = "clients" in set([p.name.replace(" ", "-").lower() for r in user.roles for p in r.permissions]) - if allowed: - return True, 0 - elif client is None: + if client is None: client = Client.create(db) - return False, client.code - elif client.enabled: - return True, client.code - elif client.otp == otp: - client.otp = None + allowed = "clients" in set([p.name.replace(" ", "-").lower() for r in user.roles for p in r.permissions]) + if not client.enabled and otp is not None and client.otp == otp: client.enabled = True - return True, client.code - else: - return False, client.code + client.otp = None + return allowed or client.enabled, client async def get_current_user( diff --git a/brewman/brewman/core/session.py b/brewman/brewman/core/session.py index 13abcd87..995ffff9 100644 --- a/brewman/brewman/core/session.py +++ b/brewman/brewman/core/session.py @@ -1,35 +1,35 @@ from datetime import date, timedelta -def get_date(session) -> str: +def get_date(session: dict) -> str: if "date" not in session: session["date"] = date.today().strftime("%d-%b-%Y") return session["date"] -def set_date(date_, session): +def set_date(date_: str, session: dict) -> str: session["date"] = date_ return session["date"] -def get_start_date(session): +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"] -def get_finish_date(session): +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"] -def set_period(start, finish, session): +def set_period(start, finish, session: dict): session["start"] = start if isinstance(start, str) else start.strftime("%d-%b-%Y") session["finish"] = finish if isinstance(finish, str) else finish.strftime("%d-%b-%Y") -def get_first_day(dt, d_years=0, d_months=0): +def get_first_day(dt: date, d_years=0, d_months=0) -> date: # d_years, d_months are "deltas" to apply to dt y, m = dt.year + d_years, dt.month + d_months a, m = divmod(m - 1, 12) diff --git a/brewman/brewman/models/auth.py b/brewman/brewman/models/auth.py index 443b6340..9eb6c362 100644 --- a/brewman/brewman/models/auth.py +++ b/brewman/brewman/models/auth.py @@ -7,7 +7,7 @@ from hashlib import md5 from sqlalchemy import Boolean, Column, DateTime, Integer, Unicode, UniqueConstraint from sqlalchemy.dialects.postgresql import UUID -from sqlalchemy.orm import relationship, synonym +from sqlalchemy.orm import Session, relationship, synonym from sqlalchemy.schema import ForeignKey, Table from .meta import Base @@ -46,12 +46,12 @@ class Client(Base): self.id = id_ @classmethod - def create(cls, dbsession): + def create(cls, db: Session): client_code = random.randint(1000, 9999) otp = random.randint(1000, 9999) name = "".join(random.choice(string.ascii_uppercase + string.digits) for x in range(6)) client = Client(client_code, name, False, otp) - dbsession.add(client) + db.add(client) return client diff --git a/brewman/brewman/routers/account.py b/brewman/brewman/routers/account.py index f558c4ff..57af26ee 100644 --- a/brewman/brewman/routers/account.py +++ b/brewman/brewman/routers/account.py @@ -4,7 +4,7 @@ from datetime import datetime from decimal import Decimal from typing import List -import brewman.schemas.master as schemas +import brewman.schemas.account as schemas from fastapi import APIRouter, Depends, HTTPException, Security, status from sqlalchemy import func @@ -15,7 +15,7 @@ from ..core.security import get_current_active_user as get_user from ..db.session import SessionLocal from ..models.master import Account, AccountBase, AccountType, CostCentre from ..models.voucher import Journal, Voucher, VoucherType -from ..schemas.auth import UserToken +from ..schemas.user import UserToken router = APIRouter() @@ -93,12 +93,12 @@ def update( raise -@router.delete("/{id_}", response_model=schemas.AccountIn) +@router.delete("/{id_}", response_model=schemas.AccountBlank) def delete( id_: uuid.UUID, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["accounts"]), -) -> schemas.AccountIn: +) -> schemas.AccountBlank: account: Account = db.query(Account).filter(Account.id == id_).first() can_delete, reason = account.can_delete("advanced-delete" in user.permissions) if can_delete: @@ -113,11 +113,11 @@ def delete( ) -@router.get("", response_model=schemas.AccountIn) +@router.get("", response_model=schemas.AccountBlank) def show_blank( db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["accounts"]), -) -> schemas.AccountIn: +) -> schemas.AccountBlank: return account_blank() @@ -199,18 +199,19 @@ def account_info(item: Account) -> schemas.Account: ) -def account_blank() -> schemas.AccountIn: - return schemas.AccountIn( +def account_blank() -> schemas.AccountBlank: + return schemas.AccountBlank( name="", type=AccountType.by_name("Creditors").id, isActive=True, isReconcilable=False, isStarred=False, costCentre=CostCentre.overall(), + isFixture=False, ) -def delete_with_data(account: Account, db: Session): +def delete_with_data(account: Account, db: Session) -> None: suspense_account = db.query(Account).filter(Account.id == Account.suspense()).first() query: List[Voucher] = ( db.query(Voucher) diff --git a/brewman/brewman/routers/account_types.py b/brewman/brewman/routers/account_types.py index 88c02f4c..08d69f77 100644 --- a/brewman/brewman/routers/account_types.py +++ b/brewman/brewman/routers/account_types.py @@ -1,17 +1,17 @@ from typing import List -import brewman.schemas.master as schemas +import brewman.schemas.account_type as schemas from fastapi import APIRouter, Depends from ..core.security import get_current_active_user as get_user from ..models.master import AccountType -from ..schemas.auth import UserToken +from ..schemas.user import UserToken router = APIRouter() @router.get("", response_model=List[schemas.AccountType]) -def account_type_list(user: UserToken = Depends(get_user)): +def account_type_list(user: UserToken = Depends(get_user)) -> List[schemas.AccountType]: return [schemas.AccountType(id=item.id, name=item.name) for item in AccountType.list()] diff --git a/brewman/brewman/routers/attendance.py b/brewman/brewman/routers/attendance.py index ce8ae89c..0ed19048 100644 --- a/brewman/brewman/routers/attendance.py +++ b/brewman/brewman/routers/attendance.py @@ -13,7 +13,7 @@ from ..db.session import SessionLocal from ..models.master import Employee from ..models.voucher import Attendance from ..routers.fingerprint import get_prints -from ..schemas.auth import UserToken +from ..schemas.user import UserToken router = APIRouter() diff --git a/brewman/brewman/routers/attendance_types.py b/brewman/brewman/routers/attendance_types.py index cc6b63a4..d76dfcc1 100644 --- a/brewman/brewman/routers/attendance_types.py +++ b/brewman/brewman/routers/attendance_types.py @@ -2,7 +2,7 @@ from fastapi import APIRouter, Depends from ..core.security import get_current_active_user as get_user from ..models.master import AttendanceType -from ..schemas.auth import UserToken +from ..schemas.user import UserToken router = APIRouter() diff --git a/brewman/brewman/routers/auth/client.py b/brewman/brewman/routers/auth/client.py index fee90668..772d901b 100644 --- a/brewman/brewman/routers/auth/client.py +++ b/brewman/brewman/routers/auth/client.py @@ -1,6 +1,6 @@ import uuid -import brewman.schemas.auth as schemas +import brewman.schemas.client as schemas from fastapi import APIRouter, Depends, HTTPException, Security, status from sqlalchemy import desc @@ -10,7 +10,7 @@ from sqlalchemy.orm import Session from ...core.security import get_current_active_user as get_user from ...db.session import SessionLocal from ...models.auth import Client, LoginHistory -from ...schemas.auth import UserToken +from ...schemas.user import UserToken router = APIRouter() diff --git a/brewman/brewman/routers/auth/role.py b/brewman/brewman/routers/auth/role.py index d32c8897..20038ba8 100644 --- a/brewman/brewman/routers/auth/role.py +++ b/brewman/brewman/routers/auth/role.py @@ -1,8 +1,8 @@ import uuid -from typing import List, Optional +from typing import List -import brewman.schemas.auth as schemas +import brewman.schemas.role as schemas from fastapi import APIRouter, Depends, HTTPException, Security, status from sqlalchemy.exc import SQLAlchemyError @@ -11,7 +11,7 @@ from sqlalchemy.orm import Session from ...core.security import get_current_active_user as get_user from ...db.session import SessionLocal from ...models.auth import Permission, Role -from ...schemas.auth import UserToken +from ...schemas.user import UserToken router = APIRouter() @@ -31,7 +31,7 @@ def save( data: schemas.RoleIn, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["users"]), -): +) -> schemas.Role: try: item = Role(data.name) db.add(item) @@ -55,7 +55,7 @@ def update( data: schemas.RoleIn, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["users"]), -): +) -> schemas.Role: try: item: Role = db.query(Role).filter(Role.id == id_).first() item.name = data.name @@ -73,7 +73,7 @@ def update( raise -def add_permissions(role: Role, permissions: List[schemas.PermissionItem], db): +def add_permissions(role: Role, permissions: List[schemas.PermissionItem], db: Session) -> None: for permission in permissions: gp = [p for p in role.permissions if p.id == permission.id_] gp = None if len(gp) == 0 else gp[0] @@ -83,12 +83,12 @@ def add_permissions(role: Role, permissions: List[schemas.PermissionItem], db): role.permissions.remove(gp) -@router.delete("/{id_}") +@router.delete("/{id_}", response_model=schemas.RoleBlank) def delete( id_: uuid.UUID, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["users"]), -): +) -> schemas.RoleBlank: try: item: Role = db.query(Role).filter(Role.id == id_).first() if item is None: @@ -106,25 +106,25 @@ def delete( raise -@router.get("") +@router.get("", response_model=schemas.RoleBlank) def show_blank( db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["users"]), -): - return role_info(None, db) +) -> schemas.RoleBlank: + return role_blank(db) @router.get("/list", response_model=List[schemas.RoleList]) async def show_list( db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["users"]), -): +) -> List[schemas.RoleList]: return [ - { - "id": item.id, - "name": item.name, - "permissions": [p.name for p in sorted(item.permissions, key=lambda p: p.name)], - } + schemas.RoleList( + id=item.id, + name=item.name, + permissions=[p.name for p in sorted(item.permissions, key=lambda p: p.name)], + ) for item in db.query(Role).order_by(Role.name).all() ] @@ -134,7 +134,7 @@ def show_id( id_: uuid.UUID, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["users"]), -): +) -> schemas.Role: item: Role = db.query(Role).filter(Role.id == id_).first() if item is None: raise HTTPException( @@ -144,25 +144,26 @@ def show_id( return role_info(item, db) -def role_info(item: Optional[Role], db): - if item is None: - return { - "name": "", - "permissions": [ - {"id": p.id, "name": p.name, "enabled": False} - for p in db.query(Permission).order_by(Permission.name).all() - ], - } - else: - return { - "id": item.id, - "name": item.name, - "permissions": [ - { - "id": p.id, - "name": p.name, - "enabled": True if p in item.permissions else False, - } - for p in db.query(Permission).order_by(Permission.name).all() - ], - } +def role_info(item: Role, db: Session) -> schemas.Role: + return schemas.Role( + id=item.id, + name=item.name, + permissions=[ + schemas.PermissionItem( + id=p.id, + name=p.name, + enabled=True if p in item.permissions else False, + ) + for p in db.query(Permission).order_by(Permission.name).all() + ], + ) + + +def role_blank(db: Session) -> schemas.RoleBlank: + return schemas.RoleBlank( + name="", + permissions=[ + schemas.PermissionItem(id=p.id, name=p.name, enabled=False) + for p in db.query(Permission).order_by(Permission.name).all() + ], + ) diff --git a/brewman/brewman/routers/auth/user.py b/brewman/brewman/routers/auth/user.py index 76ad54a7..b4e66ff8 100644 --- a/brewman/brewman/routers/auth/user.py +++ b/brewman/brewman/routers/auth/user.py @@ -1,8 +1,8 @@ import uuid -from typing import List, Optional +from typing import List -import brewman.schemas.auth as schemas +import brewman.schemas.user as schemas from fastapi import APIRouter, Depends, HTTPException, Security, status from sqlalchemy.exc import SQLAlchemyError @@ -11,7 +11,7 @@ from sqlalchemy.orm import Session from ...core.security import get_current_active_user as get_user from ...db.session import SessionLocal from ...models.auth import Role, User -from ...schemas.auth import UserToken +from ...schemas.user import UserToken router = APIRouter() @@ -31,7 +31,7 @@ def save( data: schemas.UserIn, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["users"]), -): +) -> schemas.User: try: item = User(name=data.name, password=data.password, locked_out=data.locked_out) db.add(item) @@ -53,7 +53,7 @@ def save( def show_me( db: Session = Depends(get_db), user: UserToken = Depends(get_user), -): +) -> schemas.User: item = db.query(User).filter(User.id == user.id_).first() return user_info(item, db, user) @@ -63,7 +63,7 @@ def update_me( data: schemas.UserIn, db: Session = Depends(get_db), user: UserToken = Depends(get_user), -): +) -> schemas.User: try: item: User = db.query(User).filter(User.id == user.id_).first() if "users" in user.permissions: @@ -91,7 +91,7 @@ def update( data: schemas.UserIn, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["users"]), -): +) -> schemas.User: try: item: User = db.query(User).filter(User.id == id_).first() item.name = data.name @@ -112,7 +112,7 @@ def update( raise -def add_roles(user: User, roles: List[schemas.RoleItem], db: Session): +def add_roles(user: User, roles: List[schemas.RoleItem], db: Session) -> None: for role in roles: ug = [g for g in user.roles if g.id == role.id_] ug = None if len(ug) == 0 else ug[0] @@ -150,21 +150,21 @@ def show_blank( db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["users"]), ): - return user_info(None, db, user) + return user_blank(db) @router.get("/list", response_model=List[schemas.UserList]) async def show_list( db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["users"]), -): +) -> List[schemas.UserList]: return [ - { - "id": item.id, - "name": item.name, - "lockedOut": item.locked_out, - "roles": [p.name for p in sorted(item.roles, key=lambda p: p.name)], - } + schemas.UserList( + id=item.id, + name=item.name, + lockedOut=item.locked_out, + roles=[p.name for p in sorted(item.roles, key=lambda p: p.name)], + ) for item in db.query(User).order_by(User.name).all() ] @@ -182,32 +182,33 @@ def show_id( id_: uuid.UUID, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["users"]), -): +) -> schemas.User: item = db.query(User).filter(User.id == id_).first() return user_info(item, db, user) -def user_info(item: Optional[User], db: Session, user: UserToken): - if item is None: - return { - "name": "", - "lockedOut": False, - "roles": [{"id": r.id, "name": r.name, "enabled": False} for r in db.query(Role).order_by(Role.name).all()], - } - else: - return { - "id": item.id, - "name": item.name, - "password": "", - "lockedOut": item.locked_out, - "roles": [ - { - "id": r.id, - "name": r.name, - "enabled": True if r in item.roles else False, - } - for r in db.query(Role).order_by(Role.name).all() - ] - if "advanced-delete" in user.permissions - else [], - } +def user_info(item: User, db: Session, user: UserToken) -> schemas.User: + return schemas.User( + id=item.id, + name=item.name, + password="", + lockedOut=item.locked_out, + roles=[ + schemas.RoleItem( + id=r.id, + name=r.name, + enabled=True if r in item.roles else False, + ) + for r in db.query(Role).order_by(Role.name).all() + ] + if "advanced-delete" in user.permissions + else [], + ) + + +def user_blank(db: Session) -> schemas.UserBlank: + return schemas.UserBlank( + name="", + lockedOut=False, + roles=[schemas.RoleItem(id=r.id, name=r.name, enabled=False) for r in db.query(Role).order_by(Role.name).all()], + ) diff --git a/brewman/brewman/routers/batch.py b/brewman/brewman/routers/batch.py index 72fd0b61..9e89b4c6 100644 --- a/brewman/brewman/routers/batch.py +++ b/brewman/brewman/routers/batch.py @@ -6,7 +6,7 @@ from sqlalchemy.orm import Session from ..core.security import get_current_active_user as get_user from ..db.session import SessionLocal from ..models.voucher import Batch -from ..schemas.auth import UserToken +from ..schemas.user import UserToken router = APIRouter() diff --git a/brewman/brewman/routers/cost_centre.py b/brewman/brewman/routers/cost_centre.py index 206680c2..7ee712be 100644 --- a/brewman/brewman/routers/cost_centre.py +++ b/brewman/brewman/routers/cost_centre.py @@ -1,8 +1,8 @@ import uuid -from typing import List, Optional +from typing import List -import brewman.schemas.master as schemas +import brewman.schemas.cost_centre as schemas from fastapi import APIRouter, Depends, HTTPException, Security, status from sqlalchemy.exc import SQLAlchemyError @@ -11,7 +11,7 @@ from sqlalchemy.orm import Session from ..core.security import get_current_active_user as get_user from ..db.session import SessionLocal from ..models.master import CostCentre -from ..schemas.auth import UserToken +from ..schemas.user import UserToken router = APIRouter() @@ -31,7 +31,7 @@ def save( data: schemas.CostCentreIn, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["cost-centres"]), -): +) -> schemas.CostCentre: try: item = CostCentre(name=data.name) db.add(item) @@ -54,7 +54,7 @@ def update( data: schemas.CostCentreIn, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["cost-centres"]), -): +) -> schemas.CostCentre: try: item = db.query(CostCentre).filter(CostCentre.id == id_).first() if item.is_fixture: @@ -76,7 +76,7 @@ def update( raise -@router.delete("/{id_}") +@router.delete("/{id_}", response_model=schemas.CostCentreBlank) def delete( id_: uuid.UUID, db: Session = Depends(get_db), @@ -90,35 +90,31 @@ def delete( status_code=status.HTTP_404_NOT_FOUND, detail="Cost Centre not found", ) - elif item.is_fixture: + if item.is_fixture: raise HTTPException( status_code=status.HTTP_423_LOCKED, detail=f"{item.name} is a fixture and cannot be edited or deleted.", ) - else: - raise HTTPException( - status_code=status.HTTP_501_NOT_IMPLEMENTED, - detail="Cost Centre deletion not implemented", - ) + raise HTTPException( + status_code=status.HTTP_501_NOT_IMPLEMENTED, + detail="Cost Centre deletion not implemented", + ) except Exception: db.rollback() raise -@router.get("") +@router.get("", response_model=schemas.CostCentreBlank) def show_blank( db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["cost-centres"]), -): - return cost_centre_info(None) +) -> schemas.CostCentreBlank: + return cost_centre_blank() @router.get("/list", response_model=List[schemas.CostCentre]) -async def show_list(db: Session = Depends(get_db), user: UserToken = Depends(get_user)): - return [ - {"id": item.id, "name": item.name, "isFixture": item.is_fixture} - for item in db.query(CostCentre).order_by(CostCentre.name).all() - ] +async def show_list(db: Session = Depends(get_db), user: UserToken = Depends(get_user)) -> List[schemas.CostCentre]: + return [cost_centre_info(item) for item in db.query(CostCentre).order_by(CostCentre.name).all()] @router.get("/{id_}", response_model=schemas.CostCentre) @@ -126,20 +122,21 @@ def show_id( id_: uuid.UUID, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["cost-centres"]), -): +) -> schemas.CostCentre: item = db.query(CostCentre).filter(CostCentre.id == id_).first() return cost_centre_info(item) -def cost_centre_info(item: Optional[CostCentre]): - if item is None: - return { - "name": "", - "isFixture": False, - } - else: - return { - "id": item.id, - "name": item.name, - "isFixture": item.is_fixture, - } +def cost_centre_info(item: CostCentre) -> schemas.CostCentre: + return schemas.CostCentre( + id=item.id, + name=item.name, + isFixture=item.is_fixture, + ) + + +def cost_centre_blank() -> schemas.CostCentreBlank: + return schemas.CostCentreBlank( + name="", + isFixture=False, + ) diff --git a/brewman/brewman/routers/credit_salary.py b/brewman/brewman/routers/credit_salary.py index 8486f8bb..c48fcdd0 100644 --- a/brewman/brewman/routers/credit_salary.py +++ b/brewman/brewman/routers/credit_salary.py @@ -10,7 +10,7 @@ from ..core.session import get_first_day, get_last_day from ..db.session import SessionLocal from ..models.master import Account, AttendanceType, Employee from ..models.voucher import Attendance, Journal, Voucher, VoucherType -from ..schemas.auth import UserToken +from ..schemas.user import UserToken router = APIRouter() diff --git a/brewman/brewman/routers/db_integrity.py b/brewman/brewman/routers/db_integrity.py index d5a24d92..cb8a1ac4 100644 --- a/brewman/brewman/routers/db_integrity.py +++ b/brewman/brewman/routers/db_integrity.py @@ -5,7 +5,7 @@ from sqlalchemy.orm import Session from ..core.security import get_current_active_user as get_user from ..db.session import SessionLocal from ..models import Attendance -from ..schemas.auth import UserToken +from ..schemas.user import UserToken router = APIRouter() diff --git a/brewman/brewman/routers/employee.py b/brewman/brewman/routers/employee.py index 1de1981a..5b2cbe7d 100644 --- a/brewman/brewman/routers/employee.py +++ b/brewman/brewman/routers/employee.py @@ -1,9 +1,9 @@ import uuid + from datetime import datetime from typing import List import brewman.schemas.employee as schemas -import brewman.schemas.master as master_schemas from fastapi import APIRouter, Depends, HTTPException, Security, status from sqlalchemy import desc @@ -14,7 +14,7 @@ from ..core.security import get_current_active_user as get_user from ..db.session import SessionLocal from ..models.master import Account, AccountBase, CostCentre, Employee from ..models.voucher import Journal, Voucher, VoucherType -from ..schemas.auth import UserToken +from ..schemas.user import UserToken router = APIRouter() @@ -184,7 +184,7 @@ def employee_info(employee: Employee) -> schemas.Employee: points=employee.points, joiningDate=employee.joining_date, leavingDate=None if employee.is_active else employee.leaving_date, - costCentre=master_schemas.CostCentreLink( + costCentre=schemas.CostCentreLink( id=employee.cost_centre_id, name=employee.cost_centre.name, ), diff --git a/brewman/brewman/routers/employee_attendance.py b/brewman/brewman/routers/employee_attendance.py index 69575306..4e16d9f9 100644 --- a/brewman/brewman/routers/employee_attendance.py +++ b/brewman/brewman/routers/employee_attendance.py @@ -13,7 +13,7 @@ from ..db.session import SessionLocal from ..models.master import Employee from ..models.voucher import Attendance from ..routers.fingerprint import get_prints -from ..schemas.auth import UserToken +from ..schemas.user import UserToken from .attendance import date_range diff --git a/brewman/brewman/routers/employee_benefit.py b/brewman/brewman/routers/employee_benefit.py index 2baa26c9..1d808130 100644 --- a/brewman/brewman/routers/employee_benefit.py +++ b/brewman/brewman/routers/employee_benefit.py @@ -17,7 +17,7 @@ from ..db.session import SessionLocal from ..models import AccountBase, Employee from ..models.validations import check_journals_are_valid from ..models.voucher import EmployeeBenefit, Journal, Voucher, VoucherType -from ..schemas.auth import UserToken +from ..schemas.user import UserToken from .db_image import save_files, update_files from .voucher import ( blank_voucher, diff --git a/brewman/brewman/routers/fingerprint.py b/brewman/brewman/routers/fingerprint.py index c4da18b6..9a429394 100644 --- a/brewman/brewman/routers/fingerprint.py +++ b/brewman/brewman/routers/fingerprint.py @@ -15,7 +15,7 @@ from ..db.session import SessionLocal from ..models.master import Employee from ..models.voucher import Fingerprint from ..routers import get_lock_info -from ..schemas.auth import UserToken +from ..schemas.user import UserToken router = APIRouter() diff --git a/brewman/brewman/routers/incentive.py b/brewman/brewman/routers/incentive.py index c0960627..4b0b2414 100644 --- a/brewman/brewman/routers/incentive.py +++ b/brewman/brewman/routers/incentive.py @@ -18,7 +18,7 @@ from ..db.session import SessionLocal from ..models import Account, AttendanceType, Employee from ..models.validations import check_journals_are_valid from ..models.voucher import Attendance, Incentive, Journal, Voucher, VoucherType -from ..schemas.auth import UserToken +from ..schemas.user import UserToken from .voucher import ( blank_voucher, check_voucher_edit_allowed, diff --git a/brewman/brewman/routers/issue.py b/brewman/brewman/routers/issue.py index 6fc2842b..52a67b08 100644 --- a/brewman/brewman/routers/issue.py +++ b/brewman/brewman/routers/issue.py @@ -17,7 +17,7 @@ from ..db.session import SessionLocal from ..models import AccountBase, CostCentre from ..models.validations import check_inventories_are_valid, check_journals_are_valid from ..models.voucher import Batch, Inventory, Journal, Voucher, VoucherType -from ..schemas.auth import UserToken +from ..schemas.user import UserToken from .db_image import save_files, update_files from .voucher import ( blank_voucher, diff --git a/brewman/brewman/routers/issue_grid.py b/brewman/brewman/routers/issue_grid.py index 09da3675..a56446dd 100644 --- a/brewman/brewman/routers/issue_grid.py +++ b/brewman/brewman/routers/issue_grid.py @@ -7,7 +7,7 @@ from sqlalchemy.orm.util import aliased from ..core.security import get_current_active_user as get_user from ..db.session import SessionLocal from ..models.voucher import Journal, Voucher, VoucherType -from ..schemas.auth import UserToken +from ..schemas.user import UserToken router = APIRouter() diff --git a/brewman/brewman/routers/journal.py b/brewman/brewman/routers/journal.py index 1407cea6..5c4c97b6 100644 --- a/brewman/brewman/routers/journal.py +++ b/brewman/brewman/routers/journal.py @@ -16,7 +16,7 @@ from ..db.session import SessionLocal from ..models import AccountBase from ..models.validations import check_journals_are_valid from ..models.voucher import Journal, Voucher, VoucherType -from ..schemas.auth import UserToken +from ..schemas.user import UserToken from .db_image import save_files, update_files from .voucher import ( blank_voucher, diff --git a/brewman/brewman/routers/lock_information.py b/brewman/brewman/routers/lock_information.py index 58130b44..687e7be5 100644 --- a/brewman/brewman/routers/lock_information.py +++ b/brewman/brewman/routers/lock_information.py @@ -4,8 +4,8 @@ from sqlalchemy.orm import Session from ..core.security import get_current_active_user as get_user from ..db.session import SessionLocal from ..models.master import DbSetting -from ..schemas.auth import UserToken from ..schemas.settings import LockInformation +from ..schemas.user import UserToken router = APIRouter() diff --git a/brewman/brewman/routers/login.py b/brewman/brewman/routers/login.py index 4aa6332f..42ccb9fe 100644 --- a/brewman/brewman/routers/login.py +++ b/brewman/brewman/routers/login.py @@ -1,4 +1,4 @@ -from datetime import timedelta +from datetime import datetime, timedelta from fastapi import ( APIRouter, @@ -12,6 +12,7 @@ from fastapi import ( ) from fastapi.responses import JSONResponse from fastapi.security import OAuth2PasswordRequestForm +from sqlalchemy import and_ from sqlalchemy.orm import Session from .. import __version__ @@ -24,7 +25,8 @@ from ..core.security import ( get_current_active_user, ) from ..db.session import SessionLocal -from ..schemas.auth import UserToken +from ..models import Client, LoginHistory +from ..schemas.user import UserToken router = APIRouter() @@ -47,24 +49,33 @@ async def login_for_access_token( otp: int = Form(None), db: Session = Depends(get_db), ): - user = authenticate_user(form_data.username, form_data.password, client_id, otp, db) + user = authenticate_user(form_data.username, form_data.password, db) if not user: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Incorrect username or password", headers={"WWW-Authenticate": "Bearer"}, ) - allowed, c_id = client_allowed(user, client_id, otp, db) + allowed, client = client_allowed(user, client_id, otp, db) + db.flush() + if allowed: + history = LoginHistory(user.id, client.id) + db.add(history) + db.execute(LoginHistory.__table__.delete(LoginHistory.date < datetime.utcnow() - timedelta(days=30))) + db.execute( + Client.__table__.delete( + and_(Client.creation_date < datetime.utcnow() - timedelta(days=3), Client.enabled == False) # noqa: E712 + ) + ) db.commit() - if c_id and c_id != client_id: - response.set_cookie(key="client_id", value=str(c_id), max_age=10 * 365 * 24 * 60 * 60) + response.set_cookie(key="client_id", value=str(client.id), max_age=10 * 365 * 24 * 60 * 60) if not allowed: not_allowed_response = JSONResponse( status_code=status.HTTP_401_UNAUTHORIZED, headers={"WWW-Authenticate": "Bearer"}, content={"detail": "Client is not registered"}, ) - not_allowed_response.set_cookie(key="client_id", value=str(c_id), max_age=10 * 365 * 24 * 60 * 60) + not_allowed_response.set_cookie(key="client_id", value=str(client.id), max_age=10 * 365 * 24 * 60 * 60) return not_allowed_response access_token_expires = timedelta(minutes=settings.JWT_TOKEN_EXPIRE_MINUTES) access_token = create_access_token( diff --git a/brewman/brewman/routers/maintenance.py b/brewman/brewman/routers/maintenance.py index d34098b6..7665b804 100644 --- a/brewman/brewman/routers/maintenance.py +++ b/brewman/brewman/routers/maintenance.py @@ -5,8 +5,8 @@ from ..core.security import get_current_active_user as get_user from ..db.session import SessionLocal from ..models import User from ..models.master import DbSetting -from ..schemas.auth import UserToken from ..schemas.settings import Maintenance +from ..schemas.user import UserToken router = APIRouter() diff --git a/brewman/brewman/routers/product.py b/brewman/brewman/routers/product.py index 0bcf4b67..1525fc8d 100644 --- a/brewman/brewman/routers/product.py +++ b/brewman/brewman/routers/product.py @@ -1,8 +1,8 @@ import uuid -from typing import Optional +from typing import List -import brewman.schemas.master as schemas +import brewman.schemas.product as schemas from fastapi import APIRouter, Depends, HTTPException, Security, status from sqlalchemy import desc @@ -13,7 +13,7 @@ from ..core.security import get_current_active_user as get_user from ..db.session import SessionLocal from ..models.master import Account, Product from ..models.voucher import Batch, Inventory, Voucher, VoucherType -from ..schemas.auth import UserToken +from ..schemas.user import UserToken router = APIRouter() @@ -33,7 +33,7 @@ def save( data: schemas.ProductIn, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["products"]), -): +) -> schemas.Product: try: item = Product( name=data.name, @@ -50,7 +50,7 @@ def save( is_sold=data.is_sold, ).create(db) db.commit() - return product_info(item.id, db) + return product_info(item) except SQLAlchemyError as e: db.rollback() raise HTTPException( @@ -68,7 +68,7 @@ def update( data: schemas.ProductIn, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["products"]), -): +) -> schemas.Product: try: item: Product = db.query(Product).filter(Product.id == id_).first() if item.is_fixture: @@ -89,7 +89,7 @@ def update( item.is_purchased = data.is_purchased item.is_sold = data.is_sold db.commit() - return product_info(item.id, db) + return product_info(item) except SQLAlchemyError as e: db.rollback() raise HTTPException( @@ -101,54 +101,39 @@ def update( raise -@router.delete("/{id_}") +@router.delete("/{id_}", response_model=schemas.ProductBlank) def delete( id_: uuid.UUID, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["products"]), -): +) -> schemas.ProductBlank: item: Product = db.query(Product).filter(Product.id == id_).first() can_delete, reason = item.can_delete("advanced-delete" in user.permissions) if can_delete: delete_with_data(item, db) db.commit() - return product_info(None, db) + return product_blank() else: - db.abort() + db.rollback() raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Cannot delete account because {reason}", + detail=f"Cannot delete product because {reason}", ) -@router.get("") +@router.get("", response_model=schemas.ProductBlank) def show_blank( db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["products"]), -): - return product_info(None, db) +) -> schemas.ProductBlank: + return product_blank() -@router.get("/list") -def show_list(db: Session = Depends(get_db), user: UserToken = Depends(get_user)): +@router.get("/list", response_model=List[schemas.Product]) +def show_list(db: Session = Depends(get_db), user: UserToken = Depends(get_user)) -> List[schemas.Product]: return [ - { - "id": item.id, - "code": item.code, - "name": item.name, - "units": item.units, - "costPrice": item.price, - "salePrice": item.sale_price, - "productGroup": item.product_group.name, - "isActive": item.is_active, - "fraction": item.fraction, - "fractionUnits": item.fraction_units, - "isPurchased": item.is_purchased, - "isSold": item.is_sold, - "productYield": item.product_yield, - "isFixture": item.is_fixture, - } + product_info(item) for item in db.query(Product) .order_by(desc(Product.is_active)) .order_by(Product.product_group_id) @@ -191,47 +176,53 @@ async def show_term( return sorted(list_, key=lambda k: k["name"]) -@router.get("/{id_}") +@router.get("/{id_}", response_model=schemas.Product) def show_id( id_: uuid.UUID, db: Session = Depends(get_db), - user: UserToken = Security(get_user, scopes=["accounts"]), -): - return product_info(id_, db) + user: UserToken = Security(get_user, scopes=["products"]), +) -> schemas.Product: + item: Product = db.query(Product).filter(Product.id == id_).first() + return product_info(item) -def product_info(id_: Optional[uuid.UUID], db: Session): - if id_ is None: - product = { - "code": "(Auto)", - "productGroup": {}, - "isActive": True, - "isPurchased": True, - "isSold": False, - } - else: - product = db.query(Product).filter(Product.id == id_).first() - product = { - "id": product.id, - "code": product.code, - "name": product.name, - "units": product.units, - "fraction": product.fraction, - "fractionUnits": product.fraction_units, - "productYield": product.product_yield, - "price": product.price, - "salePrice": product.sale_price, - "isActive": product.is_active, - "isFixture": product.is_fixture, - "isPurchased": product.is_purchased, - "isSold": product.is_sold, - "productGroup": {"id": product.product_group_id}, - "account": {"id": product.account_id}, - } +def product_info(product: Product) -> schemas.Product: + product = schemas.Product( + id=product.id, + code=product.code, + name=product.name, + units=product.units, + fraction=product.fraction, + fractionUnits=product.fraction_units, + productYield=product.product_yield, + price=product.price, + salePrice=product.sale_price, + isActive=product.is_active, + isFixture=product.is_fixture, + isPurchased=product.is_purchased, + isSold=product.is_sold, + productGroup=schemas.ProductGroupLink(id=product.product_group.id, name=product.product_group.name), + ) return product -def delete_with_data(product: Product, db: Session): +def product_blank() -> schemas.ProductBlank: + return schemas.ProductBlank( + name="", + units="", + fraction=1, + fractionUnits="", + productYield=1, + price=0, + salePrice=0, + isActive=True, + isPurchased=True, + isSold=False, + isFixture=False, + ) + + +def delete_with_data(product: Product, db: Session) -> None: suspense_product = db.query(Product).filter(Product.id == Product.suspense()).first() suspense_batch = db.query(Batch).filter(Batch.id == Batch.suspense()).first() query = ( diff --git a/brewman/brewman/routers/product_group.py b/brewman/brewman/routers/product_group.py index 72a94d77..3ed0c3df 100644 --- a/brewman/brewman/routers/product_group.py +++ b/brewman/brewman/routers/product_group.py @@ -1,8 +1,8 @@ import uuid -from typing import List, Optional +from typing import List -import brewman.schemas.master as schemas +import brewman.schemas.product_group as schemas from fastapi import APIRouter, Depends, HTTPException, Security, status from sqlalchemy.exc import SQLAlchemyError @@ -11,7 +11,7 @@ from sqlalchemy.orm import Session from ..core.security import get_current_active_user as get_user from ..db.session import SessionLocal from ..models.master import ProductGroup -from ..schemas.auth import UserToken +from ..schemas.user import UserToken router = APIRouter() @@ -31,12 +31,12 @@ def save( data: schemas.ProductGroupIn, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["product-groups"]), -): +) -> schemas.ProductGroup: try: item = ProductGroup(name=data.name) db.add(item) db.commit() - return product_group_info(item, db) + return product_group_info(item) except SQLAlchemyError as e: db.rollback() raise HTTPException( @@ -54,7 +54,7 @@ def update( data: schemas.ProductGroupIn, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["product-groups"]), -): +) -> schemas.ProductGroup: try: item = db.query(ProductGroup).filter(ProductGroup.id == id_).first() if item.is_fixture: @@ -64,7 +64,7 @@ def update( ) item.name = data.name db.commit() - return product_group_info(item, db) + return product_group_info(item) except SQLAlchemyError as e: db.rollback() raise HTTPException( @@ -76,12 +76,12 @@ def update( raise -@router.delete("/{id_}") +@router.delete("/{id_}", response_model=schemas.ProductGroupBlank) def delete( id_: uuid.UUID, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["product-groups"]), -): +) -> schemas.ProductGroupBlank: try: item = db.query(ProductGroup).filter(ProductGroup.id == id_).first() @@ -105,20 +105,17 @@ def delete( raise -@router.get("") +@router.get("", response_model=schemas.ProductGroupBlank) def show_blank( db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["product-groups"]), -): - return product_group_info(None, db) +) -> schemas.ProductGroupBlank: + return product_group_blank() @router.get("/list", response_model=List[schemas.ProductGroup]) -async def show_list(db: Session = Depends(get_db), user: UserToken = Depends(get_user)): - return [ - {"id": item.id, "name": item.name, "isFixture": item.is_fixture} - for item in db.query(ProductGroup).order_by(ProductGroup.name).all() - ] +async def show_list(db: Session = Depends(get_db), user: UserToken = Depends(get_user)) -> List[schemas.ProductGroup]: + return [product_group_info(item) for item in db.query(ProductGroup).order_by(ProductGroup.name).all()] @router.get("/{id_}", response_model=schemas.ProductGroup) @@ -126,20 +123,21 @@ def show_id( id_: uuid.UUID, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["product-groups"]), -): +) -> schemas.ProductGroup: item = db.query(ProductGroup).filter(ProductGroup.id == id_).first() - return product_group_info(item, db) + return product_group_info(item) -def product_group_info(item: Optional[ProductGroup], db): - if item is None: - return { - "name": "", - "isFixture": False, - } - else: - return { - "id": item.id, - "name": item.name, - "isFixture": item.is_fixture, - } +def product_group_info(item: ProductGroup) -> schemas.ProductGroup: + return schemas.ProductGroup( + id=item.id, + name=item.name, + isFixture=item.is_fixture, + ) + + +def product_group_blank() -> schemas.ProductGroupBlank: + return schemas.ProductGroupBlank( + name="", + isFixture=False, + ) diff --git a/brewman/brewman/routers/purchase.py b/brewman/brewman/routers/purchase.py index 1faf5787..5a0bc659 100644 --- a/brewman/brewman/routers/purchase.py +++ b/brewman/brewman/routers/purchase.py @@ -18,7 +18,7 @@ from ..db.session import SessionLocal from ..models import AccountBase, Product from ..models.validations import check_inventories_are_valid, check_journals_are_valid from ..models.voucher import Batch, Inventory, Journal, Voucher, VoucherType -from ..schemas.auth import UserToken +from ..schemas.user import UserToken from .db_image import save_files, update_files from .voucher import ( blank_voucher, diff --git a/brewman/brewman/routers/purchase_return.py b/brewman/brewman/routers/purchase_return.py index cab85edf..10af6345 100644 --- a/brewman/brewman/routers/purchase_return.py +++ b/brewman/brewman/routers/purchase_return.py @@ -17,7 +17,7 @@ from ..db.session import SessionLocal from ..models import AccountBase from ..models.validations import check_inventories_are_valid, check_journals_are_valid from ..models.voucher import Batch, Inventory, Journal, Voucher, VoucherType -from ..schemas.auth import UserToken +from ..schemas.user import UserToken from .db_image import save_files, update_files from .voucher import ( blank_voucher, diff --git a/brewman/brewman/routers/rebase.py b/brewman/brewman/routers/rebase.py index d229154d..a8f06e72 100644 --- a/brewman/brewman/routers/rebase.py +++ b/brewman/brewman/routers/rebase.py @@ -13,7 +13,7 @@ from ..db.session import SessionLocal from ..models import Batch, EmployeeBenefit, Inventory, Journal, Voucher, VoucherType from ..models.master import Account, AccountBase, CostCentre, Employee from ..models.voucher import Attendance, DbImage, Fingerprint, Incentive -from ..schemas.auth import UserToken +from ..schemas.user import UserToken router = APIRouter() diff --git a/brewman/brewman/routers/reports/balance_sheet.py b/brewman/brewman/routers/reports/balance_sheet.py index 81886f3d..04b67859 100644 --- a/brewman/brewman/routers/reports/balance_sheet.py +++ b/brewman/brewman/routers/reports/balance_sheet.py @@ -1,6 +1,7 @@ from datetime import date, datetime +from typing import List -import brewman.schemas.reports as schemas +import brewman.schemas.balance_sheet as schemas from fastapi import APIRouter, Depends, Request, Security from sqlalchemy.orm import Session @@ -13,7 +14,7 @@ from ...models.master import AccountBase, AccountType from ...models.voucher import Journal, Voucher, VoucherType from ...routers.reports.closing_stock import get_closing_stock from ...routers.reports.profit_loss import get_accumulated_profit -from ...schemas.auth import UserToken +from ...schemas.user import UserToken router = APIRouter() @@ -32,8 +33,8 @@ def get_db() -> Session: def report_blank( request: Request, user: UserToken = Security(get_user, scopes=["balance-sheet"]), -): - return {"date": get_finish_date(request.session), "body": [], "footer": None} +) -> schemas.BalanceSheet: + return schemas.BalanceSheet(date=get_finish_date(request.session), body=[], footer=None) @router.get("/{date_}", response_model=schemas.BalanceSheet) @@ -42,13 +43,13 @@ def report_data( request: Request, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["balance-sheet"]), -): +) -> schemas.BalanceSheet: body, footer = build_balance_sheet(datetime.strptime(date_, "%d-%b-%Y"), db) set_period(get_start_date(request.session), date_, request.session) - return {"date": date_, "body": body, "footer": footer} + return schemas.BalanceSheet(date=date_, body=body, footer=footer) -def build_balance_sheet(date_: date, db: Session): +def build_balance_sheet(date_: date, db: Session) -> (List[schemas.BalanceSheetItem], schemas.BalanceSheetItem): type_list = [i.id for i in AccountType.list() if i.balance_sheet] report = [] groups = dict() @@ -122,5 +123,5 @@ def build_balance_sheet(date_: date, db: Session): for item in groups.values(): report.append(item) - footer = {"name": "Total", "amount": round(total_amount, 2), "order": 100000} + footer = schemas.BalanceSheetItem(name="Total", amount=round(total_amount, 2), order=100000) return sorted(report, key=lambda d: d["order"]), footer diff --git a/brewman/brewman/routers/reports/cash_flow.py b/brewman/brewman/routers/reports/cash_flow.py index dccb4c6b..b7e8db87 100644 --- a/brewman/brewman/routers/reports/cash_flow.py +++ b/brewman/brewman/routers/reports/cash_flow.py @@ -1,6 +1,6 @@ import datetime -import brewman.schemas.reports as schemas +import brewman.schemas.cash_flow as schemas from fastapi import APIRouter, Depends, Request, Security from sqlalchemy.orm import Session @@ -12,7 +12,7 @@ from ...core.session import get_finish_date, get_start_date, set_period from ...db.session import SessionLocal from ...models.master import AccountBase, AccountType from ...models.voucher import Journal, Voucher, VoucherType -from ...schemas.auth import UserToken +from ...schemas.user import UserToken router = APIRouter() @@ -31,13 +31,13 @@ def get_db() -> Session: def report_blank( request: Request, user: UserToken = Security(get_user, scopes=["cash-flow"]), -): - return { - "startDate": get_start_date(request.session), - "finishDate": get_finish_date(request.session), - "body": {"operating": [], "investing": [], "financing": [], "details": []}, - "footer": None, - } +) -> schemas.CashFlow: + return schemas.CashFlow( + startDate=get_start_date(request.session), + finishDate=get_finish_date(request.session), + body=schemas.CashFlowBody(operating=[], investing=[], financing=[], details=[]), + footer=None, + ) @router.get("/data") diff --git a/brewman/brewman/routers/reports/closing_stock.py b/brewman/brewman/routers/reports/closing_stock.py index e2a49697..56f72593 100644 --- a/brewman/brewman/routers/reports/closing_stock.py +++ b/brewman/brewman/routers/reports/closing_stock.py @@ -1,6 +1,8 @@ from datetime import date, datetime +from decimal import Decimal +from typing import List -import brewman.schemas.reports as schemas +import brewman.schemas.closing_stock as schemas from fastapi import APIRouter, Depends, Request, Security from sqlalchemy.orm import Session @@ -11,7 +13,7 @@ from ...core.session import get_finish_date, get_start_date, set_period from ...db.session import SessionLocal from ...models.master import CostCentre, Product from ...models.voucher import Inventory, Journal, Voucher -from ...schemas.auth import UserToken +from ...schemas.user import UserToken router = APIRouter() @@ -30,8 +32,8 @@ def get_db() -> Session: def report_blank( request: Request, user: UserToken = Security(get_user, scopes=["closing-stock"]), -): - return {"date": get_finish_date(request.session), "body": []} +) -> schemas.ClosingStock: + return schemas.ClosingStock(date=get_finish_date(request.session), body=[]) @router.get("/{date_}", response_model=schemas.ClosingStock) @@ -40,15 +42,15 @@ def report_data( request: Request, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["closing-stock"]), -): +) -> schemas.ClosingStock: set_period(get_start_date(request.session), date_, request.session) - return { - "date": date_, - "body": build_report(datetime.strptime(date_, "%d-%b-%Y").date(), db), - } + return schemas.ClosingStock( + date=date_, + body=build_report(datetime.strptime(date_, "%d-%b-%Y").date(), db), + ) -def build_report(date_: date, db: Session): +def build_report(date_: date, db: Session) -> List[schemas.ClosingStockItem]: amount_sum = func.sum(Journal.debit * Inventory.quantity * Inventory.rate * (1 + Inventory.tax)).label("amount") quantity_sum = func.sum(Journal.debit * Inventory.quantity).label("quantity") query = ( @@ -67,17 +69,17 @@ def build_report(date_: date, db: Session): for product, quantity, amount in query: if quantity != 0 and amount != 0: body.append( - { - "product": product.full_name, - "group": product.product_group.name, - "quantity": quantity, - "amount": amount, - } + schemas.ClosingStockItem( + product=product.full_name, + group=product.product_group.name, + quantity=quantity, + amount=amount, + ) ) return body -def get_opening_stock(date_: date, db: Session): +def get_opening_stock(date_: date, db: Session) -> Decimal: opening_stock = ( db.query(func.sum(Inventory.quantity * Inventory.rate * (1 + Inventory.tax) * Journal.debit)) .join(Journal.voucher) @@ -90,7 +92,7 @@ def get_opening_stock(date_: date, db: Session): return 0 if opening_stock is None else opening_stock -def get_closing_stock(date_, db: Session): +def get_closing_stock(date_, db: Session) -> Decimal: closing_stock = ( db.query(func.sum(Inventory.quantity * Inventory.rate * (1 + Inventory.tax) * Journal.debit)) .join(Journal.voucher) diff --git a/brewman/brewman/routers/reports/daybook.py b/brewman/brewman/routers/reports/daybook.py index 1bbc1a9f..2f57fb3d 100644 --- a/brewman/brewman/routers/reports/daybook.py +++ b/brewman/brewman/routers/reports/daybook.py @@ -1,7 +1,7 @@ from datetime import date, datetime from typing import List -import brewman.schemas.reports as schemas +import brewman.schemas.daybook as schemas from fastapi import APIRouter, Depends, Request, Security from sqlalchemy.orm import Session, joinedload_all @@ -10,7 +10,7 @@ from ...core.security import get_current_active_user as get_user from ...core.session import get_finish_date, get_start_date, set_period from ...db.session import SessionLocal from ...models.voucher import Journal, Voucher, VoucherType -from ...schemas.auth import UserToken +from ...schemas.user import UserToken router = APIRouter() diff --git a/brewman/brewman/routers/reports/ledger.py b/brewman/brewman/routers/reports/ledger.py index 22b7ba3d..fe9e7202 100644 --- a/brewman/brewman/routers/reports/ledger.py +++ b/brewman/brewman/routers/reports/ledger.py @@ -1,7 +1,9 @@ import datetime import uuid -import brewman.schemas.reports as schemas +from typing import List + +import brewman.schemas.ledger as schemas from fastapi import APIRouter, Depends, Request, Security from sqlalchemy.orm import Session, joinedload_all @@ -12,7 +14,7 @@ from ...core.session import get_finish_date, get_start_date, set_period from ...db.session import SessionLocal from ...models.master import AccountBase from ...models.voucher import Journal, Voucher, VoucherType -from ...schemas.auth import UserToken +from ...schemas.user import UserToken router = APIRouter() @@ -31,13 +33,13 @@ def get_db() -> Session: def show_blank( request: Request, user: UserToken = Security(get_user, scopes=["ledger"]), -): - return { - "startDate": get_start_date(request.session), - "finishDate": get_finish_date(request.session), - "account": None, - "body": [], - } +) -> schemas.Ledger: + return schemas.Ledger( + startDate=get_start_date(request.session), + finishDate=get_finish_date(request.session), + account=None, + body=[], + ) @router.get("/{id_}", response_model=schemas.Ledger) @@ -48,21 +50,21 @@ def show_data( f: str = None, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["ledger"]), -): +) -> schemas.Ledger: account = db.query(AccountBase).filter(AccountBase.id == id_).first() 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) 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.Ledger( + 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: str, finish_date: str, db: Session) -> List[schemas.LedgerItem]: body = [] opening = opening_balance(account_id, start_date, db) body.append(opening) @@ -98,26 +100,26 @@ 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, - "url": [ + schemas.LedgerItem( + id=voucher.id, + date=voucher.date.strftime("%d-%b-%Y"), + name=name, + url=[ "/", VoucherType.by_id(voucher.type).name.replace(" ", "-").lower(), str(voucher.id), ], - "type": VoucherType.by_id(voucher.type).name, - "narration": voucher.narration, - "debit": debit, - "credit": credit, - "posted": voucher.posted, - } + type=VoucherType.by_id(voucher.type).name, + narration=voucher.narration, + debit=debit, + credit=credit, + posted=voucher.posted, + ) ) return body -def opening_balance(account_id, start_date, db): +def opening_balance(account_id: uuid.UUID, start_date: str, db: Session) -> schemas.LedgerItem: opening = ( db.query(func.sum(Journal.amount * Journal.debit)) .join(Journal.voucher) @@ -133,14 +135,14 @@ def opening_balance(account_id, start_date, db): else: debit = opening credit = 0 - return { - "date": start_date, - "id": None, - "name": "Opening Balance", - "type": "Opening Balance", - "url": [], - "narration": "", - "debit": debit, - "credit": credit, - "posted": True, - } + return schemas.LedgerItem( + date=start_date, + id=None, + name="Opening Balance", + type="Opening Balance", + url=[], + narration="", + debit=debit, + credit=credit, + posted=True, + ) diff --git a/brewman/brewman/routers/reports/net_transactions.py b/brewman/brewman/routers/reports/net_transactions.py index 162dfc2f..6d1001f9 100644 --- a/brewman/brewman/routers/reports/net_transactions.py +++ b/brewman/brewman/routers/reports/net_transactions.py @@ -1,6 +1,7 @@ from datetime import date, datetime +from typing import List -import brewman.schemas.reports as schemas +import brewman.schemas.net_transactions as schemas from fastapi import APIRouter, Depends, Request, Security from sqlalchemy.orm import Session @@ -11,7 +12,7 @@ from ...core.session import get_finish_date, get_start_date, set_period from ...db.session import SessionLocal from ...models.master import AccountBase from ...models.voucher import Journal, Voucher, VoucherType -from ...schemas.auth import UserToken +from ...schemas.user import UserToken router = APIRouter() @@ -30,12 +31,12 @@ def get_db() -> Session: def show_blank( request: Request, user: UserToken = Security(get_user, scopes=["net-transactions"]), -): - return { - "startDate": get_start_date(request.session), - "finishDate": get_finish_date(request.session), - "body": [], - } +) -> schemas.NetTransactions: + return schemas.NetTransactions( + startDate=get_start_date(request.session), + finishDate=get_finish_date(request.session), + body=[], + ) @router.get("/{start}/{finish}", response_model=schemas.NetTransactions) @@ -45,20 +46,20 @@ def show_data( request: Request, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["net-transactions"]), -): +) -> schemas.NetTransactions: set_period(start, finish, request.session) - return { - "startDate": start, - "finishDate": finish, - "body": build_report( + return schemas.NetTransactions( + startDate=start, + finishDate=finish, + body=build_report( datetime.strptime(start, "%d-%b-%Y"), datetime.strptime(finish, "%d-%b-%Y"), db, ), - } + ) -def build_report(start_date: date, finish_date: date, db: Session): +def build_report(start_date: date, finish_date: date, db: Session) -> List[schemas.NetTransactionsItem]: amount_sum = func.sum(Journal.amount * Journal.debit).label("amount") query = ( db.query(AccountBase, amount_sum) @@ -76,6 +77,10 @@ def build_report(start_date: date, finish_date: date, db: Session): body = [] for account, amount in query: if amount != 0: - tag = "debit" if amount > 0 else "credit" - body.append({"type": account.type_object.name, "name": account.name, tag: amount}) + item = schemas.NetTransactionsItem(type=account.type_object.name, name=account.name) + if amount > 0: + item.debit = amount + else: + item.credit = amount + body.append(item) return body diff --git a/brewman/brewman/routers/reports/product_ledger.py b/brewman/brewman/routers/reports/product_ledger.py index c01807c9..939fcc6a 100644 --- a/brewman/brewman/routers/reports/product_ledger.py +++ b/brewman/brewman/routers/reports/product_ledger.py @@ -1,8 +1,10 @@ import uuid from datetime import date, datetime +from decimal import Decimal +from typing import List -import brewman.schemas.reports as schemas +import brewman.schemas.product_ledger as schemas from fastapi import APIRouter, Depends, Request, Security from sqlalchemy.orm import Session, joinedload @@ -13,7 +15,7 @@ from ...core.session import get_finish_date, get_start_date, set_period from ...db.session import SessionLocal from ...models.master import CostCentre, Product from ...models.voucher import Inventory, Journal, Voucher, VoucherType -from ...schemas.auth import UserToken +from ...schemas.user import UserToken router = APIRouter() @@ -32,13 +34,13 @@ def get_db() -> Session: def show_blank( request: Request, user: UserToken = Security(get_user, scopes=["product-ledger"]), -): - return { - "startDate": get_start_date(request.session), - "finishDate": get_finish_date(request.session), - "product": None, - "body": [], - } +) -> schemas.ProductLedger: + return schemas.ProductLedger( + startDate=get_start_date(request.session), + finishDate=get_finish_date(request.session), + product=None, + body=[], + ) @router.get("/{id_}", response_model=schemas.ProductLedger) @@ -49,7 +51,7 @@ def show_data( f: str = None, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["product-ledger"]), -): +) -> schemas.ProductLedger: product = db.query(Product).filter(Product.id == id_).first() 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) @@ -60,15 +62,17 @@ def show_data( db, ) set_period(start_date, finish_date, request.session) - return { - "startDate": start_date, - "finishDate": finish_date, - "product": {"id": product.id, "name": product.name}, - "body": body, - } + return schemas.ProductLedger( + startDate=start_date, + finishDate=finish_date, + product=schemas.ProductLink(id=product.id, name=product.name), + body=body, + ) -def build_report(product_id: uuid.UUID, start_date: date, finish_date: date, db: Session): +def build_report( + product_id: uuid.UUID, start_date: date, finish_date: date, db: Session +) -> List[schemas.ProductLedgerItem]: body = [] running_total_q, running_total_a, opening = opening_balance(product_id, start_date, db) body.append(opening) @@ -106,31 +110,33 @@ def build_report(product_id: uuid.UUID, start_date: date, finish_date: date, db: running_total_a += row.Inventory.amount * journal_debit body.append( - { - "id": row.Voucher.id, - "date": row.Voucher.date.strftime("%d-%b-%Y"), - "name": name, - "url": [ + schemas.ProductLedgerItem( + id=row.Voucher.id, + date=row.Voucher.date.strftime("%d-%b-%Y"), + name=name, + url=[ "/", VoucherType.by_id(row.Voucher.type).name.replace(" ", "-").lower(), str(row.Voucher.id), ], - "type": VoucherType.by_id(row.Voucher.type).name, - "narration": row.Voucher.narration, - "posted": row.Voucher.posted or VoucherType.by_id(row.Voucher.type).name == "Issue", - "debitQuantity": debit_q, - "debitAmount": debit_a, - "creditQuantity": credit_q, - "creditAmount": credit_a, - "runningQuantity": running_total_q, - "runningAmount": running_total_a, - } + type=VoucherType.by_id(row.Voucher.type).name, + narration=row.Voucher.narration, + posted=row.Voucher.posted or VoucherType.by_id(row.Voucher.type).name == "Issue", + debitQuantity=debit_q, + debitAmount=debit_a, + creditQuantity=credit_q, + creditAmount=credit_a, + runningQuantity=running_total_q, + runningAmount=running_total_a, + ) ) return body -def opening_balance(product_id: uuid.UUID, start_date: date, db: Session): +def opening_balance( + product_id: uuid.UUID, start_date: date, db: Session +) -> (Decimal, Decimal, schemas.ProductLedgerItem): quantity, amount = ( db.query( func.sum(Inventory.quantity * Journal.debit), @@ -160,19 +166,19 @@ def opening_balance(product_id: uuid.UUID, start_date: date, db: Session): return ( quantity, amount, - { - "id": None, - "date": start_date.strftime("%d-%b-%Y"), - "name": "Opening Balance", - "url": [], - "type": "Opening Balance", - "narration": "", - "posted": True, - "debitQuantity": debit_quantity, - "debitAmount": debit_amount, - "creditQuantity": 0, - "creditAmount": 0, - "runningQuantity": quantity, - "runningAmount": amount, - }, + schemas.ProductLedgerItem( + id=None, + date=start_date.strftime("%d-%b-%Y"), + name="Opening Balance", + url=[], + type="Opening Balance", + narration="", + posted=True, + debitQuantity=debit_quantity, + debitAmount=debit_amount, + creditQuantity=0, + creditAmount=0, + runningQuantity=quantity, + runningAmount=amount, + ), ) diff --git a/brewman/brewman/routers/reports/profit_loss.py b/brewman/brewman/routers/reports/profit_loss.py index 6b5e3b29..891fecdd 100644 --- a/brewman/brewman/routers/reports/profit_loss.py +++ b/brewman/brewman/routers/reports/profit_loss.py @@ -1,6 +1,8 @@ from datetime import date, datetime +from decimal import Decimal +from typing import List -import brewman.schemas.reports as schemas +import brewman.schemas.profit_loss as schemas from fastapi import APIRouter, Depends, Request, Security from sqlalchemy.orm import Session @@ -12,7 +14,7 @@ from ...db.session import SessionLocal from ...models.master import AccountBase, AccountType from ...models.voucher import Journal, Voucher, VoucherType from ...routers.reports.closing_stock import get_closing_stock, get_opening_stock -from ...schemas.auth import UserToken +from ...schemas.user import UserToken router = APIRouter() @@ -31,13 +33,13 @@ def get_db() -> Session: def report_blank( request: Request, user: UserToken = Security(get_user, scopes=["profit-&-loss"]), -): - return { - "startDate": get_start_date(request.session), - "finishDate": get_finish_date(request.session), - "body": [], - "footer": None, - } +) -> schemas.ProfitLoss: + return schemas.ProfitLoss( + startDate=get_start_date(request.session), + finishDate=get_finish_date(request.session), + body=[], + footer=None, + ) @router.get("/{start}/{finish}", response_model=schemas.ProfitLoss) @@ -47,20 +49,22 @@ def report_data( request: Request, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["profit-&-loss"]), -): +) -> schemas.ProfitLoss: body, footer = build_profit_loss(datetime.strptime(start, "%d-%b-%Y"), datetime.strptime(finish, "%d-%b-%Y"), db) set_period(start, finish, request.session) - return { - "startDate": start, - "finishDate": finish, - "body": body, - "footer": footer, - } + return schemas.ProfitLoss( + startDate=start, + finishDate=finish, + body=body, + footer=footer, + ) -def build_profit_loss(start_date: date, finish_date: date, db: Session): +def build_profit_loss( + start_date: date, finish_date: date, db: Session +) -> (List[schemas.ProfitLossItem], schemas.ProfitLossItem): profit_type_list = [i.id for i in AccountType.list() if i.balance_sheet is False] - report = [] + report: List[schemas.ProfitLossItem] = [] groups = {} amount_sum = func.sum(Journal.amount * Journal.debit) @@ -83,14 +87,14 @@ def build_profit_loss(start_date: date, finish_date: date, db: Session): closing_stock = get_closing_stock(finish_date, db) total_amount = (opening_stock - closing_stock) * -1 - report.append({"name": "Opening Stock", "amount": opening_stock * -1, "order": 200001}) - report.append({"name": "Closing Stock", "amount": closing_stock, "order": 290000}) + report.append(schemas.ProfitLossItem(name="Opening Stock", amount=opening_stock * -1, order=200001)) + report.append(schemas.ProfitLossItem(name="Closing Stock", amount=closing_stock, order=290000)) purchase_group = AccountType.by_id(2) - groups[purchase_group.id] = { - "group": purchase_group.name, - "total": total_amount, - "order": purchase_group.order, - } + groups[purchase_group.id] = schemas.ProfitLossItem( + group=purchase_group.name, + total=total_amount, + order=purchase_group.order, + ) counter = 0 for account, amount in query: @@ -102,36 +106,36 @@ def build_profit_loss(start_date: date, finish_date: date, db: Session): if amount != 0: counter += 10 report.append( - { - "name": account.name, - "amount": amount, - "order": account_type.order + counter, - } + schemas.ProfitLossItem( + name=account.name, + amount=amount, + order=account_type.order + counter, + ) ) if account_type.id in groups: - groups[account_type.id]["total"] += amount + groups[account_type.id].total += amount else: - groups[account_type.id] = { - "group": account_type.name, - "total": amount, - "order": account_type.order, - } + groups[account_type.id] = schemas.ProfitLossItem( + group=account_type.name, + total=amount, + order=account_type.order, + ) # Add Subtotals for item in groups.values(): report.append(item) # Add Net - footer = { - "group": "Net Profit" if total_amount > 0 else "Net Loss", - "total": total_amount, - "order": 1000000, - } + footer = schemas.ProfitLossItem( + group="Net Profit" if total_amount > 0 else "Net Loss", + total=total_amount, + order=1000000, + ) - return sorted(report, key=lambda d: d["order"]), footer + return sorted(report, key=lambda d: d.order), footer -def get_accumulated_profit(finish_date, db): +def get_accumulated_profit(finish_date: date, db: Session) -> Decimal: type_list = [i.id for i in AccountType.list() if i.balance_sheet is False] accumulated_profit = ( diff --git a/brewman/brewman/routers/reports/purchase_entries.py b/brewman/brewman/routers/reports/purchase_entries.py index d36634c5..66d0d6a4 100644 --- a/brewman/brewman/routers/reports/purchase_entries.py +++ b/brewman/brewman/routers/reports/purchase_entries.py @@ -1,7 +1,7 @@ from datetime import date, datetime from typing import List -import brewman.schemas.reports as schemas +import brewman.schemas.purchase_entries as schemas from fastapi import APIRouter, Depends, Request, Security from sqlalchemy.orm import Session @@ -10,7 +10,7 @@ from ...core.security import get_current_active_user as get_user from ...core.session import get_finish_date, get_start_date, set_period from ...db.session import SessionLocal from ...models.voucher import Voucher, VoucherType -from ...schemas.auth import UserToken +from ...schemas.user import UserToken router = APIRouter() @@ -29,12 +29,12 @@ def get_db() -> Session: def report_blank( request: Request, user: UserToken = Security(get_user, scopes=["purchase-entries"]), -): - return { - "startDate": get_start_date(request.session), - "finishDate": get_finish_date(request.session), - "body": [], - } +) -> schemas.PurchaseEntries: + return schemas.PurchaseEntries( + startDate=get_start_date(request.session), + finishDate=get_finish_date(request.session), + body=[], + ) @router.get("/{start}/{finish}", response_model=schemas.PurchaseEntries) @@ -44,15 +44,15 @@ def report_data( request: Request, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["purchase-entries"]), -): +) -> schemas.PurchaseEntries: start_date = datetime.strptime(start, "%d-%b-%Y") finish_date = datetime.strptime(finish, "%d-%b-%Y") body = build_report(start_date, finish_date, db) set_period(start, finish, request.session) - return {"startDate": start, "finishDate": finish, "body": body} + return schemas.PurchaseEntries(startDate=start, finishDate=finish, body=body) -def build_report(start_date: date, finish_date, db): +def build_report(start_date: date, finish_date: date, db: Session) -> List[schemas.PurchaseEntriesItem]: body = [] query: List[Voucher] = ( db.query(Voucher) @@ -67,20 +67,20 @@ def build_report(start_date: date, finish_date, db): journal = [j for j in voucher.journals if j.debit == -1] journal = journal[0] for item in voucher.inventories: - row = { - "date": voucher.date.strftime("%d-%b-%Y"), - "supplier": journal.account.name, - "url": [ + row = schemas.PurchaseEntriesItem( + date=voucher.date, + supplier=journal.account.name, + url=[ "/", VoucherType.by_id(voucher.type).name.replace(" ", "-").lower(), str(voucher.id), ], - "product": item.product.full_name, - "quantity": item.quantity, - "rate": item.rate, - "tax": item.tax, - "discount": item.discount, - "amount": item.amount, - } + product=item.product.full_name, + quantity=item.quantity, + rate=item.rate, + tax=item.tax, + discount=item.discount, + amount=item.amount, + ) body.append(row) return body diff --git a/brewman/brewman/routers/reports/purchases.py b/brewman/brewman/routers/reports/purchases.py index 3b46b36b..91e0c081 100644 --- a/brewman/brewman/routers/reports/purchases.py +++ b/brewman/brewman/routers/reports/purchases.py @@ -1,6 +1,8 @@ import datetime -import brewman.schemas.reports as schemas +from typing import List + +import brewman.schemas.purchases as schemas from fastapi import APIRouter, Depends, Request, Security from sqlalchemy.orm import Session @@ -11,7 +13,7 @@ from ...core.session import get_finish_date, get_start_date, set_period from ...db.session import SessionLocal from ...models.master import CostCentre, Product from ...models.voucher import Inventory, Journal, Voucher, VoucherType -from ...schemas.auth import UserToken +from ...schemas.user import UserToken router = APIRouter() @@ -30,7 +32,7 @@ def get_db() -> Session: def report_blank( request: Request, user: UserToken = Security(get_user, scopes=["purchases"]), -): +) -> schemas.Purchases: return schemas.Purchases( startDate=get_start_date(request.session), finishDate=get_finish_date(request.session), @@ -46,7 +48,7 @@ def report_data( request: Request, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["purchases"]), -): +) -> schemas.Purchases: body, footer = build_report(start, finish, db) set_period(start, finish, request.session) return schemas.Purchases( @@ -57,7 +59,9 @@ def report_data( ) -def build_report(start_date, finish_date, db): +def build_report( + start_date: str, finish_date: str, db: Session +) -> (List[schemas.PurchasesItem], schemas.PurchasesItem): body = [] quantity_sum = func.sum(Journal.debit * Inventory.quantity).label("quantity") amount_sum = func.sum(Journal.debit * Inventory.quantity * Inventory.rate * (1 + Inventory.tax)).label("amount") diff --git a/brewman/brewman/routers/reports/raw_material_cost.py b/brewman/brewman/routers/reports/raw_material_cost.py index dcf326a7..e914e8e9 100644 --- a/brewman/brewman/routers/reports/raw_material_cost.py +++ b/brewman/brewman/routers/reports/raw_material_cost.py @@ -1,8 +1,9 @@ import uuid from datetime import date, datetime +from typing import List -import brewman.schemas.reports as schemas +import brewman.schemas.raw_material_cost as schemas from fastapi import APIRouter, Depends, Request, Security from sqlalchemy.orm import Session @@ -13,7 +14,7 @@ from ...core.session import get_finish_date, get_start_date, set_period from ...db.session import SessionLocal from ...models.master import AccountBase, CostCentre, Product, ProductGroup from ...models.voucher import Inventory, Journal, Voucher -from ...schemas.auth import UserToken +from ...schemas.user import UserToken router = APIRouter() @@ -32,13 +33,13 @@ def get_db() -> Session: def report_blank( request: Request, user: UserToken = Security(get_user, scopes=["raw-material-cost"]), -): - return { - "startDate": get_start_date(request.session), - "finishDate": get_finish_date(request.session), - "body": [], - "footer": None, - } +) -> schemas.RawMaterialCost: + return schemas.RawMaterialCost( + startDate=get_start_date(request.session), + finishDate=get_finish_date(request.session), + body=[], + footer=None, + ) @router.get("/data", response_model=schemas.RawMaterialCost) @@ -48,15 +49,15 @@ def report_data( f: str = None, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["raw-material-cost"]), -): +) -> schemas.RawMaterialCost: body, footer = build_report(datetime.strptime(s, "%d-%b-%Y"), datetime.strptime(f, "%d-%b-%Y"), db) set_period(s, f, request.session) - return { - "startDate": s, - "finishDate": f, - "body": body, - "footer": footer, - } + return schemas.RawMaterialCost( + startDate=s, + finishDate=f, + body=body, + footer=footer, + ) @router.get("/{id_}", response_model=schemas.RawMaterialCost) @@ -67,18 +68,20 @@ def report_id( f: str = None, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["raw-material-cost"]), -): +) -> schemas.RawMaterialCost: body = build_report_id(id_, datetime.strptime(s, "%d-%b-%Y"), datetime.strptime(f, "%d-%b-%Y"), db) set_period(s, f, request.session) - return { - "id": id_, - "startDate": s, - "finishDate": f, - "body": body, - } + return schemas.RawMaterialCost( + id=id_, + startDate=s, + finishDate=f, + body=body, + ) -def build_report(start_date: date, finish_date: date, db: Session): +def build_report( + start_date: date, finish_date: date, db: Session +) -> (List[schemas.RawMaterialCostItem], schemas.RawMaterialCostItem): body = [] sum_issue = func.sum(case([(AccountBase.type == 2, Journal.signed_amount)], else_=0)).label("issue") sum_sale = func.sum(case([(AccountBase.type == 3, Journal.signed_amount * -1)], else_=0)).label("sale") @@ -104,20 +107,22 @@ def build_report(start_date: date, finish_date: date, db: Session): sales += sale rmc = 0 if sale == 0 else issue / sale body.append( - { - "name": cost_centre.name, - "issue": issue, - "sale": sale, - "rmc": rmc, - "url": ["/", "raw-material-cost", str(cost_centre.id)], - } + schemas.RawMaterialCostItem( + name=cost_centre.name, + issue=issue, + sale=sale, + rmc=rmc, + url=["/", "raw-material-cost", str(cost_centre.id)], + ) ) rmc = 0 if sales == 0 else issues / sales - return body, {"name": "Total", "issue": issues, "sale": sales, "rmc": rmc} + return body, schemas.RawMaterialCostItem(name="Total", issue=issues, sale=sales, rmc=rmc) -def build_report_id(cost_centre_id: uuid.UUID, start_date: date, finish_date: date, db: Session): +def build_report_id( + cost_centre_id: uuid.UUID, start_date: date, finish_date: date, db: Session +) -> List[schemas.RawMaterialCostItem]: sum_quantity = func.sum(Inventory.quantity * Journal.debit).label("quantity") sum_net = func.sum(Inventory.rate * Inventory.quantity * Journal.debit).label("net") sum_gross = func.sum(Inventory.amount * Journal.debit).label("gross") @@ -146,31 +151,31 @@ def build_report_id(cost_centre_id: uuid.UUID, start_date: date, finish_date: da for product, quantity, net, gross in query: if product.product_group_id in groups: group = groups[product.product_group_id] - group["net"] += net - group["gross"] += gross + group.net += net + group.gross += gross else: counter += 500 - group = { - "group": product.product_group.name, - "net": net, - "gross": gross, - "order": counter, - "heading": True, - } + group = schemas.RawMaterialCostItem( + group=product.product_group.name, + net=net, + gross=gross, + order=counter, + heading=True, + ) groups[product.product_group_id] = group counter += 1 list_.append( - { - "name": product.full_name, - "quantity": quantity, - "net": net, - "gross": gross, - "order": counter, - "heading": False, - } + schemas.RawMaterialCostItem( + name=product.full_name, + quantity=quantity, + net=net, + gross=gross, + order=counter, + heading=False, + ) ) for item in groups.values(): list_.append(item) - return sorted(list_, key=lambda d: d["order"]) + return sorted(list_, key=lambda d: d.order) diff --git a/brewman/brewman/routers/reports/reconcile.py b/brewman/brewman/routers/reports/reconcile.py index eab96502..aedc338d 100644 --- a/brewman/brewman/routers/reports/reconcile.py +++ b/brewman/brewman/routers/reports/reconcile.py @@ -10,7 +10,7 @@ from ...core.session import get_finish_date, get_start_date, set_period from ...db.session import SessionLocal from ...models.master import AccountBase from ...models.voucher import Journal, Voucher, VoucherType -from ...schemas.auth import UserToken +from ...schemas.user import UserToken router = APIRouter() diff --git a/brewman/brewman/routers/reports/stock_movement.py b/brewman/brewman/routers/reports/stock_movement.py index f256c473..25991525 100644 --- a/brewman/brewman/routers/reports/stock_movement.py +++ b/brewman/brewman/routers/reports/stock_movement.py @@ -1,7 +1,8 @@ from datetime import date, datetime from decimal import Decimal +from typing import List -import brewman.schemas.reports as schemas +import brewman.schemas.stock_movement as schemas from fastapi import APIRouter, Depends, Request, Security from sqlalchemy.orm import Session @@ -12,7 +13,7 @@ from ...core.session import get_finish_date, get_start_date, set_period from ...db.session import SessionLocal from ...models.master import CostCentre, Product from ...models.voucher import Inventory, Journal, Voucher, VoucherType -from ...schemas.auth import UserToken +from ...schemas.user import UserToken router = APIRouter() @@ -31,7 +32,7 @@ def get_db() -> Session: def report_blank( request: Request, user: UserToken = Security(get_user, scopes=["stock-movement"]), -): +) -> schemas.StockMovement: return { "startDate": get_start_date(request.session), "finishDate": get_finish_date(request.session), @@ -46,13 +47,13 @@ def report_data( request: Request, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["stock-movement"]), -): +) -> schemas.StockMovement: body = build_stock_movement(datetime.strptime(start, "%d-%b-%Y"), datetime.strptime(finish, "%d-%b-%Y"), db) set_period(start, finish, request.session) return {"startDate": start, "finishDate": finish, "body": body} -def build_stock_movement(start_date: date, finish_date: date, db: Session): +def build_stock_movement(start_date: date, finish_date: date, db: Session) -> List[schemas.StockMovementItem]: dict_ = {} quantity_sum = func.sum(Journal.debit * Inventory.quantity).label("quantity") openings = ( @@ -66,13 +67,16 @@ def build_stock_movement(start_date: date, finish_date: date, db: Session): .all() ) for product, quantity in openings: - dict_[product.id] = { - "id": product.id, - "name": product.full_name, - "group": product.product_group.name, - "opening": Decimal(round(quantity, 2)), - "url": ["/", "product-ledger", str(product.id)], - } + dict_[product.id] = schemas.StockMovementItem( + id=product.id, + name=product.full_name, + group=product.product_group.name, + opening=Decimal(round(quantity, 2)), + purchase=0, + issue=0, + closing=0, + url=["/", "product-ledger", str(product.id)], + ) purchases = ( db.query(Product, quantity_sum) .join(Product.inventories) @@ -87,15 +91,18 @@ def build_stock_movement(start_date: date, finish_date: date, db: Session): ) for product, quantity in purchases: if product.id in dict_: - dict_[product.id]["purchase"] = Decimal(round(quantity, 2)) + dict_[product.id].purchase = Decimal(round(quantity, 2)) else: - dict_[product.id] = { - "id": product.id, - "name": product.full_name, - "group": product.product_group.name, - "purchase": Decimal(round(quantity, 2)), - "url": ["/", "product-ledger", str(product.id)], - } + dict_[product.id] = schemas.StockMovementItem( + id=product.id, + name=product.full_name, + group=product.product_group.name, + opening=0, + purchase=Decimal(round(quantity, 2)), + issue=0, + closing=0, + url=["/", "product-ledger", str(product.id)], + ) issues = ( db.query(Product, quantity_sum) .join(Product.inventories) @@ -110,33 +117,26 @@ def build_stock_movement(start_date: date, finish_date: date, db: Session): ) for product, quantity in issues: if product.id in dict_: - dict_[product.id]["issue"] = Decimal(round(quantity * -1, 2)) + dict_[product.id].issue = Decimal(round(quantity * -1, 2)) else: - dict_[product.id] = { - "id": product.id, - "name": product.full_name, - "group": product.product_group.name, - "issue": Decimal(round(quantity * -1, 2)), - "url": ["/", "product-ledger", str(product.id)], - } + dict_[product.id] = schemas.StockMovementItem( + id=product.id, + name=product.full_name, + group=product.product_group.name, + opening=0, + purchase=0, + issue=Decimal(round(quantity * -1, 2)), + closing=0, + url=["/", "product-ledger", str(product.id)], + ) list_ = [value for key, value in dict_.items()] - list_ = sorted(list_, key=lambda x: x["name"].lower()) - list_ = sorted(list_, key=lambda x: x["group"].lower()) + list_ = sorted(list_, key=lambda x: x.name.lower()) + list_ = sorted(list_, key=lambda x: x.group.lower()) for i in range(len(list_), 0, -1): item = list_[i - 1] - if "opening" not in item: - item["opening"] = 0 - opening = item["opening"] - if "purchase" not in item: - item["purchase"] = 0 - purchase = item["purchase"] - if "issue" not in item: - item["issue"] = 0 - issue = item["issue"] - closing = round(opening + purchase - issue, 2) - if opening == 0 and purchase == 0 and issue == 0: + if item.opening == 0 and item.purchase == 0 and item.issue == 0: list_.remove(item) else: - item["closing"] = closing + item.closing = round(item.opening + item.purchase - item.issue, 2) return list_ diff --git a/brewman/brewman/routers/reports/trial_balance.py b/brewman/brewman/routers/reports/trial_balance.py index 08c8f6b1..6210f880 100644 --- a/brewman/brewman/routers/reports/trial_balance.py +++ b/brewman/brewman/routers/reports/trial_balance.py @@ -1,6 +1,7 @@ from datetime import date, datetime +from typing import List -import brewman.schemas.reports as schemas +import brewman.schemas.trial_balance as schemas from fastapi import APIRouter, Depends, Request, Security from sqlalchemy.orm import Session @@ -11,7 +12,7 @@ from ...core.session import get_finish_date, get_start_date, set_period from ...db.session import SessionLocal from ...models.master import AccountBase from ...models.voucher import Journal, Voucher, VoucherType -from ...schemas.auth import UserToken +from ...schemas.user import UserToken router = APIRouter() @@ -30,8 +31,8 @@ def get_db() -> Session: def report_blank( request: Request, user: UserToken = Security(get_user, scopes=["trial-balance"]), -): - return {"date": get_finish_date(request.session), "body": []} +) -> schemas.TrialBalance: + return schemas.TrialBalance(date=get_finish_date(request.session), body=[]) @router.get("/{date_}", response_model=schemas.TrialBalance) @@ -40,15 +41,15 @@ def report_data( request: Request, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["trial-balance"]), -): +) -> schemas.TrialBalance: set_period(get_start_date(request.session), date_, request.session) - return { - "date": date_, - "body": build_report(datetime.strptime(date_, "%d-%b-%Y"), db), - } + return schemas.TrialBalance( + date=date_, + body=build_report(datetime.strptime(date_, "%d-%b-%Y"), db), + ) -def build_report(date_: date, db: Session): +def build_report(date_: date, db: Session) -> List[schemas.TrialBalanceItem]: amount_sum = func.sum(Journal.amount * Journal.debit).label("amount") query = ( db.query(AccountBase, amount_sum) @@ -64,7 +65,8 @@ def build_report(date_: date, db: Session): body = [] for account, amount in query: - if amount != 0: - tag = "debit" if amount > 0 else "credit" - body.append({"type": account.type_object.name, "name": account.name, tag: amount}) + if amount > 0: + body.append(schemas.TrialBalanceItem(type=account.type_object.name, name=account.name, debit=amount)) + if amount < 0: + body.append(schemas.TrialBalanceItem(type=account.type_object.name, name=account.name, credit=amount)) return body diff --git a/brewman/brewman/routers/reports/unposted.py b/brewman/brewman/routers/reports/unposted.py index 6e7ce6b7..40c8ae59 100644 --- a/brewman/brewman/routers/reports/unposted.py +++ b/brewman/brewman/routers/reports/unposted.py @@ -1,6 +1,6 @@ from typing import List -import brewman.schemas.reports as schemas +import brewman.schemas.unposted as schemas from fastapi import APIRouter, Depends, Request, Security from sqlalchemy.orm import Session, joinedload_all @@ -8,7 +8,7 @@ from sqlalchemy.orm import Session, joinedload_all from ...core.security import get_current_active_user as get_user from ...db.session import SessionLocal from ...models.voucher import Journal, Voucher, VoucherType -from ...schemas.auth import UserToken +from ...schemas.user import UserToken router = APIRouter() diff --git a/brewman/brewman/routers/reset_stock.py b/brewman/brewman/routers/reset_stock.py index c8836ff4..b4658bf6 100644 --- a/brewman/brewman/routers/reset_stock.py +++ b/brewman/brewman/routers/reset_stock.py @@ -8,8 +8,8 @@ from ..core.security import get_current_active_user as get_user from ..db.session import SessionLocal from ..models import Batch, Inventory, Journal, Voucher, VoucherType from ..models.master import AccountBase, CostCentre, Product -from ..schemas.auth import UserToken from ..schemas.settings import ResetStock +from ..schemas.user import UserToken router = APIRouter() diff --git a/brewman/brewman/routers/voucher.py b/brewman/brewman/routers/voucher.py index 8caf24f6..9ea597f5 100644 --- a/brewman/brewman/routers/voucher.py +++ b/brewman/brewman/routers/voucher.py @@ -24,7 +24,7 @@ from ..models.voucher import ( VoucherType, ) from ..routers import get_lock_info -from ..schemas.auth import UserToken +from ..schemas.user import UserToken router = APIRouter() diff --git a/brewman/brewman/schemas/account.py b/brewman/brewman/schemas/account.py new file mode 100644 index 00000000..c0165108 --- /dev/null +++ b/brewman/brewman/schemas/account.py @@ -0,0 +1,46 @@ +import uuid + +from typing import Optional + +from brewman.schemas import to_camel +from brewman.schemas.cost_centre import CostCentreLink +from pydantic import BaseModel, Field + + +class AccountLink(BaseModel): + id_: uuid.UUID = Field(...) + name: Optional[str] + + class Config: + fields = {"id_": "id"} + + +class AccountBase(BaseModel): + name: str = Field(..., min_length=1) + is_starred: bool + is_active: bool + cost_centre: CostCentreLink + + class Config: + anystr_strip_whitespace = True + alias_generator = to_camel + + +class AccountIn(AccountBase): + type: int + is_reconcilable: bool + + +class Account(AccountIn): + id_: uuid.UUID + code: int + is_fixture: bool + + +class AccountBlank(AccountIn): + name: str + is_fixture: bool + + class Config: + anystr_strip_whitespace = True + alias_generator = to_camel diff --git a/brewman/brewman/schemas/account_type.py b/brewman/brewman/schemas/account_type.py new file mode 100644 index 00000000..d9e88079 --- /dev/null +++ b/brewman/brewman/schemas/account_type.py @@ -0,0 +1,9 @@ +from pydantic import BaseModel + + +class AccountType(BaseModel): + id_: int + name: str + + class Config: + fields = {"id_": "id"} diff --git a/brewman/brewman/schemas/auth.py b/brewman/brewman/schemas/auth.py deleted file mode 100644 index eab1d04c..00000000 --- a/brewman/brewman/schemas/auth.py +++ /dev/null @@ -1,106 +0,0 @@ -import uuid - -from datetime import datetime -from typing import List, Optional - -from brewman.schemas import to_camel -from pydantic import BaseModel - - -class ClientIn(BaseModel): - name: str - enabled: bool - otp: Optional[int] - - -class Client(ClientIn): - id_: uuid.UUID - code: int - creation_date: datetime - - -class LoginHistory(BaseModel): - id_: uuid.UUID - user_id: uuid.UUID - client_id: uuid.UUID - date: datetime - - class Config: - fields = {"id_": "id"} - anystr_strip_whitespace = True - alias_generator = to_camel - - -class PermissionItem(BaseModel): - id_: uuid.UUID - name: str - enabled: bool - - class Config: - fields = {"id_": "id"} - - -class RoleIn(BaseModel): - name: str - permissions: List[PermissionItem] - - class Config: - fields = {"id_": "id"} - anystr_strip_whitespace = True - - -class Role(RoleIn): - id_: uuid.UUID - - -class RoleList(BaseModel): - id_: uuid.UUID - name: str - permissions: List[str] - - class Config: - fields = {"id_": "id"} - anystr_strip_whitespace = True - - -class RoleItem(BaseModel): - id_: uuid.UUID - name: str - enabled: bool - - class Config: - fields = {"id_": "id"} - - -class UserIn(BaseModel): - name: str - password: str - locked_out: bool - roles: List[RoleItem] - - class Config: - fields = {"id_": "id"} - anystr_strip_whitespace = True - alias_generator = to_camel - - -class User(UserIn): - id_: uuid.UUID - - -class UserList(BaseModel): - id_: uuid.UUID - name: str - roles: List[str] - - class Config: - fields = {"id_": "id"} - anystr_strip_whitespace = True - - -class UserToken(BaseModel): - id_: uuid.UUID - name: str - locked_out: bool = None - password: str - permissions: List[str] diff --git a/brewman/brewman/schemas/balance_sheet.py b/brewman/brewman/schemas/balance_sheet.py new file mode 100644 index 00000000..a59e4992 --- /dev/null +++ b/brewman/brewman/schemas/balance_sheet.py @@ -0,0 +1,36 @@ +from datetime import date, datetime +from decimal import Decimal +from typing import List, Optional + +from brewman.schemas import to_camel +from pydantic import validator +from pydantic.main import BaseModel + + +class BalanceSheetItem(BaseModel): + name: Optional[str] + group: Optional[str] + amount: Optional[Decimal] + sub_amount: Optional[Decimal] + order: int + + class Config: + anystr_strip_whitespace = True + alias_generator = to_camel + + +class BalanceSheet(BaseModel): + date_: date + body: List[BalanceSheetItem] + footer: Optional[BalanceSheetItem] + + class Config: + anystr_strip_whitespace = True + alias_generator = to_camel + json_encoders = {date: lambda v: v.strftime("%d-%b-%Y")} + + @validator("date_", pre=True) + def parse_date(cls, value): + 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 new file mode 100644 index 00000000..719942cd --- /dev/null +++ b/brewman/brewman/schemas/cash_flow.py @@ -0,0 +1,52 @@ +from datetime import date, datetime +from decimal import Decimal +from typing import List, Optional + +from brewman.schemas import to_camel +from pydantic import Field, validator +from pydantic.main import BaseModel + + +class CashFlowItem(BaseModel): + name: str + url: Optional[List[str]] + amount: Decimal = Field(multiple_of=0.01) + + class Config: + anystr_strip_whitespace = True + alias_generator = to_camel + + +class CashFlowBody(BaseModel): + operating: List[CashFlowItem] + investing: List[CashFlowItem] + financing: List[CashFlowItem] + details: List[CashFlowItem] + + class Config: + anystr_strip_whitespace = True + alias_generator = to_camel + + +class CashFlow(BaseModel): + start_date: date + finish_date: date + body: CashFlowBody + footer: Optional[List[CashFlowItem]] + + 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/client.py b/brewman/brewman/schemas/client.py new file mode 100644 index 00000000..2bb16d79 --- /dev/null +++ b/brewman/brewman/schemas/client.py @@ -0,0 +1,18 @@ +import uuid + +from datetime import datetime +from typing import Optional + +from pydantic.main import BaseModel + + +class ClientIn(BaseModel): + name: str + enabled: bool + otp: Optional[int] + + +class Client(ClientIn): + id_: uuid.UUID + code: int + creation_date: datetime diff --git a/brewman/brewman/schemas/closing_stock.py b/brewman/brewman/schemas/closing_stock.py new file mode 100644 index 00000000..4f81b433 --- /dev/null +++ b/brewman/brewman/schemas/closing_stock.py @@ -0,0 +1,34 @@ +from datetime import date, datetime +from decimal import Decimal +from typing import List + +from brewman.schemas import to_camel +from pydantic import validator +from pydantic.main import BaseModel + + +class ClosingStockItem(BaseModel): + product: str + group: str + quantity: Decimal + amount: Decimal + + class Config: + anystr_strip_whitespace = True + alias_generator = to_camel + + +class ClosingStock(BaseModel): + date_: date + body: List[ClosingStockItem] + + class Config: + anystr_strip_whitespace = True + alias_generator = to_camel + json_encoders = {date: lambda v: v.strftime("%d-%b-%Y")} + + @validator("date_", pre=True) + def parse_date(cls, value): + if isinstance(value, date): + return value + return datetime.strptime(value, "%d-%b-%Y").date() diff --git a/brewman/brewman/schemas/cost_centre.py b/brewman/brewman/schemas/cost_centre.py new file mode 100644 index 00000000..a6cb6c5b --- /dev/null +++ b/brewman/brewman/schemas/cost_centre.py @@ -0,0 +1,39 @@ +import uuid + +from typing import Optional + +from brewman.schemas import to_camel +from pydantic import BaseModel, Field + + +class CostCentreIn(BaseModel): + name: str = Field(..., min_length=1) + + class Config: + anystr_strip_whitespace = True + + +class CostCentre(CostCentreIn): + id_: uuid.UUID + is_fixture: bool + + class Config: + anystr_strip_whitespace = True + alias_generator = to_camel + + +class CostCentreLink(BaseModel): + id_: uuid.UUID = Field(...) + name: Optional[str] + + class Config: + fields = {"id_": "id"} + + +class CostCentreBlank(CostCentreIn): + name: str + is_fixture: bool + + class Config: + anystr_strip_whitespace = True + alias_generator = to_camel diff --git a/brewman/brewman/schemas/daybook.py b/brewman/brewman/schemas/daybook.py new file mode 100644 index 00000000..c21054ba --- /dev/null +++ b/brewman/brewman/schemas/daybook.py @@ -0,0 +1,56 @@ +import uuid + +from datetime import date, datetime +from decimal import Decimal +from typing import List, Optional + +from brewman.schemas import to_camel +from pydantic import validator +from pydantic.main import BaseModel + + +class DaybookItem(BaseModel): + id_: Optional[uuid.UUID] + date_: date + url: List[str] + type_: str + narration: str + debit_text: Optional[str] + debit_amount: Optional[Decimal] + credit_text: Optional[str] + credit_amount: Optional[Decimal] + posted: bool + + @validator("date_", pre=True) + def parse_date(cls, value): + 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 Daybook(BaseModel): + start_date: date + finish_date: date + body: List[DaybookItem] + + 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/db_setting.py b/brewman/brewman/schemas/db_setting.py new file mode 100644 index 00000000..5b80202d --- /dev/null +++ b/brewman/brewman/schemas/db_setting.py @@ -0,0 +1,9 @@ +import uuid + +from pydantic import BaseModel + + +class DbSetting(BaseModel): + id_: uuid.UUID + name: str + data: bytes diff --git a/brewman/brewman/schemas/employee.py b/brewman/brewman/schemas/employee.py index e8315a63..82574317 100644 --- a/brewman/brewman/schemas/employee.py +++ b/brewman/brewman/schemas/employee.py @@ -1,12 +1,13 @@ import uuid + from datetime import date, datetime from decimal import Decimal from typing import Optional -from pydantic import Field, validator - from brewman.schemas import to_camel -from brewman.schemas.master import AccountBase +from brewman.schemas.account import AccountBase +from brewman.schemas.cost_centre import CostCentreLink +from pydantic import BaseModel, Field, validator class EmployeeIn(AccountBase): @@ -57,3 +58,13 @@ class EmployeeBlank(EmployeeIn): anystr_strip_whitespace = True alias_generator = to_camel json_encoders = {datetime: lambda v: v.strftime("%d-%b-%Y %H:%M"), date: lambda v: v.strftime("%d-%b-%Y")} + + +class EmployeeLink(BaseModel): + id_: uuid.UUID = Field(...) + name: Optional[str] + designation: Optional[str] + cost_centre: CostCentreLink + + class Config: + alias_generator = to_camel diff --git a/brewman/brewman/schemas/input.py b/brewman/brewman/schemas/input.py index e6331815..fc9eaeef 100644 --- a/brewman/brewman/schemas/input.py +++ b/brewman/brewman/schemas/input.py @@ -6,7 +6,8 @@ from decimal import Decimal from typing import List, Optional from brewman.schemas import to_camel -from brewman.schemas.master import AccountLink, CostCentreLink +from brewman.schemas.account import AccountLink +from brewman.schemas.cost_centre import CostCentreLink from brewman.schemas.voucher import ( EmployeeBenefit, Incentive, diff --git a/brewman/brewman/schemas/ledger.py b/brewman/brewman/schemas/ledger.py new file mode 100644 index 00000000..d449c4ca --- /dev/null +++ b/brewman/brewman/schemas/ledger.py @@ -0,0 +1,57 @@ +import uuid + +from datetime import date, datetime +from decimal import Decimal +from typing import List, Optional + +from brewman.schemas import to_camel +from brewman.schemas.account import AccountLink +from pydantic import validator +from pydantic.main import BaseModel + + +class LedgerItem(BaseModel): + id_: Optional[uuid.UUID] + date_: date + name: str + url: List[str] + type_: str + narration: str + debit: Decimal + credit: Decimal + posted: bool + + @validator("date_", pre=True) + def parse_date(cls, value): + 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 Ledger(BaseModel): + start_date: date + finish_date: date + account: Optional[AccountLink] + body: List[LedgerItem] + + 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/login_history.py b/brewman/brewman/schemas/login_history.py new file mode 100644 index 00000000..1cc0435b --- /dev/null +++ b/brewman/brewman/schemas/login_history.py @@ -0,0 +1,18 @@ +import uuid + +from datetime import datetime + +from brewman.schemas import to_camel +from pydantic import BaseModel + + +class LoginHistory(BaseModel): + id_: uuid.UUID + user_id: uuid.UUID + client_id: uuid.UUID + date: datetime + + class Config: + fields = {"id_": "id"} + anystr_strip_whitespace = True + alias_generator = to_camel diff --git a/brewman/brewman/schemas/master.py b/brewman/brewman/schemas/master.py deleted file mode 100644 index 54d512f9..00000000 --- a/brewman/brewman/schemas/master.py +++ /dev/null @@ -1,164 +0,0 @@ -import uuid - -from datetime import date, datetime -from decimal import Decimal -from typing import Optional - -from brewman.schemas import to_camel -from pydantic import BaseModel, Field, validator - - -class AccountLink(BaseModel): - id_: uuid.UUID = Field(...) - name: Optional[str] - - class Config: - fields = {"id_": "id"} - - -class CostCentreLink(BaseModel): - id_: uuid.UUID = Field(...) - name: Optional[str] - - class Config: - fields = {"id_": "id"} - - -class EmployeeLink(BaseModel): - id_: uuid.UUID = Field(...) - name: Optional[str] - designation: Optional[str] - cost_centre: CostCentreLink - - class Config: - alias_generator = to_camel - - -class ProductGroupIn(BaseModel): - name: str = Field(..., min_length=1) - - -class ProductGroup(ProductGroupIn): - id_: uuid.UUID - is_fixture: bool - - class Config: - fields = {"id_": "id"} - anystr_strip_whitespace = True - alias_generator = to_camel - - -class ProductGroupLink(BaseModel): - id_: uuid.UUID = Field(...) - - class Config: - fields = {"id_": "id"} - - -class ProductLink(BaseModel): - id_: uuid.UUID = Field(...) - name: Optional[str] - - class Config: - fields = {"id_": "id"} - - -class ProductIn(BaseModel): - name: str = Field(..., min_length=1) - units: str - fraction: Decimal = Field(ge=1, default=1) - fraction_units: str - product_yield: Decimal = Field(gt=0, le=1, default=1) - product_group: ProductGroupLink = Field(...) - price: Decimal = Field(ge=0, multiple_of=0.01, default=0) - sale_price: Decimal = Field(ge=0, multiple_of=0.01, default=0) - is_active: bool - is_purchased: bool - is_sold: bool - - class Config: - fields = {"id_": "id"} - anystr_strip_whitespace = True - alias_generator = to_camel - - -class Product(ProductIn): - id_: uuid.UUID - code: int - account: AccountLink = Field(...) - is_fixture: bool - - -class Recipe(BaseModel): - id_: uuid.UUID - product_id: uuid.UUID - - quantity: Decimal - cost_price: Decimal - sale_price: Decimal - notes: str - - valid_from: date - valid_to: date - - effective_from: date - effective_to: date - - -class RecipeItem(BaseModel): - id_: uuid.UUID - recipe_id: uuid.UUID - product_id: uuid.UUID - quantity: int - price: int - - -class CostCentreIn(BaseModel): - name: str = Field(..., min_length=1) - - -class CostCentre(CostCentreIn): - id_: uuid.UUID - is_fixture: bool - - class Config: - fields = {"id_": "id"} - anystr_strip_whitespace = True - alias_generator = to_camel - - -class AccountBase(BaseModel): - name: str = Field(..., min_length=1) - is_starred: bool - is_active: bool - cost_centre: CostCentreLink - - class Config: - fields = {"id_": "id"} - anystr_strip_whitespace = True - alias_generator = to_camel - - -class AccountIn(AccountBase): - type: int - is_reconcilable: bool - - -class Account(AccountIn): - id_: uuid.UUID - code: int - is_fixture: bool - - -class DbSetting(BaseModel): - id_: uuid.UUID - name: str - data: bytes - - -class AccountType(BaseModel): - id_: int - name: str - - class Config: - fields = {"id_": "id"} diff --git a/brewman/brewman/schemas/net_transactions.py b/brewman/brewman/schemas/net_transactions.py new file mode 100644 index 00000000..7593479f --- /dev/null +++ b/brewman/brewman/schemas/net_transactions.py @@ -0,0 +1,42 @@ +from datetime import date, datetime +from decimal import Decimal +from typing import List, Optional + +from brewman.schemas import to_camel +from pydantic import Field, validator +from pydantic.main import BaseModel + + +class NetTransactionsItem(BaseModel): + type_: str + name: str + debit: Optional[Decimal] + credit: Optional[Decimal] + + class Config: + anystr_strip_whitespace = True + alias_generator = to_camel + json_encoders = {date: lambda v: v.strftime("%d-%b-%Y")} + + +class NetTransactions(BaseModel): + start_date: date + finish_date: date + body: List[NetTransactionsItem] + + 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/permission.py b/brewman/brewman/schemas/permission.py new file mode 100644 index 00000000..a43d2fdf --- /dev/null +++ b/brewman/brewman/schemas/permission.py @@ -0,0 +1,12 @@ +import uuid + +from pydantic.main import BaseModel + + +class PermissionItem(BaseModel): + id_: uuid.UUID + name: str + enabled: bool + + class Config: + fields = {"id_": "id"} diff --git a/brewman/brewman/schemas/product.py b/brewman/brewman/schemas/product.py new file mode 100644 index 00000000..090e3b8d --- /dev/null +++ b/brewman/brewman/schemas/product.py @@ -0,0 +1,51 @@ +import uuid + +from decimal import Decimal +from typing import Optional + +from brewman.schemas import to_camel +from brewman.schemas.product_group import ProductGroupLink +from pydantic import BaseModel, Field + + +class ProductLink(BaseModel): + id_: uuid.UUID = Field(...) + name: Optional[str] + + class Config: + fields = {"id_": "id"} + + +class ProductIn(BaseModel): + name: str = Field(..., min_length=1) + units: str + fraction: Decimal = Field(ge=1, default=1) + fraction_units: str + product_yield: Decimal = Field(gt=0, le=1, default=1) + product_group: ProductGroupLink = Field(...) + price: Decimal = Field(ge=0, multiple_of=0.01, default=0) + sale_price: Decimal = Field(ge=0, multiple_of=0.01, default=0) + is_active: bool + is_purchased: bool + is_sold: bool + + class Config: + fields = {"id_": "id"} + anystr_strip_whitespace = True + alias_generator = to_camel + + +class Product(ProductIn): + id_: uuid.UUID + code: int + is_fixture: bool + + +class ProductBlank(ProductIn): + name: str + product_group: Optional[ProductGroupLink] + is_fixture: bool + + class Config: + anystr_strip_whitespace = True + alias_generator = to_camel diff --git a/brewman/brewman/schemas/product_group.py b/brewman/brewman/schemas/product_group.py new file mode 100644 index 00000000..c7af927e --- /dev/null +++ b/brewman/brewman/schemas/product_group.py @@ -0,0 +1,36 @@ +import uuid + +from typing import Optional + +from brewman.schemas import to_camel +from pydantic import BaseModel, Field + + +class ProductGroupIn(BaseModel): + name: str = Field(..., min_length=1) + + +class ProductGroup(ProductGroupIn): + id_: uuid.UUID + is_fixture: bool + + class Config: + anystr_strip_whitespace = True + alias_generator = to_camel + + +class ProductGroupBlank(ProductGroupIn): + name: str + is_fixture: bool + + class Config: + anystr_strip_whitespace = True + alias_generator = to_camel + + +class ProductGroupLink(BaseModel): + id_: uuid.UUID = Field(...) + name: Optional[str] + + class Config: + fields = {"id_": "id"} diff --git a/brewman/brewman/schemas/product_ledger.py b/brewman/brewman/schemas/product_ledger.py new file mode 100644 index 00000000..5cbfb023 --- /dev/null +++ b/brewman/brewman/schemas/product_ledger.py @@ -0,0 +1,61 @@ +import uuid + +from datetime import date, datetime +from decimal import Decimal +from typing import List, Optional + +from brewman.schemas import to_camel +from brewman.schemas.product import ProductLink +from pydantic import validator +from pydantic.main import BaseModel + + +class ProductLedgerItem(BaseModel): + id_: Optional[uuid.UUID] + date_: date + name: str + url: List[str] + type_: str + narration: str + debit_quantity: Optional[Decimal] + debit_amount: Optional[Decimal] + credit_quantity: Optional[Decimal] + credit_amount: Optional[Decimal] + running_quantity: Decimal + running_amount: Decimal + posted: bool + + @validator("date_", pre=True) + def parse_date(cls, value): + 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 ProductLedger(BaseModel): + start_date: date + finish_date: date + product: Optional[ProductLink] + body: List[ProductLedgerItem] + + 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/profit_loss.py b/brewman/brewman/schemas/profit_loss.py new file mode 100644 index 00000000..392b02fd --- /dev/null +++ b/brewman/brewman/schemas/profit_loss.py @@ -0,0 +1,44 @@ +from datetime import date, datetime +from decimal import Decimal +from typing import List, Optional + +from brewman.schemas import to_camel +from pydantic import validator +from pydantic.main import BaseModel + + +class ProfitLossItem(BaseModel): + group: Optional[str] + name: Optional[str] + amount: Optional[Decimal] + total: Optional[Decimal] + order: int + + class Config: + anystr_strip_whitespace = True + alias_generator = to_camel + json_encoders = {date: lambda v: v.strftime("%d-%b-%Y")} + + +class ProfitLoss(BaseModel): + start_date: date + finish_date: date + body: List[ProfitLossItem] + footer: Optional[ProfitLossItem] + + 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/purchase_entries.py b/brewman/brewman/schemas/purchase_entries.py new file mode 100644 index 00000000..f3c7e5c7 --- /dev/null +++ b/brewman/brewman/schemas/purchase_entries.py @@ -0,0 +1,52 @@ +from datetime import date, datetime +from decimal import Decimal +from typing import List + +from brewman.schemas import to_camel +from pydantic import validator +from pydantic.main import BaseModel + + +class PurchaseEntriesItem(BaseModel): + date_: date + url: List[str] + supplier: str + product: str + quantity: Decimal + rate: Decimal + tax: Decimal + discount: Decimal + amount: Decimal + + @validator("date_", pre=True) + def parse_date(cls, value): + if isinstance(value, date): + return value + return datetime.strptime(value, "%d-%b-%Y").date() + + class Config: + anystr_strip_whitespace = True + alias_generator = to_camel + + +class PurchaseEntries(BaseModel): + start_date: date + finish_date: date + body: List[PurchaseEntriesItem] + + 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/purchases.py b/brewman/brewman/schemas/purchases.py new file mode 100644 index 00000000..376e20d5 --- /dev/null +++ b/brewman/brewman/schemas/purchases.py @@ -0,0 +1,43 @@ +from datetime import date, datetime +from decimal import Decimal +from typing import List, Optional + +from brewman.schemas import to_camel +from pydantic import validator +from pydantic.main import BaseModel + + +class PurchasesItem(BaseModel): + name: str + quantity: Decimal + rate: Decimal + amount: Decimal + url: List[str] + + class Config: + anystr_strip_whitespace = True + alias_generator = to_camel + + +class Purchases(BaseModel): + start_date: date + finish_date: date + body: List[PurchasesItem] + footer: Optional[PurchasesItem] + + 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/raw_material_cost.py b/brewman/brewman/schemas/raw_material_cost.py new file mode 100644 index 00000000..05cf5e27 --- /dev/null +++ b/brewman/brewman/schemas/raw_material_cost.py @@ -0,0 +1,53 @@ +import uuid + +from datetime import date, datetime +from decimal import Decimal +from typing import List, Optional + +from brewman.schemas import to_camel +from pydantic import validator +from pydantic.main import BaseModel + + +class RawMaterialCostItem(BaseModel): + name: Optional[str] + issue: Optional[Decimal] + sale: Optional[Decimal] + rmc: Optional[Decimal] + url: Optional[List[str]] + + group: Optional[str] + quantity: Optional[Decimal] + net: Optional[Decimal] + gross: Optional[Decimal] + + heading: Optional[bool] + + class Config: + anystr_strip_whitespace = True + alias_generator = to_camel + + +class RawMaterialCost(BaseModel): + id_: Optional[uuid.UUID] + start_date: date + finish_date: date + body: List[RawMaterialCostItem] + footer: Optional[RawMaterialCostItem] + + 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/recipe.py b/brewman/brewman/schemas/recipe.py new file mode 100644 index 00000000..d510df4a --- /dev/null +++ b/brewman/brewman/schemas/recipe.py @@ -0,0 +1,30 @@ +import uuid + +from datetime import date +from decimal import Decimal + +from pydantic import BaseModel + + +class Recipe(BaseModel): + id_: uuid.UUID + product_id: uuid.UUID + + quantity: Decimal + cost_price: Decimal + sale_price: Decimal + notes: str + + valid_from: date + valid_till: date + + effective_from: date + effective_till: date + + +class RecipeItem(BaseModel): + id_: uuid.UUID + recipe_id: uuid.UUID + product_id: uuid.UUID + quantity: int + price: int diff --git a/brewman/brewman/schemas/reports.py b/brewman/brewman/schemas/reports.py deleted file mode 100644 index ee259ae4..00000000 --- a/brewman/brewman/schemas/reports.py +++ /dev/null @@ -1,540 +0,0 @@ -import uuid - -from datetime import date, datetime -from decimal import Decimal -from typing import List, Optional - -from brewman.schemas import to_camel -from brewman.schemas.master import AccountLink, ProductLink -from pydantic import BaseModel, Field, validator - - -class LedgerItem(BaseModel): - id_: Optional[uuid.UUID] - date_: date - name: str - url: List[str] - type_: str - narration: str - debit: Decimal - credit: Decimal - posted: bool - - @validator("date_", pre=True) - def parse_date(cls, value): - 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 Ledger(BaseModel): - start_date: date - finish_date: date - account: Optional[AccountLink] - body: List[LedgerItem] - - 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() - - -class BalanceSheetItem(BaseModel): - name: Optional[str] - group: Optional[str] - amount: Optional[Decimal] - sub_amount: Optional[Decimal] - order: int - - class Config: - anystr_strip_whitespace = True - alias_generator = to_camel - - -class BalanceSheet(BaseModel): - date_: date - body: List[BalanceSheetItem] - footer: Optional[BalanceSheetItem] - - class Config: - anystr_strip_whitespace = True - alias_generator = to_camel - json_encoders = {date: lambda v: v.strftime("%d-%b-%Y")} - - @validator("date_", pre=True) - def parse_date(cls, value): - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - -class CashFlowItem(BaseModel): - name: str - url: Optional[List[str]] - amount: Decimal = Field(multiple_of=0.01) - - class Config: - anystr_strip_whitespace = True - alias_generator = to_camel - - -class CashFlowBody(BaseModel): - operating: List[CashFlowItem] - investing: List[CashFlowItem] - financing: List[CashFlowItem] - details: List[CashFlowItem] - - class Config: - anystr_strip_whitespace = True - alias_generator = to_camel - - -class CashFlow(BaseModel): - start_date: date - finish_date: date - body: CashFlowBody - footer: Optional[List[CashFlowItem]] - - 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() - - -class ClosingStockItem(BaseModel): - product: str - group: str - quantity: Decimal - amount: Decimal - - class Config: - anystr_strip_whitespace = True - alias_generator = to_camel - - -class ClosingStock(BaseModel): - date_: date - body: List[ClosingStockItem] - - class Config: - anystr_strip_whitespace = True - alias_generator = to_camel - json_encoders = {date: lambda v: v.strftime("%d-%b-%Y")} - - @validator("date_", pre=True) - def parse_date(cls, value): - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - -class DaybookItem(BaseModel): - id_: Optional[uuid.UUID] - date_: date - url: List[str] - type_: str - narration: str - debit_text: Optional[str] - debit_amount: Optional[Decimal] - credit_text: Optional[str] - credit_amount: Optional[Decimal] - posted: bool - - @validator("date_", pre=True) - def parse_date(cls, value): - 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 Daybook(BaseModel): - start_date: date - finish_date: date - body: List[DaybookItem] - - 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() - - -class NetTransactionsItem(BaseModel): - type_: str - name: str - debit: Optional[Decimal] = Field(multiple_of=0.01) - credit: Optional[Decimal] = Field(multiple_of=0.01) - - class Config: - anystr_strip_whitespace = True - alias_generator = to_camel - json_encoders = {date: lambda v: v.strftime("%d-%b-%Y")} - - -class NetTransactions(BaseModel): - start_date: date - finish_date: date - body: List[NetTransactionsItem] - - 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() - - -class ProductLedgerItem(BaseModel): - id_: Optional[uuid.UUID] - date_: date - name: str - url: List[str] - type_: str - narration: str - debit_quantity: Optional[Decimal] - debit_amount: Optional[Decimal] - credit_quantity: Optional[Decimal] - credit_amount: Optional[Decimal] - running_quantity: Decimal - running_amount: Decimal - posted: bool - - @validator("date_", pre=True) - def parse_date(cls, value): - 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 ProductLedger(BaseModel): - start_date: date - finish_date: date - product: Optional[ProductLink] - body: List[ProductLedgerItem] - - 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() - - -class ProfitLossItem(BaseModel): - group: Optional[str] - name: Optional[str] - amount: Optional[Decimal] - total: Optional[Decimal] - order: int - - class Config: - anystr_strip_whitespace = True - alias_generator = to_camel - json_encoders = {date: lambda v: v.strftime("%d-%b-%Y")} - - -class ProfitLoss(BaseModel): - start_date: date - finish_date: date - body: List[ProfitLossItem] - footer: Optional[ProfitLossItem] - - 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() - - -class PurchaseEntriesItem(BaseModel): - date_: date - url: List[str] - supplier: str - product: str - quantity: Decimal - rate: Decimal - tax: Decimal - discount: Decimal - amount: Decimal - - @validator("date_", pre=True) - def parse_date(cls, value): - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - class Config: - anystr_strip_whitespace = True - alias_generator = to_camel - - -class PurchaseEntries(BaseModel): - start_date: date - finish_date: date - body: List[PurchaseEntriesItem] - - 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() - - -class PurchasesItem(BaseModel): - name: str - quantity: Decimal - rate: Decimal - amount: Decimal - url: List[str] - - class Config: - anystr_strip_whitespace = True - alias_generator = to_camel - - -class Purchases(BaseModel): - start_date: date - finish_date: date - body: List[PurchasesItem] - footer: Optional[PurchasesItem] - - 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() - - -class RawMaterialCostItem(BaseModel): - name: Optional[str] - issue: Optional[Decimal] - sale: Optional[Decimal] - rmc: Optional[Decimal] - url: Optional[List[str]] - - group: Optional[str] - quantity: Optional[Decimal] - net: Optional[Decimal] - gross: Optional[Decimal] - - heading: Optional[bool] - - class Config: - anystr_strip_whitespace = True - alias_generator = to_camel - - -class RawMaterialCost(BaseModel): - id_: Optional[uuid.UUID] - start_date: date - finish_date: date - body: List[RawMaterialCostItem] - footer: Optional[RawMaterialCostItem] - - 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() - - -class StockMovementItem(BaseModel): - id_: uuid.UUID - group: str - name: str - opening: Decimal - purchase: Decimal - issue: Decimal - closing: Decimal - url: List[str] - - class Config: - anystr_strip_whitespace = True - alias_generator = to_camel - - -class StockMovement(BaseModel): - start_date: date - finish_date: date - body: List[StockMovementItem] - - 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() - - -class TrialBalanceItem(BaseModel): - type_: Optional[str] - name: Optional[str] - debit: Optional[Decimal] - credit: Optional[Decimal] - - class Config: - anystr_strip_whitespace = True - alias_generator = to_camel - - -class TrialBalance(BaseModel): - date_: date - body: List[TrialBalanceItem] - - class Config: - anystr_strip_whitespace = True - alias_generator = to_camel - json_encoders = {date: lambda v: v.strftime("%d-%b-%Y")} - - @validator("date_", pre=True) - def parse_date(cls, value): - if isinstance(value, date): - return value - return datetime.strptime(value, "%d-%b-%Y").date() - - -class Unposted(BaseModel): - id_: uuid.UUID - date_: date - url: List[str] - type_: str - narration: str - debit_name: str - debit_amount: Decimal - credit_name: str - credit_amount: Decimal - - @validator("date_", pre=True) - def parse_date(cls, value): - 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")} diff --git a/brewman/brewman/schemas/role.py b/brewman/brewman/schemas/role.py new file mode 100644 index 00000000..c3c3e5db --- /dev/null +++ b/brewman/brewman/schemas/role.py @@ -0,0 +1,50 @@ +import uuid + +from typing import List + +from brewman.schemas.permission import PermissionItem +from pydantic import Field +from pydantic.main import BaseModel + + +class RoleIn(BaseModel): + name: str = Field(..., min_length=1) + permissions: List[PermissionItem] + + class Config: + anystr_strip_whitespace = True + + +class Role(RoleIn): + id_: uuid.UUID + + class Config: + fields = {"id_": "id"} + anystr_strip_whitespace = True + + +class RoleList(BaseModel): + id_: uuid.UUID + name: str + permissions: List[str] + + class Config: + fields = {"id_": "id"} + anystr_strip_whitespace = True + + +class RoleItem(BaseModel): + id_: uuid.UUID + name: str + enabled: bool + + class Config: + fields = {"id_": "id"} + + +class RoleBlank(RoleIn): + name: str + + class Config: + fields = {"id_": "id"} + anystr_strip_whitespace = True diff --git a/brewman/brewman/schemas/stock_movement.py b/brewman/brewman/schemas/stock_movement.py new file mode 100644 index 00000000..eb8ebec1 --- /dev/null +++ b/brewman/brewman/schemas/stock_movement.py @@ -0,0 +1,47 @@ +import uuid + +from datetime import date, datetime +from decimal import Decimal +from typing import List + +from brewman.schemas import to_camel +from pydantic import validator +from pydantic.main import BaseModel + + +class StockMovementItem(BaseModel): + id_: uuid.UUID + group: str + name: str + opening: Decimal + purchase: Decimal + issue: Decimal + closing: Decimal + url: List[str] + + class Config: + anystr_strip_whitespace = True + alias_generator = to_camel + + +class StockMovement(BaseModel): + start_date: date + finish_date: date + body: List[StockMovementItem] + + 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/trial_balance.py b/brewman/brewman/schemas/trial_balance.py new file mode 100644 index 00000000..da49ce1a --- /dev/null +++ b/brewman/brewman/schemas/trial_balance.py @@ -0,0 +1,34 @@ +from datetime import date, datetime +from decimal import Decimal +from typing import List, Optional + +from brewman.schemas import to_camel +from pydantic import validator +from pydantic.main import BaseModel + + +class TrialBalanceItem(BaseModel): + type_: Optional[str] + name: Optional[str] + debit: Optional[Decimal] + credit: Optional[Decimal] + + class Config: + anystr_strip_whitespace = True + alias_generator = to_camel + + +class TrialBalance(BaseModel): + date_: date + body: List[TrialBalanceItem] + + class Config: + anystr_strip_whitespace = True + alias_generator = to_camel + json_encoders = {date: lambda v: v.strftime("%d-%b-%Y")} + + @validator("date_", pre=True) + def parse_date(cls, value): + if isinstance(value, date): + return value + return datetime.strptime(value, "%d-%b-%Y").date() diff --git a/brewman/brewman/schemas/unposted.py b/brewman/brewman/schemas/unposted.py new file mode 100644 index 00000000..75a5c27e --- /dev/null +++ b/brewman/brewman/schemas/unposted.py @@ -0,0 +1,31 @@ +import uuid + +from datetime import date, datetime +from decimal import Decimal +from typing import List + +from brewman.schemas import to_camel +from pydantic import BaseModel, validator + + +class Unposted(BaseModel): + id_: uuid.UUID + date_: date + url: List[str] + type_: str + narration: str + debit_name: str + debit_amount: Decimal + credit_name: str + credit_amount: Decimal + + @validator("date_", pre=True) + def parse_date(cls, value): + 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")} diff --git a/brewman/brewman/schemas/user.py b/brewman/brewman/schemas/user.py new file mode 100644 index 00000000..8f42d933 --- /dev/null +++ b/brewman/brewman/schemas/user.py @@ -0,0 +1,53 @@ +import uuid + +from typing import List + +from brewman.schemas import to_camel +from brewman.schemas.role import RoleItem +from pydantic import Field +from pydantic.main import BaseModel + + +class UserIn(BaseModel): + name: str = Field(..., min_length=1) + password: str + locked_out: bool + roles: List[RoleItem] + + class Config: + anystr_strip_whitespace = True + alias_generator = to_camel + + +class User(UserIn): + id_: uuid.UUID + + class Config: + anystr_strip_whitespace = True + alias_generator = to_camel + + +class UserList(BaseModel): + id_: uuid.UUID + name: str + roles: List[str] + + class Config: + fields = {"id_": "id"} + anystr_strip_whitespace = True + + +class UserToken(BaseModel): + id_: uuid.UUID + name: str + locked_out: bool = None + password: str + permissions: List[str] + + +class UserBlank(UserIn): + name: str + + class Config: + anystr_strip_whitespace = True + alias_generator = to_camel diff --git a/brewman/brewman/schemas/voucher.py b/brewman/brewman/schemas/voucher.py index 5c9520d6..4250c8cc 100644 --- a/brewman/brewman/schemas/voucher.py +++ b/brewman/brewman/schemas/voucher.py @@ -6,12 +6,10 @@ from decimal import Decimal from typing import List, Optional from brewman.schemas import to_camel -from brewman.schemas.master import ( - AccountLink, - CostCentreLink, - EmployeeLink, - ProductLink, -) +from brewman.schemas.account import AccountLink +from brewman.schemas.cost_centre import CostCentreLink +from brewman.schemas.employee import EmployeeLink +from brewman.schemas.product import ProductLink from fastapi import Form from pydantic import BaseModel, Field, validator diff --git a/overlord/src/app/core/product.ts b/overlord/src/app/core/product.ts index 208748aa..20597861 100644 --- a/overlord/src/app/core/product.ts +++ b/overlord/src/app/core/product.ts @@ -1,4 +1,3 @@ -import { Account } from './account'; import { ProductGroup } from './product-group'; export class Product { @@ -15,8 +14,7 @@ export class Product { isFixture: boolean; isPurchased: boolean; isSold: boolean; - productGroup: ProductGroup; - account: Account; + productGroup?: ProductGroup; public constructor(init?: Partial) { this.code = 0; @@ -31,8 +29,6 @@ export class Product { this.isFixture = false; this.isPurchased = true; this.isSold = false; - this.productGroup = new ProductGroup(); - this.account = new Account(); Object.assign(this, init); } } diff --git a/overlord/src/app/product/product-detail/product-detail.component.ts b/overlord/src/app/product/product-detail/product-detail.component.ts index d6f79e9e..f7411992 100644 --- a/overlord/src/app/product/product-detail/product-detail.component.ts +++ b/overlord/src/app/product/product-detail/product-detail.component.ts @@ -67,7 +67,7 @@ export class ProductDetailComponent implements OnInit, AfterViewInit { isPurchased: this.item.isPurchased, isSold: this.item.isSold, isActive: this.item.isActive, - productGroup: this.item.productGroup.id ? this.item.productGroup.id : '', + productGroup: this.item.productGroup ? this.item.productGroup.id : '', }); } @@ -126,6 +126,9 @@ export class ProductDetailComponent implements OnInit, AfterViewInit { this.item.isPurchased = formModel.isPurchased; this.item.isSold = formModel.isSold; this.item.isActive = formModel.isActive; + if (this.item.productGroup === undefined) { + this.item.productGroup = new ProductGroup(); + } this.item.productGroup.id = formModel.productGroup; return this.item; } diff --git a/overlord/src/app/product/product-list/product-list-datasource.ts b/overlord/src/app/product/product-list/product-list-datasource.ts index 2adea36d..5242e6ea 100644 --- a/overlord/src/app/product/product-list/product-list-datasource.ts +++ b/overlord/src/app/product/product-list/product-list-datasource.ts @@ -6,6 +6,7 @@ import { merge, Observable, of as observableOf } from 'rxjs'; import { map, tap } from 'rxjs/operators'; import { Product } from '../../core/product'; +import { ProductGroup } from '../../core/product-group'; /** Simple sort comparator for example ID/Name columns (for client-side sorting). */ function compare(a: string | number, b: string | number, isAsc: boolean) { @@ -82,7 +83,11 @@ export class ProductListDataSource extends DataSource { case 'name': return compare(a.name, b.name, isAsc); case 'productGroup': - return compare(a.productGroup.name, b.productGroup.name, isAsc); + return compare( + (a.productGroup as ProductGroup).name, + (b.productGroup as ProductGroup).name, + isAsc, + ); default: return 0; } diff --git a/overlord/src/app/product/product-list/product-list.component.html b/overlord/src/app/product/product-list/product-list.component.html index 3b25de84..bc911c8c 100644 --- a/overlord/src/app/product/product-list/product-list.component.html +++ b/overlord/src/app/product/product-list/product-list.component.html @@ -46,7 +46,7 @@ Cost Price - {{ row.costPrice | currency: 'INR' }} + {{ row.price | currency: 'INR' }} @@ -58,7 +58,7 @@ Product Group - {{ row.productGroup }} + {{ row.productGroup?.name }}