Skip to content

Commit

Permalink
feat: implementing gated top down allocation table subsidy service ap…
Browse files Browse the repository at this point in the history
…i usage
  • Loading branch information
alex-sheehan-edx committed Nov 7, 2023
1 parent d518a4d commit 8591d17
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,22 @@ import PropTypes from 'prop-types';
import { connect } from 'react-redux';

import LearnerCreditAllocationTable from './LearnerCreditAllocationTable';
import { useBudgetId, useOfferRedemptions } from './data';
import { useBudgetId, useOfferRedemptions, useSubsidyAccessPolicy } from './data';

const BudgetDetailRedemptions = ({ enterpriseUUID }) => {
const BudgetDetailRedemptions = ({ enterpriseFeatures, enterpriseUUID }) => {
const { enterpriseOfferId, subsidyAccessPolicyId } = useBudgetId();
const { data: subsidyAccessPolicy } = useSubsidyAccessPolicy(subsidyAccessPolicyId);
const {
isLoading,
offerRedemptions,
fetchOfferRedemptions,
} = useOfferRedemptions(enterpriseUUID, enterpriseOfferId, subsidyAccessPolicyId);

} = useOfferRedemptions(
enterpriseUUID,
enterpriseOfferId,
subsidyAccessPolicyId,
enterpriseFeatures.topDownAssignmentRealTimeLcm,
subsidyAccessPolicy?.subsidyUuid,
);
return (
<section>
<h3 className="mb-3">Spent</h3>
Expand All @@ -30,11 +36,15 @@ const BudgetDetailRedemptions = ({ enterpriseUUID }) => {
};

const mapStateToProps = state => ({
enterpriseFeatures: state.portalConfiguration.enterpriseFeatures,
enterpriseUUID: state.portalConfiguration.enterpriseId,
});

BudgetDetailRedemptions.propTypes = {
enterpriseUUID: PropTypes.string.isRequired,
enterpriseFeatures: PropTypes.shape({
topDownAssignmentRealTimeLcm: PropTypes.bool,
}).isRequired,
};

export default connect(mapStateToProps)(BudgetDetailRedemptions);
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
import debounce from 'lodash.debounce';

import EnterpriseDataApiService from '../../../../data/services/EnterpriseDataApiService';
import SubsidyApiService from '../../../../data/services/EnterpriseSubsidyApiService';
import { API_FIELDS_BY_TABLE_COLUMN_ACCESSOR } from '../constants';
import { transformUtilizationTableResults } from '../utils';
import { transformUtilizationTableResults, transformUtilizationTableTransactionResults } from '../utils';

const applySortByToOptions = (sortBy, options) => {
const orderingStrings = sortBy.map(({ id, desc }) => {
Expand All @@ -29,19 +30,27 @@ const applySortByToOptions = (sortBy, options) => {
});
};

const applyFiltersToOptions = (filters, options) => {
const applyFiltersToOptions = (filters, options, fetchTransactions = null) => {
const courseProductLineSearchQuery = filters?.find(filter => filter.id === 'courseProductLine')?.value;
const searchQuery = filters?.find(filter => filter.id.toLowerCase() === 'enrollment details')?.value;
const searchQuery = filters?.find(filter => filter.id.toLowerCase() === 'enrollmentdetails')?.value;

if (courseProductLineSearchQuery) {
Object.assign(options, { courseProductLine: courseProductLineSearchQuery });
}
if (searchQuery) {
Object.assign(options, { searchAll: searchQuery });
const optionsObject = {};
optionsObject[fetchTransactions ? 'search' : 'searchAll'] = searchQuery;
Object.assign(options, optionsObject);
}
};

const useOfferRedemptions = (enterpriseUUID, offerId = null, budgetId = null) => {
const useOfferRedemptions = (
enterpriseUUID,
offerId = null,
budgetId = null,

Check warning on line 50 in src/components/learner-credit-management/data/hooks/useOfferRedemptions.js

View check run for this annotation

Codecov / codecov/patch

src/components/learner-credit-management/data/hooks/useOfferRedemptions.js#L49-L50

Added lines #L49 - L50 were not covered by tests
fetchTransactions = null,
subsidyUuid = null,
) => {
const shouldTrackFetchEvents = useRef(false);
const [isLoading, setIsLoading] = useState(true);
const [offerRedemptions, setOfferRedemptions] = useState({
Expand Down Expand Up @@ -69,14 +78,27 @@ const useOfferRedemptions = (enterpriseUUID, offerId = null, budgetId = null) =>
applySortByToOptions(args.sortBy, options);
}
if (args.filters?.length > 0) {
applyFiltersToOptions(args.filters, options);
applyFiltersToOptions(args.filters, options, fetchTransactions);
}
const response = await EnterpriseDataApiService.fetchCourseEnrollments(
enterpriseUUID,
options,
);
const data = camelCaseObject(response.data);
const transformedTableResults = transformUtilizationTableResults(data.results);
let data;
let transformedTableResults;
if (offerId === null && fetchTransactions) { // and do the feature flag check here
const response = await SubsidyApiService.fetchCustomerTransactions(

Check warning on line 86 in src/components/learner-credit-management/data/hooks/useOfferRedemptions.js

View check run for this annotation

Codecov / codecov/patch

src/components/learner-credit-management/data/hooks/useOfferRedemptions.js#L86

Added line #L86 was not covered by tests
enterpriseUUID,
subsidyUuid,
options,
);
data = camelCaseObject(response.data);
transformedTableResults = transformUtilizationTableTransactionResults(data.results);

Check warning on line 92 in src/components/learner-credit-management/data/hooks/useOfferRedemptions.js

View check run for this annotation

Codecov / codecov/patch

src/components/learner-credit-management/data/hooks/useOfferRedemptions.js#L91-L92

Added lines #L91 - L92 were not covered by tests
} else {
const response = await EnterpriseDataApiService.fetchCourseEnrollments(
enterpriseUUID,
options,
);
data = camelCaseObject(response.data);
transformedTableResults = transformUtilizationTableResults(data.results);
}

setOfferRedemptions({
itemCount: data.count,
pageCount: data.numPages,
Expand Down Expand Up @@ -104,7 +126,7 @@ const useOfferRedemptions = (enterpriseUUID, offerId = null, budgetId = null) =>
if (offerId || budgetId) {
fetch();
}
}, [enterpriseUUID, offerId, budgetId, shouldTrackFetchEvents]);
}, [enterpriseUUID, offerId, budgetId, shouldTrackFetchEvents, fetchTransactions, subsidyUuid]);

const debouncedFetchOfferRedemptions = useMemo(() => debounce(fetchOfferRedemptions, 300), [fetchOfferRedemptions]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ describe('useOfferRedemptions', () => {
{ id: 'enrollmentDate', desc: true },
],
filters: [
{ id: 'Enrollment Details', value: mockOfferEnrollments[0].user_email },
{ id: 'enrollmentDetails', value: mockOfferEnrollments[0].user_email },
],
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export const mockAssignableSubsidyAccessPolicy = {
spendAvailableUsd: 10000,
},
isAssignable: true,
subsidyUuid: 'mock-subsidy-uuid',
};

export const mockPerLearnerSpendLimitSubsidyAccessPolicy = {
Expand All @@ -22,4 +23,5 @@ export const mockPerLearnerSpendLimitSubsidyAccessPolicy = {
spendAvailableUsd: 10000,
},
isAssignable: false,
subsidyUuid: 'mock-subsidy-uuid',
};
10 changes: 10 additions & 0 deletions src/components/learner-credit-management/data/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,16 @@ export const transformUtilizationTableResults = results => results.map(result =>
courseKey: result.courseKey,
}));

export const transformUtilizationTableTransactionResults = results => results.map(result => ({
created: result.created,
enterpriseEnrollmentId: result.fulfillmentIdentifier,
userEmail: result.lmsUserEmail,
courseTitle: result.contentTitle,
courseListPrice: result.unit === 'usd_cents' ? -1 * (result.quantity / 100) : -1 * results.quantity,

Check warning on line 103 in src/components/learner-credit-management/data/utils.js

View check run for this annotation

Codecov / codecov/patch

src/components/learner-credit-management/data/utils.js#L103

Added line #L103 was not covered by tests
uuid: uuidv4(),
courseKey: result.contentKey,
}));

/**
* Gets appropriate color variant for the annotated progress bar.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,11 +197,11 @@ describe('<BudgetDetailPage />', () => {
it.each([
{
budgetId: mockEnterpriseOfferId,
expectedUseOfferRedemptionsArgs: [enterpriseUUID, mockEnterpriseOfferId, null],
expectedUseOfferRedemptionsArgs: [enterpriseUUID, mockEnterpriseOfferId, null, true, undefined],
},
{
budgetId: mockSubsidyAccessPolicyUUID,
expectedUseOfferRedemptionsArgs: [enterpriseUUID, null, mockSubsidyAccessPolicyUUID],
expectedUseOfferRedemptionsArgs: [enterpriseUUID, null, mockSubsidyAccessPolicyUUID, true, undefined],
},
])('displays spend table in "Activity" tab with empty results (%s)', async ({
budgetId,
Expand Down
12 changes: 12 additions & 0 deletions src/data/services/EnterpriseSubsidyApiService.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,20 @@ import { configuration } from '../../config';
class SubsidyApiService {
static baseUrl = `${configuration.ENTERPRISE_SUBSIDY_BASE_URL}/api/v1`;

static subsidyV2Url = `${configuration.ENTERPRISE_SUBSIDY_BASE_URL}/api/v2/subsidies`;

static apiClient = getAuthenticatedHttpClient;

static fetchCustomerTransactions(customerUuid, subsidyUuid, options = {}) {
const queryParams = new URLSearchParams({

Check warning on line 14 in src/data/services/EnterpriseSubsidyApiService.js

View check run for this annotation

Codecov / codecov/patch

src/data/services/EnterpriseSubsidyApiService.js#L13-L14

Added lines #L13 - L14 were not covered by tests
...snakeCaseObject(options),
});
const url = `${SubsidyApiService.subsidyV2Url}/${subsidyUuid}/transactions/?${queryParams.toString()}`;
return SubsidyApiService.apiClient({

Check warning on line 18 in src/data/services/EnterpriseSubsidyApiService.js

View check run for this annotation

Codecov / codecov/patch

src/data/services/EnterpriseSubsidyApiService.js#L17-L18

Added lines #L17 - L18 were not covered by tests
useCache: configuration.USE_API_CACHE,
}).get(url, { clearCacheEntry: true });
}

static getSubsidyByCustomerUUID(uuid, options = {}) {
const queryParams = new URLSearchParams({
enterprise_customer_uuid: uuid,
Expand Down

0 comments on commit 8591d17

Please sign in to comment.