Selaa lähdekoodia

if have open item round, select new round by location if have overlap lcoation will tell those item will skip

stockTakeUpdate
CANCERYS\kw093 1 viikko sitten
vanhempi
commit
e4b2e48dc8
4 muutettua tiedostoa jossa 124 lisäystä ja 0 poistoa
  1. +21
    -0
      src/app/api/stockTake/actions.ts
  2. +99
    -0
      src/components/StockTakeManagement/PickerCardList.tsx
  3. +2
    -0
      src/i18n/en/stockTake.json
  4. +2
    -0
      src/i18n/zh/stockTake.json

+ 21
- 0
src/app/api/stockTake/actions.ts Näytä tiedosto

@@ -428,6 +428,27 @@ export type StockTakeCreateItemOption = {
};

/** 來源:All / PP / PF / other(與庫存批次現況報告一致) */
/** 建立區域輪:與 open 貨品輪重疊、本輪會略過的貨品 */
export type SectionCreateItemOverlap = {
itemId: number;
itemCode: string | null;
itemName: string | null;
stockTakeSection: string | null;
stockTakeRoundId: number;
stockTakeRoundName: string | null;
};

export const getSectionCreateItemOverlaps = async (sections: string[]) => {
const list = sections.map((s) => s.trim()).filter((s) => s.length > 0);
if (list.length === 0) return [] as SectionCreateItemOverlap[];
const params = new URLSearchParams();
list.forEach((s) => params.append("sections", s));
return serverFetchJson<SectionCreateItemOverlap[]>(
`${BASE_API_URL}/stockTakeRecord/sectionCreateItemOverlaps?${params.toString()}`,
{ method: "GET" },
);
};

export const getAvailableLotsForStockTakeCreate = async (
itemIds: number[],
lotOrigin?: string | null,


+ 99
- 0
src/components/StockTakeManagement/PickerCardList.tsx Näytä tiedosto

@@ -35,6 +35,7 @@ import {
Accordion,
AccordionSummary,
AccordionDetails,
Alert,
} from "@mui/material";
import SearchIcon from "@mui/icons-material/Search";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
@@ -53,7 +54,9 @@ import {
getLatestStockTakeRoundMeta,
getBusyStockTakeSections,
getAvailableLotsForStockTakeCreate,
getSectionCreateItemOverlaps,
searchItemsForStockTakeCreate,
type SectionCreateItemOverlap,
type StockTakeCreateLotOption,
} from "@/app/api/stockTake/actions";
import { fetchStockTakeSections } from "@/app/api/warehouse/actions";
@@ -357,6 +360,8 @@ const PickerCardList: React.FC<PickerCardListProps> = ({
const [createDialogSelectedLotLineIds, setCreateDialogSelectedLotLineIds] = useState<number[]>([]);
const [createDialogLotsLoading, setCreateDialogLotsLoading] = useState(false);
const [createDialogItemSearching, setCreateDialogItemSearching] = useState(false);
const [sectionItemOverlaps, setSectionItemOverlaps] = useState<SectionCreateItemOverlap[]>([]);
const [sectionOverlapsLoading, setSectionOverlapsLoading] = useState(false);
const [sectionsLoading, setSectionsLoading] = useState(false);
const [stockTakeSectionRows, setStockTakeSectionRows] = useState<StockTakeSectionInfo[]>([]);
const createStockTakeInFlightRef = useRef(false);
@@ -1197,6 +1202,39 @@ const PickerCardList: React.FC<PickerCardListProps> = ({
};
}, [openConfirmDialog, createMode, createDialogSelectedItems, createDialogLotOrigin]);

useEffect(() => {
if (!openConfirmDialog || createMode !== "section") {
setSectionItemOverlaps([]);
setSectionOverlapsLoading(false);
return;
}
const sections = createDialogSelectedSections.map((s) => s.trim()).filter((s) => s.length > 0);
if (sections.length === 0) {
setSectionItemOverlaps([]);
setSectionOverlapsLoading(false);
return;
}
let cancelled = false;
const timer = window.setTimeout(() => {
setSectionOverlapsLoading(true);
getSectionCreateItemOverlaps(sections)
.then((rows) => {
if (!cancelled) setSectionItemOverlaps(Array.isArray(rows) ? rows : []);
})
.catch((e) => {
console.error("Failed to load section create item overlaps:", e);
if (!cancelled) setSectionItemOverlaps([]);
})
.finally(() => {
if (!cancelled) setSectionOverlapsLoading(false);
});
}, 250);
return () => {
cancelled = true;
window.clearTimeout(timer);
};
}, [openConfirmDialog, createMode, createDialogSelectedSections]);

useEffect(() => {
setSectionsLoading(true);
fetchStockTakeSections()
@@ -1318,8 +1356,26 @@ const PickerCardList: React.FC<PickerCardListProps> = ({
setCreateDialogSelectedItems([]);
setCreateDialogLotOptions([]);
setCreateDialogSelectedLotLineIds([]);
setSectionItemOverlaps([]);
setSectionOverlapsLoading(false);
onCreateDialogClose?.();
};

const formatSectionItemOverlapLine = useCallback(
(row: SectionCreateItemOverlap) => {
const itemLabel = [row.itemCode, row.itemName].filter(Boolean).join(" ").trim() || `#${row.itemId}`;
const roundName = row.stockTakeRoundName?.trim()
? `「${row.stockTakeRoundName.trim()}」`
: "";
return t("Section create item overlap line", {
item: itemLabel,
round: row.stockTakeRoundId,
roundName,
section: row.stockTakeSection || "-",
});
},
[t],
);
return (
<Box>
{!hideSectionList && (
@@ -2010,6 +2066,30 @@ const PickerCardList: React.FC<PickerCardListProps> = ({
</Typography>{" "}
{createMode === "item" ? t("lots unit") : t("sections unit")}
</Typography>
{createMode === "section" && sectionOverlapsLoading ? (
<Typography variant="caption" color="text.secondary">
…
</Typography>
) : null}
{createMode === "section" && sectionItemOverlaps.length > 0 ? (
<Alert severity="warning" sx={{ py: 0.5, alignItems: "flex-start" }}>
<Typography variant="caption" fontWeight={700} display="block" sx={{ mb: 0.5 }}>
{t("Section create item overlap title")}
</Typography>
<Box component="ul" sx={{ m: 0, pl: 2, maxHeight: 140, overflowY: "auto" }}>
{sectionItemOverlaps.map((row) => (
<Typography
component="li"
variant="caption"
key={`${row.itemId}-${row.stockTakeRoundId}-${row.stockTakeSection}`}
sx={{ display: "list-item" }}
>
{formatSectionItemOverlapLine(row)}
</Typography>
))}
</Box>
</Alert>
) : null}
</Box>

{/* 右側:區域卡片網格 */}
@@ -2495,6 +2575,25 @@ const PickerCardList: React.FC<PickerCardListProps> = ({
</Typography>{" "}
{createMode === "item" ? t("lots unit") : t("sections unit")}
</Typography>
{createMode === "section" && sectionItemOverlaps.length > 0 ? (
<Alert severity="warning" sx={{ py: 0.75 }}>
<Typography variant="body2" fontWeight={700} sx={{ mb: 0.5 }}>
{t("Section create item overlap title")}
</Typography>
<Box component="ul" sx={{ m: 0, pl: 2, maxHeight: 160, overflowY: "auto" }}>
{sectionItemOverlaps.map((row) => (
<Typography
component="li"
variant="body2"
key={`confirm-${row.itemId}-${row.stockTakeRoundId}-${row.stockTakeSection}`}
sx={{ display: "list-item", mb: 0.5 }}
>
{formatSectionItemOverlapLine(row)}
</Typography>
))}
</Box>
</Alert>
) : null}
</Stack>
<Box
sx={{


+ 2
- 0
src/i18n/en/stockTake.json Näytä tiedosto

@@ -57,6 +57,8 @@
"Confirm": "Confirm",
"Confirm batch save approver": "Confirm batch save",
"Confirm create stock take": "Confirm create stock take",
"Section create item overlap title": "These items are already in an open item stock-take round; this round will skip them",
"Section create item overlap line": "Item {{item}} is in round #{{round}}{{roundName}}; this round will not include it (section {{section}})",
"Control Time": "Control Time",
"Create by item": "By item",
"Create by section": "By section",


+ 2
- 0
src/i18n/zh/stockTake.json Näytä tiedosto

@@ -178,6 +178,8 @@
"Search section code or name": "搜索代碼或名稱 (例如 ST-042 或 飲品)",
"No sections match search": "沒有符合搜索條件的區域",
"Confirm create stock take": "確認建立盤點",
"Section create item overlap title": "以下貨品已在進行中的貨品盤點輪,本輪會略過(不會盤到)",
"Section create item overlap line": "貨品 {{item}} 正由輪次 #{{round}}{{roundName}} 盤點中,本輪不會包含此貨品(區域 {{section}})",
"Not filled": "(未填寫)",
"Warehouse missing stock take section drawer hint": "以下倉庫位置尚未設定盤點區域(ST-xxx),無法納入盤點區域選擇。請點各筆「前往此倉庫設定」補上。",
"Warehouse missing stock take section showing": "顯示前 {{shown}} 筆,共 {{count}} 筆",


Ladataan…
Peruuta
Tallenna