@open-mercato/core 0.9.0 → 0.9.1-develop.7459.1.99e3accd30

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 (243) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/generated/entities/customer_dictionary_entry/index.js +2 -0
  3. package/dist/generated/entities/customer_dictionary_entry/index.js.map +2 -2
  4. package/dist/generated/entities/customer_interaction/index.js +2 -0
  5. package/dist/generated/entities/customer_interaction/index.js.map +2 -2
  6. package/dist/generated/entity-fields-registry.js +2 -0
  7. package/dist/generated/entity-fields-registry.js.map +2 -2
  8. package/dist/modules/catalog/commands/categories.js +1 -1
  9. package/dist/modules/catalog/commands/categories.js.map +2 -2
  10. package/dist/modules/catalog/commands/productDeleteChildren.js +389 -0
  11. package/dist/modules/catalog/commands/productDeleteChildren.js.map +7 -0
  12. package/dist/modules/catalog/commands/products.js +52 -17
  13. package/dist/modules/catalog/commands/products.js.map +2 -2
  14. package/dist/modules/communication_channels/api/post/channels/connect/credentials/route.js +4 -6
  15. package/dist/modules/communication_channels/api/post/channels/connect/credentials/route.js.map +2 -2
  16. package/dist/modules/communication_channels/api/post/channels/connect/tenant-credentials/route.js +4 -6
  17. package/dist/modules/communication_channels/api/post/channels/connect/tenant-credentials/route.js.map +2 -2
  18. package/dist/modules/communication_channels/api/post/oauth/[provider]/initiate/route.js +47 -11
  19. package/dist/modules/communication_channels/api/post/oauth/[provider]/initiate/route.js.map +3 -3
  20. package/dist/modules/communication_channels/commands/deliver-outbound-message.js +5 -1
  21. package/dist/modules/communication_channels/commands/deliver-outbound-message.js.map +2 -2
  22. package/dist/modules/communication_channels/lib/inbound-message-origin.js +1 -1
  23. package/dist/modules/communication_channels/lib/inbound-message-origin.js.map +2 -2
  24. package/dist/modules/communication_channels/lib/oauth-state.js +4 -1
  25. package/dist/modules/communication_channels/lib/oauth-state.js.map +2 -2
  26. package/dist/modules/communication_channels/lib/use-connect-channel.js +10 -0
  27. package/dist/modules/communication_channels/lib/use-connect-channel.js.map +2 -2
  28. package/dist/modules/customers/api/activity-types/route.js +151 -0
  29. package/dist/modules/customers/api/activity-types/route.js.map +7 -0
  30. package/dist/modules/customers/api/deals/[id]/people/route.js +58 -5
  31. package/dist/modules/customers/api/deals/[id]/people/route.js.map +2 -2
  32. package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js +13 -5
  33. package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js.map +2 -2
  34. package/dist/modules/customers/api/dictionaries/[kind]/route.js +20 -5
  35. package/dist/modules/customers/api/dictionaries/[kind]/route.js.map +2 -2
  36. package/dist/modules/customers/api/interactions/route.js +3 -0
  37. package/dist/modules/customers/api/interactions/route.js.map +2 -2
  38. package/dist/modules/customers/api/interactions/tasks/route.js +1 -0
  39. package/dist/modules/customers/api/interactions/tasks/route.js.map +2 -2
  40. package/dist/modules/customers/backend/config/customers/pipeline-stages/page.meta.js +18 -0
  41. package/dist/modules/customers/backend/config/customers/pipeline-stages/page.meta.js.map +2 -2
  42. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.js +0 -23
  43. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.js.map +2 -2
  44. package/dist/modules/customers/backend/customers/deals/[id]/page.js +15 -17
  45. package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
  46. package/dist/modules/customers/calendar-event-types.js +548 -0
  47. package/dist/modules/customers/calendar-event-types.js.map +7 -0
  48. package/dist/modules/customers/commands/dictionaries.js +70 -10
  49. package/dist/modules/customers/commands/dictionaries.js.map +2 -2
  50. package/dist/modules/customers/commands/interactions.js +187 -22
  51. package/dist/modules/customers/commands/interactions.js.map +3 -3
  52. package/dist/modules/customers/components/ActivityTypeEditor.js +288 -0
  53. package/dist/modules/customers/components/ActivityTypeEditor.js.map +7 -0
  54. package/dist/modules/customers/components/DictionarySettings.js +3 -1
  55. package/dist/modules/customers/components/DictionarySettings.js.map +2 -2
  56. package/dist/modules/customers/components/calendar/CalendarEventEditor.js +391 -297
  57. package/dist/modules/customers/components/calendar/CalendarEventEditor.js.map +3 -3
  58. package/dist/modules/customers/components/calendar/CalendarSettingsModal.js +9 -74
  59. package/dist/modules/customers/components/calendar/CalendarSettingsModal.js.map +2 -2
  60. package/dist/modules/customers/components/calendar/TimeGrid.js +20 -16
  61. package/dist/modules/customers/components/calendar/TimeGrid.js.map +2 -2
  62. package/dist/modules/customers/components/calendar/editor/EventTypePanel.js +218 -0
  63. package/dist/modules/customers/components/calendar/editor/EventTypePanel.js.map +7 -0
  64. package/dist/modules/customers/components/calendar/editor/LocationField.js +7 -3
  65. package/dist/modules/customers/components/calendar/editor/LocationField.js.map +2 -2
  66. package/dist/modules/customers/components/calendar/editor/PeopleField.js +5 -2
  67. package/dist/modules/customers/components/calendar/editor/PeopleField.js.map +2 -2
  68. package/dist/modules/customers/components/calendar/editor/PriorityField.js +3 -1
  69. package/dist/modules/customers/components/calendar/editor/PriorityField.js.map +2 -2
  70. package/dist/modules/customers/components/calendar/editor/RepeatField.js +9 -1
  71. package/dist/modules/customers/components/calendar/editor/RepeatField.js.map +2 -2
  72. package/dist/modules/customers/components/calendar/editor/ResourcesField.js +4 -1
  73. package/dist/modules/customers/components/calendar/editor/ResourcesField.js.map +2 -2
  74. package/dist/modules/customers/components/calendar/editor/ScheduleSection.js +15 -7
  75. package/dist/modules/customers/components/calendar/editor/ScheduleSection.js.map +2 -2
  76. package/dist/modules/customers/components/calendar/editor/SegmentGroup.js +3 -1
  77. package/dist/modules/customers/components/calendar/editor/SegmentGroup.js.map +2 -2
  78. package/dist/modules/customers/components/calendar/editor/hooks.js +28 -13
  79. package/dist/modules/customers/components/calendar/editor/hooks.js.map +3 -3
  80. package/dist/modules/customers/components/calendar/editor/inputs.js +14 -6
  81. package/dist/modules/customers/components/calendar/editor/inputs.js.map +2 -2
  82. package/dist/modules/customers/components/calendar/editor/useCalendarCustomFields.js +54 -0
  83. package/dist/modules/customers/components/calendar/editor/useCalendarCustomFields.js.map +7 -0
  84. package/dist/modules/customers/components/calendar/editor/useEventTypeCatalog.js +82 -0
  85. package/dist/modules/customers/components/calendar/editor/useEventTypeCatalog.js.map +7 -0
  86. package/dist/modules/customers/components/calendar/types.js +1 -0
  87. package/dist/modules/customers/components/calendar/types.js.map +2 -2
  88. package/dist/modules/customers/components/detail/CompanyPeopleSection.js +78 -343
  89. package/dist/modules/customers/components/detail/CompanyPeopleSection.js.map +2 -2
  90. package/dist/modules/customers/components/detail/CreatePersonDialog.js +12 -11
  91. package/dist/modules/customers/components/detail/CreatePersonDialog.js.map +2 -2
  92. package/dist/modules/customers/components/detail/DealForm.js +7 -3
  93. package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
  94. package/dist/modules/customers/components/detail/DealPeopleSection.js +206 -0
  95. package/dist/modules/customers/components/detail/DealPeopleSection.js.map +7 -0
  96. package/dist/modules/customers/components/detail/LinkedPeopleSection.js +342 -0
  97. package/dist/modules/customers/components/detail/LinkedPeopleSection.js.map +7 -0
  98. package/dist/modules/customers/components/detail/PersonCard.js +5 -2
  99. package/dist/modules/customers/components/detail/PersonCard.js.map +2 -2
  100. package/dist/modules/customers/components/linking/adapters/personAdapter.js +3 -0
  101. package/dist/modules/customers/components/linking/adapters/personAdapter.js.map +2 -2
  102. package/dist/modules/customers/data/entities.js +6 -0
  103. package/dist/modules/customers/data/entities.js.map +2 -2
  104. package/dist/modules/customers/data/validators.js +39 -10
  105. package/dist/modules/customers/data/validators.js.map +2 -2
  106. package/dist/modules/customers/di.js +20 -1
  107. package/dist/modules/customers/di.js.map +2 -2
  108. package/dist/modules/customers/extension-points.js +13 -0
  109. package/dist/modules/customers/extension-points.js.map +2 -2
  110. package/dist/modules/customers/lib/calendar/conflicts.js +59 -16
  111. package/dist/modules/customers/lib/calendar/conflicts.js.map +2 -2
  112. package/dist/modules/customers/lib/calendar/editorPayload.js +34 -16
  113. package/dist/modules/customers/lib/calendar/editorPayload.js.map +2 -2
  114. package/dist/modules/customers/lib/calendar/eventTypeResolver.js +197 -0
  115. package/dist/modules/customers/lib/calendar/eventTypeResolver.js.map +7 -0
  116. package/dist/modules/customers/lib/calendar/interactionApplicability.js +67 -0
  117. package/dist/modules/customers/lib/calendar/interactionApplicability.js.map +7 -0
  118. package/dist/modules/customers/lib/calendar/mapItem.js +5 -2
  119. package/dist/modules/customers/lib/calendar/mapItem.js.map +2 -2
  120. package/dist/modules/customers/lib/calendar/recurrence.js +62 -1
  121. package/dist/modules/customers/lib/calendar/recurrence.js.map +2 -2
  122. package/dist/modules/customers/lib/calendar/timezone.js +111 -0
  123. package/dist/modules/customers/lib/calendar/timezone.js.map +7 -0
  124. package/dist/modules/customers/lib/interactionReadModel.js +1 -0
  125. package/dist/modules/customers/lib/interactionReadModel.js.map +2 -2
  126. package/dist/modules/customers/lib/todoCompatibility.js +1 -0
  127. package/dist/modules/customers/lib/todoCompatibility.js.map +2 -2
  128. package/dist/modules/customers/migrations/Migration20260928175058_customers.js +17 -0
  129. package/dist/modules/customers/migrations/Migration20260928175058_customers.js.map +7 -0
  130. package/dist/modules/customers/migrations/Migration20260930110235_customers.js +17 -0
  131. package/dist/modules/customers/migrations/Migration20260930110235_customers.js.map +7 -0
  132. package/dist/modules/dictionaries/components/DictionaryTable.js +60 -51
  133. package/dist/modules/dictionaries/components/DictionaryTable.js.map +2 -2
  134. package/dist/modules/messages/subscribers/message-notification.js +13 -1
  135. package/dist/modules/messages/subscribers/message-notification.js.map +2 -2
  136. package/dist/modules/payment_gateways/api/webhook/[provider]/route.js +201 -53
  137. package/dist/modules/payment_gateways/api/webhook/[provider]/route.js.map +2 -2
  138. package/dist/modules/payment_gateways/lib/gateway-service.js +37 -11
  139. package/dist/modules/payment_gateways/lib/gateway-service.js.map +2 -2
  140. package/dist/modules/planner/lib/availabilityMerge.js +101 -38
  141. package/dist/modules/planner/lib/availabilityMerge.js.map +3 -3
  142. package/dist/modules/planner/services/plannerAvailabilityService.js.map +2 -2
  143. package/dist/modules/sales/components/documents/LineItemDialog.js +159 -81
  144. package/dist/modules/sales/components/documents/LineItemDialog.js.map +2 -2
  145. package/dist/modules/sales/components/documents/lineItemUtils.js +7 -0
  146. package/dist/modules/sales/components/documents/lineItemUtils.js.map +2 -2
  147. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js +12 -7
  148. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js.map +2 -2
  149. package/dist/modules/sync_excel/lib/adapters/customers.js +28 -25
  150. package/dist/modules/sync_excel/lib/adapters/customers.js.map +2 -2
  151. package/generated/entities/customer_dictionary_entry/index.ts +1 -0
  152. package/generated/entities/customer_interaction/index.ts +1 -0
  153. package/generated/entity-fields-registry.ts +2 -0
  154. package/package.json +9 -8
  155. package/src/modules/auth/i18n/de.json +3 -0
  156. package/src/modules/auth/i18n/en.json +3 -0
  157. package/src/modules/auth/i18n/es.json +3 -0
  158. package/src/modules/auth/i18n/ko.json +3 -0
  159. package/src/modules/auth/i18n/pl.json +3 -0
  160. package/src/modules/catalog/commands/categories.ts +1 -1
  161. package/src/modules/catalog/commands/productDeleteChildren.ts +557 -0
  162. package/src/modules/catalog/commands/products.ts +58 -17
  163. package/src/modules/communication_channels/api/post/channels/connect/credentials/route.ts +4 -6
  164. package/src/modules/communication_channels/api/post/channels/connect/tenant-credentials/route.ts +4 -6
  165. package/src/modules/communication_channels/api/post/oauth/[provider]/initiate/route.ts +46 -10
  166. package/src/modules/communication_channels/commands/deliver-outbound-message.ts +6 -1
  167. package/src/modules/communication_channels/i18n/de.json +4 -0
  168. package/src/modules/communication_channels/i18n/en.json +4 -0
  169. package/src/modules/communication_channels/i18n/es.json +4 -0
  170. package/src/modules/communication_channels/i18n/ko.json +4 -0
  171. package/src/modules/communication_channels/i18n/pl.json +4 -0
  172. package/src/modules/communication_channels/lib/inbound-message-origin.ts +2 -6
  173. package/src/modules/communication_channels/lib/oauth-state.ts +4 -1
  174. package/src/modules/communication_channels/lib/use-connect-channel.ts +10 -0
  175. package/src/modules/customers/api/activity-types/route.ts +154 -0
  176. package/src/modules/customers/api/deals/[id]/people/route.ts +82 -3
  177. package/src/modules/customers/api/dictionaries/[kind]/[id]/route.ts +9 -1
  178. package/src/modules/customers/api/dictionaries/[kind]/route.ts +18 -2
  179. package/src/modules/customers/api/interactions/route.ts +4 -0
  180. package/src/modules/customers/api/interactions/tasks/route.ts +1 -0
  181. package/src/modules/customers/backend/config/customers/pipeline-stages/page.meta.ts +20 -0
  182. package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.ts +0 -29
  183. package/src/modules/customers/backend/customers/deals/[id]/page.tsx +15 -17
  184. package/src/modules/customers/calendar-event-types.ts +674 -0
  185. package/src/modules/customers/commands/dictionaries.ts +84 -8
  186. package/src/modules/customers/commands/interactions.ts +244 -21
  187. package/src/modules/customers/components/ActivityTypeEditor.tsx +239 -0
  188. package/src/modules/customers/components/DictionarySettings.tsx +5 -3
  189. package/src/modules/customers/components/calendar/CalendarEventEditor.tsx +347 -218
  190. package/src/modules/customers/components/calendar/CalendarSettingsModal.tsx +18 -96
  191. package/src/modules/customers/components/calendar/TimeGrid.tsx +20 -18
  192. package/src/modules/customers/components/calendar/editor/EventTypePanel.tsx +230 -0
  193. package/src/modules/customers/components/calendar/editor/LocationField.tsx +7 -1
  194. package/src/modules/customers/components/calendar/editor/PeopleField.tsx +4 -0
  195. package/src/modules/customers/components/calendar/editor/PriorityField.tsx +3 -0
  196. package/src/modules/customers/components/calendar/editor/RepeatField.tsx +10 -0
  197. package/src/modules/customers/components/calendar/editor/ResourcesField.tsx +4 -0
  198. package/src/modules/customers/components/calendar/editor/ScheduleSection.tsx +16 -4
  199. package/src/modules/customers/components/calendar/editor/SegmentGroup.tsx +3 -0
  200. package/src/modules/customers/components/calendar/editor/hooks.ts +29 -13
  201. package/src/modules/customers/components/calendar/editor/inputs.tsx +17 -5
  202. package/src/modules/customers/components/calendar/editor/useCalendarCustomFields.ts +49 -0
  203. package/src/modules/customers/components/calendar/editor/useEventTypeCatalog.ts +100 -0
  204. package/src/modules/customers/components/calendar/types.ts +1 -0
  205. package/src/modules/customers/components/detail/CompanyPeopleSection.tsx +94 -388
  206. package/src/modules/customers/components/detail/CreatePersonDialog.tsx +39 -19
  207. package/src/modules/customers/components/detail/DealForm.tsx +7 -3
  208. package/src/modules/customers/components/detail/DealPeopleSection.tsx +256 -0
  209. package/src/modules/customers/components/detail/LinkedPeopleSection.tsx +474 -0
  210. package/src/modules/customers/components/detail/PersonCard.tsx +12 -4
  211. package/src/modules/customers/components/detail/types.ts +1 -0
  212. package/src/modules/customers/components/linking/adapters/personAdapter.tsx +4 -0
  213. package/src/modules/customers/data/entities.ts +8 -1
  214. package/src/modules/customers/data/validators.ts +42 -7
  215. package/src/modules/customers/di.ts +21 -0
  216. package/src/modules/customers/extension-points.ts +13 -0
  217. package/src/modules/customers/i18n/de.json +104 -8
  218. package/src/modules/customers/i18n/en.json +104 -8
  219. package/src/modules/customers/i18n/es.json +104 -8
  220. package/src/modules/customers/i18n/ko.json +104 -8
  221. package/src/modules/customers/i18n/pl.json +104 -8
  222. package/src/modules/customers/lib/calendar/conflicts.ts +106 -27
  223. package/src/modules/customers/lib/calendar/editorPayload.ts +39 -19
  224. package/src/modules/customers/lib/calendar/eventTypeResolver.ts +258 -0
  225. package/src/modules/customers/lib/calendar/interactionApplicability.ts +99 -0
  226. package/src/modules/customers/lib/calendar/mapItem.ts +6 -2
  227. package/src/modules/customers/lib/calendar/recurrence.ts +65 -1
  228. package/src/modules/customers/lib/calendar/timezone.ts +117 -0
  229. package/src/modules/customers/lib/interactionReadModel.ts +1 -0
  230. package/src/modules/customers/lib/todoCompatibility.ts +2 -0
  231. package/src/modules/customers/migrations/.snapshot-open-mercato.json +34 -0
  232. package/src/modules/customers/migrations/Migration20260928175058_customers.ts +15 -0
  233. package/src/modules/customers/migrations/Migration20260930110235_customers.ts +15 -0
  234. package/src/modules/dictionaries/components/DictionaryTable.tsx +63 -56
  235. package/src/modules/messages/subscribers/message-notification.ts +13 -3
  236. package/src/modules/payment_gateways/api/webhook/[provider]/route.ts +243 -62
  237. package/src/modules/payment_gateways/lib/gateway-service.ts +39 -12
  238. package/src/modules/planner/lib/availabilityMerge.ts +106 -41
  239. package/src/modules/planner/services/plannerAvailabilityService.ts +4 -0
  240. package/src/modules/sales/components/documents/LineItemDialog.tsx +194 -111
  241. package/src/modules/sales/components/documents/lineItemUtils.ts +18 -0
  242. package/src/modules/staff/backend/staff/time-tracking/projects/[id]/page.tsx +25 -13
  243. package/src/modules/sync_excel/lib/adapters/customers.ts +33 -27
@@ -1,3 +1,4 @@
1
+ import { isDeepStrictEqual } from 'node:util'
1
2
  import { registerCommand, type CommandHandler } from '@open-mercato/shared/lib/commands'
2
3
  import type { CommandRuntimeContext } from '@open-mercato/shared/lib/commands'
3
4
  import { buildChanges } from '@open-mercato/shared/lib/commands/helpers'
@@ -26,6 +27,8 @@ import {
26
27
  } from './shared'
27
28
  import { resolveRedoSnapshot } from '@open-mercato/shared/lib/commands/redo'
28
29
  import { createLogger } from '@open-mercato/shared/lib/logger'
30
+ import { enforceCommandOptimisticLockWithGuards } from '@open-mercato/shared/lib/crud/optimistic-lock-command'
31
+ import { calendarEventTypeBehaviorSchema, type CalendarEventTypeBehavior } from '../calendar-event-types'
29
32
 
30
33
  const logger = createLogger('customers')
31
34
 
@@ -39,6 +42,7 @@ type CustomerDictionaryEntrySnapshot = {
39
42
  label: string
40
43
  color: string | null
41
44
  icon: string | null
45
+ behavior: CalendarEventTypeBehavior | null
42
46
  }
43
47
 
44
48
  type CustomerDictionaryEntryUndoPayload = {
@@ -46,6 +50,18 @@ type CustomerDictionaryEntryUndoPayload = {
46
50
  after?: CustomerDictionaryEntrySnapshot | null
47
51
  }
48
52
 
53
+ const nullableCalendarEventTypeBehaviorSchema = calendarEventTypeBehaviorSchema.nullable()
54
+
55
+ function calendarEventTypeBehaviorsEqual(
56
+ left: CalendarEventTypeBehavior | null,
57
+ right: CalendarEventTypeBehavior | null,
58
+ ): boolean {
59
+ const parsedLeft = nullableCalendarEventTypeBehaviorSchema.safeParse(left)
60
+ const parsedRight = nullableCalendarEventTypeBehaviorSchema.safeParse(right)
61
+ if (parsedLeft.success && parsedRight.success) return isDeepStrictEqual(parsedLeft.data, parsedRight.data)
62
+ return isDeepStrictEqual(left, right)
63
+ }
64
+
49
65
  function buildRoleTypeInUseError(usageCount: number, ownerAssignments: number, relationshipAssignments: number) {
50
66
  return new CrudHttpError(409, {
51
67
  code: 'role_type_in_use',
@@ -87,9 +103,27 @@ function toSnapshot(entry: CustomerDictionaryEntry): CustomerDictionaryEntrySnap
87
103
  label: entry.label,
88
104
  color: entry.color ?? null,
89
105
  icon: entry.icon ?? null,
106
+ behavior: entry.activityTypeBehavior ?? null,
90
107
  }
91
108
  }
92
109
 
110
+ function buildDictionaryChanges(
111
+ before: CustomerDictionaryEntrySnapshot,
112
+ after: CustomerDictionaryEntrySnapshot,
113
+ keys: readonly (keyof CustomerDictionaryEntrySnapshot)[],
114
+ ): Record<string, { from: unknown; to: unknown }> {
115
+ const scalarKeys = keys.filter((key) => key !== 'behavior')
116
+ const changes = buildChanges(
117
+ before as unknown as Record<string, unknown>,
118
+ after as unknown as Record<string, unknown>,
119
+ scalarKeys,
120
+ )
121
+ if (keys.includes('behavior') && !calendarEventTypeBehaviorsEqual(before.behavior, after.behavior)) {
122
+ changes.behavior = { from: before.behavior, to: after.behavior }
123
+ }
124
+ return changes
125
+ }
126
+
93
127
  async function loadSnapshot(
94
128
  em: EntityManager,
95
129
  id: string,
@@ -108,6 +142,7 @@ function applySnapshot(entry: CustomerDictionaryEntry, snapshot: CustomerDiction
108
142
  entry.label = snapshot.label
109
143
  entry.color = snapshot.color
110
144
  entry.icon = snapshot.icon
145
+ entry.activityTypeBehavior = snapshot.behavior
111
146
  }
112
147
 
113
148
  async function invalidateCache(
@@ -150,6 +185,7 @@ const createDictionaryEntryCommand: CommandHandler<CustomerDictionaryEntryCreate
150
185
  const label = parsed.label?.trim() || value.value
151
186
  const color = sanitizeColor(parsed.color)
152
187
  const icon = sanitizeIcon(parsed.icon)
188
+ const behavior = parsed.behavior
153
189
 
154
190
  const existing = await findOneWithDecryption(
155
191
  em,
@@ -168,6 +204,12 @@ const createDictionaryEntryCommand: CommandHandler<CustomerDictionaryEntryCreate
168
204
  )
169
205
 
170
206
  if (existing) {
207
+ await enforceCommandOptimisticLockWithGuards(ctx.container, {
208
+ resourceKind: 'customers.dictionary_entry',
209
+ resourceId: existing.id,
210
+ current: existing.updatedAt,
211
+ request: ctx.request ?? null,
212
+ })
171
213
  const before = toSnapshot(existing)
172
214
  let changed = false
173
215
 
@@ -183,10 +225,15 @@ const createDictionaryEntryCommand: CommandHandler<CustomerDictionaryEntryCreate
183
225
  existing.icon = icon ?? null
184
226
  changed = true
185
227
  }
228
+ if (behavior !== undefined && !isDeepStrictEqual(existing.activityTypeBehavior ?? null, behavior)) {
229
+ existing.activityTypeBehavior = behavior
230
+ changed = true
231
+ }
186
232
 
187
233
  if (changed) {
188
234
  existing.updatedAt = new Date()
189
235
  await em.flush()
236
+ await invalidateCache(ctx, toSnapshot(existing))
190
237
  return {
191
238
  entryId: existing.id,
192
239
  tenantId: parsed.tenantId,
@@ -214,9 +261,11 @@ const createDictionaryEntryCommand: CommandHandler<CustomerDictionaryEntryCreate
214
261
  label,
215
262
  color: color ?? null,
216
263
  icon: icon ?? null,
264
+ activityTypeBehavior: behavior ?? null,
217
265
  })
218
266
  em.persist(entry)
219
267
  await em.flush()
268
+ await invalidateCache(ctx, toSnapshot(entry))
220
269
 
221
270
  return {
222
271
  entryId: entry.id,
@@ -254,10 +303,10 @@ const createDictionaryEntryCommand: CommandHandler<CustomerDictionaryEntryCreate
254
303
  if (result.mode === 'updated') {
255
304
  const before = result.before ?? null
256
305
  if (!before) return null
257
- const changes = buildChanges(
258
- before as unknown as Record<string, unknown>,
259
- after as unknown as Record<string, unknown>,
260
- ['label', 'color', 'icon']
306
+ const changes = buildDictionaryChanges(
307
+ before,
308
+ after,
309
+ ['label', 'color', 'icon', 'behavior']
261
310
  )
262
311
  if (!changes || Object.keys(changes).length === 0) return null
263
312
  return {
@@ -333,6 +382,7 @@ const createDictionaryEntryCommand: CommandHandler<CustomerDictionaryEntryCreate
333
382
  label: before.label,
334
383
  color: before.color,
335
384
  icon: before.icon,
385
+ activityTypeBehavior: before.behavior,
336
386
  })
337
387
  em.persist(entry)
338
388
  } else {
@@ -369,6 +419,7 @@ const createDictionaryEntryCommand: CommandHandler<CustomerDictionaryEntryCreate
369
419
  label: after.label,
370
420
  color: after.color,
371
421
  icon: after.icon,
422
+ activityTypeBehavior: after.behavior,
372
423
  })
373
424
  em.persist(entry)
374
425
  } else {
@@ -428,6 +479,13 @@ const updateDictionaryEntryCommand: CommandHandler<CustomerDictionaryEntryUpdate
428
479
  throw notFound('Dictionary entry not found')
429
480
  }
430
481
 
482
+ await enforceCommandOptimisticLockWithGuards(ctx.container, {
483
+ resourceKind: 'customers.dictionary_entry',
484
+ resourceId: entry.id,
485
+ current: entry.updatedAt,
486
+ request: ctx.request ?? null,
487
+ })
488
+
431
489
  let changed = false
432
490
 
433
491
  if (parsed.value !== undefined) {
@@ -499,9 +557,18 @@ const updateDictionaryEntryCommand: CommandHandler<CustomerDictionaryEntryUpdate
499
557
  }
500
558
  }
501
559
 
560
+ if (
561
+ parsed.behavior !== undefined &&
562
+ !isDeepStrictEqual(entry.activityTypeBehavior ?? null, parsed.behavior)
563
+ ) {
564
+ entry.activityTypeBehavior = parsed.behavior
565
+ changed = true
566
+ }
567
+
502
568
  if (changed) {
503
569
  entry.updatedAt = new Date()
504
570
  await em.flush()
571
+ await invalidateCache(ctx, toSnapshot(entry))
505
572
  }
506
573
 
507
574
  return {
@@ -522,10 +589,10 @@ const updateDictionaryEntryCommand: CommandHandler<CustomerDictionaryEntryUpdate
522
589
  const before = snapshots.before as CustomerDictionaryEntrySnapshot | undefined
523
590
  const after = snapshots.after as CustomerDictionaryEntrySnapshot | undefined
524
591
  if (!before || !after || !result.changed) return null
525
- const changes = buildChanges(
526
- before as unknown as Record<string, unknown>,
527
- after as unknown as Record<string, unknown>,
528
- ['value', 'label', 'color', 'icon']
592
+ const changes = buildDictionaryChanges(
593
+ before,
594
+ after,
595
+ ['value', 'label', 'color', 'icon', 'behavior']
529
596
  )
530
597
  if (!changes || Object.keys(changes).length === 0) return null
531
598
  const { translate } = await resolveTranslations()
@@ -571,6 +638,7 @@ const updateDictionaryEntryCommand: CommandHandler<CustomerDictionaryEntryUpdate
571
638
  label: before.label,
572
639
  color: before.color,
573
640
  icon: before.icon,
641
+ activityTypeBehavior: before.behavior,
574
642
  })
575
643
  em.persist(entry)
576
644
  } else {
@@ -613,6 +681,12 @@ const deleteDictionaryEntryCommand: CommandHandler<CustomerDictionaryEntryDelete
613
681
  if (!entry || entry.organizationId !== parsed.organizationId || entry.tenantId !== parsed.tenantId || entry.kind !== parsed.kind) {
614
682
  throw notFound('Dictionary entry not found')
615
683
  }
684
+ await enforceCommandOptimisticLockWithGuards(ctx.container, {
685
+ resourceKind: 'customers.dictionary_entry',
686
+ resourceId: entry.id,
687
+ current: entry.updatedAt,
688
+ request: ctx.request ?? null,
689
+ })
616
690
  if (entry.kind === 'person_company_role') {
617
691
  const usage = await loadRoleTypeUsage(em, {
618
692
  tenantId: entry.tenantId,
@@ -629,6 +703,7 @@ const deleteDictionaryEntryCommand: CommandHandler<CustomerDictionaryEntryDelete
629
703
  }
630
704
  em.remove(entry)
631
705
  await em.flush()
706
+ await invalidateCache(ctx, toSnapshot(entry))
632
707
  return { entryId: entry.id }
633
708
  },
634
709
  buildLog: async ({ snapshots }) => {
@@ -675,6 +750,7 @@ const deleteDictionaryEntryCommand: CommandHandler<CustomerDictionaryEntryDelete
675
750
  label: before.label,
676
751
  color: before.color,
677
752
  icon: before.icon,
753
+ activityTypeBehavior: before.behavior,
678
754
  })
679
755
  em.persist(entry)
680
756
  } else {