| @@ -1210,12 +1210,15 @@ export const fetchMaterialPickStatus = cache(async (): Promise<MaterialPickStatu | |||||
| ); | ); | ||||
| }) | }) | ||||
| export interface ProcessStatusInfo { | export interface ProcessStatusInfo { | ||||
| processName?: string | null; | |||||
| equipmentDescription?: string | null; | |||||
| equipmentDetailName?: string | null; | |||||
| startTime?: string | null; | startTime?: string | null; | ||||
| endTime?: string | null; | endTime?: string | null; | ||||
| equipmentCode?: string | null; | |||||
| isRequired: boolean; | isRequired: boolean; | ||||
| } | } | ||||
| export interface JobProcessStatusResponse { | export interface JobProcessStatusResponse { | ||||
| jobOrderId: number; | jobOrderId: number; | ||||
| jobOrderCode: string; | jobOrderCode: string; | ||||
| @@ -1229,15 +1232,17 @@ export interface JobProcessStatusResponse { | |||||
| processes: ProcessStatusInfo[]; | processes: ProcessStatusInfo[]; | ||||
| } | } | ||||
| // 添加API调用函数 | |||||
| export const fetchJobProcessStatus = cache(async () => { | |||||
| return serverFetchJson<JobProcessStatusResponse[]>( | |||||
| `${BASE_API_URL}/product-process/Demo/JobProcessStatus`, | |||||
| { | |||||
| method: "GET", | |||||
| next: { tags: ["jobProcessStatus"] }, | |||||
| } | |||||
| ); | |||||
| export const fetchJobProcessStatus = cache(async (date?: string) => { | |||||
| const params = new URLSearchParams(); | |||||
| if (date) params.set("date", date); // yyyy-MM-dd | |||||
| const qs = params.toString(); | |||||
| const url = `${BASE_API_URL}/product-process/Demo/JobProcessStatus${qs ? `?${qs}` : ""}`; | |||||
| return serverFetchJson<JobProcessStatusResponse[]>(url, { | |||||
| method: "GET", | |||||
| next: { tags: ["jobProcessStatus"] }, | |||||
| }); | |||||
| }); | }); | ||||
| export const deleteProductProcessLine = async (lineId: number) => { | export const deleteProductProcessLine = async (lineId: number) => { | ||||
| return serverFetchJson<any>( | return serverFetchJson<any>( | ||||
| @@ -12,7 +12,7 @@ import { RecordsRes } from "../utils"; | |||||
| import { Uom } from "../settings/uom"; | import { Uom } from "../settings/uom"; | ||||
| import { convertObjToURLSearchParams } from "@/app/utils/commonUtil"; | import { convertObjToURLSearchParams } from "@/app/utils/commonUtil"; | ||||
| // import { BASE_API_URL } from "@/config/api"; | // import { BASE_API_URL } from "@/config/api"; | ||||
| import { Result } from "../settings/item"; | |||||
| export interface PostStockInLineResponse<T> { | export interface PostStockInLineResponse<T> { | ||||
| id: number | null; | id: number | null; | ||||
| name: string; | name: string; | ||||
| @@ -242,3 +242,9 @@ export const printQrCodeForSil = cache(async(data: PrintQrCodeForSilRequest) => | |||||
| }, | }, | ||||
| ) | ) | ||||
| }) | }) | ||||
| // 添加服务器端 action 用于从客户端组件获取 item 信息 | |||||
| export const fetchItemForPutAway = cache(async (id: number): Promise<Result> => { | |||||
| return serverFetchJson<Result>(`${BASE_API_URL}/items/details/${id}`, { | |||||
| next: { tags: ["items"] }, | |||||
| }); | |||||
| }); | |||||
| @@ -28,10 +28,10 @@ import { fetchStockInLineInfo } from "@/app/api/po/actions"; // Add this import | |||||
| import PickExecutionForm from "./PickExecutionForm"; | import PickExecutionForm from "./PickExecutionForm"; | ||||
| interface LotPickData { | interface LotPickData { | ||||
| id: number; | id: number; | ||||
| lotId: number; | |||||
| lotNo: string; | |||||
| lotId: number ; | |||||
| lotNo: string ; | |||||
| expiryDate: string; | expiryDate: string; | ||||
| location: string; | |||||
| location: string| null; | |||||
| stockUnit: string; | stockUnit: string; | ||||
| inQty: number; | inQty: number; | ||||
| availableQty: number; | availableQty: number; | ||||
| @@ -45,6 +45,7 @@ interface LotPickData { | |||||
| stockOutLineId?: number; | stockOutLineId?: number; | ||||
| stockOutLineStatus?: string; | stockOutLineStatus?: string; | ||||
| stockOutLineQty?: number; | stockOutLineQty?: number; | ||||
| noLot?: boolean; | |||||
| } | } | ||||
| interface PickQtyData { | interface PickQtyData { | ||||
| @@ -334,7 +334,7 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => { | |||||
| const handlePickQtyChange = useCallback((lineId: number, lotId: number, value: number | string) => { | const handlePickQtyChange = useCallback((lineId: number, lotId: number, value: number | string) => { | ||||
| console.log("Changing pick qty:", { lineId, lotId, value }); | console.log("Changing pick qty:", { lineId, lotId, value }); | ||||
| const numericValue = typeof value === 'string' ? (value === '' ? 0 : parseInt(value, 10)) : value; | |||||
| const numericValue = typeof value === 'string' ? (value === '' ? 0 : parseFloat(value) || 0) : value; | |||||
| setPickQtyData(prev => { | setPickQtyData(prev => { | ||||
| const newData = { | const newData = { | ||||
| @@ -74,7 +74,7 @@ const SearchResultsTable: React.FC<SearchResultsTableProps> = ({ | |||||
| const handleQtyChange = useCallback((itemId: number, value: string) => { | const handleQtyChange = useCallback((itemId: number, value: string) => { | ||||
| // Only allow numbers | // Only allow numbers | ||||
| if (value === "" || /^\d+$/.test(value)) { | |||||
| if (value === "" || /^\d*\.?\d+$/.test(value)) { | |||||
| const numValue = value === "" ? null : Number(value); | const numValue = value === "" ? null : Number(value); | ||||
| onQtyChange(itemId, numValue); | onQtyChange(itemId, numValue); | ||||
| } | } | ||||
| @@ -20,7 +20,7 @@ import { useTranslation } from 'react-i18next'; | |||||
| import dayjs from 'dayjs'; | import dayjs from 'dayjs'; | ||||
| import { fetchJobProcessStatus, JobProcessStatusResponse } from '@/app/api/jo/actions'; | import { fetchJobProcessStatus, JobProcessStatusResponse } from '@/app/api/jo/actions'; | ||||
| import { arrayToDayjs } from '@/app/utils/formatUtil'; | import { arrayToDayjs } from '@/app/utils/formatUtil'; | ||||
| import { FormControl, Select, MenuItem } from "@mui/material"; | |||||
| const REFRESH_INTERVAL = 10 * 60 * 1000; // 10 minutes | const REFRESH_INTERVAL = 10 * 60 * 1000; // 10 minutes | ||||
| const JobProcessStatus: React.FC = () => { | const JobProcessStatus: React.FC = () => { | ||||
| @@ -29,6 +29,7 @@ const JobProcessStatus: React.FC = () => { | |||||
| const [loading, setLoading] = useState<boolean>(true); | const [loading, setLoading] = useState<boolean>(true); | ||||
| const refreshCountRef = useRef<number>(0); | const refreshCountRef = useRef<number>(0); | ||||
| const [currentTime, setCurrentTime] = useState(dayjs()); | const [currentTime, setCurrentTime] = useState(dayjs()); | ||||
| const [selectedDate, setSelectedDate] = useState(dayjs().format("YYYY-MM-DD")); | |||||
| // Update current time every second for countdown | // Update current time every second for countdown | ||||
| useEffect(() => { | useEffect(() => { | ||||
| @@ -41,21 +42,8 @@ const JobProcessStatus: React.FC = () => { | |||||
| const loadData = useCallback(async () => { | const loadData = useCallback(async () => { | ||||
| setLoading(true); | setLoading(true); | ||||
| try { | try { | ||||
| const result = await fetchJobProcessStatus(); | |||||
| // On second refresh, filter out completed jobs | |||||
| if (refreshCountRef.current >= 1) { | |||||
| const filtered = result.filter(item => { | |||||
| // Check if all required processes are completed | |||||
| const allCompleted = item.processes | |||||
| .filter(p => p.isRequired) | |||||
| .every(p => p.endTime != null); | |||||
| return !allCompleted; | |||||
| }); | |||||
| setData(filtered); | |||||
| } else { | |||||
| setData(result); | |||||
| } | |||||
| const result = await fetchJobProcessStatus(selectedDate); | |||||
| setData(result); | |||||
| refreshCountRef.current += 1; | refreshCountRef.current += 1; | ||||
| } catch (error) { | } catch (error) { | ||||
| console.error('Error fetching job process status:', error); | console.error('Error fetching job process status:', error); | ||||
| @@ -63,7 +51,7 @@ const JobProcessStatus: React.FC = () => { | |||||
| } finally { | } finally { | ||||
| setLoading(false); | setLoading(false); | ||||
| } | } | ||||
| }, []); | |||||
| }, [selectedDate]); | |||||
| useEffect(() => { | useEffect(() => { | ||||
| loadData(); | loadData(); | ||||
| @@ -183,12 +171,22 @@ const JobProcessStatus: React.FC = () => { | |||||
| return ( | return ( | ||||
| <Card sx={{ mb: 2 }}> | <Card sx={{ mb: 2 }}> | ||||
| <CardContent> | <CardContent> | ||||
| <Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 3 }}> | |||||
| <Typography variant="h5" sx={{ fontWeight: 600 }}> | |||||
| {t("Job Process Status", )} | |||||
| </Typography> | |||||
| </Box> | |||||
| <Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 3 }}> | |||||
| <Typography variant="h5" sx={{ fontWeight: 600 }}> | |||||
| {t("Job Process Status")} | |||||
| </Typography> | |||||
| <FormControl size="small" sx={{ minWidth: 160 }}> | |||||
| <Select | |||||
| value={selectedDate} | |||||
| onChange={(e) => setSelectedDate(e.target.value)} | |||||
| > | |||||
| <MenuItem value={dayjs().format("YYYY-MM-DD")}>今天</MenuItem> | |||||
| <MenuItem value={dayjs().subtract(1, "day").format("YYYY-MM-DD")}>昨天</MenuItem> | |||||
| <MenuItem value={dayjs().subtract(2, "day").format("YYYY-MM-DD")}>前天</MenuItem> | |||||
| </Select> | |||||
| </FormControl> | |||||
| </Box> | |||||
| <Box sx={{ mt: 2 }}> | <Box sx={{ mt: 2 }}> | ||||
| {loading ? ( | {loading ? ( | ||||
| @@ -285,12 +283,16 @@ const JobProcessStatus: React.FC = () => { | |||||
| </TableCell> | </TableCell> | ||||
| ); | ); | ||||
| } | } | ||||
| const label = [ | |||||
| process.processName, | |||||
| process.equipmentDescription, | |||||
| process.equipmentDetailName ? `-${process.equipmentDetailName}` : "", | |||||
| ].filter(Boolean).join(" "); | |||||
| // 如果工序是必需的,显示三行(Start、Finish、Wait Time) | // 如果工序是必需的,显示三行(Start、Finish、Wait Time) | ||||
| return ( | return ( | ||||
| <TableCell key={index} align="center"> | <TableCell key={index} align="center"> | ||||
| <Box sx={{ display: 'flex', flexDirection: 'column', gap: 0.5 }}> | <Box sx={{ display: 'flex', flexDirection: 'column', gap: 0.5 }}> | ||||
| <Typography variant="body2">{process.equipmentCode || '-'}</Typography> | |||||
| <Typography variant="body2">{label || "-"}</Typography> | |||||
| <Typography variant="body2"> | <Typography variant="body2"> | ||||
| {formatTime(process.startTime)} | {formatTime(process.startTime)} | ||||
| </Typography> | </Typography> | ||||
| @@ -467,17 +467,19 @@ const handleRelease = useCallback(async ( jobOrderId: number) => { | |||||
| align: "left", | align: "left", | ||||
| headerAlign: "left", | headerAlign: "left", | ||||
| type: "number", | type: "number", | ||||
| sortable: false, // ✅ 禁用排序 | |||||
| }, | }, | ||||
| { | { | ||||
| field: "itemCode", | field: "itemCode", | ||||
| headerName: t("Item Code"), | |||||
| headerName: t("Material Code"), | |||||
| flex: 0.6, | flex: 0.6, | ||||
| sortable: false, // ✅ 禁用排序 | |||||
| }, | }, | ||||
| { | { | ||||
| field: "itemName", | field: "itemName", | ||||
| headerName: t("Item Name"), | headerName: t("Item Name"), | ||||
| flex: 1, | flex: 1, | ||||
| sortable: false, // ✅ 禁用排序 | |||||
| renderCell: (params: GridRenderCellParams<JobOrderLineInfo>) => { | renderCell: (params: GridRenderCellParams<JobOrderLineInfo>) => { | ||||
| return `${params.value} (${params.row.reqUom})`; | return `${params.value} (${params.row.reqUom})`; | ||||
| }, | }, | ||||
| @@ -488,21 +490,35 @@ const handleRelease = useCallback(async ( jobOrderId: number) => { | |||||
| flex: 0.7, | flex: 0.7, | ||||
| align: "right", | align: "right", | ||||
| headerAlign: "right", | headerAlign: "right", | ||||
| renderCell: (params: GridRenderCellParams<JobOrderLineInfo>) => { | |||||
| const qty = showBaseQty ? params.row.baseReqQty : params.value; | |||||
| const uom = showBaseQty ? params.row.reqBaseUom : params.row.reqUom; | |||||
| sortable: false, // ✅ 禁用排序 | |||||
| // ✅ 将切换功能移到 header | |||||
| renderHeader: () => { | |||||
| const qty = showBaseQty ? t("Base") : t("Req"); | |||||
| const uom = showBaseQty ? t("Base UOM") : t(" "); | |||||
| return ( | return ( | ||||
| <Box | <Box | ||||
| onClick={toggleBaseQty} | onClick={toggleBaseQty} | ||||
| sx={{ | sx={{ | ||||
| cursor: "pointer", | cursor: "pointer", | ||||
| userSelect: "none", | userSelect: "none", | ||||
| width: "100%", | |||||
| textAlign: "right", | |||||
| "&:hover": { | "&:hover": { | ||||
| textDecoration: "underline", | textDecoration: "underline", | ||||
| }, | }, | ||||
| }} | }} | ||||
| > | > | ||||
| {t("Bom Req. Qty")} ({uom}) | |||||
| </Box> | |||||
| ); | |||||
| }, | |||||
| // ✅ 移除 cell 中的 onClick,只显示值 | |||||
| renderCell: (params: GridRenderCellParams<JobOrderLineInfo>) => { | |||||
| const qty = showBaseQty ? params.row.baseReqQty : params.value; | |||||
| const uom = showBaseQty ? params.row.reqBaseUom : params.row.reqUom; | |||||
| return ( | |||||
| <Box sx={{ textAlign: "right" }}> | |||||
| {decimalFormatter.format(qty || 0)} ({uom || ""}) | {decimalFormatter.format(qty || 0)} ({uom || ""}) | ||||
| </Box> | </Box> | ||||
| ); | ); | ||||
| @@ -514,27 +530,39 @@ const handleRelease = useCallback(async ( jobOrderId: number) => { | |||||
| flex: 0.7, | flex: 0.7, | ||||
| align: "right", | align: "right", | ||||
| headerAlign: "right", | headerAlign: "right", | ||||
| renderCell: (params: GridRenderCellParams<JobOrderLineInfo>) => { | |||||
| const qty = showBaseQty ? params.row.baseReqQty : params.value; | |||||
| const uom = showBaseQty ? params.row.reqBaseUom : params.row.stockUom; | |||||
| sortable: false, // ✅ 禁用排序 | |||||
| // ✅ 将切换功能移到 header | |||||
| renderHeader: () => { | |||||
| const uom = showBaseQty ? t("Base UOM") : t("Stock UOM"); | |||||
| return ( | return ( | ||||
| <Box | <Box | ||||
| onClick={toggleBaseQty} | onClick={toggleBaseQty} | ||||
| sx={{ | sx={{ | ||||
| cursor: "pointer", | cursor: "pointer", | ||||
| userSelect: "none", | userSelect: "none", | ||||
| width: "100%", | |||||
| textAlign: "right", | |||||
| "&:hover": { | "&:hover": { | ||||
| textDecoration: "underline", | textDecoration: "underline", | ||||
| }, | }, | ||||
| }} | }} | ||||
| > | > | ||||
| {t("Stock Req. Qty")} ({uom}) | |||||
| </Box> | |||||
| ); | |||||
| }, | |||||
| // ✅ 移除 cell 中的 onClick | |||||
| renderCell: (params: GridRenderCellParams<JobOrderLineInfo>) => { | |||||
| const qty = showBaseQty ? params.row.baseReqQty : params.value; | |||||
| const uom = showBaseQty ? params.row.reqBaseUom : params.row.stockUom; | |||||
| return ( | |||||
| <Box sx={{ textAlign: "right" }}> | |||||
| {decimalFormatter.format(qty || 0)} ({uom || ""}) | {decimalFormatter.format(qty || 0)} ({uom || ""}) | ||||
| </Box> | </Box> | ||||
| ); | ); | ||||
| }, | }, | ||||
| }, | }, | ||||
| { | { | ||||
| field: "stockAvailable", | field: "stockAvailable", | ||||
| headerName: t("Stock Available"), | headerName: t("Stock Available"), | ||||
| @@ -542,22 +570,35 @@ const handleRelease = useCallback(async ( jobOrderId: number) => { | |||||
| align: "right", | align: "right", | ||||
| headerAlign: "right", | headerAlign: "right", | ||||
| type: "number", | type: "number", | ||||
| renderCell: (params: GridRenderCellParams<JobOrderLineInfo>) => { | |||||
| const stockAvailable = getStockAvailable(params.row); | |||||
| const qty = showBaseQty ? params.row.baseStockQty : (stockAvailable || 0); | |||||
| const uom = showBaseQty ? params.row.stockBaseUom : params.row.stockUom; | |||||
| sortable: false, // ✅ 禁用排序 | |||||
| // ✅ 将切换功能移到 header | |||||
| renderHeader: () => { | |||||
| const uom = showBaseQty ? t("Base UOM") : t("Stock UOM"); | |||||
| return ( | return ( | ||||
| <Box | <Box | ||||
| onClick={toggleBaseQty} | onClick={toggleBaseQty} | ||||
| sx={{ | sx={{ | ||||
| cursor: "pointer", | cursor: "pointer", | ||||
| userSelect: "none", | userSelect: "none", | ||||
| width: "100%", | |||||
| textAlign: "right", | |||||
| "&:hover": { | "&:hover": { | ||||
| textDecoration: "underline", | textDecoration: "underline", | ||||
| }, | }, | ||||
| }} | }} | ||||
| > | > | ||||
| {t("Stock Available")} ({uom}) | |||||
| </Box> | |||||
| ); | |||||
| }, | |||||
| // ✅ 移除 cell 中的 onClick | |||||
| renderCell: (params: GridRenderCellParams<JobOrderLineInfo>) => { | |||||
| const stockAvailable = getStockAvailable(params.row); | |||||
| const qty = showBaseQty ? params.row.baseStockQty : (stockAvailable || 0); | |||||
| const uom = showBaseQty ? params.row.stockBaseUom : params.row.stockUom; | |||||
| return ( | |||||
| <Box sx={{ textAlign: "right" }}> | |||||
| {decimalFormatter.format(qty || 0)} ({uom || ""}) | {decimalFormatter.format(qty || 0)} ({uom || ""}) | ||||
| </Box> | </Box> | ||||
| ); | ); | ||||
| @@ -570,17 +611,8 @@ const handleRelease = useCallback(async ( jobOrderId: number) => { | |||||
| align: "right", | align: "right", | ||||
| headerAlign: "right", | headerAlign: "right", | ||||
| type: "number", | type: "number", | ||||
| sortable: false, // ✅ 禁用排序 | |||||
| }, | }, | ||||
| /* | |||||
| { | |||||
| field: "seqNoRemark", | |||||
| headerName: t("Seq No Remark"), | |||||
| flex: 1, | |||||
| align: "left", | |||||
| headerAlign: "left", | |||||
| type: "string", | |||||
| }, | |||||
| */ | |||||
| { | { | ||||
| field: "stockStatus", | field: "stockStatus", | ||||
| headerName: t("Stock Status"), | headerName: t("Stock Status"), | ||||
| @@ -588,8 +620,8 @@ const handleRelease = useCallback(async ( jobOrderId: number) => { | |||||
| align: "center", | align: "center", | ||||
| headerAlign: "center", | headerAlign: "center", | ||||
| type: "boolean", | type: "boolean", | ||||
| sortable: false, // ✅ 禁用排序 | |||||
| renderCell: (params: GridRenderCellParams<JobOrderLineInfo>) => { | renderCell: (params: GridRenderCellParams<JobOrderLineInfo>) => { | ||||
| return isStockSufficient(params.row) | return isStockSufficient(params.row) | ||||
| ? <CheckCircleOutlineOutlinedIcon fontSize={"large"} color="success" /> | ? <CheckCircleOutlineOutlinedIcon fontSize={"large"} color="success" /> | ||||
| : <DoDisturbAltRoundedIcon fontSize={"large"} color="error" />; | : <DoDisturbAltRoundedIcon fontSize={"large"} color="error" />; | ||||
| @@ -18,6 +18,8 @@ import { | |||||
| Card, | Card, | ||||
| CardContent, | CardContent, | ||||
| Grid, | Grid, | ||||
| Select, | |||||
| MenuItem, | |||||
| } from "@mui/material"; | } from "@mui/material"; | ||||
| import { Alert } from "@mui/material"; | import { Alert } from "@mui/material"; | ||||
| @@ -106,7 +108,9 @@ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionPro | |||||
| if (!lineDetail) return false; | if (!lineDetail) return false; | ||||
| return lineDetail.name === "包裝"; | return lineDetail.name === "包裝"; | ||||
| }, [lineDetail]) | }, [lineDetail]) | ||||
| const uomList = [ | |||||
| "個", "件", "箱", "片", "塊" | |||||
| ]; | |||||
| // ✅ 添加:刷新 line detail 的函数 | // ✅ 添加:刷新 line detail 的函数 | ||||
| const handleRefreshLineDetail = useCallback(async () => { | const handleRefreshLineDetail = useCallback(async () => { | ||||
| if (lineId) { | if (lineId) { | ||||
| @@ -179,7 +183,7 @@ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionPro | |||||
| }); | }); | ||||
| if (!lineDetail?.durationInMinutes || !lineDetail?.startTime) { | if (!lineDetail?.durationInMinutes || !lineDetail?.startTime) { | ||||
| console.log("❌ Line duration or start time is not valid", { | |||||
| console.log(" Line duration or start time is not valid", { | |||||
| durationInMinutes: lineDetail?.durationInMinutes, | durationInMinutes: lineDetail?.durationInMinutes, | ||||
| startTime: lineDetail?.startTime, | startTime: lineDetail?.startTime, | ||||
| equipmentId: lineDetail?.equipmentId, | equipmentId: lineDetail?.equipmentId, | ||||
| @@ -527,11 +531,11 @@ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionPro | |||||
| setLastPauseTime(null); | setLastPauseTime(null); | ||||
| }) | }) | ||||
| .catch(err => { | .catch(err => { | ||||
| console.error("❌ Failed to load line detail after resume", err); | |||||
| console.error(" Failed to load line detail after resume", err); | |||||
| }); | }); | ||||
| } | } | ||||
| } catch (error) { | } catch (error) { | ||||
| console.error("❌ Error resuming:", error); | |||||
| console.error(" Error resuming:", error); | |||||
| alert(t("Failed to resume. Please try again.")); | alert(t("Failed to resume. Please try again.")); | ||||
| } | } | ||||
| }; | }; | ||||
| @@ -791,7 +795,7 @@ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionPro | |||||
| /> | /> | ||||
| </TableCell> | </TableCell> | ||||
| <TableCell> | <TableCell> | ||||
| <TextField | |||||
| <Select | |||||
| fullWidth | fullWidth | ||||
| size="small" | size="small" | ||||
| value={outputData.outputFromProcessUom} | value={outputData.outputFromProcessUom} | ||||
| @@ -799,7 +803,17 @@ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionPro | |||||
| ...outputData, | ...outputData, | ||||
| outputFromProcessUom: e.target.value | outputFromProcessUom: e.target.value | ||||
| })} | })} | ||||
| /> | |||||
| displayEmpty | |||||
| > | |||||
| <MenuItem value=""> | |||||
| <em>{t("Select Unit")}</em> | |||||
| </MenuItem> | |||||
| {uomList.map((uom) => ( | |||||
| <MenuItem key={uom} value={uom}> | |||||
| {uom} | |||||
| </MenuItem> | |||||
| ))} | |||||
| </Select> | |||||
| </TableCell> | </TableCell> | ||||
| <TableCell> | <TableCell> | ||||
| <Typography fontSize={15} align="center"> <strong>{t("Description")}</strong></Typography> | <Typography fontSize={15} align="center"> <strong>{t("Description")}</strong></Typography> | ||||
| @@ -823,7 +837,7 @@ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionPro | |||||
| /> | /> | ||||
| </TableCell> | </TableCell> | ||||
| <TableCell> | <TableCell> | ||||
| <TextField | |||||
| <Select | |||||
| fullWidth | fullWidth | ||||
| size="small" | size="small" | ||||
| value={outputData.defectUom} | value={outputData.defectUom} | ||||
| @@ -831,7 +845,17 @@ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionPro | |||||
| ...outputData, | ...outputData, | ||||
| defectUom: e.target.value | defectUom: e.target.value | ||||
| })} | })} | ||||
| /> | |||||
| displayEmpty | |||||
| > | |||||
| <MenuItem value=""> | |||||
| <em>{t("Select Unit")}</em> | |||||
| </MenuItem> | |||||
| {uomList.map((uom) => ( | |||||
| <MenuItem key={uom} value={uom}> | |||||
| {uom} | |||||
| </MenuItem> | |||||
| ))} | |||||
| </Select> | |||||
| </TableCell> | </TableCell> | ||||
| <TableCell> | <TableCell> | ||||
| <TextField | <TextField | ||||
| @@ -861,7 +885,7 @@ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionPro | |||||
| /> | /> | ||||
| </TableCell> | </TableCell> | ||||
| <TableCell> | <TableCell> | ||||
| <TextField | |||||
| <Select | |||||
| fullWidth | fullWidth | ||||
| size="small" | size="small" | ||||
| value={outputData.defect2Uom} | value={outputData.defect2Uom} | ||||
| @@ -869,7 +893,17 @@ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionPro | |||||
| ...outputData, | ...outputData, | ||||
| defect2Uom: e.target.value | defect2Uom: e.target.value | ||||
| })} | })} | ||||
| /> | |||||
| displayEmpty | |||||
| > | |||||
| <MenuItem value=""> | |||||
| <em>{t("Select Unit")}</em> | |||||
| </MenuItem> | |||||
| {uomList.map((uom) => ( | |||||
| <MenuItem key={uom} value={uom}> | |||||
| {uom} | |||||
| </MenuItem> | |||||
| ))} | |||||
| </Select> | |||||
| </TableCell> | </TableCell> | ||||
| <TableCell> | <TableCell> | ||||
| <TextField | <TextField | ||||
| @@ -899,7 +933,7 @@ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionPro | |||||
| /> | /> | ||||
| </TableCell> | </TableCell> | ||||
| <TableCell> | <TableCell> | ||||
| <TextField | |||||
| <Select | |||||
| fullWidth | fullWidth | ||||
| size="small" | size="small" | ||||
| value={outputData.defect3Uom} | value={outputData.defect3Uom} | ||||
| @@ -907,7 +941,17 @@ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionPro | |||||
| ...outputData, | ...outputData, | ||||
| defect3Uom: e.target.value | defect3Uom: e.target.value | ||||
| })} | })} | ||||
| /> | |||||
| displayEmpty | |||||
| > | |||||
| <MenuItem value=""> | |||||
| <em>{t("Select Unit")}</em> | |||||
| </MenuItem> | |||||
| {uomList.map((uom) => ( | |||||
| <MenuItem key={uom} value={uom}> | |||||
| {uom} | |||||
| </MenuItem> | |||||
| ))} | |||||
| </Select> | |||||
| </TableCell> | </TableCell> | ||||
| <TableCell> | <TableCell> | ||||
| <TextField | <TextField | ||||
| @@ -937,7 +981,7 @@ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionPro | |||||
| /> | /> | ||||
| </TableCell> | </TableCell> | ||||
| <TableCell> | <TableCell> | ||||
| <TextField | |||||
| <Select | |||||
| fullWidth | fullWidth | ||||
| size="small" | size="small" | ||||
| value={outputData.scrapUom} | value={outputData.scrapUom} | ||||
| @@ -945,7 +989,17 @@ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionPro | |||||
| ...outputData, | ...outputData, | ||||
| scrapUom: e.target.value | scrapUom: e.target.value | ||||
| })} | })} | ||||
| /> | |||||
| displayEmpty | |||||
| > | |||||
| <MenuItem value=""> | |||||
| <em>{t("Select Unit")}</em> | |||||
| </MenuItem> | |||||
| {uomList.map((uom) => ( | |||||
| <MenuItem key={uom} value={uom}> | |||||
| {uom} | |||||
| </MenuItem> | |||||
| ))} | |||||
| </Select> | |||||
| </TableCell> | </TableCell> | ||||
| </TableRow> | </TableRow> | ||||
| </TableBody> | </TableBody> | ||||
| @@ -207,7 +207,13 @@ | |||||
| "Job Order Match": "工單對料", | "Job Order Match": "工單對料", | ||||
| "All Pick Order Lots": "所有提料單批號", | "All Pick Order Lots": "所有提料單批號", | ||||
| "Row per page": "每頁行數", | "Row per page": "每頁行數", | ||||
| "Select Unit": "選擇單位", | |||||
| "No data available": "沒有資料", | "No data available": "沒有資料", | ||||
| "Bom Req. Qty": "需求數(BOM單位)", | |||||
| "Material Name": "材料清單", | |||||
| "Material Code": "材料清單", | |||||
| "Base UOM": "基本單位", | |||||
| "Stock UOM": "庫存單位", | |||||
| "jodetail": "工單細節", | "jodetail": "工單細節", | ||||
| "Sign out": "登出", | "Sign out": "登出", | ||||
| "By-product": "副產品", | "By-product": "副產品", | ||||
| @@ -25,6 +25,8 @@ | |||||
| "Overall Time Remaining": "總剩餘時間", | "Overall Time Remaining": "總剩餘時間", | ||||
| "User not found with staffNo:": "用戶不存在", | "User not found with staffNo:": "用戶不存在", | ||||
| "Time Remaining": "剩餘時間", | "Time Remaining": "剩餘時間", | ||||
| "Base UOM": "基本單位", | |||||
| "Stock UOM": "庫存單位", | |||||
| "Over Time": "超時", | "Over Time": "超時", | ||||
| "Staff No:": "員工編號:", | "Staff No:": "員工編號:", | ||||
| "Timer Paused": "計時器已暫停", | "Timer Paused": "計時器已暫停", | ||||
| @@ -107,6 +109,8 @@ | |||||
| "Lines with sufficient stock: ": "可提料項目數量: ", | "Lines with sufficient stock: ": "可提料項目數量: ", | ||||
| "Lines with insufficient stock: ": "未能提料項目數量: ", | "Lines with insufficient stock: ": "未能提料項目數量: ", | ||||
| "Item Name": "成品/半成品", | "Item Name": "成品/半成品", | ||||
| "Material Code": "材料編號", | |||||
| "Select Unit": "選擇單位", | |||||
| "Job Order Pickexcution": "工單提料", | "Job Order Pickexcution": "工單提料", | ||||
| "Pick Order Detail": "提料單細節", | "Pick Order Detail": "提料單細節", | ||||
| "Finished Job Order Record": "已完成工單記錄", | "Finished Job Order Record": "已完成工單記錄", | ||||