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