Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add soft validation of member codes to training request update form #2560

Merged
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
48 changes: 15 additions & 33 deletions amy/extforms/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@
WorkshopInquiryRequestBaseForm,
WorkshopRequestBaseForm,
)
from extrequests.utils import MemberCodeValidationError, member_code_valid_training
from workshops.fields import (
CheckboxSelectMultipleWithOthers,
RadioSelectWithOther,
Select2Widget,
)
from workshops.forms import BootstrapHelper
from workshops.models import Membership, TrainingRequest
from workshops.models import TrainingRequest
from workshops.utils.feature_flags import feature_flag_enabled


Expand Down Expand Up @@ -92,6 +93,7 @@ class Meta:
}

def __init__(self, *args, **kwargs):
# request is required for ENFORCE_MEMBER_CODES flag
self.request_http = kwargs.pop("request", None)
super().__init__(*args, **kwargs)

Expand Down Expand Up @@ -225,45 +227,25 @@ def validate_member_code(
if not member_code:
return None

member_code_valid = self.member_code_valid(member_code)
if member_code_valid and member_code_override:
# case where a user corrects their code but ticks the box anyway
# checkbox doesn't need to be ticked, so correct it quietly and continue
self.cleaned_data["member_code_override"] = False
self.set_display_member_code_override(visible=False)
elif not member_code_valid:
# check code validity
# grace period: 90 days before and after
try:
member_code_is_valid = member_code_valid_training(
member_code, date.today(), grace_before=90, grace_after=90
)
if member_code_is_valid and member_code_override:
# case where a user corrects their code but ticks the box anyway
# checkbox doesn't need to be ticked, so correct it quietly and continue
self.cleaned_data["member_code_override"] = False
self.set_display_member_code_override(visible=False)
except MemberCodeValidationError:
self.set_display_member_code_override(visible=True)
if not member_code_override:
# user must either correct the code or tick the override
errors["member_code"] = ValidationError(error_msg)

return errors

def member_code_valid(self, code: str) -> bool:
"""Returns True if the code matches an active Membership with available seats,
including a grace period of 90 days before and after the Membership dates.
"""
try:
# find relevant membership - may raise Membership.DoesNotExist
membership = Membership.objects.get(registration_code=code)
except Membership.DoesNotExist:
return False

# confirm that membership is currently active
# grace period: 90 days before and after
if not membership.active_on_date(date.today(), grace_before=90, grace_after=90):
return False

# confirm that membership has training seats remaining
if (
membership.public_instructor_training_seats_remaining
+ membership.inhouse_instructor_training_seats_remaining
<= 0
):
return False

return True

def clean(self):
super().clean()
errors = dict()
Expand Down
47 changes: 47 additions & 0 deletions amy/extrequests/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
SelfOrganisedSubmission,
WorkshopInquiryRequest,
)
from extrequests.utils import MemberCodeValidationError, member_code_valid_training
from workshops.fields import (
CheckboxSelectMultipleWithOthers,
CurriculumModelMultipleChoiceField,
Expand Down Expand Up @@ -1407,6 +1408,52 @@ class Meta:
"state": forms.RadioSelect(),
}

def __init__(self, *args, **kwargs):
# request is required for ENFORCE_MEMBER_CODES flag
self.request_http = kwargs.pop("request", None)
super().__init__(*args, **kwargs)

def clean(self):
super().clean()

errors = self.validate_member_code(request=self.request_http)
if errors:
raise ValidationError(errors)

@feature_flag_enabled("ENFORCE_MEMBER_CODES")
def validate_member_code(
self, request: HttpRequest
) -> None | dict[str, ValidationError]:
errors = dict()
member_code = self.cleaned_data.get("member_code", "")
member_code_override = self.cleaned_data.get("member_code_override", False)

if not member_code:
return None

try:
member_code_is_valid = member_code_valid_training(
member_code,
self.instance.created_at.date(),
grace_before=90,
grace_after=90,
)
if member_code_is_valid and member_code_override:
# case where a user corrects their code but ticks the box anyway
# checkbox doesn't need to be ticked, so correct it quietly and continue
self.cleaned_data["member_code_override"] = False
except MemberCodeValidationError as e:
if not member_code_override:
# user must either correct the code or tick the override
error_msg = (
"This code is invalid: "
f"{e.message} "
"Tick the checkbox below to ignore this message."
)
errors["member_code"] = ValidationError(error_msg)

return errors


class TrainingRequestsSelectionForm(forms.Form):
trainingrequest_a = forms.ModelChoiceField(
Expand Down
Loading