n8n-nodes-ritsa-crm 4.0.2 → 4.0.3
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.
|
@@ -12,149 +12,45 @@ class RitsaTools {
|
|
|
12
12
|
version: 4,
|
|
13
13
|
subtitle: '={{$parameter["action"]}}',
|
|
14
14
|
description: 'Ritsa CRM Yapay Zeka Aracı. KURAL: ID alanları otomatik çözülür — customerName, serviceName, staffName gibi İSİM alanlarını kullan, asla sayısal ID uydurma!',
|
|
15
|
-
defaults: {
|
|
16
|
-
name: 'Ritsa CRM',
|
|
17
|
-
},
|
|
15
|
+
defaults: { name: 'Ritsa CRM' },
|
|
18
16
|
// @ts-ignore
|
|
19
17
|
usableAsTool: true,
|
|
20
18
|
inputs: ['main'],
|
|
21
19
|
outputs: ['main'],
|
|
22
|
-
credentials: [
|
|
23
|
-
{
|
|
24
|
-
name: 'ritsaCrmApi',
|
|
25
|
-
required: true,
|
|
26
|
-
},
|
|
27
|
-
],
|
|
20
|
+
credentials: [{ name: 'ritsaCrmApi', required: true }],
|
|
28
21
|
properties: [
|
|
29
|
-
// ─── ACTION ───────────────────────────────────────────────────────────
|
|
30
22
|
{
|
|
31
|
-
displayName: 'İşlem (Action)',
|
|
32
|
-
name: 'action',
|
|
33
|
-
type: 'string',
|
|
34
|
-
default: 'getCustomers',
|
|
23
|
+
displayName: 'İşlem (Action)', name: 'action', type: 'string', default: 'getCustomers',
|
|
35
24
|
description: 'Yapılacak işlem. Değerler: getCustomers, createCustomer, createAppointment, getAppointments, updateAppointmentStatus, sendMessage, getOpportunities, updateLeadStatus, makeSale, createPayment, getReceivables, collectReceivable, getDailySummary, getCallbacks, createCallback, markAsCalled, getAutomationRules, runAutomation, getDashboardSummary, getServices, getStaff, getBranches, getPreConsultations, createPreConsultation, updatePreConsultation, checkStock',
|
|
36
25
|
},
|
|
37
|
-
// ─── IDENTITY FIELDS (names, never IDs) ─────────────────────────────
|
|
38
26
|
{
|
|
39
|
-
displayName: 'Müşteri Adı (Customer Name)',
|
|
40
|
-
name: 'customerName',
|
|
41
|
-
type: 'string',
|
|
42
|
-
default: '',
|
|
27
|
+
displayName: 'Müşteri Adı (Customer Name)', name: 'customerName', type: 'string', default: '',
|
|
43
28
|
description: 'Müşterinin adı veya ad soyad bilgisi (Örn: "İlker Gülen"). ID UYDURMA — sistem otomatik bulur.',
|
|
44
29
|
},
|
|
45
30
|
{
|
|
46
|
-
displayName: 'Hizmet Adı (Service Name)',
|
|
47
|
-
name: 'serviceName',
|
|
48
|
-
type: 'string',
|
|
49
|
-
default: '',
|
|
31
|
+
displayName: 'Hizmet Adı (Service Name)', name: 'serviceName', type: 'string', default: '',
|
|
50
32
|
description: 'Hizmetin adı (Örn: "Lazer", "Cilt Bakımı"). ID UYDURMA — sistem otomatik bulur.',
|
|
51
33
|
},
|
|
52
34
|
{
|
|
53
|
-
displayName: 'Personel Adı (Staff Name)',
|
|
54
|
-
|
|
55
|
-
type: 'string',
|
|
56
|
-
default: '',
|
|
57
|
-
description: 'Personelin adı (Örn: "Ahmet"). Boş bırakılırsa ilk müsait personel atanır. ID UYDURMA.',
|
|
58
|
-
},
|
|
59
|
-
// ─── DATE / TIME ──────────────────────────────────────────────────────
|
|
60
|
-
{
|
|
61
|
-
displayName: 'Tarih (Date)',
|
|
62
|
-
name: 'date',
|
|
63
|
-
type: 'string',
|
|
64
|
-
default: '',
|
|
65
|
-
description: 'Tarih bilgisi. Format: YYYY-MM-DD (Örn: "2026-07-01")',
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
displayName: 'Saat (Time)',
|
|
69
|
-
name: 'time',
|
|
70
|
-
type: 'string',
|
|
71
|
-
default: '',
|
|
72
|
-
description: 'Saat bilgisi. Format: HH:MM (Örn: "14:00")',
|
|
73
|
-
},
|
|
74
|
-
// ─── SEARCH / TEXT ────────────────────────────────────────────────────
|
|
75
|
-
{
|
|
76
|
-
displayName: 'Arama Sorgusu (Search Query)',
|
|
77
|
-
name: 'searchQuery',
|
|
78
|
-
type: 'string',
|
|
79
|
-
default: '',
|
|
80
|
-
description: 'Arama veya filtreleme için kullanılacak metin.',
|
|
81
|
-
},
|
|
82
|
-
{
|
|
83
|
-
displayName: 'Notlar (Notes)',
|
|
84
|
-
name: 'notes',
|
|
85
|
-
type: 'string',
|
|
86
|
-
default: '',
|
|
87
|
-
description: 'İşleme eklenecek notlar veya açıklama.',
|
|
88
|
-
},
|
|
89
|
-
// ─── MESSAGING ────────────────────────────────────────────────────────
|
|
90
|
-
{
|
|
91
|
-
displayName: 'Mesaj İçeriği (Message)',
|
|
92
|
-
name: 'messageContent',
|
|
93
|
-
type: 'string',
|
|
94
|
-
default: '',
|
|
95
|
-
description: 'Müşteriye gönderilecek WhatsApp veya SMS mesajı.',
|
|
96
|
-
},
|
|
97
|
-
// ─── STATUS UPDATE ────────────────────────────────────────────────────
|
|
98
|
-
{
|
|
99
|
-
displayName: 'Yeni Durum (New Status)',
|
|
100
|
-
name: 'newStatus',
|
|
101
|
-
type: 'string',
|
|
102
|
-
default: '',
|
|
103
|
-
description: 'Güncellenecek yeni durum değeri (Örn: "confirmed", "cancelled", "contacted", "won", "lost").',
|
|
104
|
-
},
|
|
105
|
-
// ─── FINANCIAL ────────────────────────────────────────────────────────
|
|
106
|
-
{
|
|
107
|
-
displayName: 'Net Tutar (Net Amount)',
|
|
108
|
-
name: 'netAmount',
|
|
109
|
-
type: 'number',
|
|
110
|
-
default: 0,
|
|
111
|
-
description: 'İşlemin net tutarı (TL).',
|
|
112
|
-
},
|
|
113
|
-
{
|
|
114
|
-
displayName: 'İndirim Tutarı (Discount Amount)',
|
|
115
|
-
name: 'discountAmount',
|
|
116
|
-
type: 'number',
|
|
117
|
-
default: 0,
|
|
118
|
-
description: 'Uygulanacak indirim miktarı (TL).',
|
|
119
|
-
},
|
|
120
|
-
{
|
|
121
|
-
displayName: 'Satış Kalemleri JSON (Sale Items)',
|
|
122
|
-
name: 'saleItemsJson',
|
|
123
|
-
type: 'string',
|
|
124
|
-
default: '[]',
|
|
125
|
-
description: 'Satış kalemleri listesi (JSON Array). Örn: [{"name":"Lazer","quantity":1,"unit_price":350}]',
|
|
126
|
-
},
|
|
127
|
-
{
|
|
128
|
-
displayName: 'Ödeme Yöntemleri JSON (Payment Methods)',
|
|
129
|
-
name: 'paymentMethodsJson',
|
|
130
|
-
type: 'string',
|
|
131
|
-
default: '[]',
|
|
132
|
-
description: 'Ödeme yöntemleri listesi (JSON Array). Örn: [{"method":"cash","amount":350}]',
|
|
133
|
-
},
|
|
134
|
-
// ─── REFERENCE IDs (only when absolutely necessary) ──────────────────
|
|
135
|
-
{
|
|
136
|
-
displayName: 'Referans ID',
|
|
137
|
-
name: 'referenceId',
|
|
138
|
-
type: 'number',
|
|
139
|
-
default: 0,
|
|
140
|
-
description: 'Belirli bir kaydı güncellemek veya silmek için kullanılan ID numarası (Örn: updateAppointmentStatus için appointmentId, collectReceivable için receivableId). Listeden dönülen gerçek bir ID değer olmalıdır.',
|
|
141
|
-
},
|
|
142
|
-
// ─── BRANCH ──────────────────────────────────────────────────────────
|
|
143
|
-
{
|
|
144
|
-
displayName: 'Şube ID (Branch ID)',
|
|
145
|
-
name: 'branchId',
|
|
146
|
-
type: 'number',
|
|
147
|
-
default: '',
|
|
148
|
-
description: 'İşlem yapılacak şube ID numarası. Boş bırakılırsa Credentials içindeki şube ID kullanılır.',
|
|
35
|
+
displayName: 'Personel Adı (Staff Name)', name: 'staffName', type: 'string', default: '',
|
|
36
|
+
description: 'Personelin adı. Boş bırakılırsa ilk müsait personel atanır. ID UYDURMA.',
|
|
149
37
|
},
|
|
150
|
-
|
|
38
|
+
{ displayName: 'Tarih (Date)', name: 'date', type: 'string', default: '', description: 'Format: YYYY-MM-DD (Örn: "2026-07-01")' },
|
|
39
|
+
{ displayName: 'Saat (Time)', name: 'time', type: 'string', default: '', description: 'Format: HH:MM (Örn: "14:00")' },
|
|
40
|
+
{ displayName: 'Arama Sorgusu (Search Query)', name: 'searchQuery', type: 'string', default: '', description: 'Arama veya filtreleme metni.' },
|
|
41
|
+
{ displayName: 'Notlar (Notes)', name: 'notes', type: 'string', default: '', description: 'İşleme eklenecek notlar.' },
|
|
42
|
+
{ displayName: 'Mesaj İçeriği (Message)', name: 'messageContent', type: 'string', default: '', description: 'Gönderilecek WhatsApp/SMS mesajı.' },
|
|
43
|
+
{ displayName: 'Yeni Durum (New Status)', name: 'newStatus', type: 'string', default: '', description: 'Yeni durum (Örn: "confirmed", "cancelled", "won", "lost").' },
|
|
44
|
+
{ displayName: 'Net Tutar (Net Amount)', name: 'netAmount', type: 'number', default: 0, description: 'Net tutar (TL).' },
|
|
45
|
+
{ displayName: 'İndirim Tutarı (Discount Amount)', name: 'discountAmount', type: 'number', default: 0, description: 'İndirim tutarı (TL).' },
|
|
46
|
+
{ displayName: 'Satış Kalemleri JSON (Sale Items)', name: 'saleItemsJson', type: 'string', default: '[]', description: 'Örn: [{"name":"Lazer","quantity":1,"unit_price":350}]' },
|
|
47
|
+
{ displayName: 'Ödeme Yöntemleri JSON (Payment Methods)', name: 'paymentMethodsJson', type: 'string', default: '[]', description: 'Örn: [{"method":"cash","amount":350}]' },
|
|
151
48
|
{
|
|
152
|
-
displayName: '
|
|
153
|
-
|
|
154
|
-
type: 'string',
|
|
155
|
-
default: '{}',
|
|
156
|
-
description: 'Yukarıdaki alanlara sığmayan ek parametreler için JSON. Normalde boş bırakılabilir.',
|
|
49
|
+
displayName: 'Referans ID', name: 'referenceId', type: 'number', default: 0,
|
|
50
|
+
description: 'Güncelleme/silme işlemleri için ID (Örn: updateAppointmentStatus için randevu ID\'si).',
|
|
157
51
|
},
|
|
52
|
+
{ displayName: 'Şube ID (Branch ID)', name: 'branchId', type: 'number', default: 0, description: 'Boş bırakılırsa Credentials içindeki şube ID kullanılır.' },
|
|
53
|
+
{ displayName: 'Ek Parametreler (Extra JSON)', name: 'extraJson', type: 'string', default: '{}', description: 'Ek parametreler için JSON. Normalde boş bırakılabilir.' },
|
|
158
54
|
],
|
|
159
55
|
};
|
|
160
56
|
}
|
|
@@ -163,364 +59,275 @@ class RitsaTools {
|
|
|
163
59
|
const returnData = [];
|
|
164
60
|
const credentials = await this.getCredentials('ritsaCrmApi');
|
|
165
61
|
const rawBase = credentials.baseUrl.replace(/\/$/, '');
|
|
166
|
-
// Ensure /api prefix exists exactly once
|
|
167
62
|
const baseUrl = rawBase.endsWith('/api') ? rawBase : `${rawBase}/api`;
|
|
168
63
|
const apiKey = credentials.apiKey;
|
|
169
64
|
const globalBranchId = credentials.branchId || 1;
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
const options = {
|
|
65
|
+
const apiReq = async (method, path, body, qs, bId) => {
|
|
66
|
+
const opts = {
|
|
173
67
|
headers: {
|
|
174
68
|
Authorization: `Bearer ${apiKey}`,
|
|
175
69
|
'Content-Type': 'application/json',
|
|
176
|
-
'X-Branch-Id': String(
|
|
70
|
+
'X-Branch-Id': String(bId || globalBranchId),
|
|
177
71
|
},
|
|
178
|
-
method,
|
|
179
|
-
uri: `${baseUrl}${path}`,
|
|
180
|
-
json: true,
|
|
72
|
+
method, uri: `${baseUrl}${path}`, json: true,
|
|
181
73
|
};
|
|
182
|
-
if (qs && Object.keys(qs).length
|
|
183
|
-
|
|
74
|
+
if (qs && Object.keys(qs).length)
|
|
75
|
+
opts.qs = qs;
|
|
184
76
|
if (body && ['POST', 'PUT', 'PATCH', 'DELETE'].includes(method))
|
|
185
|
-
|
|
186
|
-
return this.helpers.request(
|
|
77
|
+
opts.body = body;
|
|
78
|
+
return this.helpers.request(opts);
|
|
79
|
+
};
|
|
80
|
+
const ALIASES = {
|
|
81
|
+
searchcustomer: 'getCustomers', searchcustomers: 'getCustomers', listcustomers: 'getCustomers',
|
|
82
|
+
createcustomer: 'createCustomer', addcustomer: 'createCustomer', newcustomer: 'createCustomer',
|
|
83
|
+
getcustomerdetails: 'getCustomerDetails', getcustomer: 'getCustomerDetails',
|
|
84
|
+
listappointments: 'getAppointments', getappointments: 'getAppointments',
|
|
85
|
+
createappointment: 'createAppointment', addappointment: 'createAppointment', bookappointment: 'createAppointment',
|
|
86
|
+
updateappointment: 'updateAppointmentStatus', updateappointmentstatus: 'updateAppointmentStatus', changeappointmentstatus: 'updateAppointmentStatus',
|
|
87
|
+
sendmessage: 'sendMessage', sendwhatsapp: 'sendMessage', sendsms: 'sendMessage',
|
|
88
|
+
getmessages: 'getMessages',
|
|
89
|
+
getopportunities: 'getOpportunities',
|
|
90
|
+
updateleadstatus: 'updateLeadStatus', changeleadstatus: 'updateLeadStatus',
|
|
91
|
+
getcurrentsession: 'getCurrentSession',
|
|
92
|
+
togglesession: 'toggleSession', opensession: 'toggleSession', closesession: 'toggleSession',
|
|
93
|
+
makesale: 'makeSale', createsale: 'makeSale',
|
|
94
|
+
getreceivables: 'getReceivables',
|
|
95
|
+
collectreceivable: 'collectReceivable', payreceivable: 'collectReceivable',
|
|
96
|
+
getautomationrules: 'getAutomationRules',
|
|
97
|
+
toggleautomation: 'toggleAutomation', runautomation: 'runAutomation',
|
|
98
|
+
getdailysummary: 'getDailySummary',
|
|
99
|
+
createpayment: 'createPayment', makepayment: 'createPayment',
|
|
100
|
+
refundpayment: 'refundPayment',
|
|
101
|
+
getcallbacks: 'getCallbacks',
|
|
102
|
+
createcallback: 'createCallback', addcallback: 'createCallback',
|
|
103
|
+
markascalled: 'markAsCalled', markcalled: 'markAsCalled',
|
|
104
|
+
getdashboardsummary: 'getDashboardSummary', dashboard: 'getDashboardSummary',
|
|
105
|
+
getnavbadges: 'getNavBadges',
|
|
106
|
+
checkstock: 'checkStock', searchproduct: 'checkStock', getproducts: 'checkStock',
|
|
107
|
+
getservices: 'getServices',
|
|
108
|
+
getstaff: 'getStaff',
|
|
109
|
+
getbranches: 'getBranches',
|
|
110
|
+
getpreconsultations: 'getPreConsultations', listpreconsultations: 'getPreConsultations',
|
|
111
|
+
createpreconsultation: 'createPreConsultation', addpreconsultation: 'createPreConsultation',
|
|
112
|
+
updatepreconsultation: 'updatePreConsultation',
|
|
187
113
|
};
|
|
114
|
+
// These actions don't need customer lookup before proceeding
|
|
115
|
+
const NO_CUSTOMER_RESOLVE = new Set([
|
|
116
|
+
'getCustomers', 'createCustomer', 'getPreConsultations', 'createPreConsultation',
|
|
117
|
+
'getServices', 'getStaff', 'getBranches', 'getDashboardSummary', 'getNavBadges',
|
|
118
|
+
'getAutomationRules', 'getDailySummary', 'getCurrentSession', 'getAppointments',
|
|
119
|
+
'getMessages', 'getOpportunities', 'getReceivables', 'getCallbacks', 'checkStock',
|
|
120
|
+
]);
|
|
188
121
|
for (let i = 0; i < items.length; i++) {
|
|
189
122
|
let action = this.getNodeParameter('action', i).toLowerCase().trim();
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
const
|
|
195
|
-
const
|
|
196
|
-
const
|
|
197
|
-
const
|
|
198
|
-
if (!
|
|
199
|
-
return
|
|
123
|
+
if (ALIASES[action])
|
|
124
|
+
action = ALIASES[action];
|
|
125
|
+
const rawBId = this.getNodeParameter('branchId', i, 0);
|
|
126
|
+
const bId = rawBId > 0 ? rawBId : globalBranchId;
|
|
127
|
+
const s = (k) => this.getNodeParameter(k, i, '') || '';
|
|
128
|
+
const n = (k) => this.getNodeParameter(k, i, 0) || 0;
|
|
129
|
+
const j = (k, fb) => {
|
|
130
|
+
const r = s(k);
|
|
131
|
+
if (!r || r === '{}' || r === '[]')
|
|
132
|
+
return fb;
|
|
200
133
|
try {
|
|
201
|
-
return JSON.parse(
|
|
134
|
+
return JSON.parse(r);
|
|
202
135
|
}
|
|
203
136
|
catch {
|
|
204
|
-
return
|
|
137
|
+
return fb;
|
|
205
138
|
}
|
|
206
139
|
};
|
|
207
|
-
|
|
208
|
-
const extra = parseJsonParam('extraJson', {});
|
|
209
|
-
// ── Action Aliases ────────────────────────────────────────────────────
|
|
210
|
-
const aliases = {
|
|
211
|
-
searchcustomer: 'getCustomers', searchcustomers: 'getCustomers', listcustomers: 'getCustomers',
|
|
212
|
-
createcustomer: 'createCustomer', addcustomer: 'createCustomer', newcustomer: 'createCustomer',
|
|
213
|
-
getcustomerdetails: 'getCustomerDetails', getcustomer: 'getCustomerDetails',
|
|
214
|
-
listappointments: 'getAppointments', searchappointments: 'getAppointments', getappointments: 'getAppointments',
|
|
215
|
-
createappointment: 'createAppointment', addappointment: 'createAppointment', bookappointment: 'createAppointment',
|
|
216
|
-
updateappointment: 'updateAppointmentStatus', changeappointmentstatus: 'updateAppointmentStatus', updateappointmentstatus: 'updateAppointmentStatus',
|
|
217
|
-
sendmessage: 'sendMessage', sendwhatsapp: 'sendMessage', sendsms: 'sendMessage',
|
|
218
|
-
getmessages: 'getMessages', listmessages: 'getMessages',
|
|
219
|
-
getopportunities: 'getOpportunities', listopportunities: 'getOpportunities',
|
|
220
|
-
updateleadstatus: 'updateLeadStatus', changeleadstatus: 'updateLeadStatus',
|
|
221
|
-
getcurrentsession: 'getCurrentSession', getsession: 'getCurrentSession',
|
|
222
|
-
togglesession: 'toggleSession', opensession: 'toggleSession', closesession: 'toggleSession',
|
|
223
|
-
makesale: 'makeSale', createsale: 'makeSale',
|
|
224
|
-
getreceivables: 'getReceivables', listreceivables: 'getReceivables',
|
|
225
|
-
collectreceivable: 'collectReceivable', payreceivable: 'collectReceivable',
|
|
226
|
-
getautomationrules: 'getAutomationRules', listautomations: 'getAutomationRules',
|
|
227
|
-
toggleautomation: 'toggleAutomation', runautomation: 'runAutomation',
|
|
228
|
-
getdailysummary: 'getDailySummary', summary: 'getDailySummary',
|
|
229
|
-
createpayment: 'createPayment', makepayment: 'createPayment',
|
|
230
|
-
refundpayment: 'refundPayment', cancelpayment: 'refundPayment',
|
|
231
|
-
getcallbacks: 'getCallbacks', listcallbacks: 'getCallbacks',
|
|
232
|
-
createcallback: 'addCallback', addcallback: 'createCallback',
|
|
233
|
-
markascalled: 'markAsCalled', completecallback: 'markAsCalled', markcalled: 'markAsCalled',
|
|
234
|
-
getdashboardsummary: 'getDashboardSummary', dashboard: 'getDashboardSummary',
|
|
235
|
-
getnavbadges: 'getNavBadges', badges: 'getNavBadges',
|
|
236
|
-
checkstock: 'checkStock', searchproduct: 'checkStock', getproducts: 'checkStock', listproducts: 'checkStock',
|
|
237
|
-
getservices: 'getServices', listservices: 'getServices',
|
|
238
|
-
getstaff: 'getStaff', liststaff: 'getStaff',
|
|
239
|
-
getbranches: 'getBranches', listbranches: 'getBranches',
|
|
240
|
-
getpreconsultations: 'getPreConsultations', listpreconsultations: 'getPreConsultations', searchpreconsultation: 'getPreConsultations',
|
|
241
|
-
createpreconsultation: 'createPreConsultation', addpreconsultation: 'createPreConsultation',
|
|
242
|
-
updatepreconsultation: 'updatePreConsultation', editpreconsultation: 'updatePreConsultation',
|
|
243
|
-
};
|
|
244
|
-
if (aliases[action])
|
|
245
|
-
action = aliases[action];
|
|
140
|
+
const extra = j('extraJson', {});
|
|
246
141
|
try {
|
|
247
|
-
let
|
|
248
|
-
// ── AUTO-RESOLVE
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
}
|
|
266
|
-
catch (e) {
|
|
267
|
-
throw e;
|
|
142
|
+
let resp;
|
|
143
|
+
// ── CUSTOMER AUTO-RESOLVE ─────────────────────────────────────────
|
|
144
|
+
const custName = s('customerName').trim();
|
|
145
|
+
let custId = 0;
|
|
146
|
+
if (custName) {
|
|
147
|
+
const r = await apiReq('GET', '/customers', undefined, { search: custName, branch_id: bId }, bId).catch(() => []);
|
|
148
|
+
if (Array.isArray(r) && r.length > 0) {
|
|
149
|
+
custId = r[0].id;
|
|
150
|
+
}
|
|
151
|
+
else if (!NO_CUSTOMER_RESOLVE.has(action)) {
|
|
152
|
+
// SOFT ERROR: workflow continues, AI Agent reads and reacts
|
|
153
|
+
returnData.push({ json: {
|
|
154
|
+
success: false,
|
|
155
|
+
action_required: 'createCustomer',
|
|
156
|
+
error: `"${custName}" adında müşteri bulunamadı.`,
|
|
157
|
+
instruction: `Önce action=createCustomer, customerName="${custName}" ile müşteriyi sisteme ekle. Sonra "${action}" işlemini tekrar yap.`,
|
|
158
|
+
} });
|
|
159
|
+
continue;
|
|
268
160
|
}
|
|
269
161
|
}
|
|
270
|
-
//
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
if (
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
if (!finalServiceId)
|
|
280
|
-
throw new Error(`"${serviceNameRaw}" adında aktif hizmet bulunamadı. Lütfen getServices ile mevcut hizmetleri listeleyin.`);
|
|
162
|
+
// ── SERVICE AUTO-RESOLVE ──────────────────────────────────────────
|
|
163
|
+
const svcName = s('serviceName').trim();
|
|
164
|
+
let svcId = 0;
|
|
165
|
+
if (svcName) {
|
|
166
|
+
const r = await apiReq('GET', '/services', undefined, { branch_id: bId }, bId).catch(() => []);
|
|
167
|
+
if (Array.isArray(r) && r.length > 0) {
|
|
168
|
+
const m = r.find((x) => x.name.toLowerCase().includes(svcName.toLowerCase()));
|
|
169
|
+
if (m) {
|
|
170
|
+
svcId = m.id;
|
|
281
171
|
}
|
|
282
172
|
else {
|
|
283
|
-
|
|
173
|
+
returnData.push({ json: {
|
|
174
|
+
success: false,
|
|
175
|
+
action_required: 'getServices',
|
|
176
|
+
error: `"${svcName}" adında aktif hizmet bulunamadı.`,
|
|
177
|
+
instruction: 'action=getServices ile mevcut hizmetleri listele, doğru hizmet adıyla tekrar dene.',
|
|
178
|
+
available_services: r.map((x) => ({ id: x.id, name: x.name })),
|
|
179
|
+
} });
|
|
180
|
+
continue;
|
|
284
181
|
}
|
|
285
182
|
}
|
|
286
|
-
catch (e) {
|
|
287
|
-
throw e;
|
|
288
|
-
}
|
|
289
183
|
}
|
|
290
|
-
//
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
if (
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
const match = stRes.find((s) => `${s.first_name} ${s.last_name}`.toLowerCase().includes(fullName));
|
|
299
|
-
finalStaffId = match ? match.id : stRes[0].id;
|
|
300
|
-
}
|
|
184
|
+
// ── STAFF AUTO-RESOLVE ────────────────────────────────────────────
|
|
185
|
+
const staffName = s('staffName').trim();
|
|
186
|
+
let staffId = 0;
|
|
187
|
+
if (staffName) {
|
|
188
|
+
const r = await apiReq('GET', '/staff', undefined, { branch_id: bId }, bId).catch(() => []);
|
|
189
|
+
if (Array.isArray(r) && r.length > 0) {
|
|
190
|
+
const m = r.find((x) => `${x.first_name} ${x.last_name}`.toLowerCase().includes(staffName.toLowerCase()));
|
|
191
|
+
staffId = m ? m.id : r[0].id;
|
|
301
192
|
}
|
|
302
|
-
catch (e) { }
|
|
303
193
|
}
|
|
304
|
-
if (!
|
|
305
|
-
|
|
194
|
+
if (!staffId)
|
|
195
|
+
staffId = 1;
|
|
306
196
|
// ── ROUTE ─────────────────────────────────────────────────────────
|
|
307
197
|
switch (action) {
|
|
308
|
-
// --- CUSTOMERS ---
|
|
309
198
|
case 'getCustomers': {
|
|
310
|
-
const q =
|
|
311
|
-
|
|
199
|
+
const q = s('searchQuery') || custName;
|
|
200
|
+
resp = await apiReq('GET', '/customers', undefined, { ...(q ? { search: q } : {}), branch_id: bId }, bId);
|
|
312
201
|
break;
|
|
313
202
|
}
|
|
314
|
-
case 'getCustomerDetails':
|
|
315
|
-
|
|
203
|
+
case 'getCustomerDetails':
|
|
204
|
+
resp = await apiReq('GET', `/customers/${custId || n('referenceId')}`, undefined, { branch_id: bId }, bId);
|
|
316
205
|
break;
|
|
317
|
-
}
|
|
318
206
|
case 'createCustomer': {
|
|
319
|
-
const [
|
|
320
|
-
|
|
321
|
-
first_name: firstName,
|
|
322
|
-
last_name: rest.join(' '),
|
|
323
|
-
phone: extra.phone || '',
|
|
324
|
-
branch_id: resolvedBranchId,
|
|
325
|
-
});
|
|
207
|
+
const [fn, ...ln] = custName.split(' ');
|
|
208
|
+
resp = await apiReq('POST', '/customers', { first_name: fn, last_name: ln.join(' '), phone: extra.phone || '', branch_id: bId }, undefined, bId);
|
|
326
209
|
break;
|
|
327
210
|
}
|
|
328
|
-
case 'updateCustomer':
|
|
329
|
-
|
|
211
|
+
case 'updateCustomer':
|
|
212
|
+
resp = await apiReq('PUT', `/customers/${custId || n('referenceId')}`, { ...extra, branch_id: bId }, undefined, bId);
|
|
330
213
|
break;
|
|
331
|
-
}
|
|
332
|
-
// --- APPOINTMENTS ---
|
|
333
214
|
case 'getAppointments': {
|
|
334
|
-
const qs = { branch_id:
|
|
335
|
-
if (
|
|
336
|
-
qs.date =
|
|
337
|
-
|
|
215
|
+
const qs = { branch_id: bId };
|
|
216
|
+
if (s('date'))
|
|
217
|
+
qs.date = s('date');
|
|
218
|
+
resp = await apiReq('GET', '/appointments', undefined, qs, bId);
|
|
338
219
|
break;
|
|
339
220
|
}
|
|
340
|
-
case 'createAppointment':
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
service_id: finalServiceId,
|
|
347
|
-
scheduled_at: `${dateVal}T${timeVal}:00`,
|
|
348
|
-
notes: str('notes'),
|
|
349
|
-
source: 'ai_agent',
|
|
350
|
-
branch_id: resolvedBranchId,
|
|
351
|
-
}, undefined, resolvedBranchId);
|
|
221
|
+
case 'createAppointment':
|
|
222
|
+
resp = await apiReq('POST', '/appointments', {
|
|
223
|
+
customer_id: custId, staff_id: staffId, service_id: svcId,
|
|
224
|
+
scheduled_at: `${s('date')}T${s('time')}:00`,
|
|
225
|
+
notes: s('notes'), source: 'ai_agent', branch_id: bId,
|
|
226
|
+
}, undefined, bId);
|
|
352
227
|
break;
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
responseData = await apiRequest('PUT', `/appointments/${num('referenceId')}`, { status: str('newStatus'), branch_id: resolvedBranchId }, undefined, resolvedBranchId);
|
|
228
|
+
case 'updateAppointmentStatus':
|
|
229
|
+
resp = await apiReq('PUT', `/appointments/${n('referenceId')}`, { status: s('newStatus'), branch_id: bId }, undefined, bId);
|
|
356
230
|
break;
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
case 'getMessages': {
|
|
360
|
-
responseData = await apiRequest('GET', '/messages', undefined, { branch_id: resolvedBranchId }, resolvedBranchId);
|
|
231
|
+
case 'getMessages':
|
|
232
|
+
resp = await apiReq('GET', '/messages', undefined, { branch_id: bId }, bId);
|
|
361
233
|
break;
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
responseData = await apiRequest('POST', '/opportunities/send-bulk-message', {
|
|
365
|
-
customer_ids: [finalCustomerId],
|
|
366
|
-
message: str('messageContent'),
|
|
367
|
-
channel: 'whatsapp',
|
|
368
|
-
}, undefined, resolvedBranchId);
|
|
234
|
+
case 'sendMessage':
|
|
235
|
+
resp = await apiReq('POST', '/opportunities/send-bulk-message', { customer_ids: [custId], message: s('messageContent'), channel: 'whatsapp' }, undefined, bId);
|
|
369
236
|
break;
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
case 'getOpportunities': {
|
|
373
|
-
responseData = await apiRequest('GET', '/opportunities', undefined, { branch_id: resolvedBranchId }, resolvedBranchId);
|
|
237
|
+
case 'getOpportunities':
|
|
238
|
+
resp = await apiReq('GET', '/opportunities', undefined, { branch_id: bId }, bId);
|
|
374
239
|
break;
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
responseData = await apiRequest('PUT', `/opportunities/${num('referenceId')}`, { status: str('newStatus'), branch_id: resolvedBranchId }, undefined, resolvedBranchId);
|
|
240
|
+
case 'updateLeadStatus':
|
|
241
|
+
resp = await apiReq('PUT', `/opportunities/${n('referenceId')}`, { status: s('newStatus'), branch_id: bId }, undefined, bId);
|
|
378
242
|
break;
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
case 'getCurrentSession': {
|
|
382
|
-
responseData = await apiRequest('GET', '/pos/sessions/current', undefined, { branch_id: resolvedBranchId }, resolvedBranchId);
|
|
243
|
+
case 'getCurrentSession':
|
|
244
|
+
resp = await apiReq('GET', '/pos/sessions/current', undefined, { branch_id: bId }, bId);
|
|
383
245
|
break;
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
responseData = await apiRequest('POST', `/pos/sessions/${extra.sessionAction || 'open'}`, { branch_id: resolvedBranchId }, undefined, resolvedBranchId);
|
|
246
|
+
case 'toggleSession':
|
|
247
|
+
resp = await apiReq('POST', `/pos/sessions/${extra.sessionAction || 'open'}`, { branch_id: bId }, undefined, bId);
|
|
387
248
|
break;
|
|
388
|
-
}
|
|
389
249
|
case 'makeSale': {
|
|
390
|
-
|
|
391
|
-
let
|
|
392
|
-
const
|
|
393
|
-
if (!
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
customer_id: finalCustomerId,
|
|
397
|
-
items: items_arr,
|
|
398
|
-
payment_methods: p_methods,
|
|
399
|
-
discount_amount: num('discountAmount'),
|
|
400
|
-
branch_id: resolvedBranchId,
|
|
401
|
-
}, undefined, resolvedBranchId);
|
|
250
|
+
const items_arr = j('saleItemsJson', []);
|
|
251
|
+
let pm = j('paymentMethodsJson', []);
|
|
252
|
+
const net = n('netAmount');
|
|
253
|
+
if (!pm.length)
|
|
254
|
+
pm = [{ method: 'cash', amount: net }];
|
|
255
|
+
resp = await apiReq('POST', '/pos/sale', { customer_id: custId, items: items_arr, payment_methods: pm, discount_amount: n('discountAmount'), branch_id: bId }, undefined, bId);
|
|
402
256
|
break;
|
|
403
257
|
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
responseData = await apiRequest('GET', '/pos/receivables', undefined, { branch_id: resolvedBranchId }, resolvedBranchId);
|
|
258
|
+
case 'getReceivables':
|
|
259
|
+
resp = await apiReq('GET', '/pos/receivables', undefined, { branch_id: bId }, bId);
|
|
407
260
|
break;
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
responseData = await apiRequest('POST', `/pos/receivables/${num('referenceId')}/collect`, { branch_id: resolvedBranchId }, undefined, resolvedBranchId);
|
|
261
|
+
case 'collectReceivable':
|
|
262
|
+
resp = await apiReq('POST', `/pos/receivables/${n('referenceId')}/collect`, { branch_id: bId }, undefined, bId);
|
|
411
263
|
break;
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
case 'getDailySummary': {
|
|
415
|
-
responseData = await apiRequest('GET', '/payments/daily-summary', undefined, { branch_id: resolvedBranchId }, resolvedBranchId);
|
|
264
|
+
case 'getDailySummary':
|
|
265
|
+
resp = await apiReq('GET', '/payments/daily-summary', undefined, { branch_id: bId }, bId);
|
|
416
266
|
break;
|
|
417
|
-
}
|
|
418
267
|
case 'createPayment': {
|
|
419
|
-
let
|
|
420
|
-
const
|
|
421
|
-
if (!
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
customer_id: finalCustomerId,
|
|
425
|
-
net_amount: netAmt,
|
|
426
|
-
discount_amount: num('discountAmount'),
|
|
427
|
-
payment_methods: p_methods,
|
|
428
|
-
branch_id: resolvedBranchId,
|
|
429
|
-
...extra,
|
|
430
|
-
}, undefined, resolvedBranchId);
|
|
268
|
+
let pm = j('paymentMethodsJson', []);
|
|
269
|
+
const net = n('netAmount');
|
|
270
|
+
if (!pm.length)
|
|
271
|
+
pm = [{ method: 'cash', amount: net }];
|
|
272
|
+
resp = await apiReq('POST', '/payments', { customer_id: custId, net_amount: net, discount_amount: n('discountAmount'), payment_methods: pm, branch_id: bId, ...extra }, undefined, bId);
|
|
431
273
|
break;
|
|
432
274
|
}
|
|
433
|
-
case 'refundPayment':
|
|
434
|
-
|
|
275
|
+
case 'refundPayment':
|
|
276
|
+
resp = await apiReq('POST', `/payments/${n('referenceId')}/refund`, { branch_id: bId }, undefined, bId);
|
|
435
277
|
break;
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
case 'getCallbacks': {
|
|
439
|
-
responseData = await apiRequest('GET', '/callbacks', undefined, { branch_id: resolvedBranchId }, resolvedBranchId);
|
|
278
|
+
case 'getCallbacks':
|
|
279
|
+
resp = await apiReq('GET', '/callbacks', undefined, { branch_id: bId }, bId);
|
|
440
280
|
break;
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
responseData = await apiRequest('POST', '/callbacks', {
|
|
444
|
-
customer_id: finalCustomerId,
|
|
445
|
-
notes: str('notes'),
|
|
446
|
-
call_at: extra.callAt || '',
|
|
447
|
-
branch_id: resolvedBranchId,
|
|
448
|
-
}, undefined, resolvedBranchId);
|
|
281
|
+
case 'createCallback':
|
|
282
|
+
resp = await apiReq('POST', '/callbacks', { customer_id: custId, notes: s('notes'), call_at: extra.callAt || '', branch_id: bId }, undefined, bId);
|
|
449
283
|
break;
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
responseData = await apiRequest('POST', `/callbacks/${num('referenceId')}/mark-called`, { branch_id: resolvedBranchId }, undefined, resolvedBranchId);
|
|
284
|
+
case 'markAsCalled':
|
|
285
|
+
resp = await apiReq('POST', `/callbacks/${n('referenceId')}/mark-called`, { branch_id: bId }, undefined, bId);
|
|
453
286
|
break;
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
case 'getAutomationRules': {
|
|
457
|
-
responseData = await apiRequest('GET', '/automations', undefined, { branch_id: resolvedBranchId }, resolvedBranchId);
|
|
287
|
+
case 'getAutomationRules':
|
|
288
|
+
resp = await apiReq('GET', '/automations', undefined, { branch_id: bId }, bId);
|
|
458
289
|
break;
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
responseData = await apiRequest('POST', `/automations/${num('referenceId')}/toggle`, { branch_id: resolvedBranchId }, undefined, resolvedBranchId);
|
|
290
|
+
case 'toggleAutomation':
|
|
291
|
+
resp = await apiReq('POST', `/automations/${n('referenceId')}/toggle`, { branch_id: bId }, undefined, bId);
|
|
462
292
|
break;
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
responseData = await apiRequest('POST', '/automations/run-now', { rule_id: num('referenceId'), branch_id: resolvedBranchId }, undefined, resolvedBranchId);
|
|
293
|
+
case 'runAutomation':
|
|
294
|
+
resp = await apiReq('POST', '/automations/run-now', { rule_id: n('referenceId'), branch_id: bId }, undefined, bId);
|
|
466
295
|
break;
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
case 'getDashboardSummary': {
|
|
470
|
-
responseData = await apiRequest('GET', '/dashboard/summary', undefined, { branch_id: resolvedBranchId }, resolvedBranchId);
|
|
296
|
+
case 'getDashboardSummary':
|
|
297
|
+
resp = await apiReq('GET', '/dashboard/summary', undefined, { branch_id: bId }, bId);
|
|
471
298
|
break;
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
responseData = await apiRequest('GET', '/dashboard/nav-badges', undefined, { branch_id: resolvedBranchId }, resolvedBranchId);
|
|
299
|
+
case 'getNavBadges':
|
|
300
|
+
resp = await apiReq('GET', '/dashboard/nav-badges', undefined, { branch_id: bId }, bId);
|
|
475
301
|
break;
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
case 'getServices': {
|
|
479
|
-
responseData = await apiRequest('GET', '/services', undefined, { branch_id: resolvedBranchId }, resolvedBranchId);
|
|
302
|
+
case 'getServices':
|
|
303
|
+
resp = await apiReq('GET', '/services', undefined, { branch_id: bId }, bId);
|
|
480
304
|
break;
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
responseData = await apiRequest('GET', '/staff', undefined, { branch_id: resolvedBranchId }, resolvedBranchId);
|
|
305
|
+
case 'getStaff':
|
|
306
|
+
resp = await apiReq('GET', '/staff', undefined, { branch_id: bId }, bId);
|
|
484
307
|
break;
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
responseData = await apiRequest('GET', '/branches', undefined, { branch_id: resolvedBranchId }, resolvedBranchId);
|
|
308
|
+
case 'getBranches':
|
|
309
|
+
resp = await apiReq('GET', '/branches', undefined, { branch_id: bId }, bId);
|
|
488
310
|
break;
|
|
489
|
-
}
|
|
490
311
|
case 'checkStock': {
|
|
491
|
-
const q =
|
|
492
|
-
|
|
312
|
+
const q = s('searchQuery');
|
|
313
|
+
resp = await apiReq('GET', '/products', undefined, { ...(q ? { search: q } : {}), branch_id: bId }, bId);
|
|
493
314
|
break;
|
|
494
315
|
}
|
|
495
|
-
// --- PRE-CONSULTATIONS ---
|
|
496
316
|
case 'getPreConsultations': {
|
|
497
|
-
const q =
|
|
498
|
-
|
|
317
|
+
const q = s('searchQuery') || custName;
|
|
318
|
+
resp = await apiReq('GET', '/pre-consultations', undefined, { ...(q ? { search: q } : {}), branch_id: bId }, bId);
|
|
499
319
|
break;
|
|
500
320
|
}
|
|
501
|
-
case 'createPreConsultation':
|
|
502
|
-
|
|
503
|
-
caller_name: customerNameRaw,
|
|
504
|
-
phone: extra.phone || '',
|
|
505
|
-
notes: str('notes'),
|
|
506
|
-
branch_id: resolvedBranchId,
|
|
507
|
-
}, undefined, resolvedBranchId);
|
|
321
|
+
case 'createPreConsultation':
|
|
322
|
+
resp = await apiReq('POST', '/pre-consultations', { caller_name: custName, phone: extra.phone || '', notes: s('notes'), branch_id: bId }, undefined, bId);
|
|
508
323
|
break;
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
responseData = await apiRequest('PUT', `/pre-consultations/${num('referenceId')}`, {
|
|
512
|
-
status: str('newStatus'),
|
|
513
|
-
notes: str('notes'),
|
|
514
|
-
branch_id: resolvedBranchId,
|
|
515
|
-
...extra,
|
|
516
|
-
}, undefined, resolvedBranchId);
|
|
324
|
+
case 'updatePreConsultation':
|
|
325
|
+
resp = await apiReq('PUT', `/pre-consultations/${n('referenceId')}`, { status: s('newStatus'), notes: s('notes'), branch_id: bId, ...extra }, undefined, bId);
|
|
517
326
|
break;
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
responseData = { success: false, error: `Desteklenmeyen işlem: "${action}". Geçerli action değerlerini kontrol edin.` };
|
|
521
|
-
}
|
|
327
|
+
default:
|
|
328
|
+
resp = { success: false, error: `Desteklenmeyen işlem: "${action}".` };
|
|
522
329
|
}
|
|
523
|
-
returnData.push({ json:
|
|
330
|
+
returnData.push({ json: resp });
|
|
524
331
|
}
|
|
525
332
|
catch (error) {
|
|
526
333
|
if (this.continueOnFail()) {
|