浏览代码

update

master
CANCERYS\kw093 3 个月前
父节点
当前提交
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 Jobcreatitem from "./Jobcreatitem";
import { useSession } from "next-auth/react"; import { useSession } from "next-auth/react";
import { SessionWithTokens } from "@/config/authConfig"; import { SessionWithTokens } from "@/config/authConfig";
import PickExecutionDetail from "./GoodPickExecutiondetail";
interface Props { interface Props {
pickOrders: PickOrderResult[]; pickOrders: PickOrderResult[];
} }
@@ -276,27 +276,22 @@ const PickOrderSearch: React.FC<Props> = ({ pickOrders }) => {
p: 2, p: 2,
borderBottom: '1px solid #e0e0e0' borderBottom: '1px solid #e0e0e0'
}}> }}>
<Stack rowGap={2}>
<Grid container>
<Stack rowGap={2}>
<Grid container alignItems="center">
<Grid item xs={8}> <Grid item xs={8}>
<Typography variant="h4" marginInlineEnd={2}> <Typography variant="h4" marginInlineEnd={2}>
{t("Finished Good Order")} {t("Finished Good Order")}
</Typography> </Typography>
</Grid> </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> </Button>
{isOpenCreateModal &&
<CreatePickOrderModal
open={isOpenCreateModal}
onClose={closeCreateModal}
items={items}
/>
}
</Grid> </Grid>
*/}
</Grid> </Grid>
</Stack> </Stack>
</Box> </Box>
@@ -306,26 +301,10 @@ const PickOrderSearch: React.FC<Props> = ({ pickOrders }) => {
borderBottom: '1px solid #e0e0e0' borderBottom: '1px solid #e0e0e0'
}}> }}>
<Tabs value={tabIndex} onChange={handleTabChange} variant="scrollable"> <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> </Tabs>
{isAssigning && (
<Box sx={{ p: 1, textAlign: 'center' }}>
<Typography variant="caption" color="primary">
{t("Assigning pick order...")}
</Typography>
</Box>
)}
</Box> </Box>


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


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

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


return ( return (
<FormProvider {...formProps}> <FormProvider {...formProps}>
<Stack spacing={2}>
{/* Search Box */} {/* Search Box */}
<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 ? ( {fgPickOrdersLoading ? (
<Box sx={{ display: 'flex', justifyContent: 'center', p: 3 }}> <Box sx={{ display: 'flex', justifyContent: 'center', p: 3 }}>
<CircularProgress /> <CircularProgress />
@@ -972,9 +965,8 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {






{/*


{/* Combined Lot Table */}
<Box> <Box>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}> <Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}>
<Typography variant="h6" gutterBottom sx={{ mb: 0 }}> <Typography variant="h6" gutterBottom sx={{ mb: 0 }}>
@@ -992,12 +984,11 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
<TableCell>{t("Route")}</TableCell> <TableCell>{t("Route")}</TableCell>
<TableCell>{t("Item Name")}</TableCell> <TableCell>{t("Item Name")}</TableCell>
<TableCell>{t("Lot#")}</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("Lot Required Pick Qty")}</TableCell>
<TableCell align="right">{t("Original Available Qty")}</TableCell>
<TableCell align="center">{t("Lot Actual Pick 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> <TableCell align="center">{t("Action")}</TableCell>
</TableRow> </TableRow>
</TableHead> </TableHead>
@@ -1045,9 +1036,7 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
</Typography> </Typography>
</Box> </Box>
</TableCell> </TableCell>
<TableCell>{lot.pickOrderTargetDate}</TableCell>
{/* <TableCell>{lot.location}</TableCell> */}
<TableCell align="right">{calculateRemainingRequiredQty(lot).toLocaleString()}</TableCell>

<TableCell align="right"> <TableCell align="right">
{(() => { {(() => {
const inQty = lot.inQty || 0; const inQty = lot.inQty || 0;
@@ -1057,7 +1046,7 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
})()} })()}
</TableCell> </TableCell>
<TableCell align="center"> <TableCell align="center">
{/* ✅ QR Scan Button if not scanned, otherwise show TextField + Issue button */}
{!lot.stockOutLineId ? ( {!lot.stockOutLineId ? (
<Button <Button
variant="outlined" variant="outlined"
@@ -1106,12 +1095,13 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
max: calculateRemainingRequiredQty(lot), max: calculateRemainingRequiredQty(lot),
step: 0.01 step: 0.01
}} }}
sx={{
width: '80px',
sx={{
width: '60px',
height: '28px',
'& .MuiInputBase-input': { '& .MuiInputBase-input': {
fontSize: '0.75rem',
fontSize: '0.7rem',
textAlign: 'center', textAlign: 'center',
padding: '8px 4px'
padding: '6px 8px'
} }
}} }}
placeholder="0" placeholder="0"
@@ -1136,14 +1126,7 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
</Stack> </Stack>
)} )}
</TableCell> </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"> <TableCell align="center">
<Stack direction="column" spacing={1} alignItems="center"> <Stack direction="column" spacing={1} alignItems="center">
<Button <Button
@@ -1168,14 +1151,17 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
{t("Submit")} {t("Submit")}
</Button> </Button>
</Stack> </Stack>
</TableCell>
</TableCell>
</TableRow> </TableRow>
)) ))
)} )}
</TableBody> </TableBody>
</Table> </Table>
</TableContainer> </TableContainer>
*/}
{/*
<TablePagination <TablePagination
component="div" component="div"
count={combinedLotData.length} count={combinedLotData.length}
@@ -1192,7 +1178,7 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
</Box> </Box>
</Stack> </Stack>


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


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


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

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


<Grid item xs={12}> <Grid item xs={12}>
<TextField <TextField
fullWidth fullWidth
label={t('actualPickQty')}
label={t('Actual Pick Qty')}
type="number" type="number"
value={formData.actualPickQty || 0} value={formData.actualPickQty || 0}
onChange={(e) => handleInputChange('actualPickQty', parseFloat(e.target.value) || 0)} onChange={(e) => handleInputChange('actualPickQty', parseFloat(e.target.value) || 0)}
error={!!errors.actualPickQty} error={!!errors.actualPickQty}
helperText={errors.actualPickQty || t('Enter the quantity actually picked')}
// helperText={errors.actualPickQty || t('Enter the quantity actually picked')}
variant="outlined" variant="outlined"
/> />
</Grid> </Grid>
@@ -285,12 +285,12 @@ const calculateRequiredQty = useCallback((lot: LotPickData) => {
<Grid item xs={12}> <Grid item xs={12}>
<TextField <TextField
fullWidth fullWidth
label={t('missQty')}
label={t('Missing item Qty')}
type="number" type="number"
value={formData.missQty || 0} value={formData.missQty || 0}
onChange={(e) => handleInputChange('missQty', parseFloat(e.target.value) || 0)} onChange={(e) => handleInputChange('missQty', parseFloat(e.target.value) || 0)}
error={!!errors.missQty} 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" variant="outlined"
/> />
</Grid> </Grid>
@@ -298,31 +298,31 @@ const calculateRequiredQty = useCallback((lot: LotPickData) => {
<Grid item xs={12}> <Grid item xs={12}>
<TextField <TextField
fullWidth fullWidth
label={t('badItemQty')}
label={t('Bad Item Qty')}
type="number" type="number"
value={formData.badItemQty || 0} value={formData.badItemQty || 0}
onChange={(e) => handleInputChange('badItemQty', parseFloat(e.target.value) || 0)} onChange={(e) => handleInputChange('badItemQty', parseFloat(e.target.value) || 0)}
error={!!errors.badItemQty} 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" variant="outlined"
/> />
</Grid> </Grid>
{/* ✅ Show issue description and handler fields when bad items > 0 */} {/* ✅ Show issue description and handler fields when bad items > 0 */}
{(formData.badItemQty && formData.badItemQty > 0) && (
{(formData.badItemQty && formData.badItemQty > 0) ? (
<> <>
<Grid item xs={12}> <Grid item xs={12}>
<TextField <TextField
fullWidth fullWidth
id="issueRemark" id="issueRemark"
label={t('issueRemark')}
label={t('Issue Remark')}
multiline multiline
rows={4} rows={4}
value={formData.issueRemark || ''} value={formData.issueRemark || ''}
onChange={(e) => handleInputChange('issueRemark', e.target.value)} onChange={(e) => handleInputChange('issueRemark', e.target.value)}
error={!!errors.issueRemark} error={!!errors.issueRemark}
helperText={errors.issueRemark} helperText={errors.issueRemark}
placeholder={t('Describe the issue with bad items')}
//placeholder={t('Describe the issue with bad items')}
variant="outlined" variant="outlined"
/> />
</Grid> </Grid>
@@ -349,7 +349,7 @@ const calculateRequiredQty = useCallback((lot: LotPickData) => {
</FormControl> </FormControl>
</Grid> </Grid>
</> </>
)}
) : (<></>)}
</Grid> </Grid>
</Box> </Box>
</DialogContent> </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"> <Stack direction="row" spacing={1} alignItems="center">
{/* ✅ 恢复 TextField 用于正常数量输入 */} {/* ✅ 恢复 TextField 用于正常数量输入 */}
<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 按钮用于问题情况 */} {/* ✅ 添加 Pick Form 按钮用于问题情况 */}
@@ -775,6 +775,12 @@ const LotTable: React.FC<LotTableProps> = ({
variant="outlined" variant="outlined"
size="small" size="small"
onClick={() => handlePickExecutionForm(lot)} onClick={() => handlePickExecutionForm(lot)}
disabled={
(lot.lotAvailability === 'expired' ||
lot.lotAvailability === 'status_unavailable' ||
lot.lotAvailability === 'rejected') ||
selectedLotRowId !== `row_${index}`
}
sx={{ sx={{
fontSize: '0.7rem', fontSize: '0.7rem',
py: 0.5, py: 0.5,


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

@@ -301,9 +301,9 @@ const calculateRequiredQty = useCallback((lot: LotPickData) => {
variant="outlined" variant="outlined"
/> />
</Grid> </Grid>
{/* ✅ Show issue description and handler fields when bad items > 0 */} {/* ✅ Show issue description and handler fields when bad items > 0 */}
{(formData.badItemQty && formData.badItemQty > 0) && (
{(formData.badItemQty && formData.badItemQty > 0) ? (
<> <>
<Grid item xs={12}> <Grid item xs={12}>
<TextField <TextField
@@ -343,8 +343,9 @@ const calculateRequiredQty = useCallback((lot: LotPickData) => {
</FormControl> </FormControl>
</Grid> </Grid>
</> </>
)}
</Grid>
) : (<></>)}
</Grid>
</Box> </Box>
</DialogContent> </DialogContent>
<DialogActions> <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.":"此表單僅用於報告問題。您必須報告缺少的貨品或不良貨品。", "This form is for reporting issues only. You must report either missing items or bad items.":"此表單僅用於報告問題。您必須報告缺少的貨品或不良貨品。",
"Bad item Qty":"不良貨品數量", "Bad item Qty":"不良貨品數量",
"Missing item Qty":"缺少貨品數量", "Missing item Qty":"缺少貨品數量",
"Bad Item Qty":"不良貨品數量",
"Missing Item Qty":"缺少貨品數量",
"Actual Pick Qty":"實際提料數量", "Actual Pick Qty":"實際提料數量",
"Required Qty":"所需數量", "Required Qty":"所需數量",
"Issue Remark":"問題描述", "Issue Remark":"問題描述",
@@ -234,5 +236,11 @@
"Qty is required":"必需輸入數量", "Qty is required":"必需輸入數量",
"Qty is not allowed to be greater than remaining available qty":"輸入數量不能大於剩餘可用數量", "Qty is not allowed to be greater than remaining available qty":"輸入數量不能大於剩餘可用數量",
"Qty is not allowed to be greater than required 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":"沒有成品提料單"
} }

正在加载...
取消
保存