create-crm-tmp 1.1.2 → 2.0.0
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/package.json +1 -1
- package/template/.prettierignore +2 -0
- package/template/README.md +53 -67
- package/template/components.json +22 -0
- package/template/exemple-contacts.csv +54 -0
- package/template/next.config.ts +27 -1
- package/template/package.json +64 -27
- package/template/prisma/schema.prisma +821 -72
- package/template/skills-lock.json +25 -0
- package/template/src/app/(auth)/invite/[token]/page.tsx +21 -24
- package/template/src/app/(auth)/reset-password/complete/page.tsx +12 -21
- package/template/src/app/(auth)/reset-password/page.tsx +12 -8
- package/template/src/app/(auth)/reset-password/verify/page.tsx +12 -8
- package/template/src/app/(auth)/signin/page.tsx +20 -17
- package/template/src/app/(dashboard)/agenda/page.tsx +2231 -2188
- package/template/src/app/(dashboard)/automatisation/[id]/page.tsx +10 -7
- package/template/src/app/(dashboard)/automatisation/_components/workflow-editor.tsx +680 -323
- package/template/src/app/(dashboard)/automatisation/new/page.tsx +11 -8
- package/template/src/app/(dashboard)/automatisation/page.tsx +473 -180
- package/template/src/app/(dashboard)/closing/page.tsx +500 -468
- package/template/src/app/(dashboard)/contacts/[id]/page.tsx +5035 -4126
- package/template/src/app/(dashboard)/contacts/companies/[id]/page.tsx +1703 -0
- package/template/src/app/(dashboard)/contacts/loading.tsx +13 -0
- package/template/src/app/(dashboard)/contacts/page.tsx +3776 -2064
- package/template/src/app/(dashboard)/dashboard/page.tsx +37 -519
- package/template/src/app/(dashboard)/error.tsx +37 -0
- package/template/src/app/(dashboard)/layout.tsx +1 -1
- package/template/src/app/(dashboard)/loading.tsx +5 -0
- package/template/src/app/(dashboard)/settings/loading.tsx +19 -0
- package/template/src/app/(dashboard)/settings/page.tsx +2685 -2489
- package/template/src/app/(dashboard)/templates/page.tsx +500 -300
- package/template/src/app/(dashboard)/users/list/page.tsx +356 -350
- package/template/src/app/(dashboard)/users/page.tsx +279 -310
- package/template/src/app/(dashboard)/users/permissions/page.tsx +104 -99
- package/template/src/app/(dashboard)/users/roles/page.tsx +164 -137
- package/template/src/app/api/audit-logs/route.ts +1 -1
- package/template/src/app/api/auth/google/callback/route.ts +8 -5
- package/template/src/app/api/auth/google/disconnect/route.ts +2 -2
- package/template/src/app/api/companies/[id]/activities/route.ts +131 -0
- package/template/src/app/api/companies/[id]/route.ts +195 -0
- package/template/src/app/api/companies/export/route.ts +206 -0
- package/template/src/app/api/companies/route.ts +166 -0
- package/template/src/app/api/contact-views/[id]/pin/route.ts +69 -0
- package/template/src/app/api/contact-views/[id]/route.ts +197 -0
- package/template/src/app/api/contact-views/route.ts +146 -0
- package/template/src/app/api/contacts/[id]/files/[fileId]/preview/route.ts +77 -0
- package/template/src/app/api/contacts/[id]/files/[fileId]/route.ts +7 -17
- package/template/src/app/api/contacts/[id]/files/route.ts +83 -44
- package/template/src/app/api/contacts/[id]/interactions/route.ts +37 -0
- package/template/src/app/api/contacts/[id]/kyc/route.ts +71 -0
- package/template/src/app/api/contacts/[id]/meet/route.ts +38 -29
- package/template/src/app/api/contacts/[id]/route.ts +111 -20
- package/template/src/app/api/contacts/[id]/send-email/route.ts +6 -0
- package/template/src/app/api/contacts/[id]/workflows/run/route.ts +61 -0
- package/template/src/app/api/contacts/export/route.ts +12 -17
- package/template/src/app/api/contacts/import/route.ts +22 -19
- package/template/src/app/api/contacts/import-preview/route.ts +139 -0
- package/template/src/app/api/contacts/route.ts +202 -49
- package/template/src/app/api/dashboard/stats/route.ts +9 -292
- package/template/src/app/api/integrations/google-sheet/sync/route.ts +203 -185
- package/template/src/app/api/invite/complete/route.ts +20 -23
- package/template/src/app/api/reminders/route.ts +1 -0
- package/template/src/app/api/reset-password/complete/route.ts +11 -13
- package/template/src/app/api/send/route.ts +9 -85
- package/template/src/app/api/settings/closing-reasons/[id]/route.ts +10 -21
- package/template/src/app/api/settings/closing-reasons/route.ts +10 -21
- package/template/src/app/api/settings/company/route.ts +19 -26
- package/template/src/app/api/settings/google-ads/[id]/route.ts +20 -23
- package/template/src/app/api/settings/google-ads/route.ts +20 -23
- package/template/src/app/api/settings/google-sheet/[id]/route.ts +20 -23
- package/template/src/app/api/settings/google-sheet/auto-map/route.ts +23 -32
- package/template/src/app/api/settings/google-sheet/preview/route.ts +104 -0
- package/template/src/app/api/settings/google-sheet/route.ts +20 -23
- package/template/src/app/api/settings/meta-leads/[id]/route.ts +20 -23
- package/template/src/app/api/settings/meta-leads/route.ts +20 -23
- package/template/src/app/api/settings/statuses/[id]/route.ts +33 -23
- package/template/src/app/api/settings/statuses/route.ts +24 -22
- package/template/src/app/api/statuses/route.ts +2 -5
- package/template/src/app/api/tasks/[id]/attendees/route.ts +14 -7
- package/template/src/app/api/tasks/[id]/route.ts +161 -137
- package/template/src/app/api/tasks/meet/route.ts +11 -8
- package/template/src/app/api/tasks/route.ts +155 -95
- package/template/src/app/api/templates/[id]/route.ts +22 -13
- package/template/src/app/api/templates/route.ts +22 -5
- package/template/src/app/api/users/[id]/resend-invite/route.ts +95 -0
- package/template/src/app/api/users/[id]/route.ts +16 -1
- package/template/src/app/api/users/commercials/route.ts +38 -0
- package/template/src/app/api/users/for-agenda/route.ts +1 -2
- package/template/src/app/api/users/route.ts +94 -55
- package/template/src/app/api/webhooks/google-ads/route.ts +20 -1
- package/template/src/app/api/webhooks/meta-leads/route.ts +18 -1
- package/template/src/app/api/workflows/[id]/route.ts +33 -6
- package/template/src/app/api/workflows/process/route.ts +509 -146
- package/template/src/app/api/workflows/route.ts +46 -4
- package/template/src/app/globals.css +210 -101
- package/template/src/app/layout.tsx +19 -8
- package/template/src/app/page.tsx +37 -7
- package/template/src/components/address-autocomplete.tsx +232 -0
- package/template/src/components/contacts/filter-bar.tsx +181 -0
- package/template/src/components/contacts/filter-builder.tsx +589 -0
- package/template/src/components/contacts/save-view-dialog.tsx +160 -0
- package/template/src/components/contacts/views-tab-bar.tsx +440 -0
- package/template/src/components/dashboard/activity-chart.tsx +31 -39
- package/template/src/components/dashboard/dashboard-content.tsx +79 -0
- package/template/src/components/dashboard/stat-card.tsx +40 -42
- package/template/src/components/dashboard/tasks-pie-chart.tsx +34 -37
- package/template/src/components/dashboard/upcoming-tasks-list.tsx +78 -72
- package/template/src/components/date-picker.tsx +396 -0
- package/template/src/components/editor.tsx +27 -13
- package/template/src/components/email-template.tsx +4 -2
- package/template/src/components/global-search.tsx +358 -0
- package/template/src/components/header.tsx +57 -62
- package/template/src/components/invitation-email-template.tsx +4 -2
- package/template/src/components/lazy-editor.tsx +11 -0
- package/template/src/components/meet-cancellation-email-template.tsx +11 -3
- package/template/src/components/meet-confirmation-email-template.tsx +10 -3
- package/template/src/components/meet-update-email-template.tsx +10 -3
- package/template/src/components/page-header.tsx +19 -15
- package/template/src/components/protected-page.tsx +94 -0
- package/template/src/components/reset-password-email-template.tsx +4 -2
- package/template/src/components/sidebar.tsx +92 -94
- package/template/src/components/skeleton.tsx +128 -42
- package/template/src/components/ui/accordion.tsx +64 -0
- package/template/src/components/ui/alert-dialog.tsx +139 -0
- package/template/src/components/ui/button.tsx +60 -0
- package/template/src/components/view-as-banner.tsx +1 -1
- package/template/src/components/view-as-modal.tsx +21 -16
- package/template/src/config/nav-pages.ts +108 -0
- package/template/src/contexts/app-toast-context.tsx +174 -0
- package/template/src/contexts/sidebar-context.tsx +16 -47
- package/template/src/contexts/task-reminder-context.tsx +6 -6
- package/template/src/contexts/view-as-context.tsx +11 -16
- package/template/src/hooks/use-alert.tsx +65 -0
- package/template/src/hooks/use-confirm.tsx +87 -0
- package/template/src/hooks/use-contact-views.ts +140 -0
- package/template/src/hooks/use-contacts.ts +69 -0
- package/template/src/hooks/use-fetch.ts +17 -0
- package/template/src/hooks/use-focus-trap.ts +73 -0
- package/template/src/hooks/use-statuses.ts +22 -0
- package/template/src/lib/address-api.ts +155 -0
- package/template/src/lib/cache.ts +73 -0
- package/template/src/lib/check-permission.ts +12 -177
- package/template/src/lib/contact-interactions.ts +3 -1
- package/template/src/lib/contact-view-filters.ts +341 -0
- package/template/src/lib/dashboard-stats.ts +224 -0
- package/template/src/lib/date-utils.ts +49 -0
- package/template/src/lib/get-auth-user.ts +25 -0
- package/template/src/lib/google-calendar.ts +54 -12
- package/template/src/lib/google-drive.ts +796 -75
- package/template/src/lib/google-fetch.ts +63 -0
- package/template/src/lib/local-storage.ts +34 -0
- package/template/src/lib/permissions.ts +245 -47
- package/template/src/lib/prisma.ts +11 -11
- package/template/src/lib/roles.ts +14 -39
- package/template/src/lib/template-variables.ts +67 -33
- package/template/src/lib/utils.ts +26 -2
- package/template/src/lib/workflow-executor.ts +445 -229
- package/template/src/proxy.ts +34 -73
- package/template/src/types/contact-views.ts +351 -0
- package/template/src/types/yousign.ts +52 -0
- package/template/vercel.json +12 -0
- package/template/WORKFLOWS_CRON.md +0 -185
- package/template/prisma/migrations/20251126144728_init/migration.sql +0 -78
- package/template/prisma/migrations/20251126155204_add_user_roles/migration.sql +0 -5
- package/template/prisma/migrations/20251128095126_add_company_info/migration.sql +0 -19
- package/template/prisma/migrations/20251128123321_add_smtp_config/migration.sql +0 -22
- package/template/prisma/migrations/20251128132303_add_status/migration.sql +0 -23
- package/template/prisma/migrations/20251201102207_add_user_active/migration.sql +0 -75
- package/template/prisma/migrations/20251201105507_add_email_signature/migration.sql +0 -2
- package/template/prisma/migrations/20251201151122_add_tasks/migration.sql +0 -45
- package/template/prisma/migrations/20251202111854_add_task_reminder/migration.sql +0 -2
- package/template/prisma/migrations/20251202135859_add_google_meet_integration/migration.sql +0 -27
- package/template/prisma/migrations/20251203103317_add_meta_lead_integration/migration.sql +0 -20
- package/template/prisma/migrations/20251203104002_add_google_ads_integration/migration.sql +0 -18
- package/template/prisma/migrations/20251203112122_add_google_sheet_integration/migration.sql +0 -32
- package/template/prisma/migrations/20251203153853_allow_multiple_integration_configs/migration.sql +0 -20
- package/template/prisma/migrations/20251205141705_update_user_roles/migration.sql +0 -12
- package/template/prisma/migrations/20251205150000_add_commercial_and_telepro_assignment/migration.sql +0 -21
- package/template/prisma/migrations/20251205160000_add_interaction_logging/migration.sql +0 -11
- package/template/prisma/migrations/20251208090314_add_automatic_interaction_types/migration.sql +0 -12
- package/template/prisma/migrations/20251208094843_mg/migration.sql +0 -14
- package/template/prisma/migrations/20251208100000_add_company_support/migration.sql +0 -14
- package/template/prisma/migrations/20251208110000_add_templates/migration.sql +0 -26
- package/template/prisma/migrations/20251208141304_add_video_conference_task_type/migration.sql +0 -2
- package/template/prisma/migrations/20251209104759_add_internal_note_to_task/migration.sql +0 -2
- package/template/prisma/migrations/20251209134803_add_company_field/migration.sql +0 -2
- package/template/prisma/migrations/20251209150000_rename_company_to_company_name/migration.sql +0 -3
- package/template/prisma/migrations/20251209150016_add_email_tracking/migration.sql +0 -21
- package/template/prisma/migrations/20251209155908_add_notify_contact_to_task/migration.sql +0 -2
- package/template/prisma/migrations/20251210110019_add_appointment_types/migration.sql +0 -10
- package/template/prisma/migrations/20251210113928_add_contact_files/migration.sql +0 -26
- package/template/prisma/migrations/20251212132339_add_custom_roles/migration.sql +0 -24
- package/template/prisma/migrations/20251215104448_add_file_interaction_types/migration.sql +0 -11
- package/template/prisma/migrations/20251215145616_add_closing_reasons/migration.sql +0 -12
- package/template/prisma/migrations/20251216140850_add_log_users/migration.sql +0 -25
- package/template/prisma/migrations/20251216151000_rename_perdu_to_ferme/migration.sql +0 -8
- package/template/prisma/migrations/20251216162318_add_column_mappings_to_google_sheet/migration.sql +0 -2
- package/template/prisma/migrations/20251216185127_add_workflows/migration.sql +0 -80
- package/template/prisma/migrations/20251216192237_add_scheduled_workflow_actions/migration.sql +0 -32
- package/template/prisma/migrations/20251220000000_add_task_interaction_type/migration.sql +0 -4
- package/template/prisma/migrations/20251221000000_add_task_type/migration.sql +0 -3
- package/template/prisma/migrations/20251221000001_add_event_color/migration.sql +0 -23
- package/template/prisma/migrations/20260210114913_add_dashboard_widget/migration.sql +0 -20
- package/template/prisma/migrations/migration_lock.toml +0 -3
- package/template/src/app/(dashboard)/users/layout.tsx +0 -30
- package/template/src/app/api/dashboard/widgets/[id]/route.ts +0 -47
- package/template/src/app/api/dashboard/widgets/route.ts +0 -181
- package/template/src/components/dashboard/add-widget-dialog.tsx +0 -161
- package/template/src/components/dashboard/color-picker.tsx +0 -65
- package/template/src/components/dashboard/contacts-chart.tsx +0 -69
- package/template/src/components/dashboard/interactions-by-type-chart.tsx +0 -121
- package/template/src/components/dashboard/recent-activity.tsx +0 -157
- package/template/src/components/dashboard/sales-analytics-chart.tsx +0 -77
- package/template/src/components/dashboard/status-distribution-chart.tsx +0 -82
- package/template/src/components/dashboard/top-contacts-list.tsx +0 -119
- package/template/src/components/dashboard/widget-wrapper.tsx +0 -39
- package/template/src/contexts/dashboard-theme-context.tsx +0 -58
- package/template/src/lib/dashboard-themes.ts +0 -140
- package/template/src/lib/default-widgets.ts +0 -14
- package/template/src/lib/widget-registry.ts +0 -177
|
@@ -3,39 +3,55 @@ import nodemailer from 'nodemailer';
|
|
|
3
3
|
import { decrypt } from '@/lib/encryption';
|
|
4
4
|
import { replaceTemplateVariables } from '@/lib/template-variables';
|
|
5
5
|
|
|
6
|
+
const WORKFLOW_INCLUDE = {
|
|
7
|
+
actions: {
|
|
8
|
+
orderBy: { order: 'asc' as const },
|
|
9
|
+
include: {
|
|
10
|
+
emailTemplate: true,
|
|
11
|
+
newStatus: true,
|
|
12
|
+
conditionStatus: true,
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
user: {
|
|
16
|
+
include: {
|
|
17
|
+
smtpConfig: true,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const CONTACT_INCLUDE = {
|
|
23
|
+
status: true,
|
|
24
|
+
company: { select: { name: true, id: true } },
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
function getContactVariables(contact: any) {
|
|
28
|
+
return {
|
|
29
|
+
firstName: contact.firstName || '',
|
|
30
|
+
lastName: contact.lastName || '',
|
|
31
|
+
civility: contact.civility || '',
|
|
32
|
+
email: contact.email || '',
|
|
33
|
+
phone: contact.phone || '',
|
|
34
|
+
secondaryPhone: contact.secondaryPhone || '',
|
|
35
|
+
address: contact.address || '',
|
|
36
|
+
city: contact.city || '',
|
|
37
|
+
postalCode: contact.postalCode || '',
|
|
38
|
+
companyName: contact.company?.name || '',
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
6
42
|
/**
|
|
7
43
|
* Exécute les workflows déclenchés par la création d'un contact
|
|
8
44
|
*/
|
|
9
45
|
export async function executeWorkflowsOnContactCreated(contactId: string) {
|
|
10
46
|
try {
|
|
11
|
-
// Récupérer tous les workflows actifs avec le déclencheur CONTACT_CREATED
|
|
12
47
|
const workflows = await prisma.workflow.findMany({
|
|
13
|
-
where: {
|
|
14
|
-
|
|
15
|
-
triggerType: 'CONTACT_CREATED',
|
|
16
|
-
},
|
|
17
|
-
include: {
|
|
18
|
-
actions: {
|
|
19
|
-
orderBy: { order: 'asc' },
|
|
20
|
-
include: {
|
|
21
|
-
emailTemplate: true,
|
|
22
|
-
newStatus: true,
|
|
23
|
-
conditionStatus: true,
|
|
24
|
-
},
|
|
25
|
-
},
|
|
26
|
-
user: {
|
|
27
|
-
include: {
|
|
28
|
-
smtpConfig: true,
|
|
29
|
-
},
|
|
30
|
-
},
|
|
31
|
-
},
|
|
48
|
+
where: { active: true, triggerType: 'CONTACT_CREATED' },
|
|
49
|
+
include: WORKFLOW_INCLUDE,
|
|
32
50
|
});
|
|
33
51
|
|
|
34
52
|
const contact = await prisma.contact.findUnique({
|
|
35
53
|
where: { id: contactId },
|
|
36
|
-
include:
|
|
37
|
-
status: true,
|
|
38
|
-
},
|
|
54
|
+
include: CONTACT_INCLUDE,
|
|
39
55
|
});
|
|
40
56
|
|
|
41
57
|
if (!contact) return;
|
|
@@ -57,52 +73,56 @@ export async function executeWorkflowsOnStatusChanged(
|
|
|
57
73
|
newStatusId: string | null,
|
|
58
74
|
) {
|
|
59
75
|
try {
|
|
60
|
-
// Récupérer tous les workflows actifs avec le déclencheur STATUS_CHANGED
|
|
61
76
|
const workflows = await prisma.workflow.findMany({
|
|
62
|
-
where: {
|
|
63
|
-
active: true,
|
|
64
|
-
triggerType: 'STATUS_CHANGED',
|
|
65
|
-
},
|
|
77
|
+
where: { active: true, triggerType: 'STATUS_CHANGED' },
|
|
66
78
|
include: {
|
|
67
|
-
|
|
68
|
-
orderBy: { order: 'asc' },
|
|
69
|
-
include: {
|
|
70
|
-
emailTemplate: true,
|
|
71
|
-
newStatus: true,
|
|
72
|
-
conditionStatus: true,
|
|
73
|
-
},
|
|
74
|
-
},
|
|
79
|
+
...WORKFLOW_INCLUDE,
|
|
75
80
|
triggerStatus: true,
|
|
76
81
|
triggerToStatus: true,
|
|
77
|
-
user: {
|
|
78
|
-
include: {
|
|
79
|
-
smtpConfig: true,
|
|
80
|
-
},
|
|
81
|
-
},
|
|
82
82
|
},
|
|
83
83
|
});
|
|
84
84
|
|
|
85
85
|
const contact = await prisma.contact.findUnique({
|
|
86
86
|
where: { id: contactId },
|
|
87
|
-
include:
|
|
88
|
-
status: true,
|
|
89
|
-
},
|
|
87
|
+
include: CONTACT_INCLUDE,
|
|
90
88
|
});
|
|
91
89
|
|
|
92
90
|
if (!contact) return;
|
|
93
91
|
|
|
94
|
-
// Filtrer les workflows qui correspondent aux conditions de déclenchement
|
|
95
92
|
const matchingWorkflows = workflows.filter((workflow) => {
|
|
96
|
-
|
|
97
|
-
if (workflow.triggerFromStatusId && oldStatusId !== workflow.triggerFromStatusId) {
|
|
93
|
+
if (workflow.triggerFromStatusId && oldStatusId !== workflow.triggerFromStatusId)
|
|
98
94
|
return false;
|
|
99
|
-
|
|
95
|
+
if (workflow.triggerToStatusId && newStatusId !== workflow.triggerToStatusId) return false;
|
|
96
|
+
return true;
|
|
97
|
+
});
|
|
100
98
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
99
|
+
for (const workflow of matchingWorkflows) {
|
|
100
|
+
await executeWorkflowActions(workflow, contactId, contact);
|
|
101
|
+
}
|
|
102
|
+
} catch (error) {
|
|
103
|
+
console.error("Erreur lors de l'exécution des workflows pour changement de statut:", error);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Exécute les workflows déclenchés par la complétion d'une tâche
|
|
109
|
+
*/
|
|
110
|
+
export async function executeWorkflowsOnTaskCompleted(contactId: string, taskType: string) {
|
|
111
|
+
try {
|
|
112
|
+
const workflows = await prisma.workflow.findMany({
|
|
113
|
+
where: { active: true, triggerType: 'TASK_COMPLETED' },
|
|
114
|
+
include: WORKFLOW_INCLUDE,
|
|
115
|
+
});
|
|
105
116
|
|
|
117
|
+
const contact = await prisma.contact.findUnique({
|
|
118
|
+
where: { id: contactId },
|
|
119
|
+
include: CONTACT_INCLUDE,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
if (!contact) return;
|
|
123
|
+
|
|
124
|
+
const matchingWorkflows = workflows.filter((workflow) => {
|
|
125
|
+
if (workflow.triggerTaskType && workflow.triggerTaskType !== taskType) return false;
|
|
106
126
|
return true;
|
|
107
127
|
});
|
|
108
128
|
|
|
@@ -110,34 +130,155 @@ export async function executeWorkflowsOnStatusChanged(
|
|
|
110
130
|
await executeWorkflowActions(workflow, contactId, contact);
|
|
111
131
|
}
|
|
112
132
|
} catch (error) {
|
|
113
|
-
console.error("Erreur lors de l'exécution des workflows pour
|
|
133
|
+
console.error("Erreur lors de l'exécution des workflows pour tâche complétée:", error);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Exécute les workflows déclenchés par la création d'une transaction
|
|
139
|
+
*/
|
|
140
|
+
export async function executeWorkflowsOnTransactionCreated(contactId: string) {
|
|
141
|
+
try {
|
|
142
|
+
const workflows = await prisma.workflow.findMany({
|
|
143
|
+
where: { active: true, triggerType: 'TRANSACTION_CREATED' },
|
|
144
|
+
include: WORKFLOW_INCLUDE,
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
const contact = await prisma.contact.findUnique({
|
|
148
|
+
where: { id: contactId },
|
|
149
|
+
include: CONTACT_INCLUDE,
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
if (!contact) return;
|
|
153
|
+
|
|
154
|
+
for (const workflow of workflows) {
|
|
155
|
+
await executeWorkflowActions(workflow, contactId, contact);
|
|
156
|
+
}
|
|
157
|
+
} catch (error) {
|
|
158
|
+
console.error("Erreur lors de l'exécution des workflows pour transaction créée:", error);
|
|
114
159
|
}
|
|
115
160
|
}
|
|
116
161
|
|
|
162
|
+
/**
|
|
163
|
+
* Exécute les workflows déclenchés par un changement de statut de transaction
|
|
164
|
+
*/
|
|
165
|
+
export async function executeWorkflowsOnTransactionStatusChanged(
|
|
166
|
+
contactId: string,
|
|
167
|
+
oldStatus: string | null,
|
|
168
|
+
newStatus: string,
|
|
169
|
+
) {
|
|
170
|
+
try {
|
|
171
|
+
const workflows = await prisma.workflow.findMany({
|
|
172
|
+
where: { active: true, triggerType: 'TRANSACTION_STATUS_CHANGED' },
|
|
173
|
+
include: WORKFLOW_INCLUDE,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
const contact = await prisma.contact.findUnique({
|
|
177
|
+
where: { id: contactId },
|
|
178
|
+
include: CONTACT_INCLUDE,
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
if (!contact) return;
|
|
182
|
+
|
|
183
|
+
const matchingWorkflows = workflows.filter((workflow) => {
|
|
184
|
+
if (
|
|
185
|
+
workflow.triggerTransactionFromStatus &&
|
|
186
|
+
oldStatus !== workflow.triggerTransactionFromStatus
|
|
187
|
+
)
|
|
188
|
+
return false;
|
|
189
|
+
if (workflow.triggerTransactionToStatus && newStatus !== workflow.triggerTransactionToStatus)
|
|
190
|
+
return false;
|
|
191
|
+
return true;
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
for (const workflow of matchingWorkflows) {
|
|
195
|
+
await executeWorkflowActions(workflow, contactId, contact);
|
|
196
|
+
}
|
|
197
|
+
} catch (error) {
|
|
198
|
+
console.error(
|
|
199
|
+
"Erreur lors de l'exécution des workflows pour changement de statut de transaction:",
|
|
200
|
+
error,
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Exécute les workflows déclenchés par un changement d'assignation
|
|
207
|
+
*/
|
|
208
|
+
export async function executeWorkflowsOnAssignmentChanged(contactId: string) {
|
|
209
|
+
try {
|
|
210
|
+
const workflows = await prisma.workflow.findMany({
|
|
211
|
+
where: { active: true, triggerType: 'CONTACT_ASSIGNMENT_CHANGED' },
|
|
212
|
+
include: WORKFLOW_INCLUDE,
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
const contact = await prisma.contact.findUnique({
|
|
216
|
+
where: { id: contactId },
|
|
217
|
+
include: CONTACT_INCLUDE,
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
if (!contact) return;
|
|
221
|
+
|
|
222
|
+
for (const workflow of workflows) {
|
|
223
|
+
await executeWorkflowActions(workflow, contactId, contact);
|
|
224
|
+
}
|
|
225
|
+
} catch (error) {
|
|
226
|
+
console.error("Erreur lors de l'exécution des workflows pour changement d'assignation:", error);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Exécute manuellement un workflow sur un contact
|
|
232
|
+
*/
|
|
233
|
+
export async function executeWorkflowManually(workflowId: string, contactId: string) {
|
|
234
|
+
const workflow = await prisma.workflow.findUnique({
|
|
235
|
+
where: { id: workflowId },
|
|
236
|
+
include: WORKFLOW_INCLUDE,
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
if (!workflow || !workflow.active || workflow.triggerType !== 'MANUAL') {
|
|
240
|
+
throw new Error('Workflow introuvable, inactif ou non manuel');
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const contact = await prisma.contact.findUnique({
|
|
244
|
+
where: { id: contactId },
|
|
245
|
+
include: CONTACT_INCLUDE,
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
if (!contact) {
|
|
249
|
+
throw new Error('Contact introuvable');
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
await executeWorkflowActions(workflow, contactId, contact);
|
|
253
|
+
}
|
|
254
|
+
|
|
117
255
|
/**
|
|
118
256
|
* Exécute les actions d'un workflow pour un contact
|
|
119
257
|
*/
|
|
120
258
|
async function executeWorkflowActions(workflow: any, contactId: string, contact: any) {
|
|
121
259
|
for (const action of workflow.actions) {
|
|
122
|
-
// Vérifier la condition
|
|
260
|
+
// Vérifier la condition statut
|
|
123
261
|
if (action.conditionOperator && action.conditionStatusId) {
|
|
124
|
-
const contactStatusId = contact.statusId;
|
|
125
262
|
const conditionMet =
|
|
126
263
|
action.conditionOperator === 'EQUALS'
|
|
127
|
-
?
|
|
128
|
-
:
|
|
264
|
+
? contact.statusId === action.conditionStatusId
|
|
265
|
+
: contact.statusId !== action.conditionStatusId;
|
|
266
|
+
if (!conditionMet) continue;
|
|
267
|
+
}
|
|
129
268
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
269
|
+
// Vérifier la condition origine
|
|
270
|
+
if (action.conditionOrigin) {
|
|
271
|
+
if ((contact.origin || '') !== action.conditionOrigin) continue;
|
|
133
272
|
}
|
|
134
273
|
|
|
135
|
-
//
|
|
274
|
+
// Vérifier la condition société
|
|
275
|
+
if (action.conditionHasCompany === true && !contact.companyId) continue;
|
|
276
|
+
if (action.conditionHasCompany === false && contact.companyId) continue;
|
|
277
|
+
|
|
136
278
|
const delayMs =
|
|
137
279
|
(action.delayDays || 0) * 24 * 60 * 60 * 1000 + (action.delayHours || 0) * 60 * 60 * 1000;
|
|
138
280
|
const executeAt = new Date(Date.now() + delayMs);
|
|
139
281
|
|
|
140
|
-
// Exécuter l'action selon son type
|
|
141
282
|
switch (action.actionType) {
|
|
142
283
|
case 'SEND_EMAIL':
|
|
143
284
|
await executeSendEmailAction(action, workflow, contact, executeAt);
|
|
@@ -151,47 +292,39 @@ async function executeWorkflowActions(workflow: any, contactId: string, contact:
|
|
|
151
292
|
case 'CREATE_TASK':
|
|
152
293
|
await executeCreateTaskAction(action, workflow, contactId, executeAt);
|
|
153
294
|
break;
|
|
295
|
+
case 'ASSIGN_CONTACT':
|
|
296
|
+
await executeAssignContactAction(action, workflow, contactId, executeAt);
|
|
297
|
+
break;
|
|
298
|
+
case 'ADD_NOTE':
|
|
299
|
+
await executeAddNoteAction(action, workflow, contactId, contact, executeAt);
|
|
300
|
+
break;
|
|
301
|
+
case 'NOTIFY_USER':
|
|
302
|
+
await executeNotifyUserAction(action, workflow, contactId, executeAt);
|
|
303
|
+
break;
|
|
154
304
|
case 'WAIT':
|
|
155
|
-
// L'action "Attendre" est gérée par le délai, rien à faire ici
|
|
156
305
|
break;
|
|
157
306
|
}
|
|
158
307
|
}
|
|
159
308
|
}
|
|
160
309
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
310
|
+
// ---------------------------------------------------------------------------
|
|
311
|
+
// Actions
|
|
312
|
+
// ---------------------------------------------------------------------------
|
|
313
|
+
|
|
164
314
|
async function executeSendEmailAction(action: any, workflow: any, contact: any, executeAt: Date) {
|
|
165
|
-
if (!action.emailTemplateId || !workflow.user.smtpConfig)
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
315
|
+
if (!action.emailTemplateId || !workflow.user.smtpConfig) return;
|
|
168
316
|
|
|
169
317
|
try {
|
|
170
318
|
const template = action.emailTemplate;
|
|
171
319
|
if (!template) return;
|
|
172
320
|
|
|
173
|
-
|
|
174
|
-
const variables = {
|
|
175
|
-
firstName: contact.firstName || '',
|
|
176
|
-
lastName: contact.lastName || '',
|
|
177
|
-
civility: contact.civility || '',
|
|
178
|
-
email: contact.email || '',
|
|
179
|
-
phone: contact.phone || '',
|
|
180
|
-
secondaryPhone: contact.secondaryPhone || '',
|
|
181
|
-
address: contact.address || '',
|
|
182
|
-
city: contact.city || '',
|
|
183
|
-
postalCode: contact.postalCode || '',
|
|
184
|
-
companyName: contact.companyName || '',
|
|
185
|
-
};
|
|
186
|
-
|
|
321
|
+
const variables = getContactVariables(contact);
|
|
187
322
|
const subject = replaceTemplateVariables(template.subject || '', variables);
|
|
188
323
|
const content = replaceTemplateVariables(template.content || '', variables);
|
|
189
324
|
|
|
190
|
-
// Si le délai est 0, envoyer immédiatement, sinon planifier
|
|
191
325
|
if (executeAt <= new Date()) {
|
|
192
326
|
await sendEmailImmediate(workflow, contact, template, subject, content);
|
|
193
327
|
} else {
|
|
194
|
-
// Planifier l'action pour exécution différée
|
|
195
328
|
await prisma.scheduledWorkflowAction.create({
|
|
196
329
|
data: {
|
|
197
330
|
workflowId: workflow.id,
|
|
@@ -216,34 +349,19 @@ async function executeSendEmailAction(action: any, workflow: any, contact: any,
|
|
|
216
349
|
}
|
|
217
350
|
}
|
|
218
351
|
|
|
219
|
-
/**
|
|
220
|
-
* Exécute l'action "Envoyer un SMS"
|
|
221
|
-
*/
|
|
222
352
|
async function executeSendSMSAction(action: any, workflow: any, contact: any, executeAt: Date) {
|
|
223
|
-
if (!action.smsMessage || !contact.phone)
|
|
224
|
-
return;
|
|
225
|
-
}
|
|
353
|
+
if (!action.smsMessage || !contact.phone) return;
|
|
226
354
|
|
|
227
355
|
try {
|
|
228
|
-
// Remplacer les variables dans le message
|
|
229
356
|
let message = action.smsMessage;
|
|
230
|
-
const variables: Record<string, string> =
|
|
231
|
-
firstName: contact.firstName || '',
|
|
232
|
-
lastName: contact.lastName || '',
|
|
233
|
-
email: contact.email || '',
|
|
234
|
-
phone: contact.phone || '',
|
|
235
|
-
companyName: contact.companyName || '',
|
|
236
|
-
};
|
|
237
|
-
|
|
357
|
+
const variables: Record<string, string> = getContactVariables(contact);
|
|
238
358
|
for (const [key, value] of Object.entries(variables)) {
|
|
239
359
|
message = message.replace(new RegExp(`{${key}}`, 'g'), value);
|
|
240
360
|
}
|
|
241
361
|
|
|
242
|
-
// Si le délai est 0, envoyer immédiatement
|
|
243
362
|
if (executeAt <= new Date()) {
|
|
244
363
|
await sendSMSImmediate(workflow, contact, message);
|
|
245
364
|
} else {
|
|
246
|
-
// Planifier l'action pour exécution différée
|
|
247
365
|
await prisma.scheduledWorkflowAction.create({
|
|
248
366
|
data: {
|
|
249
367
|
workflowId: workflow.id,
|
|
@@ -264,35 +382,25 @@ async function executeSendSMSAction(action: any, workflow: any, contact: any, ex
|
|
|
264
382
|
}
|
|
265
383
|
}
|
|
266
384
|
|
|
267
|
-
/**
|
|
268
|
-
* Exécute l'action "Changer le statut"
|
|
269
|
-
*/
|
|
270
385
|
async function executeChangeStatusAction(
|
|
271
386
|
action: any,
|
|
272
387
|
workflow: any,
|
|
273
388
|
contactId: string,
|
|
274
389
|
executeAt: Date,
|
|
275
390
|
) {
|
|
276
|
-
if (!action.newStatusId)
|
|
277
|
-
return;
|
|
278
|
-
}
|
|
391
|
+
if (!action.newStatusId) return;
|
|
279
392
|
|
|
280
393
|
try {
|
|
281
|
-
// Si le délai est 0, changer immédiatement
|
|
282
394
|
if (executeAt <= new Date()) {
|
|
283
395
|
await changeStatusImmediate(contactId, action.newStatusId);
|
|
284
396
|
} else {
|
|
285
|
-
// Planifier l'action pour exécution différée
|
|
286
397
|
await prisma.scheduledWorkflowAction.create({
|
|
287
398
|
data: {
|
|
288
399
|
workflowId: workflow.id,
|
|
289
400
|
actionId: action.id,
|
|
290
|
-
contactId
|
|
401
|
+
contactId,
|
|
291
402
|
actionType: 'CHANGE_STATUS',
|
|
292
|
-
actionData: {
|
|
293
|
-
newStatusId: action.newStatusId,
|
|
294
|
-
workflowName: workflow.name,
|
|
295
|
-
},
|
|
403
|
+
actionData: { newStatusId: action.newStatusId, workflowName: workflow.name },
|
|
296
404
|
executeAt,
|
|
297
405
|
},
|
|
298
406
|
});
|
|
@@ -302,35 +410,30 @@ async function executeChangeStatusAction(
|
|
|
302
410
|
}
|
|
303
411
|
}
|
|
304
412
|
|
|
305
|
-
/**
|
|
306
|
-
* Exécute l'action "Créer une tâche"
|
|
307
|
-
*/
|
|
308
413
|
async function executeCreateTaskAction(
|
|
309
414
|
action: any,
|
|
310
415
|
workflow: any,
|
|
311
416
|
contactId: string,
|
|
312
417
|
executeAt: Date,
|
|
313
418
|
) {
|
|
314
|
-
if (!action.taskTitle)
|
|
315
|
-
return;
|
|
316
|
-
}
|
|
419
|
+
if (!action.taskTitle) return;
|
|
317
420
|
|
|
318
421
|
try {
|
|
319
|
-
// Si le délai est 0, créer immédiatement
|
|
320
422
|
if (executeAt <= new Date()) {
|
|
321
423
|
await createTaskImmediate(action, workflow, contactId, executeAt);
|
|
322
424
|
} else {
|
|
323
|
-
// Planifier l'action pour exécution différée
|
|
324
425
|
await prisma.scheduledWorkflowAction.create({
|
|
325
426
|
data: {
|
|
326
427
|
workflowId: workflow.id,
|
|
327
428
|
actionId: action.id,
|
|
328
|
-
contactId
|
|
429
|
+
contactId,
|
|
329
430
|
actionType: 'CREATE_TASK',
|
|
330
431
|
actionData: {
|
|
331
432
|
taskTitle: action.taskTitle,
|
|
332
433
|
taskDescription: action.taskDescription || '',
|
|
333
|
-
|
|
434
|
+
taskType: action.taskType || 'OTHER',
|
|
435
|
+
taskPriority: action.taskPriority || 'MEDIUM',
|
|
436
|
+
taskAssignedUserId: action.taskAssignedUserId || workflow.userId,
|
|
334
437
|
userId: workflow.userId,
|
|
335
438
|
workflowName: workflow.name,
|
|
336
439
|
},
|
|
@@ -343,134 +446,247 @@ async function executeCreateTaskAction(
|
|
|
343
446
|
}
|
|
344
447
|
}
|
|
345
448
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
*/
|
|
349
|
-
|
|
350
|
-
/**
|
|
351
|
-
* Envoie un email immédiatement
|
|
352
|
-
*/
|
|
353
|
-
export async function sendEmailImmediate(
|
|
449
|
+
async function executeAssignContactAction(
|
|
450
|
+
action: any,
|
|
354
451
|
workflow: any,
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
subject: string,
|
|
358
|
-
content: string,
|
|
452
|
+
contactId: string,
|
|
453
|
+
executeAt: Date,
|
|
359
454
|
) {
|
|
360
|
-
|
|
361
|
-
if (!workflow.user.smtpConfig) {
|
|
362
|
-
throw new Error('Configuration SMTP non trouvée');
|
|
363
|
-
}
|
|
455
|
+
if (!action.assignCommercialId && !action.assignTeleproId) return;
|
|
364
456
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
457
|
+
try {
|
|
458
|
+
if (executeAt <= new Date()) {
|
|
459
|
+
await assignContactImmediate(contactId, action.assignCommercialId, action.assignTeleproId);
|
|
460
|
+
} else {
|
|
461
|
+
await prisma.scheduledWorkflowAction.create({
|
|
462
|
+
data: {
|
|
463
|
+
workflowId: workflow.id,
|
|
464
|
+
actionId: action.id,
|
|
465
|
+
contactId,
|
|
466
|
+
actionType: 'ASSIGN_CONTACT',
|
|
467
|
+
actionData: {
|
|
468
|
+
assignCommercialId: action.assignCommercialId,
|
|
469
|
+
assignTeleproId: action.assignTeleproId,
|
|
470
|
+
workflowName: workflow.name,
|
|
471
|
+
},
|
|
472
|
+
executeAt,
|
|
473
|
+
},
|
|
474
|
+
});
|
|
371
475
|
}
|
|
476
|
+
} catch (error) {
|
|
477
|
+
console.error("Erreur lors de l'assignation du contact:", error);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
372
480
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
},
|
|
382
|
-
});
|
|
481
|
+
async function executeAddNoteAction(
|
|
482
|
+
action: any,
|
|
483
|
+
workflow: any,
|
|
484
|
+
contactId: string,
|
|
485
|
+
contact: any,
|
|
486
|
+
executeAt: Date,
|
|
487
|
+
) {
|
|
488
|
+
if (!action.noteContent) return;
|
|
383
489
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
: workflow.user.smtpConfig.fromEmail,
|
|
388
|
-
to: contact.email,
|
|
389
|
-
subject,
|
|
390
|
-
html: content,
|
|
391
|
-
});
|
|
490
|
+
try {
|
|
491
|
+
const variables = getContactVariables(contact);
|
|
492
|
+
const content = replaceTemplateVariables(action.noteContent, variables);
|
|
392
493
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
494
|
+
if (executeAt <= new Date()) {
|
|
495
|
+
await addNoteImmediate(contactId, content, workflow.userId, workflow.name);
|
|
496
|
+
} else {
|
|
497
|
+
await prisma.scheduledWorkflowAction.create({
|
|
498
|
+
data: {
|
|
499
|
+
workflowId: workflow.id,
|
|
500
|
+
actionId: action.id,
|
|
501
|
+
contactId,
|
|
502
|
+
actionType: 'ADD_NOTE',
|
|
503
|
+
actionData: {
|
|
504
|
+
noteContent: action.noteContent,
|
|
505
|
+
userId: workflow.userId,
|
|
506
|
+
workflowName: workflow.name,
|
|
507
|
+
},
|
|
508
|
+
executeAt,
|
|
509
|
+
},
|
|
510
|
+
});
|
|
511
|
+
}
|
|
404
512
|
} catch (error) {
|
|
405
|
-
console.error("Erreur lors de l'
|
|
406
|
-
throw error;
|
|
513
|
+
console.error("Erreur lors de l'ajout de la note:", error);
|
|
407
514
|
}
|
|
408
515
|
}
|
|
409
516
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
517
|
+
async function executeNotifyUserAction(
|
|
518
|
+
action: any,
|
|
519
|
+
workflow: any,
|
|
520
|
+
contactId: string,
|
|
521
|
+
executeAt: Date,
|
|
522
|
+
) {
|
|
523
|
+
if (!action.notifyUserId || !action.taskTitle) return;
|
|
524
|
+
|
|
414
525
|
try {
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
526
|
+
if (executeAt <= new Date()) {
|
|
527
|
+
await notifyUserImmediate(action, workflow, contactId, executeAt);
|
|
528
|
+
} else {
|
|
529
|
+
await prisma.scheduledWorkflowAction.create({
|
|
530
|
+
data: {
|
|
531
|
+
workflowId: workflow.id,
|
|
532
|
+
actionId: action.id,
|
|
533
|
+
contactId,
|
|
534
|
+
actionType: 'NOTIFY_USER',
|
|
535
|
+
actionData: {
|
|
536
|
+
notifyUserId: action.notifyUserId,
|
|
537
|
+
taskTitle: action.taskTitle,
|
|
538
|
+
taskDescription: action.taskDescription || '',
|
|
539
|
+
userId: workflow.userId,
|
|
540
|
+
workflowName: workflow.name,
|
|
541
|
+
},
|
|
542
|
+
executeAt,
|
|
543
|
+
},
|
|
544
|
+
});
|
|
545
|
+
}
|
|
429
546
|
} catch (error) {
|
|
430
|
-
console.error("Erreur lors de l'
|
|
431
|
-
throw error;
|
|
547
|
+
console.error("Erreur lors de la notification de l'utilisateur:", error);
|
|
432
548
|
}
|
|
433
549
|
}
|
|
434
550
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
551
|
+
// ---------------------------------------------------------------------------
|
|
552
|
+
// Fonctions d'exécution immédiate (utilisées aussi par le cron)
|
|
553
|
+
// ---------------------------------------------------------------------------
|
|
554
|
+
|
|
555
|
+
export async function sendEmailImmediate(
|
|
556
|
+
workflow: any,
|
|
557
|
+
contact: any,
|
|
558
|
+
template: any,
|
|
559
|
+
subject: string,
|
|
560
|
+
content: string,
|
|
561
|
+
) {
|
|
562
|
+
if (!workflow.user.smtpConfig) {
|
|
563
|
+
throw new Error('Configuration SMTP non trouvée');
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
let password: string;
|
|
439
567
|
try {
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
});
|
|
444
|
-
} catch (error) {
|
|
445
|
-
console.error('Erreur lors du changement de statut:', error);
|
|
446
|
-
throw error;
|
|
568
|
+
password = decrypt(workflow.user.smtpConfig.password);
|
|
569
|
+
} catch {
|
|
570
|
+
password = workflow.user.smtpConfig.password;
|
|
447
571
|
}
|
|
572
|
+
|
|
573
|
+
const transporter = nodemailer.createTransport({
|
|
574
|
+
host: workflow.user.smtpConfig.host,
|
|
575
|
+
port: workflow.user.smtpConfig.port,
|
|
576
|
+
secure: workflow.user.smtpConfig.secure,
|
|
577
|
+
auth: { user: workflow.user.smtpConfig.username, pass: password },
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
await transporter.sendMail({
|
|
581
|
+
from: workflow.user.smtpConfig.fromName
|
|
582
|
+
? `"${workflow.user.smtpConfig.fromName}" <${workflow.user.smtpConfig.fromEmail}>`
|
|
583
|
+
: workflow.user.smtpConfig.fromEmail,
|
|
584
|
+
to: contact.email,
|
|
585
|
+
subject,
|
|
586
|
+
html: content,
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
await prisma.interaction.create({
|
|
590
|
+
data: {
|
|
591
|
+
contactId: contact.id,
|
|
592
|
+
type: 'EMAIL',
|
|
593
|
+
title: `Email automatique: ${template.name}`,
|
|
594
|
+
content: `Email envoyé automatiquement via le workflow "${workflow.name}"`,
|
|
595
|
+
userId: workflow.userId,
|
|
596
|
+
date: new Date(),
|
|
597
|
+
},
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
export async function sendSMSImmediate(workflow: any, contact: any, message: string) {
|
|
602
|
+
console.log(`SMS à envoyer à ${contact.phone}: ${message}`);
|
|
603
|
+
|
|
604
|
+
await prisma.interaction.create({
|
|
605
|
+
data: {
|
|
606
|
+
contactId: contact.id,
|
|
607
|
+
type: 'SMS',
|
|
608
|
+
title: 'SMS automatique',
|
|
609
|
+
content: message,
|
|
610
|
+
userId: workflow.userId,
|
|
611
|
+
date: new Date(),
|
|
612
|
+
},
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
export async function changeStatusImmediate(contactId: string, newStatusId: string) {
|
|
617
|
+
await prisma.contact.update({
|
|
618
|
+
where: { id: contactId },
|
|
619
|
+
data: { statusId: newStatusId },
|
|
620
|
+
});
|
|
448
621
|
}
|
|
449
622
|
|
|
450
|
-
/**
|
|
451
|
-
* Crée une tâche immédiatement
|
|
452
|
-
*/
|
|
453
623
|
export async function createTaskImmediate(
|
|
454
624
|
action: any,
|
|
455
625
|
workflow: any,
|
|
456
626
|
contactId: string,
|
|
457
627
|
scheduledAt: Date,
|
|
458
628
|
) {
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
629
|
+
await prisma.task.create({
|
|
630
|
+
data: {
|
|
631
|
+
type: action.taskType || 'OTHER',
|
|
632
|
+
title: action.taskTitle,
|
|
633
|
+
description: action.taskDescription || '',
|
|
634
|
+
priority: action.taskPriority || 'MEDIUM',
|
|
635
|
+
scheduledAt,
|
|
636
|
+
contactId,
|
|
637
|
+
assignedUserId: action.taskAssignedUserId || workflow.userId,
|
|
638
|
+
createdById: workflow.userId,
|
|
639
|
+
},
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
export async function assignContactImmediate(
|
|
644
|
+
contactId: string,
|
|
645
|
+
commercialId?: string | null,
|
|
646
|
+
teleproId?: string | null,
|
|
647
|
+
) {
|
|
648
|
+
const data: any = {};
|
|
649
|
+
if (commercialId !== undefined && commercialId !== null) data.assignedCommercialId = commercialId;
|
|
650
|
+
if (teleproId !== undefined && teleproId !== null) data.assignedTeleproId = teleproId;
|
|
651
|
+
if (Object.keys(data).length === 0) return;
|
|
652
|
+
|
|
653
|
+
await prisma.contact.update({ where: { id: contactId }, data });
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
export async function addNoteImmediate(
|
|
657
|
+
contactId: string,
|
|
658
|
+
content: string,
|
|
659
|
+
userId: string | null,
|
|
660
|
+
workflowName: string,
|
|
661
|
+
) {
|
|
662
|
+
await prisma.interaction.create({
|
|
663
|
+
data: {
|
|
664
|
+
contactId,
|
|
665
|
+
type: 'NOTE',
|
|
666
|
+
title: `Note automatique (${workflowName})`,
|
|
667
|
+
content,
|
|
668
|
+
userId,
|
|
669
|
+
date: new Date(),
|
|
670
|
+
},
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
export async function notifyUserImmediate(
|
|
675
|
+
action: any,
|
|
676
|
+
workflow: any,
|
|
677
|
+
contactId: string,
|
|
678
|
+
scheduledAt: Date,
|
|
679
|
+
) {
|
|
680
|
+
await prisma.task.create({
|
|
681
|
+
data: {
|
|
682
|
+
type: 'OTHER',
|
|
683
|
+
title: action.taskTitle,
|
|
684
|
+
description: action.taskDescription || '',
|
|
685
|
+
priority: 'MEDIUM',
|
|
686
|
+
scheduledAt,
|
|
687
|
+
contactId,
|
|
688
|
+
assignedUserId: action.notifyUserId,
|
|
689
|
+
createdById: workflow.userId,
|
|
690
|
+
},
|
|
691
|
+
});
|
|
476
692
|
}
|