@bytebrand/export-cars 1.26.16 → 1.26.18

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,62 +1,62 @@
1
- {
2
- "name": "@bytebrand/export-cars",
3
- "version": "1.26.16",
4
- "description": "",
5
- "author": "bytebrand",
6
- "license": "UNLICENSED",
7
- "scripts": {
8
- "prebuild": "rimraf dist",
9
- "build": "nest build",
10
- "prepublishOnly": "npm run build",
11
- "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
12
- "lint": "eslint -c .eslintrc.js --ext .ts src/",
13
- "start:dev": "nest start --watch"
14
- },
15
- "dependencies": {
16
- "@bytebrand/autode-request": "^1.19.2",
17
- "@bytebrand/car-basic-data-scope": "^9.11.24",
18
- "@bytebrand/car-description": "^1.17.20",
19
- "@bytebrand/car-schema": "^3.14.15",
20
- "@bytebrand/chunkable": "^5.19.8",
21
- "@bytebrand/cronicle-helper": "^1.21.8",
22
- "@bytebrand/fe-financing": "^5.14.19",
23
- "@bytebrand/i18n-dictionaries": "^1.5.0",
24
- "@bytebrand/import-helper": "^0.45.0",
25
- "@bytebrand/schema-tools": "^3.18.8",
26
- "@bytebrand/zipper": "^2.2.0",
27
- "@nestjs/cache-manager": "^2.1.0",
28
- "@nestjs/common": "^10.2.7",
29
- "@nestjs/core": "^10.2.7",
30
- "axios": "^1.5.1",
31
- "bottleneck": "^2.19.5",
32
- "cache-manager": "^5.2.4",
33
- "csv-write-stream": "^2.0.0",
34
- "google-spreadsheet": "^3.3.0",
35
- "lodash": "^4.17.21",
36
- "moment": "^2.29.4",
37
- "nest-commander": "~3.15.0",
38
- "reflect-metadata": "^0.1.13",
39
- "vin-generator": "^1.0.1"
40
- },
41
- "devDependencies": {
42
- "@nestjs/cli": "^10.1.18",
43
- "@types/google-spreadsheet": "^3.3.0",
44
- "@types/inquirer": "^8.2.4",
45
- "@types/lodash": "^4.14.186",
46
- "@types/node": "^16.18.58",
47
- "@typescript-eslint/eslint-plugin": "^6.8.0",
48
- "@typescript-eslint/parser": "^6.8.0",
49
- "eslint": "^8.0.1",
50
- "eslint-config-prettier": "^8.3.0",
51
- "eslint-plugin-prettier": "^4.0.0",
52
- "gts": "^3.1.1",
53
- "prettier": "^2.3.2",
54
- "ts-node": "^10.0.0",
55
- "tsconfig-paths": "4.1.0",
56
- "typescript": "^5.2.2"
57
- },
58
- "engines": {
59
- "node": ">=14.0.0"
60
- },
61
- "gitHead": "e827f35865032821bf3a081f03a026d3d3f8a540"
62
- }
1
+ {
2
+ "name": "@bytebrand/export-cars",
3
+ "version": "1.26.18",
4
+ "description": "",
5
+ "author": "bytebrand",
6
+ "license": "UNLICENSED",
7
+ "scripts": {
8
+ "prebuild": "rimraf dist",
9
+ "build": "nest build",
10
+ "prepublishOnly": "npm run build",
11
+ "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
12
+ "lint": "eslint -c .eslintrc.js --ext .ts src/",
13
+ "start:dev": "nest start --watch"
14
+ },
15
+ "dependencies": {
16
+ "@bytebrand/autode-request": "^1.19.2",
17
+ "@bytebrand/car-basic-data-scope": "^9.11.24",
18
+ "@bytebrand/car-description": "^1.17.20",
19
+ "@bytebrand/car-schema": "^3.14.15",
20
+ "@bytebrand/chunkable": "^5.19.8",
21
+ "@bytebrand/cronicle-helper": "^1.21.8",
22
+ "@bytebrand/fe-financing": "^5.14.19",
23
+ "@bytebrand/i18n-dictionaries": "^1.5.0",
24
+ "@bytebrand/import-helper": "^0.45.0",
25
+ "@bytebrand/schema-tools": "^3.18.8",
26
+ "@bytebrand/zipper": "^2.2.0",
27
+ "@nestjs/cache-manager": "^2.1.0",
28
+ "@nestjs/common": "^10.2.7",
29
+ "@nestjs/core": "^10.2.7",
30
+ "axios": "^1.5.1",
31
+ "bottleneck": "^2.19.5",
32
+ "cache-manager": "^5.2.4",
33
+ "csv-write-stream": "^2.0.0",
34
+ "google-spreadsheet": "^3.3.0",
35
+ "lodash": "^4.17.21",
36
+ "moment": "^2.29.4",
37
+ "nest-commander": "~3.15.0",
38
+ "reflect-metadata": "^0.1.13",
39
+ "vin-generator": "^1.0.1"
40
+ },
41
+ "devDependencies": {
42
+ "@nestjs/cli": "^10.1.18",
43
+ "@types/google-spreadsheet": "^3.3.0",
44
+ "@types/inquirer": "^8.2.4",
45
+ "@types/lodash": "^4.14.186",
46
+ "@types/node": "^16.18.58",
47
+ "@typescript-eslint/eslint-plugin": "^6.8.0",
48
+ "@typescript-eslint/parser": "^6.8.0",
49
+ "eslint": "^8.0.1",
50
+ "eslint-config-prettier": "^8.3.0",
51
+ "eslint-plugin-prettier": "^4.0.0",
52
+ "gts": "^3.1.1",
53
+ "prettier": "^2.3.2",
54
+ "ts-node": "^10.0.0",
55
+ "tsconfig-paths": "4.1.0",
56
+ "typescript": "^5.2.2"
57
+ },
58
+ "engines": {
59
+ "node": ">=14.0.0"
60
+ },
61
+ "gitHead": "e827f35865032821bf3a081f03a026d3d3f8a540"
62
+ }
@@ -23,6 +23,10 @@ export class Config {
23
23
  numberIN: string;
24
24
  dateIN: string;
25
25
  };
26
+ // When set, CarService.filterCar exports a car only if price.currentSalesPrice
27
+ // is a valid number in (0, maxCurrentSalesPrice] — missing/invalid price means
28
+ // the car is not exported. Unset = no price cap for this export type.
29
+ maxCurrentSalesPrice?: number;
26
30
  pathesConfig: {
27
31
  localDirectory: string;
28
32
  localConvertedDirectory: string;
@@ -1,11 +1,13 @@
1
1
  import { Params } from '../interfaces/params.interface';
2
2
  import { Config } from './config';
3
3
  import { ExportType, FileType } from '../enums';
4
+ import { MAX_FEED_SALES_PRICE_EUR } from '../constants/feed-price.constants';
4
5
 
5
6
  export class FacebookShopConfig extends Config {
6
7
  constructor(params: Params) {
7
8
  super(params);
8
9
  this.pathesConfig.localConvertedDirectory = `resources/${ExportType.FACEBOOK_SHOP}-export`;
10
+ this.maxCurrentSalesPrice = MAX_FEED_SALES_PRICE_EUR;
9
11
  this.imageUrl = this.environmentConfig[params.environment].baseImageUrl;
10
12
  this.outputFiles = [
11
13
  `${this.pathesConfig.localConvertedDirectory}/api_export_cars_${ExportType.FACEBOOK_SHOP}.${FileType.XML}`,
@@ -1,4 +1,5 @@
1
1
  import { ExportType, FileType, ImageType } from 'src/enums';
2
+ import { MAX_FEED_SALES_PRICE_EUR } from '../constants/feed-price.constants';
2
3
  import { Params } from '../interfaces/params.interface';
3
4
  import { Config } from './config';
4
5
 
@@ -6,6 +7,7 @@ export class FbConfig extends Config {
6
7
  constructor(params: Params) {
7
8
  super(params);
8
9
  this.pathesConfig.localConvertedDirectory = `resources/${ExportType.FB}-export`;
10
+ this.maxCurrentSalesPrice = MAX_FEED_SALES_PRICE_EUR;
9
11
  this.imageUrl = this.environmentConfig[params.environment].imageUrl;
10
12
  this.imageType = ImageType.marketing;
11
13
  this.marketingImageConfigId = this.environmentConfig[params.environment].fbMarketingImageId;
@@ -1,4 +1,5 @@
1
1
  import { ExportType, FileType, ImageType } from 'src/enums';
2
+ import { MAX_FEED_SALES_PRICE_EUR } from '../constants/feed-price.constants';
2
3
  import { Params } from '../interfaces/params.interface';
3
4
  import { Config } from './config';
4
5
 
@@ -7,6 +8,7 @@ export class GoogleConfig extends Config {
7
8
  super(params);
8
9
  this.pathesConfig.localConvertedDirectory = `resources/${ExportType.GOOGLE}-export`;
9
10
  this.imageType = ImageType.default;
11
+ this.maxCurrentSalesPrice = MAX_FEED_SALES_PRICE_EUR;
10
12
  this.outputFiles = [
11
13
  `${this.pathesConfig.localConvertedDirectory}/api_export_cars_${ExportType.GOOGLE}.${FileType.XML}`,
12
14
  ];
@@ -0,0 +1,4 @@
1
+ // Shared eligibility cap for the Google Vehicle Ads and Meta AIA/AMA feeds:
2
+ // only cars whose end-customer selling price (price.currentSalesPrice) is
3
+ // ≤ €80,000 may be exported. €80,000.00 itself is still included.
4
+ export const MAX_FEED_SALES_PRICE_EUR = 80_000;
@@ -35,7 +35,9 @@ export class FacebookShopExporter implements Exporter {
35
35
  const chunkData = chunk.data.filter(car => this.carService.filterCar(car, this.rulesService.rules));
36
36
 
37
37
  for (let i = 0; i < chunkData.length; i += 1) {
38
- const data = chunk.data[i];
38
+ // chunkData, not chunk.data: indexing the unfiltered array here used to
39
+ // re-admit cars that filterCar had just rejected (and drop valid ones).
40
+ const data = chunkData[i];
39
41
 
40
42
  // console.time(`Transform ${data.car._id}`);
41
43
  const items = await facebookShopTransformer.transform(data);
@@ -3,7 +3,9 @@ import { Inject, Injectable } from '@nestjs/common';
3
3
  import { DataSchemes } from '@bytebrand/car-schema';
4
4
  import { Cache } from 'cache-manager';
5
5
  import Bottleneck from 'bottleneck';
6
+ import { get, toNumber } from 'lodash';
6
7
  import { RequestService } from './request.service';
8
+ import { ConfigService } from './config.service';
7
9
  import { DateRule } from '../rules/date.rule';
8
10
  import { NumberRule } from '../rules/number.rule';
9
11
  import { StringRule } from '../rules/string.rule';
@@ -17,6 +19,7 @@ export class CarService {
17
19
  constructor(
18
20
  @Inject(CACHE_MANAGER) private cacheManager: Cache,
19
21
  private requestService: RequestService,
22
+ private configService: ConfigService,
20
23
  ) { }
21
24
 
22
25
  private _counter: CarsCounter;
@@ -33,6 +36,16 @@ export class CarService {
33
36
  if (!((car?.metaData?.imagesCount ?? 0) > 0)) {
34
37
  return false;
35
38
  }
39
+ // Shared price cap for export types that set maxCurrentSalesPrice (Google
40
+ // Vehicle Ads, Meta AIA/AMA): the end-customer selling price must be a valid
41
+ // number ≤ the cap; a missing/invalid/non-positive price also excludes the car.
42
+ const maxPrice = this.configService.config.maxCurrentSalesPrice;
43
+ if (maxPrice) {
44
+ const price = toNumber(get(car, 'price.currentSalesPrice', null));
45
+ if (!isFinite(price) || price <= 0 || price > maxPrice) {
46
+ return false;
47
+ }
48
+ }
36
49
  return rules.every(e => e.applyRule(car));
37
50
  }
38
51
 
@@ -98,9 +98,9 @@ export class FacebookShopTransformer {
98
98
 
99
99
  return offers.map((offer): FacebookShopItem => ({
100
100
  ...base,
101
- // Plain long auto.de ID � the `_${offerType}` suffix (ADV-160, one item per offer) broke
102
- // matching once only the financing offer is exported; re-add a suffix ONLY if multiple
103
- // offer types per vehicle are ever exported again.
101
+ // Plain long auto.de ID � the `_${offerType}` suffix (ADV-160, one item per offer) broke
102
+ // matching once only the financing offer is exported; re-add a suffix ONLY if multiple
103
+ // offer types per vehicle are ever exported again.
104
104
  vehicle_offer_id: vehicleId,
105
105
  title: offer.title,
106
106
  offer_description: offer.description,