@open-mercato/core 0.4.6-develop-9ff1d4a9a2 → 0.4.6-develop-219dae16c5

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 (107) hide show
  1. package/dist/modules/currencies/backend/exchange-rates/[id]/page.js +17 -154
  2. package/dist/modules/currencies/backend/exchange-rates/[id]/page.js.map +3 -3
  3. package/dist/modules/currencies/backend/exchange-rates/create/page.js +14 -152
  4. package/dist/modules/currencies/backend/exchange-rates/create/page.js.map +2 -2
  5. package/dist/modules/currencies/lib/exchangeRateFormConfig.js +167 -0
  6. package/dist/modules/currencies/lib/exchangeRateFormConfig.js.map +7 -0
  7. package/dist/modules/customers/api/dashboard/widgets/utils.js +1 -34
  8. package/dist/modules/customers/api/dashboard/widgets/utils.js.map +2 -2
  9. package/dist/modules/customers/commands/activities.js +3 -8
  10. package/dist/modules/customers/commands/activities.js.map +2 -2
  11. package/dist/modules/customers/commands/comments.js +2 -8
  12. package/dist/modules/customers/commands/comments.js.map +2 -2
  13. package/dist/modules/dashboards/lib/widgetScope.js +38 -0
  14. package/dist/modules/dashboards/lib/widgetScope.js.map +7 -0
  15. package/dist/modules/entities/lib/makeActivityRoute.js +265 -0
  16. package/dist/modules/entities/lib/makeActivityRoute.js.map +7 -0
  17. package/dist/modules/resources/api/activities.js +24 -232
  18. package/dist/modules/resources/api/activities.js.map +2 -2
  19. package/dist/modules/resources/commands/activities.js +3 -8
  20. package/dist/modules/resources/commands/activities.js.map +2 -2
  21. package/dist/modules/resources/commands/comments.js +2 -8
  22. package/dist/modules/resources/commands/comments.js.map +2 -2
  23. package/dist/modules/sales/api/dashboard/widgets/new-orders/route.js +27 -182
  24. package/dist/modules/sales/api/dashboard/widgets/new-orders/route.js.map +2 -2
  25. package/dist/modules/sales/api/dashboard/widgets/new-quotes/route.js +28 -183
  26. package/dist/modules/sales/api/dashboard/widgets/new-quotes/route.js.map +2 -2
  27. package/dist/modules/sales/api/order-line-statuses/route.js +15 -194
  28. package/dist/modules/sales/api/order-line-statuses/route.js.map +2 -2
  29. package/dist/modules/sales/api/order-lines/route.js +15 -281
  30. package/dist/modules/sales/api/order-lines/route.js.map +2 -2
  31. package/dist/modules/sales/api/order-statuses/route.js +15 -194
  32. package/dist/modules/sales/api/order-statuses/route.js.map +2 -2
  33. package/dist/modules/sales/api/payment-statuses/route.js +15 -194
  34. package/dist/modules/sales/api/payment-statuses/route.js.map +2 -2
  35. package/dist/modules/sales/api/quote-lines/route.js +15 -279
  36. package/dist/modules/sales/api/quote-lines/route.js.map +2 -2
  37. package/dist/modules/sales/api/shipment-statuses/route.js +15 -194
  38. package/dist/modules/sales/api/shipment-statuses/route.js.map +2 -2
  39. package/dist/modules/sales/components/PaymentMethodsSettings.js +3 -84
  40. package/dist/modules/sales/components/PaymentMethodsSettings.js.map +2 -2
  41. package/dist/modules/sales/components/ProviderFieldInput.js +86 -0
  42. package/dist/modules/sales/components/ProviderFieldInput.js.map +7 -0
  43. package/dist/modules/sales/components/ShippingMethodsSettings.js +3 -82
  44. package/dist/modules/sales/components/ShippingMethodsSettings.js.map +2 -2
  45. package/dist/modules/sales/lib/makeSalesLineRoute.js +308 -0
  46. package/dist/modules/sales/lib/makeSalesLineRoute.js.map +7 -0
  47. package/dist/modules/sales/lib/makeStatusDictionaryRoute.js +206 -0
  48. package/dist/modules/sales/lib/makeStatusDictionaryRoute.js.map +7 -0
  49. package/dist/modules/sales/widgets/dashboard/makeDashboardWidgetRoute.js +178 -0
  50. package/dist/modules/sales/widgets/dashboard/makeDashboardWidgetRoute.js.map +7 -0
  51. package/dist/modules/sales/widgets/dashboard/new-orders/widget.client.js +1 -39
  52. package/dist/modules/sales/widgets/dashboard/new-orders/widget.client.js.map +2 -2
  53. package/dist/modules/sales/widgets/dashboard/new-quotes/widget.client.js +1 -39
  54. package/dist/modules/sales/widgets/dashboard/new-quotes/widget.client.js.map +2 -2
  55. package/dist/modules/sales/widgets/dashboard/shared.js +46 -0
  56. package/dist/modules/sales/widgets/dashboard/shared.js.map +7 -0
  57. package/dist/modules/staff/api/activities.js +24 -232
  58. package/dist/modules/staff/api/activities.js.map +2 -2
  59. package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js +14 -34
  60. package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js.map +2 -2
  61. package/dist/modules/staff/backend/staff/my-leave-requests/[id]/page.js +15 -34
  62. package/dist/modules/staff/backend/staff/my-leave-requests/[id]/page.js.map +2 -2
  63. package/dist/modules/staff/commands/activities.js +3 -8
  64. package/dist/modules/staff/commands/activities.js.map +2 -2
  65. package/dist/modules/staff/commands/comments.js +2 -8
  66. package/dist/modules/staff/commands/comments.js.map +2 -2
  67. package/dist/modules/staff/lib/leaveRequestHelpers.js +41 -0
  68. package/dist/modules/staff/lib/leaveRequestHelpers.js.map +7 -0
  69. package/package.json +2 -2
  70. package/src/modules/currencies/backend/exchange-rates/[id]/page.tsx +20 -180
  71. package/src/modules/currencies/backend/exchange-rates/create/page.tsx +16 -175
  72. package/src/modules/currencies/lib/exchangeRateFormConfig.ts +200 -0
  73. package/src/modules/customers/api/dashboard/widgets/utils.ts +1 -53
  74. package/src/modules/customers/commands/activities.ts +2 -8
  75. package/src/modules/customers/commands/comments.ts +2 -8
  76. package/src/modules/dashboards/i18n/de.json +3 -0
  77. package/src/modules/dashboards/i18n/en.json +3 -0
  78. package/src/modules/dashboards/i18n/es.json +3 -0
  79. package/src/modules/dashboards/i18n/pl.json +3 -0
  80. package/src/modules/dashboards/lib/widgetScope.ts +53 -0
  81. package/src/modules/entities/lib/makeActivityRoute.ts +327 -0
  82. package/src/modules/resources/api/activities.ts +25 -269
  83. package/src/modules/resources/commands/activities.ts +2 -7
  84. package/src/modules/resources/commands/comments.ts +2 -8
  85. package/src/modules/sales/api/dashboard/widgets/new-orders/route.ts +29 -244
  86. package/src/modules/sales/api/dashboard/widgets/new-quotes/route.ts +30 -245
  87. package/src/modules/sales/api/order-line-statuses/route.ts +16 -209
  88. package/src/modules/sales/api/order-lines/route.ts +16 -300
  89. package/src/modules/sales/api/order-statuses/route.ts +16 -209
  90. package/src/modules/sales/api/payment-statuses/route.ts +16 -209
  91. package/src/modules/sales/api/quote-lines/route.ts +16 -298
  92. package/src/modules/sales/api/shipment-statuses/route.ts +16 -209
  93. package/src/modules/sales/components/PaymentMethodsSettings.tsx +3 -88
  94. package/src/modules/sales/components/ProviderFieldInput.tsx +85 -0
  95. package/src/modules/sales/components/ShippingMethodsSettings.tsx +3 -86
  96. package/src/modules/sales/lib/makeSalesLineRoute.ts +345 -0
  97. package/src/modules/sales/lib/makeStatusDictionaryRoute.ts +229 -0
  98. package/src/modules/sales/widgets/dashboard/makeDashboardWidgetRoute.ts +247 -0
  99. package/src/modules/sales/widgets/dashboard/new-orders/widget.client.tsx +7 -50
  100. package/src/modules/sales/widgets/dashboard/new-quotes/widget.client.tsx +7 -49
  101. package/src/modules/sales/widgets/dashboard/shared.ts +44 -0
  102. package/src/modules/staff/api/activities.ts +25 -269
  103. package/src/modules/staff/backend/staff/leave-requests/[id]/page.tsx +15 -69
  104. package/src/modules/staff/backend/staff/my-leave-requests/[id]/page.tsx +16 -65
  105. package/src/modules/staff/commands/activities.ts +2 -7
  106. package/src/modules/staff/commands/comments.ts +2 -8
  107. package/src/modules/staff/lib/leaveRequestHelpers.ts +78 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../src/modules/sales/api/dashboard/widgets/new-orders/route.ts"],
4
- "sourcesContent": ["import { createHash } from 'node:crypto'\r\nimport { NextResponse } from 'next/server'\r\nimport { z } from 'zod'\r\nimport type { FilterQuery } from '@mikro-orm/core'\r\nimport type { CacheStrategy } from '@open-mercato/cache'\r\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\r\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\r\nimport { runWithCacheTenant } from '@open-mercato/cache'\r\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\r\nimport { findAndCountWithDecryption } from '@open-mercato/shared/lib/encryption/find'\r\nimport { resolveDateRange } from '@open-mercato/ui/backend/date-range'\r\nimport { SalesOrder } from '../../../../data/entities'\r\nimport { extractCustomerName, type DatePeriodOption } from '../helpers'\r\nimport { resolveWidgetScope, type WidgetScopeContext } from '../../../../../customers/api/dashboard/widgets/utils'\r\n\r\nconst WIDGET_CACHE_TTL = 120_000\r\nconst WIDGET_CACHE_SEGMENT_TTL = 86_400_000\r\nconst WIDGET_CACHE_SEGMENT_KEY = 'widget-data:__segment__'\r\nconst WIDGET_CACHE_TAGS = ['widget-data', 'widget-data:sales:orders']\r\nconst WIDGET_CACHE_ID = 'sales:new-orders'\r\n\r\nconst querySchema = z.object({\r\n limit: z.coerce.number().min(1).max(20).default(5),\r\n datePeriod: z.enum(['last24h', 'last7d', 'last30d', 'custom']).default('last24h'),\r\n customFrom: z.string().optional(),\r\n customTo: z.string().optional(),\r\n tenantId: z.string().uuid().optional(),\r\n organizationId: z.string().uuid().optional(),\r\n})\r\n\r\nexport const metadata = {\r\n GET: { requireAuth: true, requireFeatures: ['dashboards.view', 'sales.widgets.new-orders'] },\r\n}\r\n\r\ntype WidgetContext = WidgetScopeContext & {\r\n limit: number\r\n datePeriod: DatePeriodOption\r\n customFrom?: string\r\n customTo?: string\r\n}\r\n\r\ntype NewOrdersWidgetResponse = {\r\n items: Array<{\r\n id: string\r\n orderNumber: string\r\n status: string | null\r\n fulfillmentStatus: string | null\r\n paymentStatus: string | null\r\n customerName: string | null\r\n customerEntityId: string | null\r\n netAmount: string\r\n grossAmount: string\r\n currency: string | null\r\n createdAt: string\r\n }>\r\n total: number\r\n dateRange: {\r\n from: string\r\n to: string\r\n }\r\n}\r\n\r\nfunction normalizeOrganizationIds(organizationIds: string[] | null): string[] | null {\r\n if (organizationIds === null) return null\r\n const set = new Set(organizationIds)\r\n return Array.from(set).sort((a, b) => a.localeCompare(b))\r\n}\r\n\r\nfunction buildCacheKey(params: {\r\n tenantId: string\r\n organizationIds: string[] | null\r\n limit: number\r\n datePeriod: DatePeriodOption\r\n customFrom?: string\r\n customTo?: string\r\n}): string {\r\n const hash = createHash('sha256')\r\n hash.update(\r\n JSON.stringify({\r\n widget: WIDGET_CACHE_ID,\r\n ...params,\r\n organizationIds: normalizeOrganizationIds(params.organizationIds),\r\n })\r\n )\r\n return `widget-data:${hash.digest('hex').slice(0, 16)}`\r\n}\r\n\r\nasync function resolveContext(req: Request, translate: (key: string, fallback?: string) => string): Promise<WidgetContext> {\r\n const url = new URL(req.url)\r\n const rawQuery: Record<string, string> = {}\r\n for (const [key, value] of url.searchParams.entries()) rawQuery[key] = value\r\n const parsed = querySchema.safeParse(rawQuery)\r\n if (!parsed.success) {\r\n throw new CrudHttpError(400, { error: translate('sales.errors.invalid_query', 'Invalid query parameters') })\r\n }\r\n\r\n const { container, em, tenantId, organizationIds } = await resolveWidgetScope(req, translate, {\r\n tenantId: parsed.data.tenantId ?? null,\r\n organizationId: parsed.data.organizationId ?? null,\r\n })\r\n\r\n return {\r\n container,\r\n em,\r\n tenantId,\r\n organizationIds,\r\n limit: parsed.data.limit,\r\n datePeriod: parsed.data.datePeriod,\r\n customFrom: parsed.data.customFrom,\r\n customTo: parsed.data.customTo,\r\n }\r\n}\r\n\r\nexport async function GET(req: Request) {\r\n const { translate } = await resolveTranslations()\r\n try {\r\n const { container, em, tenantId, organizationIds, limit, datePeriod, customFrom, customTo } = await resolveContext(\r\n req,\r\n translate\r\n )\r\n const range = (() => {\r\n if (datePeriod === 'custom') {\r\n const from = customFrom ? new Date(customFrom) : new Date(0)\r\n const to = customTo ? new Date(customTo) : new Date()\r\n return { start: from, end: to }\r\n }\r\n const preset = datePeriod === 'last7d' ? 'last_7_days' : datePeriod === 'last30d' ? 'last_30_days' : 'today'\r\n return resolveDateRange(preset)\r\n })()\r\n\r\n let cache: CacheStrategy | null = null\r\n try {\r\n cache = container.resolve<CacheStrategy>('cache')\r\n } catch {\r\n cache = null\r\n }\r\n\r\n \r\n\r\n // Use shared WidgetDataService to fetch aggregated widget data\r\n const cacheKey = buildCacheKey({ tenantId, organizationIds, limit, datePeriod, customFrom, customTo })\r\n const tenantScope = tenantId ?? null\r\n\r\n if (cache) {\r\n try {\r\n const cached = await runWithCacheTenant(tenantScope, () => cache!.get(cacheKey))\r\n if (cached && typeof cached === 'object' && 'items' in (cached as object)) {\r\n return NextResponse.json(cached)\r\n }\r\n } catch {\r\n }\r\n }\r\n\r\n const where: FilterQuery<SalesOrder> = {\r\n tenantId,\r\n deletedAt: null,\r\n createdAt: { $gte: range.start, $lte: range.end },\r\n }\r\n\r\n if (Array.isArray(organizationIds)) {\r\n const unique = Array.from(new Set(organizationIds))\r\n where.organizationId = unique.length === 1 ? unique[0] : { $in: unique }\r\n }\r\n\r\n const organizationIdScope = Array.isArray(organizationIds) && organizationIds.length === 1 ? organizationIds[0] : null\r\n const [orders, total] = await findAndCountWithDecryption(\r\n em,\r\n SalesOrder,\r\n where,\r\n {\r\n limit,\r\n orderBy: { createdAt: 'desc' as const },\r\n },\r\n { tenantId, organizationId: organizationIdScope },\r\n )\r\n\r\n const items = orders.map((order) => ({\r\n id: order.id,\r\n orderNumber: order.orderNumber,\r\n status: order.status ?? null,\r\n fulfillmentStatus: order.fulfillmentStatus ?? null,\r\n paymentStatus: order.paymentStatus ?? null,\r\n customerName: extractCustomerName(order.customerSnapshot) ?? null,\r\n customerEntityId: order.customerEntityId ?? null,\r\n netAmount: order.grandTotalNetAmount ?? '0',\r\n grossAmount: order.grandTotalGrossAmount ?? '0',\r\n currency: order.currencyCode ?? null,\r\n createdAt: order.createdAt.toISOString(),\r\n }))\r\n\r\n const response: NewOrdersWidgetResponse = {\r\n items,\r\n total,\r\n dateRange: { from: range.start.toISOString(), to: range.end.toISOString() },\r\n }\r\n\r\n if (cache) {\r\n try {\r\n await runWithCacheTenant(tenantScope, () => cache!.set(cacheKey, response, { ttl: WIDGET_CACHE_TTL, tags: WIDGET_CACHE_TAGS }))\r\n await runWithCacheTenant(tenantScope, () => cache!.set(\r\n WIDGET_CACHE_SEGMENT_KEY,\r\n { updatedAt: response.dateRange.to },\r\n { ttl: WIDGET_CACHE_SEGMENT_TTL, tags: ['widget-data'] },\r\n ))\r\n } catch {\r\n }\r\n }\r\n\r\n return NextResponse.json(response)\r\n } catch (err) {\r\n if (err instanceof CrudHttpError) {\r\n return NextResponse.json(err.body, { status: err.status })\r\n }\r\n console.error('sales.widgets.newOrders failed', err)\r\n return NextResponse.json(\r\n { error: translate('sales.widgets.newOrders.error', 'Failed to load orders') },\r\n { status: 500 },\r\n )\r\n }\r\n}\r\n\r\nconst orderItemSchema = z.object({\r\n id: z.string().uuid(),\r\n orderNumber: z.string(),\r\n status: z.string().nullable(),\r\n fulfillmentStatus: z.string().nullable(),\r\n paymentStatus: z.string().nullable(),\r\n customerName: z.string().nullable(),\r\n customerEntityId: z.string().uuid().nullable(),\r\n netAmount: z.string(),\r\n grossAmount: z.string(),\r\n currency: z.string().nullable(),\r\n createdAt: z.string(),\r\n})\r\n\r\nconst responseSchema = z.object({\r\n items: z.array(orderItemSchema),\r\n total: z.number(),\r\n dateRange: z.object({\r\n from: z.string(),\r\n to: z.string(),\r\n }),\r\n})\r\n\r\nconst widgetErrorSchema = z.object({ error: z.string() })\r\n\r\nexport const openApi: OpenApiRouteDoc = {\r\n tag: 'Sales',\r\n summary: 'New orders dashboard widget',\r\n description: 'Fetches recently created sales orders for the dashboard widget with a configurable date period.',\r\n methods: {\r\n GET: {\r\n summary: 'Fetch recently created sales orders',\r\n query: querySchema,\r\n responses: [{ status: 200, description: 'List of recent orders', schema: responseSchema }],\r\n errors: [\r\n { status: 400, description: 'Invalid query parameters', schema: widgetErrorSchema },\r\n { status: 401, description: 'Unauthorized', schema: widgetErrorSchema },\r\n { status: 403, description: 'Forbidden', schema: widgetErrorSchema },\r\n { status: 500, description: 'Widget failed to load', schema: widgetErrorSchema },\r\n ],\r\n },\r\n },\r\n}\r\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { z } from 'zod'\r\nimport { SalesOrder } from '../../../../data/entities'\r\nimport { extractCustomerName } from '../helpers'\r\nimport { makeDashboardWidgetRoute } from '../../../../widgets/dashboard/makeDashboardWidgetRoute'\r\n\r\nconst orderItemSchema = z.object({\r\n id: z.string().uuid(),\r\n orderNumber: z.string(),\r\n status: z.string().nullable(),\r\n fulfillmentStatus: z.string().nullable(),\r\n paymentStatus: z.string().nullable(),\r\n customerName: z.string().nullable(),\r\n customerEntityId: z.string().uuid().nullable(),\r\n netAmount: z.string(),\r\n grossAmount: z.string(),\r\n currency: z.string().nullable(),\r\n createdAt: z.string(),\r\n})\r\n\r\nconst { GET, metadata, openApi } = makeDashboardWidgetRoute({\r\n entity: SalesOrder,\r\n cacheId: 'sales:new-orders',\r\n cacheTags: ['widget-data:sales:orders'],\r\n feature: 'sales.widgets.new-orders',\r\n itemSchema: orderItemSchema,\r\n errorPrefix: 'sales.widgets.newOrders',\r\n openApi: {\r\n summary: 'New orders dashboard widget',\r\n description: 'Fetches recently created sales orders for the dashboard widget with a configurable date period.',\r\n getSummary: 'Fetch recently created sales orders',\r\n itemDescription: 'List of recent orders',\r\n errorFallback: 'Failed to load orders',\r\n },\r\n mapItem: (order) => ({\r\n id: order.id as string,\r\n orderNumber: order.orderNumber as string,\r\n status: (order.status as string) ?? null,\r\n fulfillmentStatus: (order.fulfillmentStatus as string) ?? null,\r\n paymentStatus: (order.paymentStatus as string) ?? null,\r\n customerName: extractCustomerName(order.customerSnapshot) ?? null,\r\n customerEntityId: (order.customerEntityId as string) ?? null,\r\n netAmount: (order.grandTotalNetAmount as string) ?? '0',\r\n grossAmount: (order.grandTotalGrossAmount as string) ?? '0',\r\n currency: (order.currencyCode as string) ?? null,\r\n createdAt: order.createdAt ? (order.createdAt as Date).toISOString() : new Date().toISOString(),\r\n }),\r\n})\r\n\r\nexport { GET, metadata, openApi }\r\n"],
5
+ "mappings": "AAAA,SAAS,SAAS;AAClB,SAAS,kBAAkB;AAC3B,SAAS,2BAA2B;AACpC,SAAS,gCAAgC;AAEzC,MAAM,kBAAkB,EAAE,OAAO;AAAA,EAC/B,IAAI,EAAE,OAAO,EAAE,KAAK;AAAA,EACpB,aAAa,EAAE,OAAO;AAAA,EACtB,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,mBAAmB,EAAE,OAAO,EAAE,SAAS;AAAA,EACvC,eAAe,EAAE,OAAO,EAAE,SAAS;AAAA,EACnC,cAAc,EAAE,OAAO,EAAE,SAAS;AAAA,EAClC,kBAAkB,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EAC7C,WAAW,EAAE,OAAO;AAAA,EACpB,aAAa,EAAE,OAAO;AAAA,EACtB,UAAU,EAAE,OAAO,EAAE,SAAS;AAAA,EAC9B,WAAW,EAAE,OAAO;AACtB,CAAC;AAED,MAAM,EAAE,KAAK,UAAU,QAAQ,IAAI,yBAAyB;AAAA,EAC1D,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,WAAW,CAAC,0BAA0B;AAAA,EACtC,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,SAAS;AAAA,IACP,SAAS;AAAA,IACT,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,iBAAiB;AAAA,IACjB,eAAe;AAAA,EACjB;AAAA,EACA,SAAS,CAAC,WAAW;AAAA,IACnB,IAAI,MAAM;AAAA,IACV,aAAa,MAAM;AAAA,IACnB,QAAS,MAAM,UAAqB;AAAA,IACpC,mBAAoB,MAAM,qBAAgC;AAAA,IAC1D,eAAgB,MAAM,iBAA4B;AAAA,IAClD,cAAc,oBAAoB,MAAM,gBAAgB,KAAK;AAAA,IAC7D,kBAAmB,MAAM,oBAA+B;AAAA,IACxD,WAAY,MAAM,uBAAkC;AAAA,IACpD,aAAc,MAAM,yBAAoC;AAAA,IACxD,UAAW,MAAM,gBAA2B;AAAA,IAC5C,WAAW,MAAM,YAAa,MAAM,UAAmB,YAAY,KAAI,oBAAI,KAAK,GAAE,YAAY;AAAA,EAChG;AACF,CAAC;",
6
6
  "names": []
7
7
  }
@@ -1,164 +1,7 @@
1
- import { createHash } from "node:crypto";
2
- import { NextResponse } from "next/server";
3
1
  import { z } from "zod";
4
- import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
5
- import { runWithCacheTenant } from "@open-mercato/cache";
6
- import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
7
- import { findAndCountWithDecryption } from "@open-mercato/shared/lib/encryption/find";
8
- import { resolveDateRange } from "@open-mercato/ui/backend/date-range";
9
2
  import { SalesQuote } from "../../../../data/entities.js";
10
3
  import { extractCustomerName } from "../helpers.js";
11
- import { resolveWidgetScope } from "../../../../../customers/api/dashboard/widgets/utils.js";
12
- const WIDGET_CACHE_TTL = 12e4;
13
- const WIDGET_CACHE_SEGMENT_TTL = 864e5;
14
- const WIDGET_CACHE_SEGMENT_KEY = "widget-data:__segment__";
15
- const WIDGET_CACHE_TAGS = ["widget-data", "widget-data:sales:quotes"];
16
- const WIDGET_CACHE_ID = "sales:new-quotes";
17
- const querySchema = z.object({
18
- limit: z.coerce.number().min(1).max(20).default(5),
19
- datePeriod: z.enum(["last24h", "last7d", "last30d", "custom"]).default("last24h"),
20
- customFrom: z.string().optional(),
21
- customTo: z.string().optional(),
22
- tenantId: z.string().uuid().optional(),
23
- organizationId: z.string().uuid().optional()
24
- });
25
- const metadata = {
26
- GET: { requireAuth: true, requireFeatures: ["dashboards.view", "sales.widgets.new-quotes"] }
27
- };
28
- function normalizeOrganizationIds(organizationIds) {
29
- if (organizationIds === null) return null;
30
- const set = new Set(organizationIds);
31
- return Array.from(set).sort((a, b) => a.localeCompare(b));
32
- }
33
- function buildCacheKey(params) {
34
- const hash = createHash("sha256");
35
- hash.update(
36
- JSON.stringify({
37
- widget: WIDGET_CACHE_ID,
38
- ...params,
39
- organizationIds: normalizeOrganizationIds(params.organizationIds)
40
- })
41
- );
42
- return `widget-data:${hash.digest("hex").slice(0, 16)}`;
43
- }
44
- async function resolveContext(req, translate) {
45
- const url = new URL(req.url);
46
- const rawQuery = {};
47
- for (const [key, value] of url.searchParams.entries()) rawQuery[key] = value;
48
- const parsed = querySchema.safeParse(rawQuery);
49
- if (!parsed.success) {
50
- throw new CrudHttpError(400, { error: translate("sales.errors.invalid_query", "Invalid query parameters") });
51
- }
52
- const { container, em, tenantId, organizationIds } = await resolveWidgetScope(req, translate, {
53
- tenantId: parsed.data.tenantId ?? null,
54
- organizationId: parsed.data.organizationId ?? null
55
- });
56
- return {
57
- container,
58
- em,
59
- tenantId,
60
- organizationIds,
61
- limit: parsed.data.limit,
62
- datePeriod: parsed.data.datePeriod,
63
- customFrom: parsed.data.customFrom,
64
- customTo: parsed.data.customTo
65
- };
66
- }
67
- async function GET(req) {
68
- const { translate } = await resolveTranslations();
69
- try {
70
- const { container, em, tenantId, organizationIds, limit, datePeriod, customFrom, customTo } = await resolveContext(
71
- req,
72
- translate
73
- );
74
- const range = (() => {
75
- if (datePeriod === "custom") {
76
- const from = customFrom ? new Date(customFrom) : /* @__PURE__ */ new Date(0);
77
- const to = customTo ? new Date(customTo) : /* @__PURE__ */ new Date();
78
- return { start: from, end: to };
79
- }
80
- const preset = datePeriod === "last7d" ? "last_7_days" : datePeriod === "last30d" ? "last_30_days" : "today";
81
- return resolveDateRange(preset);
82
- })();
83
- let cache = null;
84
- try {
85
- cache = container.resolve("cache");
86
- } catch {
87
- cache = null;
88
- }
89
- const cacheKey = buildCacheKey({ tenantId, organizationIds, limit, datePeriod, customFrom, customTo });
90
- const tenantScope = tenantId ?? null;
91
- if (cache) {
92
- try {
93
- const cached = await runWithCacheTenant(tenantScope, () => cache.get(cacheKey));
94
- if (cached && typeof cached === "object" && "items" in cached) {
95
- return NextResponse.json(cached);
96
- }
97
- } catch {
98
- }
99
- }
100
- const where = {
101
- tenantId,
102
- deletedAt: null,
103
- createdAt: { $gte: range.start, $lte: range.end }
104
- };
105
- if (Array.isArray(organizationIds)) {
106
- const unique = Array.from(new Set(organizationIds));
107
- where.organizationId = unique.length === 1 ? unique[0] : { $in: unique };
108
- }
109
- const organizationIdScope = Array.isArray(organizationIds) && organizationIds.length === 1 ? organizationIds[0] : null;
110
- const [quotes, total] = await findAndCountWithDecryption(
111
- em,
112
- SalesQuote,
113
- where,
114
- {
115
- limit,
116
- orderBy: { createdAt: "desc" }
117
- },
118
- { tenantId, organizationId: organizationIdScope }
119
- );
120
- const items = quotes.map((quote) => ({
121
- id: quote.id,
122
- quoteNumber: quote.quoteNumber,
123
- status: quote.status ?? null,
124
- customerName: extractCustomerName(quote.customerSnapshot) ?? null,
125
- customerEntityId: quote.customerEntityId ?? null,
126
- validFrom: quote.validFrom ? quote.validFrom.toISOString() : null,
127
- validUntil: quote.validUntil ? quote.validUntil.toISOString() : null,
128
- netAmount: quote.grandTotalNetAmount ?? "0",
129
- grossAmount: quote.grandTotalGrossAmount ?? "0",
130
- currency: quote.currencyCode ?? null,
131
- createdAt: quote.createdAt.toISOString(),
132
- convertedOrderId: quote.convertedOrderId ?? null
133
- }));
134
- const response = {
135
- items,
136
- total,
137
- dateRange: { from: range.start.toISOString(), to: range.end.toISOString() }
138
- };
139
- if (cache) {
140
- try {
141
- await runWithCacheTenant(tenantScope, () => cache.set(cacheKey, response, { ttl: WIDGET_CACHE_TTL, tags: WIDGET_CACHE_TAGS }));
142
- await runWithCacheTenant(tenantScope, () => cache.set(
143
- WIDGET_CACHE_SEGMENT_KEY,
144
- { updatedAt: response.dateRange.to },
145
- { ttl: WIDGET_CACHE_SEGMENT_TTL, tags: ["widget-data"] }
146
- ));
147
- } catch {
148
- }
149
- }
150
- return NextResponse.json(response);
151
- } catch (err) {
152
- if (err instanceof CrudHttpError) {
153
- return NextResponse.json(err.body, { status: err.status });
154
- }
155
- console.error("sales.widgets.newQuotes failed", err);
156
- return NextResponse.json(
157
- { error: translate("sales.widgets.newQuotes.error", "Failed to load quotes") },
158
- { status: 500 }
159
- );
160
- }
161
- }
4
+ import { makeDashboardWidgetRoute } from "../../../../widgets/dashboard/makeDashboardWidgetRoute.js";
162
5
  const quoteItemSchema = z.object({
163
6
  id: z.string().uuid(),
164
7
  quoteNumber: z.string(),
@@ -173,33 +16,35 @@ const quoteItemSchema = z.object({
173
16
  createdAt: z.string(),
174
17
  convertedOrderId: z.string().uuid().nullable()
175
18
  });
176
- const responseSchema = z.object({
177
- items: z.array(quoteItemSchema),
178
- total: z.number(),
179
- dateRange: z.object({
180
- from: z.string(),
181
- to: z.string()
19
+ const { GET, metadata, openApi } = makeDashboardWidgetRoute({
20
+ entity: SalesQuote,
21
+ cacheId: "sales:new-quotes",
22
+ cacheTags: ["widget-data:sales:quotes"],
23
+ feature: "sales.widgets.new-quotes",
24
+ itemSchema: quoteItemSchema,
25
+ errorPrefix: "sales.widgets.newQuotes",
26
+ openApi: {
27
+ summary: "New quotes dashboard widget",
28
+ description: "Fetches recently created sales quotes for the dashboard widget with a configurable date period.",
29
+ getSummary: "Fetch recently created sales quotes",
30
+ itemDescription: "List of recent quotes",
31
+ errorFallback: "Failed to load quotes"
32
+ },
33
+ mapItem: (quote) => ({
34
+ id: quote.id,
35
+ quoteNumber: quote.quoteNumber,
36
+ status: quote.status ?? null,
37
+ customerName: extractCustomerName(quote.customerSnapshot) ?? null,
38
+ customerEntityId: quote.customerEntityId ?? null,
39
+ validFrom: quote.validFrom ? quote.validFrom.toISOString() : null,
40
+ validUntil: quote.validUntil ? quote.validUntil.toISOString() : null,
41
+ netAmount: quote.grandTotalNetAmount ?? "0",
42
+ grossAmount: quote.grandTotalGrossAmount ?? "0",
43
+ currency: quote.currencyCode ?? null,
44
+ createdAt: quote.createdAt ? quote.createdAt.toISOString() : (/* @__PURE__ */ new Date()).toISOString(),
45
+ convertedOrderId: quote.convertedOrderId ?? null
182
46
  })
183
47
  });
184
- const widgetErrorSchema = z.object({ error: z.string() });
185
- const openApi = {
186
- tag: "Sales",
187
- summary: "New quotes dashboard widget",
188
- description: "Fetches recently created sales quotes for the dashboard widget with a configurable date period.",
189
- methods: {
190
- GET: {
191
- summary: "Fetch recently created sales quotes",
192
- query: querySchema,
193
- responses: [{ status: 200, description: "List of recent quotes", schema: responseSchema }],
194
- errors: [
195
- { status: 400, description: "Invalid query parameters", schema: widgetErrorSchema },
196
- { status: 401, description: "Unauthorized", schema: widgetErrorSchema },
197
- { status: 403, description: "Forbidden", schema: widgetErrorSchema },
198
- { status: 500, description: "Widget failed to load", schema: widgetErrorSchema }
199
- ]
200
- }
201
- }
202
- };
203
48
  export {
204
49
  GET,
205
50
  metadata,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../src/modules/sales/api/dashboard/widgets/new-quotes/route.ts"],
4
- "sourcesContent": ["import { createHash } from 'node:crypto'\r\nimport { NextResponse } from 'next/server'\r\nimport { z } from 'zod'\r\nimport type { FilterQuery } from '@mikro-orm/core'\r\nimport type { CacheStrategy } from '@open-mercato/cache'\r\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\r\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\r\nimport { runWithCacheTenant } from '@open-mercato/cache'\r\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\r\nimport { findAndCountWithDecryption } from '@open-mercato/shared/lib/encryption/find'\r\nimport { resolveDateRange } from '@open-mercato/ui/backend/date-range'\r\nimport { SalesQuote } from '../../../../data/entities'\r\nimport { extractCustomerName, type DatePeriodOption } from '../helpers'\r\nimport { resolveWidgetScope, type WidgetScopeContext } from '../../../../../customers/api/dashboard/widgets/utils'\r\n\r\nconst WIDGET_CACHE_TTL = 120_000\r\nconst WIDGET_CACHE_SEGMENT_TTL = 86_400_000\r\nconst WIDGET_CACHE_SEGMENT_KEY = 'widget-data:__segment__'\r\nconst WIDGET_CACHE_TAGS = ['widget-data', 'widget-data:sales:quotes']\r\nconst WIDGET_CACHE_ID = 'sales:new-quotes'\r\n\r\nconst querySchema = z.object({\r\n limit: z.coerce.number().min(1).max(20).default(5),\r\n datePeriod: z.enum(['last24h', 'last7d', 'last30d', 'custom']).default('last24h'),\r\n customFrom: z.string().optional(),\r\n customTo: z.string().optional(),\r\n tenantId: z.string().uuid().optional(),\r\n organizationId: z.string().uuid().optional(),\r\n})\r\n\r\nexport const metadata = {\r\n GET: { requireAuth: true, requireFeatures: ['dashboards.view', 'sales.widgets.new-quotes'] },\r\n}\r\n\r\ntype WidgetContext = WidgetScopeContext & {\r\n limit: number\r\n datePeriod: DatePeriodOption\r\n customFrom?: string\r\n customTo?: string\r\n}\r\n\r\ntype NewQuotesWidgetResponse = {\r\n items: Array<{\r\n id: string\r\n quoteNumber: string\r\n status: string | null\r\n customerName: string | null\r\n customerEntityId: string | null\r\n validFrom: string | null\r\n validUntil: string | null\r\n netAmount: string\r\n grossAmount: string\r\n currency: string | null\r\n createdAt: string\r\n convertedOrderId: string | null\r\n }>\r\n total: number\r\n dateRange: {\r\n from: string\r\n to: string\r\n }\r\n}\r\n\r\nfunction normalizeOrganizationIds(organizationIds: string[] | null): string[] | null {\r\n if (organizationIds === null) return null\r\n const set = new Set(organizationIds)\r\n return Array.from(set).sort((a, b) => a.localeCompare(b))\r\n}\r\n\r\nfunction buildCacheKey(params: {\r\n tenantId: string\r\n organizationIds: string[] | null\r\n limit: number\r\n datePeriod: DatePeriodOption\r\n customFrom?: string\r\n customTo?: string\r\n}): string {\r\n const hash = createHash('sha256')\r\n hash.update(\r\n JSON.stringify({\r\n widget: WIDGET_CACHE_ID,\r\n ...params,\r\n organizationIds: normalizeOrganizationIds(params.organizationIds),\r\n })\r\n )\r\n return `widget-data:${hash.digest('hex').slice(0, 16)}`\r\n}\r\n\r\nasync function resolveContext(req: Request, translate: (key: string, fallback?: string) => string): Promise<WidgetContext> {\r\n const url = new URL(req.url)\r\n const rawQuery: Record<string, string> = {}\r\n for (const [key, value] of url.searchParams.entries()) rawQuery[key] = value\r\n const parsed = querySchema.safeParse(rawQuery)\r\n if (!parsed.success) {\r\n throw new CrudHttpError(400, { error: translate('sales.errors.invalid_query', 'Invalid query parameters') })\r\n }\r\n\r\n const { container, em, tenantId, organizationIds } = await resolveWidgetScope(req, translate, {\r\n tenantId: parsed.data.tenantId ?? null,\r\n organizationId: parsed.data.organizationId ?? null,\r\n })\r\n\r\n return {\r\n container,\r\n em,\r\n tenantId,\r\n organizationIds,\r\n limit: parsed.data.limit,\r\n datePeriod: parsed.data.datePeriod,\r\n customFrom: parsed.data.customFrom,\r\n customTo: parsed.data.customTo,\r\n }\r\n}\r\n\r\nexport async function GET(req: Request) {\r\n const { translate } = await resolveTranslations()\r\n try {\r\n const { container, em, tenantId, organizationIds, limit, datePeriod, customFrom, customTo } = await resolveContext(\r\n req,\r\n translate\r\n )\r\n const range = (() => {\r\n if (datePeriod === 'custom') {\r\n const from = customFrom ? new Date(customFrom) : new Date(0)\r\n const to = customTo ? new Date(customTo) : new Date()\r\n return { start: from, end: to }\r\n }\r\n const preset = datePeriod === 'last7d' ? 'last_7_days' : datePeriod === 'last30d' ? 'last_30_days' : 'today'\r\n return resolveDateRange(preset)\r\n })()\r\n\r\n let cache: CacheStrategy | null = null\r\n try {\r\n cache = container.resolve<CacheStrategy>('cache')\r\n } catch {\r\n cache = null\r\n }\r\n\r\n \r\n\r\n const cacheKey = buildCacheKey({ tenantId, organizationIds, limit, datePeriod, customFrom, customTo })\r\n const tenantScope = tenantId ?? null\r\n\r\n if (cache) {\r\n try {\r\n const cached = await runWithCacheTenant(tenantScope, () => cache!.get(cacheKey))\r\n if (cached && typeof cached === 'object' && 'items' in (cached as object)) {\r\n return NextResponse.json(cached)\r\n }\r\n } catch {\r\n }\r\n }\r\n\r\n const where: FilterQuery<SalesQuote> = {\r\n tenantId,\r\n deletedAt: null,\r\n createdAt: { $gte: range.start, $lte: range.end },\r\n }\r\n\r\n if (Array.isArray(organizationIds)) {\r\n const unique = Array.from(new Set(organizationIds))\r\n where.organizationId = unique.length === 1 ? unique[0] : { $in: unique }\r\n }\r\n\r\n const organizationIdScope = Array.isArray(organizationIds) && organizationIds.length === 1 ? organizationIds[0] : null\r\n const [quotes, total] = await findAndCountWithDecryption(\r\n em,\r\n SalesQuote,\r\n where,\r\n {\r\n limit,\r\n orderBy: { createdAt: 'desc' as const },\r\n },\r\n { tenantId, organizationId: organizationIdScope },\r\n )\r\n\r\n const items = quotes.map((quote) => ({\r\n id: quote.id,\r\n quoteNumber: quote.quoteNumber,\r\n status: quote.status ?? null,\r\n customerName: extractCustomerName(quote.customerSnapshot) ?? null,\r\n customerEntityId: quote.customerEntityId ?? null,\r\n validFrom: quote.validFrom ? quote.validFrom.toISOString() : null,\r\n validUntil: quote.validUntil ? quote.validUntil.toISOString() : null,\r\n netAmount: quote.grandTotalNetAmount ?? '0',\r\n grossAmount: quote.grandTotalGrossAmount ?? '0',\r\n currency: quote.currencyCode ?? null,\r\n createdAt: quote.createdAt.toISOString(),\r\n convertedOrderId: quote.convertedOrderId ?? null,\r\n }))\r\n\r\n const response: NewQuotesWidgetResponse = {\r\n items,\r\n total,\r\n dateRange: { from: range.start.toISOString(), to: range.end.toISOString() },\r\n }\r\n\r\n if (cache) {\r\n try {\r\n await runWithCacheTenant(tenantScope, () => cache!.set(cacheKey, response, { ttl: WIDGET_CACHE_TTL, tags: WIDGET_CACHE_TAGS }))\r\n await runWithCacheTenant(tenantScope, () => cache!.set(\r\n WIDGET_CACHE_SEGMENT_KEY,\r\n { updatedAt: response.dateRange.to },\r\n { ttl: WIDGET_CACHE_SEGMENT_TTL, tags: ['widget-data'] },\r\n ))\r\n } catch {\r\n }\r\n }\r\n\r\n return NextResponse.json(response)\r\n } catch (err) {\r\n if (err instanceof CrudHttpError) {\r\n return NextResponse.json(err.body, { status: err.status })\r\n }\r\n console.error('sales.widgets.newQuotes failed', err)\r\n return NextResponse.json(\r\n { error: translate('sales.widgets.newQuotes.error', 'Failed to load quotes') },\r\n { status: 500 },\r\n )\r\n }\r\n}\r\n\r\nconst quoteItemSchema = z.object({\r\n id: z.string().uuid(),\r\n quoteNumber: z.string(),\r\n status: z.string().nullable(),\r\n customerName: z.string().nullable(),\r\n customerEntityId: z.string().uuid().nullable(),\r\n validFrom: z.string().nullable(),\r\n validUntil: z.string().nullable(),\r\n netAmount: z.string(),\r\n grossAmount: z.string(),\r\n currency: z.string().nullable(),\r\n createdAt: z.string(),\r\n convertedOrderId: z.string().uuid().nullable(),\r\n})\r\n\r\nconst responseSchema = z.object({\r\n items: z.array(quoteItemSchema),\r\n total: z.number(),\r\n dateRange: z.object({\r\n from: z.string(),\r\n to: z.string(),\r\n }),\r\n})\r\n\r\nconst widgetErrorSchema = z.object({ error: z.string() })\r\n\r\nexport const openApi: OpenApiRouteDoc = {\r\n tag: 'Sales',\r\n summary: 'New quotes dashboard widget',\r\n description: 'Fetches recently created sales quotes for the dashboard widget with a configurable date period.',\r\n methods: {\r\n GET: {\r\n summary: 'Fetch recently created sales quotes',\r\n query: querySchema,\r\n responses: [{ status: 200, description: 'List of recent quotes', schema: responseSchema }],\r\n errors: [\r\n { status: 400, description: 'Invalid query parameters', schema: widgetErrorSchema },\r\n { status: 401, description: 'Unauthorized', schema: widgetErrorSchema },\r\n { status: 403, description: 'Forbidden', schema: widgetErrorSchema },\r\n { status: 500, description: 'Widget failed to load', schema: widgetErrorSchema },\r\n ],\r\n },\r\n },\r\n}\r\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { z } from 'zod'\r\nimport { SalesQuote } from '../../../../data/entities'\r\nimport { extractCustomerName } from '../helpers'\r\nimport { makeDashboardWidgetRoute } from '../../../../widgets/dashboard/makeDashboardWidgetRoute'\r\n\r\nconst quoteItemSchema = z.object({\r\n id: z.string().uuid(),\r\n quoteNumber: z.string(),\r\n status: z.string().nullable(),\r\n customerName: z.string().nullable(),\r\n customerEntityId: z.string().uuid().nullable(),\r\n validFrom: z.string().nullable(),\r\n validUntil: z.string().nullable(),\r\n netAmount: z.string(),\r\n grossAmount: z.string(),\r\n currency: z.string().nullable(),\r\n createdAt: z.string(),\r\n convertedOrderId: z.string().uuid().nullable(),\r\n})\r\n\r\nconst { GET, metadata, openApi } = makeDashboardWidgetRoute({\r\n entity: SalesQuote,\r\n cacheId: 'sales:new-quotes',\r\n cacheTags: ['widget-data:sales:quotes'],\r\n feature: 'sales.widgets.new-quotes',\r\n itemSchema: quoteItemSchema,\r\n errorPrefix: 'sales.widgets.newQuotes',\r\n openApi: {\r\n summary: 'New quotes dashboard widget',\r\n description: 'Fetches recently created sales quotes for the dashboard widget with a configurable date period.',\r\n getSummary: 'Fetch recently created sales quotes',\r\n itemDescription: 'List of recent quotes',\r\n errorFallback: 'Failed to load quotes',\r\n },\r\n mapItem: (quote) => ({\r\n id: quote.id as string,\r\n quoteNumber: quote.quoteNumber as string,\r\n status: (quote.status as string) ?? null,\r\n customerName: extractCustomerName(quote.customerSnapshot) ?? null,\r\n customerEntityId: (quote.customerEntityId as string) ?? null,\r\n validFrom: quote.validFrom ? (quote.validFrom as Date).toISOString() : null,\r\n validUntil: quote.validUntil ? (quote.validUntil as Date).toISOString() : null,\r\n netAmount: (quote.grandTotalNetAmount as string) ?? '0',\r\n grossAmount: (quote.grandTotalGrossAmount as string) ?? '0',\r\n currency: (quote.currencyCode as string) ?? null,\r\n createdAt: quote.createdAt ? (quote.createdAt as Date).toISOString() : new Date().toISOString(),\r\n convertedOrderId: (quote.convertedOrderId as string) ?? null,\r\n }),\r\n})\r\n\r\nexport { GET, metadata, openApi }\r\n"],
5
+ "mappings": "AAAA,SAAS,SAAS;AAClB,SAAS,kBAAkB;AAC3B,SAAS,2BAA2B;AACpC,SAAS,gCAAgC;AAEzC,MAAM,kBAAkB,EAAE,OAAO;AAAA,EAC/B,IAAI,EAAE,OAAO,EAAE,KAAK;AAAA,EACpB,aAAa,EAAE,OAAO;AAAA,EACtB,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,cAAc,EAAE,OAAO,EAAE,SAAS;AAAA,EAClC,kBAAkB,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EAC7C,WAAW,EAAE,OAAO,EAAE,SAAS;AAAA,EAC/B,YAAY,EAAE,OAAO,EAAE,SAAS;AAAA,EAChC,WAAW,EAAE,OAAO;AAAA,EACpB,aAAa,EAAE,OAAO;AAAA,EACtB,UAAU,EAAE,OAAO,EAAE,SAAS;AAAA,EAC9B,WAAW,EAAE,OAAO;AAAA,EACpB,kBAAkB,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAC/C,CAAC;AAED,MAAM,EAAE,KAAK,UAAU,QAAQ,IAAI,yBAAyB;AAAA,EAC1D,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,WAAW,CAAC,0BAA0B;AAAA,EACtC,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,SAAS;AAAA,IACP,SAAS;AAAA,IACT,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,iBAAiB;AAAA,IACjB,eAAe;AAAA,EACjB;AAAA,EACA,SAAS,CAAC,WAAW;AAAA,IACnB,IAAI,MAAM;AAAA,IACV,aAAa,MAAM;AAAA,IACnB,QAAS,MAAM,UAAqB;AAAA,IACpC,cAAc,oBAAoB,MAAM,gBAAgB,KAAK;AAAA,IAC7D,kBAAmB,MAAM,oBAA+B;AAAA,IACxD,WAAW,MAAM,YAAa,MAAM,UAAmB,YAAY,IAAI;AAAA,IACvE,YAAY,MAAM,aAAc,MAAM,WAAoB,YAAY,IAAI;AAAA,IAC1E,WAAY,MAAM,uBAAkC;AAAA,IACpD,aAAc,MAAM,yBAAoC;AAAA,IACxD,UAAW,MAAM,gBAA2B;AAAA,IAC5C,WAAW,MAAM,YAAa,MAAM,UAAmB,YAAY,KAAI,oBAAI,KAAK,GAAE,YAAY;AAAA,IAC9F,kBAAmB,MAAM,oBAA+B;AAAA,EAC1D;AACF,CAAC;",
6
6
  "names": []
7
7
  }
@@ -1,201 +1,22 @@
1
- import { z } from "zod";
2
- import { makeCrudRoute } from "@open-mercato/shared/lib/crud/factory";
3
- import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
4
- import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
5
- import { Dictionary, DictionaryEntry } from "@open-mercato/core/modules/dictionaries/data/entities";
6
1
  import { E } from "../../../../generated/entities.ids.generated.js";
7
2
  import * as F from "../../../../generated/entities/dictionary_entry/index.js";
8
- import { statusDictionaryCreateSchema, statusDictionaryUpdateSchema } from "../../data/validators.js";
9
- import { getSalesDictionaryDefinition, ensureSalesDictionary } from "../../lib/dictionaries.js";
10
- import { parseScopedCommandInput, resolveCrudRecordId } from "../utils.js";
11
- import {
12
- createPagedListResponseSchema,
13
- createSalesCrudOpenApi,
14
- defaultDeleteRequestSchema
15
- } from "../openapi.js";
16
- import { escapeLikePattern } from "@open-mercato/shared/lib/db/escapeLikePattern";
17
- const rawBodySchema = z.object({}).passthrough();
18
- const listSchema = z.object({
19
- page: z.coerce.number().min(1).default(1),
20
- pageSize: z.coerce.number().min(1).max(100).default(50),
21
- search: z.string().optional(),
22
- sortField: z.string().optional(),
23
- sortDir: z.enum(["asc", "desc"]).optional()
24
- }).passthrough();
25
- const kind = "order-line-status";
26
- const definition = getSalesDictionaryDefinition(kind);
27
- const metadata = {
28
- GET: { requireAuth: true, requireFeatures: ["sales.settings.manage"] },
29
- POST: { requireAuth: true, requireFeatures: ["sales.settings.manage"] },
30
- PUT: { requireAuth: true, requireFeatures: ["sales.settings.manage"] },
31
- DELETE: { requireAuth: true, requireFeatures: ["sales.settings.manage"] }
32
- };
33
- const dictionaryItemSchema = z.object({
34
- id: z.string().uuid(),
35
- value: z.string(),
36
- label: z.string().nullable(),
37
- color: z.string().nullable(),
38
- icon: z.string().nullable(),
39
- organizationId: z.string().uuid().nullable(),
40
- tenantId: z.string().uuid().nullable(),
41
- createdAt: z.string(),
42
- updatedAt: z.string()
43
- });
44
- const dictionaryListResponseSchema = createPagedListResponseSchema(dictionaryItemSchema);
45
- const normalizeId = (value) => {
46
- if (typeof value !== "string") return null;
47
- const trimmed = value.trim();
48
- return trimmed.length > 0 ? trimmed : null;
49
- };
50
- async function resolveDictionaryContext(ctx) {
51
- if (!ctx.auth || !ctx.auth.tenantId) {
52
- throw new CrudHttpError(401, { error: "Tenant context is required." });
53
- }
54
- const em = ctx.container.resolve("em");
55
- const tenantId = ctx.auth.tenantId;
56
- const candidateOrgIds = /* @__PURE__ */ new Set();
57
- const pushCandidate = (value) => {
58
- const normalized = normalizeId(value);
59
- if (normalized) candidateOrgIds.add(normalized);
60
- };
61
- pushCandidate(ctx.selectedOrganizationId);
62
- pushCandidate(ctx.auth.orgId ?? null);
63
- const scope = ctx.organizationScope;
64
- if (scope) {
65
- if (Array.isArray(scope.filterIds)) {
66
- for (const id of scope.filterIds) pushCandidate(id);
67
- }
68
- if (Array.isArray(scope.allowedIds)) {
69
- for (const id of scope.allowedIds) pushCandidate(id);
70
- }
71
- }
72
- for (const orgId of candidateOrgIds) {
73
- const dictionary = await ensureSalesDictionary({
74
- em,
75
- tenantId,
76
- organizationId: orgId,
77
- kind
78
- });
79
- if (dictionary) {
80
- return { dictionaryId: dictionary.id, organizationId: orgId };
81
- }
3
+ import { makeStatusDictionaryRoute } from "../../lib/makeStatusDictionaryRoute.js";
4
+ const route = makeStatusDictionaryRoute({
5
+ kind: "order-line-status",
6
+ entityId: E.dictionaries.dictionary_entry,
7
+ fieldConstants: F,
8
+ openApi: {
9
+ resourceName: "Order line status",
10
+ pluralName: "Order line statuses",
11
+ description: "Manage custom order line statuses available for sales documents."
82
12
  }
83
- const fallback = await em.findOne(
84
- Dictionary,
85
- {
86
- tenantId,
87
- key: definition.key,
88
- deletedAt: null
89
- },
90
- { orderBy: { createdAt: "asc" } }
91
- );
92
- if (fallback) {
93
- return { dictionaryId: fallback.id, organizationId: fallback.organizationId };
94
- }
95
- throw new CrudHttpError(400, { error: "Organization context is required." });
96
- }
97
- const crud = makeCrudRoute({
98
- metadata,
99
- orm: {
100
- entity: DictionaryEntry,
101
- idField: "id",
102
- orgField: "organizationId",
103
- tenantField: "tenantId",
104
- softDeleteField: null
105
- },
106
- list: {
107
- schema: listSchema,
108
- entityId: E.dictionaries.dictionary_entry,
109
- fields: [
110
- F.id,
111
- F.value,
112
- F.label,
113
- F.color,
114
- F.icon,
115
- F.organization_id,
116
- F.tenant_id,
117
- F.created_at,
118
- F.updated_at
119
- ],
120
- sortFieldMap: {
121
- id: F.id,
122
- value: F.value,
123
- label: F.label,
124
- createdAt: F.created_at,
125
- updatedAt: F.updated_at
126
- },
127
- buildFilters: async (query, ctx) => {
128
- const { dictionaryId } = await resolveDictionaryContext(ctx);
129
- const filters = {
130
- dictionary_id: dictionaryId
131
- };
132
- if (query.search && query.search.trim().length > 0) {
133
- const term = `%${escapeLikePattern(query.search.trim())}%`;
134
- filters.$or = [
135
- { [F.value]: { $ilike: term } },
136
- { [F.label]: { $ilike: term } }
137
- ];
138
- }
139
- return filters;
140
- },
141
- transformItem: (item) => ({
142
- id: item.id,
143
- value: item.value,
144
- label: item.label,
145
- color: item.color ?? null,
146
- icon: item.icon ?? null,
147
- organizationId: item.organization_id ?? null,
148
- tenantId: item.tenant_id ?? null,
149
- createdAt: item.created_at,
150
- updatedAt: item.updated_at
151
- })
152
- },
153
- actions: {
154
- create: {
155
- commandId: `${definition.commandPrefix}.create`,
156
- schema: rawBodySchema,
157
- mapInput: async ({ raw, ctx }) => {
158
- const { translate } = await resolveTranslations();
159
- return parseScopedCommandInput(statusDictionaryCreateSchema, raw ?? {}, ctx, translate);
160
- },
161
- response: ({ result }) => ({ id: result?.entryId ?? null }),
162
- status: 201
163
- },
164
- update: {
165
- commandId: `${definition.commandPrefix}.update`,
166
- schema: rawBodySchema,
167
- mapInput: async ({ raw, ctx }) => {
168
- const { translate } = await resolveTranslations();
169
- return parseScopedCommandInput(statusDictionaryUpdateSchema, raw ?? {}, ctx, translate);
170
- },
171
- response: () => ({ ok: true })
172
- },
173
- delete: {
174
- commandId: `${definition.commandPrefix}.delete`,
175
- schema: rawBodySchema,
176
- mapInput: async ({ parsed, ctx }) => {
177
- const { translate } = await resolveTranslations();
178
- const id = resolveCrudRecordId(parsed, ctx, translate);
179
- return { id };
180
- },
181
- response: () => ({ ok: true })
182
- }
183
- }
184
- });
185
- const openApi = createSalesCrudOpenApi({
186
- resourceName: "Order line status",
187
- pluralName: "Order line statuses",
188
- description: "Manage custom order line statuses available for sales documents.",
189
- querySchema: listSchema,
190
- listResponseSchema: dictionaryListResponseSchema,
191
- create: { schema: statusDictionaryCreateSchema },
192
- update: { schema: statusDictionaryUpdateSchema },
193
- del: { schema: defaultDeleteRequestSchema }
194
13
  });
195
- const GET = crud.GET;
196
- const POST = crud.POST;
197
- const PUT = crud.PUT;
198
- const DELETE = crud.DELETE;
14
+ const metadata = route.metadata;
15
+ const openApi = route.openApi;
16
+ const GET = route.GET;
17
+ const POST = route.POST;
18
+ const PUT = route.PUT;
19
+ const DELETE = route.DELETE;
199
20
  export {
200
21
  DELETE,
201
22
  GET,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/sales/api/order-line-statuses/route.ts"],
4
- "sourcesContent": ["import { z } from 'zod'\nimport { makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { Dictionary, DictionaryEntry } from '@open-mercato/core/modules/dictionaries/data/entities'\nimport { E } from '#generated/entities.ids.generated'\nimport * as F from '#generated/entities/dictionary_entry'\nimport { statusDictionaryCreateSchema, statusDictionaryUpdateSchema } from '../../data/validators'\nimport { getSalesDictionaryDefinition, ensureSalesDictionary, type SalesDictionaryKind } from '../../lib/dictionaries'\nimport { parseScopedCommandInput, resolveCrudRecordId } from '../utils'\nimport {\n createPagedListResponseSchema,\n createSalesCrudOpenApi,\n defaultDeleteRequestSchema,\n} from '../openapi'\nimport { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'\n\nconst rawBodySchema = z.object({}).passthrough()\n\nconst listSchema = z\n .object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n search: z.string().optional(),\n sortField: z.string().optional(),\n sortDir: z.enum(['asc', 'desc']).optional(),\n })\n .passthrough()\n\nconst kind: SalesDictionaryKind = 'order-line-status'\nconst definition = getSalesDictionaryDefinition(kind)\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['sales.settings.manage'] },\n POST: { requireAuth: true, requireFeatures: ['sales.settings.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['sales.settings.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['sales.settings.manage'] },\n}\n\nconst dictionaryItemSchema = z.object({\n id: z.string().uuid(),\n value: z.string(),\n label: z.string().nullable(),\n color: z.string().nullable(),\n icon: z.string().nullable(),\n organizationId: z.string().uuid().nullable(),\n tenantId: z.string().uuid().nullable(),\n createdAt: z.string(),\n updatedAt: z.string(),\n})\n\nconst dictionaryListResponseSchema = createPagedListResponseSchema(dictionaryItemSchema)\n\nconst normalizeId = (value: unknown): string | null => {\n if (typeof value !== 'string') return null\n const trimmed = value.trim()\n return trimmed.length > 0 ? trimmed : null\n}\n\nasync function resolveDictionaryContext(ctx: any): Promise<{ dictionaryId: string; organizationId: string | null }> {\n if (!ctx.auth || !ctx.auth.tenantId) {\n throw new CrudHttpError(401, { error: 'Tenant context is required.' })\n }\n const em = ctx.container.resolve('em') as EntityManager\n const tenantId: string = ctx.auth.tenantId\n const candidateOrgIds = new Set<string>()\n const pushCandidate = (value: unknown) => {\n const normalized = normalizeId(value)\n if (normalized) candidateOrgIds.add(normalized)\n }\n pushCandidate(ctx.selectedOrganizationId)\n pushCandidate(ctx.auth.orgId ?? null)\n const scope = ctx.organizationScope\n if (scope) {\n if (Array.isArray(scope.filterIds)) {\n for (const id of scope.filterIds) pushCandidate(id)\n }\n if (Array.isArray(scope.allowedIds)) {\n for (const id of scope.allowedIds) pushCandidate(id)\n }\n }\n\n for (const orgId of candidateOrgIds) {\n const dictionary = await ensureSalesDictionary({\n em,\n tenantId,\n organizationId: orgId,\n kind,\n })\n if (dictionary) {\n return { dictionaryId: dictionary.id, organizationId: orgId }\n }\n }\n\n const fallback = await em.findOne(\n Dictionary,\n {\n tenantId,\n key: definition.key,\n deletedAt: null,\n },\n { orderBy: { createdAt: 'asc' } },\n )\n if (fallback) {\n return { dictionaryId: fallback.id, organizationId: fallback.organizationId }\n }\n throw new CrudHttpError(400, { error: 'Organization context is required.' })\n}\n\nconst crud = makeCrudRoute({\n metadata,\n orm: {\n entity: DictionaryEntry,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: null,\n },\n list: {\n schema: listSchema,\n entityId: E.dictionaries.dictionary_entry,\n fields: [\n F.id,\n F.value,\n F.label,\n F.color,\n F.icon,\n F.organization_id,\n F.tenant_id,\n F.created_at,\n F.updated_at,\n ],\n sortFieldMap: {\n id: F.id,\n value: F.value,\n label: F.label,\n createdAt: F.created_at,\n updatedAt: F.updated_at,\n },\n buildFilters: async (query, ctx) => {\n const { dictionaryId } = await resolveDictionaryContext(ctx)\n const filters: Record<string, unknown> = {\n dictionary_id: dictionaryId,\n }\n if (query.search && query.search.trim().length > 0) {\n const term = `%${escapeLikePattern(query.search.trim())}%`\n filters.$or = [\n { [F.value]: { $ilike: term } },\n { [F.label]: { $ilike: term } },\n ]\n }\n return filters\n },\n transformItem: (item: any) => ({\n id: item.id,\n value: item.value,\n label: item.label,\n color: item.color ?? null,\n icon: item.icon ?? null,\n organizationId: item.organization_id ?? null,\n tenantId: item.tenant_id ?? null,\n createdAt: item.created_at,\n updatedAt: item.updated_at,\n }),\n },\n actions: {\n create: {\n commandId: `${definition.commandPrefix}.create`,\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n return parseScopedCommandInput(statusDictionaryCreateSchema, raw ?? {}, ctx, translate)\n },\n response: ({ result }) => ({ id: result?.entryId ?? null }),\n status: 201,\n },\n update: {\n commandId: `${definition.commandPrefix}.update`,\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n return parseScopedCommandInput(statusDictionaryUpdateSchema, raw ?? {}, ctx, translate)\n },\n response: () => ({ ok: true }),\n },\n delete: {\n commandId: `${definition.commandPrefix}.delete`,\n schema: rawBodySchema,\n mapInput: async ({ parsed, ctx }) => {\n const { translate } = await resolveTranslations()\n const id = resolveCrudRecordId(parsed, ctx, translate)\n return { id }\n },\n response: () => ({ ok: true }),\n },\n },\n})\n\nexport const openApi = createSalesCrudOpenApi({\n resourceName: 'Order line status',\n pluralName: 'Order line statuses',\n description: 'Manage custom order line statuses available for sales documents.',\n querySchema: listSchema,\n listResponseSchema: dictionaryListResponseSchema,\n create: { schema: statusDictionaryCreateSchema },\n update: { schema: statusDictionaryUpdateSchema },\n del: { schema: defaultDeleteRequestSchema },\n})\n\nexport const GET = crud.GET\nexport const POST = crud.POST\nexport const PUT = crud.PUT\nexport const DELETE = crud.DELETE\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { E } from '#generated/entities.ids.generated'\nimport * as F from '#generated/entities/dictionary_entry'\nimport { makeStatusDictionaryRoute } from '../../lib/makeStatusDictionaryRoute'\n\nconst route = makeStatusDictionaryRoute({\n kind: 'order-line-status',\n entityId: E.dictionaries.dictionary_entry,\n fieldConstants: F,\n openApi: {\n resourceName: 'Order line status',\n pluralName: 'Order line statuses',\n description: 'Manage custom order line statuses available for sales documents.',\n },\n})\n\nexport const metadata = route.metadata\nexport const openApi = route.openApi\nexport const GET = route.GET\nexport const POST = route.POST\nexport const PUT = route.PUT\nexport const DELETE = route.DELETE\n"],
5
+ "mappings": "AAAA,SAAS,SAAS;AAClB,YAAY,OAAO;AACnB,SAAS,iCAAiC;AAE1C,MAAM,QAAQ,0BAA0B;AAAA,EACtC,MAAM;AAAA,EACN,UAAU,EAAE,aAAa;AAAA,EACzB,gBAAgB;AAAA,EAChB,SAAS;AAAA,IACP,cAAc;AAAA,IACd,YAAY;AAAA,IACZ,aAAa;AAAA,EACf;AACF,CAAC;AAEM,MAAM,WAAW,MAAM;AACvB,MAAM,UAAU,MAAM;AACtB,MAAM,MAAM,MAAM;AAClB,MAAM,OAAO,MAAM;AACnB,MAAM,MAAM,MAAM;AAClB,MAAM,SAAS,MAAM;",
6
6
  "names": []
7
7
  }