@bytebrand/export-cars 1.22.21 → 1.22.23

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.21",
3
+ "version": "1.22.23",
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,17 +173,18 @@ 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;
@@ -194,6 +195,10 @@ export interface GoogleXmlItem {
194
195
  vehicle_option: string[];
195
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,
@@ -69,6 +69,7 @@ export class GoogleTransformer {
69
69
  link_template: `${this.getLink(car)}?store={store_code}`,
70
70
  condition: this.mapState(car),
71
71
  price: this.getPrice(car),
72
+ vehicle_msrp: this.getVehicleMsrp(car),
72
73
  // certified_pre-owned: '',
73
74
  brand: this.transformService.make(car),
74
75
  model: this.transformService.model(car),
@@ -79,6 +80,10 @@ export class GoogleTransformer {
79
80
  vehicle_option: this.getExtrasField(car),
80
81
  body_style: this.mapBodyStyle(car),
81
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),
82
87
  date_first_registered: this.getDateFirstRegistered(car),
83
88
  fuel_consumption: `${this.transformService.fuelConsumption(car)}l/100km`,
84
89
  energy_consumption: this.getEnergyConsumption(car),
@@ -86,7 +91,7 @@ export class GoogleTransformer {
86
91
  fuel_consumption_discharged_battery: this.getFuelConsumptionDischargedBattery(car),
87
92
  co2_emissions: `${this.transformService.cO2Emission(car)}g/km`,
88
93
  description: this.processText((await this.getDescription(car))),
89
- identifier_exists: 'no',
94
+ identifier_exists: 'yes',
90
95
  availability: 'in_stock',
91
96
  // custom_label_0: '',
92
97
  // custom_label_1: '',
@@ -111,8 +116,12 @@ export class GoogleTransformer {
111
116
  'electric_range',
112
117
  'fuel_consumption_discharged_battery',
113
118
  'installment',
114
- 'VIN',
115
119
  'body_style',
120
+ 'vehicle_msrp',
121
+ 'transmission',
122
+ 'drive',
123
+ 'doors',
124
+ 'capacity',
116
125
  ];
117
126
  for (const key of optionalKeys) {
118
127
  if (!item[key]) {
@@ -130,9 +139,9 @@ export class GoogleTransformer {
130
139
  return true
131
140
  }
132
141
 
133
- private getVin(car: DataSchemes.Car): string | undefined {
142
+ private getVin(car: DataSchemes.Car): string {
134
143
  const vin = get(car, 'metaData.vin', '');
135
- return isValidVin(vin) ? vin : undefined;
144
+ return isValidVin(vin) ? vin : get(car, 'marketing.fakeVIN', '');
136
145
  }
137
146
 
138
147
  private getTitle(car: DataSchemes.Car): string {
@@ -205,12 +214,13 @@ export class GoogleTransformer {
205
214
  }
206
215
  }
207
216
 
208
- private getRetailPrice(car: DataSchemes.Car): string {
209
- const retailPrice = get(car, 'recommendedRetailPrice', '');
210
- if (retailPrice) {
211
- return `${round(retailPrice, 2)} EUR`;
217
+ private getVehicleMsrp(car: DataSchemes.Car): string | undefined {
218
+ const retailPrice = get(car, 'price.recomendedRetailPrice');
219
+ const salesPrice = get(car, 'price.currentSalesPrice', 0);
220
+ if (!retailPrice || round(retailPrice, 2) === round(salesPrice, 2)) {
221
+ return undefined;
212
222
  }
213
- return this.getPrice(car);
223
+ return `${round(retailPrice, 2)} EUR`;
214
224
  }
215
225
 
216
226
  private getPrice(car: DataSchemes.Car): string {
@@ -246,7 +256,7 @@ export class GoogleTransformer {
246
256
  return get(available, availability);
247
257
  }
248
258
 
249
- private getEnergyClass(car: DataSchemes.Car): string {
259
+ private mapEnergyClass(selector: string): string {
250
260
  const available = {
251
261
  selector_energyEfficiencyClass_a: 'A',
252
262
  selector_energyEfficiencyClass_ap: 'A+',
@@ -260,21 +270,42 @@ export class GoogleTransformer {
260
270
  selector_energyEfficiencyClass_g: 'G',
261
271
  selector_unknown: 'default',
262
272
  };
263
- const energyClass = get(car, 'environmentEmissions.energyEfficiencyClass', 'selector_unknown');
264
- const value = get(available, energyClass);
273
+ const value = get(available, selector);
265
274
  return value ? value.replace(/\+/g, '') : '';
266
275
  }
267
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
+
268
285
  private getCertification(car: DataSchemes.Car) {
269
- const value = this.getEnergyClass(car);
270
- if (!value || value === 'default') {
271
- 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
+ });
272
295
  }
273
- return {
274
- certification_authority: 'BMWK',
275
- certification_name: 'Vehicle_CO2_Class',
276
- certification_value: value,
277
- };
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;
278
309
  }
279
310
 
280
311
  private getEnergyConsumption(car: DataSchemes.Car): string | undefined {
@@ -371,6 +402,32 @@ export class GoogleTransformer {
371
402
  return get(available, category);
372
403
  }
373
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
+
374
431
  private getExtrasField(car: DataSchemes.Car): string[] {
375
432
  const booleanFields: string[] = [
376
433
  'engineData.startStopSystem',
@@ -483,13 +540,19 @@ export class GoogleTransformer {
483
540
  return !mileage ? 10 : mileage;
484
541
  }
485
542
 
486
- private getYear(car: DataSchemes.Car) {
487
- const firstModelsProductionDate = +get(car, 'mainData.firstModelsProductionDate', 0);
488
-
489
- const date = new Date(firstModelsProductionDate);
490
- const firstModelsProductionYear = date.getFullYear();
543
+ private getYear(car: DataSchemes.Car): number {
544
+ // mainData.firstModelsProductionDate/constructionYear are already 4-digit years
545
+ // (mobile.de "vintage"); firstRegistration/metaData.creationDate are unix timestamps
546
+ // in seconds. Not every car has firstModelsProductionDate (e.g. dealer FTP imports),
547
+ // so fall through to other date sources — `year` is a mandatory Google feed field.
548
+ const firstModelsProductionYear = toSafeInteger(get(car, 'mainData.firstModelsProductionDate', 0));
549
+ const constructionYear = toSafeInteger(get(car, 'mainData.constructionYear', 0));
550
+ const firstRegistrationYear = toSafeInteger(moment(+get(car, 'mainData.firstRegistration', 0) * 1000).format('YYYY'));
551
+ const metaCreationYear = toSafeInteger(moment(+get(car, 'metaData.creationDate', 0) * 1000).format('YYYY'));
552
+ const currentYear = toSafeInteger(moment().format('YYYY'));
491
553
 
492
- return firstModelsProductionYear > TIMESTAMP_FIRST_YEAR ? firstModelsProductionYear : null
554
+ return [firstModelsProductionYear, constructionYear, firstRegistrationYear, metaCreationYear, currentYear]
555
+ .find(year => year > TIMESTAMP_FIRST_YEAR);
493
556
  }
494
557
 
495
558
  private getDateFirstRegistered(car: DataSchemes.Car): string {