|
- "use client";
-
- import {
- Autocomplete,
- Box,
- Button,
- CircularProgress,
- FormControl,
- Grid,
- Paper,
- Stack,
- Table,
- TableBody,
- TableCell,
- TableContainer,
- TableHead,
- TableRow,
- TextField,
- Typography,
- Checkbox,
- FormControlLabel,
- Select,
- MenuItem,
- InputLabel,
- TablePagination,
- } from "@mui/material";
- import { useCallback, useEffect, useMemo, useState } from "react";
- import { useTranslation } from "react-i18next";
- import {
- ByItemsSummary,
- ConsoPickOrderResult,
- PickOrderLine,
- PickOrderResult,
- } from "@/app/api/pickOrder";
- import { useRouter } from "next/navigation";
- import { GridInputRowSelectionModel } from "@mui/x-data-grid";
- import {
- fetchConsoDetail,
- fetchConsoPickOrderClient,
- releasePickOrder,
- ReleasePickOrderInputs,
- fetchPickOrderDetails,
- fetchAllPickOrderDetails,
- GetPickOrderInfoResponse,
- GetPickOrderLineInfo,
- createStockOutLine,
- updateStockOutLineStatus,
- resuggestPickOrder,
- } from "@/app/api/pickOrder/actions";
- import { EditNote } from "@mui/icons-material";
- import { fetchNameList, NameList } from "@/app/api/user/actions";
- import {
- FormProvider,
- SubmitErrorHandler,
- SubmitHandler,
- useForm,
- } from "react-hook-form";
- import { pickOrderStatusMap } from "@/app/utils/formatUtil";
- import { QcItemWithChecks } from "@/app/api/qc";
- import { fetchQcItemCheck, fetchPickOrderQcResult } from "@/app/api/qc/actions";
-
- import { PurchaseQcResult } from "@/app/api/po/actions";
- import PickQcStockInModalVer2 from "./PickQcStockInModalVer3";
- import { fetchPickOrderLineLotDetails, PickOrderLotDetailResponse } from "@/app/api/pickOrder/actions";
- import SearchResults, { Column } from "../SearchResults/SearchResults";
- import { defaultPagingController } from "../SearchResults/SearchResults";
- import SearchBox, { Criterion } from "../SearchBox";
- import dayjs from "dayjs";
- import { dummyQCData } from "../PoDetail/dummyQcTemplate";
- import { CreateStockOutLine } from "@/app/api/pickOrder/actions";
- import LotTable from './LotTable';
- import { updateInventoryLotLineStatus, updateInventoryStatus, updateInventoryLotLineQuantities } from "@/app/api/inventory/actions";
-
- interface Props {
- filterArgs: Record<string, any>;
- }
-
- interface LotPickData {
- id: number;
- lotId: number;
- lotNo: string;
- expiryDate: string;
- location: string;
- stockUnit: string;
- availableQty: number;
- requiredQty: number;
- actualPickQty: number;
- lotStatus: string;
- lotAvailability: 'available' | 'insufficient_stock' | 'expired' | 'status_unavailable';
- stockOutLineId?: number;
- stockOutLineStatus?: string;
- stockOutLineQty?: number;
- }
-
- interface PickQtyData {
- [lineId: number]: {
- [lotId: number]: number;
- };
- }
-
- const PickExecution: React.FC<Props> = ({ filterArgs }) => {
- const { t } = useTranslation("pickOrder");
- const router = useRouter();
- const [filteredPickOrders, setFilteredPickOrders] = useState(
- [] as ConsoPickOrderResult[],
- );
- const [isLoading, setIsLoading] = useState(false);
- const [selectedConsoCode, setSelectedConsoCode] = useState<string | undefined>();
- const [revertIds, setRevertIds] = useState<GridInputRowSelectionModel>([]);
- const [totalCount, setTotalCount] = useState<number>();
- const [usernameList, setUsernameList] = useState<NameList[]>([]);
-
- const [byPickOrderRows, setByPickOrderRows] = useState<
- Omit<PickOrderResult, "items">[] | undefined
- >(undefined);
- const [byItemsRows, setByItemsRows] = useState<ByItemsSummary[] | undefined>(
- undefined,
- );
- const [disableRelease, setDisableRelease] = useState<boolean>(true);
- const [selectedRowId, setSelectedRowId] = useState<number | null>(null);
-
- const [pickOrderDetails, setPickOrderDetails] = useState<GetPickOrderInfoResponse | null>(null);
- const [detailLoading, setDetailLoading] = useState(false);
-
- const [pickQtyData, setPickQtyData] = useState<PickQtyData>({});
- const [lotData, setLotData] = useState<LotPickData[]>([]);
-
- const [qcItems, setQcItems] = useState<QcItemWithChecks[]>([]);
- const [qcModalOpen, setQcModalOpen] = useState(false);
- const [selectedItemForQc, setSelectedItemForQc] = useState<GetPickOrderLineInfo & {
- pickOrderCode: string;
- qcResult?: PurchaseQcResult[];
- } | null>(null);
- const [selectedLotForQc, setSelectedLotForQc] = useState<LotPickData | null>(null);
-
- // ✅ Add lot selection state variables
- const [selectedLotRowId, setSelectedLotRowId] = useState<string | null>(null);
- const [selectedLotId, setSelectedLotId] = useState<number | null>(null);
-
- // 新增:分页控制器
- const [mainTablePagingController, setMainTablePagingController] = useState({
- pageNum: 0,
- pageSize: 10,
- });
- const [lotTablePagingController, setLotTablePagingController] = useState({
- pageNum: 0,
- pageSize: 10,
- });
-
- // Add missing search state variables
- const [searchQuery, setSearchQuery] = useState<Record<string, any>>({});
- const [originalPickOrderData, setOriginalPickOrderData] = useState<GetPickOrderInfoResponse | null>(null);
-
- const formProps = useForm<ReleasePickOrderInputs>();
- const errors = formProps.formState.errors;
-
- const onDetailClick = useCallback(
- (pickOrder: any) => {
- console.log(pickOrder);
- const status = pickOrder.status;
- if (pickOrderStatusMap[status] >= 3) {
- router.push(`/pickOrder/detail?consoCode=${pickOrder.consoCode}`);
- } else {
- setSelectedConsoCode(pickOrder.consoCode);
- }
- },
- [router],
- );
-
- const fetchNewPageConsoPickOrder = useCallback(
- async (
- pagingController: Record<string, number>,
- filterArgs: Record<string, number>,
- ) => {
- setIsLoading(true);
- const params = {
- ...pagingController,
- ...filterArgs,
- };
- const res = await fetchConsoPickOrderClient(params);
- if (res) {
- console.log(res);
- setFilteredPickOrders(res.records);
- setTotalCount(res.total);
- }
- setIsLoading(false);
- },
- [],
- );
-
- useEffect(() => {
- fetchNewPageConsoPickOrder({ limit: 10, offset: 0 }, filterArgs);
- }, [fetchNewPageConsoPickOrder, filterArgs]);
- const handleUpdateStockOutLineStatus = useCallback(async (
- stockOutLineId: number,
- status: string,
- qty?: number
- ) => {
- try {
- const updateData = {
- id: stockOutLineId,
- status: status,
- qty: qty
- };
-
- console.log("Updating stock out line status:", updateData);
- const result = await updateStockOutLineStatus(updateData);
-
- if (result) {
- console.log("Stock out line status updated successfully:", result);
-
- // Refresh lot data to show updated status
- if (selectedRowId) {
- handleRowSelect(selectedRowId);
- }
- }
- } catch (error) {
- console.error("Error updating stock out line status:", error);
-
- }
- }, [selectedRowId]);
- const isReleasable = useCallback((itemList: ByItemsSummary[]): boolean => {
- let isReleasable = true;
- for (const item of itemList) {
- isReleasable = item.requiredQty >= item.availableQty;
- if (!isReleasable) return isReleasable;
- }
- return isReleasable;
- }, []);
-
- const fetchConso = useCallback(
- async (consoCode: string) => {
- const res = await fetchConsoDetail(consoCode);
- const nameListRes = await fetchNameList();
- if (res) {
- console.log(res);
- setByPickOrderRows(res.pickOrders);
- setByItemsRows(res.items);
- setDisableRelease(isReleasable(res.items));
- } else {
- console.log("error");
- console.log(res);
- }
- if (nameListRes) {
- console.log(nameListRes);
- setUsernameList(nameListRes);
- }
- },
- [isReleasable],
- );
-
- const handleFetchAllPickOrderDetails = useCallback(async () => {
- setDetailLoading(true);
- try {
- const data = await fetchAllPickOrderDetails();
- setPickOrderDetails(data);
- setOriginalPickOrderData(data); // Store original data for filtering
- console.log("All Pick Order Details:", data);
-
- const initialPickQtyData: PickQtyData = {};
- data.pickOrders.forEach((pickOrder: any) => {
- pickOrder.pickOrderLines.forEach((line: any) => {
- initialPickQtyData[line.id] = {};
- });
- });
- setPickQtyData(initialPickQtyData);
-
- } catch (error) {
- console.error("Error fetching all pick order details:", error);
- } finally {
- setDetailLoading(false);
- }
- }, []);
-
- useEffect(() => {
- handleFetchAllPickOrderDetails();
- }, [handleFetchAllPickOrderDetails]);
-
- const onChange = useCallback(
- (event: React.SyntheticEvent, newValue: NameList) => {
- console.log(newValue);
- formProps.setValue("assignTo", newValue.id);
- },
- [formProps],
- );
-
- const onSubmit = useCallback<SubmitHandler<ReleasePickOrderInputs>>(
- async (data, event) => {
- console.log(data);
- try {
- const res = await releasePickOrder(data);
- console.log(res);
- if (res.consoCode.length > 0) {
- console.log(res);
- router.push(`/pickOrder/detail?consoCode=${res.consoCode}`);
- } else {
- console.log(res);
- }
- } catch (error) {
- console.log(error);
- }
- },
- [router],
- );
-
- const onSubmitError = useCallback<SubmitErrorHandler<ReleasePickOrderInputs>>(
- (errors) => {},
- [],
- );
-
- const handleConsolidate_revert = useCallback(() => {
- console.log(revertIds);
- }, [revertIds]);
-
- useEffect(() => {
- if (selectedConsoCode) {
- fetchConso(selectedConsoCode);
- formProps.setValue("consoCode", selectedConsoCode);
- }
- }, [selectedConsoCode, fetchConso, formProps]);
-
- const handlePickQtyChange = useCallback((lineId: number, lotId: number, value: number | string) => {
- console.log("Changing pick qty:", { lineId, lotId, value });
-
- // ✅ Handle both number and string values
- const numericValue = typeof value === 'string' ? (value === '' ? 0 : parseInt(value, 10)) : value;
-
- setPickQtyData(prev => {
- const newData = {
- ...prev,
- [lineId]: {
- ...prev[lineId],
- [lotId]: numericValue
- }
- };
- console.log("New pick qty data:", newData);
- return newData;
- });
- }, []);
-
- const handleSubmitPickQty = useCallback(async (lineId: number, lotId: number) => {
- const qty = pickQtyData[lineId]?.[lotId] || 0;
- console.log(`提交拣货数量: Line ${lineId}, Lot ${lotId}, Qty ${qty}`);
-
- // ✅ Find the stock out line for this lot
- const selectedLot = lotData.find(lot => lot.lotId === lotId);
- if (!selectedLot?.stockOutLineId) {
- return;
- }
-
- try {
- // ✅ Only two statuses: partially_completed or completed
- let newStatus = 'partially_completed'; // Default status
-
- if (qty >= selectedLot.requiredQty) {
- newStatus = 'completed'; // Full quantity picked
- }
- // If qty < requiredQty, stays as 'partially_completed'
-
- // ✅ Function 1: Update stock out line with new status and quantity
- try {
- // ✅ Function 1: Update stock out line with new status and quantity
- const stockOutLineUpdate = await updateStockOutLineStatus({
- id: selectedLot.stockOutLineId,
- status: newStatus,
- qty: qty
- });
-
- console.log("✅ Stock out line updated:", stockOutLineUpdate);
-
- } catch (error) {
- console.error("❌ Error updating stock out line:", error);
- return; // Stop execution if this fails
- }
-
- // ✅ Function 2: Update inventory lot line (balance hold_qty and out_qty)
- if (qty > 0) {
- const inventoryLotLineUpdate = await updateInventoryLotLineQuantities({
- inventoryLotLineId: lotId,
- qty: qty,
- status: 'available',
- operation: 'pick'
- });
-
- console.log("Inventory lot line updated:", inventoryLotLineUpdate);
- }
-
- // ✅ Function 3: Handle inventory table onhold if needed
- if (newStatus === 'completed') {
- // All required quantity picked - might need to update inventory status
- // Note: We'll handle inventory update in a separate function or after selectedRow is available
- console.log("Completed status - inventory update needed but selectedRow not available yet");
- }
-
- console.log("All updates completed successfully");
-
- // ✅ Refresh lot data to show updated quantities
- if (selectedRowId) {
- await handleRowSelect(selectedRowId, true);
- // Note: We'll handle refresh after the function is properly defined
- console.log("Data refresh needed but handleRowSelect not available yet");
- }
- await handleFetchAllPickOrderDetails();
- } catch (error) {
- console.error("Error updating pick quantity:", error);
- }
- }, [pickQtyData, lotData, selectedRowId]);
-
- const getTotalPickedQty = useCallback((lineId: number) => {
- const lineData = pickQtyData[lineId];
- if (!lineData) return 0;
- return Object.values(lineData).reduce((sum, qty) => sum + qty, 0);
- }, [pickQtyData]);
-
-
-
- const handleQcCheck = useCallback(async (line: GetPickOrderLineInfo, pickOrderCode: string) => {
- // ✅ Get the selected lot for QC
- if (!selectedLotId) {
-
- return;
- }
-
- const selectedLot = lotData.find(lot => lot.lotId === selectedLotId);
- if (!selectedLot) {
- //alert("Selected lot not found in lot data");
- return;
- }
-
- // ✅ Check if stock out line exists
- if (!selectedLot.stockOutLineId) {
- //alert("Please create a stock out line first before performing QC check");
- return;
- }
-
- setSelectedLotForQc(selectedLot);
-
- // ✅ ALWAYS use dummy data for consistent behavior
- const transformedDummyData = dummyQCData.map(item => ({
- id: item.id,
- code: item.code,
- name: item.name,
- itemId: line.itemId,
- lowerLimit: undefined,
- upperLimit: undefined,
- description: item.qcDescription,
- // ✅ Always reset QC result properties to undefined for fresh start
- qcPassed: undefined,
- failQty: undefined,
- remarks: undefined
- }));
-
- setQcItems(transformedDummyData as QcItemWithChecks[]);
-
- // ✅ Get existing QC results if any (for display purposes only)
- let qcResult: any[] = [];
- try {
- const rawQcResult = await fetchPickOrderQcResult(line.id);
- qcResult = rawQcResult.map((result: any) => ({
- ...result,
- isPassed: result.isPassed || false
- }));
- } catch (error) {
- // No existing QC result found - this is normal
- }
-
- setSelectedItemForQc({
- ...line,
- pickOrderCode,
- qcResult
- });
- setQcModalOpen(true);
- }, [lotData, selectedLotId, setQcItems]);
-
- const handleCloseQcModal = useCallback(() => {
- console.log("Closing QC modal");
- setQcModalOpen(false);
- setSelectedItemForQc(null);
- }, []);
-
- const handleSetItemDetail = useCallback((item: any) => {
- setSelectedItemForQc(item);
- }, []);
-
- // 新增:处理分页变化
- const handleMainTablePageChange = useCallback((event: unknown, newPage: number) => {
- setMainTablePagingController(prev => ({
- ...prev,
- pageNum: newPage,
- }));
- }, []);
-
- const handleMainTablePageSizeChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
- const newPageSize = parseInt(event.target.value, 10);
- setMainTablePagingController({
- pageNum: 0,
- pageSize: newPageSize,
- });
- }, []);
-
- const handleLotTablePageChange = useCallback((event: unknown, newPage: number) => {
- setLotTablePagingController(prev => ({
- ...prev,
- pageNum: newPage,
- }));
- }, []);
-
- const handleLotTablePageSizeChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
- const newPageSize = parseInt(event.target.value, 10);
- setLotTablePagingController({
- pageNum: 0,
- pageSize: newPageSize,
- });
- }, []);
-
- // ✅ Fix lot selection logic
- const handleLotSelection = useCallback((uniqueLotId: string, lotId: number) => {
- // If clicking the same lot, unselect it
- if (selectedLotRowId === uniqueLotId) {
- setSelectedLotRowId(null);
- setSelectedLotId(null);
- } else {
- // Select the new lot
- setSelectedLotRowId(uniqueLotId);
- setSelectedLotId(lotId);
- }
- }, [selectedLotRowId]);
-
- // ✅ Add function to handle row selection that resets lot selection
- const handleRowSelect = useCallback(async (lineId: number, preserveLotSelection: boolean = false) => {
- setSelectedRowId(lineId);
-
- // ✅ Only reset lot selection if not preserving
- if (!preserveLotSelection) {
- setSelectedLotRowId(null);
- setSelectedLotId(null);
- }
-
- try {
- const lotDetails = await fetchPickOrderLineLotDetails(lineId);
- console.log("Lot details from API:", lotDetails);
-
- const realLotData: LotPickData[] = lotDetails.map((lot: any) => ({
- id: lot.id, // This should be the unique row ID for the table
- lotId: lot.lotId, // This is the inventory lot line ID
- lotNo: lot.lotNo,
- expiryDate: lot.expiryDate ? new Date(lot.expiryDate).toLocaleDateString() : 'N/A',
- location: lot.location,
- stockUnit: lot.stockUnit,
- availableQty: lot.availableQty,
- requiredQty: lot.requiredQty,
- actualPickQty: lot.actualPickQty || 0,
- lotStatus: lot.lotStatus,
- lotAvailability: lot.lotAvailability,
- stockOutLineId: lot.stockOutLineId,
- stockOutLineStatus: lot.stockOutLineStatus,
- stockOutLineQty: lot.stockOutLineQty
- }));
-
- setLotData(realLotData);
- } catch (error) {
- console.error("Error fetching lot details:", error);
- setLotData([]);
- }
- }, []);
-
- const prepareMainTableData = useMemo(() => {
- if (!pickOrderDetails) return [];
-
- return pickOrderDetails.pickOrders.flatMap((pickOrder) =>
- pickOrder.pickOrderLines.map((line) => {
- // 修复:处理 availableQty 可能为 null 的情况
- const availableQty = line.availableQty ?? 0;
- const balanceToPick = availableQty - line.requiredQty;
-
- // ✅ 使用 dayjs 进行一致的日期格式化
- const formattedTargetDate = pickOrder.targetDate
- ? dayjs(pickOrder.targetDate).format('YYYY-MM-DD')
- : 'N/A';
-
- return {
- ...line,
- pickOrderCode: pickOrder.code,
- targetDate: formattedTargetDate, // ✅ 使用 dayjs 格式化的日期
- balanceToPick: balanceToPick,
- pickedQty: line.pickedQty,
- // 确保 availableQty 不为 null
- availableQty: availableQty,
- };
- })
- );
- }, [pickOrderDetails]);
-
- const prepareLotTableData = useMemo(() => {
- return lotData.map((lot) => ({
- ...lot,
- id: lot.lotId,
- }));
- }, [lotData]);
-
- // 新增:分页数据
- const paginatedMainTableData = useMemo(() => {
- const startIndex = mainTablePagingController.pageNum * mainTablePagingController.pageSize;
- const endIndex = startIndex + mainTablePagingController.pageSize;
- return prepareMainTableData.slice(startIndex, endIndex);
- }, [prepareMainTableData, mainTablePagingController]);
-
- const paginatedLotTableData = useMemo(() => {
- const startIndex = lotTablePagingController.pageNum * lotTablePagingController.pageSize;
- const endIndex = startIndex + lotTablePagingController.pageSize;
- return prepareLotTableData.slice(startIndex, endIndex);
- }, [prepareLotTableData, lotTablePagingController]);
-
- const selectedRow = useMemo(() => {
- if (!selectedRowId || !pickOrderDetails) return null;
-
- for (const pickOrder of pickOrderDetails.pickOrders) {
- const foundLine = pickOrder.pickOrderLines.find(line => line.id === selectedRowId);
- if (foundLine) {
- return { ...foundLine, pickOrderCode: pickOrder.code };
- }
- }
- return null;
- }, [selectedRowId, pickOrderDetails]);
- const handleInventoryUpdate = useCallback(async (itemId: number, lotId: number, qty: number) => {
- try {
- const inventoryUpdate = await updateInventoryStatus({
- itemId: itemId,
- lotId: lotId,
- status: 'reserved',
- qty: qty
- });
-
- console.log("Inventory status updated:", inventoryUpdate);
- } catch (error) {
- console.error("Error updating inventory status:", error);
- }
- }, []);
-
- // ✅ Add this function after handleRowSelect is defined
- const handleDataRefresh = useCallback(async () => {
- if (selectedRowId) {
- try {
- await handleRowSelect(selectedRowId, true);
- } catch (error) {
- console.error("Error refreshing data:", error);
- }
- }
- }, [selectedRowId, handleRowSelect]);
- const handleInsufficientStock = useCallback(async () => {
- console.log("Insufficient stock - testing resuggest API");
-
- if (!selectedRowId || !pickOrderDetails) {
- // alert("Please select a pick order line first");
- return;
- }
-
- // Find the pick order ID from the selected row
- let pickOrderId: number | null = null;
- for (const pickOrder of pickOrderDetails.pickOrders) {
- const foundLine = pickOrder.pickOrderLines.find(line => line.id === selectedRowId);
- if (foundLine) {
- pickOrderId = pickOrder.id;
- break;
- }
- }
-
- if (!pickOrderId) {
- // alert("Could not find pick order ID for selected line");
- return;
- }
-
- try {
- console.log(`Calling resuggest API for pick order ID: ${pickOrderId}`);
-
- // Call the resuggest API
- const result = await resuggestPickOrder(pickOrderId);
-
- console.log("Resuggest API result:", result);
-
- if (result.code === "SUCCESS") {
- //alert(`✅ Resuggest successful!\n\nMessage: ${result.message}\n\nRemoved: ${result.message?.includes('Removed') ? 'Yes' : 'No'}\nCreated: ${result.message?.includes('created') ? 'Yes' : 'No'}`);
-
- // Refresh the lot data to show the new suggestions
- if (selectedRowId) {
- await handleRowSelect(selectedRowId);
- }
-
- // Also refresh the main pick order details
- await handleFetchAllPickOrderDetails();
-
- } else {
- //alert(`❌ Resuggest failed!\n\nError: ${result.message}`);
- }
-
- } catch (error) {
- console.error("Error calling resuggest API:", error);
- //alert(`❌ Error calling resuggest API:\n\n${error instanceof Error ? error.message : 'Unknown error'}`);
- }
- }, [selectedRowId, pickOrderDetails, handleRowSelect, handleFetchAllPickOrderDetails]);
-
- // Add this function (around line 350)
- const hasSelectedLots = useCallback((lineId: number) => {
- return selectedLotRowId !== null;
- }, [selectedLotRowId]);
-
- // Add state for showing input body
- const [showInputBody, setShowInputBody] = useState(false);
- const [selectedLotForInput, setSelectedLotForInput] = useState<LotPickData | null>(null);
-
- // Add function to handle lot selection for input body display
- const handleLotSelectForInput = useCallback((lot: LotPickData) => {
- setSelectedLotForInput(lot);
- setShowInputBody(true);
- }, []);
-
- // Add function to generate input body
- const generateInputBody = useCallback((): CreateStockOutLine | null => {
- if (!selectedLotForInput || !selectedRowId || !selectedRow || !pickOrderDetails?.consoCode) {
- return null;
- }
-
- return {
- consoCode: pickOrderDetails.consoCode,
- pickOrderLineId: selectedRowId,
- inventoryLotLineId: selectedLotForInput.lotId,
- qty: 0.0
- };
- }, [selectedLotForInput, selectedRowId, selectedRow, pickOrderDetails?.consoCode]);
-
-
-
- // Add function to handle create stock out line
- const handleCreateStockOutLine = useCallback(async (inventoryLotLineId: number) => {
- if (!selectedRowId || !pickOrderDetails?.consoCode) {
- console.error("Missing required data for creating stock out line.");
- return;
- }
-
- try {
- // ✅ Store current lot selection before refresh
- const currentSelectedLotRowId = selectedLotRowId;
- const currentSelectedLotId = selectedLotId;
-
- const stockOutLineData: CreateStockOutLine = {
- consoCode: pickOrderDetails.consoCode,
- pickOrderLineId: selectedRowId,
- inventoryLotLineId: inventoryLotLineId,
- qty: 0.0
- };
-
- console.log("=== STOCK OUT LINE CREATION DEBUG ===");
- console.log("Input Body:", JSON.stringify(stockOutLineData, null, 2));
-
- // ✅ Use the correct API function
- const result = await createStockOutLine(stockOutLineData);
-
- console.log("Stock Out Line created:", result);
-
- if (result) {
- console.log("Stock out line created successfully:", result);
-
- // ✅ Auto-refresh data after successful creation
- console.log("🔄 Refreshing data after stock out line creation...");
-
- try {
- // ✅ Refresh lot data for the selected row (maintains selection)
- if (selectedRowId) {
- await handleRowSelect(selectedRowId, true); // ✅ Preserve lot selection
- }
-
- // ✅ Refresh main pick order details
- await handleFetchAllPickOrderDetails();
-
- console.log("✅ Data refresh completed - lot selection maintained!");
- } catch (refreshError) {
- console.error("❌ Error refreshing data:", refreshError);
- }
-
- setShowInputBody(false); // Hide preview after successful creation
- } else {
- console.error("Failed to create stock out line: No response");
- }
- } catch (error) {
- console.error("Error creating stock out line:", error);
- }
- }, [selectedRowId, pickOrderDetails?.consoCode, handleRowSelect, handleFetchAllPickOrderDetails, selectedLotRowId, selectedLotId]);
-
- // ✅ New function to refresh data while preserving lot selection
- const handleRefreshDataPreserveSelection = useCallback(async () => {
- if (!selectedRowId) return;
-
- // ✅ Store current lot selection
- const currentSelectedLotRowId = selectedLotRowId;
- const currentSelectedLotId = selectedLotId;
-
- try {
- // ✅ Refresh lot data
- await handleRowSelect(selectedRowId, true); // ✅ Preserve selection
-
- // ✅ Refresh main pick order details
- await handleFetchAllPickOrderDetails();
-
- // ✅ Restore lot selection
- setSelectedLotRowId(currentSelectedLotRowId);
- setSelectedLotId(currentSelectedLotId);
-
- console.log("✅ Data refreshed with selection preserved");
- } catch (error) {
- console.error("❌ Error refreshing data:", error);
- }
- }, [selectedRowId, selectedLotRowId, selectedLotId, handleRowSelect, handleFetchAllPickOrderDetails]);
-
- // 自定义主表格组件
- const CustomMainTable = () => {
- return (
- <>
- <TableContainer component={Paper}>
- <Table>
- <TableHead>
- <TableRow>
- <TableCell>{t("Selected")}</TableCell>
- <TableCell>{t("Pick Order Code")}</TableCell>
- <TableCell>{t("Item Code")}</TableCell>
- <TableCell>{t("Item Name")}</TableCell>
- <TableCell align="right">{t("Order Quantity")}</TableCell>
- <TableCell align="right">{t("Current Stock")}</TableCell>
- <TableCell align="right">{t("Qty Already Picked")}</TableCell>
- <TableCell align="right">{t("Stock Unit")}</TableCell>
- <TableCell align="right">{t("Target Date")}</TableCell>
- </TableRow>
- </TableHead>
- <TableBody>
- {paginatedMainTableData.length === 0 ? (
- <TableRow>
- <TableCell colSpan={7} align="center">
- <Typography variant="body2" color="text.secondary">
- {t("No data available")}
- </Typography>
- </TableCell>
- </TableRow>
- ) : (
- paginatedMainTableData.map((line) => {
- // 修复:处理 availableQty 可能为 null 的情况,并确保负值显示为 0
- const availableQty = line.availableQty ?? 0;
- const balanceToPick = Math.max(0, availableQty - line.requiredQty); // 确保不为负数
- const totalPickedQty = getTotalPickedQty(line.id);
- const actualPickedQty = line.pickedQty ?? 0;
- return (
- <TableRow
- key={line.id}
- sx={{
- "& > *": { borderBottom: "unset" },
- color: "black",
- backgroundColor: selectedRowId === line.id ? "action.selected" : "inherit",
- cursor: "pointer",
- "&:hover": {
- backgroundColor: "action.hover",
- },
- }}
- >
- <TableCell align="center" sx={{ width: "60px" }}>
- <Checkbox
- checked={selectedRowId === line.id}
- onChange={(e) => {
- if (e.target.checked) {
- handleRowSelect(line.id);
- } else {
- setSelectedRowId(null);
- setLotData([]);
- }
- }}
- onClick={(e) => e.stopPropagation()}
- />
- </TableCell>
- <TableCell align="left">{line.pickOrderCode}</TableCell>
- <TableCell align="left">{line.itemCode}</TableCell>
- <TableCell align="left">{line.itemName}</TableCell>
- <TableCell align="right">{line.requiredQty}</TableCell>
- <TableCell align="right" sx={{
- color: availableQty >= line.requiredQty ? 'success.main' : 'error.main',
- }}>
- {availableQty.toLocaleString()} {/* 添加千位分隔符 */}
- </TableCell>
- <TableCell align="right">{actualPickedQty}</TableCell>
- <TableCell align="right">{line.uomDesc}</TableCell>
- <TableCell align="right">{line.targetDate}</TableCell>
- </TableRow>
- );
- })
- )}
- </TableBody>
- </Table>
- </TableContainer>
-
- <TablePagination
- component="div"
- count={prepareMainTableData.length}
- page={mainTablePagingController.pageNum}
- rowsPerPage={mainTablePagingController.pageSize}
- onPageChange={handleMainTablePageChange}
- onRowsPerPageChange={handleMainTablePageSizeChange}
- rowsPerPageOptions={[10, 25, 50]}
- labelRowsPerPage={t("Rows per page")}
- labelDisplayedRows={({ from, to, count }) =>
- `${from}-${to} of ${count !== -1 ? count : `more than ${to}`}`
- }
- />
- </>
- );
- };
-
- // Add search criteria
- const searchCriteria: Criterion<any>[] = useMemo(
- () => [
- {
- label: t("Item Code"),
- paramName: "itemCode",
- type: "text",
- },
- {
- label: t("Pick Order Code"),
- paramName: "pickOrderCode",
- type: "text",
- },
- {
- label: t("Item Name"),
- paramName: "itemName",
- type: "text",
- },
- {
- label: t("Target Date From"),
- label2: t("Target Date To"),
- paramName: "targetDate",
- type: "dateRange",
- },
- ],
- [t],
- );
-
- // Add search handler
- const handleSearch = useCallback((query: Record<string, any>) => {
- setSearchQuery({ ...query });
- console.log("Search query:", query);
-
- if (!originalPickOrderData) return;
-
- const filtered = originalPickOrderData.pickOrders.filter((pickOrder) => {
- // Check if any line in this pick order matches the search criteria
- return pickOrder.pickOrderLines.some((line) => {
- const itemCodeMatch = !query.itemCode ||
- line.itemCode?.toLowerCase().includes((query.itemCode || "").toLowerCase());
-
- const itemNameMatch = !query.itemName ||
- line.itemName?.toLowerCase().includes((query.itemName || "").toLowerCase());
-
- const pickOrderCodeMatch = !query.pickOrderCode ||
- pickOrder.code?.toLowerCase().includes((query.pickOrderCode || "").toLowerCase());
-
-
- return itemCodeMatch && itemNameMatch && pickOrderCodeMatch ;
- });
- });
-
- // Create filtered data structure
- const filteredData: GetPickOrderInfoResponse = {
- ...originalPickOrderData,
- pickOrders: filtered
- };
-
- setPickOrderDetails(filteredData);
- console.log("Filtered pick orders count:", filtered.length);
- }, [originalPickOrderData, t]);
-
- // Add reset handler
- const handleReset = useCallback(() => {
- setSearchQuery({});
- if (originalPickOrderData) {
- setPickOrderDetails(originalPickOrderData);
- }
- }, [originalPickOrderData]);
-
- // Add this to debug the lot data
- useEffect(() => {
- console.log("Lot data:", lotData);
- console.log("Pick Qty Data:", pickQtyData);
- }, [lotData, pickQtyData]);
-
- return (
- <FormProvider {...formProps}>
- <Stack spacing={2}>
- {/* Search Box */}
- <Box>
- <SearchBox
- criteria={searchCriteria}
- onSearch={handleSearch}
- onReset={handleReset}
- />
- </Box>
-
- {/* 主表格 */}
- <Box>
- <Typography variant="h6" gutterBottom>
- {t("Pick Order Details")}
- </Typography>
- {detailLoading ? (
- <Box display="flex" justifyContent="center" alignItems="center" minHeight="200px">
- <CircularProgress size={40} />
- </Box>
- ) : pickOrderDetails ? (
- <CustomMainTable />
- ) : (
- <Box display="flex" justifyContent="center" alignItems="center" minHeight="200px">
- <Typography variant="body2" color="text.secondary">
- 正在載入數據...
- </Typography>
- </Box>
- )}
- </Box>
-
- {/* 批次表格 - 放在主表格下方 */}
- {selectedRow && (
- <Box>
- <Typography variant="h6" gutterBottom>
- Item lot to be Pick: {selectedRow.pickOrderCode} - {selectedRow.itemName}
- </Typography>
-
- {/* 检查是否有可用的批次数据 */}
- {lotData.length > 0 ? (
- <LotTable
- lotData={lotData}
- selectedRowId={selectedRowId}
- selectedRow={selectedRow}
- pickQtyData={pickQtyData}
- selectedLotRowId={selectedLotRowId}
- selectedLotId={selectedLotId}
- onLotSelection={handleLotSelection}
- onPickQtyChange={handlePickQtyChange}
- onSubmitPickQty={handleSubmitPickQty}
- onCreateStockOutLine={handleCreateStockOutLine}
- onQcCheck={handleQcCheck}
- onLotSelectForInput={handleLotSelectForInput}
- showInputBody={showInputBody}
- setShowInputBody={setShowInputBody}
- selectedLotForInput={selectedLotForInput}
- generateInputBody={generateInputBody}
- />
- ) : (
- <Box
- sx={{
- p: 3,
- textAlign: 'center',
- border: '1px solid',
- borderColor: 'divider',
- borderRadius: 1,
- backgroundColor: 'background.paper'
- }}
- >
- <Typography variant="body1" color="text.secondary" gutterBottom>
- {selectedRow.availableQty === null || selectedRow.availableQty === 0
- ? t("No available stock for this item")
- : t("No lot details available for this item")
- }
- </Typography>
- <Typography variant="body2" color="text.secondary">
- {selectedRow.availableQty === null || selectedRow.availableQty === 0
- ? t("Current stock is insufficient or unavailable")
- : t("Please check inventory status")
- }
- </Typography>
- </Box>
- )}
-
- {/* Action buttons below the lot table */}
- <Box sx={{ mt: 2 }}>
- <Stack direction="row" spacing={1}>
- <Button
- variant="contained"
- onClick={() => handleInsufficientStock()}
- sx={{ whiteSpace: 'nowrap' }}
- >
- {t("Insufficient Stock & Pick Another Lot")}
- </Button>
- </Stack>
- </Box>
- </Box>
- )}
-
- {/* Action Buttons */}
- {selectedRow && (
- <Grid container>
- <Grid item xs={12} display="flex" justifyContent="end" alignItems="end">
- <Button
- disabled={(revertIds as number[]).length < 1}
- variant="outlined"
- onClick={handleConsolidate_revert}
- sx={{ mr: 1 }}
- >
- {t("remove")}
- </Button>
- <Button
- disabled={disableRelease}
- variant="contained"
- onClick={formProps.handleSubmit(onSubmit, onSubmitError)}
- >
- {t("release")}
- </Button>
- </Grid>
- </Grid>
- )}
-
- {/* QC Modal */}
- {selectedItemForQc && qcModalOpen && (
- <PickQcStockInModalVer2
- open={qcModalOpen}
- onClose={handleCloseQcModal}
- itemDetail={selectedItemForQc}
- setItemDetail={handleSetItemDetail}
- qc={qcItems}
- warehouse={[]}
- qcItems={qcItems}
- setQcItems={setQcItems}
- selectedLotId={selectedLotForQc?.stockOutLineId}
- onStockOutLineUpdate={() => {
- if (selectedRowId) {
- handleRowSelect(selectedRowId);
- }
- }}
- lotData={lotData}
- />
- )}
- </Stack>
- </FormProvider>
- );
- };
-
- export default PickExecution;
|