feat: Phase 2A — product cost tracking
- Products: add cost_simple (flat €) and cost_breakdown (JSON line items) - OrderItems: add unit_cost snapshot written at time of order creation - Snapshot logic: breakdown sum takes priority over cost_simple; NULL if neither set - Product schema: serialize/deserialize cost_breakdown as JSON; expose in API - Product performance report: add trackable_profit, uncosted_revenue, has_gap per product - Manager UI: cost section in product edit form (simple / detailed toggle with live margin %) - Products list: show margin % or cost-error badge per product - Product performance report: profit column + gap warning banner Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -950,16 +950,29 @@ def product_performance(
|
||||
"category_id": product.category_id if product else None,
|
||||
"qty_sold": 0,
|
||||
"revenue": 0.0,
|
||||
"trackable_profit": 0.0,
|
||||
"uncosted_revenue": 0.0,
|
||||
"uncosted_items": 0,
|
||||
"order_ids": set(),
|
||||
}
|
||||
summary[pid]["qty_sold"] += item.quantity
|
||||
summary[pid]["revenue"] += item.unit_price * item.quantity
|
||||
qty = item.quantity
|
||||
revenue = item.unit_price * qty
|
||||
summary[pid]["qty_sold"] += qty
|
||||
summary[pid]["revenue"] += revenue
|
||||
summary[pid]["order_ids"].add(item.order_id)
|
||||
if item.unit_cost is not None:
|
||||
summary[pid]["trackable_profit"] += revenue - (item.unit_cost * qty)
|
||||
else:
|
||||
summary[pid]["uncosted_revenue"] += revenue
|
||||
summary[pid]["uncosted_items"] += qty
|
||||
|
||||
result = []
|
||||
for entry in summary.values():
|
||||
entry["order_count"] = len(entry.pop("order_ids"))
|
||||
entry["revenue"] = round(entry["revenue"], 2)
|
||||
entry["trackable_profit"] = round(entry["trackable_profit"], 2)
|
||||
entry["uncosted_revenue"] = round(entry["uncosted_revenue"], 2)
|
||||
entry["has_gap"] = entry["uncosted_items"] > 0
|
||||
result.append(entry)
|
||||
|
||||
result.sort(key=lambda x: x["qty_sold"], reverse=True)
|
||||
|
||||
Reference in New Issue
Block a user