@dainamite/cpq 0.4.0 → 0.5.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 (63) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/MIGRATION.md +27 -0
  3. package/dist/modules/cpq/api/_helpers/handleArcError.js +2 -2
  4. package/dist/modules/cpq/api/_helpers/handleArcError.js.map +2 -2
  5. package/dist/modules/cpq/api/orders/[orderId]/activate/route.js +2 -2
  6. package/dist/modules/cpq/api/orders/[orderId]/activate/route.js.map +2 -2
  7. package/dist/modules/cpq/api/orders/[orderId]/route.js +2 -2
  8. package/dist/modules/cpq/api/orders/[orderId]/route.js.map +2 -2
  9. package/dist/modules/cpq/api/orders/[orderId]/status/route.js +2 -2
  10. package/dist/modules/cpq/api/orders/[orderId]/status/route.js.map +2 -2
  11. package/dist/modules/cpq/api/orders/route.js +2 -2
  12. package/dist/modules/cpq/api/orders/route.js.map +2 -2
  13. package/dist/modules/cpq/api/quotes/[quoteId]/clone/route.js +2 -2
  14. package/dist/modules/cpq/api/quotes/[quoteId]/clone/route.js.map +2 -2
  15. package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/route.js +3 -3
  16. package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/route.js.map +2 -2
  17. package/dist/modules/cpq/api/quotes/[quoteId]/recalculate/route.js +2 -2
  18. package/dist/modules/cpq/api/quotes/[quoteId]/recalculate/route.js.map +2 -2
  19. package/dist/modules/cpq/api/quotes/[quoteId]/route.js +2 -2
  20. package/dist/modules/cpq/api/quotes/[quoteId]/route.js.map +2 -2
  21. package/dist/modules/cpq/api/quotes/route.js +2 -2
  22. package/dist/modules/cpq/api/quotes/route.js.map +2 -2
  23. package/dist/modules/cpq/backend/cpq/inventory/AssetsTable.js.map +1 -1
  24. package/dist/modules/cpq/backend/cpq/inventory/SubscriptionsTable.js.map +1 -1
  25. package/dist/modules/cpq/backend/cpq/offerings/page.js.map +1 -1
  26. package/dist/modules/cpq/backend/cpq/orders/page.js.map +1 -1
  27. package/dist/modules/cpq/backend/cpq/price-rules/page.js.map +1 -1
  28. package/dist/modules/cpq/backend/cpq/pricing/page.js.map +1 -1
  29. package/dist/modules/cpq/backend/cpq/quotes/[id]/page.js.map +2 -2
  30. package/dist/modules/cpq/backend/cpq/quotes/page.js.map +1 -1
  31. package/dist/modules/cpq/backend/cpq/specifications/page.js.map +1 -1
  32. package/dist/modules/cpq/backend/cpq/wizards/page.js.map +1 -1
  33. package/dist/modules/cpq/components/CpqListView.js.map +2 -2
  34. package/dist/modules/cpq/services/cpqOrderService.js +9 -2
  35. package/dist/modules/cpq/services/cpqOrderService.js.map +2 -2
  36. package/dist/modules/cpq/services/cpqQuotingService.js +9 -2
  37. package/dist/modules/cpq/services/cpqQuotingService.js.map +2 -2
  38. package/package.json +9 -9
  39. package/src/modules/cpq/__integration__/TC-CPQ-002.spec.ts +8 -5
  40. package/src/modules/cpq/api/_helpers/handleArcError.ts +12 -12
  41. package/src/modules/cpq/api/orders/[orderId]/activate/route.ts +2 -2
  42. package/src/modules/cpq/api/orders/[orderId]/route.ts +2 -2
  43. package/src/modules/cpq/api/orders/[orderId]/status/route.ts +2 -2
  44. package/src/modules/cpq/api/orders/route.ts +2 -2
  45. package/src/modules/cpq/api/quotes/[quoteId]/clone/route.ts +2 -2
  46. package/src/modules/cpq/api/quotes/[quoteId]/items/[lineId]/route.ts +3 -3
  47. package/src/modules/cpq/api/quotes/[quoteId]/recalculate/route.ts +2 -2
  48. package/src/modules/cpq/api/quotes/[quoteId]/route.ts +2 -2
  49. package/src/modules/cpq/api/quotes/route.ts +2 -2
  50. package/src/modules/cpq/backend/cpq/inventory/AssetsTable.tsx +1 -1
  51. package/src/modules/cpq/backend/cpq/inventory/SubscriptionsTable.tsx +1 -1
  52. package/src/modules/cpq/backend/cpq/offerings/page.tsx +1 -1
  53. package/src/modules/cpq/backend/cpq/orders/page.tsx +1 -1
  54. package/src/modules/cpq/backend/cpq/price-rules/page.tsx +1 -1
  55. package/src/modules/cpq/backend/cpq/pricing/page.tsx +1 -1
  56. package/src/modules/cpq/backend/cpq/quotes/[id]/page.tsx +4 -3
  57. package/src/modules/cpq/backend/cpq/quotes/page.tsx +1 -1
  58. package/src/modules/cpq/backend/cpq/specifications/page.tsx +1 -1
  59. package/src/modules/cpq/backend/cpq/wizards/page.tsx +1 -1
  60. package/src/modules/cpq/components/CpqListView.tsx +2 -1
  61. package/src/modules/cpq/services/__tests__/errorBrands.test.ts +72 -0
  62. package/src/modules/cpq/services/cpqOrderService.ts +10 -0
  63. package/src/modules/cpq/services/cpqQuotingService.ts +14 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # @dainamite/cpq
2
2
 
3
+ ## 0.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - d7ce539: Require Open Mercato `0.9.0`. Peer ranges move from `^0.6.7` to `^0.9.0` for
8
+ `@open-mercato/core`, `shared`, `ui`, `events` and `queue`, and the MikroORM
9
+ peers from `^7.1.5` to `^7.1.14`. Consumers on Open Mercato `0.6.x`–`0.8.x`
10
+ must upgrade before taking these versions.
11
+
12
+ `@dainamite/cpq` and `@dainamite/billing` now peer on `@tanstack/react-table`
13
+ `^9.0.0` (was `^8.0.0 || ^9.0.0`): Open Mercato 0.7 moved its `DataTable` to
14
+ TanStack Table v9, and list pages now import `LegacyColumnDef` from
15
+ `@tanstack/react-table/legacy`, which v8 does not have.
16
+
17
+ `@dainamite/cpq` also fixes CPQ quote and order API routes answering `500`
18
+ instead of the service's typed status (e.g. `404 Quote not found`). Next
19
+ bundles the package once per server layer and the DI container is shared, so
20
+ the route could catch a `QuotingError` / `OrderError` from the other copy of
21
+ the class and `instanceof` failed. Both classes now carry a `Symbol.for`
22
+ brand; new `isQuotingError` / `isOrderError` guards are exported alongside
23
+ them and should be used instead of `instanceof` in route handlers.
24
+
3
25
  ## 0.4.0
4
26
 
5
27
  ### Minor Changes
package/MIGRATION.md CHANGED
@@ -166,6 +166,33 @@ the `@dainamite/cpq` package, per
166
166
 
167
167
  ## Schema / API changelog (track breaking changes here)
168
168
 
169
+ ### 2026-11-03 — Open Mercato 0.9.0 peers; `isQuotingError` / `isOrderError`
170
+
171
+ **Peer ranges (breaking for consumers).** `@open-mercato/*` `^0.9.0`,
172
+ MikroORM `^7.1.14`, `@tanstack/react-table` `^9.0.0` (list pages import
173
+ `LegacyColumnDef` from `@tanstack/react-table/legacy`, absent in v8). See
174
+ `.changeset/open-mercato-090-peers.md`.
175
+
176
+ **Do not use `instanceof QuotingError` / `instanceof OrderError` across the
177
+ DI boundary.** Next bundles this package once per server layer (page SSR vs
178
+ route handler) while the DI container is shared through `globalThis`, so a
179
+ route can catch an error built from the other copy of the class and
180
+ `instanceof` is `false` — a typed 404/409 becomes a 500. This was already
181
+ true before this release; it was just undetected. Both classes now carry a
182
+ `Symbol.for` brand, and two guards are exported next to them:
183
+
184
+ ```ts
185
+ import { isQuotingError } from '@dainamite/cpq/modules/cpq/services/cpqQuotingService'
186
+ import { isOrderError } from '@dainamite/cpq/modules/cpq/services/cpqOrderService'
187
+
188
+ } catch (err) {
189
+ if (isQuotingError(err)) return NextResponse.json({ error: err.message }, { status: err.status })
190
+ ```
191
+
192
+ Consumer code that copied CPQ's old route catch blocks should switch to the
193
+ guards. `instanceof` remains fine inside the service layer itself, where the
194
+ thrower and the catcher are the same module copy.
195
+
169
196
  ### 2026-06-02 — XD-297 Charge model / pricing method split
170
197
 
171
198
  Splits the conflated `cpq_product_charges.pricing_method` into two orthogonal
@@ -1,7 +1,7 @@
1
1
  import { NextResponse } from "next/server";
2
2
  import { z } from "zod";
3
3
  import { OptimisticLockError } from "@mikro-orm/core";
4
- import { QuotingError } from "../../services/cpqQuotingService.js";
4
+ import { isQuotingError } from "../../services/cpqQuotingService.js";
5
5
  function handleArcError(err, ctx) {
6
6
  if (err instanceof z.ZodError) {
7
7
  return NextResponse.json(
@@ -9,7 +9,7 @@ function handleArcError(err, ctx) {
9
9
  { status: 400 }
10
10
  );
11
11
  }
12
- if (err instanceof QuotingError) {
12
+ if (isQuotingError(err)) {
13
13
  const body = { error: err.message };
14
14
  if (err.cpqStatus) body.cpqStatus = err.cpqStatus;
15
15
  if (err.details) Object.assign(body, err.details);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/cpq/api/_helpers/handleArcError.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { OptimisticLockError } from '@mikro-orm/core'\nimport { QuotingError } from '../../services/cpqQuotingService'\n\n/**\n * Shared error \u2192 JSON-response converter for ARC routes (XD-250).\n *\n * Handles three families:\n * 1. Zod validation errors \u2192 400 with `details` array.\n * 2. Service-layer `QuotingError` (matched both via `instanceof` AND via\n * duck-typing on `name === 'QuotingError'`) \u2192 propagates `err.status`.\n * The duck-typing fallback exists because Turbopack hot-reload can\n * produce two distinct class instances for the same source file\n * across module boundaries, breaking `instanceof` checks at runtime.\n * 3. cpqInventoryService throws plain `{ status, error }` objects \u2192\n * propagate the embedded status.\n * 4. Anything else \u2192 500 + console.error with the route context.\n *\n * Returning the response keeps route handlers terse:\n * `} catch (err) { return handleArcError(err, 'cpq/quotes/from-subscription.POST') }`\n */\nexport function handleArcError(err: unknown, ctx: string): NextResponse {\n if (err instanceof z.ZodError) {\n return NextResponse.json(\n { error: 'Validation error', details: err.issues },\n { status: 400 },\n )\n }\n\n if (err instanceof QuotingError) {\n const body: Record<string, unknown> = { error: err.message }\n if (err.cpqStatus) body.cpqStatus = err.cpqStatus\n if (err.details) Object.assign(body, err.details)\n return NextResponse.json(body, { status: err.status })\n }\n\n // MikroORM optimistic-lock collision on subscription.version (XD-250 spec\n // \u00A7 Risks \"Order activation racing with another mutation\"). Both\n // `instanceof OptimisticLockError` AND constructor-name fallback to\n // survive Turbopack class drift.\n if (\n err instanceof OptimisticLockError ||\n (err &&\n typeof err === 'object' &&\n typeof (err as { constructor?: { name?: string } }).constructor?.name === 'string' &&\n (err as { constructor: { name: string } }).constructor.name === 'OptimisticLockError')\n ) {\n return NextResponse.json(\n { error: 'Concurrent modification detected', details: (err as Error).message },\n { status: 409 },\n )\n }\n\n // Duck-typed QuotingError (Turbopack instanceof drift) and inventory-service\n // plain throw shape `{ status, error }`. Both surface a numeric `status`.\n // Also propagates `details` (object) into response body \u2014 preserves the\n // structured payload (e.g. `existingQuoteId`) that the typed branch above\n // would have surfaced if instanceof had matched.\n if (\n err &&\n typeof err === 'object' &&\n 'status' in err &&\n typeof (err as { status: unknown }).status === 'number'\n ) {\n const e = err as {\n status: number\n message?: string\n error?: string\n name?: string\n details?: Record<string, unknown>\n }\n const message = e.message ?? e.error ?? 'Conflict'\n const body: Record<string, unknown> = { error: message }\n if (e.details && typeof e.details === 'object') Object.assign(body, e.details)\n return NextResponse.json(body, { status: e.status })\n }\n\n console.error(`[${ctx}]`, err)\n return NextResponse.json({ error: 'Internal error' }, { status: 500 })\n}\n"],
5
- "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,2BAA2B;AACpC,SAAS,oBAAoB;AAmBtB,SAAS,eAAe,KAAc,KAA2B;AACtE,MAAI,eAAe,EAAE,UAAU;AAC7B,WAAO,aAAa;AAAA,MAClB,EAAE,OAAO,oBAAoB,SAAS,IAAI,OAAO;AAAA,MACjD,EAAE,QAAQ,IAAI;AAAA,IAChB;AAAA,EACF;AAEA,MAAI,eAAe,cAAc;AAC/B,UAAM,OAAgC,EAAE,OAAO,IAAI,QAAQ;AAC3D,QAAI,IAAI,UAAW,MAAK,YAAY,IAAI;AACxC,QAAI,IAAI,QAAS,QAAO,OAAO,MAAM,IAAI,OAAO;AAChD,WAAO,aAAa,KAAK,MAAM,EAAE,QAAQ,IAAI,OAAO,CAAC;AAAA,EACvD;AAMA,MACE,eAAe,uBACd,OACC,OAAO,QAAQ,YACf,OAAQ,IAA4C,aAAa,SAAS,YACzE,IAA0C,YAAY,SAAS,uBAClE;AACA,WAAO,aAAa;AAAA,MAClB,EAAE,OAAO,oCAAoC,SAAU,IAAc,QAAQ;AAAA,MAC7E,EAAE,QAAQ,IAAI;AAAA,IAChB;AAAA,EACF;AAOA,MACE,OACA,OAAO,QAAQ,YACf,YAAY,OACZ,OAAQ,IAA4B,WAAW,UAC/C;AACA,UAAM,IAAI;AAOV,UAAM,UAAU,EAAE,WAAW,EAAE,SAAS;AACxC,UAAM,OAAgC,EAAE,OAAO,QAAQ;AACvD,QAAI,EAAE,WAAW,OAAO,EAAE,YAAY,SAAU,QAAO,OAAO,MAAM,EAAE,OAAO;AAC7E,WAAO,aAAa,KAAK,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC;AAAA,EACrD;AAEA,UAAQ,MAAM,IAAI,GAAG,KAAK,GAAG;AAC7B,SAAO,aAAa,KAAK,EAAE,OAAO,iBAAiB,GAAG,EAAE,QAAQ,IAAI,CAAC;AACvE;",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { OptimisticLockError } from '@mikro-orm/core'\nimport { isQuotingError } from '../../services/cpqQuotingService'\n\n/**\n * Shared error \u2192 JSON-response converter for ARC routes (XD-250).\n *\n * Handles three families:\n * 1. Zod validation errors \u2192 400 with `details` array.\n * 2. Service-layer `QuotingError` (matched via its cross-realm brand,\n * `isQuotingError`, with a duck-typed fallback below) \u2192 propagates\n * `err.status`. `instanceof` is unreliable here: Next bundles this\n * package once per server layer, so the route and the DI-resolved\n * service can hold two distinct copies of the class \u2014 in production\n * too, not only under hot-reload.\n * 3. cpqInventoryService throws plain `{ status, error }` objects \u2192\n * propagate the embedded status.\n * 4. Anything else \u2192 500 + console.error with the route context.\n *\n * Returning the response keeps route handlers terse:\n * `} catch (err) { return handleArcError(err, 'cpq/quotes/from-subscription.POST') }`\n */\nexport function handleArcError(err: unknown, ctx: string): NextResponse {\n if (err instanceof z.ZodError) {\n return NextResponse.json(\n { error: 'Validation error', details: err.issues },\n { status: 400 },\n )\n }\n\n if (isQuotingError(err)) {\n const body: Record<string, unknown> = { error: err.message }\n if (err.cpqStatus) body.cpqStatus = err.cpqStatus\n if (err.details) Object.assign(body, err.details)\n return NextResponse.json(body, { status: err.status })\n }\n\n // MikroORM optimistic-lock collision on subscription.version (XD-250 spec\n // \u00A7 Risks \"Order activation racing with another mutation\"). Both\n // `instanceof OptimisticLockError` AND constructor-name fallback to\n // survive Turbopack class drift.\n if (\n err instanceof OptimisticLockError ||\n (err &&\n typeof err === 'object' &&\n typeof (err as { constructor?: { name?: string } }).constructor?.name === 'string' &&\n (err as { constructor: { name: string } }).constructor.name === 'OptimisticLockError')\n ) {\n return NextResponse.json(\n { error: 'Concurrent modification detected', details: (err as Error).message },\n { status: 409 },\n )\n }\n\n // cpqInventoryService's plain throw shape `{ status, error }` (QuotingError\n // from any bundle copy is already handled by `isQuotingError` above).\n // Propagates `details` (object) into the response body so structured\n // payloads survive here too.\n if (\n err &&\n typeof err === 'object' &&\n 'status' in err &&\n typeof (err as { status: unknown }).status === 'number'\n ) {\n const e = err as {\n status: number\n message?: string\n error?: string\n name?: string\n details?: Record<string, unknown>\n }\n const message = e.message ?? e.error ?? 'Conflict'\n const body: Record<string, unknown> = { error: message }\n if (e.details && typeof e.details === 'object') Object.assign(body, e.details)\n return NextResponse.json(body, { status: e.status })\n }\n\n console.error(`[${ctx}]`, err)\n return NextResponse.json({ error: 'Internal error' }, { status: 500 })\n}\n"],
5
+ "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,2BAA2B;AACpC,SAAS,sBAAsB;AAoBxB,SAAS,eAAe,KAAc,KAA2B;AACtE,MAAI,eAAe,EAAE,UAAU;AAC7B,WAAO,aAAa;AAAA,MAClB,EAAE,OAAO,oBAAoB,SAAS,IAAI,OAAO;AAAA,MACjD,EAAE,QAAQ,IAAI;AAAA,IAChB;AAAA,EACF;AAEA,MAAI,eAAe,GAAG,GAAG;AACvB,UAAM,OAAgC,EAAE,OAAO,IAAI,QAAQ;AAC3D,QAAI,IAAI,UAAW,MAAK,YAAY,IAAI;AACxC,QAAI,IAAI,QAAS,QAAO,OAAO,MAAM,IAAI,OAAO;AAChD,WAAO,aAAa,KAAK,MAAM,EAAE,QAAQ,IAAI,OAAO,CAAC;AAAA,EACvD;AAMA,MACE,eAAe,uBACd,OACC,OAAO,QAAQ,YACf,OAAQ,IAA4C,aAAa,SAAS,YACzE,IAA0C,YAAY,SAAS,uBAClE;AACA,WAAO,aAAa;AAAA,MAClB,EAAE,OAAO,oCAAoC,SAAU,IAAc,QAAQ;AAAA,MAC7E,EAAE,QAAQ,IAAI;AAAA,IAChB;AAAA,EACF;AAMA,MACE,OACA,OAAO,QAAQ,YACf,YAAY,OACZ,OAAQ,IAA4B,WAAW,UAC/C;AACA,UAAM,IAAI;AAOV,UAAM,UAAU,EAAE,WAAW,EAAE,SAAS;AACxC,UAAM,OAAgC,EAAE,OAAO,QAAQ;AACvD,QAAI,EAAE,WAAW,OAAO,EAAE,YAAY,SAAU,QAAO,OAAO,MAAM,EAAE,OAAO;AAC7E,WAAO,aAAa,KAAK,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC;AAAA,EACrD;AAEA,UAAQ,MAAM,IAAI,GAAG,KAAK,GAAG;AAC7B,SAAO,aAAa,KAAK,EAAE,OAAO,iBAAiB,GAAG,EAAE,QAAQ,IAAI,CAAC;AACvE;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  import { NextResponse } from "next/server";
2
2
  import { OptimisticLockError } from "@mikro-orm/core";
3
3
  import { resolveCpqRouteContext } from "../../../context.js";
4
- import { OrderError } from "../../../../services/cpqOrderService.js";
4
+ import { isOrderError } from "../../../../services/cpqOrderService.js";
5
5
  const metadata = {
6
6
  POST: { requireAuth: true, requireFeatures: ["cpq.orders.manage"] }
7
7
  };
@@ -15,7 +15,7 @@ async function POST(req, { params }) {
15
15
  const result = await service.activateOrder(orderId, scope);
16
16
  return NextResponse.json(result);
17
17
  } catch (err) {
18
- if (err instanceof OrderError) {
18
+ if (isOrderError(err)) {
19
19
  return NextResponse.json({ error: err.message }, { status: err.status });
20
20
  }
21
21
  if (err instanceof OptimisticLockError || err && typeof err === "object" && err.constructor?.name === "OptimisticLockError") {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../src/modules/cpq/api/orders/%5BorderId%5D/activate/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { OptimisticLockError } from '@mikro-orm/core'\nimport { resolveCpqRouteContext } from '../../../context'\nimport { OrderError } from '../../../../services/cpqOrderService'\nimport type { DefaultCpqOrderService } from '../../../../services/cpqOrderService'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['cpq.orders.manage'] },\n}\n\nexport async function POST(req: Request, { params }: { params: Promise<{ orderId: string }> }) {\n try {\n const ctx = await resolveCpqRouteContext(req)\n if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const { orderId } = await params\n const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }\n\n const service = ctx.container.resolve('cpqOrderService') as DefaultCpqOrderService\n const result = await service.activateOrder(orderId, scope)\n return NextResponse.json(result)\n } catch (err) {\n if (err instanceof OrderError) {\n return NextResponse.json({ error: err.message }, { status: err.status })\n }\n // XD-250 ARC: optimistic-lock collision on subscription.version when two\n // ARC orders touching the same sub race to activate. Spec \u00A7 Risks:\n // \"second activation gets 409, order stays in pending_activation,\n // retryable\". Constructor-name fallback survives Turbopack class drift.\n if (\n err instanceof OptimisticLockError ||\n (err &&\n typeof err === 'object' &&\n (err as { constructor?: { name?: string } }).constructor?.name === 'OptimisticLockError')\n ) {\n return NextResponse.json(\n { error: 'Concurrent modification detected', details: (err as Error).message },\n { status: 409 },\n )\n }\n const errObj = err as Record<string, unknown>\n if (typeof errObj?.status === 'number' && typeof errObj?.error === 'string') {\n return NextResponse.json({ error: errObj.error }, { status: errObj.status as number })\n }\n console.error('[cpq/orders/[orderId]/activate.POST]', err)\n return NextResponse.json({ error: 'Internal error' }, { status: 500 })\n }\n}\n"],
5
- "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,2BAA2B;AACpC,SAAS,8BAA8B;AACvC,SAAS,kBAAkB;AAGpB,MAAM,WAAW;AAAA,EACtB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,mBAAmB,EAAE;AACpE;AAEA,eAAsB,KAAK,KAAc,EAAE,OAAO,GAA6C;AAC7F,MAAI;AACF,UAAM,MAAM,MAAM,uBAAuB,GAAG;AAC5C,QAAI,CAAC,IAAI,KAAM,QAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAElF,UAAM,EAAE,QAAQ,IAAI,MAAM;AAC1B,UAAM,QAAQ,EAAE,gBAAgB,IAAI,gBAAgB,UAAU,IAAI,SAAS;AAE3E,UAAM,UAAU,IAAI,UAAU,QAAQ,iBAAiB;AACvD,UAAM,SAAS,MAAM,QAAQ,cAAc,SAAS,KAAK;AACzD,WAAO,aAAa,KAAK,MAAM;AAAA,EACjC,SAAS,KAAK;AACZ,QAAI,eAAe,YAAY;AAC7B,aAAO,aAAa,KAAK,EAAE,OAAO,IAAI,QAAQ,GAAG,EAAE,QAAQ,IAAI,OAAO,CAAC;AAAA,IACzE;AAKA,QACE,eAAe,uBACd,OACC,OAAO,QAAQ,YACd,IAA4C,aAAa,SAAS,uBACrE;AACA,aAAO,aAAa;AAAA,QAClB,EAAE,OAAO,oCAAoC,SAAU,IAAc,QAAQ;AAAA,QAC7E,EAAE,QAAQ,IAAI;AAAA,MAChB;AAAA,IACF;AACA,UAAM,SAAS;AACf,QAAI,OAAO,QAAQ,WAAW,YAAY,OAAO,QAAQ,UAAU,UAAU;AAC3E,aAAO,aAAa,KAAK,EAAE,OAAO,OAAO,MAAM,GAAG,EAAE,QAAQ,OAAO,OAAiB,CAAC;AAAA,IACvF;AACA,YAAQ,MAAM,wCAAwC,GAAG;AACzD,WAAO,aAAa,KAAK,EAAE,OAAO,iBAAiB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACvE;AACF;",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { OptimisticLockError } from '@mikro-orm/core'\nimport { resolveCpqRouteContext } from '../../../context'\nimport { isOrderError } from '../../../../services/cpqOrderService'\nimport type { DefaultCpqOrderService } from '../../../../services/cpqOrderService'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['cpq.orders.manage'] },\n}\n\nexport async function POST(req: Request, { params }: { params: Promise<{ orderId: string }> }) {\n try {\n const ctx = await resolveCpqRouteContext(req)\n if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const { orderId } = await params\n const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }\n\n const service = ctx.container.resolve('cpqOrderService') as DefaultCpqOrderService\n const result = await service.activateOrder(orderId, scope)\n return NextResponse.json(result)\n } catch (err) {\n if (isOrderError(err)) {\n return NextResponse.json({ error: err.message }, { status: err.status })\n }\n // XD-250 ARC: optimistic-lock collision on subscription.version when two\n // ARC orders touching the same sub race to activate. Spec \u00A7 Risks:\n // \"second activation gets 409, order stays in pending_activation,\n // retryable\". Constructor-name fallback survives Turbopack class drift.\n if (\n err instanceof OptimisticLockError ||\n (err &&\n typeof err === 'object' &&\n (err as { constructor?: { name?: string } }).constructor?.name === 'OptimisticLockError')\n ) {\n return NextResponse.json(\n { error: 'Concurrent modification detected', details: (err as Error).message },\n { status: 409 },\n )\n }\n const errObj = err as Record<string, unknown>\n if (typeof errObj?.status === 'number' && typeof errObj?.error === 'string') {\n return NextResponse.json({ error: errObj.error }, { status: errObj.status as number })\n }\n console.error('[cpq/orders/[orderId]/activate.POST]', err)\n return NextResponse.json({ error: 'Internal error' }, { status: 500 })\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,2BAA2B;AACpC,SAAS,8BAA8B;AACvC,SAAS,oBAAoB;AAGtB,MAAM,WAAW;AAAA,EACtB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,mBAAmB,EAAE;AACpE;AAEA,eAAsB,KAAK,KAAc,EAAE,OAAO,GAA6C;AAC7F,MAAI;AACF,UAAM,MAAM,MAAM,uBAAuB,GAAG;AAC5C,QAAI,CAAC,IAAI,KAAM,QAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAElF,UAAM,EAAE,QAAQ,IAAI,MAAM;AAC1B,UAAM,QAAQ,EAAE,gBAAgB,IAAI,gBAAgB,UAAU,IAAI,SAAS;AAE3E,UAAM,UAAU,IAAI,UAAU,QAAQ,iBAAiB;AACvD,UAAM,SAAS,MAAM,QAAQ,cAAc,SAAS,KAAK;AACzD,WAAO,aAAa,KAAK,MAAM;AAAA,EACjC,SAAS,KAAK;AACZ,QAAI,aAAa,GAAG,GAAG;AACrB,aAAO,aAAa,KAAK,EAAE,OAAO,IAAI,QAAQ,GAAG,EAAE,QAAQ,IAAI,OAAO,CAAC;AAAA,IACzE;AAKA,QACE,eAAe,uBACd,OACC,OAAO,QAAQ,YACd,IAA4C,aAAa,SAAS,uBACrE;AACA,aAAO,aAAa;AAAA,QAClB,EAAE,OAAO,oCAAoC,SAAU,IAAc,QAAQ;AAAA,QAC7E,EAAE,QAAQ,IAAI;AAAA,MAChB;AAAA,IACF;AACA,UAAM,SAAS;AACf,QAAI,OAAO,QAAQ,WAAW,YAAY,OAAO,QAAQ,UAAU,UAAU;AAC3E,aAAO,aAAa,KAAK,EAAE,OAAO,OAAO,MAAM,GAAG,EAAE,QAAQ,OAAO,OAAiB,CAAC;AAAA,IACvF;AACA,YAAQ,MAAM,wCAAwC,GAAG;AACzD,WAAO,aAAa,KAAK,EAAE,OAAO,iBAAiB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACvE;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,6 +1,6 @@
1
1
  import { NextResponse } from "next/server";
2
2
  import { resolveCpqRouteContext } from "../../context.js";
3
- import { OrderError } from "../../../services/cpqOrderService.js";
3
+ import { isOrderError } from "../../../services/cpqOrderService.js";
4
4
  const metadata = {
5
5
  GET: { requireAuth: true, requireFeatures: ["cpq.orders.view"] }
6
6
  };
@@ -14,7 +14,7 @@ async function GET(req, { params }) {
14
14
  const result = await service.getOrder(orderId, scope);
15
15
  return NextResponse.json(result);
16
16
  } catch (err) {
17
- if (err instanceof OrderError) {
17
+ if (isOrderError(err)) {
18
18
  return NextResponse.json({ error: err.message }, { status: err.status });
19
19
  }
20
20
  console.error("[cpq/orders/[orderId].GET]", err);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/cpq/api/orders/%5BorderId%5D/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { resolveCpqRouteContext } from '../../context'\nimport { OrderError } from '../../../services/cpqOrderService'\nimport type { DefaultCpqOrderService } from '../../../services/cpqOrderService'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['cpq.orders.view'] },\n}\n\nexport async function GET(req: Request, { params }: { params: Promise<{ orderId: string }> }) {\n try {\n const ctx = await resolveCpqRouteContext(req)\n if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const { orderId } = await params\n const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }\n\n const service = ctx.container.resolve('cpqOrderService') as DefaultCpqOrderService\n const result = await service.getOrder(orderId, scope)\n return NextResponse.json(result)\n } catch (err) {\n if (err instanceof OrderError) {\n return NextResponse.json({ error: err.message }, { status: err.status })\n }\n console.error('[cpq/orders/[orderId].GET]', err)\n return NextResponse.json({ error: 'Internal error' }, { status: 500 })\n }\n}\n"],
5
- "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,8BAA8B;AACvC,SAAS,kBAAkB;AAGpB,MAAM,WAAW;AAAA,EACtB,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,iBAAiB,EAAE;AACjE;AAEA,eAAsB,IAAI,KAAc,EAAE,OAAO,GAA6C;AAC5F,MAAI;AACF,UAAM,MAAM,MAAM,uBAAuB,GAAG;AAC5C,QAAI,CAAC,IAAI,KAAM,QAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAElF,UAAM,EAAE,QAAQ,IAAI,MAAM;AAC1B,UAAM,QAAQ,EAAE,gBAAgB,IAAI,gBAAgB,UAAU,IAAI,SAAS;AAE3E,UAAM,UAAU,IAAI,UAAU,QAAQ,iBAAiB;AACvD,UAAM,SAAS,MAAM,QAAQ,SAAS,SAAS,KAAK;AACpD,WAAO,aAAa,KAAK,MAAM;AAAA,EACjC,SAAS,KAAK;AACZ,QAAI,eAAe,YAAY;AAC7B,aAAO,aAAa,KAAK,EAAE,OAAO,IAAI,QAAQ,GAAG,EAAE,QAAQ,IAAI,OAAO,CAAC;AAAA,IACzE;AACA,YAAQ,MAAM,8BAA8B,GAAG;AAC/C,WAAO,aAAa,KAAK,EAAE,OAAO,iBAAiB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACvE;AACF;",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { resolveCpqRouteContext } from '../../context'\nimport { isOrderError } from '../../../services/cpqOrderService'\nimport type { DefaultCpqOrderService } from '../../../services/cpqOrderService'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['cpq.orders.view'] },\n}\n\nexport async function GET(req: Request, { params }: { params: Promise<{ orderId: string }> }) {\n try {\n const ctx = await resolveCpqRouteContext(req)\n if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const { orderId } = await params\n const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }\n\n const service = ctx.container.resolve('cpqOrderService') as DefaultCpqOrderService\n const result = await service.getOrder(orderId, scope)\n return NextResponse.json(result)\n } catch (err) {\n if (isOrderError(err)) {\n return NextResponse.json({ error: err.message }, { status: err.status })\n }\n console.error('[cpq/orders/[orderId].GET]', err)\n return NextResponse.json({ error: 'Internal error' }, { status: 500 })\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,8BAA8B;AACvC,SAAS,oBAAoB;AAGtB,MAAM,WAAW;AAAA,EACtB,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,iBAAiB,EAAE;AACjE;AAEA,eAAsB,IAAI,KAAc,EAAE,OAAO,GAA6C;AAC5F,MAAI;AACF,UAAM,MAAM,MAAM,uBAAuB,GAAG;AAC5C,QAAI,CAAC,IAAI,KAAM,QAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAElF,UAAM,EAAE,QAAQ,IAAI,MAAM;AAC1B,UAAM,QAAQ,EAAE,gBAAgB,IAAI,gBAAgB,UAAU,IAAI,SAAS;AAE3E,UAAM,UAAU,IAAI,UAAU,QAAQ,iBAAiB;AACvD,UAAM,SAAS,MAAM,QAAQ,SAAS,SAAS,KAAK;AACpD,WAAO,aAAa,KAAK,MAAM;AAAA,EACjC,SAAS,KAAK;AACZ,QAAI,aAAa,GAAG,GAAG;AACrB,aAAO,aAAa,KAAK,EAAE,OAAO,IAAI,QAAQ,GAAG,EAAE,QAAQ,IAAI,OAAO,CAAC;AAAA,IACzE;AACA,YAAQ,MAAM,8BAA8B,GAAG;AAC/C,WAAO,aAAa,KAAK,EAAE,OAAO,iBAAiB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACvE;AACF;",
6
6
  "names": []
7
7
  }
@@ -2,7 +2,7 @@ import { NextResponse } from "next/server";
2
2
  import { z } from "zod";
3
3
  import { resolveCpqRouteContext } from "../../../context.js";
4
4
  import { cpqOrderStatusTransitionSchema } from "../../../../data/validators.js";
5
- import { OrderError } from "../../../../services/cpqOrderService.js";
5
+ import { isOrderError } from "../../../../services/cpqOrderService.js";
6
6
  const metadata = {
7
7
  POST: { requireAuth: true, requireFeatures: ["cpq.orders.manage"] }
8
8
  };
@@ -20,7 +20,7 @@ async function POST(req, { params }) {
20
20
  if (err instanceof z.ZodError) {
21
21
  return NextResponse.json({ error: "Validation error", details: err.issues }, { status: 400 });
22
22
  }
23
- if (err instanceof OrderError) {
23
+ if (isOrderError(err)) {
24
24
  return NextResponse.json({ error: err.message }, { status: err.status });
25
25
  }
26
26
  const errObj = err;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../src/modules/cpq/api/orders/%5BorderId%5D/status/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { resolveCpqRouteContext } from '../../../context'\nimport { cpqOrderStatusTransitionSchema } from '../../../../data/validators'\nimport { OrderError } from '../../../../services/cpqOrderService'\nimport type { DefaultCpqOrderService } from '../../../../services/cpqOrderService'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['cpq.orders.manage'] },\n}\n\nexport async function POST(req: Request, { params }: { params: Promise<{ orderId: string }> }) {\n try {\n const ctx = await resolveCpqRouteContext(req)\n if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const { orderId } = await params\n const body = cpqOrderStatusTransitionSchema.parse(await req.json())\n const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }\n\n const service = ctx.container.resolve('cpqOrderService') as DefaultCpqOrderService\n const result = await service.transitionOrderStatus(orderId, body.targetStatus, scope)\n return NextResponse.json(result)\n } catch (err) {\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: 'Validation error', details: err.issues }, { status: 400 })\n }\n if (err instanceof OrderError) {\n return NextResponse.json({ error: err.message }, { status: err.status })\n }\n const errObj = err as Record<string, unknown>\n if (typeof errObj?.status === 'number' && typeof errObj?.error === 'string') {\n return NextResponse.json({ error: errObj.error }, { status: errObj.status as number })\n }\n console.error('[cpq/orders/[orderId]/status.POST]', err)\n return NextResponse.json({ error: 'Internal error' }, { status: 500 })\n }\n}\n"],
5
- "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,8BAA8B;AACvC,SAAS,sCAAsC;AAC/C,SAAS,kBAAkB;AAGpB,MAAM,WAAW;AAAA,EACtB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,mBAAmB,EAAE;AACpE;AAEA,eAAsB,KAAK,KAAc,EAAE,OAAO,GAA6C;AAC7F,MAAI;AACF,UAAM,MAAM,MAAM,uBAAuB,GAAG;AAC5C,QAAI,CAAC,IAAI,KAAM,QAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAElF,UAAM,EAAE,QAAQ,IAAI,MAAM;AAC1B,UAAM,OAAO,+BAA+B,MAAM,MAAM,IAAI,KAAK,CAAC;AAClE,UAAM,QAAQ,EAAE,gBAAgB,IAAI,gBAAgB,UAAU,IAAI,SAAS;AAE3E,UAAM,UAAU,IAAI,UAAU,QAAQ,iBAAiB;AACvD,UAAM,SAAS,MAAM,QAAQ,sBAAsB,SAAS,KAAK,cAAc,KAAK;AACpF,WAAO,aAAa,KAAK,MAAM;AAAA,EACjC,SAAS,KAAK;AACZ,QAAI,eAAe,EAAE,UAAU;AAC7B,aAAO,aAAa,KAAK,EAAE,OAAO,oBAAoB,SAAS,IAAI,OAAO,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC9F;AACA,QAAI,eAAe,YAAY;AAC7B,aAAO,aAAa,KAAK,EAAE,OAAO,IAAI,QAAQ,GAAG,EAAE,QAAQ,IAAI,OAAO,CAAC;AAAA,IACzE;AACA,UAAM,SAAS;AACf,QAAI,OAAO,QAAQ,WAAW,YAAY,OAAO,QAAQ,UAAU,UAAU;AAC3E,aAAO,aAAa,KAAK,EAAE,OAAO,OAAO,MAAM,GAAG,EAAE,QAAQ,OAAO,OAAiB,CAAC;AAAA,IACvF;AACA,YAAQ,MAAM,sCAAsC,GAAG;AACvD,WAAO,aAAa,KAAK,EAAE,OAAO,iBAAiB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACvE;AACF;",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { resolveCpqRouteContext } from '../../../context'\nimport { cpqOrderStatusTransitionSchema } from '../../../../data/validators'\nimport { isOrderError } from '../../../../services/cpqOrderService'\nimport type { DefaultCpqOrderService } from '../../../../services/cpqOrderService'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['cpq.orders.manage'] },\n}\n\nexport async function POST(req: Request, { params }: { params: Promise<{ orderId: string }> }) {\n try {\n const ctx = await resolveCpqRouteContext(req)\n if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const { orderId } = await params\n const body = cpqOrderStatusTransitionSchema.parse(await req.json())\n const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }\n\n const service = ctx.container.resolve('cpqOrderService') as DefaultCpqOrderService\n const result = await service.transitionOrderStatus(orderId, body.targetStatus, scope)\n return NextResponse.json(result)\n } catch (err) {\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: 'Validation error', details: err.issues }, { status: 400 })\n }\n if (isOrderError(err)) {\n return NextResponse.json({ error: err.message }, { status: err.status })\n }\n const errObj = err as Record<string, unknown>\n if (typeof errObj?.status === 'number' && typeof errObj?.error === 'string') {\n return NextResponse.json({ error: errObj.error }, { status: errObj.status as number })\n }\n console.error('[cpq/orders/[orderId]/status.POST]', err)\n return NextResponse.json({ error: 'Internal error' }, { status: 500 })\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,8BAA8B;AACvC,SAAS,sCAAsC;AAC/C,SAAS,oBAAoB;AAGtB,MAAM,WAAW;AAAA,EACtB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,mBAAmB,EAAE;AACpE;AAEA,eAAsB,KAAK,KAAc,EAAE,OAAO,GAA6C;AAC7F,MAAI;AACF,UAAM,MAAM,MAAM,uBAAuB,GAAG;AAC5C,QAAI,CAAC,IAAI,KAAM,QAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAElF,UAAM,EAAE,QAAQ,IAAI,MAAM;AAC1B,UAAM,OAAO,+BAA+B,MAAM,MAAM,IAAI,KAAK,CAAC;AAClE,UAAM,QAAQ,EAAE,gBAAgB,IAAI,gBAAgB,UAAU,IAAI,SAAS;AAE3E,UAAM,UAAU,IAAI,UAAU,QAAQ,iBAAiB;AACvD,UAAM,SAAS,MAAM,QAAQ,sBAAsB,SAAS,KAAK,cAAc,KAAK;AACpF,WAAO,aAAa,KAAK,MAAM;AAAA,EACjC,SAAS,KAAK;AACZ,QAAI,eAAe,EAAE,UAAU;AAC7B,aAAO,aAAa,KAAK,EAAE,OAAO,oBAAoB,SAAS,IAAI,OAAO,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC9F;AACA,QAAI,aAAa,GAAG,GAAG;AACrB,aAAO,aAAa,KAAK,EAAE,OAAO,IAAI,QAAQ,GAAG,EAAE,QAAQ,IAAI,OAAO,CAAC;AAAA,IACzE;AACA,UAAM,SAAS;AACf,QAAI,OAAO,QAAQ,WAAW,YAAY,OAAO,QAAQ,UAAU,UAAU;AAC3E,aAAO,aAAa,KAAK,EAAE,OAAO,OAAO,MAAM,GAAG,EAAE,QAAQ,OAAO,OAAiB,CAAC;AAAA,IACvF;AACA,YAAQ,MAAM,sCAAsC,GAAG;AACvD,WAAO,aAAa,KAAK,EAAE,OAAO,iBAAiB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACvE;AACF;",
6
6
  "names": []
7
7
  }
@@ -2,7 +2,7 @@ import { NextResponse } from "next/server";
2
2
  import { z } from "zod";
3
3
  import { resolveCpqRouteContext } from "../context.js";
4
4
  import { cpqConvertQuoteToOrderSchema } from "../../data/validators.js";
5
- import { OrderError } from "../../services/cpqOrderService.js";
5
+ import { isOrderError } from "../../services/cpqOrderService.js";
6
6
  const metadata = {
7
7
  POST: { requireAuth: true, requireFeatures: ["cpq.orders.manage"] },
8
8
  GET: { requireAuth: true, requireFeatures: ["cpq.orders.view"] }
@@ -53,7 +53,7 @@ function handleError(err, context) {
53
53
  if (err instanceof z.ZodError) {
54
54
  return NextResponse.json({ error: "Validation error", details: err.issues }, { status: 400 });
55
55
  }
56
- if (err instanceof OrderError) {
56
+ if (isOrderError(err)) {
57
57
  return NextResponse.json({ error: err.message }, { status: err.status });
58
58
  }
59
59
  const errObj = err;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/cpq/api/orders/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { resolveCpqRouteContext } from '../context'\nimport { cpqConvertQuoteToOrderSchema } from '../../data/validators'\nimport { OrderError } from '../../services/cpqOrderService'\nimport type { DefaultCpqOrderService } from '../../services/cpqOrderService'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['cpq.orders.manage'] },\n GET: { requireAuth: true, requireFeatures: ['cpq.orders.view'] },\n}\n\nexport async function POST(req: Request) {\n try {\n const ctx = await resolveCpqRouteContext(req)\n if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const body = cpqConvertQuoteToOrderSchema.parse(await req.json())\n const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }\n\n const service = ctx.container.resolve('cpqOrderService') as DefaultCpqOrderService\n const result = await service.convertQuoteToOrder(body.quoteId, scope)\n return NextResponse.json(result, { status: 201 })\n } catch (err) {\n return handleError(err, 'cpq/orders.POST')\n }\n}\n\nexport async function GET(req: Request) {\n try {\n const ctx = await resolveCpqRouteContext(req)\n if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }\n const url = new URL(req.url)\n const ALLOWED_SORT_FIELDS = ['createdAt', 'updatedAt', 'cpqStatus', 'activatedAt', 'currencyCode'] as const\n const sortFieldParam = url.searchParams.get('sortField') ?? ''\n const sortField = (ALLOWED_SORT_FIELDS as readonly string[]).includes(sortFieldParam)\n ? (sortFieldParam as (typeof ALLOWED_SORT_FIELDS)[number])\n : undefined\n const sortDir = url.searchParams.get('sortDir') === 'asc' ? 'asc' : 'desc'\n\n const filters = {\n customerId: url.searchParams.get('customerId') ?? undefined,\n sourceQuoteId: url.searchParams.get('sourceQuoteId') ?? undefined,\n salesOrderId: url.searchParams.get('salesOrderId') ?? undefined,\n cpqStatus: url.searchParams.get('cpqStatus') ?? undefined,\n currencyCode: url.searchParams.get('currencyCode') ?? undefined,\n search: url.searchParams.get('search') ?? undefined,\n sortField,\n sortDir,\n page: url.searchParams.has('page') ? Number(url.searchParams.get('page')) : undefined,\n pageSize: url.searchParams.has('pageSize') ? Number(url.searchParams.get('pageSize')) : undefined,\n } as const\n\n const service = ctx.container.resolve('cpqOrderService') as DefaultCpqOrderService\n const result = await service.listOrders(filters, scope)\n return NextResponse.json(result)\n } catch (err) {\n return handleError(err, 'cpq/orders.GET')\n }\n}\n\nfunction handleError(err: unknown, context: string) {\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: 'Validation error', details: err.issues }, { status: 400 })\n }\n if (err instanceof OrderError) {\n return NextResponse.json({ error: err.message }, { status: err.status })\n }\n const errObj = err as Record<string, unknown>\n if (typeof errObj?.status === 'number' && typeof errObj?.error === 'string') {\n return NextResponse.json({ error: errObj.error }, { status: errObj.status as number })\n }\n console.error(`[${context}]`, err)\n return NextResponse.json({ error: 'Internal error' }, { status: 500 })\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { resolveCpqRouteContext } from '../context'\nimport { cpqConvertQuoteToOrderSchema } from '../../data/validators'\nimport { isOrderError } from '../../services/cpqOrderService'\nimport type { DefaultCpqOrderService } from '../../services/cpqOrderService'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['cpq.orders.manage'] },\n GET: { requireAuth: true, requireFeatures: ['cpq.orders.view'] },\n}\n\nexport async function POST(req: Request) {\n try {\n const ctx = await resolveCpqRouteContext(req)\n if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const body = cpqConvertQuoteToOrderSchema.parse(await req.json())\n const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }\n\n const service = ctx.container.resolve('cpqOrderService') as DefaultCpqOrderService\n const result = await service.convertQuoteToOrder(body.quoteId, scope)\n return NextResponse.json(result, { status: 201 })\n } catch (err) {\n return handleError(err, 'cpq/orders.POST')\n }\n}\n\nexport async function GET(req: Request) {\n try {\n const ctx = await resolveCpqRouteContext(req)\n if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }\n const url = new URL(req.url)\n const ALLOWED_SORT_FIELDS = ['createdAt', 'updatedAt', 'cpqStatus', 'activatedAt', 'currencyCode'] as const\n const sortFieldParam = url.searchParams.get('sortField') ?? ''\n const sortField = (ALLOWED_SORT_FIELDS as readonly string[]).includes(sortFieldParam)\n ? (sortFieldParam as (typeof ALLOWED_SORT_FIELDS)[number])\n : undefined\n const sortDir = url.searchParams.get('sortDir') === 'asc' ? 'asc' : 'desc'\n\n const filters = {\n customerId: url.searchParams.get('customerId') ?? undefined,\n sourceQuoteId: url.searchParams.get('sourceQuoteId') ?? undefined,\n salesOrderId: url.searchParams.get('salesOrderId') ?? undefined,\n cpqStatus: url.searchParams.get('cpqStatus') ?? undefined,\n currencyCode: url.searchParams.get('currencyCode') ?? undefined,\n search: url.searchParams.get('search') ?? undefined,\n sortField,\n sortDir,\n page: url.searchParams.has('page') ? Number(url.searchParams.get('page')) : undefined,\n pageSize: url.searchParams.has('pageSize') ? Number(url.searchParams.get('pageSize')) : undefined,\n } as const\n\n const service = ctx.container.resolve('cpqOrderService') as DefaultCpqOrderService\n const result = await service.listOrders(filters, scope)\n return NextResponse.json(result)\n } catch (err) {\n return handleError(err, 'cpq/orders.GET')\n }\n}\n\nfunction handleError(err: unknown, context: string) {\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: 'Validation error', details: err.issues }, { status: 400 })\n }\n if (isOrderError(err)) {\n return NextResponse.json({ error: err.message }, { status: err.status })\n }\n const errObj = err as Record<string, unknown>\n if (typeof errObj?.status === 'number' && typeof errObj?.error === 'string') {\n return NextResponse.json({ error: errObj.error }, { status: errObj.status as number })\n }\n console.error(`[${context}]`, err)\n return NextResponse.json({ error: 'Internal error' }, { status: 500 })\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,6 +1,6 @@
1
1
  import { NextResponse } from "next/server";
2
2
  import { resolveCpqRouteContext } from "../../../context.js";
3
- import { QuotingError } from "../../../../services/cpqQuotingService.js";
3
+ import { isQuotingError } from "../../../../services/cpqQuotingService.js";
4
4
  import { resolveQuotingService } from "../../../resolveQuotingService.js";
5
5
  const metadata = {
6
6
  POST: { requireAuth: true, requireFeatures: ["cpq.quotes.manage"] }
@@ -15,7 +15,7 @@ async function POST(req, { params }) {
15
15
  const result = await service.cloneQuote(quoteId, scope);
16
16
  return NextResponse.json(result, { status: 201 });
17
17
  } catch (err) {
18
- if (err instanceof QuotingError) {
18
+ if (isQuotingError(err)) {
19
19
  return NextResponse.json({ error: err.message }, { status: err.status });
20
20
  }
21
21
  console.error("[cpq/quotes/[quoteId]/clone.POST]", err);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../src/modules/cpq/api/quotes/%5BquoteId%5D/clone/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { resolveCpqRouteContext } from '../../../context'\nimport { QuotingError } from '../../../../services/cpqQuotingService'\nimport { resolveQuotingService } from '../../../resolveQuotingService'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['cpq.quotes.manage'] },\n}\n\nexport async function POST(req: Request, { params }: { params: Promise<{ quoteId: string }> }) {\n try {\n const ctx = await resolveCpqRouteContext(req)\n if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const { quoteId } = await params\n const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }\n\n const service = resolveQuotingService(ctx)\n const result = await service.cloneQuote(quoteId, scope)\n return NextResponse.json(result, { status: 201 })\n } catch (err) {\n if (err instanceof QuotingError) {\n return NextResponse.json({ error: err.message }, { status: err.status })\n }\n console.error('[cpq/quotes/[quoteId]/clone.POST]', err)\n return NextResponse.json({ error: 'Internal error' }, { status: 500 })\n }\n}\n"],
5
- "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,8BAA8B;AACvC,SAAS,oBAAoB;AAC7B,SAAS,6BAA6B;AAE/B,MAAM,WAAW;AAAA,EACtB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,mBAAmB,EAAE;AACpE;AAEA,eAAsB,KAAK,KAAc,EAAE,OAAO,GAA6C;AAC7F,MAAI;AACF,UAAM,MAAM,MAAM,uBAAuB,GAAG;AAC5C,QAAI,CAAC,IAAI,KAAM,QAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAElF,UAAM,EAAE,QAAQ,IAAI,MAAM;AAC1B,UAAM,QAAQ,EAAE,gBAAgB,IAAI,gBAAgB,UAAU,IAAI,SAAS;AAE3E,UAAM,UAAU,sBAAsB,GAAG;AACzC,UAAM,SAAS,MAAM,QAAQ,WAAW,SAAS,KAAK;AACtD,WAAO,aAAa,KAAK,QAAQ,EAAE,QAAQ,IAAI,CAAC;AAAA,EAClD,SAAS,KAAK;AACZ,QAAI,eAAe,cAAc;AAC/B,aAAO,aAAa,KAAK,EAAE,OAAO,IAAI,QAAQ,GAAG,EAAE,QAAQ,IAAI,OAAO,CAAC;AAAA,IACzE;AACA,YAAQ,MAAM,qCAAqC,GAAG;AACtD,WAAO,aAAa,KAAK,EAAE,OAAO,iBAAiB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACvE;AACF;",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { resolveCpqRouteContext } from '../../../context'\nimport { isQuotingError } from '../../../../services/cpqQuotingService'\nimport { resolveQuotingService } from '../../../resolveQuotingService'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['cpq.quotes.manage'] },\n}\n\nexport async function POST(req: Request, { params }: { params: Promise<{ quoteId: string }> }) {\n try {\n const ctx = await resolveCpqRouteContext(req)\n if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const { quoteId } = await params\n const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }\n\n const service = resolveQuotingService(ctx)\n const result = await service.cloneQuote(quoteId, scope)\n return NextResponse.json(result, { status: 201 })\n } catch (err) {\n if (isQuotingError(err)) {\n return NextResponse.json({ error: err.message }, { status: err.status })\n }\n console.error('[cpq/quotes/[quoteId]/clone.POST]', err)\n return NextResponse.json({ error: 'Internal error' }, { status: 500 })\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,8BAA8B;AACvC,SAAS,sBAAsB;AAC/B,SAAS,6BAA6B;AAE/B,MAAM,WAAW;AAAA,EACtB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,mBAAmB,EAAE;AACpE;AAEA,eAAsB,KAAK,KAAc,EAAE,OAAO,GAA6C;AAC7F,MAAI;AACF,UAAM,MAAM,MAAM,uBAAuB,GAAG;AAC5C,QAAI,CAAC,IAAI,KAAM,QAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAElF,UAAM,EAAE,QAAQ,IAAI,MAAM;AAC1B,UAAM,QAAQ,EAAE,gBAAgB,IAAI,gBAAgB,UAAU,IAAI,SAAS;AAE3E,UAAM,UAAU,sBAAsB,GAAG;AACzC,UAAM,SAAS,MAAM,QAAQ,WAAW,SAAS,KAAK;AACtD,WAAO,aAAa,KAAK,QAAQ,EAAE,QAAQ,IAAI,CAAC;AAAA,EAClD,SAAS,KAAK;AACZ,QAAI,eAAe,GAAG,GAAG;AACvB,aAAO,aAAa,KAAK,EAAE,OAAO,IAAI,QAAQ,GAAG,EAAE,QAAQ,IAAI,OAAO,CAAC;AAAA,IACzE;AACA,YAAQ,MAAM,qCAAqC,GAAG;AACtD,WAAO,aAAa,KAAK,EAAE,OAAO,iBAAiB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACvE;AACF;",
6
6
  "names": []
7
7
  }
@@ -2,7 +2,7 @@ import { NextResponse } from "next/server";
2
2
  import { z } from "zod";
3
3
  import { resolveCpqRouteContext } from "../../../../context.js";
4
4
  import { cpqUpdateQuoteItemSchema } from "../../../../../data/validators.js";
5
- import { QuotingError } from "../../../../../services/cpqQuotingService.js";
5
+ import { isQuotingError } from "../../../../../services/cpqQuotingService.js";
6
6
  import { resolveQuotingService } from "../../../../resolveQuotingService.js";
7
7
  const metadata = {
8
8
  PUT: { requireAuth: true, requireFeatures: ["cpq.quotes.manage"] },
@@ -22,7 +22,7 @@ async function PUT(req, { params }) {
22
22
  if (err instanceof z.ZodError) {
23
23
  return NextResponse.json({ error: "Validation error", details: err.issues }, { status: 400 });
24
24
  }
25
- if (err instanceof QuotingError) {
25
+ if (isQuotingError(err)) {
26
26
  return NextResponse.json({ error: err.message }, { status: err.status });
27
27
  }
28
28
  console.error("[cpq/quotes/[quoteId]/items/[lineId].PUT]", err);
@@ -39,7 +39,7 @@ async function DELETE(req, { params }) {
39
39
  const result = await service.removeQuoteItem(quoteId, lineId, scope);
40
40
  return NextResponse.json(result);
41
41
  } catch (err) {
42
- if (err instanceof QuotingError) {
42
+ if (isQuotingError(err)) {
43
43
  return NextResponse.json({ error: err.message }, { status: err.status });
44
44
  }
45
45
  console.error("[cpq/quotes/[quoteId]/items/[lineId].DELETE]", err);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../../src/modules/cpq/api/quotes/%5BquoteId%5D/items/%5BlineId%5D/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { resolveCpqRouteContext } from '../../../../context'\nimport { cpqUpdateQuoteItemSchema } from '../../../../../data/validators'\nimport { QuotingError } from '../../../../../services/cpqQuotingService'\nimport { resolveQuotingService } from '../../../../resolveQuotingService'\n\nexport const metadata = {\n PUT: { requireAuth: true, requireFeatures: ['cpq.quotes.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['cpq.quotes.manage'] },\n}\n\nexport async function PUT(req: Request, { params }: { params: Promise<{ quoteId: string; lineId: string }> }) {\n try {\n const ctx = await resolveCpqRouteContext(req)\n if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const { quoteId, lineId } = await params\n const body = cpqUpdateQuoteItemSchema.parse(await req.json())\n const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }\n\n const service = resolveQuotingService(ctx)\n const result = await service.updateQuoteItem(quoteId, lineId, body, scope)\n return NextResponse.json(result)\n } catch (err) {\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: 'Validation error', details: err.issues }, { status: 400 })\n }\n if (err instanceof QuotingError) {\n return NextResponse.json({ error: err.message }, { status: err.status })\n }\n console.error('[cpq/quotes/[quoteId]/items/[lineId].PUT]', err)\n return NextResponse.json({ error: 'Internal error' }, { status: 500 })\n }\n}\n\nexport async function DELETE(req: Request, { params }: { params: Promise<{ quoteId: string; lineId: string }> }) {\n try {\n const ctx = await resolveCpqRouteContext(req)\n if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const { quoteId, lineId } = await params\n const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }\n\n const service = resolveQuotingService(ctx)\n const result = await service.removeQuoteItem(quoteId, lineId, scope)\n return NextResponse.json(result)\n } catch (err) {\n if (err instanceof QuotingError) {\n return NextResponse.json({ error: err.message }, { status: err.status })\n }\n console.error('[cpq/quotes/[quoteId]/items/[lineId].DELETE]', err)\n return NextResponse.json({ error: 'Internal error' }, { status: 500 })\n }\n}\n"],
5
- "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,8BAA8B;AACvC,SAAS,gCAAgC;AACzC,SAAS,oBAAoB;AAC7B,SAAS,6BAA6B;AAE/B,MAAM,WAAW;AAAA,EACtB,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,mBAAmB,EAAE;AAAA,EACjE,QAAQ,EAAE,aAAa,MAAM,iBAAiB,CAAC,mBAAmB,EAAE;AACtE;AAEA,eAAsB,IAAI,KAAc,EAAE,OAAO,GAA6D;AAC5G,MAAI;AACF,UAAM,MAAM,MAAM,uBAAuB,GAAG;AAC5C,QAAI,CAAC,IAAI,KAAM,QAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAElF,UAAM,EAAE,SAAS,OAAO,IAAI,MAAM;AAClC,UAAM,OAAO,yBAAyB,MAAM,MAAM,IAAI,KAAK,CAAC;AAC5D,UAAM,QAAQ,EAAE,gBAAgB,IAAI,gBAAgB,UAAU,IAAI,SAAS;AAE3E,UAAM,UAAU,sBAAsB,GAAG;AACzC,UAAM,SAAS,MAAM,QAAQ,gBAAgB,SAAS,QAAQ,MAAM,KAAK;AACzE,WAAO,aAAa,KAAK,MAAM;AAAA,EACjC,SAAS,KAAK;AACZ,QAAI,eAAe,EAAE,UAAU;AAC7B,aAAO,aAAa,KAAK,EAAE,OAAO,oBAAoB,SAAS,IAAI,OAAO,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC9F;AACA,QAAI,eAAe,cAAc;AAC/B,aAAO,aAAa,KAAK,EAAE,OAAO,IAAI,QAAQ,GAAG,EAAE,QAAQ,IAAI,OAAO,CAAC;AAAA,IACzE;AACA,YAAQ,MAAM,6CAA6C,GAAG;AAC9D,WAAO,aAAa,KAAK,EAAE,OAAO,iBAAiB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACvE;AACF;AAEA,eAAsB,OAAO,KAAc,EAAE,OAAO,GAA6D;AAC/G,MAAI;AACF,UAAM,MAAM,MAAM,uBAAuB,GAAG;AAC5C,QAAI,CAAC,IAAI,KAAM,QAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAElF,UAAM,EAAE,SAAS,OAAO,IAAI,MAAM;AAClC,UAAM,QAAQ,EAAE,gBAAgB,IAAI,gBAAgB,UAAU,IAAI,SAAS;AAE3E,UAAM,UAAU,sBAAsB,GAAG;AACzC,UAAM,SAAS,MAAM,QAAQ,gBAAgB,SAAS,QAAQ,KAAK;AACnE,WAAO,aAAa,KAAK,MAAM;AAAA,EACjC,SAAS,KAAK;AACZ,QAAI,eAAe,cAAc;AAC/B,aAAO,aAAa,KAAK,EAAE,OAAO,IAAI,QAAQ,GAAG,EAAE,QAAQ,IAAI,OAAO,CAAC;AAAA,IACzE;AACA,YAAQ,MAAM,gDAAgD,GAAG;AACjE,WAAO,aAAa,KAAK,EAAE,OAAO,iBAAiB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACvE;AACF;",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { resolveCpqRouteContext } from '../../../../context'\nimport { cpqUpdateQuoteItemSchema } from '../../../../../data/validators'\nimport { isQuotingError } from '../../../../../services/cpqQuotingService'\nimport { resolveQuotingService } from '../../../../resolveQuotingService'\n\nexport const metadata = {\n PUT: { requireAuth: true, requireFeatures: ['cpq.quotes.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['cpq.quotes.manage'] },\n}\n\nexport async function PUT(req: Request, { params }: { params: Promise<{ quoteId: string; lineId: string }> }) {\n try {\n const ctx = await resolveCpqRouteContext(req)\n if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const { quoteId, lineId } = await params\n const body = cpqUpdateQuoteItemSchema.parse(await req.json())\n const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }\n\n const service = resolveQuotingService(ctx)\n const result = await service.updateQuoteItem(quoteId, lineId, body, scope)\n return NextResponse.json(result)\n } catch (err) {\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: 'Validation error', details: err.issues }, { status: 400 })\n }\n if (isQuotingError(err)) {\n return NextResponse.json({ error: err.message }, { status: err.status })\n }\n console.error('[cpq/quotes/[quoteId]/items/[lineId].PUT]', err)\n return NextResponse.json({ error: 'Internal error' }, { status: 500 })\n }\n}\n\nexport async function DELETE(req: Request, { params }: { params: Promise<{ quoteId: string; lineId: string }> }) {\n try {\n const ctx = await resolveCpqRouteContext(req)\n if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const { quoteId, lineId } = await params\n const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }\n\n const service = resolveQuotingService(ctx)\n const result = await service.removeQuoteItem(quoteId, lineId, scope)\n return NextResponse.json(result)\n } catch (err) {\n if (isQuotingError(err)) {\n return NextResponse.json({ error: err.message }, { status: err.status })\n }\n console.error('[cpq/quotes/[quoteId]/items/[lineId].DELETE]', err)\n return NextResponse.json({ error: 'Internal error' }, { status: 500 })\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,8BAA8B;AACvC,SAAS,gCAAgC;AACzC,SAAS,sBAAsB;AAC/B,SAAS,6BAA6B;AAE/B,MAAM,WAAW;AAAA,EACtB,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,mBAAmB,EAAE;AAAA,EACjE,QAAQ,EAAE,aAAa,MAAM,iBAAiB,CAAC,mBAAmB,EAAE;AACtE;AAEA,eAAsB,IAAI,KAAc,EAAE,OAAO,GAA6D;AAC5G,MAAI;AACF,UAAM,MAAM,MAAM,uBAAuB,GAAG;AAC5C,QAAI,CAAC,IAAI,KAAM,QAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAElF,UAAM,EAAE,SAAS,OAAO,IAAI,MAAM;AAClC,UAAM,OAAO,yBAAyB,MAAM,MAAM,IAAI,KAAK,CAAC;AAC5D,UAAM,QAAQ,EAAE,gBAAgB,IAAI,gBAAgB,UAAU,IAAI,SAAS;AAE3E,UAAM,UAAU,sBAAsB,GAAG;AACzC,UAAM,SAAS,MAAM,QAAQ,gBAAgB,SAAS,QAAQ,MAAM,KAAK;AACzE,WAAO,aAAa,KAAK,MAAM;AAAA,EACjC,SAAS,KAAK;AACZ,QAAI,eAAe,EAAE,UAAU;AAC7B,aAAO,aAAa,KAAK,EAAE,OAAO,oBAAoB,SAAS,IAAI,OAAO,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC9F;AACA,QAAI,eAAe,GAAG,GAAG;AACvB,aAAO,aAAa,KAAK,EAAE,OAAO,IAAI,QAAQ,GAAG,EAAE,QAAQ,IAAI,OAAO,CAAC;AAAA,IACzE;AACA,YAAQ,MAAM,6CAA6C,GAAG;AAC9D,WAAO,aAAa,KAAK,EAAE,OAAO,iBAAiB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACvE;AACF;AAEA,eAAsB,OAAO,KAAc,EAAE,OAAO,GAA6D;AAC/G,MAAI;AACF,UAAM,MAAM,MAAM,uBAAuB,GAAG;AAC5C,QAAI,CAAC,IAAI,KAAM,QAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAElF,UAAM,EAAE,SAAS,OAAO,IAAI,MAAM;AAClC,UAAM,QAAQ,EAAE,gBAAgB,IAAI,gBAAgB,UAAU,IAAI,SAAS;AAE3E,UAAM,UAAU,sBAAsB,GAAG;AACzC,UAAM,SAAS,MAAM,QAAQ,gBAAgB,SAAS,QAAQ,KAAK;AACnE,WAAO,aAAa,KAAK,MAAM;AAAA,EACjC,SAAS,KAAK;AACZ,QAAI,eAAe,GAAG,GAAG;AACvB,aAAO,aAAa,KAAK,EAAE,OAAO,IAAI,QAAQ,GAAG,EAAE,QAAQ,IAAI,OAAO,CAAC;AAAA,IACzE;AACA,YAAQ,MAAM,gDAAgD,GAAG;AACjE,WAAO,aAAa,KAAK,EAAE,OAAO,iBAAiB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACvE;AACF;",
6
6
  "names": []
7
7
  }
@@ -2,7 +2,7 @@ import { NextResponse } from "next/server";
2
2
  import { z } from "zod";
3
3
  import { resolveCpqRouteContext } from "../../../context.js";
4
4
  import { cpqRecalculateSchema } from "../../../../data/validators.js";
5
- import { QuotingError } from "../../../../services/cpqQuotingService.js";
5
+ import { isQuotingError } from "../../../../services/cpqQuotingService.js";
6
6
  import { resolveQuotingService } from "../../../resolveQuotingService.js";
7
7
  const metadata = {
8
8
  POST: { requireAuth: true, requireFeatures: ["cpq.quotes.view"] }
@@ -21,7 +21,7 @@ async function POST(req, { params }) {
21
21
  if (err instanceof z.ZodError) {
22
22
  return NextResponse.json({ error: "Validation error", details: err.issues }, { status: 400 });
23
23
  }
24
- if (err instanceof QuotingError) {
24
+ if (isQuotingError(err)) {
25
25
  return NextResponse.json({ error: err.message }, { status: err.status });
26
26
  }
27
27
  console.error("[cpq/quotes/[quoteId]/recalculate.POST]", err);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../src/modules/cpq/api/quotes/%5BquoteId%5D/recalculate/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { resolveCpqRouteContext } from '../../../context'\nimport { cpqRecalculateSchema } from '../../../../data/validators'\nimport { QuotingError } from '../../../../services/cpqQuotingService'\nimport { resolveQuotingService } from '../../../resolveQuotingService'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['cpq.quotes.view'] },\n}\n\nexport async function POST(req: Request, { params }: { params: Promise<{ quoteId: string }> }) {\n try {\n const ctx = await resolveCpqRouteContext(req)\n if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const { quoteId } = await params\n const body = cpqRecalculateSchema.parse(await req.json())\n const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }\n\n const service = resolveQuotingService(ctx)\n const result = await service.recalculate(quoteId, body, scope)\n return NextResponse.json(result)\n } catch (err) {\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: 'Validation error', details: err.issues }, { status: 400 })\n }\n if (err instanceof QuotingError) {\n return NextResponse.json({ error: err.message }, { status: err.status })\n }\n console.error('[cpq/quotes/[quoteId]/recalculate.POST]', err)\n return NextResponse.json({ error: 'Internal error' }, { status: 500 })\n }\n}\n"],
5
- "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,8BAA8B;AACvC,SAAS,4BAA4B;AACrC,SAAS,oBAAoB;AAC7B,SAAS,6BAA6B;AAE/B,MAAM,WAAW;AAAA,EACtB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,iBAAiB,EAAE;AAClE;AAEA,eAAsB,KAAK,KAAc,EAAE,OAAO,GAA6C;AAC7F,MAAI;AACF,UAAM,MAAM,MAAM,uBAAuB,GAAG;AAC5C,QAAI,CAAC,IAAI,KAAM,QAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAElF,UAAM,EAAE,QAAQ,IAAI,MAAM;AAC1B,UAAM,OAAO,qBAAqB,MAAM,MAAM,IAAI,KAAK,CAAC;AACxD,UAAM,QAAQ,EAAE,gBAAgB,IAAI,gBAAgB,UAAU,IAAI,SAAS;AAE3E,UAAM,UAAU,sBAAsB,GAAG;AACzC,UAAM,SAAS,MAAM,QAAQ,YAAY,SAAS,MAAM,KAAK;AAC7D,WAAO,aAAa,KAAK,MAAM;AAAA,EACjC,SAAS,KAAK;AACZ,QAAI,eAAe,EAAE,UAAU;AAC7B,aAAO,aAAa,KAAK,EAAE,OAAO,oBAAoB,SAAS,IAAI,OAAO,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC9F;AACA,QAAI,eAAe,cAAc;AAC/B,aAAO,aAAa,KAAK,EAAE,OAAO,IAAI,QAAQ,GAAG,EAAE,QAAQ,IAAI,OAAO,CAAC;AAAA,IACzE;AACA,YAAQ,MAAM,2CAA2C,GAAG;AAC5D,WAAO,aAAa,KAAK,EAAE,OAAO,iBAAiB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACvE;AACF;",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { resolveCpqRouteContext } from '../../../context'\nimport { cpqRecalculateSchema } from '../../../../data/validators'\nimport { isQuotingError } from '../../../../services/cpqQuotingService'\nimport { resolveQuotingService } from '../../../resolveQuotingService'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['cpq.quotes.view'] },\n}\n\nexport async function POST(req: Request, { params }: { params: Promise<{ quoteId: string }> }) {\n try {\n const ctx = await resolveCpqRouteContext(req)\n if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const { quoteId } = await params\n const body = cpqRecalculateSchema.parse(await req.json())\n const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }\n\n const service = resolveQuotingService(ctx)\n const result = await service.recalculate(quoteId, body, scope)\n return NextResponse.json(result)\n } catch (err) {\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: 'Validation error', details: err.issues }, { status: 400 })\n }\n if (isQuotingError(err)) {\n return NextResponse.json({ error: err.message }, { status: err.status })\n }\n console.error('[cpq/quotes/[quoteId]/recalculate.POST]', err)\n return NextResponse.json({ error: 'Internal error' }, { status: 500 })\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,8BAA8B;AACvC,SAAS,4BAA4B;AACrC,SAAS,sBAAsB;AAC/B,SAAS,6BAA6B;AAE/B,MAAM,WAAW;AAAA,EACtB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,iBAAiB,EAAE;AAClE;AAEA,eAAsB,KAAK,KAAc,EAAE,OAAO,GAA6C;AAC7F,MAAI;AACF,UAAM,MAAM,MAAM,uBAAuB,GAAG;AAC5C,QAAI,CAAC,IAAI,KAAM,QAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAElF,UAAM,EAAE,QAAQ,IAAI,MAAM;AAC1B,UAAM,OAAO,qBAAqB,MAAM,MAAM,IAAI,KAAK,CAAC;AACxD,UAAM,QAAQ,EAAE,gBAAgB,IAAI,gBAAgB,UAAU,IAAI,SAAS;AAE3E,UAAM,UAAU,sBAAsB,GAAG;AACzC,UAAM,SAAS,MAAM,QAAQ,YAAY,SAAS,MAAM,KAAK;AAC7D,WAAO,aAAa,KAAK,MAAM;AAAA,EACjC,SAAS,KAAK;AACZ,QAAI,eAAe,EAAE,UAAU;AAC7B,aAAO,aAAa,KAAK,EAAE,OAAO,oBAAoB,SAAS,IAAI,OAAO,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC9F;AACA,QAAI,eAAe,GAAG,GAAG;AACvB,aAAO,aAAa,KAAK,EAAE,OAAO,IAAI,QAAQ,GAAG,EAAE,QAAQ,IAAI,OAAO,CAAC;AAAA,IACzE;AACA,YAAQ,MAAM,2CAA2C,GAAG;AAC5D,WAAO,aAAa,KAAK,EAAE,OAAO,iBAAiB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACvE;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  import { NextResponse } from "next/server";
2
2
  import { resolveCpqRouteContext } from "../../context.js";
3
3
  import { cpqUpdateQuoteSchema } from "../../../data/validators.js";
4
- import { QuotingError } from "../../../services/cpqQuotingService.js";
4
+ import { isQuotingError } from "../../../services/cpqQuotingService.js";
5
5
  import { resolveQuotingService } from "../../resolveQuotingService.js";
6
6
  import { CpqQuoteConfiguration, CpqQuoteLineConfiguration } from "../../../data/entities.js";
7
7
  import { SalesQuote, SalesQuoteLine } from "@open-mercato/core/modules/sales/data/entities";
@@ -21,7 +21,7 @@ async function GET(req, { params }) {
21
21
  const result = await service.getQuote(quoteId, scope);
22
22
  return NextResponse.json(result);
23
23
  } catch (err) {
24
- if (err instanceof QuotingError) {
24
+ if (isQuotingError(err)) {
25
25
  return NextResponse.json({ error: err.message }, { status: err.status });
26
26
  }
27
27
  console.error("[cpq/quotes/[quoteId].GET]", err);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/cpq/api/quotes/%5BquoteId%5D/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { resolveCpqRouteContext } from '../../context'\nimport { cpqUpdateQuoteSchema } from '../../../data/validators'\nimport { QuotingError } from '../../../services/cpqQuotingService'\nimport { resolveQuotingService } from '../../resolveQuotingService'\nimport { CpqQuoteConfiguration, CpqQuoteLineConfiguration } from '../../../data/entities'\nimport { SalesQuote, SalesQuoteLine } from '@open-mercato/core/modules/sales/data/entities'\nimport { handleArcError } from '../../_helpers/handleArcError'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['cpq.quotes.view'] },\n PATCH: { requireAuth: true, requireFeatures: ['cpq.quotes.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['cpq.quotes.manage'] },\n}\n\nexport async function GET(req: Request, { params }: { params: Promise<{ quoteId: string }> }) {\n try {\n const ctx = await resolveCpqRouteContext(req)\n if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const { quoteId } = await params\n const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }\n\n const service = resolveQuotingService(ctx)\n const result = await service.getQuote(quoteId, scope)\n return NextResponse.json(result)\n } catch (err) {\n if (err instanceof QuotingError) {\n return NextResponse.json({ error: err.message }, { status: err.status })\n }\n console.error('[cpq/quotes/[quoteId].GET]', err)\n return NextResponse.json({ error: 'Internal error' }, { status: 500 })\n }\n}\n\nexport async function PATCH(\n req: Request,\n { params }: { params: Promise<{ quoteId: string }> },\n) {\n try {\n const ctx = await resolveCpqRouteContext(req)\n if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const { quoteId } = await params\n const body = cpqUpdateQuoteSchema.parse(await req.json())\n const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }\n\n if (body.quoteType !== undefined) {\n // ARC quote-type one-way transition (XD-250 DD-ARC-9). The service\n // enforces 'new' \u2192 'amend'/'renew'/'cancel' only; subsequent changes\n // return 409.\n const service = resolveQuotingService(ctx)\n const result = await service.setQuoteType(quoteId, body.quoteType, scope)\n return NextResponse.json(result)\n }\n\n return NextResponse.json({ error: 'Nothing to update' }, { status: 400 })\n } catch (err) {\n return handleArcError(err, 'cpq/quotes/[quoteId].PATCH')\n }\n}\n\nexport async function DELETE(req: Request, { params }: { params: Promise<{ quoteId: string }> }) {\n try {\n const ctx = await resolveCpqRouteContext(req)\n if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const { quoteId } = await params\n const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }\n const em = ctx.em\n const now = new Date()\n\n let cpqConfig = await em.findOne(CpqQuoteConfiguration, {\n id: quoteId, organizationId: scope.organizationId, tenantId: scope.tenantId, deletedAt: null,\n })\n if (!cpqConfig) {\n cpqConfig = await em.findOne(CpqQuoteConfiguration, {\n quoteId, organizationId: scope.organizationId, tenantId: scope.tenantId, deletedAt: null,\n })\n }\n if (!cpqConfig) return NextResponse.json({ error: 'Quote not found' }, { status: 404 })\n\n const terminalStatuses = ['accepted', 'rejected', 'cancelled']\n if (terminalStatuses.includes(cpqConfig.cpqStatus)) {\n return NextResponse.json({ error: `Cannot delete a quote in '${cpqConfig.cpqStatus}' status` }, { status: 409 })\n }\n\n const lineConfigs = await em.find(CpqQuoteLineConfiguration, {\n quoteConfigurationId: cpqConfig.id, organizationId: scope.organizationId, tenantId: scope.tenantId, deletedAt: null,\n })\n for (const lc of lineConfigs) {\n lc.deletedAt = now\n const salesLine = await em.findOne(SalesQuoteLine, { id: lc.quoteLineId })\n if (salesLine) salesLine.deletedAt = now\n }\n\n cpqConfig.deletedAt = now\n\n const salesQuote = await em.findOne(SalesQuote, { id: cpqConfig.quoteId })\n if (salesQuote) salesQuote.deletedAt = now\n\n await em.flush()\n\n return NextResponse.json({ ok: true })\n } catch (err) {\n console.error('[cpq/quotes/[quoteId].DELETE]', err)\n return NextResponse.json({ error: 'Internal error' }, { status: 500 })\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { resolveCpqRouteContext } from '../../context'\nimport { cpqUpdateQuoteSchema } from '../../../data/validators'\nimport { isQuotingError } from '../../../services/cpqQuotingService'\nimport { resolveQuotingService } from '../../resolveQuotingService'\nimport { CpqQuoteConfiguration, CpqQuoteLineConfiguration } from '../../../data/entities'\nimport { SalesQuote, SalesQuoteLine } from '@open-mercato/core/modules/sales/data/entities'\nimport { handleArcError } from '../../_helpers/handleArcError'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['cpq.quotes.view'] },\n PATCH: { requireAuth: true, requireFeatures: ['cpq.quotes.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['cpq.quotes.manage'] },\n}\n\nexport async function GET(req: Request, { params }: { params: Promise<{ quoteId: string }> }) {\n try {\n const ctx = await resolveCpqRouteContext(req)\n if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const { quoteId } = await params\n const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }\n\n const service = resolveQuotingService(ctx)\n const result = await service.getQuote(quoteId, scope)\n return NextResponse.json(result)\n } catch (err) {\n if (isQuotingError(err)) {\n return NextResponse.json({ error: err.message }, { status: err.status })\n }\n console.error('[cpq/quotes/[quoteId].GET]', err)\n return NextResponse.json({ error: 'Internal error' }, { status: 500 })\n }\n}\n\nexport async function PATCH(\n req: Request,\n { params }: { params: Promise<{ quoteId: string }> },\n) {\n try {\n const ctx = await resolveCpqRouteContext(req)\n if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const { quoteId } = await params\n const body = cpqUpdateQuoteSchema.parse(await req.json())\n const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }\n\n if (body.quoteType !== undefined) {\n // ARC quote-type one-way transition (XD-250 DD-ARC-9). The service\n // enforces 'new' \u2192 'amend'/'renew'/'cancel' only; subsequent changes\n // return 409.\n const service = resolveQuotingService(ctx)\n const result = await service.setQuoteType(quoteId, body.quoteType, scope)\n return NextResponse.json(result)\n }\n\n return NextResponse.json({ error: 'Nothing to update' }, { status: 400 })\n } catch (err) {\n return handleArcError(err, 'cpq/quotes/[quoteId].PATCH')\n }\n}\n\nexport async function DELETE(req: Request, { params }: { params: Promise<{ quoteId: string }> }) {\n try {\n const ctx = await resolveCpqRouteContext(req)\n if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const { quoteId } = await params\n const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }\n const em = ctx.em\n const now = new Date()\n\n let cpqConfig = await em.findOne(CpqQuoteConfiguration, {\n id: quoteId, organizationId: scope.organizationId, tenantId: scope.tenantId, deletedAt: null,\n })\n if (!cpqConfig) {\n cpqConfig = await em.findOne(CpqQuoteConfiguration, {\n quoteId, organizationId: scope.organizationId, tenantId: scope.tenantId, deletedAt: null,\n })\n }\n if (!cpqConfig) return NextResponse.json({ error: 'Quote not found' }, { status: 404 })\n\n const terminalStatuses = ['accepted', 'rejected', 'cancelled']\n if (terminalStatuses.includes(cpqConfig.cpqStatus)) {\n return NextResponse.json({ error: `Cannot delete a quote in '${cpqConfig.cpqStatus}' status` }, { status: 409 })\n }\n\n const lineConfigs = await em.find(CpqQuoteLineConfiguration, {\n quoteConfigurationId: cpqConfig.id, organizationId: scope.organizationId, tenantId: scope.tenantId, deletedAt: null,\n })\n for (const lc of lineConfigs) {\n lc.deletedAt = now\n const salesLine = await em.findOne(SalesQuoteLine, { id: lc.quoteLineId })\n if (salesLine) salesLine.deletedAt = now\n }\n\n cpqConfig.deletedAt = now\n\n const salesQuote = await em.findOne(SalesQuote, { id: cpqConfig.quoteId })\n if (salesQuote) salesQuote.deletedAt = now\n\n await em.flush()\n\n return NextResponse.json({ ok: true })\n } catch (err) {\n console.error('[cpq/quotes/[quoteId].DELETE]', err)\n return NextResponse.json({ error: 'Internal error' }, { status: 500 })\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -2,7 +2,7 @@ import { NextResponse } from "next/server";
2
2
  import { z } from "zod";
3
3
  import { resolveCpqRouteContext } from "../context.js";
4
4
  import { cpqCreateQuoteSchema } from "../../data/validators.js";
5
- import { QuotingError } from "../../services/cpqQuotingService.js";
5
+ import { isQuotingError } from "../../services/cpqQuotingService.js";
6
6
  import { resolveQuotingService } from "../resolveQuotingService.js";
7
7
  import { CpqQuoteConfiguration } from "../../data/entities.js";
8
8
  import { SalesQuote } from "@open-mercato/core/modules/sales/data/entities";
@@ -81,7 +81,7 @@ function handleError(err, context) {
81
81
  if (err instanceof z.ZodError) {
82
82
  return NextResponse.json({ error: "Validation error", details: err.issues }, { status: 400 });
83
83
  }
84
- if (err instanceof QuotingError) {
84
+ if (isQuotingError(err)) {
85
85
  const body = { error: err.message };
86
86
  if (err.cpqStatus) body.cpqStatus = err.cpqStatus;
87
87
  return NextResponse.json(body, { status: err.status });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/cpq/api/quotes/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { resolveCpqRouteContext } from '../context'\nimport { cpqCreateQuoteSchema } from '../../data/validators'\nimport { QuotingError } from '../../services/cpqQuotingService'\nimport { resolveQuotingService } from '../resolveQuotingService'\nimport { CpqQuoteConfiguration } from '../../data/entities'\nimport { SalesQuote } from '@open-mercato/core/modules/sales/data/entities'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['cpq.quotes.manage'] },\n GET: { requireAuth: true, requireFeatures: ['cpq.quotes.view'] },\n}\n\nexport async function POST(req: Request) {\n try {\n const ctx = await resolveCpqRouteContext(req)\n if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const body = cpqCreateQuoteSchema.parse(await req.json())\n const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }\n\n const service = resolveQuotingService(ctx)\n const result = await service.createQuote(body, scope)\n return NextResponse.json(result, { status: 201 })\n } catch (err) {\n return handleError(err, 'cpq/quotes.POST')\n }\n}\n\nexport async function GET(req: Request) {\n try {\n const ctx = await resolveCpqRouteContext(req)\n if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const url = new URL(req.url)\n const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId, deletedAt: null }\n\n const page = Math.max(1, Number(url.searchParams.get('page') ?? '1'))\n const pageSize = Math.min(100, Math.max(1, Number(url.searchParams.get('pageSize') ?? '50')))\n\n const filters: Record<string, unknown> = { ...scope }\n\n const cpqStatus = url.searchParams.get('cpqStatus')\n if (cpqStatus) filters.cpqStatus = cpqStatus\n const currencyCode = url.searchParams.get('currencyCode')\n if (currencyCode) filters.currencyCode = currencyCode\n const customerId = url.searchParams.get('customerId')\n if (customerId) filters.customerId = customerId\n\n // Free-text search across quoteId, customerId (case-insensitive)\n const search = url.searchParams.get('search')?.trim()\n if (search) {\n filters.$or = [\n { quoteId: { $ilike: `%${search}%` } },\n { customerId: { $ilike: `%${search}%` } },\n ]\n }\n\n const ALLOWED_SORT_FIELDS = ['createdAt', 'updatedAt', 'cpqStatus', 'version', 'currencyCode'] as const\n const sortFieldParam = url.searchParams.get('sortField') ?? ''\n const sortField = (ALLOWED_SORT_FIELDS as readonly string[]).includes(sortFieldParam)\n ? (sortFieldParam as (typeof ALLOWED_SORT_FIELDS)[number])\n : 'createdAt'\n const sortDir = url.searchParams.get('sortDir') === 'asc' ? 'asc' : 'desc'\n\n const [items, total] = await ctx.em.findAndCount(CpqQuoteConfiguration, filters, {\n limit: pageSize,\n offset: (page - 1) * pageSize,\n orderBy: { [sortField]: sortDir },\n })\n\n // Enrich rows with `quoteNumber` (e.g. \"QUOTE-20260512-00005\") so the\n // list shows human-meaningful identifiers instead of truncated UUIDs.\n // Batched lookup keeps the cost flat regardless of page size.\n const salesQuoteIds = [...new Set(items.map((i) => i.quoteId).filter(Boolean))]\n const salesQuotes = salesQuoteIds.length > 0\n ? await ctx.em.find(SalesQuote, {\n id: { $in: salesQuoteIds },\n organizationId: ctx.organizationId,\n tenantId: ctx.tenantId,\n })\n : []\n const quoteNumberById = new Map<string, string | null>(\n salesQuotes.map((q) => [q.id, q.quoteNumber ?? null]),\n )\n\n return NextResponse.json({\n items: items.map((item) => ({\n ...item,\n quoteNumber: quoteNumberById.get(item.quoteId) ?? null,\n })),\n total,\n page,\n pageSize,\n totalPages: Math.ceil(total / pageSize),\n })\n } catch (err) {\n return handleError(err, 'cpq/quotes.GET')\n }\n}\n\nfunction handleError(err: unknown, context: string) {\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: 'Validation error', details: err.issues }, { status: 400 })\n }\n if (err instanceof QuotingError) {\n const body: Record<string, unknown> = { error: err.message }\n if (err.cpqStatus) body.cpqStatus = err.cpqStatus\n return NextResponse.json(body, { status: err.status })\n }\n console.error(`[${context}]`, err)\n return NextResponse.json({ error: 'Internal error' }, { status: 500 })\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { resolveCpqRouteContext } from '../context'\nimport { cpqCreateQuoteSchema } from '../../data/validators'\nimport { isQuotingError } from '../../services/cpqQuotingService'\nimport { resolveQuotingService } from '../resolveQuotingService'\nimport { CpqQuoteConfiguration } from '../../data/entities'\nimport { SalesQuote } from '@open-mercato/core/modules/sales/data/entities'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['cpq.quotes.manage'] },\n GET: { requireAuth: true, requireFeatures: ['cpq.quotes.view'] },\n}\n\nexport async function POST(req: Request) {\n try {\n const ctx = await resolveCpqRouteContext(req)\n if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const body = cpqCreateQuoteSchema.parse(await req.json())\n const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }\n\n const service = resolveQuotingService(ctx)\n const result = await service.createQuote(body, scope)\n return NextResponse.json(result, { status: 201 })\n } catch (err) {\n return handleError(err, 'cpq/quotes.POST')\n }\n}\n\nexport async function GET(req: Request) {\n try {\n const ctx = await resolveCpqRouteContext(req)\n if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const url = new URL(req.url)\n const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId, deletedAt: null }\n\n const page = Math.max(1, Number(url.searchParams.get('page') ?? '1'))\n const pageSize = Math.min(100, Math.max(1, Number(url.searchParams.get('pageSize') ?? '50')))\n\n const filters: Record<string, unknown> = { ...scope }\n\n const cpqStatus = url.searchParams.get('cpqStatus')\n if (cpqStatus) filters.cpqStatus = cpqStatus\n const currencyCode = url.searchParams.get('currencyCode')\n if (currencyCode) filters.currencyCode = currencyCode\n const customerId = url.searchParams.get('customerId')\n if (customerId) filters.customerId = customerId\n\n // Free-text search across quoteId, customerId (case-insensitive)\n const search = url.searchParams.get('search')?.trim()\n if (search) {\n filters.$or = [\n { quoteId: { $ilike: `%${search}%` } },\n { customerId: { $ilike: `%${search}%` } },\n ]\n }\n\n const ALLOWED_SORT_FIELDS = ['createdAt', 'updatedAt', 'cpqStatus', 'version', 'currencyCode'] as const\n const sortFieldParam = url.searchParams.get('sortField') ?? ''\n const sortField = (ALLOWED_SORT_FIELDS as readonly string[]).includes(sortFieldParam)\n ? (sortFieldParam as (typeof ALLOWED_SORT_FIELDS)[number])\n : 'createdAt'\n const sortDir = url.searchParams.get('sortDir') === 'asc' ? 'asc' : 'desc'\n\n const [items, total] = await ctx.em.findAndCount(CpqQuoteConfiguration, filters, {\n limit: pageSize,\n offset: (page - 1) * pageSize,\n orderBy: { [sortField]: sortDir },\n })\n\n // Enrich rows with `quoteNumber` (e.g. \"QUOTE-20260512-00005\") so the\n // list shows human-meaningful identifiers instead of truncated UUIDs.\n // Batched lookup keeps the cost flat regardless of page size.\n const salesQuoteIds = [...new Set(items.map((i) => i.quoteId).filter(Boolean))]\n const salesQuotes = salesQuoteIds.length > 0\n ? await ctx.em.find(SalesQuote, {\n id: { $in: salesQuoteIds },\n organizationId: ctx.organizationId,\n tenantId: ctx.tenantId,\n })\n : []\n const quoteNumberById = new Map<string, string | null>(\n salesQuotes.map((q) => [q.id, q.quoteNumber ?? null]),\n )\n\n return NextResponse.json({\n items: items.map((item) => ({\n ...item,\n quoteNumber: quoteNumberById.get(item.quoteId) ?? null,\n })),\n total,\n page,\n pageSize,\n totalPages: Math.ceil(total / pageSize),\n })\n } catch (err) {\n return handleError(err, 'cpq/quotes.GET')\n }\n}\n\nfunction handleError(err: unknown, context: string) {\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: 'Validation error', details: err.issues }, { status: 400 })\n }\n if (isQuotingError(err)) {\n const body: Record<string, unknown> = { error: err.message }\n if (err.cpqStatus) body.cpqStatus = err.cpqStatus\n return NextResponse.json(body, { status: err.status })\n }\n console.error(`[${context}]`, err)\n return NextResponse.json({ error: 'Internal error' }, { status: 500 })\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }