@bisondesk/core-sdk 1.0.493 → 1.0.495

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 (44) hide show
  1. package/lib/constants.d.ts +2 -0
  2. package/lib/constants.d.ts.map +1 -1
  3. package/lib/constants.js +2 -0
  4. package/lib/constants.js.map +1 -1
  5. package/lib/types/comparables.d.ts +2 -0
  6. package/lib/types/comparables.d.ts.map +1 -1
  7. package/lib/types/comparables.js.map +1 -1
  8. package/lib/types/crm.d.ts +8 -0
  9. package/lib/types/crm.d.ts.map +1 -1
  10. package/lib/types/crm.js.map +1 -1
  11. package/lib/types/interests.d.ts +8 -0
  12. package/lib/types/interests.d.ts.map +1 -1
  13. package/lib/types/interests.js +2 -0
  14. package/lib/types/interests.js.map +1 -1
  15. package/lib/types/leasing-search.d.ts +1 -0
  16. package/lib/types/leasing-search.d.ts.map +1 -1
  17. package/lib/types/leasing-search.js.map +1 -1
  18. package/lib/types/marketing-emails.d.ts +57 -0
  19. package/lib/types/marketing-emails.d.ts.map +1 -0
  20. package/lib/types/marketing-emails.js +2 -0
  21. package/lib/types/marketing-emails.js.map +1 -0
  22. package/lib/types/marketing.d.ts +0 -1
  23. package/lib/types/marketing.d.ts.map +1 -1
  24. package/lib/types/marketing.js +0 -1
  25. package/lib/types/marketing.js.map +1 -1
  26. package/lib/types/tenants.d.ts +1 -0
  27. package/lib/types/tenants.d.ts.map +1 -1
  28. package/lib/types/tenants.js +1 -0
  29. package/lib/types/tenants.js.map +1 -1
  30. package/lib/utils/interests.d.ts +4 -0
  31. package/lib/utils/interests.d.ts.map +1 -0
  32. package/lib/utils/interests.js +110 -0
  33. package/lib/utils/interests.js.map +1 -0
  34. package/package.json +1 -1
  35. package/src/constants.ts +2 -0
  36. package/src/types/comparables.ts +2 -0
  37. package/src/types/crm.ts +11 -0
  38. package/src/types/interests.ts +14 -0
  39. package/src/types/leasing-search.ts +1 -0
  40. package/src/types/marketing-emails.ts +67 -0
  41. package/src/types/marketing.ts +1 -1
  42. package/src/types/tenants.ts +1 -0
  43. package/src/utils/interests.ts +150 -0
  44. package/tsconfig.tsbuildinfo +1 -1
@@ -16,6 +16,7 @@ export declare enum TenantModule {
16
16
  Docs = "docs",
17
17
  Leads = "leads",
18
18
  Leasing = "leasing",
19
+ Marketing = "marketing",
19
20
  Insights = "insights",
20
21
  Opportunities = "opportunities",
21
22
  Offers = "offers",
@@ -1 +1 @@
1
- {"version":3,"file":"tenants.d.ts","sourceRoot":"/","sources":["types/tenants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AAClF,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD,MAAM,MAAM,YAAY,GAAG;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC;CAInC,CAAC;AAEF,oBAAY,YAAY;IACtB,cAAc,mBAAmB;IACjC,GAAG,QAAQ;IACX,GAAG,QAAQ;IACX,IAAI,SAAS;IACb,KAAK,UAAU;IACf,OAAO,YAAY;IACnB,QAAQ,aAAa;IACrB,aAAa,kBAAkB;IAC/B,MAAM,WAAW;IACjB,kBAAkB,yBAAyB;IAC3C,QAAQ,aAAa;IACrB,KAAK,UAAU;IACf,aAAa,oBAAoB;IACjC,SAAS,cAAc;IACvB,qBAAqB,2BAA2B;IAChD,KAAK,UAAU;CAChB;AAED,MAAM,MAAM,MAAM,GAAG,YAAY,GAAG;IAClC,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,SAAS,EAAE,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC;IACjC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IAGvB,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B,QAAQ,EAAE,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC;IAChC,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,aAAa,EAAE;QACb,KAAK,EAAE,eAAe,EAAE,CAAC;KAC1B,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,MAAM,GAAG;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE;QACR,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,QAAQ,EAAE,aAAa,CAAC;IAExB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,gBAAgB,GAAG,SAAS,CAAC;IAC3C,OAAO,EAAE,WAAW,GAAG,SAAS,CAAC;IACjC,aAAa,EAAE,gBAAgB,GAAG,SAAS,CAAC;CAC7C,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC"}
1
+ {"version":3,"file":"tenants.d.ts","sourceRoot":"/","sources":["types/tenants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AAClF,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD,MAAM,MAAM,YAAY,GAAG;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC;CAInC,CAAC;AAEF,oBAAY,YAAY;IACtB,cAAc,mBAAmB;IACjC,GAAG,QAAQ;IACX,GAAG,QAAQ;IACX,IAAI,SAAS;IACb,KAAK,UAAU;IACf,OAAO,YAAY;IACnB,SAAS,cAAc;IACvB,QAAQ,aAAa;IACrB,aAAa,kBAAkB;IAC/B,MAAM,WAAW;IACjB,kBAAkB,yBAAyB;IAC3C,QAAQ,aAAa;IACrB,KAAK,UAAU;IACf,aAAa,oBAAoB;IACjC,SAAS,cAAc;IACvB,qBAAqB,2BAA2B;IAChD,KAAK,UAAU;CAChB;AAED,MAAM,MAAM,MAAM,GAAG,YAAY,GAAG;IAClC,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,SAAS,EAAE,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC;IACjC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IAGvB,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B,QAAQ,EAAE,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC;IAChC,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,aAAa,EAAE;QACb,KAAK,EAAE,eAAe,EAAE,CAAC;KAC1B,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,MAAM,GAAG;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE;QACR,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,QAAQ,EAAE,aAAa,CAAC;IAExB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,gBAAgB,GAAG,SAAS,CAAC;IAC3C,OAAO,EAAE,WAAW,GAAG,SAAS,CAAC;IACjC,aAAa,EAAE,gBAAgB,GAAG,SAAS,CAAC;CAC7C,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC"}
@@ -6,6 +6,7 @@ export var TenantModule;
6
6
  TenantModule["Docs"] = "docs";
7
7
  TenantModule["Leads"] = "leads";
8
8
  TenantModule["Leasing"] = "leasing";
9
+ TenantModule["Marketing"] = "marketing";
9
10
  TenantModule["Insights"] = "insights";
10
11
  TenantModule["Opportunities"] = "opportunities";
11
12
  TenantModule["Offers"] = "offers";
@@ -1 +1 @@
1
- {"version":3,"file":"tenants.js","sourceRoot":"/","sources":["types/tenants.ts"],"names":[],"mappings":"AAgBA,MAAM,CAAN,IAAY,YAiBX;AAjBD,WAAY,YAAY;IACtB,iDAAiC,CAAA;IACjC,2BAAW,CAAA;IACX,2BAAW,CAAA;IACX,6BAAa,CAAA;IACb,+BAAe,CAAA;IACf,mCAAmB,CAAA;IACnB,qCAAqB,CAAA;IACrB,+CAA+B,CAAA;IAC/B,iCAAiB,CAAA;IACjB,2DAA2C,CAAA;IAC3C,qCAAqB,CAAA;IACrB,+BAAe,CAAA;IACf,iDAAiC,CAAA;IACjC,uCAAuB,CAAA;IACvB,gEAAgD,CAAA;IAChD,+BAAe,CAAA;AACjB,CAAC,EAjBW,YAAY,KAAZ,YAAY,QAiBvB","sourcesContent":["import { CognitoInfo, LocationValue, Region } from '@bisondesk/commons-sdk/types';\nimport { OpportunityType } from '../constants.js';\n\nexport type PublicTenant = {\n defaultCountry: string;\n defaultCurrency: string;\n defaultLanguage: string;\n id: string; // the id is provided on creation bc we assume tenants will be created by someone on the team\n name: string;\n region: Region;\n subdomains: [string, ...string[]];\n\n // Pay attention to what information is added here since\n // the tenant can be obtained without authentication\n};\n\nexport enum TenantModule {\n Administration = 'administration',\n App = 'app',\n CRM = 'crm',\n Docs = 'docs',\n Leads = 'leads',\n Leasing = 'leasing',\n Insights = 'insights',\n Opportunities = 'opportunities',\n Offers = 'offers',\n VehiclesMasterData = 'vehicles_master_data',\n Vehicles = 'vehicles',\n Tasks = 'tasks',\n TrackAndTrace = 'track_and_trace',\n Bootstrap = 'bootstrap',\n WhatsappNotifications = 'whatsapp_notifications',\n Hexon = 'hexon',\n}\n\nexport type Tenant = PublicTenant & {\n cognito?: CognitoInfo;\n languages: [string, ...string[]];\n conglomerate?: string;\n emailDomain: string;\n hyperdmsIds?: string[];\n hyperportalId?: string;\n\n /* deprecated */\n exactAccounting?: boolean;\n\n branches: [Branch, ...Branch[]];\n modules: TenantModule[];\n test?: boolean;\n opportunities: {\n types: OpportunityType[];\n };\n};\n\nexport type Branch = {\n id: string;\n name: string;\n country: string;\n language: string;\n exactAccounting?: boolean;\n currency: string;\n adminNumberPrefix: string;\n stockNumberPrefix: string;\n vatNumber?: string;\n vatRates: {\n standard: string; //percentage value (e.g. 21)\n };\n location: LocationValue;\n\n storecoveExternalId?: number;\n};\n\nexport type HdmsVehiclesSync = {\n active: boolean;\n};\n\nexport type HdmsCrmSync = {\n active: boolean;\n};\n\nexport type HdmsDocumentSync = {\n active: boolean;\n};\n\nexport type TenantSyncSettings = {\n tenantId: string;\n hdmsVehicles: HdmsVehiclesSync | undefined;\n hdmsCrm: HdmsCrmSync | undefined;\n hdmsDocuments: HdmsDocumentSync | undefined;\n};\n\nexport type WhatsappStateResponse = {\n enabled: boolean;\n};\n"]}
1
+ {"version":3,"file":"tenants.js","sourceRoot":"/","sources":["types/tenants.ts"],"names":[],"mappings":"AAgBA,MAAM,CAAN,IAAY,YAkBX;AAlBD,WAAY,YAAY;IACtB,iDAAiC,CAAA;IACjC,2BAAW,CAAA;IACX,2BAAW,CAAA;IACX,6BAAa,CAAA;IACb,+BAAe,CAAA;IACf,mCAAmB,CAAA;IACnB,uCAAuB,CAAA;IACvB,qCAAqB,CAAA;IACrB,+CAA+B,CAAA;IAC/B,iCAAiB,CAAA;IACjB,2DAA2C,CAAA;IAC3C,qCAAqB,CAAA;IACrB,+BAAe,CAAA;IACf,iDAAiC,CAAA;IACjC,uCAAuB,CAAA;IACvB,gEAAgD,CAAA;IAChD,+BAAe,CAAA;AACjB,CAAC,EAlBW,YAAY,KAAZ,YAAY,QAkBvB","sourcesContent":["import { CognitoInfo, LocationValue, Region } from '@bisondesk/commons-sdk/types';\nimport { OpportunityType } from '../constants.js';\n\nexport type PublicTenant = {\n defaultCountry: string;\n defaultCurrency: string;\n defaultLanguage: string;\n id: string; // the id is provided on creation bc we assume tenants will be created by someone on the team\n name: string;\n region: Region;\n subdomains: [string, ...string[]];\n\n // Pay attention to what information is added here since\n // the tenant can be obtained without authentication\n};\n\nexport enum TenantModule {\n Administration = 'administration',\n App = 'app',\n CRM = 'crm',\n Docs = 'docs',\n Leads = 'leads',\n Leasing = 'leasing',\n Marketing = 'marketing',\n Insights = 'insights',\n Opportunities = 'opportunities',\n Offers = 'offers',\n VehiclesMasterData = 'vehicles_master_data',\n Vehicles = 'vehicles',\n Tasks = 'tasks',\n TrackAndTrace = 'track_and_trace',\n Bootstrap = 'bootstrap',\n WhatsappNotifications = 'whatsapp_notifications',\n Hexon = 'hexon',\n}\n\nexport type Tenant = PublicTenant & {\n cognito?: CognitoInfo;\n languages: [string, ...string[]];\n conglomerate?: string;\n emailDomain: string;\n hyperdmsIds?: string[];\n hyperportalId?: string;\n\n /* deprecated */\n exactAccounting?: boolean;\n\n branches: [Branch, ...Branch[]];\n modules: TenantModule[];\n test?: boolean;\n opportunities: {\n types: OpportunityType[];\n };\n};\n\nexport type Branch = {\n id: string;\n name: string;\n country: string;\n language: string;\n exactAccounting?: boolean;\n currency: string;\n adminNumberPrefix: string;\n stockNumberPrefix: string;\n vatNumber?: string;\n vatRates: {\n standard: string; //percentage value (e.g. 21)\n };\n location: LocationValue;\n\n storecoveExternalId?: number;\n};\n\nexport type HdmsVehiclesSync = {\n active: boolean;\n};\n\nexport type HdmsCrmSync = {\n active: boolean;\n};\n\nexport type HdmsDocumentSync = {\n active: boolean;\n};\n\nexport type TenantSyncSettings = {\n tenantId: string;\n hdmsVehicles: HdmsVehiclesSync | undefined;\n hdmsCrm: HdmsCrmSync | undefined;\n hdmsDocuments: HdmsDocumentSync | undefined;\n};\n\nexport type WhatsappStateResponse = {\n enabled: boolean;\n};\n"]}
@@ -0,0 +1,4 @@
1
+ import { Interest } from '../types/interests.js';
2
+ import { VehicleExternalInfo } from '../types/vehicles.js';
3
+ export declare const mapVehicleToInterest: (userId: string, vehicleExternalInfo: VehicleExternalInfo, referenceDate?: Date) => Interest;
4
+ //# sourceMappingURL=interests.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interests.d.ts","sourceRoot":"/","sources":["utils/interests.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAE3D,eAAO,MAAM,oBAAoB,WACvB,MAAM,uBACO,mBAAmB,kBACzB,IAAI,KAClB,QAyCF,CAAC"}
@@ -0,0 +1,110 @@
1
+ import { isoString } from '@bisondesk/commons-sdk/date-utils';
2
+ import { generateId } from '@bisondesk/commons-sdk/ids';
3
+ import { UTCDate } from '@date-fns/utc';
4
+ import { first, range, uniq } from 'lodash-es';
5
+ import { KM_ROUNDING, MAX_AGE, MAX_KM, MAX_PRICE, PRICE_ROUNDING } from '../constants.js';
6
+ export const mapVehicleToInterest = (userId, vehicleExternalInfo, referenceDate = new UTCDate()) => {
7
+ return {
8
+ id: generateId(),
9
+ createdAt: isoString(),
10
+ createdBy: userId,
11
+ modifiedAt: isoString(),
12
+ modifiedBy: userId,
13
+ bodystyle: vehicleExternalInfo.general.bodystyle,
14
+ cabins: toCabins(toArray(vehicleExternalInfo.body?.cabin?.type)),
15
+ category: vehicleExternalInfo.general.category,
16
+ retarderIntarder: vehicleExternalInfo.accessories.retarderIntarder ? true : undefined,
17
+ adr: vehicleExternalInfo.accessories.adr,
18
+ damaged: vehicleExternalInfo.condition?.damaged ?? false,
19
+ extended: toArray(vehicleExternalInfo.superstructure?.dimensions?.extended),
20
+ heightened: toArray(vehicleExternalInfo.superstructure?.dimensions?.heightened),
21
+ used: vehicleExternalInfo.condition?.used,
22
+ crane: vehicleExternalInfo.superstructure?.crane?.present,
23
+ tailgate: vehicleExternalInfo.superstructure?.tailgate?.present,
24
+ axleConfigurations: toArray(vehicleExternalInfo.powertrain?.axles?.configuration),
25
+ suspensions: toSuspensions(toArray(vehicleExternalInfo.powertrain?.axles?.suspension)),
26
+ transmissions: toTransmissions(toArray(vehicleExternalInfo.powertrain?.transmission?.type)),
27
+ euronorms: toEuronorms(vehicleExternalInfo),
28
+ kilometers: vehicleExternalInfo.condition?.used
29
+ ? toRound(KM_ROUNDING, vehicleExternalInfo.condition?.odometer?.km, MAX_KM)
30
+ : undefined,
31
+ horsepower: toMinHorsepower(vehicleExternalInfo, -0.1),
32
+ versions: vehicleExternalInfo.general.make != null
33
+ ? [
34
+ {
35
+ make: vehicleExternalInfo.general.make,
36
+ models: toArray(vehicleExternalInfo.general.model),
37
+ },
38
+ ]
39
+ : undefined,
40
+ price: toMaxPrice(vehicleExternalInfo.salesConditions.price),
41
+ age: vehicleExternalInfo.condition?.used
42
+ ? toAge(referenceDate, vehicleExternalInfo.history?.advertisingYear)
43
+ : undefined,
44
+ autoGenerated: true,
45
+ };
46
+ };
47
+ const isHeavyVehicle = (vehicleExternalInfo) => ['Tractor Head', 'Truck'].includes(vehicleExternalInfo.general.category);
48
+ const toCabins = (values) => {
49
+ if (values == null || values.length === 0) {
50
+ return;
51
+ }
52
+ if (values.some((v) => v.startsWith('Sleep'))) {
53
+ return uniq([...values, 'Sleep / Medium', 'Sleep / Large']);
54
+ }
55
+ return values;
56
+ };
57
+ const toSuspensions = (values) => {
58
+ if (values == null || values.length === 0) {
59
+ return;
60
+ }
61
+ if (values.includes('Air/Steel')) {
62
+ return uniq([...values, 'Air']);
63
+ }
64
+ return values;
65
+ };
66
+ const toTransmissions = (values) => {
67
+ if (values == null || values.length === 0) {
68
+ return;
69
+ }
70
+ if (values.includes('Manual')) {
71
+ return;
72
+ }
73
+ if (values.includes('Semi-Automatic')) {
74
+ return uniq([...values, 'Automatic']);
75
+ }
76
+ return values;
77
+ };
78
+ const toEuronorms = (vehicleExternalInfo) => {
79
+ const values = toArray(vehicleExternalInfo.powertrain?.emissions?.class);
80
+ if (values == null || values.length === 0 || !isHeavyVehicle(vehicleExternalInfo)) {
81
+ return;
82
+ }
83
+ const minEuronorm = 5;
84
+ const maxEuronorm = 6;
85
+ const lowestEuronorm = first(values.map((v) => Number(v.charAt(v.length - 1))).sort());
86
+ if (lowestEuronorm < minEuronorm) {
87
+ return;
88
+ }
89
+ return range(lowestEuronorm, maxEuronorm + 1).map((r) => `Euro ${r}`);
90
+ };
91
+ const toAge = (referenceDate, year) => {
92
+ if (year == null) {
93
+ return;
94
+ }
95
+ if (year > referenceDate.getUTCFullYear()) {
96
+ return;
97
+ }
98
+ return toRound(1, referenceDate.getUTCFullYear() - year, MAX_AGE);
99
+ };
100
+ const toMaxPrice = (value) => value != null ? toRound(PRICE_ROUNDING, Number(value) * 1.15, MAX_PRICE) : undefined;
101
+ const toMinHorsepower = (vehicleExternalInfo, shiftRatio) => {
102
+ const hasHorsepower = vehicleExternalInfo.powertrain?.engine?.power?.hp != null;
103
+ const isHorsepowerImportant = isHeavyVehicle(vehicleExternalInfo);
104
+ if (hasHorsepower && isHorsepowerImportant) {
105
+ return toRound(5, vehicleExternalInfo.powertrain.engine.power.hp * (1 + shiftRatio));
106
+ }
107
+ };
108
+ const toRound = (r, value, max = Infinity) => value != null ? Math.min(Math.ceil(Number(value) / r) * r, max) : undefined;
109
+ const toArray = (value) => (value ? [value] : []);
110
+ //# sourceMappingURL=interests.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interests.js","sourceRoot":"/","sources":["utils/interests.ts"],"names":[],"mappings":"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","sourcesContent":["import { isoString } from '@bisondesk/commons-sdk/date-utils';\nimport { generateId } from '@bisondesk/commons-sdk/ids';\nimport { UTCDate } from '@date-fns/utc';\nimport { first, range, uniq } from 'lodash-es';\nimport { KM_ROUNDING, MAX_AGE, MAX_KM, MAX_PRICE, PRICE_ROUNDING } from '../constants.js';\nimport { Interest } from '../types/interests.js';\nimport { VehicleExternalInfo } from '../types/vehicles.js';\n\nexport const mapVehicleToInterest = (\n userId: string,\n vehicleExternalInfo: VehicleExternalInfo,\n referenceDate: Date = new UTCDate()\n): Interest => {\n return {\n id: generateId(),\n createdAt: isoString(),\n createdBy: userId,\n modifiedAt: isoString(),\n modifiedBy: userId,\n bodystyle: vehicleExternalInfo.general.bodystyle,\n cabins: toCabins(toArray(vehicleExternalInfo.body?.cabin?.type)),\n category: vehicleExternalInfo.general.category,\n retarderIntarder: vehicleExternalInfo.accessories.retarderIntarder ? true : undefined,\n adr: vehicleExternalInfo.accessories.adr,\n damaged: vehicleExternalInfo.condition?.damaged ?? false,\n extended: toArray(vehicleExternalInfo.superstructure?.dimensions?.extended),\n heightened: toArray(vehicleExternalInfo.superstructure?.dimensions?.heightened),\n used: vehicleExternalInfo.condition?.used,\n crane: vehicleExternalInfo.superstructure?.crane?.present,\n tailgate: vehicleExternalInfo.superstructure?.tailgate?.present,\n axleConfigurations: toArray(vehicleExternalInfo.powertrain?.axles?.configuration),\n suspensions: toSuspensions(toArray(vehicleExternalInfo.powertrain?.axles?.suspension)),\n transmissions: toTransmissions(toArray(vehicleExternalInfo.powertrain?.transmission?.type)),\n euronorms: toEuronorms(vehicleExternalInfo),\n kilometers: vehicleExternalInfo.condition?.used\n ? toRound(KM_ROUNDING, vehicleExternalInfo.condition?.odometer?.km, MAX_KM)\n : undefined,\n horsepower: toMinHorsepower(vehicleExternalInfo, -0.1),\n versions:\n vehicleExternalInfo.general.make != null\n ? [\n {\n make: vehicleExternalInfo.general.make,\n models: toArray(vehicleExternalInfo.general.model),\n },\n ]\n : undefined,\n price: toMaxPrice(vehicleExternalInfo.salesConditions.price),\n age: vehicleExternalInfo.condition?.used\n ? toAge(referenceDate, vehicleExternalInfo.history?.advertisingYear)\n : undefined,\n autoGenerated: true,\n };\n};\n\nconst isHeavyVehicle = (vehicleExternalInfo: VehicleExternalInfo) =>\n ['Tractor Head', 'Truck'].includes(vehicleExternalInfo.general.category);\n\nconst toCabins = (values: string[] | undefined): string[] | undefined => {\n if (values == null || values.length === 0) {\n return;\n }\n\n if (values.some((v) => v.startsWith('Sleep'))) {\n // If it allows sleep, then add the two largest sleep options as well\n return uniq([...values, 'Sleep / Medium', 'Sleep / Large']);\n }\n\n return values;\n};\n\nconst toSuspensions = (values: string[] | undefined): string[] | undefined => {\n if (values == null || values.length === 0) {\n return;\n }\n\n if (values.includes('Air/Steel')) {\n // If it allows some axles with air suspension, then allow all\n return uniq([...values, 'Air']);\n }\n\n return values;\n};\n\nconst toTransmissions = (values: string[] | undefined): string[] | undefined => {\n if (values == null || values.length === 0) {\n return;\n }\n\n if (values.includes('Manual')) {\n // ignore transmission restriction\n return;\n }\n\n if (values.includes('Semi-Automatic')) {\n // also consider values that are better\n return uniq([...values, 'Automatic']);\n }\n\n return values;\n};\n\nconst toEuronorms = (vehicleExternalInfo: VehicleExternalInfo): string[] | undefined => {\n const values = toArray(vehicleExternalInfo.powertrain?.emissions?.class);\n if (values == null || values.length === 0 || !isHeavyVehicle(vehicleExternalInfo)) {\n return;\n }\n\n const minEuronorm = 5;\n const maxEuronorm = 6;\n\n const lowestEuronorm = first(values.map((v) => Number(v.charAt(v.length - 1))).sort())!;\n if (lowestEuronorm < minEuronorm) {\n return; // ignore Euronorm restriction\n }\n\n return range(lowestEuronorm, maxEuronorm + 1).map((r) => `Euro ${r}`);\n};\n\nconst toAge = (referenceDate: Date, year?: number) => {\n if (year == null) {\n return;\n }\n\n if (year > referenceDate.getUTCFullYear()) {\n return;\n }\n\n return toRound(1, referenceDate.getUTCFullYear() - year, MAX_AGE);\n};\n\nconst toMaxPrice = (value?: number | string) =>\n value != null ? toRound(PRICE_ROUNDING, Number(value) * 1.15, MAX_PRICE) : undefined;\n\nconst toMinHorsepower = (\n vehicleExternalInfo: VehicleExternalInfo,\n shiftRatio: number\n): number | undefined => {\n const hasHorsepower = vehicleExternalInfo.powertrain?.engine?.power?.hp != null;\n const isHorsepowerImportant = isHeavyVehicle(vehicleExternalInfo);\n\n if (hasHorsepower && isHorsepowerImportant) {\n return toRound(5, vehicleExternalInfo.powertrain!.engine!.power!.hp! * (1 + shiftRatio));\n }\n};\n\nconst toRound = (r: number, value?: number | string, max: number = Infinity) =>\n value != null ? Math.min(Math.ceil(Number(value) / r) * r, max) : undefined;\n\nconst toArray = (value?: string): string[] => (value ? [value] : []);\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bisondesk/core-sdk",
3
- "version": "1.0.493",
3
+ "version": "1.0.495",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "scripts": {
package/src/constants.ts CHANGED
@@ -7,6 +7,8 @@ export { BusinessEntityIds } from '@bisondesk/commons-sdk/constants';
7
7
  //
8
8
  export const APP_LANGUAGES_PICKLIST_ID = 'appLanguages';
9
9
  export const CLIENT_LANGUAGES_PICKLIST_ID = 'clientLanguages';
10
+ export const CRM_CONTACT_TAGS_PICKLIST_ID = 'crmContactTags';
11
+ export const CRM_ORG_TAGS_PICKLIST_ID = 'crmOrganizationTags';
10
12
  export const TIMEZONES_PICKLIST_ID = 'timezones';
11
13
  export const COUNTRIES_PICKLIST_ID = 'countries';
12
14
  export const VEHICLE_MAIN_FEATURES_PICKLIST_ID = 'vehicleMainFeatures';
@@ -10,6 +10,8 @@ export type ComparableSpecRange = {
10
10
 
11
11
  export interface ComparableSpecs {
12
12
  id: string;
13
+ createdAt: string;
14
+ createdBy: string;
13
15
  modifiedAt: string;
14
16
  modifiedBy: string;
15
17
 
package/src/types/crm.ts CHANGED
@@ -5,6 +5,7 @@ import {
5
5
  SearchPermissions,
6
6
  } from '@bisondesk/commons-sdk/types';
7
7
  import { PublicSearchDefinition } from './definitions.js';
8
+ import { Interest } from './interests.js';
8
9
  import { BaseEvent } from './internal-events.js';
9
10
  import { ReferenceData } from './utils.js';
10
11
 
@@ -109,6 +110,8 @@ type BaseOrganization = {
109
110
  trustworthy?: boolean;
110
111
  // meta
111
112
  slbPartner?: boolean;
113
+
114
+ tags?: string[]; // custom tags for the organization
112
115
  };
113
116
 
114
117
  export type NewOrganization = BaseOrganization & {
@@ -165,6 +168,8 @@ export type SearchOrganization = {
165
168
  lastActivityAt: string | undefined; // last activity at this organization
166
169
  lastActivityBy: string | undefined; // user that did the last activity at this organization
167
170
  engagementTypes: OrgEngagementType[] | undefined; // types of engagements this organization has in the tenant
171
+
172
+ accountManagers: string[]; // dynamically determined account managers for this organization
168
173
  };
169
174
  };
170
175
 
@@ -177,6 +182,10 @@ export type InputSearchOrganization = SearchOrganization & {
177
182
  export type SearchContact = {
178
183
  contact: Contact;
179
184
  org?: Organization;
185
+ interests?: Interest[];
186
+ custom: {
187
+ accountManagers: string[]; // dynamically determined account managers for this contact
188
+ };
180
189
  };
181
190
 
182
191
  export type InputSearchContact = SearchContact & {
@@ -229,6 +238,8 @@ type BaseContact = {
229
238
  disabledAt?: string;
230
239
  disabledBy?: string;
231
240
  disabledReason?: string;
241
+
242
+ tags?: string[]; // custom tags for the contact
232
243
  };
233
244
 
234
245
  export type NewContact = BaseContact & {
@@ -23,6 +23,14 @@ export interface OpportunityInterest extends Interest {
23
23
  organizationId?: string;
24
24
  }
25
25
 
26
+ export interface ContactInterest extends Interest {
27
+ contactId: string;
28
+ }
29
+
30
+ export interface OrganizationInterest extends Interest {
31
+ organizationId: string;
32
+ }
33
+
26
34
  export interface HdmsInterest extends Interest {
27
35
  hdmsSaleId: string;
28
36
  }
@@ -30,6 +38,12 @@ export interface HdmsInterest extends Interest {
30
38
  export const isOpportunityInterest = (interest: Interest): interest is OpportunityInterest =>
31
39
  'opportunityId' in interest;
32
40
 
41
+ export const isContactInterest = (interest: Interest): interest is ContactInterest =>
42
+ 'contactId' in interest && !isOpportunityInterest(interest);
43
+
44
+ export const isOrganizationInterest = (interest: Interest): interest is OrganizationInterest =>
45
+ 'organizationId' in interest && !isOpportunityInterest(interest) && !isContactInterest(interest);
46
+
33
47
  export type LeadUpdate =
34
48
  | {
35
49
  dismissed: true;
@@ -22,6 +22,7 @@ export type SearchLeasingContract = {
22
22
  insurance?: InsuranceEntry;
23
23
  externalTracking?: boolean;
24
24
  accountManager?: string;
25
+ lastIssuedInvoiceDate?: string;
25
26
  };
26
27
  };
27
28
 
@@ -0,0 +1,67 @@
1
+ import { EmailDestination } from '@bisondesk/commons-sdk/messages';
2
+ import { AttachmentValue, NextRequest } from '@bisondesk/commons-sdk/types';
3
+ import { SearchRequest } from './search.js';
4
+
5
+ export type CampaignSearchRequest = {
6
+ fullText?: string;
7
+ query: NonNullable<SearchRequest['query']>;
8
+ };
9
+
10
+ export type MarketingEmailCampaign = {
11
+ id: string;
12
+ createdAt: string;
13
+ createdBy: string;
14
+ name: string;
15
+
16
+ vehicleIds: string[];
17
+ subject: string;
18
+ message: string;
19
+ pictures?: AttachmentValue[];
20
+
21
+ targets: {
22
+ emails?: EmailDestination[];
23
+ queries?: CampaignSearchRequest[];
24
+ leads?: boolean;
25
+ };
26
+
27
+ runAt?: string;
28
+ // determined on the moment of sending by merging all the targets
29
+ fullEmailList?: string[];
30
+ };
31
+
32
+ export type MarketingCampaignSentEmail = {
33
+ campaignId: string;
34
+ contactId?: string;
35
+ email: string;
36
+ runAt: string;
37
+
38
+ sentAt?: string;
39
+ deliveredAt?: string;
40
+ bouncedAt?: string;
41
+ complaintAt?: string;
42
+ openedAt?: string;
43
+ clickedAt?: string;
44
+ };
45
+
46
+ export type MarketingCampaignEmailEvent = {
47
+ tenantId: string;
48
+ campaignId: string;
49
+ email: string;
50
+ runAt: string;
51
+ contactId?: string;
52
+ name?: string;
53
+ };
54
+
55
+ export type MarketingCampaignStatistics = {
56
+ totalEmails: number;
57
+ successfulDeliveries: number;
58
+ bouncedEmails: number;
59
+ sentEmails: number;
60
+ complaints: number;
61
+ openedEmails: number;
62
+ clickedEmails: number;
63
+ };
64
+
65
+ export type SentEmailsSearchRequest = NextRequest & {
66
+ fullText?: string;
67
+ };
@@ -7,7 +7,7 @@ export enum MarketingPlatform {
7
7
  machineryplanet = 'machineryplanet',
8
8
  machinetrack = 'machinetrack',
9
9
  marktnet = 'marktnet',
10
- mobile = 'mobile',
10
+ // mobile = 'mobile',
11
11
  truck1st = 'truck1st', // Truck1.eu
12
12
  trucks = 'trucks', //Trucks.nl
13
13
  truckscout = 'truckscout24',
@@ -21,6 +21,7 @@ export enum TenantModule {
21
21
  Docs = 'docs',
22
22
  Leads = 'leads',
23
23
  Leasing = 'leasing',
24
+ Marketing = 'marketing',
24
25
  Insights = 'insights',
25
26
  Opportunities = 'opportunities',
26
27
  Offers = 'offers',
@@ -0,0 +1,150 @@
1
+ import { isoString } from '@bisondesk/commons-sdk/date-utils';
2
+ import { generateId } from '@bisondesk/commons-sdk/ids';
3
+ import { UTCDate } from '@date-fns/utc';
4
+ import { first, range, uniq } from 'lodash-es';
5
+ import { KM_ROUNDING, MAX_AGE, MAX_KM, MAX_PRICE, PRICE_ROUNDING } from '../constants.js';
6
+ import { Interest } from '../types/interests.js';
7
+ import { VehicleExternalInfo } from '../types/vehicles.js';
8
+
9
+ export const mapVehicleToInterest = (
10
+ userId: string,
11
+ vehicleExternalInfo: VehicleExternalInfo,
12
+ referenceDate: Date = new UTCDate()
13
+ ): Interest => {
14
+ return {
15
+ id: generateId(),
16
+ createdAt: isoString(),
17
+ createdBy: userId,
18
+ modifiedAt: isoString(),
19
+ modifiedBy: userId,
20
+ bodystyle: vehicleExternalInfo.general.bodystyle,
21
+ cabins: toCabins(toArray(vehicleExternalInfo.body?.cabin?.type)),
22
+ category: vehicleExternalInfo.general.category,
23
+ retarderIntarder: vehicleExternalInfo.accessories.retarderIntarder ? true : undefined,
24
+ adr: vehicleExternalInfo.accessories.adr,
25
+ damaged: vehicleExternalInfo.condition?.damaged ?? false,
26
+ extended: toArray(vehicleExternalInfo.superstructure?.dimensions?.extended),
27
+ heightened: toArray(vehicleExternalInfo.superstructure?.dimensions?.heightened),
28
+ used: vehicleExternalInfo.condition?.used,
29
+ crane: vehicleExternalInfo.superstructure?.crane?.present,
30
+ tailgate: vehicleExternalInfo.superstructure?.tailgate?.present,
31
+ axleConfigurations: toArray(vehicleExternalInfo.powertrain?.axles?.configuration),
32
+ suspensions: toSuspensions(toArray(vehicleExternalInfo.powertrain?.axles?.suspension)),
33
+ transmissions: toTransmissions(toArray(vehicleExternalInfo.powertrain?.transmission?.type)),
34
+ euronorms: toEuronorms(vehicleExternalInfo),
35
+ kilometers: vehicleExternalInfo.condition?.used
36
+ ? toRound(KM_ROUNDING, vehicleExternalInfo.condition?.odometer?.km, MAX_KM)
37
+ : undefined,
38
+ horsepower: toMinHorsepower(vehicleExternalInfo, -0.1),
39
+ versions:
40
+ vehicleExternalInfo.general.make != null
41
+ ? [
42
+ {
43
+ make: vehicleExternalInfo.general.make,
44
+ models: toArray(vehicleExternalInfo.general.model),
45
+ },
46
+ ]
47
+ : undefined,
48
+ price: toMaxPrice(vehicleExternalInfo.salesConditions.price),
49
+ age: vehicleExternalInfo.condition?.used
50
+ ? toAge(referenceDate, vehicleExternalInfo.history?.advertisingYear)
51
+ : undefined,
52
+ autoGenerated: true,
53
+ };
54
+ };
55
+
56
+ const isHeavyVehicle = (vehicleExternalInfo: VehicleExternalInfo) =>
57
+ ['Tractor Head', 'Truck'].includes(vehicleExternalInfo.general.category);
58
+
59
+ const toCabins = (values: string[] | undefined): string[] | undefined => {
60
+ if (values == null || values.length === 0) {
61
+ return;
62
+ }
63
+
64
+ if (values.some((v) => v.startsWith('Sleep'))) {
65
+ // If it allows sleep, then add the two largest sleep options as well
66
+ return uniq([...values, 'Sleep / Medium', 'Sleep / Large']);
67
+ }
68
+
69
+ return values;
70
+ };
71
+
72
+ const toSuspensions = (values: string[] | undefined): string[] | undefined => {
73
+ if (values == null || values.length === 0) {
74
+ return;
75
+ }
76
+
77
+ if (values.includes('Air/Steel')) {
78
+ // If it allows some axles with air suspension, then allow all
79
+ return uniq([...values, 'Air']);
80
+ }
81
+
82
+ return values;
83
+ };
84
+
85
+ const toTransmissions = (values: string[] | undefined): string[] | undefined => {
86
+ if (values == null || values.length === 0) {
87
+ return;
88
+ }
89
+
90
+ if (values.includes('Manual')) {
91
+ // ignore transmission restriction
92
+ return;
93
+ }
94
+
95
+ if (values.includes('Semi-Automatic')) {
96
+ // also consider values that are better
97
+ return uniq([...values, 'Automatic']);
98
+ }
99
+
100
+ return values;
101
+ };
102
+
103
+ const toEuronorms = (vehicleExternalInfo: VehicleExternalInfo): string[] | undefined => {
104
+ const values = toArray(vehicleExternalInfo.powertrain?.emissions?.class);
105
+ if (values == null || values.length === 0 || !isHeavyVehicle(vehicleExternalInfo)) {
106
+ return;
107
+ }
108
+
109
+ const minEuronorm = 5;
110
+ const maxEuronorm = 6;
111
+
112
+ const lowestEuronorm = first(values.map((v) => Number(v.charAt(v.length - 1))).sort())!;
113
+ if (lowestEuronorm < minEuronorm) {
114
+ return; // ignore Euronorm restriction
115
+ }
116
+
117
+ return range(lowestEuronorm, maxEuronorm + 1).map((r) => `Euro ${r}`);
118
+ };
119
+
120
+ const toAge = (referenceDate: Date, year?: number) => {
121
+ if (year == null) {
122
+ return;
123
+ }
124
+
125
+ if (year > referenceDate.getUTCFullYear()) {
126
+ return;
127
+ }
128
+
129
+ return toRound(1, referenceDate.getUTCFullYear() - year, MAX_AGE);
130
+ };
131
+
132
+ const toMaxPrice = (value?: number | string) =>
133
+ value != null ? toRound(PRICE_ROUNDING, Number(value) * 1.15, MAX_PRICE) : undefined;
134
+
135
+ const toMinHorsepower = (
136
+ vehicleExternalInfo: VehicleExternalInfo,
137
+ shiftRatio: number
138
+ ): number | undefined => {
139
+ const hasHorsepower = vehicleExternalInfo.powertrain?.engine?.power?.hp != null;
140
+ const isHorsepowerImportant = isHeavyVehicle(vehicleExternalInfo);
141
+
142
+ if (hasHorsepower && isHorsepowerImportant) {
143
+ return toRound(5, vehicleExternalInfo.powertrain!.engine!.power!.hp! * (1 + shiftRatio));
144
+ }
145
+ };
146
+
147
+ const toRound = (r: number, value?: number | string, max: number = Infinity) =>
148
+ value != null ? Math.min(Math.ceil(Number(value) / r) * r, max) : undefined;
149
+
150
+ const toArray = (value?: string): string[] => (value ? [value] : []);