瀏覽代碼

update

master
CANCERYS\kw093 2 月之前
父節點
當前提交
310bf72f7f
共有 7 個檔案被更改,包括 1355 行新增137 行删除
  1. +14
    -34
      src/components/FinishedGoodSearch/FinishedGoodSearch.tsx
  2. +21
    -34
      src/components/FinishedGoodSearch/GoodPickExecution.tsx
  3. +14
    -14
      src/components/FinishedGoodSearch/GoodPickExecutionForm.tsx
  4. +1236
    -0
      src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx
  5. +56
    -50
      src/components/PickOrderSearch/LotTable.tsx
  6. +5
    -4
      src/components/PickOrderSearch/PickExecutionForm.tsx
  7. +9
    -1
      src/i18n/zh/pickOrder.json

+ 14
- 34
src/components/FinishedGoodSearch/FinishedGoodSearch.tsx 查看文件

@@ -28,7 +28,7 @@ import { fetchPickOrderClient, autoAssignAndReleasePickOrder } from "@/app/api/p
import Jobcreatitem from "./Jobcreatitem";
import { useSession } from "next-auth/react";
import { SessionWithTokens } from "@/config/authConfig";
import PickExecutionDetail from "./GoodPickExecutiondetail";
interface Props {
pickOrders: PickOrderResult[];
}
@@ -276,27 +276,22 @@ const PickOrderSearch: React.FC<Props> = ({ pickOrders }) => {
p: 2,
borderBottom: '1px solid #e0e0e0'
}}>
<Stack rowGap={2}>
<Grid container>
<Stack rowGap={2}>
<Grid container alignItems="center">
<Grid item xs={8}>
<Typography variant="h4" marginInlineEnd={2}>
{t("Finished Good Order")}
</Typography>
</Grid>
{/*
<Grid item xs={4} display="flex" justifyContent="end" alignItems="end">
<Button onClick={openCreateModal}>
{t("create")}
<Grid item xs={4} display="flex" justifyContent="end" alignItems="center">
<Button
variant="contained"
onClick={handleManualAssign}
disabled={isAssigning}
>
{isAssigning ? t("Assigning pick order...") : t("Assign & Release")}
</Button>
{isOpenCreateModal &&
<CreatePickOrderModal
open={isOpenCreateModal}
onClose={closeCreateModal}
items={items}
/>
}
</Grid>
*/}
</Grid>
</Stack>
</Box>
@@ -306,26 +301,10 @@ const PickOrderSearch: React.FC<Props> = ({ pickOrders }) => {
borderBottom: '1px solid #e0e0e0'
}}>
<Tabs value={tabIndex} onChange={handleTabChange} variant="scrollable">
<Tab
label={t("Pick Execution")}
iconPosition="end"
onClick={handleManualAssign}
sx={{
cursor: 'pointer',
'&:hover': {
color: 'primary.main',
}
}}
title={t("Click to assign a new pick order")}
/>
<Tab label={t("Pick Execution")} iconPosition="end" />
<Tab label={t("Pick Execution Detail")} iconPosition="end" />
</Tabs>
{isAssigning && (
<Box sx={{ p: 1, textAlign: 'center' }}>
<Typography variant="caption" color="primary">
{t("Assigning pick order...")}
</Typography>
</Box>
)}
</Box>

{/* Content section - NO overflow: 'auto' here */}
@@ -333,6 +312,7 @@ const PickOrderSearch: React.FC<Props> = ({ pickOrders }) => {
p: 2
}}>
{tabIndex === 0 && <PickExecution filterArgs={filterArgs} />}
{tabIndex === 1 && <PickExecutionDetail filterArgs={filterArgs} />}
</Box>
</Box>
);


+ 21
- 34
src/components/FinishedGoodSearch/GoodPickExecution.tsx 查看文件

@@ -934,17 +934,10 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {

return (
<FormProvider {...formProps}>
<Stack spacing={2}>
{/* Search Box */}
<Box>
{/*
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}>
<Typography variant="h6" gutterBottom sx={{ mb: 0 }}>
{t("FG Pick Orders")}
</Typography>
</Box>
*/}
{fgPickOrdersLoading ? (
<Box sx={{ display: 'flex', justifyContent: 'center', p: 3 }}>
<CircularProgress />
@@ -972,9 +965,8 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {



{/*

{/* Combined Lot Table */}
<Box>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}>
<Typography variant="h6" gutterBottom sx={{ mb: 0 }}>
@@ -992,12 +984,11 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
<TableCell>{t("Route")}</TableCell>
<TableCell>{t("Item Name")}</TableCell>
<TableCell>{t("Lot#")}</TableCell>
<TableCell>{t("Target Date")}</TableCell>
{/* <TableCell>{t("Lot Location")}</TableCell> */}

<TableCell align="right">{t("Lot Required Pick Qty")}</TableCell>
<TableCell align="right">{t("Original Available Qty")}</TableCell>
<TableCell align="center">{t("Lot Actual Pick Qty")}</TableCell>
{/* <TableCell align="right">{t("Remaining Available Qty")}</TableCell> */}
<TableCell align="center">{t("Action")}</TableCell>
</TableRow>
</TableHead>
@@ -1045,9 +1036,7 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
</Typography>
</Box>
</TableCell>
<TableCell>{lot.pickOrderTargetDate}</TableCell>
{/* <TableCell>{lot.location}</TableCell> */}
<TableCell align="right">{calculateRemainingRequiredQty(lot).toLocaleString()}</TableCell>

<TableCell align="right">
{(() => {
const inQty = lot.inQty || 0;
@@ -1057,7 +1046,7 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
})()}
</TableCell>
<TableCell align="center">
{/* ✅ QR Scan Button if not scanned, otherwise show TextField + Issue button */}
{!lot.stockOutLineId ? (
<Button
variant="outlined"
@@ -1106,12 +1095,13 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
max: calculateRemainingRequiredQty(lot),
step: 0.01
}}
sx={{
width: '80px',
sx={{
width: '60px',
height: '28px',
'& .MuiInputBase-input': {
fontSize: '0.75rem',
fontSize: '0.7rem',
textAlign: 'center',
padding: '8px 4px'
padding: '6px 8px'
}
}}
placeholder="0"
@@ -1136,14 +1126,7 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
</Stack>
)}
</TableCell>
{/* <TableCell align="right">
{(() => {
const inQty = lot.inQty || 0;
const outQty = lot.outQty || 0;
const result = inQty - outQty;
return result.toLocaleString();
})()}
</TableCell> */}

<TableCell align="center">
<Stack direction="column" spacing={1} alignItems="center">
<Button
@@ -1168,14 +1151,17 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
{t("Submit")}
</Button>
</Stack>
</TableCell>
</TableCell>
</TableRow>
))
)}
</TableBody>
</Table>
</TableContainer>
*/}
{/*
<TablePagination
component="div"
count={combinedLotData.length}
@@ -1192,7 +1178,7 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
</Box>
</Stack>

{/* ✅ QR Code Modal */}
<QrCodeModal
open={qrModalOpen}
onClose={() => {
@@ -1206,7 +1192,7 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
onQrCodeSubmit={handleQrCodeSubmitFromModal}
/>

{/* ✅ Good Pick Execution Form Modal */}
{pickExecutionFormOpen && selectedLotForExecutionForm && (
<GoodPickExecutionForm
open={pickExecutionFormOpen}
@@ -1234,6 +1220,7 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
pickOrderCreateDate={new Date()}
/>
)}
*/}
</FormProvider>
);
};


+ 14
- 14
src/components/FinishedGoodSearch/GoodPickExecutionForm.tsx 查看文件

@@ -250,34 +250,34 @@ const calculateRequiredQty = useCallback((lot: LotPickData) => {
<Grid item xs={6}>
<TextField
fullWidth
label={t('requiredQty')}
label={t('Required Qty')}
value={requiredQty || 0}
disabled
variant="outlined"
helperText={t('Still need to pick')}
// helperText={t('Still need to pick')}
/>
</Grid>
<Grid item xs={6}>
<TextField
fullWidth
label={t('remainingAvailableQty')}
label={t('Remaining Available Qty')}
value={remainingAvailableQty}
disabled
variant="outlined"
helperText={t('Available in warehouse')}
// helperText={t('Available in warehouse')}
/>
</Grid>

<Grid item xs={12}>
<TextField
fullWidth
label={t('actualPickQty')}
label={t('Actual Pick Qty')}
type="number"
value={formData.actualPickQty || 0}
onChange={(e) => handleInputChange('actualPickQty', parseFloat(e.target.value) || 0)}
error={!!errors.actualPickQty}
helperText={errors.actualPickQty || t('Enter the quantity actually picked')}
// helperText={errors.actualPickQty || t('Enter the quantity actually picked')}
variant="outlined"
/>
</Grid>
@@ -285,12 +285,12 @@ const calculateRequiredQty = useCallback((lot: LotPickData) => {
<Grid item xs={12}>
<TextField
fullWidth
label={t('missQty')}
label={t('Missing item Qty')}
type="number"
value={formData.missQty || 0}
onChange={(e) => handleInputChange('missQty', parseFloat(e.target.value) || 0)}
error={!!errors.missQty}
helperText={errors.missQty || t('Enter missing quantity (required if no bad items)')}
// helperText={errors.missQty || t('Enter missing quantity (required if no bad items)')}
variant="outlined"
/>
</Grid>
@@ -298,31 +298,31 @@ const calculateRequiredQty = useCallback((lot: LotPickData) => {
<Grid item xs={12}>
<TextField
fullWidth
label={t('badItemQty')}
label={t('Bad Item Qty')}
type="number"
value={formData.badItemQty || 0}
onChange={(e) => handleInputChange('badItemQty', parseFloat(e.target.value) || 0)}
error={!!errors.badItemQty}
helperText={errors.badItemQty || t('Enter bad item quantity (required if no missing items)')}
// helperText={errors.badItemQty || t('Enter bad item quantity (required if no missing items)')}
variant="outlined"
/>
</Grid>
{/* ✅ Show issue description and handler fields when bad items > 0 */}
{(formData.badItemQty && formData.badItemQty > 0) && (
{(formData.badItemQty && formData.badItemQty > 0) ? (
<>
<Grid item xs={12}>
<TextField
fullWidth
id="issueRemark"
label={t('issueRemark')}
label={t('Issue Remark')}
multiline
rows={4}
value={formData.issueRemark || ''}
onChange={(e) => handleInputChange('issueRemark', e.target.value)}
error={!!errors.issueRemark}
helperText={errors.issueRemark}
placeholder={t('Describe the issue with bad items')}
//placeholder={t('Describe the issue with bad items')}
variant="outlined"
/>
</Grid>
@@ -349,7 +349,7 @@ const calculateRequiredQty = useCallback((lot: LotPickData) => {
</FormControl>
</Grid>
</>
)}
) : (<></>)}
</Grid>
</Box>
</DialogContent>


+ 1236
- 0
src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx
文件差異過大導致無法顯示
查看文件


+ 56
- 50
src/components/PickOrderSearch/LotTable.tsx 查看文件

@@ -718,56 +718,56 @@ const LotTable: React.FC<LotTableProps> = ({
<Stack direction="row" spacing={1} alignItems="center">
{/* ✅ 恢复 TextField 用于正常数量输入 */}
<TextField
type="number"
size="small"
value={pickQtyData[selectedRowId!]?.[lot.lotId] || ''}
onChange={(e) => {
if (selectedRowId) {
const inputValue = parseFloat(e.target.value) || 0;
const maxAllowed = Math.min(calculateRemainingAvailableQty(lot), calculateRemainingRequiredQty(lot));
{/*
// ✅ Validate input
if (inputValue > maxAllowed) {
// Set validation error for this lot
setValidationErrors(prev => ({ ...prev, [`lot_${lot.lotId}`]: `${t('Input quantity cannot exceed')} ${maxAllowed}` }));
return;
} else {
// Clear validation error if valid
setValidationErrors(prev => {
const newErrors = { ...prev };
delete newErrors[`lot_${lot.lotId}`];
return newErrors;
});
*/}
onPickQtyChange(selectedRowId, lot.lotId, inputValue);
}
}}
disabled={
(lot.lotAvailability === 'expired' ||
lot.lotAvailability === 'status_unavailable' ||
lot.lotAvailability === 'rejected') ||
selectedLotRowId !== `row_${index}` ||
lot.stockOutLineStatus === 'completed'
}
error={!!validationErrors[`lot_${lot.lotId}`]} // ✅ Show red border when error
helperText={validationErrors[`lot_${lot.lotId}`]} // ✅ Show red error text below
inputProps={{
min: 0,
max: calculateRemainingRequiredQty(lot),
step: 0.01
}}
sx={{
width: '60px',
height: '28px',
'& .MuiInputBase-input': {
fontSize: '0.7rem',
textAlign: 'center',
padding: '6px 8px'
}
}}
placeholder="0"
type="number"
size="small"
value={pickQtyData[selectedRowId!]?.[lot.lotId] || ''}
onChange={(e) => {
if (selectedRowId) {
const inputValue = parseFloat(e.target.value) || 0;
const maxAllowed = Math.min(calculateRemainingAvailableQty(lot), calculateRemainingRequiredQty(lot));
{/*
// ✅ Validate input
if (inputValue > maxAllowed) {
// Set validation error for this lot
setValidationErrors(prev => ({ ...prev, [`lot_${lot.lotId}`]: `${t('Input quantity cannot exceed')} ${maxAllowed}` }));
return;
} else {
// Clear validation error if valid
setValidationErrors(prev => {
const newErrors = { ...prev };
delete newErrors[`lot_${lot.lotId}`];
return newErrors;
});
*/}
onPickQtyChange(selectedRowId, lot.lotId, inputValue);
}
}}
disabled={
(lot.lotAvailability === 'expired' ||
lot.lotAvailability === 'status_unavailable' ||
lot.lotAvailability === 'rejected') ||
selectedLotRowId !== `row_${index}` ||
lot.stockOutLineStatus === 'completed'
}
error={!!validationErrors[`lot_${lot.lotId}`]} // ✅ Show red border when error
helperText={validationErrors[`lot_${lot.lotId}`]} // ✅ Show red error text below
inputProps={{
min: 0,
max: calculateRemainingRequiredQty(lot),
step: 0.01
}}
sx={{
width: '60px',
height: '28px',
'& .MuiInputBase-input': {
fontSize: '0.7rem',
textAlign: 'center',
padding: '6px 8px'
}
}}
placeholder="0"
/>
{/* ✅ 添加 Pick Form 按钮用于问题情况 */}
@@ -775,6 +775,12 @@ const LotTable: React.FC<LotTableProps> = ({
variant="outlined"
size="small"
onClick={() => handlePickExecutionForm(lot)}
disabled={
(lot.lotAvailability === 'expired' ||
lot.lotAvailability === 'status_unavailable' ||
lot.lotAvailability === 'rejected') ||
selectedLotRowId !== `row_${index}`
}
sx={{
fontSize: '0.7rem',
py: 0.5,


+ 5
- 4
src/components/PickOrderSearch/PickExecutionForm.tsx 查看文件

@@ -301,9 +301,9 @@ const calculateRequiredQty = useCallback((lot: LotPickData) => {
variant="outlined"
/>
</Grid>
{/* ✅ Show issue description and handler fields when bad items > 0 */}
{(formData.badItemQty && formData.badItemQty > 0) && (
{(formData.badItemQty && formData.badItemQty > 0) ? (
<>
<Grid item xs={12}>
<TextField
@@ -343,8 +343,9 @@ const calculateRequiredQty = useCallback((lot: LotPickData) => {
</FormControl>
</Grid>
</>
)}
</Grid>
) : (<></>)}
</Grid>
</Box>
</DialogContent>
<DialogActions>


+ 9
- 1
src/i18n/zh/pickOrder.json 查看文件

@@ -227,6 +227,8 @@
"This form is for reporting issues only. You must report either missing items or bad items.":"此表單僅用於報告問題。您必須報告缺少的貨品或不良貨品。",
"Bad item Qty":"不良貨品數量",
"Missing item Qty":"缺少貨品數量",
"Bad Item Qty":"不良貨品數量",
"Missing Item Qty":"缺少貨品數量",
"Actual Pick Qty":"實際提料數量",
"Required Qty":"所需數量",
"Issue Remark":"問題描述",
@@ -234,5 +236,11 @@
"Qty is required":"必需輸入數量",
"Qty is not allowed to be greater than remaining available qty":"輸入數量不能大於剩餘可用數量",
"Qty is not allowed to be greater than required qty":"輸入數量不能大於所需數量",
"At least one issue must be reported":"至少需要報告一個問題"
"At least one issue must be reported":"至少需要報告一個問題",
"issueRemark":"問題描述是必需的",
"handler":"處理者",
"Max":"最大值",
"Route":"路線",
"Index":"編號",
"No FG pick orders found":"沒有成品提料單"
}

Loading…
取消
儲存