148 lines
4.0 KiB
Python
148 lines
4.0 KiB
Python
import uuid
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Security, status
|
|
from sqlalchemy import func
|
|
from sqlalchemy.orm import Session
|
|
|
|
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
|
|
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
# Dependency
|
|
def get_db() -> Session:
|
|
try:
|
|
db = SessionLocal()
|
|
yield db
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
@router.post("/{id_}")
|
|
def rebase(
|
|
id_: uuid.UUID,
|
|
item: ResetStock,
|
|
db: Session = Depends(get_db),
|
|
user: UserToken = Security(get_user, scopes=["reset-stock"]),
|
|
):
|
|
product: Product = db.query(Product).filter(Product.id == id_).first()
|
|
|
|
if item.reset_date > item.stock_date:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
|
detail="Reset cannot be after the stock date",
|
|
)
|
|
|
|
change = round(item.quantity, 2) - get_closing_stock(
|
|
product, item.stock_date, db=db
|
|
)
|
|
if change == 0:
|
|
return {"No Change Needed"}
|
|
final = get_closing_stock(product, db=db)
|
|
if final + change < 0:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
|
detail="Current Quantity will get negative. Cannot proceed",
|
|
)
|
|
|
|
batch = get_last_batch(product, db)
|
|
set_batches(batch, final + change, db)
|
|
|
|
create_voucher(batch, change, item.reset_date, user.id_, db)
|
|
db.commit()
|
|
return {}
|
|
|
|
|
|
def get_closing_stock(product, finish_date=None, db=None):
|
|
query = (
|
|
db.query(func.sum(Inventory.quantity * Journal.debit))
|
|
.join(Voucher)
|
|
.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())
|
|
)
|
|
if finish_date is not None:
|
|
query = query.filter(Voucher.date <= finish_date)
|
|
query = query.one()
|
|
return 0 if query is None else query[0]
|
|
|
|
|
|
def get_last_batch(product, db):
|
|
batch = (
|
|
db.query(Batch)
|
|
.filter(Batch.product_id == product.id)
|
|
.order_by(Batch.name.desc())
|
|
.first()
|
|
)
|
|
if batch is None:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
|
detail="Details for the product exist. Just add a purchase entry",
|
|
)
|
|
return batch
|
|
|
|
|
|
def set_batches(batch, quantity, db):
|
|
batch.quantity_remaining = quantity
|
|
batches = (
|
|
db.query(Batch)
|
|
.filter(Batch.id != batch.id)
|
|
.filter(Batch.product_id == batch.product_id)
|
|
)
|
|
for item in batches:
|
|
item.quantity_remaining = 0
|
|
pass
|
|
|
|
|
|
def create_voucher(batch, quantity, date_, user_id, db):
|
|
voucher = Voucher(
|
|
date=date_,
|
|
narration="Product Reset",
|
|
user_id=user_id,
|
|
type_=VoucherType.by_name("Issue"),
|
|
)
|
|
db.add(voucher)
|
|
|
|
if quantity > 0:
|
|
source = CostCentre.cost_centre_overall()
|
|
destination = CostCentre.cost_centre_purchase()
|
|
else:
|
|
destination = CostCentre.cost_centre_overall()
|
|
source = CostCentre.cost_centre_purchase()
|
|
|
|
inventory = Inventory(
|
|
product_id=batch.product.id,
|
|
quantity=abs(quantity),
|
|
rate=batch.rate,
|
|
tax=batch.tax,
|
|
discount=batch.discount,
|
|
batch=batch,
|
|
)
|
|
voucher.inventories.append(inventory)
|
|
db.add(inventory)
|
|
|
|
amount = round(inventory.amount, 2)
|
|
source = Journal(
|
|
debit=-1,
|
|
account_id=AccountBase.all_purchases(),
|
|
amount=amount,
|
|
cost_centre_id=source,
|
|
)
|
|
voucher.journals.append(source)
|
|
db.add(source)
|
|
destination = Journal(
|
|
debit=1,
|
|
account_id=AccountBase.all_purchases(),
|
|
amount=amount,
|
|
cost_centre_id=destination,
|
|
)
|
|
voucher.journals.append(destination)
|
|
db.add(destination)
|