evo360-types 1.3.411 → 1.3.413
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/dist/apps/evo-campaigns/zod-schemas.d.ts +17367 -0
- package/dist/apps/evo-campaigns/zod-schemas.js +354 -0
- package/dist/apps/evo-campaigns/zod-schemas.ts +389 -0
- package/dist/apps/evo-finops/zod-schemas.d.ts +96 -96
- package/dist/apps/evo-integrations/zod-schemas.d.ts +66 -66
- package/dist/apps/evo-med/calendar/zod-schemas.d.ts +72 -72
- package/dist/apps/evo-med/dic/zod-schemas.d.ts +6 -6
- package/dist/apps/evo-med/people/zod-schemas.d.ts +16 -16
- package/dist/apps/evo-notifications/quick-reply-action/zod-schemas.d.ts +33 -6
- package/dist/apps/evo-notifications/quick-reply-action/zod-schemas.js +12 -2
- package/dist/apps/evo-notifications/quick-reply-action/zod-schemas.ts +13 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/index.ts +2 -0
- package/dist/types/evo-campaigns/index.d.ts +355 -0
- package/dist/types/evo-campaigns/index.js +34 -0
- package/dist/types/evo-campaigns/index.ts +495 -0
- package/dist/types/evo-crm/lead/index.d.ts +16 -2
- package/dist/types/evo-crm/lead/index.ts +20 -1
- package/package.json +1 -1
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
import type { FirestoreDocumentReference, IFireDoc } from "../shared";
|
|
2
|
+
import type { QualificationFunnelLevel } from "../evo-crm/dic";
|
|
3
|
+
export declare const EVO_CAMPAIGNS_APP = "evo-campaigns";
|
|
4
|
+
export declare const CAMPAIGNS_COLLECTION = "campaigns";
|
|
5
|
+
export declare const CAMPAIGN_BUCKETS_COLLECTION = "buckets";
|
|
6
|
+
export declare const CAMPAIGN_RECIPIENTS_COLLECTION = "recipients";
|
|
7
|
+
export declare const CAMPAIGN_CONVERSIONS_COLLECTION = "conversions";
|
|
8
|
+
/** Subcollection on the lead doc (evo-crm) — history + frequency + attribution. */
|
|
9
|
+
export declare const LEAD_CAMPAIGN_HISTORY_COLLECTION = "campaign-history";
|
|
10
|
+
export declare const EvoCampaignsPermissions: {
|
|
11
|
+
readonly Read: "evo_campaigns_read";
|
|
12
|
+
readonly Manage: "evo_campaigns_manage";
|
|
13
|
+
readonly Export: "evo_campaigns_export";
|
|
14
|
+
readonly CostsView: "evo_campaigns_costs_view";
|
|
15
|
+
};
|
|
16
|
+
export type EvoCampaignsPermissions = (typeof EvoCampaignsPermissions)[keyof typeof EvoCampaignsPermissions];
|
|
17
|
+
/** Dictionary reference snapshot ({code,name}) — purpose, objective, close status... */
|
|
18
|
+
export interface ICampaignCodeName {
|
|
19
|
+
code: string;
|
|
20
|
+
name: string;
|
|
21
|
+
}
|
|
22
|
+
/** Campaign FSM (§1.3). */
|
|
23
|
+
export type CampaignStatus = "draft" | "scheduled" | "running" | "paused" | "finished" | "canceled" | "finished_error";
|
|
24
|
+
/** Business classification of the campaign. */
|
|
25
|
+
export type CampaignType = "conversion" | "informative" | "relationship" | "operational" | "promotional";
|
|
26
|
+
/** Delivery channel of a campaign. */
|
|
27
|
+
export type CampaignChannel = "whatsapp" | "email";
|
|
28
|
+
/** Where a template variable gets its value from at send time. */
|
|
29
|
+
export type CampaignVariableSource = "placeholder" | "fixed" | "csv_column" | "qr_link";
|
|
30
|
+
export interface ICampaignVariableMapping {
|
|
31
|
+
/** Template variable name/index this mapping fills. */
|
|
32
|
+
variable: string;
|
|
33
|
+
source: CampaignVariableSource;
|
|
34
|
+
/** source='placeholder': placeholder token (e.g. 'patient.first_name'). */
|
|
35
|
+
token?: string;
|
|
36
|
+
/** source='fixed': literal value. */
|
|
37
|
+
value?: string;
|
|
38
|
+
/** source='csv_column': column name in the uploaded CSV. */
|
|
39
|
+
column?: string;
|
|
40
|
+
/** source='qr_link': the CTA whose /qr link is injected. */
|
|
41
|
+
cta_id?: string;
|
|
42
|
+
}
|
|
43
|
+
export interface ICampaignCta {
|
|
44
|
+
cta_id: string;
|
|
45
|
+
label: string;
|
|
46
|
+
/** WhatsApp template button position (0,1,2...). */
|
|
47
|
+
button_index?: number;
|
|
48
|
+
/** Final destination for the /qr redirect. */
|
|
49
|
+
target_url?: string;
|
|
50
|
+
}
|
|
51
|
+
export interface ICampaignPacingWindow {
|
|
52
|
+
/** 'HH:mm' local (tenant timezone). */
|
|
53
|
+
start: string;
|
|
54
|
+
/** 'HH:mm' local (tenant timezone). */
|
|
55
|
+
end: string;
|
|
56
|
+
}
|
|
57
|
+
export interface ICampaignPacing {
|
|
58
|
+
batch_size: number;
|
|
59
|
+
batch_interval_minutes: number;
|
|
60
|
+
daily_limit: number;
|
|
61
|
+
allowed_window: ICampaignPacingWindow;
|
|
62
|
+
}
|
|
63
|
+
export interface ICampaignTicketPolicy {
|
|
64
|
+
/** 'close' auto-closes the ticket after send (default, Q7); 'keep_open' leaves it. */
|
|
65
|
+
mode: "close" | "keep_open";
|
|
66
|
+
/** Status from the ticket-closes dic applied when mode='close'. */
|
|
67
|
+
close_status?: ICampaignCodeName;
|
|
68
|
+
/** Optional note stamped on the closed ticket. */
|
|
69
|
+
note?: string;
|
|
70
|
+
}
|
|
71
|
+
export interface ICampaignMedia {
|
|
72
|
+
/** 'image' | 'video' | 'document' — WhatsApp header media type. */
|
|
73
|
+
type: string;
|
|
74
|
+
gcs_path?: string;
|
|
75
|
+
url?: string;
|
|
76
|
+
}
|
|
77
|
+
/** Per-channel configuration (whatsapp/email) of a campaign. */
|
|
78
|
+
export interface ICampaignChannelConfig {
|
|
79
|
+
/** evo-chat channel id (WABA channel or email channel). */
|
|
80
|
+
channel_id: string;
|
|
81
|
+
/** HSM/email template id. */
|
|
82
|
+
template_id: string;
|
|
83
|
+
/** WhatsApp: template language code (e.g. 'pt_BR'). */
|
|
84
|
+
template_language?: string;
|
|
85
|
+
/** WhatsApp: template category from introspection (MARKETING/UTILITY/AUTHENTICATION). */
|
|
86
|
+
template_category?: string;
|
|
87
|
+
variable_mappings: ICampaignVariableMapping[];
|
|
88
|
+
/** WhatsApp header media (when the template requires it). */
|
|
89
|
+
media?: ICampaignMedia;
|
|
90
|
+
ctas: ICampaignCta[];
|
|
91
|
+
/** Outbound department for the resulting ticket. */
|
|
92
|
+
department_id?: string;
|
|
93
|
+
ticket_policy: ICampaignTicketPolicy;
|
|
94
|
+
pacing: ICampaignPacing;
|
|
95
|
+
/** Email: overrides the template subject when set (F5). */
|
|
96
|
+
subject_override?: string;
|
|
97
|
+
/** Email: preheader text. */
|
|
98
|
+
preheader?: string;
|
|
99
|
+
}
|
|
100
|
+
export interface ICampaignAttribution {
|
|
101
|
+
/** Conversion window in days (default 30). */
|
|
102
|
+
window_days: number;
|
|
103
|
+
/** Conversion event key (default 'appointment_scheduled'). */
|
|
104
|
+
conversion_event: string;
|
|
105
|
+
/** statusBase values that count as a conversion (Q10 default). */
|
|
106
|
+
appointment_status_base_in: string[];
|
|
107
|
+
/** Minimum interaction required to attribute a conversion. */
|
|
108
|
+
requires: "delivery" | "click" | "reply";
|
|
109
|
+
}
|
|
110
|
+
export interface ICampaignNewLeadConfig {
|
|
111
|
+
dist_channel: ICampaignCodeName;
|
|
112
|
+
/** Optional (Q8) — no default; only stamped when explicitly selected. */
|
|
113
|
+
qualification?: {
|
|
114
|
+
code: string;
|
|
115
|
+
name: string;
|
|
116
|
+
funnelLevel: QualificationFunnelLevel;
|
|
117
|
+
} | null;
|
|
118
|
+
tags?: string[];
|
|
119
|
+
}
|
|
120
|
+
export interface ICampaignFrequency {
|
|
121
|
+
/** Minimum days since the last marketing touch (Q4, default 15). */
|
|
122
|
+
min_days: number;
|
|
123
|
+
/** Bypass the frequency check (requires evo_campaigns_manage, Q13). */
|
|
124
|
+
override: boolean;
|
|
125
|
+
}
|
|
126
|
+
export interface ICampaignSchedule {
|
|
127
|
+
mode: "immediate" | "scheduled";
|
|
128
|
+
scheduled_at?: Date | null;
|
|
129
|
+
}
|
|
130
|
+
export interface ICampaignSentToday {
|
|
131
|
+
/** 'yyyy-MM-dd' in tenant timezone. */
|
|
132
|
+
date: string;
|
|
133
|
+
per_channel: {
|
|
134
|
+
whatsapp?: number;
|
|
135
|
+
email?: number;
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
export interface ICampaignDispatchState {
|
|
139
|
+
cursor_doc_id?: string | null;
|
|
140
|
+
next_dispatch_task_id?: string | null;
|
|
141
|
+
sent_today?: ICampaignSentToday;
|
|
142
|
+
batches_dispatched?: number;
|
|
143
|
+
}
|
|
144
|
+
export type CampaignPrevalidationStatus = "approved" | "warnings" | "blocked";
|
|
145
|
+
export interface ICampaignPrevalidation {
|
|
146
|
+
status: CampaignPrevalidationStatus;
|
|
147
|
+
errors: string[];
|
|
148
|
+
warnings: string[];
|
|
149
|
+
ran_at?: Date | null;
|
|
150
|
+
}
|
|
151
|
+
export interface ICampaignCostEstimate {
|
|
152
|
+
per_channel: Record<string, unknown>;
|
|
153
|
+
total?: number;
|
|
154
|
+
currency?: string;
|
|
155
|
+
tariff_version?: string;
|
|
156
|
+
estimated_at?: Date | null;
|
|
157
|
+
}
|
|
158
|
+
/** Per-channel delivery/engagement counters (increment/CAS). */
|
|
159
|
+
export interface ICampaignChannelCounters {
|
|
160
|
+
pending?: number;
|
|
161
|
+
sent?: number;
|
|
162
|
+
delivered?: number;
|
|
163
|
+
read?: number;
|
|
164
|
+
failed?: number;
|
|
165
|
+
clicked?: number;
|
|
166
|
+
replied?: number;
|
|
167
|
+
}
|
|
168
|
+
export interface ICampaignCounters {
|
|
169
|
+
raw?: number;
|
|
170
|
+
unique?: number;
|
|
171
|
+
eligible?: number;
|
|
172
|
+
excluded_optout?: number;
|
|
173
|
+
excluded_frequency?: number;
|
|
174
|
+
excluded_invalid?: number;
|
|
175
|
+
excluded_no_channel?: number;
|
|
176
|
+
excluded_duplicate?: number;
|
|
177
|
+
per_channel?: {
|
|
178
|
+
whatsapp?: ICampaignChannelCounters;
|
|
179
|
+
email?: ICampaignChannelCounters;
|
|
180
|
+
};
|
|
181
|
+
converted?: number;
|
|
182
|
+
}
|
|
183
|
+
export interface ICampaign extends IFireDoc {
|
|
184
|
+
name: string;
|
|
185
|
+
description?: string | null;
|
|
186
|
+
purpose: ICampaignCodeName;
|
|
187
|
+
objective: ICampaignCodeName;
|
|
188
|
+
campaign_type: CampaignType;
|
|
189
|
+
tags?: string[];
|
|
190
|
+
status: CampaignStatus;
|
|
191
|
+
/** true until the post-finished attribution window closes. */
|
|
192
|
+
attribution_open: boolean;
|
|
193
|
+
/** now + window_days, set when the campaign finishes. */
|
|
194
|
+
attribution_deadline?: Date | null;
|
|
195
|
+
attribution: ICampaignAttribution;
|
|
196
|
+
channels: {
|
|
197
|
+
whatsapp?: ICampaignChannelConfig;
|
|
198
|
+
email?: ICampaignChannelConfig;
|
|
199
|
+
};
|
|
200
|
+
new_lead_config?: ICampaignNewLeadConfig;
|
|
201
|
+
frequency: ICampaignFrequency;
|
|
202
|
+
schedule: ICampaignSchedule;
|
|
203
|
+
dispatch_state?: ICampaignDispatchState;
|
|
204
|
+
prevalidation?: ICampaignPrevalidation;
|
|
205
|
+
cost_estimate?: ICampaignCostEstimate;
|
|
206
|
+
counters?: ICampaignCounters;
|
|
207
|
+
created_by?: FirestoreDocumentReference;
|
|
208
|
+
updated_by?: FirestoreDocumentReference;
|
|
209
|
+
scheduled_by?: FirestoreDocumentReference;
|
|
210
|
+
started_by?: FirestoreDocumentReference;
|
|
211
|
+
paused_by?: FirestoreDocumentReference;
|
|
212
|
+
resumed_by?: FirestoreDocumentReference;
|
|
213
|
+
canceled_by?: FirestoreDocumentReference;
|
|
214
|
+
scheduled_at?: Date | null;
|
|
215
|
+
started_at?: Date | null;
|
|
216
|
+
paused_at?: Date | null;
|
|
217
|
+
resumed_at?: Date | null;
|
|
218
|
+
canceled_at?: Date | null;
|
|
219
|
+
finished_at?: Date | null;
|
|
220
|
+
cancel_reason?: string | null;
|
|
221
|
+
error_reason?: string | null;
|
|
222
|
+
}
|
|
223
|
+
export type CampaignBucketSourceType = "leads" | "patients" | "appointments" | "csv";
|
|
224
|
+
export interface ICampaignBucketCsvFile {
|
|
225
|
+
gcs_path: string;
|
|
226
|
+
original_name: string;
|
|
227
|
+
headers: string[];
|
|
228
|
+
row_count: number;
|
|
229
|
+
/** Invalid rows with reason, capped at 200 in the doc (full report in GCS). */
|
|
230
|
+
invalid_rows?: Array<{
|
|
231
|
+
row: number;
|
|
232
|
+
reason: string;
|
|
233
|
+
[key: string]: unknown;
|
|
234
|
+
}>;
|
|
235
|
+
}
|
|
236
|
+
export interface ICampaignBucketCounts {
|
|
237
|
+
raw?: number;
|
|
238
|
+
unique_contribution?: number;
|
|
239
|
+
eligible?: number;
|
|
240
|
+
invalid?: number;
|
|
241
|
+
opt_out?: number;
|
|
242
|
+
frequency_excluded?: number;
|
|
243
|
+
no_channel?: number;
|
|
244
|
+
}
|
|
245
|
+
export interface ICampaignBucket extends IFireDoc {
|
|
246
|
+
source_type: CampaignBucketSourceType;
|
|
247
|
+
label: string;
|
|
248
|
+
/** Shape depends on source_type (Algolia filters or CSV mapping). */
|
|
249
|
+
filters?: Record<string, unknown>;
|
|
250
|
+
csv_file?: ICampaignBucketCsvFile;
|
|
251
|
+
counts?: ICampaignBucketCounts;
|
|
252
|
+
}
|
|
253
|
+
/** Flat field driving the batch queue (indexed). */
|
|
254
|
+
export type RecipientDispatchState = "pending" | "dispatched" | "done" | "excluded";
|
|
255
|
+
/** Per-channel delivery status of a recipient (monotonic). */
|
|
256
|
+
export type RecipientChannelStatus = "pending" | "scheduled" | "sent" | "delivered" | "read" | "failed" | "clicked" | "replied";
|
|
257
|
+
/** Aggregated, monotonic recipient status (Portuguese labels — FE display). */
|
|
258
|
+
export type RecipientInteractionStatus = "pendente" | "inelegivel" | "agendado" | "enviado" | "entregue" | "lido" | "clicou" | "respondeu" | "convertido" | "falhou" | "optout" | "cancelado";
|
|
259
|
+
export interface ICampaignRecipientContact {
|
|
260
|
+
display_name?: string;
|
|
261
|
+
mobile?: string;
|
|
262
|
+
email?: string;
|
|
263
|
+
external_id?: string;
|
|
264
|
+
}
|
|
265
|
+
export interface ICampaignRecipientChannelState {
|
|
266
|
+
status: RecipientChannelStatus;
|
|
267
|
+
task_id?: string;
|
|
268
|
+
qr_action_ids?: string[];
|
|
269
|
+
sent_at?: Date | null;
|
|
270
|
+
delivered_at?: Date | null;
|
|
271
|
+
read_at?: Date | null;
|
|
272
|
+
clicked_at?: Date | null;
|
|
273
|
+
replied_at?: Date | null;
|
|
274
|
+
error?: string | null;
|
|
275
|
+
}
|
|
276
|
+
export interface ICampaignRecipientEligibility {
|
|
277
|
+
per_channel: {
|
|
278
|
+
whatsapp?: "eligible" | "excluded";
|
|
279
|
+
email?: "eligible" | "excluded";
|
|
280
|
+
};
|
|
281
|
+
/** channel -> exclusion reason. */
|
|
282
|
+
exclusion_reasons?: Record<string, string>;
|
|
283
|
+
}
|
|
284
|
+
export interface ICampaignRecipient extends IFireDoc {
|
|
285
|
+
lead_id?: string;
|
|
286
|
+
patient_id?: string;
|
|
287
|
+
is_new_lead: boolean;
|
|
288
|
+
contact: ICampaignRecipientContact;
|
|
289
|
+
bucket_ids: string[];
|
|
290
|
+
primary_bucket_id: string;
|
|
291
|
+
csv_variables?: Record<string, string>;
|
|
292
|
+
/** FLAT field for the batch queue (indexed). */
|
|
293
|
+
dispatch_state: RecipientDispatchState;
|
|
294
|
+
eligibility: ICampaignRecipientEligibility;
|
|
295
|
+
channels: {
|
|
296
|
+
whatsapp?: ICampaignRecipientChannelState;
|
|
297
|
+
email?: ICampaignRecipientChannelState;
|
|
298
|
+
};
|
|
299
|
+
interaction_status: RecipientInteractionStatus;
|
|
300
|
+
converted_at?: Date | null;
|
|
301
|
+
conversion_id?: string | null;
|
|
302
|
+
}
|
|
303
|
+
export type CampaignConversionSourceEntity = "appointment" | "qr_action" | "ticket" | "message";
|
|
304
|
+
export interface ICampaignConversion extends IFireDoc {
|
|
305
|
+
lead_id: string;
|
|
306
|
+
recipient_id: string;
|
|
307
|
+
conversion_event: string;
|
|
308
|
+
source_entity_type: CampaignConversionSourceEntity;
|
|
309
|
+
source_entity_id: string;
|
|
310
|
+
channel?: CampaignChannel;
|
|
311
|
+
cta_id?: string;
|
|
312
|
+
attributed_at: Date;
|
|
313
|
+
attribution_model: "last_campaign_in_window";
|
|
314
|
+
window_days: number;
|
|
315
|
+
metadata?: Record<string, unknown>;
|
|
316
|
+
}
|
|
317
|
+
export interface ILeadCampaignHistoryEntry extends IFireDoc {
|
|
318
|
+
campaign_id: string;
|
|
319
|
+
campaign_name: string;
|
|
320
|
+
/** e.g. 'marketing' — filters the frequency query (Q4). */
|
|
321
|
+
classification_level: string;
|
|
322
|
+
channels_sent: CampaignChannel[];
|
|
323
|
+
template_ids?: Record<string, string>;
|
|
324
|
+
bucket_ids?: string[];
|
|
325
|
+
first_sent_at?: Date | null;
|
|
326
|
+
last_sent_at?: Date | null;
|
|
327
|
+
delivered_at?: Date | null;
|
|
328
|
+
read_at?: Date | null;
|
|
329
|
+
clicked_at?: Date | null;
|
|
330
|
+
replied_at?: Date | null;
|
|
331
|
+
converted_at?: Date | null;
|
|
332
|
+
interaction_status: RecipientInteractionStatus;
|
|
333
|
+
}
|
|
334
|
+
export interface ITariffItem {
|
|
335
|
+
code: string;
|
|
336
|
+
name: string;
|
|
337
|
+
description?: string;
|
|
338
|
+
/** 'messaging' | 'fees' | ... */
|
|
339
|
+
context: string;
|
|
340
|
+
channel?: string;
|
|
341
|
+
provider?: string;
|
|
342
|
+
/** ISO country code, e.g. 'BR'. */
|
|
343
|
+
country?: string;
|
|
344
|
+
/** 'marketing' | 'utility' | 'authentication'. */
|
|
345
|
+
category?: string;
|
|
346
|
+
currency: string;
|
|
347
|
+
calc_type: "fixed" | "percentage";
|
|
348
|
+
value: number;
|
|
349
|
+
/** For percentage: what the percent is applied over. */
|
|
350
|
+
percent_base?: string;
|
|
351
|
+
valid_from: Date;
|
|
352
|
+
valid_to?: Date | null;
|
|
353
|
+
active: boolean;
|
|
354
|
+
metadata?: Record<string, unknown>;
|
|
355
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EvoCampaignsPermissions = exports.LEAD_CAMPAIGN_HISTORY_COLLECTION = exports.CAMPAIGN_CONVERSIONS_COLLECTION = exports.CAMPAIGN_RECIPIENTS_COLLECTION = exports.CAMPAIGN_BUCKETS_COLLECTION = exports.CAMPAIGNS_COLLECTION = exports.EVO_CAMPAIGNS_APP = void 0;
|
|
4
|
+
// ======================================================
|
|
5
|
+
// evo-campaigns (feat-081 — Hub Omni Campaigns)
|
|
6
|
+
//
|
|
7
|
+
// Campaign orchestrator: aggregate entity over audience enumeration,
|
|
8
|
+
// multi-bucket dedup, pacing, ticket auto-close, conversion attribution
|
|
9
|
+
// and exportable reporting. Firestore holds mutable state ONLY; business
|
|
10
|
+
// events flow to BigQuery via evo_campaigns.events.
|
|
11
|
+
//
|
|
12
|
+
// Firestore (per tenant):
|
|
13
|
+
// - App root: /tenants/{t}/apps/evo-campaigns
|
|
14
|
+
// - Campaigns: /tenants/{t}/apps/evo-campaigns/campaigns/{campaignId}
|
|
15
|
+
// - Buckets: .../campaigns/{campaignId}/buckets/{bucketId}
|
|
16
|
+
// - Recipients: .../campaigns/{campaignId}/recipients/{recipientId}
|
|
17
|
+
// - Conversions: .../campaigns/{campaignId}/conversions/{leadId}
|
|
18
|
+
// - Lead history: /tenants/{t}/apps/evo-crm/leads/{leadId}/campaign-history/{campaignId}
|
|
19
|
+
// ======================================================
|
|
20
|
+
// ----- App + Firestore collections
|
|
21
|
+
exports.EVO_CAMPAIGNS_APP = "evo-campaigns";
|
|
22
|
+
exports.CAMPAIGNS_COLLECTION = "campaigns";
|
|
23
|
+
exports.CAMPAIGN_BUCKETS_COLLECTION = "buckets";
|
|
24
|
+
exports.CAMPAIGN_RECIPIENTS_COLLECTION = "recipients";
|
|
25
|
+
exports.CAMPAIGN_CONVERSIONS_COLLECTION = "conversions";
|
|
26
|
+
/** Subcollection on the lead doc (evo-crm) — history + frequency + attribution. */
|
|
27
|
+
exports.LEAD_CAMPAIGN_HISTORY_COLLECTION = "campaign-history";
|
|
28
|
+
// ----- Permissions (Q13)
|
|
29
|
+
exports.EvoCampaignsPermissions = {
|
|
30
|
+
Read: "evo_campaigns_read",
|
|
31
|
+
Manage: "evo_campaigns_manage",
|
|
32
|
+
Export: "evo_campaigns_export",
|
|
33
|
+
CostsView: "evo_campaigns_costs_view",
|
|
34
|
+
};
|