diff --git a/.claude/settings.json b/.claude/settings.json
new file mode 100644
index 000000000..736ac9b80
--- /dev/null
+++ b/.claude/settings.json
@@ -0,0 +1,5 @@
+{
+ "enabledPlugins": {
+ "github@claude-plugins-official": true
+ }
+}
diff --git a/CLAUDE.md b/CLAUDE.md
index 89c307d15..dd6449d2e 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -74,3 +74,7 @@ npm install && npm run dev
- Status: green/blue/yellow/red
- Charts: Custom SVG, CSS Grid for layouts
- No emojis in UI
+
+## Code Standards
+
+- **Comments**: Always document non-obvious logic changes with comments. Only add a comment when the WHY is non-obvious: a hidden constraint, a subtle invariant, a workaround for a specific bug, behavior that would surprise a reader.
diff --git a/client/src/App.vue b/client/src/App.vue
index c2da05a5c..dd6849b1c 100644
--- a/client/src/App.vue
+++ b/client/src/App.vue
@@ -22,6 +22,9 @@
| {{ t('orders.table.orderNumber') }} | +Warehouse | +{{ t('orders.table.items') }} | +{{ t('orders.table.status') }} | +{{ t('orders.table.orderDate') }} | +Lead Time (days) | +{{ t('orders.table.totalValue') }} | +
|---|---|---|---|---|---|---|
| {{ order.order_number }} | +{{ order.warehouse }} | +
+
+
+ + {{ t('orders.itemsCount', { count: order.items.length }) }} ++
+
+
+ {{ translateProductName(item.name) }}
+
+
+ |
+ + + {{ t('orders.statusSubmitted') }} + + | +{{ formatDate(order.order_date) }} | +{{ calculateLeadTime(order.expected_delivery) }} | +{{ currencySymbol }}{{ order.total_value.toLocaleString() }} | +
| {{ order.order_number }} | {{ translateCustomerName(order.customer) }} |
@@ -138,11 +188,28 @@ export default {
'Delivered': 'success',
'Shipped': 'info',
'Processing': 'warning',
- 'Backordered': 'danger'
+ 'Backordered': 'danger',
+ 'Submitted Orders': 'info'
}
return statusMap[status] || 'info'
}
+ const submittedOrders = computed(() =>
+ orders.value.filter(order => order.status === 'Submitted Orders')
+ )
+
+ const regularOrders = computed(() =>
+ orders.value.filter(order => order.status !== 'Submitted Orders')
+ )
+
+ const calculateLeadTime = (expectedDeliveryDate) => {
+ const today = new Date()
+ const delivery = new Date(expectedDeliveryDate)
+ const diffTime = Math.abs(delivery - today)
+ const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24))
+ return diffDays
+ }
+
const formatDate = (dateString) => {
const { currentLocale } = useI18n()
const locale = currentLocale.value === 'ja' ? 'ja-JP' : 'en-US'
@@ -160,12 +227,15 @@ export default {
loading,
error,
orders,
+ submittedOrders,
+ regularOrders,
getOrdersByStatus,
getOrderStatusClass,
formatDate,
currencySymbol,
translateProductName,
- translateCustomerName
+ translateCustomerName,
+ calculateLeadTime
}
}
}
@@ -203,6 +273,24 @@ export default {
width: 120px;
}
+.col-warehouse {
+ width: 140px;
+}
+
+.col-leadtime {
+ width: 140px;
+ text-align: center;
+}
+
+.submitted-section {
+ margin-bottom: 2rem;
+ border-top: 3px solid #3b82f6;
+}
+
+.mock-section {
+ border-top: 3px solid #9ca3af;
+}
+
/* Items details styling */
.items-details {
position: relative;
diff --git a/client/src/views/Restocking.vue b/client/src/views/Restocking.vue
new file mode 100644
index 000000000..d5f66e16d
--- /dev/null
+++ b/client/src/views/Restocking.vue
@@ -0,0 +1,476 @@
+
+
+
+
+
+
+
+
diff --git a/server/main.py b/server/main.py
index a0c2d8c5a..f89375a94 100644
--- a/server/main.py
+++ b/server/main.py
@@ -3,6 +3,8 @@
from typing import List, Optional
from pydantic import BaseModel
from mock_data import inventory_items, orders, demand_forecasts, backlog_items, spending_summary, monthly_spending, category_spending, recent_transactions, purchase_orders
+from datetime import datetime, timedelta
+import uuid
app = FastAPI(title="Factory Inventory Management System")
@@ -14,6 +16,15 @@
'Q4-2025': ['2025-10', '2025-11', '2025-12']
}
+# Lead time mapping by product category (in days)
+CATEGORY_LEAD_TIMES = {
+ 'Circuit Boards': 14,
+ 'Sensors': 10,
+ 'Actuators': 12,
+ 'Controllers': 7,
+ 'Power Supplies': 10
+}
+
def filter_by_month(items: list, month: Optional[str]) -> list:
"""Filter items by month/quarter based on order_date field"""
if not month or month == 'all':
@@ -120,6 +131,12 @@ class CreatePurchaseOrderRequest(BaseModel):
expected_delivery_date: str
notes: Optional[str] = None
+class RestockingOrderRequest(BaseModel):
+ warehouse: str
+ items: List[dict]
+ budget: float
+ recommended_items: List[dict]
+
# API endpoints
@app.get("/")
def root():
@@ -304,6 +321,73 @@ def get_monthly_trends():
result.sort(key=lambda x: x['month'])
return result
+@app.post("/api/orders", response_model=Order)
+def create_restocking_order(order_req: RestockingOrderRequest):
+ """Create a new restocking order"""
+ # Validate budget
+ total_cost = 0
+ order_items = []
+
+ # Get inventory items to fetch product info
+ for item_req in order_req.items:
+ inventory_item = next(
+ (inv for inv in inventory_items if inv["sku"] == item_req["sku"]),
+ None
+ )
+ if not inventory_item:
+ raise HTTPException(status_code=404, detail=f"Item {item_req['sku']} not found")
+
+ unit_cost = inventory_item["unit_cost"]
+ quantity = item_req["quantity"]
+ item_total = unit_cost * quantity
+ total_cost += item_total
+
+ order_items.append({
+ "sku": item_req["sku"],
+ "name": inventory_item["name"],
+ "quantity": quantity,
+ "unit_price": unit_cost
+ })
+
+ # Validate budget constraint
+ if total_cost > order_req.budget:
+ raise HTTPException(status_code=400, detail=f"Order total (${total_cost:.2f}) exceeds budget (${order_req.budget:.2f})")
+
+ # Calculate lead time based on first item's category
+ first_item_category = order_items[0].get("name", "") if order_items else ""
+ # Get category from first ordered item
+ first_sku = order_req.items[0]["sku"] if order_req.items else None
+ lead_time_days = 10 # default
+ if first_sku:
+ first_inv = next((inv for inv in inventory_items if inv["sku"] == first_sku), None)
+ if first_inv:
+ category = first_inv["category"]
+ lead_time_days = CATEGORY_LEAD_TIMES.get(category, 10)
+
+ # Create new order
+ now = datetime.utcnow()
+ order_date = now.isoformat()
+ expected_delivery = (now + timedelta(days=lead_time_days)).isoformat()
+
+ new_order = {
+ "id": str(uuid.uuid4()),
+ "order_number": f"ORD-RESTOCK-{len(orders) + 1001}",
+ "customer": "Internal Restocking",
+ "items": order_items,
+ "status": "Submitted Orders",
+ "order_date": order_date,
+ "expected_delivery": expected_delivery,
+ "total_value": round(total_cost, 2),
+ "warehouse": order_req.warehouse,
+ "category": order_items[0].get("name", "") if order_items else None,
+ "recommended_items": order_req.recommended_items
+ }
+
+ # Add to orders list
+ orders.append(new_order)
+
+ return new_order
+
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8001)
+
+
+ {{ t('restocking.title') }}+{{ t('restocking.description') }} +
+
+
+
+
+
+
+
+
+
+
+ {{ currencySymbol }}{{ budget.toLocaleString() }}
+
+ {{ t('common.loading') }}
+ {{ error }}
+
+
+
+
+
+
+
+ {{ t('restocking.itemsRecommended') }}
+ {{ itemsSelectedCount }}
+
+
+ {{ t('restocking.totalCost') }}
+ {{ currencySymbol }}{{ totalCost.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 }) }}
+
+
+ {{ t('restocking.budgetRemaining') }}
+ {{ currencySymbol }}{{ budgetRemaining.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 }) }}
+
+
+
+
+
+
+ {{ t('restocking.recommendations') }}+
+ {{ t('restocking.noRecommendations') }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('restocking.selectAtLeastOne') }}
+
+
+ {{ t('restocking.overBudget') }}
+
+
+
+ {{ submitError }}
+ |