Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ npm install && npm run dev
- `GET /api/spending/*` - Summary, monthly, categories, transactions

## Common Issues
0. Always document non-obvious logic changes with comments
1. Use unique keys in v-for (not `index`) - use `sku`, `month`, etc.
2. Validate dates before `.getMonth()` calls
3. Update Pydantic models when changing JSON data structure
Expand Down
5 changes: 4 additions & 1 deletion client/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@
<router-link to="/demand" :class="{ active: $route.path === '/demand' }">
{{ t('nav.demandForecast') }}
</router-link>
<router-link to="/restocking" :class="{ active: $route.path === '/restocking' }">
{{ t('nav.restocking') }}
</router-link>
<router-link to="/reports" :class="{ active: $route.path === '/reports' }">
Reports
{{ t('nav.reports') }}
</router-link>
</nav>
<LanguageSwitcher />
Expand Down
37 changes: 37 additions & 0 deletions client/src/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,5 +102,42 @@ export const api = {
async getPurchaseOrderByBacklogItem(backlogItemId) {
const response = await axios.get(`${API_BASE_URL}/purchase-orders/${backlogItemId}`)
return response.data
},

async getQuarterlyReports(filters = {}) {
const params = new URLSearchParams()
if (filters.warehouse && filters.warehouse !== 'all') params.append('warehouse', filters.warehouse)
if (filters.category && filters.category !== 'all') params.append('category', filters.category)
if (filters.status && filters.status !== 'all') params.append('status', filters.status)
if (filters.month && filters.month !== 'all') params.append('month', filters.month)

const response = await axios.get(`${API_BASE_URL}/reports/quarterly?${params.toString()}`)
return response.data
},

async getMonthlyTrends(filters = {}) {
const params = new URLSearchParams()
if (filters.warehouse && filters.warehouse !== 'all') params.append('warehouse', filters.warehouse)
if (filters.category && filters.category !== 'all') params.append('category', filters.category)
if (filters.status && filters.status !== 'all') params.append('status', filters.status)
if (filters.month && filters.month !== 'all') params.append('month', filters.month)

const response = await axios.get(`${API_BASE_URL}/reports/monthly-trends?${params.toString()}`)
return response.data
},

async getRestockingRecommendations() {
const response = await axios.get(`${API_BASE_URL}/restocking/recommendations`)
return response.data
},

async submitRestockingOrder(data) {
const response = await axios.post(`${API_BASE_URL}/restocking/orders`, data)
return response.data
},

async getSubmittedRestockingOrders() {
const response = await axios.get(`${API_BASE_URL}/restocking/submitted-orders`)
return response.data
}
}
60 changes: 60 additions & 0 deletions client/src/locales/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ export default {
orders: 'Orders',
finance: 'Finance',
demandForecast: 'Demand Forecast',
restocking: 'Restocking',
reports: 'Reports',
companyName: 'Catalyst Components',
subtitle: 'Inventory Management System'
},
Expand Down Expand Up @@ -188,6 +190,64 @@ export default {
}
},

// Restocking
restocking: {
title: 'Restocking',
description: 'Set a budget and get priority-ranked recommendations from demand forecasts',
budget: 'Budget',
recommendations: 'Recommendations',
table: {
select: 'Select',
sku: 'SKU',
itemName: 'Item Name',
trend: 'Trend',
forecastedDemand: 'Forecasted Demand',
currentStock: 'Current Stock',
restockQty: 'Restock Qty',
unitCost: 'Unit Cost',
totalCost: 'Total Cost'
},
selectedItems: 'Selected Items',
totalCost: 'Total Cost',
remainingBudget: 'Remaining Budget',
placeOrder: 'Place Restocking Order',
orderSuccess: 'Restocking order placed successfully!',
orderNumber: 'Order Number',
noRecommendations: 'No restocking recommendations - all items are sufficiently stocked.',
submittedOrders: 'Submitted Restocking Orders',
noSubmittedOrders: 'No restocking orders have been submitted yet.',
items: 'Items',
status: 'Status',
orderDate: 'Order Date',
expectedDelivery: 'Expected Delivery',
leadTime: 'Lead Time',
totalValue: 'Total Value',
days: 'days'
},

// Reports
reports: {
title: 'Performance Reports',
description: 'View quarterly performance metrics and monthly trends',
quarterlyPerformance: 'Quarterly Performance',
monthlyRevenueTrend: 'Monthly Revenue Trend',
monthOverMonth: 'Month-over-Month Analysis',
quarter: 'Quarter',
totalOrders: 'Total Orders',
totalRevenue: 'Total Revenue',
avgOrderValue: 'Avg Order Value',
fulfillmentRate: 'Fulfillment Rate',
month: 'Month',
orders: 'Orders',
revenue: 'Revenue',
change: 'Change',
growthRate: 'Growth Rate',
totalRevenueYTD: 'Total Revenue (YTD)',
avgMonthlyRevenue: 'Avg Monthly Revenue',
totalOrdersYTD: 'Total Orders (YTD)',
bestPerformingQuarter: 'Best Performing Quarter'
},

// Filters
filters: {
timePeriod: 'Time Period',
Expand Down
60 changes: 60 additions & 0 deletions client/src/locales/ja.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ export default {
orders: '注文',
finance: '財務',
demandForecast: '需要予測',
restocking: '補充',
reports: 'レポート',
companyName: '触媒コンポーネンツ',
subtitle: '在庫管理システム'
},
Expand Down Expand Up @@ -188,6 +190,64 @@ export default {
}
},

// Restocking
restocking: {
title: '補充',
description: '予算を設定し、需要予測に基づく優先順位付きの補充提案を取得',
budget: '予算',
recommendations: '推奨事項',
table: {
select: '選択',
sku: 'SKU',
itemName: '品目名',
trend: 'トレンド',
forecastedDemand: '予測需要',
currentStock: '現在庫',
restockQty: '補充数量',
unitCost: '単価',
totalCost: '合計コスト'
},
selectedItems: '選択品目',
totalCost: '合計コスト',
remainingBudget: '残予算',
placeOrder: '補充注文を発注',
orderSuccess: '補充注文が正常に発注されました!',
orderNumber: '注文番号',
noRecommendations: '補充の推奨事項はありません - すべての品目は十分な在庫があります。',
submittedOrders: '発注済み補充注文',
noSubmittedOrders: 'まだ補充注文は発注されていません。',
items: '品目',
status: 'ステータス',
orderDate: '注文日',
expectedDelivery: '予定配達日',
leadTime: 'リードタイム',
totalValue: '合計金額',
days: '日'
},

// Reports
reports: {
title: 'パフォーマンスレポート',
description: '四半期業績指標と月次トレンドの表示',
quarterlyPerformance: '四半期業績',
monthlyRevenueTrend: '月次収益トレンド',
monthOverMonth: '月次比較分析',
quarter: '四半期',
totalOrders: '総注文数',
totalRevenue: '総収益',
avgOrderValue: '平均注文額',
fulfillmentRate: '履行率',
month: '月',
orders: '注文',
revenue: '収益',
change: '変化',
growthRate: '成長率',
totalRevenueYTD: '総収益(年初来)',
avgMonthlyRevenue: '平均月次収益',
totalOrdersYTD: '総注文数(年初来)',
bestPerformingQuarter: '最高業績四半期'
},

// Filters
filters: {
timePeriod: '期間',
Expand Down
4 changes: 3 additions & 1 deletion client/src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Orders from './views/Orders.vue'
import Demand from './views/Demand.vue'
import Spending from './views/Spending.vue'
import Reports from './views/Reports.vue'
import Restocking from './views/Restocking.vue'

const router = createRouter({
history: createWebHistory(),
Expand All @@ -16,7 +17,8 @@ const router = createRouter({
{ path: '/orders', component: Orders },
{ path: '/demand', component: Demand },
{ path: '/spending', component: Spending },
{ path: '/reports', component: Reports }
{ path: '/reports', component: Reports },
{ path: '/restocking', component: Restocking }
]
})

Expand Down
69 changes: 68 additions & 1 deletion client/src/views/Orders.vue
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,45 @@
</table>
</div>
</div>

<div class="card" style="margin-top: 1.5rem;">
<div class="card-header">
<h3 class="card-title">{{ t('restocking.submittedOrders') }}</h3>
</div>
<div v-if="submittedOrders.length === 0" class="empty-state">
{{ t('restocking.noSubmittedOrders') }}
</div>
<div v-else class="table-container">
<table class="orders-table">
<thead>
<tr>
<th class="col-order-number">{{ t('orders.table.orderNumber') }}</th>
<th class="col-items">{{ t('restocking.items') }}</th>
<th class="col-status">{{ t('restocking.status') }}</th>
<th class="col-date">{{ t('restocking.orderDate') }}</th>
<th class="col-date">{{ t('restocking.expectedDelivery') }}</th>
<th class="col-date">{{ t('restocking.leadTime') }}</th>
<th class="col-value">{{ t('restocking.totalValue') }}</th>
</tr>
</thead>
<tbody>
<tr v-for="order in submittedOrders" :key="order.id">
<td class="col-order-number"><strong>{{ order.order_number }}</strong></td>
<td class="col-items">{{ order.items.length }} {{ t('common.items') }}</td>
<td class="col-status">
<span :class="['badge', getOrderStatusClass(order.status)]">
{{ t(`status.${order.status.toLowerCase()}`) }}
</span>
</td>
<td class="col-date">{{ formatDate(order.order_date) }}</td>
<td class="col-date">{{ formatDate(order.expected_delivery) }}</td>
<td class="col-date">{{ calculateLeadTime(order.order_date, order.expected_delivery) }} {{ t('restocking.days') }}</td>
<td class="col-value"><strong>{{ currencySymbol }}{{ order.total_value.toLocaleString() }}</strong></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</template>
Expand All @@ -95,6 +134,7 @@ export default {
const loading = ref(true)
const error = ref(null)
const orders = ref([])
const submittedOrders = ref([])

// Use shared filters
const {
Expand Down Expand Up @@ -124,6 +164,21 @@ export default {
}
}

const loadSubmittedOrders = async () => {
try {
submittedOrders.value = await api.getSubmittedRestockingOrders()
} catch (err) {
console.error('Failed to load submitted restocking orders:', err)
}
}

const calculateLeadTime = (orderDate, deliveryDate) => {
const start = new Date(orderDate)
const end = new Date(deliveryDate)
const diffTime = Math.abs(end - start)
return Math.ceil(diffTime / (1000 * 60 * 60 * 24))
}

// Watch for filter changes and reload data
watch([selectedPeriod, selectedLocation, selectedCategory, selectedStatus], () => {
loadOrders()
Expand Down Expand Up @@ -153,16 +208,22 @@ export default {
})
}

onMounted(loadOrders)
onMounted(() => {
loadOrders()
loadSubmittedOrders()
})

return {
t,
loading,
error,
orders,
submittedOrders,
getOrdersByStatus,
getOrderStatusClass,
formatDate,
calculateLeadTime,
loadSubmittedOrders,
currencySymbol,
translateProductName,
translateCustomerName
Expand Down Expand Up @@ -276,4 +337,10 @@ export default {
font-size: 0.813rem;
color: #64748b;
}

.empty-state {
padding: 2rem;
text-align: center;
color: #64748b;
}
</style>
Loading