Skip to content

Commit be0e8b7

Browse files
authored
Enable admin users to edit benefits from sponsorship application via django admin (#1710)
* New admin form to manage sponsorship's benefits * Can only edit benefits if sponsorship wasn't approved or rejected * black
1 parent b111092 commit be0e8b7

File tree

4 files changed

+158
-10
lines changed

4 files changed

+158
-10
lines changed

sponsors/admin.py

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
LegalClause,
1919
)
2020
from sponsors import use_cases
21-
from sponsors.forms import SponsorshipReviewAdminForm
21+
from sponsors.forms import SponsorshipReviewAdminForm, SponsorBenefitAdminInlineForm
2222
from sponsors.exceptions import SponsorshipInvalidStatusException
2323
from cms.admin import ContentManageableModelAdmin
2424

@@ -94,10 +94,29 @@ class SponsorAdmin(ContentManageableModelAdmin):
9494

9595
class SponsorBenefitInline(admin.TabularInline):
9696
model = SponsorBenefit
97-
readonly_fields = ["name", "benefit_internal_value"]
98-
fields = ["name", "benefit_internal_value"]
97+
form = SponsorBenefitAdminInlineForm
98+
fields = ["sponsorship_benefit", "benefit_internal_value"]
9999
extra = 0
100-
can_delete = False
100+
101+
def has_add_permission(self, request):
102+
# this work around is necessary because the `obj` parameter was added to
103+
# InlineModelAdmin.has_add_permission only in Django 2.1.x and we're using 2.0.x
104+
has_add_permission = super().has_add_permission(request)
105+
match = request.resolver_match
106+
if match.url_name == "sponsors_sponsorship_change":
107+
sponsorship = self.parent_model.objects.get(pk=match.kwargs["object_id"])
108+
has_add_permission = has_add_permission and sponsorship.open_for_editing
109+
return has_add_permission
110+
111+
def get_readonly_fields(self, request, obj=None):
112+
if obj and not obj.open_for_editing:
113+
return ["sponsorship_benefit", "benefit_internal_value"]
114+
return []
115+
116+
def has_delete_permission(self, request, obj=None):
117+
if not obj:
118+
return True
119+
return obj.open_for_editing
101120

102121

103122
@admin.register(Sponsorship)
@@ -265,17 +284,19 @@ def get_sponsor_primary_phone(self, obj):
265284

266285
def get_sponsor_mailing_address(self, obj):
267286
sponsor = obj.sponsor
268-
city_row = f'{sponsor.city} - {sponsor.get_country_display()} ({sponsor.country})'
287+
city_row = (
288+
f"{sponsor.city} - {sponsor.get_country_display()} ({sponsor.country})"
289+
)
269290
if sponsor.state:
270-
city_row = f'{sponsor.city} - {sponsor.state} - {sponsor.get_country_display()} ({sponsor.country})'
291+
city_row = f"{sponsor.city} - {sponsor.state} - {sponsor.get_country_display()} ({sponsor.country})"
271292

272293
mail_row = sponsor.mailing_address_line_1
273294
if sponsor.mailing_address_line_2:
274-
mail_row += f' - {sponsor.mailing_address_line_2}'
295+
mail_row += f" - {sponsor.mailing_address_line_2}"
275296

276-
html = f'<p>{city_row}</p>'
277-
html += f'<p>{mail_row}</p>'
278-
html += f'<p>{sponsor.postal_code}</p>'
297+
html = f"<p>{city_row}</p>"
298+
html += f"<p>{mail_row}</p>"
299+
html += f"<p>{sponsor.postal_code}</p>"
279300
return mark_safe(html)
280301

281302
get_sponsor_mailing_address.short_description = "Mailing/Billing Address"

sponsors/forms.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
Sponsor,
1414
SponsorContact,
1515
Sponsorship,
16+
SponsorBenefit,
1617
)
1718

1819

@@ -331,3 +332,40 @@ def clean(self):
331332
raise forms.ValidationError("End date must be greater than start date")
332333

333334
return cleaned_data
335+
336+
337+
class SponsorBenefitAdminInlineForm(forms.ModelForm):
338+
sponsorship_benefit = forms.ModelChoiceField(
339+
queryset=SponsorshipBenefit.objects.select_related("program"),
340+
)
341+
342+
def __init__(self, *args, **kwargs):
343+
super().__init__(*args, **kwargs)
344+
if "benefit_internal_value" in self.fields:
345+
self.fields["benefit_internal_value"].required = True
346+
347+
class Meta:
348+
model = SponsorBenefit
349+
fields = ["sponsorship_benefit", "sponsorship", "benefit_internal_value"]
350+
351+
def save(self, commit=True):
352+
sponsorship = self.cleaned_data["sponsorship"]
353+
benefit = self.cleaned_data["sponsorship_benefit"]
354+
value = self.cleaned_data["benefit_internal_value"]
355+
356+
if not (self.instance and self.instance.pk): # new benefit
357+
self.instance = SponsorBenefit(sponsorship=sponsorship)
358+
else:
359+
self.instance.refresh_from_db()
360+
361+
self.instance.benefit_internal_value = value
362+
if benefit.pk != self.instance.sponsorship_benefit_id:
363+
self.instance.sponsorship_benefit = benefit
364+
self.instance.name = benefit.name
365+
self.instance.description = benefit.description
366+
self.instance.program = benefit.program
367+
368+
if commit:
369+
self.instance.save()
370+
371+
return self.instance

sponsors/models.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,10 @@ def package_benefits(self):
361361
def added_benefits(self):
362362
return self.benefits.filter(added_by_user=True)
363363

364+
@property
365+
def open_for_editing(self):
366+
return self.status == self.APPLIED
367+
364368
@property
365369
def next_status(self):
366370
states_map = {

sponsors/tests/test_forms.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
SponsorContact,
1010
Sponsor,
1111
SponsorContactFormSet,
12+
SponsorBenefitAdminInlineForm,
13+
SponsorBenefit,
14+
Sponsorship,
1215
)
1316
from sponsors.models import SponsorshipBenefit, SponsorContact
1417
from .utils import get_static_image_file_as_upload
@@ -355,3 +358,85 @@ def test_invalidate_formset_if_no_form(self):
355358
self.data["contact-TOTAL_FORMS"] = 0
356359
formset = SponsorContactFormSet(self.data, prefix="contact")
357360
self.assertFalse(formset.is_valid())
361+
362+
363+
class SponsorBenefitAdminInlineFormTests(TestCase):
364+
def setUp(self):
365+
self.benefit = baker.make(SponsorshipBenefit)
366+
self.sponsorship = baker.make(Sponsorship)
367+
self.data = {
368+
"sponsorship_benefit": self.benefit.pk,
369+
"sponsorship": self.sponsorship.pk,
370+
"benefit_internal_value": 200,
371+
}
372+
373+
def test_required_fields_for_new_sponsor_benefit(self):
374+
required_fields = [
375+
"sponsorship_benefit",
376+
"sponsorship",
377+
"benefit_internal_value",
378+
]
379+
380+
form = SponsorBenefitAdminInlineForm({})
381+
self.assertFalse(form.is_valid())
382+
383+
for required in required_fields:
384+
self.assertIn(required, form.errors)
385+
self.assertEqual(len(required_fields), len(form.errors))
386+
387+
def test_create_new_sponsor_benefit_for_sponsorship(self):
388+
form = SponsorBenefitAdminInlineForm(data=self.data)
389+
self.assertTrue(form.is_valid(), form.errors)
390+
391+
sponsor_benefit = form.save()
392+
sponsor_benefit.refresh_from_db()
393+
394+
self.assertEqual(sponsor_benefit.sponsorship, self.sponsorship)
395+
self.assertEqual(sponsor_benefit.sponsorship_benefit, self.benefit)
396+
self.assertEqual(sponsor_benefit.name, self.benefit.name)
397+
self.assertEqual(sponsor_benefit.description, self.benefit.description)
398+
self.assertEqual(sponsor_benefit.program, self.benefit.program)
399+
self.assertEqual(sponsor_benefit.benefit_internal_value, 200)
400+
401+
def test_update_existing_sponsor_benefit(self):
402+
sponsor_benefit = baker.make(
403+
SponsorBenefit,
404+
sponsorship=self.sponsorship,
405+
sponsorship_benefit=self.benefit,
406+
)
407+
new_benefit = baker.make(SponsorshipBenefit)
408+
self.data["sponsorship_benefit"] = new_benefit.pk
409+
410+
form = SponsorBenefitAdminInlineForm(data=self.data, instance=sponsor_benefit)
411+
self.assertTrue(form.is_valid(), form.errors)
412+
form.save()
413+
sponsor_benefit.refresh_from_db()
414+
415+
self.assertEqual(1, SponsorBenefit.objects.count())
416+
self.assertEqual(sponsor_benefit.sponsorship, self.sponsorship)
417+
self.assertEqual(sponsor_benefit.sponsorship_benefit, new_benefit)
418+
self.assertEqual(sponsor_benefit.name, new_benefit.name)
419+
self.assertEqual(sponsor_benefit.description, new_benefit.description)
420+
self.assertEqual(sponsor_benefit.program, new_benefit.program)
421+
self.assertEqual(sponsor_benefit.benefit_internal_value, 200)
422+
423+
def test_do_not_update_sponsorship_if_it_doesn_change(self):
424+
sponsor_benefit = baker.make(
425+
SponsorBenefit,
426+
sponsorship=self.sponsorship,
427+
sponsorship_benefit=self.benefit,
428+
)
429+
new_benefit = baker.make(SponsorshipBenefit)
430+
431+
form = SponsorBenefitAdminInlineForm(data=self.data, instance=sponsor_benefit)
432+
self.assertTrue(form.is_valid(), form.errors)
433+
form.save()
434+
sponsor_benefit.refresh_from_db()
435+
self.benefit.name = "new name"
436+
self.benefit.save()
437+
438+
self.assertEqual(1, SponsorBenefit.objects.count())
439+
self.assertEqual(sponsor_benefit.sponsorship, self.sponsorship)
440+
self.assertEqual(sponsor_benefit.sponsorship_benefit, self.benefit)
441+
self.assertNotEqual(sponsor_benefit.name, "new name")
442+
self.assertEqual(sponsor_benefit.benefit_internal_value, 200)

0 commit comments

Comments
 (0)