n8n-nodes-aivence-realty 0.1.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/README.md ADDED
@@ -0,0 +1,127 @@
1
+ # n8n-nodes-aivence-realty
2
+
3
+ **Nodo n8n para AivenceRealty CRM (Laravel).**
4
+
5
+ Este paquete añade nodos para interactuar con el CRM inmobiliario AivenceRealty. Permite operaciones completas sobre Properties, Leads, Maintenance Orders, Contractors, y más.
6
+
7
+ ## 📦 Instalación
8
+
9
+ ```bash
10
+ npm install n8n-nodes-aivence-realty
11
+ ```
12
+
13
+ O desde la interfaz de n8n: **Community Nodes → Install** y escribir `n8n-nodes-aivence-realty`.
14
+
15
+ ## 🔐 Configuración
16
+
17
+ 1. Obtén un **Bearer Token** desde tu panel de AivenceRealty
18
+ 2. En n8n, ve a **Credentials** y crea credenciales de tipo **AivenceRealty API**
19
+ 3. Introduce:
20
+ - **URL Base**: `https://realty.aivence.com` (o tu dominio)
21
+ - **Bearer Token**: Tu token de API
22
+ 4. Guarda las credenciales
23
+
24
+ ## 📋 Recursos Disponibles
25
+
26
+ ### 1. **Property** (Propiedades)
27
+ - `list` - Listar todas las propiedades
28
+ - `getSimilar` - Obtener propiedades similares
29
+ - `getStatistics` - Estadísticas de propiedades
30
+
31
+ ### 2. **Lead** (Leads)
32
+ - `list` - Listar leads
33
+ - `create` - Crear nuevo lead (campos: nombre, telefono, mensaje)
34
+
35
+ ### 3. **Tenant** (Inquilinos)
36
+ - `list` - Listar inquilinos
37
+
38
+ ### 4. **Maintenance Order** (Órdenes de Mantenimiento)
39
+ - `checkDuplicate` - Verificar si existe orden duplicada
40
+ - `create` - Crear nueva orden
41
+ - `get` - Obtener orden por ID
42
+ - `update` - Actualizar orden
43
+ - `list` - Listar órdenes
44
+
45
+ ### 5. **Contractor** (Contratistas)
46
+ - `findByCategory` - Buscar por categoría (fontanería, electricidad, etc.)
47
+ - `findAlternatives` - Buscar contratistas alternativos
48
+
49
+ ### 6. **Monthly Cost** (Costos Mensuales)
50
+ - `track` - Registrar costo mensual
51
+
52
+ ### 7. **WhatsApp**
53
+ - `sendMessage` - Enviar mensaje por WhatsApp (Evolution API)
54
+
55
+ ## 🎯 Ejemplos de Uso
56
+
57
+ ### Crear un Lead
58
+
59
+ ```json
60
+ {
61
+ "resource": "lead",
62
+ "operation": "create",
63
+ "nombre": "Juan Pérez",
64
+ "telefono": "5491112345678",
65
+ "mensaje": "Busco departamento 2 ambientes en Palermo"
66
+ }
67
+ ```
68
+
69
+ ### Crear Orden de Mantenimiento
70
+
71
+ ```json
72
+ {
73
+ "resource": "maintenanceOrder",
74
+ "operation": "create",
75
+ "clienteId": 123,
76
+ "propiedadId": 456,
77
+ "contratistaId": 789,
78
+ "categoria": "fontanería",
79
+ "urgencia": "alta",
80
+ "descripcionCorta": "Fuga de agua",
81
+ "descripcionDetallada": "Fuga en baño principal..."
82
+ }
83
+ ```
84
+
85
+ ## 🔧 Desarrollo Local
86
+
87
+ ```bash
88
+ # Clonar repositorio
89
+ git clone https://github.com/aivence/n8n-nodes-aivence-realty.git
90
+ cd n8n-nodes-aivence-realty
91
+
92
+ # Instalar dependencias
93
+ npm install
94
+
95
+ # Compilar
96
+ npm run build
97
+
98
+ # Vincular localmente
99
+ npm link
100
+ cd ~/.n8n
101
+ npm link n8n-nodes-aivence-realty
102
+
103
+ # Iniciar n8n
104
+ n8n start
105
+ ```
106
+
107
+ ## 📝 Changelog
108
+
109
+ ### v0.1.0 (2025-10-09)
110
+ - 🎉 Versión inicial
111
+ - ✅ 7 recursos implementados
112
+ - ✅ 15 operaciones totales
113
+ - ✅ Integración completa con API Laravel
114
+
115
+ ## 📄 Licencia
116
+
117
+ MIT
118
+
119
+ ## 👨‍💻 Autor
120
+
121
+ **Federico Aivence** - [federico@aivence.com](mailto:federico@aivence.com)
122
+
123
+ ## 🔗 Links
124
+
125
+ - [AivenceRealty CRM](https://realty.aivence.com)
126
+ - [Repositorio GitHub](https://github.com/aivence/n8n-nodes-aivence-realty)
127
+ - [Reportar Issues](https://github.com/aivence/n8n-nodes-aivence-realty/issues)
@@ -0,0 +1,9 @@
1
+ import { IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties } from 'n8n-workflow';
2
+ export declare class AivenceRealtyApi implements ICredentialType {
3
+ name: string;
4
+ displayName: string;
5
+ documentationUrl: string;
6
+ properties: INodeProperties[];
7
+ authenticate: IAuthenticateGeneric;
8
+ test: ICredentialTestRequest;
9
+ }
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AivenceRealtyApi = void 0;
4
+ class AivenceRealtyApi {
5
+ constructor() {
6
+ this.name = 'aivenceRealtyApi';
7
+ this.displayName = 'AivenceRealty API';
8
+ this.documentationUrl = 'https://realty.aivence.com/api-docs';
9
+ this.properties = [
10
+ {
11
+ displayName: 'URL Base',
12
+ name: 'baseUrl',
13
+ type: 'string',
14
+ default: 'https://realty.aivence.com',
15
+ description: 'URL base de la API de AivenceRealty',
16
+ required: true,
17
+ },
18
+ {
19
+ displayName: 'Bearer Token',
20
+ name: 'apiKey',
21
+ type: 'string',
22
+ typeOptions: { password: true },
23
+ default: '',
24
+ description: 'Token de autorización Bearer para la API',
25
+ required: true,
26
+ },
27
+ ];
28
+ this.authenticate = {
29
+ type: 'generic',
30
+ properties: {
31
+ headers: {
32
+ 'Authorization': '=Bearer {{$credentials.apiKey}}',
33
+ 'Accept': 'application/json',
34
+ 'Content-Type': 'application/json',
35
+ },
36
+ },
37
+ };
38
+ this.test = {
39
+ request: {
40
+ baseURL: '={{$credentials.baseUrl}}',
41
+ url: '/api/health',
42
+ },
43
+ };
44
+ }
45
+ }
46
+ exports.AivenceRealtyApi = AivenceRealtyApi;
@@ -0,0 +1,5 @@
1
+ import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
2
+ export declare class AivenceRealty implements INodeType {
3
+ description: INodeTypeDescription;
4
+ execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
5
+ }
@@ -0,0 +1,777 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AivenceRealty = void 0;
4
+ class AivenceRealty {
5
+ constructor() {
6
+ this.description = {
7
+ displayName: 'AivenceRealty CRM',
8
+ name: 'aivenceRealty',
9
+ icon: 'file:aivencerealty.png',
10
+ group: ['transform'],
11
+ version: 1,
12
+ subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
13
+ description: 'Integración con AivenceRealty CRM Inmobiliario',
14
+ defaults: {
15
+ name: 'AivenceRealty CRM',
16
+ },
17
+ inputs: ['main'],
18
+ outputs: ['main'],
19
+ credentials: [
20
+ {
21
+ name: 'aivenceRealtyApi',
22
+ required: true,
23
+ },
24
+ ],
25
+ properties: [
26
+ // ============================================
27
+ // RESOURCE SELECTOR
28
+ // ============================================
29
+ {
30
+ displayName: 'Recurso',
31
+ name: 'resource',
32
+ type: 'options',
33
+ noDataExpression: true,
34
+ options: [
35
+ {
36
+ name: 'Property',
37
+ value: 'property',
38
+ },
39
+ {
40
+ name: 'Lead',
41
+ value: 'lead',
42
+ },
43
+ {
44
+ name: 'Tenant',
45
+ value: 'tenant',
46
+ },
47
+ {
48
+ name: 'Maintenance Order',
49
+ value: 'maintenanceOrder',
50
+ },
51
+ {
52
+ name: 'Contractor',
53
+ value: 'contractor',
54
+ },
55
+ {
56
+ name: 'Monthly Cost',
57
+ value: 'monthlyCost',
58
+ },
59
+ {
60
+ name: 'WhatsApp',
61
+ value: 'whatsapp',
62
+ },
63
+ ],
64
+ default: 'property',
65
+ description: 'Recurso del CRM a operar',
66
+ },
67
+ // ============================================
68
+ // PROPERTY OPERATIONS
69
+ // ============================================
70
+ {
71
+ displayName: 'Operación',
72
+ name: 'operation',
73
+ type: 'options',
74
+ noDataExpression: true,
75
+ displayOptions: {
76
+ show: {
77
+ resource: ['property'],
78
+ },
79
+ },
80
+ options: [
81
+ {
82
+ name: 'List',
83
+ value: 'list',
84
+ description: 'Listar todas las propiedades',
85
+ action: 'Listar propiedades',
86
+ },
87
+ {
88
+ name: 'Get Similar',
89
+ value: 'getSimilar',
90
+ description: 'Obtener propiedades similares',
91
+ action: 'Obtener propiedades similares',
92
+ },
93
+ {
94
+ name: 'Get Statistics',
95
+ value: 'getStatistics',
96
+ description: 'Obtener estadísticas de propiedades',
97
+ action: 'Obtener estadísticas',
98
+ },
99
+ ],
100
+ default: 'list',
101
+ },
102
+ // ============================================
103
+ // LEAD OPERATIONS
104
+ // ============================================
105
+ {
106
+ displayName: 'Operación',
107
+ name: 'operation',
108
+ type: 'options',
109
+ noDataExpression: true,
110
+ displayOptions: {
111
+ show: {
112
+ resource: ['lead'],
113
+ },
114
+ },
115
+ options: [
116
+ {
117
+ name: 'List',
118
+ value: 'list',
119
+ description: 'Listar todos los leads',
120
+ action: 'Listar leads',
121
+ },
122
+ {
123
+ name: 'Create',
124
+ value: 'create',
125
+ description: 'Crear un nuevo lead',
126
+ action: 'Crear lead',
127
+ },
128
+ ],
129
+ default: 'list',
130
+ },
131
+ // Lead Create Fields
132
+ {
133
+ displayName: 'Nombre',
134
+ name: 'nombre',
135
+ type: 'string',
136
+ required: true,
137
+ displayOptions: {
138
+ show: {
139
+ resource: ['lead'],
140
+ operation: ['create'],
141
+ },
142
+ },
143
+ default: '',
144
+ description: 'Nombre completo del lead',
145
+ },
146
+ {
147
+ displayName: 'Teléfono',
148
+ name: 'telefono',
149
+ type: 'string',
150
+ required: true,
151
+ displayOptions: {
152
+ show: {
153
+ resource: ['lead'],
154
+ operation: ['create'],
155
+ },
156
+ },
157
+ default: '',
158
+ description: 'Número de teléfono del lead',
159
+ },
160
+ {
161
+ displayName: 'Mensaje',
162
+ name: 'mensaje',
163
+ type: 'string',
164
+ typeOptions: {
165
+ rows: 4,
166
+ },
167
+ required: true,
168
+ displayOptions: {
169
+ show: {
170
+ resource: ['lead'],
171
+ operation: ['create'],
172
+ },
173
+ },
174
+ default: '',
175
+ description: 'Mensaje o consulta del lead',
176
+ },
177
+ // ============================================
178
+ // TENANT OPERATIONS
179
+ // ============================================
180
+ {
181
+ displayName: 'Operación',
182
+ name: 'operation',
183
+ type: 'options',
184
+ noDataExpression: true,
185
+ displayOptions: {
186
+ show: {
187
+ resource: ['tenant'],
188
+ },
189
+ },
190
+ options: [
191
+ {
192
+ name: 'List',
193
+ value: 'list',
194
+ description: 'Listar todos los inquilinos',
195
+ action: 'Listar inquilinos',
196
+ },
197
+ ],
198
+ default: 'list',
199
+ },
200
+ // ============================================
201
+ // MAINTENANCE ORDER OPERATIONS
202
+ // ============================================
203
+ {
204
+ displayName: 'Operación',
205
+ name: 'operation',
206
+ type: 'options',
207
+ noDataExpression: true,
208
+ displayOptions: {
209
+ show: {
210
+ resource: ['maintenanceOrder'],
211
+ },
212
+ },
213
+ options: [
214
+ {
215
+ name: 'Check Duplicate',
216
+ value: 'checkDuplicate',
217
+ description: 'Verificar si existe orden duplicada',
218
+ action: 'Verificar duplicado',
219
+ },
220
+ {
221
+ name: 'Create',
222
+ value: 'create',
223
+ description: 'Crear nueva orden de trabajo',
224
+ action: 'Crear orden',
225
+ },
226
+ {
227
+ name: 'Get',
228
+ value: 'get',
229
+ description: 'Obtener orden por ID',
230
+ action: 'Obtener orden',
231
+ },
232
+ {
233
+ name: 'Update',
234
+ value: 'update',
235
+ description: 'Actualizar orden existente',
236
+ action: 'Actualizar orden',
237
+ },
238
+ {
239
+ name: 'List',
240
+ value: 'list',
241
+ description: 'Listar órdenes de trabajo',
242
+ action: 'Listar órdenes',
243
+ },
244
+ ],
245
+ default: 'list',
246
+ },
247
+ // Maintenance Order - Check Duplicate Fields
248
+ {
249
+ displayName: 'Property ID',
250
+ name: 'propiedadId',
251
+ type: 'number',
252
+ required: true,
253
+ displayOptions: {
254
+ show: {
255
+ resource: ['maintenanceOrder'],
256
+ operation: ['checkDuplicate'],
257
+ },
258
+ },
259
+ default: 0,
260
+ description: 'ID de la propiedad',
261
+ },
262
+ // Maintenance Order - Get/Update ID Field
263
+ {
264
+ displayName: 'Order ID',
265
+ name: 'orderId',
266
+ type: 'number',
267
+ required: true,
268
+ displayOptions: {
269
+ show: {
270
+ resource: ['maintenanceOrder'],
271
+ operation: ['get', 'update'],
272
+ },
273
+ },
274
+ default: 0,
275
+ description: 'ID de la orden de trabajo',
276
+ },
277
+ // Maintenance Order - Create Fields
278
+ {
279
+ displayName: 'Cliente ID',
280
+ name: 'clienteId',
281
+ type: 'number',
282
+ required: true,
283
+ displayOptions: {
284
+ show: {
285
+ resource: ['maintenanceOrder'],
286
+ operation: ['create'],
287
+ },
288
+ },
289
+ default: 0,
290
+ },
291
+ {
292
+ displayName: 'Propiedad ID',
293
+ name: 'propiedadId',
294
+ type: 'number',
295
+ required: true,
296
+ displayOptions: {
297
+ show: {
298
+ resource: ['maintenanceOrder'],
299
+ operation: ['create'],
300
+ },
301
+ },
302
+ default: 0,
303
+ },
304
+ {
305
+ displayName: 'Contratista ID',
306
+ name: 'contratistaId',
307
+ type: 'number',
308
+ required: true,
309
+ displayOptions: {
310
+ show: {
311
+ resource: ['maintenanceOrder'],
312
+ operation: ['create'],
313
+ },
314
+ },
315
+ default: 0,
316
+ },
317
+ {
318
+ displayName: 'Categoría',
319
+ name: 'categoria',
320
+ type: 'options',
321
+ required: true,
322
+ displayOptions: {
323
+ show: {
324
+ resource: ['maintenanceOrder'],
325
+ operation: ['create'],
326
+ },
327
+ },
328
+ options: [
329
+ { name: 'Fontanería', value: 'fontanería' },
330
+ { name: 'Electricidad', value: 'electricidad' },
331
+ { name: 'Pintura', value: 'pintura' },
332
+ { name: 'Cerrajería', value: 'cerrajería' },
333
+ { name: 'Carpintería', value: 'carpintería' },
334
+ { name: 'Aires Acondicionados', value: 'aires_acondicionados' },
335
+ { name: 'Otro', value: 'otro' },
336
+ ],
337
+ default: 'fontanería',
338
+ },
339
+ {
340
+ displayName: 'Urgencia',
341
+ name: 'urgencia',
342
+ type: 'options',
343
+ required: true,
344
+ displayOptions: {
345
+ show: {
346
+ resource: ['maintenanceOrder'],
347
+ operation: ['create'],
348
+ },
349
+ },
350
+ options: [
351
+ { name: 'Baja', value: 'baja' },
352
+ { name: 'Media', value: 'media' },
353
+ { name: 'Alta', value: 'alta' },
354
+ ],
355
+ default: 'media',
356
+ },
357
+ {
358
+ displayName: 'Descripción Corta',
359
+ name: 'descripcionCorta',
360
+ type: 'string',
361
+ required: true,
362
+ displayOptions: {
363
+ show: {
364
+ resource: ['maintenanceOrder'],
365
+ operation: ['create'],
366
+ },
367
+ },
368
+ default: '',
369
+ },
370
+ {
371
+ displayName: 'Descripción Detallada',
372
+ name: 'descripcionDetallada',
373
+ type: 'string',
374
+ typeOptions: { rows: 4 },
375
+ required: true,
376
+ displayOptions: {
377
+ show: {
378
+ resource: ['maintenanceOrder'],
379
+ operation: ['create'],
380
+ },
381
+ },
382
+ default: '',
383
+ },
384
+ // ============================================
385
+ // CONTRACTOR OPERATIONS
386
+ // ============================================
387
+ {
388
+ displayName: 'Operación',
389
+ name: 'operation',
390
+ type: 'options',
391
+ noDataExpression: true,
392
+ displayOptions: {
393
+ show: {
394
+ resource: ['contractor'],
395
+ },
396
+ },
397
+ options: [
398
+ {
399
+ name: 'Find By Category',
400
+ value: 'findByCategory',
401
+ description: 'Buscar contratista por categoría',
402
+ action: 'Buscar por categoría',
403
+ },
404
+ {
405
+ name: 'Find Alternatives',
406
+ value: 'findAlternatives',
407
+ description: 'Buscar contratistas alternativos',
408
+ action: 'Buscar alternativos',
409
+ },
410
+ ],
411
+ default: 'findByCategory',
412
+ },
413
+ // Contractor - Find By Category
414
+ {
415
+ displayName: 'Categoría',
416
+ name: 'categoria',
417
+ type: 'options',
418
+ required: true,
419
+ displayOptions: {
420
+ show: {
421
+ resource: ['contractor'],
422
+ operation: ['findByCategory'],
423
+ },
424
+ },
425
+ options: [
426
+ { name: 'Fontanería', value: 'fontanería' },
427
+ { name: 'Electricidad', value: 'electricidad' },
428
+ { name: 'Pintura', value: 'pintura' },
429
+ { name: 'Cerrajería', value: 'cerrajería' },
430
+ { name: 'Carpintería', value: 'carpintería' },
431
+ { name: 'Aires Acondicionados', value: 'aires_acondicionados' },
432
+ ],
433
+ default: 'fontanería',
434
+ },
435
+ // ============================================
436
+ // MONTHLY COST OPERATIONS
437
+ // ============================================
438
+ {
439
+ displayName: 'Operación',
440
+ name: 'operation',
441
+ type: 'options',
442
+ noDataExpression: true,
443
+ displayOptions: {
444
+ show: {
445
+ resource: ['monthlyCost'],
446
+ },
447
+ },
448
+ options: [
449
+ {
450
+ name: 'Track',
451
+ value: 'track',
452
+ description: 'Registrar costo mensual',
453
+ action: 'Registrar costo',
454
+ },
455
+ ],
456
+ default: 'track',
457
+ },
458
+ // Monthly Cost - Track Fields
459
+ {
460
+ displayName: 'Mes',
461
+ name: 'mes',
462
+ type: 'number',
463
+ required: true,
464
+ displayOptions: {
465
+ show: {
466
+ resource: ['monthlyCost'],
467
+ operation: ['track'],
468
+ },
469
+ },
470
+ default: new Date().getMonth() + 1,
471
+ description: 'Mes (1-12)',
472
+ },
473
+ {
474
+ displayName: 'Año',
475
+ name: 'anio',
476
+ type: 'number',
477
+ required: true,
478
+ displayOptions: {
479
+ show: {
480
+ resource: ['monthlyCost'],
481
+ operation: ['track'],
482
+ },
483
+ },
484
+ default: new Date().getFullYear(),
485
+ },
486
+ {
487
+ displayName: 'Categoría',
488
+ name: 'categoria',
489
+ type: 'string',
490
+ required: true,
491
+ displayOptions: {
492
+ show: {
493
+ resource: ['monthlyCost'],
494
+ operation: ['track'],
495
+ },
496
+ },
497
+ default: '',
498
+ },
499
+ {
500
+ displayName: 'Total Gastado',
501
+ name: 'totalGastado',
502
+ type: 'number',
503
+ required: true,
504
+ displayOptions: {
505
+ show: {
506
+ resource: ['monthlyCost'],
507
+ operation: ['track'],
508
+ },
509
+ },
510
+ default: 0,
511
+ },
512
+ // ============================================
513
+ // WHATSAPP OPERATIONS
514
+ // ============================================
515
+ {
516
+ displayName: 'Operación',
517
+ name: 'operation',
518
+ type: 'options',
519
+ noDataExpression: true,
520
+ displayOptions: {
521
+ show: {
522
+ resource: ['whatsapp'],
523
+ },
524
+ },
525
+ options: [
526
+ {
527
+ name: 'Send Message',
528
+ value: 'sendMessage',
529
+ description: 'Enviar mensaje por WhatsApp',
530
+ action: 'Enviar mensaje',
531
+ },
532
+ ],
533
+ default: 'sendMessage',
534
+ },
535
+ // WhatsApp - Send Message Fields
536
+ {
537
+ displayName: 'Número',
538
+ name: 'numero',
539
+ type: 'string',
540
+ required: true,
541
+ displayOptions: {
542
+ show: {
543
+ resource: ['whatsapp'],
544
+ operation: ['sendMessage'],
545
+ },
546
+ },
547
+ default: '',
548
+ placeholder: '5491112345678',
549
+ description: 'Número de WhatsApp (con código de país)',
550
+ },
551
+ {
552
+ displayName: 'Mensaje',
553
+ name: 'mensaje',
554
+ type: 'string',
555
+ typeOptions: { rows: 4 },
556
+ required: true,
557
+ displayOptions: {
558
+ show: {
559
+ resource: ['whatsapp'],
560
+ operation: ['sendMessage'],
561
+ },
562
+ },
563
+ default: '',
564
+ },
565
+ ],
566
+ };
567
+ }
568
+ async execute() {
569
+ const items = this.getInputData();
570
+ const returnData = [];
571
+ const credentials = await this.getCredentials('aivenceRealtyApi');
572
+ const baseUrl = credentials.baseUrl;
573
+ for (let i = 0; i < items.length; i++) {
574
+ try {
575
+ const resource = this.getNodeParameter('resource', i);
576
+ const operation = this.getNodeParameter('operation', i);
577
+ let responseData;
578
+ // ============================================
579
+ // PROPERTY RESOURCE
580
+ // ============================================
581
+ if (resource === 'property') {
582
+ if (operation === 'list') {
583
+ responseData = await this.helpers.httpRequest({
584
+ method: 'GET',
585
+ url: `${baseUrl}/api/v1/properties`,
586
+ json: true,
587
+ });
588
+ }
589
+ else if (operation === 'getSimilar') {
590
+ responseData = await this.helpers.httpRequest({
591
+ method: 'GET',
592
+ url: `${baseUrl}/api/v1/properties/similares`,
593
+ json: true,
594
+ });
595
+ }
596
+ else if (operation === 'getStatistics') {
597
+ responseData = await this.helpers.httpRequest({
598
+ method: 'GET',
599
+ url: `${baseUrl}/api/v1/properties/estadisticas`,
600
+ json: true,
601
+ });
602
+ }
603
+ }
604
+ // ============================================
605
+ // LEAD RESOURCE
606
+ // ============================================
607
+ else if (resource === 'lead') {
608
+ if (operation === 'list') {
609
+ responseData = await this.helpers.httpRequest({
610
+ method: 'GET',
611
+ url: `${baseUrl}/api/v1/leads`,
612
+ json: true,
613
+ });
614
+ }
615
+ else if (operation === 'create') {
616
+ const body = {
617
+ nombre: this.getNodeParameter('nombre', i),
618
+ telefono: this.getNodeParameter('telefono', i),
619
+ mensaje: this.getNodeParameter('mensaje', i),
620
+ };
621
+ responseData = await this.helpers.httpRequest({
622
+ method: 'POST',
623
+ url: `${baseUrl}/api/v1/leads`,
624
+ body,
625
+ json: true,
626
+ });
627
+ }
628
+ }
629
+ // ============================================
630
+ // TENANT RESOURCE
631
+ // ============================================
632
+ else if (resource === 'tenant') {
633
+ if (operation === 'list') {
634
+ responseData = await this.helpers.httpRequest({
635
+ method: 'GET',
636
+ url: `${baseUrl}/api/v1/tenants`,
637
+ json: true,
638
+ });
639
+ }
640
+ }
641
+ // ============================================
642
+ // MAINTENANCE ORDER RESOURCE
643
+ // ============================================
644
+ else if (resource === 'maintenanceOrder') {
645
+ if (operation === 'checkDuplicate') {
646
+ const body = {
647
+ propiedad_id: this.getNodeParameter('propiedadId', i),
648
+ estado: 'abierto',
649
+ };
650
+ responseData = await this.helpers.httpRequest({
651
+ method: 'POST',
652
+ url: `${baseUrl}/api/ordenes-trabajo/verificar-duplicado`,
653
+ body,
654
+ json: true,
655
+ });
656
+ }
657
+ else if (operation === 'create') {
658
+ const body = {
659
+ cliente_id: this.getNodeParameter('clienteId', i),
660
+ propiedad_id: this.getNodeParameter('propiedadId', i),
661
+ contratista_id: this.getNodeParameter('contratistaId', i),
662
+ categoria: this.getNodeParameter('categoria', i),
663
+ urgencia: this.getNodeParameter('urgencia', i),
664
+ descripcion_corta: this.getNodeParameter('descripcionCorta', i),
665
+ descripcion_detallada: this.getNodeParameter('descripcionDetallada', i),
666
+ estado: 'pendiente_aceptacion',
667
+ };
668
+ responseData = await this.helpers.httpRequest({
669
+ method: 'POST',
670
+ url: `${baseUrl}/api/ordenes-trabajo`,
671
+ body,
672
+ json: true,
673
+ });
674
+ }
675
+ else if (operation === 'get') {
676
+ const orderId = this.getNodeParameter('orderId', i);
677
+ responseData = await this.helpers.httpRequest({
678
+ method: 'GET',
679
+ url: `${baseUrl}/api/ordenes-trabajo/${orderId}`,
680
+ json: true,
681
+ });
682
+ }
683
+ else if (operation === 'update') {
684
+ const orderId = this.getNodeParameter('orderId', i);
685
+ // Aquí puedes añadir los campos a actualizar según necesites
686
+ responseData = await this.helpers.httpRequest({
687
+ method: 'PATCH',
688
+ url: `${baseUrl}/api/ordenes-trabajo/${orderId}`,
689
+ body: {},
690
+ json: true,
691
+ });
692
+ }
693
+ else if (operation === 'list') {
694
+ responseData = await this.helpers.httpRequest({
695
+ method: 'GET',
696
+ url: `${baseUrl}/api/ordenes-trabajo`,
697
+ json: true,
698
+ });
699
+ }
700
+ }
701
+ // ============================================
702
+ // CONTRACTOR RESOURCE
703
+ // ============================================
704
+ else if (resource === 'contractor') {
705
+ if (operation === 'findByCategory') {
706
+ const categoria = this.getNodeParameter('categoria', i);
707
+ responseData = await this.helpers.httpRequest({
708
+ method: 'GET',
709
+ url: `${baseUrl}/api/contratistas/por-categoria`,
710
+ qs: { categoria },
711
+ json: true,
712
+ });
713
+ }
714
+ else if (operation === 'findAlternatives') {
715
+ responseData = await this.helpers.httpRequest({
716
+ method: 'GET',
717
+ url: `${baseUrl}/api/contratistas/alternativos`,
718
+ json: true,
719
+ });
720
+ }
721
+ }
722
+ // ============================================
723
+ // MONTHLY COST RESOURCE
724
+ // ============================================
725
+ else if (resource === 'monthlyCost') {
726
+ if (operation === 'track') {
727
+ const body = {
728
+ mes: this.getNodeParameter('mes', i),
729
+ anio: this.getNodeParameter('anio', i),
730
+ categoria: this.getNodeParameter('categoria', i),
731
+ total_gastado: this.getNodeParameter('totalGastado', i),
732
+ };
733
+ responseData = await this.helpers.httpRequest({
734
+ method: 'POST',
735
+ url: `${baseUrl}/api/costos-mensuales`,
736
+ body,
737
+ json: true,
738
+ });
739
+ }
740
+ }
741
+ // ============================================
742
+ // WHATSAPP RESOURCE
743
+ // ============================================
744
+ else if (resource === 'whatsapp') {
745
+ if (operation === 'sendMessage') {
746
+ const body = {
747
+ number: this.getNodeParameter('numero', i),
748
+ text: this.getNodeParameter('mensaje', i),
749
+ };
750
+ responseData = await this.helpers.httpRequest({
751
+ method: 'POST',
752
+ url: `${baseUrl}/api/evolution/send-message`,
753
+ body,
754
+ json: true,
755
+ });
756
+ }
757
+ }
758
+ // Formatear respuesta
759
+ const executionData = this.helpers.constructExecutionMetaData(this.helpers.returnJsonArray(responseData), { itemData: { item: i } });
760
+ returnData.push(...executionData);
761
+ }
762
+ catch (error) {
763
+ if (this.continueOnFail()) {
764
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
765
+ returnData.push({
766
+ json: { error: errorMessage },
767
+ pairedItem: { item: i },
768
+ });
769
+ continue;
770
+ }
771
+ throw error;
772
+ }
773
+ }
774
+ return [returnData];
775
+ }
776
+ }
777
+ exports.AivenceRealty = AivenceRealty;
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "n8n-nodes-aivence-realty",
3
+ "version": "0.1.0",
4
+ "description": "Nodo n8n para integrar el CRM inmobiliario AivenceRealty",
5
+ "keywords": [
6
+ "n8n-community-node-package",
7
+ "n8n-nodes",
8
+ "AivenceRealty",
9
+ "CRM",
10
+ "Real Estate",
11
+ "Inmobiliario",
12
+ "Laravel"
13
+ ],
14
+ "license": "MIT",
15
+ "homepage": "https://realty.aivence.com",
16
+ "author": {
17
+ "name": "Federico Aivence",
18
+ "email": "federico@aivence.com"
19
+ },
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "https://github.com/aivence/n8n-nodes-aivence-realty.git"
23
+ },
24
+ "main": "index.js",
25
+ "scripts": {
26
+ "build": "tsc && gulp build:icons",
27
+ "dev": "tsc --watch",
28
+ "format": "prettier nodes credentials --write",
29
+ "lint": "eslint nodes credentials package.json",
30
+ "lintfix": "eslint nodes credentials package.json --fix",
31
+ "prepublishOnly": "npm run build"
32
+ },
33
+ "files": [
34
+ "dist"
35
+ ],
36
+ "n8n": {
37
+ "n8nNodesApiVersion": 1,
38
+ "credentials": [
39
+ "dist/credentials/AivenceRealtyApi.credentials.js"
40
+ ],
41
+ "nodes": [
42
+ "dist/nodes/AivenceRealty/AivenceRealty.node.js"
43
+ ]
44
+ },
45
+ "devDependencies": {
46
+ "@typescript-eslint/parser": "^7.15.0",
47
+ "eslint": "^8.56.0",
48
+ "eslint-plugin-n8n-nodes-base": "^1.16.1",
49
+ "gulp": "^4.0.2",
50
+ "n8n-workflow": "^1.0.0",
51
+ "prettier": "^3.3.2",
52
+ "typescript": "^5.5.3"
53
+ },
54
+ "peerDependencies": {
55
+ "n8n-workflow": "*"
56
+ }
57
+ }