Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 27 additions & 27 deletions enterprise_access/apps/api/v1/views/browse_and_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,9 @@
from enterprise_access.apps.subsidy_access_policy.models import SubsidyAccessPolicy
from enterprise_access.apps.subsidy_request.constants import (
REUSABLE_REQUEST_STATES,
LearnerCreditAdditionalActionStates,
LearnerCreditRequestActionErrorReasons,
LearnerCreditRequestActionTypes,
LearnerCreditRequestUserMessages,
SegmentEvents,
SubsidyRequestStates,
SubsidyTypeChoices
Expand All @@ -71,11 +72,7 @@
send_learner_credit_bnr_request_approve_task,
send_reminder_email_for_pending_learner_credit_request
)
from enterprise_access.apps.subsidy_request.utils import (
get_action_choice,
get_error_reason_choice,
get_user_message_choice
)
from enterprise_access.apps.subsidy_request.utils import get_error_reason_choice
from enterprise_access.apps.track.segment import track_event
from enterprise_access.utils import format_traceback, get_subsidy_model

Expand Down Expand Up @@ -898,8 +895,8 @@ def create(self, request, *args, **kwargs):
self._reuse_existing_request(existing_request, course_price)
LearnerCreditRequestActions.create_action(
learner_credit_request=existing_request,
recent_action=get_action_choice(SubsidyRequestStates.REQUESTED),
status=get_user_message_choice(SubsidyRequestStates.REQUESTED),
recent_action=LearnerCreditRequestActionTypes.REQUESTED,
status=LearnerCreditRequestUserMessages.REQUESTED,
)
# Trigger admin email notification with the latest request
send_learner_credit_bnr_admins_email_with_new_requests_task.delay(
Expand Down Expand Up @@ -941,8 +938,8 @@ def create(self, request, *args, **kwargs):
lcr = LearnerCreditRequest.objects.get(uuid=lcr_uuid)
LearnerCreditRequestActions.create_action(
learner_credit_request=lcr,
recent_action=get_action_choice(SubsidyRequestStates.REQUESTED),
status=get_user_message_choice(SubsidyRequestStates.REQUESTED),
recent_action=LearnerCreditRequestActionTypes.REQUESTED,
status=LearnerCreditRequestUserMessages.REQUESTED,
)

# Trigger admin email notification with the latest request
Expand Down Expand Up @@ -982,8 +979,8 @@ def approve(self, request, *args, **kwargs):
# Log "approve" as recent action in the Request Action model.
lc_request_action = LearnerCreditRequestActions.create_action(
learner_credit_request=lc_request,
recent_action=get_action_choice(SubsidyRequestStates.APPROVED),
status=get_user_message_choice(SubsidyRequestStates.APPROVED),
recent_action=LearnerCreditRequestActionTypes.APPROVED,
status=LearnerCreditRequestUserMessages.APPROVED,
)

try:
Expand Down Expand Up @@ -1014,7 +1011,7 @@ def approve(self, request, *args, **kwargs):
logger.exception(error_msg)

# Update approve action with error reason.
lc_request_action.status = get_user_message_choice(SubsidyRequestStates.REQUESTED)
lc_request_action.status = LearnerCreditRequestUserMessages.REQUESTED
lc_request_action.error_reason = get_error_reason_choice(
LearnerCreditRequestActionErrorReasons.FAILED_APPROVAL
)
Expand Down Expand Up @@ -1044,8 +1041,8 @@ def cancel(self, request, *args, **kwargs):
error_msg = None
lc_action = LearnerCreditRequestActions.create_action(
learner_credit_request=learner_credit_request,
recent_action=get_action_choice(SubsidyRequestStates.CANCELLED),
status=get_user_message_choice(SubsidyRequestStates.CANCELLED),
recent_action=LearnerCreditRequestActionTypes.CANCELLED,
status=LearnerCreditRequestUserMessages.CANCELLED,
)

try:
Expand All @@ -1059,7 +1056,7 @@ def cancel(self, request, *args, **kwargs):
lc_action.error_reason = get_error_reason_choice(
LearnerCreditRequestActionErrorReasons.FAILED_CANCELLATION
)
lc_action.status = get_user_message_choice(SubsidyRequestStates.APPROVED)
lc_action.status = LearnerCreditRequestUserMessages.APPROVED
lc_action.traceback = error_msg
lc_action.save()
return Response(error_msg, status=status.HTTP_422_UNPROCESSABLE_ENTITY)
Expand All @@ -1081,7 +1078,7 @@ def cancel(self, request, *args, **kwargs):
lc_action.error_reason = get_error_reason_choice(
LearnerCreditRequestActionErrorReasons.FAILED_CANCELLATION
)
lc_action.status = get_user_message_choice(SubsidyRequestStates.APPROVED)
lc_action.status = LearnerCreditRequestUserMessages.APPROVED
lc_action.traceback = error_msg
lc_action.save()
return Response(status=status.HTTP_422_UNPROCESSABLE_ENTITY)
Expand All @@ -1102,17 +1099,20 @@ def remind(self, request, *args, **kwargs):

action_instance = LearnerCreditRequestActions.create_action(
learner_credit_request=learner_credit_request,
recent_action=get_action_choice(LearnerCreditAdditionalActionStates.REMINDED),
status=get_user_message_choice(LearnerCreditAdditionalActionStates.REMINDED),
recent_action=LearnerCreditRequestActionTypes.REMINDED,
status=LearnerCreditRequestUserMessages.REMINDED,
)

try:
send_reminder_email_for_pending_learner_credit_request.delay(assignment.uuid)
return Response(status=status.HTTP_200_OK)
except Exception as exc: # pylint: disable=broad-except
# Optionally log an errored action here if the task couldn't be queued
action_instance.status = get_user_message_choice(LearnerCreditRequestActionErrorReasons.EMAIL_ERROR)
action_instance.error_reason = str(exc)
action_instance.status = LearnerCreditRequestUserMessages.APPROVED
action_instance.error_reason = get_error_reason_choice(
LearnerCreditRequestActionErrorReasons.EMAIL_ERROR
)
action_instance.traceback = format_traceback(exc)
action_instance.save()
return Response(status=status.HTTP_422_UNPROCESSABLE_ENTITY)

Expand Down Expand Up @@ -1142,19 +1142,19 @@ def decline(self, *args, **kwargs):
# Create the action instance before attempting the decline operation
action_instance = LearnerCreditRequestActions.create_action(
learner_credit_request=learner_credit_request,
recent_action=get_action_choice(SubsidyRequestStates.DECLINED),
status=get_user_message_choice(SubsidyRequestStates.DECLINED),
recent_action=LearnerCreditRequestActionTypes.DECLINED,
status=LearnerCreditRequestUserMessages.DECLINED,
)

try:
with transaction.atomic():
learner_credit_request.decline(self.user)
except (ValidationError, IntegrityError, DatabaseError) as exc:
action_instance.status = get_user_message_choice(SubsidyRequestStates.REQUESTED)
action_instance.status = LearnerCreditRequestActionTypes.REQUESTED
action_instance.error_reason = get_error_reason_choice(
LearnerCreditRequestActionErrorReasons.FAILED_DECLINE
)
action_instance.traceback = str(exc)
action_instance.traceback = format_traceback(exc)
action_instance.save()

logger.exception(f"Error declining learner credit request {learner_credit_request_uuid}: {exc}")
Expand All @@ -1178,11 +1178,11 @@ def decline(self, *args, **kwargs):
try:
unlink_users_from_enterprise_task.delay(enterprise_customer_uuid, [lms_user_id])
except (ConnectionError, TimeoutError, OSError) as exc:
action_instance.status = get_user_message_choice(SubsidyRequestStates.REQUESTED)
action_instance.status = LearnerCreditRequestActionTypes.REQUESTED
action_instance.error_reason = get_error_reason_choice(
LearnerCreditRequestActionErrorReasons.FAILED_DECLINE
)
action_instance.traceback = str(exc)
action_instance.traceback = format_traceback(exc)
action_instance.save()

logger.exception(
Expand Down
67 changes: 56 additions & 11 deletions enterprise_access/apps/subsidy_request/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ class SubsidyRequestStates:
CHOICES = COMMON_STATES + LC_REQUEST_STATES


# DEPRECATED: This class was used to add 'reminded' to the list of possible
# actions. Its functionality has been consolidated into the self-contained
# `LearnerCreditRequestActionTypes` class.
class LearnerCreditAdditionalActionStates:
""" Additional states specifically for LearnerCreditRequestActions. """

Expand All @@ -44,7 +47,8 @@ class LearnerCreditAdditionalActionStates:
)


# Combined choices for LearnerCreditRequestAction model
# DEPRECATED: This variable combined states from multiple classes in a way that
# was confusing and brittle. Use `LearnerCreditRequestActionTypes.CHOICES` instead.
LearnerCreditRequestActionChoices = SubsidyRequestStates.CHOICES + LearnerCreditAdditionalActionStates.CHOICES


Expand Down Expand Up @@ -85,20 +89,61 @@ class SubsidyTypeChoices:
SUBSIDY_REQUEST_BULK_OPERATION_BATCH_SIZE = 100


class LearnerCreditRequestActionTypes:
"""
Defines the set of possible values for the `recent_action` field on the
`LearnerCreditRequestActions` model. This represents the specific event
or operation that occurred (e.g., an approval, a reminder).
"""
REQUESTED = 'requested'
APPROVED = 'approved'
DECLINED = 'declined'
ERROR = 'error'
ACCEPTED = 'accepted'
CANCELLED = 'cancelled'
EXPIRED = 'expired'
REVERSED = 'reversed'
REMINDED = 'reminded'

CHOICES = (
(REQUESTED, "Requested"),
(APPROVED, "Approved"),
(DECLINED, "Declined"),
(ERROR, "Error"),
(ACCEPTED, "Accepted"),
(CANCELLED, "Cancelled"),
(EXPIRED, "Expired"),
(REVERSED, "Reversed"),
(REMINDED, "Reminded"),
)


class LearnerCreditRequestUserMessages:
"""
User-facing messages for LearnerCreditRequestActions status field.
Reusing the state keys from SubsidyRequestStates but with different display messages.
Defines the set of possible values for the `status` field on the
`LearnerCreditRequestActions` model. This represents the user-facing
status label that is displayed in the UI as a result of an action.
"""
REQUESTED = 'requested'
REMINDED = 'reminded'
APPROVED = 'approved'
ACCEPTED = 'accepted'
DECLINED = 'declined'
REVERSED = 'reversed'
CANCELLED = 'cancelled'
EXPIRED = 'expired'
ERROR = 'error'

CHOICES = (
(SubsidyRequestStates.REQUESTED, "Requested"),
(LearnerCreditAdditionalActionStates.REMINDED, "Waiting For Learner"),
(SubsidyRequestStates.APPROVED, "Waiting For Learner"),
(SubsidyRequestStates.ACCEPTED, "Redeemed By Learner"),
(SubsidyRequestStates.DECLINED, "Declined"),
(SubsidyRequestStates.REVERSED, "Refunded"),
(SubsidyRequestStates.CANCELLED, "Cancelled"),
(SubsidyRequestStates.EXPIRED, "Expired"),
(REQUESTED, "Requested"),
(REMINDED, "Waiting For Learner"),
(APPROVED, "Waiting For Learner"),
(ACCEPTED, "Redeemed By Learner"),
(DECLINED, "Declined"),
(REVERSED, "Refunded"),
(CANCELLED, "Cancelled"),
(EXPIRED, "Expired"),
(ERROR, "Error"),
)


Expand Down
15 changes: 7 additions & 8 deletions enterprise_access/apps/subsidy_request/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,8 @@

from enterprise_access.apps.subsidy_request.constants import (
SUBSIDY_REQUEST_BULK_OPERATION_BATCH_SIZE,
LearnerCreditAdditionalActionStates,
LearnerCreditRequestActionChoices,
LearnerCreditRequestActionErrorReasons,
LearnerCreditRequestActionTypes,
LearnerCreditRequestUserMessages,
SubsidyRequestStates,
SubsidyTypeChoices
Expand Down Expand Up @@ -487,19 +486,19 @@ def annotate_dynamic_fields_onto_queryset(cls, queryset):
),
When(
Q(state=SubsidyRequestStates.REQUESTED),
then=Value(SubsidyRequestStates.REQUESTED)
then=Value(LearnerCreditRequestUserMessages.REQUESTED)
),
When(
Q(state=SubsidyRequestStates.DECLINED),
then=Value(SubsidyRequestStates.DECLINED)
then=Value(LearnerCreditRequestUserMessages.DECLINED)
),
When(
Q(state=SubsidyRequestStates.CANCELLED),
then=Value(SubsidyRequestStates.CANCELLED)
then=Value(LearnerCreditRequestUserMessages.CANCELLED)
),
When(
Q(state=SubsidyRequestStates.ERROR),
then=Value(SubsidyRequestStates.ERROR)
then=Value(LearnerCreditRequestUserMessages.ERROR)
),
When(
Q(state=SubsidyRequestStates.APPROVED),
Expand Down Expand Up @@ -560,7 +559,7 @@ class LearnerCreditRequestActions(TimeStampedModel):
blank=False,
null=False,
db_index=True,
choices=LearnerCreditRequestActionChoices,
choices=LearnerCreditRequestActionTypes.CHOICES,
help_text="The type of action taken on the learner credit request.",
)

Expand Down Expand Up @@ -615,7 +614,7 @@ def create_action(
Args:
learner_credit_request (LearnerCreditRequest): The associated learner credit request.
recent_action (str): The type of action taken (must be a valid choice from
LearnerCreditRequestActionChoices).
LearnerCreditRequestActionTypes).
status (str): The status message (must be a valid choice from LearnerCreditRequestUserMessages.CHOICES).
error_reason (str, optional): The error reason if applicable (must be a valid choice
from LearnerCreditRequestActionErrorReasons.CHOICES).
Expand Down
11 changes: 8 additions & 3 deletions enterprise_access/apps/subsidy_request/tests/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@

from enterprise_access.apps.content_assignments.tests.factories import LearnerContentAssignmentFactory
from enterprise_access.apps.core.tests.factories import UserFactory
from enterprise_access.apps.subsidy_request.constants import SubsidyRequestStates, SubsidyTypeChoices
from enterprise_access.apps.subsidy_request.constants import (
LearnerCreditRequestActionTypes,
LearnerCreditRequestUserMessages,
SubsidyRequestStates,
SubsidyTypeChoices
)
from enterprise_access.apps.subsidy_request.models import (
CouponCodeRequest,
LearnerCreditRequest,
Expand Down Expand Up @@ -105,8 +110,8 @@ class LearnerCreditRequestActionsFactory(factory.django.DjangoModelFactory):
Test factory for the `LearnerCreditRequestActions` model.
"""
uuid = factory.LazyFunction(uuid4)
recent_action = get_action_choice(SubsidyRequestStates.REQUESTED)
status = get_user_message_choice(SubsidyRequestStates.REQUESTED)
recent_action = LearnerCreditRequestActionTypes.REQUESTED
status = LearnerCreditRequestUserMessages.REQUESTED
learner_credit_request = factory.SubFactory(LearnerCreditRequestFactory)
error_reason = None
traceback = None
Expand Down
Loading