diff --git a/apps/backend/src/services/order/order-service.test.ts b/apps/backend/src/services/order/order-service.test.ts index c1ba1b3c..bd091c6b 100644 --- a/apps/backend/src/services/order/order-service.test.ts +++ b/apps/backend/src/services/order/order-service.test.ts @@ -1,7 +1,42 @@ import test from 'node:test' import assert from 'node:assert/strict' -import { mergeSourceOrderState } from './order-service.js' +import { + isOrderItemConfiguredForFulfillment, + mergeSourceOrderState, +} from './order-service.js' +import type { OrderItemRow } from '../../types/repository/rows.js' + +function buildOrderItem(snapshot: OrderItemRow['item_snapshot_json']): OrderItemRow { + return { + id: 1, + order_id: 1, + sku_code: 'TEST-SKU', + sku_name: '测试商品', + quantity: 1, + spec_json: '{}', + item_snapshot_json: snapshot, + } +} + +test('isOrderItemConfiguredForFulfillment accepts PostgreSQL JSONB object results', () => { + assert.equal( + isOrderItemConfiguredForFulfillment(buildOrderItem({ isConfigured: true })), + true, + ) +}) + +test('isOrderItemConfiguredForFulfillment accepts serialized snapshots', () => { + assert.equal( + isOrderItemConfiguredForFulfillment(buildOrderItem('{"isConfigured":true}')), + true, + ) +}) + +test('isOrderItemConfiguredForFulfillment rejects unconfigured or malformed snapshots', () => { + assert.equal(isOrderItemConfiguredForFulfillment(buildOrderItem({})), false) + assert.equal(isOrderItemConfiguredForFulfillment(buildOrderItem('{invalid')), false) +}) test('mergeSourceOrderState keeps paid state when a late created source event arrives', () => { const merged = mergeSourceOrderState( diff --git a/apps/backend/src/services/order/order-service.ts b/apps/backend/src/services/order/order-service.ts index 1d03e159..32c1b421 100644 --- a/apps/backend/src/services/order/order-service.ts +++ b/apps/backend/src/services/order/order-service.ts @@ -283,17 +283,24 @@ export function mergeSourceOrderState( } } -function isOrderItemConfiguredForFulfillment(item: OrderItemRow): boolean { +export function isOrderItemConfiguredForFulfillment(item: OrderItemRow): boolean { + const rawSnapshot = item.item_snapshot_json + const snapshot = + rawSnapshot && typeof rawSnapshot === 'object' && !Array.isArray(rawSnapshot) + ? rawSnapshot + : parseOrderItemSnapshot(rawSnapshot) + + return Boolean(snapshot && snapshot.isConfigured === true) +} + +function parseOrderItemSnapshot(value: unknown): Record | null { try { - const snapshot = JSON.parse(String(item.item_snapshot_json || '{}')) - return Boolean( - snapshot && - typeof snapshot === 'object' && - !Array.isArray(snapshot) && - snapshot.isConfigured === true, - ) + const parsed = JSON.parse(String(value || '{}')) + return parsed && typeof parsed === 'object' && !Array.isArray(parsed) + ? parsed as Record + : null } catch { - return false + return null } }