richie-education 2.29.2 → 2.29.3-dev48

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/i18n/locales/ar-SA.json +193 -125
  2. package/i18n/locales/es-ES.json +193 -125
  3. package/i18n/locales/fa-IR.json +193 -125
  4. package/i18n/locales/fr-CA.json +193 -125
  5. package/i18n/locales/fr-FR.json +193 -125
  6. package/i18n/locales/ko-KR.json +193 -125
  7. package/i18n/locales/pt-PT.json +193 -125
  8. package/i18n/locales/ru-RU.json +193 -125
  9. package/i18n/locales/vi-VN.json +193 -125
  10. package/js/api/joanie.ts +42 -17
  11. package/js/api/lms/dummy.ts +1 -12
  12. package/js/components/ContractFrame/AbstractContractFrame.spec.tsx +16 -9
  13. package/js/components/ContractFrame/AbstractContractFrame.tsx +28 -23
  14. package/js/components/ContractFrame/LearnerContractFrame.tsx +2 -2
  15. package/js/components/ContractFrame/_styles.scss +6 -14
  16. package/js/components/ContractStatus/index.spec.tsx +1 -1
  17. package/js/components/ContractStatus/index.tsx +1 -1
  18. package/js/components/{SaleTunnel/CreditCardSelector → CreditCardSelector}/index.spec.tsx +15 -45
  19. package/js/components/{SaleTunnel/CreditCardSelector → CreditCardSelector}/index.tsx +19 -26
  20. package/js/components/DownloadContractButton/index.spec.tsx +1 -1
  21. package/js/components/Form/utils.ts +2 -1
  22. package/js/components/OpenEdxFullNameForm/index.spec.tsx +229 -0
  23. package/js/components/OpenEdxFullNameForm/index.tsx +7 -7
  24. package/js/components/PaymentInterfaces/LyraPopIn.tsx +2 -2
  25. package/js/components/PaymentInterfaces/PayplugLightbox.tsx +1 -1
  26. package/js/components/PaymentInterfaces/__mocks__/index.tsx +1 -4
  27. package/js/components/PaymentInterfaces/types.ts +5 -2
  28. package/js/components/PaymentScheduleGrid/_styles.scss +13 -0
  29. package/js/components/PaymentScheduleGrid/index.tsx +50 -70
  30. package/js/components/PurchaseButton/index.spec.tsx +84 -37
  31. package/js/components/PurchaseButton/index.tsx +9 -27
  32. package/js/components/SaleTunnel/AddressSelector/index.spec.tsx +2 -2
  33. package/js/components/SaleTunnel/CertificateSaleTunnel/index.tsx +2 -2
  34. package/js/components/SaleTunnel/CredentialSaleTunnel/index.tsx +6 -10
  35. package/js/components/SaleTunnel/GenericSaleTunnel.tsx +105 -48
  36. package/js/components/SaleTunnel/SaleTunnelInformation/index.tsx +16 -20
  37. package/js/components/SaleTunnel/SaleTunnelSavePaymentMethod/_styles.scss +12 -0
  38. package/js/components/SaleTunnel/SaleTunnelSavePaymentMethod/index.tsx +157 -0
  39. package/js/components/SaleTunnel/SaleTunnelSuccess/index.tsx +15 -29
  40. package/js/components/SaleTunnel/Sponsors/SaleTunnelSponsors.scss +4 -5
  41. package/js/components/SaleTunnel/Sponsors/SaleTunnelSponsors.tsx +39 -11
  42. package/js/components/SaleTunnel/SubscriptionButton/_styles.scss +7 -0
  43. package/js/components/SaleTunnel/SubscriptionButton/index.tsx +202 -0
  44. package/js/components/SaleTunnel/_styles.scss +16 -5
  45. package/js/components/SaleTunnel/hooks/useTerms.tsx +0 -77
  46. package/js/components/SaleTunnel/index.credential.spec.tsx +14 -25
  47. package/js/components/SaleTunnel/index.full-process.spec.tsx +114 -48
  48. package/js/components/SaleTunnel/index.spec.tsx +333 -717
  49. package/js/components/SaleTunnel/index.tsx +2 -1
  50. package/js/components/SignContractButton/index.omniscientOrders.spec.tsx +16 -11
  51. package/js/components/SignContractButton/index.spec.tsx +16 -20
  52. package/js/components/SignContractButton/index.tsx +3 -1
  53. package/js/hooks/useCreditCards/index.spec.tsx +70 -6
  54. package/js/hooks/useCreditCards/index.ts +49 -11
  55. package/js/hooks/useOrders/index.spec.tsx +322 -0
  56. package/js/hooks/{useOrders.ts → useOrders/index.ts} +40 -14
  57. package/js/hooks/usePaymentSchedule.tsx +23 -0
  58. package/js/hooks/useProductOrder/index.spec.tsx +77 -60
  59. package/js/hooks/useProductOrder/index.tsx +2 -2
  60. package/js/hooks/useResources/useResourcesRoot.ts +4 -3
  61. package/js/index.tsx +2 -0
  62. package/js/pages/DashboardCreditCardsManagement/CreditCardBrandLogo.spec.tsx +1 -1
  63. package/js/pages/DashboardCreditCardsManagement/CreditCardBrandLogo.tsx +4 -2
  64. package/js/pages/DashboardOrderLayout/index.spec.tsx +10 -1
  65. package/js/pages/TeacherDashboardContractsLayout/components/ContractActionsBar/index.spec.tsx +8 -5
  66. package/js/pages/TeacherDashboardContractsLayout/components/SignOrganizationContractButton/index.spec.tsx +8 -9
  67. package/js/pages/TeacherDashboardCourseLearnersLayout/components/CourseLearnerDataGrid/index.spec.tsx +1 -1
  68. package/js/pages/TeacherDashboardCourseLearnersLayout/components/CourseLearnerDataGrid/index.tsx +1 -6
  69. package/js/settings/settings.prod.ts +0 -2
  70. package/js/settings/settings.test.ts +16 -2
  71. package/js/translations/ar-SA.json +1 -1
  72. package/js/translations/es-ES.json +1 -1
  73. package/js/translations/fa-IR.json +1 -1
  74. package/js/translations/fr-CA.json +1 -1
  75. package/js/translations/fr-FR.json +1 -1
  76. package/js/translations/ko-KR.json +1 -1
  77. package/js/translations/pt-PT.json +1 -1
  78. package/js/translations/ru-RU.json +1 -1
  79. package/js/translations/vi-VN.json +1 -1
  80. package/js/types/Joanie.ts +81 -32
  81. package/js/utils/OrderHelper/index.ts +46 -38
  82. package/js/utils/PaymentScheduleHelper/index.spec.tsx +78 -0
  83. package/js/utils/PaymentScheduleHelper/index.ts +30 -0
  84. package/js/utils/ProductHelper/index.spec.ts +322 -166
  85. package/js/utils/ProductHelper/index.ts +32 -0
  86. package/js/utils/test/factories/joanie.ts +72 -69
  87. package/js/widgets/Dashboard/components/DashboardItem/Contract/index.spec.tsx +2 -2
  88. package/js/widgets/Dashboard/components/DashboardItem/CourseEnrolling/index.tsx +8 -18
  89. package/js/widgets/Dashboard/components/DashboardItem/Enrollment/DashboardItemEnrollment.spec.tsx +23 -0
  90. package/js/widgets/Dashboard/components/DashboardItem/Enrollment/DashboardItemEnrollment.tsx +23 -1
  91. package/js/widgets/Dashboard/components/DashboardItem/Enrollment/ProductCertificateFooter/index.spec.tsx +145 -34
  92. package/js/widgets/Dashboard/components/DashboardItem/Enrollment/ProductCertificateFooter/index.tsx +175 -37
  93. package/js/widgets/Dashboard/components/DashboardItem/Order/CertificateItem/index.tsx +51 -0
  94. package/js/widgets/Dashboard/components/DashboardItem/Order/ContractItem/index.tsx +52 -0
  95. package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemNotResumable.spec.tsx +109 -0
  96. package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrder.spec.tsx +219 -5
  97. package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrder.tsx +111 -245
  98. package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrderContract.spec.tsx +61 -8
  99. package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrderContract.useUnionResource.cache.spec.tsx +17 -7
  100. package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemSavePaymentMethod.spec.tsx +116 -0
  101. package/js/widgets/Dashboard/components/DashboardItem/Order/Installment/index.tsx +175 -0
  102. package/js/widgets/Dashboard/components/DashboardItem/Order/OrderPaymentDetailsModal/_styles.scss +7 -0
  103. package/js/widgets/Dashboard/components/DashboardItem/Order/OrderPaymentDetailsModal/index.tsx +127 -0
  104. package/js/widgets/Dashboard/components/DashboardItem/Order/OrderPaymentRetryModal/index.tsx +216 -0
  105. package/js/widgets/Dashboard/components/DashboardItem/Order/OrderStateLearnerMessage/index.spec.tsx +18 -71
  106. package/js/widgets/Dashboard/components/DashboardItem/Order/OrderStateLearnerMessage/index.tsx +40 -25
  107. package/js/widgets/Dashboard/components/DashboardItem/Order/OrderStateMessage/index.tsx +28 -22
  108. package/js/widgets/Dashboard/components/DashboardItem/Order/OrderStateTeacherMessage/index.spec.tsx +18 -73
  109. package/js/widgets/Dashboard/components/DashboardItem/Order/OrderStateTeacherMessage/index.tsx +32 -16
  110. package/js/widgets/Dashboard/components/DashboardItem/Order/OrganizationBlock/index.tsx +150 -0
  111. package/js/widgets/Dashboard/components/DashboardOrderLoader/index.tsx +32 -14
  112. package/js/widgets/Dashboard/components/Signature/SignatureDummy.tsx +25 -3
  113. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/components/CourseProductCourseRuns/EnrollableCourseRunList.tsx +2 -6
  114. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/components/CourseProductCourseRuns/index.spec.tsx +7 -14
  115. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/components/CourseRunItem/index.spec.tsx +7 -5
  116. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/components/CourseRunItem/index.tsx +5 -7
  117. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.spec.tsx +242 -332
  118. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.stories.tsx +12 -13
  119. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.tsx +10 -21
  120. package/js/widgets/SyllabusCourseRunsList/components/CourseRunEnrollment/index.joanie.spec.tsx +2 -2
  121. package/package.json +45 -44
  122. package/scss/components/_index.scss +4 -2
  123. package/js/components/PaymentButton/_styles.scss +0 -27
  124. package/js/components/SaleTunnel/GenericPaymentButton/index.tsx +0 -340
  125. package/js/components/SaleTunnel/SaleTunnelNotValidated/index.tsx +0 -70
  126. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/components/ProductSignatureHeader/index.tsx +0 -41
  127. /package/js/components/{SaleTunnel/CreditCardSelector → CreditCardSelector}/_styles.scss +0 -0
@@ -259,14 +259,42 @@ export interface EnrollmentLight {
259
259
 
260
260
  // Order
261
261
  export enum OrderState {
262
- DRAFT = 'draft',
263
- SUBMITTED = 'submitted',
262
+ ASSIGNED = 'assigned',
264
263
  CANCELED = 'canceled',
264
+ COMPLETED = 'completed',
265
+ DRAFT = 'draft',
266
+ FAILED_PAYMENT = 'failed_payment',
267
+ NO_PAYMENT = 'no_payment',
265
268
  PENDING = 'pending',
266
- VALIDATED = 'validated',
267
- }
268
-
269
- export const ACTIVE_ORDER_STATES = [OrderState.PENDING, OrderState.VALIDATED, OrderState.SUBMITTED];
269
+ PENDING_PAYMENT = 'pending_payment',
270
+ SIGNING = 'signing',
271
+ TO_SAVE_PAYMENT_METHOD = 'to_save_payment_method',
272
+ TO_SIGN = 'to_sign',
273
+ }
274
+
275
+ export const PURCHASABLE_ORDER_STATES = [
276
+ OrderState.DRAFT,
277
+ OrderState.ASSIGNED,
278
+ OrderState.TO_SIGN,
279
+ OrderState.SIGNING,
280
+ OrderState.TO_SAVE_PAYMENT_METHOD,
281
+ ];
282
+
283
+ export const ACTIVE_ORDER_STATES = [
284
+ OrderState.PENDING,
285
+ OrderState.PENDING_PAYMENT,
286
+ OrderState.NO_PAYMENT,
287
+ OrderState.FAILED_PAYMENT,
288
+ OrderState.COMPLETED,
289
+ ];
290
+
291
+ export const NOT_CANCELED_ORDER_STATES = [...ACTIVE_ORDER_STATES, ...PURCHASABLE_ORDER_STATES];
292
+
293
+ export const ENROLLABLE_ORDER_STATES = [
294
+ OrderState.COMPLETED,
295
+ OrderState.PENDING_PAYMENT,
296
+ OrderState.FAILED_PAYMENT,
297
+ ];
270
298
 
271
299
  export interface Order {
272
300
  id: string;
@@ -286,6 +314,8 @@ export interface Order {
286
314
  organization_id: Organization['id'];
287
315
  organization: Organization;
288
316
  order_group_id?: OrderGroup['id'];
317
+ payment_schedule?: PaymentSchedule;
318
+ credit_card_id?: CreditCard['id'];
289
319
  }
290
320
 
291
321
  export interface CredentialOrder extends Order {
@@ -293,20 +323,12 @@ export interface CredentialOrder extends Order {
293
323
  enrollment: null;
294
324
  }
295
325
 
296
- export interface CredentialOrderWithPaymentInfo extends CredentialOrder {
297
- payment_info: Payment;
298
- }
299
-
300
326
  export interface CertificateOrder extends Order {
301
327
  course: null;
302
328
  enrollment: EnrollmentLight;
303
329
  target_courses: never[];
304
330
  }
305
331
 
306
- export interface CertificateOrderWithPaymentInfo extends CertificateOrder {
307
- payment_info: Payment;
308
- }
309
-
310
332
  export type OrderLite = Pick<
311
333
  Order,
312
334
  | 'id'
@@ -325,9 +347,11 @@ export interface AbstractNestedOrder {
325
347
  product_title: string;
326
348
  owner_name: string;
327
349
  state: OrderState;
350
+ course: Nullable<CourseLight>;
351
+ enrollment: Nullable<EnrollmentLight>;
328
352
  }
329
353
  export interface NestedCertificateOrder extends AbstractNestedOrder {
330
- course: undefined;
354
+ course: null;
331
355
  enrollment: EnrollmentLight;
332
356
  }
333
357
  export const isNestedCredentialOrder = (
@@ -338,10 +362,13 @@ export const isNestedCredentialOrder = (
338
362
 
339
363
  export interface NestedCredentialOrder extends AbstractNestedOrder {
340
364
  course: CourseLight;
341
- enrollment: undefined;
365
+ enrollment: null;
342
366
  }
343
367
 
344
- export type OrderEnrollment = Pick<Order, 'id' | 'state' | 'product_id' | 'certificate_id'>;
368
+ export type OrderEnrollment = Pick<
369
+ Order,
370
+ 'id' | 'state' | 'product_id' | 'certificate_id' | 'payment_schedule'
371
+ >;
345
372
 
346
373
  export interface NestedCourseOrder {
347
374
  id: Order['id'];
@@ -372,10 +399,10 @@ export interface OrderGroup {
372
399
  }
373
400
 
374
401
  export enum CreditCardBrand {
375
- MASTERCARD = 'Mastercard',
376
- MAESTRO = 'Maestro',
377
- VISA = 'Visa',
378
- CB = 'CB',
402
+ MASTERCARD = 'mastercard',
403
+ MAESTRO = 'maestro',
404
+ VISA = 'visa',
405
+ CB = 'cb',
379
406
  }
380
407
 
381
408
  // Credit Card
@@ -416,6 +443,22 @@ export interface OrderPaymentInfo {
416
443
  payment_info: Payment;
417
444
  }
418
445
 
446
+ export enum PaymentScheduleState {
447
+ PENDING = 'pending',
448
+ PAID = 'paid',
449
+ REFUSED = 'refused',
450
+ }
451
+
452
+ export interface PaymentInstallment {
453
+ id: string;
454
+ amount: number;
455
+ currency: string;
456
+ due_date: string;
457
+ state: PaymentScheduleState;
458
+ }
459
+
460
+ export type PaymentSchedule = readonly PaymentInstallment[];
461
+
419
462
  // - API
420
463
  export interface AddressCreationPayload extends Omit<Address, 'id' | 'is_main'> {
421
464
  is_main?: boolean;
@@ -424,7 +467,7 @@ export interface AddressCreationPayload extends Omit<Address, 'id' | 'is_main'>
424
467
  interface AbstractOrderProductCreationPayload {
425
468
  product_id: Product['id'];
426
469
  order_group_id?: OrderGroup['id'];
427
- has_consent_to_terms: boolean;
470
+ billing_address: Omit<Address, 'id' | 'is_main'>;
428
471
  }
429
472
 
430
473
  interface OrderCertificateCreationPayload extends AbstractOrderProductCreationPayload {
@@ -436,15 +479,13 @@ export interface OrderCredentialCreationPayload extends AbstractOrderProductCrea
436
479
 
437
480
  export type OrderCreationPayload = OrderCertificateCreationPayload | OrderCredentialCreationPayload;
438
481
 
439
- interface OrderAbortPayload {
440
- id: Order['id'];
441
- payment_id?: string;
442
- }
482
+ export type OrderSubmitInstallmentPayment = {
483
+ credit_card_id?: string;
484
+ };
443
485
 
444
- interface OrderSubmitPayload {
486
+ interface OrderSetPaymentMethodPayload {
445
487
  id: Order['id'];
446
- billing_address: Omit<Address, 'id' | 'is_main'>;
447
- credit_card_id?: CreditCard['id'];
488
+ credit_card_id: CreditCard['id'];
448
489
  }
449
490
 
450
491
  export interface PaginatedResourceQuery extends ResourcesQuery {
@@ -539,14 +580,15 @@ interface APIUser {
539
580
  update(payload: Address): Promise<Address>;
540
581
  };
541
582
  creditCards: {
542
- create(payload: Omit<CreditCard, 'id'>): Promise<CreditCard>;
543
583
  delete(id: CreditCard['id']): Promise<void>;
584
+ get(): Promise<PaginatedResponse<CreditCard>>;
544
585
  get(filters?: ResourcesQuery): Promise<CreditCard>;
545
586
  get(): Promise<CreditCard[]>;
546
587
  update(payload: CreditCard): Promise<CreditCard>;
588
+ tokenize(): Promise<Payment>;
547
589
  };
548
590
  orders: {
549
- abort(payload: OrderAbortPayload): Promise<void>;
591
+ cancel(id: Order['id']): Promise<void>;
550
592
  create(payload: OrderCreationPayload): Promise<CredentialOrder | CertificateOrder>;
551
593
  get<Filters extends OrderResourcesQuery = OrderResourcesQuery>(
552
594
  filters?: Filters,
@@ -556,8 +598,12 @@ interface APIUser {
556
598
  invoice: {
557
599
  download(payload: { order_id: Order['id']; invoice_reference: string }): Promise<File>;
558
600
  };
559
- submit(payload: OrderSubmitPayload): Promise<OrderPaymentInfo>;
560
601
  submit_for_signature(id: string): Promise<ContractInvitationLinkResponse>;
602
+ submit_installment_payment(
603
+ id: string,
604
+ payload?: OrderSubmitInstallmentPayment,
605
+ ): Promise<Payment>;
606
+ set_payment_method(payload: OrderSetPaymentMethodPayload): Promise<void>;
561
607
  };
562
608
  certificates: {
563
609
  download(id: string): Promise<File>;
@@ -614,6 +660,9 @@ export interface API {
614
660
  : Promise<PaginatedResponse<CourseListItem>>;
615
661
  products: {
616
662
  get(filters?: CourseProductQueryFilters): Promise<Nullable<CourseProductRelation>>;
663
+ paymentSchedule: {
664
+ get(filters?: CourseProductQueryFilters): Promise<Nullable<PaymentSchedule>>;
665
+ };
617
666
  };
618
667
  orders: {
619
668
  get(
@@ -1,49 +1,52 @@
1
1
  import {
2
- OrderEnrollment,
3
2
  ACTIVE_ORDER_STATES,
3
+ ENROLLABLE_ORDER_STATES,
4
+ NestedCourseOrder,
4
5
  Order,
6
+ OrderEnrollment,
5
7
  OrderState,
6
- ContractDefinition,
7
- NestedCourseOrder,
8
+ PURCHASABLE_ORDER_STATES,
8
9
  } from 'types/Joanie';
9
10
 
10
11
  export enum OrderStatus {
12
+ ASSIGNED = 'assigned',
13
+ CANCELED = 'canceled',
14
+ COMPLETED = 'completed',
11
15
  DRAFT = 'draft',
12
- SUBMITTED = 'submitted',
16
+ FAILED_PAYMENT = 'failed_payment',
17
+ NO_PAYMENT = 'no_payment',
18
+ PASSED = 'passed',
13
19
  PENDING = 'pending',
14
- CANCELED = 'canceled',
15
- WAITING_SIGNATURE = 'waiting_signature',
20
+ PENDING_PAYMENT = 'pending_payment',
16
21
  WAITING_COUNTER_SIGNATURE = 'waiting_counter_signature',
17
- COMPLETED = 'completed',
18
- ON_GOING = 'on_going',
22
+ WAITING_PAYMENT_METHOD = 'waiting_payment_method',
23
+ WAITING_SIGNATURE = 'waiting_signature',
19
24
  }
20
25
 
21
26
  /**
22
27
  * Helper class for orders
23
28
  */
24
29
  export class OrderHelper {
25
- static getState(order: Order | NestedCourseOrder, contractDefinition?: ContractDefinition) {
26
- const { certificate_id: certificateId } = order;
27
-
28
- if (order.state === OrderState.VALIDATED) {
29
- if (OrderHelper.orderNeedsSignature(order, contractDefinition)) {
30
- return OrderStatus.WAITING_SIGNATURE;
31
- }
32
- if (OrderHelper.orderNeedsCounterSignature(order)) {
33
- return OrderStatus.WAITING_COUNTER_SIGNATURE;
34
- }
35
- if (certificateId) {
36
- return OrderStatus.COMPLETED;
37
- } else {
38
- return OrderStatus.ON_GOING;
39
- }
30
+ static getState(order: Order | NestedCourseOrder) {
31
+ if (OrderHelper.allowEnrollment(order) && OrderHelper.orderNeedsCounterSignature(order)) {
32
+ return OrderStatus.WAITING_COUNTER_SIGNATURE;
33
+ }
34
+ if (order.state === OrderState.COMPLETED && order.certificate_id) {
35
+ return OrderStatus.PASSED;
40
36
  }
41
37
 
42
38
  const orderStatusMap = {
39
+ [OrderState.ASSIGNED]: OrderStatus.ASSIGNED,
40
+ [OrderState.CANCELED]: OrderStatus.CANCELED,
41
+ [OrderState.COMPLETED]: OrderStatus.COMPLETED,
43
42
  [OrderState.DRAFT]: OrderStatus.DRAFT,
44
- [OrderState.SUBMITTED]: OrderStatus.SUBMITTED,
43
+ [OrderState.FAILED_PAYMENT]: OrderStatus.FAILED_PAYMENT,
44
+ [OrderState.NO_PAYMENT]: OrderStatus.NO_PAYMENT,
45
45
  [OrderState.PENDING]: OrderStatus.PENDING,
46
- [OrderState.CANCELED]: OrderStatus.CANCELED,
46
+ [OrderState.PENDING_PAYMENT]: OrderStatus.PENDING_PAYMENT,
47
+ [OrderState.SIGNING]: OrderStatus.WAITING_SIGNATURE,
48
+ [OrderState.TO_SAVE_PAYMENT_METHOD]: OrderStatus.WAITING_PAYMENT_METHOD,
49
+ [OrderState.TO_SIGN]: OrderStatus.WAITING_SIGNATURE,
47
50
  };
48
51
 
49
52
  if (order.state in orderStatusMap) {
@@ -62,18 +65,8 @@ export class OrderHelper {
62
65
  return orders.find(filter);
63
66
  }
64
67
 
65
- /**
66
- * tell us if a order need to be sign by it's owner (the learner user).
67
- */
68
- static orderNeedsSignature(
69
- order: Order | NestedCourseOrder,
70
- contractDefinition?: ContractDefinition,
71
- ) {
72
- return (
73
- order?.state === OrderState.VALIDATED &&
74
- contractDefinition &&
75
- !(order.contract && order.contract.student_signed_on)
76
- );
68
+ static orderNeedsSignature(order: Order | NestedCourseOrder) {
69
+ return [OrderState.TO_SIGN, OrderState.SIGNING].includes(order.state);
77
70
  }
78
71
 
79
72
  /**
@@ -81,10 +74,25 @@ export class OrderHelper {
81
74
  */
82
75
  static orderNeedsCounterSignature(order: Order | NestedCourseOrder) {
83
76
  return (
84
- order?.state === OrderState.VALIDATED &&
77
+ ACTIVE_ORDER_STATES.includes(order.state) &&
85
78
  order.contract &&
86
79
  order.contract.student_signed_on &&
87
80
  !order.contract.organization_signed_on
88
81
  );
89
82
  }
83
+
84
+ static allowEnrollment(order?: Order | NestedCourseOrder | OrderEnrollment) {
85
+ if (!order) return false;
86
+ return ENROLLABLE_ORDER_STATES.includes(order.state);
87
+ }
88
+
89
+ static isActive(order?: Order | NestedCourseOrder | OrderEnrollment) {
90
+ if (!order) return false;
91
+ return ACTIVE_ORDER_STATES.includes(order.state);
92
+ }
93
+
94
+ static isPurchasable(order?: Order | NestedCourseOrder | OrderEnrollment) {
95
+ if (!order) return true;
96
+ return PURCHASABLE_ORDER_STATES.includes(order.state);
97
+ }
90
98
  }
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Test suite for the helper `PaymentScheduleHelper`.
3
+ */
4
+ import PaymentScheduleHelper from 'utils/PaymentScheduleHelper/index';
5
+ import { PaymentInstallmentFactory } from 'utils/test/factories/joanie';
6
+ import { PaymentScheduleState } from 'types/Joanie';
7
+
8
+ describe('PaymentScheduleHelper', () => {
9
+ describe('getFailedInstallment', () => {
10
+ it('should return undefined if payment_schedule is undefined', () => {
11
+ const foundInstallment = PaymentScheduleHelper.getFailedInstallment(undefined);
12
+
13
+ expect(foundInstallment).toBeUndefined();
14
+ });
15
+
16
+ it('should return undefined if payment schedule does not have a failed installment', () => {
17
+ const schedule = PaymentInstallmentFactory({ state: PaymentScheduleState.PENDING }).many(3);
18
+ const foundInstallment = PaymentScheduleHelper.getFailedInstallment(schedule);
19
+
20
+ expect(foundInstallment).toBeUndefined();
21
+ });
22
+
23
+ it('should return the older failed installment', () => {
24
+ const schedule = [
25
+ PaymentInstallmentFactory({
26
+ state: PaymentScheduleState.PAID,
27
+ due_date: '2024-06-21',
28
+ }).one(),
29
+ PaymentInstallmentFactory({
30
+ state: PaymentScheduleState.REFUSED,
31
+ due_date: '2024-08-20',
32
+ }).one(),
33
+ PaymentInstallmentFactory({
34
+ state: PaymentScheduleState.REFUSED,
35
+ due_date: '2024-07-20',
36
+ }).one(),
37
+ ];
38
+ const foundInstallment = PaymentScheduleHelper.getFailedInstallment(schedule);
39
+
40
+ expect(foundInstallment?.due_date).toBe(schedule[2].due_date);
41
+ });
42
+ });
43
+
44
+ describe('getPendingInstallment', () => {
45
+ it('should return undefined if payment_schedule is undefined', () => {
46
+ const foundInstallment = PaymentScheduleHelper.getPendingInstallment(undefined);
47
+
48
+ expect(foundInstallment).toBeUndefined();
49
+ });
50
+
51
+ it('should return undefined if payment schedule does not have a pending installment', () => {
52
+ const schedule = PaymentInstallmentFactory({ state: PaymentScheduleState.PAID }).many(3);
53
+ const foundInstallment = PaymentScheduleHelper.getPendingInstallment(schedule);
54
+
55
+ expect(foundInstallment).toBeUndefined();
56
+ });
57
+
58
+ it('should return the older pending installment', () => {
59
+ const schedule = [
60
+ PaymentInstallmentFactory({
61
+ state: PaymentScheduleState.PAID,
62
+ due_date: '2024-06-21',
63
+ }).one(),
64
+ PaymentInstallmentFactory({
65
+ state: PaymentScheduleState.PENDING,
66
+ due_date: '2024-08-20',
67
+ }).one(),
68
+ PaymentInstallmentFactory({
69
+ state: PaymentScheduleState.PENDING,
70
+ due_date: '2024-07-20',
71
+ }).one(),
72
+ ];
73
+
74
+ const foundInstallment = PaymentScheduleHelper.getPendingInstallment(schedule);
75
+ expect(foundInstallment?.due_date).toBe(schedule[2].due_date);
76
+ });
77
+ });
78
+ });
@@ -0,0 +1,30 @@
1
+ import { PaymentInstallment, PaymentSchedule, PaymentScheduleState } from 'types/Joanie';
2
+ import { Maybe } from 'types/utils';
3
+
4
+ export default class PaymentScheduleHelper {
5
+ static #sortByDueDateAsc(a: PaymentInstallment, b: PaymentInstallment) {
6
+ const dateA = new Date(a.due_date).getTime();
7
+ const dateB = new Date(b.due_date).getTime();
8
+ return dateA - dateB;
9
+ }
10
+
11
+ static #getInstallmentByState(
12
+ payment_schedule: Maybe<PaymentSchedule>,
13
+ state: PaymentScheduleState,
14
+ ) {
15
+ if (!payment_schedule) return undefined;
16
+
17
+ const installments = [...payment_schedule];
18
+ return installments
19
+ ?.sort(this.#sortByDueDateAsc)
20
+ .find((installment) => installment.state === state);
21
+ }
22
+
23
+ static getFailedInstallment(payment_schedule: Maybe<PaymentSchedule>) {
24
+ return this.#getInstallmentByState(payment_schedule, PaymentScheduleState.REFUSED);
25
+ }
26
+
27
+ static getPendingInstallment(payment_schedule: Maybe<PaymentSchedule>) {
28
+ return this.#getInstallmentByState(payment_schedule, PaymentScheduleState.PENDING);
29
+ }
30
+ }