Improve the inventory page
This commit is contained in:
@@ -109,14 +109,16 @@ class StockRepository(BaseRepository[Stock, StockCreate, StockUpdate]):
|
||||
raise
|
||||
|
||||
async def get_expiring_stock(
|
||||
self,
|
||||
tenant_id: UUID,
|
||||
self,
|
||||
tenant_id: UUID,
|
||||
days_ahead: int = 7
|
||||
) -> List[Tuple[Stock, Ingredient]]:
|
||||
"""Get stock items expiring within specified days"""
|
||||
"""Get stock items expiring within specified days using state-dependent expiration logic"""
|
||||
try:
|
||||
expiry_date = datetime.now() + timedelta(days=days_ahead)
|
||||
|
||||
|
||||
# Use final_expiration_date if available (for transformed products),
|
||||
# otherwise use regular expiration_date
|
||||
result = await self.session.execute(
|
||||
select(Stock, Ingredient)
|
||||
.join(Ingredient, Stock.ingredient_id == Ingredient.id)
|
||||
@@ -124,24 +126,39 @@ class StockRepository(BaseRepository[Stock, StockCreate, StockUpdate]):
|
||||
and_(
|
||||
Stock.tenant_id == tenant_id,
|
||||
Stock.is_available == True,
|
||||
Stock.expiration_date.isnot(None),
|
||||
Stock.expiration_date <= expiry_date
|
||||
or_(
|
||||
and_(
|
||||
Stock.final_expiration_date.isnot(None),
|
||||
Stock.final_expiration_date <= expiry_date
|
||||
),
|
||||
and_(
|
||||
Stock.final_expiration_date.is_(None),
|
||||
Stock.expiration_date.isnot(None),
|
||||
Stock.expiration_date <= expiry_date
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
.order_by(
|
||||
asc(
|
||||
func.coalesce(Stock.final_expiration_date, Stock.expiration_date)
|
||||
)
|
||||
)
|
||||
.order_by(asc(Stock.expiration_date))
|
||||
)
|
||||
|
||||
|
||||
return result.all()
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Failed to get expiring stock", error=str(e), tenant_id=tenant_id)
|
||||
raise
|
||||
|
||||
async def get_expired_stock(self, tenant_id: UUID) -> List[Tuple[Stock, Ingredient]]:
|
||||
"""Get stock items that have expired"""
|
||||
"""Get stock items that have expired using state-dependent expiration logic"""
|
||||
try:
|
||||
current_date = datetime.now()
|
||||
|
||||
|
||||
# Use final_expiration_date if available (for transformed products),
|
||||
# otherwise use regular expiration_date
|
||||
result = await self.session.execute(
|
||||
select(Stock, Ingredient)
|
||||
.join(Ingredient, Stock.ingredient_id == Ingredient.id)
|
||||
@@ -149,31 +166,45 @@ class StockRepository(BaseRepository[Stock, StockCreate, StockUpdate]):
|
||||
and_(
|
||||
Stock.tenant_id == tenant_id,
|
||||
Stock.is_available == True,
|
||||
Stock.expiration_date.isnot(None),
|
||||
Stock.expiration_date < current_date
|
||||
or_(
|
||||
and_(
|
||||
Stock.final_expiration_date.isnot(None),
|
||||
Stock.final_expiration_date < current_date
|
||||
),
|
||||
and_(
|
||||
Stock.final_expiration_date.is_(None),
|
||||
Stock.expiration_date.isnot(None),
|
||||
Stock.expiration_date < current_date
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
.order_by(
|
||||
desc(
|
||||
func.coalesce(Stock.final_expiration_date, Stock.expiration_date)
|
||||
)
|
||||
)
|
||||
.order_by(desc(Stock.expiration_date))
|
||||
)
|
||||
|
||||
|
||||
return result.all()
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Failed to get expired stock", error=str(e), tenant_id=tenant_id)
|
||||
raise
|
||||
|
||||
async def reserve_stock(
|
||||
self,
|
||||
tenant_id: UUID,
|
||||
ingredient_id: UUID,
|
||||
self,
|
||||
tenant_id: UUID,
|
||||
ingredient_id: UUID,
|
||||
quantity: float,
|
||||
fifo: bool = True
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""Reserve stock using FIFO/LIFO method"""
|
||||
"""Reserve stock using FIFO/LIFO method with state-dependent expiration"""
|
||||
try:
|
||||
# Get available stock ordered by expiration date
|
||||
order_clause = asc(Stock.expiration_date) if fifo else desc(Stock.expiration_date)
|
||||
|
||||
# Order by appropriate expiration date based on transformation status
|
||||
effective_expiration = func.coalesce(Stock.final_expiration_date, Stock.expiration_date)
|
||||
order_clause = asc(effective_expiration) if fifo else desc(effective_expiration)
|
||||
|
||||
result = await self.session.execute(
|
||||
select(Stock).where(
|
||||
and_(
|
||||
@@ -364,27 +395,133 @@ class StockRepository(BaseRepository[Stock, StockCreate, StockUpdate]):
|
||||
raise
|
||||
|
||||
async def mark_expired_stock(self, tenant_id: UUID) -> int:
|
||||
"""Mark expired stock items as expired"""
|
||||
"""Mark expired stock items as expired using state-dependent expiration logic"""
|
||||
try:
|
||||
current_date = datetime.now()
|
||||
|
||||
|
||||
# Mark items as expired based on final_expiration_date or expiration_date
|
||||
result = await self.session.execute(
|
||||
update(Stock)
|
||||
.where(
|
||||
and_(
|
||||
Stock.tenant_id == tenant_id,
|
||||
Stock.expiration_date < current_date,
|
||||
Stock.is_expired == False
|
||||
Stock.is_expired == False,
|
||||
or_(
|
||||
and_(
|
||||
Stock.final_expiration_date.isnot(None),
|
||||
Stock.final_expiration_date < current_date
|
||||
),
|
||||
and_(
|
||||
Stock.final_expiration_date.is_(None),
|
||||
Stock.expiration_date.isnot(None),
|
||||
Stock.expiration_date < current_date
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
.values(is_expired=True, quality_status="expired")
|
||||
)
|
||||
|
||||
|
||||
expired_count = result.rowcount
|
||||
logger.info(f"Marked {expired_count} stock items as expired", tenant_id=tenant_id)
|
||||
|
||||
logger.info(f"Marked {expired_count} stock items as expired using state-dependent logic", tenant_id=tenant_id)
|
||||
|
||||
return expired_count
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Failed to mark expired stock", error=str(e), tenant_id=tenant_id)
|
||||
raise
|
||||
|
||||
async def get_stock_by_production_stage(
|
||||
self,
|
||||
tenant_id: UUID,
|
||||
production_stage: 'ProductionStage',
|
||||
ingredient_id: Optional[UUID] = None
|
||||
) -> List['Stock']:
|
||||
"""Get stock items by production stage"""
|
||||
try:
|
||||
conditions = [
|
||||
Stock.tenant_id == tenant_id,
|
||||
Stock.production_stage == production_stage,
|
||||
Stock.is_available == True
|
||||
]
|
||||
|
||||
if ingredient_id:
|
||||
conditions.append(Stock.ingredient_id == ingredient_id)
|
||||
|
||||
result = await self.session.execute(
|
||||
select(Stock)
|
||||
.where(and_(*conditions))
|
||||
.order_by(asc(func.coalesce(Stock.final_expiration_date, Stock.expiration_date)))
|
||||
)
|
||||
|
||||
return result.scalars().all()
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Failed to get stock by production stage", error=str(e), production_stage=production_stage)
|
||||
raise
|
||||
|
||||
async def get_stock_entries(
|
||||
self,
|
||||
tenant_id: UUID,
|
||||
skip: int = 0,
|
||||
limit: int = 100,
|
||||
ingredient_id: Optional[UUID] = None,
|
||||
available_only: bool = True
|
||||
) -> List[Stock]:
|
||||
"""Get stock entries with filtering and pagination"""
|
||||
try:
|
||||
conditions = [Stock.tenant_id == tenant_id]
|
||||
|
||||
if available_only:
|
||||
conditions.append(Stock.is_available == True)
|
||||
|
||||
if ingredient_id:
|
||||
conditions.append(Stock.ingredient_id == ingredient_id)
|
||||
|
||||
query = (
|
||||
select(Stock)
|
||||
.where(and_(*conditions))
|
||||
.order_by(desc(Stock.created_at))
|
||||
.offset(skip)
|
||||
.limit(limit)
|
||||
)
|
||||
|
||||
result = await self.session.execute(query)
|
||||
return result.scalars().all()
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Failed to get stock entries", error=str(e), tenant_id=tenant_id)
|
||||
raise
|
||||
|
||||
async def delete_by_ingredient(self, ingredient_id: UUID, tenant_id: UUID) -> int:
|
||||
"""Delete all stock entries for a specific ingredient"""
|
||||
try:
|
||||
from sqlalchemy import delete
|
||||
|
||||
stmt = delete(Stock).where(
|
||||
and_(
|
||||
Stock.ingredient_id == ingredient_id,
|
||||
Stock.tenant_id == tenant_id
|
||||
)
|
||||
)
|
||||
|
||||
result = await self.session.execute(stmt)
|
||||
deleted_count = result.rowcount
|
||||
|
||||
logger.info(
|
||||
"Deleted stock entries for ingredient",
|
||||
ingredient_id=str(ingredient_id),
|
||||
tenant_id=str(tenant_id),
|
||||
deleted_count=deleted_count
|
||||
)
|
||||
|
||||
return deleted_count
|
||||
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
"Failed to delete stock entries for ingredient",
|
||||
error=str(e),
|
||||
ingredient_id=str(ingredient_id),
|
||||
tenant_id=str(tenant_id)
|
||||
)
|
||||
raise
|
||||
Reference in New Issue
Block a user