richie-education 3.1.3-dev23 → 3.1.3-dev24

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.
@@ -13,3 +13,7 @@ let context = {
13
13
  (window as any).__richie_frontend_context__ = {
14
14
  context: RichieContextFactory(context).one(),
15
15
  };
16
+
17
+ (window as any).jest = {
18
+ fn: ((fnc: any) => fnc) as any,
19
+ };
@@ -10,7 +10,15 @@ import {
10
10
  } from 'react';
11
11
  import { SaleTunnelSponsors } from 'components/SaleTunnel/Sponsors/SaleTunnelSponsors';
12
12
  import { SaleTunnelProps } from 'components/SaleTunnel/index';
13
- import { Address, Offering, CreditCard, Order, OrderState, Product } from 'types/Joanie';
13
+ import {
14
+ Address,
15
+ Enrollment,
16
+ Offering,
17
+ CreditCard,
18
+ Order,
19
+ OrderState,
20
+ Product,
21
+ } from 'types/Joanie';
14
22
  import useProductOrder from 'hooks/useProductOrder';
15
23
  import { SaleTunnelSuccess } from 'components/SaleTunnel/SaleTunnelSuccess';
16
24
  import WebAnalyticsAPIHandler from 'api/web-analytics';
@@ -27,6 +35,7 @@ export interface SaleTunnelContextType {
27
35
  product: Product;
28
36
  webAnalyticsEventKey: string;
29
37
  offering?: Offering;
38
+ enrollment?: Enrollment;
30
39
 
31
40
  // internal
32
41
  step: SaleTunnelStep;
@@ -115,6 +124,7 @@ export const GenericSaleTunnel = (props: GenericSaleTunnelProps) => {
115
124
  order,
116
125
  product: props.product,
117
126
  offering: props.offering,
127
+ enrollment: props.enrollment,
118
128
  props,
119
129
  billingAddress,
120
130
  setBillingAddress,
@@ -101,7 +101,11 @@ const Email = () => {
101
101
  };
102
102
 
103
103
  const Total = () => {
104
- const { product, offering } = useSaleTunnelContext();
104
+ const { product, offering, enrollment } = useSaleTunnelContext();
105
+ const totalPrice =
106
+ enrollment?.offerings?.[0]?.rules?.discounted_price ??
107
+ offering?.rules.discounted_price ??
108
+ product.price;
105
109
  return (
106
110
  <div className="sale-tunnel__total">
107
111
  <div className="sale-tunnel__total__amount mt-t" data-testid="sale-tunnel__total__amount">
@@ -109,11 +113,7 @@ const Total = () => {
109
113
  <FormattedMessage {...messages.totalLabel} />
110
114
  </div>
111
115
  <div className="block-title">
112
- <FormattedNumber
113
- value={offering?.rules.discounted_price || product.price}
114
- style="currency"
115
- currency={product.price_currency}
116
- />
116
+ <FormattedNumber value={totalPrice} style="currency" currency={product.price_currency} />
117
117
  </div>
118
118
  </div>
119
119
  </div>
@@ -8,6 +8,7 @@ import { useState } from 'react';
8
8
  import { OrderState, Product, ProductType, NOT_CANCELED_ORDER_STATES } from 'types/Joanie';
9
9
  import {
10
10
  RichieContextFactory as mockRichieContextFactory,
11
+ CourseStateFactory,
11
12
  UserFactory,
12
13
  PacedCourseFactory,
13
14
  } from 'utils/test/factories/richie';
@@ -16,12 +17,14 @@ import {
16
17
  CertificateOrderFactory,
17
18
  CertificateProductFactory,
18
19
  OfferingFactory,
20
+ CourseRunFactory,
19
21
  CredentialOrderFactory,
20
22
  CredentialProductFactory,
21
23
  CreditCardFactory,
22
24
  EnrollmentFactory,
23
25
  PaymentInstallmentFactory,
24
26
  } from 'utils/test/factories/joanie';
27
+ import { Priority } from 'types';
25
28
  import { render } from 'utils/test/render';
26
29
  import { SaleTunnel, SaleTunnelProps } from 'components/SaleTunnel/index';
27
30
  import { setupJoanieSession } from 'utils/test/wrappers/JoanieAppWrapper';
@@ -97,7 +100,7 @@ describe.each([
97
100
  return (
98
101
  <SaleTunnel
99
102
  {...props}
100
- enrollment={enrollment}
103
+ enrollment={props.enrollment ?? enrollment}
101
104
  course={productType === ProductType.CREDENTIAL ? course : undefined}
102
105
  isOpen={open}
103
106
  onClose={() => setOpen(false)}
@@ -449,6 +452,57 @@ describe.each([
449
452
  );
450
453
  });
451
454
 
455
+ // Fixes the issue : https://github.com/openfun/richie/issues/2645
456
+ it('should show the certificate product total with discounted price', async () => {
457
+ const product = ProductFactory({
458
+ price: 600,
459
+ target_courses: [course],
460
+ }).one();
461
+ const enrollmentDiscounted = EnrollmentFactory({
462
+ course_run: CourseRunFactory({
463
+ state: CourseStateFactory({ priority: Priority.ONGOING_OPEN }).one(),
464
+ course,
465
+ }).one(),
466
+ offerings: [
467
+ OfferingFactory({
468
+ product,
469
+ rules: {
470
+ discounted_price: 540,
471
+ discount_rate: 0.1,
472
+ },
473
+ }).one(),
474
+ ],
475
+ }).one();
476
+
477
+ if (product.type === ProductType.CERTIFICATE) {
478
+ enrollmentDiscounted.offerings[0].product = product;
479
+
480
+ fetchMock.get(
481
+ `https://joanie.endpoint/api/v1.0/orders/?enrollment_id=${enrollmentDiscounted.id}&product_id=${product.id}&state=pending&state=pending_payment&state=no_payment&state=failed_payment&state=completed&state=draft&state=assigned&state=to_sign&state=signing&state=to_save_payment_method`,
482
+ {
483
+ results: [],
484
+ next: null,
485
+ previous: null,
486
+ count: 0,
487
+ },
488
+ );
489
+
490
+ render(
491
+ <Wrapper product={product} enrollment={enrollmentDiscounted} isWithdrawable={true} />,
492
+ { queryOptions: { client: createTestQueryClient({ user: richieUser }) } },
493
+ );
494
+
495
+ const $totalAmount = screen.getByTestId('sale-tunnel__total__amount');
496
+ expect($totalAmount).toHaveTextContent(
497
+ 'Total' +
498
+ formatPrice(
499
+ enrollmentDiscounted.offerings[0].rules.discounted_price!,
500
+ product.price_currency,
501
+ ).replace(/(\u202F|\u00a0)/g, ' '),
502
+ );
503
+ }
504
+ });
505
+
452
506
  it('should show the product payment schedule with discounted price', async () => {
453
507
  const intl = createIntl({ locale: 'en' });
454
508
  const schedule = PaymentInstallmentFactory().many(2);
@@ -1,6 +1,11 @@
1
1
  import { StoryObj, Meta } from '@storybook/react';
2
2
  import { BaseJoanieAppWrapper } from 'utils/test/wrappers/BaseJoanieAppWrapper';
3
- import { ProductFactory } from 'utils/test/factories/joanie';
3
+ import {
4
+ CertificateProductFactory,
5
+ EnrollmentFactory,
6
+ OfferingFactory,
7
+ ProductFactory,
8
+ } from 'utils/test/factories/joanie';
4
9
  import { PacedCourseFactory } from 'utils/test/factories/richie';
5
10
  import { SaleTunnel, SaleTunnelProps } from './index';
6
11
 
@@ -28,6 +33,16 @@ export default {
28
33
 
29
34
  type Story = StoryObj<typeof SaleTunnel>;
30
35
 
31
- export const Default: Story = {
36
+ export const Credential: Story = {
32
37
  args: {},
33
38
  };
39
+
40
+ export const CertificateDiscount: Story = {
41
+ args: {
42
+ product: CertificateProductFactory({ price: 100, price_currency: 'EUR' }).one(),
43
+ course: PacedCourseFactory().one(),
44
+ enrollment: EnrollmentFactory({
45
+ offerings: OfferingFactory({ rules: { discounted_price: 80 } }).many(1),
46
+ }).one(),
47
+ },
48
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "richie-education",
3
- "version": "3.1.3-dev23",
3
+ "version": "3.1.3-dev24",
4
4
  "description": "A CMS to build learning portals for Open Education",
5
5
  "main": "sandbox/manage.py",
6
6
  "scripts": {