@bytebrand/export-cars 1.21.7 → 1.21.9

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.
Files changed (156) hide show
  1. package/.editorconfig +8 -8
  2. package/.eslintignore +1 -1
  3. package/.eslintrc.js +24 -24
  4. package/.prettierrc.js +7 -7
  5. package/README.md +65 -65
  6. package/dist/configs/index.d.ts +1 -0
  7. package/dist/configs/index.js +1 -0
  8. package/dist/configs/index.js.map +1 -1
  9. package/dist/configs/santander.config.d.ts +5 -0
  10. package/dist/configs/santander.config.js +20 -0
  11. package/dist/configs/santander.config.js.map +1 -0
  12. package/dist/enums/export-type.enum.d.ts +1 -0
  13. package/dist/enums/export-type.enum.js +1 -0
  14. package/dist/enums/export-type.enum.js.map +1 -1
  15. package/dist/export.command.js +1 -1
  16. package/dist/export.command.js.map +1 -1
  17. package/dist/export.schema.d.ts +6 -2
  18. package/dist/export.schema.js +4 -0
  19. package/dist/export.schema.js.map +1 -1
  20. package/dist/exporters/index.d.ts +1 -0
  21. package/dist/exporters/index.js +1 -0
  22. package/dist/exporters/index.js.map +1 -1
  23. package/dist/exporters/santander.exporter.d.ts +12 -0
  24. package/dist/exporters/santander.exporter.js +63 -0
  25. package/dist/exporters/santander.exporter.js.map +1 -0
  26. package/dist/formats/auto24-xml.builder.js +2 -2
  27. package/dist/formats/google-xml.builder.js +3 -3
  28. package/dist/formats/modix-xml.builder.js +2 -2
  29. package/dist/formats/ooyyo-xml.builder.js +1 -1
  30. package/dist/formats/santander-csv.builder.d.ts +6 -0
  31. package/dist/formats/santander-csv.builder.js +17 -0
  32. package/dist/formats/santander-csv.builder.js.map +1 -0
  33. package/dist/tsconfig.build.tsbuildinfo +1 -1
  34. package/nest-cli.json +5 -5
  35. package/package.json +61 -61
  36. package/src/app.module.ts +38 -38
  37. package/src/configs/alleskralle.config.ts +15 -15
  38. package/src/configs/as24.config.ts +13 -13
  39. package/src/configs/auto24.config.ts +15 -15
  40. package/src/configs/autoboerse.config.ts +16 -16
  41. package/src/configs/autouncle.config.ts +14 -14
  42. package/src/configs/awin.config.ts +15 -15
  43. package/src/configs/by-owner.config.ts +14 -14
  44. package/src/configs/config.ts +83 -83
  45. package/src/configs/criteo.config.ts +17 -17
  46. package/src/configs/dasparking.config.ts +15 -15
  47. package/src/configs/eauto.config.ts +15 -15
  48. package/src/configs/evium.config.ts +18 -18
  49. package/src/configs/facebook-shop.config.ts +16 -16
  50. package/src/configs/fb.config.ts +16 -16
  51. package/src/configs/google.config.ts +14 -14
  52. package/src/configs/index.ts +22 -21
  53. package/src/configs/kleinanzeigeFocus.config.ts +14 -14
  54. package/src/configs/marktde.config.ts +18 -18
  55. package/src/configs/meinstadt.config.ts +15 -15
  56. package/src/configs/mitula.config.ts +14 -14
  57. package/src/configs/ooyyo.config.ts +15 -15
  58. package/src/configs/santander.config.ts +17 -0
  59. package/src/configs/trovit.config.ts +14 -14
  60. package/src/configs/yescar.config.ts +17 -17
  61. package/src/constants/autouncle.contants.ts +4 -4
  62. package/src/constants/fb.constants.ts +7 -7
  63. package/src/constants/index.ts +1 -1
  64. package/src/constants/modix/body-color-name.constant.ts +20 -20
  65. package/src/constants/modix/category.constant.ts +14 -14
  66. package/src/constants/modix/fuel.constant.ts +14 -14
  67. package/src/constants/modix/index.ts +7 -7
  68. package/src/constants/modix/manufacturers.constant.ts +7498 -7498
  69. package/src/constants/modix/transmission.constant.ts +8 -8
  70. package/src/constants/modix/usage-type.constant.ts +13 -13
  71. package/src/constants/modix/vehicle-class.constant.ts +17 -17
  72. package/src/enums/environment.enum.ts +5 -5
  73. package/src/enums/export-type.enum.ts +33 -32
  74. package/src/enums/file-types.enum.ts +8 -8
  75. package/src/enums/image-type.enum.ts +4 -4
  76. package/src/enums/index.ts +4 -4
  77. package/src/export.command.ts +37 -37
  78. package/src/export.schema.ts +134 -128
  79. package/src/exporters/as24.exporter.ts +69 -69
  80. package/src/exporters/auto24.exporter.ts +48 -48
  81. package/src/exporters/autouncle.exporter.ts +60 -60
  82. package/src/exporters/awin.exporter.ts +47 -47
  83. package/src/exporters/by-owner.exporter.ts +44 -44
  84. package/src/exporters/facebook-shop.exporter.ts +51 -51
  85. package/src/exporters/fb.exporter.ts +52 -52
  86. package/src/exporters/google.exporter.ts +45 -45
  87. package/src/exporters/index.ts +15 -14
  88. package/src/exporters/meinstadt.exporter.ts +55 -55
  89. package/src/exporters/mobile.exporter.ts +64 -64
  90. package/src/exporters/modix.exporter.ts +56 -56
  91. package/src/exporters/ooyyo.exporter.ts +48 -48
  92. package/src/exporters/rubrikk.exporter.ts +53 -53
  93. package/src/exporters/santander.exporter.ts +50 -0
  94. package/src/exporters/trovit.exporter.ts +51 -51
  95. package/src/formats/auto24-xml.builder.ts +54 -54
  96. package/src/formats/autouncle-json.builder.ts +20 -20
  97. package/src/formats/awin-csv.builder.ts +16 -16
  98. package/src/formats/facebook-shop.builder.ts +69 -69
  99. package/src/formats/fb-xml.builder.ts +71 -71
  100. package/src/formats/google-xml.builder.ts +35 -35
  101. package/src/formats/index.ts +5 -5
  102. package/src/formats/meinstadt-json.builder.ts +19 -19
  103. package/src/formats/modix-xml.builder.ts +180 -180
  104. package/src/formats/ooyyo-xml.builder.ts +48 -48
  105. package/src/formats/rubrikk-xml.builder.ts +27 -27
  106. package/src/formats/santander-csv.builder.ts +15 -0
  107. package/src/formats/trovit-xml.builder.ts +44 -44
  108. package/src/interfaces/atributes-wrap-item.intreface.ts +6 -6
  109. package/src/interfaces/dealers-mapping.interface.ts +7 -7
  110. package/src/interfaces/description.interface.ts +24 -24
  111. package/src/interfaces/export-item.interface.ts +366 -366
  112. package/src/interfaces/exporter.interface.ts +3 -3
  113. package/src/interfaces/index.ts +3 -3
  114. package/src/interfaces/input.interface.ts +6 -6
  115. package/src/interfaces/mapping-rules.interface.ts +11 -11
  116. package/src/interfaces/modix/index.ts +1 -1
  117. package/src/interfaces/modix/modix-item-list.interface.ts +5 -5
  118. package/src/interfaces/params.interface.ts +13 -13
  119. package/src/interfaces/rule-parser.interface.ts +3 -3
  120. package/src/main.ts +14 -14
  121. package/src/rules/abstract.rule.ts +37 -37
  122. package/src/rules/date.rule.ts +19 -19
  123. package/src/rules/number.rule.ts +15 -15
  124. package/src/rules/string.rule.ts +8 -8
  125. package/src/services/car.service.ts +49 -49
  126. package/src/services/config.service.ts +15 -15
  127. package/src/services/images.service.ts +207 -207
  128. package/src/services/index.ts +9 -9
  129. package/src/services/loader.service.ts +29 -29
  130. package/src/services/mapping.service.ts +91 -91
  131. package/src/services/request.service.ts +44 -44
  132. package/src/services/rules.service.ts +61 -61
  133. package/src/services/transformer.service.ts +129 -129
  134. package/src/services/user.service.ts +57 -57
  135. package/src/transformers/as24.transformer.ts +253 -253
  136. package/src/transformers/auto24.transfomer.ts +155 -155
  137. package/src/transformers/autouncle.transformer.ts +236 -236
  138. package/src/transformers/awin.transformer.ts +193 -193
  139. package/src/transformers/by-owner.transformer.ts +354 -354
  140. package/src/transformers/facebook-shop.transformer.ts +93 -93
  141. package/src/transformers/fb-description.transformer.ts +316 -316
  142. package/src/transformers/fb-equipment.transformer.ts +161 -161
  143. package/src/transformers/fb.transformer.ts +655 -655
  144. package/src/transformers/google.transformer.ts +169 -169
  145. package/src/transformers/index.ts +5 -5
  146. package/src/transformers/meinstadt.transformer.ts +92 -92
  147. package/src/transformers/mobile.transformer.ts +482 -482
  148. package/src/transformers/modix.transformer.ts +227 -227
  149. package/src/transformers/ooyyo.transformer.ts +109 -109
  150. package/src/transformers/rubrikk.transformer.ts +200 -200
  151. package/src/transformers/trovit.transformer.ts +63 -63
  152. package/src/utils/converter.utils.ts +15 -15
  153. package/src/utils/index.ts +2 -2
  154. package/src/utils/validator.utils.ts +7 -7
  155. package/tsconfig.build.json +4 -4
  156. package/tsconfig.json +29 -29
@@ -1,482 +1,482 @@
1
- import { get, isEmpty, isNil, set, toString, upperFirst } from 'lodash';
2
-
3
- import { Injectable } from '@nestjs/common';
4
-
5
- import { CarDescriptionFactory, DescriptionTypeEnum } from '@bytebrand/car-description';
6
- import { ImboImageResponseInterface } from '@bytebrand/chunkable';
7
- import { LoggerService } from '@bytebrand/cronicle-helper';
8
- import { DataSchemes } from '@bytebrand/car-schema';
9
-
10
- import { MappingService, UserService, ImagesService } from '../services';
11
- import { MappingRules } from '../interfaces/mapping-rules.interface';
12
- import { isNotEmptyArray } from '../utils';
13
- import { ConfigService } from '../services';
14
- import { ExportType } from '../enums';
15
-
16
- const vinGenerator = require('vin-generator');
17
-
18
- @Injectable()
19
- export class MobileTransformer {
20
- private currentCar: DataSchemes.Car;
21
-
22
- constructor(
23
- private mappingService: MappingService,
24
- private userService: UserService,
25
- private configService: ConfigService,
26
- private imagesService: ImagesService,
27
- ) { }
28
-
29
- async transform(carData: DataSchemes.Car, images: ImboImageResponseInterface[]): Promise<unknown> {
30
- this.currentCar = carData;
31
- const transformedCarData = {};
32
- const rules = await this.mappingService.getRules();
33
- for (const rule of rules) {
34
- if (rule.custom === 'yes') {
35
- const transformedField = await this.customTransformers(rule, carData, images);
36
- if (transformedField) {
37
- set(transformedCarData, `${rule.importField}`, transformedField);
38
- }
39
- } else {
40
- const fieldValue: any = get(carData, `${rule.autodeField}`, null);
41
- if (rule.autodeField === 'driveSuspension.gearbox' || rule.autodeField === 'mainData.mileage') {
42
- set(transformedCarData, `${rule.importField}`, await this.customTransformers(rule, carData, images));
43
- } else {
44
- if (!isNil(fieldValue)) {
45
- const transformedField = await this.transformField(fieldValue, rule);
46
-
47
- if (!isNil(transformedField)) {
48
- set(transformedCarData, `${rule.importField}`, transformedField);
49
- }
50
- }
51
- }
52
- }
53
- }
54
-
55
- set(transformedCarData, 150, toString(get(carData, 'rate.monthlyInstallment', '')));
56
- set(transformedCarData, 293, toString(get(carData, 'leasing.monthlyInstallment', '')));
57
- set(transformedCarData, 1, get(carData, 'metaData.vehicleId', null));
58
-
59
- if (this.configService.config.campaign === 'eauto' && carData.offer.ftpFolder === 'auto_de_eautos') {
60
- set(transformedCarData, 12, get(carData, 'offer.internalNumber', null));
61
- }
62
-
63
- return transformedCarData;
64
- }
65
-
66
- private async transformField(fieldValue: any, rule: MappingRules): Promise<string | void> {
67
- const transformProcedureName = `${rule.autodeFieldType}To${upperFirst(rule.importFieldType)}`;
68
-
69
- switch (transformProcedureName) {
70
- case 'stringToString': {
71
- if (rule.autodeField === 'offer.offerDescription') {
72
- return await this.offerStringToString();
73
- }
74
- return this.stringToString(fieldValue);
75
- }
76
- case 'integerToString': {
77
- return this.numberToString(fieldValue);
78
- }
79
- case 'numberToString': {
80
- return this.numberToString(fieldValue);
81
- }
82
- case 'booleanToString': {
83
- return this.booleanToString(fieldValue);
84
- }
85
- case 'selectorToSelector': {
86
- return this.selectorToSelector(fieldValue, rule);
87
- }
88
- case 'selectorToString': {
89
- return this.selectorToString(fieldValue, rule);
90
- }
91
- case 'selector[]ToSelector': {
92
- return this.selectorArrayToSelector(fieldValue, rule);
93
- }
94
- case 'selector[]ToString': {
95
- return this.selectorArrayToString(fieldValue, rule);
96
- }
97
- case 'dateToString': {
98
- return this.dateToString(fieldValue, rule);
99
- }
100
- case 'dateToMobile_delivery': {
101
- return this.dateToString(fieldValue, rule);
102
- }
103
- case 'stringToMobile_model': {
104
- return this.stringToMobile_model(fieldValue);
105
- }
106
- case 'numberToMobile_consumption': {
107
- return this.numberToMobile_consumption(fieldValue);
108
- }
109
- case 'booleanToMobile_vatCalc': {
110
- return this.booleanToMobile_vatCalc(fieldValue);
111
- }
112
- case 'selectorToMobile_extColor_selector': {
113
- return this.selectorToSelector(fieldValue, rule);
114
- }
115
- case 'selectorToCategoryOrClass_selector': {
116
- return this.selectorToCategoryOrClass_selector(fieldValue);
117
- }
118
- default:
119
- LoggerService.log(`missing transform: ${transformProcedureName}`);
120
- break;
121
- }
122
- }
123
-
124
- private async offerStringToString(): Promise<string> {
125
- const dealerId = get(this.currentCar, 'metaData.ownerId');
126
- const descriptionSchema = await this.userService.getDefaultSummaryByDealerId(dealerId);
127
- const descriptionBuilder = CarDescriptionFactory.create(DescriptionTypeEnum.Mobile, this.currentCar);
128
- return descriptionBuilder.transform(descriptionSchema);
129
- }
130
-
131
- private async customTransformers(rule: MappingRules, car: DataSchemes.Car, images: ImboImageResponseInterface[]): Promise<unknown> {
132
- switch (rule.autodeField) {
133
- case 'image': {
134
- return this.getImages(car, images);
135
- }
136
- case 'autodeUrl': {
137
- return this.getAutodeUrl(car);
138
- }
139
- case 'persistentId': {
140
- return this.getPersistentId(car);
141
- }
142
- case 'price.vatDeductible': {
143
- return this.getVatDeductible(car);
144
- }
145
- case 'mainData.numberOfPreviousOwners': {
146
- return this.getNumberOfPreviousOwners(car);
147
- }
148
- case 'mainData.condition': {
149
- return this.getUsageType(car);
150
- }
151
- case 'price.vatRate': {
152
- return this.getVatRate(car);
153
- }
154
- case 'price.currentSalesPrice': {
155
- return this.getCurrentSalesPrice(car);
156
- }
157
- case 'driveSuspension.gearbox': {
158
- return this.getGearBox(car);
159
- }
160
- case 'mainData.mileage': {
161
- return this.getMilage(car);
162
- }
163
- case 'consumption.consumptionPowerCombined': {
164
- return this.getWLTP(car);
165
- }
166
- case 'environmentEmissions.emissionClass': {
167
- return this.getEmissionClass(car);
168
- }
169
- case 'environmentEmissions.energyEfficiencyClass': {
170
- return this.getEnergyEfficiencyClass(car);
171
- }
172
- case 'environmentEmissions.emissionSticker': {
173
- return this.getEmissionSticker(car);
174
- }
175
- case 'environmentEmissions.co2': {
176
- return this.getCO2(car);
177
- }
178
- case 'metaData.vin': {
179
- return this.getVinCode(car);
180
- }
181
- default:
182
- return '';
183
- }
184
- }
185
-
186
- private async getPersistentId(car: DataSchemes.Car) {
187
- switch (this.configService.config.campaign) {
188
- case ExportType.EAUTO:
189
- if (car.offer.ftpFolder === 'auto_de_eautos') {
190
- const dealers = await this.mappingService.getDealersMapping();
191
- const dealerId = dealers.find(dealer => dealer.firebaseUserId === car.metaData.ownerId)?.dealerId;
192
- if (dealerId) {
193
- return dealerId;
194
- }
195
- }
196
- return '416';
197
- case ExportType.MARKTDE:
198
- return '04109003';
199
- case ExportType.EVIUM:
200
- return '10022';
201
- default:
202
- throw new Error(`Wrong export type ${this.configService.config.campaign}`);
203
- }
204
- }
205
-
206
- private getVatDeductible(car: DataSchemes.Car): string {
207
- return this.booleanToString(!get(car, 'price.vatDeductible', false));
208
- }
209
-
210
- private getWLTP(car: DataSchemes.Car): string | void {
211
- const envkvCompliant = get(car, 'consumption.envkvCompliant');
212
- const hybridPlagin = get(car, 'engineData.hybridPlugin');
213
-
214
- if (envkvCompliant && hybridPlagin) {
215
- const powerCombined = get(car, 'consumption.consumptionPowerCombined');
216
- return String(powerCombined);
217
- }
218
- }
219
-
220
- private getEmissionClass(car: DataSchemes.Car): string | void {
221
- const emissionClass = get(car, 'environmentEmissions.emissionClass', 'selector_unknown');
222
- switch (emissionClass) {
223
- case 'selector_emissionClass_euro1': {
224
- return '1';
225
- }
226
- case 'selector_emissionClass_euro2': {
227
- return '2';
228
- }
229
- case 'selector_emissionClass_euro3': {
230
- return '3';
231
- }
232
- case 'selector_emissionClass_euro4':
233
- case 'selector_unknown': {
234
- return '4';
235
- }
236
- case 'selector_emissionClass_euro5': {
237
- return '5';
238
- }
239
- case 'selector_emissionClass_euro6': {
240
- return '6';
241
- }
242
- case 'selector_emissionClass_euro6b': {
243
- return '6b';
244
- }
245
- case 'selector_emissionClass_euro6c': {
246
- return '6c';
247
- }
248
- case 'selector_emissionClass_euro6d': {
249
- return '6d';
250
- }
251
- case 'selector_emissionClass_euro6d_temp': {
252
- return '6d-TEMP';
253
- }
254
- }
255
- }
256
-
257
- private getEnergyEfficiencyClass(car: DataSchemes.Car): string {
258
- const energyEfficiencyClass = get(car, 'environmentEmissions.energyEfficiencyClass', 'selector_unknown');
259
- switch (energyEfficiencyClass) {
260
- case 'selector_energyEfficiencyClass_appp': {
261
- return 'A+++';
262
- }
263
- case 'selector_energyEfficiencyClass_app': {
264
- return 'A++';
265
- }
266
- case 'selector_energyEfficiencyClass_ap': {
267
- return 'A+';
268
- }
269
- case 'selector_unknown':
270
- case null: {
271
- return 'C';
272
- }
273
- default: {
274
- return energyEfficiencyClass.slice(-1).toUpperCase();
275
- }
276
- }
277
- }
278
-
279
- private getEmissionSticker(car: DataSchemes.Car): string | void {
280
- const emissionSticker = get(car, 'environmentEmissions.emissionSticker', 'selector_unknown');
281
- switch (emissionSticker) {
282
- case 'selector_emissionSticker_no': {
283
- return '1';
284
- }
285
- case 'selector_emissionSticker_red': {
286
- return '2';
287
- }
288
- case 'selector_emissionSticker_yellow':
289
- case 'selector_unknown': {
290
- return '3';
291
- }
292
- case 'selector_emissionSticker_green': {
293
- return '4';
294
- }
295
- }
296
- }
297
-
298
- private getCO2(car: DataSchemes.Car): string {
299
- const consumptionFuel = get(car, 'consumption.fuel', 'selector_unknown');
300
- if (consumptionFuel === 'selector_fuel_electrical') {
301
- return '0';
302
- }
303
- return String(get(car, 'environmentEmissions.co2'));
304
- }
305
-
306
- private getNumberOfPreviousOwners(car: DataSchemes.Car): string {
307
- const usageType = this.getUsageType(car);
308
- if (usageType === '1' || !get(car, 'mainData.numberOfPreviousOwners')) {
309
- return '0';
310
- }
311
- return String(get(car, 'mainData.numberOfPreviousOwners'));
312
- }
313
-
314
- private getUsageType(car: DataSchemes.Car): string {
315
- const firstRegistration = get(car, 'mainData.firstRegistration');
316
- if (firstRegistration) {
317
- return '0';
318
- }
319
- return '1';
320
- }
321
-
322
- private getVatRate(car: DataSchemes.Car): string {
323
- const isVatDeductible = get(car, 'price.vatDeductible', false);
324
- if (isVatDeductible) {
325
- return String(car.price.vatRate || 19);
326
- }
327
- return '0';
328
- }
329
-
330
- private getCurrentSalesPrice(car: DataSchemes.Car): string {
331
- const vat = get(car, 'price.vatDeductible', false);
332
- const currentSalesPrice = get(car, 'price.currentSalesPrice');
333
- if (vat) {
334
- const vatRate = get(car, 'price.vatRate', 19);
335
- const price = (currentSalesPrice * 100) / (100 + vatRate);
336
- return String(price.toFixed(2));
337
- }
338
- return String(currentSalesPrice);
339
- }
340
-
341
- private getVinCode(car: DataSchemes.Car): string {
342
- const vin = get(car, 'metaData.vin', '');
343
-
344
- if (this.configService.config.campaign === 'marktde' && !vin) {
345
- const fakeVin = get(car, 'marketing.fakeVIN', '');
346
- if (!isEmpty(fakeVin)) {
347
- return fakeVin;
348
- }
349
- return vinGenerator.generateVin();
350
- }
351
-
352
- return vin;
353
- }
354
-
355
- private getAutodeUrl(car: DataSchemes.Car): string {
356
- return `https://www.${this.configService.config.domain}${car._id}?source=classifieds&medium=listing&campaign=${this.configService.config.campaign}`;
357
- }
358
-
359
- private async getImages(car: DataSchemes.Car, images: ImboImageResponseInterface[]): Promise<string | null> {
360
- const transformedImages = await this.imagesService.getImages(car, images, this.configService.config.imageType, false);
361
-
362
- if (this.configService.config.campaign == ExportType.EVIUM) return null;
363
-
364
- if (isNotEmptyArray(transformedImages)) {
365
- return transformedImages.map((el: string) => el).join(';');
366
- }
367
- return null;
368
- }
369
-
370
- private stringToString(fieldValue: string): string {
371
- return isEmpty(fieldValue) ? null : fieldValue;
372
- }
373
-
374
- private numberToString(fieldValue: number): string {
375
- return String(fieldValue);
376
- }
377
-
378
- private booleanToString(fieldValue: boolean): string {
379
- return String(Number(fieldValue));
380
- }
381
-
382
- private selectorToSelector(fieldValue: string, rule: MappingRules): string | void {
383
- if (fieldValue === 'selector_unknown' && rule.autodeField === 'heatingCooling.climatisation') {
384
- return '';
385
- }
386
-
387
- if (fieldValue === 'selector_unknown' || fieldValue === 'selector_interiorColor_rainbow') {
388
- return '';
389
- }
390
- const pairs = rule.selectors.map(p => p.split(':').reverse());
391
- const [match] = pairs.filter(p => p[0] === fieldValue);
392
- // consider adding values not mapped in table
393
- if (match) {
394
- return match[1];
395
- }
396
- }
397
-
398
- private selectorToString(fieldValue: string, rule: MappingRules): string {
399
- return this.booleanToString(fieldValue === rule.autodeFieldSelector);
400
- }
401
-
402
- private selectorArrayToSelector(fieldValue: string[], rule: MappingRules): string | void {
403
- const lastVal = fieldValue[fieldValue.length - 1];
404
- const match = this.selectorToSelector(lastVal, rule);
405
-
406
- if (match && match !== 'default') {
407
- return match;
408
- }
409
- }
410
-
411
- private selectorArrayToString(fieldValue: string[], rule: MappingRules): string {
412
- const match = fieldValue.includes(rule.autodeFieldSelector);
413
- return this.booleanToString(match);
414
- }
415
-
416
- private dateToString(fieldValue: number, rule: MappingRules): string {
417
- const date = new Date(fieldValue * 1000);
418
- if (["['6']", "['7']", "['8']"].includes(rule.importField)) {
419
- return `${date.getMonth() + 1}.${date.getFullYear()}`;
420
- }
421
- return `${date.getDate()}.${date.getMonth() + 1}.${date.getFullYear()}`;
422
- }
423
-
424
- private stringToMobile_model(fieldValue: string): string {
425
- const model = fieldValue;
426
- const subModel = get(this.currentCar, 'mainData.subModel');
427
- return subModel && subModel !== model ? `${model} ${subModel}` : model;
428
- }
429
-
430
- private numberToMobile_consumption(fieldValue: number): string {
431
- const consumption = this.numberToString(fieldValue);
432
- return consumption.replace('.', ',');
433
- }
434
-
435
- private booleanToMobile_vatCalc(fieldValue: boolean): string {
436
- return this.booleanToString(fieldValue);
437
- }
438
-
439
- private selectorToCategoryOrClass_selector(fieldValue: string): string {
440
- const rules: { [key: string]: string } = {
441
- selector_category_convertible: 'Cabrio',
442
- selector_category_combi: 'Kombi',
443
- selector_category_sedan: 'Limousine',
444
- selector_category_suv: 'OffRoad',
445
- selector_category_compact: 'SmallCar',
446
- selector_category_coupe: 'SportsCar',
447
- selector_category_van: 'Van',
448
- selector_category_minibus: 'Van',
449
- selector_category_transporter: 'OtherCar',
450
- selector_unknown: 'OtherCar',
451
- };
452
-
453
- const found = rules[fieldValue];
454
- if (found) {
455
- return `Car.${found}`;
456
- } else {
457
- LoggerService.log(`Missing mapping for: ${fieldValue}`);
458
- return 'Car.OtherCar';
459
- }
460
- }
461
-
462
- private getGearBox(car: DataSchemes.Car): unknown {
463
- const rules: { [x: string]: string } = {
464
- selector_gearbox_manualShift: '1',
465
- selector_gearbox_semiAutomatic: '2',
466
- selector_gearbox_automatic: '3',
467
- selector_unknown: '0',
468
- };
469
- const autodeGearboxSelector = get(car, 'driveSuspension.gearbox', 'selector_unknown');
470
-
471
- return rules[autodeGearboxSelector];
472
- }
473
-
474
- private getMilage(car: DataSchemes.Car): string {
475
- // TODO:: fix if cbd
476
- const mileage = car.mainData.mileage;
477
- if (mileage <= 0) {
478
- return '0';
479
- }
480
- return mileage.toString();
481
- }
482
- }
1
+ import { get, isEmpty, isNil, set, toString, upperFirst } from 'lodash';
2
+
3
+ import { Injectable } from '@nestjs/common';
4
+
5
+ import { CarDescriptionFactory, DescriptionTypeEnum } from '@bytebrand/car-description';
6
+ import { ImboImageResponseInterface } from '@bytebrand/chunkable';
7
+ import { LoggerService } from '@bytebrand/cronicle-helper';
8
+ import { DataSchemes } from '@bytebrand/car-schema';
9
+
10
+ import { MappingService, UserService, ImagesService } from '../services';
11
+ import { MappingRules } from '../interfaces/mapping-rules.interface';
12
+ import { isNotEmptyArray } from '../utils';
13
+ import { ConfigService } from '../services';
14
+ import { ExportType } from '../enums';
15
+
16
+ const vinGenerator = require('vin-generator');
17
+
18
+ @Injectable()
19
+ export class MobileTransformer {
20
+ private currentCar: DataSchemes.Car;
21
+
22
+ constructor(
23
+ private mappingService: MappingService,
24
+ private userService: UserService,
25
+ private configService: ConfigService,
26
+ private imagesService: ImagesService,
27
+ ) { }
28
+
29
+ async transform(carData: DataSchemes.Car, images: ImboImageResponseInterface[]): Promise<unknown> {
30
+ this.currentCar = carData;
31
+ const transformedCarData = {};
32
+ const rules = await this.mappingService.getRules();
33
+ for (const rule of rules) {
34
+ if (rule.custom === 'yes') {
35
+ const transformedField = await this.customTransformers(rule, carData, images);
36
+ if (transformedField) {
37
+ set(transformedCarData, `${rule.importField}`, transformedField);
38
+ }
39
+ } else {
40
+ const fieldValue: any = get(carData, `${rule.autodeField}`, null);
41
+ if (rule.autodeField === 'driveSuspension.gearbox' || rule.autodeField === 'mainData.mileage') {
42
+ set(transformedCarData, `${rule.importField}`, await this.customTransformers(rule, carData, images));
43
+ } else {
44
+ if (!isNil(fieldValue)) {
45
+ const transformedField = await this.transformField(fieldValue, rule);
46
+
47
+ if (!isNil(transformedField)) {
48
+ set(transformedCarData, `${rule.importField}`, transformedField);
49
+ }
50
+ }
51
+ }
52
+ }
53
+ }
54
+
55
+ set(transformedCarData, 150, toString(get(carData, 'rate.monthlyInstallment', '')));
56
+ set(transformedCarData, 293, toString(get(carData, 'leasing.monthlyInstallment', '')));
57
+ set(transformedCarData, 1, get(carData, 'metaData.vehicleId', null));
58
+
59
+ if (this.configService.config.campaign === 'eauto' && carData.offer.ftpFolder === 'auto_de_eautos') {
60
+ set(transformedCarData, 12, get(carData, 'offer.internalNumber', null));
61
+ }
62
+
63
+ return transformedCarData;
64
+ }
65
+
66
+ private async transformField(fieldValue: any, rule: MappingRules): Promise<string | void> {
67
+ const transformProcedureName = `${rule.autodeFieldType}To${upperFirst(rule.importFieldType)}`;
68
+
69
+ switch (transformProcedureName) {
70
+ case 'stringToString': {
71
+ if (rule.autodeField === 'offer.offerDescription') {
72
+ return await this.offerStringToString();
73
+ }
74
+ return this.stringToString(fieldValue);
75
+ }
76
+ case 'integerToString': {
77
+ return this.numberToString(fieldValue);
78
+ }
79
+ case 'numberToString': {
80
+ return this.numberToString(fieldValue);
81
+ }
82
+ case 'booleanToString': {
83
+ return this.booleanToString(fieldValue);
84
+ }
85
+ case 'selectorToSelector': {
86
+ return this.selectorToSelector(fieldValue, rule);
87
+ }
88
+ case 'selectorToString': {
89
+ return this.selectorToString(fieldValue, rule);
90
+ }
91
+ case 'selector[]ToSelector': {
92
+ return this.selectorArrayToSelector(fieldValue, rule);
93
+ }
94
+ case 'selector[]ToString': {
95
+ return this.selectorArrayToString(fieldValue, rule);
96
+ }
97
+ case 'dateToString': {
98
+ return this.dateToString(fieldValue, rule);
99
+ }
100
+ case 'dateToMobile_delivery': {
101
+ return this.dateToString(fieldValue, rule);
102
+ }
103
+ case 'stringToMobile_model': {
104
+ return this.stringToMobile_model(fieldValue);
105
+ }
106
+ case 'numberToMobile_consumption': {
107
+ return this.numberToMobile_consumption(fieldValue);
108
+ }
109
+ case 'booleanToMobile_vatCalc': {
110
+ return this.booleanToMobile_vatCalc(fieldValue);
111
+ }
112
+ case 'selectorToMobile_extColor_selector': {
113
+ return this.selectorToSelector(fieldValue, rule);
114
+ }
115
+ case 'selectorToCategoryOrClass_selector': {
116
+ return this.selectorToCategoryOrClass_selector(fieldValue);
117
+ }
118
+ default:
119
+ LoggerService.log(`missing transform: ${transformProcedureName}`);
120
+ break;
121
+ }
122
+ }
123
+
124
+ private async offerStringToString(): Promise<string> {
125
+ const dealerId = get(this.currentCar, 'metaData.ownerId');
126
+ const descriptionSchema = await this.userService.getDefaultSummaryByDealerId(dealerId);
127
+ const descriptionBuilder = CarDescriptionFactory.create(DescriptionTypeEnum.Mobile, this.currentCar);
128
+ return descriptionBuilder.transform(descriptionSchema);
129
+ }
130
+
131
+ private async customTransformers(rule: MappingRules, car: DataSchemes.Car, images: ImboImageResponseInterface[]): Promise<unknown> {
132
+ switch (rule.autodeField) {
133
+ case 'image': {
134
+ return this.getImages(car, images);
135
+ }
136
+ case 'autodeUrl': {
137
+ return this.getAutodeUrl(car);
138
+ }
139
+ case 'persistentId': {
140
+ return this.getPersistentId(car);
141
+ }
142
+ case 'price.vatDeductible': {
143
+ return this.getVatDeductible(car);
144
+ }
145
+ case 'mainData.numberOfPreviousOwners': {
146
+ return this.getNumberOfPreviousOwners(car);
147
+ }
148
+ case 'mainData.condition': {
149
+ return this.getUsageType(car);
150
+ }
151
+ case 'price.vatRate': {
152
+ return this.getVatRate(car);
153
+ }
154
+ case 'price.currentSalesPrice': {
155
+ return this.getCurrentSalesPrice(car);
156
+ }
157
+ case 'driveSuspension.gearbox': {
158
+ return this.getGearBox(car);
159
+ }
160
+ case 'mainData.mileage': {
161
+ return this.getMilage(car);
162
+ }
163
+ case 'consumption.consumptionPowerCombined': {
164
+ return this.getWLTP(car);
165
+ }
166
+ case 'environmentEmissions.emissionClass': {
167
+ return this.getEmissionClass(car);
168
+ }
169
+ case 'environmentEmissions.energyEfficiencyClass': {
170
+ return this.getEnergyEfficiencyClass(car);
171
+ }
172
+ case 'environmentEmissions.emissionSticker': {
173
+ return this.getEmissionSticker(car);
174
+ }
175
+ case 'environmentEmissions.co2': {
176
+ return this.getCO2(car);
177
+ }
178
+ case 'metaData.vin': {
179
+ return this.getVinCode(car);
180
+ }
181
+ default:
182
+ return '';
183
+ }
184
+ }
185
+
186
+ private async getPersistentId(car: DataSchemes.Car) {
187
+ switch (this.configService.config.campaign) {
188
+ case ExportType.EAUTO:
189
+ if (car.offer.ftpFolder === 'auto_de_eautos') {
190
+ const dealers = await this.mappingService.getDealersMapping();
191
+ const dealerId = dealers.find(dealer => dealer.firebaseUserId === car.metaData.ownerId)?.dealerId;
192
+ if (dealerId) {
193
+ return dealerId;
194
+ }
195
+ }
196
+ return '416';
197
+ case ExportType.MARKTDE:
198
+ return '04109003';
199
+ case ExportType.EVIUM:
200
+ return '10022';
201
+ default:
202
+ throw new Error(`Wrong export type ${this.configService.config.campaign}`);
203
+ }
204
+ }
205
+
206
+ private getVatDeductible(car: DataSchemes.Car): string {
207
+ return this.booleanToString(!get(car, 'price.vatDeductible', false));
208
+ }
209
+
210
+ private getWLTP(car: DataSchemes.Car): string | void {
211
+ const envkvCompliant = get(car, 'consumption.envkvCompliant');
212
+ const hybridPlagin = get(car, 'engineData.hybridPlugin');
213
+
214
+ if (envkvCompliant && hybridPlagin) {
215
+ const powerCombined = get(car, 'consumption.consumptionPowerCombined');
216
+ return String(powerCombined);
217
+ }
218
+ }
219
+
220
+ private getEmissionClass(car: DataSchemes.Car): string | void {
221
+ const emissionClass = get(car, 'environmentEmissions.emissionClass', 'selector_unknown');
222
+ switch (emissionClass) {
223
+ case 'selector_emissionClass_euro1': {
224
+ return '1';
225
+ }
226
+ case 'selector_emissionClass_euro2': {
227
+ return '2';
228
+ }
229
+ case 'selector_emissionClass_euro3': {
230
+ return '3';
231
+ }
232
+ case 'selector_emissionClass_euro4':
233
+ case 'selector_unknown': {
234
+ return '4';
235
+ }
236
+ case 'selector_emissionClass_euro5': {
237
+ return '5';
238
+ }
239
+ case 'selector_emissionClass_euro6': {
240
+ return '6';
241
+ }
242
+ case 'selector_emissionClass_euro6b': {
243
+ return '6b';
244
+ }
245
+ case 'selector_emissionClass_euro6c': {
246
+ return '6c';
247
+ }
248
+ case 'selector_emissionClass_euro6d': {
249
+ return '6d';
250
+ }
251
+ case 'selector_emissionClass_euro6d_temp': {
252
+ return '6d-TEMP';
253
+ }
254
+ }
255
+ }
256
+
257
+ private getEnergyEfficiencyClass(car: DataSchemes.Car): string {
258
+ const energyEfficiencyClass = get(car, 'environmentEmissions.energyEfficiencyClass', 'selector_unknown');
259
+ switch (energyEfficiencyClass) {
260
+ case 'selector_energyEfficiencyClass_appp': {
261
+ return 'A+++';
262
+ }
263
+ case 'selector_energyEfficiencyClass_app': {
264
+ return 'A++';
265
+ }
266
+ case 'selector_energyEfficiencyClass_ap': {
267
+ return 'A+';
268
+ }
269
+ case 'selector_unknown':
270
+ case null: {
271
+ return 'C';
272
+ }
273
+ default: {
274
+ return energyEfficiencyClass.slice(-1).toUpperCase();
275
+ }
276
+ }
277
+ }
278
+
279
+ private getEmissionSticker(car: DataSchemes.Car): string | void {
280
+ const emissionSticker = get(car, 'environmentEmissions.emissionSticker', 'selector_unknown');
281
+ switch (emissionSticker) {
282
+ case 'selector_emissionSticker_no': {
283
+ return '1';
284
+ }
285
+ case 'selector_emissionSticker_red': {
286
+ return '2';
287
+ }
288
+ case 'selector_emissionSticker_yellow':
289
+ case 'selector_unknown': {
290
+ return '3';
291
+ }
292
+ case 'selector_emissionSticker_green': {
293
+ return '4';
294
+ }
295
+ }
296
+ }
297
+
298
+ private getCO2(car: DataSchemes.Car): string {
299
+ const consumptionFuel = get(car, 'consumption.fuel', 'selector_unknown');
300
+ if (consumptionFuel === 'selector_fuel_electrical') {
301
+ return '0';
302
+ }
303
+ return String(get(car, 'environmentEmissions.co2'));
304
+ }
305
+
306
+ private getNumberOfPreviousOwners(car: DataSchemes.Car): string {
307
+ const usageType = this.getUsageType(car);
308
+ if (usageType === '1' || !get(car, 'mainData.numberOfPreviousOwners')) {
309
+ return '0';
310
+ }
311
+ return String(get(car, 'mainData.numberOfPreviousOwners'));
312
+ }
313
+
314
+ private getUsageType(car: DataSchemes.Car): string {
315
+ const firstRegistration = get(car, 'mainData.firstRegistration');
316
+ if (firstRegistration) {
317
+ return '0';
318
+ }
319
+ return '1';
320
+ }
321
+
322
+ private getVatRate(car: DataSchemes.Car): string {
323
+ const isVatDeductible = get(car, 'price.vatDeductible', false);
324
+ if (isVatDeductible) {
325
+ return String(car.price.vatRate || 19);
326
+ }
327
+ return '0';
328
+ }
329
+
330
+ private getCurrentSalesPrice(car: DataSchemes.Car): string {
331
+ const vat = get(car, 'price.vatDeductible', false);
332
+ const currentSalesPrice = get(car, 'price.currentSalesPrice');
333
+ if (vat) {
334
+ const vatRate = get(car, 'price.vatRate', 19);
335
+ const price = (currentSalesPrice * 100) / (100 + vatRate);
336
+ return String(price.toFixed(2));
337
+ }
338
+ return String(currentSalesPrice);
339
+ }
340
+
341
+ private getVinCode(car: DataSchemes.Car): string {
342
+ const vin = get(car, 'metaData.vin', '');
343
+
344
+ if (this.configService.config.campaign === 'marktde' && !vin) {
345
+ const fakeVin = get(car, 'marketing.fakeVIN', '');
346
+ if (!isEmpty(fakeVin)) {
347
+ return fakeVin;
348
+ }
349
+ return vinGenerator.generateVin();
350
+ }
351
+
352
+ return vin;
353
+ }
354
+
355
+ private getAutodeUrl(car: DataSchemes.Car): string {
356
+ return `https://www.${this.configService.config.domain}${car._id}?source=classifieds&medium=listing&campaign=${this.configService.config.campaign}`;
357
+ }
358
+
359
+ private async getImages(car: DataSchemes.Car, images: ImboImageResponseInterface[]): Promise<string | null> {
360
+ const transformedImages = await this.imagesService.getImages(car, images, this.configService.config.imageType, false);
361
+
362
+ if (this.configService.config.campaign == ExportType.EVIUM) return null;
363
+
364
+ if (isNotEmptyArray(transformedImages)) {
365
+ return transformedImages.map((el: string) => el).join(';');
366
+ }
367
+ return null;
368
+ }
369
+
370
+ private stringToString(fieldValue: string): string {
371
+ return isEmpty(fieldValue) ? null : fieldValue;
372
+ }
373
+
374
+ private numberToString(fieldValue: number): string {
375
+ return String(fieldValue);
376
+ }
377
+
378
+ private booleanToString(fieldValue: boolean): string {
379
+ return String(Number(fieldValue));
380
+ }
381
+
382
+ private selectorToSelector(fieldValue: string, rule: MappingRules): string | void {
383
+ if (fieldValue === 'selector_unknown' && rule.autodeField === 'heatingCooling.climatisation') {
384
+ return '';
385
+ }
386
+
387
+ if (fieldValue === 'selector_unknown' || fieldValue === 'selector_interiorColor_rainbow') {
388
+ return '';
389
+ }
390
+ const pairs = rule.selectors.map(p => p.split(':').reverse());
391
+ const [match] = pairs.filter(p => p[0] === fieldValue);
392
+ // consider adding values not mapped in table
393
+ if (match) {
394
+ return match[1];
395
+ }
396
+ }
397
+
398
+ private selectorToString(fieldValue: string, rule: MappingRules): string {
399
+ return this.booleanToString(fieldValue === rule.autodeFieldSelector);
400
+ }
401
+
402
+ private selectorArrayToSelector(fieldValue: string[], rule: MappingRules): string | void {
403
+ const lastVal = fieldValue[fieldValue.length - 1];
404
+ const match = this.selectorToSelector(lastVal, rule);
405
+
406
+ if (match && match !== 'default') {
407
+ return match;
408
+ }
409
+ }
410
+
411
+ private selectorArrayToString(fieldValue: string[], rule: MappingRules): string {
412
+ const match = fieldValue.includes(rule.autodeFieldSelector);
413
+ return this.booleanToString(match);
414
+ }
415
+
416
+ private dateToString(fieldValue: number, rule: MappingRules): string {
417
+ const date = new Date(fieldValue * 1000);
418
+ if (["['6']", "['7']", "['8']"].includes(rule.importField)) {
419
+ return `${date.getMonth() + 1}.${date.getFullYear()}`;
420
+ }
421
+ return `${date.getDate()}.${date.getMonth() + 1}.${date.getFullYear()}`;
422
+ }
423
+
424
+ private stringToMobile_model(fieldValue: string): string {
425
+ const model = fieldValue;
426
+ const subModel = get(this.currentCar, 'mainData.subModel');
427
+ return subModel && subModel !== model ? `${model} ${subModel}` : model;
428
+ }
429
+
430
+ private numberToMobile_consumption(fieldValue: number): string {
431
+ const consumption = this.numberToString(fieldValue);
432
+ return consumption.replace('.', ',');
433
+ }
434
+
435
+ private booleanToMobile_vatCalc(fieldValue: boolean): string {
436
+ return this.booleanToString(fieldValue);
437
+ }
438
+
439
+ private selectorToCategoryOrClass_selector(fieldValue: string): string {
440
+ const rules: { [key: string]: string } = {
441
+ selector_category_convertible: 'Cabrio',
442
+ selector_category_combi: 'Kombi',
443
+ selector_category_sedan: 'Limousine',
444
+ selector_category_suv: 'OffRoad',
445
+ selector_category_compact: 'SmallCar',
446
+ selector_category_coupe: 'SportsCar',
447
+ selector_category_van: 'Van',
448
+ selector_category_minibus: 'Van',
449
+ selector_category_transporter: 'OtherCar',
450
+ selector_unknown: 'OtherCar',
451
+ };
452
+
453
+ const found = rules[fieldValue];
454
+ if (found) {
455
+ return `Car.${found}`;
456
+ } else {
457
+ LoggerService.log(`Missing mapping for: ${fieldValue}`);
458
+ return 'Car.OtherCar';
459
+ }
460
+ }
461
+
462
+ private getGearBox(car: DataSchemes.Car): unknown {
463
+ const rules: { [x: string]: string } = {
464
+ selector_gearbox_manualShift: '1',
465
+ selector_gearbox_semiAutomatic: '2',
466
+ selector_gearbox_automatic: '3',
467
+ selector_unknown: '0',
468
+ };
469
+ const autodeGearboxSelector = get(car, 'driveSuspension.gearbox', 'selector_unknown');
470
+
471
+ return rules[autodeGearboxSelector];
472
+ }
473
+
474
+ private getMilage(car: DataSchemes.Car): string {
475
+ // TODO:: fix if cbd
476
+ const mileage = car.mainData.mileage;
477
+ if (mileage <= 0) {
478
+ return '0';
479
+ }
480
+ return mileage.toString();
481
+ }
482
+ }