@open-mercato/core 0.4.6-develop-a96241c478 → 0.4.6-develop-7722ab3d39

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 (116) hide show
  1. package/dist/modules/api_docs/frontend/docs/api/page.js +1 -1
  2. package/dist/modules/api_docs/frontend/docs/api/page.js.map +2 -2
  3. package/dist/modules/attachments/api/library/[id]/route.js +0 -1
  4. package/dist/modules/attachments/api/library/[id]/route.js.map +2 -2
  5. package/dist/modules/attachments/components/AttachmentLibrary.js +1 -1
  6. package/dist/modules/attachments/components/AttachmentLibrary.js.map +2 -2
  7. package/dist/modules/attachments/lib/partitionEnv.js +1 -1
  8. package/dist/modules/attachments/lib/partitionEnv.js.map +2 -2
  9. package/dist/modules/auth/backend/users/page.js +1 -1
  10. package/dist/modules/auth/backend/users/page.js.map +2 -2
  11. package/dist/modules/auth/cli.js +1 -1
  12. package/dist/modules/auth/cli.js.map +2 -2
  13. package/dist/modules/auth/commands/users.js +1 -1
  14. package/dist/modules/auth/commands/users.js.map +2 -2
  15. package/dist/modules/business_rules/components/utils/formHelpers.js +1 -1
  16. package/dist/modules/business_rules/components/utils/formHelpers.js.map +2 -2
  17. package/dist/modules/catalog/backend/catalog/products/create/page.js +1 -1
  18. package/dist/modules/catalog/backend/catalog/products/create/page.js.map +2 -2
  19. package/dist/modules/catalog/commands/products.js +1 -1
  20. package/dist/modules/catalog/commands/products.js.map +2 -2
  21. package/dist/modules/catalog/commands/shared.js +1 -1
  22. package/dist/modules/catalog/commands/shared.js.map +2 -2
  23. package/dist/modules/catalog/components/PriceKindSettings.js +1 -1
  24. package/dist/modules/catalog/components/PriceKindSettings.js.map +2 -2
  25. package/dist/modules/catalog/components/products/productForm.js +1 -1
  26. package/dist/modules/catalog/components/products/productForm.js.map +2 -2
  27. package/dist/modules/configs/lib/upgrade-actions.js.map +1 -1
  28. package/dist/modules/currencies/services/providers/raiffeisen.js +1 -1
  29. package/dist/modules/currencies/services/providers/raiffeisen.js.map +2 -2
  30. package/dist/modules/customers/cli.js +2 -2
  31. package/dist/modules/customers/cli.js.map +2 -2
  32. package/dist/modules/customers/lib/detailHelpers.js +1 -1
  33. package/dist/modules/customers/lib/detailHelpers.js.map +2 -2
  34. package/dist/modules/entities/cli.js +1 -1
  35. package/dist/modules/entities/cli.js.map +2 -2
  36. package/dist/modules/entities/lib/field-definitions.js +1 -1
  37. package/dist/modules/entities/lib/field-definitions.js.map +2 -2
  38. package/dist/modules/entities/lib/install-from-ce.js +1 -1
  39. package/dist/modules/entities/lib/install-from-ce.js.map +2 -2
  40. package/dist/modules/inbox_ops/lib/emailParser.js +1 -1
  41. package/dist/modules/inbox_ops/lib/emailParser.js.map +2 -2
  42. package/dist/modules/inbox_ops/widgets/notifications/ProposalCreatedRenderer.js +8 -0
  43. package/dist/modules/inbox_ops/widgets/notifications/ProposalCreatedRenderer.js.map +2 -2
  44. package/dist/modules/perspectives/services/perspectiveService.js +1 -1
  45. package/dist/modules/perspectives/services/perspectiveService.js.map +2 -2
  46. package/dist/modules/query_index/components/QueryIndexesTable.js +7 -7
  47. package/dist/modules/query_index/components/QueryIndexesTable.js.map +2 -2
  48. package/dist/modules/query_index/lib/engine.js +1 -1
  49. package/dist/modules/query_index/lib/engine.js.map +2 -2
  50. package/dist/modules/resources/commands/resources.js +1 -1
  51. package/dist/modules/resources/commands/resources.js.map +2 -2
  52. package/dist/modules/resources/lib/seeds.js +1 -1
  53. package/dist/modules/resources/lib/seeds.js.map +2 -2
  54. package/dist/modules/sales/api/dashboard/widgets/new-orders/route.js +1 -1
  55. package/dist/modules/sales/api/dashboard/widgets/new-orders/route.js.map +2 -2
  56. package/dist/modules/sales/api/dashboard/widgets/new-quotes/route.js +1 -1
  57. package/dist/modules/sales/api/dashboard/widgets/new-quotes/route.js.map +2 -2
  58. package/dist/modules/sales/commands/documents.js +2 -2
  59. package/dist/modules/sales/commands/documents.js.map +2 -2
  60. package/dist/modules/sales/components/documents/SalesDocumentsTable.js +1 -1
  61. package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
  62. package/dist/modules/sales/components/documents/ShipmentDialog.js +1 -1
  63. package/dist/modules/sales/components/documents/ShipmentDialog.js.map +2 -2
  64. package/dist/modules/sales/lib/shipments/snapshots.js.map +1 -1
  65. package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js +8 -0
  66. package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js.map +2 -2
  67. package/dist/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.js +8 -0
  68. package/dist/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.js.map +2 -2
  69. package/dist/modules/workflows/backend/instances/page.js +2 -2
  70. package/dist/modules/workflows/backend/instances/page.js.map +2 -2
  71. package/dist/modules/workflows/backend/tasks/page.js +1 -1
  72. package/dist/modules/workflows/backend/tasks/page.js.map +2 -2
  73. package/dist/modules/workflows/components/DefinitionTriggersEditor.js +1 -1
  74. package/dist/modules/workflows/components/DefinitionTriggersEditor.js.map +2 -2
  75. package/dist/modules/workflows/lib/graph-utils.js +1 -1
  76. package/dist/modules/workflows/lib/graph-utils.js.map +2 -2
  77. package/package.json +2 -2
  78. package/src/modules/api_docs/frontend/docs/api/page.tsx +1 -1
  79. package/src/modules/attachments/api/library/[id]/route.ts +0 -1
  80. package/src/modules/attachments/components/AttachmentLibrary.tsx +1 -1
  81. package/src/modules/attachments/lib/partitionEnv.ts +1 -1
  82. package/src/modules/auth/backend/users/page.tsx +1 -1
  83. package/src/modules/auth/cli.ts +1 -1
  84. package/src/modules/auth/commands/users.ts +1 -1
  85. package/src/modules/business_rules/components/utils/formHelpers.ts +1 -1
  86. package/src/modules/catalog/backend/catalog/products/create/page.tsx +1 -1
  87. package/src/modules/catalog/commands/products.ts +1 -1
  88. package/src/modules/catalog/commands/shared.ts +1 -1
  89. package/src/modules/catalog/components/PriceKindSettings.tsx +1 -1
  90. package/src/modules/catalog/components/products/productForm.ts +1 -1
  91. package/src/modules/configs/lib/upgrade-actions.ts +1 -1
  92. package/src/modules/currencies/services/providers/raiffeisen.ts +1 -1
  93. package/src/modules/customers/cli.ts +2 -2
  94. package/src/modules/customers/lib/detailHelpers.ts +1 -1
  95. package/src/modules/entities/cli.ts +1 -1
  96. package/src/modules/entities/lib/field-definitions.ts +1 -1
  97. package/src/modules/entities/lib/install-from-ce.ts +1 -1
  98. package/src/modules/inbox_ops/lib/emailParser.ts +1 -1
  99. package/src/modules/inbox_ops/widgets/notifications/ProposalCreatedRenderer.tsx +8 -0
  100. package/src/modules/perspectives/services/perspectiveService.ts +1 -1
  101. package/src/modules/query_index/components/QueryIndexesTable.tsx +7 -7
  102. package/src/modules/query_index/lib/engine.ts +1 -1
  103. package/src/modules/resources/commands/resources.ts +1 -1
  104. package/src/modules/resources/lib/seeds.ts +1 -1
  105. package/src/modules/sales/api/dashboard/widgets/new-orders/route.ts +1 -1
  106. package/src/modules/sales/api/dashboard/widgets/new-quotes/route.ts +1 -1
  107. package/src/modules/sales/commands/documents.ts +2 -2
  108. package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +1 -1
  109. package/src/modules/sales/components/documents/ShipmentDialog.tsx +1 -1
  110. package/src/modules/sales/lib/shipments/snapshots.ts +1 -1
  111. package/src/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.tsx +8 -0
  112. package/src/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.tsx +8 -0
  113. package/src/modules/workflows/backend/instances/page.tsx +2 -2
  114. package/src/modules/workflows/backend/tasks/page.tsx +1 -1
  115. package/src/modules/workflows/components/DefinitionTriggersEditor.tsx +1 -1
  116. package/src/modules/workflows/lib/graph-utils.ts +1 -1
@@ -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()\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 { 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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",
6
6
  "names": []
7
7
  }
@@ -2136,8 +2136,8 @@ function normalizeTagIds(tags) {
2136
2136
  function buildTagChange(beforeTags, afterTags) {
2137
2137
  const beforeIds = normalizeTagIds(beforeTags?.map((tag) => tag.tagId));
2138
2138
  const afterIds = normalizeTagIds(afterTags?.map((tag) => tag.tagId));
2139
- beforeIds.sort();
2140
- afterIds.sort();
2139
+ beforeIds.sort((a, b) => a.localeCompare(b));
2140
+ afterIds.sort((a, b) => a.localeCompare(b));
2141
2141
  if (beforeIds.length === afterIds.length && beforeIds.every((id, index) => id === afterIds[index])) {
2142
2142
  return null;
2143
2143
  }