diff --git a/brewman/main.py b/brewman/main.py index 514ff2aa..5f5fbac8 100644 --- a/brewman/main.py +++ b/brewman/main.py @@ -14,7 +14,7 @@ from .routers import ( product, product_group, recipe, - login + login, ) from .routers.auth import client, user, role from .db.base_class import Base @@ -31,18 +31,30 @@ app.include_router(login.router, tags=["login"]) app.include_router(account.router, prefix="/api/accounts", tags=["accounts"]) app.include_router(account_types.router, prefix="/api/account-types", tags=["accounts"]) app.include_router(attendance.router, prefix="/api/attendance", tags=["attendance"]) -app.include_router(attendance_types.router, prefix="/api/attendance-types", tags=["attendance"]) -app.include_router(employee_attendance.router, prefix="/api/employee-attendance", tags=["attendance"]) -app.include_router(employee_attendance.router, prefix="/api/employee-attendance", tags=["attendance"]) -app.include_router(attendance_report.router, prefix="/api/attendance-report", tags=["attendance"]) -app.include_router(cost_centre.router, prefix="/api/cost-centres", tags=["cost-centres"]) +app.include_router( + attendance_types.router, prefix="/api/attendance-types", tags=["attendance"] +) +app.include_router( + employee_attendance.router, prefix="/api/employee-attendance", tags=["attendance"] +) +app.include_router( + employee_attendance.router, prefix="/api/employee-attendance", tags=["attendance"] +) +app.include_router( + attendance_report.router, prefix="/api/attendance-report", tags=["attendance"] +) +app.include_router( + cost_centre.router, prefix="/api/cost-centres", tags=["cost-centres"] +) app.include_router(employee.router, prefix="/api/employees", tags=["employees"]) app.include_router(fingerprint.router, prefix="/api/fingerprint", tags=["employees"]) app.include_router(product.router, prefix="/api/products", tags=["products"]) -app.include_router(product_group.router, prefix="/api/product-groups", tags=["products"]) +app.include_router( + product_group.router, prefix="/api/product-groups", tags=["products"] +) app.include_router(recipe.router, prefix="/api/recipes", tags=["products"]) -app.include_router(client.router, prefix="/api/clients", tags=["users"]) +app.include_router(client.router, prefix="/api/clients", tags=["clients"]) app.include_router(role.router, prefix="/api/roles", tags=["users"]) app.include_router(user.router, prefix="/api/users", tags=["users"]) diff --git a/brewman/models/auth.py b/brewman/models/auth.py index 56a2f280..c37e67c3 100644 --- a/brewman/models/auth.py +++ b/brewman/models/auth.py @@ -29,7 +29,13 @@ class Client(Base): login_history = relationship("LoginHistory", backref="client") def __init__( - self, code=None, name=None, enabled=False, otp=None, creation_date=None, id_=None + self, + code=None, + name=None, + enabled=False, + otp=None, + creation_date=None, + id_=None, ): self.code = code self.name = name diff --git a/brewman/models/master.py b/brewman/models/master.py index 47c41915..943c7fda 100644 --- a/brewman/models/master.py +++ b/brewman/models/master.py @@ -32,10 +32,7 @@ class Product(Base): fraction_units = Column("fraction_units", Unicode(255), nullable=False) product_yield = Column("product_yield", Numeric, nullable=False) product_group_id = Column( - "product_group_id", - GUID(), - ForeignKey("product_groups.id"), - nullable=False, + "product_group_id", GUID(), ForeignKey("product_groups.id"), nullable=False, ) account_id = Column("account_id", GUID(), ForeignKey("accounts.id"), nullable=False) price = Column("cost_price", Numeric, nullable=False) @@ -174,8 +171,7 @@ class RecipeItem(Base): __table_args__ = (UniqueConstraint("recipe_id", "product_id"),) id = Column("recipe_item_id", GUID(), primary_key=True, default=uuid.uuid4) - recipe_id = Column( - "recipe_id", GUID(), ForeignKey("recipes.id"), nullable=False) + recipe_id = Column("recipe_id", GUID(), ForeignKey("recipes.id"), nullable=False) product_id = Column("product_id", GUID(), ForeignKey("products.id"), nullable=False) quantity = Column("quantity", Integer, nullable=False) price = Column("price", Integer, nullable=False) @@ -265,7 +261,9 @@ class AccountBase(Base): is_starred = Column("is_starred", Boolean, nullable=False) is_active = Column("is_active", Boolean, nullable=False) is_reconcilable = Column("is_reconcilable", Boolean, nullable=False) - cost_centre_id = Column("cost_centre_id", GUID(), ForeignKey("cost_centres.id"), nullable=False) + cost_centre_id = Column( + "cost_centre_id", GUID(), ForeignKey("cost_centres.id"), nullable=False + ) is_fixture = Column("is_fixture", Boolean, nullable=False) __mapper_args__ = {"polymorphic_on": account_type} diff --git a/brewman/routers/auth/client.py b/brewman/routers/auth/client.py index c098af0f..725b3479 100644 --- a/brewman/routers/auth/client.py +++ b/brewman/routers/auth/client.py @@ -1,51 +1,92 @@ +import traceback import uuid -from sqlalchemy import desc -from brewman.models.auth import Client, LoginHistory -from fastapi import APIRouter +from fastapi import APIRouter, HTTPException, status, Depends, Security +from sqlalchemy import desc +from sqlalchemy.exc import SQLAlchemyError +from sqlalchemy.orm import Session + +from ...schemas.auth import UserToken +from ...core.security import get_current_active_user as get_user +from ...db.session import SessionLocal +from ...models.auth import Client, LoginHistory +import brewman.schemas.auth as schemas router = APIRouter() -@router.put("/{id}") # "Clients" -def update(request): - item = ( - request.dbsession.query(Client) - .filter(Client.id == uuid.UUID(request.matchdict["id"])) - .first() - ) - item.enabled = request.json_body["enabled"] - if item.enabled: - item.otp = None - item.name = request.json_body["name"] - transaction.commit() - return {} +# Dependency +def get_db(): + try: + db = SessionLocal() + yield db + finally: + db.close() -@router.delete("/{id}") # "Clients" -def delete(request): - id_ = request.matchdict.get("id", None) - if id_ is None: - response = Response("Client not found") - response.status_int = 500 - return response - - client = request.dbsession.query(Client).filter(Client.id == uuid.UUID(id_)).first() - request.dbsession.execute( - LoginHistory.__table__.delete(LoginHistory.client_id == client.id) - ) - request.dbsession.delete(client) - transaction.commit() - return {} +@router.put("/{id_}", response_model=schemas.Client) +def update( + id_: uuid.UUID, + data: schemas.ClientIn, + db: Session = Depends(get_db), + user: UserToken = Security(get_user, scopes=["clients"]), +): + try: + item: Client = db.query(Client).filter(Client.id == id_).first() + item.enabled = data.enabled + if item.enabled: + item.otp = None + item.name = data.name + db.commit() + return {} + except SQLAlchemyError as e: + db.rollback() + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e), + ) + except Exception: + db.rollback() + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=traceback.format_exc(), + ) -@router.get("/") # "Clients" -async def show_list(l: bool): - list_ = request.dbsession.query(Client).order_by(Client.name).all() +@router.delete("/{id_}") +def delete( + id_: uuid.UUID, + db: Session = Depends(get_db), + user: UserToken = Security(get_user, scopes=["clients"]), +): + try: + item: Client = db.query(Client).filter(Client.id == id_).first() + db.execute(LoginHistory.__table__.delete(LoginHistory.client_id == item.id)) + db.delete(item) + db.commit() + return {} + except SQLAlchemyError as e: + db.rollback() + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e), + ) + except Exception: + db.rollback() + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=traceback.format_exc(), + ) + + +@router.get("/list") +async def show_list( + db: Session = Depends(get_db), + user: UserToken = Security(get_user, scopes=["clients"]), +): + list_ = db.query(Client).order_by(Client.name).all() clients = [] for item in list_: last_login = ( - request.dbsession.query(LoginHistory) + db.query(LoginHistory) .filter(LoginHistory.client_id == item.id) .order_by(desc(LoginHistory.date)) .first() @@ -69,13 +110,13 @@ async def show_list(l: bool): return clients -@router.get("/{id}") # "Clients" -def show_id(request): - item = ( - request.dbsession.query(Client) - .filter(Client.id == uuid.UUID(request.matchdict["id"])) - .first() - ) +@router.get("/{id_}") +def show_id( + id_: uuid.UUID, + db: Session = Depends(get_db), + user: UserToken = Security(get_user, scopes=["clients"]), +): + item: Client = db.query(Client).filter(Client.id == id_).first() return { "id": item.id, "code": item.code, diff --git a/brewman/routers/auth/role.py b/brewman/routers/auth/role.py index 01f822b5..ad46b4d2 100644 --- a/brewman/routers/auth/role.py +++ b/brewman/routers/auth/role.py @@ -39,8 +39,7 @@ def save( except SQLAlchemyError as e: db.rollback() raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=str(e), + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e), ) except Exception: db.rollback() @@ -66,8 +65,7 @@ def update( except SQLAlchemyError as e: db.rollback() raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=str(e), + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e), ) except Exception: db.rollback() @@ -82,7 +80,9 @@ def add_permissions(role: Role, permissions: List[schemas.PermissionItem], db): gp = [p for p in role.permissions if p.id == permission.id_] gp = None if len(gp) == 0 else gp[0] if permission.enabled and gp is None: - role.permissions.append(db.query(Permission).filter(Permission.id == permission.id_).one()) + role.permissions.append( + db.query(Permission).filter(Permission.id == permission.id_).one() + ) elif not permission.enabled and gp: role.permissions.remove(gp) @@ -122,9 +122,18 @@ def show_blank( @router.get("/list", response_model=List[schemas.RoleList]) -async def show_list(db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["users"])): +async def show_list( + db: Session = Depends(get_db), + user: UserToken = Security(get_user, scopes=["users"]), +): return [ - {"id": item.id, "name": item.name, "permissions": [p.name for p in sorted(item.permissions, key=lambda p: p.name)]} + { + "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() ] @@ -135,7 +144,7 @@ def show_id( db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["users"]), ): - item = db.query(Role).filter(Role.id == id_).first() + item: Role = db.query(Role).filter(Role.id == id_).first() return role_info(item, db) @@ -146,7 +155,7 @@ def role_info(item: Optional[Role], db): "permissions": [ {"id": p.id, "name": p.name, "enabled": False} for p in db.query(Permission).order_by(Permission.name).all() - ] + ], } else: return { @@ -159,5 +168,5 @@ def role_info(item: Optional[Role], db): "enabled": True if item in item.permissions else False, } for item in db.query(Role).order_by(Role.name).all() - ] + ], } diff --git a/brewman/routers/auth/user.py b/brewman/routers/auth/user.py index f4201e88..65ae6b39 100644 --- a/brewman/routers/auth/user.py +++ b/brewman/routers/auth/user.py @@ -31,11 +31,7 @@ def save( user: UserToken = Security(get_user, scopes=["users"]), ): try: - item = User( - name=data.name, - password=data.password, - locked_out=data.locked_out - ) + item = User(name=data.name, password=data.password, locked_out=data.locked_out) db.add(item) add_roles(item, data.roles, db) db.commit() @@ -43,8 +39,7 @@ def save( except SQLAlchemyError as e: db.rollback() raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=str(e), + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e), ) except Exception: db.rollback() @@ -56,8 +51,7 @@ def save( @router.get("/me", response_model=schemas.User) def show_me( - db: Session = Depends(get_db), - user: UserToken = Depends(get_user), + db: Session = Depends(get_db), user: UserToken = Depends(get_user), ): item = db.query(User).filter(User.id == user.id_).first() return user_info(item, db, user) @@ -82,8 +76,7 @@ def update_me( except SQLAlchemyError as e: db.rollback() raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=str(e), + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e), ) except Exception: db.rollback() @@ -112,8 +105,7 @@ def update( except SQLAlchemyError as e: db.rollback() raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=str(e), + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e), ) except Exception: db.rollback() @@ -135,9 +127,9 @@ def add_roles(user: User, roles: List[schemas.RoleItem], db: Session): @router.delete("/{id_}") def delete( - id_: uuid.UUID, - db: Session = Depends(get_db), - user: UserToken = Security(get_user, scopes=["users"]), + id_: uuid.UUID, + db: Session = Depends(get_db), + user: UserToken = Security(get_user, scopes=["users"]), ): try: item: User = db.query(User).filter(User.id == id_).first() @@ -168,15 +160,25 @@ def show_blank( @router.get("/list", response_model=List[schemas.UserList]) -async def show_list(db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["users"])): +async def show_list( + db: Session = Depends(get_db), + user: UserToken = Security(get_user, scopes=["users"]), +): 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)]} + { + "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() ] @router.get("/active") -async def show_active(db: Session = Depends(get_db), user: UserToken = Depends(get_user)): +async def show_active( + db: Session = Depends(get_db), user: UserToken = Depends(get_user) +): return [ {"name": item.name} for item in db.query(User).filter(User.locked_out == False).order_by(User.name) @@ -196,10 +198,12 @@ def show_id( def user_info(item: Optional[User], db: Session, user: UserToken): if item is None: return { - "name": "", "lockedOut": False, "roles": [ + "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 { @@ -214,5 +218,7 @@ def user_info(item: Optional[User], db: Session, user: UserToken): "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 [], + ] + if "advanced-delete" in user.permissions + else [], } diff --git a/brewman/routers/login.py b/brewman/routers/login.py index 87a2668f..88c5c337 100644 --- a/brewman/routers/login.py +++ b/brewman/routers/login.py @@ -49,7 +49,7 @@ async def login_for_access_token( ) ), "user_id": str(user.id), - "locked_out": user.locked_out + "locked_out": user.locked_out, }, expires_delta=access_token_expires, ) diff --git a/brewman/routers/product.py b/brewman/routers/product.py index a2e5f8c0..375f66a0 100644 --- a/brewman/routers/product.py +++ b/brewman/routers/product.py @@ -28,9 +28,9 @@ def get_db(): @router.post("/", response_model=schemas.Product) def save( - data: schemas.ProductIn, - db: Session = Depends(get_db), - user: UserToken = Security(get_user, scopes=["products"]), + data: schemas.ProductIn, + db: Session = Depends(get_db), + user: UserToken = Security(get_user, scopes=["products"]), ): try: item = Product( @@ -45,15 +45,14 @@ def save( sale_price=data.sale_price, is_active=data.is_active, is_purchased=data.is_purchased, - is_sold=data.is_sold + is_sold=data.is_sold, ).create(db) db.commit() return product_info(item.id, db) except SQLAlchemyError as e: db.rollback() raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=str(e), + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e), ) except Exception: db.rollback() @@ -127,7 +126,8 @@ def delete( @router.get("/") # "Products" def show_blank( - db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["products"]) + db: Session = Depends(get_db), + user: UserToken = Security(get_user, scopes=["products"]), ): return product_info(None, db) @@ -151,7 +151,11 @@ def show_list(db: Session = Depends(get_db), user: UserToken = Depends(get_user) "productYield": item.product_yield, "isFixture": item.is_fixture, } - for item in db.query(Product).order_by(desc(Product.is_active)).order_by(Product.product_group_id).order_by(Product.name).all() + for item in db.query(Product) + .order_by(desc(Product.is_active)) + .order_by(Product.product_group_id) + .order_by(Product.name) + .all() ] @@ -180,9 +184,9 @@ async def show_term( "productYield": item.product_yield, "isSold": item.is_sold, "salePrice": item.sale_price, - } if extended else { - "id": item.id, "name": item.full_name, "price": item.price } + if extended + else {"id": item.id, "name": item.full_name, "price": item.price} ) if count is not None and index == count - 1: break @@ -231,13 +235,9 @@ def product_info(id_: Optional[uuid.UUID], db: Session): def delete_with_data(product: Product, db: Session): suspense_product = ( - db.query(Product) - .filter(Product.id == Product.suspense()) - .first() - ) - suspense_batch = ( - db.query(Batch).filter(Batch.id == Batch.suspense()).first() + db.query(Product).filter(Product.id == Product.suspense()).first() ) + suspense_batch = db.query(Batch).filter(Batch.id == Batch.suspense()).first() query = ( db.query(Voucher) .options(joinedload_all(Voucher.inventories, Inventory.product, innerjoin=True)) @@ -264,11 +264,15 @@ def delete_with_data(product: Product, db: Session): prod_inv.tax = 0 prod_inv.discount = 0 prod_inv.batch = suspense_batch - voucher.narration += f"\nSuspense \u20B9{prod_inv.amount:,.2f} is {product.name}" + voucher.narration += ( + f"\nSuspense \u20B9{prod_inv.amount:,.2f} is {product.name}" + ) else: sus_inv.quantity += prod_inv.amount db.delete(prod_inv) - voucher.narration += f"\nDeleted \u20B9{prod_inv.amount:,.2f} of {product.name}" + voucher.narration += ( + f"\nDeleted \u20B9{prod_inv.amount:,.2f} of {product.name}" + ) for batch in product.batches: db.delete(batch) db.delete(product) diff --git a/brewman/schemas/auth.py b/brewman/schemas/auth.py index b9a1698e..54f7a5c6 100644 --- a/brewman/schemas/auth.py +++ b/brewman/schemas/auth.py @@ -5,16 +5,19 @@ from pydantic import BaseModel def to_camel(string: str) -> str: - first, *others = string.split('_') - return ''.join([first] + [word.capitalize() for word in others]) + first, *others = string.split("_") + return "".join([first] + [word.capitalize() for word in others]) -class Client(BaseModel): - id_: uuid.UUID - code: int +class ClientIn(BaseModel): name: str enabled: bool otp: int + + +class Client(ClientIn): + id_: uuid.UUID + code: int creation_date: datetime @@ -25,7 +28,7 @@ class LoginHistory(BaseModel): date: datetime class Config: - fields = {'id_': 'id'} + fields = {"id_": "id"} anystr_strip_whitespace = True alias_generator = to_camel @@ -36,7 +39,7 @@ class PermissionItem(BaseModel): enabled: bool class Config: - fields = {'id_': 'id'} + fields = {"id_": "id"} class RoleIn(BaseModel): @@ -44,7 +47,7 @@ class RoleIn(BaseModel): permissions: List[PermissionItem] class Config: - fields = {'id_': 'id'} + fields = {"id_": "id"} anystr_strip_whitespace = True @@ -58,7 +61,7 @@ class RoleList(BaseModel): permissions: List[str] class Config: - fields = {'id_': 'id'} + fields = {"id_": "id"} anystr_strip_whitespace = True @@ -68,7 +71,7 @@ class RoleItem(BaseModel): enabled: bool class Config: - fields = {'id_': 'id'} + fields = {"id_": "id"} class UserIn(BaseModel): @@ -78,7 +81,7 @@ class UserIn(BaseModel): roles: List[RoleItem] class Config: - fields = {'id_': 'id'} + fields = {"id_": "id"} anystr_strip_whitespace = True alias_generator = to_camel @@ -93,7 +96,7 @@ class UserList(BaseModel): roles: List[str] class Config: - fields = {'id_': 'id'} + fields = {"id_": "id"} anystr_strip_whitespace = True