brewman/brewman/routers/product.py

257 lines
8.2 KiB
Python

import uuid
from typing import Optional
from fastapi import APIRouter, HTTPException, status, Depends, Security
from sqlalchemy import desc
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import joinedload_all, Session
from ..schemas.auth import UserToken
import brewman.schemas.master as schemas
from ..core.security import get_current_active_user as get_user
from ..db.session import SessionLocal
from ..models.master import Product, Account
from ..models.voucher import Voucher, Batch, Inventory, VoucherType
router = APIRouter()
# Dependency
def get_db():
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.post("/", response_model=schemas.Product)
def save(
data: schemas.ProductIn, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["products"]),
):
try:
item = Product(
name=data.name,
units=data.units,
fraction=data.fraction,
fraction_units=data.fraction_units,
product_yield=data.product_yield,
product_group_id=data.product_group.id_,
account_id=Account.all_purchases(),
price=data.price,
sale_price=data.sale_price,
is_active=data.is_active,
is_purchased=data.is_purchased,
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),
)
except Exception:
db.rollback()
raise
@router.put("/{id_}", response_model=schemas.Product)
def update(
id_: uuid.UUID,
data: schemas.ProductIn,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["products"]),
):
try:
item: Product = db.query(Product).filter(Product.id == id_).first()
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.",
)
item.name = data.name
item.units = data.units
item.fraction = data.fraction
item.fraction_units = data.fraction_units
item.product_yield = data.product_yield
item.product_group_id = data.product_group.id_
item.account_id = Account.all_purchases()
item.price = data.price
item.sale_price = data.sale_price
item.is_active = data.is_active
item.is_purchased = data.is_purchased
item.is_sold = data.is_sold
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),
)
except Exception:
db.rollback()
raise
@router.delete("/{id_}")
def delete(
id_: uuid.UUID, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["products"]),
):
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)
else:
db.abort()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Cannot delete account because {reason}",
)
@router.get("/")
def show_blank(
db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["products"]),
):
return product_info(None, db)
@router.get("/list")
def show_list(db: Session = Depends(get_db), user: UserToken = Depends(get_user)):
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,
}
for item in db.query(Product)
.order_by(desc(Product.is_active))
.order_by(Product.product_group_id)
.order_by(Product.name)
.all()
]
@router.get("/query")
async def show_term(
q: str = None,
a: bool = None,
c: int = None,
p: bool = None,
e: bool = False,
db: Session = Depends(get_db),
current_user: UserToken = Depends(get_user),
):
count = c
extended = e
list_ = []
for index, item in enumerate(Product.query(q, p, a, db)):
list_.append(
{
"id": item.id,
"name": item.full_name,
"price": item.price,
"units": item.units,
"fraction": item.fraction,
"fractionUnits": item.fraction_units,
"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 count is not None and index == count - 1:
break
return sorted(list_, key=lambda k: k["name"])
@router.get("/{id_}")
def show_id(
id_: uuid.UUID, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["accounts"]),
):
return product_info(id_, db)
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},
}
return product
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()
query = (
db.query(Voucher)
.options(joinedload_all(Voucher.inventories, Inventory.product, innerjoin=True))
.filter(Voucher.inventories.any(Inventory.product_id == product.id))
.all()
)
for voucher in query:
others, sus_inv, prod_inv = False, None, None
for inventory in voucher.inventories:
if inventory.product_id == product.id:
prod_inv = inventory
elif inventory.product_id == Product.suspense():
sus_inv = inventory
else:
others = True
if not others and voucher.type == VoucherType.by_id("Issue"):
db.delete(voucher)
else:
if sus_inv is None:
prod_inv.product = suspense_product
prod_inv.quantity = prod_inv.amount
prod_inv.rate = 1
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}"
else:
sus_inv.quantity += prod_inv.amount
db.delete(prod_inv)
voucher.narration += f"\nDeleted \u20B9{prod_inv.amount:,.2f} of {product.name}"
for batch in product.batches:
db.delete(batch)
db.delete(product)