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
- name: 'staffName',
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
- // ─── EXTRA ───────────────────────────────────────────────────────────
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: 'Ek Parametreler (Extra JSON)',
153
- name: 'extraJson',
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
- // Helper: API request branchId injected per-call via qs/body
171
- const apiRequest = async (method, path, body, qs, branchId) => {
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(branchId || globalBranchId),
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 > 0)
183
- options.qs = qs;
74
+ if (qs && Object.keys(qs).length)
75
+ opts.qs = qs;
184
76
  if (body && ['POST', 'PUT', 'PATCH', 'DELETE'].includes(method))
185
- options.body = body;
186
- return this.helpers.request(options);
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
- const nodeBranchIdRaw = this.getNodeParameter('branchId', i, 0);
191
- // Use credentials value when node field is 0 or empty
192
- const resolvedBranchId = nodeBranchIdRaw && nodeBranchIdRaw > 0 ? nodeBranchIdRaw : globalBranchId;
193
- // Param helpers
194
- const str = (key) => this.getNodeParameter(key, i, '') || '';
195
- const num = (key) => this.getNodeParameter(key, i, 0) || 0;
196
- const parseJsonParam = (key, fallback) => {
197
- const raw = str(key);
198
- if (!raw || raw === '{}' || raw === '[]')
199
- return fallback;
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(raw);
134
+ return JSON.parse(r);
202
135
  }
203
136
  catch {
204
- return fallback;
137
+ return fb;
205
138
  }
206
139
  };
207
- // Override with extraJson keys
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 responseData;
248
- // ── AUTO-RESOLVE: names → IDs ─────────────────────────────────────
249
- const branchQs = { branch_id: resolvedBranchId };
250
- // Customer auto-resolve
251
- let finalCustomerId = 0;
252
- const customerNameRaw = str('customerName').trim();
253
- if (customerNameRaw) {
254
- try {
255
- const cRes = await apiRequest('GET', '/customers', undefined, { search: customerNameRaw, branch_id: resolvedBranchId }, resolvedBranchId);
256
- if (Array.isArray(cRes) && cRes.length > 0) {
257
- finalCustomerId = cRes[0].id;
258
- }
259
- else {
260
- // Customer not found — only throw for write operations, not for getCustomers itself
261
- if (!['getCustomers', 'getPreConsultations', 'createCustomer'].includes(action)) {
262
- throw new Error(`"${customerNameRaw}" adında müşteri bulunamadı. Lütfen önce createCustomer ile müşteriyi ekleyin, ardından tekrar deneyin.`);
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
- // Service auto-resolve — branch_id zorunlu (WHERE s.branch_id = ?)
271
- let finalServiceId = 0;
272
- const serviceNameRaw = str('serviceName').trim();
273
- if (serviceNameRaw) {
274
- try {
275
- const sRes = await apiRequest('GET', '/services', undefined, { branch_id: resolvedBranchId }, resolvedBranchId);
276
- if (Array.isArray(sRes) && sRes.length > 0) {
277
- const match = sRes.find((s) => s.name.toLowerCase().includes(serviceNameRaw.toLowerCase()));
278
- finalServiceId = match ? match.id : 0;
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
- throw new Error('Hizmet listesi boş döndü. Şube ID doğru mu?');
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
- // Staff auto-resolve
291
- let finalStaffId = 0;
292
- const staffNameRaw = str('staffName').trim();
293
- if (staffNameRaw) {
294
- try {
295
- const stRes = await apiRequest('GET', '/staff', undefined, { branch_id: resolvedBranchId }, resolvedBranchId);
296
- if (Array.isArray(stRes) && stRes.length > 0) {
297
- const fullName = staffNameRaw.toLowerCase();
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 (!finalStaffId)
305
- finalStaffId = 1; // fallback to first staff
194
+ if (!staffId)
195
+ staffId = 1;
306
196
  // ── ROUTE ─────────────────────────────────────────────────────────
307
197
  switch (action) {
308
- // --- CUSTOMERS ---
309
198
  case 'getCustomers': {
310
- const q = str('searchQuery') || customerNameRaw;
311
- responseData = await apiRequest('GET', '/customers', undefined, { ...(q ? { search: q } : {}), branch_id: resolvedBranchId }, resolvedBranchId);
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
- responseData = await apiRequest('GET', `/customers/${finalCustomerId || num('referenceId')}`, undefined, { branch_id: resolvedBranchId }, resolvedBranchId);
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 [firstName, ...rest] = customerNameRaw.split(' ');
320
- responseData = await apiRequest('POST', '/customers', {
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
- responseData = await apiRequest('PUT', `/customers/${finalCustomerId || num('referenceId')}`, { ...extra, branch_id: resolvedBranchId }, undefined, resolvedBranchId);
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: resolvedBranchId };
335
- if (str('date'))
336
- qs.date = str('date');
337
- responseData = await apiRequest('GET', '/appointments', undefined, qs, resolvedBranchId);
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
- const dateVal = str('date') || extra.date || '';
342
- const timeVal = str('time') || extra.time || '';
343
- responseData = await apiRequest('POST', '/appointments', {
344
- customer_id: finalCustomerId,
345
- staff_id: finalStaffId,
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
- case 'updateAppointmentStatus': {
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
- // --- MESSAGING ---
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
- case 'sendMessage': {
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
- // --- OPPORTUNITIES ---
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
- case 'updateLeadStatus': {
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
- // --- POS / SESSION ---
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
- case 'toggleSession': {
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
- let items_arr = parseJsonParam('saleItemsJson', []);
391
- let p_methods = parseJsonParam('paymentMethodsJson', []);
392
- const netAmt = num('netAmount') || extra.netAmount || 0;
393
- if (!p_methods.length)
394
- p_methods = [{ method: 'cash', amount: netAmt }];
395
- responseData = await apiRequest('POST', '/pos/sale', {
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
- // --- RECEIVABLES ---
405
- case 'getReceivables': {
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
- case 'collectReceivable': {
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
- // --- PAYMENTS ---
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 p_methods = parseJsonParam('paymentMethodsJson', []);
420
- const netAmt = num('netAmount');
421
- if (!p_methods.length)
422
- p_methods = [{ method: 'cash', amount: netAmt }];
423
- responseData = await apiRequest('POST', '/payments', {
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
- responseData = await apiRequest('POST', `/payments/${num('referenceId')}/refund`, { branch_id: resolvedBranchId }, undefined, resolvedBranchId);
275
+ case 'refundPayment':
276
+ resp = await apiReq('POST', `/payments/${n('referenceId')}/refund`, { branch_id: bId }, undefined, bId);
435
277
  break;
436
- }
437
- // --- CALLBACKS ---
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
- case 'createCallback': {
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
- case 'markAsCalled': {
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
- // --- AUTOMATIONS ---
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
- case 'toggleAutomation': {
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
- case 'runAutomation': {
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
- // --- DASHBOARD ---
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
- case 'getNavBadges': {
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
- // --- CATALOG ---
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
- case 'getStaff': {
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
- case 'getBranches': {
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 = str('searchQuery');
492
- responseData = await apiRequest('GET', '/products', undefined, { ...(q ? { search: q } : {}), branch_id: resolvedBranchId }, resolvedBranchId);
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 = str('searchQuery') || customerNameRaw;
498
- responseData = await apiRequest('GET', '/pre-consultations', undefined, { ...(q ? { search: q } : {}), branch_id: resolvedBranchId }, resolvedBranchId);
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
- responseData = await apiRequest('POST', '/pre-consultations', {
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
- case 'updatePreConsultation': {
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
- default: {
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: responseData });
330
+ returnData.push({ json: resp });
524
331
  }
525
332
  catch (error) {
526
333
  if (this.continueOnFail()) {