@bisondesk/core-sdk 1.0.643 → 1.0.645
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/lib/types/delivery-settings.d.ts +1 -0
- package/lib/types/delivery-settings.d.ts.map +1 -1
- package/lib/types/delivery-settings.js.map +1 -1
- package/lib/types/internal-events.d.ts +3 -1
- package/lib/types/internal-events.d.ts.map +1 -1
- package/lib/types/internal-events.js +2 -0
- package/lib/types/internal-events.js.map +1 -1
- package/lib/types/journeys.d.ts +1 -0
- package/lib/types/journeys.d.ts.map +1 -1
- package/lib/types/journeys.js.map +1 -1
- package/lib/types/marketing-emails.d.ts +28 -0
- package/lib/types/marketing-emails.d.ts.map +1 -1
- package/lib/types/marketing-emails.js.map +1 -1
- package/lib/types/offers.d.ts +1 -0
- package/lib/types/offers.d.ts.map +1 -1
- package/lib/types/offers.js.map +1 -1
- package/lib/types/quotes.d.ts +6 -0
- package/lib/types/quotes.d.ts.map +1 -1
- package/lib/types/quotes.js.map +1 -1
- package/lib/types/transports.d.ts +93 -21
- package/lib/types/transports.d.ts.map +1 -1
- package/lib/types/transports.js +18 -2
- package/lib/types/transports.js.map +1 -1
- package/lib/types/vehicles.d.ts +2 -0
- package/lib/types/vehicles.d.ts.map +1 -1
- package/lib/types/vehicles.js.map +1 -1
- package/lib/utils/search.d.ts +4 -2
- package/lib/utils/search.d.ts.map +1 -1
- package/lib/utils/search.js +19 -1
- package/lib/utils/search.js.map +1 -1
- package/lib/utils/transports.d.ts +9 -0
- package/lib/utils/transports.d.ts.map +1 -0
- package/lib/utils/transports.js +15 -0
- package/lib/utils/transports.js.map +1 -0
- package/lib/utils/vehicles.d.ts +1 -0
- package/lib/utils/vehicles.d.ts.map +1 -1
- package/lib/utils/vehicles.js +2 -1
- package/lib/utils/vehicles.js.map +1 -1
- package/package.json +1 -1
- package/src/types/comparables.js +2 -0
- package/src/types/comparables.js.map +1 -0
- package/src/types/crm.js +50 -0
- package/src/types/crm.js.map +1 -0
- package/src/types/delivery-settings.ts +1 -0
- package/src/types/fields.js +2 -0
- package/src/types/fields.js.map +1 -0
- package/src/types/insights.js +2 -0
- package/src/types/insights.js.map +1 -0
- package/src/types/internal-events.ts +2 -0
- package/src/types/internet-vehicles.js +2 -0
- package/src/types/internet-vehicles.js.map +1 -0
- package/src/types/journeys.js +2 -0
- package/src/types/journeys.js.map +1 -0
- package/src/types/journeys.ts +5 -0
- package/src/types/leasing-settings.js +7 -0
- package/src/types/leasing-settings.js.map +1 -0
- package/src/types/marketing-emails.ts +33 -0
- package/src/types/offers.js +56 -0
- package/src/types/offers.js.map +1 -0
- package/src/types/offers.ts +1 -0
- package/src/types/opportunities.js +106 -0
- package/src/types/opportunities.js.map +1 -0
- package/src/types/payments.js +7 -0
- package/src/types/payments.js.map +1 -0
- package/src/types/quotes.js +42 -0
- package/src/types/quotes.js.map +1 -0
- package/src/types/quotes.ts +6 -0
- package/src/types/reservations.js +5 -0
- package/src/types/reservations.js.map +1 -0
- package/src/types/search.js +2 -0
- package/src/types/search.js.map +1 -0
- package/src/types/tenants.js +20 -0
- package/src/types/tenants.js.map +1 -0
- package/src/types/transports.ts +153 -28
- package/src/types/utils.js +2 -0
- package/src/types/utils.js.map +1 -0
- package/src/types/vehicles.js +94 -0
- package/src/types/vehicles.js.map +1 -0
- package/src/types/vehicles.ts +6 -1
- package/src/utils/opportunities.js +48 -0
- package/src/utils/opportunities.js.map +1 -0
- package/src/utils/search.ts +31 -2
- package/src/utils/transports.ts +37 -0
- package/src/utils/vehicles.ts +9 -1
- package/tsconfig.tsbuildinfo +1 -1
package/src/types/transports.ts
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { GPSCoord, NextList, PaginatedList } from '@bisondesk/commons-sdk/types';
|
|
2
2
|
import { Organization } from './crm.js';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { BaseEvent } from './internal-events.js';
|
|
4
|
+
import { Opportunity, OpportunityPaymentStatus } from './opportunities.js';
|
|
5
|
+
import {
|
|
6
|
+
AddressDeliveryType,
|
|
7
|
+
DeliveryTypes,
|
|
8
|
+
PickUpDeliveryType,
|
|
9
|
+
PortDeliverytype,
|
|
10
|
+
} from './quotes.js';
|
|
5
11
|
import { BaseSearchRequest, SortOrder } from './search.js';
|
|
6
|
-
import { ReferenceData } from './utils.js';
|
|
12
|
+
import { DataRecord, ReferenceData } from './utils.js';
|
|
7
13
|
import { Vehicle, VehiclePurchase } from './vehicles.js';
|
|
8
14
|
|
|
9
15
|
export enum TransportType {
|
|
@@ -42,49 +48,66 @@ export enum TransportPriority {
|
|
|
42
48
|
Urgent = 'URGENT',
|
|
43
49
|
}
|
|
44
50
|
|
|
45
|
-
export const
|
|
51
|
+
export const TransportSalesDeliveryType = {
|
|
46
52
|
Address: DeliveryTypes.ADDRESS,
|
|
47
53
|
Port: DeliveryTypes.PORT,
|
|
48
54
|
PortAndShip: DeliveryTypes.PORT_AND_SHIP,
|
|
55
|
+
PickUp: DeliveryTypes.PICK_UP,
|
|
49
56
|
} as const;
|
|
50
57
|
|
|
51
|
-
|
|
52
|
-
|
|
58
|
+
/**
|
|
59
|
+
* The customer collects the vehicle. Nothing is driven anywhere, but the papers still have to be
|
|
60
|
+
* arranged and paid for — transit plates, technical control, insurance — which is why it is a
|
|
61
|
+
* delivery type administration reviews rather than the absence of one.
|
|
62
|
+
*
|
|
63
|
+
* `dateOfPickUpByCustomer` is dropped from the quote's shape: the transport's own `date` is the
|
|
64
|
+
* collection day, and two fields for one date only ever disagree.
|
|
65
|
+
*/
|
|
66
|
+
export type TransportPickUpDeliveryType = Omit<PickUpDeliveryType, 'dateOfPickUpByCustomer'>;
|
|
67
|
+
|
|
68
|
+
export type TransportSalesDeliveryType = (
|
|
69
|
+
| PortDeliverytype
|
|
70
|
+
| AddressDeliveryType
|
|
71
|
+
| TransportPickUpDeliveryType
|
|
72
|
+
) & {
|
|
73
|
+
cleaningNeeded?: boolean;
|
|
74
|
+
driver?: string;
|
|
75
|
+
transportCompany?: string;
|
|
76
|
+
date?: string; // Pick-up (purchase) | Delivery (OPP) (leaves parking)
|
|
77
|
+
adminCost?: string; // user-entered administrative cost summed into totalCostExclVat
|
|
78
|
+
stackingVehicleCostPrice?: string; // overrides the stacking rate from the delivery settings
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export type TransportPurchaseDeliveryType = AddressDeliveryType & {
|
|
82
|
+
cleaningNeeded?: boolean;
|
|
83
|
+
driver?: string;
|
|
84
|
+
transportCompany?: string;
|
|
85
|
+
date?: string; // Pick-up (purchase) | Delivery (OPP) (leaves parking)
|
|
86
|
+
adminCost?: string; // user-entered administrative cost summed into totalCostExclVat
|
|
87
|
+
stackingVehicleCostPrice?: string; // overrides the stacking rate from the delivery settings
|
|
88
|
+
};
|
|
53
89
|
|
|
54
90
|
export type Transport = {
|
|
55
91
|
id: string;
|
|
56
92
|
vehicleId: string;
|
|
57
93
|
opportunityId?: string;
|
|
58
94
|
|
|
59
|
-
type: TransportType;
|
|
60
|
-
deliveryType: TransportDeliveryType;
|
|
61
95
|
status: TransportStatus;
|
|
62
|
-
|
|
63
96
|
priority: TransportPriority;
|
|
64
97
|
|
|
65
98
|
// administrative fields
|
|
66
99
|
adminApproved: boolean;
|
|
67
|
-
|
|
100
|
+
adminApprovedAt?: string;
|
|
101
|
+
adminApprovedBy?: string;
|
|
102
|
+
transportWithoutPayment?: boolean;
|
|
68
103
|
notes?: string;
|
|
69
104
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
port?: {
|
|
76
|
-
sourcePort?: string;
|
|
77
|
-
destinationPort?: string;
|
|
78
|
-
|
|
79
|
-
deliveryLocation?: string;
|
|
80
|
-
bookingRef?: string;
|
|
81
|
-
shippingCompany?: string;
|
|
82
|
-
ets?: string;
|
|
83
|
-
eta?: string;
|
|
105
|
+
totals: {
|
|
106
|
+
totalRetailAmountExclVat: string;
|
|
107
|
+
totalCostExclVat: string;
|
|
108
|
+
estimatedTotalCostExclVat?: string;
|
|
84
109
|
};
|
|
85
110
|
|
|
86
|
-
address?: LocationValue;
|
|
87
|
-
|
|
88
111
|
log: Array<{
|
|
89
112
|
status: TransportStatus;
|
|
90
113
|
completedAt: string;
|
|
@@ -100,7 +123,16 @@ export type Transport = {
|
|
|
100
123
|
createdBy: string;
|
|
101
124
|
updatedAt: string;
|
|
102
125
|
updatedBy: string;
|
|
103
|
-
}
|
|
126
|
+
} & (
|
|
127
|
+
| {
|
|
128
|
+
type: TransportType.PurchasePickUp;
|
|
129
|
+
delivery: TransportPurchaseDeliveryType;
|
|
130
|
+
}
|
|
131
|
+
| {
|
|
132
|
+
type: TransportType.SaleDelivery;
|
|
133
|
+
delivery: TransportSalesDeliveryType;
|
|
134
|
+
}
|
|
135
|
+
);
|
|
104
136
|
|
|
105
137
|
export enum TransportWarningCode {
|
|
106
138
|
PurchaseLogisticsNotReady = 'purchase_logistics_not_ready',
|
|
@@ -111,15 +143,108 @@ export type TransportWarning = {
|
|
|
111
143
|
details?: Record<string, string | number | boolean>;
|
|
112
144
|
};
|
|
113
145
|
|
|
146
|
+
export type TransportCost = Pick<Transport, 'totals'>;
|
|
147
|
+
|
|
148
|
+
export type TransportRecord = DataRecord<Transport>;
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* What a requester may change on a transport, field group by field group. The server filters an
|
|
152
|
+
* update through these and returns the ones granted on the record, so the drawer disables what the
|
|
153
|
+
* user cannot touch instead of deciding for itself from the user's roles.
|
|
154
|
+
*/
|
|
155
|
+
export enum TransportActions {
|
|
156
|
+
/** The route: addresses, ports, dates, costs. */
|
|
157
|
+
SetDelivery = 'set_delivery',
|
|
158
|
+
SetNotes = 'set_notes',
|
|
159
|
+
/** The administrative sign-off, and the clearance to move before the payment. */
|
|
160
|
+
SetApproval = 'set_approval',
|
|
161
|
+
/** The board column. */
|
|
162
|
+
SetStatus = 'set_status',
|
|
163
|
+
SetPriority = 'set_priority',
|
|
164
|
+
SetBookingRef = 'set_booking_ref',
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Every field a write may set, in one shape. `delivery` is replaced whole when it is sent — there is
|
|
169
|
+
* no partial merge into the stored one — which is why `bookingRef` rides alongside as a scalar the
|
|
170
|
+
* server writes into the delivery it ends up with.
|
|
171
|
+
*/
|
|
172
|
+
export type TransportUpdate = {
|
|
173
|
+
delivery?: TransportSalesDeliveryType | TransportPurchaseDeliveryType;
|
|
174
|
+
notes?: string | null;
|
|
175
|
+
adminApproved?: boolean;
|
|
176
|
+
transportWithoutPayment?: boolean;
|
|
177
|
+
status?: TransportStatus;
|
|
178
|
+
priority?: TransportPriority;
|
|
179
|
+
bookingRef?: string | null;
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
type BaseTransportEvent = {
|
|
183
|
+
actionAt: string;
|
|
184
|
+
userId: string;
|
|
185
|
+
tenantId: string;
|
|
186
|
+
transportId: string;
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
export type TransportCreateEvent = BaseTransportEvent & {
|
|
190
|
+
action: 'create';
|
|
191
|
+
data: Transport;
|
|
192
|
+
previousData?: undefined;
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
export type TransportUpdateEvent = BaseTransportEvent & {
|
|
196
|
+
action: 'update';
|
|
197
|
+
data: Transport;
|
|
198
|
+
previousData: Transport;
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
/** Published whenever a transport is written. Carries both sides so subscribers judge relevance. */
|
|
202
|
+
export type TransportEvent = BaseEvent & (TransportCreateEvent | TransportUpdateEvent);
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Where the money stands on the record the transport serves: the customer's payments on the
|
|
206
|
+
* opportunity for a sale delivery, ours to the supplier for a purchase pick-up. Denormalised at
|
|
207
|
+
* indexing time so the board can show it without joining.
|
|
208
|
+
*/
|
|
209
|
+
export type TransportPayment = {
|
|
210
|
+
status: OpportunityPaymentStatus;
|
|
211
|
+
/** Only a sale delivery knows the ratio — a pick-up has milestones, not a running total. */
|
|
212
|
+
percentagePaid?: string;
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
/** Which address the map pin came from, so the UI can tell an exact pin from an approximate one. */
|
|
216
|
+
export enum TransportLocationSource {
|
|
217
|
+
DeliveryAddress = 'delivery_address',
|
|
218
|
+
OriginPort = 'origin_port',
|
|
219
|
+
OrgAddress = 'org_address',
|
|
220
|
+
}
|
|
221
|
+
|
|
114
222
|
export type SearchTransport = {
|
|
115
223
|
transport: Transport;
|
|
116
224
|
vehicle: Vehicle;
|
|
117
225
|
org: Organization;
|
|
118
226
|
opportunity?: Opportunity;
|
|
119
227
|
purchase: VehiclePurchase;
|
|
228
|
+
tasks: {
|
|
229
|
+
totalTasks: number;
|
|
230
|
+
openTasks: number;
|
|
231
|
+
nextDueAt?: string;
|
|
232
|
+
};
|
|
120
233
|
custom: {
|
|
121
234
|
commercialUserIds: string[];
|
|
122
235
|
warnings: TransportWarning[];
|
|
236
|
+
priorityOrdinal: number;
|
|
237
|
+
payment: TransportPayment;
|
|
238
|
+
/**
|
|
239
|
+
* Where the transport happens: the delivery address, or the origin port for a shipped delivery.
|
|
240
|
+
* Denormalised into one field because the map clusters over a single geo_point, and a port
|
|
241
|
+
* delivery carries no address of its own.
|
|
242
|
+
*
|
|
243
|
+
* Deliberately narrower than `GPSCoord`: a geo_point field rejects any key beyond lat/lon, so
|
|
244
|
+
* the map zoom the coordinates picker stores must not reach the index.
|
|
245
|
+
*/
|
|
246
|
+
location?: Pick<GPSCoord, 'lat' | 'lon'>;
|
|
247
|
+
locationSource?: TransportLocationSource;
|
|
123
248
|
};
|
|
124
249
|
};
|
|
125
250
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["utils.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
export var VehicleRoles;
|
|
2
|
+
(function (VehicleRoles) {
|
|
3
|
+
VehicleRoles["SALE"] = "SALE";
|
|
4
|
+
VehicleRoles["OWN_USE"] = "OWN_USE";
|
|
5
|
+
VehicleRoles["RENTAL"] = "RENTAL";
|
|
6
|
+
VehicleRoles["BUILD"] = "BUILD";
|
|
7
|
+
VehicleRoles["UNKNOWN"] = "UNKNOWN";
|
|
8
|
+
VehicleRoles["BANK_FINANCE"] = "BANK_FINANCE";
|
|
9
|
+
})(VehicleRoles || (VehicleRoles = {}));
|
|
10
|
+
export var VehicleFuelType;
|
|
11
|
+
(function (VehicleFuelType) {
|
|
12
|
+
VehicleFuelType["Petrol"] = "PETROL";
|
|
13
|
+
VehicleFuelType["Diesel"] = "DIESEL";
|
|
14
|
+
VehicleFuelType["LPG"] = "LPG";
|
|
15
|
+
VehicleFuelType["LPG_G3"] = "LPG_G3";
|
|
16
|
+
VehicleFuelType["Electric"] = "ELECTRIC";
|
|
17
|
+
VehicleFuelType["CNG"] = "CNG";
|
|
18
|
+
VehicleFuelType["Hydrogen"] = "HYDROGEN";
|
|
19
|
+
VehicleFuelType["BioEthanol"] = "BIO_ETHANOL";
|
|
20
|
+
VehicleFuelType["BioDiesel"] = "BIO_DIESEL";
|
|
21
|
+
VehicleFuelType["E85"] = "E85";
|
|
22
|
+
VehicleFuelType["HCNG"] = "HCNG";
|
|
23
|
+
VehicleFuelType["LPG_Petrol"] = "LPG_PETROL";
|
|
24
|
+
VehicleFuelType["LPG_Diesel"] = "LPG_DIESEL";
|
|
25
|
+
VehicleFuelType["LPG_G3_Petrol"] = "LPG_G3_PETROL";
|
|
26
|
+
VehicleFuelType["LPG_G3_Diesel"] = "LPG_G3_DIESEL";
|
|
27
|
+
VehicleFuelType["Electric_Diesel"] = "ELECTRIC_DIESEL";
|
|
28
|
+
VehicleFuelType["Electric_Petrol"] = "ELECTRIC_PETROL";
|
|
29
|
+
VehicleFuelType["Hybrid"] = "HYBRID";
|
|
30
|
+
VehicleFuelType["Other"] = "OTHER";
|
|
31
|
+
})(VehicleFuelType || (VehicleFuelType = {}));
|
|
32
|
+
export var VehicleTachoTypes;
|
|
33
|
+
(function (VehicleTachoTypes) {
|
|
34
|
+
VehicleTachoTypes["Analog"] = "ANALOG";
|
|
35
|
+
VehicleTachoTypes["Digital"] = "DIGITAL";
|
|
36
|
+
VehicleTachoTypes["SmartTacho1"] = "SMART_TACHO_1";
|
|
37
|
+
VehicleTachoTypes["SmartTacho2"] = "SMART_TACHO_2";
|
|
38
|
+
})(VehicleTachoTypes || (VehicleTachoTypes = {}));
|
|
39
|
+
export var SearchVehicleSaleDealStatus;
|
|
40
|
+
(function (SearchVehicleSaleDealStatus) {
|
|
41
|
+
SearchVehicleSaleDealStatus["NotSold"] = "NOT_SOLD";
|
|
42
|
+
SearchVehicleSaleDealStatus["Reserved"] = "RESERVED";
|
|
43
|
+
SearchVehicleSaleDealStatus["Sold"] = "SOLD";
|
|
44
|
+
})(SearchVehicleSaleDealStatus || (SearchVehicleSaleDealStatus = {}));
|
|
45
|
+
export var VehiclePurchaseDealStatus;
|
|
46
|
+
(function (VehiclePurchaseDealStatus) {
|
|
47
|
+
VehiclePurchaseDealStatus["Marketing"] = "MARKETING";
|
|
48
|
+
VehiclePurchaseDealStatus["PurchaseEvaluation"] = "PURCHASE_EVALUATION";
|
|
49
|
+
VehiclePurchaseDealStatus["PurchaseAgreed"] = "PURCHASE_AGREED";
|
|
50
|
+
VehiclePurchaseDealStatus["FirstPaymentAllowed"] = "FIRST_PAYMENT_ALLOWED";
|
|
51
|
+
VehiclePurchaseDealStatus["FirstPaymentSent"] = "FIRST_PAYMENT_SENT";
|
|
52
|
+
VehiclePurchaseDealStatus["FullPaymentAllowed"] = "FULL_PAYMENT_ALLOWED";
|
|
53
|
+
VehiclePurchaseDealStatus["FullPaymentSent"] = "FULL_PAYMENT_SENT";
|
|
54
|
+
})(VehiclePurchaseDealStatus || (VehiclePurchaseDealStatus = {}));
|
|
55
|
+
export var VehiclePurchaseLogisticsStatus;
|
|
56
|
+
(function (VehiclePurchaseLogisticsStatus) {
|
|
57
|
+
VehiclePurchaseLogisticsStatus["AtOrigin"] = "AT_ORIGIN";
|
|
58
|
+
VehiclePurchaseLogisticsStatus["Arrived"] = "ARRIVED";
|
|
59
|
+
VehiclePurchaseLogisticsStatus["CheckedIn"] = "CHECKED_IN";
|
|
60
|
+
})(VehiclePurchaseLogisticsStatus || (VehiclePurchaseLogisticsStatus = {}));
|
|
61
|
+
export var VehiclePurchaseDeliveryType;
|
|
62
|
+
(function (VehiclePurchaseDeliveryType) {
|
|
63
|
+
VehiclePurchaseDeliveryType["PickUp"] = "PICK_UP";
|
|
64
|
+
VehiclePurchaseDeliveryType["DropOff"] = "DROP_OFF";
|
|
65
|
+
})(VehiclePurchaseDeliveryType || (VehiclePurchaseDeliveryType = {}));
|
|
66
|
+
export var VehicleActions;
|
|
67
|
+
(function (VehicleActions) {
|
|
68
|
+
VehicleActions["EditAdminDocs"] = "edit_admin_docs";
|
|
69
|
+
VehicleActions["EditEngineeringDocs"] = "edit_engineering_docs";
|
|
70
|
+
VehicleActions["EditMarketing"] = "edit_marketing";
|
|
71
|
+
VehicleActions["EditMedia"] = "edit_media";
|
|
72
|
+
VehicleActions["EditPictures"] = "edit_pictures";
|
|
73
|
+
VehicleActions["EditPrices"] = "edit_prices";
|
|
74
|
+
VehicleActions["EditPurchaseDeal"] = "edit_purchase_deal";
|
|
75
|
+
VehicleActions["EditPurchaseLogistics"] = "edit_purchase_logistics";
|
|
76
|
+
VehicleActions["EditSpecs"] = "edit_specs";
|
|
77
|
+
VehicleActions["EditVideos"] = "edit_videos";
|
|
78
|
+
VehicleActions["ViewLeads"] = "view_leads";
|
|
79
|
+
VehicleActions["ViewSimilarOnline"] = "view_similar_online";
|
|
80
|
+
VehicleActions["ViewPurchase"] = "view_purchase";
|
|
81
|
+
VehicleActions["ViewPriceHistory"] = "view_price_history";
|
|
82
|
+
VehicleActions["DeleteVehicle"] = "delete_vehicle";
|
|
83
|
+
})(VehicleActions || (VehicleActions = {}));
|
|
84
|
+
export var PurchaseActions;
|
|
85
|
+
(function (PurchaseActions) {
|
|
86
|
+
PurchaseActions["ViewDeal"] = "view_deal";
|
|
87
|
+
PurchaseActions["ViewSupplierOrganization"] = "view_supplier_organization";
|
|
88
|
+
PurchaseActions["ViewContact"] = "view_contact";
|
|
89
|
+
PurchaseActions["ViewLegalAddress"] = "view_legal_address";
|
|
90
|
+
PurchaseActions["ViewPickupAddress"] = "view_pickup_address";
|
|
91
|
+
PurchaseActions["ViewEstimatedCosts"] = "view_estimated_costs";
|
|
92
|
+
PurchaseActions["ViewFinanceDocuments"] = "view_finance_documents";
|
|
93
|
+
})(PurchaseActions || (PurchaseActions = {}));
|
|
94
|
+
//# sourceMappingURL=vehicles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vehicles.js","sourceRoot":"","sources":["vehicles.ts"],"names":[],"mappings":"AAeA,MAAM,CAAN,IAAY,YAOX;AAPD,WAAY,YAAY;IACtB,6BAAa,CAAA;IACb,mCAAmB,CAAA;IACnB,iCAAiB,CAAA;IACjB,+BAAe,CAAA;IACf,mCAAmB,CAAA;IACnB,6CAA6B,CAAA;AAC/B,CAAC,EAPW,YAAY,KAAZ,YAAY,QAOvB;AAED,MAAM,CAAN,IAAY,eAoBX;AApBD,WAAY,eAAe;IACzB,oCAAiB,CAAA;IACjB,oCAAiB,CAAA;IACjB,8BAAW,CAAA;IACX,oCAAiB,CAAA;IACjB,wCAAqB,CAAA;IACrB,8BAAW,CAAA;IACX,wCAAqB,CAAA;IACrB,6CAA0B,CAAA;IAC1B,2CAAwB,CAAA;IACxB,8BAAW,CAAA;IACX,gCAAa,CAAA;IACb,4CAAyB,CAAA;IACzB,4CAAyB,CAAA;IACzB,kDAA+B,CAAA;IAC/B,kDAA+B,CAAA;IAC/B,sDAAmC,CAAA;IACnC,sDAAmC,CAAA;IACnC,oCAAiB,CAAA;IACjB,kCAAe,CAAA;AACjB,CAAC,EApBW,eAAe,KAAf,eAAe,QAoB1B;AAED,MAAM,CAAN,IAAY,iBAKX;AALD,WAAY,iBAAiB;IAC3B,sCAAiB,CAAA;IACjB,wCAAmB,CAAA;IACnB,kDAA6B,CAAA;IAC7B,kDAA6B,CAAA;AAC/B,CAAC,EALW,iBAAiB,KAAjB,iBAAiB,QAK5B;AAkLD,MAAM,CAAN,IAAY,2BAIX;AAJD,WAAY,2BAA2B;IACrC,mDAAoB,CAAA;IACpB,oDAAqB,CAAA;IACrB,4CAAa,CAAA;AACf,CAAC,EAJW,2BAA2B,KAA3B,2BAA2B,QAItC;AAuWD,MAAM,CAAN,IAAY,yBAQX;AARD,WAAY,yBAAyB;IACnC,oDAAuB,CAAA;IACvB,uEAA0C,CAAA;IAC1C,+DAAkC,CAAA;IAClC,0EAA6C,CAAA;IAC7C,oEAAuC,CAAA;IACvC,wEAA2C,CAAA;IAC3C,kEAAqC,CAAA;AACvC,CAAC,EARW,yBAAyB,KAAzB,yBAAyB,QAQpC;AAED,MAAM,CAAN,IAAY,8BAIX;AAJD,WAAY,8BAA8B;IACxC,wDAAsB,CAAA;IACtB,qDAAmB,CAAA;IACnB,0DAAwB,CAAA;AAC1B,CAAC,EAJW,8BAA8B,KAA9B,8BAA8B,QAIzC;AAED,MAAM,CAAN,IAAY,2BAGX;AAHD,WAAY,2BAA2B;IACrC,iDAAkB,CAAA;IAClB,mDAAoB,CAAA;AACtB,CAAC,EAHW,2BAA2B,KAA3B,2BAA2B,QAGtC;AAwGD,MAAM,CAAN,IAAY,cAgBX;AAhBD,WAAY,cAAc;IACxB,mDAAiC,CAAA;IACjC,+DAA6C,CAAA;IAC7C,kDAAgC,CAAA;IAChC,0CAAwB,CAAA;IACxB,gDAA8B,CAAA;IAC9B,4CAA0B,CAAA;IAC1B,yDAAuC,CAAA;IACvC,mEAAiD,CAAA;IACjD,0CAAwB,CAAA;IACxB,4CAA0B,CAAA;IAC1B,0CAAwB,CAAA;IACxB,2DAAyC,CAAA;IACzC,gDAA8B,CAAA;IAC9B,yDAAuC,CAAA;IACvC,kDAAgC,CAAA;AAClC,CAAC,EAhBW,cAAc,KAAd,cAAc,QAgBzB;AAED,MAAM,CAAN,IAAY,eAQX;AARD,WAAY,eAAe;IACzB,yCAAsB,CAAA;IACtB,0EAAuD,CAAA;IACvD,+CAA4B,CAAA;IAC5B,0DAAuC,CAAA;IACvC,4DAAyC,CAAA;IACzC,8DAA2C,CAAA;IAC3C,kEAA+C,CAAA;AACjD,CAAC,EARW,eAAe,KAAf,eAAe,QAQ1B"}
|
package/src/types/vehicles.ts
CHANGED
|
@@ -142,6 +142,7 @@ export type NewVehicleRequest = {
|
|
|
142
142
|
deliveryType?: VehiclePurchaseDeliveryType;
|
|
143
143
|
pickupAddress?: LocationValue;
|
|
144
144
|
status: VehiclePurchaseLogisticsStatus;
|
|
145
|
+
transportWithoutPayment?: boolean;
|
|
145
146
|
};
|
|
146
147
|
prices: {
|
|
147
148
|
minimum?: string;
|
|
@@ -593,7 +594,10 @@ export type VehicleExternalInfo = {
|
|
|
593
594
|
* declared a location for this country.
|
|
594
595
|
*
|
|
595
596
|
* Only the original owner recomputes on save; cross-tenant copies preserve
|
|
596
|
-
* the array.
|
|
597
|
+
* the array. One exception: the target operator may edit the branch-country
|
|
598
|
+
* entry's `deliveryPrice`. The first such edit is a price takeover
|
|
599
|
+
* (`internal.sourcePriceOverride`), after which the snapshot collapses to the
|
|
600
|
+
* branch-country row and the sync stops rebuilding sales conditions.
|
|
597
601
|
*/
|
|
598
602
|
localizedPricing?: Array<{
|
|
599
603
|
countryCode: string;
|
|
@@ -852,6 +856,7 @@ export type VehiclePurchase = {
|
|
|
852
856
|
deliveryType?: VehiclePurchaseDeliveryType;
|
|
853
857
|
pickupAddress?: LocationValue;
|
|
854
858
|
availableWeek?: string; // ISOWEEK, e.g. 2016-W02
|
|
859
|
+
transportWithoutPayment?: boolean;
|
|
855
860
|
};
|
|
856
861
|
};
|
|
857
862
|
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { OpportunityStatus, OpportunityStatusOrder } from '../types/opportunities.js';
|
|
2
|
+
const compareOpportunityStatus = (input) => {
|
|
3
|
+
const { currentStatus, referenceStatus } = input;
|
|
4
|
+
const currentStatusIdx = OpportunityStatusOrder.indexOf(currentStatus);
|
|
5
|
+
const referenceStatusIdx = OpportunityStatusOrder.indexOf(referenceStatus);
|
|
6
|
+
return {
|
|
7
|
+
currentStatusIdx,
|
|
8
|
+
referenceStatusIdx,
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
export const getPreviousStatus = (currentStatus) => {
|
|
12
|
+
const currentIndex = OpportunityStatusOrder.indexOf(currentStatus);
|
|
13
|
+
if (currentIndex === 0) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
return OpportunityStatusOrder[currentIndex - 1];
|
|
17
|
+
};
|
|
18
|
+
export const getNextStatus = (status) => {
|
|
19
|
+
const statusIndex = OpportunityStatusOrder.indexOf(status);
|
|
20
|
+
if (statusIndex >= OpportunityStatusOrder.length - 1) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
return OpportunityStatusOrder[statusIndex + 1];
|
|
24
|
+
};
|
|
25
|
+
export const isSameOrAfterReferenceOpportunityStatus = (input) => {
|
|
26
|
+
const { currentStatusIdx, referenceStatusIdx } = compareOpportunityStatus(input);
|
|
27
|
+
return currentStatusIdx >= referenceStatusIdx;
|
|
28
|
+
};
|
|
29
|
+
export const isAfterReferenceOpportunityStatus = (input) => {
|
|
30
|
+
const { currentStatusIdx, referenceStatusIdx } = compareOpportunityStatus(input);
|
|
31
|
+
return currentStatusIdx > referenceStatusIdx;
|
|
32
|
+
};
|
|
33
|
+
export const isSameOrBeforeReferenceOpportunityStatus = (input) => {
|
|
34
|
+
const { currentStatusIdx, referenceStatusIdx } = compareOpportunityStatus(input);
|
|
35
|
+
return currentStatusIdx <= referenceStatusIdx;
|
|
36
|
+
};
|
|
37
|
+
export const isBeforeReferenceOpportunityStatus = (input) => {
|
|
38
|
+
const { currentStatusIdx, referenceStatusIdx } = compareOpportunityStatus(input);
|
|
39
|
+
return currentStatusIdx < referenceStatusIdx;
|
|
40
|
+
};
|
|
41
|
+
export const isOpportunityLost = (input) => input.lostAt != null;
|
|
42
|
+
export const getOpportunityReservedStatus = () => [
|
|
43
|
+
OpportunityStatus.REVIEW,
|
|
44
|
+
OpportunityStatus.PREPARATION,
|
|
45
|
+
OpportunityStatus.DELIVERY,
|
|
46
|
+
];
|
|
47
|
+
export const isOpportunityReserved = (input) => getOpportunityReservedStatus().includes(input.status);
|
|
48
|
+
//# sourceMappingURL=opportunities.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opportunities.js","sourceRoot":"","sources":["opportunities.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAOnG,MAAM,wBAAwB,GAAG,CAAC,KAAmB,EAAE,EAAE;IACvD,MAAM,EAAE,aAAa,EAAE,eAAe,EAAE,GAAG,KAAK,CAAC;IAEjD,MAAM,gBAAgB,GAAG,sBAAsB,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IACvE,MAAM,kBAAkB,GAAG,sBAAsB,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IAE3E,OAAO;QACL,gBAAgB;QAChB,kBAAkB;KACnB,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAC/B,aAAgC,EACD,EAAE;IACjC,MAAM,YAAY,GAAG,sBAAsB,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IACnE,IAAI,YAAY,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO;IACT,CAAC;IACD,OAAO,sBAAsB,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC;AAClD,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,MAAyB,EAAiC,EAAE;IACxF,MAAM,WAAW,GAAG,sBAAsB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3D,IAAI,WAAW,IAAI,sBAAsB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrD,OAAO;IACT,CAAC;IACD,OAAO,sBAAsB,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC;AACjD,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,uCAAuC,GAAG,CAAC,KAAmB,EAAW,EAAE;IACtF,MAAM,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,GAAG,wBAAwB,CAAC,KAAK,CAAC,CAAC;IAEjF,OAAO,gBAAgB,IAAI,kBAAkB,CAAC;AAChD,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,iCAAiC,GAAG,CAAC,KAAmB,EAAW,EAAE;IAChF,MAAM,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,GAAG,wBAAwB,CAAC,KAAK,CAAC,CAAC;IAEjF,OAAO,gBAAgB,GAAG,kBAAkB,CAAC;AAC/C,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,wCAAwC,GAAG,CAAC,KAAmB,EAAW,EAAE;IACvF,MAAM,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,GAAG,wBAAwB,CAAC,KAAK,CAAC,CAAC;IAEjF,OAAO,gBAAgB,IAAI,kBAAkB,CAAC;AAChD,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,kCAAkC,GAAG,CAAC,KAAmB,EAAW,EAAE;IACjF,MAAM,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,GAAG,wBAAwB,CAAC,KAAK,CAAC,CAAC;IAEjF,OAAO,gBAAgB,GAAG,kBAAkB,CAAC;AAC/C,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,KAAkB,EAAW,EAAE,CAAC,KAAK,CAAC,MAAM,IAAI,IAAI,CAAC;AAEvF,MAAM,CAAC,MAAM,4BAA4B,GAAG,GAAwB,EAAE,CAAC;IACrE,iBAAiB,CAAC,MAAM;IACxB,iBAAiB,CAAC,WAAW;IAC7B,iBAAiB,CAAC,QAAQ;CAC3B,CAAC;AAEF,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,KAAkB,EAAW,EAAE,CACnE,4BAA4B,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC"}
|
package/src/utils/search.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { LeasingContract } from '../types/leasing.js';
|
|
|
5
5
|
import { DebtorsDossier, SearchDebtorsDossier } from '../types/leasing-debtors.js';
|
|
6
6
|
import { SearchLeasingContract } from '../types/leasing-search.js';
|
|
7
7
|
import { Opportunity, SearchOpportunity } from '../types/opportunities.js';
|
|
8
|
+
import { SearchTransport, Transport } from '../types/transports.js';
|
|
8
9
|
import { SearchVehicle, Vehicle } from '../types/vehicles.js';
|
|
9
10
|
|
|
10
11
|
export type GlobalBusinessEntity =
|
|
@@ -12,13 +13,15 @@ export type GlobalBusinessEntity =
|
|
|
12
13
|
| Vehicle
|
|
13
14
|
| Organization
|
|
14
15
|
| LeasingContract
|
|
15
|
-
| DebtorsDossier
|
|
16
|
+
| DebtorsDossier
|
|
17
|
+
| Transport;
|
|
16
18
|
export type GlobalSearchEntity =
|
|
17
19
|
| SearchOpportunity
|
|
18
20
|
| SearchVehicle
|
|
19
21
|
| SearchOrganization
|
|
20
22
|
| SearchLeasingContract
|
|
21
|
-
| SearchDebtorsDossier
|
|
23
|
+
| SearchDebtorsDossier
|
|
24
|
+
| SearchTransport;
|
|
22
25
|
|
|
23
26
|
export const getCustomerInformation = (org?: Organization, contact?: Contact) => {
|
|
24
27
|
if (org != null) {
|
|
@@ -41,6 +44,7 @@ export const mappings: {
|
|
|
41
44
|
[key in BusinessEntityIds]?: {
|
|
42
45
|
getMainEntity: (entity: any) => GlobalBusinessEntity;
|
|
43
46
|
getTitle: (entity: any) => string;
|
|
47
|
+
getTitleForSearchEntity?: (entity: any) => string;
|
|
44
48
|
getTags: (entity: any) => string[];
|
|
45
49
|
getTagsForSearchEntity?: (entity: any) => string[];
|
|
46
50
|
getDescription?: (entity: any) => string;
|
|
@@ -123,6 +127,30 @@ export const mappings: {
|
|
|
123
127
|
getTitle: (dossier: DebtorsDossier) => dossier.id,
|
|
124
128
|
getTags: (dossier: DebtorsDossier) => [dossier.id].filter(Boolean) as string[],
|
|
125
129
|
},
|
|
130
|
+
// A transport on its own knows only ids, so the readable title and description come from the
|
|
131
|
+
// search entity (vehicle + customer). The bare-record variants stay on the id as a fallback.
|
|
132
|
+
[BusinessEntityIds.Transports]: {
|
|
133
|
+
getMainEntity: (entity: SearchTransport) => entity.transport,
|
|
134
|
+
getTitle: (transport: Transport) => transport.id,
|
|
135
|
+
getTitleForSearchEntity: (entity: SearchTransport) =>
|
|
136
|
+
entity.vehicle?.external.identification?.stockNumber ?? entity.transport.id,
|
|
137
|
+
getDescription: (entity: SearchTransport) => {
|
|
138
|
+
const customer = getCustomerInformation(entity.org);
|
|
139
|
+
const parts = [customer?.code, customer?.title].filter(Boolean) as string[];
|
|
140
|
+
// The API rejects an empty association description, so never return one.
|
|
141
|
+
return parts.length > 0 ? joinWithSeparator(parts) : entity.transport.id;
|
|
142
|
+
},
|
|
143
|
+
getTags: (transport: Transport) => [transport.id],
|
|
144
|
+
getTagsForSearchEntity: (entity: SearchTransport) => {
|
|
145
|
+
const vehicleTags =
|
|
146
|
+
entity.vehicle != null
|
|
147
|
+
? getTagsForBusinessEntity(BusinessEntityIds.Vehicles, entity.vehicle)
|
|
148
|
+
: [];
|
|
149
|
+
return [...vehicleTags, entity.transport.id, entity.transport.opportunityId].filter(
|
|
150
|
+
Boolean,
|
|
151
|
+
) as string[];
|
|
152
|
+
},
|
|
153
|
+
},
|
|
126
154
|
};
|
|
127
155
|
|
|
128
156
|
const getMapping = (businessEntityId: BusinessEntityIds) => {
|
|
@@ -186,6 +214,7 @@ export const getTitleForSearchBusinessEntity = (
|
|
|
186
214
|
businessEntityId: BusinessEntityIds,
|
|
187
215
|
searchEntity: GlobalSearchEntity,
|
|
188
216
|
) =>
|
|
217
|
+
getMapping(businessEntityId).getTitleForSearchEntity?.(searchEntity) ??
|
|
189
218
|
getTitleForBusinessEntity(
|
|
190
219
|
businessEntityId,
|
|
191
220
|
getMainEntityFromSearchEntity(businessEntityId, searchEntity),
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Opportunity } from '../types/opportunities.js';
|
|
2
|
+
import { SearchTransport, Transport, TransportType } from '../types/transports.js';
|
|
3
|
+
import { VehiclePurchase, VehiclePurchaseLogisticsStatus } from '../types/vehicles.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A transport is finished when the journey it describes has demonstrably happened: the customer has
|
|
7
|
+
* the vehicle for a sale delivery, the vehicle is on our park for a purchase pick-up. From then on
|
|
8
|
+
* the record is history and the server refuses edits to it.
|
|
9
|
+
*
|
|
10
|
+
* Not the same thing as the transport's own `doneAt`. Delivery can be declared on the opportunity
|
|
11
|
+
* (a CMR, a delivery confirmation) and arrival on the purchase, without the transport ever being
|
|
12
|
+
* moved — and reversing either of those is what unlocks the transport again, which a status stamped
|
|
13
|
+
* on the transport itself could not express.
|
|
14
|
+
*
|
|
15
|
+
* Shared so the board, the drawer and the server all answer the question the same way.
|
|
16
|
+
*/
|
|
17
|
+
export const isTransportCompleted = (
|
|
18
|
+
transport: Transport,
|
|
19
|
+
context: { opportunity?: Opportunity; purchase?: VehiclePurchase },
|
|
20
|
+
): boolean => {
|
|
21
|
+
if (transport.type === TransportType.SaleDelivery) {
|
|
22
|
+
return context.opportunity?.deliveredAt != null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const status = context.purchase?.logistics.status;
|
|
26
|
+
return (
|
|
27
|
+
status === VehiclePurchaseLogisticsStatus.Arrived ||
|
|
28
|
+
status === VehiclePurchaseLogisticsStatus.CheckedIn
|
|
29
|
+
);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/** The same question asked of a search document, which already carries both sides. */
|
|
33
|
+
export const isSearchTransportCompleted = (record: SearchTransport): boolean =>
|
|
34
|
+
isTransportCompleted(record.transport, {
|
|
35
|
+
opportunity: record.opportunity,
|
|
36
|
+
purchase: record.purchase,
|
|
37
|
+
});
|
package/src/utils/vehicles.ts
CHANGED
|
@@ -190,7 +190,15 @@ export const getNextRelatedStockNumber = (stockNumber: string): string => {
|
|
|
190
190
|
return `${baseStockNumber}${nextCounterLetters}`;
|
|
191
191
|
};
|
|
192
192
|
|
|
193
|
-
|
|
193
|
+
/**
|
|
194
|
+
* Canonical VIN shape: exactly 17 characters from the VIN alphabet — letters A–Z and
|
|
195
|
+
* digits 0–9, excluding I, O and Q. Unanchored so callers can wrap their own
|
|
196
|
+
* boundary/anchor policy when scanning free text (mirror of {@link buildStockNumberPattern}).
|
|
197
|
+
* Use {@link VIN_PATTERN} / {@link isValidVin} to validate a whole string.
|
|
198
|
+
*/
|
|
199
|
+
export const VIN_BODY_PATTERN = '[A-HJ-NPR-Z0-9]{17}';
|
|
200
|
+
|
|
201
|
+
export const VIN_PATTERN = new RegExp(`^${VIN_BODY_PATTERN}$`);
|
|
194
202
|
|
|
195
203
|
export const isValidVin = (vin: string): boolean => VIN_PATTERN.test(vin);
|
|
196
204
|
|