Skip to content

Commit

Permalink
sponsorship: allow admins to temporarily unlock finalized sponsorship…
Browse files Browse the repository at this point in the history
…s for editing (#2249)
  • Loading branch information
ewdurbin committed Feb 16, 2023
1 parent 240865e commit 2ce2442
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 3 deletions.
18 changes: 17 additions & 1 deletion sponsors/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,7 @@ def get_readonly_fields(self, request, obj):
"get_custom_benefits_removed_by_user",
]

if obj and obj.status != Sponsorship.APPLIED:
if obj and not obj.open_for_editing:
extra = ["start_date", "end_date", "package", "level_name", "sponsorship_fee"]
readonly_fields.extend(extra)

Expand Down Expand Up @@ -554,6 +554,16 @@ def get_urls(self):
self.admin_site.admin_view(self.list_uploaded_assets_view),
name=f"{base_name}_list_uploaded_assets",
),
path(
"<int:pk>/unlock",
self.admin_site.admin_view(self.unlock_view),
name=f"{base_name}_unlock",
),
path(
"<int:pk>/lock",
self.admin_site.admin_view(self.lock_view),
name=f"{base_name}_lock",
),
]
return my_urls + urls

Expand Down Expand Up @@ -677,6 +687,12 @@ def approve_signed_sponsorship_view(self, request, pk):
def list_uploaded_assets_view(self, request, pk):
return views_admin.list_uploaded_assets(self, request, pk)

def unlock_view(self, request, pk):
return views_admin.unlock_view(self, request, pk)

def lock_view(self, request, pk):
return views_admin.lock_view(self, request, pk)


@admin.register(SponsorshipCurrentYear)
class SponsorshipCurrentYearAdmin(admin.ModelAdmin):
Expand Down
32 changes: 32 additions & 0 deletions sponsors/migrations/0094_sponsorship_locked.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Generated by Django 2.2.24 on 2023-02-16 13:55

from django.db import migrations, models

from sponsors.models.sponsorship import Sponsorship as _Sponsorship

def forwards_func(apps, schema_editor):
Sponsorship = apps.get_model('sponsors', 'Sponsorship')
db_alias = schema_editor.connection.alias

for sponsorship in Sponsorship.objects.all():
sponsorship.locked = not (sponsorship.status == _Sponsorship.APPLIED)
sponsorship.save()

def reverse_func(apps, schema_editor):
pass


class Migration(migrations.Migration):

dependencies = [
('sponsors', '0093_auto_20230214_2113'),
]

operations = [
migrations.AddField(
model_name='sponsorship',
name='locked',
field=models.BooleanField(default=False),
),
migrations.RunPython(forwards_func, reverse_func)
]
1 change: 1 addition & 0 deletions sponsors/models/contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ def execute(self, commit=True, force=False):

self.status = self.EXECUTED
self.sponsorship.status = Sponsorship.FINALIZED
self.sponsorship.locked = True
self.sponsorship.finalized_on = timezone.now().date()
if commit:
self.sponsorship.save()
Expand Down
15 changes: 14 additions & 1 deletion sponsors/models/sponsorship.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ class Sponsorship(models.Model):
status = models.CharField(
max_length=20, choices=STATUS_CHOICES, default=APPLIED, db_index=True
)
locked = models.BooleanField(default=False)

start_date = models.DateField(null=True, blank=True)
end_date = models.DateField(null=True, blank=True)
Expand Down Expand Up @@ -211,6 +212,12 @@ def __str__(self):
repr += f" [{start} - {end}]"
return repr

def save(self, *args, **kwargs):
if "locked" not in kwargs.get("update_fields", []):
if self.status != self.APPLIED:
self.locked = True
return super().save(*args, **kwargs)

@classmethod
@transaction.atomic
def new(cls, sponsor, benefits, package=None, submited_by=None):
Expand Down Expand Up @@ -287,6 +294,7 @@ def reject(self):
msg = f"Can't reject a {self.get_status_display()} sponsorship."
raise InvalidStatusException(msg)
self.status = self.REJECTED
self.locked = True
self.rejected_on = timezone.now().date()

def approve(self, start_date, end_date):
Expand All @@ -297,6 +305,7 @@ def approve(self, start_date, end_date):
msg = f"Start date greater or equal than end date"
raise SponsorshipInvalidDateRangeException(msg)
self.status = self.APPROVED
self.locked = True
self.start_date = start_date
self.end_date = end_date
self.approved_on = timezone.now().date()
Expand All @@ -320,6 +329,10 @@ def rollback_to_editing(self):
self.approved_on = None
self.rejected_on = None

@property
def unlocked(self):
return not self.locked

@property
def verified_emails(self):
emails = [self.submited_by.email]
Expand Down Expand Up @@ -353,7 +366,7 @@ def added_benefits(self):

@property
def open_for_editing(self):
return self.status == self.APPLIED
return (self.status == self.APPLIED) or (self.unlocked)

@property
def next_status(self):
Expand Down
38 changes: 38 additions & 0 deletions sponsors/views_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,44 @@ def rollback_to_editing_view(ModelAdmin, request, pk):
)


def unlock_view(ModelAdmin, request, pk):
sponsorship = get_object_or_404(ModelAdmin.get_queryset(request), pk=pk)

if request.method.upper() == "POST" and request.POST.get("confirm") == "yes":
try:
sponsorship.locked = False
sponsorship.save(update_fields=['locked'])
ModelAdmin.message_user(
request, "Sponsorship is now unlocked!", messages.SUCCESS
)
except InvalidStatusException as e:
ModelAdmin.message_user(request, str(e), messages.ERROR)

redirect_url = reverse(
"admin:sponsors_sponsorship_change", args=[sponsorship.pk]
)
return redirect(redirect_url)

context = {"sponsorship": sponsorship}
return render(
request,
"sponsors/admin/unlock.html",
context=context,
)


def lock_view(ModelAdmin, request, pk):
sponsorship = get_object_or_404(ModelAdmin.get_queryset(request), pk=pk)

sponsorship.locked = True
sponsorship.save()

redirect_url = reverse(
"admin:sponsors_sponsorship_change", args=[sponsorship.pk]
)
return redirect(redirect_url)


def execute_contract_view(ModelAdmin, request, pk):
contract = get_object_or_404(ModelAdmin.get_queryset(request), pk=pk)

Expand Down
14 changes: 13 additions & 1 deletion templates/sponsors/admin/sponsorship_change_form.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{% extends "admin/change_form.html" %}
{% load i18n admin_urls %}
{% load i18n admin_urls admin_modify %}

{% block object-tools-items %}
{% with original as sp %}
Expand Down Expand Up @@ -39,6 +39,18 @@
<a href="{% url 'admin:sponsors_sponsorship_list_uploaded_assets' sp.pk %}">Uploaded Assets</a>
</li>

{% if sp.unlocked and sp.status != sp.APPLIED %}
<li>
<a href="{% url 'admin:sponsors_sponsorship_lock' sp.pk %}" style="background: #70bf2b">Lock</a>
</li>
{% endif %}

{% if sp.locked and sp.status == sp.FINALIZED %}
<li>
<a href="{% url 'admin:sponsors_sponsorship_unlock' sp.pk %}">Unlock</a>
</li>
{% endif %}

{% endwith %}

{{ block.super }}
Expand Down
35 changes: 35 additions & 0 deletions templates/sponsors/admin/unlock.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{% extends 'admin/base_site.html' %}
{% load i18n static sponsors %}

{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/forms.css" %}">{% endblock %}

{% block title %}Unlock Finalized Sponsorship {{ sponsorship }} | python.org{% endblock %}

{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a> &gt
<a href="{% url 'admin:app_list' app_label='sponsors' %}">{% trans 'Sponsors' %}</a> &gt
<a href="{% url 'admin:sponsors_sponsorship_changelist' %}">{% trans 'Sponsorship' %}</a> &gt
<a href="{% url 'admin:sponsors_sponsorship_change' sponsorship.pk %}">{{ sponsorship }}</a> &gt
{% trans 'Unlock Finalized Sponsorship' %}
</div>
{% endblock %}

{% block content %}
<h1>Unlock Finalized Sponsorship</h1>
<p>Please review the sponsorship application and click in the Unlock button if you want to proceed.</p>
<div id="content-main">
<form action="" method="post" id="new_psf_board_meeting_form">
{% csrf_token %}

<pre>{% full_sponsorship sponsorship display_fee=True %}</pre>

<input name="confirm" value="yes" style="display:none">

<div class="submit-row">
<input type="submit" value="Unlock" class="default">
</div>

</form>
<div>
</div>{% endblock %}

0 comments on commit 2ce2442

Please sign in to comment.