@open-mercato/core 0.7.1-develop.7112.1.d486e6f1b2 → 0.7.1-develop.7120.1.a0154ac412

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 (79) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/helpers/integration/crmFixtures.js.map +1 -1
  3. package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js +9 -2
  4. package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js.map +2 -2
  5. package/dist/modules/communication_channels/commands/deliver-outbound-message.js +34 -2
  6. package/dist/modules/communication_channels/commands/deliver-outbound-message.js.map +2 -2
  7. package/dist/modules/communication_channels/lib/outbound-recipient.js +5 -1
  8. package/dist/modules/communication_channels/lib/outbound-recipient.js.map +2 -2
  9. package/dist/modules/communication_channels/lib/outbound-reply-ref.js +49 -0
  10. package/dist/modules/communication_channels/lib/outbound-reply-ref.js.map +7 -0
  11. package/dist/modules/communication_channels/lib/test-seed.js +5 -0
  12. package/dist/modules/communication_channels/lib/test-seed.js.map +2 -2
  13. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealClosure.js +1 -1
  14. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealClosure.js.map +1 -1
  15. package/dist/modules/customers/backend/customers/deals/page.js +4 -3
  16. package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
  17. package/dist/modules/customers/backend/customers/deals/pipeline/components/QuickDealDialog.js +2 -2
  18. package/dist/modules/customers/backend/customers/deals/pipeline/components/QuickDealDialog.js.map +2 -2
  19. package/dist/modules/customers/backend/customers/deals/pipeline/components/StatusFilterPopover.js +1 -1
  20. package/dist/modules/customers/backend/customers/deals/pipeline/components/StatusFilterPopover.js.map +1 -1
  21. package/dist/modules/customers/backend/customers/deals/pipeline/page.js +1 -1
  22. package/dist/modules/customers/backend/customers/deals/pipeline/page.js.map +2 -2
  23. package/dist/modules/customers/cli.js +5 -5
  24. package/dist/modules/customers/cli.js.map +2 -2
  25. package/dist/modules/customers/commands/deals.js.map +2 -2
  26. package/dist/modules/customers/components/detail/pipelineStageUtils.js +1 -0
  27. package/dist/modules/customers/components/detail/pipelineStageUtils.js.map +2 -2
  28. package/dist/modules/customers/components/linking/adapters/dealAdapter.js +2 -2
  29. package/dist/modules/customers/components/linking/adapters/dealAdapter.js.map +2 -2
  30. package/dist/modules/customers/lib/closureStage.js.map +2 -2
  31. package/dist/modules/customers/lib/dealStatus.js +6 -4
  32. package/dist/modules/customers/lib/dealStatus.js.map +2 -2
  33. package/dist/modules/customers/lib/dealsSummaryQueries.js +3 -3
  34. package/dist/modules/customers/lib/dealsSummaryQueries.js.map +1 -1
  35. package/dist/modules/customers/migrations/Migration20260824180000_deal_status_lost.js +30 -0
  36. package/dist/modules/customers/migrations/Migration20260824180000_deal_status_lost.js.map +7 -0
  37. package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/config.js.map +1 -1
  38. package/dist/modules/eudr/notifications.js +5 -0
  39. package/dist/modules/eudr/notifications.js.map +2 -2
  40. package/dist/modules/query_index/lib/search-tokens.js +59 -28
  41. package/dist/modules/query_index/lib/search-tokens.js.map +2 -2
  42. package/dist/modules/sales/commands/documents.js +120 -37
  43. package/dist/modules/sales/commands/documents.js.map +2 -2
  44. package/dist/modules/warranty_claims/notifications.js +5 -0
  45. package/dist/modules/warranty_claims/notifications.js.map +2 -2
  46. package/dist/modules/wms/notifications.js +2 -0
  47. package/dist/modules/wms/notifications.js.map +2 -2
  48. package/package.json +7 -7
  49. package/src/helpers/integration/crmFixtures.ts +1 -1
  50. package/src/modules/communication_channels/api/post/channels/[id]/test-send/route.ts +21 -9
  51. package/src/modules/communication_channels/commands/deliver-outbound-message.ts +45 -1
  52. package/src/modules/communication_channels/lib/adapter.ts +11 -0
  53. package/src/modules/communication_channels/lib/outbound-recipient.ts +14 -1
  54. package/src/modules/communication_channels/lib/outbound-reply-ref.ts +138 -0
  55. package/src/modules/communication_channels/lib/test-seed.ts +19 -0
  56. package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealClosure.ts +1 -1
  57. package/src/modules/customers/backend/customers/deals/page.tsx +4 -3
  58. package/src/modules/customers/backend/customers/deals/pipeline/components/QuickDealDialog.tsx +5 -5
  59. package/src/modules/customers/backend/customers/deals/pipeline/components/StatusFilterPopover.tsx +3 -3
  60. package/src/modules/customers/backend/customers/deals/pipeline/page.tsx +2 -2
  61. package/src/modules/customers/cli.ts +5 -5
  62. package/src/modules/customers/commands/deals.ts +4 -3
  63. package/src/modules/customers/components/detail/pipelineStageUtils.ts +1 -0
  64. package/src/modules/customers/components/linking/adapters/dealAdapter.tsx +2 -2
  65. package/src/modules/customers/i18n/de.json +1 -1
  66. package/src/modules/customers/i18n/en.json +1 -1
  67. package/src/modules/customers/i18n/es.json +1 -1
  68. package/src/modules/customers/i18n/ko.json +1 -1
  69. package/src/modules/customers/i18n/pl.json +1 -1
  70. package/src/modules/customers/lib/closureStage.ts +2 -1
  71. package/src/modules/customers/lib/dealStatus.ts +23 -12
  72. package/src/modules/customers/lib/dealsSummaryQueries.ts +3 -3
  73. package/src/modules/customers/migrations/Migration20260824180000_deal_status_lost.ts +55 -0
  74. package/src/modules/dashboards/widgets/dashboard/pipeline-summary/config.ts +1 -1
  75. package/src/modules/eudr/notifications.ts +5 -0
  76. package/src/modules/query_index/lib/search-tokens.ts +100 -43
  77. package/src/modules/sales/commands/documents.ts +153 -39
  78. package/src/modules/warranty_claims/notifications.ts +5 -0
  79. package/src/modules/wms/notifications.ts +2 -0
@@ -1,6 +1,7 @@
1
1
  // @ts-nocheck
2
2
 
3
3
  import { randomUUID } from "crypto";
4
+ import { isDeepStrictEqual } from "node:util";
4
5
  import { z } from "zod";
5
6
  import { registerCommand } from "@open-mercato/shared/lib/commands";
6
7
  import type { CommandHandler } from "@open-mercato/shared/lib/commands";
@@ -9197,6 +9198,88 @@ const createInvoiceCommand: CommandHandler<
9197
9198
  },
9198
9199
  };
9199
9200
 
9201
+ const invoiceUpdateChangeKeys = [
9202
+ "invoiceNumber",
9203
+ "statusEntryId",
9204
+ "status",
9205
+ "issueDate",
9206
+ "dueDate",
9207
+ "currencyCode",
9208
+ "subtotalNetAmount",
9209
+ "subtotalGrossAmount",
9210
+ "discountTotalAmount",
9211
+ "taxTotalAmount",
9212
+ "grandTotalNetAmount",
9213
+ "grandTotalGrossAmount",
9214
+ "paidTotalAmount",
9215
+ "outstandingAmount",
9216
+ "metadata",
9217
+ ] as const;
9218
+
9219
+ function toAuditISOString(value: unknown): string | null {
9220
+ if (value instanceof Date) {
9221
+ return Number.isNaN(value.getTime()) ? null : value.toISOString();
9222
+ }
9223
+ if (typeof value === "string") {
9224
+ const parsed = new Date(value);
9225
+ return Number.isNaN(parsed.getTime()) ? null : parsed.toISOString();
9226
+ }
9227
+ return null;
9228
+ }
9229
+
9230
+ function auditValuesEqual(left: unknown, right: unknown): boolean {
9231
+ if (Object.is(left, right)) return true;
9232
+ if (left instanceof Date || right instanceof Date) {
9233
+ const leftIso = toAuditISOString(left);
9234
+ const rightIso = toAuditISOString(right);
9235
+ return leftIso !== null && rightIso !== null && leftIso === rightIso;
9236
+ }
9237
+ return isDeepStrictEqual(left, right);
9238
+ }
9239
+
9240
+ function buildAuditChanges(
9241
+ before: Record<string, unknown>,
9242
+ after: Record<string, unknown>,
9243
+ keys: readonly string[],
9244
+ ): Record<string, { from: unknown; to: unknown }> {
9245
+ const changedKeys = keys.filter((key) => !auditValuesEqual(before[key], after[key]));
9246
+ return buildChanges(before, after, changedKeys);
9247
+ }
9248
+
9249
+ function selectInvoiceUpdateChangeKeys(
9250
+ input: z.infer<typeof invoiceUpdateSchema>,
9251
+ ): Array<(typeof invoiceUpdateChangeKeys)[number]> {
9252
+ return invoiceUpdateChangeKeys.filter((key) =>
9253
+ key === "status"
9254
+ ? input.statusEntryId !== undefined
9255
+ : input[key as keyof z.infer<typeof invoiceUpdateSchema>] !== undefined,
9256
+ );
9257
+ }
9258
+
9259
+ function applyInvoiceHeaderUpdate(
9260
+ invoice: SalesInvoice,
9261
+ input: z.infer<typeof invoiceUpdateSchema>,
9262
+ resolvedStatus: string | null | undefined,
9263
+ ): void {
9264
+ if (input.invoiceNumber !== undefined) invoice.invoiceNumber = input.invoiceNumber;
9265
+ if (input.statusEntryId !== undefined) {
9266
+ invoice.statusEntryId = input.statusEntryId;
9267
+ invoice.status = resolvedStatus ?? null;
9268
+ }
9269
+ if (input.issueDate !== undefined) invoice.issueDate = input.issueDate;
9270
+ if (input.dueDate !== undefined) invoice.dueDate = input.dueDate;
9271
+ if (input.currencyCode !== undefined) invoice.currencyCode = input.currencyCode;
9272
+ if (input.subtotalNetAmount !== undefined) invoice.subtotalNetAmount = toNumericString(input.subtotalNetAmount);
9273
+ if (input.subtotalGrossAmount !== undefined) invoice.subtotalGrossAmount = toNumericString(input.subtotalGrossAmount);
9274
+ if (input.discountTotalAmount !== undefined) invoice.discountTotalAmount = toNumericString(input.discountTotalAmount);
9275
+ if (input.taxTotalAmount !== undefined) invoice.taxTotalAmount = toNumericString(input.taxTotalAmount);
9276
+ if (input.grandTotalNetAmount !== undefined) invoice.grandTotalNetAmount = toNumericString(input.grandTotalNetAmount);
9277
+ if (input.grandTotalGrossAmount !== undefined) invoice.grandTotalGrossAmount = toNumericString(input.grandTotalGrossAmount);
9278
+ if (input.paidTotalAmount !== undefined) invoice.paidTotalAmount = toNumericString(input.paidTotalAmount);
9279
+ if (input.outstandingAmount !== undefined) invoice.outstandingAmount = toNumericString(input.outstandingAmount);
9280
+ if (input.metadata !== undefined) invoice.metadata = input.metadata;
9281
+ }
9282
+
9200
9283
  const updateInvoiceCommand: CommandHandler<
9201
9284
  z.infer<typeof invoiceUpdateSchema>,
9202
9285
  { invoiceId: string }
@@ -9225,29 +9308,12 @@ const updateInvoiceCommand: CommandHandler<
9225
9308
  deletedAt: null,
9226
9309
  });
9227
9310
 
9228
- const changes = buildChanges(invoice, parsed, [
9229
- "invoiceNumber",
9230
- "statusEntryId",
9231
- "status",
9232
- "issueDate",
9233
- "dueDate",
9234
- "currencyCode",
9235
- "subtotalNetAmount",
9236
- "subtotalGrossAmount",
9237
- "discountTotalAmount",
9238
- "taxTotalAmount",
9239
- "grandTotalNetAmount",
9240
- "grandTotalGrossAmount",
9241
- "paidTotalAmount",
9242
- "outstandingAmount",
9243
- "metadata",
9244
- ]);
9245
-
9311
+ let resolvedStatus: string | null | undefined;
9246
9312
  if (parsed.statusEntryId !== undefined) {
9247
- invoice.status = await resolveDictionaryEntryValue(em, parsed.statusEntryId ?? null, { tenantId: invoice.tenantId });
9313
+ resolvedStatus = await resolveDictionaryEntryValue(em, parsed.statusEntryId ?? null, { tenantId: invoice.tenantId });
9248
9314
  }
9249
9315
 
9250
- Object.assign(invoice, changes);
9316
+ applyInvoiceHeaderUpdate(invoice, parsed, resolvedStatus);
9251
9317
  invoice.updatedAt = new Date();
9252
9318
  await em.flush();
9253
9319
 
@@ -9283,11 +9349,18 @@ const updateInvoiceCommand: CommandHandler<
9283
9349
  const em = (ctx.container.resolve("em") as EntityManager).fork();
9284
9350
  return loadInvoiceSnapshot(em, result.invoiceId);
9285
9351
  },
9286
- buildLog: async ({ result, snapshots }) => {
9352
+ buildLog: async ({ input, result, snapshots }) => {
9287
9353
  const before = snapshots.before as InvoiceGraphSnapshot | undefined;
9288
9354
  const after = snapshots.after as InvoiceGraphSnapshot | undefined;
9289
9355
  if (!after) return null;
9290
9356
  const { translate } = await resolveTranslations();
9357
+ const changes = before
9358
+ ? buildAuditChanges(
9359
+ before.invoice as unknown as Record<string, unknown>,
9360
+ after.invoice as unknown as Record<string, unknown>,
9361
+ selectInvoiceUpdateChangeKeys(input),
9362
+ )
9363
+ : {};
9291
9364
  return {
9292
9365
  actionLabel: translate("sales.audit.invoices.update", "Update invoice"),
9293
9366
  resourceKind: "sales.invoice",
@@ -9296,6 +9369,7 @@ const updateInvoiceCommand: CommandHandler<
9296
9369
  organizationId: after.invoice.organizationId,
9297
9370
  snapshotBefore: before,
9298
9371
  snapshotAfter: after,
9372
+ changes: Object.keys(changes).length ? changes : null,
9299
9373
  payload: {
9300
9374
  undo: { before, after } satisfies InvoiceUndoPayload,
9301
9375
  },
@@ -9704,6 +9778,52 @@ const createCreditMemoCommand: CommandHandler<
9704
9778
  },
9705
9779
  };
9706
9780
 
9781
+ const creditMemoUpdateChangeKeys = [
9782
+ "creditMemoNumber",
9783
+ "statusEntryId",
9784
+ "status",
9785
+ "reason",
9786
+ "issueDate",
9787
+ "currencyCode",
9788
+ "subtotalNetAmount",
9789
+ "subtotalGrossAmount",
9790
+ "taxTotalAmount",
9791
+ "grandTotalNetAmount",
9792
+ "grandTotalGrossAmount",
9793
+ "metadata",
9794
+ ] as const;
9795
+
9796
+ function selectCreditMemoUpdateChangeKeys(
9797
+ input: z.infer<typeof creditMemoUpdateSchema>,
9798
+ ): Array<(typeof creditMemoUpdateChangeKeys)[number]> {
9799
+ return creditMemoUpdateChangeKeys.filter((key) =>
9800
+ key === "status"
9801
+ ? input.statusEntryId !== undefined
9802
+ : input[key as keyof z.infer<typeof creditMemoUpdateSchema>] !== undefined,
9803
+ );
9804
+ }
9805
+
9806
+ function applyCreditMemoHeaderUpdate(
9807
+ creditMemo: SalesCreditMemo,
9808
+ input: z.infer<typeof creditMemoUpdateSchema>,
9809
+ resolvedStatus: string | null | undefined,
9810
+ ): void {
9811
+ if (input.creditMemoNumber !== undefined) creditMemo.creditMemoNumber = input.creditMemoNumber;
9812
+ if (input.statusEntryId !== undefined) {
9813
+ creditMemo.statusEntryId = input.statusEntryId;
9814
+ creditMemo.status = resolvedStatus ?? null;
9815
+ }
9816
+ if (input.reason !== undefined) creditMemo.reason = input.reason;
9817
+ if (input.issueDate !== undefined) creditMemo.issueDate = input.issueDate;
9818
+ if (input.currencyCode !== undefined) creditMemo.currencyCode = input.currencyCode;
9819
+ if (input.subtotalNetAmount !== undefined) creditMemo.subtotalNetAmount = toNumericString(input.subtotalNetAmount);
9820
+ if (input.subtotalGrossAmount !== undefined) creditMemo.subtotalGrossAmount = toNumericString(input.subtotalGrossAmount);
9821
+ if (input.taxTotalAmount !== undefined) creditMemo.taxTotalAmount = toNumericString(input.taxTotalAmount);
9822
+ if (input.grandTotalNetAmount !== undefined) creditMemo.grandTotalNetAmount = toNumericString(input.grandTotalNetAmount);
9823
+ if (input.grandTotalGrossAmount !== undefined) creditMemo.grandTotalGrossAmount = toNumericString(input.grandTotalGrossAmount);
9824
+ if (input.metadata !== undefined) creditMemo.metadata = input.metadata;
9825
+ }
9826
+
9707
9827
  const updateCreditMemoCommand: CommandHandler<
9708
9828
  z.infer<typeof creditMemoUpdateSchema>,
9709
9829
  { creditMemoId: string }
@@ -9732,26 +9852,12 @@ const updateCreditMemoCommand: CommandHandler<
9732
9852
  deletedAt: null,
9733
9853
  });
9734
9854
 
9735
- const changes = buildChanges(creditMemo, parsed, [
9736
- "creditMemoNumber",
9737
- "statusEntryId",
9738
- "status",
9739
- "reason",
9740
- "issueDate",
9741
- "currencyCode",
9742
- "subtotalNetAmount",
9743
- "subtotalGrossAmount",
9744
- "taxTotalAmount",
9745
- "grandTotalNetAmount",
9746
- "grandTotalGrossAmount",
9747
- "metadata",
9748
- ]);
9749
-
9855
+ let resolvedStatus: string | null | undefined;
9750
9856
  if (parsed.statusEntryId !== undefined) {
9751
- creditMemo.status = await resolveDictionaryEntryValue(em, parsed.statusEntryId ?? null, { tenantId: creditMemo.tenantId });
9857
+ resolvedStatus = await resolveDictionaryEntryValue(em, parsed.statusEntryId ?? null, { tenantId: creditMemo.tenantId });
9752
9858
  }
9753
9859
 
9754
- Object.assign(creditMemo, changes);
9860
+ applyCreditMemoHeaderUpdate(creditMemo, parsed, resolvedStatus);
9755
9861
  creditMemo.updatedAt = new Date();
9756
9862
  await em.flush();
9757
9863
 
@@ -9787,11 +9893,18 @@ const updateCreditMemoCommand: CommandHandler<
9787
9893
  const em = (ctx.container.resolve("em") as EntityManager).fork();
9788
9894
  return loadCreditMemoSnapshot(em, result.creditMemoId);
9789
9895
  },
9790
- buildLog: async ({ result, snapshots }) => {
9896
+ buildLog: async ({ input, result, snapshots }) => {
9791
9897
  const before = snapshots.before as CreditMemoGraphSnapshot | undefined;
9792
9898
  const after = snapshots.after as CreditMemoGraphSnapshot | undefined;
9793
9899
  if (!after) return null;
9794
9900
  const { translate } = await resolveTranslations();
9901
+ const changes = before
9902
+ ? buildAuditChanges(
9903
+ before.creditMemo as unknown as Record<string, unknown>,
9904
+ after.creditMemo as unknown as Record<string, unknown>,
9905
+ selectCreditMemoUpdateChangeKeys(input),
9906
+ )
9907
+ : {};
9795
9908
  return {
9796
9909
  actionLabel: translate("sales.audit.credit_memos.update", "Update credit memo"),
9797
9910
  resourceKind: "sales.credit_memo",
@@ -9800,6 +9913,7 @@ const updateCreditMemoCommand: CommandHandler<
9800
9913
  organizationId: after.creditMemo.organizationId,
9801
9914
  snapshotBefore: before,
9802
9915
  snapshotAfter: after,
9916
+ changes: Object.keys(changes).length ? changes : null,
9803
9917
  payload: {
9804
9918
  undo: { before, after } satisfies CreditMemoUndoPayload,
9805
9919
  },
@@ -4,6 +4,7 @@ export const notificationTypes: NotificationTypeDefinition[] = [
4
4
  {
5
5
  type: 'warranty_claims.claim.submitted',
6
6
  module: 'warranty_claims',
7
+ channels: ['in_app', 'email'],
7
8
  titleKey: 'warranty_claims.notifications.submitted.title',
8
9
  bodyKey: 'warranty_claims.notifications.submitted.body',
9
10
  icon: 'clipboard-check',
@@ -23,6 +24,7 @@ export const notificationTypes: NotificationTypeDefinition[] = [
23
24
  {
24
25
  type: 'warranty_claims.claim.assigned',
25
26
  module: 'warranty_claims',
27
+ channels: ['in_app', 'email'],
26
28
  titleKey: 'warranty_claims.notifications.assigned.title',
27
29
  bodyKey: 'warranty_claims.notifications.assigned.body',
28
30
  icon: 'user-check',
@@ -42,6 +44,7 @@ export const notificationTypes: NotificationTypeDefinition[] = [
42
44
  {
43
45
  type: 'warranty_claims.claim.status_changed',
44
46
  module: 'warranty_claims',
47
+ channels: ['in_app', 'email'],
45
48
  titleKey: 'warranty_claims.notifications.statusChanged.title',
46
49
  bodyKey: 'warranty_claims.notifications.statusChanged.body',
47
50
  icon: 'refresh-cw',
@@ -61,6 +64,7 @@ export const notificationTypes: NotificationTypeDefinition[] = [
61
64
  {
62
65
  type: 'warranty_claims.claim.escalated',
63
66
  module: 'warranty_claims',
67
+ channels: ['in_app', 'email'],
64
68
  titleKey: 'warranty_claims.notifications.escalated.title',
65
69
  bodyKey: 'warranty_claims.notifications.escalated.body',
66
70
  icon: 'alarm-clock',
@@ -80,6 +84,7 @@ export const notificationTypes: NotificationTypeDefinition[] = [
80
84
  {
81
85
  type: 'warranty_claims.claim.customer_replied',
82
86
  module: 'warranty_claims',
87
+ channels: ['in_app', 'email'],
83
88
  titleKey: 'warranty_claims.notifications.customerReplied.title',
84
89
  bodyKey: 'warranty_claims.notifications.customerReplied.body',
85
90
  icon: 'message-square-reply',
@@ -4,6 +4,7 @@ export const notificationTypes: NotificationTypeDefinition[] = [
4
4
  {
5
5
  type: 'wms.inventory.low_stock',
6
6
  module: 'wms',
7
+ channels: ['in_app', 'email'],
7
8
  titleKey: 'wms.notifications.lowStock.title',
8
9
  bodyKey: 'wms.notifications.lowStock.body',
9
10
  icon: 'alert-triangle',
@@ -23,6 +24,7 @@ export const notificationTypes: NotificationTypeDefinition[] = [
23
24
  {
24
25
  type: 'wms.inventory.reservation_shortfall',
25
26
  module: 'wms',
27
+ channels: ['in_app', 'email'],
26
28
  titleKey: 'wms.notifications.reservationShortfall.title',
27
29
  bodyKey: 'wms.notifications.reservationShortfall.body',
28
30
  icon: 'alert-triangle',