| @@ -7,6 +7,7 @@ export interface BomCombo { | |||||
| value: number; | value: number; | ||||
| label: string; | label: string; | ||||
| outputQty: number; | outputQty: number; | ||||
| outputQtyUom: string; | |||||
| } | } | ||||
| export const preloadBomCombo = (() => { | export const preloadBomCombo = (() => { | ||||
| @@ -248,6 +248,8 @@ export interface ProductProcessWithLinesResponse { | |||||
| isDark: string; | isDark: string; | ||||
| isDense: number; | isDense: number; | ||||
| isFloat: string; | isFloat: string; | ||||
| timeSequence: number; | |||||
| complexity: number; | |||||
| scrapRate: number; | scrapRate: number; | ||||
| allergicSubstance: string; | allergicSubstance: string; | ||||
| itemId: number; | itemId: number; | ||||
| @@ -315,6 +317,8 @@ export interface AllJoborderProductProcessInfoResponse { | |||||
| endTime?: string; | endTime?: string; | ||||
| date: string; | date: string; | ||||
| bomId?: number; | bomId?: number; | ||||
| assignedTo: number; | |||||
| pickOrderId: number; | |||||
| itemName: string; | itemName: string; | ||||
| requiredQty: number; | requiredQty: number; | ||||
| jobOrderId: number; | jobOrderId: number; | ||||
| @@ -834,7 +838,15 @@ export const assignJobOrderPickOrder = async (pickOrderId: number, userId: numbe | |||||
| } | } | ||||
| ); | ); | ||||
| }; | }; | ||||
| export const unAssignJobOrderPickOrder = async (pickOrderId: number) => { | |||||
| return serverFetchJson<AssignJobOrderResponse>( | |||||
| `${BASE_API_URL}/jo/unassign-job-order-pick-order/${pickOrderId}`, | |||||
| { | |||||
| method: "POST", | |||||
| headers: { "Content-Type": "application/json" }, | |||||
| } | |||||
| ); | |||||
| }; | |||||
| // 获取 Job Order 分层数据 | // 获取 Job Order 分层数据 | ||||
| export const fetchJobOrderLotsHierarchical = cache(async (userId: number) => { | export const fetchJobOrderLotsHierarchical = cache(async (userId: number) => { | ||||
| return serverFetchJson<JobOrderLotsHierarchicalResponse>( | return serverFetchJson<JobOrderLotsHierarchicalResponse>( | ||||
| @@ -3,7 +3,7 @@ import { JoDetail } from "@/app/api/jo"; | |||||
| import { SaveJo, manualCreateJo } from "@/app/api/jo/actions"; | import { SaveJo, manualCreateJo } from "@/app/api/jo/actions"; | ||||
| import { OUTPUT_DATE_FORMAT, OUTPUT_TIME_FORMAT, dateStringToDayjs, dayjsToDateString, dayjsToDateTimeString } from "@/app/utils/formatUtil"; | import { OUTPUT_DATE_FORMAT, OUTPUT_TIME_FORMAT, dateStringToDayjs, dayjsToDateString, dayjsToDateTimeString } from "@/app/utils/formatUtil"; | ||||
| import { Check } from "@mui/icons-material"; | import { Check } from "@mui/icons-material"; | ||||
| import { Autocomplete, Box, Button, Card, Grid, Modal, Stack, TextField, Typography ,FormControl, InputLabel, Select, MenuItem} from "@mui/material"; | |||||
| import { Autocomplete, Box, Button, Card, Grid, Modal, Stack, TextField, Typography ,FormControl, InputLabel, Select, MenuItem,InputAdornment} from "@mui/material"; | |||||
| import { DatePicker, DateTimePicker, LocalizationProvider } from "@mui/x-date-pickers"; | import { DatePicker, DateTimePicker, LocalizationProvider } from "@mui/x-date-pickers"; | ||||
| import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; | import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; | ||||
| import dayjs, { Dayjs } from "dayjs"; | import dayjs, { Dayjs } from "dayjs"; | ||||
| @@ -66,7 +66,7 @@ const JoCreateFormModal: React.FC<Props> = ({ | |||||
| msg(t("update success")); | msg(t("update success")); | ||||
| onModalClose(); | onModalClose(); | ||||
| } | } | ||||
| }, []) | |||||
| }, [onSearch, onModalClose, t]) | |||||
| const onSubmitError = useCallback<SubmitErrorHandler<SaveJo>>((error) => { | const onSubmitError = useCallback<SubmitErrorHandler<SaveJo>>((error) => { | ||||
| console.log(error) | console.log(error) | ||||
| @@ -166,25 +166,36 @@ const JoCreateFormModal: React.FC<Props> = ({ | |||||
| required: "Req. Qty. required!", | required: "Req. Qty. required!", | ||||
| validate: (value) => value > 0 | validate: (value) => value > 0 | ||||
| }} | }} | ||||
| render={({ field, fieldState: { error } }) => ( | |||||
| <TextField | |||||
| {...field} | |||||
| label={t("Req. Qty")} | |||||
| fullWidth | |||||
| error={Boolean(error)} | |||||
| variant="outlined" | |||||
| type="number" | |||||
| disabled={true} | |||||
| // sx={{ | |||||
| // backgroundColor: "background.paper", | |||||
| // }} | |||||
| value={field.value ?? ""} | |||||
| onChange={(e) => { | |||||
| const val = e.target.value === "" ? undefined : Number(e.target.value); | |||||
| field.onChange(val); | |||||
| }} | |||||
| /> | |||||
| )} | |||||
| render={({ field, fieldState: { error } }) => { | |||||
| const selectedBom = bomCombo.find(bom => bom.id === formProps.watch("bomId")); | |||||
| const uom = selectedBom?.outputQtyUom || ""; | |||||
| return ( | |||||
| <TextField | |||||
| {...field} | |||||
| label={t("Req. Qty")} | |||||
| fullWidth | |||||
| error={Boolean(error)} | |||||
| variant="outlined" | |||||
| type="number" | |||||
| disabled={true} | |||||
| value={field.value ?? ""} | |||||
| onChange={(e) => { | |||||
| const val = e.target.value === "" ? undefined : Number(e.target.value); | |||||
| field.onChange(val); | |||||
| }} | |||||
| InputProps={{ | |||||
| endAdornment: uom ? ( | |||||
| <InputAdornment position="end"> | |||||
| <Typography variant="body2" sx={{ color: "text.secondary" }}> | |||||
| {uom} | |||||
| </Typography> | |||||
| </InputAdornment> | |||||
| ) : null | |||||
| }} | |||||
| /> | |||||
| ); | |||||
| }} | |||||
| /> | /> | ||||
| </Grid> | </Grid> | ||||
| <Grid item xs={12} sm={12} md={6}> | <Grid item xs={12} sm={12} md={6}> | ||||
| @@ -400,7 +400,8 @@ const JoSearch: React.FC<Props> = ({ defaultInputs, bomCombo, printerCombo, jobT | |||||
| bomCombo={bomCombo} | bomCombo={bomCombo} | ||||
| onClose={onCloseCreateJoModal} | onClose={onCloseCreateJoModal} | ||||
| onSearch={() => { | onSearch={() => { | ||||
| setInputs(defaultInputs); | |||||
| setPagingController(defaultPagingController); | |||||
| }} | }} | ||||
| /> | /> | ||||
| @@ -10,9 +10,15 @@ interface SubComponents { | |||||
| } | } | ||||
| const JoSearchWrapper: React.FC & SubComponents = async () => { | const JoSearchWrapper: React.FC & SubComponents = async () => { | ||||
| const today = new Date(); | |||||
| const todayStr = today.toISOString().split('T')[0]; | |||||
| const defaultInputs: SearchJoResultRequest = { | const defaultInputs: SearchJoResultRequest = { | ||||
| code: "", | code: "", | ||||
| itemName: "", | itemName: "", | ||||
| planStart: `${todayStr}T00:00`, | |||||
| planStartTo: `${todayStr}T23:59:59`, | |||||
| } | } | ||||
| const [ | const [ | ||||
| @@ -1007,7 +1007,7 @@ const handleIssueNoLotStockOutLine = useCallback(async (stockOutLineId: number) | |||||
| showInputBody={showInputBody} | showInputBody={showInputBody} | ||||
| onIssueNoLotStockOutLine={handleIssueNoLotStockOutLine} | onIssueNoLotStockOutLine={handleIssueNoLotStockOutLine} | ||||
| setShowInputBody={setShowInputBody} | setShowInputBody={setShowInputBody} | ||||
| selectedLotForInput={selectedLotForInput} | |||||
| //selectedLotForInput={selectedLotForInput} | |||||
| generateInputBody={generateInputBody} | generateInputBody={generateInputBody} | ||||
| // Add missing props | // Add missing props | ||||
| totalPickedByAllPickOrders={0} // You can calculate this from lotData if needed | totalPickedByAllPickOrders={0} // You can calculate this from lotData if needed | ||||
| @@ -10,6 +10,7 @@ interface Props { | |||||
| itemName?: string; | itemName?: string; | ||||
| jobType?: string; | jobType?: string; | ||||
| outputQty?: number | string; | outputQty?: number | string; | ||||
| outputQtyUom?: string; | |||||
| date?: string; | date?: string; | ||||
| }; | }; | ||||
| } | } | ||||
| @@ -32,7 +33,7 @@ const ProcessSummaryHeader: React.FC<Props> = ({ processData }) => { | |||||
| </Typography> | </Typography> | ||||
| <Typography variant="body2" color="text.secondary" sx={{ mt: 1 }}> | <Typography variant="body2" color="text.secondary" sx={{ mt: 1 }}> | ||||
| {t("Qty")}: <strong style={{ color: "green" }}>{processData?.outputQty}</strong> | |||||
| {t("Qty")}: <strong style={{ color: "green" }}>{processData?.outputQty}</strong> ({processData?.outputQtyUom ?? ""}) | |||||
| </Typography> | </Typography> | ||||
| <Typography variant="body2" color="text.secondary" sx={{ mt: 1 }}> | <Typography variant="body2" color="text.secondary" sx={{ mt: 1 }}> | ||||
| {t("Production Date")}: <strong style={{ color: "green" }}>{processData?.date ? dayjs(processData.date).format(OUTPUT_DATE_FORMAT) : ""}</strong> | {t("Production Date")}: <strong style={{ color: "green" }}>{processData?.date ? dayjs(processData.date).format(OUTPUT_DATE_FORMAT) : ""}</strong> | ||||
| @@ -599,12 +599,14 @@ const processQrCode = useCallback((qrValue: string, lineId: number) => { | |||||
| <Typography variant="subtitle2" sx={{ fontWeight: 600 }}> | <Typography variant="subtitle2" sx={{ fontWeight: 600 }}> | ||||
| {t("Time Information(mins)")} | {t("Time Information(mins)")} | ||||
| </Typography> | </Typography> | ||||
| {/* | |||||
| <Typography variant="caption" sx={{ color: 'text.secondary' }}> | <Typography variant="caption" sx={{ color: 'text.secondary' }}> | ||||
| {t("Processing Time")}- | {t("Processing Time")}- | ||||
| </Typography> | </Typography> | ||||
| <Typography variant="caption" sx={{ color: 'text.secondary' }}> | <Typography variant="caption" sx={{ color: 'text.secondary' }}> | ||||
| {t("Setup Time")} - {t("Changeover Time")} | {t("Setup Time")} - {t("Changeover Time")} | ||||
| </Typography> | </Typography> | ||||
| */} | |||||
| </Box> | </Box> | ||||
| </TableCell> | </TableCell> | ||||
| <TableCell align="center">{t("Status")}</TableCell> | <TableCell align="center">{t("Status")}</TableCell> | ||||
| @@ -637,7 +639,20 @@ const processQrCode = useCallback((qrValue: string, lineId: number) => { | |||||
| <TableCell><Typography fontWeight={500}>{line.prepTimeInMinutes} </Typography></TableCell> | <TableCell><Typography fontWeight={500}>{line.prepTimeInMinutes} </Typography></TableCell> | ||||
| <TableCell><Typography fontWeight={500}>{line.postProdTimeInMinutes} </Typography></TableCell> | <TableCell><Typography fontWeight={500}>{line.postProdTimeInMinutes} </Typography></TableCell> | ||||
| */} | */} | ||||
| <TableCell><Typography fontWeight={500}>{line.durationInMinutes} - {line.prepTimeInMinutes} - {line.postProdTimeInMinutes} </Typography></TableCell> | |||||
| <TableCell> | |||||
| <Box sx={{ display: 'flex', flexDirection: 'column', gap: 0.5 }}> | |||||
| <Typography variant="body2" > | |||||
| {t("Processing Time")}: {line.durationInMinutes}{t("mins")} | |||||
| </Typography> | |||||
| <Typography variant="body2" > | |||||
| {t("Setup Time")}: {line.prepTimeInMinutes} {t("mins")} | |||||
| </Typography> | |||||
| <Typography variant="body2" > | |||||
| {t("Changeover Time")}: {line.postProdTimeInMinutes} {t("mins")} | |||||
| </Typography> | |||||
| </Box> | |||||
| </TableCell> | |||||
| <TableCell align="center"> | <TableCell align="center"> | ||||
| {isCompleted ? ( | {isCompleted ? ( | ||||
| <Chip label={t("Completed")} color="success" size="small" | <Chip label={t("Completed")} color="success" size="small" | ||||
| @@ -38,6 +38,7 @@ import { releaseJo, startJo } from "@/app/api/jo/actions"; | |||||
| import JobPickExecutionsecondscan from "../Jodetail/JobPickExecutionsecondscan"; | import JobPickExecutionsecondscan from "../Jodetail/JobPickExecutionsecondscan"; | ||||
| import ProcessSummaryHeader from "./ProcessSummaryHeader"; | import ProcessSummaryHeader from "./ProcessSummaryHeader"; | ||||
| import EditIcon from "@mui/icons-material/Edit"; | import EditIcon from "@mui/icons-material/Edit"; | ||||
| interface JobOrderLine { | interface JobOrderLine { | ||||
| id: number; | id: number; | ||||
| jobOrderId: number; | jobOrderId: number; | ||||
| @@ -292,10 +293,10 @@ const handleRelease = useCallback(async ( jobOrderId: number) => { | |||||
| </Grid> | </Grid> | ||||
| <Grid item xs={6}> | <Grid item xs={6}> | ||||
| <TextField | <TextField | ||||
| label={t("Is Dark | Dense | Float| Scrap Rate| Allergic Substance")} | |||||
| label={t("Is Dark | Dense | Float| Scrap Rate| Allergic Substance | Time Sequence | Complexity")} | |||||
| fullWidth | fullWidth | ||||
| disabled={true} | disabled={true} | ||||
| value={`${processData?.isDark == null || processData?.isDark === "" ? t("N/A") : processData.isDark} | ${processData?.isDense == null || processData?.isDense === "" || processData?.isDense === 0 ? t("N/A") : processData.isDense} | ${processData?.isFloat == null || processData?.isFloat === "" ? t("N/A") : processData.isFloat} | ${processData?.scrapRate == -1 || processData?.scrapRate === "" ? t("N/A") : processData.scrapRate} | ${processData?.allergicSubstance == null || processData?.allergicSubstance === "" ? t("N/A") :t (processData.allergicSubstance)}`} | |||||
| value={`${processData?.isDark == null || processData?.isDark === "" ? t("N/A") : processData.isDark} | ${processData?.isDense == null || processData?.isDense === "" || processData?.isDense === 0 ? t("N/A") : processData.isDense} | ${processData?.isFloat == null || processData?.isFloat === "" ? t("N/A") : processData.isFloat} | ${processData?.scrapRate == -1 || processData?.scrapRate === "" ? t("N/A") : processData.scrapRate} | ${processData?.allergicSubstance == null || processData?.allergicSubstance === "" ? t("N/A") :t (processData.allergicSubstance)} | ${processData?.timeSequence == null || processData?.timeSequence === "" ? t("N/A") : processData.timeSequence} | ${processData?.complexity == null || processData?.complexity === "" ? t("N/A") : processData.complexity}`} | |||||
| /> | /> | ||||
| </Grid> | </Grid> | ||||
| @@ -311,10 +312,10 @@ const handleRelease = useCallback(async ( jobOrderId: number) => { | |||||
| headerName: t("Seq"), | headerName: t("Seq"), | ||||
| flex: 0.2, | flex: 0.2, | ||||
| align: "left", | align: "left", | ||||
| headerAlign: "center", | |||||
| headerAlign: "left", | |||||
| type: "number", | type: "number", | ||||
| renderCell: (params) => { | renderCell: (params) => { | ||||
| return <Typography sx={{ fontSize: "14px" }}>{params.value}</Typography>; | |||||
| return <Typography sx={{ fontSize: "18px" }}>{params.value}</Typography>; | |||||
| }, | }, | ||||
| }, | }, | ||||
| { | { | ||||
| @@ -322,9 +323,9 @@ const handleRelease = useCallback(async ( jobOrderId: number) => { | |||||
| headerName: t("Remark"), | headerName: t("Remark"), | ||||
| flex: 1, | flex: 1, | ||||
| align: "left", | align: "left", | ||||
| headerAlign: "center", | |||||
| headerAlign: "left", | |||||
| renderCell: (params) => { | renderCell: (params) => { | ||||
| return <Typography sx={{ fontSize: "14px" }}>{params.value || ""}</Typography>; | |||||
| return <Typography sx={{ fontSize: "18px" }}>{params.value || ""}</Typography>; | |||||
| }, | }, | ||||
| }, | }, | ||||
| ]; | ]; | ||||
| @@ -521,9 +522,9 @@ const handleRelease = useCallback(async ( jobOrderId: number) => { | |||||
| <Tab label={t("Production Process Line Remark")} /> | <Tab label={t("Production Process Line Remark")} /> | ||||
| {!fromJosave && ( | |||||
| {/* {!fromJosave && ( | |||||
| <Tab label={t("Matching Stock")} /> | <Tab label={t("Matching Stock")} /> | ||||
| )} | |||||
| )} */} | |||||
| </Tabs> | </Tabs> | ||||
| </Box> | </Box> | ||||
| @@ -545,8 +546,8 @@ const handleRelease = useCallback(async ( jobOrderId: number) => { | |||||
| )} | )} | ||||
| {tabIndex === 3 && <ProductionProcessesLineRemarkTableContent />} | {tabIndex === 3 && <ProductionProcessesLineRemarkTableContent />} | ||||
| {tabIndex === 4 && <JobPickExecutionsecondscan filterArgs={{ jobOrderId: jobOrderId }} />} | |||||
| {/* {tabIndex === 4 && <JobPickExecutionsecondscan filterArgs={{ jobOrderId: jobOrderId }} />} */} | |||||
| <Dialog | <Dialog | ||||
| open={openOperationPriorityDialog} | open={openOperationPriorityDialog} | ||||
| onClose={handleClosePriorityDialog} | onClose={handleClosePriorityDialog} | ||||
| @@ -24,19 +24,22 @@ import { | |||||
| AllJoborderProductProcessInfoResponse, | AllJoborderProductProcessInfoResponse, | ||||
| updateJo, | updateJo, | ||||
| fetchProductProcessesByJobOrderId, | fetchProductProcessesByJobOrderId, | ||||
| completeProductProcessLine | |||||
| completeProductProcessLine, | |||||
| assignJobOrderPickOrder | |||||
| } from "@/app/api/jo/actions"; | } from "@/app/api/jo/actions"; | ||||
| import { StockInLineInput } from "@/app/api/stockIn"; | import { StockInLineInput } from "@/app/api/stockIn"; | ||||
| import { PrinterCombo } from "@/app/api/settings/printer"; | import { PrinterCombo } from "@/app/api/settings/printer"; | ||||
| import JobPickExecutionsecondscan from "../Jodetail/JobPickExecutionsecondscan"; | |||||
| interface ProductProcessListProps { | interface ProductProcessListProps { | ||||
| onSelectProcess: (jobOrderId: number|undefined, productProcessId: number|undefined) => void; | onSelectProcess: (jobOrderId: number|undefined, productProcessId: number|undefined) => void; | ||||
| onSelectMatchingStock: (jobOrderId: number|undefined, productProcessId: number|undefined) => void; | |||||
| printerCombo: PrinterCombo[]; | printerCombo: PrinterCombo[]; | ||||
| } | } | ||||
| const PER_PAGE = 6; | const PER_PAGE = 6; | ||||
| const ProductProcessList: React.FC<ProductProcessListProps> = ({ onSelectProcess, printerCombo }) => { | |||||
| const ProductProcessList: React.FC<ProductProcessListProps> = ({ onSelectProcess, printerCombo ,onSelectMatchingStock}) => { | |||||
| const { t } = useTranslation( ["common", "production","purchaseOrder"]); | const { t } = useTranslation( ["common", "production","purchaseOrder"]); | ||||
| const { data: session } = useSession() as { data: SessionWithTokens | null }; | const { data: session } = useSession() as { data: SessionWithTokens | null }; | ||||
| const sessionToken = session as SessionWithTokens | null; | const sessionToken = session as SessionWithTokens | null; | ||||
| @@ -45,6 +48,44 @@ const ProductProcessList: React.FC<ProductProcessListProps> = ({ onSelectProcess | |||||
| const [page, setPage] = useState(0); | const [page, setPage] = useState(0); | ||||
| const [openModal, setOpenModal] = useState<boolean>(false); | const [openModal, setOpenModal] = useState<boolean>(false); | ||||
| const [modalInfo, setModalInfo] = useState<StockInLineInput>(); | const [modalInfo, setModalInfo] = useState<StockInLineInput>(); | ||||
| const currentUserId = session?.id ? parseInt(session.id) : undefined; | |||||
| const handleAssignPickOrder = useCallback(async (pickOrderId: number, jobOrderId?: number, productProcessId?: number) => { | |||||
| if (!currentUserId) { | |||||
| alert(t("无法获取用户ID")); | |||||
| return; | |||||
| } | |||||
| try { | |||||
| console.log("🔄 Assigning pick order:", pickOrderId, "to user:", currentUserId); | |||||
| // 调用分配 API 并读取响应 | |||||
| const assignResult = await assignJobOrderPickOrder(pickOrderId, currentUserId); | |||||
| console.log("📦 Assign result:", assignResult); | |||||
| // 检查分配是否成功 | |||||
| if (assignResult.message === "Successfully assigned") { | |||||
| console.log("✅ Successfully assigned pick order"); | |||||
| console.log("✅ Pick order ID:", assignResult.id); | |||||
| console.log("✅ Pick order code:", assignResult.code); | |||||
| // 分配成功后,导航到 second scan 页面 | |||||
| if (onSelectMatchingStock && jobOrderId) { | |||||
| onSelectMatchingStock(jobOrderId, productProcessId); | |||||
| } else { | |||||
| alert(t("分配成功")); | |||||
| } | |||||
| } else { | |||||
| // 分配失败 | |||||
| console.error("❌ Assignment failed:", assignResult.message); | |||||
| alert(t(`分配失败: ${assignResult.message || "未知错误"}`)); | |||||
| } | |||||
| } catch (error: any) { | |||||
| console.error("❌ Error assigning pick order:", error); | |||||
| alert(t(`分配时出错: ${error?.message || "未知错误"}。请稍后重试。`)); | |||||
| } | |||||
| }, [currentUserId, t, onSelectMatchingStock]); | |||||
| const handleViewStockIn = useCallback((process: AllJoborderProductProcessInfoResponse) => { | const handleViewStockIn = useCallback((process: AllJoborderProductProcessInfoResponse) => { | ||||
| if (!process.stockInLineId) { | if (!process.stockInLineId) { | ||||
| alert(t("Invalid Stock In Line Id")); | alert(t("Invalid Stock In Line Id")); | ||||
| @@ -224,6 +265,14 @@ const ProductProcessList: React.FC<ProductProcessListProps> = ({ onSelectProcess | |||||
| <Button variant="contained" size="small" onClick={() => onSelectProcess(process.jobOrderId, process.id)}> | <Button variant="contained" size="small" onClick={() => onSelectProcess(process.jobOrderId, process.id)}> | ||||
| {t("View Details")} | {t("View Details")} | ||||
| </Button> | </Button> | ||||
| <Button | |||||
| variant="contained" | |||||
| size="small" | |||||
| disabled={process.assignedTo != null} | |||||
| onClick={() => handleAssignPickOrder(process.pickOrderId, process.jobOrderId, process.id)} | |||||
| > | |||||
| {t("Matching Stock")} | |||||
| </Button> | |||||
| {statusLower !== "completed" && ( | {statusLower !== "completed" && ( | ||||
| <Button variant="contained" size="small" onClick={() => handleUpdateJo(process)}> | <Button variant="contained" size="small" onClick={() => handleUpdateJo(process)}> | ||||
| {t("Update Job Order")} | {t("Update Job Order")} | ||||
| @@ -5,7 +5,7 @@ import { SessionWithTokens } from "@/config/authConfig"; | |||||
| import ProductionProcessList from "@/components/ProductionProcess/ProductionProcessList"; | import ProductionProcessList from "@/components/ProductionProcess/ProductionProcessList"; | ||||
| import ProductionProcessDetail from "@/components/ProductionProcess/ProductionProcessDetail"; | import ProductionProcessDetail from "@/components/ProductionProcess/ProductionProcessDetail"; | ||||
| import ProductionProcessJobOrderDetail from "@/components/ProductionProcess/ProductionProcessJobOrderDetail"; | import ProductionProcessJobOrderDetail from "@/components/ProductionProcess/ProductionProcessJobOrderDetail"; | ||||
| import JobPickExecutionsecondscan from "@/components/Jodetail/JobPickExecutionsecondscan"; | |||||
| import { | import { | ||||
| fetchProductProcesses, | fetchProductProcesses, | ||||
| fetchProductProcessesByJobOrderId, | fetchProductProcessesByJobOrderId, | ||||
| @@ -28,11 +28,21 @@ interface ProductionProcessPageProps { | |||||
| const ProductionProcessPage: React.FC<ProductionProcessPageProps> = ({ printerCombo }) => { | const ProductionProcessPage: React.FC<ProductionProcessPageProps> = ({ printerCombo }) => { | ||||
| const [selectedProcessId, setSelectedProcessId] = useState<number | null>(null); | const [selectedProcessId, setSelectedProcessId] = useState<number | null>(null); | ||||
| const [selectedMatchingStock, setSelectedMatchingStock] = useState<{ | |||||
| jobOrderId: number; | |||||
| productProcessId: number; | |||||
| } | null>(null); | |||||
| const { data: session } = useSession() as { data: SessionWithTokens | null }; | const { data: session } = useSession() as { data: SessionWithTokens | null }; | ||||
| const currentUserId = session?.id ? parseInt(session.id) : undefined; | const currentUserId = session?.id ? parseInt(session.id) : undefined; | ||||
| // …原有逻辑省略… | |||||
| if (selectedMatchingStock) { | |||||
| return ( | |||||
| <JobPickExecutionsecondscan | |||||
| filterArgs={{ jobOrderId: selectedMatchingStock.jobOrderId }} | |||||
| onBack={() => setSelectedMatchingStock(null)} | |||||
| /> | |||||
| ); | |||||
| } | |||||
| if (selectedProcessId !== null) { | if (selectedProcessId !== null) { | ||||
| return ( | return ( | ||||
| <ProductionProcessJobOrderDetail | <ProductionProcessJobOrderDetail | ||||
| @@ -51,6 +61,12 @@ const ProductionProcessPage: React.FC<ProductionProcessPageProps> = ({ printerCo | |||||
| setSelectedProcessId(id); | setSelectedProcessId(id); | ||||
| } | } | ||||
| }} | }} | ||||
| onSelectMatchingStock={(jobOrderId, productProcessId) => { | |||||
| setSelectedMatchingStock({ | |||||
| jobOrderId: jobOrderId || 0, | |||||
| productProcessId: productProcessId || 0 | |||||
| }); | |||||
| }} | |||||
| /> | /> | ||||
| ); | ); | ||||
| }; | }; | ||||
| @@ -118,7 +118,7 @@ | |||||
| "Stop Scan": "停止掃碼", | "Stop Scan": "停止掃碼", | ||||
| "Scan Result": "掃碼結果", | "Scan Result": "掃碼結果", | ||||
| "Expiry Date": "有效期", | "Expiry Date": "有效期", | ||||
| "Is Dark | Dense | Float| Scrap Rate| Allergic Substance": "顔色深淺度 | 濃淡 | 浮沉 | 損耗率 | 過敏原", | |||||
| "Pick Order Code": "提料單編號", | "Pick Order Code": "提料單編號", | ||||
| "Target Date": "需求日期", | "Target Date": "需求日期", | ||||
| "Lot Required Pick Qty": "批號需求數量", | "Lot Required Pick Qty": "批號需求數量", | ||||
| @@ -183,7 +183,7 @@ | |||||
| "Back": "返回", | "Back": "返回", | ||||
| "BoM Material": "物料清單", | "BoM Material": "物料清單", | ||||
| "N/A": "不適用", | "N/A": "不適用", | ||||
| "Is Dark | Dense | Float | Scrap Rate | Allergic Substance": "顔色深淺度 | 濃淡 | 浮沉 | 損耗率 | 過敏原", | |||||
| "Is Dark | Dense | Float| Scrap Rate| Allergic Substance | Time Sequence | Complexity": "顔色深淺度 | 濃淡 | 浮沉 | 損耗率 | 過敏原 | 時間順序 | 複雜度", | |||||
| "Item Code": "物料編號", | "Item Code": "物料編號", | ||||
| "Item Name": "物料名稱", | "Item Name": "物料名稱", | ||||
| "Job Order Info": "工單信息", | "Job Order Info": "工單信息", | ||||
| @@ -210,6 +210,7 @@ | |||||
| "Reason": "原因", | "Reason": "原因", | ||||
| "Invalid Stock In Line Id": "無效庫存行ID", | "Invalid Stock In Line Id": "無效庫存行ID", | ||||
| "Production date": "生產日期", | "Production date": "生產日期", | ||||
| "update production priority": "更新生產優先級", | |||||
| "Required Qty": "需求數量", | "Required Qty": "需求數量", | ||||
| "Total processes": "總流程數", | "Total processes": "總流程數", | ||||
| "View Details": "查看詳情", | "View Details": "查看詳情", | ||||
| @@ -47,6 +47,7 @@ | |||||
| "Pause Reason": "暫停原因", | "Pause Reason": "暫停原因", | ||||
| "Reason": "原因", | "Reason": "原因", | ||||
| "Stock Available": "倉庫可用數", | "Stock Available": "倉庫可用數", | ||||
| "update production priority": "更新生產優先級", | |||||
| "Staff No": "員工編號", | "Staff No": "員工編號", | ||||
| "Please scan staff no": "請掃描員工編號", | "Please scan staff no": "請掃描員工編號", | ||||
| "Stock Status": "可提料", | "Stock Status": "可提料", | ||||
| @@ -349,8 +350,8 @@ | |||||
| "View": "查看", | "View": "查看", | ||||
| "Back": "返回", | "Back": "返回", | ||||
| "N/A": "不適用", | "N/A": "不適用", | ||||
| "BoM Material": "半成品/成品清單", | |||||
| "Is Dark | Dense | Float| Scrap Rate| Allergic Substance": "顔色深淺度 | 濃淡 | 浮沉 | 損耗率 | 過敏原", | |||||
| "BoM Material": "物料清單", | |||||
| "Is Dark | Dense | Float| Scrap Rate| Allergic Substance | Time Sequence | Complexity": "顔色深淺度 | 濃淡 | 浮沉 | 損耗率 | 過敏原 | 時間順序 | 複雜度", | |||||
| "Item Code": "物料編號", | "Item Code": "物料編號", | ||||
| "Item Name": "物料名稱", | "Item Name": "物料名稱", | ||||
| "Enter the number of cartons: ": "請輸入箱數:", | "Enter the number of cartons: ": "請輸入箱數:", | ||||