Add "check-in message" to registration types master github/master
authorMagnus Hagander <magnus@hagander.net>
Sun, 10 Aug 2025 12:58:08 +0000 (14:58 +0200)
committerMagnus Hagander <magnus@hagander.net>
Sun, 10 Aug 2025 12:58:08 +0000 (14:58 +0200)
This message will be shown (highlighed) when an attendee is checked in,
and can be used to for example alert the person doing the check-in that
they should validate the registration by checking a student-id.

To do so, the "highlight this field" functionality is moved into the
backend and returned as part of the API, instead of the very limited "if
' NOT ' is present in the field text then highlight it" (used only for
unconfirmed policies) that was previously used.

Fixes #190

docs/confreg/registrations.md
docs/confreg/tickets.md
media/js/scanner.js
postgresqleu/confreg/backendforms.py
postgresqleu/confreg/checkin.py
postgresqleu/confreg/migrations/0118_registrationtype_checkinmessage.py [new file with mode: 0644]
postgresqleu/confreg/models.py

index daaada0c45ab07d19fb21c7094644706877621cf..92119887a6a95bb5cc0516e811f16742386fcd37 100644 (file)
@@ -238,6 +238,14 @@ Alert message
 registration. This is typically used for things like informing people of special
 requirements such as a student ID to access student discounts.
 
+Check-in message
+: This message is shown when the attendee is [checked in](tickets#checkin),
+either using the web app or the native app. It
+is shown only to the person making the check-in. This cam typically be
+used to alert the check-in operator that they should verify the
+attendees eligibility for the registration type, for example by
+checking a student or corporate id.
+
 Autocancel invoices
 : If registrations with this registration type should override the value for
 autocancel from the [conference](configuring). The lowest value of
index af30769424554fc73cf108b0bcea943273410e56..b638ce4d25a8f6e40e526f0763318fe1e4775e74 100644 (file)
@@ -45,7 +45,7 @@ the desired number of queue partitions, and the system will calculate
 (based on current confirmed attendees) which letters to send to which
 queue.
 
-## Check-in
+## Check-in <a name="checkin"></a>
 
 The check-in system is designed for those working the registration
 desk at the event. It's a small web app designed specifically to be
index ac869016bc3e94a28e28d59548a895663b0a97c8..7d2c088b51ab89483b9e2f7321428d823342842a 100644 (file)
@@ -74,7 +74,7 @@ function add_dynamic_fields(reg, cl, regcompleted) {
 
             if (typeof(val) == 'string') {
                 let e = $('<dd/>').text(val).addClass('found_dyn');
-                if (val.includes(' NOT ')) {
+                if (reg['highlight'].includes(a[0])) {
                     e = $(e).addClass('found_dyn_warn');
                 }
                 elements.push(e);
index 9d146e163d4573c974be534558e74f948399e589..90a8305e515a98f1caa531f0fe70ec43f9b25dca 100644 (file)
@@ -437,7 +437,7 @@ class BackendRegistrationTypeForm(BackendForm):
 
     class Meta:
         model = RegistrationType
-        fields = ['regtype', 'regclass', 'cost', 'active', 'activeuntil', 'days', 'sortkey', 'specialtype', 'require_phone', 'alertmessage', 'invoice_autocancel_hours', 'requires_option', 'upsell_target']
+        fields = ['regtype', 'regclass', 'cost', 'active', 'activeuntil', 'days', 'sortkey', 'specialtype', 'require_phone', 'alertmessage', 'checkinmessage', 'invoice_autocancel_hours', 'requires_option', 'upsell_target']
 
     @classmethod
     def get_column_filters(cls, conference):
@@ -525,6 +525,7 @@ class BackendRegistrationTypeForm(BackendForm):
                                      specialtype=source.specialtype,
                                      # Not copying days
                                      alertmessage=source.alertmessage,
+                                     checkinmessage=source.checkinmessage,
                                      upsell_target=source.upsell_target,
                                      # Not copying invoice_autocancel_hours
                                      # Not copying requires_option
index 0fec990e80ec54297b335e8039a5e41611f7cecc..2b6e829d4818db89193904065df01f14431a6f9e 100644 (file)
@@ -87,6 +87,7 @@ def _do_checkin_scan(request, conference, is_admin, extra=None):
         'scanfields': [
             ["name", "Name"],
             ["type", "Registration type"],
+            ["checkinmessage", "Check-in message"],
             ["policyconfirmed", "Policy confirmed"],
             ["photoconsent", "Photo consent"],
             ["tshirt", "T-Shirt size"],
@@ -252,11 +253,17 @@ def _get_reg_json(r, fieldscan=False):
         'tshirt': r.shirtsize and r.shirtsize.shirtsize,
         'additional': [a.name for a in r.additionaloptions.all()],
         'token': r.publictoken if fieldscan else r.idtoken,
+        'highlight': [],
     }
     if r.conference.askphotoconsent:
         d['photoconsent'] = r.photoconsent and "Photos OK" or "Photos NOT OK"
     if r.conference.confirmpolicy:
         d['policyconfirmed'] = r.policyconfirmedat and "Policy confirmed" or "Policy NOT confirmed"
+        if not r.policyconfirmedat:
+            d['highlight'].append('policyconfirmed')
+    if r.regtype.checkinmessage:
+        d['checkinmessage'] = r.regtype.checkinmessage
+        d['highlight'].append('checkinmessage')
     if r.checkedinat and not fieldscan:
         d['already'] = {
             'title': 'Attendee already checked in',
diff --git a/postgresqleu/confreg/migrations/0118_registrationtype_checkinmessage.py b/postgresqleu/confreg/migrations/0118_registrationtype_checkinmessage.py
new file mode 100644 (file)
index 0000000..a8c656c
--- /dev/null
@@ -0,0 +1,18 @@
+# Generated by Django 4.2.11 on 2025-08-10 12:32
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('confreg', '0117_pronouns'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='registrationtype',
+            name='checkinmessage',
+            field=models.TextField(blank=True, help_text='Message shown when the attendee is checked in', verbose_name='Check-in message'),
+        ),
+    ]
index ea4e877c1bf382de394cf8fc542721e8ececaba7..0366990c2b850c86d7531a2918578769cb110ea5 100644 (file)
@@ -466,6 +466,7 @@ class RegistrationType(models.Model):
     require_phone = models.BooleanField(null=False, blank=False, default=False, help_text="Require phone number to be entered")
     days = models.ManyToManyField(RegistrationDay, blank=True)
     alertmessage = models.TextField(null=False, blank=True, verbose_name="Alert message", help_text="Message shown in popup to user when completing the registration")
+    checkinmessage = models.TextField(null=False, blank=True, verbose_name='Check-in message', help_text='Message shown when the attendee is checked in')
     upsell_target = models.BooleanField(null=False, blank=False, default=False, help_text='Is target registration type for upselling in order to add additional options')
     invoice_autocancel_hours = models.IntegerField(blank=True, null=True, validators=[MinValueValidator(1), ], verbose_name="Autocancel invoices", help_text="Automatically cancel invoices after this many hours")
     requires_option = models.ManyToManyField('ConferenceAdditionalOption', blank=True, help_text='Requires at least one of the selected additional options to be picked')