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
23 changes: 23 additions & 0 deletions .github/workflows/claude.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Claude Code
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
jobs:
claude:
if: contains(github.event.comment.body, '@claude')
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
issues: write
id-token: write
actions: read
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 1
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
3 changes: 3 additions & 0 deletions client/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
<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
</router-link>
Expand Down
10 changes: 10 additions & 0 deletions client/src/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,16 @@ export const api = {
return response.data
},

async getRestockOrders() {
const response = await axios.get(`${API_BASE_URL}/restock-orders`)
return response.data
},

async createRestockOrder(restockOrderData) {
const response = await axios.post(`${API_BASE_URL}/restock-orders`, restockOrderData)
return response.data
},

async createPurchaseOrder(purchaseOrderData) {
const response = await axios.post(`${API_BASE_URL}/purchase-orders`, purchaseOrderData)
return response.data
Expand Down
50 changes: 50 additions & 0 deletions client/src/locales/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export default {
orders: 'Orders',
finance: 'Finance',
demandForecast: 'Demand Forecast',
restocking: 'Restocking',
companyName: 'Catalyst Components',
subtitle: 'Inventory Management System'
},
Expand Down Expand Up @@ -112,6 +113,18 @@ export default {
onTimeDelivery: 'On-Time Delivery',
itemsCount: '{count} items',
quantity: 'Qty',
submittedOrders: 'Submitted Orders',
submittedEmpty: 'No restocking orders submitted yet. Create one from the Restocking tab.',
submittedTable: {
orderNumber: 'Order Number',
items: 'Items',
submitted: 'Submitted',
leadTime: 'Lead Time',
expectedDelivery: 'Expected Delivery',
totalValue: 'Total Value',
status: 'Status'
},
leadTimeDays: '{count} days',
table: {
orderNumber: 'Order Number',
orderId: 'Order ID',
Expand Down Expand Up @@ -189,6 +202,43 @@ export default {
},

// Filters
restocking: {
title: 'Restocking',
description: 'Set a budget and restock the items with the largest forecast shortfall',
budget: 'Available Budget',
budgetHint: 'Drag to set how much you can spend this cycle',
fullCoverage: 'Full coverage',
recommended: 'Recommended Restock',
recommendedCount: '{count} of {total} items recommended',
allocated: 'Allocated',
remaining: 'Remaining',
itemsSelected: 'Items',
longestLeadTime: 'Longest Lead Time',
placeOrder: 'Place Order',
submitting: 'Submitting...',
orderPlaced: 'Order {orderNumber} submitted. Expected delivery {date}.',
viewInOrders: 'View in Orders',
nothingAffordable: 'This budget is too small to cover a full unit of any shortfall item. Increase the budget to see recommendations.',
noShortfall: 'No item is forecast to exceed its current demand, so there is nothing to restock.',
partial: 'Partial',
partialHint: 'Budget covers {quantity} of {needed} units',
fullHint: 'Covers the full shortfall',
excluded: 'Not recommended',
excludedNoShortfall: 'Forecast is at or below current demand',
excludedNoBudget: 'Budget exhausted before this item',
days: '{count} days',
table: {
sku: 'SKU',
itemName: 'Item Name',
shortfall: 'Shortfall',
unitCost: 'Unit Cost',
quantity: 'Restock Qty',
lineTotal: 'Line Total',
leadTime: 'Lead Time',
coverage: 'Coverage'
}
},

filters: {
timePeriod: 'Time Period',
location: 'Location',
Expand Down
50 changes: 50 additions & 0 deletions client/src/locales/ja.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export default {
orders: '注文',
finance: '財務',
demandForecast: '需要予測',
restocking: '補充発注',
companyName: '触媒コンポーネンツ',
subtitle: '在庫管理システム'
},
Expand Down Expand Up @@ -112,6 +113,18 @@ export default {
onTimeDelivery: '定時配達',
itemsCount: '{count}件',
quantity: '数量',
submittedOrders: '送信済み発注',
submittedEmpty: '送信済みの補充発注はまだありません。補充発注タブから作成してください。',
submittedTable: {
orderNumber: '発注番号',
items: '品目',
submitted: '送信日',
leadTime: 'リードタイム',
expectedDelivery: '納品予定日',
totalValue: '合計金額',
status: 'ステータス'
},
leadTimeDays: '{count}日',
table: {
orderNumber: '注文番号',
orderId: '注文ID',
Expand Down Expand Up @@ -189,6 +202,43 @@ export default {
},

// Filters
restocking: {
title: '補充発注',
description: '予算を設定し、予測不足量が最も大きい品目を補充します',
budget: '利用可能予算',
budgetHint: 'このサイクルで使用できる金額をドラッグして設定します',
fullCoverage: '全量充当',
recommended: '推奨補充リスト',
recommendedCount: '{total}件中{count}件を推奨',
allocated: '割当額',
remaining: '残額',
itemsSelected: '品目数',
longestLeadTime: '最長リードタイム',
placeOrder: '発注する',
submitting: '送信中...',
orderPlaced: '発注 {orderNumber} を送信しました。納品予定日は {date} です。',
viewInOrders: '注文タブで確認',
nothingAffordable: 'この予算では不足品目を1単位も充当できません。予算を増やしてください。',
noShortfall: '現在の需要を上回る予測の品目がないため、補充対象はありません。',
partial: '一部',
partialHint: '予算で{needed}単位中{quantity}単位を充当',
fullHint: '不足量を全量充当',
excluded: '推奨対象外',
excludedNoShortfall: '予測が現在の需要以下です',
excludedNoBudget: 'この品目の前に予算を使い切りました',
days: '{count}日',
table: {
sku: 'SKU',
itemName: '品目名',
shortfall: '不足量',
unitCost: '単価',
quantity: '補充数量',
lineTotal: '小計',
leadTime: 'リードタイム',
coverage: '充当状況'
}
},

filters: {
timePeriod: '期間',
location: '場所',
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
87 changes: 85 additions & 2 deletions client/src/views/Orders.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,58 @@
</div>
</div>

<div class="card">
<div class="card-header">
<h3 class="card-title">{{ t('orders.submittedOrders') }} ({{ restockOrders.length }})</h3>
</div>
<div v-if="restockOrdersError" class="error">{{ restockOrdersError }}</div>
<div v-else-if="restockOrders.length === 0" class="empty-state">
{{ t('orders.submittedEmpty') }}
</div>
<div v-else class="table-container">
<table class="restock-orders-table">
<thead>
<tr>
<th>{{ t('orders.submittedTable.orderNumber') }}</th>
<th>{{ t('orders.submittedTable.items') }}</th>
<th>{{ t('orders.submittedTable.submitted') }}</th>
<th>{{ t('orders.submittedTable.leadTime') }}</th>
<th>{{ t('orders.submittedTable.expectedDelivery') }}</th>
<th>{{ t('orders.submittedTable.totalValue') }}</th>
<th>{{ t('orders.submittedTable.status') }}</th>
</tr>
</thead>
<tbody>
<tr v-for="restockOrder in restockOrders" :key="restockOrder.id">
<td><strong>{{ restockOrder.order_number }}</strong></td>
<td>
<details class="items-details">
<summary class="items-summary">
{{ t('orders.itemsCount', { count: restockOrder.items.length }) }}
</summary>
<div class="items-dropdown">
<div v-for="item in restockOrder.items" :key="item.item_sku" class="item-entry">
<span class="item-name">{{ translateProductName(item.item_name) }}</span>
<span class="item-meta">{{ t('orders.quantity') }}: {{ item.quantity }} @ {{ currencySymbol }}{{ item.unit_cost }}</span>
</div>
</div>
</details>
</td>
<td>{{ formatDate(restockOrder.submitted_date) }}</td>
<td>{{ t('orders.leadTimeDays', { count: restockOrder.lead_time_days }) }}</td>
<td>{{ formatDate(restockOrder.expected_delivery) }}</td>
<td><strong>{{ currencySymbol }}{{ restockOrder.total_value.toLocaleString() }}</strong></td>
<td>
<span :class="['badge', getOrderStatusClass(restockOrder.status)]">
{{ restockOrder.status }}
</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>

<div class="card">
<div class="card-header">
<h3 class="card-title">{{ t('orders.allOrders') }} ({{ orders.length }})</h3>
Expand Down Expand Up @@ -96,6 +148,9 @@ export default {
const error = ref(null)
const orders = ref([])

const restockOrders = ref([])
const restockOrdersError = ref(null)

// Use shared filters
const {
selectedPeriod,
Expand Down Expand Up @@ -125,10 +180,21 @@ export default {
}

// Watch for filter changes and reload data
// Restock orders carry no warehouse/category/status/date dimension,
// so they must NOT be reloaded when the global filters change.
watch([selectedPeriod, selectedLocation, selectedCategory, selectedStatus], () => {
loadOrders()
})

const loadRestockOrders = async () => {
try {
restockOrdersError.value = null
restockOrders.value = await api.getRestockOrders()
} catch (err) {
restockOrdersError.value = 'Failed to load submitted orders: ' + err.message
}
}

const getOrdersByStatus = (status) => {
return orders.value.filter(order => order.status === status)
}
Expand All @@ -138,7 +204,8 @@ export default {
'Delivered': 'success',
'Shipped': 'info',
'Processing': 'warning',
'Backordered': 'danger'
'Backordered': 'danger',
'Submitted': 'info'
}
return statusMap[status] || 'info'
}
Expand All @@ -153,13 +220,18 @@ export default {
})
}

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

return {
t,
loading,
error,
orders,
restockOrders,
restockOrdersError,
getOrdersByStatus,
getOrderStatusClass,
formatDate,
Expand Down Expand Up @@ -276,4 +348,15 @@ export default {
font-size: 0.813rem;
color: #64748b;
}

.empty-state {
padding: 1.5rem;
text-align: center;
color: #64748b;
font-size: 0.938rem;
}

.restock-orders-table {
width: 100%;
}
</style>
Loading