brewman/brewman/brewman/routers/reports/product_ledger.py

184 lines
5.6 KiB
Python

import uuid
from datetime import date, datetime
import brewman.schemas.reports as schemas
from fastapi import APIRouter, Depends, Request, Security
from sqlalchemy.orm import Session, joinedload
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.ProductLedger)
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": [],
}
@router.get("/{id_}", response_model=schemas.ProductLedger)
def show_data(
id_: uuid.UUID,
request: Request,
s: str = None,
f: str = None,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["product-ledger"]),
):
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)
body = build_report(
product.id,
datetime.strptime(start_date, "%d-%b-%Y"),
datetime.strptime(finish_date, "%d-%b-%Y"),
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,
}
def build_report(
product_id: uuid.UUID, start_date: date, finish_date: date, db: Session
):
body = []
running_total_q, running_total_a, opening = opening_balance(
product_id, start_date, db
)
body.append(opening)
query = (
db.query(Voucher, Inventory, Journal)
.options(
joinedload(Journal.account, innerjoin=True),
joinedload(Journal.cost_centre, innerjoin=True),
)
.filter(Voucher.id == Inventory.voucher_id)
.filter(Voucher.id == Journal.voucher_id)
.filter(Inventory.product_id == product_id)
.filter(Journal.cost_centre_id != CostCentre.cost_centre_purchase())
.filter(Voucher.date >= start_date)
.filter(Voucher.date <= finish_date)
.order_by(Voucher.date)
.order_by(Voucher.last_edit_date)
.all()
)
for row in query:
journal_debit = row.Journal.debit * -1
name = (
row.Journal.cost_centre.name
if row.Voucher.type == VoucherType.by_name("Issue").id
else row.Journal.account.name
)
debit_q = row.Inventory.quantity if journal_debit == 1 else None
debit_a = row.Inventory.amount if journal_debit == 1 else None
credit_q = row.Inventory.quantity if journal_debit != 1 else None
credit_a = row.Inventory.amount if journal_debit != 1 else None
running_total_q += row.Inventory.quantity * journal_debit
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": [
"/",
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,
}
)
return body
def opening_balance(product_id: uuid.UUID, start_date: date, db: Session):
quantity, amount = (
db.query(
func.sum(Inventory.quantity * Journal.debit),
func.sum(Inventory.amount * Journal.debit),
)
.join(Inventory.voucher)
.join(Voucher.journals)
.filter(Voucher.id == Inventory.voucher_id)
.filter(Voucher.id == Journal.voucher_id)
.filter(Inventory.product_id == product_id)
.filter(Journal.cost_centre_id == CostCentre.cost_centre_purchase())
.filter(Voucher.date < start_date)
.one()
)
if quantity and quantity > 0:
debit_quantity = quantity
debit_amount = amount
else:
debit_quantity = None
debit_amount = None
if quantity is None:
quantity = 0
amount = 0
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,
},
)