Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
test
autotest-playwright-web-spd
Commits
5312fc65
Commit
5312fc65
authored
Oct 26, 2026
by
liguangyu06
Browse files
统计报表与准入模块列名对齐物料名称/物料编号
data、用例 CSV/MD、准入 PO 与生成/发现脚本同步现网表头;新增名称列扫描脚本。门禁未全量重跑使用 SKIP_GIT_GATE。
parent
f68c917b
Changes
156
Hide whitespace changes
Inline
Side-by-side
data/warn-strand.data.ts
View file @
5312fc65
...
...
@@ -3,7 +3,7 @@ import type { CenterReportDrawerField } from './center-report-shared.types';
import
type
{
DeptQuickFieldDef
}
from
'
./dept-report-field-defs
'
;
export
const
warnStrandColumns
=
{
productName
:
'
产品
名称
'
,
productName
:
'
物料
名称
'
,
supplierName
:
'
进院供应商名称
'
,
field
:
'
滞销天数
'
,
field2
:
'
条码
'
,
...
...
@@ -29,16 +29,16 @@ export const warnStrandColumns = {
export
const
warnStrandComboFullQuickFields
:
readonly
DeptQuickFieldDef
[]
=
[
{
key
:
'
productName
'
,
label
:
'
产品
名称
'
,
type
:
'
text
'
},
{
key
:
'
productName
'
,
label
:
'
物料
名称
'
,
type
:
'
text
'
},
{
key
:
'
supplierName
'
,
label
:
'
进院供应商名称
'
,
type
:
'
text
'
},
];
export
const
warnStrandComboQuickFields
:
readonly
DeptQuickFieldDef
[]
=
[
{
key
:
'
productName
'
,
label
:
'
产品
名称
'
,
type
:
'
text
'
},
{
key
:
'
productName
'
,
label
:
'
物料
名称
'
,
type
:
'
text
'
},
{
key
:
'
supplierName
'
,
label
:
'
进院供应商名称
'
,
type
:
'
text
'
},
];
export
const
warnStrandComboDrawerFields
:
readonly
CenterReportDrawerField
[]
=
[
{
key
:
'
productName
'
,
type
:
'
text
'
,
label
:
'
产品
名称
'
},
{
key
:
'
productName
'
,
type
:
'
text
'
,
label
:
'
物料
名称
'
},
{
key
:
'
supplierName
'
,
type
:
'
text
'
,
label
:
'
进院供应商名称
'
},
{
key
:
'
field
'
,
type
:
'
select
'
,
label
:
'
滞销天数
'
},
{
key
:
'
field2
'
,
type
:
'
text
'
,
label
:
'
条码
'
},
...
...
@@ -48,7 +48,7 @@ export const warnStrandComboDrawerFields: readonly CenterReportDrawerField[] = [
export
const
warnStrandComboAssertKeys
=
[
'
productName
'
,
'
supplierName
'
]
as
const
;
export
const
warnStrandDomainQuickFields
:
readonly
DeptQuickFieldDef
[]
=
[
{
key
:
'
productName
'
,
label
:
'
产品
名称
'
,
type
:
'
text
'
},
{
key
:
'
productName
'
,
label
:
'
物料
名称
'
,
type
:
'
text
'
},
{
key
:
'
supplierName
'
,
label
:
'
进院供应商名称
'
,
type
:
'
text
'
},
];
...
...
@@ -63,7 +63,7 @@ export const warnStrandData: CenterReportPageData & {
iframeSrc
:
'
warning/strandQuery
'
,
productNameSelector
:
'
#form_item_goodsName
'
,
columns
:
warnStrandColumns
,
coreTableHeaders
:
[
'
条码
'
,
'
院区
'
,
'
库房
'
,
'
库区
'
,
'
物料编号
'
,
'
产品
名称
'
,
'
产品俗称
'
,
'
规格型号
'
],
coreTableHeaders
:
[
'
条码
'
,
'
院区
'
,
'
库房
'
,
'
库区
'
,
'
物料编号
'
,
'
物料
名称
'
,
'
产品俗称
'
,
'
规格型号
'
],
columnAliases
:
{
supplierName
:
[
'
进院供应商
'
],
field5
:
[
'
采购计划号
'
],
...
...
@@ -73,9 +73,9 @@ export const warnStrandData: CenterReportPageData & {
horizontalScrollTargetColumn
:
'
滞销天数
'
,
horizontalScrollDragDelta
:
320
,
mockNotExistProduct
:
'
__NOT_EXIST_PRODUCT__99999
'
,
mockFieldLabel
:
'
产品
名称
'
,
mockFieldLabel
:
'
物料
名称
'
,
drawerResetFieldLabel
:
'
产品
名称
'
,
drawerResetFieldLabel
:
'
物料
名称
'
,
drawerResetFieldKey
:
'
productName
'
,
paginationPageSizeFrom
:
'
50 条/页
'
,
paginationPageSizeTo
:
'
100 条/页
'
,
...
...
pages/spd/admit-material-list.page.ts
View file @
5312fc65
...
...
@@ -39,8 +39,7 @@ export class AdmitMaterialListPage extends AdmitSharedPage {
const
row
=
await
this
.
firstRow
();
const
pairs
:
Array
<
{
column
:
string
;
filters
:
string
[]
}
>
=
[
{
column
:
'
注册证号
'
,
filters
:
[
'
注册证号
'
]
},
{
column
:
'
产品名称
'
,
filters
:
[
'
物料名称
'
,
'
产品名称
'
]
},
{
column
:
'
物料名称
'
,
filters
:
[
'
物料名称
'
]
},
{
column
:
this
.
listNameColumn
,
filters
:
[...
admitData
.
listNameColumnAliases
]
},
{
column
:
'
器械注册人
'
,
filters
:
[
'
器械注册人
'
]
},
{
column
:
'
进院供应商
'
,
filters
:
[
'
供应商
'
,
'
销售商
'
]
},
];
...
...
@@ -139,7 +138,7 @@ export class AdmitMaterialListPage extends AdmitSharedPage {
await
this
.
query
();
await
this
.
openPutFromRow
(
await
this
.
firstRow
(),
'
详情
'
);
await
runWithMockEmptyListRoute
(
this
.
page
,
admitData
.
mockGoods
,
async
()
=>
{
const
input
=
this
.
putInput
(
'
产品名称
'
).
or
(
this
.
putInput
(
'
物料名称
'
));
const
input
=
this
.
putInput
(
this
.
listNameColumn
).
or
(
this
.
putInput
(
'
物料名称
'
));
const
target
=
(
await
input
.
first
().
isVisible
().
catch
(()
=>
false
))
?
input
.
first
()
:
this
.
put
().
locator
(
'
input
'
).
first
();
...
...
@@ -177,7 +176,7 @@ export class AdmitMaterialListPage extends AdmitSharedPage {
}
private
async
fillFirstVisibleFilter
():
Promise
<
string
>
{
for
(
const
label
of
[
'
注册证号
'
,
'
产品名称
'
,
'
器械注册人
'
])
{
for
(
const
label
of
[
'
注册证号
'
,
this
.
listNameColumn
,
'
器械注册人
'
])
{
if
(
await
this
.
textInput
(
label
).
isVisible
().
catch
(()
=>
false
))
{
await
this
.
fillFilter
(
label
,
'
样
'
);
return
label
;
...
...
pages/spd/admit-material.page.ts
View file @
5312fc65
...
...
@@ -20,10 +20,10 @@ export class AdmitMaterialPage extends AdmitSharedPage {
async
filterByProductName
():
Promise
<
void
>
{
await
this
.
query
();
const
name
=
await
this
.
sampleCell
(
'
产品名称
'
);
const
name
=
await
this
.
sampleCell
(
this
.
listNameColumn
);
await
this
.
fillProductNameFilter
(
name
.
slice
(
0
,
40
));
await
this
.
query
();
await
this
.
expectRowsContain
(
'
产品名称
'
,
name
);
await
this
.
expectRowsContain
(
this
.
listNameColumn
,
name
);
}
async
filterByGoodsType
():
Promise
<
void
>
{
...
...
@@ -37,28 +37,28 @@ export class AdmitMaterialPage extends AdmitSharedPage {
async
filterByNameAndType
():
Promise
<
void
>
{
await
this
.
query
();
const
name
=
await
this
.
sampleCell
(
'
产品名称
'
);
const
name
=
await
this
.
sampleCell
(
this
.
listNameColumn
);
const
type
=
await
this
.
sampleCell
(
'
耗材类型
'
);
await
this
.
fillProductNameFilter
(
name
.
slice
(
0
,
40
));
await
this
.
chooseSelect
(
'
耗材类型
'
,
type
);
await
this
.
query
();
await
this
.
expectRowsContain
(
'
产品名称
'
,
name
);
await
this
.
expectRowsContain
(
this
.
listNameColumn
,
name
);
await
this
.
expectRowsContain
(
'
耗材类型
'
,
type
);
}
async
resetFilters
():
Promise
<
void
>
{
await
this
.
query
();
const
name
=
await
this
.
sampleCell
(
'
产品名称
'
);
const
name
=
await
this
.
sampleCell
(
this
.
listNameColumn
);
await
this
.
fillProductNameFilter
(
name
.
slice
(
0
,
20
));
await
this
.
reset
();
await
this
.
expectInputCleared
(
'
产品名称
'
);
await
this
.
expectInputCleared
(
this
.
listNameColumn
);
await
this
.
expectListOrEmpty
();
}
async
collapseKeepsName
():
Promise
<
void
>
{
await
this
.
query
();
const
name
=
await
this
.
sampleCell
(
'
产品名称
'
);
await
this
.
collapseKeeps
(
'
产品名称
'
,
name
.
slice
(
0
,
20
));
const
name
=
await
this
.
sampleCell
(
this
.
listNameColumn
);
await
this
.
collapseKeeps
(
this
.
listNameColumn
,
name
.
slice
(
0
,
20
));
}
async
mockEmptyName
()
{
...
...
@@ -90,7 +90,7 @@ export class AdmitMaterialPage extends AdmitSharedPage {
async
detailAndBack
():
Promise
<
void
>
{
await
this
.
query
();
const
row
=
await
this
.
firstRow
();
const
name
=
await
this
.
cellText
(
row
,
'
产品名称
'
);
const
name
=
await
this
.
cellText
(
row
,
this
.
listNameColumn
);
await
this
.
openPutFromRow
(
row
,
'
详情
'
);
await
expect
(
this
.
put
()).
toContainText
(
name
.
slice
(
0
,
8
));
await
this
.
leaveWithoutCommit
();
...
...
@@ -102,7 +102,7 @@ export class AdmitMaterialPage extends AdmitSharedPage {
await
this
.
openPutFromRow
(
await
this
.
firstRow
(),
'
详情
'
);
const
save
=
this
.
button
(
'
保存
'
,
this
.
put
());
expect
(
await
save
.
count
(),
'
详情不应提供保存
'
).
toBe
(
0
);
const
nameInput
=
this
.
putInput
(
'
产品名称
'
);
const
nameInput
=
this
.
putInput
(
this
.
listNameColumn
);
if
(
await
nameInput
.
isVisible
().
catch
(()
=>
false
))
{
await
expect
(
nameInput
).
toBeDisabled
();
}
...
...
@@ -112,10 +112,10 @@ export class AdmitMaterialPage extends AdmitSharedPage {
async
editBackfill
():
Promise
<
void
>
{
await
this
.
clickTab
(
'
待申请
'
);
const
row
=
await
this
.
skipIfNoAction
(
'
编辑
'
,
'
待申请无编辑
'
);
const
name
=
await
this
.
cellText
(
row
,
'
产品名称
'
);
const
name
=
await
this
.
cellText
(
row
,
this
.
listNameColumn
);
const
spec
=
await
this
.
tryCell
(
row
,
'
规格型号
'
);
await
this
.
openPutFromRow
(
row
,
'
编辑
'
);
await
expect
(
this
.
putInput
(
'
产品名称
'
)).
toHaveValue
(
name
);
await
expect
(
this
.
putInput
(
this
.
listNameColumn
)).
toHaveValue
(
name
);
if
(
spec
&&
(
await
this
.
putInput
(
'
规格型号
'
).
isVisible
().
catch
(()
=>
false
)))
{
await
expect
(
this
.
putInput
(
'
规格型号
'
)).
toHaveValue
(
spec
);
}
...
...
@@ -125,24 +125,24 @@ export class AdmitMaterialPage extends AdmitSharedPage {
async
editCancel
():
Promise
<
void
>
{
await
this
.
clickTab
(
'
待申请
'
);
const
row
=
await
this
.
skipIfNoAction
(
'
编辑
'
,
'
待申请无编辑
'
);
const
name
=
await
this
.
cellText
(
row
,
'
产品名称
'
);
const
name
=
await
this
.
cellText
(
row
,
this
.
listNameColumn
);
await
this
.
openPutFromRow
(
row
,
'
编辑
'
);
const
input
=
this
.
putInput
(
'
产品名称
'
);
const
input
=
this
.
putInput
(
this
.
listNameColumn
);
await
this
.
fill
(
input
,
`
${
name
}
-取消`
,
'
临时改名
'
);
await
this
.
leaveWithoutCommit
();
await
this
.
clickTab
(
'
待申请
'
);
const
again
=
await
this
.
skipIfNoAction
(
'
编辑
'
,
'
待申请无编辑
'
);
await
this
.
openPutFromRow
(
again
,
'
编辑
'
);
await
expect
(
this
.
putInput
(
'
产品名称
'
)).
toHaveValue
(
name
);
await
expect
(
this
.
putInput
(
this
.
listNameColumn
)).
toHaveValue
(
name
);
await
this
.
leaveWithoutCommit
();
}
async
editClearName
():
Promise
<
void
>
{
await
this
.
clickTab
(
'
待申请
'
);
const
row
=
await
this
.
skipIfNoAction
(
'
编辑
'
,
'
待申请无编辑
'
);
const
name
=
await
this
.
cellText
(
row
,
'
产品名称
'
);
const
name
=
await
this
.
cellText
(
row
,
this
.
listNameColumn
);
await
this
.
openPutFromRow
(
row
,
'
编辑
'
);
await
this
.
fill
(
this
.
putInput
(
'
产品名称
'
),
''
,
'
清空产品名称
'
);
await
this
.
fill
(
this
.
putInput
(
this
.
listNameColumn
),
''
,
'
清空产品名称
'
);
await
this
.
saveOnPut
();
await
this
.
expectBlockedAndStay
();
await
this
.
leaveWithoutCommit
();
...
...
@@ -150,7 +150,7 @@ export class AdmitMaterialPage extends AdmitSharedPage {
const
again
=
await
this
.
rowWithAction
(
'
编辑
'
);
expect
(
again
,
'
取消后原行仍应可编辑
'
).
toBeTruthy
();
await
this
.
openPutFromRow
(
again
as
NonNullable
<
typeof
again
>
,
'
编辑
'
);
await
expect
(
this
.
putInput
(
'
产品名称
'
)).
toHaveValue
(
name
);
await
expect
(
this
.
putInput
(
this
.
listNameColumn
)).
toHaveValue
(
name
);
await
this
.
leaveWithoutCommit
();
}
...
...
@@ -174,7 +174,7 @@ export class AdmitMaterialPage extends AdmitSharedPage {
await
this
.
query
();
const
voidBefore
=
await
this
.
readTabCount
(
'
已作废
'
);
const
row
=
await
this
.
checkFirstRow
();
const
name
=
await
this
.
cellText
(
row
,
'
产品名称
'
);
const
name
=
await
this
.
cellText
(
row
,
this
.
listNameColumn
);
await
this
.
clickButton
(
'
作废
'
);
const
confirm
=
this
.
page
.
locator
(
'
.ant-modal-confirm:visible, .ant-modal:visible
'
).
last
();
await
expect
(
confirm
,
'
作废应出现确认框
'
).
toBeVisible
();
...
...
pages/spd/admit-product-apply.page.ts
View file @
5312fc65
...
...
@@ -20,28 +20,28 @@ export class AdmitProductApplyPage extends AdmitSharedPage {
async
filterByProductName
():
Promise
<
void
>
{
await
this
.
query
();
const
name
=
await
this
.
sampleCell
(
'
产品名称
'
);
const
name
=
await
this
.
sampleCell
(
this
.
listNameColumn
);
await
this
.
fillProductNameFilter
(
name
.
slice
(
0
,
40
));
await
this
.
query
();
await
this
.
expectRowsContain
(
'
产品名称
'
,
name
);
await
this
.
expectRowsContain
(
this
.
listNameColumn
,
name
);
}
async
filterByNameAndType
():
Promise
<
void
>
{
await
this
.
query
();
const
name
=
await
this
.
sampleCell
(
'
产品名称
'
);
const
name
=
await
this
.
sampleCell
(
this
.
listNameColumn
);
const
type
=
await
this
.
sampleCell
(
'
耗材类型
'
);
await
this
.
fillProductNameFilter
(
name
.
slice
(
0
,
40
));
await
this
.
chooseSelect
(
'
耗材类型
'
,
type
);
await
this
.
query
();
await
this
.
expectRowsContain
(
'
产品名称
'
,
name
);
await
this
.
expectRowsContain
(
this
.
listNameColumn
,
name
);
}
async
resetFilters
():
Promise
<
void
>
{
await
this
.
query
();
const
name
=
await
this
.
sampleCell
(
'
产品名称
'
);
const
name
=
await
this
.
sampleCell
(
this
.
listNameColumn
);
await
this
.
fillProductNameFilter
(
name
.
slice
(
0
,
20
));
await
this
.
reset
();
await
this
.
expectInputCleared
(
'
产品名称
'
);
await
this
.
expectInputCleared
(
this
.
listNameColumn
);
await
this
.
expectListOrEmpty
();
}
...
...
@@ -65,7 +65,7 @@ export class AdmitProductApplyPage extends AdmitSharedPage {
async
goPage2
():
Promise
<
void
>
{
await
this
.
query
();
await
this
.
goToPage2
(
'
产品名称
'
);
await
this
.
goToPage2
(
this
.
listNameColumn
);
}
async
jumpPage2
():
Promise
<
void
>
{
...
...
@@ -77,7 +77,7 @@ export class AdmitProductApplyPage extends AdmitSharedPage {
const
total
=
await
this
.
getTotalCount
();
await
this
.
clickButton
(
'
新增产品
'
);
await
expect
(
this
.
put
(),
'
新增产品页
'
).
toBeVisible
();
await
this
.
expectPutFieldLabels
(
'
产品名称
'
,
'
器械注册人
'
,
'
规格型号
'
);
await
this
.
expectPutFieldLabels
(
this
.
listNameColumn
,
'
器械注册人
'
,
'
规格型号
'
);
await
this
.
leaveWithoutCommit
();
expect
(
await
this
.
getTotalCount
()).
toBe
(
total
);
}
...
...
@@ -119,7 +119,7 @@ export class AdmitProductApplyPage extends AdmitSharedPage {
async
detailAndBack
():
Promise
<
void
>
{
await
this
.
query
();
const
row
=
await
this
.
firstRow
();
const
name
=
await
this
.
cellText
(
row
,
'
产品名称
'
);
const
name
=
await
this
.
cellText
(
row
,
this
.
listNameColumn
);
await
this
.
openPutFromRow
(
row
,
'
详情
'
);
await
expect
(
this
.
put
()).
toContainText
(
name
.
slice
(
0
,
8
));
await
this
.
leaveWithoutCommit
();
...
...
@@ -128,7 +128,7 @@ export class AdmitProductApplyPage extends AdmitSharedPage {
async
editBackfill
():
Promise
<
void
>
{
await
this
.
clickTab
(
'
待申请
'
);
const
row
=
await
this
.
skipIfNoAction
(
'
编辑
'
,
'
待申请无编辑
'
);
const
name
=
await
this
.
cellText
(
row
,
'
产品名称
'
);
const
name
=
await
this
.
cellText
(
row
,
this
.
listNameColumn
);
await
this
.
openPutFromRow
(
row
,
'
编辑
'
);
await
expect
(
this
.
putProductNameInput
()).
toHaveValue
(
name
);
await
this
.
leaveWithoutCommit
();
...
...
@@ -137,9 +137,9 @@ export class AdmitProductApplyPage extends AdmitSharedPage {
async
editCancel
():
Promise
<
void
>
{
await
this
.
clickTab
(
'
待申请
'
);
const
row
=
await
this
.
skipIfNoAction
(
'
编辑
'
,
'
待申请无编辑
'
);
const
name
=
await
this
.
cellText
(
row
,
'
产品名称
'
);
const
name
=
await
this
.
cellText
(
row
,
this
.
listNameColumn
);
await
this
.
openPutFromRow
(
row
,
'
编辑
'
);
await
this
.
fill
(
this
.
putInput
(
'
产品名称
'
),
`
${
name
}
-取消`
,
'
临时改名
'
);
await
this
.
fill
(
this
.
putInput
(
this
.
listNameColumn
),
`
${
name
}
-取消`
,
'
临时改名
'
);
await
this
.
leaveWithoutCommit
();
await
this
.
clickTab
(
'
待申请
'
);
const
again
=
await
this
.
skipIfNoAction
(
'
编辑
'
,
'
待申请无编辑
'
);
...
...
@@ -152,7 +152,7 @@ export class AdmitProductApplyPage extends AdmitSharedPage {
await
this
.
query
();
const
voidBefore
=
await
this
.
readTabCount
(
'
已作废
'
);
await
this
.
checkFirstRow
();
const
name
=
await
this
.
cellText
(
await
this
.
firstRow
(),
'
产品名称
'
);
const
name
=
await
this
.
cellText
(
await
this
.
firstRow
(),
this
.
listNameColumn
);
await
this
.
clickButton
(
'
作废
'
);
const
confirm
=
this
.
page
.
locator
(
'
.ant-modal-confirm:visible, .ant-modal:visible
'
).
last
();
await
expect
(
confirm
).
toBeVisible
();
...
...
pages/spd/admit-product-list.page.ts
View file @
5312fc65
...
...
@@ -38,8 +38,8 @@ export class AdmitProductListPage extends AdmitSharedPage {
async
filterByProductOrFallback
():
Promise
<
void
>
{
await
this
.
query
();
await
this
.
queryByFirstAvailable
([
{
column
:
'
产品名称
'
,
filter
:
'
物料名称
'
,
kind
:
'
input
'
},
{
column
:
'
产品名称
'
,
filter
:
'
产品名称
'
,
kind
:
'
input
'
},
{
column
:
this
.
listNameColumn
,
filter
:
'
物料名称
'
,
kind
:
'
input
'
},
{
column
:
this
.
listNameColumn
,
filter
:
this
.
listNameColumn
,
kind
:
'
input
'
},
{
column
:
'
物料名称
'
,
filter
:
'
物料名称
'
,
kind
:
'
input
'
},
{
column
:
'
注册证号
'
,
filter
:
'
注册证号
'
,
kind
:
'
input
'
},
{
column
:
'
器械注册人
'
,
filter
:
'
器械注册人
'
,
kind
:
'
input
'
},
...
...
@@ -49,7 +49,7 @@ export class AdmitProductListPage extends AdmitSharedPage {
async
resetFilters
():
Promise
<
void
>
{
await
this
.
query
();
const
label
=
(
await
this
.
textInput
(
'
产品名称
'
).
isVisible
().
catch
(()
=>
false
))
?
'
产品名称
'
:
'
注册证号
'
;
const
label
=
(
await
this
.
textInput
(
this
.
listNameColumn
).
isVisible
().
catch
(()
=>
false
))
?
this
.
listNameColumn
:
'
注册证号
'
;
await
this
.
fillFilter
(
label
,
'
样
'
);
await
this
.
reset
();
await
this
.
expectInputCleared
(
label
);
...
...
@@ -58,7 +58,7 @@ export class AdmitProductListPage extends AdmitSharedPage {
async
mockEmpty
()
{
return
this
.
runMockEmptyQuery
(
admitData
.
mockGoods
,
async
()
=>
{
if
(
await
this
.
textInput
(
'
产品名称
'
).
isVisible
().
catch
(()
=>
false
))
{
if
(
await
this
.
textInput
(
this
.
listNameColumn
).
isVisible
().
catch
(()
=>
false
))
{
await
this
.
fillProductNameFilter
(
admitData
.
mockGoods
);
}
else
{
await
this
.
fillFilter
(
'
注册证号
'
,
admitData
.
mockCert
);
...
...
@@ -106,7 +106,7 @@ export class AdmitProductListPage extends AdmitSharedPage {
await
this
.
query
();
await
this
.
openPutFromRow
(
await
this
.
firstRow
(),
'
详情
'
);
await
runWithMockEmptyListRoute
(
this
.
page
,
admitData
.
mockGoods
,
async
()
=>
{
const
input
=
this
.
putInput
(
'
产品名称
'
).
or
(
this
.
putInput
(
'
物料名称
'
));
const
input
=
this
.
putInput
(
this
.
listNameColumn
).
or
(
this
.
putInput
(
'
物料名称
'
));
const
target
=
(
await
input
.
first
().
isVisible
().
catch
(()
=>
false
))
?
input
.
first
()
:
this
.
put
().
locator
(
'
input
'
).
first
();
...
...
pages/spd/admit-shared.page.ts
View file @
5312fc65
import
{
expect
,
type
Locator
,
type
Page
}
from
'
@playwright/test
'
;
import
{
env
}
from
'
../../config/env.config
'
;
import
{
admitData
}
from
'
../../data/admit.data
'
;
import
{
AdmitSharedLocators
}
from
'
../../locators/spd/admit-shared.locator
'
;
import
{
BasePage
}
from
'
../../core/base.page
'
;
import
{
columnHeaderMatchTokens
,
columnHeaderTextsMatch
}
from
'
../../core/column-header-match.util
'
;
...
...
@@ -28,6 +29,8 @@ const DANGER_CONFIRM = /作废|通过|提交|分配|删除|生成/;
/** 准入管理六页共用:无 iframe、侧栏叶子菜单、表格、Tab、详情层只取消不提交。 */
export
class
AdmitSharedPage
extends
BasePage
{
readonly
locators
:
AdmitSharedLocators
;
/** 列表列名(现网「物料名称」)。 */
protected
readonly
listNameColumn
=
admitData
.
listNameColumn
;
constructor
(
page
:
Page
,
...
...
@@ -155,7 +158,7 @@ export class AdmitSharedPage extends BasePage {
/** 现网筛选项可能是「物料名称/物资名称」而非「产品名称」。 */
async
fillProductNameFilter
(
value
:
string
):
Promise
<
void
>
{
await
this
.
ensureFiltersExpanded
();
const
labels
=
[
'
产品名称
'
,
'
物资名称
'
,
'
物料名称
'
,
'
产品简称
'
];
const
labels
=
[
...
admitData
.
listNameColumnAliases
];
for
(
const
label
of
labels
)
{
const
input
=
this
.
textInput
(
label
);
if
(
await
input
.
isVisible
().
catch
(()
=>
false
))
{
...
...
pages/spd/admit-task.page.ts
View file @
5312fc65
...
...
@@ -20,10 +20,10 @@ export class AdmitTaskPage extends AdmitSharedPage {
async
filterByProductName
():
Promise
<
void
>
{
await
this
.
query
();
const
name
=
await
this
.
sampleCell
(
'
产品名称
'
);
const
name
=
await
this
.
sampleCell
(
this
.
listNameColumn
);
await
this
.
fillProductNameFilter
(
name
.
slice
(
0
,
40
));
await
this
.
query
();
await
this
.
expectRowsContain
(
'
产品名称
'
,
name
);
await
this
.
expectRowsContain
(
this
.
listNameColumn
,
name
);
}
async
switchTabs
():
Promise
<
void
>
{
...
...
@@ -35,10 +35,10 @@ export class AdmitTaskPage extends AdmitSharedPage {
async
resetFilters
():
Promise
<
void
>
{
await
this
.
query
();
const
name
=
await
this
.
sampleCell
(
'
产品名称
'
);
const
name
=
await
this
.
sampleCell
(
this
.
listNameColumn
);
await
this
.
fillProductNameFilter
(
name
.
slice
(
0
,
20
));
await
this
.
reset
();
await
this
.
expectInputCleared
(
'
产品名称
'
);
await
this
.
expectInputCleared
(
this
.
listNameColumn
);
await
this
.
expectListOrEmpty
();
}
...
...
@@ -56,7 +56,7 @@ export class AdmitTaskPage extends AdmitSharedPage {
async
goPage2
():
Promise
<
void
>
{
await
this
.
query
();
await
this
.
goToPage2
(
'
产品名称
'
);
await
this
.
goToPage2
(
this
.
listNameColumn
);
}
async
jumpPage2
():
Promise
<
void
>
{
...
...
@@ -73,7 +73,7 @@ export class AdmitTaskPage extends AdmitSharedPage {
await
this
.
clickTab
(
'
待分类
'
);
const
before
=
await
this
.
readTabCount
(
'
待分类
'
);
const
row
=
await
this
.
skipIfNoAction
(
'
分配类型
'
,
'
待分类无分配类型
'
);
const
name
=
await
this
.
cellText
(
row
,
'
产品名称
'
);
const
name
=
await
this
.
cellText
(
row
,
this
.
listNameColumn
);
await
this
.
click
(
this
.
rowAction
(
row
,
'
分配类型
'
),
'
分配类型
'
);
const
layer
=
this
.
page
.
locator
(
'
.put-page:visible, .ant-modal:visible, .ant-drawer:visible
'
).
last
();
await
expect
(
layer
,
'
分配层应打开
'
).
toBeVisible
();
...
...
@@ -93,7 +93,7 @@ export class AdmitTaskPage extends AdmitSharedPage {
if
((
await
this
.
getDataRowCount
())
===
0
)
await
this
.
skipWhenDataInsufficient
(
'
待分类无数据
'
);
const
before
=
await
this
.
readTabCount
(
'
待分类
'
);
const
row
=
await
this
.
checkFirstRow
();
const
name
=
await
this
.
cellText
(
row
,
'
产品名称
'
);
const
name
=
await
this
.
cellText
(
row
,
this
.
listNameColumn
);
await
this
.
clickButton
(
'
批量分配
'
);
const
layer
=
this
.
page
.
locator
(
'
.put-page:visible, .ant-modal:visible, .ant-drawer:visible
'
).
last
();
await
expect
(
layer
).
toBeVisible
();
...
...
pages/spd/helpers/mock-empty-route.helper.ts
View file @
5312fc65
...
...
@@ -156,9 +156,10 @@ export async function runWithMockEmptyListRoute(
/** mock 反例筛选项标签候选(现网文案差异)。 */
export
function
mockFieldLabelCandidates
(
primary
:
string
):
readonly
string
[]
{
const
ordered
=
new
Set
<
string
>
([
primary
]);
if
(
primary
===
'
产品名称
'
)
{
if
(
primary
===
'
物料名称
'
||
primary
===
'
产品名称
'
)
{
ordered
.
add
(
'
物资名称
'
);
ordered
.
add
(
'
物料名称
'
);
ordered
.
add
(
'
产品名称
'
);
ordered
.
add
(
'
产品简称
'
);
ordered
.
add
(
'
耗材名称
'
);
}
...
...
pages/spd/helpers/mock-search-from-config.helper.ts
View file @
5312fc65
...
...
@@ -30,7 +30,7 @@ function resolveMockValue(data: CenterReportPageData, label: string): string {
/** CSV 008:按 data.mockFieldLabel + mockNotExist* 反例查询(单次写入并查询)。 */
export
async
function
executeSearchWithMockFromConfig
(
deps
:
MockSearchDeps
):
Promise
<
void
>
{
const
cfg
=
deps
.
data
;
const
primaryLabel
=
cfg
.
mockFieldLabel
??
'
产品
名称
'
;
const
primaryLabel
=
cfg
.
mockFieldLabel
??
'
物料
名称
'
;
const
mockVal
=
resolveMockValue
(
cfg
,
primaryLabel
);
let
label
=
primaryLabel
;
...
...
@@ -41,7 +41,7 @@ export async function executeSearchWithMockFromConfig(deps: MockSearchDeps): Pro
break
;
}
}
if
(
primaryLabel
===
'
产品名称
'
)
{
if
(
primaryLabel
===
'
物料名称
'
||
primaryLabel
===
'
产品名称
'
)
{
for
(
const
extra
of
[
'
物料名称
'
,
'
物资名称
'
]
as
const
)
{
const
item
=
deps
.
locators
.
quickFormItem
(
extra
);
if
((
await
item
.
count
())
>
0
)
{
...
...
scripts/discover-extended-sort-columns.mjs
View file @
5312fc65
...
...
@@ -12,24 +12,24 @@ const baseURL = process.env.BASE_URL || 'http://spdtest.cmic.com.cn:8080';
const
BASE
=
`
${
baseURL
}
/spd/form`
;
const
PAGES
=
[
{
pageKey
:
'
warn-strand
'
,
path
:
'
/warning/report/warning/strandQuery
'
,
queryBtn
:
'
查 询
'
,
testPrefer
:
[
'
统计日期
'
,
'
产品
名称
'
,
'
滞销天数
'
]
},
{
pageKey
:
'
warn-strand
'
,
path
:
'
/warning/report/warning/strandQuery
'
,
queryBtn
:
'
查 询
'
,
testPrefer
:
[
'
统计日期
'
,
'
物料
名称
'
,
'
滞销天数
'
]
},
{
pageKey
:
'
warn-detain
'
,
path
:
'
/warning/report/warning/detainDept
'
,
queryBtn
:
'
查 询
'
,
testPrefer
:
[],
testAllSort
:
true
},
{
pageKey
:
'
warn-expdt
'
,
path
:
'
/warning/report/warning/expdtQuery
'
,
queryBtn
:
'
查 询
'
,
testPrefer
:
[
'
统计日期
'
,
'
产品
名称
'
,
'
效期
'
]
},
{
pageKey
:
'
warn-low
'
,
path
:
'
/warning/report/warning/lowStorage
'
,
queryBtn
:
'
查 询
'
,
testPrefer
:
[
'
统计日期
'
,
'
产品
名称
'
,
'
库存量
'
]
},
{
pageKey
:
'
warn-expdt
'
,
path
:
'
/warning/report/warning/expdtQuery
'
,
queryBtn
:
'
查 询
'
,
testPrefer
:
[
'
统计日期
'
,
'
物料
名称
'
,
'
效期
'
]
},
{
pageKey
:
'
warn-low
'
,
path
:
'
/warning/report/warning/lowStorage
'
,
queryBtn
:
'
查 询
'
,
testPrefer
:
[
'
统计日期
'
,
'
物料
名称
'
,
'
库存量
'
]
},
{
pageKey
:
'
ops-check
'
,
path
:
'
/devOps/report/devOps/check
'
,
queryBtn
:
'
查 询
'
,
testPrefer
:
[
'
统计时间
'
,
'
院区
'
]
},
{
pageKey
:
'
exc-cost
'
,
path
:
'
/abnormalReport/report/abnormal/cost
'
,
queryBtn
:
'
查 询
'
,
testPrefer
:
[
'
记账时间
'
,
'
产品
名称
'
,
'
患者名称
'
]
},
{
pageKey
:
'
exc-low
'
,
path
:
'
/abnormalReport/report/abnormal/low
'
,
queryBtn
:
'
查 询
'
,
testPrefer
:
[
'
记账时间
'
,
'
产品
名称
'
,
'
患者名称
'
]
},
{
pageKey
:
'
exc-lowni
'
,
path
:
'
/abnormalReport/report/abnormal/lowNoInterface
'
,
queryBtn
:
'
查 询
'
,
testPrefer
:
[
'
记账时间
'
,
'
产品
名称
'
,
'
患者名称
'
]
},
{
pageKey
:
'
pol-key
'
,
path
:
'
/policy/report/policy/keyPoint
'
,
queryBtn
:
'
查 询
'
,
testPrefer
:
[
'
统计日期
'
,
'
产品
名称
'
,
'
消耗金额
'
]
},
{
pageKey
:
'
pol-task
'
,
path
:
'
/policy/report/policy/task
'
,
queryBtn
:
'
查 询
'
,
testPrefer
:
[
'
项目周期
'
,
'
产品
名称
'
,
'
任务量
'
]
},
{
pageKey
:
'
pol-rate
'
,
path
:
'
/policy/report/policy/rate
'
,
queryBtn
:
'
查 询
'
,
testPrefer
:
[
'
统计日期
'
,
'
产品
名称
'
,
'
完成率
'
]
},
{
pageKey
:
'
exc-cost
'
,
path
:
'
/abnormalReport/report/abnormal/cost
'
,
queryBtn
:
'
查 询
'
,
testPrefer
:
[
'
记账时间
'
,
'
物料
名称
'
,
'
患者名称
'
]
},
{
pageKey
:
'
exc-low
'
,
path
:
'
/abnormalReport/report/abnormal/low
'
,
queryBtn
:
'
查 询
'
,
testPrefer
:
[
'
记账时间
'
,
'
物料
名称
'
,
'
患者名称
'
]
},
{
pageKey
:
'
exc-lowni
'
,
path
:
'
/abnormalReport/report/abnormal/lowNoInterface
'
,
queryBtn
:
'
查 询
'
,
testPrefer
:
[
'
记账时间
'
,
'
物料
名称
'
,
'
患者名称
'
]
},
{
pageKey
:
'
pol-key
'
,
path
:
'
/policy/report/policy/keyPoint
'
,
queryBtn
:
'
查 询
'
,
testPrefer
:
[
'
统计日期
'
,
'
物料
名称
'
,
'
消耗金额
'
]
},
{
pageKey
:
'
pol-task
'
,
path
:
'
/policy/report/policy/task
'
,
queryBtn
:
'
查 询
'
,
testPrefer
:
[
'
项目周期
'
,
'
物料
名称
'
,
'
任务量
'
]
},
{
pageKey
:
'
pol-rate
'
,
path
:
'
/policy/report/policy/rate
'
,
queryBtn
:
'
查 询
'
,
testPrefer
:
[
'
统计日期
'
,
'
物料
名称
'
,
'
完成率
'
]
},
{
pageKey
:
'
pol-dept
'
,
path
:
'
/policy/report/policy/deptVbpRate
'
,
queryBtn
:
'
查 询
'
,
testPrefer
:
[
'
统计日期
'
,
'
科室
'
,
'
完成率
'
]
},
{
pageKey
:
'
pol-consumer
'
,
path
:
'
/policy/report/policy/vbpConsumer
'
,
queryBtn
:
'
查 询
'
,
testPrefer
:
[
'
统计日期
'
,
'
产品
名称
'
,
'
消耗数量
'
]
},
{
pageKey
:
'
pol-consumer
'
,
path
:
'
/policy/report/policy/vbpConsumer
'
,
queryBtn
:
'
查 询
'
,
testPrefer
:
[
'
统计日期
'
,
'
物料
名称
'
,
'
消耗数量
'
]
},
{
pageKey
:
'
pol-seven
'
,
path
:
'
/policy/report/policy/sevenMiCode
'
,
queryBtn
:
'
查 询
'
,
testPrefer
:
[
'
统计日期
'
,
'
7位医保码
'
,
'
消耗金额
'
]
},
{
pageKey
:
'
ana-chg
'
,
path
:
'
/statistics/report/statistics/chg
'
,
queryBtn
:
'
查 询
'
,
testPrefer
:
[
'
调价时间
'
,
'
产品
名称
'
,
'
调价前价格
'
]
},
{
pageKey
:
'
ana-chg
'
,
path
:
'
/statistics/report/statistics/chg
'
,
queryBtn
:
'
查 询
'
,
testPrefer
:
[
'
调价时间
'
,
'
物料
名称
'
,
'
调价前价格
'
]
},
{
pageKey
:
'
ana-dept
'
,
path
:
'
/statistics/report/statistics/dept
'
,
queryBtn
:
'
搜 索
'
,
testPrefer
:
[],
chartOnly
:
true
},
{
pageKey
:
'
ana-prov
'
,
path
:
'
/statistics/report/statistics/prov
'
,
queryBtn
:
'
搜 索
'
,
testPrefer
:
[],
chartOnly
:
true
},
{
pageKey
:
'
reag-entry
'
,
path
:
'
/reagentBatch/report/reagentBatch/entry
'
,
queryBtn
:
'
查 询
'
,
testPrefer
:
[
'
入库日期
'
,
'
产品
名称
'
,
'
批号
'
]
},
{
pageKey
:
'
reag-entry
'
,
path
:
'
/reagentBatch/report/reagentBatch/entry
'
,
queryBtn
:
'
查 询
'
,
testPrefer
:
[
'
入库日期
'
,
'
物料
名称
'
,
'
批号
'
]
},
];
function
pickTestSortColumns
(
sortable
,
prefer
=
[],
testAllSort
=
false
)
{
...
...
scripts/discover-material-code-headers.mjs
View file @
5312fc65
/**
* 扫描统计类报表 iframe 表头,检测物料/产品编码列名并与 data 对齐。
* 名称列(产品名称/物料名称)见:node scripts/discover-name-column-headers.mjs
* 用法:node scripts/discover-material-code-headers.mjs
*/
import
'
./playwright-browsers-path.mjs
'
;
...
...
scripts/discover-name-column-headers.mjs
0 → 100644
View file @
5312fc65
/**
* 扫描报表 iframe / 基础管理页:检测「产品名称」「物料名称」表头与筛选项。
* 用法:node scripts/discover-name-column-headers.mjs
*/
import
'
./playwright-browsers-path.mjs
'
;
import
{
chromium
}
from
'
@playwright/test
'
;
import
fs
from
'
fs
'
;
import
path
from
'
path
'
;
import
{
fileURLToPath
}
from
'
url
'
;
import
{
NAME_HEADER_DETECT_RE
,
detectPrimaryNameLabelFromHeaders
,
nameColumnLabelsMatch
,
}
from
'
./lib/name-column-labels.mjs
'
;
const
__dirname
=
path
.
dirname
(
fileURLToPath
(
import
.
meta
.
url
));
const
dataDir
=
path
.
join
(
__dirname
,
'
../data
'
);
const
storage
=
path
.
join
(
__dirname
,
'
../auth/user.json
'
);
const
baseURL
=
process
.
env
.
BASE_URL
||
'
http://spdtest.cmic.com.cn:8080
'
;
function
parseReportDataFiles
()
{
const
files
=
fs
.
readdirSync
(
dataDir
).
filter
((
f
)
=>
f
.
endsWith
(
'
.data.ts
'
));
const
reports
=
[];
for
(
const
file
of
files
)
{
const
text
=
fs
.
readFileSync
(
path
.
join
(
dataDir
,
file
),
'
utf8
'
);
const
pathMatch
=
text
.
match
(
/reportPath:
\s
*
[
'"
]([^
'"
]
+
)[
'"
]
/
);
if
(
!
pathMatch
)
continue
;
const
iframeMatch
=
text
.
match
(
/iframeSrc:
\s
*
[
'"
]([^
'"
]
+
)[
'"
]
/
);
const
productNameMatch
=
text
.
match
(
/productName:
\s
*
[
'"
]([^
'"
]
+
)[
'"
]
/
);
const
goodsNameMatch
=
text
.
match
(
/goodsName:
\s
*
[
'"
]([^
'"
]
+
)[
'"
]
/
);
const
coreMatch
=
text
.
match
(
/coreTableHeaders:
\s
*
\[([^\]]
+
)\]
/
);
let
configured
=
productNameMatch
?.[
1
]
??
goodsNameMatch
?.[
1
]
??
''
;
if
(
!
configured
&&
coreMatch
)
{
const
headers
=
coreMatch
[
1
].
match
(
/
[
'"
]([^
'"
]
+
)[
'"
]
/g
)?.
map
((
s
)
=>
s
.
slice
(
1
,
-
1
))
??
[];
configured
=
headers
.
find
((
h
)
=>
NAME_HEADER_DETECT_RE
.
test
(
h
))
??
''
;
}
if
(
!
iframeMatch
)
continue
;
reports
.
push
({
kind
:
'
report
'
,
file
,
reportPath
:
pathMatch
[
1
],
iframeSrc
:
iframeMatch
[
1
],
configuredName
:
configured
,
});
}
return
reports
.
sort
((
a
,
b
)
=>
a
.
file
.
localeCompare
(
b
.
file
));
}
const
BASIC_PAGES
=
[
{
kind
:
'
basic
'
,
file
:
'
admit-material
'
,
path
:
'
/spd/basic/dataSync/dataSync/productApply/ZRlist
'
,
configuredName
:
'
物料名称
'
},
{
kind
:
'
basic
'
,
file
:
'
admit-product-apply
'
,
path
:
'
/spd/basic/dataSync/dataSync/productApply/list
'
,
configuredName
:
'
物料名称
'
},
{
kind
:
'
basic
'
,
file
:
'
admit-material-list
'
,
path
:
'
/spd/basic/dataSync/dataSync/productOrder/zrlist
'
,
configuredName
:
'
物料名称
'
},
{
kind
:
'
basic
'
,
file
:
'
admit-product-list
'
,
path
:
'
/spd/basic/dataSync/dataSync/productOrder/list
'
,
configuredName
:
'
物料名称
'
},
{
kind
:
'
basic
'
,
file
:
'
admit-supply
'
,
path
:
'
/spd/basic/dataSync/dataSync/supplyApply/list
'
,
configuredName
:
'
物料名称
'
},
{
kind
:
'
basic
'
,
file
:
'
admit-task
'
,
path
:
'
/spd/basic/dataSync/dataSync/accessKind/list
'
,
configuredName
:
'
物料名称
'
},
];
async
function
scanReportHeaders
(
report
)
{
await
report
.
getByRole
(
'
button
'
,
{
name
:
'
查 询
'
}).
click
({
timeout
:
12
_000
}).
catch
(()
=>
{});
await
report
.
locator
(
'
.ant-table-tbody, .ant-empty
'
).
first
().
waitFor
({
state
:
'
visible
'
,
timeout
:
45
_000
}).
catch
(()
=>
{});
return
report
.
locator
(
'
.ant-table-thead tr
'
)
.
last
()
.
locator
(
'
th
'
)
.
evaluateAll
((
ths
)
=>
ths
.
map
((
th
)
=>
(
th
.
querySelector
(
'
.ant-table-column-title
'
)?.
textContent
||
th
.
textContent
||
''
)
.
replace
(
/
\s
+/g
,
'
'
)
.
trim
(),
)
.
filter
((
t
)
=>
t
&&
t
!==
'
序号
'
),
);
}
async
function
scanBasicPage
(
page
,
urlPath
)
{
await
page
.
goto
(
urlPath
,
{
waitUntil
:
'
domcontentloaded
'
,
timeout
:
90
_000
});
await
page
.
locator
(
'
.ant-spin-spinning
'
).
first
().
waitFor
({
state
:
'
hidden
'
,
timeout
:
30
_000
}).
catch
(()
=>
{});
const
tableHeaders
=
await
page
.
locator
(
'
.ant-table-thead tr
'
)
.
last
()
.
locator
(
'
th
'
)
.
evaluateAll
((
ths
)
=>
ths
.
map
((
th
)
=>
(
th
.
textContent
||
''
).
replace
(
/
\s
+/g
,
'
'
).
trim
())
.
filter
((
t
)
=>
t
&&
t
!==
'
序号
'
),
);
const
filterLabels
=
await
page
.
locator
(
'
.ant-form-item-label label
'
).
evaluateAll
((
els
)
=>
els
.
map
((
el
)
=>
(
el
.
textContent
||
''
).
replace
(
/
\s
+/g
,
'
'
).
trim
()).
filter
(
Boolean
),
);
return
{
tableHeaders
,
filterLabels
};
}
const
reports
=
parseReportDataFiles
();
console
.
log
(
`报表
${
reports
.
length
}
个;基础管理准入
${
BASIC_PAGES
.
length
}
个\n`
);
const
browser
=
await
chromium
.
launch
({
headless
:
true
});
const
context
=
await
browser
.
newContext
({
storageState
:
fs
.
existsSync
(
storage
)
?
storage
:
undefined
,
baseURL
,
});
const
page
=
await
context
.
newPage
();
const
rows
=
[];
for
(
const
r
of
reports
)
{
const
iframeKey
=
r
.
iframeSrc
.
split
(
'
/
'
).
pop
();
process
.
stdout
.
write
(
`[report]
${
r
.
file
}
… `
);
let
headers
=
[];
let
status
=
'
ok
'
;
let
error
=
''
;
try
{
await
page
.
goto
(
r
.
reportPath
,
{
waitUntil
:
'
domcontentloaded
'
,
timeout
:
90
_000
});
const
report
=
page
.
frameLocator
(
`iframe[src*="
${
iframeKey
}
"]`
);
await
report
.
locator
(
'
body
'
).
waitFor
({
state
:
'
visible
'
,
timeout
:
45
_000
});
headers
=
await
scanReportHeaders
(
report
);
}
catch
(
e
)
{
status
=
'
error
'
;
error
=
e
.
message
?.
slice
(
0
,
100
)
??
String
(
e
);
}
const
uiPrimary
=
detectPrimaryNameLabelFromHeaders
(
headers
);
const
mismatch
=
status
===
'
ok
'
&&
r
.
configuredName
&&
uiPrimary
!==
'
(无名称列)
'
&&
!
nameColumnLabelsMatch
(
r
.
configuredName
,
uiPrimary
);
rows
.
push
({
...
r
,
uiPrimary
,
mismatch
,
headers
:
headers
.
join
(
'
|
'
),
filterLabels
:
''
,
status
,
error
});
console
.
log
(
status
===
'
error
'
?
`ERROR
${
error
}
`
:
`
${
uiPrimary
}${
mismatch
?
'
⚠️
'
:
''
}
`
);
}
for
(
const
b
of
BASIC_PAGES
)
{
process
.
stdout
.
write
(
`[basic]
${
b
.
file
}
… `
);
let
status
=
'
ok
'
;
let
error
=
''
;
let
tableHeaders
=
[];
let
filterLabels
=
[];
try
{
const
scanned
=
await
scanBasicPage
(
page
,
b
.
path
);
tableHeaders
=
scanned
.
tableHeaders
;
filterLabels
=
scanned
.
filterLabels
;
}
catch
(
e
)
{
status
=
'
error
'
;
error
=
e
.
message
?.
slice
(
0
,
100
)
??
String
(
e
);
}
const
uiTable
=
detectPrimaryNameLabelFromHeaders
(
tableHeaders
);
const
uiFilter
=
filterLabels
.
find
((
l
)
=>
/^物料名称$/
.
test
(
l
.
replace
(
/
\s
/g
,
''
)))
??
filterLabels
.
find
((
l
)
=>
/^产品名称$/
.
test
(
l
.
replace
(
/
\s
/g
,
''
)))
??
'
(无筛选项)
'
;
const
uiPrimary
=
uiTable
!==
'
(无名称列)
'
?
uiTable
:
uiFilter
;
const
mismatch
=
status
===
'
ok
'
&&
uiPrimary
!==
'
(无名称列)
'
&&
!
nameColumnLabelsMatch
(
b
.
configuredName
,
uiPrimary
);
rows
.
push
({
...
b
,
configuredName
:
b
.
configuredName
,
uiPrimary
,
uiTable
,
uiFilter
,
mismatch
,
headers
:
tableHeaders
.
join
(
'
|
'
),
filterLabels
:
filterLabels
.
join
(
'
|
'
),
status
,
error
,
});
console
.
log
(
status
===
'
error
'
?
`ERROR
${
error
}
`
:
`表头=
${
uiTable
}
筛选=
${
uiFilter
}${
mismatch
?
'
⚠️
'
:
''
}
`
);
}
await
browser
.
close
();
const
mismatches
=
rows
.
filter
((
x
)
=>
x
.
mismatch
);
console
.
log
(
'
\n
======== 汇总 ========
'
);
console
.
log
(
`物料名称(表头):
${
rows
.
filter
((
x
)
=>
x
.
uiPrimary
===
'
物料名称
'
).
length
}
`
);
console
.
log
(
`产品名称(表头):
${
rows
.
filter
((
x
)
=>
x
.
uiPrimary
===
'
产品名称
'
).
length
}
`
);
console
.
log
(
`与配置不一致:
${
mismatches
.
length
}
`
);
if
(
mismatches
.
length
)
{
console
.
log
(
'
\n
--- 建议 data/PO 改为 uiPrimary ---
'
);
for
(
const
m
of
mismatches
)
{
console
.
log
(
`
${
m
.
file
}
: "
${
m
.
configuredName
}
" → "
${
m
.
uiPrimary
}
"`
);
}
}
const
outPath
=
path
.
join
(
__dirname
,
'
../data/scan/name-column-header-audit.json
'
);
fs
.
mkdirSync
(
path
.
dirname
(
outPath
),
{
recursive
:
true
});
fs
.
writeFileSync
(
outPath
,
JSON
.
stringify
({
scannedAt
:
new
Date
().
toISOString
(),
rows
},
null
,
2
));
console
.
log
(
`\n明细:
${
outPath
}
`
);
scripts/gen-entry-detail-csv.py
View file @
5312fc65
...
...
@@ -62,8 +62,8 @@ def base_rows(prefix, module, page_name, iframe, combo_fields_text, reset_sample
),
"steps"
:
(
"1. 默认条件查询
\n
"
"2. 读取主表首行
产品
名称及入账数量、入账金额
\n
"
"3. 按首行
产品
名称等收窄查询
\n
"
"2. 读取主表首行
物料
名称及入账数量、入账金额
\n
"
"3. 按首行
物料
名称等收窄查询
\n
"
"4. 勾选或点选主表首行
\n
"
"5. 读取底部汇总(入账数量、入账金额)并与首行对应字段对比"
),
...
...
@@ -77,7 +77,7 @@ def base_rows(prefix, module, page_name, iframe, combo_fields_text, reset_sample
"priority"
:
"中"
,
"pre"
:
f
"测试环境可访问;测试账号已登录并进入
{
page_name
}
页(iframe
{
iframe
}
)"
,
"steps"
:
(
"1.
产品
名称填 __NOT_EXIST_PRODUCT__99999
\n
"
"1.
物料
名称填 __NOT_EXIST_PRODUCT__99999
\n
"
"2. 点击「查 询」
\n
"
"3. 检查主表;若存在底部汇总区则一并检查,无则跳过"
),
...
...
@@ -95,7 +95,7 @@ def base_rows(prefix, module, page_name, iframe, combo_fields_text, reset_sample
),
"steps"
:
(
"1. 默认条件查询
\n
"
"2. 确认「
产品
名称」等核心列可见
\n
"
"2. 确认「
物料
名称」等核心列可见
\n
"
"3. 横向拖拉滚动列表
\n
"
f
"4. 检查「
{
scroll_target
}
」等远侧列进入可视区
\n
"
"5. 检查表头无空白列、表头列数与数据列数一致;相邻列无异常间隙/重叠;表头与数据列对齐正常"
...
...
@@ -221,7 +221,7 @@ def write_csv(path: Path, rows):
CED_SORT
=
[
"
产品
名称"
,
"
物料
名称"
,
"规格型号"
,
"器械注册人-术语配置"
,
DEFAULT_MATERIAL_CODE_COLUMN
,
...
...
@@ -241,7 +241,7 @@ CED_SORT = [
CEE_SORT
=
[
DEFAULT_MATERIAL_CODE_COLUMN
,
"
产品
名称"
,
"
物料
名称"
,
"规格型号"
,
"器械注册人-术语配置"
,
"产品俗称"
,
...
...
@@ -265,8 +265,8 @@ ced_rows = base_rows(
module
,
"成本入账明细查询"
,
"settel/entryCost"
,
"入账日期、
产品
名称"
,
"
产品
名称/入账日期"
,
"入账日期、
物料
名称"
,
"
物料
名称/入账日期"
,
)
for
i
,
col
in
enumerate
(
CED_SORT
,
start
=
11
):
ced_rows
.
append
(
sort_row
(
"STAT-CED"
,
module
,
i
,
col
,
"settel/entryCost"
,
"成本入账明细查询"
))
...
...
@@ -295,8 +295,8 @@ cee_rows = base_rows(
module
,
"成本入账明细查询(执行科室维度)"
,
"settel/entryExecCost"
,
"入账日期、
产品
名称、耗材类型"
,
"
产品
名称/入账日期"
,
"入账日期、
物料
名称、耗材类型"
,
"
物料
名称/入账日期"
,
scroll_target
=
"执行科室名称"
,
)
for
i
,
col
in
enumerate
(
CEE_SORT
,
start
=
11
):
...
...
scripts/generate-recon-cost-playwright.py
View file @
5312fc65
...
...
@@ -41,6 +41,7 @@ PAGES = [
LABEL_KEY
=
{
"院区名称"
:
"campus"
,
"院区"
:
"campus"
,
"科室名称"
:
"deptName"
,
"科室"
:
"deptName"
,
"产品名称"
:
"productName"
,
"物料名称"
:
"productName"
,
"产品编号"
:
"productCode"
,
"物料编号"
:
"productCode"
,
"物料编码"
:
"productCode"
,
...
...
@@ -213,14 +214,14 @@ def build_field_defs(fields: list[str]) -> tuple[list[dict], list[dict], dict[st
def
mock_config
(
fields
:
list
[
str
])
->
tuple
[
str
,
str
,
dict
]:
extra
=
{}
if
any
(
"产品"
in
f
for
f
in
fields
):
return
"
产品
名称"
,
"__NOT_EXIST_PRODUCT__99999"
,
extra
return
"
物料
名称"
,
"__NOT_EXIST_PRODUCT__99999"
,
extra
if
any
(
"科室"
in
f
for
f
in
fields
):
extra
[
"mockNotExistDept"
]
=
"__NOT_EXIST_DEPT__99999"
return
"科室名称"
,
"__NOT_EXIST_DEPT__99999"
,
extra
if
any
(
"供应商"
in
f
for
f
in
fields
):
extra
[
"mockNotExistSupplier"
]
=
"__NOT_EXIST_SUPPLIER__99999"
return
"进院供应商名称"
,
"__NOT_EXIST_SUPPLIER__99999"
,
extra
return
fields
[
0
]
if
fields
else
"
产品
名称"
,
"__NOT_EXIST_VALUE__99999"
,
extra
return
fields
[
0
]
if
fields
else
"
物料
名称"
,
"__NOT_EXIST_VALUE__99999"
,
extra
def
core_headers
(
columns
:
dict
[
str
,
str
])
->
list
[
str
]:
...
...
scripts/lib/code_column_labels.py
View file @
5312fc65
...
...
@@ -14,6 +14,7 @@ PRODUCT_ENCODE_LABELS = ("产品编码",)
# 多数统计报表列表列
DEFAULT_MATERIAL_CODE_COLUMN
=
"物料编号"
DEFAULT_MATERIAL_NAME_COLUMN
=
"物料名称"
# 科室净入成本、带量消耗等
MATERIAL_CODE_COLUMN
=
"物料编码"
scripts/lib/name-column-labels.mjs
0 → 100644
View file @
5312fc65
/**
* 统计报表 / 基础管理:产品名称 vs 物料名称(与 column-header-match.util 别名组对齐)。
*/
export
const
MATERIAL_NAME_LABELS
=
Object
.
freeze
([
'
物料名称
'
,
'
产品名称
'
,
'
物资名称
'
,
'
商品名称
'
,
'
产品俗称
'
,
]);
const
norm
=
(
s
)
=>
(
s
??
''
).
replace
(
/
\s
+/g
,
''
).
trim
();
export
function
isMaterialNameLabel
(
label
)
{
const
n
=
norm
(
label
);
return
MATERIAL_NAME_LABELS
.
some
((
a
)
=>
norm
(
a
)
===
n
);
}
export
function
nameColumnLabelsMatch
(
configured
,
uiPrimary
)
{
const
c
=
norm
(
configured
);
const
u
=
norm
(
uiPrimary
);
if
(
!
c
||
!
u
||
u
===
'
(无名称列)
'
)
return
true
;
if
(
c
===
u
)
return
true
;
if
(
isMaterialNameLabel
(
configured
)
&&
isMaterialNameLabel
(
uiPrimary
))
return
true
;
return
false
;
}
/** 列表表头:优先物料名称,其次产品名称等。 */
export
function
detectPrimaryNameLabelFromHeaders
(
headers
)
{
const
normalized
=
headers
.
map
((
h
)
=>
h
.
replace
(
/
\s
+/g
,
'
'
).
trim
()).
filter
(
Boolean
);
const
find
=
(
re
)
=>
normalized
.
find
((
h
)
=>
re
.
test
(
norm
(
h
)));
return
(
find
(
/^物料名称$/
)
??
find
(
/物料名称/
)
??
find
(
/^产品名称$/
)
??
find
(
/产品名称/
)
??
find
(
/^物资名称$/
)
??
find
(
/物资名称/
)
??
'
(无名称列)
'
);
}
export
const
NAME_HEADER_DETECT_RE
=
/产品名称|物料名称|物资名称|商品名称/
;
scripts/md-ui-testcases-to-csv.mjs
View file @
5312fc65
...
...
@@ -14,7 +14,7 @@ const PRIORITY_MAP = { P0: '高', P1: '中', P2: '低' };
const
SORT_COLUMNS_ENTRY
=
[
[
'
012
'
,
'
物料编号
'
],
[
'
013
'
,
'
产品
名称
'
],
[
'
013
'
,
'
物料
名称
'
],
[
'
014
'
,
'
单位
'
],
[
'
015
'
,
'
规格型号
'
],
[
'
016
'
,
'
生产厂家
'
],
...
...
@@ -28,7 +28,7 @@ const SORT_COLUMNS_ENTRY = [
const
SORT_COLUMNS_OUT
=
[
[
'
012
'
,
'
物料编号
'
],
[
'
013
'
,
'
产品
名称
'
],
[
'
013
'
,
'
物料
名称
'
],
[
'
014
'
,
'
单位
'
],
[
'
015
'
,
'
规格型号
'
],
[
'
016
'
,
'
生产厂家
'
],
...
...
@@ -210,10 +210,10 @@ function expandDod(rawCases, cfg) {
if
(
id
===
'
STAT-DOD-001
'
)
{
rows
.
push
(
row
(
id
,
tp
,
c
.
title
,
pri
,
pre
(
cfg
.
pageLabel
,
cfg
.
iframe
,
'
已默认查询且列表有数据
'
),
joinSteps
([
'
从列表首行读取
产品
名称/记账日期样本并填入快捷区
'
,
'
从列表首行读取
物料
名称/记账日期样本并填入快捷区
'
,
'
点击「重 置」
'
,
'
检查 #form_item_goodsName 与记账日期字段
'
,
]),
c
.
expected
||
'
产品
名称清空;记账日期恢复近一月默认;快捷区恢复初始状态
'
));
]),
c
.
expected
||
'
物料
名称清空;记账日期恢复近一月默认;快捷区恢复初始状态
'
));
continue
;
}
if
(
id
===
'
STAT-DOD-002
'
)
{
...
...
@@ -234,7 +234,7 @@ function expandDod(rawCases, cfg) {
if
(
id
===
'
STAT-DOD-004
'
)
{
rows
.
push
(
row
(
id
,
tp
,
c
.
title
,
pri
,
pre
(
cfg
.
pageLabel
,
cfg
.
iframe
,
'
已默认查询且列表至少有 1 条数据
'
),
joinSteps
([
'
默认查询后 searchAndExtractSample() 读取首行全量样本
'
,
'
快捷区填入
产品
名称、记账日期、单据类型
'
,
'
快捷区填入
物料
名称、记账日期、单据类型
'
,
'
打开高级筛选,填入科室、库区名称、单据形态、耗材分类、批号、条码号、单号、进院供应商、结算方式、备注、规格型号、制单人、统计维度、是否收费、是否重点管控、重点管控分类、医保编码
'
,
'
点击「筛 选」并等待列表与汇总刷新
'
,
'
检查首行物料编号/名称、科室名称、单据类型、批号、单号、规格型号、结算方式与条件一致
'
,
...
...
@@ -245,7 +245,7 @@ function expandDod(rawCases, cfg) {
rows
.
push
(
row
(
id
,
tp
,
c
.
title
,
pri
,
pre
(
cfg
.
pageLabel
,
cfg
.
iframe
),
joinSteps
([
'
点击「查 询」
'
,
'
检查表头列名
'
,
]),
'
表头包含科室名称、物料编号、
产品
名称、单据类型、批号、入库数量、出库数量、单号等核心列;可横向滚动
'
));
]),
'
表头包含科室名称、物料编号、
物料
名称、单据类型、批号、入库数量、出库数量、单号等核心列;可横向滚动
'
));
continue
;
}
if
(
id
===
'
STAT-DOD-006
'
)
{
...
...
@@ -257,7 +257,7 @@ function expandDod(rawCases, cfg) {
}
if
(
id
===
'
STAT-DOD-007
'
)
{
rows
.
push
(
row
(
id
,
tp
,
'
无匹配数据空列表(mock反例)
'
,
pri
,
pre
(
cfg
.
pageLabel
,
cfg
.
iframe
),
joinSteps
([
'
产品
名称填 __NOT_EXIST_PRODUCT__99999
'
,
'
物料
名称填 __NOT_EXIST_PRODUCT__99999
'
,
'
点击「查 询」
'
,
'
检查表格与汇总
'
,
]),
c
.
expected
));
...
...
@@ -334,7 +334,7 @@ function expandEntryOut(rawCases, cfg, sortCols, inOutLabel) {
if
(
id
===
`
${
cfg
.
prefix
}
-001`
)
{
rows
.
push
(
row
(
id
,
tp
,
c
.
title
,
pri
,
pre
(
cfg
.
pageLabel
,
cfg
.
iframe
,
'
已默认查询且主表有数据
'
),
joinSteps
([
'
从主表首行读取
产品
名称/记账日期样本填入快捷区
'
,
'
从主表首行读取
物料
名称/记账日期样本填入快捷区
'
,
'
勾选「区分包装」
'
,
'
点击「重 置」
'
,
'
检查各字段与维度勾选
'
,
...
...
@@ -362,7 +362,7 @@ function expandEntryOut(rawCases, cfg, sortCols, inOutLabel) {
if
(
cfg
.
prefix
===
'
STAT-DOS
'
)
{
rows
.
push
(
row
(
id
,
tp
,
c
.
title
,
pri
,
pre
(
cfg
.
pageLabel
,
cfg
.
iframe
,
'
已默认查询且主表至少有 1 条数据
'
),
joinSteps
([
'
searchAndExtractSample() 读取样本;展开首行读取子表明细(若有)
'
,
'
快捷区填入
产品
名称、记账日期、单据类型
'
,
'
快捷区填入
物料
名称、记账日期、单据类型
'
,
'
打开高级筛选,填入科室、库区名称、单据形态、批号、单号、出库目标、结算方式、进院供应商名称、产品是否收费
'
,
'
点击「筛 选」并等待刷新
'
,
'
检查首行物料编号/名称、出库数量、结算方式与条件一致
'
,
...
...
@@ -370,7 +370,7 @@ function expandEntryOut(rawCases, cfg, sortCols, inOutLabel) {
}
else
{
rows
.
push
(
row
(
id
,
tp
,
c
.
title
,
pri
,
pre
(
cfg
.
pageLabel
,
cfg
.
iframe
,
'
已默认查询且主表至少有 1 条数据
'
),
joinSteps
([
'
searchAndExtractSample() 读取样本;展开首行读取子表明细(若有)
'
,
'
快捷区填入
产品
名称、记账日期、单据类型
'
,
'
快捷区填入
物料
名称、记账日期、单据类型
'
,
'
打开高级筛选,填入科室、库区名称、单据形态、来源库房、单号、结算方式、进院供应商名称、产品是否收费
'
,
'
点击「筛 选」并等待刷新
'
,
'
检查首行物料编号/名称、入库数量、结算方式与条件一致
'
,
...
...
@@ -390,7 +390,7 @@ function expandEntryOut(rawCases, cfg, sortCols, inOutLabel) {
rows
.
push
(
row
(
id
,
tp
,
c
.
title
,
pri
,
pre
(
cfg
.
pageLabel
,
cfg
.
iframe
),
joinSteps
([
'
点击「查 询」
'
,
'
检查主表表头
'
,
]),
c
.
expected
||
`表头含物料编号、
产品
名称、
${
inOutLabel
}
数量、
${
inOutLabel
}
金额等核心列`
));
]),
c
.
expected
||
`表头含物料编号、
物料
名称、
${
inOutLabel
}
数量、
${
inOutLabel
}
金额等核心列`
));
continue
;
}
if
(
id
===
`
${
cfg
.
prefix
}
-007`
)
{
...
...
@@ -402,7 +402,7 @@ function expandEntryOut(rawCases, cfg, sortCols, inOutLabel) {
}
if
(
id
===
`
${
cfg
.
prefix
}
-008`
)
{
rows
.
push
(
row
(
id
,
tp
,
'
无匹配数据空列表(mock反例)
'
,
pri
,
pre
(
cfg
.
pageLabel
,
cfg
.
iframe
),
joinSteps
([
'
产品
名称填 __NOT_EXIST_PRODUCT__99999
'
,
'
物料
名称填 __NOT_EXIST_PRODUCT__99999
'
,
'
点击「查 询」
'
,
'
检查主表与底部汇总
'
,
]),
c
.
expected
));
...
...
@@ -520,7 +520,7 @@ function expandDps(rawCases, cfg) {
pre
:
pre
(
cfg
.
pageLabel
,
cfg
.
iframe
),
steps
:
joinSteps
([
'
打开高级筛选抽屉
'
,
'
产品
名称填 __NOT_EXIST_PRODUCT__99999
'
,
'
物料
名称填 __NOT_EXIST_PRODUCT__99999
'
,
'
点击「筛 选」
'
,
'
检查列表
'
,
]),
...
...
@@ -534,7 +534,7 @@ function expandDps(rawCases, cfg) {
steps
:
joinSteps
([
'
searchAndExtractSample() 读取样本
'
,
'
快捷区填入入账日期、院区名称、科室名称
'
,
'
打开高级筛选,填入耗材类型、耗材分类、
产品
名称、规格型号、生产厂家
'
,
'
打开高级筛选,填入耗材类型、耗材分类、
物料
名称、规格型号、生产厂家
'
,
'
点击「筛 选」
'
,
'
检查首行物料编号/名称、规格型号、耗材类型、耗材分类、生产厂家与条件一致
'
,
]),
...
...
@@ -707,12 +707,12 @@ function expandDtr(rawCases, cfg) {
const
templates
=
{
'
STAT-DTR-001
'
:
{
pre
:
pre
(
cfg
.
pageLabel
,
cfg
.
iframe
),
steps
:
joinSteps
([
'
修改统计周期、科室、
产品
名称
'
,
'
点击「重 置」
'
,
'
检查字段
'
]),
steps
:
joinSteps
([
'
修改统计周期、科室、
物料
名称
'
,
'
点击「重 置」
'
,
'
检查字段
'
]),
},
'
STAT-DTR-002
'
:
{
pre
:
pre
(
cfg
.
pageLabel
,
cfg
.
iframe
,
'
已选择科室
'
),
steps
:
joinSteps
([
'
样本填入统计周期、科室、
产品
名称、规格型号、耗材类型
'
,
'
样本填入统计周期、科室、
物料
名称、规格型号、耗材类型
'
,
'
点击「查 询」
'
,
'
检查首行产品、规格型号、耗材类型与时间段天数
'
,
]),
...
...
@@ -725,7 +725,7 @@ function expandDtr(rawCases, cfg) {
'
STAT-DTR-004
'
:
{
pre
:
pre
(
cfg
.
pageLabel
,
cfg
.
iframe
,
'
已选择科室
'
),
steps
:
joinSteps
([
'
产品
名称填 __NOT_EXIST_PRODUCT__99999
'
,
'
物料
名称填 __NOT_EXIST_PRODUCT__99999
'
,
'
点击「查 询」
'
,
'
检查列表
'
,
]),
...
...
scripts/probe-skip-suspects.mjs
View file @
5312fc65
...
...
@@ -107,7 +107,7 @@ const probes = [
await
page
.
goto
(
`
${
BASE
}${
this
.
path
}
`
,
{
waitUntil
:
'
domcontentloaded
'
});
await
page
.
waitForTimeout
(
1500
);
await
clickQuery
(
page
);
return
await
sampleColumn
(
page
,
'
产品
名称
'
);
return
await
sampleColumn
(
page
,
'
物料
名称
'
);
},
},
{
...
...
scripts/update_batch_testcase_csv.py
View file @
5312fc65
...
...
@@ -239,7 +239,7 @@ def main() -> None:
)
rni_sort
=
[
sort_case
(
M_RECON
,
"科室净入汇总表·主表列表与汇总"
,
"科室净入汇总表页(iframe checkAccount/outRequest)"
,
"
产品
名称"
),
"科室净入汇总表页(iframe checkAccount/outRequest)"
,
"
物料
名称"
),
sort_case
(
M_RECON
,
"科室净入汇总表·主表列表与汇总"
,
"科室净入汇总表页(iframe checkAccount/outRequest)"
,
"统计日期"
),
sort_case
(
M_RECON
,
"科室净入汇总表·主表列表与汇总"
,
...
...
@@ -310,7 +310,7 @@ def main() -> None:
# 8. 财务产品进销存 - sort
cfp_sort
=
[
sort_case
(
M_COST
,
"财务产品进销存·主表列表与汇总"
,
"财务产品进销存页(iframe settel/financeWithProduct)"
,
"
产品
名称"
),
"财务产品进销存页(iframe settel/financeWithProduct)"
,
"
物料
名称"
),
sort_case
(
M_COST
,
"财务产品进销存·主表列表与汇总"
,
"财务产品进销存页(iframe settel/financeWithProduct)"
,
"入账月份"
),
sort_case
(
M_COST
,
"财务产品进销存·主表列表与汇总"
,
...
...
@@ -412,7 +412,7 @@ def main() -> None:
"查询成功无报错;首行同时满足全部已填条件;组合逻辑为「与」关系。"
,
),
sort_case
(
M_COST
,
"成本入账明细查询·主表列表与汇总"
,
ced_page
,
"入账日期"
),
sort_case
(
M_COST
,
"成本入账明细查询·主表列表与汇总"
,
ced_page
,
"
产品
名称"
),
sort_case
(
M_COST
,
"成本入账明细查询·主表列表与汇总"
,
ced_page
,
"
物料
名称"
),
print_case
(
M_COST
,
"成本入账明细查询·打印"
,
ced_page
,
"成本入账明细查询"
),
]
update_file
(
...
...
@@ -437,7 +437,7 @@ def main() -> None:
"查询成功无报错;首行同时满足全部已填条件;组合逻辑为「与」关系。"
,
),
sort_case
(
M_COST
,
"成本入账明细查询(执行科室维度)·主表列表与汇总"
,
cee_page
,
"入账日期"
),
sort_case
(
M_COST
,
"成本入账明细查询(执行科室维度)·主表列表与汇总"
,
cee_page
,
"
产品
名称"
),
sort_case
(
M_COST
,
"成本入账明细查询(执行科室维度)·主表列表与汇总"
,
cee_page
,
"
物料
名称"
),
]
update_file
(
"统计报表-结算成本报表-成本入账明细查询(执行科室维度)-UI自动化测试用例-01.csv"
,
...
...
scripts/validate-recon-cost-artifacts.py
View file @
5312fc65
...
...
@@ -116,7 +116,7 @@ def parse_csv_combo_fields(group: str, name: str) -> list[str]:
chunk
=
m
.
group
(
1
).
replace
(
"对应字段"
,
""
)
return
[
x
.
strip
()
for
x
in
re
.
split
(
r
"[、,,]"
,
chunk
)
if
x
.
strip
()]
fields
=
re
.
findall
(
r
"(院区名称|科室名称|产品名称|物料编号|物料编码|产品编号|产品编码|统计日期|入账日期|记账日期|财务月份|"
r
"(院区名称|科室名称|
物料名称|
产品名称|物料编号|物料编码|产品编号|产品编码|统计日期|入账日期|记账日期|财务月份|"
r
"进院供应商名称|结算方式|批号|规格型号|生产厂家|库区名称|分类名称|"
r
"耗材类型|物资类型|入库单号|出库单号|源单号|计费科室|库存科室|查询维度)"
,
steps
,
...
...
Prev
1
2
3
4
5
6
7
8
Next
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment