Skip to content

✨ (backend||frontend) CPF #1273

Merged
jonathanreveille merged 15 commits intomainfrom
feat/add_deep_link_to_my_cpf
Mar 20, 2026
Merged

✨ (backend||frontend) CPF #1273
jonathanreveille merged 15 commits intomainfrom
feat/add_deep_link_to_my_cpf

Conversation

@jonathanreveille
Copy link
Copy Markdown
Member

@jonathanreveille jonathanreveille commented Jan 23, 2026

Purpose

This PR will define the model that we will use to store deep links for offering in this issue

A deep link is related to an offering. Each deep link is unique for the pair offering and organization. The deep links are exclusive to product type credential.

If you want your API to suggest DeepLinks on an offering, you must set them first into the backoffice.
The backoffice allows the admin user to create/edit/delete or to activate or deactivate them.

Once we have a confirmation about a new order that was created outside our platform, we implemented a new way to generate standalone orders for this case. Once the standalone order is created, the admin user will communicate the voucher code to the organization responsible of the learner, and thus, pass the voucher code to the learner.

On the learner's side, he will have a voucher code that is only eligible for 1 offering exclusively. The voucher code will not allow the learner to subscribe to another offering.

Backoffice to add a deep link on an offering (2 organizations related)
Capture d’écran du 2026-03-20 15-01-17

Capture d’écran du 2026-03-20 15-02-20

Activate/deactivate one or both :
Capture d’écran du 2026-03-20 15-02-27

Capture d’écran du 2026-03-20 15-02-32

Create standalone order
Capture d’écran du 2026-03-20 15-03-02

Capture d’écran du 2026-03-20 15-03-15 Capture d’écran du 2026-03-20 15-05-17

Our API consumer side :
Capture d’écran du 2026-03-20 15-04-01

Proposal

  • Add OfferingDeepLink model to store links of offering
    to purchase a training from an external platform
  • Add admin API to manage offering deep links for organizations
  • Add client API endpoint to return a deep link value in OfferingViewSet
  • Handle deep link in back office
  • Create standalone to_own orders in admin
  • Expose voucher code and claim status in order admin API
  • Display voucher code in order detail view
  • Add voucher code copy-to-clipboard in orders list
  • Reject voucher codes from prepaid orders when used on a different offering

@jonathanreveille jonathanreveille self-assigned this Jan 23, 2026
@jonathanreveille jonathanreveille force-pushed the feat/add_deep_link_to_my_cpf branch from 019ac98 to fe05baa Compare January 23, 2026 15:57
@jonathanreveille jonathanreveille force-pushed the feature/add_purchase_order_reference_confirm_quote branch 4 times, most recently from d81994c to 75626da Compare January 26, 2026 16:23
@jonathanreveille jonathanreveille force-pushed the feat/add_deep_link_to_my_cpf branch from fe05baa to f1d384f Compare January 26, 2026 17:01
@jonathanreveille jonathanreveille changed the title 🚧(backend) deep linking per organization and offering ✨ (backend) deep linking per organization and offering Jan 26, 2026
@jonathanreveille jonathanreveille force-pushed the feature/add_purchase_order_reference_confirm_quote branch from 0a3fb8a to 4e43fe6 Compare January 27, 2026 09:50
Base automatically changed from feature/add_purchase_order_reference_confirm_quote to main January 27, 2026 10:16
@jonathanreveille jonathanreveille force-pushed the feat/add_deep_link_to_my_cpf branch 9 times, most recently from 2173e96 to 6d690b4 Compare February 4, 2026 14:12
@kernicPanel kernicPanel requested a review from Copilot February 6, 2026 09:01
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new OfferingDeepLink model to support CPF (Mon Compte Formation) deep linking functionality, allowing organizations to register external subscription links for course offerings. The feature enables learners to subscribe to courses through external platforms, with each organization able to provide their own unique deep link per offering. The model is restricted to credential-type products only and includes validation to ensure organizations are properly associated with offerings.

Changes:

  • Added OfferingDeepLink model with validation rules ensuring organization-offering relationships, credential-only product type restriction, and unique deep links
  • Created OfferingDeepLinkFactory to support testing with automatic organization assignment logic
  • Implemented comprehensive test suite validating model constraints, uniqueness requirements, and relationship rules

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 15 comments.

File Description
src/backend/joanie/core/models/courses.py Defines the OfferingDeepLink model with fields for deep_link, offering, organization, and is_active, plus custom validation logic in clean() method
src/backend/joanie/core/factories.py Adds OfferingDeepLinkFactory with lazy organization attribute that finds available organizations from the offering
src/backend/joanie/tests/core/models/test_offering_deep_link.py Comprehensive test suite covering credential-only constraint, uniqueness rules, organization-offering validation, and successful creation
src/backend/joanie/core/migrations/0091_offeringdeeplink_and_more.py Django migration creating the joanie_offering_deep_link table with appropriate fields and constraints

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/backend/joanie/core/models/courses.py Outdated
Comment thread src/backend/joanie/tests/core/models/test_offering_deep_link.py
Comment thread src/backend/joanie/tests/core/models/test_offering_deep_link.py Outdated
Comment thread src/backend/joanie/tests/core/models/test_offering_deep_link.py
Comment thread src/backend/joanie/tests/core/models/test_offering_deep_link.py
Comment thread src/backend/joanie/core/models/courses.py Outdated
Comment thread src/backend/joanie/tests/core/models/test_offering_deep_link.py
Comment thread src/backend/joanie/tests/core/models/test_offering_deep_link.py
Comment thread src/backend/joanie/tests/core/models/test_offering_deep_link.py
Comment thread src/backend/joanie/tests/core/models/test_offering_deep_link.py
Comment thread src/backend/joanie/tests/core/models/test_offering_deep_link.py Outdated
@jonathanreveille jonathanreveille force-pushed the feat/add_deep_link_to_my_cpf branch 3 times, most recently from 18821b2 to a16f217 Compare February 12, 2026 16:17
@jonathanreveille jonathanreveille force-pushed the feat/add_deep_link_to_my_cpf branch from a16f217 to 79713ff Compare February 26, 2026 15:27
@jonathanreveille jonathanreveille changed the title ✨ (backend) deep linking per organization and offering ✨ (backend) CPF Mar 3, 2026
@jonathanreveille jonathanreveille changed the title ✨ (backend) CPF ✨ (backend||frontent) CPF Mar 3, 2026
@jonathanreveille jonathanreveille force-pushed the feat/add_deep_link_to_my_cpf branch 2 times, most recently from 7f95166 to fcb6317 Compare March 16, 2026 08:47
@jonathanreveille jonathanreveille changed the title ✨ (backend||frontent) CPF ✨ (backend||frontend) CPF Mar 16, 2026
@liamls liamls force-pushed the feat/add_deep_link_to_my_cpf branch 4 times, most recently from 9ce9049 to ca0db43 Compare March 20, 2026 08:49
jonathanreveille and others added 15 commits March 20, 2026 14:38
We want to suggest deep links of an offering which will
allow the learners to subscribe to the course outside
from the platform. This link will redirect the learner
to a platform where he can purchase the course session
and use credits earned through work.

Each organization that is related to the offering
can only one unique link for redirection.
We want admin backoffice users to be able to manage deep links
of an offering for organizations.
We want admin authenticated user to be able to activate
on or off the deep links of an offering. This will allow
them to activate or deactivate the possibility to redirect
the learners to the external platform to make subscriptions.
The client API of the offering viewset returns one of the
deep link in a random manner. It only returns a deep link
when the offering deep links are activate and when some
exists. Otherwise, it returns None.
We want to let the user add and edit deep links
in joanie back office, since the CRUD management
was implemented.
Admins can now POST to /api/v1.0/admin/orders/ to create an isolated
order in the `to_own` state with a 100%-discount voucher attached,
without requiring a batch order flow.

A new AdminOrderCreateSerializer validates product_id (required),
course_code and organization_id (optional). perform_create follows
the same pattern as generate_orders_and_send_vouchers: the order is
saved first without a voucher so that full_clean() passes with a null
owner, then the voucher is attached before transitioning through
assign() and forcing the to_own state.
Admins can now create standalone orders directly from the order list
page via a dedicated creation form with Product, Course and
Organization search fields.

The OrderRepository exposes a create() method, useOrders exposes it
through the apiInterface, and a new OrderCreateForm / create page
handle the flow with redirect to the order view on success.
Add a voucher field to AdminOrderSerializer returning the code and
whether the voucher has been claimed (is_used = owner is not None),
so the admin frontend can display and copy the code until it is used.
Show the voucher code in monospace in the order details section.
When the voucher is still available, a green "Available" chip acts
as a copy-to-clipboard button. Once claimed, the code is struck
through and a grey "Used" chip replaces the copy action.
Show the voucher code in the list with a copy button. The code
truncates with ellipsis when the column is too narrow, while
the button always stays visible via flexShrink.

Also generalizes the copy confirmation message from "Link added
to your clipboard" to "Copied to clipboard".
The OfferingSearch component sends a `query` parameter to search
offerings by product title, course title or course code. Without a
filterset on the viewset, that parameter was silently ignored,
making it impossible to find a specific offering among many.
Offerings with a single organization don't exercise the organization
selector in the order creation form. Adding a second organization to
each dev offering makes it easier to test that flow locally.
A canceled order transitions to a state included in
ORDER_STATES_VOUCHER_CLAIMABLE, which caused the 100% voucher to
appear usable again and be applicable as a discount on a new order.
Now that we different payment methods in our sales tunnel
such as credit card, external deep links, and batch orders.
Some payment methods like orders of batch order and
prepaid orders from external platform, do not require
to have a contract signed or a payment schedule
because they are both fully prepaid. We have changed
the key in the payment plan endpoint, to return
whether the API consumer should skip the contract input
or not.
When get_prepaid_order() finds no matching order, the code
fell through and created a new order with the 100% discount.
This guard rejects vouchers tied to a to_own order when used
on a different product/course.
@jonathanreveille jonathanreveille force-pushed the feat/add_deep_link_to_my_cpf branch from 46d014c to e6a62de Compare March 20, 2026 13:45
@jonathanreveille jonathanreveille merged commit d59eb9d into main Mar 20, 2026
17 checks passed
@jonathanreveille jonathanreveille deleted the feat/add_deep_link_to_my_cpf branch March 20, 2026 14:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants