Skip to content

Commit

Permalink
Allow sponsorship payment terms(due date) to be changed
Browse files Browse the repository at this point in the history
Allow the due date to be configured on an individual sponsorship level,
both specifying a number-of-days-to-pay and a hard final date.

Closes #170
  • Loading branch information
ssinger authored and mhagander committed Nov 24, 2024
1 parent 2958a2b commit ee701d6
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 16 deletions.
11 changes: 11 additions & 0 deletions docs/confreg/sponsors.md
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,17 @@ Instant buy available
administrator must manually move the sponsorship forward in the
process once a signed contract is received.

Number of days until payment is due
: The number of days until a sponsorship invoice is due. This defaults to 30
to give net 30 terms. The actual due date for an invoice might be restricted
by either *The Date the payment is due by* field.

The Date the payment is due by
: The latest date that *Number of days until payment is due* applies until.
Invoices that would be due after this date are instead due at this time or
now(if this time is in the past). This defaults to 5 days before the conference
starts.

Payment methods for generated invoices
: Which payment methods will be listed on the generated
invoices. Typically the instant buy levels support payment by
Expand Down
8 changes: 6 additions & 2 deletions postgresqleu/confsponsor/backendforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from django.conf import settings

from collections import OrderedDict
import datetime
import json

from postgresqleu.util.db import exec_to_scalar
Expand Down Expand Up @@ -309,11 +310,12 @@ class BackendSponsorshipLevelForm(BackendForm):
})
allow_copy_previous = True
auto_cascade_delete_to = ['sponsorshiplevel_paymentmethods', 'sponsorshipbenefit']
exclude_date_validators = ['paymentdueby', ]

class Meta:
model = SponsorshipLevel
fields = ['levelname', 'urlname', 'levelcost', 'available', 'public', 'maxnumber', 'instantbuy',
'paymentmethods', 'invoiceextradescription', 'contract', 'canbuyvoucher', 'canbuydiscountcode']
'paymentdays', 'paymentdueby', 'paymentmethods', 'invoiceextradescription', 'contract', 'canbuyvoucher', 'canbuydiscountcode']
widgets = {
'paymentmethods': django.forms.CheckboxSelectMultiple,
}
Expand All @@ -327,7 +329,7 @@ class Meta:
{
'id': 'contract',
'legend': 'Contract information',
'fields': ['instantbuy', 'contract', ],
'fields': ['instantbuy', 'contract', 'paymentdays', 'paymentdueby'],
},
{
'id': 'payment',
Expand All @@ -344,6 +346,8 @@ class Meta:
def fix_fields(self):
self.fields['contract'].queryset = SponsorshipContract.objects.filter(conference=self.conference)
self.fields['paymentmethods'].label_from_instance = lambda x: "{0}{1}".format(x.internaldescription, x.active and " " or " (INACTIVE)")
if not self.initial.get('paymentdueby', None):
self.initial['paymentdueby'] = self.conference.startdate - datetime.timedelta(days=5)

def clean(self):
cleaned_data = super(BackendSponsorshipLevelForm, self).clean()
Expand Down
23 changes: 9 additions & 14 deletions postgresqleu/confsponsor/invoicehandler.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from django.utils import timezone
from django.conf import settings

from datetime import datetime, timedelta
from datetime import datetime, timedelta, time
import base64
import os

Expand Down Expand Up @@ -172,23 +171,19 @@ def create_sponsor_invoice(user, sponsor, override_duedate=None):
level = sponsor.level

invoicerows, reverse_vat = _invoicerows_for_sponsor(sponsor)
daystopay = timedelta(days=level.paymentdays)

if override_duedate:
duedate = override_duedate
elif conference.startdate < today_conference() + timedelta(days=5):
# If conference happens in the next 5 days, invoice is due immediately
elif level.paymentdueby < today_conference():
# The payment deadline has passed. Invoices are due immediately
duedate = timezone.now()
elif conference.startdate < today_conference() + timedelta(days=30):
# Less than 30 days before the conference, set the due date to
# 5 days before the conference
duedate = timezone.make_aware(datetime.combine(
conference.startdate - timedelta(days=5),
timezone.now().time()
))
elif level.paymentdueby < today_conference() + daystopay:
# The payment terms go beyond the payment deadline. The payment is due
# at the deadline
duedate = datetime.combine(level.paymentdueby, time(0, 0, 0, 0), conference.tzobj)
else:
# More than 30 days before the conference, set the due date
# to 30 days from now.
duedate = timezone.now() + timedelta(days=30)
duedate = timezone.now() + daystopay

manager = InvoiceManager()
processor = invoicemodels.InvoiceProcessor.objects.get(processorname="confsponsor processor")
Expand Down
35 changes: 35 additions & 0 deletions postgresqleu/confsponsor/migrations/0033_payment_terms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Generated by Django 3.2.22 on 2024-11-15 22:27

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('confsponsor', '0032_sponsorshipbenefit_include_in_data'),
]

operations = [
migrations.AddField(
model_name='sponsorshiplevel',
name='paymentdueby',
field=models.DateField(blank=True, help_text='The last acceptable due date for payments. If payment terms go beyond this date then the invoice is due at this date', null=True, verbose_name='The latest date the payment is due by'),
),
migrations.RunSQL(
"""
update confsponsor_sponsorshiplevel set paymentdueby=conf.startdate-'5 days'::interval from confreg_conference conf
where conf.id = conference_id
""",
""
),
migrations.AlterField(
model_name='sponsorshiplevel',
name='paymentdueby',
field=models.DateField(blank=False, help_text='The last acceptable due date for payments. If payment terms go beyond this date then the invoice is due at this date', null=False, verbose_name='The latest date the payment is due by'),
),
migrations.AddField(
model_name='sponsorshiplevel',
name='paymentdays',
field=models.IntegerField(default=30, null=False, verbose_name='Number of days until payment is due'),
),
]
5 changes: 5 additions & 0 deletions postgresqleu/confsponsor/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ class SponsorshipLevel(models.Model):
contract = models.ForeignKey(SponsorshipContract, blank=True, null=True, on_delete=models.CASCADE)
canbuyvoucher = models.BooleanField(null=False, blank=False, default=True, verbose_name="Can buy vouchers")
canbuydiscountcode = models.BooleanField(null=False, blank=False, default=True, verbose_name="Can buy discount codes")
paymentdays = models.IntegerField(null=False, blank=False, default=30, verbose_name="Number of days until payment is due")
paymentdueby = models.DateField(
null=False, blank=False, verbose_name="The latest date the payment is due by",
help_text="The last acceptable due date for payments. If payment terms go beyond this date then the invoice is due at this date",
)

def __str__(self):
return self.levelname
Expand Down

0 comments on commit ee701d6

Please sign in to comment.