@bytebrand/export-cars 1.22.20 → 1.22.22

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bytebrand/export-cars",
3
- "version": "1.22.20",
3
+ "version": "1.22.22",
4
4
  "description": "",
5
5
  "author": "bytebrand",
6
6
  "license": "UNLICENSED",
@@ -49,17 +49,17 @@ export class GoogleXmlBuilder extends XMLBuilderBase {
49
49
  </g:${rootTag}>`;
50
50
  }
51
51
 
52
- private getCertificationElement(rootTag: string, data: {
52
+ private getCertificationElement(rootTag: string, data: Array<{
53
53
  certification_authority: string;
54
54
  certification_name: string;
55
55
  certification_code?: string;
56
56
  certification_value?: string;
57
- }): string {
58
- return `<g:${rootTag}>
59
- <g:certification_authority>${data.certification_authority}</g:certification_authority>
60
- <g:certification_name>${data.certification_name}</g:certification_name>
61
- <g:certification_value>${data.certification_value}</g:certification_value>
62
- </g:${rootTag}>`;
57
+ }>): string {
58
+ return data.map(certification => `<g:${rootTag}>
59
+ <g:certification_authority>${certification.certification_authority}</g:certification_authority>
60
+ <g:certification_name>${certification.certification_name}</g:certification_name>
61
+ <g:certification_value>${certification.certification_value}</g:certification_value>
62
+ </g:${rootTag}>`).join('');
63
63
  }
64
64
 
65
65
  private getInstallmentElement(rootTag: string, data: { paybackPeriod: string; monthlyInstallment: string; apr?: string }): string {
@@ -173,27 +173,32 @@ export interface GoogleXmlItem {
173
173
  availability?: string;
174
174
  energy_efficiency_class?: string;
175
175
  installment?: { paybackPeriod: any; monthlyInstallment: any; apr: string };
176
+ vehicle_msrp?: string;
176
177
 
177
178
  vehicle_fulfillment: {
178
179
  option: string;
179
180
  store_code: string;
180
181
  };
181
- certification?: {
182
+ certification?: Array<{
182
183
  certification_authority: string;
183
184
  certification_name: string;
184
185
  certification_code?: string;
185
186
  certification_value?: string;
186
- };
187
+ }>;
187
188
  link_template: string;
188
189
  additional_image_link: string[];
189
190
  model: string;
190
- VIN: string;
191
+ VIN?: string;
191
192
  trim: string;
192
193
  year: number;
193
194
  mileage: string;
194
195
  vehicle_option: string[];
195
- body_style: string,
196
+ body_style?: string,
196
197
  engine: string,
198
+ transmission?: string;
199
+ drive?: string;
200
+ doors?: number;
201
+ capacity?: number;
197
202
  date_first_registered: string,
198
203
  fuel_consumption: string,
199
204
  energy_consumption?: string,
@@ -7,6 +7,7 @@ import { ImboImageResponseInterface } from '@bytebrand/chunkable';
7
7
  import { Injectable } from '@nestjs/common';
8
8
  import { ConfigService, ImagesService, TransformerService, UserService } from '../services';
9
9
  import { GoogleXmlItem } from '../interfaces';
10
+ import { isValidVin } from '../utils';
10
11
  import moment from 'moment';
11
12
 
12
13
  const GOOGLE_MAX_TRIM_LENGTH = 50;
@@ -59,7 +60,7 @@ export class GoogleTransformer {
59
60
  certification: this.getCertification(car),
60
61
  title: this.getTitle(car),
61
62
  product_type: this.getCategory(car),
62
- VIN: this.transformService.vinCode(car),
63
+ VIN: this.getVin(car),
63
64
  id: carId,
64
65
  image_link: allImages[0],
65
66
  additional_image_link: allImages.slice(1, 10),
@@ -68,6 +69,7 @@ export class GoogleTransformer {
68
69
  link_template: `${this.getLink(car)}?store={store_code}`,
69
70
  condition: this.mapState(car),
70
71
  price: this.getPrice(car),
72
+ vehicle_msrp: this.getVehicleMsrp(car),
71
73
  // certified_pre-owned: '',
72
74
  brand: this.transformService.make(car),
73
75
  model: this.transformService.model(car),
@@ -78,6 +80,10 @@ export class GoogleTransformer {
78
80
  vehicle_option: this.getExtrasField(car),
79
81
  body_style: this.mapBodyStyle(car),
80
82
  engine: this.mapFuel(car),
83
+ transmission: this.mapTransmission(car),
84
+ drive: this.mapDrive(car),
85
+ doors: this.getDoors(car),
86
+ capacity: this.getCapacity(car),
81
87
  date_first_registered: this.getDateFirstRegistered(car),
82
88
  fuel_consumption: `${this.transformService.fuelConsumption(car)}l/100km`,
83
89
  energy_consumption: this.getEnergyConsumption(car),
@@ -85,7 +91,7 @@ export class GoogleTransformer {
85
91
  fuel_consumption_discharged_battery: this.getFuelConsumptionDischargedBattery(car),
86
92
  co2_emissions: `${this.transformService.cO2Emission(car)}g/km`,
87
93
  description: this.processText((await this.getDescription(car))),
88
- identifier_exists: 'no',
94
+ identifier_exists: 'yes',
89
95
  availability: 'in_stock',
90
96
  // custom_label_0: '',
91
97
  // custom_label_1: '',
@@ -110,6 +116,12 @@ export class GoogleTransformer {
110
116
  'electric_range',
111
117
  'fuel_consumption_discharged_battery',
112
118
  'installment',
119
+ 'body_style',
120
+ 'vehicle_msrp',
121
+ 'transmission',
122
+ 'drive',
123
+ 'doors',
124
+ 'capacity',
113
125
  ];
114
126
  for (const key of optionalKeys) {
115
127
  if (!item[key]) {
@@ -124,12 +136,14 @@ export class GoogleTransformer {
124
136
  if (images.length === 0)
125
137
  return false
126
138
 
127
- if (!this.mapBodyStyle(car))
128
- return false
129
-
130
139
  return true
131
140
  }
132
141
 
142
+ private getVin(car: DataSchemes.Car): string {
143
+ const vin = get(car, 'metaData.vin', '');
144
+ return isValidVin(vin) ? vin : get(car, 'marketing.fakeVIN', '');
145
+ }
146
+
133
147
  private getTitle(car: DataSchemes.Car): string {
134
148
  const model = toString(get(car, 'mainData.model', ''));
135
149
  const brand = trim(this.getBrand(car));
@@ -200,12 +214,13 @@ export class GoogleTransformer {
200
214
  }
201
215
  }
202
216
 
203
- private getRetailPrice(car: DataSchemes.Car): string {
204
- const retailPrice = get(car, 'recommendedRetailPrice', '');
205
- if (retailPrice) {
206
- return `${round(retailPrice, 2)} EUR`;
217
+ private getVehicleMsrp(car: DataSchemes.Car): string | undefined {
218
+ const retailPrice = get(car, 'recommendedRetailPrice');
219
+ const salesPrice = get(car, 'price.currentSalesPrice', 0);
220
+ if (!retailPrice || round(retailPrice, 2) === round(salesPrice, 2)) {
221
+ return undefined;
207
222
  }
208
- return this.getPrice(car);
223
+ return `${round(retailPrice, 2)} EUR`;
209
224
  }
210
225
 
211
226
  private getPrice(car: DataSchemes.Car): string {
@@ -241,7 +256,7 @@ export class GoogleTransformer {
241
256
  return get(available, availability);
242
257
  }
243
258
 
244
- private getEnergyClass(car: DataSchemes.Car): string {
259
+ private mapEnergyClass(selector: string): string {
245
260
  const available = {
246
261
  selector_energyEfficiencyClass_a: 'A',
247
262
  selector_energyEfficiencyClass_ap: 'A+',
@@ -255,21 +270,42 @@ export class GoogleTransformer {
255
270
  selector_energyEfficiencyClass_g: 'G',
256
271
  selector_unknown: 'default',
257
272
  };
258
- const energyClass = get(car, 'environmentEmissions.energyEfficiencyClass', 'selector_unknown');
259
- const value = get(available, energyClass);
273
+ const value = get(available, selector);
260
274
  return value ? value.replace(/\+/g, '') : '';
261
275
  }
262
276
 
277
+ private getEnergyClass(car: DataSchemes.Car): string {
278
+ return this.mapEnergyClass(get(car, 'environmentEmissions.energyEfficiencyClass', 'selector_unknown'));
279
+ }
280
+
281
+ private getDischargedEnergyClass(car: DataSchemes.Car): string {
282
+ return this.mapEnergyClass(get(car, 'environmentEmissions.wltpDischargedEnergyEfficiencyClass', 'selector_unknown'));
283
+ }
284
+
263
285
  private getCertification(car: DataSchemes.Car) {
264
- const value = this.getEnergyClass(car);
265
- if (!value || value === 'default') {
266
- return undefined;
286
+ const certifications: Array<{ certification_authority: string; certification_name: string; certification_value: string }> = [];
287
+
288
+ const co2Class = this.getEnergyClass(car);
289
+ if (co2Class && co2Class !== 'default') {
290
+ certifications.push({
291
+ certification_authority: 'BMWK',
292
+ certification_name: 'Vehicle_CO2_Class',
293
+ certification_value: co2Class,
294
+ });
267
295
  }
268
- return {
269
- certification_authority: 'BMWK',
270
- certification_name: 'Vehicle_CO2_Class',
271
- certification_value: value,
272
- };
296
+
297
+ if (get(car, 'engineData.hybridPlugin')) {
298
+ const dischargedClass = this.getDischargedEnergyClass(car);
299
+ if (dischargedClass && dischargedClass !== 'default') {
300
+ certifications.push({
301
+ certification_authority: 'BMWK',
302
+ certification_name: 'Vehicle_CO2_Class_Discharged_Battery',
303
+ certification_value: dischargedClass,
304
+ });
305
+ }
306
+ }
307
+
308
+ return certifications.length ? certifications : undefined;
273
309
  }
274
310
 
275
311
  private getEnergyConsumption(car: DataSchemes.Car): string | undefined {
@@ -366,6 +402,32 @@ export class GoogleTransformer {
366
402
  return get(available, category);
367
403
  }
368
404
 
405
+ private mapTransmission(car: DataSchemes.Car): string | undefined {
406
+ const available = {
407
+ selector_gearbox_manualShift: 'MANUAL',
408
+ selector_gearbox_semiAutomatic: 'AUTOMATIC',
409
+ selector_gearbox_automatic: 'AUTOMATIC',
410
+ };
411
+ return get(available, get(car, 'driveSuspension.gearbox'));
412
+ }
413
+
414
+ private mapDrive(car: DataSchemes.Car): string | undefined {
415
+ const available = {
416
+ selector_driveType_frontWheelDrive: 'FWD',
417
+ selector_driveType_rearWheelDrive: 'RWD',
418
+ selector_driveType_allWheelDrive: 'AWD',
419
+ };
420
+ return get(available, get(car, 'driveSuspension.driveType'));
421
+ }
422
+
423
+ private getDoors(car: DataSchemes.Car): number | undefined {
424
+ return toSafeInteger(get(car, 'mainData.doors')) || undefined;
425
+ }
426
+
427
+ private getCapacity(car: DataSchemes.Car): number | undefined {
428
+ return toSafeInteger(get(car, 'engineData.cubicCapacity')) || undefined;
429
+ }
430
+
369
431
  private getExtrasField(car: DataSchemes.Car): string[] {
370
432
  const booleanFields: string[] = [
371
433
  'engineData.startStopSystem',
@@ -479,10 +541,9 @@ export class GoogleTransformer {
479
541
  }
480
542
 
481
543
  private getYear(car: DataSchemes.Car) {
482
- const firstModelsProductionDate = +get(car, 'mainData.firstModelsProductionDate', 0);
483
-
484
- const date = new Date(firstModelsProductionDate);
485
- const firstModelsProductionYear = date.getFullYear();
544
+ // mainData.firstModelsProductionDate is already a 4-digit year (mobile.de "vintage"),
545
+ // not a timestamp — do not run it through `new Date(...)`.
546
+ const firstModelsProductionYear = toSafeInteger(get(car, 'mainData.firstModelsProductionDate', 0));
486
547
 
487
548
  return firstModelsProductionYear > TIMESTAMP_FIRST_YEAR ? firstModelsProductionYear : null
488
549
  }