test: provision finance fixture prerequisites
This commit is contained in:
@@ -55,6 +55,13 @@ export function normalizeModuleOrder(modules) {
|
|||||||
normalized.splice(normalized.indexOf('phase3-loading') + 1, 0, 'phase3-order-splitting')
|
normalized.splice(normalized.indexOf('phase3-loading') + 1, 0, 'phase3-order-splitting')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const financeChain = ['finance-full-fixtures', 'finance-reconciliation-full', 'finance-settlement-full']
|
||||||
|
const firstFinanceIndex = normalized.findIndex((name) => financeChain.includes(name))
|
||||||
|
if (firstFinanceIndex >= 0) {
|
||||||
|
const prerequisites = ['merchant-qualification-fixture', 'orders', 'inbound-measurement', 'phase3-container-loading']
|
||||||
|
const missing = prerequisites.filter((name) => !normalized.includes(name))
|
||||||
|
if (missing.length > 0) normalized.splice(firstFinanceIndex, 0, ...missing)
|
||||||
|
}
|
||||||
if (normalized.includes('fee-items')) {
|
if (normalized.includes('fee-items')) {
|
||||||
const prerequisites = ['merchant-qualification-fixture', 'orders']
|
const prerequisites = ['merchant-qualification-fixture', 'orders']
|
||||||
const feeItemsIndex = normalized.indexOf('fee-items')
|
const feeItemsIndex = normalized.indexOf('fee-items')
|
||||||
|
|||||||
@@ -97,6 +97,10 @@ node scripts/admin-test-env-acceptance.mjs --modules suppliers
|
|||||||
node --test scripts/admin-test-env-modules/suppliers-contract.test.mjs
|
node --test scripts/admin-test-env-modules/suppliers-contract.test.mjs
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Finance 全链前置
|
||||||
|
|
||||||
|
`finance-full-fixtures` 会通过正式供应商 API 创建本轮隔离的 ACTIVE 供应商,并按费用项目类别校验后再生成 PAYABLE;不会使用历史供应商 ID。只请求 `finance-full-fixtures,finance-reconciliation-full,finance-settlement-full` 时,总入口会自动补齐 `merchant-qualification-fixture -> orders -> inbound-measurement -> phase3-container-loading`,确保 Finance 能消费本轮真实已完成柜次。若这些前置仍未产出同 marker 的完成柜次,Finance 会 fail-closed,不能把 finance-only 前置不足计为 Finance 业务成功。
|
||||||
|
|
||||||
## 国内整柜与客户运营统计
|
## 国内整柜与客户运营统计
|
||||||
|
|
||||||
- `domestic-container.mjs`:整柜入库、出库、库存列表和详情;静态契约为 `domestic-container-contract.test.mjs`。
|
- `domestic-container.mjs`:整柜入库、出库、库存列表和详情;静态契约为 `domestic-container-contract.test.mjs`。
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import assert from 'node:assert/strict'
|
|||||||
import fs from 'node:fs'
|
import fs from 'node:fs'
|
||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
import { fileURLToPath } from 'node:url'
|
import { fileURLToPath } from 'node:url'
|
||||||
import { buildContainerCostBody, normalizeFinanceFixtureSources, run,
|
import { buildContainerCostBody, buildSupplierBody, normalizeFinanceFixtureSources, run,
|
||||||
supplierCategoryForFeeItem } from './finance-full-fixtures.mjs'
|
supplierCategoryForFeeItem } from './finance-full-fixtures.mjs'
|
||||||
|
|
||||||
const here = path.dirname(fileURLToPath(import.meta.url))
|
const here = path.dirname(fileURLToPath(import.meta.url))
|
||||||
@@ -40,6 +40,16 @@ test('PAYABLE fee selection requires the formal flag and a Finance-supported sup
|
|||||||
assert.equal(supplierCategoryForFeeItem({ feeCode: 'UNSUPPORTED', feeName: '操作费' }), null)
|
assert.equal(supplierCategoryForFeeItem({ feeCode: 'UNSUPPORTED', feeName: '操作费' }), null)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('Finance PAYABLE fixture creates an isolated ACTIVE supplier through the formal API', () => {
|
||||||
|
const body = buildSupplierBody('ADMIN-API-20260815-FINANCE', 'BOOKING')
|
||||||
|
assert.equal(body.category, 'BOOKING')
|
||||||
|
assert.equal(body.scope, 'DOMESTIC')
|
||||||
|
assert.equal(body.status, 'ACTIVE')
|
||||||
|
assert.match(body.name, /^财务验收供应商-/)
|
||||||
|
assert.match(source, /POST', '\/api\/platform\/v1\/suppliers\?tenantId=0'/)
|
||||||
|
assert.match(source, /detail\?\.status === 'ACTIVE' && detail\?\.category === category/)
|
||||||
|
})
|
||||||
|
|
||||||
test('automatic fee fixture completes the reviewed pricing workflow without recalculating', () => {
|
test('automatic fee fixture completes the reviewed pricing workflow without recalculating', () => {
|
||||||
assert.match(source, /latest\?\.feeId/)
|
assert.match(source, /latest\?\.feeId/)
|
||||||
assert.doesNotMatch(source, /latest\?\.id/)
|
assert.doesNotMatch(source, /latest\?\.id/)
|
||||||
|
|||||||
@@ -36,6 +36,19 @@ export function supplierCategoryForFeeItem(item) {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function buildSupplierBody(marker, category) {
|
||||||
|
const suffix = String(marker || '').replace(/[^0-9A-Za-z]/g, '').slice(-24)
|
||||||
|
return {
|
||||||
|
name: `财务验收供应商-${suffix}`,
|
||||||
|
category,
|
||||||
|
...(category === 'BOOKING' ? { scope: 'DOMESTIC' } : {}),
|
||||||
|
region: '宁波', legalRepresentative: '财务验收法人', contactName: '财务验收联系人',
|
||||||
|
phone: '13800000000', email: `finance-supplier-${suffix || 'fixture'}@example.com`,
|
||||||
|
address: '财务验收地址', businessLicenseNo: `FIN${suffix}`.slice(0, 64),
|
||||||
|
licenseFileId: null, status: 'ACTIVE', settlementPeriod: 'MONTHLY_30', remark: marker,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function createOrder(context, suffix, pricingProfile = null) {
|
async function createOrder(context, suffix, pricingProfile = null) {
|
||||||
if (context.financeFixtureHooks?.createOrder) return context.financeFixtureHooks.createOrder(context, suffix, pricingProfile)
|
if (context.financeFixtureHooks?.createOrder) return context.financeFixtureHooks.createOrder(context, suffix, pricingProfile)
|
||||||
const originalMarker = context.marker
|
const originalMarker = context.marker
|
||||||
@@ -230,10 +243,19 @@ async function loadPublishedAutomaticFee(context, resource) {
|
|||||||
async function loadSupplier(context, category) {
|
async function loadSupplier(context, category) {
|
||||||
if (context.financeFixtureHooks?.loadSupplier) return context.financeFixtureHooks.loadSupplier(context, category)
|
if (context.financeFixtureHooks?.loadSupplier) return context.financeFixtureHooks.loadSupplier(context, category)
|
||||||
if (!category) return null
|
if (!category) return null
|
||||||
const path = `/api/platform/v1/suppliers?page=1&size=200&status=ACTIVE&category=${encodeURIComponent(category)}`
|
const body = buildSupplierBody(context.marker, category)
|
||||||
const response = await context.request('GET', path)
|
const response = await request(context, 'POST', '/api/platform/v1/suppliers?tenantId=0', body)
|
||||||
const page = context.apiSuccess(MODULE, '查询 Warehouse ACTIVE 费用类别供应商', response, `category=${category}`, OPTIONS)
|
const supplierId = id(context.apiSuccess(MODULE, '创建本轮 Finance 隔离供应商', response,
|
||||||
return rows(page).find((item) => item?.status === 'ACTIVE' && item?.category === category && item?.id && item?.name) || null
|
`category=${category}`, OPTIONS))
|
||||||
|
if (!supplierId) return null
|
||||||
|
const detailResponse = await context.request('GET', `/api/platform/v1/suppliers/${encodeURIComponent(supplierId)}?tenantId=0`)
|
||||||
|
const detail = context.apiSuccess(MODULE, '回读本轮 Finance 隔离供应商', detailResponse,
|
||||||
|
`supplierId=${supplierId}`, OPTIONS)
|
||||||
|
context.assert(MODULE, 'Finance 供应商为 ACTIVE 且类别匹配费用项目', detailResponse,
|
||||||
|
id(detail?.id) === supplierId && detail?.status === 'ACTIVE' && detail?.category === category
|
||||||
|
&& detail?.name === body.name,
|
||||||
|
`supplierId=${supplierId}, status=${detail?.status || 'missing'}, category=${detail?.category || 'missing'}`, OPTIONS)
|
||||||
|
return detail?.status === 'ACTIVE' && detail?.category === category ? detail : null
|
||||||
}
|
}
|
||||||
|
|
||||||
async function uploadVoucher(context, customerId) {
|
async function uploadVoucher(context, customerId) {
|
||||||
|
|||||||
@@ -135,3 +135,11 @@ test('default Admin HTTP entry carries one marker-isolated loading chain through
|
|||||||
assert.match(finalize, /统一硬门禁通过后推进 LOADED 且五类有效证据仍完整/)
|
assert.match(finalize, /统一硬门禁通过后推进 LOADED 且五类有效证据仍完整/)
|
||||||
assert.match(finalize, /统一订单详情投影同一订单为 LOADED[\s\S]*统一订单列表投影同一订单为 LOADED/)
|
assert.match(finalize, /统一订单详情投影同一订单为 LOADED[\s\S]*统一订单列表投影同一订单为 LOADED/)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('Finance-only HTTP entry expands the completed-container producer prerequisites', () => {
|
||||||
|
const focused = normalizeModuleOrder(['finance-full-fixtures', 'finance-reconciliation-full', 'finance-settlement-full'])
|
||||||
|
assert.deepEqual(focused.slice(0, 4), [
|
||||||
|
'merchant-qualification-fixture', 'orders', 'inbound-measurement', 'phase3-container-loading',
|
||||||
|
])
|
||||||
|
assert.deepEqual(focused.slice(-3), ['finance-full-fixtures', 'finance-reconciliation-full', 'finance-settlement-full'])
|
||||||
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user