149 lines
4.9 KiB
Python
149 lines
4.9 KiB
Python
|
|
#!/usr/bin/env python3
|
|||
|
|
"""
|
|||
|
|
Test script to demonstrate and verify the weighted average cost calculation
|
|||
|
|
Location: services/inventory/tests/test_weighted_average_cost.py
|
|||
|
|
"""
|
|||
|
|
import sys
|
|||
|
|
from decimal import Decimal
|
|||
|
|
|
|||
|
|
|
|||
|
|
def calculate_weighted_average(current_stock: float, current_avg_cost: float,
|
|||
|
|
new_quantity: float, new_unit_cost: float) -> float:
|
|||
|
|
"""
|
|||
|
|
Calculate weighted average cost - mirrors the implementation in ingredient_repository.py
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
current_stock: Current stock quantity before purchase
|
|||
|
|
current_avg_cost: Current average cost per unit
|
|||
|
|
new_quantity: Quantity being purchased
|
|||
|
|
new_unit_cost: Unit cost of new purchase
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
New average cost per unit
|
|||
|
|
"""
|
|||
|
|
if current_stock <= 0:
|
|||
|
|
return new_unit_cost
|
|||
|
|
|
|||
|
|
total_cost = (current_stock * current_avg_cost) + (new_quantity * new_unit_cost)
|
|||
|
|
total_quantity = current_stock + new_quantity
|
|||
|
|
return total_cost / total_quantity
|
|||
|
|
|
|||
|
|
|
|||
|
|
def print_test_case(case_num: int, title: str, current_stock: float, current_avg_cost: float,
|
|||
|
|
new_quantity: float, new_unit_cost: float):
|
|||
|
|
"""Print a formatted test case with calculation details"""
|
|||
|
|
print(f"\nTest Case {case_num}: {title}")
|
|||
|
|
print("-" * 60)
|
|||
|
|
print(f"Current Stock: {current_stock} kg @ €{current_avg_cost:.2f}/kg")
|
|||
|
|
print(f"New Purchase: {new_quantity} kg @ €{new_unit_cost:.2f}/kg")
|
|||
|
|
|
|||
|
|
new_avg_cost = calculate_weighted_average(current_stock, current_avg_cost,
|
|||
|
|
new_quantity, new_unit_cost)
|
|||
|
|
|
|||
|
|
if current_stock > 0:
|
|||
|
|
total_cost = (current_stock * current_avg_cost) + (new_quantity * new_unit_cost)
|
|||
|
|
total_quantity = current_stock + new_quantity
|
|||
|
|
print(f"Calculation: ({current_stock} × €{current_avg_cost:.2f} + {new_quantity} × €{new_unit_cost:.2f}) / {total_quantity}")
|
|||
|
|
print(f" = (€{current_stock * current_avg_cost:.2f} + €{new_quantity * new_unit_cost:.2f}) / {total_quantity}")
|
|||
|
|
print(f" = €{total_cost:.2f} / {total_quantity}")
|
|||
|
|
|
|||
|
|
print(f"→ New Average Cost: €{new_avg_cost:.2f}/kg")
|
|||
|
|
|
|||
|
|
return new_avg_cost
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_weighted_average_calculation():
|
|||
|
|
"""Run comprehensive tests of the weighted average cost calculation"""
|
|||
|
|
print("=" * 80)
|
|||
|
|
print("WEIGHTED AVERAGE COST CALCULATION - COMPREHENSIVE TEST SUITE")
|
|||
|
|
print("=" * 80)
|
|||
|
|
|
|||
|
|
# Test Case 1: First Purchase (Bootstrap case)
|
|||
|
|
print_test_case(
|
|||
|
|
1, "First Purchase (No Existing Stock)",
|
|||
|
|
current_stock=0,
|
|||
|
|
current_avg_cost=0,
|
|||
|
|
new_quantity=100,
|
|||
|
|
new_unit_cost=5.00
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# Test Case 2: Same Price Purchase
|
|||
|
|
print_test_case(
|
|||
|
|
2, "Second Purchase at Same Price",
|
|||
|
|
current_stock=100,
|
|||
|
|
current_avg_cost=5.00,
|
|||
|
|
new_quantity=50,
|
|||
|
|
new_unit_cost=5.00
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# Test Case 3: Price Increase
|
|||
|
|
avg_cost = print_test_case(
|
|||
|
|
3, "Purchase at Higher Price (Inflation)",
|
|||
|
|
current_stock=150,
|
|||
|
|
current_avg_cost=5.00,
|
|||
|
|
new_quantity=50,
|
|||
|
|
new_unit_cost=6.00
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# Test Case 4: Large Volume Discount
|
|||
|
|
avg_cost = print_test_case(
|
|||
|
|
4, "Large Purchase with Volume Discount",
|
|||
|
|
current_stock=200,
|
|||
|
|
current_avg_cost=5.25,
|
|||
|
|
new_quantity=200,
|
|||
|
|
new_unit_cost=4.50
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# Test Case 5: Small Purchase After Consumption
|
|||
|
|
avg_cost = print_test_case(
|
|||
|
|
5, "Purchase After Heavy Consumption",
|
|||
|
|
current_stock=50,
|
|||
|
|
current_avg_cost=4.88,
|
|||
|
|
new_quantity=100,
|
|||
|
|
new_unit_cost=5.50
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# Test Case 6: Tiny Emergency Purchase
|
|||
|
|
print_test_case(
|
|||
|
|
6, "Small Emergency Purchase at Premium Price",
|
|||
|
|
current_stock=150,
|
|||
|
|
current_avg_cost=5.29,
|
|||
|
|
new_quantity=10,
|
|||
|
|
new_unit_cost=8.00
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# Summary
|
|||
|
|
print("\n" + "=" * 80)
|
|||
|
|
print("KEY INSIGHTS")
|
|||
|
|
print("=" * 80)
|
|||
|
|
print("""
|
|||
|
|
✓ The weighted average considers both QUANTITY and PRICE:
|
|||
|
|
- Larger purchases have more impact on the average
|
|||
|
|
- Smaller purchases have minimal impact
|
|||
|
|
|
|||
|
|
✓ Behavior with price changes:
|
|||
|
|
- Price increases gradually raise the average (dampened by existing stock)
|
|||
|
|
- Price decreases gradually lower the average (dampened by existing stock)
|
|||
|
|
- Volume discounts can significantly lower costs when buying in bulk
|
|||
|
|
|
|||
|
|
✓ Business implications:
|
|||
|
|
- Encourages bulk purchasing when prices are favorable
|
|||
|
|
- Protects against price spike impacts (averaged over time)
|
|||
|
|
- Provides accurate COGS for financial reporting
|
|||
|
|
- Helps identify procurement opportunities (compare to standard_cost)
|
|||
|
|
|
|||
|
|
✓ Implementation notes:
|
|||
|
|
- Calculation happens automatically on every stock addition
|
|||
|
|
- No user intervention required
|
|||
|
|
- Logged for audit purposes
|
|||
|
|
- Works with FIFO stock consumption
|
|||
|
|
""")
|
|||
|
|
|
|||
|
|
print("=" * 80)
|
|||
|
|
print("✓ All tests completed successfully!")
|
|||
|
|
print("=" * 80)
|
|||
|
|
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
test_weighted_average_calculation()
|