Skip to content

Commit 361789f

Browse files
committed
Replace "instantbuy" setting on sponsorship levels with contract levels
This allows explicit specification of "No contract, Click-through contract or Full contract", instead of the definitely-hard-to-grok combination of whether a contract existed and if the checkbox was set or not.
1 parent c43773a commit 361789f

File tree

10 files changed

+115
-55
lines changed

10 files changed

+115
-55
lines changed

postgresqleu/confsponsor/backendforms.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ def __init__(self, conference, *args, **kwargs):
304304

305305
class BackendSponsorshipLevelForm(BackendForm):
306306
helplink = 'sponsors#level'
307-
list_fields = ['levelname', 'levelcost', 'available', 'public', ]
307+
list_fields = ['levelname', 'levelcost', 'available', 'public', 'contractlevel']
308308
linked_objects = OrderedDict({
309309
'benefit': BackendSponsorshipLevelBenefitManager(),
310310
})
@@ -315,7 +315,7 @@ class BackendSponsorshipLevelForm(BackendForm):
315315

316316
class Meta:
317317
model = SponsorshipLevel
318-
fields = ['levelname', 'urlname', 'levelcost', 'available', 'public', 'maxnumber', 'instantbuy',
318+
fields = ['levelname', 'urlname', 'levelcost', 'available', 'public', 'maxnumber', 'contractlevel',
319319
'paymentdays', 'paymentdueby', 'paymentmethods', 'invoiceextradescription', 'contract', 'canbuyvoucher', 'canbuydiscountcode']
320320
widgets = {
321321
'paymentmethods': django.forms.CheckboxSelectMultiple,
@@ -330,7 +330,7 @@ class Meta:
330330
{
331331
'id': 'contract',
332332
'legend': 'Contract information',
333-
'fields': ['instantbuy', 'contract', 'paymentdays', 'paymentdueby'],
333+
'fields': ['contractlevel', 'contract', 'paymentdays', 'paymentdueby'],
334334
},
335335
{
336336
'id': 'payment',
@@ -353,13 +353,19 @@ def fix_fields(self):
353353
def clean(self):
354354
cleaned_data = super(BackendSponsorshipLevelForm, self).clean()
355355

356-
if not (cleaned_data.get('instantbuy', False) or cleaned_data['contract']):
357-
self.add_error('instantbuy', 'Sponsorship level must either be instant signup or have a contract')
358-
self.add_error('contract', 'Sponsorship level must either be instant signup or have a contract')
359-
360-
if int(cleaned_data['levelcost'] == 0) and cleaned_data.get('instantbuy', False):
361-
self.add_error('levelcost', 'Sponsorships with zero cost can not be instant signup')
362-
self.add_error('instantbuy', 'Sponsorships with zero cost can not be instant signup')
356+
if cleaned_data['contractlevel'] == 0:
357+
if cleaned_data['contract']:
358+
self.add_error('contract', 'Contracts cannot be specified when contract level is No contract')
359+
if cleaned_data['levelcost'] == 0:
360+
self.add_error('levelcost', 'Cost cannot be zero when contract level is No contract')
361+
elif cleaned_data['contractlevel'] == 1:
362+
if not cleaned_data['contract']:
363+
self.add_error('contract', 'Contract is required when contract level is Click-through')
364+
if cleaned_data['levelcost'] == 0:
365+
self.add_error('levelcost', 'Cost cannot be zero when contract level is Click-through')
366+
else:
367+
if not cleaned_data['contract']:
368+
self.add_error('contract', 'Contract is required when contract level is Full')
363369

364370
return cleaned_data
365371

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Generated by Django 4.2.11 on 2025-02-26 20:22
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('confsponsor', '0033_payment_terms'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='sponsorshiplevel',
15+
name='contractlevel',
16+
field=models.IntegerField(choices=[(0, 'No contract'), (1, 'Click-through contract'), (2, 'Full contract')], default=0, verbose_name='Contract level'),
17+
),
18+
migrations.RunSQL("""
19+
UPDATE confsponsor_sponsorshiplevel SET contractlevel=CASE
20+
WHEN instantbuy AND contract_id IS NOT NULL THEN 1
21+
WHEN instantbuy AND contract_id IS NULL THEN 0
22+
ELSE 2 END""",
23+
"""
24+
UPDATE confsponsor_sponsorshiplevel SET instantbuy=(contractlevel < 2)"""),
25+
migrations.RemoveField(
26+
model_name='sponsorshiplevel',
27+
name='instantbuy',
28+
),
29+
]

postgresqleu/confsponsor/models.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@
2323
(2, 'Company is from outside EU'),
2424
)
2525

26+
CONTRACT_LEVEL_CHOICES = (
27+
(0, 'No contract'),
28+
(1, 'Click-through contract'),
29+
(2, 'Full contract'),
30+
)
31+
CONTRACT_LEVEL_MAP = dict(CONTRACT_LEVEL_CHOICES)
32+
2633

2734
class SponsorshipContract(models.Model):
2835
conference = models.ForeignKey(Conference, null=False, blank=False, on_delete=models.CASCADE)
@@ -47,7 +54,8 @@ class SponsorshipLevel(models.Model):
4754
public = models.BooleanField(null=False, blank=False, default=True, verbose_name="Publicly visible",
4855
help_text="If unchecked the sponsorship level will be treated as internal, for example for testing")
4956
maxnumber = models.IntegerField(null=False, blank=False, default=0, verbose_name="Maximum number of sponsors")
50-
instantbuy = models.BooleanField(null=False, blank=False, default=False, verbose_name="Instant buy available")
57+
contractlevel = models.IntegerField(null=False, blank=False, default=0, verbose_name="Contract level",
58+
choices=CONTRACT_LEVEL_CHOICES)
5159
paymentmethods = models.ManyToManyField(InvoicePaymentMethod, blank=False, verbose_name="Payment methods for generated invoices")
5260
invoiceextradescription = models.TextField(
5361
blank=True, null=False, verbose_name="Invoice extra description",
@@ -90,6 +98,13 @@ def can_signup(self):
9098
return True
9199
return False
92100

101+
@cached_property
102+
def contractlevel_name(self):
103+
return CONTRACT_LEVEL_MAP[self.contractlevel]
104+
105+
def _display_contractlevel(self, cache):
106+
return self.contractlevel_name
107+
93108

94109
class SponsorshipBenefit(models.Model):
95110
level = models.ForeignKey(SponsorshipLevel, null=False, blank=False, on_delete=models.CASCADE)

postgresqleu/confsponsor/util.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ def sponsorleveldata(conference):
5353
'cost': format_currency(lvl.levelcost),
5454
'available': lvl.available,
5555
'maxnumber': lvl.maxnumber,
56-
'instantbuy': lvl.instantbuy,
56+
'instantbuy': lvl.contractlevel == 1 or (lvl.contractlevel == 0 and cost > 0), # legacy
57+
'contractlevel': lvl.contractlevel_name,
5758
'benefits': [dict(_get_benefit_data(b)) for b in lvl.sponsorshipbenefit_set.all()
5859
],
5960
}

postgresqleu/confsponsor/views.py

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ def sponsor_contractview(request, sponsorid):
222222
# Should not happen
223223
raise Http404("No contract at this level")
224224

225-
if sponsor.level.instantbuy:
225+
if sponsor.level.contractlevel == 1:
226226
# Click-through contract
227227

228228
resp = HttpResponse(content_type='application/pdf')
@@ -375,6 +375,9 @@ def _generate_and_send_sponsor_contract(sponsor):
375375
conference = sponsor.conference
376376
level = sponsor.level
377377

378+
if level.contractlevel == 0:
379+
return
380+
378381
pdf = fill_pdf_fields(
379382
level.contract.contractpdf,
380383
get_pdf_fields_for_conference(conference, sponsor),
@@ -386,7 +389,7 @@ def _generate_and_send_sponsor_contract(sponsor):
386389
send_sponsor_manager_email(
387390
sponsor,
388391
'Your contract for {}'.format(conference.conferencename),
389-
'confsponsor/mail/{}.txt'.format('sponsor_contract_instant' if level.instantbuy else 'sponsor_contract_manual'),
392+
'confsponsor/mail/{}.txt'.format('sponsor_contract_instant' if level.contractlevel == 1 else 'sponsor_contract_manual'),
390393
{
391394
'conference': conference,
392395
'sponsor': sponsor,
@@ -452,7 +455,7 @@ def sponsor_signup(request, confurlname, levelurlname):
452455
# Stage 2 = contract choice. When submitted, sign up.
453456
# If there is no contract needed on this level, or there is no choice
454457
# of contract because only one available, we bypass stage 1.
455-
if stage == '1' and (level.instantbuy or not conference.contractprovider or not conference.manualcontracts):
458+
if stage == '1' and (level.contractlevel != 2 or not conference.contractprovider or not conference.manualcontracts):
456459
stage = '2'
457460

458461
def _render_contract_choices():
@@ -485,7 +488,6 @@ def _render_contract_choices():
485488
'level': level,
486489
'form': form,
487490
'noform': 1,
488-
'needscontract': not (level.instantbuy or not conference.contractprovider),
489491
'sponsorname': form.cleaned_data['name'],
490492
'vatnumber': form.cleaned_data['vatnumber'] if settings.EU_VAT else None,
491493
'previewaddr': get_sponsor_invoice_address(form.cleaned_data['name'],
@@ -502,7 +504,7 @@ def _render_contract_choices():
502504
# If the Continue editing button is selected we should go back
503505
# to just rendering the normal form. Otherwise, go ahead and create the record.
504506
if request.POST.get('submit', '') != 'Continue editing':
505-
if request.POST.get('contractchoice', '') not in ('0', '1') and not level.instantbuy:
507+
if request.POST.get('contractchoice', '') not in ('0', '1') and level.contractlevel == 2:
506508
return _render_contract_choices()
507509

508510
social = {
@@ -520,7 +522,7 @@ def _render_contract_choices():
520522
level=level,
521523
social=social,
522524
invoiceaddr=form.cleaned_data['address'],
523-
signmethod=1 if request.POST.get('contractchoice', '') == '1' or not conference.contractprovider or level.instantbuy else 0,
525+
signmethod=1 if request.POST.get('contractchoice', '') == '1' or not conference.contractprovider or level.contractlevel < 2 else 0,
524526
autoapprovesigned=conference.autocontracts,
525527
)
526528
if settings.EU_VAT:
@@ -534,10 +536,10 @@ def _render_contract_choices():
534536

535537
error = None
536538

537-
if level.instantbuy:
538-
if sponsor.level.contract:
539-
# Instantbuy levels that has a contract should get an implicit contract
540-
# attached to an email.
539+
if level.contractlevel < 2:
540+
# No contract or click-through contract
541+
if level.contractlevel == 1:
542+
# Click-through contract
541543
_generate_and_send_sponsor_contract(sponsor)
542544

543545
mailstr += "Level does not require a signed contract. Verify the details and approve\nthe sponsorship using:\n\n{0}/events/sponsor/admin/{1}/{2}/".format(

template/confsponsor/admin_dashboard.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ <h2>Unconfirmed sponsors</h2>
8686
{%else%}
8787
<span class="label label-success">Invoiced</span>
8888
{%endif%}
89-
{%elif s.level.instantbuy%}
89+
{%elif s.level.contractlevel < 2 %}
9090
<span class="label label-warning" title="Sponsor details for instant buy levels have to be verified before invoice is issued">Pending organizer verification</span>
9191
{%else%}
9292
{%if s.signmethod == 0 and s.contract %}

template/confsponsor/admin_sponsor.html

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,8 @@ <h4>Send new contract</h4>
140140
<p>
141141
This sponsorship is awaiting an <a href="/invoiceadmin/{{sponsor.invoice.pk}}/">invoice</a> to be paid.
142142
</p>
143-
{%if not sponsor.level.instantbuy%}
144-
{%comment%}Instant buy sponsorships should never be manually confirmed{%endcomment%}
143+
{%if sponsor.level.contractlevel == 2 %}
144+
{%comment%}Only full contract sponsorships should be manually confirmed{%endcomment%}
145145
<p>
146146
<b>Iff</b> there is a signed <i>and</i> countersigned contract available
147147
for this sponsor, it can be confirmed before the invoice is paid.
@@ -161,7 +161,7 @@ <h4>Send new contract</h4>
161161
{%else%}
162162
{%comment%}Sponsor has no invoice{%endcomment%}
163163
{%if sponsor.level.levelcost %}
164-
{%if sponsor.level.instantbuy%}
164+
{%if sponsor.level.contractlevel < 2%}
165165
<p>
166166
This sponsorship has not yet been issued an invoice. This is an
167167
"instant buy" level sponsorship, so as soon as the sponsorship
@@ -191,7 +191,7 @@ <h4>Send new contract</h4>
191191
has been received, go ahead and generate the invoice.
192192
</p>
193193
{%endif%}{%comment%}Digital contracts{%endcomment%}
194-
{%endif%}{%comment%}Instant buy{%endcomment%}
194+
{%endif%}{%comment%}contractlevel < 2{%endcomment%}
195195
{%else%}{%comment%}levelcost != 0 {%endcomment%}
196196
<p>
197197
This sponsorship has zero cost, which means payment is handled manually.

template/confsponsor/admin_sponsor_details.html

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,11 @@
3939
<tr>
4040
<th>Contract:</th>
4141
<td>
42-
{%if sponsor.level.instantbuy %}
43-
{%if sponsor.level.contract %}
44-
Click-through contract completed. <form class="inline-block-form" method="post" action="resendcontract/">{% csrf_token %}<input type="submit" class="btn btn-sm btn-default confirm-btn" value="Re-send contract anyway" data-confirm="Are you sure you want to re-send a new contract to this sponsor?{%if sponsor.signmethod == 0%} {{conference.contractprovider.implementation.resendprompt}}{%endif%}"></form>
45-
{%else%}
42+
{%if sponsor.level.contractlevel == 0 %}
4643
No contract needed for this level.
47-
{%endif%}
48-
{%else%}
44+
{%elif sponsor.level.contractlevel == 1 %}
45+
Click-through contract completed. {%if not sponsor.confirmed%}<form class="inline-block-form" method="post" action="resendcontract/">{% csrf_token %}<input type="submit" class="btn btn-sm btn-default confirm-btn" value="Re-send contract anyway" data-confirm="Are you sure you want to re-send a new contract to this sponsor?{%if sponsor.signmethod == 0%} {{conference.contractprovider.implementation.resendprompt}}{%endif%}"></form>{%endif%}
46+
{%else%}{%comment%}Full contract{%endcomment%}
4947
{%if sponsor.signmethod == 0%}
5048
Digital contract.<br/>
5149
{%if sponsor.contract.completed%}Signed ({{sponsor.contract.firstsigned}}) and countersigned ({{sponsor.contract.completed}}).
@@ -63,7 +61,7 @@
6361
<form class="inline-block-form" method="post" action="resendcontract/">{% csrf_token %}<input type="submit" class="btn btn-sm btn-default confirm-btn" value="Re-send contract" data-confirm="Are you sure you want to re-send a new contract to this sponsor?{%if sponsor.signmethod == 0%} {{conference.contractprovider.implementation.resendprompt}}{%endif%}"></form>
6462
{%endif%}
6563
{%endif%}{# can resend #}
66-
{%endif%}{# instant buy #}
64+
{%endif%}{# contractlevel #}
6765
</td>
6866
</tr>
6967
{%if sponsor.invoice%}

template/confsponsor/signupform.html

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,6 @@ <h1>Conference Sponsorship Signup - {{conference}}</h1>
77
form below to initiate your sponsorship!
88
</p>
99

10-
{%if level.contract%}
11-
<p>
12-
Before you complete the signup form, please <em>make sure</em> you have read the
13-
<a href="/events/sponsor/previewcontract/{{level.contract.id}}/">contract</a>, and
14-
agree with the contents in it.
15-
</p>
16-
{%endif%}
17-
1810
{%if form.errors%}
1911
<p>
2012
<b>NOTE!</b> Your submitted form contained errors and has <b>not</b> been saved!
@@ -46,16 +38,31 @@ <h1>Conference Sponsorship Signup - {{conference}}</h1>
4638
</div>
4739

4840
{%if previewaddr%}
49-
{%if needscontract%}
41+
{%if level.contractlevel > 0%}
5042
<h4>Contract details</h4>
43+
{% if level.contractlevel == 1 %}
44+
<p>
45+
This sponsorship level uses a <strong>click-through contract</strong>. This
46+
means that by signing up, you agree to the
47+
<a href="/events/sponsor/previewcontract/{{level.contract.id}}/">contract</a>, and
48+
accept that no changes can be made to it.
49+
</p>
50+
{% else %}
51+
<p>
52+
This sponsorship level requires a signed contract. Please review the
53+
<a href="/events/sponsor/previewcontract/{{level.contract.id}}/">contract</a> before
54+
completing the sign-up, and <em>only continue if you agree with it</em>.
55+
Once signed up, you will receive a contract or signing.
56+
</p>
57+
{% endif %}
5158
<p>
5259
The contract for the sponsorship will be issued to the company name
5360
<strong><em>{{sponsorname}}</em></strong>{%if vatnumber%} with VAT
5461
number <strong><em>{{vatnumber}}</em></strong>{%endif%}.
55-
</p>
56-
<p>
57-
It will <strong>not</strong> be possible to change this after this
58-
step, so if anything about it is incorrect please click <i>Continue
62+
63+
It will <strong>not</strong> be possible to change this, or any other
64+
details in the contract, after this
65+
step. If anything about it is incorrect please click <i>Continue
5966
editing</i> and correct it <i>before</i> proceeding.
6067
</p>
6168
{%endif%}
@@ -92,7 +99,7 @@ <h4>Contract signing</h4>
9299
<input type="hidden" name="stage" value="2">
93100
{%endif%}{# contractchoices #}
94101

95-
{%if not level.instantbuy and not noform %}
102+
{%if level.contractlevle == 2 and not noform %}
96103
<p>
97104
Please note that due to the level of this sponsorship contract, we
98105
will require a signed contract, apart from the confirmation

template/confsponsor/sponsor.html

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,20 +34,22 @@ <h1>Conference Sponsorship - {{sponsor.conference}}</h1>
3434
<th>Status:</th>
3535
<td>{%if sponsor.confirmed%}Confirmed ({{sponsor.confirmedat}}){%else%}<i>Awaiting confirmation</i>{%endif%}</td>
3636
</tr>
37-
{% if sponsor.confirmed and sponsor.level.contract %}
37+
{% if sponsor.confirmed %}
3838
<tr>
3939
<th>Contract:</th>
4040
<td>
41-
{% if sponsor.level.instantbuy %}
41+
{% if sponsor.level.contractlevel == 0 %}
42+
This level requires no contract.
43+
{% elif sponsor.level.contractlevel == 1 %}
4244
Click-through contract agreed to. <a href="contractview/" class="btn btn-outline-dark btn-sm">View copy of contract</a>
4345
{% else %}
4446
{%if sponsor.signmethod == 0%}
4547
Digital contract completed {{sponsor.contract.completed}}.
4648
{%if sponsor.contract.completed and sponsor.contract.has_completed_pdf %}<a href="contractview/" class="btn btn-outline-dark btn-sm">View signed contract</a>{%endif%}
4749
{% else %}
48-
Manual contract.
49-
{% endif %}
50-
{% endif %}
50+
Manual contract signed.
51+
{% endif %}{%comment%}digital contract/manual contract{%endcomment%}
52+
{% endif %}{%comment%}contractlevel{%endcomment%}
5153
</td>
5254
</tr>
5355
{% endif %}
@@ -73,8 +75,8 @@ <h1>Conference Sponsorship - {{sponsor.conference}}</h1>
7375
{%endwith%}
7476
{%else%}
7577
{%comment%}No invoice generated{%endcomment%}
76-
{%if sponsor.level.instantbuy%}
77-
{%comment%}No invoice generated but instantbuy active, so awaiting admin approval{%endcomment%}
78+
{%if sponsor.level.contractlevel < 2 %}
79+
{%comment%}No invoice generated but clickthrough contract or no contract, so awaiting admin approval{%endcomment%}
7880
<p>
7981
Your sponsorship request has been submitted, and is currently awaiting confirmation
8082
from the conference organizers. As soon as it has been, an invoice will be automatically

0 commit comments

Comments
 (0)