Improve the frontend modals
This commit is contained in:
@@ -398,11 +398,80 @@ class IngredientRepository(BaseRepository[Ingredient, IngredientCreate, Ingredie
|
||||
from app.schemas.inventory import IngredientUpdate
|
||||
update_data = IngredientUpdate(last_purchase_price=price)
|
||||
return await self.update(ingredient_id, update_data)
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Failed to update last purchase price", error=str(e), ingredient_id=ingredient_id)
|
||||
raise
|
||||
|
||||
async def update_weighted_average_cost(
|
||||
self,
|
||||
ingredient_id: UUID,
|
||||
current_stock_quantity: float,
|
||||
new_purchase_quantity: float,
|
||||
new_unit_cost: float
|
||||
) -> Optional[Ingredient]:
|
||||
"""
|
||||
Update the average cost using weighted average calculation.
|
||||
|
||||
Formula:
|
||||
new_average_cost = (current_stock_qty × current_avg_cost + new_qty × new_cost) / (current_stock_qty + new_qty)
|
||||
|
||||
Args:
|
||||
ingredient_id: ID of the ingredient
|
||||
current_stock_quantity: Current stock quantity before this purchase
|
||||
new_purchase_quantity: Quantity being purchased
|
||||
new_unit_cost: Unit cost of the new purchase
|
||||
|
||||
Returns:
|
||||
Updated ingredient or None if not found
|
||||
"""
|
||||
try:
|
||||
# Get current ingredient data
|
||||
ingredient = await self.get_by_id(ingredient_id)
|
||||
if not ingredient:
|
||||
logger.warning("Ingredient not found for average cost update", ingredient_id=ingredient_id)
|
||||
return None
|
||||
|
||||
from decimal import Decimal
|
||||
|
||||
# Get current average cost (default to new cost if not set)
|
||||
current_avg_cost = float(ingredient.average_cost) if ingredient.average_cost else float(new_unit_cost)
|
||||
|
||||
# Calculate weighted average
|
||||
# If no current stock, just use the new purchase price
|
||||
if current_stock_quantity <= 0:
|
||||
new_average_cost = Decimal(str(new_unit_cost))
|
||||
else:
|
||||
# Weighted average formula
|
||||
total_cost = (current_stock_quantity * current_avg_cost) + (new_purchase_quantity * new_unit_cost)
|
||||
total_quantity = current_stock_quantity + new_purchase_quantity
|
||||
new_average_cost = Decimal(str(total_cost / total_quantity))
|
||||
|
||||
# Update the ingredient
|
||||
from app.schemas.inventory import IngredientUpdate
|
||||
update_data = IngredientUpdate(average_cost=new_average_cost)
|
||||
updated_ingredient = await self.update(ingredient_id, update_data)
|
||||
|
||||
logger.info(
|
||||
"Updated weighted average cost",
|
||||
ingredient_id=ingredient_id,
|
||||
old_average_cost=current_avg_cost,
|
||||
new_average_cost=float(new_average_cost),
|
||||
current_stock_qty=current_stock_quantity,
|
||||
new_purchase_qty=new_purchase_quantity,
|
||||
new_unit_cost=new_unit_cost
|
||||
)
|
||||
|
||||
return updated_ingredient
|
||||
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
"Failed to update weighted average cost",
|
||||
error=str(e),
|
||||
ingredient_id=ingredient_id
|
||||
)
|
||||
raise
|
||||
|
||||
async def get_ingredients_by_category(self, tenant_id: UUID, category: str) -> List[Ingredient]:
|
||||
"""Get all ingredients in a specific category"""
|
||||
try:
|
||||
|
||||
@@ -28,7 +28,9 @@ class StockMovementRepository(BaseRepository[StockMovement, StockMovementCreate,
|
||||
self,
|
||||
movement_data: StockMovementCreate,
|
||||
tenant_id: UUID,
|
||||
created_by: Optional[UUID] = None
|
||||
created_by: Optional[UUID] = None,
|
||||
quantity_before: Optional[float] = None,
|
||||
quantity_after: Optional[float] = None
|
||||
) -> StockMovement:
|
||||
"""Create a new stock movement record"""
|
||||
try:
|
||||
@@ -37,6 +39,12 @@ class StockMovementRepository(BaseRepository[StockMovement, StockMovementCreate,
|
||||
create_data['tenant_id'] = tenant_id
|
||||
create_data['created_by'] = created_by
|
||||
|
||||
# Add quantity_before and quantity_after if provided
|
||||
if quantity_before is not None:
|
||||
create_data['quantity_before'] = quantity_before
|
||||
if quantity_after is not None:
|
||||
create_data['quantity_after'] = quantity_after
|
||||
|
||||
# Ensure movement_type is properly converted to enum value
|
||||
if 'movement_type' in create_data:
|
||||
movement_type = create_data['movement_type']
|
||||
@@ -65,6 +73,8 @@ class StockMovementRepository(BaseRepository[StockMovement, StockMovementCreate,
|
||||
ingredient_id=record.ingredient_id,
|
||||
movement_type=record.movement_type if record.movement_type else None,
|
||||
quantity=record.quantity,
|
||||
quantity_before=record.quantity_before,
|
||||
quantity_after=record.quantity_after,
|
||||
tenant_id=tenant_id
|
||||
)
|
||||
return record
|
||||
@@ -453,7 +463,7 @@ class StockMovementRepository(BaseRepository[StockMovement, StockMovementCreate,
|
||||
# Generate reference number
|
||||
reference_number = f"AUTO-EXPIRE-{batch_number or stock_id}"
|
||||
|
||||
# Create movement data
|
||||
# Create movement data (without quantity_before/quantity_after - these will be calculated by the caller)
|
||||
movement_data = {
|
||||
'tenant_id': tenant_id,
|
||||
'ingredient_id': ingredient_id,
|
||||
@@ -462,8 +472,6 @@ class StockMovementRepository(BaseRepository[StockMovement, StockMovementCreate,
|
||||
'quantity': quantity,
|
||||
'unit_cost': Decimal(str(unit_cost)) if unit_cost else None,
|
||||
'total_cost': total_cost,
|
||||
'quantity_before': quantity,
|
||||
'quantity_after': 0,
|
||||
'reference_number': reference_number,
|
||||
'reason_code': 'expired',
|
||||
'notes': f"Lote automáticamente marcado como caducado. Vencimiento: {expiration_date.strftime('%Y-%m-%d')}",
|
||||
@@ -536,4 +544,4 @@ class StockMovementRepository(BaseRepository[StockMovement, StockMovementCreate,
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Failed to get inventory waste total", error=str(e), tenant_id=str(tenant_id))
|
||||
raise
|
||||
raise
|
||||
|
||||
Reference in New Issue
Block a user