Skip to content
Merged
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ __pycache__
.coverage
.env
.DS_Store
.envrc
1 change: 1 addition & 0 deletions sponsors/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ class SponsorshipAdmin(admin.ModelAdmin):
"end_date",
"get_contract",
"level_name",
"overlapped_by",
),
},
),
Expand Down
5 changes: 5 additions & 0 deletions sponsors/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -375,11 +375,16 @@ def user_with_previous_sponsors(self):
class SponsorshipReviewAdminForm(forms.ModelForm):
start_date = forms.DateField(widget=AdminDateWidget(), required=False)
end_date = forms.DateField(widget=AdminDateWidget(), required=False)
overlapped_by = forms.ModelChoiceField(queryset=Sponsorship.objects.select_related("sponsor", "package"), required=False)

def __init__(self, *args, **kwargs):
force_required = kwargs.pop("force_required", False)
super().__init__(*args, **kwargs)
if self.instance:
qs = self.fields["overlapped_by"].queryset.exclude(id=self.instance.id)
self.fields["overlapped_by"].queryset = qs.filter(sponsor_id=self.instance.sponsor_id)
if force_required:
self.fields.pop("overlapped_by") # overlapped should never be displayed on approval
for field_name in self.fields:
self.fields[field_name].required = True

Expand Down
20 changes: 20 additions & 0 deletions sponsors/migrations/0075_auto_20220303_2023.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Generated by Django 2.2.24 on 2022-03-03 20:23

from django.db import migrations, models
import django.db.models.deletion
import django.db.models.manager


class Migration(migrations.Migration):

dependencies = [
('sponsors', '0074_auto_20220211_1659'),
]

operations = [
migrations.AddField(
model_name='sponsorship',
name='overlapped_by',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='sponsors.Sponsorship'),
),
]
6 changes: 4 additions & 2 deletions sponsors/models/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@ def visible_to(self, user):
def finalized(self):
return self.filter(status=self.model.FINALIZED)

def active_on_date(self, ref_date):
return self.filter(start_date__lte=ref_date, end_date__gte=ref_date)

def enabled(self):
"""Sponsorship which are finalized and enabled"""
today = timezone.now().date()
qs = self.finalized()
return qs.filter(start_date__lte=today, end_date__gte=today)
return self.finalized().active_on_date(today).exclude(overlapped_by__isnull=False)

def with_logo_placement(self, logo_place=None, publisher=None):
from sponsors.models import LogoPlacement, SponsorBenefit
Expand Down
1 change: 1 addition & 0 deletions sponsors/models/sponsorship.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ class Sponsorship(models.Model):
"name")
package = models.ForeignKey(SponsorshipPackage, null=True, on_delete=models.SET_NULL)
sponsorship_fee = models.PositiveIntegerField(null=True, blank=True)
overlapped_by = models.ForeignKey("self", null=True, on_delete=models.SET_NULL)

assets = GenericRelation(GenericAsset)

Expand Down
3 changes: 3 additions & 0 deletions sponsors/tests/baker_recipes.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@

today = date.today()
two_days = timedelta(days=2)
thirty_days = timedelta(days=30)

empty_contract = Recipe(
Contract,
sponsorship__sponsor__name="Sponsor",
sponsorship__start_date=today,
sponsorship__end_date=today + thirty_days,
benefits_list="",
legal_clauses="",
)
Expand All @@ -20,6 +22,7 @@
Contract,
sponsorship__sponsor__name="Awaiting Sponsor",
sponsorship__start_date=today,
sponsorship__end_date=today + thirty_days,
benefits_list="- benefit 1",
legal_clauses="",
status=Contract.AWAITING_SIGNATURE,
Expand Down
8 changes: 8 additions & 0 deletions sponsors/tests/test_managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ def test_enabled_sponsorships(self):
start_date=today - 2 * two_days,
end_date=today - two_days
)
# shouldn't list overlapped sponsorships
baker.make(
Sponsorship,
status=Sponsorship.FINALIZED,
start_date=today - two_days,
end_date=today + two_days,
overlapped_by=enabled,
)

qs = Sponsorship.objects.enabled()

Expand Down
61 changes: 59 additions & 2 deletions sponsors/tests/test_use_cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

from sponsors import use_cases
from sponsors.notifications import *
from sponsors.models import Sponsorship, Contract, SponsorEmailNotificationTemplate
from sponsors.models import Sponsorship, Contract, SponsorEmailNotificationTemplate, Sponsor


class CreateSponsorshipApplicationUseCaseTests(TestCase):
Expand Down Expand Up @@ -220,7 +220,7 @@ def test_execute_and_update_database_object(self):
self.assertEqual(b"Contract content", self.contract.signed_document.read())
self.assertEqual(f"{Contract.SIGNED_PDF_DIR}1234.txt", self.contract.signed_document.name)

def test_build_use_case_with_default_notificationss(self):
def test_build_use_case_with_default_notifications(self):
uc = use_cases.ExecuteExistingContractUseCase.build()
self.assertEqual(len(uc.notifications), 2)
self.assertIsInstance(
Expand All @@ -230,6 +230,63 @@ def test_build_use_case_with_default_notificationss(self):
uc.notifications[1], RefreshSponsorshipsCache,
)

def test_execute_contract_flag_overlapping_sponsorships(self):
sponsorship = self.contract.sponsorship
self.use_case.execute(self.contract, self.file)
self.contract.refresh_from_db()
recent_contract = baker.make_recipe(
"sponsors.tests.empty_contract",
status=Contract.DRAFT,
sponsorship__sponsor=sponsorship.sponsor,
sponsorship__start_date=sponsorship.start_date + timedelta(days=5),
sponsorship__end_date=sponsorship.end_date + timedelta(days=5),
)

self.use_case.execute(recent_contract, self.file)
recent_contract.refresh_from_db()
sponsorship.refresh_from_db()

self.assertEqual(recent_contract.status, Contract.EXECUTED)
self.assertEqual(sponsorship.overlapped_by, recent_contract.sponsorship)

def test_execute_contract_do_not_flag_overlap_if_no_date_range_conflict(self):
sponsorship = self.contract.sponsorship
self.use_case.execute(self.contract, self.file)
self.contract.refresh_from_db()
recent_contract = baker.make_recipe(
"sponsors.tests.empty_contract",
status=Contract.DRAFT,
sponsorship__sponsor=sponsorship.sponsor,
sponsorship__start_date=sponsorship.end_date + timedelta(days=1),
sponsorship__end_date=sponsorship.end_date + timedelta(days=5),
)

self.use_case.execute(recent_contract, self.file)
recent_contract.refresh_from_db()
sponsorship.refresh_from_db()

self.assertEqual(recent_contract.status, Contract.EXECUTED)
self.assertIsNone(sponsorship.overlapped_by)

def test_execute_contract_do_not_flag_overlap_if_from_other_sponsor(self):
sponsorship = self.contract.sponsorship
self.use_case.execute(self.contract, self.file)
self.contract.refresh_from_db()
recent_contract = baker.make_recipe(
"sponsors.tests.empty_contract",
status=Contract.DRAFT,
sponsorship__sponsor=baker.make(Sponsor),
sponsorship__start_date=sponsorship.start_date + timedelta(days=5),
sponsorship__end_date=sponsorship.end_date + timedelta(days=5),
)

self.use_case.execute(recent_contract, self.file)
recent_contract.refresh_from_db()
sponsorship.refresh_from_db()

self.assertEqual(recent_contract.status, Contract.EXECUTED)
self.assertIsNone(sponsorship.overlapped_by)


class NullifyContractUseCaseTests(TestCase):
def setUp(self):
Expand Down
6 changes: 6 additions & 0 deletions sponsors/use_cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ class ExecuteExistingContractUseCase(BaseUseCaseWithNotifications):
def execute(self, contract, contract_file, **kwargs):
contract.signed_document = contract_file
contract.execute(force=self.force_execute)
overlapping_sponsorship = Sponsorship.objects.filter(
sponsor=contract.sponsorship.sponsor,
).exclude(
id=contract.sponsorship.id
).enabled().active_on_date(contract.sponsorship.start_date)
overlapping_sponsorship.update(overlapped_by=contract.sponsorship)
self.notify(
request=kwargs.get("request"),
contract=contract,
Expand Down