"use server"; import { BASE_API_URL } from "@/config/api"; // import { ServerFetchError, serverFetchJson, serverFetchWithNoContent } from "@/app/utils/fetchUtil"; import { revalidateTag } from "next/cache"; import { cache } from "react"; import { serverFetch, serverFetchJson, serverFetchWithNoContent } from "@/app/utils/fetchUtil"; import { QcItemResult } from "../settings/qcItem"; import { RecordsRes } from "../utils"; import { DoResult } from "."; import { GridRowId, GridRowSelectionModel } from "@mui/x-data-grid"; import { GET } from "../auth/[...nextauth]/route"; import { stringify } from "querystring"; import { convertObjToURLSearchParams } from "@/app/utils/commonUtil"; export interface CreateConsoDoInput { ids: GridRowSelectionModel; } export interface DoDetail { id: number; code: string; supplierCode: string; shopCode: string; shopName: string; currencyCode: string; orderDate: string; estimatedArrivalDate: string; completeDate: string; status: string; deliveryOrderLines: DoDetailLine[]; } export interface DoDetailLine { id: number; itemNo: string; qty: number; price: number; status: string; itemName?: string; uomCode?: string; uom?: string; shortUom?: string; } export interface DoSearchAll { id: number; code: string; status: string; estimatedArrivalDate: number[]; orderDate: number[]; supplierName: string; shopName: string; shopAddress?: string; } export interface DoSearchLiteResponse { records: DoSearchAll[]; total: number; } export interface ReleaseDoRequest { id: number; } export interface ReleaseDoResponse { id: number; entity: { status: string } } export interface AssignByStoreRequest { storeId: string; // "2/F" or "4/F" assignTo: number; } export interface AssignByStoreResponse { id: number; code: string; name: string; type: string; message: string; errorPosition: string; entity: any; } export interface PrintDeliveryNoteRequest{ doPickOrderId: number; printerId: number; printQty: number; numOfCarton: number; isDraft: boolean; } export interface PrintDeliveryNoteResponse{ success: boolean; message?: string } export interface PrintDNLabelsRequest{ doPickOrderId: number; printerId: number; printQty: number; numOfCarton: number; } export interface PrintDNLabelsRespone{ success: boolean; message?: string } export interface BatchReleaseRequest { ids: number[]; } export interface BatchReleaseResponse { success: boolean; message?: string } export interface getTicketReleaseTable { id: number; storeId: string | null; ticketNo: string | null; pickOrderId: number | null; doOrderId: number | null; pickOrderCode: string | null; deliveryOrderCode: string | null; loadingSequence: number | null; ticketStatus: string | null; truckId: number | null; truckDepartureTime: string | null; shopId: number | null; handledBy: number | null; ticketReleaseTime: string | null; ticketCompleteDateTime: string | null; truckLanceCode: string | null; shopCode: string | null; shopName: string | null; requiredDeliveryDate: string | null; handlerName: string | null; numberOfFGItems: number; /** 進行中 do_pick_order 為 true,才可呼叫 force-complete / revert-assignment(id 為 do_pick_order 主鍵) */ isActiveDoPickOrder?: boolean; } export interface TruckScheduleDashboardItem { storeId: string | null; truckId: number | null; truckLanceCode: string | null; truckDepartureTime: string | number[] | null; numberOfShopsToServe: number; numberOfPickTickets: number; totalItemsToPick: number; numberOfTicketsReleased: number; firstTicketStartTime: string | number[] | null; numberOfTicketsCompleted: number; lastTicketEndTime: string | number[] | null; pickTimeTakenMinutes: number | null; } export interface SearchDeliveryOrderInfoRequest { code: string; shopName: string; status: string; orderStartDate: string; orderEndDate: string; estArrStartDate: string; estArrEndDate: string; pageSize: number; pageNum: number; } export interface SearchDeliveryOrderInfoResponse { records: DeliveryOrderInfo[]; total: number; } export interface DeliveryOrderInfo { id: number; code: string; shopName: string; supplierName: string; // 改为必需字段 status: string; orderDate: string; estimatedArrivalDate: string; deliveryOrderLines: DoDetailLine[]; } export const fetchDoRecordByPage = cache(async (data?: SearchDeliveryOrderInfoRequest) => { const queryStr = convertObjToURLSearchParams(data) console.log("queryStr", queryStr) const response = serverFetchJson( `${BASE_API_URL}/jo/getRecordByPage?${queryStr}`, { method: "GET", headers: { "Content-Type": "application/json" }, next: { tags: ["jos"] } } ) return response }) export const fetchTicketReleaseTable = cache(async (startDate: string, endDate: string)=> { return await serverFetchJson( `${BASE_API_URL}/doPickOrder/ticket-release-table/${startDate}&${endDate}`, { method: "GET", } ); }); export const fetchTruckScheduleDashboard = cache(async (date?: string) => { const url = date ? `${BASE_API_URL}/doPickOrder/truck-schedule-dashboard?date=${date}` : `${BASE_API_URL}/doPickOrder/truck-schedule-dashboard`; return await serverFetchJson( url, { method: "GET", } ); }); export const startBatchReleaseAsyncSingle = cache(async (data: { doId: number; userId: number }) => { const { doId, userId } = data; return await serverFetchJson<{ id: number|null; code: string; entity?: any }>( `${BASE_API_URL}/doPickOrder/batch-release/async-single?userId=${userId}`, { method: "POST", body: JSON.stringify(doId), headers: { "Content-Type": "application/json" }, } ); }); export const startBatchReleaseAsync = cache(async (data: { ids: number[]; userId: number }) => { const { ids, userId } = data; return await serverFetchJson<{ id: number|null; code: string; entity?: any }>( `${BASE_API_URL}/doPickOrder/batch-release/async?userId=${userId}`, { method: "POST", body: JSON.stringify(ids), headers: { "Content-Type": "application/json" }, } ); }); export const getBatchReleaseProgress = cache(async (jobId: string) => { return await serverFetchJson<{ id: number|null; code: string; entity?: any }>( `${BASE_API_URL}/doPickOrder/batch-release/progress/${jobId}`, { method: "GET" } ); }); export const assignPickOrderByStore = cache(async (data: AssignByStoreRequest) => { return await serverFetchJson(`${BASE_API_URL}/doPickOrder/assign-by-store`, { method: "POST", body: JSON.stringify(data), headers: { "Content-Type": "application/json" }, }) }) export const releaseAssignedPickOrderByStore = cache(async (data: AssignByStoreRequest) => { return await serverFetchJson(`${BASE_API_URL}/doPickOrder/release-assigned-by-store`, { method: "POST", body: JSON.stringify(data), headers: { "Content-Type": "application/json" }, }) }) export async function releaseDo(input: ReleaseDoRequest) { const response = await serverFetchJson(`${BASE_API_URL}/do/release`, { method: 'POST', body: JSON.stringify(input), headers: { 'Content-Type': 'application/json', }, }); revalidateTag('do'); return response; } export const preloadDo = () => { fetchDoList(); }; export const fetchDoList = cache(async () => { return serverFetchJson(`${BASE_API_URL}/do/list`, { next: { tags: ["doList"] }, }); }); export const fetchDoDetail = cache(async (id: number) => { return serverFetchJson(`${BASE_API_URL}/do/detail/${id}`, { method: "GET", headers: { "Content-Type": "application/json" }, next: { tags: ["doDetail"] } }); }); export async function fetchDoSearch( code: string, shopName: string, status: string, orderStartDate: string, orderEndDate: string, estArrStartDate: string, estArrEndDate: string, pageNum?: number, pageSize?: number, truckLanceCode?: string ): Promise { // 构建请求体 const requestBody: any = { code: code || null, shopName: shopName || null, status: status || null, estimatedArrivalDate: estArrStartDate || null, // 使用单个日期字段 truckLanceCode: truckLanceCode || null, pageNum: pageNum || 1, pageSize: pageSize || 10, }; // 如果日期不为空,转换为 LocalDateTime 格式 if (estArrStartDate) { requestBody.estimatedArrivalDate = estArrStartDate; // 格式: "2026-01-19T00:00:00" } else { requestBody.estimatedArrivalDate = null; } const url = `${BASE_API_URL}/do/search-do-lite`; const data = await serverFetchJson(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(requestBody), }); return data; } export async function fetchDoSearchList( code: string, shopName: string, status: string, orderStartDate: string, orderEndDate: string, etaFrom: string, etaTo: string, page = 0, size = 500 ): Promise { const params = new URLSearchParams(); if (code) params.append("code", code); if (shopName) params.append("shopName", shopName); if (status) params.append("status", status); if (orderStartDate) params.append("orderFrom", orderStartDate); if (orderEndDate) params.append("orderTo", orderEndDate); if (etaFrom) params.append("etaFrom", etaFrom); if (etaTo) params.append("etaTo", etaTo); params.append("page", String(page)); params.append("size", String(size)); const res = await fetch(`/api/delivery-order/search-do-list?${params.toString()}`); const pageData = await res.json(); // Spring Page 结构 return pageData.content; // 前端继续沿用你原来的 client-side 分页逻辑 } export async function printDN(request: PrintDeliveryNoteRequest){ const params = new URLSearchParams(); params.append('doPickOrderId', request.doPickOrderId.toString()); params.append('printerId', request.printerId.toString()); if (request.printQty !== null && request.printQty !== undefined) { params.append('printQty', request.printQty.toString()); } params.append('numOfCarton', request.numOfCarton.toString()); params.append('isDraft', request.isDraft.toString()); try { const response = await serverFetch(`${BASE_API_URL}/do/print-DN?${params.toString()}`, { method: "GET", }); if (response.ok) { return { success: true, message: "Print job sent successfully (DN)" } as PrintDeliveryNoteResponse; } const errorText = await response.text(); console.error("Print DN error:", errorText); return { success: false, message: "No data found for this pick order." } as PrintDeliveryNoteResponse; } catch (error) { console.error("Error in printDN:", error); return { success: false, message: "No data found for this pick order." } as PrintDeliveryNoteResponse; } } export async function printDNLabels(request: PrintDNLabelsRequest){ const params = new URLSearchParams(); params.append('doPickOrderId', request.doPickOrderId.toString()); params.append('printerId', request.printerId.toString()); if (request.printQty !== null && request.printQty !== undefined) { params.append('printQty', request.printQty.toString()); } params.append('numOfCarton', request.numOfCarton.toString()); const response = await serverFetchWithNoContent(`${BASE_API_URL}/do/print-DNLabels?${params.toString()}`,{ method: "GET" }); return { success: true, message: "Print job sent successfully (labels)"} as PrintDeliveryNoteResponse } export interface Check4FTruckBatchResponse { hasProblem: boolean; problems: ProblemDoDto[]; } export interface ProblemDoDto { deliveryOrderId: number; deliveryOrderCode: string; targetDate: string; availableTrucks: TruckInfoDto[]; } export interface TruckInfoDto { id: number; truckLanceCode: string; departureTime: string; storeId: string; shopCode: string; shopName: string; } export const check4FTrucksBatch = cache(async (doIds: number[]) => { return await serverFetchJson(`${BASE_API_URL}/do/check-4f-trucks-batch`, { method: "POST", body: JSON.stringify(doIds), headers: { "Content-Type": "application/json" }, }); }); export async function fetchAllDoSearch( code: string, shopName: string, status: string, estArrStartDate: string, truckLanceCode?: string // 添加这个参数 ): Promise { // 使用一个很大的 pageSize 来获取所有匹配的记录 const requestBody: any = { code: code || null, shopName: shopName || null, status: status || null, estimatedArrivalDate: estArrStartDate || null, truckLanceCode: truckLanceCode || null, // 添加这个字段 pageNum: 1, pageSize: 10000, // 使用一个很大的值来获取所有记录 }; if (estArrStartDate) { requestBody.estimatedArrivalDate = estArrStartDate; } else { requestBody.estimatedArrivalDate = null; } const url = `${BASE_API_URL}/do/search-do-lite`; const data = await serverFetchJson(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(requestBody), }); return data.records; }