193 lines
9.2 KiB
JavaScript
193 lines
9.2 KiB
JavaScript
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, /<th>操作<\/th>/);
|
|
assert.doesNotMatch(componentSource, /继续处理\s*<\/button>/);
|
|
assert.doesNotMatch(componentSource, /v-if="canContinueStocktake\(row\)"/);
|
|
assert.match(componentSource, /hasBlockingDiffErrors\(previewLines\.value\)/);
|
|
assert.match(componentSource, /盘点清单已导入:发现/);
|
|
|
|
const styleSource = readFileSync(
|
|
fileURLToPath(new URL("../src/styles/main.css", import.meta.url)),
|
|
"utf8"
|
|
);
|
|
const stocktakeDiffModalStart = styleSource.indexOf(".stocktake-diff-modal {");
|
|
const stocktakeDiffModalEnd = styleSource.indexOf("\n}", stocktakeDiffModalStart);
|
|
assert.notEqual(stocktakeDiffModalStart, -1);
|
|
assert.notEqual(stocktakeDiffModalEnd, -1);
|
|
const stocktakeDiffModalBlock = styleSource.slice(stocktakeDiffModalStart, stocktakeDiffModalEnd + 2);
|
|
|
|
assert.match(styleSource, /\.stocktake-diff-modal \.stocktake-diff-table-v2[\s\S]*margin: 14px 18px 0;/);
|
|
assert.match(styleSource, /\.stocktake-diff-modal \.stocktake-footer[\s\S]*min-height: 72px;/);
|
|
assert.match(styleSource, /\.stocktake-diff-modal-mask[\s\S]*box-sizing: border-box;/);
|
|
assert.match(styleSource, /\.stocktake-diff-modal-mask[\s\S]*z-index: 3;/);
|
|
assert.match(stocktakeDiffModalBlock, /height: min\(820px, 100%\);/);
|
|
assert.match(stocktakeDiffModalBlock, /max-height: 100%;/);
|
|
assert.doesNotMatch(stocktakeDiffModalBlock, /calc\(100vh - 64px\)/);
|
|
assert.match(styleSource, /\.stocktake-diff-modal \.stocktake-footer[\s\S]*justify-content: flex-end;/);
|
|
assert.match(styleSource, /\.stocktake-confirm-mask[\s\S]*z-index: 4;/);
|
|
assert.match(styleSource, /\.stocktake-diff-cell,[\s\S]*overflow-wrap: anywhere;/);
|
|
assert.match(styleSource, /\.stocktake-diff-cell span,[\s\S]*line-height: 1\.55;/);
|
|
|
|
const applyLoadedStart = componentSource.indexOf("async function applyLoadedStocktake(stocktake, options = {})");
|
|
const activeStocktakeAssignment = componentSource.indexOf("activeStocktake.value = stocktake;", applyLoadedStart);
|
|
const clearConfirmDialogAssignment = componentSource.indexOf("confirmDialogOpen.value = false;", applyLoadedStart);
|
|
const clearPreviewLinesAssignment = componentSource.indexOf("previewLines.value = [];", applyLoadedStart);
|
|
const clearActiveStocktakeAssignment = componentSource.indexOf("activeStocktake.value = null;", applyLoadedStart);
|
|
const clearPreviewSummaryAssignment = componentSource.indexOf("previewSummary.value = {};", applyLoadedStart);
|
|
const lineFetchAssignment = componentSource.indexOf(
|
|
"fetchResource(`/inventory/stocktakes/${stocktake.id}/lines?warehouse_id=${currentWarehouseId.value}`, null)",
|
|
applyLoadedStart
|
|
);
|
|
const lineFetchGuard = componentSource.indexOf('throw new Error("读取盘库明细失败,请重新打开盘库单");', applyLoadedStart);
|
|
const activePanelAssignment = componentSource.indexOf(
|
|
'activePanel.value = nextPanel === "diff" || nextPanel === "history" ? "import" : nextPanel;',
|
|
applyLoadedStart
|
|
);
|
|
|
|
assert.notEqual(applyLoadedStart, -1);
|
|
assert.notEqual(activeStocktakeAssignment, -1);
|
|
assert.notEqual(clearConfirmDialogAssignment, -1);
|
|
assert.notEqual(clearPreviewLinesAssignment, -1);
|
|
assert.notEqual(clearActiveStocktakeAssignment, -1);
|
|
assert.notEqual(clearPreviewSummaryAssignment, -1);
|
|
assert.notEqual(lineFetchAssignment, -1);
|
|
assert.notEqual(lineFetchGuard, -1);
|
|
assert.notEqual(activePanelAssignment, -1);
|
|
assert.ok(clearConfirmDialogAssignment < lineFetchAssignment);
|
|
assert.ok(clearPreviewLinesAssignment < lineFetchAssignment);
|
|
assert.ok(clearActiveStocktakeAssignment < lineFetchAssignment);
|
|
assert.ok(clearPreviewSummaryAssignment < lineFetchAssignment);
|
|
assert.ok(lineFetchAssignment < activeStocktakeAssignment);
|
|
assert.ok(lineFetchAssignment < activePanelAssignment);
|
|
|
|
console.log("stocktake workflow tests passed");
|