@open-mercato/core 0.9.1-develop.7477.1.64ba865101 → 0.9.1-develop.7497.1.543ea44c40

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 (84) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/business_rules/data/validators.js +8 -3
  3. package/dist/modules/business_rules/data/validators.js.map +2 -2
  4. package/dist/modules/catalog/api/dictionaries/[key]/route.js +35 -7
  5. package/dist/modules/catalog/api/dictionaries/[key]/route.js.map +2 -2
  6. package/dist/modules/communication_channels/lib/email-mime.js +1 -2
  7. package/dist/modules/communication_channels/lib/email-mime.js.map +2 -2
  8. package/dist/modules/configs/cli.js +8 -2
  9. package/dist/modules/configs/cli.js.map +2 -2
  10. package/dist/modules/customers/api/companies/[id]/people/route.js +3 -0
  11. package/dist/modules/customers/api/companies/[id]/people/route.js.map +2 -2
  12. package/dist/modules/customers/api/companies/route.js +3 -3
  13. package/dist/modules/customers/api/companies/route.js.map +2 -2
  14. package/dist/modules/customers/api/deals/[id]/companies/route.js +3 -0
  15. package/dist/modules/customers/api/deals/[id]/companies/route.js.map +2 -2
  16. package/dist/modules/customers/api/deals/[id]/people/route.js +3 -0
  17. package/dist/modules/customers/api/deals/[id]/people/route.js.map +2 -2
  18. package/dist/modules/customers/api/dictionaries/kind-settings/route.js +6 -0
  19. package/dist/modules/customers/api/dictionaries/kind-settings/route.js.map +2 -2
  20. package/dist/modules/customers/api/labels/route.js +10 -1
  21. package/dist/modules/customers/api/labels/route.js.map +2 -2
  22. package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js +6 -0
  23. package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js.map +2 -2
  24. package/dist/modules/customers/api/people/[id]/companies/enriched/route.js +3 -0
  25. package/dist/modules/customers/api/people/[id]/companies/enriched/route.js.map +2 -2
  26. package/dist/modules/customers/api/people/[id]/companies/route.js +6 -0
  27. package/dist/modules/customers/api/people/[id]/companies/route.js.map +2 -2
  28. package/dist/modules/customers/api/people/route.js +3 -3
  29. package/dist/modules/customers/api/people/route.js.map +2 -2
  30. package/dist/modules/customers/commands/deals.js +2 -0
  31. package/dist/modules/customers/commands/deals.js.map +2 -2
  32. package/dist/modules/customers/components/ActivityTypeEditor.js +11 -5
  33. package/dist/modules/customers/components/ActivityTypeEditor.js.map +2 -2
  34. package/dist/modules/customers/components/detail/InlineEditors.js +7 -3
  35. package/dist/modules/customers/components/detail/InlineEditors.js.map +2 -2
  36. package/dist/modules/customers/components/detail/PersonHighlights.js +1 -1
  37. package/dist/modules/customers/components/detail/PersonHighlights.js.map +2 -2
  38. package/dist/modules/customers/components/detail/PlannedActivitiesSection.js +4 -5
  39. package/dist/modules/customers/components/detail/PlannedActivitiesSection.js.map +2 -2
  40. package/dist/modules/data_sync/di.js.map +2 -2
  41. package/dist/modules/data_sync/lib/sync-schedule-service.js +60 -27
  42. package/dist/modules/data_sync/lib/sync-schedule-service.js.map +2 -2
  43. package/dist/modules/messages/commands/messages.js +10 -7
  44. package/dist/modules/messages/commands/messages.js.map +2 -2
  45. package/dist/modules/messages/lib/constants.js +3 -1
  46. package/dist/modules/messages/lib/constants.js.map +2 -2
  47. package/dist/modules/messages/subscribers/message-notification.js +2 -1
  48. package/dist/modules/messages/subscribers/message-notification.js.map +2 -2
  49. package/dist/modules/sales/api/documents/factory.js +3 -4
  50. package/dist/modules/sales/api/documents/factory.js.map +2 -2
  51. package/dist/modules/sales/commands/payments.js +9 -1
  52. package/dist/modules/sales/commands/payments.js.map +2 -2
  53. package/package.json +7 -7
  54. package/src/modules/business_rules/data/validators.ts +12 -3
  55. package/src/modules/catalog/api/dictionaries/[key]/route.ts +49 -7
  56. package/src/modules/communication_channels/lib/email-mime.ts +1 -2
  57. package/src/modules/configs/cli.ts +10 -1
  58. package/src/modules/customers/api/companies/[id]/people/route.ts +3 -0
  59. package/src/modules/customers/api/companies/route.ts +6 -6
  60. package/src/modules/customers/api/deals/[id]/companies/route.ts +3 -0
  61. package/src/modules/customers/api/deals/[id]/people/route.ts +3 -0
  62. package/src/modules/customers/api/dictionaries/kind-settings/route.ts +6 -0
  63. package/src/modules/customers/api/labels/route.ts +10 -1
  64. package/src/modules/customers/api/people/[id]/companies/[linkId]/route.ts +6 -0
  65. package/src/modules/customers/api/people/[id]/companies/enriched/route.ts +3 -0
  66. package/src/modules/customers/api/people/[id]/companies/route.ts +6 -0
  67. package/src/modules/customers/api/people/route.ts +6 -6
  68. package/src/modules/customers/commands/deals.ts +2 -0
  69. package/src/modules/customers/components/ActivityTypeEditor.tsx +11 -5
  70. package/src/modules/customers/components/detail/InlineEditors.tsx +8 -4
  71. package/src/modules/customers/components/detail/PersonHighlights.tsx +4 -1
  72. package/src/modules/customers/components/detail/PlannedActivitiesSection.tsx +4 -5
  73. package/src/modules/customers/i18n/de.json +2 -0
  74. package/src/modules/customers/i18n/en.json +2 -0
  75. package/src/modules/customers/i18n/es.json +2 -0
  76. package/src/modules/customers/i18n/ko.json +2 -0
  77. package/src/modules/customers/i18n/pl.json +2 -0
  78. package/src/modules/data_sync/di.ts +1 -0
  79. package/src/modules/data_sync/lib/sync-schedule-service.ts +80 -28
  80. package/src/modules/messages/commands/messages.ts +10 -7
  81. package/src/modules/messages/lib/constants.ts +6 -0
  82. package/src/modules/messages/subscribers/message-notification.ts +2 -0
  83. package/src/modules/sales/api/documents/factory.ts +6 -7
  84. package/src/modules/sales/commands/payments.ts +10 -2
@@ -18,7 +18,7 @@ import {
18
18
  resolveActorFeatures,
19
19
  resolveMessageChannelThreadAccess,
20
20
  } from '../lib/channelThreadAccess'
21
- import { MESSAGE_ATTACHMENT_ENTITY_ID, MESSAGE_ENTITY_ID } from '../lib/constants'
21
+ import { MESSAGE_ATTACHMENT_ENTITY_ID, MESSAGE_ENTITY_ID, SYSTEM_SENDER_USER_ID } from '../lib/constants'
22
22
  import { canUseChannelThreadFallback } from '../lib/routeHelpers'
23
23
  import { getMessageTypeOrDefault } from '../lib/message-types-registry'
24
24
  import { validateMessageObjectsForType } from '../lib/object-validation'
@@ -779,24 +779,25 @@ const replyMessageCommand: CommandHandler<unknown, { id: string; externalEmail:
779
779
  const messageType = getMessageTypeOrDefault(original.type)
780
780
  if (messageType.allowReply === false) throw new Error('Reply is not allowed for this message type')
781
781
 
782
+ const isReplyRecipient = (userId: string) => userId !== input.userId && userId !== SYSTEM_SENDER_USER_ID
782
783
  const recipientIds = new Set(
783
784
  (input.recipients ?? [])
784
785
  .map((recipient) => recipient.userId)
785
- .filter((recipientUserId) => recipientUserId !== input.userId),
786
+ .filter(isReplyRecipient),
786
787
  )
787
788
 
788
789
  if (recipientIds.size === 0) {
789
790
  const originalRecipients = await em.find(MessageRecipient, { messageId: original.id, deletedAt: null })
790
791
  if (input.replyAll) {
791
- if (original.senderUserId !== input.userId) recipientIds.add(original.senderUserId)
792
+ if (isReplyRecipient(original.senderUserId)) recipientIds.add(original.senderUserId)
792
793
  for (const recipient of originalRecipients) {
793
- if (recipient.recipientUserId !== input.userId) recipientIds.add(recipient.recipientUserId)
794
+ if (isReplyRecipient(recipient.recipientUserId)) recipientIds.add(recipient.recipientUserId)
794
795
  }
795
796
  } else if (original.senderUserId !== input.userId) {
796
- recipientIds.add(original.senderUserId)
797
+ if (isReplyRecipient(original.senderUserId)) recipientIds.add(original.senderUserId)
797
798
  } else {
798
799
  for (const recipient of originalRecipients) {
799
- if (recipient.recipientUserId !== input.userId) {
800
+ if (isReplyRecipient(recipient.recipientUserId)) {
800
801
  recipientIds.add(recipient.recipientUserId)
801
802
  break
802
803
  }
@@ -807,7 +808,9 @@ const replyMessageCommand: CommandHandler<unknown, { id: string; externalEmail:
807
808
  recipientIds.add(input.userId)
808
809
  }
809
810
  }
810
- if (recipientIds.size === 0) throw new Error('No recipients available for reply')
811
+ if (recipientIds.size === 0 && original.senderUserId !== SYSTEM_SENDER_USER_ID) {
812
+ throw new Error('No recipients available for reply')
813
+ }
811
814
 
812
815
  let messageId = ''
813
816
  let responseExternalEmail: string | null = null
@@ -5,3 +5,9 @@ export const MESSAGE_ATTACHMENT_PARTITION = 'messages'
5
5
  // Resource kind used by the command-level OSS optimistic-lock guard so stale
6
6
  // draft edits and message actions fail with the structured 409 conflict.
7
7
  export const MESSAGE_OPTIMISTIC_LOCK_RESOURCE_KIND = 'messages.message'
8
+
9
+ // Sender of messages composed on behalf of the platform rather than a person —
10
+ // a communication channel's inbound ingest, inbox_ops, warranty_claims. No
11
+ // `users` row exists for it, so it can never be a reply or notification
12
+ // recipient (#6391).
13
+ export const SYSTEM_SENDER_USER_ID = '00000000-0000-0000-0000-000000000000'
@@ -10,6 +10,7 @@ import {
10
10
  import { User } from '../../auth/data/entities'
11
11
  import { Message } from '../data/entities'
12
12
  import { notificationTypes } from '../notifications'
13
+ import { SYSTEM_SENDER_USER_ID } from '../lib/constants'
13
14
  import { resolveMessageChannelThreadAccess } from '../lib/channelThreadAccess'
14
15
  import {
15
16
  EXTERNAL_CONVERSATION_SOURCE_ENTITY_TYPE,
@@ -156,6 +157,7 @@ async function isChannelRoutedMessage(payload: MessageSentPayload, ctx: Resolver
156
157
 
157
158
  export default async function handle(payload: MessageSentPayload, ctx: ResolverContext): Promise<void> {
158
159
  const uniqueRecipientUserIds = Array.from(new Set(payload.recipientUserIds))
160
+ .filter((recipientUserId) => recipientUserId !== SYSTEM_SENDER_USER_ID)
159
161
 
160
162
  const typeDef = notificationTypes.find((type) => type.type === 'messages.new')
161
163
  if (typeDef && uniqueRecipientUserIds.length > 0) {
@@ -24,7 +24,7 @@ import { parseScopedCommandInput, resolveCrudRecordId } from '../utils'
24
24
  import { documentUpdateSchema } from '../../commands/documents'
25
25
  import { buildIlikeTerm } from '@open-mercato/shared/lib/db/buildIlikeTerm'
26
26
  import { parseBooleanToken } from '@open-mercato/shared/lib/boolean'
27
- import { parseIdsParam } from '@open-mercato/shared/lib/crud/ids'
27
+ import { isUuidListParam, parseIdsParam, splitIdListParam } from '@open-mercato/shared/lib/crud/ids'
28
28
  import type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'
29
29
  import { parseDecryptedFieldValue } from '@open-mercato/shared/lib/encryption/tenantDataEncryptionService'
30
30
 
@@ -111,7 +111,10 @@ const listSchema = z
111
111
  totalGrossMax: z.coerce.number().optional(),
112
112
  dateFrom: z.string().optional(),
113
113
  dateTo: z.string().optional(),
114
- tagIds: z.string().optional(),
114
+ tagIds: z
115
+ .string()
116
+ .optional()
117
+ .refine(isUuidListParam, { message: 'tagIds must be a comma-separated list of UUIDs' }),
115
118
  tagIdsEmpty: z.string().optional(),
116
119
  sortField: z.string().optional(),
117
120
  sortDir: z.enum(['asc', 'desc']).optional(),
@@ -180,11 +183,7 @@ function buildFilters(query: ListQuery, numberColumn: string, kind: DocumentKind
180
183
  if (Object.keys(dateRange).length) {
181
184
  filters.created_at = dateRange
182
185
  }
183
- const tagIdsRaw = typeof query.tagIds === 'string' ? query.tagIds : ''
184
- const tagIds = tagIdsRaw
185
- .split(',')
186
- .map((value) => value.trim())
187
- .filter((value) => value.length > 0)
186
+ const tagIds = splitIdListParam(query.tagIds)
188
187
  if (parseBooleanToken(query.tagIdsEmpty) === true) {
189
188
  filters.id = { $eq: '00000000-0000-0000-0000-000000000000' }
190
189
  } else if (tagIds.length) {
@@ -250,7 +250,14 @@ export async function restorePaymentSnapshot(em: EntityManager, snapshot: Paymen
250
250
  em.persist(entity)
251
251
  }
252
252
 
253
- async function recomputeOrderPaymentTotals(
253
+ const NON_SETTLING_PAYMENT_STATUSES = new Set(['failed', 'canceled', 'cancelled'])
254
+
255
+ export function paymentCountsTowardOrderBalance(payment: Pick<SalesPayment, 'status'>): boolean {
256
+ const status = typeof payment.status === 'string' ? payment.status.trim().toLowerCase() : ''
257
+ return !NON_SETTLING_PAYMENT_STATUSES.has(status)
258
+ }
259
+
260
+ export async function recomputeOrderPaymentTotals(
254
261
  em: EntityManager,
255
262
  order: SalesOrder,
256
263
  options?: { lock?: boolean }
@@ -282,10 +289,11 @@ async function recomputeOrderPaymentTotals(
282
289
  if (paymentId) paymentIds.add(paymentId)
283
290
  })
284
291
 
285
- const payments =
292
+ const loadedPayments =
286
293
  paymentIds.size > 0
287
294
  ? await findWithDecryption(em, SalesPayment, { id: { $in: Array.from(paymentIds) }, deletedAt: null, ...scope }, {}, scope)
288
295
  : await findWithDecryption(em, SalesPayment, { order: orderId, deletedAt: null, ...scope }, {}, scope)
296
+ const payments = loadedPayments.filter(paymentCountsTowardOrderBalance)
289
297
 
290
298
  const resolvePaidAmount = (payment: SalesPayment) => {
291
299
  const captured = toNumber(payment.capturedAmount)