@horizon-integrations/si9-crm 1.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/dist/index.mjs ADDED
@@ -0,0 +1,1409 @@
1
+ "use strict";
2
+
3
+ // src/schemas/si9-property-schema.zod.ts
4
+ import { z } from "zod";
5
+ var categorySchema = z.object({
6
+ id: z.number().nullish(),
7
+ name: z.string().nullish()
8
+ }).passthrough();
9
+ var generalDataSchema = z.object({
10
+ board: z.string().nullish(),
11
+ situation: z.string().nullish(),
12
+ category: categorySchema.nullish(),
13
+ description: z.string().nullish(),
14
+ title: z.string().nullish(),
15
+ allotment: z.string().nullish(),
16
+ costCenter: z.string().nullish(),
17
+ urlVideo: z.string().nullish(),
18
+ urlTour: z.string().nullish()
19
+ }).passthrough();
20
+ var destinationSchema = z.object({
21
+ sale: z.boolean().nullish(),
22
+ rent: z.boolean().nullish(),
23
+ season: z.boolean().nullish(),
24
+ private: z.boolean().nullish(),
25
+ application: z.boolean().nullish(),
26
+ exchange: z.boolean().nullish()
27
+ }).passthrough();
28
+ var valuesSchema = z.object({
29
+ saleValue: z.number().nullish(),
30
+ rentValue: z.number().nullish(),
31
+ discountValue: z.number().nullish(),
32
+ fciValue: z.number().nullish(),
33
+ iptuValue: z.number().nullish(),
34
+ assignedValue: z.number().nullish(),
35
+ trashFee: z.number().nullish(),
36
+ disasterFee: z.number().nullish(),
37
+ condominiumFee: z.number().nullish()
38
+ }).passthrough();
39
+ var citySchema = z.object({
40
+ id: z.union([z.number(), z.null()]).nullish(),
41
+ name: z.string().nullish(),
42
+ state: z.string().nullish()
43
+ }).passthrough();
44
+ var districtSchema = z.object({
45
+ id: z.union([z.number(), z.null()]).nullish(),
46
+ name: z.string().nullish()
47
+ }).passthrough();
48
+ var locationSchema = z.object({
49
+ city: citySchema.nullish(),
50
+ address: z.string().nullish(),
51
+ number: z.string().nullish(),
52
+ zipCode: z.string().nullish(),
53
+ district: districtSchema.nullish(),
54
+ apartment: z.string().nullish(),
55
+ block: z.string().nullish(),
56
+ floor: z.string().nullish(),
57
+ quatrain: z.string().nullish(),
58
+ lot: z.string().nullish(),
59
+ complement: z.string().nullish(),
60
+ referencePoint: z.string().nullish(),
61
+ longitude: z.string().nullish(),
62
+ latitude: z.string().nullish(),
63
+ blocksMap: z.unknown().nullish()
64
+ }).passthrough();
65
+ var websiteSchema = z.object({
66
+ highlight: z.boolean().nullish(),
67
+ superHighlight: z.boolean().nullish(),
68
+ showInCore: z.boolean().nullish(),
69
+ mcmv: z.boolean().nullish()
70
+ }).passthrough();
71
+ var dimensionsSchema = z.object({
72
+ buildingArea: z.number().nullish(),
73
+ landArea: z.number().nullish(),
74
+ usefulArea: z.number().nullish(),
75
+ privateArea: z.number().nullish(),
76
+ internalArea: z.number().nullish(),
77
+ registeredArea: z.number().nullish(),
78
+ totalArea: z.number().nullish(),
79
+ commonArea: z.number().nullish(),
80
+ garageArea: z.number().nullish(),
81
+ frontSize: z.number().nullish(),
82
+ fundsSize: z.number().nullish(),
83
+ leftSize: z.number().nullish(),
84
+ rightSize: z.number().nullish()
85
+ }).passthrough();
86
+ var featuresSchema = z.object({
87
+ // Numéricos
88
+ room: z.number().nullish(),
89
+ kitchen: z.number().nullish(),
90
+ bedroom: z.number().nullish(),
91
+ suite: z.number().nullish(),
92
+ closet: z.number().nullish(),
93
+ bathroom: z.number().nullish(),
94
+ toilet: z.number().nullish(),
95
+ balcony: z.number().nullish(),
96
+ coveredGarage: z.number().nullish(),
97
+ outdoorGarage: z.number().nullish(),
98
+ arCondicionado: z.number().nullish(),
99
+ // Acabamentos (string/enum)
100
+ floor: z.string().nullish(),
101
+ roof: z.string().nullish(),
102
+ ceiling: z.string().nullish(),
103
+ paving: z.string().nullish(),
104
+ pavement: z.string().nullish(),
105
+ // Booleans
106
+ shed: z.boolean().nullish(),
107
+ needMaid: z.boolean().nullish(),
108
+ serviceArea: z.boolean().nullish(),
109
+ pantry: z.boolean().nullish(),
110
+ furnished: z.boolean().nullish(),
111
+ semiFurnished: z.boolean().nullish(),
112
+ wineHouse: z.boolean().nullish(),
113
+ fittedKitchen: z.boolean().nullish(),
114
+ kennel: z.boolean().nullish(),
115
+ waterTank: z.boolean().nullish(),
116
+ barbecueGrill: z.boolean().nullish(),
117
+ telephone: z.boolean().nullish(),
118
+ porchWithGrill: z.boolean().nullish(),
119
+ elevator: z.boolean().nullish(),
120
+ porch: z.boolean().nullish(),
121
+ winterGarden: z.boolean().nullish(),
122
+ penthouseApartment: z.boolean().nullish(),
123
+ highStandard: z.boolean().nullish(),
124
+ release: z.boolean().nullish(),
125
+ condominium: z.boolean().nullish(),
126
+ inPlant: z.boolean().nullish(),
127
+ coast: z.boolean().nullish(),
128
+ serviceBathroom: z.boolean().nullish(),
129
+ lounge: z.boolean().nullish(),
130
+ onlineConcierge: z.boolean().nullish(),
131
+ demiSuite: z.boolean().nullish(),
132
+ newOrSemiNew: z.boolean().nullish()
133
+ }).passthrough();
134
+ var securitySchema = z.object({
135
+ alarm: z.boolean().nullish(),
136
+ electricFence: z.boolean().nullish(),
137
+ intercom: z.boolean().nullish(),
138
+ concierge: z.boolean().nullish(),
139
+ walled: z.boolean().nullish(),
140
+ gate: z.boolean().nullish(),
141
+ electronicGate: z.boolean().nullish(),
142
+ protectionGrid: z.boolean().nullish()
143
+ }).passthrough();
144
+ var recreationSchema = z.object({
145
+ playgroud: z.boolean().nullish(),
146
+ // typo da SI9 (sem "n")
147
+ sauna: z.boolean().nullish(),
148
+ pool: z.boolean().nullish(),
149
+ gym: z.boolean().nullish(),
150
+ fireplace: z.boolean().nullish(),
151
+ gourmetSpace: z.boolean().nullish(),
152
+ multisportCourt: z.boolean().nullish(),
153
+ partyRoom: z.boolean().nullish(),
154
+ whirlpool: z.boolean().nullish(),
155
+ heating: z.string().nullish(),
156
+ positionSun: z.string().nullish(),
157
+ positionCourt: z.string().nullish()
158
+ }).passthrough();
159
+ var ruralSchema = z.object({
160
+ river: z.boolean().nullish(),
161
+ mine: z.boolean().nullish(),
162
+ hose: z.boolean().nullish(),
163
+ footballField: z.boolean().nullish(),
164
+ bushels: z.number().nullish(),
165
+ acre: z.number().nullish(),
166
+ pasture: z.string().nullish(),
167
+ mechanized: z.string().nullish(),
168
+ reserve: z.number().nullish(),
169
+ pigsty: z.number().nullish(),
170
+ aviary: z.number().nullish(),
171
+ weir: z.number().nullish(),
172
+ woodHouse: z.number().nullish(),
173
+ brickHouse: z.number().nullish(),
174
+ mixedHouse: z.number().nullish()
175
+ }).passthrough();
176
+ var companySchema = z.object({
177
+ id: z.number().nullish(),
178
+ fancyName: z.string().nullish(),
179
+ corporateName: z.string().nullish(),
180
+ address: z.string().nullish(),
181
+ phoneNumber: z.string().nullish(),
182
+ siteUrl: z.string().nullish()
183
+ }).passthrough();
184
+ var imageSchema = z.object({
185
+ url: z.string().nullish(),
186
+ subtitle: z.string().nullish(),
187
+ displaySite: z.boolean().nullish(),
188
+ pic360: z.union([z.boolean(), z.string()]).nullish()
189
+ }).passthrough();
190
+ var fieldSchema = z.object({
191
+ field: z.string().nullish(),
192
+ value: z.unknown().nullish()
193
+ }).passthrough();
194
+ var Si9PropertySchemaZod = z.object({
195
+ id: z.number(),
196
+ generalData: generalDataSchema.nullish(),
197
+ destination: destinationSchema.nullish(),
198
+ values: valuesSchema.nullish(),
199
+ location: locationSchema.nullish(),
200
+ website: websiteSchema.nullish(),
201
+ dimensions: dimensionsSchema.nullish(),
202
+ features: featuresSchema.nullish(),
203
+ security: securitySchema.nullish(),
204
+ recreation: recreationSchema.nullish(),
205
+ rural: ruralSchema.nullish(),
206
+ company: companySchema.nullish(),
207
+ images: z.array(imageSchema).nullish(),
208
+ fields: z.array(fieldSchema).nullish()
209
+ }).passthrough();
210
+ var validateSi9PropertySchema = (data) => {
211
+ return Si9PropertySchemaZod.parse(data);
212
+ };
213
+ var safeValidateSi9PropertySchema = (data) => {
214
+ return Si9PropertySchemaZod.safeParse(data);
215
+ };
216
+
217
+ // src/services/PropertyConverter/convertBaseFields.ts
218
+ var FIELDS_RECONHECIDOS = {
219
+ Corretor: (result, value) => {
220
+ if (value) result.corretor_nome = value;
221
+ },
222
+ "Geolocaliza\xE7\xE3o aproximada": (result, _value) => {
223
+ }
224
+ };
225
+ function convertBaseFields(imovel) {
226
+ const result = {};
227
+ result.reference = String(imovel.id);
228
+ result.title = imovel.generalData?.title || "Sem t\xEDtulo";
229
+ result.description = imovel.generalData?.description || "Sem descri\xE7\xE3o";
230
+ result.source_updated_at = (/* @__PURE__ */ new Date()).toISOString();
231
+ result.currency = "BRL";
232
+ result.unit_area = "m2";
233
+ result.unit_distance = "meters";
234
+ const dest = imovel.destination;
235
+ if (dest) {
236
+ const operacao = [];
237
+ if (dest.sale) operacao.push("venda");
238
+ if (dest.rent) operacao.push("locacao");
239
+ if (dest.season) operacao.push("temporada");
240
+ if (operacao.length > 0) result.operacao = operacao;
241
+ }
242
+ if (imovel.generalData?.category?.name) {
243
+ result.tipo = imovel.generalData.category.name;
244
+ }
245
+ const values = imovel.values;
246
+ if (values) {
247
+ if (values.saleValue && values.saleValue > 0 && imovel.destination?.sale) {
248
+ result.valor_venda = values.saleValue;
249
+ }
250
+ if (values.rentValue && values.rentValue > 0 && imovel.destination?.rent) {
251
+ result.valor_locacao = values.rentValue;
252
+ }
253
+ if (values.condominiumFee && values.condominiumFee > 0) {
254
+ result.valor_condominio = values.condominiumFee;
255
+ }
256
+ if (values.iptuValue && values.iptuValue > 0) {
257
+ result.valor_iptu = values.iptuValue;
258
+ }
259
+ }
260
+ const dim = imovel.dimensions;
261
+ if (dim) {
262
+ if (dim.totalArea && dim.totalArea > 0) result.area_total = dim.totalArea;
263
+ if (dim.privateArea && dim.privateArea > 0)
264
+ result.area_privativa = dim.privateArea;
265
+ if (dim.usefulArea && dim.usefulArea > 0) result.area_util = dim.usefulArea;
266
+ }
267
+ const feat = imovel.features;
268
+ if (feat) {
269
+ if (feat.bedroom && feat.bedroom > 0) result.dormitorios = feat.bedroom;
270
+ if (feat.suite && feat.suite > 0) result.suites = feat.suite;
271
+ if (feat.bathroom && feat.bathroom > 0) result.banheiros = feat.bathroom;
272
+ const covered = feat.coveredGarage || 0;
273
+ const outdoor = feat.outdoorGarage || 0;
274
+ const totalGaragens = covered + outdoor;
275
+ if (totalGaragens > 0) result.vagas_garagem = totalGaragens;
276
+ }
277
+ const loc = imovel.location;
278
+ if (loc) {
279
+ if (loc.zipCode) {
280
+ const cepLimpo = loc.zipCode.replace(/\D/g, "");
281
+ if (cepLimpo.length === 8) {
282
+ result.endereco_cep = `${cepLimpo.slice(0, 5)}-${cepLimpo.slice(5)}`;
283
+ } else {
284
+ result.endereco_cep = loc.zipCode;
285
+ }
286
+ }
287
+ if (loc.city?.state) result.endereco_estado = loc.city.state;
288
+ if (loc.city?.name) result.endereco_cidade = loc.city.name;
289
+ if (loc.district?.name) result.endereco_bairro = loc.district.name;
290
+ if (loc.address) result.endereco_logradouro = loc.address;
291
+ if (loc.number) result.endereco_numero = loc.number;
292
+ if (loc.complement) result.endereco_complemento = loc.complement;
293
+ if (loc.referencePoint) result.endereco_referencia = loc.referencePoint;
294
+ if (loc.latitude && loc.longitude) {
295
+ const lat = Number(loc.latitude);
296
+ const lng = Number(loc.longitude);
297
+ if (!isNaN(lat) && !isNaN(lng) && lat !== 0 && lng !== 0) {
298
+ result.lat = lat;
299
+ result.lng = lng;
300
+ }
301
+ }
302
+ }
303
+ if (imovel.generalData?.allotment) {
304
+ result.condominio_nome = imovel.generalData.allotment;
305
+ }
306
+ if (imovel.website?.highlight || imovel.website?.superHighlight) {
307
+ result.destaque = true;
308
+ }
309
+ if (imovel.images && imovel.images.length > 0) {
310
+ const visibleImages = imovel.images.filter(
311
+ (img) => img.displaySite !== false && img.url
312
+ );
313
+ result.images = visibleImages.map((img, index) => ({
314
+ full: img.url || "",
315
+ md: img.url || "",
316
+ sm: img.url || "",
317
+ cover: index === 0
318
+ }));
319
+ if (visibleImages.length > 0 && visibleImages[0].url) {
320
+ result.main_image = {
321
+ full: visibleImages[0].url,
322
+ md: visibleImages[0].url,
323
+ sm: visibleImages[0].url
324
+ };
325
+ }
326
+ } else {
327
+ result.images = [];
328
+ }
329
+ if (imovel.generalData?.urlVideo) {
330
+ const url = imovel.generalData.urlVideo;
331
+ result.videos = [
332
+ {
333
+ url,
334
+ embed_url: url
335
+ }
336
+ ];
337
+ } else {
338
+ result.videos = [];
339
+ }
340
+ if (imovel.generalData?.urlTour) {
341
+ result.virtual_tours = [
342
+ {
343
+ embed_url: imovel.generalData.urlTour
344
+ }
345
+ ];
346
+ } else {
347
+ result.virtual_tours = [];
348
+ }
349
+ const fields = imovel.fields;
350
+ if (fields && fields.length > 0) {
351
+ for (const f of fields) {
352
+ if (!f.field) continue;
353
+ const handler = FIELDS_RECONHECIDOS[String(f.field)];
354
+ if (handler) {
355
+ handler(result, String(f.value || ""));
356
+ }
357
+ }
358
+ const hasGeoField = fields.some(
359
+ (f) => String(f.field) === "Geolocaliza\xE7\xE3o aproximada"
360
+ );
361
+ if (hasGeoField && result.lat && result.lng) {
362
+ result.geo_aproximado = {
363
+ lat: result.lat,
364
+ lng: result.lng
365
+ };
366
+ }
367
+ }
368
+ return result;
369
+ }
370
+
371
+ // src/helpers/si9-labels.ts
372
+ var FEATURES_LABELS = {
373
+ shed: "Galp\xE3o",
374
+ needMaid: "Depend\xEAncia de empregada",
375
+ serviceArea: "\xC1rea de servi\xE7o",
376
+ pantry: "Despensa",
377
+ furnished: "Mobiliado",
378
+ semiFurnished: "Semi-mobiliado",
379
+ wineHouse: "Adega",
380
+ fittedKitchen: "Cozinha planejada",
381
+ kennel: "Canil",
382
+ waterTank: "Cisterna",
383
+ barbecueGrill: "Churrasqueira",
384
+ telephone: "Telefone",
385
+ porchWithGrill: "Varanda gourmet",
386
+ elevator: "Elevador",
387
+ porch: "Varanda",
388
+ winterGarden: "Jardim de inverno",
389
+ penthouseApartment: "Cobertura",
390
+ highStandard: "Alto padr\xE3o",
391
+ release: "Lan\xE7amento",
392
+ condominium: "Condom\xEDnio",
393
+ inPlant: "Na planta",
394
+ coast: "Litoral",
395
+ serviceBathroom: "Banheiro de servi\xE7o",
396
+ lounge: "Sala de estar",
397
+ onlineConcierge: "Portaria online",
398
+ demiSuite: "Demi-su\xEDte",
399
+ newOrSemiNew: "Novo ou seminovo"
400
+ };
401
+ var SECURITY_LABELS = {
402
+ alarm: "Alarme",
403
+ electricFence: "Cerca el\xE9trica",
404
+ intercom: "Interfone",
405
+ concierge: "Portaria",
406
+ walled: "Murado",
407
+ gate: "Port\xE3o",
408
+ electronicGate: "Port\xE3o eletr\xF4nico",
409
+ protectionGrid: "Grade de prote\xE7\xE3o"
410
+ };
411
+ var RECREATION_LABELS = {
412
+ playgroud: "Playground",
413
+ // typo da SI9 (sem "n")
414
+ sauna: "Sauna",
415
+ pool: "Piscina",
416
+ gym: "Academia",
417
+ fireplace: "Lareira",
418
+ gourmetSpace: "Espa\xE7o gourmet",
419
+ multisportCourt: "Quadra poliesportiva",
420
+ partyRoom: "Sal\xE3o de festas",
421
+ whirlpool: "Hidromassagem"
422
+ };
423
+ var RURAL_LABELS = {
424
+ river: "Rio",
425
+ mine: "Mina",
426
+ hose: "Mangueira",
427
+ footballField: "Campo de futebol"
428
+ };
429
+ function booleanFieldsToLabels(obj, labelMap) {
430
+ if (!obj) return [];
431
+ const labels = [];
432
+ for (const [key, label] of Object.entries(labelMap)) {
433
+ if (obj[key] === true) {
434
+ labels.push(label);
435
+ }
436
+ }
437
+ return labels;
438
+ }
439
+
440
+ // src/services/PropertyConverter/convertExtendedFields.ts
441
+ function convertExtendedFields(imovel) {
442
+ const result = {};
443
+ const values = imovel.values;
444
+ if (values) {
445
+ if (values.discountValue && values.discountValue > 0)
446
+ result.values_discount_value = values.discountValue;
447
+ if (values.fciValue && values.fciValue > 0)
448
+ result.values_fci_value = values.fciValue;
449
+ if (values.assignedValue && values.assignedValue > 0)
450
+ result.values_assigned_value = values.assignedValue;
451
+ if (values.trashFee && values.trashFee > 0)
452
+ result.values_trash_fee = values.trashFee;
453
+ if (values.disasterFee && values.disasterFee > 0)
454
+ result.values_disaster_fee = values.disasterFee;
455
+ }
456
+ if (imovel.generalData?.situation)
457
+ result.general_data_situation = imovel.generalData.situation;
458
+ if (imovel.generalData?.board)
459
+ result.general_data_board = imovel.generalData.board;
460
+ if (imovel.website?.superHighlight === true)
461
+ result.website_super_highlight = true;
462
+ if (imovel.website?.showInCore === true)
463
+ result.website_show_in_core = true;
464
+ if (imovel.website?.mcmv === true)
465
+ result.website_mcmv = true;
466
+ if (imovel.destination?.exchange === true)
467
+ result.destination_exchange = true;
468
+ const loc = imovel.location;
469
+ if (loc) {
470
+ if (loc.apartment) result.location_apartment = loc.apartment;
471
+ if (loc.block) result.location_block = loc.block;
472
+ if (loc.floor) result.location_floor = loc.floor;
473
+ if (loc.quatrain) result.location_quatrain = loc.quatrain;
474
+ if (loc.lot) result.location_lot = loc.lot;
475
+ }
476
+ const dim = imovel.dimensions;
477
+ if (dim) {
478
+ if (dim.buildingArea && dim.buildingArea > 0)
479
+ result.dimensions_building_area = dim.buildingArea;
480
+ if (dim.landArea && dim.landArea > 0)
481
+ result.dimensions_land_area = dim.landArea;
482
+ if (dim.internalArea && dim.internalArea > 0)
483
+ result.dimensions_internal_area = dim.internalArea;
484
+ if (dim.registeredArea && dim.registeredArea > 0)
485
+ result.dimensions_registered_area = dim.registeredArea;
486
+ if (dim.commonArea && dim.commonArea > 0)
487
+ result.dimensions_common_area = dim.commonArea;
488
+ if (dim.garageArea && dim.garageArea > 0)
489
+ result.dimensions_garage_area = dim.garageArea;
490
+ if (dim.frontSize && dim.frontSize > 0)
491
+ result.dimensions_front_size = dim.frontSize;
492
+ if (dim.fundsSize && dim.fundsSize > 0)
493
+ result.dimensions_funds_size = dim.fundsSize;
494
+ if (dim.leftSize && dim.leftSize > 0)
495
+ result.dimensions_left_size = dim.leftSize;
496
+ if (dim.rightSize && dim.rightSize > 0)
497
+ result.dimensions_right_size = dim.rightSize;
498
+ }
499
+ const feat = imovel.features;
500
+ const features = booleanFieldsToLabels(
501
+ feat,
502
+ FEATURES_LABELS
503
+ );
504
+ if (features.length > 0) result.features = features;
505
+ if (feat) {
506
+ if (feat.room && feat.room > 0) result.features_room = feat.room;
507
+ if (feat.kitchen && feat.kitchen > 0) result.features_kitchen = feat.kitchen;
508
+ if (feat.closet && feat.closet > 0) result.features_closet = feat.closet;
509
+ if (feat.balcony && feat.balcony > 0) result.features_balcony = feat.balcony;
510
+ if (feat.arCondicionado && feat.arCondicionado > 0)
511
+ result.features_ar_condicionado = feat.arCondicionado;
512
+ if (feat.toilet && feat.toilet > 0) result.features_toilet = feat.toilet;
513
+ if (feat.floor) result.features_floor = feat.floor;
514
+ if (feat.roof) result.features_roof = feat.roof;
515
+ if (feat.ceiling) result.features_ceiling = feat.ceiling;
516
+ if (feat.paving) result.features_paving = feat.paving;
517
+ if (feat.pavement) result.features_pavement = feat.pavement;
518
+ }
519
+ const security = booleanFieldsToLabels(
520
+ imovel.security,
521
+ SECURITY_LABELS
522
+ );
523
+ if (security.length > 0) result.security = security;
524
+ const recreation = booleanFieldsToLabels(
525
+ imovel.recreation,
526
+ RECREATION_LABELS
527
+ );
528
+ if (recreation.length > 0) result.recreation = recreation;
529
+ if (imovel.recreation?.heating)
530
+ result.recreation_heating = imovel.recreation.heating;
531
+ if (imovel.recreation?.positionSun)
532
+ result.recreation_position_sun = imovel.recreation.positionSun;
533
+ if (imovel.recreation?.positionCourt)
534
+ result.recreation_position_court = imovel.recreation.positionCourt;
535
+ const rural = imovel.rural;
536
+ if (rural) {
537
+ const ruralLabels = booleanFieldsToLabels(
538
+ rural,
539
+ RURAL_LABELS
540
+ );
541
+ if (ruralLabels.length > 0) result.rural = ruralLabels;
542
+ if (rural.bushels && rural.bushels > 0) result.rural_bushels = rural.bushels;
543
+ if (rural.acre && rural.acre > 0) result.rural_acre = rural.acre;
544
+ if (rural.pasture) result.rural_pasture = rural.pasture;
545
+ if (rural.mechanized) result.rural_mechanized = rural.mechanized;
546
+ if (rural.reserve && rural.reserve > 0) result.rural_reserve = rural.reserve;
547
+ if (rural.pigsty && rural.pigsty > 0) result.rural_pigsty = rural.pigsty;
548
+ if (rural.aviary && rural.aviary > 0) result.rural_aviary = rural.aviary;
549
+ if (rural.weir && rural.weir > 0) result.rural_weir = rural.weir;
550
+ if (rural.woodHouse && rural.woodHouse > 0)
551
+ result.rural_wood_house = rural.woodHouse;
552
+ if (rural.brickHouse && rural.brickHouse > 0)
553
+ result.rural_brick_house = rural.brickHouse;
554
+ if (rural.mixedHouse && rural.mixedHouse > 0)
555
+ result.rural_mixed_house = rural.mixedHouse;
556
+ }
557
+ const fields = imovel.fields;
558
+ if (fields && fields.length > 0) {
559
+ const fieldsFormatados = fields.filter((f) => f.field).map((f) => ({
560
+ field: String(f.field),
561
+ value: String(f.value || "")
562
+ }));
563
+ if (fieldsFormatados.length > 0) result.fields = fieldsFormatados;
564
+ }
565
+ return result;
566
+ }
567
+
568
+ // src/services/PropertyConverter/index.ts
569
+ function convertSi9PropertyToHorizon(rawImovel) {
570
+ const validation = safeValidateSi9PropertySchema(rawImovel);
571
+ if (!validation.success) {
572
+ const errors = validation.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
573
+ throw new Error(`Valida\xE7\xE3o SI9 falhou: ${errors}`);
574
+ }
575
+ const imovel = validation.data;
576
+ const baseFields = convertBaseFields(imovel);
577
+ const extendedFields = convertExtendedFields(imovel);
578
+ const result = {
579
+ ...baseFields,
580
+ ...extendedFields
581
+ };
582
+ return result;
583
+ }
584
+
585
+ // src/services/PropertyDownloader.ts
586
+ import { promises as fs } from "fs";
587
+ import { join } from "path";
588
+ var SI9_DEFAULT_CREDENTIALS = {
589
+ username: "ghisi-api",
590
+ password: "y6HyTKxAZ40tx8D8w%5E3)",
591
+ authorization: "ZDc3ZmYyMzUtNmEyNS00YzE3LWI4NDktNjM3NWQ2NGM0ZTVhOmI2ZDhhMTgxLTQ5OWEtNGRjNi1hNGU0LTlkZTUxZjNjMDE5Nw=="
592
+ };
593
+ var PropertyDownloader = class {
594
+ constructor(config) {
595
+ this.token = null;
596
+ this.tokenExpiresAt = 0;
597
+ this.credentials = config.credentials;
598
+ this.baseUrl = config.baseUrl || "https://api.si9sistemas.com.br/imobilsi9-api";
599
+ this.outputDir = config.outputDir;
600
+ }
601
+ /**
602
+ * Autentica via OAuth 2.0 Password Grant
603
+ * IMPORTANTE: NÃO enviar Content-Type no auth (causa 500 na API)
604
+ */
605
+ async authenticate() {
606
+ const url = `${this.baseUrl}/oauth/token?grant_type=password&username=${this.credentials.username}&password=${this.credentials.password}`;
607
+ console.log(`\u{1F510} Autenticando na SI9...`);
608
+ const response = await fetch(url, {
609
+ method: "POST",
610
+ headers: {
611
+ Authorization: `Basic ${this.credentials.authorization}`
612
+ }
613
+ });
614
+ if (!response.ok) {
615
+ const errorText = await response.text();
616
+ throw new Error(`Erro de autentica\xE7\xE3o ${response.status}: ${errorText}`);
617
+ }
618
+ const data = await response.json();
619
+ console.log(`\u2705 Token obtido (expira em ${data.expires_in}s)`);
620
+ return data;
621
+ }
622
+ /**
623
+ * Garante que o token está válido, renovando se necessário
624
+ * Margem de segurança: renova 60s antes de expirar
625
+ */
626
+ async ensureToken() {
627
+ if (this.token && Date.now() < this.tokenExpiresAt) return;
628
+ const auth = await this.authenticate();
629
+ this.token = auth.access_token;
630
+ this.tokenExpiresAt = Date.now() + auth.expires_in * 1e3 - 6e4;
631
+ }
632
+ /**
633
+ * Busca todos os imóveis (SI9 não tem paginação)
634
+ */
635
+ async getProperties() {
636
+ await this.ensureToken();
637
+ const url = `${this.baseUrl}/property`;
638
+ console.log(`\u{1F517} Requisi\xE7\xE3o: GET ${url}`);
639
+ const response = await fetch(url, {
640
+ headers: {
641
+ Authorization: `Bearer ${this.token}`
642
+ }
643
+ });
644
+ if (!response.ok) {
645
+ const errorText = await response.text();
646
+ throw new Error(`Erro ${response.status}: ${errorText}`);
647
+ }
648
+ return await response.json();
649
+ }
650
+ async ensureOutputDir() {
651
+ try {
652
+ await fs.access(this.outputDir);
653
+ } catch {
654
+ await fs.mkdir(this.outputDir, { recursive: true });
655
+ }
656
+ }
657
+ /**
658
+ * Baixa TODOS os imóveis e salva em arquivo único
659
+ * (SI9 não tem paginação — retorna tudo de uma vez)
660
+ */
661
+ async downloadAll() {
662
+ await this.ensureOutputDir();
663
+ const errors = [];
664
+ let downloadedItems = 0;
665
+ console.log(`\u{1F680} Iniciando download completo da API SI9...`);
666
+ console.log(`\u{1F4C1} Salvando em: ${this.outputDir}`);
667
+ try {
668
+ const properties = await this.getProperties();
669
+ if (!Array.isArray(properties)) {
670
+ throw new Error(`Resposta inesperada: esperava array, recebeu ${typeof properties}`);
671
+ }
672
+ downloadedItems = properties.length;
673
+ console.log(`\u{1F4CA} Recebidos: ${downloadedItems} im\xF3veis`);
674
+ if (downloadedItems > 0) {
675
+ const filePath = join(this.outputDir, "all-properties.json");
676
+ await fs.writeFile(filePath, JSON.stringify(properties, null, 2), "utf8");
677
+ console.log(`\u2705 Salvo em: all-properties.json`);
678
+ } else {
679
+ console.log(`\u26A0\uFE0F Nenhum im\xF3vel retornado (array vazio)`);
680
+ }
681
+ } catch (error) {
682
+ const errorMsg = `Erro no download: ${error instanceof Error ? error.message : String(error)}`;
683
+ errors.push(errorMsg);
684
+ console.error(`\u274C ${errorMsg}`);
685
+ }
686
+ console.log(`
687
+ \u{1F4CA} RESUMO DO DOWNLOAD:`);
688
+ console.log(`\u2705 Propriedades baixadas: ${downloadedItems}`);
689
+ if (errors.length > 0) {
690
+ console.log(`\u274C Erros: ${errors.length}`);
691
+ errors.forEach((err) => console.log(` - ${err}`));
692
+ }
693
+ return {
694
+ totalItems: downloadedItems,
695
+ downloadedItems,
696
+ errors
697
+ };
698
+ }
699
+ };
700
+
701
+ // src/schemas/horizon-property-schema-by-si9.ts
702
+ import { horizonPropertySchemaBase } from "@horizon-domains/property-model";
703
+ var BASE_SOURCE_MAP = {
704
+ // Sistema
705
+ source_published_at: "(Vazio - n\xE3o dispon\xEDvel na API)",
706
+ source_updated_at: "gerado com new Date().toISOString()",
707
+ currency: "fixo 'BRL'",
708
+ unit_area: "fixo 'm2'",
709
+ unit_distance: "fixo 'meters'",
710
+ // Cabeçalho
711
+ reference: "id (number\u2192string)",
712
+ title: "generalData.title",
713
+ description: "generalData.description",
714
+ // SEO
715
+ seo_slug: "(Vazio - n\xE3o dispon\xEDvel na API)",
716
+ seo_title: "(Vazio - n\xE3o dispon\xEDvel na API)",
717
+ seo_description: "(Vazio - n\xE3o dispon\xEDvel na API)",
718
+ seo_keywords: "(Vazio - n\xE3o dispon\xEDvel na API)",
719
+ // Mídia
720
+ main_image: "images[0] (filtrar displaySite=true, primeira imagem)",
721
+ images: "images[] (filtrar displaySite=true, excluir pic360)",
722
+ videos: "generalData.urlVideo (array com 1 item se existir)",
723
+ virtual_tours: "generalData.urlTour (array com 1 item se existir)",
724
+ // Ficha técnica
725
+ operacao: "destination.sale\u2192'venda', destination.rent\u2192'locacao', destination.season\u2192'temporada' (array)",
726
+ tipo: "generalData.category.name",
727
+ dormitorios: "features.bedroom",
728
+ suites: "features.suite",
729
+ banheiros: "features.bathroom (s\xF3 bathroom, toilet vai pra features_toilet separado)",
730
+ vagas_garagem: "features.coveredGarage + features.outdoorGarage (soma cobertas + descobertas)",
731
+ area_total: "dimensions.totalArea (m\xB2)",
732
+ area_privativa: "dimensions.privateArea (m\xB2)",
733
+ area_util: "dimensions.usefulArea (m\xB2)",
734
+ destaque: "website.highlight || website.superHighlight (true se qualquer um true)",
735
+ // Valores
736
+ valor_venda: "values.saleValue (REAIS, s\xF3 se destination.sale=true e > 0)",
737
+ valor_locacao: "values.rentValue (REAIS, s\xF3 se destination.rent=true e > 0)",
738
+ valor_condominio: "values.condominiumFee (REAIS)",
739
+ valor_iptu: "values.iptuValue (REAIS, ANUAL)",
740
+ // Localização
741
+ endereco_cep: "location.zipCode (formatado 00000-000)",
742
+ endereco_estado: "location.city.state",
743
+ endereco_cidade: "location.city.name",
744
+ endereco_bairro: "location.district.name",
745
+ endereco_logradouro: "location.address",
746
+ endereco_numero: "location.number",
747
+ endereco_complemento: "location.complement",
748
+ endereco_referencia: "location.referencePoint",
749
+ endereco_zona: "(Vazio - n\xE3o dispon\xEDvel na API)",
750
+ lat: "location.latitude (string\u2192number)",
751
+ lng: "location.longitude (string\u2192number)",
752
+ // Relações
753
+ corretor_key: "(Vazio - n\xE3o dispon\xEDvel na API)",
754
+ corretor_nome: "Campo personalizado 'Corretor' (se existir)",
755
+ condominio_key: "(Vazio - n\xE3o dispon\xEDvel na API)",
756
+ condominio_nome: "generalData.allotment (nome do empreendimento/loteamento/edif\xEDcio)"
757
+ };
758
+ var HorizonPropertySchemaBySi9 = {
759
+ entity: "property",
760
+ version: "3.2.0",
761
+ description: "Schema estendido SI9 CRM \u2014 45 base com source tracking + extras snake_case",
762
+ fields: [
763
+ // Injeta source nos campos base
764
+ ...horizonPropertySchemaBase.fields.map((field) => ({
765
+ ...field,
766
+ audit: {
767
+ ...field.audit,
768
+ ...BASE_SOURCE_MAP[field.key] ? { source: BASE_SOURCE_MAP[field.key] } : {}
769
+ }
770
+ })),
771
+ // ========================================
772
+ // values.*
773
+ // ========================================
774
+ {
775
+ key: "values_discount_value",
776
+ type: "Number",
777
+ format: "currency",
778
+ unit: "BRL",
779
+ categories: ["valores"],
780
+ ui: { label: "Valor de desconto" },
781
+ audit: { origin: "si9:property", source: "values.discountValue (REAIS)" }
782
+ },
783
+ {
784
+ key: "values_fci_value",
785
+ type: "Number",
786
+ format: "currency",
787
+ unit: "BRL",
788
+ categories: ["valores"],
789
+ ui: { label: "Valor FCI" },
790
+ audit: { origin: "si9:property", source: "values.fciValue (REAIS)" }
791
+ },
792
+ {
793
+ key: "values_assigned_value",
794
+ type: "Number",
795
+ format: "currency",
796
+ unit: "BRL",
797
+ categories: ["valores"],
798
+ ui: { label: "Valor atribu\xEDdo" },
799
+ audit: { origin: "si9:property", source: "values.assignedValue (REAIS)" }
800
+ },
801
+ {
802
+ key: "values_trash_fee",
803
+ type: "Number",
804
+ format: "currency",
805
+ unit: "BRL",
806
+ categories: ["valores"],
807
+ ui: { label: "Taxa de lixo" },
808
+ audit: { origin: "si9:property", source: "values.trashFee (REAIS)" }
809
+ },
810
+ {
811
+ key: "values_disaster_fee",
812
+ type: "Number",
813
+ format: "currency",
814
+ unit: "BRL",
815
+ categories: ["valores"],
816
+ ui: { label: "Taxa de sinistro" },
817
+ audit: { origin: "si9:property", source: "values.disasterFee (REAIS)" }
818
+ },
819
+ // ========================================
820
+ // general_data.*
821
+ // ========================================
822
+ {
823
+ key: "general_data_situation",
824
+ type: "String",
825
+ categories: ["situacoes"],
826
+ ui: { label: "Situa\xE7\xE3o" },
827
+ audit: {
828
+ origin: "si9:property",
829
+ source: "generalData.situation (enum: AVALIABLE, INACTIVE, RENTED, RESERVED, SOLD, INVOICE, TODOCUMENT, DELETE, TERMINATION, RENOVATION)"
830
+ }
831
+ },
832
+ {
833
+ key: "general_data_board",
834
+ type: "String",
835
+ categories: ["comercial"],
836
+ ui: { label: "Placa" },
837
+ audit: { origin: "si9:property", source: "generalData.board" }
838
+ },
839
+ // ========================================
840
+ // website.*
841
+ // ========================================
842
+ {
843
+ key: "website_super_highlight",
844
+ type: "Boolean",
845
+ categories: ["comercial"],
846
+ ui: { label: "Super destaque" },
847
+ audit: { origin: "si9:property", source: "website.superHighlight" }
848
+ },
849
+ {
850
+ key: "website_show_in_core",
851
+ type: "Boolean",
852
+ categories: ["comercial"],
853
+ ui: { label: "Mostrar no portal" },
854
+ audit: { origin: "si9:property", source: "website.showInCore" }
855
+ },
856
+ {
857
+ key: "website_mcmv",
858
+ type: "Boolean",
859
+ categories: ["comercial"],
860
+ ui: { label: "Minha Casa Minha Vida" },
861
+ audit: { origin: "si9:property", source: "website.mcmv" }
862
+ },
863
+ // ========================================
864
+ // destination.*
865
+ // ========================================
866
+ {
867
+ key: "destination_exchange",
868
+ type: "Boolean",
869
+ categories: ["comercial"],
870
+ ui: { label: "Aceita permuta" },
871
+ audit: { origin: "si9:property", source: "destination.exchange" }
872
+ },
873
+ // ========================================
874
+ // location.* (complementar)
875
+ // ========================================
876
+ {
877
+ key: "location_apartment",
878
+ type: "String",
879
+ categories: ["endereco"],
880
+ ui: { label: "Apartamento" },
881
+ audit: { origin: "si9:property", source: "location.apartment" }
882
+ },
883
+ {
884
+ key: "location_block",
885
+ type: "String",
886
+ categories: ["endereco"],
887
+ ui: { label: "Bloco" },
888
+ audit: { origin: "si9:property", source: "location.block" }
889
+ },
890
+ {
891
+ key: "location_floor",
892
+ type: "String",
893
+ categories: ["endereco"],
894
+ ui: { label: "Andar" },
895
+ audit: { origin: "si9:property", source: "location.floor (string: '1', '10', 'Terreo')" }
896
+ },
897
+ {
898
+ key: "location_quatrain",
899
+ type: "String",
900
+ categories: ["endereco"],
901
+ ui: { label: "Quadra" },
902
+ audit: { origin: "si9:property", source: "location.quatrain" }
903
+ },
904
+ {
905
+ key: "location_lot",
906
+ type: "String",
907
+ categories: ["endereco"],
908
+ ui: { label: "Lote" },
909
+ audit: { origin: "si9:property", source: "location.lot" }
910
+ },
911
+ // ========================================
912
+ // dimensions.*
913
+ // ========================================
914
+ {
915
+ key: "dimensions_building_area",
916
+ type: "Number",
917
+ format: "area",
918
+ unit: "m2",
919
+ categories: ["medidas"],
920
+ ui: { label: "\xC1rea constru\xEDda" },
921
+ audit: { origin: "si9:property", source: "dimensions.buildingArea (m\xB2)" }
922
+ },
923
+ {
924
+ key: "dimensions_land_area",
925
+ type: "Number",
926
+ format: "area",
927
+ unit: "m2",
928
+ categories: ["medidas"],
929
+ ui: { label: "\xC1rea do terreno" },
930
+ audit: { origin: "si9:property", source: "dimensions.landArea (m\xB2)" }
931
+ },
932
+ {
933
+ key: "dimensions_internal_area",
934
+ type: "Number",
935
+ format: "area",
936
+ unit: "m2",
937
+ categories: ["medidas"],
938
+ ui: { label: "\xC1rea interna" },
939
+ audit: { origin: "si9:property", source: "dimensions.internalArea (m\xB2)" }
940
+ },
941
+ {
942
+ key: "dimensions_registered_area",
943
+ type: "Number",
944
+ format: "area",
945
+ unit: "m2",
946
+ categories: ["medidas"],
947
+ ui: { label: "\xC1rea registrada" },
948
+ audit: { origin: "si9:property", source: "dimensions.registeredArea (m\xB2)" }
949
+ },
950
+ {
951
+ key: "dimensions_common_area",
952
+ type: "Number",
953
+ format: "area",
954
+ unit: "m2",
955
+ categories: ["medidas"],
956
+ ui: { label: "\xC1rea comum" },
957
+ audit: { origin: "si9:property", source: "dimensions.commonArea (m\xB2)" }
958
+ },
959
+ {
960
+ key: "dimensions_garage_area",
961
+ type: "Number",
962
+ format: "area",
963
+ unit: "m2",
964
+ categories: ["medidas"],
965
+ ui: { label: "\xC1rea da garagem" },
966
+ audit: { origin: "si9:property", source: "dimensions.garageArea (m\xB2)" }
967
+ },
968
+ {
969
+ key: "dimensions_front_size",
970
+ type: "Number",
971
+ format: "distance",
972
+ unit: "m",
973
+ categories: ["medidas"],
974
+ ui: { label: "Frente do terreno" },
975
+ audit: { origin: "si9:property", source: "dimensions.frontSize (metros)" }
976
+ },
977
+ {
978
+ key: "dimensions_funds_size",
979
+ type: "Number",
980
+ format: "distance",
981
+ unit: "m",
982
+ categories: ["medidas"],
983
+ ui: { label: "Fundos do terreno" },
984
+ audit: { origin: "si9:property", source: "dimensions.fundsSize (metros)" }
985
+ },
986
+ {
987
+ key: "dimensions_left_size",
988
+ type: "Number",
989
+ format: "distance",
990
+ unit: "m",
991
+ categories: ["medidas"],
992
+ ui: { label: "Lateral esquerda" },
993
+ audit: { origin: "si9:property", source: "dimensions.leftSize (metros)" }
994
+ },
995
+ {
996
+ key: "dimensions_right_size",
997
+ type: "Number",
998
+ format: "distance",
999
+ unit: "m",
1000
+ categories: ["medidas"],
1001
+ ui: { label: "Lateral direita" },
1002
+ audit: { origin: "si9:property", source: "dimensions.rightSize (metros)" }
1003
+ },
1004
+ // ========================================
1005
+ // features (booleans → array) + features.* (numbers/strings)
1006
+ // ========================================
1007
+ {
1008
+ key: "features",
1009
+ type: "String[]",
1010
+ categories: ["ficha-tecnica"],
1011
+ db: { type: "jsonb", index: "gin" },
1012
+ ui: { label: "Caracter\xEDsticas" },
1013
+ audit: {
1014
+ origin: "si9:property",
1015
+ source: "features booleans true \u2192 labels PT-BR via FEATURES_LABELS (27 booleans)"
1016
+ }
1017
+ },
1018
+ {
1019
+ key: "features_room",
1020
+ type: "Number",
1021
+ categories: ["ficha-tecnica"],
1022
+ ui: { label: "Salas" },
1023
+ audit: { origin: "si9:property", source: "features.room" }
1024
+ },
1025
+ {
1026
+ key: "features_kitchen",
1027
+ type: "Number",
1028
+ categories: ["ficha-tecnica"],
1029
+ ui: { label: "Cozinhas" },
1030
+ audit: { origin: "si9:property", source: "features.kitchen" }
1031
+ },
1032
+ {
1033
+ key: "features_closet",
1034
+ type: "Number",
1035
+ categories: ["ficha-tecnica"],
1036
+ ui: { label: "Closets" },
1037
+ audit: { origin: "si9:property", source: "features.closet" }
1038
+ },
1039
+ {
1040
+ key: "features_balcony",
1041
+ type: "Number",
1042
+ categories: ["ficha-tecnica"],
1043
+ ui: { label: "Sacadas" },
1044
+ audit: { origin: "si9:property", source: "features.balcony" }
1045
+ },
1046
+ {
1047
+ key: "features_ar_condicionado",
1048
+ type: "Number",
1049
+ categories: ["ficha-tecnica"],
1050
+ ui: { label: "Ar condicionado" },
1051
+ audit: { origin: "si9:property", source: "features.arCondicionado (quantidade)" }
1052
+ },
1053
+ {
1054
+ key: "features_toilet",
1055
+ type: "Number",
1056
+ categories: ["ficha-tecnica"],
1057
+ ui: { label: "Lavabos" },
1058
+ audit: { origin: "si9:property", source: "features.toilet (separado de banheiros)" }
1059
+ },
1060
+ {
1061
+ key: "features_floor",
1062
+ type: "String",
1063
+ categories: ["estrutura"],
1064
+ ui: { label: "Tipo de piso" },
1065
+ audit: { origin: "si9:property", source: "features.floor (ex: Porcelanato, Madeira)" }
1066
+ },
1067
+ {
1068
+ key: "features_roof",
1069
+ type: "String",
1070
+ categories: ["estrutura"],
1071
+ ui: { label: "Tipo de telhado" },
1072
+ audit: { origin: "si9:property", source: "features.roof (ex: Cer\xE2mico, Laje)" }
1073
+ },
1074
+ {
1075
+ key: "features_ceiling",
1076
+ type: "String",
1077
+ categories: ["estrutura"],
1078
+ ui: { label: "Tipo de forro" },
1079
+ audit: { origin: "si9:property", source: "features.ceiling (ex: Gesso, PVC, Laje)" }
1080
+ },
1081
+ {
1082
+ key: "features_paving",
1083
+ type: "String",
1084
+ categories: ["estrutura"],
1085
+ ui: { label: "Pavimenta\xE7\xE3o interna" },
1086
+ audit: { origin: "si9:property", source: "features.paving (string livre, dados inconsistentes)" }
1087
+ },
1088
+ {
1089
+ key: "features_pavement",
1090
+ type: "String",
1091
+ categories: ["estrutura"],
1092
+ ui: { label: "Tipo de cal\xE7amento" },
1093
+ audit: { origin: "si9:property", source: "features.pavement (enum: COBBLESTONE, ASPHALT, GRAVEL)" }
1094
+ },
1095
+ // ========================================
1096
+ // security (booleans → array)
1097
+ // ========================================
1098
+ {
1099
+ key: "security",
1100
+ type: "String[]",
1101
+ categories: ["ficha-tecnica"],
1102
+ db: { type: "jsonb", index: "gin" },
1103
+ ui: { label: "Seguran\xE7a" },
1104
+ audit: {
1105
+ origin: "si9:property",
1106
+ source: "security booleans true \u2192 labels PT-BR via SECURITY_LABELS (8 booleans)"
1107
+ }
1108
+ },
1109
+ // ========================================
1110
+ // recreation (booleans → array) + recreation.* (enums)
1111
+ // ========================================
1112
+ {
1113
+ key: "recreation",
1114
+ type: "String[]",
1115
+ categories: ["ficha-tecnica"],
1116
+ db: { type: "jsonb", index: "gin" },
1117
+ ui: { label: "Lazer" },
1118
+ audit: {
1119
+ origin: "si9:property",
1120
+ source: "recreation booleans true \u2192 labels PT-BR via RECREATION_LABELS (9 booleans)"
1121
+ }
1122
+ },
1123
+ {
1124
+ key: "recreation_heating",
1125
+ type: "String",
1126
+ categories: ["estrutura"],
1127
+ ui: { label: "Aquecimento" },
1128
+ audit: { origin: "si9:property", source: "recreation.heating (enum: GAS, SOLAR, ELETRIC)" }
1129
+ },
1130
+ {
1131
+ key: "recreation_position_sun",
1132
+ type: "String",
1133
+ categories: ["estrutura"],
1134
+ ui: { label: "Posi\xE7\xE3o solar" },
1135
+ audit: { origin: "si9:property", source: "recreation.positionSun (enum: NORTH, SOUTH, EAST, WEST)" }
1136
+ },
1137
+ {
1138
+ key: "recreation_position_court",
1139
+ type: "String",
1140
+ categories: ["estrutura"],
1141
+ ui: { label: "Posi\xE7\xE3o na quadra" },
1142
+ audit: { origin: "si9:property", source: "recreation.positionCourt (enum: MIDDLE_COURT, CORNER, SUB_CORNER)" }
1143
+ },
1144
+ // ========================================
1145
+ // rural (booleans → array) + rural.* (numbers/strings)
1146
+ // ========================================
1147
+ {
1148
+ key: "rural",
1149
+ type: "String[]",
1150
+ categories: ["rural"],
1151
+ db: { type: "jsonb", index: "gin" },
1152
+ ui: { label: "Caracter\xEDsticas rurais" },
1153
+ audit: {
1154
+ origin: "si9:property",
1155
+ source: "rural booleans true \u2192 labels PT-BR via RURAL_LABELS (4 booleans: river, mine, hose, footballField)"
1156
+ }
1157
+ },
1158
+ {
1159
+ key: "rural_bushels",
1160
+ type: "Number",
1161
+ categories: ["rural"],
1162
+ ui: { label: "Sacas" },
1163
+ audit: { origin: "si9:property", source: "rural.bushels (capacidade produtiva)" }
1164
+ },
1165
+ {
1166
+ key: "rural_acre",
1167
+ type: "Number",
1168
+ categories: ["rural"],
1169
+ ui: { label: "Alqueires" },
1170
+ audit: { origin: "si9:property", source: "rural.acre" }
1171
+ },
1172
+ {
1173
+ key: "rural_pasture",
1174
+ type: "String",
1175
+ categories: ["rural"],
1176
+ ui: { label: "Pastagem" },
1177
+ audit: { origin: "si9:property", source: "rural.pasture (ex: formado, degradado)" }
1178
+ },
1179
+ {
1180
+ key: "rural_mechanized",
1181
+ type: "String",
1182
+ categories: ["rural"],
1183
+ ui: { label: "Mecanizada" },
1184
+ audit: { origin: "si9:property", source: "rural.mechanized (ex: total, parcial)" }
1185
+ },
1186
+ {
1187
+ key: "rural_reserve",
1188
+ type: "Number",
1189
+ categories: ["rural"],
1190
+ ui: { label: "Reserva" },
1191
+ audit: { origin: "si9:property", source: "rural.reserve (hectares)" }
1192
+ },
1193
+ {
1194
+ key: "rural_pigsty",
1195
+ type: "Number",
1196
+ categories: ["rural"],
1197
+ ui: { label: "Pocilgas" },
1198
+ audit: { origin: "si9:property", source: "rural.pigsty" }
1199
+ },
1200
+ {
1201
+ key: "rural_aviary",
1202
+ type: "Number",
1203
+ categories: ["rural"],
1204
+ ui: { label: "Avi\xE1rios" },
1205
+ audit: { origin: "si9:property", source: "rural.aviary" }
1206
+ },
1207
+ {
1208
+ key: "rural_weir",
1209
+ type: "Number",
1210
+ categories: ["rural"],
1211
+ ui: { label: "A\xE7udes" },
1212
+ audit: { origin: "si9:property", source: "rural.weir" }
1213
+ },
1214
+ {
1215
+ key: "rural_wood_house",
1216
+ type: "Number",
1217
+ categories: ["rural"],
1218
+ ui: { label: "Casas de madeira" },
1219
+ audit: { origin: "si9:property", source: "rural.woodHouse" }
1220
+ },
1221
+ {
1222
+ key: "rural_brick_house",
1223
+ type: "Number",
1224
+ categories: ["rural"],
1225
+ ui: { label: "Casas de alvenaria" },
1226
+ audit: { origin: "si9:property", source: "rural.brickHouse" }
1227
+ },
1228
+ {
1229
+ key: "rural_mixed_house",
1230
+ type: "Number",
1231
+ categories: ["rural"],
1232
+ ui: { label: "Casas mistas" },
1233
+ audit: { origin: "si9:property", source: "rural.mixedHouse" }
1234
+ },
1235
+ // ========================================
1236
+ // fields (campos extras)
1237
+ // ========================================
1238
+ {
1239
+ key: "fields",
1240
+ type: "Json",
1241
+ categories: ["dados-extras"],
1242
+ db: { type: "jsonb" },
1243
+ ui: { label: "Campos extras" },
1244
+ audit: {
1245
+ origin: "si9:property",
1246
+ source: "fields[] ({field, value} \u2014 campos personalizados criados pela imobili\xE1ria)"
1247
+ }
1248
+ }
1249
+ ]
1250
+ };
1251
+
1252
+ // src/schemas/horizon-property-schema-by-si9.zod.ts
1253
+ import { z as z2 } from "zod";
1254
+ import { HorizonPropertySchemaBaseZod } from "@horizon-domains/property-model";
1255
+ var ImageSchema = z2.object({
1256
+ md: z2.string().optional(),
1257
+ sm: z2.string().optional(),
1258
+ full: z2.string().optional(),
1259
+ cover: z2.boolean().optional()
1260
+ }).passthrough();
1261
+ var VideoSchema = z2.object({
1262
+ url: z2.string().optional(),
1263
+ embed_url: z2.string().optional()
1264
+ }).passthrough();
1265
+ var VirtualTourSchema = z2.object({
1266
+ embed_url: z2.string().optional()
1267
+ }).passthrough();
1268
+ var MainImageSchema = z2.object({
1269
+ md: z2.string().optional(),
1270
+ sm: z2.string().optional(),
1271
+ full: z2.string().optional()
1272
+ }).passthrough();
1273
+ var HorizonPropertySchemaBySi9Zod = HorizonPropertySchemaBaseZod.omit({ images: true, videos: true, virtual_tours: true, main_image: true }).extend({
1274
+ // Mídia (override mais flexível)
1275
+ images: z2.array(ImageSchema).optional(),
1276
+ videos: z2.array(VideoSchema).optional(),
1277
+ virtual_tours: z2.array(VirtualTourSchema).optional(),
1278
+ main_image: MainImageSchema.optional(),
1279
+ // ========================================
1280
+ // SI9 EXTRAS — values.*
1281
+ // ========================================
1282
+ values_discount_value: z2.number().min(0).optional(),
1283
+ values_fci_value: z2.number().min(0).optional(),
1284
+ values_assigned_value: z2.number().min(0).optional(),
1285
+ values_trash_fee: z2.number().min(0).optional(),
1286
+ values_disaster_fee: z2.number().min(0).optional(),
1287
+ // ========================================
1288
+ // SI9 EXTRAS — general_data.*
1289
+ // ========================================
1290
+ general_data_situation: z2.string().optional(),
1291
+ general_data_board: z2.string().optional(),
1292
+ general_data_allotment: z2.string().optional(),
1293
+ // ========================================
1294
+ // SI9 EXTRAS — website.*
1295
+ // ========================================
1296
+ website_super_highlight: z2.boolean().optional(),
1297
+ website_show_in_core: z2.boolean().optional(),
1298
+ website_mcmv: z2.boolean().optional(),
1299
+ // ========================================
1300
+ // SI9 EXTRAS — destination.*
1301
+ // ========================================
1302
+ destination_exchange: z2.boolean().optional(),
1303
+ // ========================================
1304
+ // SI9 EXTRAS — location.* (complementar)
1305
+ // ========================================
1306
+ location_apartment: z2.string().optional(),
1307
+ location_block: z2.string().optional(),
1308
+ location_floor: z2.string().optional(),
1309
+ location_quatrain: z2.string().optional(),
1310
+ location_lot: z2.string().optional(),
1311
+ // ========================================
1312
+ // SI9 EXTRAS — dimensions.*
1313
+ // ========================================
1314
+ dimensions_building_area: z2.number().min(0).optional(),
1315
+ dimensions_land_area: z2.number().min(0).optional(),
1316
+ dimensions_internal_area: z2.number().min(0).optional(),
1317
+ dimensions_registered_area: z2.number().min(0).optional(),
1318
+ dimensions_common_area: z2.number().min(0).optional(),
1319
+ dimensions_garage_area: z2.number().min(0).optional(),
1320
+ dimensions_front_size: z2.number().min(0).optional(),
1321
+ dimensions_funds_size: z2.number().min(0).optional(),
1322
+ dimensions_left_size: z2.number().min(0).optional(),
1323
+ dimensions_right_size: z2.number().min(0).optional(),
1324
+ // ========================================
1325
+ // SI9 EXTRAS — features (booleans → array)
1326
+ // ========================================
1327
+ features: z2.array(z2.string()).optional(),
1328
+ // ========================================
1329
+ // SI9 EXTRAS — features.* (numbers/strings)
1330
+ // ========================================
1331
+ features_room: z2.number().optional(),
1332
+ features_kitchen: z2.number().optional(),
1333
+ features_closet: z2.number().optional(),
1334
+ features_balcony: z2.number().optional(),
1335
+ features_ar_condicionado: z2.number().optional(),
1336
+ features_toilet: z2.number().optional(),
1337
+ features_floor: z2.string().optional(),
1338
+ features_roof: z2.string().optional(),
1339
+ features_ceiling: z2.string().optional(),
1340
+ features_paving: z2.string().optional(),
1341
+ features_pavement: z2.string().optional(),
1342
+ // ========================================
1343
+ // SI9 EXTRAS — security (booleans → array)
1344
+ // ========================================
1345
+ security: z2.array(z2.string()).optional(),
1346
+ // ========================================
1347
+ // SI9 EXTRAS — recreation (booleans → array)
1348
+ // ========================================
1349
+ recreation: z2.array(z2.string()).optional(),
1350
+ // ========================================
1351
+ // SI9 EXTRAS — recreation.* (strings/enums)
1352
+ // ========================================
1353
+ recreation_heating: z2.string().optional(),
1354
+ recreation_position_sun: z2.string().optional(),
1355
+ recreation_position_court: z2.string().optional(),
1356
+ // ========================================
1357
+ // SI9 EXTRAS — rural (booleans → array)
1358
+ // ========================================
1359
+ rural: z2.array(z2.string()).optional(),
1360
+ // ========================================
1361
+ // SI9 EXTRAS — rural.* (numbers/strings)
1362
+ // ========================================
1363
+ rural_bushels: z2.number().optional(),
1364
+ rural_acre: z2.number().optional(),
1365
+ rural_pasture: z2.string().optional(),
1366
+ rural_mechanized: z2.string().optional(),
1367
+ rural_reserve: z2.number().optional(),
1368
+ rural_pigsty: z2.number().optional(),
1369
+ rural_aviary: z2.number().optional(),
1370
+ rural_weir: z2.number().optional(),
1371
+ rural_wood_house: z2.number().optional(),
1372
+ rural_brick_house: z2.number().optional(),
1373
+ rural_mixed_house: z2.number().optional(),
1374
+ // ========================================
1375
+ // SI9 EXTRAS — fields (campos extras)
1376
+ // ========================================
1377
+ fields: z2.array(
1378
+ z2.object({
1379
+ field: z2.string(),
1380
+ value: z2.string()
1381
+ })
1382
+ ).optional()
1383
+ }).passthrough();
1384
+ var validateHorizonPropertySchemaBySi9 = (data) => {
1385
+ return HorizonPropertySchemaBySi9Zod.parse(data);
1386
+ };
1387
+ var safeValidateHorizonPropertySchemaBySi9 = (data) => {
1388
+ return HorizonPropertySchemaBySi9Zod.safeParse(data);
1389
+ };
1390
+ export {
1391
+ FEATURES_LABELS,
1392
+ HorizonPropertySchemaBySi9,
1393
+ HorizonPropertySchemaBySi9Zod,
1394
+ PropertyDownloader,
1395
+ RECREATION_LABELS,
1396
+ RURAL_LABELS,
1397
+ SECURITY_LABELS,
1398
+ SI9_DEFAULT_CREDENTIALS,
1399
+ Si9PropertySchemaZod,
1400
+ booleanFieldsToLabels,
1401
+ convertBaseFields,
1402
+ convertExtendedFields,
1403
+ convertSi9PropertyToHorizon,
1404
+ safeValidateHorizonPropertySchemaBySi9,
1405
+ safeValidateSi9PropertySchema,
1406
+ validateHorizonPropertySchemaBySi9,
1407
+ validateSi9PropertySchema
1408
+ };
1409
+ //# sourceMappingURL=index.mjs.map