Skip to content

feat: replace enterprise_customer_for_request with CourseModePriceRequested filter#365

Open
kiram15 wants to merge 1 commit into
release-ulmofrom
kiram15/ENT-11573
Open

feat: replace enterprise_customer_for_request with CourseModePriceRequested filter#365
kiram15 wants to merge 1 commit into
release-ulmofrom
kiram15/ENT-11573

Conversation

@kiram15

@kiram15 kiram15 commented Jun 26, 2026

Copy link
Copy Markdown

Description

Remove direct enterprise_support import from course_modes/views.py and replace with a call to the CourseModePriceRequested openedx-filter.

https://2u-internal.atlassian.net/browse/ENT-11573

openedx-filters #338 — CourseModePriceRequested filter definition
edx-enterprise #2556 — CalculateEnterpriseDiscountedPrice pipeline step
edx-platform #365 — view wired to invoke the filter

Testing instructions

E2E testing instructions detailed in the edx-enterprise PR

Copilot AI review requested due to automatic review settings June 26, 2026 20:28

Copilot AI left a comment

Copy link
Copy Markdown

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 replaces a direct dependency on enterprise_support in the course modes “choose your track” view with an Open edX filter hook (CourseModeCheckoutStarted), allowing enterprise checkout context (e.g., enterprise customer) to be provided via the filters framework and used to compute discounted pricing.

Changes:

  • Replaced enterprise_customer_for_request(request) with CourseModeCheckoutStarted.run_filter(...) to obtain enterprise_customer.
  • Updated unit tests to patch the filter call instead of patching the removed enterprise-support API.

Reviewed changes

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

File Description
common/djangoapps/course_modes/views.py Swaps enterprise-support lookup for CourseModeCheckoutStarted filter call in checkout/price calculation path.
common/djangoapps/course_modes/tests/test_views.py Updates mocks/patching to align tests with the new filter-based integration point.

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

Comment thread common/djangoapps/course_modes/views.py Outdated
Comment thread common/djangoapps/course_modes/tests/test_views.py

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Comment thread lms/djangoapps/grades/tests/test_events.py Outdated

mock_enterprise_customer_for_request.return_value = {'name': 'dummy'} if is_enterprise_enabled else {}
enterprise_customer = {'name': 'dummy', 'uuid': 'test-uuid'} if is_enterprise_enabled else None
mock_run_filter.return_value = {'enterprise_customer': enterprise_customer}
Comment thread common/djangoapps/course_modes/views.py Outdated
Comment on lines 197 to 198
if enterprise_customer and verified_mode.sku:
course_price = get_course_final_price(request.user, verified_mode.sku, price_before_discount)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

❌ These two lines are the main thing which needs to be replaced, yet they are still here. Remember, you can always check your work by searching for "enterprise" references after your changes. Unfortunately, I think the the proposed filter needs to be reconsidered.

Here are some facts I've gleaned:

  • The goal of this branch is to show the enterprise-negotiated discounted price on the track-selection page for enterprise-linked learners only.
  • The surrounding business logic seems designed to accommodate a "full" price (price_before_discount) and a "discounted" price (get_course_final_price).
  • The helper function get_course_final_price() is only used by this enterprise-specific branch. It's what actually calculates the "enterprise-negotiated" discount, and that discount is established by enterprise customers in practice, but technically the discount could be established for non-enterprise reasons. Hence the entire code stack including get_course_final_price() and all code/APIs that it calls are not enterprise-specific.

Given all of those facts, I think the best approach is the following:

  • A filter called CourseModePriceRequested that takes a user, course_mode, and price and returns the same, with a possibly discounted price.
  • A pipeline step called CalculateEnterpriseDiscountedPrice which calls the get_course_final_price function. This step imports get_course_final_price from the platform and calls it.
  • The newly orphaned get_course_final_price function docstring should be updated to help protect it against deletion.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 6 comments.


@httpretty.activate
@patch('common.djangoapps.course_modes.views.enterprise_customer_for_request')
@patch('common.djangoapps.course_modes.views.CourseModeCheckoutStarted.run_filter')
Comment on lines +210 to 212
enterprise_customer = {'name': 'dummy', 'uuid': 'test-uuid'} if is_enterprise_enabled else None
mock_run_filter.return_value = {'enterprise_customer': enterprise_customer}
mock_get_course_final_price.return_value = discounted_price
Comment on lines +190 to 195
# this filter applies discounts (e.g. enterprise-negotiated pricing) to the price
_, _, course_price = CourseModePriceRequested.run_filter(
user=request.user,
course_mode_data=verified_mode,
price=price_before_discount,
)
Comment thread requirements/edx/doc.txt Outdated
Comment thread requirements/edx/testing.txt Outdated
Comment thread requirements/edx/development.txt Outdated
@kiram15 kiram15 changed the title feat: replace enterprise_customer_for_request with CourseModeCheckout… feat: replace enterprise_customer_for_request with CourseModeCheckoutStarted filter Jul 20, 2026
Copilot AI review requested due to automatic review settings July 22, 2026 22:07
@kiram15
kiram15 force-pushed the kiram15/ENT-11573 branch from 60a9010 to 9468ecc Compare July 22, 2026 22:07
@kiram15 kiram15 changed the title feat: replace enterprise_customer_for_request with CourseModeCheckoutStarted filter feat: replace enterprise_customer_for_request with CourseModePriceRequested filter Jul 22, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (4)

common/djangoapps/course_modes/tests/test_views.py:187

  • The view imports and calls CourseModePriceRequested.run_filter, but this test patches CourseModeCheckoutStarted.run_filter (which doesn’t appear to exist in this module). This patch target should match the filter actually invoked by the view so the test can control the displayed price deterministically.
    @patch('common.djangoapps.course_modes.views.CourseModeCheckoutStarted.run_filter')

common/djangoapps/course_modes/tests/test_views.py:212

  • CourseModePriceRequested.run_filter is unpacked as a 3-tuple in the view (_, _, course_price = ...). Returning a dict here will raise at runtime and won’t simulate the filter behavior. Mock this as (user, course_mode_data, price) (and you can drop the unused enterprise_customer dict).
        enterprise_customer = {'name': 'dummy', 'uuid': 'test-uuid'} if is_enterprise_enabled else None
        mock_run_filter.return_value = {'enterprise_customer': enterprise_customer}
        mock_get_course_final_price.return_value = discounted_price

common/djangoapps/course_modes/views.py:31

  • get_course_final_price is no longer used in this view after switching to the CourseModePriceRequested filter. Keeping the import is misleading and may fail linting as an unused import.
from common.djangoapps.course_modes.models import CourseMode
from common.djangoapps.course_modes.helpers import get_course_final_price, get_verified_track_links
from common.djangoapps.edxmako.shortcuts import render_to_response

common/djangoapps/course_modes/views.py:193

  • The PR title/description mention wiring CourseModeCheckoutStarted, but the code (and inline filter metadata) uses CourseModePriceRequested (org.openedx.learning.course_mode.price.requested.v1). Please align the PR description/title and the implementation (or switch the implementation to the intended filter) to avoid confusion for future maintainers.
            # this filter applies discounts (e.g. enterprise-negotiated pricing) to the price
            # .. filter_implemented_name: CourseModePriceRequested
            # .. filter_type: org.openedx.learning.course_mode.price.requested.v1
            _, _, course_price = CourseModePriceRequested.run_filter(

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.

3 participants