@dainamite/cpq 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/MIGRATION.md +31 -1
  3. package/README.md +17 -12
  4. package/dist/modules/cpq/api/product-attributes/route.js +28 -53
  5. package/dist/modules/cpq/api/product-attributes/route.js.map +2 -2
  6. package/dist/modules/cpq/api/product-charges/route.js +12 -2
  7. package/dist/modules/cpq/api/product-charges/route.js.map +2 -2
  8. package/dist/modules/cpq/api/product-offerings/route.js +2 -0
  9. package/dist/modules/cpq/api/product-offerings/route.js.map +2 -2
  10. package/dist/modules/cpq/backend/cpq/offerings/[id]/page.js +128 -59
  11. package/dist/modules/cpq/backend/cpq/offerings/[id]/page.js.map +2 -2
  12. package/dist/modules/cpq/backend/cpq/pricing/[id]/EntriesEditor.js +11 -11
  13. package/dist/modules/cpq/backend/cpq/pricing/[id]/EntriesEditor.js.map +2 -2
  14. package/dist/modules/cpq/backend/cpq/specifications/[id]/page.js +55 -1
  15. package/dist/modules/cpq/backend/cpq/specifications/[id]/page.js.map +2 -2
  16. package/dist/modules/cpq/cli.js +58 -4
  17. package/dist/modules/cpq/cli.js.map +3 -3
  18. package/dist/modules/cpq/data/charge-pricing.js +12 -0
  19. package/dist/modules/cpq/data/charge-pricing.js.map +7 -0
  20. package/dist/modules/cpq/data/entities.js +3 -0
  21. package/dist/modules/cpq/data/entities.js.map +2 -2
  22. package/dist/modules/cpq/data/validators.js +36 -15
  23. package/dist/modules/cpq/data/validators.js.map +2 -2
  24. package/dist/modules/cpq/events.js +7 -0
  25. package/dist/modules/cpq/events.js.map +2 -2
  26. package/dist/modules/cpq/lib/seeds/demo.js +103 -0
  27. package/dist/modules/cpq/lib/seeds/demo.js.map +7 -0
  28. package/dist/modules/cpq/migrations/Migration20260602000000_cpq_charge_model_split.js +28 -0
  29. package/dist/modules/cpq/migrations/Migration20260602000000_cpq_charge_model_split.js.map +7 -0
  30. package/dist/modules/cpq/services/cpqOrderService.js +130 -76
  31. package/dist/modules/cpq/services/cpqOrderService.js.map +2 -2
  32. package/dist/modules/cpq/services/cpqPricingService.js +53 -29
  33. package/dist/modules/cpq/services/cpqPricingService.js.map +3 -3
  34. package/dist/modules/cpq/services/types.js.map +2 -2
  35. package/package.json +4 -2
  36. package/src/modules/cpq/__integration__/TC-CPQ-012.spec.ts +34 -4
  37. package/src/modules/cpq/__tests__/package-isolation.test.ts +8 -3
  38. package/src/modules/cpq/api/product-attributes/__tests__/serializeAttribute.test.ts +62 -0
  39. package/src/modules/cpq/api/product-attributes/route.ts +34 -52
  40. package/src/modules/cpq/api/product-charges/route.ts +23 -2
  41. package/src/modules/cpq/api/product-offerings/route.ts +2 -0
  42. package/src/modules/cpq/backend/cpq/offerings/[id]/page.tsx +151 -62
  43. package/src/modules/cpq/backend/cpq/pricing/[id]/EntriesEditor.tsx +24 -22
  44. package/src/modules/cpq/backend/cpq/specifications/[id]/page.tsx +73 -1
  45. package/src/modules/cpq/cli.ts +83 -4
  46. package/src/modules/cpq/data/__tests__/charge-pricing.test.ts +49 -0
  47. package/src/modules/cpq/data/__tests__/validators.test.ts +60 -5
  48. package/src/modules/cpq/data/charge-pricing.ts +48 -0
  49. package/src/modules/cpq/data/entities.ts +10 -0
  50. package/src/modules/cpq/data/validators.ts +56 -26
  51. package/src/modules/cpq/events.ts +11 -3
  52. package/src/modules/cpq/lib/seeds/__tests__/demo.test.ts +78 -0
  53. package/src/modules/cpq/lib/seeds/demo.ts +145 -0
  54. package/src/modules/cpq/migrations/.snapshot-open-mercato.json +16 -0
  55. package/src/modules/cpq/migrations/Migration20260602000000_cpq_charge_model_split.ts +57 -0
  56. package/src/modules/cpq/services/__tests__/cpqPricingService.test.ts +172 -1
  57. package/src/modules/cpq/services/cpqOrderService.ts +214 -103
  58. package/src/modules/cpq/services/cpqPricingService.ts +82 -34
  59. package/src/modules/cpq/services/types.ts +4 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/cpq/services/cpqOrderService.ts"],
4
- "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { SalesOrder, SalesOrderLine } from '@open-mercato/core/modules/sales/data/entities'\nimport type { SalesDocumentNumberGenerator } from '@open-mercato/core/modules/sales/services/salesDocumentNumberGenerator'\nimport {\n CpqQuoteConfiguration,\n CpqQuoteLineConfiguration,\n CpqQuoteTargetSubscription,\n CpqInventorySubscription,\n CpqOrderConfiguration,\n CpqOrderLineConfiguration,\n CpqProductOffering,\n CpqProductSpecification,\n CpqInventorySubscriptionItem,\n} from '../data/entities'\nimport type {\n ApplyArcResult,\n ApplyMergeRenewalResult,\n ArcLineChange,\n CreateSubscriptionItemInput,\n DefaultCpqInventoryService,\n} from './cpqInventoryService'\nimport {\n CPQ_ORDER_TRANSITIONS,\n type CpqOrderStatus,\n type TenantScope,\n} from './types'\nimport { emitCpqEvent } from '../events'\n\n// \u2500\u2500\u2500 Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ninterface OrderListFilters {\n customerId?: string\n sourceQuoteId?: string\n salesOrderId?: string\n cpqStatus?: string | string[]\n currencyCode?: string\n search?: string\n sortField?: 'createdAt' | 'updatedAt' | 'cpqStatus' | 'activatedAt' | 'currencyCode'\n sortDir?: 'asc' | 'desc'\n page?: number\n pageSize?: number\n}\n\ninterface PaginatedResult<T> {\n items: T[]\n total: number\n page: number\n pageSize: number\n totalPages: number\n}\n\ninterface OrderLineResult {\n lineId: string\n offeringId: string | null\n offeringName: string\n offeringType: string | null\n productId: string | null\n specId: string | null\n action: string\n parentLineId: string | null\n quantity: number\n configuration: Record<string, unknown>\n startDate: string | null\n termMonths: number | null\n endDate: string | null\n nrcTotal: number\n mrcTotal: number\n charges: Array<Record<string, unknown>>\n sourceQuoteLineId: string | null\n /** XD-250 ARC: snapshot of the source subscription item's current state.\n * Null for non-ARC lines and for fresh `add` lines on amend quotes. */\n arcSource: {\n subscriptionItemId: string\n name: string\n mrcAmount: number\n nrcAmount: number\n quantity: number\n } | null\n}\n\ninterface OrderResult {\n id: string\n orderId: string\n orderNumber: string\n customerId: string\n cpqStatus: string\n sourceQuoteId: string | null\n /** XD-250 ARC quote type carried over from the source quote \u2014 used by the\n * order detail page to render the AMEND / RENEW / CANCEL badge. */\n quoteType: string\n currencyCode: string\n pricingSummary: {\n nrcTotal: number\n mrcTotal: number\n currencyCode: string\n }\n activatedAt: string | null\n createdAt: string\n lines: OrderLineResult[]\n}\n\ninterface OrderServiceDeps {\n em: EntityManager\n cpqInventoryService: DefaultCpqInventoryService\n salesDocumentNumberGenerator: SalesDocumentNumberGenerator\n}\n\nconst CPQ_TO_SALES_ORDER_STATUS: Record<CpqOrderStatus, string> = {\n draft: 'draft',\n pending_activation: 'pending',\n active: 'active',\n cancelled: 'cancelled',\n fulfilled: 'fulfilled',\n}\n\n// \u2500\u2500\u2500 Service \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport class DefaultCpqOrderService {\n private em: EntityManager\n private inventoryService: DefaultCpqInventoryService\n private numberGenerator: SalesDocumentNumberGenerator\n\n constructor(deps: OrderServiceDeps) {\n this.em = deps.em\n this.inventoryService = deps.cpqInventoryService\n this.numberGenerator = deps.salesDocumentNumberGenerator\n }\n\n // \u2500\u2500\u2500 Convert Quote to Order \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async convertQuoteToOrder(quoteId: string, scope: TenantScope): Promise<OrderResult> {\n const em = this.em\n\n const cpqConfig = await em.findOne(CpqQuoteConfiguration, {\n id: quoteId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n if (!cpqConfig) {\n const byQuoteId = await em.findOne(CpqQuoteConfiguration, {\n quoteId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n if (!byQuoteId) throw new OrderError(404, 'Quote not found')\n return this.convertFromConfig(byQuoteId, scope)\n }\n return this.convertFromConfig(cpqConfig, scope)\n }\n\n private async convertFromConfig(cpqConfig: CpqQuoteConfiguration, scope: TenantScope): Promise<OrderResult> {\n const em = this.em\n\n const blockedStatuses = ['cancelled', 'rejected']\n if (blockedStatuses.includes(cpqConfig.cpqStatus)) {\n throw new OrderError(409, `Cannot convert a quote in '${cpqConfig.cpqStatus}' status`)\n }\n\n // Idempotency guard: a quote may only convert once. If an order with\n // this quote as its `sourceQuoteId` already exists, the UI should\n // surface \"Go to order\" instead of re-running conversion \u2014 return\n // 409 with the existing id so the caller can redirect.\n const existingOrder = await em.findOne(CpqOrderConfiguration, {\n sourceQuoteId: cpqConfig.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n if (existingOrder) {\n throw new OrderError(\n 409,\n `Quote has already been converted to order ${existingOrder.orderId}`,\n )\n }\n\n const quoteLines = await em.find(CpqQuoteLineConfiguration, {\n quoteConfigurationId: cpqConfig.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n\n if (quoteLines.length === 0) {\n throw new OrderError(422, 'Cannot convert a quote with no line items')\n }\n\n const { number: orderNumber } = await this.numberGenerator.generate({\n kind: 'order',\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n })\n\n const salesOrderId = crypto.randomUUID()\n const pricingSummary = cpqConfig.pricingSummary as Record<string, unknown> | null\n const nrcTotal = Number(pricingSummary?.nrcTotal ?? 0)\n const mrcTotal = Number(pricingSummary?.mrcTotal ?? 0)\n const grandTotal = nrcTotal + mrcTotal\n\n const salesOrder = em.create(SalesOrder, {\n id: salesOrderId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n orderNumber,\n customerEntityId: cpqConfig.customerId,\n currencyCode: cpqConfig.currencyCode,\n status: 'draft',\n subtotalNetAmount: String(grandTotal),\n subtotalGrossAmount: String(grandTotal),\n discountTotalAmount: '0',\n taxTotalAmount: '0',\n shippingNetAmount: '0',\n shippingGrossAmount: '0',\n surchargeTotalAmount: '0',\n paidTotalAmount: '0',\n grandTotalNetAmount: String(grandTotal),\n grandTotalGrossAmount: String(grandTotal),\n lineItemCount: quoteLines.length,\n metadata: { sourceQuoteId: cpqConfig.id },\n createdAt: new Date(),\n updatedAt: new Date(),\n } as never)\n em.persist(salesOrder)\n\n const cpqOrderConfigId = crypto.randomUUID()\n const cpqOrderConfig = em.create(CpqOrderConfiguration, {\n id: cpqOrderConfigId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n orderId: salesOrderId,\n sourceQuoteId: cpqConfig.id,\n customerId: cpqConfig.customerId,\n cpqStatus: 'draft',\n currencyCode: cpqConfig.currencyCode,\n pricingSummary: {\n nrcTotal,\n mrcTotal,\n currencyCode: cpqConfig.currencyCode,\n },\n })\n em.persist(cpqOrderConfig)\n\n const quoteLineIdToOrderLineId = new Map<string, string>()\n\n for (let i = 0; i < quoteLines.length; i++) {\n const ql = quoteLines[i]\n\n const perInstanceNrc = Number(ql.nrcTotal)\n const perInstanceMrc = Number(ql.mrcTotal)\n const unitPriceNet = (perInstanceNrc + perInstanceMrc) / ql.quantity\n const totalNet = perInstanceNrc + perInstanceMrc\n\n const orderLineId = crypto.randomUUID()\n quoteLineIdToOrderLineId.set(ql.quoteLineId, orderLineId)\n\n const offeringName = await this.resolveOfferingName(\n em,\n ql.offeringId,\n scope,\n ql.configuration as Record<string, unknown> | null,\n )\n\n const orderLine = em.create(SalesOrderLine, {\n id: orderLineId,\n order: salesOrder,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n lineNumber: i + 1,\n kind: 'product',\n productId: ql.productId ?? null,\n name: offeringName,\n quantity: String(ql.quantity),\n unitPriceNet: String(unitPriceNet),\n unitPriceGross: String(unitPriceNet),\n totalNetAmount: String(totalNet),\n totalGrossAmount: String(totalNet),\n discountAmount: '0',\n discountPercent: '0',\n taxRate: '0',\n taxAmount: '0',\n currencyCode: cpqConfig.currencyCode,\n configuration: {\n offeringId: ql.offeringId ?? null,\n action: ql.action,\n nrcTotal: perInstanceNrc,\n mrcTotal: perInstanceMrc,\n parentLineId: ql.parentLineId ?? null,\n },\n normalizedQuantity: String(ql.quantity),\n reservedQuantity: '0',\n fulfilledQuantity: '0',\n invoicedQuantity: '0',\n returnedQuantity: '0',\n createdAt: new Date(),\n updatedAt: new Date(),\n } as never)\n em.persist(orderLine)\n const orderLineConfig = em.create(CpqOrderLineConfiguration, {\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n orderLineId,\n orderConfigurationId: cpqOrderConfig.id,\n offeringId: ql.offeringId ?? null,\n productId: ql.productId ?? null,\n specId: ql.specId ?? null,\n action: ql.action,\n parentLineId: null,\n startDate: ql.startDate,\n termMonths: ql.termMonths ?? null,\n endDate: ql.endDate,\n configuration: { ...ql.configuration, offeringName },\n quantity: ql.quantity,\n charges: ql.charges ? [...ql.charges] : null,\n nrcTotal: ql.nrcTotal,\n mrcTotal: ql.mrcTotal,\n sourceQuoteLineId: ql.quoteLineId,\n })\n em.persist(orderLineConfig)\n }\n\n await em.flush()\n\n // Resolve parent line references using the quote\u2192order line ID map\n const orderLineConfigs = await em.find(CpqOrderLineConfiguration, {\n orderConfigurationId: cpqOrderConfig.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n })\n for (const olc of orderLineConfigs) {\n if (olc.sourceQuoteLineId) {\n const sourceQl = quoteLines.find((ql) => ql.quoteLineId === olc.sourceQuoteLineId)\n if (sourceQl?.parentLineId) {\n const mappedParent = quoteLineIdToOrderLineId.get(sourceQl.parentLineId)\n if (mappedParent) olc.parentLineId = mappedParent\n }\n }\n }\n\n await em.flush()\n\n return this.buildOrderResult(em, cpqOrderConfig, salesOrder, orderLineConfigs, scope)\n }\n\n // \u2500\u2500\u2500 Activate Order \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async activateOrder(orderId: string, scope: TenantScope): Promise<OrderResult> {\n const em = this.em\n\n const orderConfig = await this.loadOrderConfig(em, orderId, scope)\n\n const currentStatus = orderConfig.cpqStatus as CpqOrderStatus\n if (currentStatus !== 'draft' && currentStatus !== 'pending_activation') {\n throw new OrderError(409, `Cannot activate order in '${currentStatus}' status`)\n }\n\n const salesOrder = await this.loadSalesOrder(em, orderConfig.orderId, scope)\n const orderLines = await em.find(CpqOrderLineConfiguration, {\n orderConfigurationId: orderConfig.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n\n // Branch on the source quote's quoteType. ARC quotes mutate existing\n // subscriptions; new-sale quotes materialize a fresh subscription.\n const sourceQuote = orderConfig.sourceQuoteId\n ? await em.findOne(CpqQuoteConfiguration, {\n id: orderConfig.sourceQuoteId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n : null\n\n const quoteType = sourceQuote?.quoteType ?? 'new'\n const arcEvents: Array<() => Promise<void>> = []\n\n if (quoteType === 'new') {\n await this.createInventoryFromOrder(orderConfig, orderLines, scope)\n } else {\n arcEvents.push(\n ...(await this.applyArcOrder(orderConfig, sourceQuote!, orderLines, scope)),\n )\n }\n\n orderConfig.cpqStatus = 'active'\n orderConfig.activatedAt = new Date()\n salesOrder.status = 'active'\n salesOrder.placedAt = new Date()\n\n await em.flush()\n\n // Emit ARC events AFTER the activation transaction commits.\n for (const fire of arcEvents) {\n try {\n await fire()\n } catch (err) {\n // Don't roll back the activation just because a subscriber blew up \u2014\n // events are best-effort once the DB state is committed.\n console.error('[cpq.arc] event emission failed', err)\n }\n }\n\n return this.buildOrderResult(em, orderConfig, salesOrder, orderLines, scope)\n }\n\n // \u2500\u2500\u2500 ARC activation branch (XD-250) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n //\n // Reads the source quote's targets + lines, then dispatches per target to\n // cpqInventoryService.apply* primitives. Returns the list of post-commit\n // event emissions so the caller can fire them outside the activation TX.\n private async applyArcOrder(\n orderConfig: CpqOrderConfiguration,\n sourceQuote: CpqQuoteConfiguration,\n orderLines: CpqOrderLineConfiguration[],\n scope: TenantScope,\n ): Promise<Array<() => Promise<void>>> {\n const em = this.em\n const quoteType = sourceQuote.quoteType as 'amend' | 'renew' | 'cancel'\n\n // Load attached targets.\n const targets = await em.find(CpqQuoteTargetSubscription, {\n quoteId: sourceQuote.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n if (targets.length === 0) {\n throw new OrderError(\n 409,\n `ARC quote ${sourceQuote.id} has no target subscriptions to activate`,\n )\n }\n\n const inventory = this.inventoryService\n\n // Group order lines by target_subscription_id \u2014 ARC order lines mirror the\n // quote lines' ARC fields (target_subscription_id is preserved through\n // convertQuoteToOrder via line.configuration when needed).\n // Source quote lines are the source of truth for ARC line data because\n // CpqOrderLineConfiguration doesn't carry target/source-item fields.\n const sourceQuoteLines = await em.find(CpqQuoteLineConfiguration, {\n quoteConfigurationId: sourceQuote.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n const linesByTarget = new Map<string | null, CpqQuoteLineConfiguration[]>()\n for (const line of sourceQuoteLines) {\n const key = line.targetSubscriptionId ?? null\n const list = linesByTarget.get(key) ?? []\n list.push(line)\n linesByTarget.set(key, list)\n }\n\n // Look up specs for `add` lines so we can build itemInput correctly.\n const specIds = [\n ...new Set(sourceQuoteLines.map((l) => l.specId).filter((x): x is string => Boolean(x))),\n ]\n const specs =\n specIds.length > 0\n ? await em.find(CpqProductSpecification, {\n id: { $in: specIds },\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n : []\n const specMap = new Map(specs.map((s) => [s.id, s]))\n\n const events: Array<() => Promise<void>> = []\n const eventBaseEnvelope = {\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n sourceQuoteId: sourceQuote.id,\n sourceOrderId: orderConfig.id,\n } as const\n\n if (quoteType === 'amend') {\n for (const target of targets) {\n const lines = linesByTarget.get(target.subscriptionId) ?? []\n const result = await inventory.applyAmendment(\n {\n subscriptionId: target.subscriptionId,\n sourceQuoteId: sourceQuote.id,\n sourceOrderId: orderConfig.id,\n performedByUserId: null,\n lineChanges: lines.map((l) => buildArcLineChange(l, specMap)),\n },\n scope,\n )\n events.push(() =>\n emitArcEvent('cpq.subscription.amended', result, eventBaseEnvelope),\n )\n }\n } else if (quoteType === 'cancel') {\n for (const target of targets) {\n const result = await inventory.applyCancel(\n {\n subscriptionId: target.subscriptionId,\n sourceQuoteId: sourceQuote.id,\n sourceOrderId: orderConfig.id,\n performedByUserId: null,\n etfAmount: sourceQuote.arcEtfAmount ?? null,\n etfCurrency: sourceQuote.arcEtfCurrency ?? null,\n reasonCode: sourceQuote.arcReasonCode ?? null,\n reasonText: sourceQuote.arcReasonText ?? null,\n },\n scope,\n )\n events.push(() =>\n emitArcEvent('cpq.subscription.cancelled', result, {\n ...eventBaseEnvelope,\n etfAmount: sourceQuote.arcEtfAmount ?? null,\n etfCurrency: sourceQuote.arcEtfCurrency ?? null,\n reasonCode: sourceQuote.arcReasonCode ?? null,\n reasonText: sourceQuote.arcReasonText ?? null,\n }),\n )\n }\n } else if (quoteType === 'renew') {\n const standalone = targets.filter((t) => t.mergeAction === 'standalone')\n const absorb = targets.filter((t) => t.mergeAction === 'absorb')\n\n if (standalone.length > 0 && absorb.length === 0) {\n for (const target of standalone) {\n if (!target.newTermStart || !target.newTermEnd) {\n throw new OrderError(\n 409,\n `Standalone renew target ${target.id} missing newTermStart/newTermEnd`,\n )\n }\n const lines = linesByTarget.get(target.subscriptionId) ?? []\n const result = await inventory.applyRenewal(\n {\n subscriptionId: target.subscriptionId,\n sourceQuoteId: sourceQuote.id,\n sourceOrderId: orderConfig.id,\n performedByUserId: null,\n term: {\n newTermStart: target.newTermStart,\n newTermEnd: target.newTermEnd,\n newTermMonths: target.newTermMonths ?? null,\n },\n lineChanges: lines.map((l) => buildArcLineChange(l, specMap)),\n },\n scope,\n )\n events.push(() =>\n emitArcEvent('cpq.subscription.renewed', result, {\n ...eventBaseEnvelope,\n term: {\n newTermStart: target.newTermStart!.toISOString(),\n newTermEnd: target.newTermEnd!.toISOString(),\n },\n }),\n )\n }\n } else if (absorb.length >= 2 && standalone.length === 0) {\n if (!sourceQuote.arcMergeNewTermStart || !sourceQuote.arcMergeNewTermEnd) {\n throw new OrderError(\n 409,\n `Merge renew quote ${sourceQuote.id} missing arc_merge_new_term_*`,\n )\n }\n // Lines targeting null are operator-edits on the new merge sub M.\n const mergeLines = linesByTarget.get(null) ?? []\n const merge = await inventory.applyMergeRenewal(\n {\n sourceIds: absorb.map((t) => t.subscriptionId),\n sourceQuoteId: sourceQuote.id,\n sourceOrderId: orderConfig.id,\n performedByUserId: null,\n term: {\n newTermStart: sourceQuote.arcMergeNewTermStart,\n newTermEnd: sourceQuote.arcMergeNewTermEnd,\n newTermMonths: sourceQuote.arcMergeNewTermMonths ?? null,\n },\n mergeMeta: {\n newSubCode: sourceQuote.arcMergeNewSubCode ?? null,\n newSubName: sourceQuote.arcMergeNewSubName ?? null,\n },\n lineChanges: mergeLines.map((l) => buildArcLineChange(l, specMap)),\n },\n scope,\n )\n\n // One event per source (`superseded`) + one for M (`merged`).\n const mergedFromSubscriptionIds = absorb.map((t) => t.subscriptionId)\n events.push(() =>\n emitMergedEvent(merge, eventBaseEnvelope, mergedFromSubscriptionIds),\n )\n for (const sourceLog of merge.sourceChangeLogs) {\n events.push(() =>\n emitSupersededEvent(sourceLog, merge.mergedSubscription.id, eventBaseEnvelope),\n )\n }\n } else {\n throw new OrderError(\n 409,\n `Inconsistent renew targets \u2014 must be either single standalone or \u22652 absorb`,\n )\n }\n } else {\n throw new OrderError(500, `Unknown quote_type '${quoteType}' for ARC activation`)\n }\n\n return events\n }\n\n // \u2500\u2500\u2500 Inventory Creation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private async createInventoryFromOrder(\n orderConfig: CpqOrderConfiguration,\n orderLines: CpqOrderLineConfiguration[],\n scope: TenantScope,\n ): Promise<void> {\n if (orderLines.length === 0) return\n\n const specIds = [...new Set(orderLines.map((l) => l.specId).filter(Boolean))] as string[]\n const specs = specIds.length > 0\n ? await this.em.find(CpqProductSpecification, {\n id: { $in: specIds },\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n : []\n const specMap = new Map(specs.map((s) => [s.id, s]))\n\n const subscriptionLines = orderLines.filter((l) => {\n const spec = l.specId ? specMap.get(l.specId) : null\n return !spec?.isAssetizable\n })\n const assetLines = orderLines.filter((l) => {\n const spec = l.specId ? specMap.get(l.specId) : null\n return spec?.isAssetizable === true\n })\n\n const hasSubscriptionContent = subscriptionLines.some(\n (l) => Number(l.mrcTotal) > 0 || Number(l.nrcTotal) > 0,\n )\n if (!hasSubscriptionContent && assetLines.length === 0) return\n\n const subscriptionName = await this.buildSubscriptionName(orderConfig, orderLines, scope)\n\n const lineWithDates = orderLines.find((l) => l.startDate != null) ?? orderLines[0]\n const lineWithTerm = orderLines.find((l) => l.termMonths != null) ?? orderLines[0]\n const subscriptionItems = subscriptionLines.map((line) => ({\n productId: line.productId ?? undefined,\n offeringId: line.offeringId ?? undefined,\n specId: line.specId ?? undefined,\n name: this.getLineName(line),\n configuration: line.configuration ?? {},\n charges: (line.charges ?? []) as Array<Record<string, unknown>>,\n mrcAmount: Number(line.mrcTotal),\n nrcAmount: Number(line.nrcTotal),\n currencyCode: orderConfig.currencyCode,\n quantity: line.quantity,\n sourceQuoteLineId: line.sourceQuoteLineId ?? undefined,\n sortOrder: orderLines.indexOf(line),\n }))\n\n const subscription = await this.inventoryService.createSubscription(\n {\n customerId: orderConfig.customerId,\n name: subscriptionName,\n billingCycle: 'monthly',\n currencyCode: orderConfig.currencyCode,\n startDate: lineWithDates?.startDate?.toISOString?.() ?? undefined,\n termMonths: lineWithTerm?.termMonths ?? undefined,\n sourceQuoteId: orderConfig.sourceQuoteId ?? undefined,\n sourceOrderId: orderConfig.id,\n items: subscriptionItems.length > 0\n ? subscriptionItems\n : [{ name: subscriptionName, mrcAmount: 0, nrcAmount: 0 }],\n },\n scope,\n )\n\n const orderLineToSubItem = new Map<string, string>()\n const allSubItems = await this.em.find(CpqInventorySubscriptionItem, {\n subscriptionId: subscription.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n }, { orderBy: { sortOrder: 'asc' } })\n\n for (let i = 0; i < subscriptionLines.length && i < allSubItems.length; i++) {\n orderLineToSubItem.set(subscriptionLines[i].orderLineId, allSubItems[i].id)\n }\n\n for (let i = 0; i < subscriptionLines.length && i < allSubItems.length; i++) {\n const orderLine = subscriptionLines[i]\n if (orderLine.parentLineId) {\n const parentSubItemId = orderLineToSubItem.get(orderLine.parentLineId)\n if (parentSubItemId) {\n allSubItems[i].parentItemId = parentSubItemId\n }\n }\n }\n await this.em.flush()\n\n for (const line of assetLines) {\n let subscriptionItemId: string | undefined\n if (line.parentLineId) {\n subscriptionItemId = orderLineToSubItem.get(line.parentLineId) ?? undefined\n }\n\n await this.inventoryService.createAsset(\n {\n customerId: orderConfig.customerId,\n subscriptionId: subscription.id,\n subscriptionItemId,\n productId: line.productId ?? undefined,\n offeringId: line.offeringId ?? undefined,\n specId: line.specId ?? undefined,\n name: this.getLineName(line),\n assetType: 'one_time',\n currencyCode: orderConfig.currencyCode,\n purchasePrice: Number(line.nrcTotal),\n quantity: line.quantity,\n purchaseDate: new Date().toISOString().split('T')[0],\n configuration: line.configuration ?? {},\n charges: (line.charges ?? []) as Array<Record<string, unknown>>,\n sourceOrderId: orderConfig.id,\n sourceQuoteLineId: line.sourceQuoteLineId ?? undefined,\n },\n scope,\n )\n }\n }\n\n // \u2500\u2500\u2500 Get / List \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async getOrder(orderId: string, scope: TenantScope): Promise<OrderResult> {\n const em = this.em\n const orderConfig = await this.loadOrderConfig(em, orderId, scope)\n const salesOrder = await this.loadSalesOrder(em, orderConfig.orderId, scope)\n const lines = await em.find(CpqOrderLineConfiguration, {\n orderConfigurationId: orderConfig.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n return this.buildOrderResult(em, orderConfig, salesOrder, lines, scope)\n }\n\n async listOrders(filters: OrderListFilters, scope: TenantScope) {\n const page = Math.max(1, filters.page ?? 1)\n const pageSize = Math.min(100, Math.max(1, filters.pageSize ?? 50))\n const offset = (page - 1) * pageSize\n\n const where: Record<string, unknown> = {\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n }\n\n if (filters.customerId) where.customerId = filters.customerId\n if (filters.sourceQuoteId) where.sourceQuoteId = filters.sourceQuoteId\n if (filters.salesOrderId) where.orderId = filters.salesOrderId\n if (filters.currencyCode) where.currencyCode = filters.currencyCode\n if (filters.cpqStatus) {\n where.cpqStatus = Array.isArray(filters.cpqStatus)\n ? { $in: filters.cpqStatus }\n : filters.cpqStatus\n }\n\n const search = filters.search?.trim()\n if (search) {\n where.$or = [\n { orderId: { $ilike: `%${search}%` } },\n { customerId: { $ilike: `%${search}%` } },\n ]\n }\n\n const sortField = filters.sortField ?? 'createdAt'\n const sortDir = filters.sortDir === 'asc' ? 'asc' : 'desc'\n\n const [configs, total] = await this.em.findAndCount(\n CpqOrderConfiguration,\n where,\n { limit: pageSize, offset, orderBy: { [sortField]: sortDir } },\n )\n\n const orderIds = configs.map((c) => c.orderId)\n const salesOrders = orderIds.length > 0\n ? await this.em.find(SalesOrder, { id: { $in: orderIds } })\n : []\n const salesOrderMap = new Map(salesOrders.map((o) => [o.id, o]))\n\n const items = configs.map((c) => ({\n ...c,\n orderNumber: salesOrderMap.get(c.orderId)?.orderNumber ?? null,\n }))\n\n return { items, total, page, pageSize, totalPages: Math.ceil(total / pageSize) }\n }\n\n // \u2500\u2500\u2500 Status Transition \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async transitionOrderStatus(orderId: string, targetStatus: string, scope: TenantScope): Promise<OrderResult> {\n const em = this.em\n const orderConfig = await this.loadOrderConfig(em, orderId, scope)\n const salesOrder = await this.loadSalesOrder(em, orderConfig.orderId, scope)\n\n const currentStatus = orderConfig.cpqStatus as CpqOrderStatus\n const allowed = CPQ_ORDER_TRANSITIONS[currentStatus] ?? []\n\n if (!allowed.includes(targetStatus as CpqOrderStatus)) {\n throw new OrderError(409, `Cannot transition from '${currentStatus}' to '${targetStatus}'`)\n }\n\n if (targetStatus === 'active') {\n return this.activateOrder(orderId, scope)\n }\n\n orderConfig.cpqStatus = targetStatus\n salesOrder.status = CPQ_TO_SALES_ORDER_STATUS[targetStatus as CpqOrderStatus] ?? targetStatus\n\n await em.flush()\n\n const lines = await em.find(CpqOrderLineConfiguration, {\n orderConfigurationId: orderConfig.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n return this.buildOrderResult(em, orderConfig, salesOrder, lines, scope)\n }\n\n // \u2500\u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private async loadOrderConfig(em: EntityManager, orderId: string, scope: TenantScope): Promise<CpqOrderConfiguration> {\n let config = await em.findOne(CpqOrderConfiguration, {\n id: orderId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n if (!config) {\n config = await em.findOne(CpqOrderConfiguration, {\n orderId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n }\n if (!config) throw new OrderError(404, 'Order not found')\n return config\n }\n\n private async loadSalesOrder(em: EntityManager, orderId: string, scope: TenantScope): Promise<SalesOrder> {\n const order = await em.findOne(SalesOrder, {\n id: orderId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n })\n if (!order) throw new OrderError(404, 'Sales order not found')\n return order\n }\n\n private async resolveOfferingInfo(\n em: EntityManager,\n offeringId: string | null | undefined,\n scope: TenantScope,\n fallbackConfig?: Record<string, unknown> | null,\n ): Promise<{ name: string; type: string | null }> {\n if (offeringId) {\n const offering = await em.findOne(CpqProductOffering, {\n id: offeringId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n if (offering) return { name: offering.name, type: offering.offeringType ?? 'simple' }\n }\n const cfg = fallbackConfig ?? null\n const fallbackName =\n (cfg?._arcMirroredName as string | undefined) ??\n (cfg?.offeringName as string | undefined) ??\n 'Configured Item'\n return { name: fallbackName, type: null }\n }\n\n private async resolveOfferingName(\n em: EntityManager,\n offeringId: string | null | undefined,\n scope: TenantScope,\n fallbackConfig?: Record<string, unknown> | null,\n ): Promise<string> {\n const info = await this.resolveOfferingInfo(em, offeringId, scope, fallbackConfig)\n return info.name\n }\n\n private getLineName(line: CpqOrderLineConfiguration): string {\n const config = line.configuration as Record<string, unknown> | null\n return (\n (config?._arcMirroredName as string | undefined) ??\n (config?.offeringName as string | undefined) ??\n (config?.name as string | undefined) ??\n 'Configured Item'\n )\n }\n\n private async buildSubscriptionName(\n orderConfig: CpqOrderConfiguration,\n orderLines: CpqOrderLineConfiguration[],\n scope: TenantScope,\n ): Promise<string> {\n if (orderLines.length === 1 && orderLines[0].offeringId) {\n return this.resolveOfferingName(this.em, orderLines[0].offeringId, scope)\n }\n const names: string[] = []\n for (const line of orderLines.slice(0, 3)) {\n if (line.offeringId) {\n names.push(await this.resolveOfferingName(this.em, line.offeringId, scope))\n }\n }\n if (names.length > 0) {\n const suffix = orderLines.length > 3 ? ` (+${orderLines.length - 3} more)` : ''\n return names.join(', ') + suffix\n }\n return `Order ${orderConfig.orderId.slice(0, 8)}`\n }\n\n private async buildOrderResult(\n em: EntityManager,\n orderConfig: CpqOrderConfiguration,\n salesOrder: SalesOrder,\n lineConfigs: CpqOrderLineConfiguration[],\n scope: TenantScope,\n ): Promise<OrderResult> {\n const lines: OrderLineResult[] = []\n\n // XD-250 ARC: batch-load source subscription items so we can render the\n // before/after diff per line on the order detail page.\n const arcItemIds = new Set<string>()\n for (const lc of lineConfigs) {\n const m = (lc.configuration as Record<string, unknown> | null)?._arcMirroredFromItemId\n if (typeof m === 'string') arcItemIds.add(m)\n }\n const arcItemMap = new Map<string, CpqInventorySubscriptionItem>()\n if (arcItemIds.size > 0) {\n const items = await em.find(CpqInventorySubscriptionItem, {\n id: { $in: Array.from(arcItemIds) },\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n for (const it of items) arcItemMap.set(it.id, it)\n }\n\n for (const lc of lineConfigs) {\n const offeringInfo = await this.resolveOfferingInfo(\n em,\n lc.offeringId,\n scope,\n lc.configuration as Record<string, unknown> | null,\n )\n const arcSource = pickArcSourceFromConfig(lc.configuration as Record<string, unknown> | null, arcItemMap)\n lines.push({\n lineId: lc.orderLineId,\n offeringId: lc.offeringId ?? null,\n offeringName: offeringInfo.name,\n offeringType: offeringInfo.type,\n productId: lc.productId ?? null,\n specId: lc.specId ?? null,\n action: lc.action,\n parentLineId: lc.parentLineId ?? null,\n quantity: lc.quantity,\n configuration: lc.configuration ?? {},\n startDate: lc.startDate?.toISOString() ?? null,\n termMonths: lc.termMonths ?? null,\n endDate: lc.endDate?.toISOString() ?? null,\n nrcTotal: Number(lc.nrcTotal),\n mrcTotal: Number(lc.mrcTotal),\n charges: (lc.charges ?? []) as Array<Record<string, unknown>>,\n sourceQuoteLineId: lc.sourceQuoteLineId ?? null,\n arcSource,\n })\n }\n\n // Source quote's quoteType \u2014 surfaced to the order header so the UI can\n // render an AMEND / RENEW / CANCEL badge alongside the order number.\n // CpqOrderConfiguration.sourceQuoteId stores CpqQuoteConfiguration.id\n // (see convertFromConfig \u2014 sourceQuoteId: cpqConfig.id), NOT the\n // SalesQuote.id, so we look it up by `id` rather than by `quoteId`.\n let quoteType = 'new'\n if (orderConfig.sourceQuoteId) {\n const sourceCfg = await em.findOne(CpqQuoteConfiguration, {\n id: orderConfig.sourceQuoteId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n })\n if (sourceCfg?.quoteType) quoteType = sourceCfg.quoteType\n }\n\n const pricing = orderConfig.pricingSummary ?? {}\n\n return {\n id: orderConfig.id,\n orderId: orderConfig.orderId,\n orderNumber: salesOrder.orderNumber ?? '',\n customerId: orderConfig.customerId,\n cpqStatus: orderConfig.cpqStatus,\n sourceQuoteId: orderConfig.sourceQuoteId ?? null,\n quoteType,\n currencyCode: orderConfig.currencyCode,\n pricingSummary: {\n nrcTotal: Number((pricing as Record<string, unknown>).nrcTotal ?? 0),\n mrcTotal: Number((pricing as Record<string, unknown>).mrcTotal ?? 0),\n currencyCode: orderConfig.currencyCode,\n },\n activatedAt: orderConfig.activatedAt?.toISOString() ?? null,\n createdAt: orderConfig.createdAt.toISOString(),\n lines,\n }\n }\n}\n\nfunction pickArcSourceFromConfig(\n cfg: Record<string, unknown> | null,\n map: Map<string, CpqInventorySubscriptionItem>,\n): OrderLineResult['arcSource'] {\n const m = cfg?._arcMirroredFromItemId\n if (typeof m !== 'string') return null\n const item = map.get(m)\n if (!item) return null\n return {\n subscriptionItemId: item.id,\n name: item.name,\n mrcAmount: Number(item.mrcAmount),\n nrcAmount: Number(item.nrcAmount),\n quantity: item.quantity,\n }\n}\n\n// \u2500\u2500\u2500 Error class \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport class OrderError extends Error {\n constructor(\n public status: number,\n message: string,\n ) {\n super(message)\n this.name = 'OrderError'\n }\n}\n\n// \u2500\u2500\u2500 ARC helpers (XD-250) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Build a service-layer ArcLineChange from a CpqQuoteLineConfiguration.\n * For 'add' actions, materializes the itemInput from line + spec data.\n */\nfunction buildArcLineChange(\n line: CpqQuoteLineConfiguration,\n specMap: Map<string, CpqProductSpecification>,\n): ArcLineChange {\n const action = line.action as 'add' | 'modify' | 'cancel'\n if (action === 'add') {\n const itemInput: Omit<CreateSubscriptionItemInput, 'name'> & { name: string } = {\n productId: line.productId ?? undefined,\n offeringId: line.offeringId ?? undefined,\n specId: line.specId ?? undefined,\n name: resolveLineName(line, specMap),\n configuration: line.configuration ?? {},\n charges: (line.charges ?? null) as Array<Record<string, unknown>> | undefined,\n mrcAmount: Number(line.mrcTotal),\n nrcAmount: Number(line.nrcTotal),\n quantity: line.quantity,\n sourceQuoteLineId: line.quoteLineId,\n }\n return {\n quoteLineId: line.quoteLineId,\n action,\n itemInput,\n parentQuoteLineId: line.parentLineId ?? null,\n }\n }\n if (action === 'cancel') {\n return {\n quoteLineId: line.quoteLineId,\n action,\n sourceSubscriptionItemId: line.sourceSubscriptionItemId ?? null,\n }\n }\n // modify\n return {\n quoteLineId: line.quoteLineId,\n action,\n sourceSubscriptionItemId: line.sourceSubscriptionItemId ?? null,\n modifyPatch: {\n configuration: line.configuration ?? undefined,\n charges: (line.charges ?? undefined) as Array<Record<string, unknown>> | undefined,\n mrcAmount: Number(line.mrcTotal),\n nrcAmount: Number(line.nrcTotal),\n quantity: line.quantity,\n },\n }\n}\n\nfunction resolveLineName(\n line: CpqQuoteLineConfiguration,\n specMap: Map<string, CpqProductSpecification>,\n): string {\n const cfg = line.configuration as Record<string, unknown> | null\n const cfgName = (cfg?._arcMirroredName as string | undefined) ?? null\n if (cfgName) return cfgName\n if (line.specId) {\n const spec = specMap.get(line.specId)\n if (spec?.name) return spec.name\n }\n return 'Configured Item'\n}\n\ninterface ArcEventEnvelope {\n organizationId: string\n tenantId: string\n sourceQuoteId: string\n sourceOrderId: string\n}\n\nasync function emitArcEvent(\n eventId:\n | 'cpq.subscription.amended'\n | 'cpq.subscription.renewed'\n | 'cpq.subscription.cancelled',\n result: ApplyArcResult,\n envelope: ArcEventEnvelope & {\n term?: { newTermStart: string; newTermEnd: string }\n etfAmount?: string | null\n etfCurrency?: string | null\n reasonCode?: string | null\n reasonText?: string | null\n },\n): Promise<void> {\n const sub = result.subscription\n await emitCpqEvent(eventId as never, {\n subscriptionId: sub.id,\n customerId: sub.customerId,\n organizationId: envelope.organizationId,\n tenantId: envelope.tenantId,\n timestamp: new Date().toISOString(),\n sourceQuoteId: envelope.sourceQuoteId,\n sourceOrderId: envelope.sourceOrderId,\n performedByUserId: null,\n changeLogId: result.changeLog.id,\n proration: buildProrationPayload(sub, result.changeLog.beforeSnapshot ?? null),\n ...(envelope.term ? { term: envelope.term } : {}),\n ...(envelope.etfAmount !== undefined ? { etfAmount: envelope.etfAmount } : {}),\n ...(envelope.etfCurrency !== undefined ? { etfCurrency: envelope.etfCurrency } : {}),\n ...(envelope.reasonCode !== undefined ? { reasonCode: envelope.reasonCode } : {}),\n ...(envelope.reasonText !== undefined ? { reasonText: envelope.reasonText } : {}),\n } as never)\n}\n\nasync function emitMergedEvent(\n merge: ApplyMergeRenewalResult,\n envelope: ArcEventEnvelope,\n mergedFromSubscriptionIds: string[],\n): Promise<void> {\n const m = merge.mergedSubscription\n await emitCpqEvent('cpq.subscription.merged' as never, {\n subscriptionId: m.id,\n customerId: m.customerId,\n organizationId: envelope.organizationId,\n tenantId: envelope.tenantId,\n timestamp: new Date().toISOString(),\n sourceQuoteId: envelope.sourceQuoteId,\n sourceOrderId: envelope.sourceOrderId,\n performedByUserId: null,\n changeLogId: merge.mergeResultChangeLog.id,\n mergedFromSubscriptionIds,\n term: {\n newTermStart: m.currentTermStart?.toISOString() ?? null,\n newTermEnd: m.currentTermEnd?.toISOString() ?? null,\n newTermMonths: m.termMonths ?? null,\n },\n proration: buildProrationPayload(m, null),\n } as never)\n}\n\nasync function emitSupersededEvent(\n sourceLog: { id: string; subscriptionId: string },\n newMergeSubId: string,\n envelope: ArcEventEnvelope,\n): Promise<void> {\n await emitCpqEvent('cpq.subscription.superseded' as never, {\n subscriptionId: sourceLog.subscriptionId,\n organizationId: envelope.organizationId,\n tenantId: envelope.tenantId,\n timestamp: new Date().toISOString(),\n sourceQuoteId: envelope.sourceQuoteId,\n sourceOrderId: envelope.sourceOrderId,\n performedByUserId: null,\n changeLogId: sourceLog.id,\n mergedIntoSubscriptionId: newMergeSubId,\n } as never)\n}\n\n/**\n * Compute proration payload sent on every ARC subscription event.\n *\n * Currently a placeholder. Proration math against the live billing cycle\n * lives in the billing module \u2014 CPQ's contract is to surface the *inputs*\n * (old/new MRC, cycle bounds) on the event payload so billing can compute\n * the delta. Until billing integration exists, we expose the essentials and\n * leave precise cycle math to follow-up work.\n */\nfunction buildProrationPayload(\n sub: CpqInventorySubscription,\n beforeSnapshot: Record<string, unknown> | null,\n): Record<string, unknown> {\n const oldMrc = beforeSnapshot\n ? Number((beforeSnapshot.mrcAmount as string | undefined) ?? 0)\n : 0\n return {\n oldMrcAmount: oldMrc,\n newMrcAmount: Number(sub.mrcAmount),\n currency: sub.currencyCode,\n billingCycleStart: null,\n billingCycleEnd: null,\n daysElapsedInCycle: null,\n daysRemainingInCycle: null,\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { SalesOrder, SalesOrderLine } from '@open-mercato/core/modules/sales/data/entities'\nimport type { SalesDocumentNumberGenerator } from '@open-mercato/core/modules/sales/services/salesDocumentNumberGenerator'\nimport {\n CpqQuoteConfiguration,\n CpqQuoteLineConfiguration,\n CpqQuoteTargetSubscription,\n CpqInventorySubscription,\n CpqOrderConfiguration,\n CpqOrderLineConfiguration,\n CpqProductOffering,\n CpqProductSpecification,\n CpqInventorySubscriptionItem,\n} from '../data/entities'\nimport type {\n ApplyArcResult,\n ApplyMergeRenewalResult,\n ArcLineChange,\n CreateSubscriptionItemInput,\n DefaultCpqInventoryService,\n} from './cpqInventoryService'\nimport {\n CPQ_ORDER_TRANSITIONS,\n type CpqOrderStatus,\n type TenantScope,\n} from './types'\nimport { emitCpqEvent } from '../events'\n\n// \u2500\u2500\u2500 Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ninterface OrderListFilters {\n customerId?: string\n sourceQuoteId?: string\n salesOrderId?: string\n cpqStatus?: string | string[]\n currencyCode?: string\n search?: string\n sortField?: 'createdAt' | 'updatedAt' | 'cpqStatus' | 'activatedAt' | 'currencyCode'\n sortDir?: 'asc' | 'desc'\n page?: number\n pageSize?: number\n}\n\ninterface PaginatedResult<T> {\n items: T[]\n total: number\n page: number\n pageSize: number\n totalPages: number\n}\n\ninterface OrderLineResult {\n lineId: string\n offeringId: string | null\n offeringName: string\n offeringType: string | null\n productId: string | null\n specId: string | null\n action: string\n parentLineId: string | null\n quantity: number\n configuration: Record<string, unknown>\n startDate: string | null\n termMonths: number | null\n endDate: string | null\n nrcTotal: number\n mrcTotal: number\n charges: Array<Record<string, unknown>>\n sourceQuoteLineId: string | null\n /** XD-250 ARC: snapshot of the source subscription item's current state.\n * Null for non-ARC lines and for fresh `add` lines on amend quotes. */\n arcSource: {\n subscriptionItemId: string\n name: string\n mrcAmount: number\n nrcAmount: number\n quantity: number\n } | null\n}\n\ninterface OrderResult {\n id: string\n orderId: string\n orderNumber: string\n customerId: string\n cpqStatus: string\n sourceQuoteId: string | null\n /** XD-250 ARC quote type carried over from the source quote \u2014 used by the\n * order detail page to render the AMEND / RENEW / CANCEL badge. */\n quoteType: string\n currencyCode: string\n pricingSummary: {\n nrcTotal: number\n mrcTotal: number\n currencyCode: string\n }\n activatedAt: string | null\n createdAt: string\n lines: OrderLineResult[]\n}\n\ninterface OrderServiceDeps {\n em: EntityManager\n cpqInventoryService: DefaultCpqInventoryService\n salesDocumentNumberGenerator: SalesDocumentNumberGenerator\n}\n\nconst CPQ_TO_SALES_ORDER_STATUS: Record<CpqOrderStatus, string> = {\n draft: 'draft',\n pending_activation: 'pending',\n active: 'active',\n cancelled: 'cancelled',\n fulfilled: 'fulfilled',\n}\n\n// \u2500\u2500\u2500 Service \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport class DefaultCpqOrderService {\n private em: EntityManager\n private inventoryService: DefaultCpqInventoryService\n private numberGenerator: SalesDocumentNumberGenerator\n\n constructor(deps: OrderServiceDeps) {\n this.em = deps.em\n this.inventoryService = deps.cpqInventoryService\n this.numberGenerator = deps.salesDocumentNumberGenerator\n }\n\n // \u2500\u2500\u2500 Convert Quote to Order \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async convertQuoteToOrder(quoteId: string, scope: TenantScope): Promise<OrderResult> {\n const em = this.em\n\n const cpqConfig = await em.findOne(CpqQuoteConfiguration, {\n id: quoteId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n if (!cpqConfig) {\n const byQuoteId = await em.findOne(CpqQuoteConfiguration, {\n quoteId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n if (!byQuoteId) throw new OrderError(404, 'Quote not found')\n return this.convertFromConfig(byQuoteId, scope)\n }\n return this.convertFromConfig(cpqConfig, scope)\n }\n\n private async convertFromConfig(cpqConfig: CpqQuoteConfiguration, scope: TenantScope): Promise<OrderResult> {\n const em = this.em\n\n const blockedStatuses = ['cancelled', 'rejected']\n if (blockedStatuses.includes(cpqConfig.cpqStatus)) {\n throw new OrderError(409, `Cannot convert a quote in '${cpqConfig.cpqStatus}' status`)\n }\n\n // Idempotency guard: a quote may only convert once. If an order with\n // this quote as its `sourceQuoteId` already exists, the UI should\n // surface \"Go to order\" instead of re-running conversion \u2014 return\n // 409 with the existing id so the caller can redirect.\n const existingOrder = await em.findOne(CpqOrderConfiguration, {\n sourceQuoteId: cpqConfig.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n if (existingOrder) {\n throw new OrderError(\n 409,\n `Quote has already been converted to order ${existingOrder.orderId}`,\n )\n }\n\n const quoteLines = await em.find(CpqQuoteLineConfiguration, {\n quoteConfigurationId: cpqConfig.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n\n if (quoteLines.length === 0) {\n throw new OrderError(422, 'Cannot convert a quote with no line items')\n }\n\n const { number: orderNumber } = await this.numberGenerator.generate({\n kind: 'order',\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n })\n\n const salesOrderId = crypto.randomUUID()\n const pricingSummary = cpqConfig.pricingSummary as Record<string, unknown> | null\n const nrcTotal = Number(pricingSummary?.nrcTotal ?? 0)\n const mrcTotal = Number(pricingSummary?.mrcTotal ?? 0)\n const grandTotal = nrcTotal + mrcTotal\n\n const salesOrder = em.create(SalesOrder, {\n id: salesOrderId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n orderNumber,\n customerEntityId: cpqConfig.customerId,\n currencyCode: cpqConfig.currencyCode,\n status: 'draft',\n subtotalNetAmount: String(grandTotal),\n subtotalGrossAmount: String(grandTotal),\n discountTotalAmount: '0',\n taxTotalAmount: '0',\n shippingNetAmount: '0',\n shippingGrossAmount: '0',\n surchargeTotalAmount: '0',\n paidTotalAmount: '0',\n grandTotalNetAmount: String(grandTotal),\n grandTotalGrossAmount: String(grandTotal),\n lineItemCount: quoteLines.length,\n metadata: { sourceQuoteId: cpqConfig.id },\n createdAt: new Date(),\n updatedAt: new Date(),\n } as never)\n em.persist(salesOrder)\n\n const cpqOrderConfigId = crypto.randomUUID()\n const cpqOrderConfig = em.create(CpqOrderConfiguration, {\n id: cpqOrderConfigId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n orderId: salesOrderId,\n sourceQuoteId: cpqConfig.id,\n customerId: cpqConfig.customerId,\n cpqStatus: 'draft',\n currencyCode: cpqConfig.currencyCode,\n pricingSummary: {\n nrcTotal,\n mrcTotal,\n currencyCode: cpqConfig.currencyCode,\n },\n })\n em.persist(cpqOrderConfig)\n\n const quoteLineIdToOrderLineId = new Map<string, string>()\n\n for (let i = 0; i < quoteLines.length; i++) {\n const ql = quoteLines[i]\n\n const perInstanceNrc = Number(ql.nrcTotal)\n const perInstanceMrc = Number(ql.mrcTotal)\n const unitPriceNet = (perInstanceNrc + perInstanceMrc) / ql.quantity\n const totalNet = perInstanceNrc + perInstanceMrc\n\n const orderLineId = crypto.randomUUID()\n quoteLineIdToOrderLineId.set(ql.quoteLineId, orderLineId)\n\n const offeringName = await this.resolveOfferingName(\n em,\n ql.offeringId,\n scope,\n ql.configuration as Record<string, unknown> | null,\n )\n\n const orderLine = em.create(SalesOrderLine, {\n id: orderLineId,\n order: salesOrder,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n lineNumber: i + 1,\n kind: 'product',\n productId: ql.productId ?? null,\n name: offeringName,\n quantity: String(ql.quantity),\n unitPriceNet: String(unitPriceNet),\n unitPriceGross: String(unitPriceNet),\n totalNetAmount: String(totalNet),\n totalGrossAmount: String(totalNet),\n discountAmount: '0',\n discountPercent: '0',\n taxRate: '0',\n taxAmount: '0',\n currencyCode: cpqConfig.currencyCode,\n configuration: {\n offeringId: ql.offeringId ?? null,\n action: ql.action,\n nrcTotal: perInstanceNrc,\n mrcTotal: perInstanceMrc,\n parentLineId: ql.parentLineId ?? null,\n },\n normalizedQuantity: String(ql.quantity),\n reservedQuantity: '0',\n fulfilledQuantity: '0',\n invoicedQuantity: '0',\n returnedQuantity: '0',\n createdAt: new Date(),\n updatedAt: new Date(),\n } as never)\n em.persist(orderLine)\n const orderLineConfig = em.create(CpqOrderLineConfiguration, {\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n orderLineId,\n orderConfigurationId: cpqOrderConfig.id,\n offeringId: ql.offeringId ?? null,\n productId: ql.productId ?? null,\n specId: ql.specId ?? null,\n action: ql.action,\n parentLineId: null,\n startDate: ql.startDate,\n termMonths: ql.termMonths ?? null,\n endDate: ql.endDate,\n configuration: { ...ql.configuration, offeringName },\n quantity: ql.quantity,\n charges: ql.charges ? [...ql.charges] : null,\n nrcTotal: ql.nrcTotal,\n mrcTotal: ql.mrcTotal,\n sourceQuoteLineId: ql.quoteLineId,\n })\n em.persist(orderLineConfig)\n }\n\n await em.flush()\n\n // Resolve parent line references using the quote\u2192order line ID map\n const orderLineConfigs = await em.find(CpqOrderLineConfiguration, {\n orderConfigurationId: cpqOrderConfig.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n })\n for (const olc of orderLineConfigs) {\n if (olc.sourceQuoteLineId) {\n const sourceQl = quoteLines.find((ql) => ql.quoteLineId === olc.sourceQuoteLineId)\n if (sourceQl?.parentLineId) {\n const mappedParent = quoteLineIdToOrderLineId.get(sourceQl.parentLineId)\n if (mappedParent) olc.parentLineId = mappedParent\n }\n }\n }\n\n await em.flush()\n\n return this.buildOrderResult(em, cpqOrderConfig, salesOrder, orderLineConfigs, scope)\n }\n\n // \u2500\u2500\u2500 Activate Order \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async activateOrder(orderId: string, scope: TenantScope): Promise<OrderResult> {\n const em = this.em\n\n const orderConfig = await this.loadOrderConfig(em, orderId, scope)\n\n const currentStatus = orderConfig.cpqStatus as CpqOrderStatus\n if (currentStatus !== 'draft' && currentStatus !== 'pending_activation') {\n throw new OrderError(409, `Cannot activate order in '${currentStatus}' status`)\n }\n\n const salesOrder = await this.loadSalesOrder(em, orderConfig.orderId, scope)\n const orderLines = await em.find(CpqOrderLineConfiguration, {\n orderConfigurationId: orderConfig.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n\n // Branch on the source quote's quoteType. ARC quotes mutate existing\n // subscriptions; new-sale quotes materialize a fresh subscription.\n const sourceQuote = orderConfig.sourceQuoteId\n ? await em.findOne(CpqQuoteConfiguration, {\n id: orderConfig.sourceQuoteId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n : null\n\n const quoteType = sourceQuote?.quoteType ?? 'new'\n const postCommitEvents: Array<() => Promise<void>> = []\n\n if (quoteType === 'new') {\n const inventory = await this.createInventoryFromOrder(orderConfig, orderLines, scope)\n if (inventory) {\n postCommitEvents.push(() => emitActivatedEvent(orderConfig, inventory, scope))\n }\n } else {\n postCommitEvents.push(\n ...(await this.applyArcOrder(orderConfig, sourceQuote!, orderLines, scope)),\n )\n }\n\n orderConfig.cpqStatus = 'active'\n orderConfig.activatedAt = new Date()\n salesOrder.status = 'active'\n salesOrder.placedAt = new Date()\n\n await em.flush()\n\n // Emit lifecycle events AFTER the activation transaction commits.\n for (const fire of postCommitEvents) {\n try {\n await fire()\n } catch (err) {\n // Don't roll back the activation just because a subscriber blew up \u2014\n // events are best-effort once the DB state is committed.\n console.error('[cpq.arc] event emission failed', err)\n }\n }\n\n return this.buildOrderResult(em, orderConfig, salesOrder, orderLines, scope)\n }\n\n // \u2500\u2500\u2500 ARC activation branch (XD-250) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n //\n // Reads the source quote's targets + lines, then dispatches per target to\n // cpqInventoryService.apply* primitives. Returns the list of post-commit\n // event emissions so the caller can fire them outside the activation TX.\n private async applyArcOrder(\n orderConfig: CpqOrderConfiguration,\n sourceQuote: CpqQuoteConfiguration,\n orderLines: CpqOrderLineConfiguration[],\n scope: TenantScope,\n ): Promise<Array<() => Promise<void>>> {\n const em = this.em\n const quoteType = sourceQuote.quoteType as 'amend' | 'renew' | 'cancel'\n\n // Load attached targets.\n const targets = await em.find(CpqQuoteTargetSubscription, {\n quoteId: sourceQuote.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n if (targets.length === 0) {\n throw new OrderError(\n 409,\n `ARC quote ${sourceQuote.id} has no target subscriptions to activate`,\n )\n }\n\n const inventory = this.inventoryService\n\n // Group order lines by target_subscription_id \u2014 ARC order lines mirror the\n // quote lines' ARC fields (target_subscription_id is preserved through\n // convertQuoteToOrder via line.configuration when needed).\n // Source quote lines are the source of truth for ARC line data because\n // CpqOrderLineConfiguration doesn't carry target/source-item fields.\n const sourceQuoteLines = await em.find(CpqQuoteLineConfiguration, {\n quoteConfigurationId: sourceQuote.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n const linesByTarget = new Map<string | null, CpqQuoteLineConfiguration[]>()\n for (const line of sourceQuoteLines) {\n const key = line.targetSubscriptionId ?? null\n const list = linesByTarget.get(key) ?? []\n list.push(line)\n linesByTarget.set(key, list)\n }\n\n // Look up specs for `add` lines so we can build itemInput correctly.\n const specIds = [\n ...new Set(sourceQuoteLines.map((l) => l.specId).filter((x): x is string => Boolean(x))),\n ]\n const specs =\n specIds.length > 0\n ? await em.find(CpqProductSpecification, {\n id: { $in: specIds },\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n : []\n const specMap = new Map(specs.map((s) => [s.id, s]))\n\n const events: Array<() => Promise<void>> = []\n const eventBaseEnvelope = {\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n sourceQuoteId: sourceQuote.id,\n sourceOrderId: orderConfig.id,\n } as const\n\n if (quoteType === 'amend') {\n for (const target of targets) {\n const lines = linesByTarget.get(target.subscriptionId) ?? []\n const result = await inventory.applyAmendment(\n {\n subscriptionId: target.subscriptionId,\n sourceQuoteId: sourceQuote.id,\n sourceOrderId: orderConfig.id,\n performedByUserId: null,\n lineChanges: lines.map((l) => buildArcLineChange(l, specMap)),\n },\n scope,\n )\n events.push(() => emitAmendedEvent(result, eventBaseEnvelope))\n }\n } else if (quoteType === 'cancel') {\n for (const target of targets) {\n const result = await inventory.applyCancel(\n {\n subscriptionId: target.subscriptionId,\n sourceQuoteId: sourceQuote.id,\n sourceOrderId: orderConfig.id,\n performedByUserId: null,\n etfAmount: sourceQuote.arcEtfAmount ?? null,\n etfCurrency: sourceQuote.arcEtfCurrency ?? null,\n reasonCode: sourceQuote.arcReasonCode ?? null,\n reasonText: sourceQuote.arcReasonText ?? null,\n },\n scope,\n )\n events.push(() => emitCancelledEvent(result, eventBaseEnvelope))\n }\n } else if (quoteType === 'renew') {\n const standalone = targets.filter((t) => t.mergeAction === 'standalone')\n const absorb = targets.filter((t) => t.mergeAction === 'absorb')\n\n if (standalone.length > 0 && absorb.length === 0) {\n for (const target of standalone) {\n if (!target.newTermStart || !target.newTermEnd) {\n throw new OrderError(\n 409,\n `Standalone renew target ${target.id} missing newTermStart/newTermEnd`,\n )\n }\n const lines = linesByTarget.get(target.subscriptionId) ?? []\n const result = await inventory.applyRenewal(\n {\n subscriptionId: target.subscriptionId,\n sourceQuoteId: sourceQuote.id,\n sourceOrderId: orderConfig.id,\n performedByUserId: null,\n term: {\n newTermStart: target.newTermStart,\n newTermEnd: target.newTermEnd,\n newTermMonths: target.newTermMonths ?? null,\n },\n lineChanges: lines.map((l) => buildArcLineChange(l, specMap)),\n },\n scope,\n )\n events.push(() => emitRenewedEvent(result, eventBaseEnvelope))\n }\n } else if (absorb.length >= 2 && standalone.length === 0) {\n if (!sourceQuote.arcMergeNewTermStart || !sourceQuote.arcMergeNewTermEnd) {\n throw new OrderError(\n 409,\n `Merge renew quote ${sourceQuote.id} missing arc_merge_new_term_*`,\n )\n }\n // Lines targeting null are operator-edits on the new merge sub M.\n const mergeLines = linesByTarget.get(null) ?? []\n const merge = await inventory.applyMergeRenewal(\n {\n sourceIds: absorb.map((t) => t.subscriptionId),\n sourceQuoteId: sourceQuote.id,\n sourceOrderId: orderConfig.id,\n performedByUserId: null,\n term: {\n newTermStart: sourceQuote.arcMergeNewTermStart,\n newTermEnd: sourceQuote.arcMergeNewTermEnd,\n newTermMonths: sourceQuote.arcMergeNewTermMonths ?? null,\n },\n mergeMeta: {\n newSubCode: sourceQuote.arcMergeNewSubCode ?? null,\n newSubName: sourceQuote.arcMergeNewSubName ?? null,\n },\n lineChanges: mergeLines.map((l) => buildArcLineChange(l, specMap)),\n },\n scope,\n )\n\n // One event per source (`superseded`) + one for M (`merged`).\n const mergedFromSubscriptionIds = absorb.map((t) => t.subscriptionId)\n events.push(() =>\n emitMergedEvent(merge, eventBaseEnvelope, mergedFromSubscriptionIds),\n )\n for (const sourceLog of merge.sourceChangeLogs) {\n events.push(() =>\n emitSupersededEvent(\n sourceLog,\n iso10(merge.mergedSubscription.currentTermStart),\n eventBaseEnvelope,\n ),\n )\n }\n } else {\n throw new OrderError(\n 409,\n `Inconsistent renew targets \u2014 must be either single standalone or \u22652 absorb`,\n )\n }\n } else {\n throw new OrderError(500, `Unknown quote_type '${quoteType}' for ARC activation`)\n }\n\n return events\n }\n\n // \u2500\u2500\u2500 Inventory Creation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private async createInventoryFromOrder(\n orderConfig: CpqOrderConfiguration,\n orderLines: CpqOrderLineConfiguration[],\n scope: TenantScope,\n ): Promise<ActivatedInventory | null> {\n if (orderLines.length === 0) return null\n\n const specIds = [...new Set(orderLines.map((l) => l.specId).filter(Boolean))] as string[]\n const specs = specIds.length > 0\n ? await this.em.find(CpqProductSpecification, {\n id: { $in: specIds },\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n : []\n const specMap = new Map(specs.map((s) => [s.id, s]))\n\n const subscriptionLines = orderLines.filter((l) => {\n const spec = l.specId ? specMap.get(l.specId) : null\n return !spec?.isAssetizable\n })\n const assetLines = orderLines.filter((l) => {\n const spec = l.specId ? specMap.get(l.specId) : null\n return spec?.isAssetizable === true\n })\n\n const hasSubscriptionContent = subscriptionLines.some(\n (l) => Number(l.mrcTotal) > 0 || Number(l.nrcTotal) > 0,\n )\n if (!hasSubscriptionContent && assetLines.length === 0) return null\n\n const subscriptionName = await this.buildSubscriptionName(orderConfig, orderLines, scope)\n\n const lineWithDates = orderLines.find((l) => l.startDate != null) ?? orderLines[0]\n const lineWithTerm = orderLines.find((l) => l.termMonths != null) ?? orderLines[0]\n const subscriptionItems = subscriptionLines.map((line) => ({\n productId: line.productId ?? undefined,\n offeringId: line.offeringId ?? undefined,\n specId: line.specId ?? undefined,\n name: this.getLineName(line),\n configuration: line.configuration ?? {},\n charges: (line.charges ?? []) as Array<Record<string, unknown>>,\n mrcAmount: Number(line.mrcTotal),\n nrcAmount: Number(line.nrcTotal),\n currencyCode: orderConfig.currencyCode,\n quantity: line.quantity,\n sourceQuoteLineId: line.sourceQuoteLineId ?? undefined,\n sortOrder: orderLines.indexOf(line),\n }))\n\n const subscription = await this.inventoryService.createSubscription(\n {\n customerId: orderConfig.customerId,\n name: subscriptionName,\n billingCycle: 'monthly',\n currencyCode: orderConfig.currencyCode,\n startDate: lineWithDates?.startDate?.toISOString?.() ?? undefined,\n termMonths: lineWithTerm?.termMonths ?? undefined,\n sourceQuoteId: orderConfig.sourceQuoteId ?? undefined,\n sourceOrderId: orderConfig.id,\n items: subscriptionItems.length > 0\n ? subscriptionItems\n : [{ name: subscriptionName, mrcAmount: 0, nrcAmount: 0 }],\n },\n scope,\n )\n\n const orderLineToSubItem = new Map<string, string>()\n const allSubItems = await this.em.find(CpqInventorySubscriptionItem, {\n subscriptionId: subscription.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n }, { orderBy: { sortOrder: 'asc' } })\n\n for (let i = 0; i < subscriptionLines.length && i < allSubItems.length; i++) {\n orderLineToSubItem.set(subscriptionLines[i].orderLineId, allSubItems[i].id)\n }\n\n for (let i = 0; i < subscriptionLines.length && i < allSubItems.length; i++) {\n const orderLine = subscriptionLines[i]\n if (orderLine.parentLineId) {\n const parentSubItemId = orderLineToSubItem.get(orderLine.parentLineId)\n if (parentSubItemId) {\n allSubItems[i].parentItemId = parentSubItemId\n }\n }\n }\n await this.em.flush()\n\n for (const line of assetLines) {\n let subscriptionItemId: string | undefined\n if (line.parentLineId) {\n subscriptionItemId = orderLineToSubItem.get(line.parentLineId) ?? undefined\n }\n\n await this.inventoryService.createAsset(\n {\n customerId: orderConfig.customerId,\n subscriptionId: subscription.id,\n subscriptionItemId,\n productId: line.productId ?? undefined,\n offeringId: line.offeringId ?? undefined,\n specId: line.specId ?? undefined,\n name: this.getLineName(line),\n assetType: 'one_time',\n currencyCode: orderConfig.currencyCode,\n purchasePrice: Number(line.nrcTotal),\n quantity: line.quantity,\n purchaseDate: new Date().toISOString().split('T')[0],\n configuration: line.configuration ?? {},\n charges: (line.charges ?? []) as Array<Record<string, unknown>>,\n sourceOrderId: orderConfig.id,\n sourceQuoteLineId: line.sourceQuoteLineId ?? undefined,\n },\n scope,\n )\n }\n\n // Pair each subscription line with its created subscription item \u2014\n // input for the `cpq.subscription.activated` billing-onboarding event.\n const eventItems = subscriptionLines\n .slice(0, allSubItems.length)\n .map((line, i) => ({\n subscriptionItemId: allSubItems[i].id,\n productName: this.getLineName(line),\n mrcTotal: Number(line.mrcTotal),\n nrcTotal: Number(line.nrcTotal),\n }))\n // Only lines with a billable charge \u2014 a zero-value line yields\n // no Billing Item anyway; emitting it would just make the\n // connector create a needless shell account.\n .filter((it) => it.mrcTotal > 0 || it.nrcTotal > 0)\n return { subscriptionId: subscription.id, items: eventItems }\n }\n\n // \u2500\u2500\u2500 Get / List \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async getOrder(orderId: string, scope: TenantScope): Promise<OrderResult> {\n const em = this.em\n const orderConfig = await this.loadOrderConfig(em, orderId, scope)\n const salesOrder = await this.loadSalesOrder(em, orderConfig.orderId, scope)\n const lines = await em.find(CpqOrderLineConfiguration, {\n orderConfigurationId: orderConfig.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n return this.buildOrderResult(em, orderConfig, salesOrder, lines, scope)\n }\n\n async listOrders(filters: OrderListFilters, scope: TenantScope) {\n const page = Math.max(1, filters.page ?? 1)\n const pageSize = Math.min(100, Math.max(1, filters.pageSize ?? 50))\n const offset = (page - 1) * pageSize\n\n const where: Record<string, unknown> = {\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n }\n\n if (filters.customerId) where.customerId = filters.customerId\n if (filters.sourceQuoteId) where.sourceQuoteId = filters.sourceQuoteId\n if (filters.salesOrderId) where.orderId = filters.salesOrderId\n if (filters.currencyCode) where.currencyCode = filters.currencyCode\n if (filters.cpqStatus) {\n where.cpqStatus = Array.isArray(filters.cpqStatus)\n ? { $in: filters.cpqStatus }\n : filters.cpqStatus\n }\n\n const search = filters.search?.trim()\n if (search) {\n where.$or = [\n { orderId: { $ilike: `%${search}%` } },\n { customerId: { $ilike: `%${search}%` } },\n ]\n }\n\n const sortField = filters.sortField ?? 'createdAt'\n const sortDir = filters.sortDir === 'asc' ? 'asc' : 'desc'\n\n const [configs, total] = await this.em.findAndCount(\n CpqOrderConfiguration,\n where,\n { limit: pageSize, offset, orderBy: { [sortField]: sortDir } },\n )\n\n const orderIds = configs.map((c) => c.orderId)\n const salesOrders = orderIds.length > 0\n ? await this.em.find(SalesOrder, { id: { $in: orderIds } })\n : []\n const salesOrderMap = new Map(salesOrders.map((o) => [o.id, o]))\n\n const items = configs.map((c) => ({\n ...c,\n orderNumber: salesOrderMap.get(c.orderId)?.orderNumber ?? null,\n }))\n\n return { items, total, page, pageSize, totalPages: Math.ceil(total / pageSize) }\n }\n\n // \u2500\u2500\u2500 Status Transition \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async transitionOrderStatus(orderId: string, targetStatus: string, scope: TenantScope): Promise<OrderResult> {\n const em = this.em\n const orderConfig = await this.loadOrderConfig(em, orderId, scope)\n const salesOrder = await this.loadSalesOrder(em, orderConfig.orderId, scope)\n\n const currentStatus = orderConfig.cpqStatus as CpqOrderStatus\n const allowed = CPQ_ORDER_TRANSITIONS[currentStatus] ?? []\n\n if (!allowed.includes(targetStatus as CpqOrderStatus)) {\n throw new OrderError(409, `Cannot transition from '${currentStatus}' to '${targetStatus}'`)\n }\n\n if (targetStatus === 'active') {\n return this.activateOrder(orderId, scope)\n }\n\n orderConfig.cpqStatus = targetStatus\n salesOrder.status = CPQ_TO_SALES_ORDER_STATUS[targetStatus as CpqOrderStatus] ?? targetStatus\n\n await em.flush()\n\n const lines = await em.find(CpqOrderLineConfiguration, {\n orderConfigurationId: orderConfig.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n return this.buildOrderResult(em, orderConfig, salesOrder, lines, scope)\n }\n\n // \u2500\u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private async loadOrderConfig(em: EntityManager, orderId: string, scope: TenantScope): Promise<CpqOrderConfiguration> {\n let config = await em.findOne(CpqOrderConfiguration, {\n id: orderId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n if (!config) {\n config = await em.findOne(CpqOrderConfiguration, {\n orderId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n }\n if (!config) throw new OrderError(404, 'Order not found')\n return config\n }\n\n private async loadSalesOrder(em: EntityManager, orderId: string, scope: TenantScope): Promise<SalesOrder> {\n const order = await em.findOne(SalesOrder, {\n id: orderId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n })\n if (!order) throw new OrderError(404, 'Sales order not found')\n return order\n }\n\n private async resolveOfferingInfo(\n em: EntityManager,\n offeringId: string | null | undefined,\n scope: TenantScope,\n fallbackConfig?: Record<string, unknown> | null,\n ): Promise<{ name: string; type: string | null }> {\n if (offeringId) {\n const offering = await em.findOne(CpqProductOffering, {\n id: offeringId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n if (offering) return { name: offering.name, type: offering.offeringType ?? 'simple' }\n }\n const cfg = fallbackConfig ?? null\n const fallbackName =\n (cfg?._arcMirroredName as string | undefined) ??\n (cfg?.offeringName as string | undefined) ??\n 'Configured Item'\n return { name: fallbackName, type: null }\n }\n\n private async resolveOfferingName(\n em: EntityManager,\n offeringId: string | null | undefined,\n scope: TenantScope,\n fallbackConfig?: Record<string, unknown> | null,\n ): Promise<string> {\n const info = await this.resolveOfferingInfo(em, offeringId, scope, fallbackConfig)\n return info.name\n }\n\n private getLineName(line: CpqOrderLineConfiguration): string {\n const config = line.configuration as Record<string, unknown> | null\n return (\n (config?._arcMirroredName as string | undefined) ??\n (config?.offeringName as string | undefined) ??\n (config?.name as string | undefined) ??\n 'Configured Item'\n )\n }\n\n private async buildSubscriptionName(\n orderConfig: CpqOrderConfiguration,\n orderLines: CpqOrderLineConfiguration[],\n scope: TenantScope,\n ): Promise<string> {\n if (orderLines.length === 1 && orderLines[0].offeringId) {\n return this.resolveOfferingName(this.em, orderLines[0].offeringId, scope)\n }\n const names: string[] = []\n for (const line of orderLines.slice(0, 3)) {\n if (line.offeringId) {\n names.push(await this.resolveOfferingName(this.em, line.offeringId, scope))\n }\n }\n if (names.length > 0) {\n const suffix = orderLines.length > 3 ? ` (+${orderLines.length - 3} more)` : ''\n return names.join(', ') + suffix\n }\n return `Order ${orderConfig.orderId.slice(0, 8)}`\n }\n\n private async buildOrderResult(\n em: EntityManager,\n orderConfig: CpqOrderConfiguration,\n salesOrder: SalesOrder,\n lineConfigs: CpqOrderLineConfiguration[],\n scope: TenantScope,\n ): Promise<OrderResult> {\n const lines: OrderLineResult[] = []\n\n // XD-250 ARC: batch-load source subscription items so we can render the\n // before/after diff per line on the order detail page.\n const arcItemIds = new Set<string>()\n for (const lc of lineConfigs) {\n const m = (lc.configuration as Record<string, unknown> | null)?._arcMirroredFromItemId\n if (typeof m === 'string') arcItemIds.add(m)\n }\n const arcItemMap = new Map<string, CpqInventorySubscriptionItem>()\n if (arcItemIds.size > 0) {\n const items = await em.find(CpqInventorySubscriptionItem, {\n id: { $in: Array.from(arcItemIds) },\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n for (const it of items) arcItemMap.set(it.id, it)\n }\n\n for (const lc of lineConfigs) {\n const offeringInfo = await this.resolveOfferingInfo(\n em,\n lc.offeringId,\n scope,\n lc.configuration as Record<string, unknown> | null,\n )\n const arcSource = pickArcSourceFromConfig(lc.configuration as Record<string, unknown> | null, arcItemMap)\n lines.push({\n lineId: lc.orderLineId,\n offeringId: lc.offeringId ?? null,\n offeringName: offeringInfo.name,\n offeringType: offeringInfo.type,\n productId: lc.productId ?? null,\n specId: lc.specId ?? null,\n action: lc.action,\n parentLineId: lc.parentLineId ?? null,\n quantity: lc.quantity,\n configuration: lc.configuration ?? {},\n startDate: lc.startDate?.toISOString() ?? null,\n termMonths: lc.termMonths ?? null,\n endDate: lc.endDate?.toISOString() ?? null,\n nrcTotal: Number(lc.nrcTotal),\n mrcTotal: Number(lc.mrcTotal),\n charges: (lc.charges ?? []) as Array<Record<string, unknown>>,\n sourceQuoteLineId: lc.sourceQuoteLineId ?? null,\n arcSource,\n })\n }\n\n // Source quote's quoteType \u2014 surfaced to the order header so the UI can\n // render an AMEND / RENEW / CANCEL badge alongside the order number.\n // CpqOrderConfiguration.sourceQuoteId stores CpqQuoteConfiguration.id\n // (see convertFromConfig \u2014 sourceQuoteId: cpqConfig.id), NOT the\n // SalesQuote.id, so we look it up by `id` rather than by `quoteId`.\n let quoteType = 'new'\n if (orderConfig.sourceQuoteId) {\n const sourceCfg = await em.findOne(CpqQuoteConfiguration, {\n id: orderConfig.sourceQuoteId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n })\n if (sourceCfg?.quoteType) quoteType = sourceCfg.quoteType\n }\n\n const pricing = orderConfig.pricingSummary ?? {}\n\n return {\n id: orderConfig.id,\n orderId: orderConfig.orderId,\n orderNumber: salesOrder.orderNumber ?? '',\n customerId: orderConfig.customerId,\n cpqStatus: orderConfig.cpqStatus,\n sourceQuoteId: orderConfig.sourceQuoteId ?? null,\n quoteType,\n currencyCode: orderConfig.currencyCode,\n pricingSummary: {\n nrcTotal: Number((pricing as Record<string, unknown>).nrcTotal ?? 0),\n mrcTotal: Number((pricing as Record<string, unknown>).mrcTotal ?? 0),\n currencyCode: orderConfig.currencyCode,\n },\n activatedAt: orderConfig.activatedAt?.toISOString() ?? null,\n createdAt: orderConfig.createdAt.toISOString(),\n lines,\n }\n }\n}\n\nfunction pickArcSourceFromConfig(\n cfg: Record<string, unknown> | null,\n map: Map<string, CpqInventorySubscriptionItem>,\n): OrderLineResult['arcSource'] {\n const m = cfg?._arcMirroredFromItemId\n if (typeof m !== 'string') return null\n const item = map.get(m)\n if (!item) return null\n return {\n subscriptionItemId: item.id,\n name: item.name,\n mrcAmount: Number(item.mrcAmount),\n nrcAmount: Number(item.nrcAmount),\n quantity: item.quantity,\n }\n}\n\n// \u2500\u2500\u2500 Error class \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport class OrderError extends Error {\n constructor(\n public status: number,\n message: string,\n ) {\n super(message)\n this.name = 'OrderError'\n }\n}\n\n// \u2500\u2500\u2500 ARC helpers (XD-250) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Build a service-layer ArcLineChange from a CpqQuoteLineConfiguration.\n * For 'add' actions, materializes the itemInput from line + spec data.\n */\nfunction buildArcLineChange(\n line: CpqQuoteLineConfiguration,\n specMap: Map<string, CpqProductSpecification>,\n): ArcLineChange {\n const action = line.action as 'add' | 'modify' | 'cancel'\n if (action === 'add') {\n const itemInput: Omit<CreateSubscriptionItemInput, 'name'> & { name: string } = {\n productId: line.productId ?? undefined,\n offeringId: line.offeringId ?? undefined,\n specId: line.specId ?? undefined,\n name: resolveLineName(line, specMap),\n configuration: line.configuration ?? {},\n charges: (line.charges ?? null) as Array<Record<string, unknown>> | undefined,\n mrcAmount: Number(line.mrcTotal),\n nrcAmount: Number(line.nrcTotal),\n quantity: line.quantity,\n sourceQuoteLineId: line.quoteLineId,\n }\n return {\n quoteLineId: line.quoteLineId,\n action,\n itemInput,\n parentQuoteLineId: line.parentLineId ?? null,\n }\n }\n if (action === 'cancel') {\n return {\n quoteLineId: line.quoteLineId,\n action,\n sourceSubscriptionItemId: line.sourceSubscriptionItemId ?? null,\n }\n }\n // modify\n return {\n quoteLineId: line.quoteLineId,\n action,\n sourceSubscriptionItemId: line.sourceSubscriptionItemId ?? null,\n modifyPatch: {\n configuration: line.configuration ?? undefined,\n charges: (line.charges ?? undefined) as Array<Record<string, unknown>> | undefined,\n mrcAmount: Number(line.mrcTotal),\n nrcAmount: Number(line.nrcTotal),\n quantity: line.quantity,\n },\n }\n}\n\nfunction resolveLineName(\n line: CpqQuoteLineConfiguration,\n specMap: Map<string, CpqProductSpecification>,\n): string {\n const cfg = line.configuration as Record<string, unknown> | null\n const cfgName = (cfg?._arcMirroredName as string | undefined) ?? null\n if (cfgName) return cfgName\n if (line.specId) {\n const spec = specMap.get(line.specId)\n if (spec?.name) return spec.name\n }\n return 'Configured Item'\n}\n\ninterface ArcEventEnvelope {\n organizationId: string\n tenantId: string\n sourceQuoteId: string\n sourceOrderId: string\n}\n\n// \u2500\u2500\u2500 Subscription-activated event (new-sale billing onboarding) \u2500\u2500\n\ntype ActivatedInventory = {\n subscriptionId: string\n items: Array<{\n subscriptionItemId: string\n productName: string\n /** Line totals \u2014 CPQ pricing has already applied line quantity. */\n mrcTotal: number\n nrcTotal: number\n }>\n}\n\n/**\n * Emit `cpq.subscription.activated` after a new-sale order activation.\n *\n * Payload matches the `@dainamite/cpq-billing-connector`\n * `cpq-subscription-activated` subscriber contract: one CPQ charge per\n * non-zero MRC / NRC total on each subscription item. The connector\n * get-or-creates the Billing Account (keyed on `customerId`) and turns\n * each charge into a Billing Item.\n */\nasync function emitActivatedEvent(\n orderConfig: CpqOrderConfiguration,\n inventory: ActivatedInventory,\n scope: TenantScope,\n): Promise<void> {\n const items = inventory.items.map((it) => ({\n subscriptionItemId: it.subscriptionItemId,\n productName: it.productName,\n // `mrcTotal` / `nrcTotal` are line totals \u2014 CPQ pricing already\n // applied the line quantity. Quantity is therefore 1 here so\n // `unitPrice \u00D7 quantity` stays internally consistent no matter\n // how the connector treats the multiplier.\n quantity: 1,\n charges: [\n ...(it.mrcTotal > 0\n ? [{ type: 'recurring', unitPrice: it.mrcTotal, description: 'Recurring charge' }]\n : []),\n ...(it.nrcTotal > 0\n ? [{ type: 'one_time', amount: it.nrcTotal, description: 'One-time charge' }]\n : []),\n ],\n }))\n await emitCpqEvent('cpq.subscription.activated' as never, {\n subscriptionId: inventory.subscriptionId,\n customerId: orderConfig.customerId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n currencyCode: orderConfig.currencyCode,\n timestamp: new Date().toISOString(),\n activationDate: new Date().toISOString().slice(0, 10),\n sourceQuoteId: orderConfig.sourceQuoteId ?? null,\n sourceOrderId: orderConfig.id,\n items,\n } as never)\n}\n\n/** Coerce a Date or `date`-column string to a `YYYY-MM-DD` string. */\nfunction iso10(value: Date | string | null | undefined): string | null {\n if (!value) return null\n if (value instanceof Date) return value.toISOString().slice(0, 10)\n return String(value).slice(0, 10)\n}\n\ntype ArcChargeItem = {\n subscriptionItemId: string\n productName: string\n quantity: number\n charges: Array<Record<string, unknown>>\n}\n\n/**\n * Build the connector's `addedItems` from a change log's `add` line\n * changes. `mrcAmount` / `nrcAmount` are line totals (CPQ pricing has\n * already applied quantity) \u2192 emit `quantity: 1` with the totals as the\n * charge rate, exactly as `emitActivatedEvent`.\n */\nfunction buildArcAddedItems(changeLog: ApplyArcResult['changeLog']): ArcChargeItem[] {\n const after = changeLog.afterSnapshot as\n | { items?: Array<{ id?: string; name?: string }> }\n | null\n const nameById = new Map(\n (after?.items ?? []).map((i) => [String(i.id), i.name ?? '']),\n )\n const summary = (changeLog.lineChanges ?? []) as Array<{\n action?: string\n itemId?: string\n mrcAmount?: unknown\n nrcAmount?: unknown\n }>\n const out: ArcChargeItem[] = []\n for (const change of summary) {\n if (change.action !== 'add' || typeof change.itemId !== 'string') continue\n const mrc = Number(change.mrcAmount)\n const nrc = Number(change.nrcAmount)\n out.push({\n subscriptionItemId: change.itemId,\n productName: nameById.get(change.itemId) || 'Configured item',\n quantity: 1,\n charges: [\n ...(mrc > 0\n ? [{ type: 'recurring', unitPrice: mrc, description: 'Recurring charge' }]\n : []),\n ...(nrc > 0\n ? [{ type: 'one_time', amount: nrc, description: 'One-time charge' }]\n : []),\n ],\n })\n }\n return out\n}\n\n/** Subscription-item ids removed by a change log's `cancel` line changes. */\nfunction buildArcRemovedItemIds(changeLog: ApplyArcResult['changeLog']): string[] {\n const summary = (changeLog.lineChanges ?? []) as Array<{\n action?: string\n itemId?: string\n }>\n return summary\n .filter((c) => c.action === 'cancel' && typeof c.itemId === 'string')\n .map((c) => c.itemId as string)\n}\n\nasync function emitAmendedEvent(\n result: ApplyArcResult,\n envelope: ArcEventEnvelope,\n): Promise<void> {\n await emitCpqEvent('cpq.subscription.amended' as never, {\n tenantId: envelope.tenantId,\n organizationId: envelope.organizationId,\n subscriptionId: result.subscription.id,\n customerId: result.subscription.customerId,\n // No operator-chosen amend date today \u2014 the activation moment is\n // the effective date. The billing connector computes the mid-cycle\n // proration from this date + the account's billing cycle.\n effectiveDate: iso10(result.changeLog.effectiveAt),\n addedItems: buildArcAddedItems(result.changeLog),\n removedSubscriptionItemIds: buildArcRemovedItemIds(result.changeLog),\n sourceQuoteId: envelope.sourceQuoteId,\n sourceOrderId: envelope.sourceOrderId,\n timestamp: new Date().toISOString(),\n } as never)\n}\n\nasync function emitRenewedEvent(\n result: ApplyArcResult,\n envelope: ArcEventEnvelope,\n): Promise<void> {\n await emitCpqEvent('cpq.subscription.renewed' as never, {\n tenantId: envelope.tenantId,\n organizationId: envelope.organizationId,\n subscriptionId: result.subscription.id,\n customerId: result.subscription.customerId,\n newTermStart: iso10(result.subscription.currentTermStart),\n newTermEnd: iso10(result.subscription.currentTermEnd),\n addedItems: buildArcAddedItems(result.changeLog),\n sourceQuoteId: envelope.sourceQuoteId,\n sourceOrderId: envelope.sourceOrderId,\n timestamp: new Date().toISOString(),\n } as never)\n}\n\nasync function emitCancelledEvent(\n result: ApplyArcResult,\n envelope: ArcEventEnvelope,\n): Promise<void> {\n await emitCpqEvent('cpq.subscription.cancelled' as never, {\n tenantId: envelope.tenantId,\n organizationId: envelope.organizationId,\n subscriptionId: result.subscription.id,\n customerId: result.subscription.customerId,\n effectiveDate: iso10(result.changeLog.effectiveAt),\n sourceQuoteId: envelope.sourceQuoteId,\n sourceOrderId: envelope.sourceOrderId,\n timestamp: new Date().toISOString(),\n } as never)\n}\n\nasync function emitMergedEvent(\n merge: ApplyMergeRenewalResult,\n envelope: ArcEventEnvelope,\n sourceSubscriptionIds: string[],\n): Promise<void> {\n await emitCpqEvent('cpq.subscription.merged' as never, {\n tenantId: envelope.tenantId,\n organizationId: envelope.organizationId,\n mergedSubscriptionId: merge.mergedSubscription.id,\n sourceSubscriptionIds,\n sourceQuoteId: envelope.sourceQuoteId,\n sourceOrderId: envelope.sourceOrderId,\n timestamp: new Date().toISOString(),\n } as never)\n}\n\nasync function emitSupersededEvent(\n sourceLog: { id: string; subscriptionId: string },\n effectiveDate: string | null,\n envelope: ArcEventEnvelope,\n): Promise<void> {\n await emitCpqEvent('cpq.subscription.superseded' as never, {\n tenantId: envelope.tenantId,\n organizationId: envelope.organizationId,\n subscriptionId: sourceLog.subscriptionId,\n effectiveDate,\n sourceQuoteId: envelope.sourceQuoteId,\n sourceOrderId: envelope.sourceOrderId,\n timestamp: new Date().toISOString(),\n } as never)\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,5 +1,6 @@
1
1
  import { CpqProductCharge, CpqPricingTable, CpqPricingTableEntry, CpqPriceRule } from "../data/entities.js";
2
2
  import { getBaseCurrencyCode } from "./types.js";
3
+ import { normalizeChargePricing } from "../data/charge-pricing.js";
3
4
  function evaluateApplicability(condition, configuration) {
4
5
  if (!condition) return true;
5
6
  const cond = condition;
@@ -10,14 +11,15 @@ function evaluateApplicability(condition, configuration) {
10
11
  if (cond.operator === "neq") return configValue !== conditionValue;
11
12
  return true;
12
13
  }
14
+ function entryMatchesDimensions(entry, dimensions, configuration) {
15
+ return dimensions.every((dim) => {
16
+ const configVal = String(configuration[dim.key] ?? "");
17
+ const entryVal = String(entry.dimensionValues[dim.key] ?? "");
18
+ return configVal.toLowerCase() === entryVal.toLowerCase();
19
+ });
20
+ }
13
21
  function matchEntry(entries, dimensions, configuration) {
14
- return entries.find(
15
- (entry) => dimensions.every((dim) => {
16
- const configVal = String(configuration[dim.key] ?? "");
17
- const entryVal = String(entry.dimensionValues[dim.key] ?? "");
18
- return configVal.toLowerCase() === entryVal.toLowerCase();
19
- })
20
- ) ?? null;
22
+ return entries.find((entry) => entryMatchesDimensions(entry, dimensions, configuration)) ?? null;
21
23
  }
22
24
  class DefaultCpqPricingService {
23
25
  constructor(em) {
@@ -157,32 +159,26 @@ class DefaultCpqPricingService {
157
159
  }
158
160
  async calculateCharge(params) {
159
161
  const { charge, configuration, tenantId, organizationId, currencyCode } = params;
160
- const method = charge.pricingMethod === "fixed" ? "flat" : charge.pricingMethod;
161
- if (charge.fixedPrice && !charge.pricingTableId) {
162
- const unitPrice = Number(charge.fixedPrice);
163
- return {
164
- chargeCode: charge.code,
165
- chargeName: charge.name,
166
- chargeType: charge.chargeType,
167
- pricingMethod: method,
168
- unitPrice,
169
- quantity: 1,
170
- totalPrice: unitPrice,
171
- currencyCode: charge.currencyCode ?? await getBaseCurrencyCode(this.em, { tenantId, organizationId })
172
- };
173
- }
162
+ const { model, source } = normalizeChargePricing(charge);
174
163
  const fallbackCurrency = currencyCode ?? charge.currencyCode ?? await getBaseCurrencyCode(this.em, { tenantId, organizationId });
175
164
  const base = {
176
165
  chargeCode: charge.code,
177
166
  chargeName: charge.name,
178
167
  chargeType: charge.chargeType,
179
- pricingMethod: method,
168
+ pricingMethod: model,
180
169
  unitPrice: 0,
181
170
  quantity: 1,
182
171
  totalPrice: 0,
183
172
  currencyCode: fallbackCurrency
184
173
  };
185
- if (!charge.pricingTableId) return base;
174
+ if (source === "fixed" || !charge.pricingTableId) {
175
+ const unitPrice = Number(charge.fixedPrice ?? 0);
176
+ if (model === "per_unit") {
177
+ const quantity = Number(configuration[charge.quantityAttributeCode ?? ""] ?? 0);
178
+ return { ...base, unitPrice, quantity, totalPrice: unitPrice * quantity };
179
+ }
180
+ return { ...base, unitPrice, quantity: 1, totalPrice: unitPrice };
181
+ }
186
182
  const table = await this.em.findOne(CpqPricingTable, {
187
183
  id: charge.pricingTableId,
188
184
  tenantId,
@@ -201,14 +197,17 @@ class DefaultCpqPricingService {
201
197
  }
202
198
  const entries = await this.em.find(CpqPricingTableEntry, entryFilters);
203
199
  const priceKey = charge.priceColumnKey ?? "";
204
- if (method === "flat") {
200
+ if (model === "flat") {
205
201
  return this.calculateFlat(base, charge, entries, table.dimensions, configuration, priceKey);
206
202
  }
207
- if (method === "per_unit") {
203
+ if (model === "per_unit") {
208
204
  return this.calculatePerUnit(base, charge, entries, table.dimensions, configuration, priceKey);
209
205
  }
210
- if (method === "tiered") {
211
- return this.calculateTiered(base, charge, entries, configuration, priceKey);
206
+ if (model === "volume") {
207
+ return this.calculateVolume(base, charge, entries, table.dimensions, configuration, priceKey);
208
+ }
209
+ if (model === "tiered") {
210
+ return this.calculateTiered(base, charge, entries, table.dimensions, configuration, priceKey);
212
211
  }
213
212
  return base;
214
213
  }
@@ -246,10 +245,12 @@ class DefaultCpqPricingService {
246
245
  const quantity = Number(configuration[charge.quantityAttributeCode ?? ""] ?? 0);
247
246
  return { ...base, unitPrice, quantity, totalPrice: unitPrice * quantity, currencyCode: entryCurrency };
248
247
  }
249
- calculateTiered(base, charge, entries, configuration, priceKey) {
248
+ calculateTiered(base, charge, entries, dimensions, configuration, priceKey) {
250
249
  const quantity = Number(configuration[charge.quantityAttributeCode ?? ""] ?? 0);
251
250
  if (quantity <= 0) return { ...base, quantity: 0, totalPrice: 0 };
252
- const sorted = [...entries].sort((a, b) => (a.tierNumber ?? 0) - (b.tierNumber ?? 0));
251
+ const matched = entries.filter((entry) => entryMatchesDimensions(entry, dimensions, configuration));
252
+ if (matched.length === 0) return base;
253
+ const sorted = [...matched].sort((a, b) => (a.tierNumber ?? 0) - (b.tierNumber ?? 0));
253
254
  let allocated = 0;
254
255
  let total = 0;
255
256
  const tiers = [];
@@ -282,6 +283,29 @@ class DefaultCpqPricingService {
282
283
  currencyCode: entryCurrency
283
284
  };
284
285
  }
286
+ // Volume pricing: the WHOLE quantity is billed at the single rate of the tier
287
+ // whose range contains the total quantity — unlike tiered, which bills each
288
+ // slice at its own rate. Dimension-filtered first, like tiered.
289
+ calculateVolume(base, charge, entries, dimensions, configuration, priceKey) {
290
+ const quantity = Number(configuration[charge.quantityAttributeCode ?? ""] ?? 0);
291
+ if (quantity <= 0) return { ...base, quantity: 0, totalPrice: 0 };
292
+ const matched = entries.filter((entry2) => entryMatchesDimensions(entry2, dimensions, configuration));
293
+ if (matched.length === 0) return base;
294
+ const sorted = [...matched].sort((a, b) => (a.tierNumber ?? 0) - (b.tierNumber ?? 0));
295
+ const entry = sorted.find((e) => {
296
+ const from = e.rangeFrom != null ? Number(e.rangeFrom) : 0;
297
+ const to = e.rangeTo != null ? Number(e.rangeTo) : Infinity;
298
+ return quantity >= from && quantity <= to;
299
+ }) ?? sorted[sorted.length - 1];
300
+ const unitPrice = entry.prices[priceKey] ?? 0;
301
+ return {
302
+ ...base,
303
+ unitPrice,
304
+ quantity,
305
+ totalPrice: unitPrice * quantity,
306
+ currencyCode: entry.currencyCode
307
+ };
308
+ }
285
309
  }
286
310
  export {
287
311
  DefaultCpqPricingService
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/cpq/services/cpqPricingService.ts"],
4
- "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { CpqProductCharge, CpqPricingTable, CpqPricingTableEntry, CpqPriceRule } from '../data/entities'\nimport type { ResolvedCharge, RuleAdjustment, TierBreakdown } from './types'\nimport { getBaseCurrencyCode } from './types'\n\nexport type { ResolvedCharge, RuleAdjustment, TierBreakdown }\n\nexport interface QuotePricingSummary {\n lines: Array<{\n lineId: string\n productId: string\n charges: ResolvedCharge[]\n }>\n totals: {\n nrcTotal: number\n mrcTotal: number\n usageCharges: Array<{ chargeCode: string; unitPrice: number; note: string }>\n }\n currencyCode: string\n}\n\ninterface ApplicabilityCondition {\n attribute: string\n operator: 'eq' | 'neq'\n value: string\n}\n\nfunction evaluateApplicability(\n condition: Record<string, unknown> | null | undefined,\n configuration: Record<string, unknown>,\n): boolean {\n if (!condition) return true\n const cond = condition as unknown as ApplicabilityCondition\n if (!cond.attribute || !cond.operator) return true\n const configValue = String(configuration[cond.attribute] ?? '')\n const conditionValue = String(cond.value ?? '')\n if (cond.operator === 'eq') return configValue === conditionValue\n if (cond.operator === 'neq') return configValue !== conditionValue\n return true\n}\n\nfunction matchEntry(\n entries: CpqPricingTableEntry[],\n dimensions: Array<{ key: string }>,\n configuration: Record<string, unknown>,\n): CpqPricingTableEntry | null {\n return (\n entries.find((entry) =>\n dimensions.every((dim) => {\n const configVal = String(configuration[dim.key] ?? '')\n const entryVal = String(entry.dimensionValues[dim.key] ?? '')\n return configVal.toLowerCase() === entryVal.toLowerCase()\n }),\n ) ?? null\n )\n}\n\nexport class DefaultCpqPricingService {\n private em: EntityManager\n\n constructor(em: EntityManager) {\n this.em = em\n }\n\n /**\n * Resolve charges for an offering (v2) or product (v1 fallback).\n * When offeringId is provided, charges are loaded by offeringId.\n * When only productId is provided, falls back to loading by productId.\n */\n async resolveProductCharges(params: {\n offeringId?: string | null\n productId?: string | null\n configuration: Record<string, unknown>\n currencyCode?: string\n tenantId: string\n organizationId: string\n }): Promise<ResolvedCharge[]> {\n const { offeringId, productId, configuration, tenantId, organizationId, currencyCode } = params\n\n const chargeFilter: Record<string, unknown> = {\n tenantId,\n organizationId,\n isActive: true,\n deletedAt: null,\n }\n\n if (offeringId) {\n chargeFilter.offeringId = offeringId\n } else if (productId) {\n chargeFilter.productId = productId\n } else {\n return []\n }\n\n const charges = await this.em.find(CpqProductCharge, chargeFilter, {\n orderBy: { sortOrder: 'asc' },\n })\n\n const applicableCharges = charges.filter((charge) =>\n evaluateApplicability(charge.applicabilityCondition, configuration),\n )\n\n const results: ResolvedCharge[] = []\n for (const charge of applicableCharges) {\n const resolved = await this.calculateCharge({\n charge,\n configuration,\n tenantId,\n organizationId,\n currencyCode,\n })\n // R-CPRICE-8: quote-level currency overrides entry-level currency for consistency.\n // Entry filtering by currencyCode (line 274) ensures price values already match.\n if (currencyCode) {\n resolved.currencyCode = currencyCode\n }\n resolved.adjustments = []\n results.push(resolved)\n }\n\n const rules = await this.loadPriceRules({\n offeringId: offeringId ?? null,\n tenantId,\n organizationId,\n })\n\n if (rules.length > 0) {\n this.applyPriceRules(results, rules, configuration)\n }\n\n return results\n }\n\n private async loadPriceRules(params: {\n offeringId: string | null | undefined\n tenantId: string\n organizationId: string\n }): Promise<CpqPriceRule[]> {\n const { offeringId, tenantId, organizationId } = params\n\n const rules = await this.em.find(\n CpqPriceRule,\n {\n tenantId,\n organizationId,\n isActive: true,\n deletedAt: null,\n $or: [\n { productOfferingId: null },\n ...(offeringId ? [{ productOfferingId: offeringId }] : []),\n ],\n },\n { orderBy: { sortOrder: 'asc' } },\n )\n\n return rules\n }\n\n private applyPriceRules(\n charges: ResolvedCharge[],\n rules: CpqPriceRule[],\n configuration: Record<string, unknown>,\n ): void {\n for (const rule of rules) {\n if (!evaluateApplicability(rule.applicabilityCondition, configuration)) continue\n\n for (const charge of charges) {\n if (rule.chargeCodeFilter && rule.chargeCodeFilter !== charge.chargeCode) continue\n if (rule.chargeTypeFilter && rule.chargeTypeFilter !== charge.chargeType) continue\n\n const unitPriceBefore = charge.unitPrice\n const ruleValue = Number(rule.value)\n let unitPriceAfter: number\n let totalPriceOverride: number | undefined\n\n switch (rule.ruleType) {\n case 'discount_percent':\n unitPriceAfter = Math.max(0, unitPriceBefore * (1 - ruleValue / 100))\n break\n case 'discount_absolute': {\n const currentTotal = charge.totalPrice ?? unitPriceBefore\n totalPriceOverride = Math.max(0, currentTotal - ruleValue)\n const qty = charge.quantity && charge.quantity > 0 ? charge.quantity : 1\n unitPriceAfter = totalPriceOverride / qty\n break\n }\n case 'surcharge_percent':\n unitPriceAfter = unitPriceBefore * (1 + ruleValue / 100)\n break\n case 'surcharge_absolute': {\n const currentTotal = charge.totalPrice ?? unitPriceBefore\n totalPriceOverride = currentTotal + ruleValue\n const qty = charge.quantity && charge.quantity > 0 ? charge.quantity : 1\n unitPriceAfter = totalPriceOverride / qty\n break\n }\n case 'price_override':\n unitPriceAfter = ruleValue\n break\n default:\n continue\n }\n\n charge.unitPrice = unitPriceAfter\n\n if (charge.chargeType === 'usage') {\n // totalPrice stays null for usage charges\n } else if (totalPriceOverride !== undefined) {\n charge.totalPrice = totalPriceOverride\n } else if (charge.quantity !== null) {\n charge.totalPrice = unitPriceAfter * charge.quantity\n } else {\n charge.totalPrice = unitPriceAfter\n }\n\n const adjustment: RuleAdjustment = {\n ruleCode: rule.code,\n ruleName: rule.name,\n ruleType: rule.ruleType,\n value: ruleValue,\n unitPriceBefore,\n unitPriceAfter,\n delta: unitPriceAfter - unitPriceBefore,\n }\n\n if (!charge.adjustments) {\n charge.adjustments = [adjustment]\n } else {\n charge.adjustments.push(adjustment)\n }\n }\n }\n }\n\n async calculateCharge(params: {\n charge: CpqProductCharge\n configuration: Record<string, unknown>\n tenantId: string\n organizationId: string\n currencyCode?: string\n }): Promise<ResolvedCharge> {\n const { charge, configuration, tenantId, organizationId, currencyCode } = params\n\n // `fixed` is a legacy alias for `flat` \u2014 older seeds emit it directly\n // and ~30+ rows in prod still carry it. Treat them identically.\n const method = charge.pricingMethod === 'fixed' ? 'flat' : charge.pricingMethod\n\n // If charge has a fixed price and no pricing table, use fixed price directly\n if (charge.fixedPrice && !charge.pricingTableId) {\n const unitPrice = Number(charge.fixedPrice)\n return {\n chargeCode: charge.code,\n chargeName: charge.name,\n chargeType: charge.chargeType as ResolvedCharge['chargeType'],\n pricingMethod: method as ResolvedCharge['pricingMethod'],\n unitPrice,\n quantity: 1,\n totalPrice: unitPrice,\n currencyCode: charge.currencyCode ?? await getBaseCurrencyCode(this.em, { tenantId, organizationId }),\n }\n }\n\n const fallbackCurrency = currencyCode ?? charge.currencyCode ?? await getBaseCurrencyCode(this.em, { tenantId, organizationId })\n const base: ResolvedCharge = {\n chargeCode: charge.code,\n chargeName: charge.name,\n chargeType: charge.chargeType as ResolvedCharge['chargeType'],\n pricingMethod: method as ResolvedCharge['pricingMethod'],\n unitPrice: 0,\n quantity: 1,\n totalPrice: 0,\n currencyCode: fallbackCurrency,\n }\n\n if (!charge.pricingTableId) return base\n\n const table = await this.em.findOne(CpqPricingTable, {\n id: charge.pricingTableId,\n tenantId,\n organizationId,\n })\n if (!table) return base\n\n const entryFilters: Record<string, unknown> = {\n pricingTableId: table.id,\n tenantId,\n organizationId,\n isActive: true,\n deletedAt: null,\n }\n if (currencyCode) {\n entryFilters.currencyCode = currencyCode\n }\n\n const entries = await this.em.find(CpqPricingTableEntry, entryFilters)\n\n const priceKey = charge.priceColumnKey ?? ''\n\n if (method === 'flat') {\n return this.calculateFlat(base, charge, entries, table.dimensions, configuration, priceKey)\n }\n if (method === 'per_unit') {\n return this.calculatePerUnit(base, charge, entries, table.dimensions, configuration, priceKey)\n }\n if (method === 'tiered') {\n return this.calculateTiered(base, charge, entries, configuration, priceKey)\n }\n\n return base\n }\n\n private calculateFlat(\n base: ResolvedCharge,\n charge: CpqProductCharge,\n entries: CpqPricingTableEntry[],\n dimensions: Array<{ key: string }>,\n configuration: Record<string, unknown>,\n priceKey: string,\n ): ResolvedCharge {\n const entry = matchEntry(entries, dimensions, configuration)\n if (!entry) return base\n const unitPrice = entry.prices[priceKey] ?? 0\n\n if (charge.chargeType === 'usage') {\n return {\n ...base,\n unitPrice,\n quantity: null,\n totalPrice: null,\n currencyCode: entry.currencyCode,\n note: charge.description ?? 'Usage-based charge \u2014 billed on actual consumption.',\n }\n }\n\n return { ...base, unitPrice, quantity: 1, totalPrice: unitPrice, currencyCode: entry.currencyCode }\n }\n\n private calculatePerUnit(\n base: ResolvedCharge,\n charge: CpqProductCharge,\n entries: CpqPricingTableEntry[],\n dimensions: Array<{ key: string }>,\n configuration: Record<string, unknown>,\n priceKey: string,\n ): ResolvedCharge {\n const entry = matchEntry(entries, dimensions, configuration)\n if (!entry) return base\n const unitPrice = entry.prices[priceKey] ?? 0\n const entryCurrency = entry.currencyCode\n\n if (charge.chargeType === 'usage') {\n return {\n ...base,\n unitPrice,\n quantity: null,\n totalPrice: null,\n currencyCode: entryCurrency,\n note: charge.description ?? 'Usage-based charge \u2014 billed on actual consumption.',\n }\n }\n\n const quantity = Number(configuration[charge.quantityAttributeCode ?? ''] ?? 0)\n return { ...base, unitPrice, quantity, totalPrice: unitPrice * quantity, currencyCode: entryCurrency }\n }\n\n private calculateTiered(\n base: ResolvedCharge,\n charge: CpqProductCharge,\n entries: CpqPricingTableEntry[],\n configuration: Record<string, unknown>,\n priceKey: string,\n ): ResolvedCharge {\n const quantity = Number(configuration[charge.quantityAttributeCode ?? ''] ?? 0)\n if (quantity <= 0) return { ...base, quantity: 0, totalPrice: 0 }\n\n const sorted = [...entries].sort((a, b) => (a.tierNumber ?? 0) - (b.tierNumber ?? 0))\n\n let allocated = 0\n let total = 0\n const tiers: TierBreakdown['tiers'] = []\n let entryCurrency = base.currencyCode\n\n for (const entry of sorted) {\n if (allocated >= quantity) break\n const to = entry.rangeTo ? Number(entry.rangeTo) : Infinity\n const tierCeil = to === Infinity ? quantity : Math.min(to, quantity)\n const qty = tierCeil - allocated\n if (qty <= 0) continue\n const price = entry.prices[priceKey] ?? 0\n entryCurrency = entry.currencyCode\n tiers.push({\n tier: entry.tierNumber ?? 0,\n from: allocated + 1,\n to: allocated + qty,\n pricePerUnit: price,\n quantity: qty,\n subtotal: qty * price,\n })\n total += qty * price\n allocated += qty\n }\n\n return {\n ...base,\n unitPrice: quantity > 0 ? total / quantity : 0,\n quantity,\n totalPrice: total,\n breakdown: { tiers },\n currencyCode: entryCurrency,\n }\n }\n}\n"],
5
- "mappings": "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",
6
- "names": []
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { CpqProductCharge, CpqPricingTable, CpqPricingTableEntry, CpqPriceRule } from '../data/entities'\nimport type { ResolvedCharge, RuleAdjustment, TierBreakdown } from './types'\nimport { getBaseCurrencyCode } from './types'\nimport { normalizeChargePricing } from '../data/charge-pricing'\n\nexport type { ResolvedCharge, RuleAdjustment, TierBreakdown }\n\nexport interface QuotePricingSummary {\n lines: Array<{\n lineId: string\n productId: string\n charges: ResolvedCharge[]\n }>\n totals: {\n nrcTotal: number\n mrcTotal: number\n usageCharges: Array<{ chargeCode: string; unitPrice: number; note: string }>\n }\n currencyCode: string\n}\n\ninterface ApplicabilityCondition {\n attribute: string\n operator: 'eq' | 'neq'\n value: string\n}\n\nfunction evaluateApplicability(\n condition: Record<string, unknown> | null | undefined,\n configuration: Record<string, unknown>,\n): boolean {\n if (!condition) return true\n const cond = condition as unknown as ApplicabilityCondition\n if (!cond.attribute || !cond.operator) return true\n const configValue = String(configuration[cond.attribute] ?? '')\n const conditionValue = String(cond.value ?? '')\n if (cond.operator === 'eq') return configValue === conditionValue\n if (cond.operator === 'neq') return configValue !== conditionValue\n return true\n}\n\nfunction entryMatchesDimensions(\n entry: CpqPricingTableEntry,\n dimensions: Array<{ key: string }>,\n configuration: Record<string, unknown>,\n): boolean {\n return dimensions.every((dim) => {\n const configVal = String(configuration[dim.key] ?? '')\n const entryVal = String(entry.dimensionValues[dim.key] ?? '')\n return configVal.toLowerCase() === entryVal.toLowerCase()\n })\n}\n\nfunction matchEntry(\n entries: CpqPricingTableEntry[],\n dimensions: Array<{ key: string }>,\n configuration: Record<string, unknown>,\n): CpqPricingTableEntry | null {\n return entries.find((entry) => entryMatchesDimensions(entry, dimensions, configuration)) ?? null\n}\n\nexport class DefaultCpqPricingService {\n private em: EntityManager\n\n constructor(em: EntityManager) {\n this.em = em\n }\n\n /**\n * Resolve charges for an offering (v2) or product (v1 fallback).\n * When offeringId is provided, charges are loaded by offeringId.\n * When only productId is provided, falls back to loading by productId.\n */\n async resolveProductCharges(params: {\n offeringId?: string | null\n productId?: string | null\n configuration: Record<string, unknown>\n currencyCode?: string\n tenantId: string\n organizationId: string\n }): Promise<ResolvedCharge[]> {\n const { offeringId, productId, configuration, tenantId, organizationId, currencyCode } = params\n\n const chargeFilter: Record<string, unknown> = {\n tenantId,\n organizationId,\n isActive: true,\n deletedAt: null,\n }\n\n if (offeringId) {\n chargeFilter.offeringId = offeringId\n } else if (productId) {\n chargeFilter.productId = productId\n } else {\n return []\n }\n\n const charges = await this.em.find(CpqProductCharge, chargeFilter, {\n orderBy: { sortOrder: 'asc' },\n })\n\n const applicableCharges = charges.filter((charge) =>\n evaluateApplicability(charge.applicabilityCondition, configuration),\n )\n\n const results: ResolvedCharge[] = []\n for (const charge of applicableCharges) {\n const resolved = await this.calculateCharge({\n charge,\n configuration,\n tenantId,\n organizationId,\n currencyCode,\n })\n // R-CPRICE-8: quote-level currency overrides entry-level currency for consistency.\n // Entry filtering by currencyCode (line 274) ensures price values already match.\n if (currencyCode) {\n resolved.currencyCode = currencyCode\n }\n resolved.adjustments = []\n results.push(resolved)\n }\n\n const rules = await this.loadPriceRules({\n offeringId: offeringId ?? null,\n tenantId,\n organizationId,\n })\n\n if (rules.length > 0) {\n this.applyPriceRules(results, rules, configuration)\n }\n\n return results\n }\n\n private async loadPriceRules(params: {\n offeringId: string | null | undefined\n tenantId: string\n organizationId: string\n }): Promise<CpqPriceRule[]> {\n const { offeringId, tenantId, organizationId } = params\n\n const rules = await this.em.find(\n CpqPriceRule,\n {\n tenantId,\n organizationId,\n isActive: true,\n deletedAt: null,\n $or: [\n { productOfferingId: null },\n ...(offeringId ? [{ productOfferingId: offeringId }] : []),\n ],\n },\n { orderBy: { sortOrder: 'asc' } },\n )\n\n return rules\n }\n\n private applyPriceRules(\n charges: ResolvedCharge[],\n rules: CpqPriceRule[],\n configuration: Record<string, unknown>,\n ): void {\n for (const rule of rules) {\n if (!evaluateApplicability(rule.applicabilityCondition, configuration)) continue\n\n for (const charge of charges) {\n if (rule.chargeCodeFilter && rule.chargeCodeFilter !== charge.chargeCode) continue\n if (rule.chargeTypeFilter && rule.chargeTypeFilter !== charge.chargeType) continue\n\n const unitPriceBefore = charge.unitPrice\n const ruleValue = Number(rule.value)\n let unitPriceAfter: number\n let totalPriceOverride: number | undefined\n\n switch (rule.ruleType) {\n case 'discount_percent':\n unitPriceAfter = Math.max(0, unitPriceBefore * (1 - ruleValue / 100))\n break\n case 'discount_absolute': {\n const currentTotal = charge.totalPrice ?? unitPriceBefore\n totalPriceOverride = Math.max(0, currentTotal - ruleValue)\n const qty = charge.quantity && charge.quantity > 0 ? charge.quantity : 1\n unitPriceAfter = totalPriceOverride / qty\n break\n }\n case 'surcharge_percent':\n unitPriceAfter = unitPriceBefore * (1 + ruleValue / 100)\n break\n case 'surcharge_absolute': {\n const currentTotal = charge.totalPrice ?? unitPriceBefore\n totalPriceOverride = currentTotal + ruleValue\n const qty = charge.quantity && charge.quantity > 0 ? charge.quantity : 1\n unitPriceAfter = totalPriceOverride / qty\n break\n }\n case 'price_override':\n unitPriceAfter = ruleValue\n break\n default:\n continue\n }\n\n charge.unitPrice = unitPriceAfter\n\n if (charge.chargeType === 'usage') {\n // totalPrice stays null for usage charges\n } else if (totalPriceOverride !== undefined) {\n charge.totalPrice = totalPriceOverride\n } else if (charge.quantity !== null) {\n charge.totalPrice = unitPriceAfter * charge.quantity\n } else {\n charge.totalPrice = unitPriceAfter\n }\n\n const adjustment: RuleAdjustment = {\n ruleCode: rule.code,\n ruleName: rule.name,\n ruleType: rule.ruleType,\n value: ruleValue,\n unitPriceBefore,\n unitPriceAfter,\n delta: unitPriceAfter - unitPriceBefore,\n }\n\n if (!charge.adjustments) {\n charge.adjustments = [adjustment]\n } else {\n charge.adjustments.push(adjustment)\n }\n }\n }\n }\n\n async calculateCharge(params: {\n charge: CpqProductCharge\n configuration: Record<string, unknown>\n tenantId: string\n organizationId: string\n currencyCode?: string\n }): Promise<ResolvedCharge> {\n const { charge, configuration, tenantId, organizationId, currencyCode } = params\n\n // Resolve the two axes (chargeModel \u00D7 pricingSource), tolerating legacy\n // rows that still carry a combined pricingMethod (flat | per_unit | tiered).\n const { model, source } = normalizeChargePricing(charge)\n\n const fallbackCurrency = currencyCode ?? charge.currencyCode ?? await getBaseCurrencyCode(this.em, { tenantId, organizationId })\n const base: ResolvedCharge = {\n chargeCode: charge.code,\n chargeName: charge.name,\n chargeType: charge.chargeType as ResolvedCharge['chargeType'],\n pricingMethod: model,\n unitPrice: 0,\n quantity: 1,\n totalPrice: 0,\n currencyCode: fallbackCurrency,\n }\n\n // \u2500\u2500 Fixed price: unit price is the charge's own fixedPrice. Also the\n // fallback when a table-sourced charge has no table configured yet. \u2500\u2500\n if (source === 'fixed' || !charge.pricingTableId) {\n const unitPrice = Number(charge.fixedPrice ?? 0)\n if (model === 'per_unit') {\n const quantity = Number(configuration[charge.quantityAttributeCode ?? ''] ?? 0)\n return { ...base, unitPrice, quantity, totalPrice: unitPrice * quantity }\n }\n // flat (volume/tiered without a table degrade to a single flat price).\n return { ...base, unitPrice, quantity: 1, totalPrice: unitPrice }\n }\n\n // \u2500\u2500 Table lookup \u2500\u2500\n const table = await this.em.findOne(CpqPricingTable, {\n id: charge.pricingTableId,\n tenantId,\n organizationId,\n })\n if (!table) return base\n\n const entryFilters: Record<string, unknown> = {\n pricingTableId: table.id,\n tenantId,\n organizationId,\n isActive: true,\n deletedAt: null,\n }\n if (currencyCode) {\n entryFilters.currencyCode = currencyCode\n }\n\n const entries = await this.em.find(CpqPricingTableEntry, entryFilters)\n\n const priceKey = charge.priceColumnKey ?? ''\n\n if (model === 'flat') {\n return this.calculateFlat(base, charge, entries, table.dimensions, configuration, priceKey)\n }\n if (model === 'per_unit') {\n return this.calculatePerUnit(base, charge, entries, table.dimensions, configuration, priceKey)\n }\n if (model === 'volume') {\n return this.calculateVolume(base, charge, entries, table.dimensions, configuration, priceKey)\n }\n if (model === 'tiered') {\n return this.calculateTiered(base, charge, entries, table.dimensions, configuration, priceKey)\n }\n\n return base\n }\n\n private calculateFlat(\n base: ResolvedCharge,\n charge: CpqProductCharge,\n entries: CpqPricingTableEntry[],\n dimensions: Array<{ key: string }>,\n configuration: Record<string, unknown>,\n priceKey: string,\n ): ResolvedCharge {\n const entry = matchEntry(entries, dimensions, configuration)\n if (!entry) return base\n const unitPrice = entry.prices[priceKey] ?? 0\n\n if (charge.chargeType === 'usage') {\n return {\n ...base,\n unitPrice,\n quantity: null,\n totalPrice: null,\n currencyCode: entry.currencyCode,\n note: charge.description ?? 'Usage-based charge \u2014 billed on actual consumption.',\n }\n }\n\n return { ...base, unitPrice, quantity: 1, totalPrice: unitPrice, currencyCode: entry.currencyCode }\n }\n\n private calculatePerUnit(\n base: ResolvedCharge,\n charge: CpqProductCharge,\n entries: CpqPricingTableEntry[],\n dimensions: Array<{ key: string }>,\n configuration: Record<string, unknown>,\n priceKey: string,\n ): ResolvedCharge {\n const entry = matchEntry(entries, dimensions, configuration)\n if (!entry) return base\n const unitPrice = entry.prices[priceKey] ?? 0\n const entryCurrency = entry.currencyCode\n\n if (charge.chargeType === 'usage') {\n return {\n ...base,\n unitPrice,\n quantity: null,\n totalPrice: null,\n currencyCode: entryCurrency,\n note: charge.description ?? 'Usage-based charge \u2014 billed on actual consumption.',\n }\n }\n\n const quantity = Number(configuration[charge.quantityAttributeCode ?? ''] ?? 0)\n return { ...base, unitPrice, quantity, totalPrice: unitPrice * quantity, currencyCode: entryCurrency }\n }\n\n private calculateTiered(\n base: ResolvedCharge,\n charge: CpqProductCharge,\n entries: CpqPricingTableEntry[],\n dimensions: Array<{ key: string }>,\n configuration: Record<string, unknown>,\n priceKey: string,\n ): ResolvedCharge {\n const quantity = Number(configuration[charge.quantityAttributeCode ?? ''] ?? 0)\n if (quantity <= 0) return { ...base, quantity: 0, totalPrice: 0 }\n\n // tiered must filter to the dimension-matched slice first; flat/per_unit do this via matchEntry.\n const matched = entries.filter((entry) => entryMatchesDimensions(entry, dimensions, configuration))\n // No row matches the configured dimensions \u2192 unpriced; fall back to base\n // (quantity 1, total 0), consistent with flat/per_unit's null-match path.\n if (matched.length === 0) return base\n\n const sorted = [...matched].sort((a, b) => (a.tierNumber ?? 0) - (b.tierNumber ?? 0))\n\n let allocated = 0\n let total = 0\n const tiers: TierBreakdown['tiers'] = []\n let entryCurrency = base.currencyCode\n\n for (const entry of sorted) {\n if (allocated >= quantity) break\n const to = entry.rangeTo ? Number(entry.rangeTo) : Infinity\n const tierCeil = to === Infinity ? quantity : Math.min(to, quantity)\n const qty = tierCeil - allocated\n if (qty <= 0) continue\n const price = entry.prices[priceKey] ?? 0\n entryCurrency = entry.currencyCode\n tiers.push({\n tier: entry.tierNumber ?? 0,\n from: allocated + 1,\n to: allocated + qty,\n pricePerUnit: price,\n quantity: qty,\n subtotal: qty * price,\n })\n total += qty * price\n allocated += qty\n }\n\n return {\n ...base,\n unitPrice: quantity > 0 ? total / quantity : 0,\n quantity,\n totalPrice: total,\n breakdown: { tiers },\n currencyCode: entryCurrency,\n }\n }\n\n // Volume pricing: the WHOLE quantity is billed at the single rate of the tier\n // whose range contains the total quantity \u2014 unlike tiered, which bills each\n // slice at its own rate. Dimension-filtered first, like tiered.\n private calculateVolume(\n base: ResolvedCharge,\n charge: CpqProductCharge,\n entries: CpqPricingTableEntry[],\n dimensions: Array<{ key: string }>,\n configuration: Record<string, unknown>,\n priceKey: string,\n ): ResolvedCharge {\n const quantity = Number(configuration[charge.quantityAttributeCode ?? ''] ?? 0)\n if (quantity <= 0) return { ...base, quantity: 0, totalPrice: 0 }\n\n const matched = entries.filter((entry) => entryMatchesDimensions(entry, dimensions, configuration))\n if (matched.length === 0) return base\n\n // Ranges are [rangeFrom, rangeTo]; rangeTo null = \u221E. Fall back to the top\n // tier if the quantity overflows every defined range.\n const sorted = [...matched].sort((a, b) => (a.tierNumber ?? 0) - (b.tierNumber ?? 0))\n const entry =\n sorted.find((e) => {\n const from = e.rangeFrom != null ? Number(e.rangeFrom) : 0\n const to = e.rangeTo != null ? Number(e.rangeTo) : Infinity\n return quantity >= from && quantity <= to\n }) ?? sorted[sorted.length - 1]\n\n const unitPrice = entry.prices[priceKey] ?? 0\n return {\n ...base,\n unitPrice,\n quantity,\n totalPrice: unitPrice * quantity,\n currencyCode: entry.currencyCode,\n }\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["entry"]
7
7
  }