|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511 |
- "use client";
- import {
- Autocomplete,
- Box,
- Button,
- CircularProgress,
- FormControl,
- Grid,
- Modal,
- TextField,
- Typography,
- Table,
- TableBody,
- TableCell,
- TableContainer,
- TableHead,
- TableRow,
- Paper,
- Checkbox,
- TablePagination,
- } from "@mui/material";
- import { useCallback, useEffect, useMemo, useState } from "react";
- import { useTranslation } from "react-i18next";
- import {
- newassignPickOrder,
- AssignPickOrderInputs,
- releaseAssignedPickOrders,
- fetchPickOrderWithStockClient, // Add this import
- } from "@/app/api/pickOrder/actions";
- import { fetchNameList, NameList } from "@/app/api/user/actions";
- import {
- FormProvider,
- useForm,
- } from "react-hook-form";
- import { isEmpty, upperFirst, groupBy } from "lodash";
- import { OUTPUT_DATE_FORMAT, arrayToDayjs } from "@/app/utils/formatUtil";
- import useUploadContext from "../UploadProvider/useUploadContext";
- import dayjs from "dayjs";
- import arraySupport from "dayjs/plugin/arraySupport";
- import SearchBox, { Criterion } from "../SearchBox";
- import { sortBy, uniqBy } from "lodash";
- import { createStockOutLine, CreateStockOutLine, fetchPickOrderDetails } from "@/app/api/pickOrder/actions";
- dayjs.extend(arraySupport);
-
- interface Props {
- filterArgs: Record<string, any>;
- }
-
- // Update the interface to match the new API response structure
- interface PickOrderRow {
- id: string;
- code: string;
- targetDate: string;
- type: string;
- status: string;
- assignTo: number;
- groupName: string;
- consoCode?: string;
- pickOrderLines: PickOrderLineRow[];
- }
-
- interface PickOrderLineRow {
- id: number;
- itemId: number;
- itemCode: string;
- itemName: string;
- availableQty: number | null;
- requiredQty: number;
- uomCode: string;
- uomDesc: string;
- suggestedList: any[];
- }
-
- const style = {
- position: "absolute",
- top: "50%",
- left: "50%",
- transform: "translate(-50%, -50%)",
- bgcolor: "background.paper",
- pt: 5,
- px: 5,
- pb: 10,
- width: { xs: "100%", sm: "100%", md: "100%" },
- };
-
- const AssignTo: React.FC<Props> = ({ filterArgs }) => {
- const { t } = useTranslation("pickOrder");
- const { setIsUploading } = useUploadContext();
- const [isUploading, setIsUploadingLocal] = useState(false);
- // Update state to use pick order data directly
- const [selectedPickOrderIds, setSelectedPickOrderIds] = useState<string[]>([]);
- const [filteredPickOrders, setFilteredPickOrders] = useState<PickOrderRow[]>([]);
- const [isLoadingItems, setIsLoadingItems] = useState(false);
- const [pagingController, setPagingController] = useState({
- pageNum: 1,
- pageSize: 10,
- });
- const [totalCountItems, setTotalCountItems] = useState<number>();
- const [modalOpen, setModalOpen] = useState(false);
- const [usernameList, setUsernameList] = useState<NameList[]>([]);
- const [searchQuery, setSearchQuery] = useState<Record<string, any>>({});
- const [originalPickOrderData, setOriginalPickOrderData] = useState<PickOrderRow[]>([]);
-
- const formProps = useForm<AssignPickOrderInputs>();
- const errors = formProps.formState.errors;
-
- // Update the handler functions to work with string IDs
- const handlePickOrderSelect = useCallback((pickOrderId: string, checked: boolean) => {
- if (checked) {
- setSelectedPickOrderIds(prev => [...prev, pickOrderId]);
- } else {
- setSelectedPickOrderIds(prev => prev.filter(id => id !== pickOrderId));
- }
- }, []);
-
- const isPickOrderSelected = useCallback((pickOrderId: string) => {
- return selectedPickOrderIds.includes(pickOrderId);
- }, [selectedPickOrderIds]);
-
- // Update the fetch function to use the correct endpoint
- const fetchNewPageItems = useCallback(
- async (pagingController: Record<string, number>, filterArgs: Record<string, any>) => {
- setIsLoadingItems(true);
- try {
- const params = {
- ...pagingController,
- ...filterArgs,
- pageNum: (pagingController.pageNum || 1) - 1,
- pageSize: pagingController.pageSize || 10,
- // Filter for assigned status only
- status: "assigned"
- };
-
- const res = await fetchPickOrderWithStockClient(params);
-
- if (res && res.records) {
- // Convert pick order data to the expected format
- const pickOrderRows: PickOrderRow[] = res.records.map((pickOrder: any) => ({
- id: pickOrder.id,
- code: pickOrder.code,
- targetDate: pickOrder.targetDate,
- type: pickOrder.type,
- status: pickOrder.status,
- assignTo: pickOrder.assignTo,
- groupName: pickOrder.groupName || "No Group",
- consoCode: pickOrder.consoCode,
- pickOrderLines: pickOrder.pickOrderLines || []
- }));
-
- setOriginalPickOrderData(pickOrderRows);
- setFilteredPickOrders(pickOrderRows);
- setTotalCountItems(res.total);
- } else {
- setFilteredPickOrders([]);
- setTotalCountItems(0);
- }
- } catch (error) {
- console.error("Error fetching pick orders:", error);
- setFilteredPickOrders([]);
- setTotalCountItems(0);
- } finally {
- setIsLoadingItems(false);
- }
- },
- [],
- );
-
- // Handle Release operation
- // Handle Release operation
- const handleRelease = useCallback(async () => {
- if (selectedPickOrderIds.length === 0) return;
-
- setIsUploading(true);
- try {
- // Get the assigned user from the selected pick orders
- const selectedPickOrders = filteredPickOrders.filter(pickOrder =>
- selectedPickOrderIds.includes(pickOrder.id)
- );
-
- // Check if all selected pick orders have the same assigned user
- const assignedUsers = selectedPickOrders.map(po => po.assignTo).filter(Boolean);
- if (assignedUsers.length === 0) {
- alert("Selected pick orders are not assigned to any user.");
- return;
- }
-
- const assignToValue = assignedUsers[0];
-
- // Validate that all pick orders are assigned to the same user
- const allSameUser = assignedUsers.every(userId => userId === assignToValue);
- if (!allSameUser) {
- alert("All selected pick orders must be assigned to the same user.");
- return;
- }
-
- console.log("Using assigned user:", assignToValue);
- console.log("selectedPickOrderIds:", selectedPickOrderIds);
-
- const releaseRes = await releaseAssignedPickOrders({
- pickOrderIds: selectedPickOrderIds.map(id => parseInt(id)),
- assignTo: assignToValue
- });
-
- if (releaseRes.code === "SUCCESS") {
- console.log("Pick orders released successfully");
-
- // Get the consoCode from the response
- const consoCode = (releaseRes.entity as any)?.consoCode;
-
- if (consoCode) {
- // Create StockOutLine records for each pick order line
- for (const pickOrder of selectedPickOrders) {
- for (const line of pickOrder.pickOrderLines) {
- try {
- const stockOutLineData = {
- consoCode: consoCode,
- pickOrderLineId: line.id,
- inventoryLotLineId: 0, // This will be set when user scans QR code
- qty: line.requiredQty,
- };
-
- console.log("Creating stock out line:", stockOutLineData);
- await createStockOutLine(stockOutLineData);
- } catch (error) {
- console.error("Error creating stock out line for line", line.id, error);
- }
- }
- }
- }
-
- fetchNewPageItems(pagingController, filterArgs);
- } else {
- console.error("Release failed:", releaseRes.message);
- }
- } catch (error) {
- console.error("Error releasing pick orders:", error);
- } finally {
- setIsUploading(false);
- }
- }, [selectedPickOrderIds, filteredPickOrders, setIsUploading, fetchNewPageItems, pagingController, filterArgs]);
-
- // Update search criteria to match the new data structure
- const searchCriteria: Criterion<any>[] = useMemo(
- () => [
- {
- label: t("Pick Order Code"),
- paramName: "code",
- type: "text",
- },
- {
- label: t("Group Code"),
- paramName: "groupName",
- type: "text",
- },
- {
- label: t("Target Date From"),
- label2: t("Target Date To"),
- paramName: "targetDate",
- type: "dateRange",
- },
- ],
- [t],
- );
-
- // Update search function to work with pick order data
- const handleSearch = useCallback((query: Record<string, any>) => {
- setSearchQuery({ ...query });
-
- const filtered = originalPickOrderData.filter((pickOrder) => {
- const pickOrderTargetDateStr = arrayToDayjs(pickOrder.targetDate);
-
- const codeMatch = !query.code ||
- pickOrder.code?.toLowerCase().includes((query.code || "").toLowerCase());
-
- const groupNameMatch = !query.groupName ||
- pickOrder.groupName?.toLowerCase().includes((query.groupName || "").toLowerCase());
-
- // Date range search
- let dateMatch = true;
- if (query.targetDate || query.targetDateTo) {
- try {
- if (query.targetDate && !query.targetDateTo) {
- const fromDate = dayjs(query.targetDate);
- dateMatch = pickOrderTargetDateStr.isSame(fromDate, 'day') ||
- pickOrderTargetDateStr.isAfter(fromDate, 'day');
- } else if (!query.targetDate && query.targetDateTo) {
- const toDate = dayjs(query.targetDateTo);
- dateMatch = pickOrderTargetDateStr.isSame(toDate, 'day') ||
- pickOrderTargetDateStr.isBefore(toDate, 'day');
- } else if (query.targetDate && query.targetDateTo) {
- const fromDate = dayjs(query.targetDate);
- const toDate = dayjs(query.targetDateTo);
- dateMatch = (pickOrderTargetDateStr.isSame(fromDate, 'day') ||
- pickOrderTargetDateStr.isAfter(fromDate, 'day')) &&
- (pickOrderTargetDateStr.isSame(toDate, 'day') ||
- pickOrderTargetDateStr.isBefore(toDate, 'day'));
- }
- } catch (error) {
- console.error("Date parsing error:", error);
- dateMatch = true;
- }
- }
-
- return codeMatch && groupNameMatch && dateMatch;
- });
-
- setFilteredPickOrders(filtered);
- }, [originalPickOrderData]);
-
- const handleReset = useCallback(() => {
- setSearchQuery({});
- setFilteredPickOrders(originalPickOrderData);
- setTimeout(() => {
- setSearchQuery({});
- }, 0);
- }, [originalPickOrderData]);
-
- // Pagination handlers
- const handlePageChange = useCallback((event: unknown, newPage: number) => {
- const newPagingController = {
- ...pagingController,
- pageNum: newPage + 1,
- };
- setPagingController(newPagingController);
- }, [pagingController]);
-
- const handlePageSizeChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
- const newPageSize = parseInt(event.target.value, 10);
- const newPagingController = {
- pageNum: 1,
- pageSize: newPageSize,
- };
- setPagingController(newPagingController);
- }, []);
-
- // Component mount effect
- useEffect(() => {
- fetchNewPageItems(pagingController, filterArgs || {});
- }, []);
-
- // Dependencies change effect
- useEffect(() => {
- if (pagingController && (filterArgs || {})) {
- fetchNewPageItems(pagingController, filterArgs || {});
- }
- }, [pagingController, filterArgs, fetchNewPageItems]);
-
- useEffect(() => {
- const loadUsernameList = async () => {
- try {
- const res = await fetchNameList();
- if (res) {
- setUsernameList(res);
- }
- } catch (error) {
- console.error("Error loading username list:", error);
- }
- };
- loadUsernameList();
- }, []);
-
- // Update the table component to work with pick order data directly
- const CustomPickOrderTable = () => {
- // Helper function to get user name
- const getUserName = useCallback((assignToId: number | null | undefined) => {
- if (!assignToId) return '-';
- const user = usernameList.find(u => u.id === assignToId);
- return user ? user.name : `User ${assignToId}`;
- }, [usernameList]);
-
- return (
- <>
- <TableContainer component={Paper}>
- <Table>
- <TableHead>
- <TableRow>
- <TableCell>{t("Selected")}</TableCell>
- <TableCell>{t("Pick Order Code")}</TableCell>
- <TableCell>{t("Group 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("Stock Unit")}</TableCell>
- <TableCell>{t("Target Date")}</TableCell>
- <TableCell>{t("Assigned To")}</TableCell>
- </TableRow>
- </TableHead>
- <TableBody>
- {filteredPickOrders.length === 0 ? (
- <TableRow>
- <TableCell colSpan={10} align="center">
- <Typography variant="body2" color="text.secondary">
- {t("No data available")}
- </Typography>
- </TableCell>
- </TableRow>
- ) : (
- filteredPickOrders.map((pickOrder) => (
- pickOrder.pickOrderLines.map((line: PickOrderLineRow, index: number) => (
- <TableRow key={`${pickOrder.id}-${line.id}`}>
- {/* Checkbox - only show for first line of each pick order */}
- <TableCell>
- {index === 0 ? (
- <Checkbox
- checked={isPickOrderSelected(pickOrder.id)}
- onChange={(e) => handlePickOrderSelect(pickOrder.id, e.target.checked)}
- disabled={!isEmpty(pickOrder.consoCode)}
- />
- ) : null}
- </TableCell>
- {/* Pick Order Code - only show for first line */}
- <TableCell>
- {index === 0 ? pickOrder.code : null}
- </TableCell>
- {/* Group Name - only show for first line */}
- <TableCell>
- {index === 0 ? pickOrder.groupName : null}
- </TableCell>
- {/* Item Code */}
- <TableCell>{line.itemCode}</TableCell>
- {/* Item Name */}
- <TableCell>{line.itemName}</TableCell>
-
- {/* Order Quantity */}
- <TableCell align="right">{line.requiredQty}</TableCell>
-
- {/* Current Stock */}
- <TableCell align="right">
- <Typography
- variant="body2"
- color={line.availableQty && line.availableQty > 0 ? "success.main" : "error.main"}
- sx={{ fontWeight: line.availableQty && line.availableQty > 0 ? 'bold' : 'normal' }}
- >
- {(line.availableQty || 0).toLocaleString()}
- </Typography>
- </TableCell>
-
- {/* Unit */}
- <TableCell align="right">{line.uomDesc}</TableCell>
-
- {/* Target Date - only show for first line */}
- <TableCell>
- {index === 0 ? (
- arrayToDayjs(pickOrder.targetDate)
- .add(-1, "month")
- .format(OUTPUT_DATE_FORMAT)
- ) : null}
- </TableCell>
-
- {/* Assigned To - only show for first line */}
- <TableCell>
- {index === 0 ? (
- <Typography variant="body2">
- {getUserName(pickOrder.assignTo)}
- </Typography>
- ) : null}
- </TableCell>
- </TableRow>
- ))
- ))
- )}
- </TableBody>
- </Table>
- </TableContainer>
-
- <TablePagination
- component="div"
- count={totalCountItems || 0}
- page={(pagingController.pageNum - 1)}
- rowsPerPage={pagingController.pageSize}
- onPageChange={handlePageChange}
- onRowsPerPageChange={handlePageSizeChange}
- rowsPerPageOptions={[10, 25, 50, 100]}
- labelRowsPerPage={t("Rows per page")}
- labelDisplayedRows={({ from, to, count }) =>
- `${from}-${to} of ${count !== -1 ? count : `more than ${to}`}`
- }
- />
- </>
- );
- };
-
- return (
- <>
- <SearchBox criteria={searchCriteria} onSearch={handleSearch} onReset={handleReset} />
- <Grid container rowGap={1}>
- <Grid item xs={12}>
- {isLoadingItems ? (
- <CircularProgress size={40} />
- ) : (
- <CustomPickOrderTable />
- )}
- </Grid>
- <Grid item xs={12}>
- <Box sx={{ display: "flex", justifyContent: "flex-start", mt: 2 }}>
- <Button
- disabled={selectedPickOrderIds.length < 1}
- variant="outlined"
- onClick={handleRelease}
- >
- {t("Release")}
- </Button>
- </Box>
- </Grid>
- </Grid>
- </>
- );
- };
-
- export default AssignTo;
|