from datetime import date, datetime from decimal import Decimal import brewman.schemas.reports as schemas from fastapi import APIRouter, Depends, Request, Security from sqlalchemy.orm import Session from sqlalchemy.sql.expression import func 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.master import CostCentre, Product from ...models.voucher import Inventory, Journal, Voucher, VoucherType from ...schemas.auth import UserToken router = APIRouter() # Dependency def get_db() -> Session: try: db = SessionLocal() yield db finally: db.close() @router.get("", response_model=schemas.StockMovement) def report_blank( request: Request, user: UserToken = Security(get_user, scopes=["stock-movement"]), ): return { "startDate": get_start_date(request.session), "finishDate": get_finish_date(request.session), "body": [], } @router.get("/{start}/{finish}", response_model=schemas.StockMovement) def report_data( start: str, finish: str, request: Request, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["stock-movement"]), ): 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): dict_ = {} quantity_sum = func.sum(Journal.debit * Inventory.quantity).label("quantity") openings = ( db.query(Product, quantity_sum) .join(Product.inventories) .join(Inventory.voucher) .join(Voucher.journals) .filter(Voucher.date < start_date) .filter(Journal.cost_centre_id == CostCentre.cost_centre_purchase()) .group_by(Product) .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)], } purchases = ( db.query(Product, quantity_sum) .join(Product.inventories) .join(Inventory.voucher) .join(Voucher.journals) .filter(Voucher.date >= start_date) .filter(Voucher.date <= finish_date) .filter(Voucher.type != VoucherType.by_name("Issue").id) .filter(Journal.cost_centre_id == CostCentre.cost_centre_purchase()) .group_by(Product) .all() ) for product, quantity in purchases: if product.id in dict_: 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)], } issues = ( db.query(Product, quantity_sum) .join(Product.inventories) .join(Inventory.voucher) .join(Voucher.journals) .filter(Voucher.date >= start_date) .filter(Voucher.date <= finish_date) .filter(Voucher.type == VoucherType.by_name("Issue").id) .filter(Journal.cost_centre_id == CostCentre.cost_centre_purchase()) .group_by(Product) .all() ) for product, quantity in issues: if product.id in dict_: 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)], } 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()) 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: list_.remove(item) else: item["closing"] = closing return list_