diff --git a/CLAUDE.md b/CLAUDE.md
index 89c307d15..462f0f798 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -48,6 +48,9 @@ npm install && npm run dev
**Data Flow**: Vue filters → `client/src/api.js` → FastAPI → In-memory filtering → Pydantic validation → Computed properties
**Reactivity**: Raw data in refs (`allOrders`, `inventoryItems`), derived data in computed properties
+## Coding Conventions
+- **Always document non-obvious logic changes with comments** — explain the *why* for edge cases, workarounds, non-trivial calculations, and ordering dependencies. Match surrounding comment style; don't over-comment obvious code.
+
## API Endpoints
- `GET /api/inventory` - Filters: warehouse, category
- `GET /api/orders` - Filters: warehouse, category, status, month
diff --git a/client/src/App.vue b/client/src/App.vue
index c2da05a5c..b61aae150 100644
--- a/client/src/App.vue
+++ b/client/src/App.vue
@@ -16,6 +16,9 @@
| {{ t('orders.table.orderNumber') }} | +{{ t('orders.table.customer') }} | +{{ t('orders.table.items') }} | +{{ t('orders.table.status') }} | +{{ t('orders.table.orderDate') }} | +{{ t('orders.table.expectedDelivery') }} | +{{ t('orders.table.leadTime') }} | +{{ t('orders.table.totalValue') }} | +
|---|---|---|---|---|---|---|---|
| {{ order.order_number }} | +{{ translateCustomerName(order.customer) }} | +
+
+
+ + {{ t('orders.itemsCount', { count: order.items.length }) }} ++
+
+
+ {{ translateProductName(item.name) }}
+
+
+ |
+ + + {{ t(`status.${order.status.toLowerCase()}`) }} + + | +{{ formatDate(order.order_date) }} | +{{ formatDate(order.expected_delivery) }} | +{{ t('orders.leadTimeDays', { days: getLeadTimeDays(order) }) }} | +{{ currencySymbol }}{{ order.total_value.toLocaleString() }} | +
| {{ order.order_number }} | {{ translateCustomerName(order.customer) }} |
@@ -138,11 +190,29 @@ export default {
'Delivered': 'success',
'Shipped': 'info',
'Processing': 'warning',
- 'Backordered': 'danger'
+ 'Backordered': 'danger',
+ // Submitted status used for restocking orders placed via the Restocking tab
+ 'Submitted': 'info'
}
return statusMap[status] || 'info'
}
+ // Orders placed via the Restocking tab arrive with status 'Submitted'
+ const submittedOrders = computed(() => orders.value.filter(o => o.status === 'Submitted'))
+ // All other orders (avoids double-listing in the main table)
+ const mainOrders = computed(() => orders.value.filter(o => o.status !== 'Submitted'))
+
+ /**
+ * Derives lead time in days from order_date → expected_delivery.
+ * Returns 0 for invalid/missing dates rather than NaN.
+ */
+ const getLeadTimeDays = (order) => {
+ const start = new Date(order.order_date)
+ const end = new Date(order.expected_delivery)
+ const diff = Math.round((end - start) / 86400000)
+ return Number.isFinite(diff) ? diff : 0
+ }
+
const formatDate = (dateString) => {
const { currentLocale } = useI18n()
const locale = currentLocale.value === 'ja' ? 'ja-JP' : 'en-US'
@@ -160,8 +230,11 @@ export default {
loading,
error,
orders,
+ submittedOrders,
+ mainOrders,
getOrdersByStatus,
getOrderStatusClass,
+ getLeadTimeDays,
formatDate,
currencySymbol,
translateProductName,
diff --git a/client/src/views/Restocking.vue b/client/src/views/Restocking.vue
new file mode 100644
index 000000000..2bc0caf36
--- /dev/null
+++ b/client/src/views/Restocking.vue
@@ -0,0 +1,466 @@
+
+
+
+
+
+
+
+
diff --git a/docs/architecture.html b/docs/architecture.html
new file mode 100644
index 000000000..f500bcc04
--- /dev/null
+++ b/docs/architecture.html
@@ -0,0 +1,441 @@
+
+
+
+
+
+
+
+
+ {{ t('restocking.title') }}+{{ t('restocking.description') }} +{{ t('common.loading') }}
+ {{ error }}
+
+
+
+
+
+
+
+
+ {{ t('restocking.budget') }}
+ {{ currencySymbol }}{{ budget.toLocaleString() }}
+
+
+ {{ t('restocking.recommendedTotal') }}
+ {{ currencySymbol }}{{ selectedTotal.toLocaleString() }}
+
+
+ {{ t('restocking.remaining') }}
+ {{ currencySymbol }}{{ remaining.toLocaleString() }}
+
+
+ {{ t('restocking.itemsSelected') }}
+ {{ selectedCount }}
+
+
+
+
+
+
+
+ {{ t('restocking.setBudget') }}+
+
+
+
+
+
+
+ {{ t('restocking.recommendedRestock') }}+
+
+
+
+
+
+
+ System Architecture +Factory Inventory Management System+A full-stack demo for tracking inventory, orders, demand, backlog, and spending across warehouses. Vue 3 single-page frontend, FastAPI backend, and in-memory data loaded from JSON files — no database. + +
+
+
+
+
+ Architecture Overview+A classic three-tier layout. The browser SPA calls a REST API over HTTP; the API filters in-memory Python collections and validates responses before returning them. +
+
+
+
+
+
+ Tier 1 Presentation · Vue 3 SPA (Vite dev server, port 3000)
+
+
+ ViewsDashboard, Inventory, Orders, Demand, Spending, ReportsComponentsFilter bar, detail modals, profile menu, tasks, i18n switcherComposablesuseFilters, useAuth, useI18n (shared reactive state)Routervue-router, 6 client-side routesAPI Clientapi.js — axios wrapper, query-param builder
+
+
+
+
+ HTTP / JSON · axios GET with query params
+
+ ▼
+
+
+
+
+ Tier 2 Application · FastAPI + Uvicorn (port 8001)
+
+
+ RoutesInventory, orders, demand, backlog, dashboard, spending, reportsFilteringapply_filters + filter_by_month (warehouse / category / status / month)Business LogicDashboard summary, quarterly & monthly-trend aggregationValidationPydantic models as response_model schemasCORSMiddleware, allow-all origins (dev only)
+
+
+
+
+ In-process function calls · module import
+
+ ▼
+
+
+
+ Tier 3 Data · In-memory collections (mock_data.py)
+
+
+ Loadermock_data.py reads JSON at startup into Python lists/dictsDatasetsinventory, orders, demand_forecasts, backlog_itemsSpendingspending summary, monthly, categories, transactionsPurchasingpurchase_orders (linked to backlog items)PersistenceNone — changes are lost on restart (reloaded from files)
+
+
+
+
+ Technology Stack+Versions as declared in
+
+
+
+
+ Frontendclient/
+
+
+ Backendserver/
+
+
+ Dataserver/data/
+
+ Toolingworkspace
+
+
+
+
+ Data Flow+How a single filtered request travels from a user click to rendered output. +
+
+
+
+
+ 1
+
+
+ User adjusts a filterFrontend+Time Period, Warehouse, Category, or Order Status changes in the
+
+
+ 2
+
+
+ View calls the API clientFrontend+A view (e.g.
+
+
+ 3
+
+
+ Request hits a FastAPI routeBackend+Endpoints such as
+
+
+ 4
+
+
+ In-memory filtering & aggregationData+
+
+
+ 5
+
+
+ Pydantic validates the responseBackend+Typed
+
+ 6
+
+
+ Vue stores & derivesFrontend+Raw results land in
+
+
+
+
+ API Endpoints+All read-only
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||