| @@ -96,9 +96,32 @@ export interface AllPickedStockTakeListReponse { | |||||
| startTime: string | null; | startTime: string | null; | ||||
| endTime: string | null; | endTime: string | null; | ||||
| planStartDate: string | null; | planStartDate: string | null; | ||||
| stockTakeSectionDescription: string | null; | |||||
| reStockTakeTrueFalse: boolean; | reStockTakeTrueFalse: boolean; | ||||
| } | } | ||||
| export const getApproverInventoryLotDetailsAll = async ( | |||||
| stockTakeId?: number | null, | |||||
| pageNum: number = 0, | |||||
| pageSize: number = 100 | |||||
| ) => { | |||||
| const params = new URLSearchParams(); | |||||
| params.append("pageNum", String(pageNum)); | |||||
| params.append("pageSize", String(pageSize)); | |||||
| if (stockTakeId != null && stockTakeId > 0) { | |||||
| params.append("stockTakeId", String(stockTakeId)); | |||||
| } | |||||
| const url = `${BASE_API_URL}/stockTakeRecord/approverInventoryLotDetailsAll?${params.toString()}`; | |||||
| const response = await serverFetchJson<RecordsRes<InventoryLotDetailResponse>>( | |||||
| url, | |||||
| { | |||||
| method: "GET", | |||||
| }, | |||||
| ); | |||||
| return response; | |||||
| } | |||||
| export const importStockTake = async (data: FormData) => { | export const importStockTake = async (data: FormData) => { | ||||
| const importStockTake = await serverFetchJson<string>( | const importStockTake = await serverFetchJson<string>( | ||||
| `${BASE_API_URL}/stockTake/import`, | `${BASE_API_URL}/stockTake/import`, | ||||
| @@ -122,12 +145,20 @@ export const getStockTakeRecords = async () => { | |||||
| } | } | ||||
| export const getStockTakeRecordsPaged = async ( | export const getStockTakeRecordsPaged = async ( | ||||
| pageNum: number, | pageNum: number, | ||||
| pageSize: number | |||||
| pageSize: number, | |||||
| params?: { sectionDescription?: string; stockTakeSections?: string } | |||||
| ) => { | ) => { | ||||
| const url = `${BASE_API_URL}/stockTakeRecord/AllPickedStockOutRecordList?pageNum=${pageNum}&pageSize=${pageSize}`; | |||||
| const res = await serverFetchJson<RecordsRes<AllPickedStockTakeListReponse>>(url, { | |||||
| method: "GET", | |||||
| }); | |||||
| const searchParams = new URLSearchParams(); | |||||
| searchParams.set("pageNum", String(pageNum)); | |||||
| searchParams.set("pageSize", String(pageSize)); | |||||
| if (params?.sectionDescription && params.sectionDescription !== "All") { | |||||
| searchParams.set("sectionDescription", params.sectionDescription); | |||||
| } | |||||
| if (params?.stockTakeSections?.trim()) { | |||||
| searchParams.set("stockTakeSections", params.stockTakeSections.trim()); | |||||
| } | |||||
| const url = `${BASE_API_URL}/stockTakeRecord/AllPickedStockOutRecordList?${searchParams.toString()}`; | |||||
| const res = await serverFetchJson<RecordsRes<AllPickedStockTakeListReponse>>(url, { method: "GET" }); | |||||
| return res; | return res; | ||||
| }; | }; | ||||
| export const getApproverStockTakeRecords = async () => { | export const getApproverStockTakeRecords = async () => { | ||||
| @@ -228,6 +259,12 @@ export interface BatchSaveApproverStockTakeRecordResponse { | |||||
| errors: string[]; | errors: string[]; | ||||
| } | } | ||||
| export interface BatchSaveApproverStockTakeAllRequest { | |||||
| stockTakeId: number; | |||||
| approverId: number; | |||||
| variancePercentTolerance?: number | null; | |||||
| } | |||||
| export const saveApproverStockTakeRecord = async ( | export const saveApproverStockTakeRecord = async ( | ||||
| request: SaveApproverStockTakeRecordRequest, | request: SaveApproverStockTakeRecordRequest, | ||||
| @@ -272,6 +309,17 @@ export const batchSaveApproverStockTakeRecords = cache(async (data: BatchSaveApp | |||||
| } | } | ||||
| ) | ) | ||||
| export const batchSaveApproverStockTakeRecordsAll = cache(async (data: BatchSaveApproverStockTakeAllRequest) => { | |||||
| return serverFetchJson<BatchSaveApproverStockTakeRecordResponse>( | |||||
| `${BASE_API_URL}/stockTakeRecord/batchSaveApproverStockTakeRecordsAll`, | |||||
| { | |||||
| method: "POST", | |||||
| body: JSON.stringify(data), | |||||
| headers: { "Content-Type": "application/json" }, | |||||
| } | |||||
| ) | |||||
| }) | |||||
| export const updateStockTakeRecordStatusToNotMatch = async ( | export const updateStockTakeRecordStatusToNotMatch = async ( | ||||
| stockTakeRecordId: number | stockTakeRecordId: number | ||||
| ) => { | ) => { | ||||
| @@ -61,22 +61,27 @@ const sectionDescriptionOptions = Array.from( | |||||
| .filter((v): v is string => !!v) | .filter((v): v is string => !!v) | ||||
| ) | ) | ||||
| ); | ); | ||||
| /* | |||||
| // 按 description + section 双条件过滤 | // 按 description + section 双条件过滤 | ||||
| const filteredSessions = stockTakeSessions.filter((s) => { | const filteredSessions = stockTakeSessions.filter((s) => { | ||||
| const matchDesc = | const matchDesc = | ||||
| filterSectionDescription === "All" || | filterSectionDescription === "All" || | ||||
| s.stockTakeSectionDescription === filterSectionDescription; | s.stockTakeSectionDescription === filterSectionDescription; | ||||
| const sessionParts = (filterStockTakeSession ?? "") | |||||
| .split(",") | |||||
| .map((p) => p.trim().toLowerCase()) | |||||
| .filter(Boolean); | |||||
| const matchSession = | const matchSession = | ||||
| !filterStockTakeSession || | |||||
| (s.stockTakeSession ?? "") | |||||
| .toString() | |||||
| .toLowerCase() | |||||
| .includes(filterStockTakeSession.toLowerCase()); | |||||
| sessionParts.length === 0 || | |||||
| sessionParts.some((part) => | |||||
| (s.stockTakeSession ?? "").toString().toLowerCase().includes(part) | |||||
| ); | |||||
| return matchDesc && matchSession; | return matchDesc && matchSession; | ||||
| }); | }); | ||||
| */ | |||||
| // SearchBox 的条件配置 | // SearchBox 的条件配置 | ||||
| const criteria: Criterion<PickerSearchKey>[] = [ | const criteria: Criterion<PickerSearchKey>[] = [ | ||||
| @@ -88,7 +93,7 @@ const criteria: Criterion<PickerSearchKey>[] = [ | |||||
| }, | }, | ||||
| { | { | ||||
| type: "text", | type: "text", | ||||
| label: t("Stock Take Section"), | |||||
| label: t("Stock Take Section (can use , to search multiple sections)"), | |||||
| paramName: "stockTakeSession", | paramName: "stockTakeSession", | ||||
| placeholder: "", | placeholder: "", | ||||
| }, | }, | ||||
| @@ -97,17 +102,24 @@ const criteria: Criterion<PickerSearchKey>[] = [ | |||||
| const handleSearch = (inputs: Record<PickerSearchKey | `${PickerSearchKey}To`, string>) => { | const handleSearch = (inputs: Record<PickerSearchKey | `${PickerSearchKey}To`, string>) => { | ||||
| setFilterSectionDescription(inputs.sectionDescription || "All"); | setFilterSectionDescription(inputs.sectionDescription || "All"); | ||||
| setFilterStockTakeSession(inputs.stockTakeSession || ""); | setFilterStockTakeSession(inputs.stockTakeSession || ""); | ||||
| fetchStockTakeSessions(0, pageSize, { | |||||
| sectionDescription: inputs.sectionDescription || "All", | |||||
| stockTakeSections: inputs.stockTakeSession ?? "", | |||||
| }); | |||||
| }; | }; | ||||
| const handleResetSearch = () => { | const handleResetSearch = () => { | ||||
| setFilterSectionDescription("All"); | setFilterSectionDescription("All"); | ||||
| setFilterStockTakeSession(""); | setFilterStockTakeSession(""); | ||||
| fetchStockTakeSessions(0, pageSize, { | |||||
| sectionDescription: "All", | |||||
| stockTakeSections: "", | |||||
| }); | |||||
| }; | }; | ||||
| const fetchStockTakeSessions = useCallback( | const fetchStockTakeSessions = useCallback( | ||||
| async (pageNum: number, size: number) => { | |||||
| async (pageNum: number, size: number, filterOverrides?: { sectionDescription: string; stockTakeSections: string }) => { | |||||
| setLoading(true); | setLoading(true); | ||||
| try { | try { | ||||
| const res = await getStockTakeRecordsPaged(pageNum, size); | |||||
| const res = await getStockTakeRecordsPaged(pageNum, size, filterOverrides); | |||||
| setStockTakeSessions(Array.isArray(res.records) ? res.records : []); | setStockTakeSessions(Array.isArray(res.records) ? res.records : []); | ||||
| setTotal(res.total || 0); | setTotal(res.total || 0); | ||||
| setPage(pageNum); | setPage(pageNum); | ||||
| @@ -252,7 +264,7 @@ const handleResetSearch = () => { | |||||
| <Typography variant="body2" color="text.secondary"> | <Typography variant="body2" color="text.secondary"> | ||||
| {t("Total Sections")}: {stockTakeSessions.length} | |||||
| {t("Total Sections")}: {total} | |||||
| </Typography> | </Typography> | ||||
| <Typography variant="body2" color="text.secondary"> | <Typography variant="body2" color="text.secondary"> | ||||
| {t("Start Stock Take Date")}: {planStartDate || "-"} | {t("Start Stock Take Date")}: {planStartDate || "-"} | ||||
| @@ -269,7 +281,7 @@ const handleResetSearch = () => { | |||||
| </Box> | </Box> | ||||
| <Grid container spacing={2}> | <Grid container spacing={2}> | ||||
| {filteredSessions.map((session: AllPickedStockTakeListReponse) => { | |||||
| {stockTakeSessions.map((session: AllPickedStockTakeListReponse) => { | |||||
| const statusColor = getStatusColor(session.status || ""); | const statusColor = getStatusColor(session.status || ""); | ||||
| const lastStockTakeDate = session.lastStockTakeDate | const lastStockTakeDate = session.lastStockTakeDate | ||||
| ? dayjs(session.lastStockTakeDate).format(OUTPUT_DATE_FORMAT) | ? dayjs(session.lastStockTakeDate).format(OUTPUT_DATE_FORMAT) | ||||
| @@ -338,7 +350,7 @@ const handleResetSearch = () => { | |||||
| })} | })} | ||||
| </Grid> | </Grid> | ||||
| {stockTakeSessions.length > 0 && ( | |||||
| {total > 0 && ( | |||||
| <TablePagination | <TablePagination | ||||
| component="div" | component="div" | ||||
| count={total} | count={total} | ||||
| @@ -50,6 +50,7 @@ | |||||
| "Process Start Time": "工序開始時間", | "Process Start Time": "工序開始時間", | ||||
| "Stock Req. Qty": "需求數", | "Stock Req. Qty": "需求數", | ||||
| "Staff No Required": "員工編號必填", | "Staff No Required": "員工編號必填", | ||||
| "Stock Take Section (can use , to search multiple sections)": "盤點區域(可使用逗號搜索多個區域)", | |||||
| "User Not Found": "用戶不存在", | "User Not Found": "用戶不存在", | ||||
| "Time Remaining": "剩餘時間", | "Time Remaining": "剩餘時間", | ||||
| "Select Printer": "選擇打印機", | "Select Printer": "選擇打印機", | ||||
| @@ -8,6 +8,12 @@ | |||||
| "UoM": "單位", | "UoM": "單位", | ||||
| "mat": "物料", | "mat": "物料", | ||||
| "variance": "差異", | "variance": "差異", | ||||
| "Plan Start Date": "計劃開始日期", | |||||
| "Total Items": "總貨品數量", | |||||
| "Total Lots": "總批號數量", | |||||
| "Stock Take Round": "盤點輪次", | |||||
| "ApproverAll": "審核員", | |||||
| "Stock Take Section (can use , to search multiple sections)": "盤點區域(可使用逗號搜索多個區域)", | |||||
| "Variance %": "差異百分比", | "Variance %": "差異百分比", | ||||
| "fg": "成品", | "fg": "成品", | ||||
| "Back to List": "返回列表", | "Back to List": "返回列表", | ||||
| @@ -17,6 +23,7 @@ | |||||
| "available": "可用", | "available": "可用", | ||||
| "Issue Qty": "問題數量", | "Issue Qty": "問題數量", | ||||
| "tke": "盤點", | "tke": "盤點", | ||||
| "Total Stock Takes": "總盤點數量", | |||||
| "Submit completed: {{success}} success, {{errors}} errors": "提交完成:{{success}} 成功,{{errors}} 錯誤", | "Submit completed: {{success}} success, {{errors}} errors": "提交完成:{{success}} 成功,{{errors}} 錯誤", | ||||
| "Submit All Inputted": "提交所有輸入", | "Submit All Inputted": "提交所有輸入", | ||||
| "Submit Bad Item": "提交不良品", | "Submit Bad Item": "提交不良品", | ||||