@fragus/sam-types 1.1.6 → 1.1.7
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 +1 -1
- package/types/accessKey/index.d.ts +0 -5
- package/types/admin/contractCreation/contractCreation.d.ts +17 -0
- package/types/admin/contractCreation/priceCalculation.d.ts +15 -0
- package/types/admin/vehicleBlacklist/index.d.ts +2 -16
- package/types/contractProviderFlags.d.ts +3 -7
- package/types/contractTemplate.d.ts +73 -1
- package/types/dashboardBanner.d.ts +17 -24
- package/types/emailCampaign.d.ts +0 -7
- package/types/emailCampaign.ts +2 -6
- package/types/index.d.ts +1 -1
- package/types/index.js +1 -0
- package/types/index.js.map +1 -1
- package/types/index.ts +1 -1
- package/types/pricePrediction.d.ts +219 -0
- package/types/pricePrediction.js +84 -0
- package/types/pricePrediction.js.map +1 -0
- package/types/pricePrediction.ts +320 -0
- package/types/providerDocument.d.ts +1 -8
- package/types/responseErrors/index.d.ts +0 -2
- package/types/contractProviderGroup.d.ts +0 -58
- package/types/responseErrors/contractProviderGroupErrors.d.ts +0 -4
package/package.json
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { AccessKeyPermission, AccessKeyService, TIsoCountry } from '../index'
|
|
2
2
|
|
|
3
|
-
/** How an access key chooses its providers: country-scoped, manually scoped, or group-scoped. */
|
|
4
|
-
export type TAccessKeyScopeMode = 'all' | 'manual' | 'group'
|
|
5
|
-
|
|
6
3
|
export interface IAccessKey {
|
|
7
4
|
accessKeyId: number
|
|
8
5
|
apiKey: string
|
|
@@ -13,7 +10,5 @@ export interface IAccessKey {
|
|
|
13
10
|
contractProviderIds: number[]
|
|
14
11
|
allProvidersAllowed: boolean
|
|
15
12
|
isoCountries: TIsoCountry[] // scope for allProvidersAllowed; non-empty when it is true
|
|
16
|
-
scopeMode: TAccessKeyScopeMode
|
|
17
|
-
contractProviderGroupId: number | null // set only in 'group' mode; resolved live at request time
|
|
18
13
|
description?: string
|
|
19
14
|
}
|
|
@@ -150,6 +150,23 @@ interface ICommonContractCreationRequest extends ICommonContractUpdateRequest {
|
|
|
150
150
|
invoiceCustomerId?: number
|
|
151
151
|
invoiceCustomer?: IAdminCustomer
|
|
152
152
|
isDownpaymentDistributed: boolean
|
|
153
|
+
/**
|
|
154
|
+
* What the seller agreed one service would cost, sent only where they put it on the agreement:
|
|
155
|
+
* the template proposes a basis, they adjust it, and an unticked one is not an agreement.
|
|
156
|
+
* Money is ex VAT, in the contract's currency.
|
|
157
|
+
*/
|
|
158
|
+
serviceCostOilPricePerLitre?: number | null
|
|
159
|
+
serviceCostOilLitresPerService?: number | null
|
|
160
|
+
serviceCostLabourRatePerHour?: number | null
|
|
161
|
+
serviceCostLabourHoursPerService?: number | null
|
|
162
|
+
/** How many services the seller expects over the term; what the cost per service is multiplied by. */
|
|
163
|
+
serviceCostNumberOfServices?: number | null
|
|
164
|
+
/**
|
|
165
|
+
* Replacing the HV traction battery, where the template covers it and the vehicle is electric.
|
|
166
|
+
* Once
|
|
167
|
+
* over the term rather than once per service: a battery is not a service item.
|
|
168
|
+
*/
|
|
169
|
+
serviceCostBattery?: number | null
|
|
153
170
|
}
|
|
154
171
|
|
|
155
172
|
export interface IContractAdjustmentRequest extends ICommonContractUpdateRequest {
|
|
@@ -80,4 +80,19 @@ export interface IContractCalculationResponse {
|
|
|
80
80
|
providerShare: PriceSpecification
|
|
81
81
|
x_debugData?: any // For debugging purposes.
|
|
82
82
|
serviceInfo?: string[] // Service information, mainly for ax.
|
|
83
|
+
/**
|
|
84
|
+
* Present only when the price came from a price prediction template. Caveats are codes with
|
|
85
|
+
* parameters rather than sentences, so the client renders them in the reader's own language;
|
|
86
|
+
* a code the client does not know can be dropped safely, which lets the API add more freely.
|
|
87
|
+
*/
|
|
88
|
+
pricePrediction?: {
|
|
89
|
+
/**
|
|
90
|
+
* Which source answered. `AI` means nothing comparable has ever been sold on this kind of
|
|
91
|
+
* vehicle, so the figure is reasoned from the vehicle itself rather than from what we charged.
|
|
92
|
+
*/
|
|
93
|
+
source?: 'Comparables' | 'AI'
|
|
94
|
+
confidence: 'high' | 'medium' | 'low'
|
|
95
|
+
comparableCount?: number
|
|
96
|
+
caveats?: Array<{ code: string; params?: { [key: string]: string | number } }>
|
|
97
|
+
}
|
|
83
98
|
}
|
|
@@ -6,10 +6,6 @@ export interface IBlacklistedVehicleRequest {
|
|
|
6
6
|
add: number[]
|
|
7
7
|
remove: number[]
|
|
8
8
|
}
|
|
9
|
-
contractProviderGroupIds?: {
|
|
10
|
-
add: number[]
|
|
11
|
-
remove: number[]
|
|
12
|
-
}
|
|
13
9
|
}
|
|
14
10
|
|
|
15
11
|
export interface IBlacklistedVehicleResponse {
|
|
@@ -18,21 +14,11 @@ export interface IBlacklistedVehicleResponse {
|
|
|
18
14
|
model: string
|
|
19
15
|
fuelType: string
|
|
20
16
|
parentRecord?: boolean
|
|
21
|
-
groupRecord?: boolean
|
|
22
17
|
}
|
|
23
18
|
|
|
24
19
|
export interface IBlacklistedVehicleListRecord extends IBlacklistedVehicleResponse {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
providerName: string | null
|
|
28
|
-
contractProviderGroupId: number | null
|
|
29
|
-
contractProviderGroupName: string | null
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/** Which providers and groups a blacklisted vehicle is registered for. */
|
|
33
|
-
export interface IBlacklistedVehicleScope {
|
|
34
|
-
providerIds: number[]
|
|
35
|
-
contractProviderGroupIds: number[]
|
|
20
|
+
providerId: number
|
|
21
|
+
providerName: string
|
|
36
22
|
}
|
|
37
23
|
|
|
38
24
|
export type BlacklistedVehicleOrderByType = 'id' | 'brand' | 'model' | 'fuelType'
|
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
export type TContractProviderFlagKey = 'RENAULT_UNIQ_PDF_FORMAT'
|
|
2
2
|
|
|
3
|
-
/** Exactly one of the two ids is sent: a flag row is owned by a provider or by a group. */
|
|
4
3
|
export interface ContractProviderFlagRequest {
|
|
5
|
-
contractProviderId
|
|
6
|
-
contractProviderGroupId?: number
|
|
4
|
+
contractProviderId: number
|
|
7
5
|
}
|
|
8
6
|
|
|
9
7
|
export interface ContractProviderFlagResponse {
|
|
10
8
|
id: number
|
|
11
9
|
featureKey: TContractProviderFlagKey
|
|
12
|
-
contractProviderId: number
|
|
13
|
-
contractProviderName: string
|
|
14
|
-
contractProviderGroupId: number | null
|
|
15
|
-
contractProviderGroupName: string | null
|
|
10
|
+
contractProviderId: number
|
|
11
|
+
contractProviderName: string
|
|
16
12
|
updatedByUser: string
|
|
17
13
|
}
|
|
@@ -4,7 +4,7 @@ import { PriceSpecification } from './priceSpecification'
|
|
|
4
4
|
import { ITermsOfServiceRequest, ITermsOfServiceResponse } from './termsOfService'
|
|
5
5
|
import { TV4PTPaymentType, TV4PTProductType } from './v4PricingTool'
|
|
6
6
|
|
|
7
|
-
export type PriceSource = 'Pricelist' | 'Autoexperten' | 'V4PricingTool'
|
|
7
|
+
export type PriceSource = 'Pricelist' | 'Autoexperten' | 'V4PricingTool' | 'PricePrediction'
|
|
8
8
|
|
|
9
9
|
export interface IContractTemplateCloneRequest {
|
|
10
10
|
templateIds: number[]
|
|
@@ -34,6 +34,42 @@ export interface IGenericContractTemplateRequest {
|
|
|
34
34
|
overdrivenCharge: PriceSpecification
|
|
35
35
|
totalPriceFactorInTenthOfCents: number | null
|
|
36
36
|
disableServiceInfoFile: boolean | null
|
|
37
|
+
/**
|
|
38
|
+
* Price prediction templates only: the terms that may be sold, in months. Whole years only
|
|
39
|
+
* (12, 24, ... ), never above 84. There is no price list to read the available terms off, so
|
|
40
|
+
* they come from here instead; leave empty to offer every term up to 84.
|
|
41
|
+
*/
|
|
42
|
+
predictionDurationMonths: number[] | null
|
|
43
|
+
/**
|
|
44
|
+
* Price prediction templates only: the mileage allowances that may be sold, in km over the whole
|
|
45
|
+
* agreement. The same allowances are offered whatever the term, which is how price lists work.
|
|
46
|
+
*/
|
|
47
|
+
predictionMileageKm: number[] | null
|
|
48
|
+
/**
|
|
49
|
+
* Price prediction templates only: the fuel types this template may be sold on. Empty or
|
|
50
|
+
* absent means every fuel; the vehicle lookup drops the template for anything not listed.
|
|
51
|
+
*/
|
|
52
|
+
predictionFuelTypes: string[] | null
|
|
53
|
+
/**
|
|
54
|
+
* Price prediction templates only: what one service is expected to cost, used as the starting
|
|
55
|
+
* point an operator adjusts from. Money is ex VAT, in the provider's currency.
|
|
56
|
+
*/
|
|
57
|
+
predictionOilPricePerLitre: number | null
|
|
58
|
+
predictionOilLitresPerService: number | null
|
|
59
|
+
predictionLabourRatePerHour: number | null
|
|
60
|
+
predictionLabourHoursPerService: number | null
|
|
61
|
+
/**
|
|
62
|
+
* Price prediction templates only: whether this agreement covers replacing the HV traction
|
|
63
|
+
* battery of an electric vehicle, never the 12 V battery. Off unless a provider deliberately turns it on -- it is the
|
|
64
|
+
* one bill that dwarfs everything else an agreement covers.
|
|
65
|
+
*/
|
|
66
|
+
predictionCoversBattery: boolean | null
|
|
67
|
+
/**
|
|
68
|
+
* Price prediction templates only: what replacing that HV traction battery is expected to cost,
|
|
69
|
+
* ex VAT in
|
|
70
|
+
* the provider's currency. Only meaningful where the cover is on.
|
|
71
|
+
*/
|
|
72
|
+
predictionBatteryCost: number | null
|
|
37
73
|
}
|
|
38
74
|
|
|
39
75
|
export interface IContractTemplateRequest extends IGenericContractTemplateRequest {
|
|
@@ -74,6 +110,42 @@ export interface IGenericContractTemplateResponse {
|
|
|
74
110
|
maxEndMileage?: number
|
|
75
111
|
totalPriceFactorInTenthOfCents: number | null
|
|
76
112
|
disableServiceInfoFile: boolean | null
|
|
113
|
+
/**
|
|
114
|
+
* Price prediction templates only: the terms that may be sold, in months. Whole years only
|
|
115
|
+
* (12, 24, ... ), never above 84. There is no price list to read the available terms off, so
|
|
116
|
+
* they come from here instead; leave empty to offer every term up to 84.
|
|
117
|
+
*/
|
|
118
|
+
predictionDurationMonths: number[] | null
|
|
119
|
+
/**
|
|
120
|
+
* Price prediction templates only: the mileage allowances that may be sold, in km over the whole
|
|
121
|
+
* agreement. The same allowances are offered whatever the term, which is how price lists work.
|
|
122
|
+
*/
|
|
123
|
+
predictionMileageKm: number[] | null
|
|
124
|
+
/**
|
|
125
|
+
* Price prediction templates only: the fuel types this template may be sold on. Empty or
|
|
126
|
+
* absent means every fuel; the vehicle lookup drops the template for anything not listed.
|
|
127
|
+
*/
|
|
128
|
+
predictionFuelTypes: string[] | null
|
|
129
|
+
/**
|
|
130
|
+
* Price prediction templates only: what one service is expected to cost, used as the starting
|
|
131
|
+
* point an operator adjusts from. Money is ex VAT, in the provider's currency.
|
|
132
|
+
*/
|
|
133
|
+
predictionOilPricePerLitre: number | null
|
|
134
|
+
predictionOilLitresPerService: number | null
|
|
135
|
+
predictionLabourRatePerHour: number | null
|
|
136
|
+
predictionLabourHoursPerService: number | null
|
|
137
|
+
/**
|
|
138
|
+
* Price prediction templates only: whether this agreement covers replacing the HV traction
|
|
139
|
+
* battery of an electric vehicle, never the 12 V battery. Off unless a provider deliberately turns it on -- it is the
|
|
140
|
+
* one bill that dwarfs everything else an agreement covers.
|
|
141
|
+
*/
|
|
142
|
+
predictionCoversBattery: boolean | null
|
|
143
|
+
/**
|
|
144
|
+
* Price prediction templates only: what replacing that HV traction battery is expected to cost,
|
|
145
|
+
* ex VAT in
|
|
146
|
+
* the provider's currency. Only meaningful where the cover is on.
|
|
147
|
+
*/
|
|
148
|
+
predictionBatteryCost: number | null
|
|
77
149
|
}
|
|
78
150
|
|
|
79
151
|
export interface IContractTemplateResponse extends IGenericContractTemplateResponse {
|
|
@@ -1,39 +1,32 @@
|
|
|
1
|
-
import { TIsoCountry } from './country'
|
|
2
|
-
|
|
1
|
+
import { TIsoCountry } from './country';
|
|
3
2
|
/**
|
|
4
3
|
* Request body for creating or updating a dashboard banner (Superadmin)
|
|
5
4
|
*/
|
|
6
5
|
export interface DashboardBannerRequest {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
startDate: string
|
|
18
|
-
/**
|
|
19
|
-
* ISO date string (YYYY-MM-DD) for the end date of the banner
|
|
20
|
-
*/
|
|
21
|
-
endDate: string
|
|
6
|
+
text: string;
|
|
7
|
+
countryIso: TIsoCountry;
|
|
8
|
+
/**
|
|
9
|
+
* ISO date string (YYYY-MM-DD) for the start date of the banner
|
|
10
|
+
*/
|
|
11
|
+
startDate: string;
|
|
12
|
+
/**
|
|
13
|
+
* ISO date string (YYYY-MM-DD) for the end date of the banner
|
|
14
|
+
*/
|
|
15
|
+
endDate: string;
|
|
22
16
|
}
|
|
23
17
|
/**
|
|
24
18
|
* Response body for retrieving a dashboard banner (Superadmin)
|
|
25
19
|
*/
|
|
26
20
|
export interface DashboardBannerResponse extends DashboardBannerRequest {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
updatedByUser: string
|
|
21
|
+
id: number;
|
|
22
|
+
/**
|
|
23
|
+
* Email of the user who last updated the banner
|
|
24
|
+
*/
|
|
25
|
+
updatedByUser: string;
|
|
33
26
|
}
|
|
34
27
|
/**
|
|
35
28
|
* Response body for retrieving dashboard banner text in Admin
|
|
36
29
|
*/
|
|
37
30
|
export interface ProviderDashboardBannerResponse {
|
|
38
|
-
|
|
31
|
+
text: string;
|
|
39
32
|
}
|
package/types/emailCampaign.d.ts
CHANGED
|
@@ -10,19 +10,12 @@ export declare enum EmailCampaignStatus {
|
|
|
10
10
|
}
|
|
11
11
|
export declare type EmailCampaignRole = 'admin' | 'seller';
|
|
12
12
|
export declare type EmailCampaignSelectionMode = 'all' | 'subset';
|
|
13
|
-
export declare type EmailCampaignProviderMode = EmailCampaignSelectionMode | 'group';
|
|
14
13
|
export declare type EmailCampaignProviderSelection = {
|
|
15
14
|
providerMode: 'all';
|
|
16
15
|
selectedProviderIds: null;
|
|
17
|
-
selectedGroupIds: null;
|
|
18
16
|
} | {
|
|
19
17
|
providerMode: 'subset';
|
|
20
18
|
selectedProviderIds: number[];
|
|
21
|
-
selectedGroupIds: null;
|
|
22
|
-
} | {
|
|
23
|
-
providerMode: 'group';
|
|
24
|
-
selectedProviderIds: null;
|
|
25
|
-
selectedGroupIds: number[];
|
|
26
19
|
};
|
|
27
20
|
export declare type EmailCampaignRecipientSelection = {
|
|
28
21
|
recipientMode: 'all';
|
package/types/emailCampaign.ts
CHANGED
|
@@ -21,15 +21,11 @@ export type EmailCampaignRole = 'admin' | 'seller'
|
|
|
21
21
|
// How a pool (providers or recipients) is narrowed: 'all' = every match, 'subset' = explicit ids.
|
|
22
22
|
export type EmailCampaignSelectionMode = 'all' | 'subset'
|
|
23
23
|
|
|
24
|
-
// The provider dimension has a third mode the recipient dimension does not: 'group'.
|
|
25
|
-
export type EmailCampaignProviderMode = EmailCampaignSelectionMode | 'group'
|
|
26
|
-
|
|
27
24
|
// Provider dimension: mode is discriminated against its selection, so an invalid combination can't type-check.
|
|
28
25
|
// The candidate-pool filter (`filters`) is carried separately on the request/detail DTOs.
|
|
29
26
|
export type EmailCampaignProviderSelection =
|
|
30
|
-
| { providerMode: 'all'; selectedProviderIds: null
|
|
31
|
-
| { providerMode: 'subset'; selectedProviderIds: number[]
|
|
32
|
-
| { providerMode: 'group'; selectedProviderIds: null; selectedGroupIds: number[] }
|
|
27
|
+
| { providerMode: 'all'; selectedProviderIds: null }
|
|
28
|
+
| { providerMode: 'subset'; selectedProviderIds: number[] }
|
|
33
29
|
|
|
34
30
|
// Recipient dimension: mode is discriminated against its user picks.
|
|
35
31
|
export type EmailCampaignRecipientSelection =
|
package/types/index.d.ts
CHANGED
|
@@ -180,6 +180,7 @@ export * from './outage';
|
|
|
180
180
|
export * from './payment';
|
|
181
181
|
export * from './priceCalculation';
|
|
182
182
|
export * from './priceSpecification';
|
|
183
|
+
export * from './pricePrediction';
|
|
183
184
|
export * from './product';
|
|
184
185
|
export * from './providerBranding';
|
|
185
186
|
export * from './queryParams';
|
|
@@ -219,4 +220,3 @@ export * from './ISendEmail';
|
|
|
219
220
|
export * from './emailCampaign';
|
|
220
221
|
export * from './contractProviderFlags';
|
|
221
222
|
export * from './providerDocument';
|
|
222
|
-
export * from './contractProviderGroup';
|
package/types/index.js
CHANGED
|
@@ -170,5 +170,6 @@ var AccessKeyService;
|
|
|
170
170
|
AccessKeyService[AccessKeyService["WorkshopOperation"] = 1] = "WorkshopOperation";
|
|
171
171
|
AccessKeyService[AccessKeyService["Report"] = 2] = "Report";
|
|
172
172
|
})(AccessKeyService = exports.AccessKeyService || (exports.AccessKeyService = {}));
|
|
173
|
+
__export(require("./pricePrediction"));
|
|
173
174
|
__export(require("./emailCampaign"));
|
|
174
175
|
//# sourceMappingURL=index.js.map
|
package/types/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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"}
|
package/types/index.ts
CHANGED
|
@@ -199,6 +199,7 @@ export * from './outage'
|
|
|
199
199
|
export * from './payment'
|
|
200
200
|
export * from './priceCalculation'
|
|
201
201
|
export * from './priceSpecification'
|
|
202
|
+
export * from './pricePrediction'
|
|
202
203
|
export * from './product'
|
|
203
204
|
export * from './providerBranding'
|
|
204
205
|
export * from './queryParams'
|
|
@@ -238,4 +239,3 @@ export * from './ISendEmail'
|
|
|
238
239
|
export * from './emailCampaign'
|
|
239
240
|
export * from './contractProviderFlags'
|
|
240
241
|
export * from './providerDocument'
|
|
241
|
-
export * from './contractProviderGroup'
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { IsoLocale } from './locale';
|
|
2
|
+
import { PriceSpecification } from './priceSpecification';
|
|
3
|
+
import { TVehicleType } from './vehicle';
|
|
4
|
+
/**
|
|
5
|
+
* The contract between the API and the clients for a predicted price.
|
|
6
|
+
*
|
|
7
|
+
* It lives here because three repositories were keeping their own copy of it by hand. Admin
|
|
8
|
+
* redeclared the response shape and said so in a comment; superadmin wrote the duration ladder out
|
|
9
|
+
* as a literal; the ceiling existed as a separate constant in each of the three. A field added on
|
|
10
|
+
* one side and forgotten on another was caught only by someone remembering to look.
|
|
11
|
+
*
|
|
12
|
+
* What stays in the API is what never crosses the wire: how comparables are matched, what the
|
|
13
|
+
* model is asked, and every tuning constant behind the estimate.
|
|
14
|
+
*/
|
|
15
|
+
/** Agreements are sold in whole years, so every offered term is a multiple of this. */
|
|
16
|
+
export declare const PREDICTION_DURATION_STEP_MONTHS = 12;
|
|
17
|
+
/** Nothing longer may be sold, whatever a template asks for. */
|
|
18
|
+
export declare const PREDICTION_MAX_DURATION_MONTHS = 108;
|
|
19
|
+
/**
|
|
20
|
+
* Every term a predicted template may offer when it names none itself: 12, 24 ... up to the
|
|
21
|
+
* ceiling. Derived rather than written out, so raising the ceiling cannot leave a stale list
|
|
22
|
+
* behind in another repository.
|
|
23
|
+
*/
|
|
24
|
+
export declare function predictionDurationLadder(): number[];
|
|
25
|
+
/** True for a term that may actually be sold: a whole number of years, up to the ceiling. */
|
|
26
|
+
export declare function isSellablePredictionDuration(months: number): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* The fuel types a predicted template may be restricted to, as the vehicle catalogue spells them.
|
|
29
|
+
*
|
|
30
|
+
* A template that names none supports every fuel: an empty restriction is not "nothing", it is
|
|
31
|
+
* "no restriction", which is what a provider who never thinks about this should get.
|
|
32
|
+
*/
|
|
33
|
+
export declare const PREDICTION_FUEL_TYPES: string[];
|
|
34
|
+
export declare type TPredictionFuelType = 'Petrol' | 'Diesel' | 'Electric' | 'Hybrid' | 'Diesel-Electric' | 'Cng' | 'Lpg' | 'Lpg-Petrol' | 'Lpg-Diesel';
|
|
35
|
+
/**
|
|
36
|
+
* Whether a template restricted to these fuel types may be offered on this vehicle. No restriction
|
|
37
|
+
* means every fuel; an unknown fuel on the vehicle matches nothing but the absence of a rule.
|
|
38
|
+
*/
|
|
39
|
+
export declare function supportsPredictionFuelType(supported: string[] | null | undefined, fuelTypeName: string | null | undefined): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* How closely the comparable contracts had to match the requested vehicle. Tiers are tried in
|
|
42
|
+
* order and the first with enough samples wins, so a wide tier signals a weak estimate.
|
|
43
|
+
*/
|
|
44
|
+
export declare type TMatchTier = 'BrandModelFuel' | 'BrandFuelPower' | 'BrandFuel' | 'FuelPower' | 'Fuel';
|
|
45
|
+
export declare type TPredictionConfidence = 'high' | 'medium' | 'low';
|
|
46
|
+
export declare type TPriceSuggestionSource = 'Comparables' | 'AI';
|
|
47
|
+
/**
|
|
48
|
+
* Why an estimate may be weaker than its single number suggests. Sent as a code and parameters
|
|
49
|
+
* rather than a sentence: a quote is read on a Danish, Swedish, Finnish or Norwegian screen, and
|
|
50
|
+
* English prose from the server would land untranslated in the middle of it.
|
|
51
|
+
*
|
|
52
|
+
* A client may render only the codes it knows and drop the rest, so the API can add one without
|
|
53
|
+
* waiting for every client. A dropped code is a reason the seller never sees, though, so a client
|
|
54
|
+
* that maps these should aim to map all of them.
|
|
55
|
+
*/
|
|
56
|
+
export declare type TPredictionCaveatCode = 'VEHICLE_AGE_UNKNOWN' | 'VEHICLE_AGE_OUTSIDE_COMPARABLES' | 'START_MILEAGE_UNKNOWN' | 'START_MILEAGE_OUTSIDE_COMPARABLES' | 'DURATION_OUTSIDE_COMPARABLES' | 'TOO_FEW_COMPARABLES' | 'SMALL_COMPARABLE_SAMPLE' | 'NO_BRAND_OR_MODEL_HISTORY' | 'MILEAGE_OUTSIDE_COMPARABLES' | 'COST_UNVERIFIED' | 'SEGMENT_UNDERPRICED' | 'COMPARABLES_STALE' | 'AI_DIVERGES_FROM_COMPARABLES' | 'TIMING_BELT_DUE_IN_TERM' | 'PRICED_BY_AI_WITHOUT_COMPARABLES';
|
|
57
|
+
export interface IPredictionCaveat {
|
|
58
|
+
code: TPredictionCaveatCode;
|
|
59
|
+
params?: {
|
|
60
|
+
[key: string]: string | number;
|
|
61
|
+
};
|
|
62
|
+
/** English, for the admin prediction endpoint only. Never render this to a customer. */
|
|
63
|
+
message: string;
|
|
64
|
+
}
|
|
65
|
+
export interface IPricePredictionRequest {
|
|
66
|
+
brand: string;
|
|
67
|
+
model: string;
|
|
68
|
+
fuelType: string;
|
|
69
|
+
modelYear?: number;
|
|
70
|
+
/** Preferred over modelYear for the vehicle's age: it is set on far more vehicles. */
|
|
71
|
+
dateVehicleFirstRegistered?: string;
|
|
72
|
+
engineMaxPower?: number;
|
|
73
|
+
vehicleType?: TVehicleType;
|
|
74
|
+
/** Odometer when the agreement starts, not the allowance it grants. */
|
|
75
|
+
startMileage?: number;
|
|
76
|
+
/** The reader's own locale. Their choice wins over the provider's, which stands in when absent. */
|
|
77
|
+
locale?: IsoLocale;
|
|
78
|
+
durationMonths: number;
|
|
79
|
+
/** Allowance over the whole contract, not per year. */
|
|
80
|
+
mileage: number;
|
|
81
|
+
adjustmentFactor?: number;
|
|
82
|
+
/** Annual price drift used to bring old comparables into today's money, e.g. 0.03 for 3%. */
|
|
83
|
+
annualPriceIndex?: number;
|
|
84
|
+
/**
|
|
85
|
+
* Whether the servicing and coverage advice is wanted alongside the price. Defaults to true.
|
|
86
|
+
* Asking for prose nobody reads is most of the output bill and most of the wait, so a caller
|
|
87
|
+
* that shows the price alone should say so.
|
|
88
|
+
*/
|
|
89
|
+
withAdvice?: boolean;
|
|
90
|
+
}
|
|
91
|
+
export interface IPricePercentiles {
|
|
92
|
+
p25: PriceSpecification;
|
|
93
|
+
p50: PriceSpecification;
|
|
94
|
+
p75: PriceSpecification;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Realized workshop cost for the same comparables. Only populated when enough of them actually
|
|
98
|
+
* carry cost data, otherwise absence of claims is indistinguishable from claims never recorded.
|
|
99
|
+
*/
|
|
100
|
+
export interface ICostEvidence {
|
|
101
|
+
contractsWithCost: number;
|
|
102
|
+
medianMonthlyCost: PriceSpecification;
|
|
103
|
+
impliedLossRatio: number;
|
|
104
|
+
}
|
|
105
|
+
export interface IPricePredictionBasis {
|
|
106
|
+
matchTier: TMatchTier;
|
|
107
|
+
comparableCount: number;
|
|
108
|
+
contractsWithPrice: number;
|
|
109
|
+
durationMatched: boolean;
|
|
110
|
+
vehicleAgeMatched: boolean;
|
|
111
|
+
startMileageMatched: boolean;
|
|
112
|
+
requestedStartMileage: number | null;
|
|
113
|
+
medianComparableStartMileage: number | null;
|
|
114
|
+
/** Age of the vehicle being priced, from first registration where given, else from model year. */
|
|
115
|
+
requestedVehicleAgeYears: number | null;
|
|
116
|
+
/** Median age of the comparable vehicles at the time each of those contracts was sold. */
|
|
117
|
+
medianComparableVehicleAgeYears: number | null;
|
|
118
|
+
medianComparableDurationMonths: number;
|
|
119
|
+
medianComparableMileagePerMonth: number;
|
|
120
|
+
mileageAdjustment: number;
|
|
121
|
+
adjustmentFactor: number;
|
|
122
|
+
annualPriceIndex: number;
|
|
123
|
+
medianComparableAgeYears: number;
|
|
124
|
+
/**
|
|
125
|
+
* Annual drift actually observed between the oldest and newest third of the comparables. Shown
|
|
126
|
+
* so the applied index can be sanity checked against the data; too noisy on small or
|
|
127
|
+
* narrow-dated samples to apply automatically.
|
|
128
|
+
*/
|
|
129
|
+
observedAnnualDrift: number | null;
|
|
130
|
+
oldestContractStart: string | null;
|
|
131
|
+
newestContractStart: string | null;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* One estimate of the price. Sources are meant to coexist rather than replace one another: an
|
|
135
|
+
* added estimate is only worth acting on if it can be read against the others and the evidence.
|
|
136
|
+
*/
|
|
137
|
+
export interface IPriceSuggestion {
|
|
138
|
+
source: TPriceSuggestionSource;
|
|
139
|
+
monthlyPrice: PriceSpecification;
|
|
140
|
+
/** The same figure in sub-units ex-VAT, the unit prices are stored and calculated in. */
|
|
141
|
+
monthlyPriceMinor: number;
|
|
142
|
+
/** Adjusted identically to monthlyPrice, so the three figures are directly comparable. */
|
|
143
|
+
range: {
|
|
144
|
+
low: PriceSpecification;
|
|
145
|
+
high: PriceSpecification;
|
|
146
|
+
};
|
|
147
|
+
rationale?: string;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* What the model expects this vehicle to need over the term. This is NOT the contract's coverage,
|
|
151
|
+
* which the template's terms decide. Never quote it as what the customer is entitled to.
|
|
152
|
+
*/
|
|
153
|
+
export interface IAnticipatedServicing {
|
|
154
|
+
summary: string;
|
|
155
|
+
items: string[];
|
|
156
|
+
/**
|
|
157
|
+
* How many ordinary services fall inside the term. A suggestion for the seller to confirm or
|
|
158
|
+
* correct, as with the timing belt: it prices an agreement only if they put it there.
|
|
159
|
+
*/
|
|
160
|
+
ordinaryServicesWithinTerm?: number;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Whether a timing belt change falls inside the agreed term or mileage. Asked on its own because
|
|
164
|
+
* it is usually the largest single bill in an agreement, and whether it lands inside the window or
|
|
165
|
+
* just outside swings the price more than anything else the vehicle will need.
|
|
166
|
+
*/
|
|
167
|
+
export interface ITimingBeltAssessment {
|
|
168
|
+
dueWithinTerm: 'yes' | 'no' | 'unknown';
|
|
169
|
+
/** One plain sentence: the interval, where this vehicle sits against it, or that it runs a chain. */
|
|
170
|
+
note: string;
|
|
171
|
+
/**
|
|
172
|
+
* How many changes fall inside the term, where one is due. A suggestion for the seller to
|
|
173
|
+
* confirm or correct: it is written onto an agreement only if they put it there.
|
|
174
|
+
*/
|
|
175
|
+
replacementsWithinTerm?: number;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Whether this electric vehicle is likely to need its HV traction battery replaced during the agreed
|
|
179
|
+
* term or mileage. Asked only of electric vehicles, and asked because it decides whether a seller
|
|
180
|
+
* should put the battery on the agreement at all -- it is the largest single thing an agreement
|
|
181
|
+
* can carry, and the cost is the provider's to bear once it is on there.
|
|
182
|
+
*
|
|
183
|
+
* A judgement about the vehicle, not about the warranty: age, distance, chemistry and how hard the
|
|
184
|
+
* car has been driven. Unknown is a real answer and better than a guess dressed as one.
|
|
185
|
+
*/
|
|
186
|
+
export interface IBatteryAssessment {
|
|
187
|
+
replacementLikelyWithinTerm: 'yes' | 'no' | 'unknown';
|
|
188
|
+
/** One plain sentence a seller can repeat: what the battery is likely to do over this term. */
|
|
189
|
+
note: string;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* What the agreement ought to cover for this vehicle, in the model's own words. Advice for whoever
|
|
193
|
+
* configures or sells it, never a statement of what a customer has bought.
|
|
194
|
+
*/
|
|
195
|
+
export interface IRecommendedCoverage {
|
|
196
|
+
summary: string;
|
|
197
|
+
items: string[];
|
|
198
|
+
}
|
|
199
|
+
/** The model's read on the vehicle. Absent when the integration is disabled or unreachable. */
|
|
200
|
+
export interface IAiReview {
|
|
201
|
+
confidence: TPredictionConfidence;
|
|
202
|
+
anticipatedServicing?: IAnticipatedServicing;
|
|
203
|
+
recommendedCoverage?: IRecommendedCoverage;
|
|
204
|
+
timingBelt?: ITimingBeltAssessment;
|
|
205
|
+
battery?: IBatteryAssessment;
|
|
206
|
+
}
|
|
207
|
+
export interface IPricePredictionEvidence {
|
|
208
|
+
basis: IPricePredictionBasis;
|
|
209
|
+
/** Prices as recorded on the comparables, before mileage or caller adjustments. */
|
|
210
|
+
historicalMonthlyPrice: IPricePercentiles;
|
|
211
|
+
costEvidence?: ICostEvidence;
|
|
212
|
+
aiReview?: IAiReview;
|
|
213
|
+
}
|
|
214
|
+
export interface IPricePredictionResult {
|
|
215
|
+
suggestions: IPriceSuggestion[];
|
|
216
|
+
evidence: IPricePredictionEvidence;
|
|
217
|
+
caveats: IPredictionCaveat[];
|
|
218
|
+
confidence: TPredictionConfidence;
|
|
219
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
/**
|
|
4
|
+
* The contract between the API and the clients for a predicted price.
|
|
5
|
+
*
|
|
6
|
+
* It lives here because three repositories were keeping their own copy of it by hand. Admin
|
|
7
|
+
* redeclared the response shape and said so in a comment; superadmin wrote the duration ladder out
|
|
8
|
+
* as a literal; the ceiling existed as a separate constant in each of the three. A field added on
|
|
9
|
+
* one side and forgotten on another was caught only by someone remembering to look.
|
|
10
|
+
*
|
|
11
|
+
* What stays in the API is what never crosses the wire: how comparables are matched, what the
|
|
12
|
+
* model is asked, and every tuning constant behind the estimate.
|
|
13
|
+
*/
|
|
14
|
+
/** Agreements are sold in whole years, so every offered term is a multiple of this. */
|
|
15
|
+
exports.PREDICTION_DURATION_STEP_MONTHS = 12;
|
|
16
|
+
/** Nothing longer may be sold, whatever a template asks for. */
|
|
17
|
+
exports.PREDICTION_MAX_DURATION_MONTHS = 108;
|
|
18
|
+
/**
|
|
19
|
+
* Every term a predicted template may offer when it names none itself: 12, 24 ... up to the
|
|
20
|
+
* ceiling. Derived rather than written out, so raising the ceiling cannot leave a stale list
|
|
21
|
+
* behind in another repository.
|
|
22
|
+
*/
|
|
23
|
+
function predictionDurationLadder() {
|
|
24
|
+
const months = [];
|
|
25
|
+
for (let m = exports.PREDICTION_DURATION_STEP_MONTHS; m <= exports.PREDICTION_MAX_DURATION_MONTHS; m += exports.PREDICTION_DURATION_STEP_MONTHS) {
|
|
26
|
+
months.push(m);
|
|
27
|
+
}
|
|
28
|
+
return months;
|
|
29
|
+
}
|
|
30
|
+
exports.predictionDurationLadder = predictionDurationLadder;
|
|
31
|
+
/** True for a term that may actually be sold: a whole number of years, up to the ceiling. */
|
|
32
|
+
function isSellablePredictionDuration(months) {
|
|
33
|
+
return months > 0 && months <= exports.PREDICTION_MAX_DURATION_MONTHS && months % exports.PREDICTION_DURATION_STEP_MONTHS === 0;
|
|
34
|
+
}
|
|
35
|
+
exports.isSellablePredictionDuration = isSellablePredictionDuration;
|
|
36
|
+
/**
|
|
37
|
+
* The fuel types a predicted template may be restricted to, as the vehicle catalogue spells them.
|
|
38
|
+
*
|
|
39
|
+
* A template that names none supports every fuel: an empty restriction is not "nothing", it is
|
|
40
|
+
* "no restriction", which is what a provider who never thinks about this should get.
|
|
41
|
+
*/
|
|
42
|
+
exports.PREDICTION_FUEL_TYPES = [
|
|
43
|
+
'Petrol',
|
|
44
|
+
'Diesel',
|
|
45
|
+
'Electric',
|
|
46
|
+
'Hybrid',
|
|
47
|
+
'Diesel-Electric',
|
|
48
|
+
'Cng',
|
|
49
|
+
'Lpg',
|
|
50
|
+
'Lpg-Petrol',
|
|
51
|
+
'Lpg-Diesel',
|
|
52
|
+
];
|
|
53
|
+
/**
|
|
54
|
+
* The same fuel written more than one way. Lookups and imports have filed Danish and Swedish
|
|
55
|
+
* spellings alongside the canonical English, and a vehicle that arrives as EL must still match a
|
|
56
|
+
* template that says Electric -- otherwise the restriction silently hides templates that were
|
|
57
|
+
* meant to be offered.
|
|
58
|
+
*/
|
|
59
|
+
const FUEL_SPELLINGS = [
|
|
60
|
+
['PETROL', 'BENZIN', 'BENSIN'],
|
|
61
|
+
['ELECTRIC', 'EL', 'EV', 'ELEKTRISK'],
|
|
62
|
+
['DIESEL'],
|
|
63
|
+
];
|
|
64
|
+
const canonical = (fuelTypeName) => {
|
|
65
|
+
const wanted = fuelTypeName.trim().toUpperCase();
|
|
66
|
+
const group = FUEL_SPELLINGS.find((spellings) => spellings.indexOf(wanted) !== -1);
|
|
67
|
+
return group ? group[0] : wanted;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Whether a template restricted to these fuel types may be offered on this vehicle. No restriction
|
|
71
|
+
* means every fuel; an unknown fuel on the vehicle matches nothing but the absence of a rule.
|
|
72
|
+
*/
|
|
73
|
+
function supportsPredictionFuelType(supported, fuelTypeName) {
|
|
74
|
+
if (!supported || !supported.length) {
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
if (!fuelTypeName) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
const wanted = canonical(fuelTypeName);
|
|
81
|
+
return supported.some((entry) => canonical(entry) === wanted);
|
|
82
|
+
}
|
|
83
|
+
exports.supportsPredictionFuelType = supportsPredictionFuelType;
|
|
84
|
+
//# sourceMappingURL=pricePrediction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pricePrediction.js","sourceRoot":"","sources":["pricePrediction.ts"],"names":[],"mappings":";;AAIA;;;;;;;;;;GAUG;AAEH,uFAAuF;AAC1E,QAAA,+BAA+B,GAAG,EAAE,CAAA;AAEjD,gEAAgE;AACnD,QAAA,8BAA8B,GAAG,GAAG,CAAA;AAEjD;;;;GAIG;AACH,SAAgB,wBAAwB;IACtC,MAAM,MAAM,GAAa,EAAE,CAAA;IAC3B,KACE,IAAI,CAAC,GAAG,uCAA+B,EACvC,CAAC,IAAI,sCAA8B,EACnC,CAAC,IAAI,uCAA+B,EACpC;QACA,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;KACf;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAVD,4DAUC;AAED,6FAA6F;AAC7F,SAAgB,4BAA4B,CAAC,MAAc;IACzD,OAAO,MAAM,GAAG,CAAC,IAAI,MAAM,IAAI,sCAA8B,IAAI,MAAM,GAAG,uCAA+B,KAAK,CAAC,CAAA;AACjH,CAAC;AAFD,oEAEC;AAGD;;;;;GAKG;AACU,QAAA,qBAAqB,GAAa;IAC7C,QAAQ;IACR,QAAQ;IACR,UAAU;IACV,QAAQ;IACR,iBAAiB;IACjB,KAAK;IACL,KAAK;IACL,YAAY;IACZ,YAAY;CACb,CAAA;AAaD;;;;;GAKG;AACH,MAAM,cAAc,GAAe;IACjC,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC;IAC9B,CAAC,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,CAAC;IACrC,CAAC,QAAQ,CAAC;CACX,CAAA;AAED,MAAM,SAAS,GAAG,CAAC,YAAoB,EAAU,EAAE;IACjD,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;IAChD,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;IAElF,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAA;AAClC,CAAC,CAAA;AAED;;;GAGG;AACH,SAAgB,0BAA0B,CACxC,SAAsC,EACtC,YAAuC;IAEvC,IAAI,CAAC,SAAS,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE;QACnC,OAAO,IAAI,CAAA;KACZ;IAED,IAAI,CAAC,YAAY,EAAE;QACjB,OAAO,KAAK,CAAA;KACb;IAED,MAAM,MAAM,GAAG,SAAS,CAAC,YAAY,CAAC,CAAA;IAEtC,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,MAAM,CAAC,CAAA;AAC/D,CAAC;AAfD,gEAeC"}
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import { IsoLocale } from './locale'
|
|
2
|
+
import { PriceSpecification } from './priceSpecification'
|
|
3
|
+
import { TVehicleType } from './vehicle'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The contract between the API and the clients for a predicted price.
|
|
7
|
+
*
|
|
8
|
+
* It lives here because three repositories were keeping their own copy of it by hand. Admin
|
|
9
|
+
* redeclared the response shape and said so in a comment; superadmin wrote the duration ladder out
|
|
10
|
+
* as a literal; the ceiling existed as a separate constant in each of the three. A field added on
|
|
11
|
+
* one side and forgotten on another was caught only by someone remembering to look.
|
|
12
|
+
*
|
|
13
|
+
* What stays in the API is what never crosses the wire: how comparables are matched, what the
|
|
14
|
+
* model is asked, and every tuning constant behind the estimate.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/** Agreements are sold in whole years, so every offered term is a multiple of this. */
|
|
18
|
+
export const PREDICTION_DURATION_STEP_MONTHS = 12
|
|
19
|
+
|
|
20
|
+
/** Nothing longer may be sold, whatever a template asks for. */
|
|
21
|
+
export const PREDICTION_MAX_DURATION_MONTHS = 108
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Every term a predicted template may offer when it names none itself: 12, 24 ... up to the
|
|
25
|
+
* ceiling. Derived rather than written out, so raising the ceiling cannot leave a stale list
|
|
26
|
+
* behind in another repository.
|
|
27
|
+
*/
|
|
28
|
+
export function predictionDurationLadder(): number[] {
|
|
29
|
+
const months: number[] = []
|
|
30
|
+
for (
|
|
31
|
+
let m = PREDICTION_DURATION_STEP_MONTHS;
|
|
32
|
+
m <= PREDICTION_MAX_DURATION_MONTHS;
|
|
33
|
+
m += PREDICTION_DURATION_STEP_MONTHS
|
|
34
|
+
) {
|
|
35
|
+
months.push(m)
|
|
36
|
+
}
|
|
37
|
+
return months
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** True for a term that may actually be sold: a whole number of years, up to the ceiling. */
|
|
41
|
+
export function isSellablePredictionDuration(months: number): boolean {
|
|
42
|
+
return months > 0 && months <= PREDICTION_MAX_DURATION_MONTHS && months % PREDICTION_DURATION_STEP_MONTHS === 0
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The fuel types a predicted template may be restricted to, as the vehicle catalogue spells them.
|
|
48
|
+
*
|
|
49
|
+
* A template that names none supports every fuel: an empty restriction is not "nothing", it is
|
|
50
|
+
* "no restriction", which is what a provider who never thinks about this should get.
|
|
51
|
+
*/
|
|
52
|
+
export const PREDICTION_FUEL_TYPES: string[] = [
|
|
53
|
+
'Petrol',
|
|
54
|
+
'Diesel',
|
|
55
|
+
'Electric',
|
|
56
|
+
'Hybrid',
|
|
57
|
+
'Diesel-Electric',
|
|
58
|
+
'Cng',
|
|
59
|
+
'Lpg',
|
|
60
|
+
'Lpg-Petrol',
|
|
61
|
+
'Lpg-Diesel',
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
export type TPredictionFuelType =
|
|
65
|
+
| 'Petrol'
|
|
66
|
+
| 'Diesel'
|
|
67
|
+
| 'Electric'
|
|
68
|
+
| 'Hybrid'
|
|
69
|
+
| 'Diesel-Electric'
|
|
70
|
+
| 'Cng'
|
|
71
|
+
| 'Lpg'
|
|
72
|
+
| 'Lpg-Petrol'
|
|
73
|
+
| 'Lpg-Diesel'
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* The same fuel written more than one way. Lookups and imports have filed Danish and Swedish
|
|
77
|
+
* spellings alongside the canonical English, and a vehicle that arrives as EL must still match a
|
|
78
|
+
* template that says Electric -- otherwise the restriction silently hides templates that were
|
|
79
|
+
* meant to be offered.
|
|
80
|
+
*/
|
|
81
|
+
const FUEL_SPELLINGS: string[][] = [
|
|
82
|
+
['PETROL', 'BENZIN', 'BENSIN'],
|
|
83
|
+
['ELECTRIC', 'EL', 'EV', 'ELEKTRISK'],
|
|
84
|
+
['DIESEL'],
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
const canonical = (fuelTypeName: string): string => {
|
|
88
|
+
const wanted = fuelTypeName.trim().toUpperCase()
|
|
89
|
+
const group = FUEL_SPELLINGS.find((spellings) => spellings.indexOf(wanted) !== -1)
|
|
90
|
+
|
|
91
|
+
return group ? group[0] : wanted
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Whether a template restricted to these fuel types may be offered on this vehicle. No restriction
|
|
96
|
+
* means every fuel; an unknown fuel on the vehicle matches nothing but the absence of a rule.
|
|
97
|
+
*/
|
|
98
|
+
export function supportsPredictionFuelType(
|
|
99
|
+
supported: string[] | null | undefined,
|
|
100
|
+
fuelTypeName: string | null | undefined,
|
|
101
|
+
): boolean {
|
|
102
|
+
if (!supported || !supported.length) {
|
|
103
|
+
return true
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (!fuelTypeName) {
|
|
107
|
+
return false
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const wanted = canonical(fuelTypeName)
|
|
111
|
+
|
|
112
|
+
return supported.some((entry) => canonical(entry) === wanted)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* How closely the comparable contracts had to match the requested vehicle. Tiers are tried in
|
|
117
|
+
* order and the first with enough samples wins, so a wide tier signals a weak estimate.
|
|
118
|
+
*/
|
|
119
|
+
export type TMatchTier = 'BrandModelFuel' | 'BrandFuelPower' | 'BrandFuel' | 'FuelPower' | 'Fuel'
|
|
120
|
+
|
|
121
|
+
export type TPredictionConfidence = 'high' | 'medium' | 'low'
|
|
122
|
+
|
|
123
|
+
export type TPriceSuggestionSource = 'Comparables' | 'AI'
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Why an estimate may be weaker than its single number suggests. Sent as a code and parameters
|
|
127
|
+
* rather than a sentence: a quote is read on a Danish, Swedish, Finnish or Norwegian screen, and
|
|
128
|
+
* English prose from the server would land untranslated in the middle of it.
|
|
129
|
+
*
|
|
130
|
+
* A client may render only the codes it knows and drop the rest, so the API can add one without
|
|
131
|
+
* waiting for every client. A dropped code is a reason the seller never sees, though, so a client
|
|
132
|
+
* that maps these should aim to map all of them.
|
|
133
|
+
*/
|
|
134
|
+
export type TPredictionCaveatCode =
|
|
135
|
+
| 'VEHICLE_AGE_UNKNOWN'
|
|
136
|
+
| 'VEHICLE_AGE_OUTSIDE_COMPARABLES'
|
|
137
|
+
| 'START_MILEAGE_UNKNOWN'
|
|
138
|
+
| 'START_MILEAGE_OUTSIDE_COMPARABLES'
|
|
139
|
+
| 'DURATION_OUTSIDE_COMPARABLES'
|
|
140
|
+
| 'TOO_FEW_COMPARABLES'
|
|
141
|
+
| 'SMALL_COMPARABLE_SAMPLE'
|
|
142
|
+
| 'NO_BRAND_OR_MODEL_HISTORY'
|
|
143
|
+
| 'MILEAGE_OUTSIDE_COMPARABLES'
|
|
144
|
+
| 'COST_UNVERIFIED'
|
|
145
|
+
| 'SEGMENT_UNDERPRICED'
|
|
146
|
+
| 'COMPARABLES_STALE'
|
|
147
|
+
| 'AI_DIVERGES_FROM_COMPARABLES'
|
|
148
|
+
| 'TIMING_BELT_DUE_IN_TERM'
|
|
149
|
+
| 'PRICED_BY_AI_WITHOUT_COMPARABLES'
|
|
150
|
+
|
|
151
|
+
export interface IPredictionCaveat {
|
|
152
|
+
code: TPredictionCaveatCode
|
|
153
|
+
params?: { [key: string]: string | number }
|
|
154
|
+
/** English, for the admin prediction endpoint only. Never render this to a customer. */
|
|
155
|
+
message: string
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export interface IPricePredictionRequest {
|
|
159
|
+
brand: string
|
|
160
|
+
model: string
|
|
161
|
+
fuelType: string
|
|
162
|
+
modelYear?: number
|
|
163
|
+
/** Preferred over modelYear for the vehicle's age: it is set on far more vehicles. */
|
|
164
|
+
dateVehicleFirstRegistered?: string
|
|
165
|
+
engineMaxPower?: number
|
|
166
|
+
vehicleType?: TVehicleType
|
|
167
|
+
/** Odometer when the agreement starts, not the allowance it grants. */
|
|
168
|
+
startMileage?: number
|
|
169
|
+
/** The reader's own locale. Their choice wins over the provider's, which stands in when absent. */
|
|
170
|
+
locale?: IsoLocale
|
|
171
|
+
durationMonths: number
|
|
172
|
+
/** Allowance over the whole contract, not per year. */
|
|
173
|
+
mileage: number
|
|
174
|
+
adjustmentFactor?: number
|
|
175
|
+
/** Annual price drift used to bring old comparables into today's money, e.g. 0.03 for 3%. */
|
|
176
|
+
annualPriceIndex?: number
|
|
177
|
+
/**
|
|
178
|
+
* Whether the servicing and coverage advice is wanted alongside the price. Defaults to true.
|
|
179
|
+
* Asking for prose nobody reads is most of the output bill and most of the wait, so a caller
|
|
180
|
+
* that shows the price alone should say so.
|
|
181
|
+
*/
|
|
182
|
+
withAdvice?: boolean
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export interface IPricePercentiles {
|
|
186
|
+
p25: PriceSpecification
|
|
187
|
+
p50: PriceSpecification
|
|
188
|
+
p75: PriceSpecification
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Realized workshop cost for the same comparables. Only populated when enough of them actually
|
|
193
|
+
* carry cost data, otherwise absence of claims is indistinguishable from claims never recorded.
|
|
194
|
+
*/
|
|
195
|
+
export interface ICostEvidence {
|
|
196
|
+
contractsWithCost: number
|
|
197
|
+
medianMonthlyCost: PriceSpecification
|
|
198
|
+
impliedLossRatio: number
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export interface IPricePredictionBasis {
|
|
202
|
+
matchTier: TMatchTier
|
|
203
|
+
comparableCount: number
|
|
204
|
+
contractsWithPrice: number
|
|
205
|
+
durationMatched: boolean
|
|
206
|
+
vehicleAgeMatched: boolean
|
|
207
|
+
startMileageMatched: boolean
|
|
208
|
+
requestedStartMileage: number | null
|
|
209
|
+
medianComparableStartMileage: number | null
|
|
210
|
+
/** Age of the vehicle being priced, from first registration where given, else from model year. */
|
|
211
|
+
requestedVehicleAgeYears: number | null
|
|
212
|
+
/** Median age of the comparable vehicles at the time each of those contracts was sold. */
|
|
213
|
+
medianComparableVehicleAgeYears: number | null
|
|
214
|
+
medianComparableDurationMonths: number
|
|
215
|
+
medianComparableMileagePerMonth: number
|
|
216
|
+
mileageAdjustment: number
|
|
217
|
+
adjustmentFactor: number
|
|
218
|
+
annualPriceIndex: number
|
|
219
|
+
medianComparableAgeYears: number
|
|
220
|
+
/**
|
|
221
|
+
* Annual drift actually observed between the oldest and newest third of the comparables. Shown
|
|
222
|
+
* so the applied index can be sanity checked against the data; too noisy on small or
|
|
223
|
+
* narrow-dated samples to apply automatically.
|
|
224
|
+
*/
|
|
225
|
+
observedAnnualDrift: number | null
|
|
226
|
+
oldestContractStart: string | null
|
|
227
|
+
newestContractStart: string | null
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* One estimate of the price. Sources are meant to coexist rather than replace one another: an
|
|
232
|
+
* added estimate is only worth acting on if it can be read against the others and the evidence.
|
|
233
|
+
*/
|
|
234
|
+
export interface IPriceSuggestion {
|
|
235
|
+
source: TPriceSuggestionSource
|
|
236
|
+
monthlyPrice: PriceSpecification
|
|
237
|
+
/** The same figure in sub-units ex-VAT, the unit prices are stored and calculated in. */
|
|
238
|
+
monthlyPriceMinor: number
|
|
239
|
+
/** Adjusted identically to monthlyPrice, so the three figures are directly comparable. */
|
|
240
|
+
range: { low: PriceSpecification; high: PriceSpecification }
|
|
241
|
+
rationale?: string
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* What the model expects this vehicle to need over the term. This is NOT the contract's coverage,
|
|
246
|
+
* which the template's terms decide. Never quote it as what the customer is entitled to.
|
|
247
|
+
*/
|
|
248
|
+
export interface IAnticipatedServicing {
|
|
249
|
+
summary: string
|
|
250
|
+
items: string[]
|
|
251
|
+
/**
|
|
252
|
+
* How many ordinary services fall inside the term. A suggestion for the seller to confirm or
|
|
253
|
+
* correct, as with the timing belt: it prices an agreement only if they put it there.
|
|
254
|
+
*/
|
|
255
|
+
ordinaryServicesWithinTerm?: number
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Whether a timing belt change falls inside the agreed term or mileage. Asked on its own because
|
|
260
|
+
* it is usually the largest single bill in an agreement, and whether it lands inside the window or
|
|
261
|
+
* just outside swings the price more than anything else the vehicle will need.
|
|
262
|
+
*/
|
|
263
|
+
export interface ITimingBeltAssessment {
|
|
264
|
+
dueWithinTerm: 'yes' | 'no' | 'unknown'
|
|
265
|
+
/** One plain sentence: the interval, where this vehicle sits against it, or that it runs a chain. */
|
|
266
|
+
note: string
|
|
267
|
+
/**
|
|
268
|
+
* How many changes fall inside the term, where one is due. A suggestion for the seller to
|
|
269
|
+
* confirm or correct: it is written onto an agreement only if they put it there.
|
|
270
|
+
*/
|
|
271
|
+
replacementsWithinTerm?: number
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Whether this electric vehicle is likely to need its HV traction battery replaced during the agreed
|
|
276
|
+
* term or mileage. Asked only of electric vehicles, and asked because it decides whether a seller
|
|
277
|
+
* should put the battery on the agreement at all -- it is the largest single thing an agreement
|
|
278
|
+
* can carry, and the cost is the provider's to bear once it is on there.
|
|
279
|
+
*
|
|
280
|
+
* A judgement about the vehicle, not about the warranty: age, distance, chemistry and how hard the
|
|
281
|
+
* car has been driven. Unknown is a real answer and better than a guess dressed as one.
|
|
282
|
+
*/
|
|
283
|
+
export interface IBatteryAssessment {
|
|
284
|
+
replacementLikelyWithinTerm: 'yes' | 'no' | 'unknown'
|
|
285
|
+
/** One plain sentence a seller can repeat: what the battery is likely to do over this term. */
|
|
286
|
+
note: string
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* What the agreement ought to cover for this vehicle, in the model's own words. Advice for whoever
|
|
291
|
+
* configures or sells it, never a statement of what a customer has bought.
|
|
292
|
+
*/
|
|
293
|
+
export interface IRecommendedCoverage {
|
|
294
|
+
summary: string
|
|
295
|
+
items: string[]
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/** The model's read on the vehicle. Absent when the integration is disabled or unreachable. */
|
|
299
|
+
export interface IAiReview {
|
|
300
|
+
confidence: TPredictionConfidence
|
|
301
|
+
anticipatedServicing?: IAnticipatedServicing
|
|
302
|
+
recommendedCoverage?: IRecommendedCoverage
|
|
303
|
+
timingBelt?: ITimingBeltAssessment
|
|
304
|
+
battery?: IBatteryAssessment
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
export interface IPricePredictionEvidence {
|
|
308
|
+
basis: IPricePredictionBasis
|
|
309
|
+
/** Prices as recorded on the comparables, before mileage or caller adjustments. */
|
|
310
|
+
historicalMonthlyPrice: IPricePercentiles
|
|
311
|
+
costEvidence?: ICostEvidence
|
|
312
|
+
aiReview?: IAiReview
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
export interface IPricePredictionResult {
|
|
316
|
+
suggestions: IPriceSuggestion[]
|
|
317
|
+
evidence: IPricePredictionEvidence
|
|
318
|
+
caveats: IPredictionCaveat[]
|
|
319
|
+
confidence: TPredictionConfidence
|
|
320
|
+
}
|
|
@@ -16,16 +16,9 @@ export interface IProviderDocument {
|
|
|
16
16
|
// Admin sees the document and its upload date only — no internal staff names.
|
|
17
17
|
export interface IProviderDocumentAdminView extends IProviderDocument {}
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
contractProviderGroupId: number
|
|
21
|
-
name: string
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Superadmin also sees the acting support users, the tombstone state, and the sharing groups, sorted by
|
|
25
|
-
// name. `groups` is empty on a provider-owned row.
|
|
19
|
+
// Superadmin additionally sees the acting support users and the tombstone state.
|
|
26
20
|
export interface IProviderDocumentSuperadminView extends IProviderDocument {
|
|
27
21
|
uploadedByName: string | null
|
|
28
22
|
deletedAt: string | null
|
|
29
23
|
deletedByName: string | null
|
|
30
|
-
groups: IProviderDocumentGroup[]
|
|
31
24
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { AdjustmentRequestResponseErrors } from './adjustmentRequestResponseErrors'
|
|
2
2
|
import { AdminPaymentErrors } from './adminPaymentErrors'
|
|
3
3
|
import { CalculatorResponseErrors } from './calculatorResponseErrors'
|
|
4
|
-
import { ContractProviderGroupErrors } from './contractProviderGroupErrors'
|
|
5
4
|
import { ContractResponseErrors } from './contractResponseErrors'
|
|
6
5
|
import { GenericResponseErrors } from './genericResponseTypes'
|
|
7
6
|
import { ImportErrors } from './importErrors'
|
|
@@ -18,7 +17,6 @@ export type ResponseErrors =
|
|
|
18
17
|
| AdjustmentRequestResponseErrors
|
|
19
18
|
| AdminPaymentErrors
|
|
20
19
|
| CalculatorResponseErrors
|
|
21
|
-
| ContractProviderGroupErrors
|
|
22
20
|
| ContractResponseErrors
|
|
23
21
|
| GenericResponseErrors
|
|
24
22
|
| ImportErrors
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { ProviderState } from './contractProvider'
|
|
2
|
-
import { EmailCampaignStatus } from './emailCampaign'
|
|
3
|
-
|
|
4
|
-
export interface IContractProviderGroupMember {
|
|
5
|
-
contractProviderId: number
|
|
6
|
-
administrativeName: string
|
|
7
|
-
isoName: string // country
|
|
8
|
-
state: ProviderState // lets every picker count Inactive members
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface IProviderGroupTag {
|
|
12
|
-
providerGroupTagId: number
|
|
13
|
-
name: string
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface IContractProviderGroupListItem {
|
|
17
|
-
contractProviderGroupId: number
|
|
18
|
-
name: string
|
|
19
|
-
description: string | null
|
|
20
|
-
tags: IProviderGroupTag[]
|
|
21
|
-
memberCount: number
|
|
22
|
-
inactiveMemberCount: number
|
|
23
|
-
archivedAt: string | null // ISO
|
|
24
|
-
updatedByEmail: string // 'USER NOT FOUND' when the acting user is gone
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export type IContractProviderGroupDetail = IContractProviderGroupListItem & {
|
|
28
|
-
members: IContractProviderGroupMember[]
|
|
29
|
-
createdAt: string // ISO
|
|
30
|
-
updatedAt: string // ISO
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export interface IContractProviderGroupUsage {
|
|
34
|
-
accessKeys: { accessKeyId: number; description: string | null }[]
|
|
35
|
-
emailCampaigns: { id: number; name: string; status: EmailCampaignStatus }[]
|
|
36
|
-
blacklistEntryCount: number
|
|
37
|
-
documentCount: number
|
|
38
|
-
dashboardBannerCount: number
|
|
39
|
-
featureFlagCount: number
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export interface IUpsertContractProviderGroupRequest {
|
|
43
|
-
name: string
|
|
44
|
-
description: string | null
|
|
45
|
-
tags: string[]
|
|
46
|
-
contractProviderIds: number[]
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// The provider-side view of membership: every joinable group, plus any archived group this provider is
|
|
50
|
-
// still in (archived groups can be left but not joined).
|
|
51
|
-
export interface IContractProviderGroupMembership {
|
|
52
|
-
groups: IContractProviderGroupListItem[]
|
|
53
|
-
memberGroupIds: number[]
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export interface ISetContractProviderGroupsRequest {
|
|
57
|
-
contractProviderGroupIds: number[] // the full set for one provider; the api diffs it against what is stored
|
|
58
|
-
}
|