@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
|
@@ -42,6 +42,11 @@ import {
|
|
|
42
42
|
} from "../lib/interactionStatus.js";
|
|
43
43
|
import { canChangeEmailVisibility } from "../lib/visibilityFilter.js";
|
|
44
44
|
import { createLogger } from "@open-mercato/shared/lib/logger";
|
|
45
|
+
import { loadCustomFieldDefinitionIndex } from "@open-mercato/shared/lib/crud/custom-fields";
|
|
46
|
+
import { resolveCatalogEventType, resolveScopedCalendarEventTypes } from "../lib/calendar/eventTypeResolver.js";
|
|
47
|
+
import { clearInapplicableCoreFields, findInapplicableCoreFields, omitHiddenCoreClears, preserveHiddenCoreValuesOnSameTypeEdit } from "../lib/calendar/interactionApplicability.js";
|
|
48
|
+
import { calendarEventTypes } from "../calendar-event-types.js";
|
|
49
|
+
import { canonicalCalendarTimezone } from "../lib/calendar/timezone.js";
|
|
45
50
|
const logger = createLogger("customers");
|
|
46
51
|
const INTERACTION_ENTITY_ID = "customers:customer_interaction";
|
|
47
52
|
const interactionCrudIndexer = {
|
|
@@ -100,6 +105,7 @@ async function loadInteractionSnapshot(em, id) {
|
|
|
100
105
|
appearanceColor: interaction.appearanceColor ?? null,
|
|
101
106
|
source: interaction.source ?? null,
|
|
102
107
|
durationMinutes: interaction.durationMinutes ?? null,
|
|
108
|
+
timezone: interaction.timezone ?? null,
|
|
103
109
|
location: interaction.location ?? null,
|
|
104
110
|
allDay: interaction.allDay ?? null,
|
|
105
111
|
recurrenceRule: interaction.recurrenceRule ?? null,
|
|
@@ -125,6 +131,61 @@ async function setInteractionCustomFields(dataEngine, interactionId, organizatio
|
|
|
125
131
|
notify: false
|
|
126
132
|
});
|
|
127
133
|
}
|
|
134
|
+
function requestsSelectableTypeEnforcement(input) {
|
|
135
|
+
return input !== null && typeof input === "object" && "enforceSelectableType" in input && input.enforceSelectableType === true;
|
|
136
|
+
}
|
|
137
|
+
async function resolveInteractionTypeBehavior(ctx, em, tenantId, organizationId, key, unchangedType, enforceSelectableType) {
|
|
138
|
+
const hierarchy = ctx.container.resolve("organizationHierarchyService");
|
|
139
|
+
const ancestors = await hierarchy.resolveAncestorIds({ tenantId, organizationId });
|
|
140
|
+
if (ancestors === null) throw notFound("Organization not found");
|
|
141
|
+
const organizationIds = [organizationId, ...ancestors.slice().reverse()];
|
|
142
|
+
let catalog;
|
|
143
|
+
try {
|
|
144
|
+
catalog = await resolveScopedCalendarEventTypes({
|
|
145
|
+
em,
|
|
146
|
+
tenantId,
|
|
147
|
+
organizationId,
|
|
148
|
+
readableOrganizationIds: organizationIds
|
|
149
|
+
});
|
|
150
|
+
} catch {
|
|
151
|
+
const { translate: translate2 } = await resolveTranslations();
|
|
152
|
+
throw new CrudHttpError(503, {
|
|
153
|
+
error: translate2("customers.calendar.activityTypes.errors.loadFailed", "Failed to load activity types"),
|
|
154
|
+
code: "activity_type_catalog_unavailable",
|
|
155
|
+
retryable: true
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
const selected = resolveCatalogEventType(catalog, key, { includeHistorical: unchangedType || !enforceSelectableType });
|
|
159
|
+
if (selected && (unchangedType || !enforceSelectableType || selected.selectable)) {
|
|
160
|
+
return { behavior: selected.behavior, organizationIds };
|
|
161
|
+
}
|
|
162
|
+
if (unchangedType) return { behavior: calendarEventTypes[0].behavior, organizationIds };
|
|
163
|
+
const { translate } = await resolveTranslations();
|
|
164
|
+
throw new CrudHttpError(400, {
|
|
165
|
+
error: translate("customers.calendar.activityTypes.errors.unavailable", "Activity type is unavailable"),
|
|
166
|
+
code: "activity_type_unavailable"
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
async function loadApplicableCustomFieldKeys(em, tenantId, organizationIds, behavior) {
|
|
170
|
+
const options = { em, entityIds: [INTERACTION_ENTITY_ID], tenantId, organizationIds };
|
|
171
|
+
const all = await loadCustomFieldDefinitionIndex(options);
|
|
172
|
+
const fieldsets = behavior.customFieldsetIds.map((id) => id === "__general__" ? "" : id);
|
|
173
|
+
const applicable = fieldsets.length ? await loadCustomFieldDefinitionIndex({ ...options, fieldset: fieldsets }) : all;
|
|
174
|
+
return {
|
|
175
|
+
defined: new Set([...all.keys()].map((key) => key.toLowerCase())),
|
|
176
|
+
allowed: new Set([...applicable.keys()].map((key) => key.toLowerCase()))
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
function inapplicableCustomFieldKeys(values, keys) {
|
|
180
|
+
return Object.entries(values).filter(([key, value]) => keys.defined.has(key.toLowerCase()) && !keys.allowed.has(key.toLowerCase()) && value !== null && value !== void 0 && value !== "" && (!Array.isArray(value) || value.length > 0)).map(([key]) => key);
|
|
181
|
+
}
|
|
182
|
+
function inapplicableFieldError(fields, message) {
|
|
183
|
+
return new CrudHttpError(400, {
|
|
184
|
+
error: message,
|
|
185
|
+
code: "activity_type_field_not_applicable",
|
|
186
|
+
fields
|
|
187
|
+
});
|
|
188
|
+
}
|
|
128
189
|
async function emitLifecycleEvent(ctx, eventId, payload) {
|
|
129
190
|
let bus = null;
|
|
130
191
|
try {
|
|
@@ -159,13 +220,18 @@ function createTransactionalDataEngine(ctx, em) {
|
|
|
159
220
|
}
|
|
160
221
|
return new DefaultDataEngine(em, ctx.container);
|
|
161
222
|
}
|
|
162
|
-
async function runInTransaction(em, operation) {
|
|
223
|
+
async function runInTransaction(em, operation, beforeWrite, input) {
|
|
224
|
+
if (typeof em.isInTransaction === "function" && em.isInTransaction()) {
|
|
225
|
+
await beforeWrite?.(em, input);
|
|
226
|
+
return operation(em);
|
|
227
|
+
}
|
|
163
228
|
const supportsBegin = typeof em.begin === "function" && typeof em.commit === "function" && typeof em.rollback === "function";
|
|
164
229
|
if (!supportsBegin) {
|
|
165
230
|
return operation(em);
|
|
166
231
|
}
|
|
167
232
|
await em.begin();
|
|
168
233
|
try {
|
|
234
|
+
await beforeWrite?.(em, input);
|
|
169
235
|
const result = await operation(em);
|
|
170
236
|
await em.commit();
|
|
171
237
|
return result;
|
|
@@ -212,6 +278,7 @@ function buildInteractionGraph(em, values) {
|
|
|
212
278
|
appearanceIcon: values.appearanceIcon,
|
|
213
279
|
appearanceColor: values.appearanceColor,
|
|
214
280
|
durationMinutes: values.durationMinutes,
|
|
281
|
+
timezone: values.timezone ?? null,
|
|
215
282
|
location: values.location,
|
|
216
283
|
allDay: values.allDay,
|
|
217
284
|
recurrenceRule: values.recurrenceRule,
|
|
@@ -229,7 +296,7 @@ const createInteractionCommand = {
|
|
|
229
296
|
id: "customers.interactions.create",
|
|
230
297
|
async execute(rawInput, ctx) {
|
|
231
298
|
const { parsed, custom } = parseWithCustomFields(interactionCreateSchema, rawInput);
|
|
232
|
-
const em = ctx.container.resolve("em").fork();
|
|
299
|
+
const em = ctx.transactionalEm ?? ctx.container.resolve("em").fork();
|
|
233
300
|
const normalizedAuthor = normalizeAuthorUserId(parsed.authorUserId ?? null, ctx.auth);
|
|
234
301
|
const { interaction, entityId, nextInteractionId } = await runInTransaction(em, async (trx) => {
|
|
235
302
|
const entity = await requireTimelineParentEntity(trx, parsed.entityId, { tenantId: parsed.tenantId, organizationId: parsed.organizationId });
|
|
@@ -238,6 +305,26 @@ const createInteractionCommand = {
|
|
|
238
305
|
if (parsed.dealId) {
|
|
239
306
|
await requireDealInScope(trx, parsed.dealId, entity.tenantId, entity.organizationId);
|
|
240
307
|
}
|
|
308
|
+
const enforceSelectableType = requestsSelectableTypeEnforcement(rawInput);
|
|
309
|
+
const { behavior, organizationIds } = await resolveInteractionTypeBehavior(
|
|
310
|
+
ctx,
|
|
311
|
+
trx,
|
|
312
|
+
entity.tenantId,
|
|
313
|
+
entity.organizationId,
|
|
314
|
+
parsed.interactionType,
|
|
315
|
+
false,
|
|
316
|
+
enforceSelectableType
|
|
317
|
+
);
|
|
318
|
+
const inapplicableCore = findInapplicableCoreFields(behavior, parsed);
|
|
319
|
+
const customKeys = Object.keys(custom).length ? await loadApplicableCustomFieldKeys(trx, entity.tenantId, organizationIds, behavior) : null;
|
|
320
|
+
const inapplicableCustom = customKeys ? inapplicableCustomFieldKeys(custom, customKeys) : [];
|
|
321
|
+
if (enforceSelectableType && (inapplicableCore.length || inapplicableCustom.length)) {
|
|
322
|
+
const { translate } = await resolveTranslations();
|
|
323
|
+
throw inapplicableFieldError(
|
|
324
|
+
[...inapplicableCore, ...inapplicableCustom.map((key) => `cf_${key}`)],
|
|
325
|
+
translate("customers.calendar.activityTypes.errors.fieldNotApplicable", "Field is not applicable to this activity type")
|
|
326
|
+
);
|
|
327
|
+
}
|
|
241
328
|
const interaction2 = buildInteractionGraph(trx, {
|
|
242
329
|
...parsed.id ? { id: parsed.id } : {},
|
|
243
330
|
organizationId: entity.organizationId,
|
|
@@ -257,6 +344,7 @@ const createInteractionCommand = {
|
|
|
257
344
|
appearanceIcon: parsed.appearanceIcon ?? null,
|
|
258
345
|
appearanceColor: parsed.appearanceColor ?? null,
|
|
259
346
|
durationMinutes: parsed.durationMinutes ?? null,
|
|
347
|
+
timezone: canonicalCalendarTimezone(parsed.timezone) ?? null,
|
|
260
348
|
location: parsed.location ?? null,
|
|
261
349
|
allDay: parsed.allDay ?? null,
|
|
262
350
|
recurrenceRule: parsed.recurrenceRule ?? null,
|
|
@@ -282,7 +370,7 @@ const createInteractionCommand = {
|
|
|
282
370
|
entityId: entity.id,
|
|
283
371
|
nextInteractionId: projection.nextInteractionId
|
|
284
372
|
};
|
|
285
|
-
});
|
|
373
|
+
}, ctx.beforeTransactionalWrite, rawInput);
|
|
286
374
|
const de = ctx.container.resolve("dataEngine");
|
|
287
375
|
await emitCrudSideEffects({
|
|
288
376
|
dataEngine: de,
|
|
@@ -305,7 +393,7 @@ const createInteractionCommand = {
|
|
|
305
393
|
return { interactionId: interaction.id, entityId };
|
|
306
394
|
},
|
|
307
395
|
captureAfter: async (_input, result, ctx) => {
|
|
308
|
-
const em = ctx.container.resolve("em").fork();
|
|
396
|
+
const em = ctx.transactionalEm ?? ctx.container.resolve("em").fork();
|
|
309
397
|
return await loadInteractionSnapshot(em, result.interactionId);
|
|
310
398
|
},
|
|
311
399
|
buildLog: async ({ result, snapshots }) => {
|
|
@@ -383,6 +471,7 @@ const createInteractionCommand = {
|
|
|
383
471
|
appearanceIcon: after.interaction.appearanceIcon,
|
|
384
472
|
appearanceColor: after.interaction.appearanceColor,
|
|
385
473
|
durationMinutes: after.interaction.durationMinutes,
|
|
474
|
+
timezone: after.interaction.timezone ?? null,
|
|
386
475
|
location: after.interaction.location,
|
|
387
476
|
allDay: after.interaction.allDay,
|
|
388
477
|
recurrenceRule: after.interaction.recurrenceRule,
|
|
@@ -411,6 +500,7 @@ const createInteractionCommand = {
|
|
|
411
500
|
interaction2.appearanceIcon = after.interaction.appearanceIcon;
|
|
412
501
|
interaction2.appearanceColor = after.interaction.appearanceColor;
|
|
413
502
|
interaction2.durationMinutes = after.interaction.durationMinutes;
|
|
503
|
+
interaction2.timezone = after.interaction.timezone ?? null;
|
|
414
504
|
interaction2.location = after.interaction.location;
|
|
415
505
|
interaction2.allDay = after.interaction.allDay;
|
|
416
506
|
interaction2.recurrenceRule = after.interaction.recurrenceRule;
|
|
@@ -462,17 +552,19 @@ const createInteractionCommand = {
|
|
|
462
552
|
return { interactionId: interaction.id, entityId: after.interaction.entityId };
|
|
463
553
|
}
|
|
464
554
|
};
|
|
555
|
+
async function prepareUpdateInteractionSnapshot(rawInput, ctx) {
|
|
556
|
+
const { parsed } = parseWithCustomFields(interactionUpdateSchema, rawInput);
|
|
557
|
+
const em = ctx.transactionalEm ?? ctx.container.resolve("em").fork();
|
|
558
|
+
const snapshot = await loadInteractionSnapshot(em, parsed.id);
|
|
559
|
+
return snapshot ? { before: snapshot } : {};
|
|
560
|
+
}
|
|
465
561
|
const updateInteractionCommand = {
|
|
466
562
|
id: "customers.interactions.update",
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
const em = ctx.container.resolve("em").fork();
|
|
470
|
-
const snapshot = await loadInteractionSnapshot(em, parsed.id);
|
|
471
|
-
return snapshot ? { before: snapshot } : {};
|
|
472
|
-
},
|
|
563
|
+
prepare: prepareUpdateInteractionSnapshot,
|
|
564
|
+
prepareSnapshotInsideTransaction: true,
|
|
473
565
|
async execute(rawInput, ctx) {
|
|
474
566
|
const { parsed, custom } = parseWithCustomFields(interactionUpdateSchema, rawInput);
|
|
475
|
-
const em = ctx.container.resolve("em").fork();
|
|
567
|
+
const em = ctx.transactionalEm ?? ctx.container.resolve("em").fork();
|
|
476
568
|
const { interaction, projections } = await runInTransaction(em, async (trx) => {
|
|
477
569
|
const interaction2 = await findOneWithDecryption(trx, CustomerInteraction, { id: parsed.id, deletedAt: null });
|
|
478
570
|
if (!interaction2) {
|
|
@@ -487,6 +579,64 @@ const updateInteractionCommand = {
|
|
|
487
579
|
current: interaction2.updatedAt,
|
|
488
580
|
request: ctx.request ?? null
|
|
489
581
|
});
|
|
582
|
+
const nextType = parsed.interactionType ?? interaction2.interactionType;
|
|
583
|
+
const changingType = nextType !== interaction2.interactionType;
|
|
584
|
+
const enforceSelectableType = requestsSelectableTypeEnforcement(rawInput);
|
|
585
|
+
const { behavior, organizationIds } = await resolveInteractionTypeBehavior(
|
|
586
|
+
ctx,
|
|
587
|
+
trx,
|
|
588
|
+
interaction2.tenantId,
|
|
589
|
+
interaction2.organizationId,
|
|
590
|
+
nextType,
|
|
591
|
+
!changingType,
|
|
592
|
+
enforceSelectableType
|
|
593
|
+
);
|
|
594
|
+
const currentCoreValues = {
|
|
595
|
+
durationMinutes: interaction2.durationMinutes,
|
|
596
|
+
timezone: interaction2.timezone ?? null,
|
|
597
|
+
allDay: interaction2.allDay,
|
|
598
|
+
recurrenceRule: interaction2.recurrenceRule,
|
|
599
|
+
recurrenceEnd: interaction2.recurrenceEnd,
|
|
600
|
+
location: interaction2.location,
|
|
601
|
+
participants: interaction2.participants,
|
|
602
|
+
priority: interaction2.priority,
|
|
603
|
+
linkedEntities: interaction2.linkedEntities
|
|
604
|
+
};
|
|
605
|
+
const typeChangeFields = changingType && enforceSelectableType ? omitHiddenCoreClears(behavior, parsed) : parsed;
|
|
606
|
+
const coreValues = { ...currentCoreValues, ...typeChangeFields };
|
|
607
|
+
const inapplicableCore = findInapplicableCoreFields(behavior, changingType ? coreValues : parsed);
|
|
608
|
+
const confirmsDiscard = parsed.confirmDiscardInapplicableValues === true;
|
|
609
|
+
const currentCustom = changingType && (enforceSelectableType || confirmsDiscard) ? await loadCustomFieldSnapshot(trx, {
|
|
610
|
+
entityId: INTERACTION_ENTITY_ID,
|
|
611
|
+
recordId: interaction2.id,
|
|
612
|
+
tenantId: interaction2.tenantId,
|
|
613
|
+
organizationId: interaction2.organizationId
|
|
614
|
+
}) : {};
|
|
615
|
+
const customValues = changingType ? { ...currentCustom, ...custom } : custom;
|
|
616
|
+
const customKeys = Object.keys(customValues).length ? await loadApplicableCustomFieldKeys(trx, interaction2.tenantId, organizationIds, behavior) : null;
|
|
617
|
+
const inapplicableCustom = customKeys ? inapplicableCustomFieldKeys(customValues, customKeys) : [];
|
|
618
|
+
if (!changingType && enforceSelectableType && (inapplicableCore.length || inapplicableCustom.length)) {
|
|
619
|
+
const { translate } = await resolveTranslations();
|
|
620
|
+
throw inapplicableFieldError(
|
|
621
|
+
[...inapplicableCore, ...inapplicableCustom.map((key) => `cf_${key}`)],
|
|
622
|
+
translate("customers.calendar.activityTypes.errors.fieldNotApplicable", "Field is not applicable to this activity type")
|
|
623
|
+
);
|
|
624
|
+
}
|
|
625
|
+
if (changingType && enforceSelectableType && (inapplicableCore.length || inapplicableCustom.length) && !confirmsDiscard) {
|
|
626
|
+
const { translate } = await resolveTranslations();
|
|
627
|
+
throw new CrudHttpError(409, {
|
|
628
|
+
error: translate("customers.calendar.activityTypes.errors.changeConfirmationRequired", "Changing type will clear existing values"),
|
|
629
|
+
code: "calendar_type_change_confirmation_required",
|
|
630
|
+
fields: [...inapplicableCore, ...inapplicableCustom.map((key) => `cf_${key}`)]
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
const discardsInapplicableValues = changingType && confirmsDiscard;
|
|
634
|
+
const clearedCore = discardsInapplicableValues ? clearInapplicableCoreFields(behavior, coreValues) : {};
|
|
635
|
+
const clearedCustom = discardsInapplicableValues ? Object.fromEntries(inapplicableCustom.map((key) => [key, Array.isArray(customValues[key]) ? [] : null])) : {};
|
|
636
|
+
const sameTypeFields = changingType ? typeChangeFields : preserveHiddenCoreValuesOnSameTypeEdit(behavior, currentCoreValues, parsed, {
|
|
637
|
+
ignoreHiddenClears: enforceSelectableType
|
|
638
|
+
});
|
|
639
|
+
const sameTypeCustom = changingType || !customKeys || !enforceSelectableType ? custom : Object.fromEntries(Object.entries(custom).filter(([key]) => !customKeys.defined.has(key.toLowerCase()) || customKeys.allowed.has(key.toLowerCase())));
|
|
490
640
|
if (parsed.visibility !== void 0 && interaction2.interactionType === "email" && (parsed.visibility ?? null) !== (interaction2.visibility ?? null)) {
|
|
491
641
|
const actorUserId = ctx.auth?.sub ?? null;
|
|
492
642
|
if (!canChangeEmailVisibility({
|
|
@@ -519,23 +669,34 @@ const updateInteractionCommand = {
|
|
|
519
669
|
if (parsed.body !== void 0) interaction2.body = parsed.body ?? null;
|
|
520
670
|
if (parsed.status !== void 0) interaction2.status = parsed.status;
|
|
521
671
|
if (parsed.scheduledAt !== void 0) interaction2.scheduledAt = parsed.scheduledAt ?? null;
|
|
672
|
+
if (parsed.timezone !== void 0) interaction2.timezone = canonicalCalendarTimezone(parsed.timezone) ?? null;
|
|
522
673
|
if (parsed.occurredAt !== void 0) interaction2.occurredAt = parsed.occurredAt ?? null;
|
|
523
|
-
if (
|
|
674
|
+
if (sameTypeFields.priority !== void 0) interaction2.priority = sameTypeFields.priority;
|
|
524
675
|
if (parsed.authorUserId !== void 0) interaction2.authorUserId = parsed.authorUserId ?? null;
|
|
525
676
|
if (parsed.ownerUserId !== void 0) interaction2.ownerUserId = parsed.ownerUserId ?? null;
|
|
526
677
|
if (parsed.appearanceIcon !== void 0) interaction2.appearanceIcon = parsed.appearanceIcon ?? null;
|
|
527
678
|
if (parsed.appearanceColor !== void 0) interaction2.appearanceColor = parsed.appearanceColor ?? null;
|
|
528
679
|
if (parsed.pinned !== void 0) interaction2.pinned = parsed.pinned;
|
|
529
|
-
if (
|
|
530
|
-
if (
|
|
531
|
-
if (
|
|
532
|
-
if (
|
|
533
|
-
if (
|
|
534
|
-
if (
|
|
680
|
+
if (sameTypeFields.durationMinutes !== void 0) interaction2.durationMinutes = sameTypeFields.durationMinutes;
|
|
681
|
+
if (sameTypeFields.location !== void 0) interaction2.location = sameTypeFields.location;
|
|
682
|
+
if (sameTypeFields.allDay !== void 0) interaction2.allDay = sameTypeFields.allDay;
|
|
683
|
+
if (sameTypeFields.recurrenceRule !== void 0) interaction2.recurrenceRule = sameTypeFields.recurrenceRule;
|
|
684
|
+
if (sameTypeFields.recurrenceEnd !== void 0) interaction2.recurrenceEnd = sameTypeFields.recurrenceEnd;
|
|
685
|
+
if (sameTypeFields.participants !== void 0) interaction2.participants = sameTypeFields.participants;
|
|
535
686
|
if (parsed.reminderMinutes !== void 0) interaction2.reminderMinutes = parsed.reminderMinutes ?? null;
|
|
536
687
|
if (parsed.visibility !== void 0) interaction2.visibility = parsed.visibility ?? null;
|
|
537
|
-
if (
|
|
688
|
+
if (sameTypeFields.linkedEntities !== void 0) interaction2.linkedEntities = sameTypeFields.linkedEntities;
|
|
538
689
|
if (parsed.guestPermissions !== void 0) interaction2.guestPermissions = parsed.guestPermissions ?? null;
|
|
690
|
+
for (const [field, value] of Object.entries(clearedCore)) {
|
|
691
|
+
if (field === "linkedEntities") interaction2.linkedEntities = value;
|
|
692
|
+
else if (field === "durationMinutes") interaction2.durationMinutes = null;
|
|
693
|
+
else if (field === "allDay") interaction2.allDay = null;
|
|
694
|
+
else if (field === "recurrenceRule") interaction2.recurrenceRule = null;
|
|
695
|
+
else if (field === "recurrenceEnd") interaction2.recurrenceEnd = null;
|
|
696
|
+
else if (field === "location") interaction2.location = null;
|
|
697
|
+
else if (field === "participants") interaction2.participants = null;
|
|
698
|
+
else if (field === "priority") interaction2.priority = null;
|
|
699
|
+
}
|
|
539
700
|
await trx.flush();
|
|
540
701
|
const entityId = typeof interaction2.entity === "string" ? interaction2.entity : interaction2.entity.id;
|
|
541
702
|
await setInteractionCustomFields(
|
|
@@ -543,7 +704,7 @@ const updateInteractionCommand = {
|
|
|
543
704
|
interaction2.id,
|
|
544
705
|
interaction2.organizationId,
|
|
545
706
|
interaction2.tenantId,
|
|
546
|
-
|
|
707
|
+
{ ...sameTypeCustom, ...clearedCustom }
|
|
547
708
|
);
|
|
548
709
|
const projection = await recomputeNextInteraction(trx, entityId);
|
|
549
710
|
const projections2 = [
|
|
@@ -554,7 +715,7 @@ const updateInteractionCommand = {
|
|
|
554
715
|
projections2.push({ entityId: previousEntityId, nextInteractionId: previousProjection.nextInteractionId });
|
|
555
716
|
}
|
|
556
717
|
return { interaction: interaction2, projections: projections2 };
|
|
557
|
-
});
|
|
718
|
+
}, ctx.beforeTransactionalWrite, rawInput);
|
|
558
719
|
const de = ctx.container.resolve("dataEngine");
|
|
559
720
|
await emitCrudSideEffects({
|
|
560
721
|
dataEngine: de,
|
|
@@ -579,7 +740,7 @@ const updateInteractionCommand = {
|
|
|
579
740
|
return { interactionId: interaction.id };
|
|
580
741
|
},
|
|
581
742
|
captureAfter: async (_input, result, ctx) => {
|
|
582
|
-
const em = ctx.container.resolve("em").fork();
|
|
743
|
+
const em = ctx.transactionalEm ?? ctx.container.resolve("em").fork();
|
|
583
744
|
return await loadInteractionSnapshot(em, result.interactionId);
|
|
584
745
|
},
|
|
585
746
|
buildLog: async ({ snapshots }) => {
|
|
@@ -633,6 +794,7 @@ const updateInteractionCommand = {
|
|
|
633
794
|
appearanceIcon: before.interaction.appearanceIcon,
|
|
634
795
|
appearanceColor: before.interaction.appearanceColor,
|
|
635
796
|
durationMinutes: before.interaction.durationMinutes,
|
|
797
|
+
timezone: before.interaction.timezone ?? null,
|
|
636
798
|
location: before.interaction.location,
|
|
637
799
|
allDay: before.interaction.allDay,
|
|
638
800
|
recurrenceRule: before.interaction.recurrenceRule,
|
|
@@ -662,6 +824,7 @@ const updateInteractionCommand = {
|
|
|
662
824
|
interaction2.appearanceIcon = before.interaction.appearanceIcon;
|
|
663
825
|
interaction2.appearanceColor = before.interaction.appearanceColor;
|
|
664
826
|
interaction2.durationMinutes = before.interaction.durationMinutes;
|
|
827
|
+
interaction2.timezone = before.interaction.timezone ?? null;
|
|
665
828
|
interaction2.location = before.interaction.location;
|
|
666
829
|
interaction2.allDay = before.interaction.allDay;
|
|
667
830
|
interaction2.recurrenceRule = before.interaction.recurrenceRule;
|
|
@@ -1077,6 +1240,7 @@ const deleteInteractionCommand = {
|
|
|
1077
1240
|
appearanceIcon: before.interaction.appearanceIcon,
|
|
1078
1241
|
appearanceColor: before.interaction.appearanceColor,
|
|
1079
1242
|
durationMinutes: before.interaction.durationMinutes,
|
|
1243
|
+
timezone: before.interaction.timezone ?? null,
|
|
1080
1244
|
location: before.interaction.location,
|
|
1081
1245
|
allDay: before.interaction.allDay,
|
|
1082
1246
|
recurrenceRule: before.interaction.recurrenceRule,
|
|
@@ -1107,6 +1271,7 @@ const deleteInteractionCommand = {
|
|
|
1107
1271
|
interaction2.appearanceIcon = before.interaction.appearanceIcon;
|
|
1108
1272
|
interaction2.appearanceColor = before.interaction.appearanceColor;
|
|
1109
1273
|
interaction2.durationMinutes = before.interaction.durationMinutes;
|
|
1274
|
+
interaction2.timezone = before.interaction.timezone ?? null;
|
|
1110
1275
|
interaction2.location = before.interaction.location;
|
|
1111
1276
|
interaction2.allDay = before.interaction.allDay;
|
|
1112
1277
|
interaction2.recurrenceRule = before.interaction.recurrenceRule;
|