from datetime import date, datetime, time, timedelta, timezone
from uuid import UUID
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy import literal, or_, select, union_all
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import Session
from app.api.workspace import get_verified_current_user
from app.db.session import get_db
from app.models.branch import Branch
from app.models.inventory import BranchInventory, InventoryTransfer, StockAdjustment
from app.models.membership import MembershipBranch
from app.models.product import Product, ProductCategory, ProductUnitConversion, ProductVariant, Unit
from app.models.user import User
from app.schemas.product import AdjustmentCreate, CategoryCreate, ConversionInput, ConversionUpdate, OpeningStockCreate, ProductCreate, ProductStatusUpdate, ProductUpdate, TransferCreate, UnitCreate, VariantInput, VariantUpdate
from app.services.products import add_product_conversion, add_product_variant, adjust, create_product, generate_product_barcode, record_opening_stock, require_branch, set_product_status, transfer, update_product, update_product_conversion, update_product_variant
from app.core.permissions import Permission
from app.services.rbac import require_branch_access, require_permission
from app.services.workspace import get_membership_for_business, get_workspace

router=APIRouter(tags=["Products & Inventory"])
def access(db,user,business_id):
    if not get_membership_for_business(db,user,business_id): raise HTTPException(404,"Business workspace not found.")
def fail(func):
    try: return func()
    except ValueError as e: raise HTTPException(422,str(e))
    except IntegrityError: raise HTTPException(409,"Duplicate SKU, barcode, or business-scoped name.")

@router.post("/api/businesses/{business_id}/categories",status_code=201)
def add_category(business_id:UUID,data:CategoryCreate,user:User=Depends(get_verified_current_user),db:Session=Depends(get_db)):
    access(db,user,business_id)
    try:
        obj=ProductCategory(business_id=business_id,**data.model_dump());db.add(obj);db.commit();db.refresh(obj);return obj
    except IntegrityError: db.rollback();raise HTTPException(409,"Category name already exists for this business.")
@router.get("/api/businesses/{business_id}/categories")
def categories(business_id:UUID,user:User=Depends(get_verified_current_user),db:Session=Depends(get_db)):
    access(db,user,business_id);return list(db.scalars(select(ProductCategory).where(ProductCategory.business_id==business_id)))
@router.post("/api/businesses/{business_id}/units",status_code=201)
def add_unit(business_id:UUID,data:UnitCreate,user:User=Depends(get_verified_current_user),db:Session=Depends(get_db)):
    access(db,user,business_id); obj=Unit(business_id=business_id,**data.model_dump());db.add(obj);db.commit();db.refresh(obj);return obj
@router.get("/api/businesses/{business_id}/units")
def units(business_id:UUID,user:User=Depends(get_verified_current_user),db:Session=Depends(get_db)):
    access(db,user,business_id);return list(db.scalars(select(Unit).where((Unit.business_id==None)|(Unit.business_id==business_id),Unit.is_active.is_(True))))
@router.post("/api/businesses/{business_id}/products",status_code=201)
def add_product(business_id:UUID,data:ProductCreate,user:User=Depends(get_verified_current_user),db:Session=Depends(get_db)):
    access(db,user,business_id)
    try:return create_product(db,business_id,data)
    except IntegrityError: db.rollback();raise HTTPException(409,"Duplicate SKU or barcode for this business.")
    except ValueError as e: db.rollback();raise HTTPException(422,str(e))
@router.get("/api/businesses/{business_id}/products")
def products(business_id:UUID,status_filter:str='all',barcode:str|None=None,query:str|None=None,user:User=Depends(get_verified_current_user),db:Session=Depends(get_db)):
    access(db,user,business_id);q=select(Product).where(Product.business_id==business_id)
    if status_filter.lower()=='active':q=q.where(Product.is_active.is_(True))
    elif status_filter.lower()=='inactive':q=q.where(Product.is_active.is_(False))
    elif status_filter.lower()!='all':raise HTTPException(422,'status_filter must be all, active, or inactive.')
    if barcode is not None:q=q.where(Product.barcode==barcode.strip())
    if query and query.strip():
        text=f"%{query.strip()}%";q=q.where(or_(Product.name.ilike(text),Product.sku.ilike(text),Product.barcode.ilike(text)))
    return list(db.scalars(q))
@router.get("/api/businesses/{business_id}/products/history")
def product_history(business_id:UUID,product_id:UUID|None=None,branch_id:UUID|None=None,reason:str|None=None,search:str|None=None,start_date:date|None=None,end_date:date|None=None,offset:int=0,limit:int=50,user:User=Depends(get_verified_current_user),db:Session=Depends(get_db)):
    membership=require_permission(db,user,business_id,Permission.MANAGE_INVENTORY)
    if offset<0 or limit<1 or limit>100:raise HTTPException(422,'offset must be non-negative and limit must be between 1 and 100.')
    q=select(StockAdjustment,Product,Branch,User).join(Product,Product.id==StockAdjustment.product_id).join(Branch,Branch.id==StockAdjustment.branch_id).join(User,User.id==StockAdjustment.performed_by_user_id).where(StockAdjustment.business_id==business_id)
    if not membership.all_branches:q=q.where(StockAdjustment.branch_id.in_(select(MembershipBranch.branch_id).where(MembershipBranch.membership_id==membership.id)))
    if product_id:q=q.where(StockAdjustment.product_id==product_id)
    if branch_id:q=q.where(StockAdjustment.branch_id==branch_id)
    if reason:q=q.where(StockAdjustment.reason==reason.upper())
    if search and search.strip():
        text=f"%{search.strip()}%";q=q.where(or_(Product.name.ilike(text),Product.sku.ilike(text),Product.barcode.ilike(text)))
    if start_date:q=q.where(StockAdjustment.created_at>=datetime.combine(start_date,time.min,tzinfo=timezone.utc))
    if end_date:q=q.where(StockAdjustment.created_at<=datetime.combine(end_date,time.max,tzinfo=timezone.utc))
    rows=db.execute(q.order_by(StockAdjustment.created_at.desc()).offset(offset).limit(limit)).all()
    return [{"id":adjustment.id,"created_at":adjustment.created_at,"product_id":product.id,"product_name":product.name,"sku":product.sku,"barcode":product.barcode,"branch_id":branch.id,"branch_name":branch.name,"reason":adjustment.reason,"quantity_changed":adjustment.quantity_changed,"previous_quantity":adjustment.previous_quantity,"resulting_quantity":adjustment.resulting_quantity,"comment":adjustment.comment,"actor_user_id":actor.id,"actor_name":actor.full_name} for adjustment,product,branch,actor in rows]
@router.get("/api/businesses/{business_id}/inventory/history")
def unified_inventory_history(
    business_id:UUID,
    product_id:UUID|None=None,
    branch_id:UUID|None=None,
    movement_type:str|None=None,
    search:str|None=None,
    start_date:date|None=None,
    end_date:date|None=None,
    offset:int=0,
    limit:int=50,
    user:User=Depends(get_verified_current_user),
    db:Session=Depends(get_db),
):
    membership=require_permission(db,user,business_id,Permission.MANAGE_INVENTORY)
    if offset<0 or limit<1 or limit>100:
        raise HTTPException(422,'offset must be non-negative and limit must be between 1 and 100.')

    allowed_branches=select(MembershipBranch.branch_id).where(MembershipBranch.membership_id==membership.id)

    adjustments=select(
        StockAdjustment.id.label("id"),
        StockAdjustment.created_at.label("created_at"),
        StockAdjustment.product_id.label("product_id"),
        Product.name.label("product_name"),
        Product.sku.label("sku"),
        Product.barcode.label("barcode"),
        StockAdjustment.branch_id.label("branch_id"),
        Branch.name.label("branch_name"),
        literal("STOCK_ADJUSTMENT").label("source_type"),
        StockAdjustment.reason.label("movement_type"),
        StockAdjustment.quantity_changed.label("quantity_changed"),
        StockAdjustment.previous_quantity.label("previous_quantity"),
        StockAdjustment.resulting_quantity.label("resulting_quantity"),
        StockAdjustment.comment.label("comment"),
        StockAdjustment.performed_by_user_id.label("actor_user_id"),
        User.full_name.label("actor_name"),
        literal("STOCK_ADJUSTMENT").label("reference_type"),
        StockAdjustment.id.label("reference_id"),
        literal(None).label("counterparty_branch_id"),
        literal(None).label("counterparty_branch_name"),
    ).join(
        Product,Product.id==StockAdjustment.product_id
    ).join(
        Branch,Branch.id==StockAdjustment.branch_id
    ).join(
        User,User.id==StockAdjustment.performed_by_user_id
    ).where(
        StockAdjustment.business_id==business_id
    )

    transfer_out=select(
        InventoryTransfer.id.label("id"),
        InventoryTransfer.created_at.label("created_at"),
        InventoryTransfer.product_id.label("product_id"),
        Product.name.label("product_name"),
        Product.sku.label("sku"),
        Product.barcode.label("barcode"),
        InventoryTransfer.source_branch_id.label("branch_id"),
        Branch.name.label("branch_name"),
        literal("INVENTORY_TRANSFER").label("source_type"),
        literal("TRANSFER_OUT").label("movement_type"),
        (-InventoryTransfer.quantity).label("quantity_changed"),
        literal(None).label("previous_quantity"),
        literal(None).label("resulting_quantity"),
        InventoryTransfer.comment.label("comment"),
        InventoryTransfer.performed_by_user_id.label("actor_user_id"),
        User.full_name.label("actor_name"),
        literal("INVENTORY_TRANSFER").label("reference_type"),
        InventoryTransfer.id.label("reference_id"),
        InventoryTransfer.destination_branch_id.label("counterparty_branch_id"),
        literal(None).label("counterparty_branch_name"),
    ).join(
        Product,Product.id==InventoryTransfer.product_id
    ).join(
        Branch,Branch.id==InventoryTransfer.source_branch_id
    ).join(
        User,User.id==InventoryTransfer.performed_by_user_id
    ).where(
        InventoryTransfer.business_id==business_id
    )

    transfer_in=select(
        InventoryTransfer.id.label("id"),
        InventoryTransfer.created_at.label("created_at"),
        InventoryTransfer.product_id.label("product_id"),
        Product.name.label("product_name"),
        Product.sku.label("sku"),
        Product.barcode.label("barcode"),
        InventoryTransfer.destination_branch_id.label("branch_id"),
        Branch.name.label("branch_name"),
        literal("INVENTORY_TRANSFER").label("source_type"),
        literal("TRANSFER_IN").label("movement_type"),
        InventoryTransfer.quantity.label("quantity_changed"),
        literal(None).label("previous_quantity"),
        literal(None).label("resulting_quantity"),
        InventoryTransfer.comment.label("comment"),
        InventoryTransfer.performed_by_user_id.label("actor_user_id"),
        User.full_name.label("actor_name"),
        literal("INVENTORY_TRANSFER").label("reference_type"),
        InventoryTransfer.id.label("reference_id"),
        InventoryTransfer.source_branch_id.label("counterparty_branch_id"),
        literal(None).label("counterparty_branch_name"),
    ).join(
        Product,Product.id==InventoryTransfer.product_id
    ).join(
        Branch,Branch.id==InventoryTransfer.destination_branch_id
    ).join(
        User,User.id==InventoryTransfer.performed_by_user_id
    ).where(
        InventoryTransfer.business_id==business_id
    )

    if not membership.all_branches:
        adjustments=adjustments.where(StockAdjustment.branch_id.in_(allowed_branches))
        transfer_out=transfer_out.where(InventoryTransfer.source_branch_id.in_(allowed_branches))
        transfer_in=transfer_in.where(InventoryTransfer.destination_branch_id.in_(allowed_branches))

    if product_id:
        adjustments=adjustments.where(StockAdjustment.product_id==product_id)
        transfer_out=transfer_out.where(InventoryTransfer.product_id==product_id)
        transfer_in=transfer_in.where(InventoryTransfer.product_id==product_id)

    if branch_id:
        require_branch_access(db,membership,business_id,branch_id)
        adjustments=adjustments.where(StockAdjustment.branch_id==branch_id)
        transfer_out=transfer_out.where(InventoryTransfer.source_branch_id==branch_id)
        transfer_in=transfer_in.where(InventoryTransfer.destination_branch_id==branch_id)

    if search and search.strip():
        text=f"%{search.strip()}%"
        product_search=or_(Product.name.ilike(text),Product.sku.ilike(text),Product.barcode.ilike(text))
        adjustments=adjustments.where(product_search)
        transfer_out=transfer_out.where(product_search)
        transfer_in=transfer_in.where(product_search)

    if start_date:
        start=datetime.combine(start_date,time.min,tzinfo=timezone.utc)
        adjustments=adjustments.where(StockAdjustment.created_at>=start)
        transfer_out=transfer_out.where(InventoryTransfer.created_at>=start)
        transfer_in=transfer_in.where(InventoryTransfer.created_at>=start)

    if end_date:
        end=datetime.combine(end_date,time.max,tzinfo=timezone.utc)
        adjustments=adjustments.where(StockAdjustment.created_at<=end)
        transfer_out=transfer_out.where(InventoryTransfer.created_at<=end)
        transfer_in=transfer_in.where(InventoryTransfer.created_at<=end)

    combined=union_all(adjustments,transfer_out,transfer_in).subquery()
    q=select(combined)

    if movement_type:
        q=q.where(combined.c.movement_type==movement_type.upper())

    rows=db.execute(
        q.order_by(combined.c.created_at.desc()).offset(offset).limit(limit)
    ).mappings().all()

    return [dict(row) for row in rows]
@router.get("/api/businesses/{business_id}/products/expiring")
def expiring_products(business_id:UUID,status_filter:str='expiring',days:int=30,category_id:UUID|None=None,product_type:str|None=None,active_filter:str='active',search:str|None=None,offset:int=0,limit:int=50,user:User=Depends(get_verified_current_user),db:Session=Depends(get_db)):
    require_permission(db,user,business_id,Permission.MANAGE_INVENTORY)
    if days<0 or days>365 or offset<0 or limit<1 or limit>100:raise HTTPException(422,'days must be 0–365, offset non-negative, and limit 1–100.')
    today=date.today(); cutoff=today+timedelta(days=days); status_filter=status_filter.lower(); active_filter=active_filter.lower()
    if status_filter not in {'expiring','expired','all'}:raise HTTPException(422,'status_filter must be expiring, expired, or all.')
    if active_filter not in {'active','inactive','all'}:raise HTTPException(422,'active_filter must be active, inactive, or all.')
    q=select(Product,ProductCategory).outerjoin(ProductCategory,ProductCategory.id==Product.category_id).where(Product.business_id==business_id,Product.expiry_date.is_not(None))
    if status_filter=='expiring':q=q.where(Product.expiry_date>=today,Product.expiry_date<=cutoff)
    elif status_filter=='expired':q=q.where(Product.expiry_date<today)
    else:q=q.where(Product.expiry_date<=cutoff)
    if category_id:q=q.where(Product.category_id==category_id)
    if product_type:q=q.where(Product.product_type==product_type)
    if active_filter=='active':q=q.where(Product.is_active.is_(True))
    elif active_filter=='inactive':q=q.where(Product.is_active.is_(False))
    if search and search.strip():
        text=f"%{search.strip()}%";q=q.where(or_(Product.name.ilike(text),Product.sku.ilike(text),Product.barcode.ilike(text)))
    rows=db.execute(q.order_by(Product.expiry_date.asc()).offset(offset).limit(limit)).all()
    return [{"product_id":product.id,"name":product.name,"sku":product.sku,"barcode":product.barcode,"category_id":product.category_id,"category_name":category.name if category else None,"product_type":product.product_type,"expiry_date":product.expiry_date,"expiry_status":"EXPIRED" if product.expiry_date<today else "EXPIRING","days_until_expiry":(product.expiry_date-today).days,"selling_price":product.selling_price,"purchase_price":product.purchase_price,"is_active":product.is_active} for product,category in rows]
@router.get("/api/businesses/{business_id}/products/{product_id}")
def product_detail(business_id:UUID,product_id:UUID,user:User=Depends(get_verified_current_user),db:Session=Depends(get_db)):
    access(db,user,business_id);obj=db.get(Product,product_id)
    if not obj or obj.business_id!=business_id:raise HTTPException(404,"Product not found.")
    category=db.get(ProductCategory,obj.category_id) if obj.category_id else None
    unit=db.get(Unit,obj.base_unit_id) if obj.base_unit_id else None
    return {"product":obj,"category":category,"base_unit":unit,"variants":list(db.scalars(select(ProductVariant).where(ProductVariant.product_id==obj.id))),"conversions":list(db.scalars(select(ProductUnitConversion).where(ProductUnitConversion.product_id==obj.id)))}
@router.patch("/api/businesses/{business_id}/products/{product_id}")
def edit_product(business_id:UUID,product_id:UUID,data:ProductUpdate,user:User=Depends(get_verified_current_user),db:Session=Depends(get_db)):
    require_permission(db,user,business_id,Permission.MANAGE_INVENTORY)
    try:return update_product(db,business_id,user.id,product_id,data)
    except LookupError as e:raise HTTPException(404,str(e))
    except IntegrityError:db.rollback();raise HTTPException(409,'Duplicate SKU or barcode for this business.')
    except ValueError as e:db.rollback();raise HTTPException(422,str(e))

@router.post("/api/businesses/{business_id}/products/{product_id}/conversions",status_code=201)
def create_conversion(business_id:UUID,product_id:UUID,data:ConversionInput,user:User=Depends(get_verified_current_user),db:Session=Depends(get_db)):
    require_permission(db,user,business_id,Permission.MANAGE_INVENTORY)
    try:
        return add_product_conversion(db,business_id,user.id,product_id,data)
    except LookupError as e:
        raise HTTPException(404,str(e))
    except IntegrityError:
        db.rollback()
        raise HTTPException(409,'Duplicate unit conversion.')
    except ValueError as e:
        db.rollback()
        raise HTTPException(422,str(e))


@router.patch("/api/businesses/{business_id}/products/{product_id}/conversions/{conversion_id}")
def edit_conversion(business_id:UUID,product_id:UUID,conversion_id:UUID,data:ConversionUpdate,user:User=Depends(get_verified_current_user),db:Session=Depends(get_db)):
    require_permission(db,user,business_id,Permission.MANAGE_INVENTORY)
    try:
        return update_product_conversion(db,business_id,user.id,product_id,conversion_id,data)
    except LookupError as e:
        raise HTTPException(404,str(e))
    except ValueError as e:
        db.rollback()
        raise HTTPException(422,str(e))


@router.post("/api/businesses/{business_id}/products/{product_id}/variants",status_code=201)
def create_variant(business_id:UUID,product_id:UUID,data:VariantInput,user:User=Depends(get_verified_current_user),db:Session=Depends(get_db)):
    require_permission(db,user,business_id,Permission.MANAGE_INVENTORY)
    try:
        return add_product_variant(db,business_id,user.id,product_id,data)
    except LookupError as e:
        raise HTTPException(404,str(e))
    except IntegrityError:
        db.rollback()
        raise HTTPException(409,'Duplicate variant SKU or barcode.')
    except ValueError as e:
        db.rollback()
        raise HTTPException(422,str(e))


@router.patch("/api/businesses/{business_id}/products/{product_id}/variants/{variant_id}")
def edit_variant(business_id:UUID,product_id:UUID,variant_id:UUID,data:VariantUpdate,user:User=Depends(get_verified_current_user),db:Session=Depends(get_db)):
    require_permission(db,user,business_id,Permission.MANAGE_INVENTORY)
    try:
        return update_product_variant(db,business_id,user.id,product_id,variant_id,data)
    except LookupError as e:
        raise HTTPException(404,str(e))
    except IntegrityError:
        db.rollback()
        raise HTTPException(409,'Duplicate variant SKU or barcode.')
    except ValueError as e:
        db.rollback()
        raise HTTPException(422,str(e))


@router.patch("/api/businesses/{business_id}/products/{product_id}/status")
def update_product_status(business_id:UUID,product_id:UUID,data:ProductStatusUpdate,user:User=Depends(get_verified_current_user),db:Session=Depends(get_db)):
    require_permission(db,user,business_id,Permission.MANAGE_INVENTORY)
    try:return set_product_status(db,business_id,user.id,product_id,data.is_active)
    except LookupError as e:raise HTTPException(404,str(e))
@router.post("/api/businesses/{business_id}/products/{product_id}/barcode/generate")
def generate_barcode(business_id:UUID,product_id:UUID,user:User=Depends(get_verified_current_user),db:Session=Depends(get_db)):
    require_permission(db,user,business_id,Permission.MANAGE_INVENTORY)
    try:return generate_product_barcode(db,business_id,user.id,product_id)
    except LookupError as e:raise HTTPException(404,str(e))
    except ValueError as e:db.rollback();raise HTTPException(422,str(e))
@router.post("/api/businesses/{business_id}/products/{product_id}/opening-stock",status_code=201)
def opening_stock(business_id:UUID,product_id:UUID,data:OpeningStockCreate,user:User=Depends(get_verified_current_user),db:Session=Depends(get_db)):
    membership=require_permission(db,user,business_id,Permission.MANAGE_INVENTORY);require_branch_access(db,membership,business_id,data.branch_id)
    try:
        adjustment,row=record_opening_stock(db,business_id,user.id,product_id,data);return {'movement_id':adjustment.id,'product_id':product_id,'branch_id':data.branch_id,'opening_quantity':data.quantity,'normalized_base_quantity':data.quantity,'resulting_quantity':row.quantity,'reason':adjustment.reason,'created_at':adjustment.created_at}
    except LookupError as e:raise HTTPException(404,str(e))
    except ValueError as e:db.rollback();raise HTTPException(422,str(e))
@router.get("/api/businesses/{business_id}/products/{product_id}/inventory")
def product_inventory(business_id:UUID,product_id:UUID,user:User=Depends(get_verified_current_user),db:Session=Depends(get_db)):
    access(db,user,business_id); product=db.get(Product,product_id)
    if not product or product.business_id!=business_id: raise HTTPException(404,"Product not found.")
    _,_,branches=get_workspace(db,user,business_id); branch_ids=[b.id for b in branches]
    rows=list(db.scalars(select(BranchInventory).where(BranchInventory.business_id==business_id,BranchInventory.product_id==product_id,BranchInventory.branch_id.in_(branch_ids))))
    return [{"inventory":row,"branch":db.get(__import__('app.models.branch',fromlist=['Branch']).Branch,row.branch_id),"variant":db.get(ProductVariant,row.variant_id) if row.variant_id else None} for row in rows]
@router.get("/api/businesses/{business_id}/products/{product_id}/stock-history")
def product_stock_history(business_id:UUID,product_id:UUID,user:User=Depends(get_verified_current_user),db:Session=Depends(get_db)):
    access(db,user,business_id); product=db.get(Product,product_id)
    if not product or product.business_id!=business_id: raise HTTPException(404,"Product not found.")
    _,_,branches=get_workspace(db,user,business_id); branch_ids=[b.id for b in branches]
    rows=list(db.scalars(select(StockAdjustment).where(StockAdjustment.business_id==business_id,StockAdjustment.product_id==product_id,StockAdjustment.branch_id.in_(branch_ids)).order_by(StockAdjustment.created_at.desc())))
    return [{"adjustment":row,"branch":db.get(__import__('app.models.branch',fromlist=['Branch']).Branch,row.branch_id),"variant":db.get(ProductVariant,row.variant_id) if row.variant_id else None,"user":db.get(User,row.performed_by_user_id)} for row in rows]
@router.get("/api/businesses/{business_id}/branches/{branch_id}/inventory")
def inventory(business_id:UUID,branch_id:UUID,user:User=Depends(get_verified_current_user),db:Session=Depends(get_db)):
    access(db,user,business_id);require_branch(db,business_id,branch_id);return list(db.scalars(select(BranchInventory).where(BranchInventory.business_id==business_id,BranchInventory.branch_id==branch_id)))
@router.post("/api/businesses/{business_id}/branches/{branch_id}/stock-adjustments",status_code=201)
def stock_adjustment(business_id:UUID,branch_id:UUID,data:AdjustmentCreate,user:User=Depends(get_verified_current_user),db:Session=Depends(get_db)):
    access(db,user,business_id)
    try:return adjust(db,business_id,user.id,branch_id,data)
    except ValueError as e:db.rollback();raise HTTPException(422,str(e))
@router.get("/api/businesses/{business_id}/branches/{branch_id}/stock-history")
def stock_history(business_id:UUID,branch_id:UUID,user:User=Depends(get_verified_current_user),db:Session=Depends(get_db)):
    access(db,user,business_id);require_branch(db,business_id,branch_id);return list(db.scalars(select(StockAdjustment).where(StockAdjustment.business_id==business_id,StockAdjustment.branch_id==branch_id).order_by(StockAdjustment.created_at.desc())))
@router.post("/api/businesses/{business_id}/inventory-transfers",status_code=201)
def inventory_transfer(business_id:UUID,data:TransferCreate,user:User=Depends(get_verified_current_user),db:Session=Depends(get_db)):
    access(db,user,business_id)
    try:
        source,destination=transfer(db,business_id,user.id,data);return {"source_quantity":source.quantity,"destination_quantity":destination.quantity}
    except ValueError as e:db.rollback();raise HTTPException(422,str(e))
