|
|
@@ -110,15 +110,15 @@ export default function ProductionSchedulePage() { |
|
|
|
|
|
|
|
|
if (response.ok) { |
|
|
if (response.ok) { |
|
|
await handleSearch(); // refresh list |
|
|
await handleSearch(); // refresh list |
|
|
alert("Forecast generated successfully!"); |
|
|
|
|
|
|
|
|
alert("成功計算排期!"); |
|
|
} else { |
|
|
} else { |
|
|
const errorText = await response.text(); |
|
|
const errorText = await response.text(); |
|
|
console.error("Forecast failed:", errorText); |
|
|
console.error("Forecast failed:", errorText); |
|
|
alert(`Forecast failed: ${response.status} - ${errorText.substring(0, 120)}`); |
|
|
|
|
|
|
|
|
alert(`計算錯誤: ${response.status} - ${errorText.substring(0, 120)}`); |
|
|
} |
|
|
} |
|
|
} catch (e) { |
|
|
} catch (e) { |
|
|
console.error("Forecast Error:", e); |
|
|
console.error("Forecast Error:", e); |
|
|
alert("Error occurred while generating forecast."); |
|
|
|
|
|
|
|
|
alert("發生不明狀況."); |
|
|
} finally { |
|
|
} finally { |
|
|
setLoading(false); |
|
|
setLoading(false); |
|
|
} |
|
|
} |
|
|
@@ -233,7 +233,11 @@ export default function ProductionSchedulePage() { |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
if (response.ok) { |
|
|
if (response.ok) { |
|
|
alert("Job Orders generated successfully!"); |
|
|
|
|
|
|
|
|
const data = await response.json(); |
|
|
|
|
|
const displayMessage = data.message || "Operation completed."; |
|
|
|
|
|
|
|
|
|
|
|
alert(displayMessage); |
|
|
|
|
|
//alert("Job Orders generated successfully!"); |
|
|
setIsDetailOpen(false); |
|
|
setIsDetailOpen(false); |
|
|
} else { |
|
|
} else { |
|
|
alert("Failed to generate jobs."); |
|
|
alert("Failed to generate jobs."); |
|
|
@@ -252,7 +256,7 @@ export default function ProductionSchedulePage() { |
|
|
<Stack direction="row" justifyContent="space-between" alignItems="center" sx={{ mb: 3 }}> |
|
|
<Stack direction="row" justifyContent="space-between" alignItems="center" sx={{ mb: 3 }}> |
|
|
<Stack direction="row" spacing={2} alignItems="center"> |
|
|
<Stack direction="row" spacing={2} alignItems="center"> |
|
|
<CalendarMonth color="primary" sx={{ fontSize: 32 }} /> |
|
|
<CalendarMonth color="primary" sx={{ fontSize: 32 }} /> |
|
|
<Typography variant="h4" sx={{ fontWeight: 'bold' }}>排期</Typography> |
|
|
|
|
|
|
|
|
<Typography variant="h4" sx={{ fontWeight: 'bold' }}>排程</Typography> |
|
|
</Stack> |
|
|
</Stack> |
|
|
|
|
|
|
|
|
<Stack direction="row" spacing={2}> |
|
|
<Stack direction="row" spacing={2}> |
|
|
@@ -263,7 +267,7 @@ export default function ProductionSchedulePage() { |
|
|
onClick={() => setIsExportDialogOpen(true)} |
|
|
onClick={() => setIsExportDialogOpen(true)} |
|
|
sx={{ fontWeight: 'bold' }} |
|
|
sx={{ fontWeight: 'bold' }} |
|
|
> |
|
|
> |
|
|
Export Excel |
|
|
|
|
|
|
|
|
匯出計劃/物料需求Excel |
|
|
</Button> |
|
|
</Button> |
|
|
<Button |
|
|
<Button |
|
|
variant="contained" |
|
|
variant="contained" |
|
|
@@ -273,7 +277,7 @@ export default function ProductionSchedulePage() { |
|
|
disabled={loading} |
|
|
disabled={loading} |
|
|
sx={{ fontWeight: 'bold' }} |
|
|
sx={{ fontWeight: 'bold' }} |
|
|
> |
|
|
> |
|
|
Forecast |
|
|
|
|
|
|
|
|
預測排期 |
|
|
</Button> |
|
|
</Button> |
|
|
</Stack> |
|
|
</Stack> |
|
|
</Stack> |
|
|
</Stack> |
|
|
@@ -281,7 +285,7 @@ export default function ProductionSchedulePage() { |
|
|
{/* Query Bar – unchanged */} |
|
|
{/* Query Bar – unchanged */} |
|
|
<Paper sx={{ p: 2, mb: 3, display: 'flex', alignItems: 'center', gap: 2, borderLeft: '6px solid #1976d2' }}> |
|
|
<Paper sx={{ p: 2, mb: 3, display: 'flex', alignItems: 'center', gap: 2, borderLeft: '6px solid #1976d2' }}> |
|
|
<TextField |
|
|
<TextField |
|
|
label="Produce Date" |
|
|
|
|
|
|
|
|
label="生產日期" |
|
|
type="date" |
|
|
type="date" |
|
|
size="small" |
|
|
size="small" |
|
|
InputLabelProps={{ shrink: true }} |
|
|
InputLabelProps={{ shrink: true }} |
|
|
@@ -289,7 +293,7 @@ export default function ProductionSchedulePage() { |
|
|
onChange={(e) => setSearchDate(e.target.value)} |
|
|
onChange={(e) => setSearchDate(e.target.value)} |
|
|
/> |
|
|
/> |
|
|
<Button variant="contained" startIcon={<Search />} onClick={handleSearch}> |
|
|
<Button variant="contained" startIcon={<Search />} onClick={handleSearch}> |
|
|
Query |
|
|
|
|
|
|
|
|
搜尋 |
|
|
</Button> |
|
|
</Button> |
|
|
</Paper> |
|
|
</Paper> |
|
|
|
|
|
|
|
|
@@ -298,11 +302,10 @@ export default function ProductionSchedulePage() { |
|
|
<Table stickyHeader size="small"> |
|
|
<Table stickyHeader size="small"> |
|
|
<TableHead> |
|
|
<TableHead> |
|
|
<TableRow sx={{ bgcolor: '#f5f5f5' }}> |
|
|
<TableRow sx={{ bgcolor: '#f5f5f5' }}> |
|
|
<TableCell align="center" sx={{ fontWeight: 'bold', width: 100 }}>Action</TableCell> |
|
|
|
|
|
<TableCell sx={{ fontWeight: 'bold' }}>ID</TableCell> |
|
|
|
|
|
<TableCell sx={{ fontWeight: 'bold' }}>Production Date</TableCell> |
|
|
|
|
|
<TableCell align="right" sx={{ fontWeight: 'bold' }}>Est. Prod Count</TableCell> |
|
|
|
|
|
<TableCell align="right" sx={{ fontWeight: 'bold' }}>Total FG Types</TableCell> |
|
|
|
|
|
|
|
|
<TableCell align="center" sx={{ fontWeight: 'bold', width: 100 }}>詳細</TableCell> |
|
|
|
|
|
<TableCell sx={{ fontWeight: 'bold' }}>生產日期</TableCell> |
|
|
|
|
|
<TableCell align="right" sx={{ fontWeight: 'bold' }}>預計生產數</TableCell> |
|
|
|
|
|
<TableCell align="right" sx={{ fontWeight: 'bold' }}>成品款數</TableCell> |
|
|
</TableRow> |
|
|
</TableRow> |
|
|
</TableHead> |
|
|
</TableHead> |
|
|
<TableBody> |
|
|
<TableBody> |
|
|
@@ -313,7 +316,6 @@ export default function ProductionSchedulePage() { |
|
|
<Visibility fontSize="small" /> |
|
|
<Visibility fontSize="small" /> |
|
|
</IconButton> |
|
|
</IconButton> |
|
|
</TableCell> |
|
|
</TableCell> |
|
|
<TableCell>#{ps.id}</TableCell> |
|
|
|
|
|
<TableCell>{formatBackendDate(ps.produceAt)}</TableCell> |
|
|
<TableCell>{formatBackendDate(ps.produceAt)}</TableCell> |
|
|
<TableCell align="right">{formatNum(ps.totalEstProdCount)}</TableCell> |
|
|
<TableCell align="right">{formatNum(ps.totalEstProdCount)}</TableCell> |
|
|
<TableCell align="right">{formatNum(ps.totalFGType)}</TableCell> |
|
|
<TableCell align="right">{formatNum(ps.totalFGType)}</TableCell> |
|
|
@@ -328,7 +330,7 @@ export default function ProductionSchedulePage() { |
|
|
<DialogTitle sx={{ bgcolor: '#1976d2', color: 'white' }}> |
|
|
<DialogTitle sx={{ bgcolor: '#1976d2', color: 'white' }}> |
|
|
<Stack direction="row" alignItems="center" spacing={1}> |
|
|
<Stack direction="row" alignItems="center" spacing={1}> |
|
|
<ListAlt /> |
|
|
<ListAlt /> |
|
|
<Typography variant="h6">Schedule Details: {selectedPs?.id} ({formatBackendDate(selectedPs?.produceAt)})</Typography> |
|
|
|
|
|
|
|
|
<Typography variant="h6">排期詳細: {selectedPs?.id} ({formatBackendDate(selectedPs?.produceAt)})</Typography> |
|
|
</Stack> |
|
|
</Stack> |
|
|
</DialogTitle> |
|
|
</DialogTitle> |
|
|
<DialogContent sx={{ p: 0 }}> |
|
|
<DialogContent sx={{ p: 0 }}> |
|
|
@@ -336,15 +338,16 @@ export default function ProductionSchedulePage() { |
|
|
<Table size="small" stickyHeader> |
|
|
<Table size="small" stickyHeader> |
|
|
<TableHead> |
|
|
<TableHead> |
|
|
<TableRow> |
|
|
<TableRow> |
|
|
<TableCell sx={{ bgcolor: '#eee', fontWeight: 'bold' }}>Job Order</TableCell> |
|
|
|
|
|
<TableCell sx={{ bgcolor: '#eee', fontWeight: 'bold' }}>Item Code</TableCell> |
|
|
|
|
|
<TableCell sx={{ bgcolor: '#eee', fontWeight: 'bold' }}>Item Name</TableCell> |
|
|
|
|
|
<TableCell align="right" sx={{ bgcolor: '#eee', fontWeight: 'bold' }}>Avg Last Month</TableCell> |
|
|
|
|
|
<TableCell align="right" sx={{ bgcolor: '#eee', fontWeight: 'bold' }}>Stock</TableCell> |
|
|
|
|
|
<TableCell align="right" sx={{ bgcolor: '#eee', fontWeight: 'bold' }}>Days Left</TableCell> |
|
|
|
|
|
<TableCell align="right" sx={{ bgcolor: '#eee', fontWeight: 'bold' }}>Batch Need</TableCell> |
|
|
|
|
|
<TableCell align="right" sx={{ bgcolor: '#eee', fontWeight: 'bold' }}>Prod Qty</TableCell> |
|
|
|
|
|
<TableCell align="center" sx={{ bgcolor: '#eee', fontWeight: 'bold' }}>Priority</TableCell> |
|
|
|
|
|
|
|
|
<TableCell sx={{ bgcolor: '#eee', fontWeight: 'bold' }}>工單號</TableCell> |
|
|
|
|
|
<TableCell sx={{ bgcolor: '#eee', fontWeight: 'bold' }}>物料編號</TableCell> |
|
|
|
|
|
<TableCell sx={{ bgcolor: '#eee', fontWeight: 'bold' }}>物料名稱</TableCell> |
|
|
|
|
|
<TableCell align="right" sx={{ bgcolor: '#eee', fontWeight: 'bold' }}>每日平均出貨量</TableCell> |
|
|
|
|
|
<TableCell align="right" sx={{ bgcolor: '#eee', fontWeight: 'bold' }}>出貨前預計存貨量</TableCell> |
|
|
|
|
|
<TableCell sx={{ bgcolor: '#eee', fontWeight: 'bold' }}>單位</TableCell> |
|
|
|
|
|
<TableCell align="right" sx={{ bgcolor: '#eee', fontWeight: 'bold' }}>可用日</TableCell> |
|
|
|
|
|
<TableCell align="right" sx={{ bgcolor: '#eee', fontWeight: 'bold' }}>生產量(批)</TableCell> |
|
|
|
|
|
<TableCell align="right" sx={{ bgcolor: '#eee', fontWeight: 'bold' }}>預計生產包數</TableCell> |
|
|
|
|
|
<TableCell align="center" sx={{ bgcolor: '#eee', fontWeight: 'bold' }}>優先度</TableCell> |
|
|
</TableRow> |
|
|
</TableRow> |
|
|
</TableHead> |
|
|
</TableHead> |
|
|
<TableBody> |
|
|
<TableBody> |
|
|
@@ -355,6 +358,7 @@ export default function ProductionSchedulePage() { |
|
|
<TableCell>{line.itemName}</TableCell> |
|
|
<TableCell>{line.itemName}</TableCell> |
|
|
<TableCell align="right">{formatNum(line.avgQtyLastMonth)}</TableCell> |
|
|
<TableCell align="right">{formatNum(line.avgQtyLastMonth)}</TableCell> |
|
|
<TableCell align="right">{formatNum(line.stockQty)}</TableCell> |
|
|
<TableCell align="right">{formatNum(line.stockQty)}</TableCell> |
|
|
|
|
|
<TableCell>{line.stockUnit}</TableCell> |
|
|
<TableCell align="right" sx={{ color: line.daysLeft < 5 ? 'error.main' : 'inherit', fontWeight: line.daysLeft < 5 ? 'bold' : 'normal' }}> |
|
|
<TableCell align="right" sx={{ color: line.daysLeft < 5 ? 'error.main' : 'inherit', fontWeight: line.daysLeft < 5 ? 'bold' : 'normal' }}> |
|
|
{line.daysLeft} |
|
|
{line.daysLeft} |
|
|
</TableCell> |
|
|
</TableCell> |
|
|
@@ -378,9 +382,10 @@ export default function ProductionSchedulePage() { |
|
|
color="primary" |
|
|
color="primary" |
|
|
startIcon={isGenerating ? <CircularProgress size={20} color="inherit" /> : <SettingsEthernet />} |
|
|
startIcon={isGenerating ? <CircularProgress size={20} color="inherit" /> : <SettingsEthernet />} |
|
|
onClick={handleAutoGenJob} |
|
|
onClick={handleAutoGenJob} |
|
|
disabled={isGenerating || !isDateToday} |
|
|
|
|
|
|
|
|
disabled={isGenerating} |
|
|
|
|
|
//disabled={isGenerating || !isDateToday} |
|
|
> |
|
|
> |
|
|
Auto Gen Job |
|
|
|
|
|
|
|
|
自動生成工單 |
|
|
</Button> |
|
|
</Button> |
|
|
</span> |
|
|
</span> |
|
|
</Tooltip> |
|
|
</Tooltip> |
|
|
@@ -390,7 +395,7 @@ export default function ProductionSchedulePage() { |
|
|
color="inherit" |
|
|
color="inherit" |
|
|
disabled={isGenerating} |
|
|
disabled={isGenerating} |
|
|
> |
|
|
> |
|
|
Close |
|
|
|
|
|
|
|
|
關閉 |
|
|
</Button> |
|
|
</Button> |
|
|
</Stack> |
|
|
</Stack> |
|
|
</DialogActions> |
|
|
</DialogActions> |
|
|
@@ -403,15 +408,15 @@ export default function ProductionSchedulePage() { |
|
|
maxWidth="sm" |
|
|
maxWidth="sm" |
|
|
fullWidth |
|
|
fullWidth |
|
|
> |
|
|
> |
|
|
<DialogTitle>Generate Production Forecast</DialogTitle> |
|
|
|
|
|
|
|
|
<DialogTitle>準備生成預計排期</DialogTitle> |
|
|
<DialogContent> |
|
|
<DialogContent> |
|
|
<DialogContentText sx={{ mb: 3 }}> |
|
|
<DialogContentText sx={{ mb: 3 }}> |
|
|
Select the starting date and number of days to forecast. |
|
|
|
|
|
|
|
|
|
|
|
</DialogContentText> |
|
|
</DialogContentText> |
|
|
|
|
|
|
|
|
<Stack spacing={3} sx={{ mt: 2 }}> |
|
|
<Stack spacing={3} sx={{ mt: 2 }}> |
|
|
<TextField |
|
|
<TextField |
|
|
label="Start Date" |
|
|
|
|
|
|
|
|
label="開始日期" |
|
|
type="date" |
|
|
type="date" |
|
|
fullWidth |
|
|
fullWidth |
|
|
value={forecastStartDate} |
|
|
value={forecastStartDate} |
|
|
@@ -422,7 +427,7 @@ export default function ProductionSchedulePage() { |
|
|
}} |
|
|
}} |
|
|
/> |
|
|
/> |
|
|
<TextField |
|
|
<TextField |
|
|
label="No. of Dates (days)" |
|
|
|
|
|
|
|
|
label="排期日數" |
|
|
type="number" |
|
|
type="number" |
|
|
fullWidth |
|
|
fullWidth |
|
|
value={forecastDays} |
|
|
value={forecastDays} |
|
|
@@ -437,13 +442,12 @@ export default function ProductionSchedulePage() { |
|
|
max: 365, |
|
|
max: 365, |
|
|
step: 1, |
|
|
step: 1, |
|
|
}} |
|
|
}} |
|
|
helperText="Number of days to generate forecast for (1–365)" |
|
|
|
|
|
/> |
|
|
/> |
|
|
</Stack> |
|
|
</Stack> |
|
|
</DialogContent> |
|
|
</DialogContent> |
|
|
<DialogActions> |
|
|
<DialogActions> |
|
|
<Button onClick={() => setIsForecastDialogOpen(false)} color="inherit"> |
|
|
<Button onClick={() => setIsForecastDialogOpen(false)} color="inherit"> |
|
|
Cancel |
|
|
|
|
|
|
|
|
取消 |
|
|
</Button> |
|
|
</Button> |
|
|
<Button |
|
|
<Button |
|
|
variant="contained" |
|
|
variant="contained" |
|
|
@@ -452,7 +456,7 @@ export default function ProductionSchedulePage() { |
|
|
disabled={!forecastStartDate || forecastDays === '' || loading} |
|
|
disabled={!forecastStartDate || forecastDays === '' || loading} |
|
|
startIcon={loading ? <CircularProgress size={20} /> : <OnlinePrediction />} |
|
|
startIcon={loading ? <CircularProgress size={20} /> : <OnlinePrediction />} |
|
|
> |
|
|
> |
|
|
Generate Forecast |
|
|
|
|
|
|
|
|
計算預測排期 |
|
|
</Button> |
|
|
</Button> |
|
|
</DialogActions> |
|
|
</DialogActions> |
|
|
</Dialog> |
|
|
</Dialog> |
|
|
@@ -464,14 +468,14 @@ export default function ProductionSchedulePage() { |
|
|
maxWidth="xs" |
|
|
maxWidth="xs" |
|
|
fullWidth |
|
|
fullWidth |
|
|
> |
|
|
> |
|
|
<DialogTitle>Export Production Schedule</DialogTitle> |
|
|
|
|
|
|
|
|
<DialogTitle>匯出排期/物料用量預計</DialogTitle> |
|
|
<DialogContent> |
|
|
<DialogContent> |
|
|
<DialogContentText sx={{ mb: 3 }}> |
|
|
<DialogContentText sx={{ mb: 3 }}> |
|
|
Select the starting date for the export. |
|
|
|
|
|
|
|
|
選擇要匯出的起始日期 |
|
|
</DialogContentText> |
|
|
</DialogContentText> |
|
|
|
|
|
|
|
|
<TextField |
|
|
<TextField |
|
|
label="From Date" |
|
|
|
|
|
|
|
|
label="起始日期" |
|
|
type="date" |
|
|
type="date" |
|
|
fullWidth |
|
|
fullWidth |
|
|
value={exportFromDate} |
|
|
value={exportFromDate} |
|
|
@@ -485,7 +489,7 @@ export default function ProductionSchedulePage() { |
|
|
</DialogContent> |
|
|
</DialogContent> |
|
|
<DialogActions> |
|
|
<DialogActions> |
|
|
<Button onClick={() => setIsExportDialogOpen(false)} color="inherit"> |
|
|
<Button onClick={() => setIsExportDialogOpen(false)} color="inherit"> |
|
|
Cancel |
|
|
|
|
|
|
|
|
取消 |
|
|
</Button> |
|
|
</Button> |
|
|
<Button |
|
|
<Button |
|
|
variant="contained" |
|
|
variant="contained" |
|
|
@@ -494,7 +498,7 @@ export default function ProductionSchedulePage() { |
|
|
disabled={!exportFromDate || loading} |
|
|
disabled={!exportFromDate || loading} |
|
|
startIcon={loading ? <CircularProgress size={20} /> : <FileDownload />} |
|
|
startIcon={loading ? <CircularProgress size={20} /> : <FileDownload />} |
|
|
> |
|
|
> |
|
|
Export |
|
|
|
|
|
|
|
|
匯出 |
|
|
</Button> |
|
|
</Button> |
|
|
</DialogActions> |
|
|
</DialogActions> |
|
|
</Dialog> |
|
|
</Dialog> |
|
|
|