"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 { serverFetchJson } from "@/app/utils/fetchUtil"; import { QcItemResult } from "../settings/qcItem"; import { RecordsRes } from "../utils"; import { ConsoPickOrderResult, PickOrderLineWithSuggestedLot, PickOrderResult, PreReleasePickOrderSummary, StockOutLine, } from "."; import { PurchaseQcResult } from "../po/actions"; // import { BASE_API_URL } from "@/config/api"; export interface SavePickOrderLineRequest { itemId: number qty: number uomId: number } export interface SavePickOrderRequest { type: string targetDate: string pickOrderLine: SavePickOrderLineRequest[] } export interface PostPickOrderResponse { id: number | null; name: string; code: string; type?: string; message: string | null; errorPosition: string entity?: T | T[]; consoCode?: string; } export interface PostStockOutLiineResponse { id: number | null; name: string; code: string; type?: string; message: string | null; errorPosition: string | keyof T; entity: T | T[] | null; } export interface ReleasePickOrderInputs { consoCode: string; assignTo: number; } export interface CreateStockOutLine { consoCode: string; pickOrderLineId: number; inventoryLotLineId: number; qty: number; } export interface UpdateStockOutLine { id: number; // consoCode: String, itemId: number; qty: number; pickOrderLineId: number; inventoryLotLineId?: number; status: string; pickTime?: string; // pickerId: number? } export interface PickOrderQcInput { qty: number; status: string; qcResult: PurchaseQcResult[]; } export interface PickOrderApprovalInput { allowQty: number; rejectQty: number; status: string; } export interface GetPickOrderInfoResponse { consoCode: string | null; pickOrders: GetPickOrderInfo[]; items: CurrentInventoryItemInfo[]; } export interface GetPickOrderInfo { id: number; code: string; consoCode: string | null; // ✅ 添加 consoCode 属性 targetDate: string | number[]; // ✅ Support both formats type: string; status: string; assignTo: number; groupName: string; // ✅ Add this field pickOrderLines: GetPickOrderLineInfo[]; } export interface GetPickOrderLineInfo { id: number; itemId: number; itemCode: string; itemName: string; availableQty: number| null; requiredQty: number; uomCode: string; uomDesc: string; suggestedList: any[]; pickedQty: number; } export interface CurrentInventoryItemInfo { id: number; code: string; name: string; uomDesc: string; availableQty: number; requiredQty: number; } export interface SavePickOrderGroupRequest { groupIds?: number[]; names?: string[]; targetDate?: string; pickOrderId?: number | null; } export interface PickOrderGroupInfo { id: number; name: string; targetDate: string | null; pickOrderId: number | null; } export interface AssignPickOrderInputs { pickOrderIds: number[]; assignTo: number; } export interface LotDetailWithStockOutLine { lotId: number; lotNo: string; expiryDate: string; location: string; stockUnit: string; availableQty: number; requiredQty: number; actualPickQty: number; suggestedPickLotId: number; lotStatus: string; lotAvailability: string; stockOutLineId?: number; stockOutLineStatus?: string; stockOutLineQty?: number; } export interface PickAnotherLotFormData { pickOrderLineId: number; lotId: number; qty: number; type: string; handlerId?: number; category?: string; releasedBy?: number; recordDate?: string; } export const recordFailLot = async (data: PickAnotherLotFormData) => { const result = await serverFetchJson( `${BASE_API_URL}/suggestedPickLot/recordFailLot`, { method: "POST", body: JSON.stringify(data), headers: { "Content-Type": "application/json" }, }, ); revalidateTag("pickorder"); return result; }; export interface PickExecutionIssueData { pickOrderId: number; pickOrderCode: string; pickOrderCreateDate: string; pickExecutionDate: string; pickOrderLineId: number; itemId: number; itemCode: string; itemDescription: string; lotId: number; lotNo: string; storeLocation: string; requiredQty: number; actualPickQty: number; missQty: number; badItemQty: number; issueRemark: string; pickerName: string; handledBy?: number; } export interface AutoAssignReleaseResponse { id: number | null; name: string; code: string; type?: string; message: string | null; errorPosition: string; entity?: { consoCode?: string; pickOrderIds?: number[]; hasActiveOrders: boolean; }; } export interface PickOrderCompletionResponse { id: number | null; name: string; code: string; type?: string; message: string | null; errorPosition: string; entity?: { hasCompletedOrders: boolean; completedOrders: Array<{ pickOrderId: number; pickOrderCode: string; consoCode: string; isCompleted: boolean; stockOutStatus: string; totalLines: number; unfinishedLines: number; }>; allOrders: Array<{ pickOrderId: number; pickOrderCode: string; consoCode: string; isCompleted: boolean; stockOutStatus: string; totalLines: number; unfinishedLines: number; }>; }; } export interface UpdateSuggestedLotLineIdRequest { newLotLineId: number; } export const updateSuggestedLotLineId = async (suggestedPickLotId: number, newLotLineId: number) => { const response = await serverFetchJson>( `${BASE_API_URL}/suggestedPickLot/update-suggested-lot/${suggestedPickLotId}`, { method: "POST", body: JSON.stringify({ newLotLineId }), headers: { "Content-Type": "application/json" }, }, ); revalidateTag("pickorder"); return response; }; export const autoAssignAndReleasePickOrder = async (userId: number): Promise => { const response = await serverFetchJson( `${BASE_API_URL}/pickOrder/auto-assign-release/${userId}`, { method: "POST", headers: { "Content-Type": "application/json" }, }, ); revalidateTag("pickorder"); return response; }; export const checkPickOrderCompletion = async (userId: number): Promise => { const response = await serverFetchJson( `${BASE_API_URL}/pickOrder/check-pick-completion/${userId}`, { method: "GET", headers: { "Content-Type": "application/json" }, }, ); return response; }; export const recordPickExecutionIssue = async (data: PickExecutionIssueData) => { const result = await serverFetchJson( `${BASE_API_URL}/pickExecution/recordIssue`, { method: "POST", body: JSON.stringify(data), headers: { "Content-Type": "application/json" }, }, ); revalidateTag("pickorder"); return result; }; export const resuggestPickOrder = async (pickOrderId: number) => { console.log("Resuggesting pick order:", pickOrderId); const result = await serverFetchJson( `${BASE_API_URL}/suggestedPickLot/resuggest/${pickOrderId}`, { method: "POST", headers: { "Content-Type": "application/json" }, }, ); revalidateTag("pickorder"); return result; }; export const updateStockOutLineStatus = async (data: { id: number; status: string; qty?: number; remarks?: string; }) => { console.log("Updating stock out line status:", data); const result = await serverFetchJson>( `${BASE_API_URL}/stockOutLine/updateStatus`, { method: "POST", body: JSON.stringify(data), headers: { "Content-Type": "application/json" }, }, ); revalidateTag("pickorder"); return result; }; // Missing function 1: newassignPickOrder export const newassignPickOrder = async (data: AssignPickOrderInputs) => { const response = await serverFetchJson( `${BASE_API_URL}/pickOrder/assign`, { method: "POST", body: JSON.stringify(data), headers: { "Content-Type": "application/json" }, }, ); revalidateTag("pickorder"); return response; }; // Missing function 2: releaseAssignedPickOrders export const releaseAssignedPickOrders = async (data: AssignPickOrderInputs) => { const response = await serverFetchJson( `${BASE_API_URL}/pickOrder/release-assigned`, { method: "POST", body: JSON.stringify(data), headers: { "Content-Type": "application/json" }, }, ); revalidateTag("pickorder"); return response; }; // Get latest group name and create it automatically export const getLatestGroupNameAndCreate = async () => { return serverFetchJson( `${BASE_API_URL}/pickOrder/groups/latest`, { method: "GET", next: { tags: ["pickorder"] }, }, ); }; // Get all groups export const fetchAllGroups = cache(async () => { return serverFetchJson( `${BASE_API_URL}/pickOrder/groups/list`, { method: "GET", next: { tags: ["pickorder"] }, }, ); }); // Create or update groups (flexible - can handle both cases) export const createOrUpdateGroups = async (data: SavePickOrderGroupRequest) => { const response = await serverFetchJson( `${BASE_API_URL}/pickOrder/groups/create`, { method: "POST", body: JSON.stringify(data), headers: { "Content-Type": "application/json" }, }, ); revalidateTag("pickorder"); return response; }; // Get groups by pick order ID export const fetchGroupsByPickOrderId = cache(async (pickOrderId: number) => { return serverFetchJson( `${BASE_API_URL}/pickOrder/groups/${pickOrderId}`, { method: "GET", next: { tags: ["pickorder"] }, }, ); }); export const fetchPickOrderDetails = cache(async (ids: string) => { return serverFetchJson( `${BASE_API_URL}/pickOrder/detail/${ids}`, { method: "GET", next: { tags: ["pickorder"] }, }, ); }); export interface PickOrderLotDetailResponse { lotId: number; lotNo: string; expiryDate: string; location: string; stockUnit: string; inQty: number; availableQty: number; requiredQty: number; actualPickQty: number; suggestedPickLotId: number; lotStatus: string; lotAvailability: 'available' | 'insufficient_stock' | 'expired' | 'status_unavailable'|'rejected'; } interface ALLPickOrderLotDetailResponse { // Pick Order Information pickOrderId: number; pickOrderCode: string; pickOrderTargetDate: string; pickOrderType: string; pickOrderStatus: string; pickOrderAssignTo: number; groupName: string; // Pick Order Line Information pickOrderLineId: number; pickOrderLineRequiredQty: number; pickOrderLineStatus: string; // Item Information itemId: number; itemCode: string; itemName: string; uomCode: string; uomDesc: string; // Lot Information lotId: number; lotNo: string; expiryDate: string; location: string; outQty: number; holdQty: number; stockUnit: string; availableQty: number; requiredQty: number; actualPickQty: number; totalPickedByAllPickOrders: number; suggestedPickLotId: number; lotStatus: string; stockOutLineId?: number; stockOutLineStatus?: string; stockOutLineQty?: number; lotAvailability: 'available' | 'insufficient_stock' | 'expired' | 'status_unavailable'|'rejected'; processingStatus: string; } interface SuggestionWithStatus { suggestionId: number; suggestionQty: number; suggestionCreated: string; lotLineId: number; lotNo: string; expiryDate: string; location: string; stockOutLineId?: number; stockOutLineStatus?: string; stockOutLineQty?: number; suggestionStatus: 'active' | 'completed' | 'rejected' | 'in_progress' | 'unknown'; } export interface CheckCompleteResponse { id: number | null; name: string; code: string; type?: string; message: string | null; errorPosition: string; } export const checkAndCompletePickOrderByConsoCode = async (consoCode: string): Promise => { const response = await serverFetchJson( `${BASE_API_URL}/pickOrder/check-complete/${consoCode}`, { method: "POST", headers: { "Content-Type": "application/json", }, }, ); revalidateTag("pickorder"); return response; }; export const fetchPickOrderDetailsOptimized = cache(async (userId?: number) => { const url = userId ? `${BASE_API_URL}/pickOrder/detail-optimized?userId=${userId}` : `${BASE_API_URL}/pickOrder/detail-optimized`; return serverFetchJson( url, { method: "GET", next: { tags: ["pickorder"] }, }, ); }); const fetchSuggestionsWithStatus = async (pickOrderLineId: number) => { try { const response = await fetch(`/api/suggestedPickLot/suggestions-with-status/${pickOrderLineId}`); const suggestions: SuggestionWithStatus[] = await response.json(); return suggestions; } catch (error) { console.error('Error fetching suggestions with status:', error); return []; } }; export const fetchALLPickOrderLineLotDetails = cache(async (userId: number): Promise => { try { console.log("🔍 Fetching all pick order line lot details for userId:", userId); // ✅ 使用 serverFetchJson 而不是直接的 fetch const data = await serverFetchJson( `${BASE_API_URL}/pickOrder/all-lots-with-details/${userId}`, { method: 'GET', next: { tags: ["pickorder"] }, } ); console.log("✅ API Response:", data); return data; } catch (error) { console.error("❌ Error fetching all pick order line lot details:", error); throw error; } }); export const fetchAllPickOrderDetails = cache(async (userId?: number) => { if (!userId) { return { consoCode: null, pickOrders: [], items: [] }; } // ✅ Use the correct endpoint with userId in the path const url = `${BASE_API_URL}/pickOrder/detail-optimized/${userId}`; return serverFetchJson( url, { method: "GET", next: { tags: ["pickorder"] }, }, ); }); export const fetchPickOrderLineLotDetails = cache(async (pickOrderLineId: number) => { return serverFetchJson( `${BASE_API_URL}/pickOrder/lot-details/${pickOrderLineId}`, { method: "GET", next: { tags: ["pickorder"] }, }, ); }); export const createPickOrder = async (data: SavePickOrderRequest) => { console.log(data); const po = await serverFetchJson( `${BASE_API_URL}/pickOrder/create`, { method: "POST", body: JSON.stringify(data), headers: { "Content-Type": "application/json" }, }, ); revalidateTag("pickorder"); return po; } export const assignPickOrder = async (ids: number[]) => { const pickOrder = await serverFetchJson( `${BASE_API_URL}/pickOrder/conso`, { method: "POST", body: JSON.stringify({ ids: ids }), headers: { "Content-Type": "application/json" }, }, ); // revalidateTag("po"); return pickOrder; }; export const consolidatePickOrder = async (ids: number[]) => { const pickOrder = await serverFetchJson( `${BASE_API_URL}/pickOrder/conso`, { method: "POST", body: JSON.stringify({ ids: ids }), headers: { "Content-Type": "application/json" }, }, ); return pickOrder; }; export const consolidatePickOrder_revert = async (ids: number[]) => { const pickOrder = await serverFetchJson( `${BASE_API_URL}/pickOrder/deconso`, { method: "POST", body: JSON.stringify({ ids: ids }), headers: { "Content-Type": "application/json" }, }, ); // revalidateTag("po"); return pickOrder; }; export const fetchPickOrderClient = cache( async (queryParams?: Record) => { if (queryParams) { const queryString = new URLSearchParams(queryParams).toString(); return serverFetchJson>( `${BASE_API_URL}/pickOrder/getRecordByPage?${queryString}`, { method: "GET", next: { tags: ["pickorder"] }, }, ); } else { return serverFetchJson>( `${BASE_API_URL}/pickOrder/getRecordByPage`, { method: "GET", next: { tags: ["pickorder"] }, }, ); } }, ); export const fetchPickOrderWithStockClient = cache( async (queryParams?: Record) => { if (queryParams) { const queryString = new URLSearchParams(queryParams).toString(); return serverFetchJson>( `${BASE_API_URL}/pickOrder/getRecordByPageWithStock?${queryString}`, { method: "GET", next: { tags: ["pickorder"] }, }, ); } else { return serverFetchJson>( `${BASE_API_URL}/pickOrder/getRecordByPageWithStock`, { method: "GET", next: { tags: ["pickorder"] }, }, ); } }, ); export const fetchConsoPickOrderClient = cache( async (queryParams?: Record) => { if (queryParams) { const queryString = new URLSearchParams(queryParams).toString(); return serverFetchJson>( `${BASE_API_URL}/pickOrder/getRecordByPage-conso?${queryString}`, { method: "GET", next: { tags: ["pickorder"] }, }, ); } else { return serverFetchJson>( `${BASE_API_URL}/pickOrder/getRecordByPage-conso`, { method: "GET", next: { tags: ["pickorder"] }, }, ); } }, ); export const fetchPickOrderLineClient = cache( async (queryParams?: Record) => { if (queryParams) { const queryString = new URLSearchParams(queryParams).toString(); return serverFetchJson>( `${BASE_API_URL}/pickOrder/get-pickorder-line-byPage?${queryString}`, { method: "GET", next: { tags: ["pickorder"] }, }, ); } else { return serverFetchJson>( `${BASE_API_URL}/pickOrder/get-pickorder-line-byPage`, { method: "GET", next: { tags: ["pickorder"] }, }, ); } }, ); export const fetchStockOutLineClient = cache( async (pickOrderLineId: number) => { return serverFetchJson( `${BASE_API_URL}/stockOutLine/getByPickOrderLineId/${pickOrderLineId}`, { method: "GET", next: { tags: ["pickorder"] }, }, ); }, ); export const fetchConsoDetail = cache(async (consoCode: string) => { return serverFetchJson( `${BASE_API_URL}/pickOrder/pre-release-info/${consoCode}`, { method: "GET", next: { tags: ["pickorder"] }, }, ); }); export const releasePickOrder = async (data: ReleasePickOrderInputs) => { console.log(data); console.log(JSON.stringify(data)); const po = await serverFetchJson<{ consoCode: string }>( `${BASE_API_URL}/pickOrder/releaseConso`, { method: "POST", body: JSON.stringify(data), headers: { "Content-Type": "application/json" }, }, ); revalidateTag("pickorder"); return po; }; export const createStockOutLine = async (data: CreateStockOutLine) => { console.log("triggering"); const po = await serverFetchJson>( `${BASE_API_URL}/stockOutLine/create`, { method: "POST", body: JSON.stringify(data), headers: { "Content-Type": "application/json" }, }, ); revalidateTag("pickorder"); return po; }; export const updateStockOutLine = async (data: UpdateStockOutLine) => { console.log(data); const po = await serverFetchJson>( `${BASE_API_URL}/stockOutLine/update`, { method: "POST", body: JSON.stringify(data), headers: { "Content-Type": "application/json" }, }, ); revalidateTag("pickorder"); return po; }; export const completeConsoPickOrder = async (consoCode: string) => { const po = await serverFetchJson>( `${BASE_API_URL}/pickOrder/consoPickOrder/complete/${consoCode}`, { method: "POST", headers: { "Content-Type": "application/json" }, }, ); revalidateTag("pickorder"); return po; }; export const fetchConsoStatus = cache(async (consoCode: string) => { return serverFetchJson<{ status: string }>( `${BASE_API_URL}/stockOut/get-status/${consoCode}`, { method: "GET", next: { tags: ["pickorder"] }, }, ); });