@bytebrand/export-cars 1.22.30 → 1.23.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bytebrand/export-cars",
3
- "version": "1.22.30",
3
+ "version": "1.23.0",
4
4
  "description": "",
5
5
  "author": "bytebrand",
6
6
  "license": "UNLICENSED",
@@ -19,6 +19,7 @@
19
19
  "@bytebrand/car-schema": "^3.14.15",
20
20
  "@bytebrand/chunkable": "^5.19.8",
21
21
  "@bytebrand/cronicle-helper": "^1.21.8",
22
+ "@bytebrand/fe-financing": "^5.14.10",
22
23
  "@bytebrand/i18n-dictionaries": "^1.5.0",
23
24
  "@bytebrand/import-helper": "^0.45.0",
24
25
  "@bytebrand/schema-tools": "^3.18.8",
@@ -6,4 +6,17 @@ export const DEFAULT_CITY = 'Sandersdorf-Brehna';
6
6
  export const DEFAULT_ADDR1 = 'Max-Planck-Str. 19';
7
7
  export const TIMESTAMP_FIRST_YEAR = 1970;
8
8
  export const META_PUSH_PRIORITY_STANDARD = 'standard';
9
- export const META_PUSH_PRIORITY_ALLOWED_VALUES = ['user_priority_1', 'user_priority_2'];
9
+ export const META_PUSH_PRIORITY_ALLOWED_VALUES = ['dealer_priority_1', 'dealer_priority_2'];
10
+
11
+ export enum OfferType {
12
+ financing = 'financing',
13
+ leasing = 'leasing',
14
+ buy = 'buy',
15
+ }
16
+
17
+ export const OFFER_DISCLAIMER = 'Für Ihre Finanzierungswünsche stellen wir eine Bonitätsanfrage. Bonität ' +
18
+ 'vorausgesetzt, ist dies ein repräsentatives Berechnungsbeispiel gem. der Angaben, welches 2/3 aller Kunden, ' +
19
+ 'im Sinne des § 17a Abs. 4 PAngV, erhalten. Dieses freibleibende Angebot der Santander Consumer Bank AG, ' +
20
+ 'Santander-Platz 1, 41061 Mönchengladbach wird vermittelt durch die auto.de GmbH, Max-Planck-Str. 19, 06796 ' +
21
+ 'Sandersdorf-Brehna, die als ungebundener Vermittler nicht beratend tätig ist. Irrtümer vorbehalten. Preise ' +
22
+ 'ggf. inkl. MwSt.';
@@ -36,10 +36,17 @@ export class FacebookShopExporter implements Exporter {
36
36
  const data = chunk.data[i];
37
37
 
38
38
  // console.time(`Transform ${data.car._id}`);
39
- const transformedCar = await facebookShopTransformer.transform(data);
39
+ const items = await facebookShopTransformer.transform(data);
40
40
  // console.timeEnd(`Transform ${data.car._id}`);
41
41
 
42
- await facebookShopXmkBuilder.addItem(transformedCar);
42
+ if (!items.length) {
43
+ console.log(`Skip car ${data.car._id} because it has no real images`);
44
+ continue;
45
+ }
46
+
47
+ for (const item of items) {
48
+ await facebookShopXmkBuilder.addItem(item);
49
+ }
43
50
  this.carService.counter.countCar(data.images);
44
51
  }
45
52
  });
@@ -1,7 +1,6 @@
1
+ import { escape, isString } from 'lodash';
1
2
  import { XMLBuilderBase } from '@bytebrand/cronicle-helper';
2
3
  import { FacebookShopItem, FacebookShopTag } from '../interfaces';
3
- import { Description, DescriptionValue } from '../interfaces/description.interface';
4
- import { isNotEmptyArray } from '../utils/validator.utils';
5
4
 
6
5
  export class FacebookShopXmlBuilder extends XMLBuilderBase {
7
6
  async buildStart(): Promise<void> {
@@ -18,11 +17,7 @@ export class FacebookShopXmlBuilder extends XMLBuilderBase {
18
17
  const tags = Object.keys(car);
19
18
 
20
19
  for (const tag of tags) {
21
- if (tag === 'description') {
22
- item += this.getDescriptionElement(tag, car[tag].value);
23
- } else {
24
- item += this.buildTagElement(tag, car[tag]);
25
- }
20
+ item += this.buildTagElement(tag, car[tag]);
26
21
  }
27
22
  item += '</item>';
28
23
 
@@ -30,36 +25,11 @@ export class FacebookShopXmlBuilder extends XMLBuilderBase {
30
25
  }
31
26
 
32
27
  buildTagElement(tag: string, params: FacebookShopTag) {
28
+ const value = isString(params.value) ? escape(params.value) : params.value;
33
29
  if (params.isGoogle) {
34
- return this.getElement(`g:${tag}`, params.value);
30
+ return this.getElement(`g:${tag}`, value);
35
31
  }
36
- return this.getElement(tag, params.value);
37
- }
38
- private getDescriptionElement(rootTag: string, descriptionBlocks: Description[]): string {
39
- const description: string = descriptionBlocks
40
- .map(({ title, values }: Description) => {
41
- if (values) {
42
- return `${title}\n${this.buildDescriptionValues(values)} `;
43
- }
44
- return '';
45
- })
46
- .join('\n');
47
- return `<${rootTag}>${description}</${rootTag}>`;
48
- }
49
-
50
- private buildDescriptionValues(values: DescriptionValue[]): string {
51
- const validValues = [];
52
-
53
- values.forEach(element => {
54
- const filteredValues = element.value.filter(el => el);
55
- if (isNotEmptyArray(filteredValues)) validValues.push(element);
56
- });
57
-
58
- return validValues
59
- .map(({ title, value }: DescriptionValue) => {
60
- return `${title}: ${value.join(', ')}`;
61
- })
62
- .join(';\n');
32
+ return this.getElement(tag, value);
63
33
  }
64
34
 
65
35
  async buildEnd(): Promise<void> {
@@ -7,7 +7,7 @@ export interface DealersMapping {
7
7
  }
8
8
 
9
9
  export interface MetaPushPriorityMapping {
10
- user_id: string;
10
+ dealer_id: string;
11
11
  meta_push_priority: string;
12
12
  comment?: string;
13
13
  }
@@ -1,7 +1,6 @@
1
1
  import { DataSchemes } from '@bytebrand/car-schema';
2
2
 
3
3
  import { IAtributeWrapItem } from './atributes-wrap-item.intreface';
4
- import { Description } from './description.interface';
5
4
 
6
5
  export interface FbXmlItem {
7
6
  url: string;
@@ -348,15 +347,32 @@ export interface dealerItem {
348
347
 
349
348
  export interface FacebookShopItem {
350
349
  id: FacebookShopTag;
350
+ vehicle_id: FacebookShopTag;
351
351
  title: FacebookShopTag;
352
- description: { value: Description[]; isGoogle: boolean };
352
+ description: FacebookShopTag;
353
353
  availability: FacebookShopTag;
354
354
  condition: FacebookShopTag;
355
355
  price: FacebookShopTag;
356
356
  link: FacebookShopTag;
357
357
  image_link: FacebookShopTag;
358
358
  additional_image_link: FacebookShopTag;
359
- brand: FacebookShopTag;
359
+ make: FacebookShopTag;
360
+ model: FacebookShopTag;
361
+ year: FacebookShopTag;
362
+
363
+ user_id: FacebookShopTag;
364
+ custom_label_4: FacebookShopTag;
365
+ dealer_privacy_policy_url: FacebookShopTag;
366
+
367
+ offer_type: FacebookShopTag;
368
+ offer_disclaimer?: FacebookShopTag;
369
+ monthly_payment?: FacebookShopTag;
370
+ down_payment?: FacebookShopTag;
371
+ term_length?: FacebookShopTag;
372
+ apr?: FacebookShopTag;
373
+ final_payment?: FacebookShopTag;
374
+ total_amount?: FacebookShopTag;
375
+ net_loan_amount?: FacebookShopTag;
360
376
 
361
377
  quantity_to_sell_on_facebook: FacebookShopTag;
362
378
  google_product_category: FacebookShopTag;
@@ -1,93 +1,265 @@
1
- import { Injectable } from '@nestjs/common';
2
-
3
- import { escape, get, join, round, toString, trim } from 'lodash';
4
-
5
- import { de, deVehicleDetailsPage, deVehicleProps } from '@bytebrand/i18n-dictionaries';
6
- import { ImboImageResponseInterface } from '@bytebrand/chunkable';
7
- import { TranslatorExtended } from '@bytebrand/schema-tools';
8
- import { DataSchemes } from '@bytebrand/car-schema';
9
-
10
- import { FacebookShopItem } from '../interfaces';
11
- import { ConfigService, ImagesService } from '../services';
12
- import { Description } from '../interfaces/description.interface';
13
- import { FbDescription } from './fb-description.transformer';
14
- import { FbEquipment } from './fb-equipment.transformer';
15
-
16
- @Injectable()
17
- export class FacebookShopTransformer {
18
- private translator: TranslatorExtended;
19
-
20
- constructor(private readonly configService: ConfigService, private readonly imageService: ImagesService) {
21
- const dict = { ...de, ...deVehicleProps, ...deVehicleDetailsPage };
22
- this.translator = new TranslatorExtended(dict);
23
- }
24
-
25
- async transform({
26
- car,
27
- images,
28
- }: {
29
- car: DataSchemes.Car;
30
- images: ImboImageResponseInterface[];
31
- }): Promise<FacebookShopItem> {
32
- const imageLinks = await this.imageService.getImages(car, images, this.configService.config.imageType, false);
33
-
34
- return {
35
- id: { value: get(car, 'metaData.vehicleId'), isGoogle: true },
36
- title: { value: this.getTitle(car), isGoogle: true },
37
- description: { value: this.getDescription(car), isGoogle: true },
38
- availability: { value: 'in stock', isGoogle: true },
39
- condition: { value: this.getCondition(car), isGoogle: true },
40
- price: { value: this.getPrice(car), isGoogle: true },
41
- link: { value: this.getCarLink(car), isGoogle: true },
42
- image_link: { value: imageLinks[0], isGoogle: true },
43
- brand: { value: get(car, 'mainData.make', ''), isGoogle: true },
44
- additional_image_link: { value: imageLinks[0], isGoogle: false },
45
- quantity_to_sell_on_facebook: { value: 1, isGoogle: true },
46
- google_product_category: { value: '916', isGoogle: true },
47
- sale_price: { value: this.getPrice(car), isGoogle: true },
48
- status: { value: 'active', isGoogle: true },
49
- color: { value: this.getColor(car), isGoogle: false },
50
- product_type: {
51
- value: 'Vehicles &amp; Parts > Vehicles > Motor Vehicles > Cars, Trucks &amp; Vans',
52
- isGoogle: true,
53
- },
54
- };
55
- }
56
-
57
- private getTitle(car: DataSchemes.Car): string {
58
- const make = toString(get(car, 'mainData.make', ''));
59
- const model = toString(get(car, 'mainData.model', ''));
60
- return trim(escape(join([make, model], ' ')));
61
- }
62
-
63
- private getCondition(car: DataSchemes.Car): string {
64
- const available = {
65
- selector_condition_new: 'New',
66
- selector_condition_used: 'Used',
67
- selector_unknown: 'New',
68
- };
69
- const condition = get(car, 'mainData.condition', 'selector_unknown');
70
- return get(available, condition);
71
- }
72
-
73
- private getCarLink(car: DataSchemes.Car): string {
74
- return `https://${this.configService.config.domain}/search/vehicle/${car._id}`;
75
- }
76
-
77
- private getPrice(car: DataSchemes.Car): string {
78
- const price = get(car, 'price.currentSalesPrice', 0);
79
- return `${round(price, 2)} EUR`;
80
- }
81
-
82
- private getColor(car: DataSchemes.Car): string {
83
- let colorSelection = get(car, 'exterior.exteriorColor', 'selector_unknown');
84
- if (!colorSelection || colorSelection === 'selector_unknown') {
85
- colorSelection = 'selector_unknown';
86
- }
87
- return this.translator.translateVal(colorSelection);
88
- }
89
-
90
- private getDescription(car: DataSchemes.Car): Description[] {
91
- return FbDescription.create(car, this.translator, new FbEquipment()).getDescriptionArray();
92
- }
93
- }
1
+ import { Injectable } from '@nestjs/common';
2
+
3
+ import { get, round, toString } from 'lodash';
4
+ import moment from 'moment';
5
+
6
+ import { de, deVehicleDetailsPage, deVehicleProps } from '@bytebrand/i18n-dictionaries';
7
+ import { ImboImageResponseInterface } from '@bytebrand/chunkable';
8
+ import { TranslatorExtended } from '@bytebrand/schema-tools';
9
+ import { DataSchemes } from '@bytebrand/car-schema';
10
+ import { getComputedFinancing, normalizeRateObject } from '@bytebrand/fe-financing';
11
+
12
+ import { FacebookShopItem, FacebookShopTag } from '../interfaces';
13
+ import { ConfigService, ImagesService, MappingService } from '../services';
14
+ import {
15
+ META_PUSH_PRIORITY_ALLOWED_VALUES,
16
+ META_PUSH_PRIORITY_STANDARD,
17
+ OFFER_DISCLAIMER,
18
+ OfferType,
19
+ } from '../constants/fb.constants';
20
+
21
+ interface OfferFinancials {
22
+ monthlyPayment: number;
23
+ downPayment: number;
24
+ termLength: number;
25
+ apr: number;
26
+ finalPayment: number;
27
+ totalAmount: number;
28
+ netLoanAmount: number;
29
+ }
30
+
31
+ interface Offer {
32
+ offerType: OfferType;
33
+ title: string;
34
+ description: string;
35
+ disclaimer?: string;
36
+ financials?: OfferFinancials;
37
+ }
38
+
39
+ const DE_LOCALE = 'de-DE';
40
+
41
+ @Injectable()
42
+ export class FacebookShopTransformer {
43
+ private translator: TranslatorExtended;
44
+
45
+ constructor(
46
+ private readonly configService: ConfigService,
47
+ private readonly imageService: ImagesService,
48
+ private readonly mappingService: MappingService,
49
+ ) {
50
+ const dict = { ...de, ...deVehicleProps, ...deVehicleDetailsPage };
51
+ this.translator = new TranslatorExtended(dict);
52
+ }
53
+
54
+ async transform({
55
+ car,
56
+ images,
57
+ }: {
58
+ car: DataSchemes.Car;
59
+ images: ImboImageResponseInterface[];
60
+ }): Promise<FacebookShopItem[]> {
61
+ // ignoreDefImages=true: cars with no real photos must be excluded, not padded with placeholders.
62
+ const imageLinks = await this.imageService.getImages(car, images, this.configService.config.imageType, true);
63
+ if (!imageLinks.length) {
64
+ return [];
65
+ }
66
+
67
+ const vehicleId = toString(get(car, 'metaData.vehicleId', car._id));
68
+ const make = toString(get(car, 'mainData.make', ''));
69
+ const model = toString(get(car, 'mainData.model', ''));
70
+ const price = this.getPriceValue(car);
71
+ const ownerId = get(car, 'metaData.ownerId', null);
72
+ const customLabel4 = await this.getMetaPushPriority(ownerId);
73
+
74
+ const offers = this.buildOffers(car, { make, model, price });
75
+ if (!offers.length) {
76
+ return [];
77
+ }
78
+
79
+ const base = {
80
+ vehicle_id: this.tag(vehicleId, true),
81
+ availability: this.tag('in stock', true),
82
+ condition: this.tag(this.getCondition(car), true),
83
+ price: this.tag(`${price} EUR`, true),
84
+ sale_price: this.tag(`${price} EUR`, true),
85
+ link: this.tag(this.getCarLink(car), true),
86
+ image_link: this.tag(imageLinks[0], true),
87
+ additional_image_link: this.tag(imageLinks[0], false),
88
+ make: this.tag(make, false),
89
+ model: this.tag(model, false),
90
+ year: this.tag(this.getYear(car), false),
91
+ user_id: this.tag(ownerId, false),
92
+ custom_label_4: this.tag(customLabel4, false),
93
+ dealer_privacy_policy_url: this.tag('https://www.auto.de/einwilligungserklaerung/', false),
94
+ quantity_to_sell_on_facebook: this.tag(1, true),
95
+ google_product_category: this.tag('916', true),
96
+ status: this.tag('active', true),
97
+ color: this.tag(this.getColor(car), false),
98
+ product_type: this.tag('Vehicles & Parts > Vehicles > Motor Vehicles > Cars, Trucks & Vans', true),
99
+ };
100
+
101
+ return offers.map((offer): FacebookShopItem => ({
102
+ ...base,
103
+ id: this.tag(`${vehicleId}_${offer.offerType}`, true),
104
+ title: this.tag(offer.title, true),
105
+ description: this.tag(offer.description, true),
106
+ offer_type: this.tag(offer.offerType, false),
107
+ ...(offer.disclaimer ? { offer_disclaimer: this.tag(offer.disclaimer, false) } : {}),
108
+ ...(offer.financials ? this.financialTags(offer.financials) : {}),
109
+ } as FacebookShopItem));
110
+ }
111
+
112
+ private buildOffers(car: DataSchemes.Car, common: { make: string; model: string; price: number }): Offer[] {
113
+ const offers: Offer[] = [];
114
+ const { make, model, price } = common;
115
+
116
+ // Read the raw, already-persisted rate/leasing values before normalizeRateObject
117
+ // mutates car.rate — these three fields must come straight from the Car object,
118
+ // matching exactly what the CDP displays (per ticket: data.rate.*/data.leasing.*).
119
+ const offersFinancing = get(car, 'rate.offersFinancing') !== false;
120
+ const rawFinancing = {
121
+ monthlyPayment: get(car, 'rate.monthlyInstallment', 0),
122
+ downPayment: get(car, 'rate.firstInstallment', 0),
123
+ termLength: get(car, 'rate.paybackPeriod', 0),
124
+ };
125
+ const rawLeasing = {
126
+ monthlyPayment: get(car, 'leasing.monthlyInstallment', 0),
127
+ downPayment: get(car, 'leasing.firstInstallment', 0),
128
+ termLength: get(car, 'leasing.paybackPeriod', 0),
129
+ };
130
+ const leasingIsEligible = this.isLeasingEligible(car);
131
+
132
+ const { financing: computedFinancing, leasing: computedLeasing } = this.computeFinancials(car);
133
+
134
+ if (offersFinancing && rawFinancing.monthlyPayment && computedFinancing) {
135
+ offers.push({
136
+ offerType: OfferType.financing,
137
+ title: `${make} ${model} finanzieren – z. B. ab ${this.formatEUR(rawFinancing.monthlyPayment)} €/Monat`,
138
+ description: `${make} ${model} finanzieren – ${rawFinancing.termLength} Monate Laufzeit, ` +
139
+ `${this.formatEUR(rawFinancing.downPayment)} € Anzahlung`,
140
+ disclaimer: OFFER_DISCLAIMER,
141
+ financials: {
142
+ ...rawFinancing,
143
+ apr: get(computedFinancing, 'annualPercentageRate', 0),
144
+ finalPayment: get(computedFinancing, 'finalInstallment', 0),
145
+ totalAmount: get(computedFinancing, 'grossLoanAmount', 0),
146
+ netLoanAmount: get(computedFinancing, 'netLoanAmount', 0),
147
+ },
148
+ });
149
+ }
150
+
151
+ if (leasingIsEligible && rawLeasing.monthlyPayment && computedLeasing) {
152
+ offers.push({
153
+ offerType: OfferType.leasing,
154
+ title: `${make} ${model} leasen – z. B. ab ${this.formatEUR(rawLeasing.monthlyPayment)} €/Monat`,
155
+ description: `${make} ${model} leasen – ${rawLeasing.termLength} Monate Laufzeit, ` +
156
+ `${this.formatEUR(rawLeasing.downPayment)} € Anzahlung`,
157
+ disclaimer: OFFER_DISCLAIMER,
158
+ financials: {
159
+ ...rawLeasing,
160
+ apr: get(computedLeasing, 'annualPercentageRate', 0),
161
+ finalPayment: get(computedLeasing, 'finalInstallment', 0),
162
+ totalAmount: get(computedLeasing, 'grossLoanAmount', 0),
163
+ netLoanAmount: get(computedLeasing, 'netLoanAmount', 0),
164
+ },
165
+ });
166
+ }
167
+
168
+ offers.push({
169
+ offerType: OfferType.buy,
170
+ title: `${make} ${model} kaufen – jetzt für ${this.formatEUR(price)} €`,
171
+ description: `${make} ${model} kaufen – jetzt für ${this.formatEUR(price)} €`,
172
+ });
173
+
174
+ return offers;
175
+ }
176
+
177
+ // Runs the same normalizeRateObject + getComputedFinancing pipeline the CDP uses, so
178
+ // apr/final_payment/total_amount/net_loan_amount (not stored anywhere on the Car object)
179
+ // match exactly what the customer sees when they click through from the ad.
180
+ private computeFinancials(car: DataSchemes.Car): { financing?: Record<string, any>; leasing?: Record<string, any> } {
181
+ try {
182
+ const normalized = normalizeRateObject(car);
183
+ if (!get(normalized, 'rate.financing')) {
184
+ return {};
185
+ }
186
+ const computed = getComputedFinancing(normalized.rate as any);
187
+ return { financing: computed.financing, leasing: computed.leasing };
188
+ } catch (error) {
189
+ console.error(error);
190
+ return {};
191
+ }
192
+ }
193
+
194
+ // Mirrors getLeasing.ts's isLeasingActive: same car ages out of leasing eligibility once
195
+ // it's older than leasing.maxAge months or past leasing.maxMileage km.
196
+ private isLeasingEligible(car: DataSchemes.Car): boolean {
197
+ const firstRegistration = get(car, 'mainData.firstRegistration', 0) as number;
198
+ const mileage = get(car, 'mainData.mileage', 0) as number;
199
+ const maxAge = get(car, 'leasing.maxAge', 24) as number;
200
+ const maxMileage = get(car, 'leasing.maxMileage', 1000000) as number;
201
+
202
+ const registrationMs = firstRegistration ? firstRegistration * 1000 : Date.now();
203
+ const maxCarAge = new Date().setMonth(new Date().getMonth() - maxAge);
204
+
205
+ return registrationMs > maxCarAge && maxMileage > mileage;
206
+ }
207
+
208
+ private financialTags(financials: OfferFinancials): Partial<FacebookShopItem> {
209
+ return {
210
+ monthly_payment: this.tag(financials.monthlyPayment, false),
211
+ down_payment: this.tag(financials.downPayment, false),
212
+ term_length: this.tag(financials.termLength, false),
213
+ apr: this.tag(financials.apr, false),
214
+ final_payment: this.tag(financials.finalPayment, false),
215
+ total_amount: this.tag(financials.totalAmount, false),
216
+ net_loan_amount: this.tag(financials.netLoanAmount, false),
217
+ };
218
+ }
219
+
220
+ private tag(value: any, isGoogle: boolean): FacebookShopTag {
221
+ return { value, isGoogle };
222
+ }
223
+
224
+ private formatEUR(value: number): string {
225
+ return Math.round(value || 0).toLocaleString(DE_LOCALE);
226
+ }
227
+
228
+ private async getMetaPushPriority(ownerId: string): Promise<string> {
229
+ const priorities = await this.mappingService.getMetaPushPriorityMapping();
230
+ const entry = priorities.find(priority => priority.dealer_id === ownerId);
231
+ const value = get(entry, 'meta_push_priority', '');
232
+ return META_PUSH_PRIORITY_ALLOWED_VALUES.includes(value) ? value : META_PUSH_PRIORITY_STANDARD;
233
+ }
234
+
235
+ private getYear(car: DataSchemes.Car): string {
236
+ const firstRegistration = get(car, 'mainData.firstRegistration', 0) as number;
237
+ return firstRegistration ? moment(firstRegistration * 1000).format('YYYY') : moment().format('YYYY');
238
+ }
239
+
240
+ private getCondition(car: DataSchemes.Car): string {
241
+ const available = {
242
+ selector_condition_new: 'new',
243
+ selector_condition_used: 'used',
244
+ selector_unknown: 'used',
245
+ };
246
+ const condition = get(car, 'mainData.condition', 'selector_unknown');
247
+ return get(available, condition);
248
+ }
249
+
250
+ private getCarLink(car: DataSchemes.Car): string {
251
+ return `https://${this.configService.config.domain}/search/vehicle/${car._id}`;
252
+ }
253
+
254
+ private getPriceValue(car: DataSchemes.Car): number {
255
+ return round(get(car, 'price.currentSalesPrice', 0), 2);
256
+ }
257
+
258
+ private getColor(car: DataSchemes.Car): string {
259
+ let colorSelection = get(car, 'exterior.exteriorColor', 'selector_unknown');
260
+ if (!colorSelection || colorSelection === 'selector_unknown') {
261
+ colorSelection = 'selector_unknown';
262
+ }
263
+ return this.translator.translateVal(colorSelection);
264
+ }
265
+ }
@@ -166,7 +166,7 @@ export class FbTransformer {
166
166
 
167
167
  private async getMetaPushPriority(ownerId: string): Promise<string> {
168
168
  const priorities = await this.mappingService.getMetaPushPriorityMapping();
169
- const entry = priorities.find(priority => priority.user_id === ownerId);
169
+ const entry = priorities.find(priority => priority.dealer_id === ownerId);
170
170
  const value = get(entry, 'meta_push_priority', '');
171
171
  return includes(META_PUSH_PRIORITY_ALLOWED_VALUES, value) ? value : META_PUSH_PRIORITY_STANDARD;
172
172
  }