| @@ -1211,7 +1211,7 @@ export const fetchMaterialPickStatus = cache(async (): Promise<MaterialPickStatu | |||||
| }) | }) | ||||
| export interface ProcessStatusInfo { | export interface ProcessStatusInfo { | ||||
| processName?: string | null; | processName?: string | null; | ||||
| equipmentDescription?: string | null; | |||||
| equipmentName?: string | null; | |||||
| equipmentDetailName?: string | null; | equipmentDetailName?: string | null; | ||||
| startTime?: string | null; | startTime?: string | null; | ||||
| endTime?: string | null; | endTime?: string | null; | ||||
| @@ -33,7 +33,7 @@ export interface PoResult { | |||||
| status: string; | status: string; | ||||
| pol?: PurchaseOrderLine[]; | pol?: PurchaseOrderLine[]; | ||||
| } | } | ||||
| export type { StockInLine } from "../stockIn"; | |||||
| export interface PurchaseOrderLine { | export interface PurchaseOrderLine { | ||||
| id: number; | id: number; | ||||
| purchaseOrderId: number; | purchaseOrderId: number; | ||||
| @@ -127,6 +127,7 @@ export interface StockInLine { | |||||
| joCode?: string; | joCode?: string; | ||||
| warehouseCode?: string; | warehouseCode?: string; | ||||
| defaultWarehouseId: number; // id for now | defaultWarehouseId: number; // id for now | ||||
| locationCode?: string; | |||||
| dnNo?: string; | dnNo?: string; | ||||
| dnDate?: number[]; | dnDate?: number[]; | ||||
| stockQty?: number; | stockQty?: number; | ||||
| @@ -285,7 +285,7 @@ const JobProcessStatus: React.FC = () => { | |||||
| } | } | ||||
| const label = [ | const label = [ | ||||
| process.processName, | process.processName, | ||||
| process.equipmentDescription, | |||||
| process.equipmentName, | |||||
| process.equipmentDetailName ? `-${process.equipmentDetailName}` : "", | process.equipmentDetailName ? `-${process.equipmentDetailName}` : "", | ||||
| ].filter(Boolean).join(" "); | ].filter(Boolean).join(" "); | ||||
| // 如果工序是必需的,显示三行(Start、Finish、Wait Time) | // 如果工序是必需的,显示三行(Start、Finish、Wait Time) | ||||
| @@ -20,6 +20,8 @@ import { useSession } from "next-auth/react"; | |||||
| import { SessionWithTokens } from "@/config/authConfig"; | import { SessionWithTokens } from "@/config/authConfig"; | ||||
| import dayjs from "dayjs"; | import dayjs from "dayjs"; | ||||
| import { OUTPUT_DATE_FORMAT } from "@/app/utils/formatUtil"; | import { OUTPUT_DATE_FORMAT } from "@/app/utils/formatUtil"; | ||||
| import { | import { | ||||
| fetchAllJoborderProductProcessInfo, | fetchAllJoborderProductProcessInfo, | ||||
| AllJoborderProductProcessInfoResponse, | AllJoborderProductProcessInfoResponse, | ||||
| @@ -50,6 +52,7 @@ const ProductProcessList: React.FC<ProductProcessListProps> = ({ onSelectProcess | |||||
| 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 currentUserId = session?.id ? parseInt(session.id) : undefined; | ||||
| const [suggestedLocationCode, setSuggestedLocationCode] = useState<string | null>(null); | const [suggestedLocationCode, setSuggestedLocationCode] = useState<string | null>(null); | ||||
| const handleAssignPickOrder = useCallback(async (pickOrderId: number, jobOrderId?: number, productProcessId?: number) => { | const handleAssignPickOrder = useCallback(async (pickOrderId: number, jobOrderId?: number, productProcessId?: number) => { | ||||
| if (!currentUserId) { | if (!currentUserId) { | ||||
| @@ -87,17 +90,17 @@ const ProductProcessList: React.FC<ProductProcessListProps> = ({ onSelectProcess | |||||
| alert(t(`Unknown error: ${error?.message || "Unknown error"}。Please try again later.`)); | alert(t(`Unknown error: ${error?.message || "Unknown error"}。Please try again later.`)); | ||||
| } | } | ||||
| }, [currentUserId, t, onSelectMatchingStock]); | }, [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")); | ||||
| return; | return; | ||||
| } | } | ||||
| setModalInfo({ | setModalInfo({ | ||||
| id: process.stockInLineId, | id: process.stockInLineId, | ||||
| //itemId: process.itemId, // 如果 process 中有 itemId,添加这一行 | |||||
| //expiryDate: dayjs().add(1, "month").format(OUTPUT_DATE_FORMAT), | //expiryDate: dayjs().add(1, "month").format(OUTPUT_DATE_FORMAT), | ||||
| // 视需要补 itemId、jobOrderId 等 | |||||
| }); | }); | ||||
| setOpenModal(true); | setOpenModal(true); | ||||
| }, [t]); | }, [t]); | ||||
| @@ -316,13 +319,14 @@ const ProductProcessList: React.FC<ProductProcessListProps> = ({ onSelectProcess | |||||
| })} | })} | ||||
| </Grid> | </Grid> | ||||
| <QcStockInModal | <QcStockInModal | ||||
| session={sessionToken} | |||||
| open={openModal} | |||||
| onClose={closeNewModal} | |||||
| inputDetail={modalInfo} | |||||
| printerCombo={printerCombo} | |||||
| printSource="productionProcess" | |||||
| /> | |||||
| session={sessionToken} | |||||
| open={openModal} | |||||
| onClose={closeNewModal} | |||||
| inputDetail={modalInfo} | |||||
| printerCombo={printerCombo} | |||||
| warehouse={[]} | |||||
| printSource="productionProcess" | |||||
| /> | |||||
| {processes.length > 0 && ( | {processes.length > 0 && ( | ||||
| <TablePagination | <TablePagination | ||||
| component="div" | component="div" | ||||
| @@ -174,42 +174,31 @@ const PutAwayModal: React.FC<Props> = ({ open, onClose, warehouse, stockInLineId | |||||
| // 根据 item 的 locationCode 设置默认 warehouseId | // 根据 item 的 locationCode 设置默认 warehouseId | ||||
| useEffect(() => { | useEffect(() => { | ||||
| const fetchItemAndSetDefaultWarehouse = async () => { | |||||
| if (itemDetail?.itemId && warehouse.length > 0 && firstWarehouseId === null) { | |||||
| // 只在第一次上架时设置默认值 | |||||
| try { | |||||
| const itemResult = await fetchItemForPutAway(itemDetail.itemId); | |||||
| const item = itemResult.item; | |||||
| // 获取 item 的 locationCode(处理大小写问题) | |||||
| const locationCode = item.LocationCode || item.locationCode; | |||||
| if (locationCode) { | |||||
| // 根据 locationCode 查找对应的 warehouse(通过 code 匹配) | |||||
| const matchedWarehouse = warehouse.find( | |||||
| (w) => w.code === locationCode || w.code?.toLowerCase() === locationCode?.toLowerCase() | |||||
| ); | |||||
| if (matchedWarehouse) { | |||||
| // 只设置用于显示的默认值,不通知父组件 | |||||
| setItemDefaultWarehouseId(matchedWarehouse.id); | |||||
| console.log("%c Set default warehouse from item locationCode (display only):", "color:green", { | |||||
| locationCode, | |||||
| warehouseId: matchedWarehouse.id, | |||||
| warehouseCode: matchedWarehouse.code | |||||
| }); | |||||
| } | |||||
| } | |||||
| } catch (error) { | |||||
| console.error("Error fetching item to get default warehouse:", error); | |||||
| // 直接使用 fetchStockInLineInfo 返回的 locationCode | |||||
| // 只在第一次上架时(firstWarehouseId === null)设置默认值 | |||||
| if (itemDetail?.locationCode && warehouse.length > 0 && firstWarehouseId === null) { | |||||
| const locationCode = itemDetail.locationCode; | |||||
| if (locationCode) { | |||||
| // 根据 locationCode 查找对应的 warehouse(通过 code 匹配) | |||||
| const matchedWarehouse = warehouse.find( | |||||
| (w) => w.code === locationCode || w.code?.toLowerCase() === locationCode?.toLowerCase() | |||||
| ); | |||||
| if (matchedWarehouse) { | |||||
| // 只设置用于显示的默认值,不通知父组件 | |||||
| setItemDefaultWarehouseId(matchedWarehouse.id); | |||||
| console.log("%c Set default warehouse from item locationCode (from API, display only):", "color:green", { | |||||
| locationCode, | |||||
| warehouseId: matchedWarehouse.id, | |||||
| warehouseCode: matchedWarehouse.code | |||||
| }); | |||||
| } else { | |||||
| console.log("%c No warehouse found for locationCode:", "color:yellow", locationCode); | |||||
| } | } | ||||
| } | } | ||||
| }; | |||||
| if (itemDetail && itemDetail.status === "received") { | |||||
| fetchItemAndSetDefaultWarehouse(); | |||||
| } | } | ||||
| }, [itemDetail, warehouse, firstWarehouseId]); | |||||
| }, [itemDetail?.locationCode, warehouse, firstWarehouseId]); | |||||
| useEffect(() => { | useEffect(() => { | ||||
| // 只使用实际扫描的 warehouseId,不使用默认值进行验证 | // 只使用实际扫描的 warehouseId,不使用默认值进行验证 | ||||
| @@ -119,12 +119,20 @@ const QcStockInModal: React.FC<Props> = ({ | |||||
| const res = await fetchStockInLineInfo(stockInLineId); | const res = await fetchStockInLineInfo(stockInLineId); | ||||
| if (res) { | if (res) { | ||||
| console.log("%c Fetched Stock In Line: ", "color:orange", res); | console.log("%c Fetched Stock In Line: ", "color:orange", res); | ||||
| console.log("%c [QC] itemId in response:", "color:yellow", res.itemId); | |||||
| console.log("%c [QC] locationCode in response:", "color:yellow", res.locationCode); | |||||
| // 如果 res 中没有 itemId,检查是否有其他方式获取 | |||||
| if (!res.itemId) { | |||||
| console.warn("%c [QC] Warning: itemId is missing in response!", "color:red"); | |||||
| } | |||||
| setStockInLineInfo({...inputDetail, ...res, expiryDate: res.expiryDate}); | setStockInLineInfo({...inputDetail, ...res, expiryDate: res.expiryDate}); | ||||
| // fetchQcResultData(stockInLineId); | |||||
| } else throw("Result is undefined"); | } else throw("Result is undefined"); | ||||
| } catch (e) { | } catch (e) { | ||||
| console.log("%c Error when fetching Stock In Line: ", "color:red", e); | console.log("%c Error when fetching Stock In Line: ", "color:red", e); | ||||
| console.log("%c Error details: ", "color:red", { | |||||
| message: e instanceof Error ? e.message : String(e), | |||||
| stack: e instanceof Error ? e.stack : undefined | |||||
| }); | |||||
| alert("Something went wrong, please retry"); | alert("Something went wrong, please retry"); | ||||
| closeHandler({}, "backdropClick"); | closeHandler({}, "backdropClick"); | ||||
| } | } | ||||
| @@ -144,16 +152,24 @@ const QcStockInModal: React.FC<Props> = ({ | |||||
| } | } | ||||
| }, [open]); | }, [open]); | ||||
| useEffect(() => { | useEffect(() => { | ||||
| // 如果后端已经在 StockInLine 中返回了 locationCode,直接使用 | |||||
| if (stockInLineInfo?.locationCode) { | |||||
| setItemLocationCode(stockInLineInfo.locationCode); | |||||
| console.log("%c [QC] item LocationCode from API:", "color:cyan", stockInLineInfo.locationCode); | |||||
| return; | |||||
| } | |||||
| // 如果没有 locationCode,尝试从 itemId 获取(向后兼容) | |||||
| const loadItemLocationCode = async () => { | const loadItemLocationCode = async () => { | ||||
| if (!stockInLineInfo?.itemId) return; | if (!stockInLineInfo?.itemId) return; | ||||
| try { | try { | ||||
| const itemResult = await fetchItemForPutAway(stockInLineInfo.itemId); | const itemResult = await fetchItemForPutAway(stockInLineInfo.itemId); | ||||
| const item = itemResult.item; | const item = itemResult.item; | ||||
| const locationCode = item.LocationCode || item.locationCode || null; | const locationCode = item.LocationCode || item.locationCode || null; | ||||
| setItemLocationCode(locationCode); | setItemLocationCode(locationCode); | ||||
| console.log("%c [QC] item LocationCode:", "color:cyan", locationCode); | |||||
| console.log("%c [QC] item LocationCode from fetchItemForPutAway:", "color:cyan", locationCode); | |||||
| } catch (error) { | } catch (error) { | ||||
| console.error("Error fetching item to get LocationCode in QC:", error); | console.error("Error fetching item to get LocationCode in QC:", error); | ||||
| setItemLocationCode(null); | setItemLocationCode(null); | ||||