| @@ -19,6 +19,8 @@ import { | |||
| DialogContentText, | |||
| DialogActions, | |||
| } from "@mui/material"; | |||
| import { SessionWithTokens } from "@/config/authConfig"; | |||
| import { useSession } from "next-auth/react"; | |||
| import SearchBox, { Criterion } from "@/components/SearchBox/SearchBox"; | |||
| import { useState, useCallback, useEffect } from "react"; | |||
| import { useTranslation } from "react-i18next"; | |||
| @@ -33,7 +35,7 @@ import { | |||
| import { fetchStockTakeSections } from "@/app/api/warehouse/actions"; | |||
| import dayjs from "dayjs"; | |||
| import { OUTPUT_DATE_FORMAT } from "@/app/utils/formatUtil"; | |||
| import { AUTH } from "@/authorities"; | |||
| const PER_PAGE = 6; | |||
| interface PickerCardListProps { | |||
| @@ -59,6 +61,9 @@ const PickerCardList: React.FC<PickerCardListProps> = ({ | |||
| const [loading, setLoading] = useState(false); | |||
| const [stockTakeSessions, setStockTakeSessions] = useState<AllPickedStockTakeListReponse[]>([]); | |||
| const [total, setTotal] = useState(0); | |||
| const { data: session } = useSession() as { data: SessionWithTokens | null }; | |||
| const abilities = session?.abilities ?? session?.user?.abilities ?? []; | |||
| const canManageStockTake = abilities.some((a) => a.trim() === AUTH.ADMIN); | |||
| /** 建立盤點後若仍在 page 0,仍強制重新載入 */ | |||
| const [listRefreshNonce, setListRefreshNonce] = useState(0); | |||
| const [creating, setCreating] = useState(false); | |||
| @@ -303,7 +308,7 @@ const handleResetSearch = () => { | |||
| variant="contained" | |||
| color="primary" | |||
| onClick={() => setOpenConfirmDialog(true)} | |||
| disabled={creating} | |||
| disabled={creating || !canManageStockTake} | |||
| > | |||
| {creating ? <CircularProgress size={20} /> : t("Create Stock Take for All Sections")} | |||
| </Button> | |||
| @@ -123,7 +123,7 @@ const PickerReStockTake: React.FC<PickerReStockTakeProps> = ({ | |||
| setLoadingDetails(false); | |||
| } | |||
| }, [selectedSession, total]); | |||
| {/* | |||
| useEffect(() => { | |||
| const inputs: Record<number, { firstQty: string; secondQty: string; firstBadQty: string; secondBadQty: string; remark: string }> = {}; | |||
| inventoryLotDetails.forEach((detail) => { | |||
| @@ -143,7 +143,31 @@ const PickerReStockTake: React.FC<PickerReStockTakeProps> = ({ | |||
| }); | |||
| setRecordInputs(inputs); | |||
| }, [inventoryLotDetails]); | |||
| */} | |||
| useEffect(() => { | |||
| setRecordInputs((prev) => { | |||
| const next: Record<number, { firstQty: string; secondQty: string; firstBadQty: string; secondBadQty: string; remark: string }> = {}; | |||
| inventoryLotDetails.forEach((detail) => { | |||
| const hasServerFirst = detail.firstStockTakeQty != null; | |||
| const hasServerSecond = detail.secondStockTakeQty != null; | |||
| const firstTotal = hasServerFirst | |||
| ? (detail.firstStockTakeQty! + (detail.firstBadQty ?? 0)).toString() | |||
| : ""; | |||
| const secondTotal = hasServerSecond | |||
| ? (detail.secondStockTakeQty! + (detail.secondBadQty ?? 0)).toString() | |||
| : ""; | |||
| const existing = prev[detail.id]; | |||
| next[detail.id] = { | |||
| firstQty: hasServerFirst ? firstTotal : (existing?.firstQty ?? firstTotal), | |||
| secondQty: hasServerSecond ? secondTotal : (existing?.secondQty ?? secondTotal), | |||
| firstBadQty: hasServerFirst ? (detail.firstBadQty?.toString() || "") : (existing?.firstBadQty ?? ""), | |||
| secondBadQty: hasServerSecond ? (detail.secondBadQty?.toString() || "") : (existing?.secondBadQty ?? ""), | |||
| remark: hasServerSecond ? (detail.remarks || "") : (existing?.remark ?? detail.remarks ?? ""), | |||
| }; | |||
| }); | |||
| return next; | |||
| }); | |||
| }, [inventoryLotDetails]); | |||
| useEffect(() => { | |||
| loadDetails(page, pageSize); | |||
| }, [page, pageSize, loadDetails]); | |||
| @@ -93,7 +93,7 @@ export const REPORTS: ReportDefinition[] = [ | |||
| { label: "物料編號 Item Code", name: "itemCode", type: "text", required: false}, | |||
| ] | |||
| }, | |||
| /* | |||
| { id: "rep-012", | |||
| title: "庫存盤點報告", | |||
| apiEndpoint: `${NEXT_PUBLIC_API_URL}/report/print-stock-take-variance`, | |||
| @@ -103,7 +103,24 @@ export const REPORTS: ReportDefinition[] = [ | |||
| { label: "物料編號 Item Code", name: "itemCode", type: "text", required: false}, | |||
| ] | |||
| }, | |||
| */ | |||
| { | |||
| id: "rep-012", | |||
| title: "庫存盤點報告", | |||
| apiEndpoint: `${NEXT_PUBLIC_API_URL}/report/print-stock-take-variance-v2`, | |||
| fields: [ | |||
| { | |||
| label: "盤點輪次 Stock Take Round", | |||
| name: "stockTakeRoundId", | |||
| type: "select", | |||
| required: true, | |||
| dynamicOptions: true, | |||
| dynamicOptionsEndpoint: `${NEXT_PUBLIC_API_URL}/report/stock-take-rounds`, | |||
| options: [] | |||
| }, | |||
| { label: "物料編號 Item Code", name: "itemCode", type: "text", required: false}, | |||
| ] | |||
| }, | |||
| { id: "rep-011", | |||
| title: "庫存明細報告", | |||
| apiEndpoint: `${NEXT_PUBLIC_API_URL}/report/print-stock-ledger`, | |||