import assert from "node:assert/strict"; import { readFileSync } from "node:fs"; import { fileURLToPath } from "node:url"; import { canContinueStocktake, diffTone, filterStocktakesForWarehouse, findActiveStocktakeForWarehouse, findOnlyActiveStocktake, hasBlockingDiffErrors, isEmptyStocktakeRead, nextStocktakePanel, splitDiffLines, stocktakeBelongsToWarehouse, stocktakeSummaryLineCount, warehouseStocktakeStatus } from "../src/utils/stocktakeWorkflow.js"; const emptyExported = { id: 10, status: "EXPORTED", summary: { match_count: 0, gain_count: 0, loss_count: 0, new_lot_count: 0, missing_count: 0, error_count: 0 }, warehouses: [{ warehouse_id: 4, warehouse_name: "辅料库" }] }; const importedWithDiff = { id: 11, status: "IMPORTED", summary: { match_count: 2, gain_count: 1, loss_count: 1, new_lot_count: 0, missing_count: 0, error_count: 0 }, warehouses: [{ warehouse_id: 1, warehouse_name: "原材料库" }] }; const lockedRaw = { id: 12, status: "LOCKED", stocktake_no: "PK-20260530-012", warehouses: [{ warehouse_id: 1, warehouse_name: "原材料库" }] }; assert.equal(stocktakeSummaryLineCount(emptyExported.summary), 0); assert.equal(isEmptyStocktakeRead(emptyExported), true); assert.equal(isEmptyStocktakeRead(importedWithDiff), false); assert.equal(isEmptyStocktakeRead({ status: "EXPORTED" }), false); assert.equal(nextStocktakePanel(null), "overview"); assert.equal(nextStocktakePanel({ status: "LOCKED" }), "export"); assert.equal(nextStocktakePanel({ status: "EXPORTED" }), "import"); assert.equal(nextStocktakePanel(emptyExported), "diff"); assert.equal(nextStocktakePanel({ ...emptyExported, summary: { match_count: 1 } }), "import"); assert.equal(nextStocktakePanel(importedWithDiff), "diff"); assert.equal(nextStocktakePanel({ status: "CONFIRMED" }), "history"); assert.equal(canContinueStocktake({ status: "LOCKED" }), true); assert.equal(canContinueStocktake({ status: "EXPORTED" }), true); assert.equal(canContinueStocktake({ status: "IMPORTED" }), true); assert.equal(canContinueStocktake({ status: " imported " }), true); assert.equal(canContinueStocktake({ status: "CONFIRMED" }), false); assert.equal(findActiveStocktakeForWarehouse([importedWithDiff, lockedRaw], 1).id, 11); assert.equal(findActiveStocktakeForWarehouse([emptyExported], 4).id, 10); assert.equal(findActiveStocktakeForWarehouse([emptyExported], 999), null); assert.equal(findActiveStocktakeForWarehouse([{ status: "LOCKED", warehouses: null }], 1), null); assert.equal(stocktakeBelongsToWarehouse(lockedRaw, 1), true); assert.equal(stocktakeBelongsToWarehouse(lockedRaw, 4), false); assert.deepEqual(filterStocktakesForWarehouse([lockedRaw, emptyExported], 1).map((row) => row.id), [12]); assert.equal(findOnlyActiveStocktake([lockedRaw]).id, 12); assert.equal(findOnlyActiveStocktake([lockedRaw, importedWithDiff]), null); assert.equal(findOnlyActiveStocktake([{ status: "CONFIRMED" }]), null); const lines = [ { id: 1, diff_type: "MATCH", row_status: "READY" }, { id: 2, diff_type: "LOSS", row_status: "READY" }, { id: 3, diff_type: "GAIN", row_status: "READY" }, { id: 4, diff_type: "MATCH", row_status: "ERROR" } ]; assert.deepEqual(splitDiffLines(lines, "ALL").map((line) => line.id), [4, 2, 3, 1]); assert.deepEqual(splitDiffLines(lines, "DIFF").map((line) => line.id), [4, 2, 3]); assert.deepEqual(splitDiffLines(lines, "ERROR").map((line) => line.id), [4]); assert.deepEqual(lines.map((line) => line.id), [1, 2, 3, 4]); assert.equal(hasBlockingDiffErrors(lines), true); assert.equal(diffTone({ diff_type: "GAIN", row_status: "READY" }), "gain"); assert.equal(diffTone({ diff_type: "LOSS", row_status: "READY" }), "loss"); assert.equal(diffTone({ diff_type: "NEW_LOT", row_status: "READY" }), "new"); assert.equal(diffTone({ diff_type: "MISSING", row_status: "READY" }), "missing"); assert.equal(diffTone({ diff_type: "MATCH", row_status: "ERROR" }), "error"); assert.equal(diffTone({ diff_type: "MATCH", row_status: "READY" }), "match"); const warehouseStatus = warehouseStocktakeStatus( { id: 1, warehouse_name: "原材料库", warehouse_type: "RAW" }, [lockedRaw] ); assert.equal(warehouseStatus.locked, true); assert.equal(warehouseStatus.activeStocktake.stocktake_no, "PK-20260530-012"); assert.equal(warehouseStatus.label, "盘库中"); const componentSource = readFileSync( fileURLToPath(new URL("../src/components/StocktakeDialog.vue", import.meta.url)), "utf8" ); assert.match(componentSource, /async function applyLoadedStocktake\(stocktake, options = \{\}\)/); assert.match(componentSource, /await applyLoadedStocktake\(stocktake, options\);/); assert.match(componentSource, /await applyLoadedStocktake\(exportedStocktake, \{ silent: true \}\);/); assert.doesNotMatch(componentSource, /findOnlyActiveStocktake\(historyRows\.value\)/); assert.match(componentSource, /findActiveStocktakeForWarehouse\(historyRows\.value, currentWarehouseId\.value\)/); assert.match(componentSource, /\/inventory\/stocktakes\?warehouse_id=\$\{currentWarehouseId\.value\}/); assert.match(componentSource, /import PaginationBar from "\.\/PaginationBar\.vue";/); assert.match(componentSource, /import \{ usePagination \} from "\.\.\/utils\/pagination";/); assert.match(componentSource, /usePagination\(currentWarehouseHistoryRows, 6\)/); assert.match(componentSource, /class="stocktake-main"/); assert.match(componentSource, /class="stocktake-flow-column"/); assert.match(componentSource, /:page-size-options="\[6\]"/); assert.match(componentSource, /v-for="row in paginatedHistoryRows"/); assert.doesNotMatch(componentSource, /v-for="row in currentWarehouseHistoryRows"/); assert.doesNotMatch(componentSource, /