@bisondesk/core-sdk 1.0.494 → 1.0.496

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 (67) hide show
  1. package/lib/constants.d.ts +2 -2
  2. package/lib/constants.d.ts.map +1 -1
  3. package/lib/constants.js +2 -2
  4. package/lib/constants.js.map +1 -1
  5. package/lib/types/activities.d.ts +5 -15
  6. package/lib/types/activities.d.ts.map +1 -1
  7. package/lib/types/activities.js +0 -1
  8. package/lib/types/activities.js.map +1 -1
  9. package/lib/types/comparables.d.ts +2 -0
  10. package/lib/types/comparables.d.ts.map +1 -1
  11. package/lib/types/comparables.js.map +1 -1
  12. package/lib/types/crm.d.ts +8 -0
  13. package/lib/types/crm.d.ts.map +1 -1
  14. package/lib/types/crm.js.map +1 -1
  15. package/lib/types/interests.d.ts +8 -0
  16. package/lib/types/interests.d.ts.map +1 -1
  17. package/lib/types/interests.js +2 -0
  18. package/lib/types/interests.js.map +1 -1
  19. package/lib/types/internal-events.d.ts +1 -2
  20. package/lib/types/internal-events.d.ts.map +1 -1
  21. package/lib/types/internal-events.js +0 -1
  22. package/lib/types/internal-events.js.map +1 -1
  23. package/lib/types/journeys.d.ts +0 -6
  24. package/lib/types/journeys.d.ts.map +1 -1
  25. package/lib/types/journeys.js.map +1 -1
  26. package/lib/types/leasing-debtors.d.ts +0 -202
  27. package/lib/types/leasing-debtors.d.ts.map +1 -1
  28. package/lib/types/leasing-debtors.js +1 -58
  29. package/lib/types/leasing-debtors.js.map +1 -1
  30. package/lib/types/leasing.d.ts +22 -1
  31. package/lib/types/leasing.d.ts.map +1 -1
  32. package/lib/types/leasing.js.map +1 -1
  33. package/lib/types/marketing-emails.d.ts +57 -0
  34. package/lib/types/marketing-emails.d.ts.map +1 -0
  35. package/lib/types/marketing-emails.js +2 -0
  36. package/lib/types/marketing-emails.js.map +1 -0
  37. package/lib/types/tenants.d.ts +1 -0
  38. package/lib/types/tenants.d.ts.map +1 -1
  39. package/lib/types/tenants.js +1 -0
  40. package/lib/types/tenants.js.map +1 -1
  41. package/lib/utils/interests.d.ts +4 -0
  42. package/lib/utils/interests.d.ts.map +1 -0
  43. package/lib/utils/interests.js +110 -0
  44. package/lib/utils/interests.js.map +1 -0
  45. package/lib/utils/search.d.ts +2 -3
  46. package/lib/utils/search.d.ts.map +1 -1
  47. package/lib/utils/search.js.map +1 -1
  48. package/package.json +1 -1
  49. package/src/constants.ts +2 -2
  50. package/src/types/activities.ts +4 -49
  51. package/src/types/comparables.ts +2 -0
  52. package/src/types/crm.ts +11 -0
  53. package/src/types/interests.ts +14 -0
  54. package/src/types/internal-events.ts +0 -1
  55. package/src/types/journeys.ts +0 -7
  56. package/src/types/leasing-debtors.ts +0 -309
  57. package/src/types/leasing.ts +20 -0
  58. package/src/types/marketing-emails.ts +67 -0
  59. package/src/types/tenants.ts +1 -0
  60. package/src/utils/interests.ts +150 -0
  61. package/src/utils/search.ts +2 -9
  62. package/tsconfig.tsbuildinfo +1 -1
  63. package/lib/utils/debtors-dossiers.d.ts +0 -15
  64. package/lib/utils/debtors-dossiers.d.ts.map +0 -1
  65. package/lib/utils/debtors-dossiers.js +0 -51
  66. package/lib/utils/debtors-dossiers.js.map +0 -1
  67. package/src/utils/debtors-dossiers.ts +0 -73
@@ -1,9 +1,4 @@
1
- import { EmailMessage } from '@bisondesk/commons-sdk/messages';
2
- import { NextList, PaginatedList } from '@bisondesk/commons-sdk/types';
3
1
  import { Contact, Organization } from './crm.js';
4
- import { LeasingContract } from './leasing.js';
5
- import { BaseSearchRequest, SortFilter } from './search.js';
6
- import { DataRecord, ReferenceData } from './utils.js';
7
2
 
8
3
  export type MonthData = {
9
4
  activeContractIds: string[];
@@ -38,307 +33,3 @@ export type SearchDebtorInfo = {
38
33
  hasActiveContracts: boolean;
39
34
  };
40
35
  };
41
-
42
- export enum DebtorsDossierStatus {
43
- AUTO_REMINDER = 'auto_reminder',
44
- OVERDUE = 'overdue',
45
- CONTACT_ATTEMPTED = 'contact_attempted',
46
- DIALOGUE = 'dialogue',
47
- PROMISE_TO_PAY = 'promise_to_pay',
48
- FAILURE = 'failure',
49
- RESOLVED = 'resolved',
50
- }
51
-
52
- export const debtorsDossierStatusToDateFieldMap: Record<
53
- DebtorsDossierStatus,
54
- { date: keyof DossierStatusLogFields; author: keyof DossierStatusLogFields } | null
55
- > = {
56
- [DebtorsDossierStatus.AUTO_REMINDER]: null,
57
- [DebtorsDossierStatus.OVERDUE]: { date: 'overdueAt', author: 'overdueBy' },
58
- [DebtorsDossierStatus.CONTACT_ATTEMPTED]: { date: 'contactedAt', author: 'contactedBy' },
59
- [DebtorsDossierStatus.DIALOGUE]: { date: 'dialogueAt', author: 'dialogueBy' },
60
- [DebtorsDossierStatus.PROMISE_TO_PAY]: { date: 'promiseToPayAt', author: 'promiseToPayBy' },
61
- [DebtorsDossierStatus.FAILURE]: { date: 'failedAt', author: 'failedBy' },
62
- [DebtorsDossierStatus.RESOLVED]: { date: 'resolvedAt', author: 'resolvedBy' },
63
- };
64
-
65
- /******
66
- * Auto Follow-Up Types
67
- ******/
68
-
69
- export type DebtorAutoFollowUp = {
70
- id: string;
71
- organizationId: string;
72
- organizationName: string;
73
- status: 'ONGOING' | 'ERROR' | 'SUCCESS' | 'EXPIRED' | 'DISCARDED';
74
- emails: EmailMessage[];
75
- createdAt: string;
76
- modifiedAt: string;
77
- completedAt?: string;
78
- nextActionAt: string;
79
- riskLevel: number;
80
- };
81
-
82
- export type ListOverdueRemindersResponse = PaginatedList<DebtorAutoFollowUp, ReferenceData>;
83
- export type ListOverdueRemindersRequest = {
84
- offset: number;
85
- limit: number;
86
- sortBy: SortFilter;
87
- };
88
-
89
- /******
90
- * Debtor Dossier Types
91
- ******/
92
-
93
- export enum DebtorsDossierActions {
94
- SET_METADATA = 'set_metadata',
95
- ADD_ACTIVITY = 'add_activity',
96
- FINISH = 'finish',
97
- SET_DOCUMENTS = 'set_documents',
98
- SET_PAYMENT_PLAN = 'set_payment_plan',
99
- SET_LEGAL_DOSSIER = 'set_legal_dossier',
100
- }
101
-
102
- export type DossierStatusLogFields = {
103
- failedAt?: string;
104
- failedBy?: string;
105
- resolvedAt?: string;
106
- contactedAt?: string;
107
- contactedBy?: string;
108
- overdueAt?: string;
109
- overdueBy?: string;
110
- resolvedBy?: string;
111
- dialogueAt?: string;
112
- dialogueBy?: string;
113
- promiseToPayAt?: string;
114
- promiseToPayBy?: string;
115
- };
116
-
117
- export type BaseDebtorsDossier = {
118
- organizationId: string;
119
- contactId?: string;
120
- tags: string[];
121
- origin: 'manual' | 'auto';
122
- autoFollowUpId?: string;
123
- administrativeUserId?: string;
124
- branchId: string;
125
- tenantId: string;
126
- };
127
- export type DebtorsDossierStatusAudit = {
128
- from: DebtorsDossierStatus;
129
- to: DebtorsDossierStatus;
130
- changedAt: string;
131
- changedBy: string;
132
- };
133
-
134
- export type DebtorsDossier = BaseDebtorsDossier &
135
- DossierStatusLogFields & {
136
- id: string;
137
- createdAt: string;
138
- createdBy: string;
139
- updatedAt: string;
140
- updatedBy: string;
141
- status: DebtorsDossierStatus;
142
- statusAudit: DebtorsDossierStatusAudit[];
143
- };
144
-
145
- export type NewDebtorsDossier = BaseDebtorsDossier & {
146
- id?: undefined;
147
- createdBy?: undefined;
148
- createdAt?: undefined;
149
- updatedBy?: undefined;
150
- updatedAt?: undefined;
151
- };
152
-
153
- export type DebtorsDossierBff = {
154
- dossier: DataRecord<DebtorsDossier>;
155
- organization?: Organization;
156
- contact?: Contact;
157
- leasingContracts: LeasingContract[];
158
- debt?: DebtorInfo;
159
- paymentPlan?: DebtorsPaymentPlan;
160
- legalDossier?: LegalDossier;
161
- trackAndTraceDevices: {
162
- /* odometer: TrackAndTraceOdometerData;
163
- device: TrackAndTraceDevice; */
164
- }[];
165
- };
166
-
167
- export type UpdateDebtorsDossier = Partial<
168
- Pick<
169
- DebtorsDossier,
170
- 'status' | 'tags' | 'contactId' | 'administrativeUserId' | 'branchId' | 'organizationId'
171
- >
172
- > & {
173
- overdue?: boolean;
174
- resolve?: boolean;
175
- terminate?: boolean;
176
- };
177
-
178
- /******
179
- * Payment Plan Types
180
- ******/
181
-
182
- export type DebtorsPaymentPlan = {
183
- id: string;
184
- debtorDossierId: string;
185
-
186
- notes?: string;
187
-
188
- reminders?: {
189
- frequency: {
190
- every: number;
191
- period: 'days' | 'weeks' | 'months';
192
- };
193
- endCondition:
194
- | {
195
- endDate: string; // ISO date string
196
- }
197
- | {
198
- occurrences: number; // Number of reminders to send
199
- };
200
- };
201
-
202
- createdAt: string;
203
- createdBy: string;
204
- updatedAt: string;
205
- updatedBy: string;
206
- };
207
-
208
- export type NewPaymentPlan = Omit<
209
- DebtorsPaymentPlan,
210
- 'id' | 'createdAt' | 'updatedAt' | 'createdBy' | 'updatedBy'
211
- >;
212
-
213
- export type UpdatePaymentPlan = Partial<Pick<DebtorsPaymentPlan, 'notes' | 'reminders'>>;
214
-
215
- /******
216
- * Legal Dossier Types
217
- ******/
218
-
219
- export enum LegalDossierStatus {
220
- ASSESSMENT = 'assessment',
221
- PRE_LEGAL_NOTICE = 'pre_legal_notice',
222
- SENT_TO_LAWYER = 'sent_to_lawyer',
223
- FILED = 'filed',
224
- IN_COURT = 'in_court',
225
- JUDGMENT = 'judgment',
226
- ENFORCEMENT = 'enforcement',
227
- SETTLEMENT = 'settlement',
228
- CLOSED_RECOVERED = 'closed_recovered',
229
- CLOSED_UNRECOVERABLE = 'closed_unrecoverable',
230
- ON_HOLD = 'on_hold',
231
- }
232
-
233
- export type LegalDossier = {
234
- id: string;
235
- debtorDossierId: string;
236
- caseNumber?: string;
237
- notes?: string;
238
- createdAt: string;
239
- createdBy: string;
240
- updatedAt: string;
241
- updatedBy: string;
242
- status: LegalDossierStatus;
243
- };
244
-
245
- export type NewLegalDossier = Omit<
246
- LegalDossier,
247
- 'id' | 'createdAt' | 'updatedAt' | 'createdBy' | 'updatedBy'
248
- >;
249
-
250
- export type UpdateLegalDossier = Partial<Pick<LegalDossier, 'caseNumber' | 'status' | 'notes'>>;
251
-
252
- /******
253
- * Debtor Dossier Events
254
- ******/
255
-
256
- export type DebtorsDossierCreateEvent = {
257
- action: 'created';
258
- data: DebtorsDossier;
259
- previousData?: undefined;
260
- tenantId: string;
261
- userId: string;
262
- };
263
-
264
- export type DebtorsDossierUpdateEvent = {
265
- action: 'updated';
266
- data: DebtorsDossier;
267
- previousData: DebtorsDossier;
268
- tenantId: string;
269
- userId: string;
270
- };
271
-
272
- export type DebtorsDossierDeleteEvent = {
273
- action: 'deleted';
274
- data?: undefined;
275
- previousData: DebtorsDossier;
276
- tenantId: string;
277
- userId: string;
278
- };
279
-
280
- export type DebtorsDossierEvent =
281
- | DebtorsDossierCreateEvent
282
- | DebtorsDossierUpdateEvent
283
- | DebtorsDossierDeleteEvent;
284
-
285
- /************
286
- *
287
- * Search
288
- */
289
-
290
- export type SearchDebtorsDossier = {
291
- contact?: Contact;
292
- debtorsDossier: DebtorsDossier;
293
- org?: Organization;
294
- legalDossier?: LegalDossier;
295
- paymentPlan?: DebtorsPaymentPlan;
296
- };
297
-
298
- /************
299
- *
300
- * Kanban
301
- */
302
-
303
- export type DebtorsDossierKanbanColumn = {
304
- status: string;
305
- results: SearchDebtorsDossier[];
306
- totalCount: number;
307
- totalValue: string;
308
- };
309
-
310
- export type MSearchDebtorsDossierKanbanColumn = {
311
- column: DebtorsDossierKanbanColumn;
312
- nextToken?: string;
313
- };
314
-
315
- export type KanbanDebtorsDossierSearchRequest = BaseSearchRequest & {
316
- nextTokens?: Record<string, string | undefined>;
317
- };
318
-
319
- export type KanbanDebtorsDossierNextList = Omit<
320
- NextList<DebtorsDossierKanbanColumn, ReferenceData>,
321
- 'nextToken'
322
- > & {
323
- nextTokens?: Record<string, string | undefined>;
324
- };
325
-
326
- export const DebtorsDossierStatusOrder: DebtorsDossierStatus[] = [
327
- DebtorsDossierStatus.AUTO_REMINDER,
328
- DebtorsDossierStatus.OVERDUE,
329
- DebtorsDossierStatus.CONTACT_ATTEMPTED,
330
- DebtorsDossierStatus.DIALOGUE,
331
- DebtorsDossierStatus.PROMISE_TO_PAY,
332
- ];
333
-
334
- export const DebtorsDossierWonColumnId = DebtorsDossierStatus.RESOLVED;
335
-
336
- export const DebtorsDossierKanbanEndColumns: string[] = [
337
- DebtorsDossierStatus.RESOLVED,
338
- DebtorsDossierStatus.FAILURE,
339
- ];
340
-
341
- export const DebtorsDossierKanbanColumns = [
342
- ...DebtorsDossierStatusOrder,
343
- ...DebtorsDossierKanbanEndColumns,
344
- ];
@@ -42,6 +42,19 @@ export type LeasingConditionEvent = {
42
42
  tenantId: string;
43
43
  };
44
44
 
45
+ export type OverdueDossier = {
46
+ id: string;
47
+ organizationId: string;
48
+ organizationName: string;
49
+ status: 'ONGOING' | 'ERROR' | 'SUCCESS' | 'EXPIRED' | 'DISCARDED';
50
+ emails: EmailMessage[];
51
+ createdAt: string;
52
+ modifiedAt: string;
53
+ completedAt?: string;
54
+ nextActionAt: string;
55
+ riskLevel: number;
56
+ };
57
+
45
58
  export type LeasingContractClient = {
46
59
  organizationId: string;
47
60
  contactIds: string[];
@@ -180,6 +193,13 @@ export type OpportunityRef = BaseRef & {
180
193
  //Since VAT is client specific and contracts can be transferred between clients, we always handle values without VAT
181
194
  };
182
195
 
196
+ export type ListOverdueRemindersResponse = PaginatedList<OverdueDossier, ReferenceData>;
197
+ export type ListOverdueRemindersRequest = {
198
+ offset: number;
199
+ limit: number;
200
+ sortBy: SortFilter;
201
+ };
202
+
183
203
  export type NewLeasingConditions = {
184
204
  parameters: {
185
205
  yearlyRoadTax: string;
@@ -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
+ };
@@ -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] : []);
@@ -1,24 +1,17 @@
1
1
  import { joinWithSeparator } from '@bisondesk/commons-sdk/formatting';
2
2
  import { BusinessEntityIds } from '../constants.js';
3
3
  import { Contact, Organization, SearchOrganization } from '../types/crm.js';
4
- import { DebtorsDossier, SearchDebtorsDossier } from '../types/leasing-debtors.js';
5
4
  import { SearchLeasingContract } from '../types/leasing-search.js';
6
5
  import { LeasingContract } from '../types/leasing.js';
7
6
  import { Opportunity, SearchOpportunity } from '../types/opportunities.js';
8
7
  import { SearchVehicle, Vehicle } from '../types/vehicles.js';
9
8
 
10
- export type GlobalBusinessEntity =
11
- | Opportunity
12
- | Vehicle
13
- | Organization
14
- | LeasingContract
15
- | DebtorsDossier;
9
+ export type GlobalBusinessEntity = Opportunity | Vehicle | Organization | LeasingContract;
16
10
  export type GlobalSearchEntity =
17
11
  | SearchOpportunity
18
12
  | SearchVehicle
19
13
  | SearchOrganization
20
- | SearchLeasingContract
21
- | SearchDebtorsDossier;
14
+ | SearchLeasingContract;
22
15
 
23
16
  export const getCustomerInformation = (org?: Organization, contact?: Contact) => {
24
17
  if (org != null) {