@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.
- package/.turbo/turbo-build.log +1 -1
- package/dist/generated/entities/customer_dictionary_entry/index.js +2 -0
- package/dist/generated/entities/customer_dictionary_entry/index.js.map +2 -2
- package/dist/generated/entities/customer_interaction/index.js +2 -0
- package/dist/generated/entities/customer_interaction/index.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +2 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/modules/catalog/commands/categories.js +1 -1
- package/dist/modules/catalog/commands/categories.js.map +2 -2
- package/dist/modules/catalog/commands/productDeleteChildren.js +389 -0
- package/dist/modules/catalog/commands/productDeleteChildren.js.map +7 -0
- package/dist/modules/catalog/commands/products.js +52 -17
- package/dist/modules/catalog/commands/products.js.map +2 -2
- package/dist/modules/communication_channels/api/post/channels/connect/credentials/route.js +4 -6
- package/dist/modules/communication_channels/api/post/channels/connect/credentials/route.js.map +2 -2
- package/dist/modules/communication_channels/api/post/channels/connect/tenant-credentials/route.js +4 -6
- package/dist/modules/communication_channels/api/post/channels/connect/tenant-credentials/route.js.map +2 -2
- package/dist/modules/communication_channels/api/post/oauth/[provider]/initiate/route.js +47 -11
- package/dist/modules/communication_channels/api/post/oauth/[provider]/initiate/route.js.map +3 -3
- package/dist/modules/communication_channels/commands/deliver-outbound-message.js +5 -1
- package/dist/modules/communication_channels/commands/deliver-outbound-message.js.map +2 -2
- package/dist/modules/communication_channels/lib/inbound-message-origin.js +1 -1
- package/dist/modules/communication_channels/lib/inbound-message-origin.js.map +2 -2
- package/dist/modules/communication_channels/lib/oauth-state.js +4 -1
- package/dist/modules/communication_channels/lib/oauth-state.js.map +2 -2
- package/dist/modules/communication_channels/lib/use-connect-channel.js +10 -0
- package/dist/modules/communication_channels/lib/use-connect-channel.js.map +2 -2
- package/dist/modules/customers/api/activity-types/route.js +151 -0
- package/dist/modules/customers/api/activity-types/route.js.map +7 -0
- package/dist/modules/customers/api/deals/[id]/people/route.js +58 -5
- package/dist/modules/customers/api/deals/[id]/people/route.js.map +2 -2
- package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js +13 -5
- package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js.map +2 -2
- package/dist/modules/customers/api/dictionaries/[kind]/route.js +20 -5
- package/dist/modules/customers/api/dictionaries/[kind]/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/route.js +3 -0
- package/dist/modules/customers/api/interactions/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/tasks/route.js +1 -0
- package/dist/modules/customers/api/interactions/tasks/route.js.map +2 -2
- package/dist/modules/customers/backend/config/customers/pipeline-stages/page.meta.js +18 -0
- package/dist/modules/customers/backend/config/customers/pipeline-stages/page.meta.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.js +0 -23
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/page.js +15 -17
- package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
- package/dist/modules/customers/calendar-event-types.js +548 -0
- package/dist/modules/customers/calendar-event-types.js.map +7 -0
- package/dist/modules/customers/commands/dictionaries.js +70 -10
- package/dist/modules/customers/commands/dictionaries.js.map +2 -2
- package/dist/modules/customers/commands/interactions.js +187 -22
- package/dist/modules/customers/commands/interactions.js.map +3 -3
- package/dist/modules/customers/components/ActivityTypeEditor.js +288 -0
- package/dist/modules/customers/components/ActivityTypeEditor.js.map +7 -0
- package/dist/modules/customers/components/DictionarySettings.js +3 -1
- package/dist/modules/customers/components/DictionarySettings.js.map +2 -2
- package/dist/modules/customers/components/calendar/CalendarEventEditor.js +391 -297
- package/dist/modules/customers/components/calendar/CalendarEventEditor.js.map +3 -3
- package/dist/modules/customers/components/calendar/CalendarSettingsModal.js +9 -74
- package/dist/modules/customers/components/calendar/CalendarSettingsModal.js.map +2 -2
- package/dist/modules/customers/components/calendar/TimeGrid.js +20 -16
- package/dist/modules/customers/components/calendar/TimeGrid.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/EventTypePanel.js +218 -0
- package/dist/modules/customers/components/calendar/editor/EventTypePanel.js.map +7 -0
- package/dist/modules/customers/components/calendar/editor/LocationField.js +7 -3
- package/dist/modules/customers/components/calendar/editor/LocationField.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/PeopleField.js +5 -2
- package/dist/modules/customers/components/calendar/editor/PeopleField.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/PriorityField.js +3 -1
- package/dist/modules/customers/components/calendar/editor/PriorityField.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/RepeatField.js +9 -1
- package/dist/modules/customers/components/calendar/editor/RepeatField.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/ResourcesField.js +4 -1
- package/dist/modules/customers/components/calendar/editor/ResourcesField.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/ScheduleSection.js +15 -7
- package/dist/modules/customers/components/calendar/editor/ScheduleSection.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/SegmentGroup.js +3 -1
- package/dist/modules/customers/components/calendar/editor/SegmentGroup.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/hooks.js +28 -13
- package/dist/modules/customers/components/calendar/editor/hooks.js.map +3 -3
- package/dist/modules/customers/components/calendar/editor/inputs.js +14 -6
- package/dist/modules/customers/components/calendar/editor/inputs.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/useCalendarCustomFields.js +54 -0
- package/dist/modules/customers/components/calendar/editor/useCalendarCustomFields.js.map +7 -0
- package/dist/modules/customers/components/calendar/editor/useEventTypeCatalog.js +82 -0
- package/dist/modules/customers/components/calendar/editor/useEventTypeCatalog.js.map +7 -0
- package/dist/modules/customers/components/calendar/types.js +1 -0
- package/dist/modules/customers/components/calendar/types.js.map +2 -2
- package/dist/modules/customers/components/detail/CompanyPeopleSection.js +78 -343
- package/dist/modules/customers/components/detail/CompanyPeopleSection.js.map +2 -2
- package/dist/modules/customers/components/detail/CreatePersonDialog.js +12 -11
- package/dist/modules/customers/components/detail/CreatePersonDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/DealForm.js +7 -3
- package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
- package/dist/modules/customers/components/detail/DealPeopleSection.js +206 -0
- package/dist/modules/customers/components/detail/DealPeopleSection.js.map +7 -0
- package/dist/modules/customers/components/detail/LinkedPeopleSection.js +342 -0
- package/dist/modules/customers/components/detail/LinkedPeopleSection.js.map +7 -0
- package/dist/modules/customers/components/detail/PersonCard.js +5 -2
- package/dist/modules/customers/components/detail/PersonCard.js.map +2 -2
- package/dist/modules/customers/components/linking/adapters/personAdapter.js +3 -0
- package/dist/modules/customers/components/linking/adapters/personAdapter.js.map +2 -2
- package/dist/modules/customers/data/entities.js +6 -0
- package/dist/modules/customers/data/entities.js.map +2 -2
- package/dist/modules/customers/data/validators.js +39 -10
- package/dist/modules/customers/data/validators.js.map +2 -2
- package/dist/modules/customers/di.js +20 -1
- package/dist/modules/customers/di.js.map +2 -2
- package/dist/modules/customers/extension-points.js +13 -0
- package/dist/modules/customers/extension-points.js.map +2 -2
- package/dist/modules/customers/lib/calendar/conflicts.js +59 -16
- package/dist/modules/customers/lib/calendar/conflicts.js.map +2 -2
- package/dist/modules/customers/lib/calendar/editorPayload.js +34 -16
- package/dist/modules/customers/lib/calendar/editorPayload.js.map +2 -2
- package/dist/modules/customers/lib/calendar/eventTypeResolver.js +197 -0
- package/dist/modules/customers/lib/calendar/eventTypeResolver.js.map +7 -0
- package/dist/modules/customers/lib/calendar/interactionApplicability.js +67 -0
- package/dist/modules/customers/lib/calendar/interactionApplicability.js.map +7 -0
- package/dist/modules/customers/lib/calendar/mapItem.js +5 -2
- package/dist/modules/customers/lib/calendar/mapItem.js.map +2 -2
- package/dist/modules/customers/lib/calendar/recurrence.js +62 -1
- package/dist/modules/customers/lib/calendar/recurrence.js.map +2 -2
- package/dist/modules/customers/lib/calendar/timezone.js +111 -0
- package/dist/modules/customers/lib/calendar/timezone.js.map +7 -0
- package/dist/modules/customers/lib/interactionReadModel.js +1 -0
- package/dist/modules/customers/lib/interactionReadModel.js.map +2 -2
- package/dist/modules/customers/lib/todoCompatibility.js +1 -0
- package/dist/modules/customers/lib/todoCompatibility.js.map +2 -2
- package/dist/modules/customers/migrations/Migration20260928175058_customers.js +17 -0
- package/dist/modules/customers/migrations/Migration20260928175058_customers.js.map +7 -0
- package/dist/modules/customers/migrations/Migration20260930110235_customers.js +17 -0
- package/dist/modules/customers/migrations/Migration20260930110235_customers.js.map +7 -0
- package/dist/modules/dictionaries/components/DictionaryTable.js +60 -51
- package/dist/modules/dictionaries/components/DictionaryTable.js.map +2 -2
- package/dist/modules/messages/subscribers/message-notification.js +13 -1
- package/dist/modules/messages/subscribers/message-notification.js.map +2 -2
- package/dist/modules/payment_gateways/api/webhook/[provider]/route.js +201 -53
- package/dist/modules/payment_gateways/api/webhook/[provider]/route.js.map +2 -2
- package/dist/modules/payment_gateways/lib/gateway-service.js +37 -11
- package/dist/modules/payment_gateways/lib/gateway-service.js.map +2 -2
- package/dist/modules/planner/lib/availabilityMerge.js +101 -38
- package/dist/modules/planner/lib/availabilityMerge.js.map +3 -3
- package/dist/modules/planner/services/plannerAvailabilityService.js.map +2 -2
- package/dist/modules/sales/components/documents/LineItemDialog.js +159 -81
- package/dist/modules/sales/components/documents/LineItemDialog.js.map +2 -2
- package/dist/modules/sales/components/documents/lineItemUtils.js +7 -0
- package/dist/modules/sales/components/documents/lineItemUtils.js.map +2 -2
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js +12 -7
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js.map +2 -2
- package/dist/modules/sync_excel/lib/adapters/customers.js +28 -25
- package/dist/modules/sync_excel/lib/adapters/customers.js.map +2 -2
- package/generated/entities/customer_dictionary_entry/index.ts +1 -0
- package/generated/entities/customer_interaction/index.ts +1 -0
- package/generated/entity-fields-registry.ts +2 -0
- package/package.json +9 -8
- package/src/modules/auth/i18n/de.json +3 -0
- package/src/modules/auth/i18n/en.json +3 -0
- package/src/modules/auth/i18n/es.json +3 -0
- package/src/modules/auth/i18n/ko.json +3 -0
- package/src/modules/auth/i18n/pl.json +3 -0
- package/src/modules/catalog/commands/categories.ts +1 -1
- package/src/modules/catalog/commands/productDeleteChildren.ts +557 -0
- package/src/modules/catalog/commands/products.ts +58 -17
- package/src/modules/communication_channels/api/post/channels/connect/credentials/route.ts +4 -6
- package/src/modules/communication_channels/api/post/channels/connect/tenant-credentials/route.ts +4 -6
- package/src/modules/communication_channels/api/post/oauth/[provider]/initiate/route.ts +46 -10
- package/src/modules/communication_channels/commands/deliver-outbound-message.ts +6 -1
- package/src/modules/communication_channels/i18n/de.json +4 -0
- package/src/modules/communication_channels/i18n/en.json +4 -0
- package/src/modules/communication_channels/i18n/es.json +4 -0
- package/src/modules/communication_channels/i18n/ko.json +4 -0
- package/src/modules/communication_channels/i18n/pl.json +4 -0
- package/src/modules/communication_channels/lib/inbound-message-origin.ts +2 -6
- package/src/modules/communication_channels/lib/oauth-state.ts +4 -1
- package/src/modules/communication_channels/lib/use-connect-channel.ts +10 -0
- package/src/modules/customers/api/activity-types/route.ts +154 -0
- package/src/modules/customers/api/deals/[id]/people/route.ts +82 -3
- package/src/modules/customers/api/dictionaries/[kind]/[id]/route.ts +9 -1
- package/src/modules/customers/api/dictionaries/[kind]/route.ts +18 -2
- package/src/modules/customers/api/interactions/route.ts +4 -0
- package/src/modules/customers/api/interactions/tasks/route.ts +1 -0
- package/src/modules/customers/backend/config/customers/pipeline-stages/page.meta.ts +20 -0
- package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.ts +0 -29
- package/src/modules/customers/backend/customers/deals/[id]/page.tsx +15 -17
- package/src/modules/customers/calendar-event-types.ts +674 -0
- package/src/modules/customers/commands/dictionaries.ts +84 -8
- package/src/modules/customers/commands/interactions.ts +244 -21
- package/src/modules/customers/components/ActivityTypeEditor.tsx +239 -0
- package/src/modules/customers/components/DictionarySettings.tsx +5 -3
- package/src/modules/customers/components/calendar/CalendarEventEditor.tsx +347 -218
- package/src/modules/customers/components/calendar/CalendarSettingsModal.tsx +18 -96
- package/src/modules/customers/components/calendar/TimeGrid.tsx +20 -18
- package/src/modules/customers/components/calendar/editor/EventTypePanel.tsx +230 -0
- package/src/modules/customers/components/calendar/editor/LocationField.tsx +7 -1
- package/src/modules/customers/components/calendar/editor/PeopleField.tsx +4 -0
- package/src/modules/customers/components/calendar/editor/PriorityField.tsx +3 -0
- package/src/modules/customers/components/calendar/editor/RepeatField.tsx +10 -0
- package/src/modules/customers/components/calendar/editor/ResourcesField.tsx +4 -0
- package/src/modules/customers/components/calendar/editor/ScheduleSection.tsx +16 -4
- package/src/modules/customers/components/calendar/editor/SegmentGroup.tsx +3 -0
- package/src/modules/customers/components/calendar/editor/hooks.ts +29 -13
- package/src/modules/customers/components/calendar/editor/inputs.tsx +17 -5
- package/src/modules/customers/components/calendar/editor/useCalendarCustomFields.ts +49 -0
- package/src/modules/customers/components/calendar/editor/useEventTypeCatalog.ts +100 -0
- package/src/modules/customers/components/calendar/types.ts +1 -0
- package/src/modules/customers/components/detail/CompanyPeopleSection.tsx +94 -388
- package/src/modules/customers/components/detail/CreatePersonDialog.tsx +39 -19
- package/src/modules/customers/components/detail/DealForm.tsx +7 -3
- package/src/modules/customers/components/detail/DealPeopleSection.tsx +256 -0
- package/src/modules/customers/components/detail/LinkedPeopleSection.tsx +474 -0
- package/src/modules/customers/components/detail/PersonCard.tsx +12 -4
- package/src/modules/customers/components/detail/types.ts +1 -0
- package/src/modules/customers/components/linking/adapters/personAdapter.tsx +4 -0
- package/src/modules/customers/data/entities.ts +8 -1
- package/src/modules/customers/data/validators.ts +42 -7
- package/src/modules/customers/di.ts +21 -0
- package/src/modules/customers/extension-points.ts +13 -0
- package/src/modules/customers/i18n/de.json +104 -8
- package/src/modules/customers/i18n/en.json +104 -8
- package/src/modules/customers/i18n/es.json +104 -8
- package/src/modules/customers/i18n/ko.json +104 -8
- package/src/modules/customers/i18n/pl.json +104 -8
- package/src/modules/customers/lib/calendar/conflicts.ts +106 -27
- package/src/modules/customers/lib/calendar/editorPayload.ts +39 -19
- package/src/modules/customers/lib/calendar/eventTypeResolver.ts +258 -0
- package/src/modules/customers/lib/calendar/interactionApplicability.ts +99 -0
- package/src/modules/customers/lib/calendar/mapItem.ts +6 -2
- package/src/modules/customers/lib/calendar/recurrence.ts +65 -1
- package/src/modules/customers/lib/calendar/timezone.ts +117 -0
- package/src/modules/customers/lib/interactionReadModel.ts +1 -0
- package/src/modules/customers/lib/todoCompatibility.ts +2 -0
- package/src/modules/customers/migrations/.snapshot-open-mercato.json +34 -0
- package/src/modules/customers/migrations/Migration20260928175058_customers.ts +15 -0
- package/src/modules/customers/migrations/Migration20260930110235_customers.ts +15 -0
- package/src/modules/dictionaries/components/DictionaryTable.tsx +63 -56
- package/src/modules/messages/subscribers/message-notification.ts +13 -3
- package/src/modules/payment_gateways/api/webhook/[provider]/route.ts +243 -62
- package/src/modules/payment_gateways/lib/gateway-service.ts +39 -12
- package/src/modules/planner/lib/availabilityMerge.ts +106 -41
- package/src/modules/planner/services/plannerAvailabilityService.ts +4 -0
- package/src/modules/sales/components/documents/LineItemDialog.tsx +194 -111
- package/src/modules/sales/components/documents/lineItemUtils.ts +18 -0
- package/src/modules/staff/backend/staff/time-tracking/projects/[id]/page.tsx +25 -13
- package/src/modules/sync_excel/lib/adapters/customers.ts +33 -27
|
@@ -50,6 +50,12 @@ import {
|
|
|
50
50
|
} from '../lib/interactionStatus'
|
|
51
51
|
import { canChangeEmailVisibility } from '../lib/visibilityFilter'
|
|
52
52
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
53
|
+
import { loadCustomFieldDefinitionIndex } from '@open-mercato/shared/lib/crud/custom-fields'
|
|
54
|
+
import type { OrganizationHierarchyService } from '@open-mercato/shared/lib/auth/principal-service'
|
|
55
|
+
import { resolveCatalogEventType, resolveScopedCalendarEventTypes } from '../lib/calendar/eventTypeResolver'
|
|
56
|
+
import { clearInapplicableCoreFields, findInapplicableCoreFields, omitHiddenCoreClears, preserveHiddenCoreValuesOnSameTypeEdit } from '../lib/calendar/interactionApplicability'
|
|
57
|
+
import { calendarEventTypes, type CalendarEventTypeBehavior } from '../calendar-event-types'
|
|
58
|
+
import { canonicalCalendarTimezone } from '../lib/calendar/timezone'
|
|
53
59
|
|
|
54
60
|
const logger = createLogger('customers')
|
|
55
61
|
|
|
@@ -110,6 +116,7 @@ type InteractionSnapshot = {
|
|
|
110
116
|
appearanceColor: string | null
|
|
111
117
|
source: string | null
|
|
112
118
|
durationMinutes: number | null
|
|
119
|
+
timezone?: string | null
|
|
113
120
|
location: string | null
|
|
114
121
|
allDay: boolean | null
|
|
115
122
|
recurrenceRule: string | null
|
|
@@ -166,6 +173,7 @@ async function loadInteractionSnapshot(em: EntityManager, id: string): Promise<I
|
|
|
166
173
|
appearanceColor: interaction.appearanceColor ?? null,
|
|
167
174
|
source: interaction.source ?? null,
|
|
168
175
|
durationMinutes: interaction.durationMinutes ?? null,
|
|
176
|
+
timezone: interaction.timezone ?? null,
|
|
169
177
|
location: interaction.location ?? null,
|
|
170
178
|
allDay: interaction.allDay ?? null,
|
|
171
179
|
recurrenceRule: interaction.recurrenceRule ?? null,
|
|
@@ -199,6 +207,88 @@ async function setInteractionCustomFields(
|
|
|
199
207
|
})
|
|
200
208
|
}
|
|
201
209
|
|
|
210
|
+
function requestsSelectableTypeEnforcement(input: unknown): boolean {
|
|
211
|
+
return input !== null && typeof input === 'object'
|
|
212
|
+
&& 'enforceSelectableType' in input && input.enforceSelectableType === true
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async function resolveInteractionTypeBehavior(
|
|
216
|
+
ctx: CommandRuntimeContext,
|
|
217
|
+
em: EntityManager,
|
|
218
|
+
tenantId: string,
|
|
219
|
+
organizationId: string,
|
|
220
|
+
key: string,
|
|
221
|
+
unchangedType: boolean,
|
|
222
|
+
enforceSelectableType: boolean,
|
|
223
|
+
): Promise<{ behavior: CalendarEventTypeBehavior; organizationIds: string[] }> {
|
|
224
|
+
const hierarchy = ctx.container.resolve('organizationHierarchyService') as OrganizationHierarchyService
|
|
225
|
+
const ancestors = await hierarchy.resolveAncestorIds({ tenantId, organizationId })
|
|
226
|
+
if (ancestors === null) throw notFound('Organization not found')
|
|
227
|
+
const organizationIds = [organizationId, ...ancestors.slice().reverse()]
|
|
228
|
+
let catalog
|
|
229
|
+
try {
|
|
230
|
+
catalog = await resolveScopedCalendarEventTypes({
|
|
231
|
+
em,
|
|
232
|
+
tenantId,
|
|
233
|
+
organizationId,
|
|
234
|
+
readableOrganizationIds: organizationIds,
|
|
235
|
+
})
|
|
236
|
+
} catch {
|
|
237
|
+
const { translate } = await resolveTranslations()
|
|
238
|
+
throw new CrudHttpError(503, {
|
|
239
|
+
error: translate('customers.calendar.activityTypes.errors.loadFailed', 'Failed to load activity types'),
|
|
240
|
+
code: 'activity_type_catalog_unavailable',
|
|
241
|
+
retryable: true,
|
|
242
|
+
})
|
|
243
|
+
}
|
|
244
|
+
const selected = resolveCatalogEventType(catalog, key, { includeHistorical: unchangedType || !enforceSelectableType })
|
|
245
|
+
if (selected && (unchangedType || !enforceSelectableType || selected.selectable)) {
|
|
246
|
+
return { behavior: selected.behavior, organizationIds }
|
|
247
|
+
}
|
|
248
|
+
if (unchangedType) return { behavior: calendarEventTypes[0]!.behavior, organizationIds }
|
|
249
|
+
const { translate } = await resolveTranslations()
|
|
250
|
+
throw new CrudHttpError(400, {
|
|
251
|
+
error: translate('customers.calendar.activityTypes.errors.unavailable', 'Activity type is unavailable'),
|
|
252
|
+
code: 'activity_type_unavailable',
|
|
253
|
+
})
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
async function loadApplicableCustomFieldKeys(
|
|
257
|
+
em: EntityManager,
|
|
258
|
+
tenantId: string,
|
|
259
|
+
organizationIds: string[],
|
|
260
|
+
behavior: CalendarEventTypeBehavior,
|
|
261
|
+
): Promise<{ defined: ReadonlySet<string>; allowed: ReadonlySet<string> }> {
|
|
262
|
+
const options = { em, entityIds: [INTERACTION_ENTITY_ID], tenantId, organizationIds }
|
|
263
|
+
const all = await loadCustomFieldDefinitionIndex(options)
|
|
264
|
+
const fieldsets = behavior.customFieldsetIds.map((id) => id === '__general__' ? '' : id)
|
|
265
|
+
const applicable = fieldsets.length
|
|
266
|
+
? await loadCustomFieldDefinitionIndex({ ...options, fieldset: fieldsets })
|
|
267
|
+
: all
|
|
268
|
+
return {
|
|
269
|
+
defined: new Set([...all.keys()].map((key) => key.toLowerCase())),
|
|
270
|
+
allowed: new Set([...applicable.keys()].map((key) => key.toLowerCase())),
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function inapplicableCustomFieldKeys(
|
|
275
|
+
values: Readonly<Record<string, unknown>>,
|
|
276
|
+
keys: { defined: ReadonlySet<string>; allowed: ReadonlySet<string> },
|
|
277
|
+
): string[] {
|
|
278
|
+
return Object.entries(values)
|
|
279
|
+
.filter(([key, value]) => keys.defined.has(key.toLowerCase()) && !keys.allowed.has(key.toLowerCase())
|
|
280
|
+
&& value !== null && value !== undefined && value !== '' && (!Array.isArray(value) || value.length > 0))
|
|
281
|
+
.map(([key]) => key)
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function inapplicableFieldError(fields: string[], message: string): CrudHttpError {
|
|
285
|
+
return new CrudHttpError(400, {
|
|
286
|
+
error: message,
|
|
287
|
+
code: 'activity_type_field_not_applicable',
|
|
288
|
+
fields,
|
|
289
|
+
})
|
|
290
|
+
}
|
|
291
|
+
|
|
202
292
|
async function emitLifecycleEvent(
|
|
203
293
|
ctx: CommandRuntimeContext,
|
|
204
294
|
eventId: string,
|
|
@@ -259,7 +349,20 @@ function createTransactionalDataEngine(ctx: CommandRuntimeContext, em: EntityMan
|
|
|
259
349
|
async function runInTransaction<TResult>(
|
|
260
350
|
em: EntityManager,
|
|
261
351
|
operation: (trx: EntityManager) => Promise<TResult>,
|
|
352
|
+
beforeWrite?: (trx: EntityManager, input?: unknown) => Promise<void>,
|
|
353
|
+
input?: unknown,
|
|
262
354
|
): Promise<TResult> {
|
|
355
|
+
if (typeof em.isInTransaction === 'function' && em.isInTransaction()) {
|
|
356
|
+
// Joining a caller-owned transaction: this helper neither begins nor commits it,
|
|
357
|
+
// so the commit boundary — and with it the point the CRUD side effects become
|
|
358
|
+
// durable — belongs to that caller. The side effects this command emits after
|
|
359
|
+
// `runInTransaction` returns therefore fire BEFORE the outer commit, unlike the
|
|
360
|
+
// self-managed branch below. No in-repo caller passes an open transaction today;
|
|
361
|
+
// a future one must either emit its own side effects after its commit or open a
|
|
362
|
+
// nested savepoint instead.
|
|
363
|
+
await beforeWrite?.(em, input)
|
|
364
|
+
return operation(em)
|
|
365
|
+
}
|
|
263
366
|
// Mirrors the SPEC-018 fix applied to withAtomicFlush: use explicit begin/commit/rollback
|
|
264
367
|
// so the outer EntityManager stays bound to the transaction, and closures over `em` inside
|
|
265
368
|
// `operation` participate in the same transaction. This avoids the em.transactional(cb)
|
|
@@ -274,6 +377,7 @@ async function runInTransaction<TResult>(
|
|
|
274
377
|
}
|
|
275
378
|
await em.begin()
|
|
276
379
|
try {
|
|
380
|
+
await beforeWrite?.(em, input)
|
|
277
381
|
const result = await operation(em)
|
|
278
382
|
await em.commit()
|
|
279
383
|
return result
|
|
@@ -328,6 +432,7 @@ type InteractionGraphValues = {
|
|
|
328
432
|
appearanceIcon: string | null
|
|
329
433
|
appearanceColor: string | null
|
|
330
434
|
durationMinutes: number | null
|
|
435
|
+
timezone: string | null
|
|
331
436
|
location: string | null
|
|
332
437
|
allDay: boolean | null
|
|
333
438
|
recurrenceRule: string | null
|
|
@@ -364,6 +469,7 @@ function buildInteractionGraph(em: EntityManager, values: InteractionGraphValues
|
|
|
364
469
|
appearanceIcon: values.appearanceIcon,
|
|
365
470
|
appearanceColor: values.appearanceColor,
|
|
366
471
|
durationMinutes: values.durationMinutes,
|
|
472
|
+
timezone: values.timezone ?? null,
|
|
367
473
|
location: values.location,
|
|
368
474
|
allDay: values.allDay,
|
|
369
475
|
recurrenceRule: values.recurrenceRule,
|
|
@@ -383,7 +489,7 @@ const createInteractionCommand: CommandHandler<InteractionCreateInput, { interac
|
|
|
383
489
|
async execute(rawInput, ctx) {
|
|
384
490
|
const { parsed, custom } = parseWithCustomFields(interactionCreateSchema, rawInput)
|
|
385
491
|
|
|
386
|
-
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
492
|
+
const em = ctx.transactionalEm ?? (ctx.container.resolve('em') as EntityManager).fork()
|
|
387
493
|
const normalizedAuthor = normalizeAuthorUserId(parsed.authorUserId ?? null, ctx.auth)
|
|
388
494
|
const { interaction, entityId, nextInteractionId } = await runInTransaction(em, async (trx) => {
|
|
389
495
|
const entity = await requireTimelineParentEntity(trx, parsed.entityId, { tenantId: parsed.tenantId, organizationId: parsed.organizationId })
|
|
@@ -394,6 +500,23 @@ const createInteractionCommand: CommandHandler<InteractionCreateInput, { interac
|
|
|
394
500
|
await requireDealInScope(trx, parsed.dealId, entity.tenantId, entity.organizationId)
|
|
395
501
|
}
|
|
396
502
|
|
|
503
|
+
const enforceSelectableType = requestsSelectableTypeEnforcement(rawInput)
|
|
504
|
+
const { behavior, organizationIds } = await resolveInteractionTypeBehavior(
|
|
505
|
+
ctx, trx, entity.tenantId, entity.organizationId, parsed.interactionType, false, enforceSelectableType,
|
|
506
|
+
)
|
|
507
|
+
const inapplicableCore = findInapplicableCoreFields(behavior, parsed)
|
|
508
|
+
const customKeys = Object.keys(custom).length
|
|
509
|
+
? await loadApplicableCustomFieldKeys(trx, entity.tenantId, organizationIds, behavior)
|
|
510
|
+
: null
|
|
511
|
+
const inapplicableCustom = customKeys ? inapplicableCustomFieldKeys(custom, customKeys) : []
|
|
512
|
+
if (enforceSelectableType && (inapplicableCore.length || inapplicableCustom.length)) {
|
|
513
|
+
const { translate } = await resolveTranslations()
|
|
514
|
+
throw inapplicableFieldError(
|
|
515
|
+
[...inapplicableCore, ...inapplicableCustom.map((key) => `cf_${key}`)],
|
|
516
|
+
translate('customers.calendar.activityTypes.errors.fieldNotApplicable', 'Field is not applicable to this activity type'),
|
|
517
|
+
)
|
|
518
|
+
}
|
|
519
|
+
|
|
397
520
|
const interaction = buildInteractionGraph(trx, {
|
|
398
521
|
...(parsed.id ? { id: parsed.id } : {}),
|
|
399
522
|
organizationId: entity.organizationId,
|
|
@@ -413,6 +536,7 @@ const createInteractionCommand: CommandHandler<InteractionCreateInput, { interac
|
|
|
413
536
|
appearanceIcon: parsed.appearanceIcon ?? null,
|
|
414
537
|
appearanceColor: parsed.appearanceColor ?? null,
|
|
415
538
|
durationMinutes: parsed.durationMinutes ?? null,
|
|
539
|
+
timezone: canonicalCalendarTimezone(parsed.timezone) ?? null,
|
|
416
540
|
location: parsed.location ?? null,
|
|
417
541
|
allDay: parsed.allDay ?? null,
|
|
418
542
|
recurrenceRule: parsed.recurrenceRule ?? null,
|
|
@@ -441,7 +565,7 @@ const createInteractionCommand: CommandHandler<InteractionCreateInput, { interac
|
|
|
441
565
|
entityId: entity.id,
|
|
442
566
|
nextInteractionId: projection.nextInteractionId,
|
|
443
567
|
}
|
|
444
|
-
})
|
|
568
|
+
}, ctx.beforeTransactionalWrite, rawInput)
|
|
445
569
|
|
|
446
570
|
const de = (ctx.container.resolve('dataEngine') as DataEngine)
|
|
447
571
|
await emitCrudSideEffects({
|
|
@@ -466,7 +590,7 @@ const createInteractionCommand: CommandHandler<InteractionCreateInput, { interac
|
|
|
466
590
|
return { interactionId: interaction.id, entityId }
|
|
467
591
|
},
|
|
468
592
|
captureAfter: async (_input, result, ctx) => {
|
|
469
|
-
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
593
|
+
const em = ctx.transactionalEm ?? (ctx.container.resolve('em') as EntityManager).fork()
|
|
470
594
|
return await loadInteractionSnapshot(em, result.interactionId)
|
|
471
595
|
},
|
|
472
596
|
buildLog: async ({ result, snapshots }) => {
|
|
@@ -544,6 +668,7 @@ const createInteractionCommand: CommandHandler<InteractionCreateInput, { interac
|
|
|
544
668
|
appearanceIcon: after.interaction.appearanceIcon,
|
|
545
669
|
appearanceColor: after.interaction.appearanceColor,
|
|
546
670
|
durationMinutes: after.interaction.durationMinutes,
|
|
671
|
+
timezone: after.interaction.timezone ?? null,
|
|
547
672
|
location: after.interaction.location,
|
|
548
673
|
allDay: after.interaction.allDay,
|
|
549
674
|
recurrenceRule: after.interaction.recurrenceRule,
|
|
@@ -572,6 +697,7 @@ const createInteractionCommand: CommandHandler<InteractionCreateInput, { interac
|
|
|
572
697
|
interaction.appearanceIcon = after.interaction.appearanceIcon
|
|
573
698
|
interaction.appearanceColor = after.interaction.appearanceColor
|
|
574
699
|
interaction.durationMinutes = after.interaction.durationMinutes
|
|
700
|
+
interaction.timezone = after.interaction.timezone ?? null
|
|
575
701
|
interaction.location = after.interaction.location
|
|
576
702
|
interaction.allDay = after.interaction.allDay
|
|
577
703
|
interaction.recurrenceRule = after.interaction.recurrenceRule
|
|
@@ -631,17 +757,20 @@ const createInteractionCommand: CommandHandler<InteractionCreateInput, { interac
|
|
|
631
757
|
|
|
632
758
|
// ─── Update ─────────────────────────────────────────────────────────
|
|
633
759
|
|
|
760
|
+
async function prepareUpdateInteractionSnapshot(rawInput: InteractionUpdateInput, ctx: CommandRuntimeContext) {
|
|
761
|
+
const { parsed } = parseWithCustomFields(interactionUpdateSchema, rawInput)
|
|
762
|
+
const em = ctx.transactionalEm ?? (ctx.container.resolve('em') as EntityManager).fork()
|
|
763
|
+
const snapshot = await loadInteractionSnapshot(em, parsed.id)
|
|
764
|
+
return snapshot ? { before: snapshot } : {}
|
|
765
|
+
}
|
|
766
|
+
|
|
634
767
|
const updateInteractionCommand: CommandHandler<InteractionUpdateInput, { interactionId: string }> = {
|
|
635
768
|
id: 'customers.interactions.update',
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
639
|
-
const snapshot = await loadInteractionSnapshot(em, parsed.id)
|
|
640
|
-
return snapshot ? { before: snapshot } : {}
|
|
641
|
-
},
|
|
769
|
+
prepare: prepareUpdateInteractionSnapshot,
|
|
770
|
+
prepareSnapshotInsideTransaction: true,
|
|
642
771
|
async execute(rawInput, ctx) {
|
|
643
772
|
const { parsed, custom } = parseWithCustomFields(interactionUpdateSchema, rawInput)
|
|
644
|
-
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
773
|
+
const em = ctx.transactionalEm ?? (ctx.container.resolve('em') as EntityManager).fork()
|
|
645
774
|
const { interaction, projections } = await runInTransaction(em, async (trx) => {
|
|
646
775
|
const interaction = await findOneWithDecryption(trx, CustomerInteraction, { id: parsed.id, deletedAt: null })
|
|
647
776
|
if (!interaction) {
|
|
@@ -663,6 +792,84 @@ const updateInteractionCommand: CommandHandler<InteractionUpdateInput, { interac
|
|
|
663
792
|
request: ctx.request ?? null,
|
|
664
793
|
})
|
|
665
794
|
|
|
795
|
+
const nextType = parsed.interactionType ?? interaction.interactionType
|
|
796
|
+
const changingType = nextType !== interaction.interactionType
|
|
797
|
+
const enforceSelectableType = requestsSelectableTypeEnforcement(rawInput)
|
|
798
|
+
const { behavior, organizationIds } = await resolveInteractionTypeBehavior(
|
|
799
|
+
ctx, trx, interaction.tenantId, interaction.organizationId, nextType, !changingType, enforceSelectableType,
|
|
800
|
+
)
|
|
801
|
+
const currentCoreValues = {
|
|
802
|
+
durationMinutes: interaction.durationMinutes,
|
|
803
|
+
timezone: interaction.timezone ?? null,
|
|
804
|
+
allDay: interaction.allDay,
|
|
805
|
+
recurrenceRule: interaction.recurrenceRule,
|
|
806
|
+
recurrenceEnd: interaction.recurrenceEnd,
|
|
807
|
+
location: interaction.location,
|
|
808
|
+
participants: interaction.participants,
|
|
809
|
+
priority: interaction.priority,
|
|
810
|
+
linkedEntities: interaction.linkedEntities,
|
|
811
|
+
}
|
|
812
|
+
const typeChangeFields = changingType && enforceSelectableType ? omitHiddenCoreClears(behavior, parsed) : parsed
|
|
813
|
+
const coreValues = { ...currentCoreValues, ...typeChangeFields }
|
|
814
|
+
const inapplicableCore = findInapplicableCoreFields(behavior, changingType ? coreValues : parsed)
|
|
815
|
+
const confirmsDiscard = parsed.confirmDiscardInapplicableValues === true
|
|
816
|
+
const currentCustom = changingType && (enforceSelectableType || confirmsDiscard)
|
|
817
|
+
? await loadCustomFieldSnapshot(trx, {
|
|
818
|
+
entityId: INTERACTION_ENTITY_ID,
|
|
819
|
+
recordId: interaction.id,
|
|
820
|
+
tenantId: interaction.tenantId,
|
|
821
|
+
organizationId: interaction.organizationId,
|
|
822
|
+
})
|
|
823
|
+
: {}
|
|
824
|
+
const customValues = changingType ? { ...currentCustom, ...custom } : custom
|
|
825
|
+
const customKeys = Object.keys(customValues).length
|
|
826
|
+
? await loadApplicableCustomFieldKeys(trx, interaction.tenantId, organizationIds, behavior)
|
|
827
|
+
: null
|
|
828
|
+
const inapplicableCustom = customKeys ? inapplicableCustomFieldKeys(customValues, customKeys) : []
|
|
829
|
+
if (!changingType && enforceSelectableType && (inapplicableCore.length || inapplicableCustom.length)) {
|
|
830
|
+
const { translate } = await resolveTranslations()
|
|
831
|
+
throw inapplicableFieldError(
|
|
832
|
+
[...inapplicableCore, ...inapplicableCustom.map((key) => `cf_${key}`)],
|
|
833
|
+
translate('customers.calendar.activityTypes.errors.fieldNotApplicable', 'Field is not applicable to this activity type'),
|
|
834
|
+
)
|
|
835
|
+
}
|
|
836
|
+
if (changingType && enforceSelectableType && (inapplicableCore.length || inapplicableCustom.length) && !confirmsDiscard) {
|
|
837
|
+
const { translate } = await resolveTranslations()
|
|
838
|
+
throw new CrudHttpError(409, {
|
|
839
|
+
error: translate('customers.calendar.activityTypes.errors.changeConfirmationRequired', 'Changing type will clear existing values'),
|
|
840
|
+
code: 'calendar_type_change_confirmation_required',
|
|
841
|
+
fields: [...inapplicableCore, ...inapplicableCustom.map((key) => `cf_${key}`)],
|
|
842
|
+
})
|
|
843
|
+
}
|
|
844
|
+
// The type-change 409 is scoped to the selectable-type opt-in, and stored
|
|
845
|
+
// values are cleared only on an explicit `confirmDiscardInapplicableValues`.
|
|
846
|
+
// A legacy caller (the schedule dialog, the deal composer, the kanban quick
|
|
847
|
+
// actions, a direct API client) sends a partial payload and does not handle
|
|
848
|
+
// the 409, so judging it on the merged stored row rejected switches it has
|
|
849
|
+
// always been able to make — e.g. a Task carrying a priority becoming a
|
|
850
|
+
// Meeting. Those callers keep the pre-catalog behavior exactly: the fields
|
|
851
|
+
// they sent are written and nothing stored is cleared, even when the new
|
|
852
|
+
// type cannot display it.
|
|
853
|
+
const discardsInapplicableValues = changingType && confirmsDiscard
|
|
854
|
+
const clearedCore = discardsInapplicableValues
|
|
855
|
+
? clearInapplicableCoreFields(behavior, coreValues)
|
|
856
|
+
: {}
|
|
857
|
+
const clearedCustom = discardsInapplicableValues
|
|
858
|
+
? Object.fromEntries(inapplicableCustom.map((key) => [key, Array.isArray(customValues[key]) ? [] : null]))
|
|
859
|
+
: {}
|
|
860
|
+
// Narrowing a same-type edit (ignoring explicit clears of hidden core fields,
|
|
861
|
+
// dropping custom fields outside the type's fieldsets) is part of the opt-in
|
|
862
|
+
// contract too: a legacy caller's write is accepted as sent.
|
|
863
|
+
const sameTypeFields = changingType
|
|
864
|
+
? typeChangeFields
|
|
865
|
+
: preserveHiddenCoreValuesOnSameTypeEdit(behavior, currentCoreValues, parsed, {
|
|
866
|
+
ignoreHiddenClears: enforceSelectableType,
|
|
867
|
+
})
|
|
868
|
+
const sameTypeCustom = changingType || !customKeys || !enforceSelectableType
|
|
869
|
+
? custom
|
|
870
|
+
: Object.fromEntries(Object.entries(custom).filter(([key]) =>
|
|
871
|
+
!customKeys.defined.has(key.toLowerCase()) || customKeys.allowed.has(key.toLowerCase())))
|
|
872
|
+
|
|
666
873
|
// Email visibility is an access-controlled field: only the interaction's
|
|
667
874
|
// author may change a
|
|
668
875
|
// private email's visibility (mirrors the dedicated PATCH .../visibility
|
|
@@ -718,24 +925,36 @@ const updateInteractionCommand: CommandHandler<InteractionUpdateInput, { interac
|
|
|
718
925
|
if (parsed.body !== undefined) interaction.body = parsed.body ?? null
|
|
719
926
|
if (parsed.status !== undefined) interaction.status = parsed.status
|
|
720
927
|
if (parsed.scheduledAt !== undefined) interaction.scheduledAt = parsed.scheduledAt ?? null
|
|
928
|
+
if (parsed.timezone !== undefined) interaction.timezone = canonicalCalendarTimezone(parsed.timezone) ?? null
|
|
721
929
|
if (parsed.occurredAt !== undefined) interaction.occurredAt = parsed.occurredAt ?? null
|
|
722
|
-
if (
|
|
930
|
+
if (sameTypeFields.priority !== undefined) interaction.priority = sameTypeFields.priority as number | null
|
|
723
931
|
if (parsed.authorUserId !== undefined) interaction.authorUserId = parsed.authorUserId ?? null
|
|
724
932
|
if (parsed.ownerUserId !== undefined) interaction.ownerUserId = parsed.ownerUserId ?? null
|
|
725
933
|
if (parsed.appearanceIcon !== undefined) interaction.appearanceIcon = parsed.appearanceIcon ?? null
|
|
726
934
|
if (parsed.appearanceColor !== undefined) interaction.appearanceColor = parsed.appearanceColor ?? null
|
|
727
935
|
if (parsed.pinned !== undefined) interaction.pinned = parsed.pinned
|
|
728
|
-
if (
|
|
729
|
-
if (
|
|
730
|
-
if (
|
|
731
|
-
if (
|
|
732
|
-
if (
|
|
733
|
-
if (
|
|
936
|
+
if (sameTypeFields.durationMinutes !== undefined) interaction.durationMinutes = sameTypeFields.durationMinutes as number | null
|
|
937
|
+
if (sameTypeFields.location !== undefined) interaction.location = sameTypeFields.location as string | null
|
|
938
|
+
if (sameTypeFields.allDay !== undefined) interaction.allDay = sameTypeFields.allDay as boolean | null
|
|
939
|
+
if (sameTypeFields.recurrenceRule !== undefined) interaction.recurrenceRule = sameTypeFields.recurrenceRule as string | null
|
|
940
|
+
if (sameTypeFields.recurrenceEnd !== undefined) interaction.recurrenceEnd = sameTypeFields.recurrenceEnd as Date | null
|
|
941
|
+
if (sameTypeFields.participants !== undefined) interaction.participants = sameTypeFields.participants as InteractionSnapshot['interaction']['participants']
|
|
734
942
|
if (parsed.reminderMinutes !== undefined) interaction.reminderMinutes = parsed.reminderMinutes ?? null
|
|
735
943
|
if (parsed.visibility !== undefined) interaction.visibility = parsed.visibility ?? null
|
|
736
|
-
if (
|
|
944
|
+
if (sameTypeFields.linkedEntities !== undefined) interaction.linkedEntities = sameTypeFields.linkedEntities as InteractionSnapshot['interaction']['linkedEntities']
|
|
737
945
|
if (parsed.guestPermissions !== undefined) interaction.guestPermissions = parsed.guestPermissions ?? null
|
|
738
946
|
|
|
947
|
+
for (const [field, value] of Object.entries(clearedCore)) {
|
|
948
|
+
if (field === 'linkedEntities') interaction.linkedEntities = value as InteractionSnapshot['interaction']['linkedEntities']
|
|
949
|
+
else if (field === 'durationMinutes') interaction.durationMinutes = null
|
|
950
|
+
else if (field === 'allDay') interaction.allDay = null
|
|
951
|
+
else if (field === 'recurrenceRule') interaction.recurrenceRule = null
|
|
952
|
+
else if (field === 'recurrenceEnd') interaction.recurrenceEnd = null
|
|
953
|
+
else if (field === 'location') interaction.location = null
|
|
954
|
+
else if (field === 'participants') interaction.participants = null
|
|
955
|
+
else if (field === 'priority') interaction.priority = null
|
|
956
|
+
}
|
|
957
|
+
|
|
739
958
|
await trx.flush()
|
|
740
959
|
|
|
741
960
|
const entityId = typeof interaction.entity === 'string' ? interaction.entity : interaction.entity.id
|
|
@@ -744,7 +963,7 @@ const updateInteractionCommand: CommandHandler<InteractionUpdateInput, { interac
|
|
|
744
963
|
interaction.id,
|
|
745
964
|
interaction.organizationId,
|
|
746
965
|
interaction.tenantId,
|
|
747
|
-
|
|
966
|
+
{ ...sameTypeCustom, ...clearedCustom },
|
|
748
967
|
)
|
|
749
968
|
|
|
750
969
|
const projection = await recomputeNextInteraction(trx, entityId)
|
|
@@ -760,7 +979,7 @@ const updateInteractionCommand: CommandHandler<InteractionUpdateInput, { interac
|
|
|
760
979
|
}
|
|
761
980
|
|
|
762
981
|
return { interaction, projections }
|
|
763
|
-
})
|
|
982
|
+
}, ctx.beforeTransactionalWrite, rawInput)
|
|
764
983
|
|
|
765
984
|
const de = (ctx.container.resolve('dataEngine') as DataEngine)
|
|
766
985
|
await emitCrudSideEffects({
|
|
@@ -787,7 +1006,7 @@ const updateInteractionCommand: CommandHandler<InteractionUpdateInput, { interac
|
|
|
787
1006
|
return { interactionId: interaction.id }
|
|
788
1007
|
},
|
|
789
1008
|
captureAfter: async (_input, result, ctx) => {
|
|
790
|
-
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
1009
|
+
const em = ctx.transactionalEm ?? (ctx.container.resolve('em') as EntityManager).fork()
|
|
791
1010
|
return await loadInteractionSnapshot(em, result.interactionId)
|
|
792
1011
|
},
|
|
793
1012
|
buildLog: async ({ snapshots }) => {
|
|
@@ -842,6 +1061,7 @@ const updateInteractionCommand: CommandHandler<InteractionUpdateInput, { interac
|
|
|
842
1061
|
appearanceIcon: before.interaction.appearanceIcon,
|
|
843
1062
|
appearanceColor: before.interaction.appearanceColor,
|
|
844
1063
|
durationMinutes: before.interaction.durationMinutes,
|
|
1064
|
+
timezone: before.interaction.timezone ?? null,
|
|
845
1065
|
location: before.interaction.location,
|
|
846
1066
|
allDay: before.interaction.allDay,
|
|
847
1067
|
recurrenceRule: before.interaction.recurrenceRule,
|
|
@@ -871,6 +1091,7 @@ const updateInteractionCommand: CommandHandler<InteractionUpdateInput, { interac
|
|
|
871
1091
|
interaction.appearanceIcon = before.interaction.appearanceIcon
|
|
872
1092
|
interaction.appearanceColor = before.interaction.appearanceColor
|
|
873
1093
|
interaction.durationMinutes = before.interaction.durationMinutes
|
|
1094
|
+
interaction.timezone = before.interaction.timezone ?? null
|
|
874
1095
|
interaction.location = before.interaction.location
|
|
875
1096
|
interaction.allDay = before.interaction.allDay
|
|
876
1097
|
interaction.recurrenceRule = before.interaction.recurrenceRule
|
|
@@ -1322,6 +1543,7 @@ const deleteInteractionCommand: CommandHandler<{ body?: Record<string, unknown>;
|
|
|
1322
1543
|
appearanceIcon: before.interaction.appearanceIcon,
|
|
1323
1544
|
appearanceColor: before.interaction.appearanceColor,
|
|
1324
1545
|
durationMinutes: before.interaction.durationMinutes,
|
|
1546
|
+
timezone: before.interaction.timezone ?? null,
|
|
1325
1547
|
location: before.interaction.location,
|
|
1326
1548
|
allDay: before.interaction.allDay,
|
|
1327
1549
|
recurrenceRule: before.interaction.recurrenceRule,
|
|
@@ -1352,6 +1574,7 @@ const deleteInteractionCommand: CommandHandler<{ body?: Record<string, unknown>;
|
|
|
1352
1574
|
interaction.appearanceIcon = before.interaction.appearanceIcon
|
|
1353
1575
|
interaction.appearanceColor = before.interaction.appearanceColor
|
|
1354
1576
|
interaction.durationMinutes = before.interaction.durationMinutes
|
|
1577
|
+
interaction.timezone = before.interaction.timezone ?? null
|
|
1355
1578
|
interaction.location = before.interaction.location
|
|
1356
1579
|
interaction.allDay = before.interaction.allDay
|
|
1357
1580
|
interaction.recurrenceRule = before.interaction.recurrenceRule
|