chore: 保存盘点开发前改动

This commit is contained in:
zgw
2026-06-25 17:42:39 +08:00
parent 1af5b3cbd3
commit 4b8bc03343
114 changed files with 10005 additions and 1392 deletions

View File

@@ -0,0 +1,92 @@
const fs = require('fs')
const path = require('path')
const root = path.resolve(__dirname, '..')
function read(file) {
return fs.readFileSync(path.join(root, file), 'utf8')
}
function assertMatch(name, content, pattern) {
if (!pattern.test(content)) {
throw new Error(name)
}
}
const barcodeData = read('ruoyi-ui/src/views/bas/barcodeData/index.vue')
assertMatch(
'条码打印必须规范化历史条码类型值',
barcodeData,
/normalizeBarcodeType\s*\([\s\S]*['"]1['"]\s*:\s*['"]CODE128['"]/
)
assertMatch(
'条码打印必须通过安全数值方法处理宽度和高度',
barcodeData,
/getBarcodeOptionNumber\s*\(/
)
assertMatch(
'条码打印宽度必须使用安全数值方法',
barcodeData,
/const lineWidth\s*=\s*this\.getBarcodeOptionNumber/
)
assertMatch(
'条码打印失败时必须显示明确错误信息',
barcodeData,
/生成条码打印内容失败/
)
const inStockinHistory = read('ruoyi-ui/src/views/search/inStockinHistory/index.vue')
assertMatch(
'入库历史单据查询必须支持点击主单行加载明细',
inStockinHistory,
/@row-click="handleRowClick"[\s\S]*highlight-current-row/
)
assertMatch(
'入库历史单据查询必须调用入库子单明细接口',
inStockinHistory,
/import\s+\{\s*listStockin\s*,\s*listStockinDetail\s*\}/
)
assertMatch(
'入库历史单据明细必须传递父单状态以查询完成明细表',
inStockinHistory,
/this\.detailQueryParams\.status\s*=\s*row\.status/
)
assertMatch(
'入库历史单据明细必须有独立分页查询参数',
inStockinHistory,
/detailQueryParams:\s*\{[\s\S]*stockinId:\s*null/
)
assertMatch(
'入库历史单据明细必须调用 listStockinDetail',
inStockinHistory,
/listStockinDetail\(this\.detailQueryParams\)/
)
const inJobFlow = read('ruoyi-ui/src/views/search/inJobFlow/index.vue')
assertMatch(
'入库流水查询必须支持点击主作业行加载明细',
inJobFlow,
/@row-click="handleRowClick"[\s\S]*highlight-current-row/
)
assertMatch(
'入库流水查询必须调用子作业明细接口',
inJobFlow,
/import\s+\{\s*listJobdetail\s*\}/
)
assertMatch(
'入库流水查询明细必须传递父任务状态以查询完成明细表',
inJobFlow,
/this\.detailQueryParams\.status\s*=\s*row\.status/
)
assertMatch(
'入库流水查询明细必须有独立分页查询参数',
inJobFlow,
/detailQueryParams:\s*\{[\s\S]*jobId:\s*null/
)
assertMatch(
'入库流水查询明细必须调用 listJobdetail',
inJobFlow,
/listJobdetail\(this\.detailQueryParams\)/
)
console.log('History query and barcode print checks passed')

View File

@@ -0,0 +1,119 @@
const fs = require('fs')
const path = require('path')
const root = path.resolve(__dirname, '..')
function read(file) {
return fs.readFileSync(path.join(root, file), 'utf8')
}
function assertMatch(name, content, pattern) {
if (!pattern.test(content)) {
throw new Error(name)
}
}
function assertOrder(name, content, markers) {
let cursor = -1
for (const marker of markers) {
const index = content.indexOf(marker)
if (index === -1 || index <= cursor) {
throw new Error(name)
}
cursor = index
}
}
const inJob = read('ruoyi-ui/src/views/in/job/index.vue')
assertMatch(
'入库作业页面必须加载入库任务类型字典',
inJob,
/dicts:\s*\[[^\]]*['"]in_job_type['"][^\]]*\]/
)
assertMatch(
'任务状态列必须使用任务状态字典渲染',
inJob,
/label="任务状态"[\s\S]*dict\.type\.mydo_job_status[\s\S]*scope\.row\.status/
)
assertMatch(
'任务类型列必须使用入库任务类型字典渲染',
inJob,
/label="任务类型"[\s\S]*dict\.type\.in_job_type[\s\S]*scope\.row\.inOrOut/
)
assertMatch(
'手动作废和手动完成按钮必须按选中作业终态统一禁用',
inJob,
/:disabled="!canOperateSelectedJob"[\s\S]*:disabled="!canOperateSelectedJob"/
)
assertMatch(
'终态状态必须包含已完成和已作废',
inJob,
/return\s+\['4',\s*'5'\]\.includes\(String\(status\)\)/
)
const stationPoint = read('ruoyi-ui/src/views/bas/stationpoint/index.vue')
assertMatch(
'点位配置弹窗点位类型下拉必须与其他控件等宽',
stationPoint,
/v-model="form\.pointType"[^>]*style="width:\s*100%"/
)
const bindTray = read('pda/pages/warehouse/inbound/bindTray.vue')
assertOrder(
'PDA 入库点位选择必须放在托盘号下面',
bindTray,
['<text class="form-label">仓库</text>', '<text class="form-label">托盘号</text>', '<text class="form-label">入库点位</text>']
)
assertMatch(
'PDA 必须在仓库和托盘号不完整时提示先选择仓库并扫描托盘号',
bindTray,
/请先选择仓库和扫描托盘号/
)
assertMatch(
'PDA 仓库和托盘号完整后必须提示扫描入库点位',
bindTray,
/请扫描入库点位/
)
assertMatch(
'PDA 绑盘并生成作业成功后必须清空托盘号和已绑定物料',
bindTray,
/resetBindPayload\(\)[\s\S]*this\.trayCode\s*=\s*''[\s\S]*this\.items\s*=\s*\[\]/
)
const pdaService = read('ruoyi-admin/src/main/java/com/ruoyi/pda/service/impl/PdaServiceImpl.java')
assertMatch(
'入库作业生成后必须兜底写入初始状态 1 和执行阶段 0',
pdaService,
/setStatus\("1"\)[\s\S]*setExecutionPhase\("0"\)/
)
assertMatch(
'入库 WCS 下发成功后必须写入状态 3 和执行阶段 1',
pdaService,
/setStatus\("3"\)[\s\S]*setExecutionPhase\("1"\)/
)
assertMatch(
'入库作业生成后必须补写仓库名称',
pdaService,
/selectBasWarehouseByWarehouseId[\s\S]*setWarehouseName/
)
const mydoJobMapper = read('ruoyi-admin/src/main/resources/mapper/in/MydoJobMapper.xml')
assertMatch(
'作业列表查询必须在 warehouse_name 为空时按 warehouse_id 兜底显示仓库名称',
mydoJobMapper,
/isnull\(nullif\(warehouse_name,\s*''\),\s*\(select top 1 bw\.warehouse_name/
)
const mydoJobService = read('ruoyi-admin/src/main/java/com/ruoyi/in/job/service/impl/MydoJobServiceImpl.java')
assertMatch(
'共用 WCS 推送成功后必须写入状态 3 和执行阶段 1',
mydoJobService,
/pushOutJobToWcs[\s\S]*setStatus\("3"\)[\s\S]*setExecutionPhase\("1"\)/
)
const sql = read('sql/20260624_update_job_dicts.sql')
assertMatch('SQL 脚本必须包含正执行状态字典', sql, /正执行/)
assertMatch('SQL 脚本必须包含生成作业执行阶段字典', sql, /生成作业/)
assertMatch('SQL 脚本必须包含下发到WCS执行阶段字典', sql, /下发到WCS/)
console.log('Inbound job fix checks passed')

View File

@@ -0,0 +1,208 @@
const fs = require('fs');
const path = require('path');
const root = path.resolve(__dirname, '..');
function read(relativePath) {
return fs.readFileSync(path.join(root, relativePath), 'utf8');
}
function assertContains(file, needle, message) {
const content = read(file);
if (!content.includes(needle)) {
throw new Error(`${message}\nMissing in ${file}: ${needle}`);
}
}
function assertMatches(file, pattern, message) {
const content = read(file);
if (!pattern.test(content)) {
throw new Error(`${message}\nPattern not found in ${file}: ${pattern}`);
}
}
assertMatches(
'ruoyi-admin/src/main/java/com/ruoyi/bas/stock/service/impl/BasStockServiceImpl.java',
/"KC"\s*\+\s*STOCK_ID_FORMATTER\.format/,
'Manual stock creation must generate KC + millisecond timestamp IDs.'
);
assertContains(
'sql/bas_stock_master_detail.sql',
"in_type",
'Inbound completion stock generation must persist the real inbound type.'
);
assertMatches(
'sql/bas_stock_master_detail.sql',
/'KC'\s*\+\s*FORMAT\(@now,\s*'yyyyMMddHHmmssfff'\)/,
'Inbound completion procedure must generate KC + millisecond timestamp stock IDs.'
);
assertContains(
'sql/bas_stock_master_detail.sql',
"@stock_id + N'-' + CONVERT",
'Inbound completion stock detail IDs must use stock ID plus detail sequence.'
);
assertContains(
'ruoyi-admin/src/main/resources/mapper/bas/BasStockDetailMapper.xml',
'bsd.product_id, bsd.product_name',
'Stock detail queries must keep material code and material name in separate columns.'
);
assertContains(
'ruoyi-ui/src/views/bas/stock/index.vue',
'label="物料编码" align="center" prop="productId"',
'Stock detail UI must render material code from productId.'
);
assertContains(
'ruoyi-ui/src/views/bas/stock/index.vue',
'label="物料名称" align="center" prop="productName"',
'Stock detail UI must render material name from productName.'
);
const outJob = read('ruoyi-ui/src/views/out/job/index.vue');
if (!outJob.includes('prop="status"') || !outJob.includes('dict.type.mydo_job_status')) {
throw new Error('Outbound job page must filter/render task status with mydo_job_status.');
}
if (!outJob.includes('dict.type.out_job_type') || !outJob.includes("dicts: ['mydo_job_execution_phase', 'mydo_job_status', 'out_job_type']")) {
throw new Error('Outbound job page must render task type with out_job_type.');
}
if (!outJob.includes("parseTime(scope.row.createTime")) {
throw new Error('Outbound job page must render createTime from createTime, not createDate.');
}
if (!outJob.includes('this.detailQueryParams.status = row.status')) {
throw new Error('Finished outbound jobs must pass parent status when loading child job details.');
}
assertContains(
'ruoyi-ui/src/views/out/stockout/index.vue',
'selectedExitId',
'Outbound allocation UI must require an exit selection.'
);
assertContains(
'ruoyi-ui/src/views/out/stockout/index.vue',
'listStationPoint',
'Outbound allocation UI must load warehouse exit/entry-exit station points.'
);
assertContains(
'ruoyi-ui/src/views/out/stockout/index.vue',
'this.detailQueryParams.status = row.status',
'Finished outbound bills must pass the parent status when loading child details.'
);
assertContains(
'ruoyi-admin/src/main/java/com/ruoyi/out/stockout/domain/OutStockoutManualAssignDTO.java',
'private String exitId;',
'Manual outbound allocation DTO must carry the selected exit.'
);
assertContains(
'ruoyi-admin/src/main/resources/mapper/out/OutStockoutMapper.xml',
'#{v_exit_id,mode=IN,jdbcType=VARCHAR}',
'Outbound allocation mapper must pass the selected exit to procedures.'
);
assertContains(
'sql/out_stockout_flow.sql',
'@v_exit_id varchar(80)',
'Outbound allocation procedures must accept a selected exit.'
);
assertContains(
'sql/out_stockout_flow.sql',
'@exit_id = @v_exit_id',
'Outbound allocation procedure must store the selected exit on generated jobs.'
);
assertContains(
'ruoyi-admin/src/main/java/com/ruoyi/in/job/service/impl/MydoJobServiceImpl.java',
'requestWcsCancelBeforeManualCancel',
'Manual job cancellation must reserve a WCS cancellation check.'
);
assertContains(
'pda/pages/warehouse/outbound/confirm.vue',
'扫描托盘条码',
'PDA outbound confirmation must scan tray barcodes.'
);
assertContains(
'pda/pages/warehouse/outbound/confirm.vue',
'materials',
'PDA outbound confirmation must render tray material details.'
);
assertContains(
'pda/pages/warehouse/outbound/confirm.vue',
'v-model="item.actualQuantity"',
'PDA outbound confirmation must capture actual outbound quantity per material.'
);
assertContains(
'pda/pages/warehouse/outbound/confirm.vue',
'materials: this.outboundInfo.materials.map',
'PDA outbound confirmation must submit actual quantities per material.'
);
assertContains(
'ruoyi-admin/src/main/java/com/ruoyi/pda/service/impl/PdaServiceImpl.java',
'createReturnInboundJobIfNeeded',
'PDA outbound confirmation must create return inbound jobs for residual tray materials.'
);
assertContains(
'ruoyi-admin/src/main/java/com/ruoyi/pda/service/impl/PdaServiceImpl.java',
'jobIdService.getId("HL")',
'Residual tray return jobs must use the 回流作业 job-id prefix.'
);
assertContains(
'ruoyi-admin/src/main/java/com/ruoyi/pda/service/impl/PdaServiceImpl.java',
'taskNoService.getTaskNo("5")',
'Residual tray return jobs must use the 回流入库 task-number type.'
);
assertContains(
'sql/20260624_update_job_dicts.sql',
"N'回流入库'",
'Inbound job dictionaries must render 回流入库 return jobs.'
);
assertContains(
'ruoyi-admin/src/main/resources/mapper/pda/PdaMapper.xml',
'selectResidualTrayMaterials',
'PDA mapper must query residual tray materials after outbound confirmation.'
);
assertContains(
'ruoyi-ui/src/views/out/locationOutbound/index.vue',
'active-text="所有货位"',
'Specified-location outbound filter switch must be labelled 所有货位.'
);
assertContains(
'ruoyi-ui/src/views/out/locationOutbound/index.vue',
'listWarehouse',
'Specified-location outbound page must load warehouse dropdown options.'
);
assertContains(
'ruoyi-ui/src/views/out/locationOutbound/index.vue',
'v-model="queryParams.warehouseId"',
'Specified-location outbound page must filter by warehouse id dropdown.'
);
assertContains(
'ruoyi-ui/src/views/out/locationOutbound/index.vue',
'@selection-change="handleSelectionChange"',
'Specified-location outbound table must support selecting locations.'
);
assertContains(
'ruoyi-ui/src/views/out/locationOutbound/index.vue',
'批量生成作业',
'Specified-location outbound page must provide a batch generation action.'
);
assertContains(
'ruoyi-ui/src/views/out/locationOutbound/index.vue',
'Promise.all(rows.map',
'Specified-location outbound page must generate outbound jobs for selected locations.'
);
const locationOutbound = read('ruoyi-ui/src/views/out/locationOutbound/index.vue');
if (locationOutbound.includes('type="expand"')) {
throw new Error('Specified-location outbound page must use a lower child-detail table instead of expand rows.');
}
if (!locationOutbound.includes('@row-click="handleRowClick"') ||
!locationOutbound.includes('currentLocation') ||
!locationOutbound.includes('locationDetailList')) {
throw new Error('Specified-location outbound page must click a parent location row and show child details below.');
}
if (!locationOutbound.includes('cargoSpaceId: row.cargoSpaceId')) {
throw new Error('Specified-location outbound generation must send cargoSpaceId so empty locations can generate jobs.');
}
if (!locationOutbound.includes("['1', '6'].includes(String(row.cargoSpaceStatus))")) {
throw new Error('Specified-location outbound must allow empty and no-stock abnormal locations to generate jobs.');
}
console.log('Outbound and stock regression checks passed.');