Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a4425addb0 | ||
|
|
471a6823e2 |
No files matched your search
@@ -1291,10 +1291,23 @@ def list_collection_protocols(
|
||||
)
|
||||
|
||||
from app.services.qc_work_order_fee_summary import build_protocol_payment_summaries
|
||||
from app.crud import sample_freight as sample_freight_svc
|
||||
|
||||
payment_summary_map = build_protocol_payment_summaries(
|
||||
db, protocol_ids, tenant_no, include_sample_payment_modes=True
|
||||
)
|
||||
all_list_sample_ids = [
|
||||
s.id
|
||||
for samples in sample_map.values()
|
||||
for s in samples
|
||||
if getattr(s, "id", None)
|
||||
]
|
||||
freight_by_sample = sample_freight_svc.freight_map_for_samples(
|
||||
db,
|
||||
tenant_no=tenant_no,
|
||||
sample_source=sample_freight_svc.SAMPLE_SOURCE_CELL,
|
||||
sample_ids=all_list_sample_ids,
|
||||
)
|
||||
|
||||
# 合并主表 + 子表数据
|
||||
result = []
|
||||
@@ -1376,11 +1389,13 @@ def list_collection_protocols(
|
||||
}
|
||||
sample_items.append(
|
||||
{
|
||||
"id": s.id,
|
||||
"sampleTypeName": type_name,
|
||||
"sampleCode": code,
|
||||
"sampleStatus": (s.sample_status or "").strip(),
|
||||
"labStorageCode": lab_code_map.get(code, "") if code else "",
|
||||
"transport": transport_dict,
|
||||
"freight": freight_by_sample.get(s.id),
|
||||
**sample_special_to_dict(s),
|
||||
**build_sample_item_reception_qc_fields(
|
||||
reception_summary_map.get(code) if code else None,
|
||||
|
||||
@@ -65,7 +65,8 @@ describe('buildCellStorageLedgerRows', () => {
|
||||
isCoStored: false,
|
||||
sampleCollectTime: '2025-09-15 08:51:00',
|
||||
motherName: '郭梦朵',
|
||||
protocolNo: 'ZKZX34000849',
|
||||
protocolNo: 'PROTO-SHOULD-NOT-EXPORT',
|
||||
sampleCode: 'ZKZX34000849',
|
||||
sampleTypeName: '围产期造血干细胞',
|
||||
regionName: '亳州区域',
|
||||
hospitalName: '涡阳县人民医院',
|
||||
@@ -73,6 +74,9 @@ describe('buildCellStorageLedgerRows', () => {
|
||||
salesUserName: '马丽',
|
||||
regionManagerUserName: '张颖',
|
||||
transportType: 'kdys',
|
||||
freightId: 'fr-cell-1',
|
||||
freightTransportLabel: '专线车',
|
||||
freightAmountLabel: '120',
|
||||
paymentSummary: {
|
||||
payMode: 'fq',
|
||||
contractAmount: 22880,
|
||||
@@ -97,11 +101,13 @@ describe('buildCellStorageLedgerRows', () => {
|
||||
]
|
||||
}
|
||||
} as unknown as ProtocolAuditFlatRow
|
||||
const [row] = buildCellStorageLedgerRows([flat])
|
||||
const { rows } = buildCellStorageLedgerRows([flat])
|
||||
const row = rows[0]
|
||||
assert.equal(row.length, CELL_STORAGE_HEADERS.length)
|
||||
assert.equal(CELL_STORAGE_HEADERS[13], '涡阳县人民医院')
|
||||
assert.equal(row[5], 'B0000004')
|
||||
assert.equal(row[6], '生效中')
|
||||
assert.equal(row[9], 'ZKZX34000849')
|
||||
assert.equal(row[11], '否')
|
||||
assert.equal(row[13], '涡阳县人民医院')
|
||||
assert.equal(row[14], '分期')
|
||||
@@ -112,7 +118,42 @@ describe('buildCellStorageLedgerRows', () => {
|
||||
assert.equal(row[25], '16400')
|
||||
assert.equal(row[26], '2025年12月15日')
|
||||
assert.equal(row[50], '马丽')
|
||||
assert.equal(row[52], '快递运输')
|
||||
assert.equal(row[52], '专线车')
|
||||
assert.equal(row[53], '120')
|
||||
})
|
||||
|
||||
it('merges transport and freight columns for the same freight batch', () => {
|
||||
const base = {
|
||||
protocol: { protocolStatus: 'sxz', remark: '', voidType: '', voidDesc: '' },
|
||||
sampleCollectTime: '2025-09-11 18:13:00',
|
||||
motherName: '甲',
|
||||
sampleCode: 'S1',
|
||||
sampleTypeName: 'NK',
|
||||
regionName: '常州',
|
||||
hospitalName: '溧阳市人民医院',
|
||||
labStorageCode: 'UBMC1',
|
||||
freightId: 'batch-cell',
|
||||
freightTransportLabel: '专线车',
|
||||
freightAmountLabel: '100'
|
||||
}
|
||||
const { rows, merges } = buildCellStorageLedgerRows([
|
||||
{ ...base, sampleIndex: 0 } as unknown as ProtocolAuditFlatRow,
|
||||
{
|
||||
...base,
|
||||
sampleIndex: 1,
|
||||
motherName: '乙',
|
||||
sampleCode: 'S2',
|
||||
labStorageCode: 'UBMC2'
|
||||
} as unknown as ProtocolAuditFlatRow
|
||||
])
|
||||
assert.equal(rows[0][52], '专线车')
|
||||
assert.equal(rows[0][53], '100')
|
||||
assert.equal(rows[1][52], '')
|
||||
assert.equal(rows[1][53], '')
|
||||
assert.deepEqual(merges, [
|
||||
{ startRow: 2, endRow: 3, startCol: 53, endCol: 53 },
|
||||
{ startRow: 2, endRow: 3, startCol: 54, endCol: 54 }
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -338,8 +338,16 @@ function researchMaternalBloodText(value: unknown): string {
|
||||
return text(value) ? text(value) : '无母血'
|
||||
}
|
||||
|
||||
/** 相邻且 freightId 相同的导出行,合并运输方式/运费列(Excel 行号含表头)。 */
|
||||
export function buildFreightMergeRanges(freightIds: string[]): LedgerMergeRange[] {
|
||||
/**
|
||||
* 相邻且 freightId 相同的导出行,合并运输方式/运费列(Excel 行号含表头)。
|
||||
* @param transportCol 运输方式列(1-based)
|
||||
* @param freightCol 运费列(1-based)
|
||||
*/
|
||||
export function buildFreightMergeRanges(
|
||||
freightIds: string[],
|
||||
transportCol = 17,
|
||||
freightCol = 18
|
||||
): LedgerMergeRange[] {
|
||||
const merges: LedgerMergeRange[] = []
|
||||
let index = 0
|
||||
while (index < freightIds.length) {
|
||||
@@ -353,14 +361,31 @@ export function buildFreightMergeRanges(freightIds: string[]): LedgerMergeRange[
|
||||
if (end - index >= 2) {
|
||||
const startRow = index + 2
|
||||
const endRow = end + 1
|
||||
merges.push({ startRow, endRow, startCol: 17, endCol: 17 })
|
||||
merges.push({ startRow, endRow, startCol: 18, endCol: 18 })
|
||||
merges.push({ startRow, endRow, startCol: transportCol, endCol: transportCol })
|
||||
merges.push({ startRow, endRow, startCol: freightCol, endCol: freightCol })
|
||||
}
|
||||
index = end
|
||||
}
|
||||
return merges
|
||||
}
|
||||
|
||||
function applyFreightMerges(
|
||||
built: string[][],
|
||||
freightIds: string[],
|
||||
transportCol: number,
|
||||
freightCol: number
|
||||
): LedgerMergeRange[] {
|
||||
const merges = buildFreightMergeRanges(freightIds, transportCol, freightCol)
|
||||
for (const merge of merges) {
|
||||
const colIndex = merge.startCol - 1
|
||||
for (let excelRow = merge.startRow + 1; excelRow <= merge.endRow; excelRow += 1) {
|
||||
const dataIndex = excelRow - 2
|
||||
if (dataIndex >= 0 && dataIndex < built.length) built[dataIndex][colIndex] = ''
|
||||
}
|
||||
}
|
||||
return merges
|
||||
}
|
||||
|
||||
/** 导出前加载作废类型字典,避免列表外的编码原样写入 Excel。 */
|
||||
export async function prepareCollectionLedgerExport(): Promise<void> {
|
||||
const { loadVoidTypeDict, getVoidTypeOptions } = await import('./void-type-dict')
|
||||
@@ -526,7 +551,7 @@ function storageLedgerRow(row: ProtocolAuditFlatRow, index: number, research: bo
|
||||
cells[6] = statusText(status, protocol.voidType)
|
||||
cells[7] = voidReason(protocol)
|
||||
cells[8] = text(row.motherName)
|
||||
cells[9] = text(row.protocolNo)
|
||||
cells[9] = text(row.sampleCode)
|
||||
cells[10] = text(row.sampleTypeName)
|
||||
cells[11] = row.isCoStored ? '是' : '否'
|
||||
cells[12] = text(row.regionName)
|
||||
@@ -557,12 +582,19 @@ function storageLedgerRow(row: ProtocolAuditFlatRow, index: number, research: bo
|
||||
}
|
||||
cells[50] = text(row.salesUserName)
|
||||
cells[51] = text(row.regionManagerUserName)
|
||||
cells[52] = transportText(row)
|
||||
cells[52] = researchTransportText(row)
|
||||
cells[53] = researchFreightText(row)
|
||||
return cells
|
||||
}
|
||||
|
||||
export function buildCellStorageLedgerRows(rows: ProtocolAuditFlatRow[]): string[][] {
|
||||
return rows.map((row, index) => storageLedgerRow(row, index, false))
|
||||
export function buildCellStorageLedgerRows(rows: ProtocolAuditFlatRow[]): {
|
||||
rows: string[][]
|
||||
merges: LedgerMergeRange[]
|
||||
} {
|
||||
const freightIds = rows.map((row) => text(row.freightId))
|
||||
const built = rows.map((row, index) => storageLedgerRow(row, index, false))
|
||||
// 自存表:运输方式=53,运费=54
|
||||
return { rows: built, merges: applyFreightMerges(built, freightIds, 53, 54) }
|
||||
}
|
||||
|
||||
export function buildResearchLedgerRows(rows: ProtocolAuditFlatRow[]): {
|
||||
@@ -571,17 +603,8 @@ export function buildResearchLedgerRows(rows: ProtocolAuditFlatRow[]): {
|
||||
} {
|
||||
const freightIds = rows.map((row) => text(row.freightId))
|
||||
const built = rows.map((row, index) => storageLedgerRow(row, index, true))
|
||||
const merges = buildFreightMergeRanges(freightIds)
|
||||
// 合并区域内仅保留首行运输/运费,其余清空,避免合并后重复显示
|
||||
for (const merge of merges) {
|
||||
if (merge.startCol !== 17 && merge.startCol !== 18) continue
|
||||
const colIndex = merge.startCol - 1
|
||||
for (let excelRow = merge.startRow + 1; excelRow <= merge.endRow; excelRow += 1) {
|
||||
const dataIndex = excelRow - 2
|
||||
if (dataIndex >= 0 && dataIndex < built.length) built[dataIndex][colIndex] = ''
|
||||
}
|
||||
}
|
||||
return { rows: built, merges }
|
||||
// 科研表:运输方式=17,运费=18
|
||||
return { rows: built, merges: applyFreightMerges(built, freightIds, 17, 18) }
|
||||
}
|
||||
|
||||
export function buildCustomerServiceLedgerRows(rows: Row[]): string[][] {
|
||||
|
||||
@@ -534,11 +534,13 @@
|
||||
ElMessage.warning('没有可导出的数据')
|
||||
return
|
||||
}
|
||||
const ledger = buildCellStorageLedgerRows(rows)
|
||||
await saveCollectionLedgerWorkbook(
|
||||
'自存表',
|
||||
CELL_STORAGE_HEADERS,
|
||||
buildCellStorageLedgerRows(rows),
|
||||
'自存表'
|
||||
ledger.rows,
|
||||
'自存表',
|
||||
ledger.merges
|
||||
)
|
||||
ElMessage.success('导出成功')
|
||||
} catch {
|
||||
|
||||
Reference in new issue
Block a user