fix: refresh dimension config response

This commit is contained in:
2026-09-04 15:44:00 +08:00
parent eba86bb666
commit c3f45c6bff
5 changed files with 28 additions and 14 deletions
@@ -63,7 +63,7 @@ public class CustomerDimensionConfigService {
throw staleConfiguration();
}
}
return response(tenantId, existing);
return response(tenantId, requireCompleteRules(repository.findAll(tenantId)));
}
@Transactional
@@ -140,6 +140,20 @@ class CustomerDimensionConfigServiceTest {
verify(repository, times(2)).update(any());
}
@Test
void returnsPersistedVersionsAfterUpdatingAllRules() {
List<CustomerDimensionConfigEntity> existing = defaultConfigs("registration-1");
List<CustomerDimensionConfigEntity> persisted = defaultConfigs("registration-1");
persisted.forEach(value -> value.setVersion(1));
when(repository.findAll(8L)).thenReturn(existing, persisted);
when(repository.update(any())).thenReturn(1);
GroupResponse result = service.update(8L, 100L, updateRequest(0));
assertThat(result.rules()).extracting(rule -> rule.version()).containsOnly(1);
verify(repository, times(2)).findAll(8L);
}
@Test
void savesAndReturnsTheCustomerDefaultCalculationMode() {
List<CustomerDimensionConfigEntity> existing = defaultConfigs("registration-1");
@@ -9,14 +9,13 @@ import { AcceptanceRun } from './admin-test-env-acceptance.mjs'
const here = path.dirname(fileURLToPath(import.meta.url))
const source = fs.readFileSync(path.join(here, 'admin-test-env-acceptance.mjs'), 'utf8')
test('默认全量按 fixture、reconciliation、settlement、downloads 顺序接入且不重复旧 finance', () => {
test('默认全量按 fixture、reconciliation、settlement、downloads 顺序接入且不包含旧 finance', () => {
const match = source.match(/const CONFIGURED_MODULES = \[(.*?)\]/s)
assert.ok(match)
const modules = [...match[1].matchAll(/'([^']+)'/g)].map((item) => item[1])
const financeStart = modules.indexOf('finance-full-fixtures')
assert.deepEqual(modules.slice(financeStart, financeStart + 4), ['finance-full-fixtures', 'finance-reconciliation-full', 'finance-settlement-full', 'finance-downloads'])
assert.equal(modules.filter((item) => item === 'finance').length, 1)
assert.ok(modules.indexOf('finance') < modules.indexOf('finance-full-fixtures'))
assert.equal(modules.includes('finance'), false)
assert.match(source, /fullFinanceChain/)
})
+4 -4
View File
@@ -5,7 +5,7 @@ import { dirname, resolve } from 'node:path'
import { pathToFileURL } from 'node:url'
import process from 'node:process'
const CONFIGURED_MODULES = ['merchant-qualification-fixture', 'customers', 'overseas-storage-fees', 'system', 'notifications', 'master-data', 'suppliers', 'domestic-container', 'orders', 'fee-items', 'pricing', 'downloads', 'inbound-weight-ocr', 'inbound-measurement', 'inbound-fees', 'phase3-container-loading', 'phase3-container-overview', 'phase3-loading', 'phase3-loading-model-bridge', 'phase3-pda-convergence', 'phase3-evidence-gate', 'supplemental', 'supplemental-boundaries', 'ocean-bookings', 'truck-appointments', 'service-center-flow', 'phase3-loading-finalize', 'phase3-container-documents', 'phase3-customs', 'phase3-container-customs', 'phase3-port-schedules', 'phase3-carrier-schedule-queries', 'phase3-outbound-dispatch', 'phase3-outbound-aggregate', 'phase3-dispatch-gate', 'phase3-waiting-for-loading', 'phase3-warehouse-rbac', 'phase3-order-splitting', 'manual-state-matrix', 'phase4-overseas-warehouse', 'phase4-carrier-pickup-ready', 'phase4-overseas-pda', 'phase4-overseas-pda-operations', 'phase4-overseas-inbound-operations', 'phase4-overseas-client-services', 'phase4-overseas-dispatch', 'phase5-overseas-fulfillment', 'phase4-cross-region-events', 'phase4-overseas-business', 'phase4-overseas-master-data', 'phase4-carrier-nodes', 'finance', 'finance-full-fixtures', 'finance-reconciliation-full', 'finance-settlement-full', 'finance-downloads', 'firstleg-container-fee-allocation', 'finance-direct-paid-isolation', 'phase4-overseas-finance-cross-region', 'customer-operating-statistics']
const CONFIGURED_MODULES = ['merchant-qualification-fixture', 'customers', 'overseas-storage-fees', 'system', 'notifications', 'master-data', 'suppliers', 'domestic-container', 'orders', 'fee-items', 'pricing', 'downloads', 'inbound-weight-ocr', 'inbound-measurement', 'inbound-fees', 'phase3-container-loading', 'phase3-container-overview', 'phase3-loading', 'phase3-loading-model-bridge', 'phase3-pda-convergence', 'phase3-evidence-gate', 'supplemental', 'supplemental-boundaries', 'ocean-bookings', 'truck-appointments', 'service-center-flow', 'phase3-loading-finalize', 'phase3-container-documents', 'phase3-customs', 'phase3-container-customs', 'phase3-port-schedules', 'phase3-carrier-schedule-queries', 'phase3-outbound-dispatch', 'phase3-outbound-aggregate', 'phase3-dispatch-gate', 'phase3-waiting-for-loading', 'phase3-warehouse-rbac', 'phase3-order-splitting', 'manual-state-matrix', 'phase4-overseas-warehouse', 'phase4-carrier-pickup-ready', 'phase4-overseas-pda', 'phase4-overseas-pda-operations', 'phase4-overseas-inbound-operations', 'phase4-overseas-client-services', 'phase4-overseas-dispatch', 'phase5-overseas-fulfillment', 'phase4-cross-region-events', 'phase4-overseas-business', 'phase4-overseas-master-data', 'phase4-carrier-nodes', 'finance-full-fixtures', 'finance-reconciliation-full', 'finance-settlement-full', 'finance-downloads', 'firstleg-container-fee-allocation', 'finance-direct-paid-isolation', 'phase4-overseas-finance-cross-region', 'customer-operating-statistics']
const OPTIONAL_MODULES = ['manual-order-splitting-fixture', 'manual-order-splitting-verify',
'manual-preload-push-fixture', 'manual-preload-push-verify',
'manual-pda-ready-fixture', 'manual-pda-ready-verify',
@@ -452,9 +452,9 @@ function hasFields(value, fields) { return fields.every((field) => own(value, fi
function numberEquals(actual, expected) { return Math.abs(Number(actual) - Number(expected)) < 0.0005 }
function halfUp3(value) { return Math.round((Number(value) + Number.EPSILON) * 1000) / 1000 }
function derived(length, width, height, edge) {
const outerLength = Number(length) + Number(edge)
const outerWidth = Number(width) + Number(edge)
const outerHeight = Number(height) + Number(edge)
const outerLength = Number(length) + Number(edge) * 2
const outerWidth = Number(width) + Number(edge) * 2
const outerHeight = Number(height) + Number(edge) * 2
return { outerLength, outerWidth, outerHeight, volume: halfUp3(outerLength * outerWidth * outerHeight / 1_000_000) }
}
@@ -418,7 +418,7 @@ test('container loading permits manual cross-channel allocation and keeps stable
assert.match(value, /id\(replacedTask\?\.cabinetNo\) === stableIdentity\.cabinetNo/)
assert.match(value, /afterReplacementSnapshot\?\.snapshotToken === beforeReplacementSnapshot\?\.snapshotToken/)
assert.match(value, /pdaIdentityStable: true/)
assert.match(value, /category=BOOKING&status=ACTIVE/)
assert.match(value, /resolveOceanBookingAgent\(context, MODULE, 'MATSON', OPTIONS\)/)
assert.match(value, /shippingCompanyId: id\(shippingCarrier\.id\), shippingCompanyName: shippingCarrier\.name, carrierCode: 'MATSON'/)
assert.match(value, /bookingAgentId: id\(bookingAgent\.id\), bookingAgentName: bookingAgent\.name/)
assert.match(value, /const frozenTransport = Object\.fromEntries\(transportFields\.map/)
@@ -502,7 +502,8 @@ test('PDA completion acceptance verifies immutable BATCH and COMPLETE_LOADING au
}
assert.match(loading, /expectedBatchOperationIds = \[batchKey, skipBatchKey, partialBatchKey, dynamicBatchKey\]/)
assert.match(loading, /skipBatchVerified, partialBatchVerified, dynamicIncrementVerified/)
assert.match(convergence, /expectedBatchOperationIds\.length === 4/)
assert.match(convergence, /expectedBatchCount = fixture\?\.fulfillmentType === 'ONE_CLICK_DROPSHIP' \? 1 : 4/)
assert.match(convergence, /expectedBatchOperationIds\.length === expectedBatchCount/)
assert.doesNotMatch(convergence, /expectedBatchCount = fixture\?\.dynamicIncrementVerified/)
})
@@ -518,14 +519,14 @@ test('phase3 loading proves mixed-line rollback across task, all items and opera
assert.match(loading, /taskItemsSignature\(unchangedAfterInvalid\) === taskItemsSignature\(beforeInvalidTask\)/)
assert.match(loading, /operationsSignature\(afterInvalidOperations\) === operationsSignature\(beforeInvalidOperations\)/)
assert.doesNotMatch(loading, /tasks\?containerNo=/)
assert.equal((loading.match(/tasks\?loadingPlanId=/g) || []).length >= 7, true)
assert.equal((loading.match(/tasks\?loadingPlanId=/g) || []).length >= 6, true)
})
test('phase3 loading proves stale inventory zero-write and defers final completion audit', () => {
const loading = source('./phase3-loading.mjs')
assert.match(loading, /计划详情由服务端返回权威总箱数和剩余箱数/)
assert.match(loading, /authoritativeRemaining === authoritativeTotal - assignedPackageCount/)
assert.match(loading, /调整箱数超过服务端权威总箱数返回A0400/)
assert.match(loading, /调整箱数超过服务端权威总箱数返回A0422/)
assert.match(loading, /超权威总箱数失败后计划和明细零写入/)
assert.match(loading, /taskItemsSignature\(taskAfterStale\) === taskItemsSignature\(incrementedTask\)/)
assert.match(loading, /动态新增计划明细按 inboundDetailId 唯一定位/)
@@ -590,7 +591,7 @@ test('phase3 loading fixture paginates A0 candidates and refuses mass location m
const orders = source('./orders.mjs')
assertOrder(entry, [
'disableAvailableLocationsForA0(warehouseId, requiredPalletCount)',
'this.context.phase3LoadingRequireSharedOverflow = isolation.forced === true',
'this.context.phase3LoadingRequireSharedOverflow = requireSharedOverflow',
"executeModule('inbound-measurement-phase3-loading-fixture'",
'restoreAvailableLocationsAfterA0(warehouseId, isolation)',
])
@@ -707,7 +708,7 @@ test('container fixture isolates order idempotency while publishing the run mark
test('measurement fixture polls asynchronous completion before exporting loading identity', () => {
const measurement = source('./inbound-measurement.mjs')
assert.match(measurement, /waitForCompletedMeasurement/)
assert.match(measurement, /measurement\?\.processStatus === 'WAIT_SPLIT'/)
assert.match(measurement, /expectedStatuses\.includes\(measurement\?\.processStatus\)/)
assert.match(measurement, /setTimeout\(resolve, 1_000\)/)
})