sahab-utils 1.0.0

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.
@@ -0,0 +1,579 @@
1
+ import { DocumentReference, GeoPoint, Timestamp } from 'firebase/firestore'
2
+
3
+ export type GeoPointType = GeoPoint;
4
+ export type TimestampType = Timestamp
5
+
6
+ export type DistenceDueration = {
7
+ distance: string;
8
+ duration: string;
9
+ };
10
+
11
+ export enum CarSize {
12
+ X_SMALL,
13
+ SMALL,
14
+ MEDIUM,
15
+ LARGE,
16
+ X_LARGE,
17
+ }
18
+
19
+ export type Car = {
20
+ carId: string;
21
+ manufacturer: string;
22
+ model: string;
23
+ size: CarSize;
24
+ year: string;
25
+ color: string;
26
+ plateLetters: string;
27
+ plateNumber: string;
28
+ imageUrl?: string;
29
+ };
30
+
31
+ export type Client = {
32
+ uid: string;
33
+ phoneNumber: string;
34
+ firstName: string;
35
+ lastName: string;
36
+ email?: string;
37
+ balance: number;
38
+ rating?: number;
39
+ imageUrl?: string;
40
+ };
41
+
42
+ export type Price = {
43
+ size: CarSize;
44
+ price: number;
45
+ };
46
+
47
+ export enum OrderStatus {
48
+ PENDING, // 0
49
+ CONFIRMED, // 1
50
+ REJECTED, // 2
51
+ EXPIRED, // 3
52
+ IN_PROGRESS, // 4
53
+ COMPLETED, // 5
54
+ CANCELLED, // 6
55
+ RATED, // 7
56
+ STARTED, // 8
57
+ }
58
+
59
+ export enum OrderPaymentType {
60
+ APPLE_PAY,
61
+ CREDIT,
62
+ CASH,
63
+ }
64
+
65
+ export type Location = {
66
+ geohash?: string;
67
+ northeast: GeoPointType;
68
+ southwest: GeoPointType;
69
+ };
70
+
71
+ // IF ADDING A NEW PROPERTY TO THIS TYPE
72
+ // PLEASE ADD IT TO THE REDUX ORDER TYPE AS WELL
73
+ // AND ALSO IN `extractReduxOrder` FUNCTION IN `utils.ts`
74
+ // IN ORDER TO AVOID ANY PROBLEMS
75
+ export type Order = {
76
+ orderId?: string;
77
+ clientRef?: DocumentReference;
78
+ clientId?: string;
79
+ client?: Client;
80
+ providerRef?: DocumentReference;
81
+ providerId?: string;
82
+ servedBy?: DocumentReference;
83
+ servedByName?: string;
84
+ createdDate?: TimestampType;
85
+ orderDate?: TimestampType;
86
+ services?: CarService[];
87
+ location?: GeoPointType;
88
+ locationUrl?: string;
89
+ willClientBeThere?: boolean;
90
+ paymentType?: OrderPaymentType;
91
+ clientRating?: number;
92
+ providerRating?: number;
93
+ orderStatus?: OrderStatus;
94
+ staticMap?: any; // For static map
95
+ isPaid?: boolean;
96
+ rejectedBy?: string[];
97
+ finishDate?: TimestampType;
98
+ arrivalDate?: TimestampType;
99
+ canceledDate?: TimestampType;
100
+ paymentAmount?: number;
101
+ sahabPercentage?: number; // 0.06
102
+ sahabClientPercentage?: number; // 0.03
103
+ sahabOnlinePercentage?: number; // 0.03
104
+ sahabProviderPercentage?: number; // 0.03
105
+ paymentId?: string;
106
+ paymentCancelationId?: string;
107
+ paymentRefundId?: string;
108
+ paymentCaptureId?: string;
109
+ canceledBy?: 'provider' | 'client';
110
+ movementsCreated?: boolean;
111
+ orderNote?: string;
112
+ cancelReason?: string;
113
+ isDismissed?: boolean;
114
+ isLate?: boolean;
115
+ promoCode?: Discount;
116
+ isTest?: boolean;
117
+ clientNumber?: string;
118
+ usedBalance?: number;
119
+ isDummyOrder?: boolean;
120
+ ratingNote?: string;
121
+ };
122
+
123
+ // ! DO NOT PLACE ANY FIRESTORE REF IN THIS OBJECT TYPE
124
+ export type ReduxOrder = {
125
+ orderId?: string;
126
+ clientId?: string;
127
+ client?: Client;
128
+ servedByPath?: string; // as "service_providers/providerId/drivers/driverId"
129
+ providerId?: string;
130
+ servedByName?: string;
131
+ createdDate?: TimestampType;
132
+ orderDate?: TimestampType;
133
+ canceledDate?: TimestampType;
134
+ services?: CarService[];
135
+ location?: GeoPointType;
136
+ locationUrl?: string;
137
+ willClientBeThere?: boolean;
138
+ paymentType?: OrderPaymentType;
139
+ clientRating?: number;
140
+ providerRating?: number;
141
+ orderStatus?: OrderStatus;
142
+ staticMap?: any; // For static map
143
+ isPaid?: boolean;
144
+ rejectedBy?: string[];
145
+ finishDate?: TimestampType;
146
+ arrivalDate?: TimestampType;
147
+ paymentAmount?: number;
148
+ sahabPercentage?: number; // 0.06
149
+ sahabClientPercentage?: number; // 0.03
150
+ sahabOnlinePercentage?: number; // 0.03
151
+ sahabProviderPercentage?: number; // 0.03
152
+ paymentId?: string;
153
+ paymentCancelationId?: string;
154
+ paymentRefundId?: string;
155
+ paymentCaptureId?: string;
156
+ canceledBy?: 'provider' | 'client';
157
+ movementsCreated?: boolean;
158
+ orderNote?: string;
159
+ cancelReason?: string;
160
+ isDismissed?: boolean;
161
+ isLate?: boolean;
162
+ promoCode?: Discount;
163
+ clientNumber?: string;
164
+ isTest?: boolean;
165
+ usedBalance?: number;
166
+ isDummyOrder?: boolean;
167
+ ratingNote?: string;
168
+ discounts?: Discount[];
169
+ };
170
+
171
+ export type MoneyMovement = {
172
+ movementId: string;
173
+ amount: number;
174
+ orderId?: string;
175
+ date: TimestampType;
176
+ movementType: MovementType;
177
+ createdBy?: string;
178
+ };
179
+
180
+ export enum MovementType {
181
+ CLIENT_ORDER_PAYMENT,
182
+ CLIENT_BALANCE_TOP_UP,
183
+ PROVIDER_NEGATIVE_BALANCE_PAYMENT,
184
+ PROVIDER_POSITIVE_BALANCE_PAYMENT,
185
+ ORDER_REFUND,
186
+ SAHAB_PAYMENT_TO_PROVIDER,
187
+ SAHAB_RECEIVE_FROM_PROVIDER,
188
+ USING_BALANCE,
189
+ }
190
+
191
+ export enum ComplaintType {
192
+ BUG,
193
+ COMPLAINT,
194
+ }
195
+
196
+ export enum ComplaintStatus {
197
+ PENDING,
198
+ IN_REVIEW,
199
+ RESOLVED,
200
+ }
201
+
202
+ export type Complaint = {
203
+ complaintId: string;
204
+ complaintNumber: string;
205
+ desc: string;
206
+ createdDate: TimestampType;
207
+ status: ComplaintStatus;
208
+ type: ComplaintType;
209
+ resolvedBy?: string;
210
+ resolveDate?: string;
211
+ };
212
+
213
+ export type City = {
214
+ cityId?: string;
215
+ cityName: string;
216
+ countryName: string;
217
+ cityName_ar: string;
218
+ countryName_ar: string;
219
+ locationBounds?: Location;
220
+ coordinates?: GeoPointType;
221
+ g?: { geohash: string; geopoint: GeoPointType };
222
+ providerUid?: string;
223
+ };
224
+
225
+ export type Driver = {
226
+ type?: 'driver';
227
+ driverId?: string;
228
+ providerUid?: string;
229
+ firstName?: string;
230
+ lastName?: string;
231
+ phoneNumber?: string;
232
+ imageUrl?: string;
233
+ language?: 'en' | 'ar';
234
+ mainPushToken?: string;
235
+ hmsPushToken?: string;
236
+ cities?: City[];
237
+ isAllowedToCross?: boolean;
238
+ deactivated?: boolean;
239
+ };
240
+
241
+ export type ProviderCar = {
242
+ carId?: string;
243
+ providerRef?: DocumentReference;
244
+ manufacturer?: string;
245
+ model?: string;
246
+ year?: string;
247
+ color?: string;
248
+ plateLetters?: string;
249
+ plateNumber?: string;
250
+ istimarahUrl?: string;
251
+ fileType?: string;
252
+ };
253
+
254
+ export type Day = 'sunday' | 'monday' | 'tuesday' | 'wednesday' | 'thursday' | 'friday' | 'saturday';
255
+
256
+ export type HH =
257
+ | '00'
258
+ | '01'
259
+ | '02'
260
+ | '03'
261
+ | '04'
262
+ | '05'
263
+ | '06'
264
+ | '07'
265
+ | '08'
266
+ | '09'
267
+ | '10'
268
+ | '11'
269
+ | '12'
270
+ | '13'
271
+ | '14'
272
+ | '15'
273
+ | '16'
274
+ | '17'
275
+ | '18'
276
+ | '19'
277
+ | '20'
278
+ | '21'
279
+ | '22'
280
+ | '23';
281
+ export type MM =
282
+ | '00'
283
+ | '01'
284
+ | '02'
285
+ | '03'
286
+ | '04'
287
+ | '05'
288
+ | '06'
289
+ | '07'
290
+ | '08'
291
+ | '09'
292
+ | '10'
293
+ | '11'
294
+ | '12'
295
+ | '13'
296
+ | '14'
297
+ | '15'
298
+ | '16'
299
+ | '17'
300
+ | '18'
301
+ | '19'
302
+ | '20'
303
+ | '21'
304
+ | '22'
305
+ | '23'
306
+ | '24'
307
+ | '25'
308
+ | '26'
309
+ | '27'
310
+ | '28'
311
+ | '29'
312
+ | '30'
313
+ | '31'
314
+ | '32'
315
+ | '33'
316
+ | '34'
317
+ | '35'
318
+ | '36'
319
+ | '37'
320
+ | '38'
321
+ | '39'
322
+ | '40'
323
+ | '41'
324
+ | '42'
325
+ | '43'
326
+ | '44'
327
+ | '45'
328
+ | '46'
329
+ | '47'
330
+ | '48'
331
+ | '49'
332
+ | '50'
333
+ | '51'
334
+ | '52'
335
+ | '53'
336
+ | '54'
337
+ | '55'
338
+ | '56'
339
+ | '57'
340
+ | '58'
341
+ | '59';
342
+ export type Hour = `${HH}:${MM}`;
343
+ export type WorkingHour = {
344
+ start: Hour;
345
+ end: Hour;
346
+ };
347
+ export type WorkingHours = {
348
+ sunday: WorkingHour[];
349
+ monday: WorkingHour[];
350
+ tuesday: WorkingHour[];
351
+ wednesday: WorkingHour[];
352
+ thursday: WorkingHour[];
353
+ friday: WorkingHour[];
354
+ saturday: WorkingHour[];
355
+ };
356
+
357
+ export enum ReviewStatus {
358
+ PENDING,
359
+ IN_REVIEW,
360
+ ACCEPTED,
361
+ DECLINED,
362
+ }
363
+
364
+ export enum RegistrationSection {
365
+ OWNER_NAME,
366
+ CITY,
367
+ RULES,
368
+ BUSINESS_INFO,
369
+ DRIVERS_INFO,
370
+ VEHICLE_PERMIT,
371
+ VEHICLE_REGISTRATION,
372
+ SERVICES,
373
+ ADDONS,
374
+ WORKING_HOURS,
375
+ OVERVIEW,
376
+ STATUS,
377
+ DONE,
378
+ }
379
+
380
+ export type DeclineReason = {
381
+ reasonDesc: {
382
+ ar: string;
383
+ en: string;
384
+ };
385
+ section: RegistrationSection;
386
+ };
387
+
388
+ export type ProviderService = {
389
+ serviceId?: string;
390
+ name?: {
391
+ ar: string;
392
+ en: string;
393
+ };
394
+ imageUrl?: string;
395
+ providerId?: string;
396
+ prices?: Price[];
397
+ description?: {
398
+ ar: string;
399
+ en: string;
400
+ };
401
+ includedServices?: {
402
+ ar: string;
403
+ en: string;
404
+ }[];
405
+ hide?: boolean;
406
+ time?: number;
407
+ numberOfVisits?: number;
408
+ };
409
+
410
+ export type ProvidedService = {
411
+ serviceId?: string;
412
+ name?: {
413
+ ar: string;
414
+ en: string;
415
+ };
416
+ imageUrl?: string;
417
+ providerId?: string;
418
+ description?: {
419
+ ar: string;
420
+ en: string;
421
+ };
422
+ price: Price;
423
+ isDone?: boolean;
424
+ finishDate?: TimestampType;
425
+ quantity?: number;
426
+ type: 'service';
427
+ time?: number;
428
+ includedServices?: {
429
+ ar: string;
430
+ en: string;
431
+ }[];
432
+ numberOfVisits?: number;
433
+ discounts?: Discount[];
434
+ };
435
+
436
+ export type ProviderAddon = {
437
+ addonId?: string;
438
+ providerId?: string;
439
+ name?: {
440
+ ar: string;
441
+ en: string;
442
+ };
443
+ prices?: Price[];
444
+ description?: {
445
+ ar: string;
446
+ en: string;
447
+ };
448
+ hide?: boolean;
449
+ time?: number;
450
+ };
451
+
452
+ export type ProvidedAddon = {
453
+ addonId?: string;
454
+ providerId?: string;
455
+ name?: {
456
+ ar: string;
457
+ en: string;
458
+ };
459
+ price?: Price;
460
+ description?: {
461
+ ar: string;
462
+ en: string;
463
+ };
464
+ quantity?: number;
465
+ type: 'addon';
466
+ time?: number;
467
+ isDone?: boolean;
468
+ finishDate?: TimestampType;
469
+ discounts?: Discount[];
470
+ };
471
+
472
+ export type Provider = {
473
+ type?: 'provider';
474
+ providerUid?: string;
475
+ providerName?: {
476
+ ar: string;
477
+ en: string;
478
+ };
479
+ providerPhoneNumber?: string; // not used anymore
480
+ providerCarNumber?: string;
481
+ numberOfDrivers?: number;
482
+ numberOfCars?: number;
483
+ totalRating?: number;
484
+ numberOfRaters?: number;
485
+ logoUrl?: string;
486
+ phoneNumber?: string;
487
+ ownerFirstName?: string;
488
+ ownerLastName?: string;
489
+ services?: ProviderService[];
490
+ addons?: ProviderAddon[];
491
+ working247?: boolean;
492
+ workingHoursV2?: WorkingHours;
493
+ submitDate?: TimestampType;
494
+ reviewStatus?: ReviewStatus;
495
+ reviewedBy?: string;
496
+ declineReasons?: DeclineReason[];
497
+ acceptOrdersAutomatically?: boolean;
498
+ freeWashes?: number;
499
+ registrationSection?: RegistrationSection;
500
+ currency?: string;
501
+ mainPushToken?: string;
502
+ hmsPushToken?: string;
503
+ isTest?: boolean;
504
+ language?: 'en' | 'ar';
505
+ balance?: number;
506
+ permitUrl?: string;
507
+ permitType?: string;
508
+ checkoutCustomerId?: string;
509
+ ownerEmail?: string;
510
+ isEmailVerified?: boolean;
511
+ receiveOrders?: boolean; // true if undefined or null
512
+ isAcceptingOrders?: boolean;
513
+ };
514
+
515
+ export type Card = {
516
+ id?: string;
517
+ issuer?: string;
518
+ expiry_month?: string;
519
+ expiry_year?: string;
520
+ name?: string;
521
+ scheme?: string;
522
+ bin?: string;
523
+ last4?: string;
524
+ customer?: {
525
+ id?: string;
526
+ };
527
+
528
+ default?: boolean;
529
+ customer_id?: string;
530
+ };
531
+
532
+ export type CheckoutCustomer = {
533
+ default?: string;
534
+ email?: string;
535
+ id?: string;
536
+ name?: string;
537
+ instruments?: Card[];
538
+ metadata?: {
539
+ iban?: string;
540
+ providerUid?: string;
541
+ };
542
+ };
543
+
544
+ export type CarService = {
545
+ car?: Car;
546
+ services: Array<ProvidedService | ProvidedAddon>;
547
+ isDone?: boolean;
548
+ };
549
+
550
+ export type Discount = {
551
+ promoCodeId?: string;
552
+ promoCode?: string;
553
+ validFrom?: TimestampType;
554
+ validTo?: TimestampType;
555
+ createdAt?: TimestampType;
556
+ allowedOnce?: boolean;
557
+ createdByProvider?: boolean;
558
+ providerUid?: string;
559
+ usedBy?: string[];
560
+ amount?: number;
561
+ isPercentage?: boolean;
562
+ maxAmount?: number;
563
+ isAutoApplied?: boolean;
564
+ servicesIds?: string[];
565
+ addonsIds?: string[];
566
+ };
567
+
568
+ export type OrderReview = {
569
+ ratingId: string;
570
+ ratingNote: string;
571
+ orderId: string;
572
+ orderDate: TimestampType;
573
+ createdDate: TimestampType;
574
+ providerId: string;
575
+ arrivalDate: string;
576
+ finishDate: string;
577
+ rating: number;
578
+ clientId: string;
579
+ };
package/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './constants/types'
2
+ export * from './ts/calcs'
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "sahab-utils",
3
+ "version": "1.0.0",
4
+ "description": "This package containts types and utils fuctions",
5
+ "main": "index.ts",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/SahabApp/sahab-utils.git"
12
+ },
13
+ "keywords": [
14
+ "sahab"
15
+ ],
16
+ "author": "Sahab",
17
+ "license": "ISC",
18
+ "bugs": {
19
+ "url": "https://github.com/SahabApp/sahab-utils/issues"
20
+ },
21
+ "homepage": "https://github.com/SahabApp/sahab-utils#readme",
22
+ "dependencies": {
23
+ "firebase": "^10.7.1"
24
+ }
25
+ }
package/ts/calcs.ts ADDED
@@ -0,0 +1,195 @@
1
+ import { CarService, Order, OrderPaymentType, ProvidedAddon, ProvidedService, ReduxOrder } from '../constants/types';
2
+ import { preventNaN } from './utils';
3
+
4
+ export function getServicesTotal(order: Order | ReduxOrder | undefined) {
5
+ let total = 0;
6
+ if (order) {
7
+ if (order.services) {
8
+ order.services.forEach((carService) => {
9
+ carService.services.forEach((service) => {
10
+ if (service.price) {
11
+ const price = service.price.price;
12
+ total += price;
13
+ }
14
+ });
15
+ });
16
+ }
17
+ }
18
+ return total;
19
+ }
20
+
21
+ export const getSahabDiscountsAmount = (order: Order | ReduxOrder | undefined) => {
22
+ const orderTotal = getServicesTotal(order);
23
+ let discounts = 0;
24
+
25
+ if (order?.promoCode?.amount && order?.promoCode?.amount > 0 && !order?.promoCode?.createdByProvider) {
26
+ if (order?.promoCode?.isPercentage) {
27
+ discounts += (preventNaN(order?.promoCode?.amount) / 100) * orderTotal;
28
+ } else {
29
+ discounts += preventNaN(order?.promoCode?.amount);
30
+ }
31
+ }
32
+
33
+ // Sahab Services Discounts
34
+ (order?.services ?? [])?.forEach((carService) => {
35
+ (carService?.services || []).forEach((service) => {
36
+ let sahabDiscounts = (service?.discounts ?? []).filter((discount) => !discount?.createdByProvider);
37
+
38
+ if (sahabDiscounts.length) {
39
+ sahabDiscounts.forEach((discount) => {
40
+ if (discount.isPercentage) {
41
+ discounts += (preventNaN(discount?.amount ?? 0) / 100) * (service?.price?.price ?? 0);
42
+ } else {
43
+ discounts += preventNaN(discount?.amount ?? 0);
44
+ }
45
+ });
46
+ }
47
+ });
48
+ });
49
+
50
+ if (order?.usedBalance && order?.usedBalance > 0) {
51
+ discounts += order?.usedBalance;
52
+ }
53
+
54
+ return discounts;
55
+ };
56
+
57
+ export const getProviderDiscountsAmount = (order: Order | ReduxOrder | undefined) => {
58
+ const orderTotal = getServicesTotal(order);
59
+ let discounts = 0;
60
+
61
+ if (order?.promoCode?.amount && order?.promoCode?.amount > 0 && order?.promoCode?.createdByProvider) {
62
+ if (order?.promoCode?.isPercentage) {
63
+ discounts += (preventNaN(order?.promoCode?.amount) / 100) * orderTotal;
64
+ } else {
65
+ discounts += preventNaN(order?.promoCode?.amount);
66
+ }
67
+ }
68
+
69
+ (order?.services ?? [])?.forEach((carService) => {
70
+ (carService?.services || []).forEach((service) => {
71
+ let sahabDiscounts = (service?.discounts ?? []).filter((discount) => discount?.createdByProvider);
72
+
73
+ if (sahabDiscounts.length) {
74
+ sahabDiscounts.forEach((discount) => {
75
+ if (discount.isPercentage) {
76
+ discounts += (preventNaN(discount?.amount ?? 0) / 100) * (service?.price?.price ?? 0);
77
+ } else {
78
+ discounts += preventNaN(discount?.amount ?? 0);
79
+ }
80
+ });
81
+ }
82
+ });
83
+ });
84
+
85
+ return discounts;
86
+ };
87
+
88
+ export const getCustomerTotalToPay = (order: Order | ReduxOrder | undefined) => {
89
+ const orderTotal = getServicesTotal(order);
90
+ const discounts = getSahabDiscountsAmount(order) + getProviderDiscountsAmount(order);
91
+
92
+ return orderTotal - discounts;
93
+ };
94
+
95
+ export const getProviderTransferAmount = (order: Order | ReduxOrder | undefined) => {
96
+ const orderTotal = getServicesTotal(order) - getProviderDiscountsAmount(order);
97
+ const paymentAmount = preventNaN(order?.paymentAmount ?? 0);
98
+
99
+ const originalPrice = orderTotal / (1 + (order?.sahabClientPercentage ?? 0));
100
+ const minimumProviderAmount = originalPrice - originalPrice * (order?.sahabProviderPercentage ?? 0);
101
+
102
+ if (order?.paymentType === OrderPaymentType.CASH) {
103
+ return minimumProviderAmount - paymentAmount;
104
+ } else {
105
+ const orderTotal = getServicesTotal(order) - getProviderDiscountsAmount(order);
106
+ const onlinePercentage = getOnlineProcessingFees(order);
107
+ const sahabFees = paymentAmount < minimumProviderAmount ? orderTotal - minimumProviderAmount : paymentAmount - minimumProviderAmount;
108
+
109
+ return orderTotal - sahabFees - onlinePercentage;
110
+ }
111
+ };
112
+
113
+ export const getProviderProfits = (order: Order | ReduxOrder | undefined) => {
114
+ return order?.paymentType === OrderPaymentType.CASH
115
+ ? getCustomerTotalToPay(order) + getProviderTransferAmount(order)
116
+ : getProviderTransferAmount(order);
117
+ };
118
+
119
+ export const getOnlineProcessingFees = (order: Order | ReduxOrder | undefined) => {
120
+ if (order?.paymentType !== OrderPaymentType.CASH) {
121
+ const orderTotal = preventNaN(order?.paymentAmount ?? 0);
122
+
123
+ return preventNaN(order?.sahabOnlinePercentage ?? 0) * orderTotal;
124
+ }
125
+
126
+ return 0;
127
+ };
128
+
129
+ export const getSahabFees = (order: Order | ReduxOrder | undefined) => {
130
+ const orderTotal = getServicesTotal(order) - getProviderDiscountsAmount(order);
131
+ const paymentAmount = preventNaN(order?.paymentAmount ?? 0);
132
+
133
+ const originalPrice = orderTotal / (1 + (order?.sahabClientPercentage ?? 0));
134
+ const minimumProviderAmount = originalPrice - originalPrice * (order?.sahabProviderPercentage ?? 0);
135
+
136
+ if (paymentAmount < minimumProviderAmount) {
137
+ return 0;
138
+ } else {
139
+ return paymentAmount - minimumProviderAmount;
140
+ }
141
+ };
142
+
143
+ export function calculateDiscountAmount(order: Order) {
144
+ let discounts = 0;
145
+
146
+ if (!order?.promoCode?.amount && !order?.services?.some((service) => service?.services?.some((s) => s?.discounts?.length))) {
147
+ return 0;
148
+ }
149
+
150
+ const orderTotal = getServicesTotal(order);
151
+ if (order?.promoCode?.isPercentage) {
152
+ discounts += (preventNaN(order?.promoCode?.amount ?? 0) / 100) * orderTotal;
153
+ } else if (order?.promoCode?.amount) {
154
+ discounts += preventNaN(order?.promoCode?.amount ?? 0);
155
+ }
156
+
157
+ if (order?.services?.some((service) => service?.services?.some((s) => s?.discounts?.length))) {
158
+ (order?.services ?? [])?.forEach((carService) => {
159
+ (carService?.services || []).forEach((service) => {
160
+ let sahabDiscounts = service?.discounts ?? [];
161
+
162
+ if (sahabDiscounts.length) {
163
+ sahabDiscounts.forEach((discount) => {
164
+ if (discount.isPercentage) {
165
+ discounts += (preventNaN(discount?.amount ?? 0) / 100) * (service?.price?.price ?? 0);
166
+ } else {
167
+ discounts += preventNaN(discount?.amount ?? 0);
168
+ }
169
+ });
170
+ }
171
+ });
172
+ });
173
+ }
174
+
175
+ return discounts;
176
+ }
177
+
178
+ export function getServiceTotal(providedService: ProvidedService | ProvidedAddon) {
179
+ let total = 0;
180
+
181
+ total += providedService?.price?.price ?? 0;
182
+ let sahabDiscounts = providedService?.discounts ?? [];
183
+
184
+ if (sahabDiscounts.length) {
185
+ sahabDiscounts.forEach((discount) => {
186
+ if (discount.isPercentage) {
187
+ total -= (preventNaN(discount?.amount ?? 0) / 100) * (providedService?.price?.price ?? 0);
188
+ } else {
189
+ total -= preventNaN(discount?.amount ?? 0);
190
+ }
191
+ });
192
+ }
193
+
194
+ return total;
195
+ }
package/ts/utils.ts ADDED
@@ -0,0 +1,6 @@
1
+ export function preventNaN(x?: number | string) {
2
+ if (x === undefined || x === null || isNaN(x as any)) {
3
+ return 0;
4
+ }
5
+ return Number(x);
6
+ }