forked from CGM_Public/pretix_original
Checkin: Allow to use presence state in rules (#4061)
This commit is contained in:
@@ -285,7 +285,7 @@ class CheckinList(LoggedModel):
|
||||
}
|
||||
allowed_vars = {
|
||||
'product', 'variation', 'now', 'now_isoweekday', 'entries_number', 'entries_today', 'entries_days',
|
||||
'minutes_since_last_entry', 'minutes_since_first_entry', 'gate',
|
||||
'minutes_since_last_entry', 'minutes_since_first_entry', 'gate', 'entry_status',
|
||||
}
|
||||
if not rules or not isinstance(rules, dict):
|
||||
return rules
|
||||
|
||||
@@ -42,8 +42,8 @@ from dateutil.tz import datetime_exists
|
||||
from django.core.files import File
|
||||
from django.db import IntegrityError, transaction
|
||||
from django.db.models import (
|
||||
BooleanField, Count, ExpressionWrapper, F, IntegerField, Max, Min,
|
||||
OuterRef, Q, Subquery, Value,
|
||||
BooleanField, Case, Count, ExpressionWrapper, F, IntegerField, Max, Min,
|
||||
OuterRef, Q, Subquery, TextField, Value, When,
|
||||
)
|
||||
from django.db.models.functions import Coalesce, TruncDate
|
||||
from django.dispatch import receiver
|
||||
@@ -273,6 +273,14 @@ def _logic_explain(rules, ev, rule_data, now_dt=None):
|
||||
var_texts[vname] = _('Only allowed before {datetime}').format(datetime=compare_to_text)
|
||||
elif operator == 'isAfter':
|
||||
var_texts[vname] = _('Only allowed after {datetime}').format(datetime=compare_to_text)
|
||||
elif var == 'entry_status':
|
||||
var_weights[vname] = (20, 0)
|
||||
if operator == '==' and rhs[0] == 'present':
|
||||
var_texts[vname] = _('Attendee is checked out')
|
||||
elif operator == '==' and rhs[0] == 'absent':
|
||||
var_texts[vname] = _('Attendee is already checked in')
|
||||
else:
|
||||
var_texts[vname] = f'{var} not {operator} {rhs}'
|
||||
elif var == 'product' or var == 'variation':
|
||||
var_weights[vname] = (1000, 0)
|
||||
var_texts[vname] = _('Ticket type not allowed')
|
||||
@@ -507,6 +515,13 @@ class LazyRuleVars:
|
||||
day=TruncDate('datetime', tzinfo=tz)
|
||||
).values('day').distinct().count()
|
||||
|
||||
@cached_property
|
||||
def entry_status(self):
|
||||
last_checkin = self._position.checkins.filter(list=self._clist).order_by('datetime').last()
|
||||
if not last_checkin or last_checkin.type == Checkin.TYPE_EXIT:
|
||||
return "absent"
|
||||
return "present"
|
||||
|
||||
@cached_property
|
||||
def minutes_since_last_entry(self):
|
||||
tz = self._clist.event.timezone
|
||||
@@ -569,6 +584,8 @@ class SQLLogic:
|
||||
'entries_days_since', 'entries_days_before'}
|
||||
|
||||
def operation_to_expression(self, rule):
|
||||
if isinstance(rule, str):
|
||||
return Value(rule)
|
||||
if not isinstance(rule, dict):
|
||||
return rule
|
||||
|
||||
@@ -770,6 +787,25 @@ class SQLLogic:
|
||||
Value(-1),
|
||||
output_field=IntegerField()
|
||||
)
|
||||
elif values[0] == 'entry_status':
|
||||
sq_last_checkin = Subquery(
|
||||
Checkin.objects.filter(
|
||||
position_id=OuterRef('pk'),
|
||||
list_id=self.list.pk,
|
||||
).order_by('-datetime').values('type')[:1]
|
||||
)
|
||||
|
||||
return Case(
|
||||
When(
|
||||
condition=Equal(
|
||||
sq_last_checkin,
|
||||
Value(Checkin.TYPE_ENTRY)
|
||||
),
|
||||
then=Value("present"),
|
||||
),
|
||||
default=Value("absent"),
|
||||
output_field=TextField()
|
||||
)
|
||||
else:
|
||||
raise ValueError(f'Unknown operator {operator}')
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ class Equal(Func):
|
||||
arg_joiner = ' = '
|
||||
arity = 2
|
||||
function = ''
|
||||
conditional = True
|
||||
|
||||
|
||||
class GreaterThan(Func):
|
||||
|
||||
@@ -35,6 +35,12 @@ $(function () {
|
||||
'cardinality': 2,
|
||||
},
|
||||
},
|
||||
'enum_entry_status': {
|
||||
'==': {
|
||||
'label': gettext('='),
|
||||
'cardinality': 2,
|
||||
},
|
||||
},
|
||||
'int_by_datetime': {
|
||||
'<': {
|
||||
'label': '<',
|
||||
@@ -109,6 +115,10 @@ $(function () {
|
||||
'label': gettext('Current day of the week (1 = Monday, 7 = Sunday)'),
|
||||
'type': 'int',
|
||||
},
|
||||
'entry_status': {
|
||||
'label': gettext('Current entry status'),
|
||||
'type': 'enum_entry_status',
|
||||
},
|
||||
'entries_number': {
|
||||
'label': gettext('Number of previous entries'),
|
||||
'type': 'int',
|
||||
@@ -180,6 +190,8 @@ $(function () {
|
||||
condition_add: gettext('Add condition'),
|
||||
minutes: gettext('minutes'),
|
||||
duplicate: gettext('Duplicate'),
|
||||
status_present: pgettext('entry_status', 'present'),
|
||||
status_absent: pgettext('entry_status', 'absent'),
|
||||
},
|
||||
hasRules: false,
|
||||
};
|
||||
|
||||
@@ -56,6 +56,11 @@
|
||||
<lookup-select2 required v-if="vartype === 'gate' && operator === 'inList'" :multiple="true"
|
||||
:value="rightoperand" v-on:input="setRightOperandGateList"
|
||||
:url="gateSelectURL"></lookup-select2>
|
||||
<select required v-if="vartype === 'enum_entry_status' && operator === '=='"
|
||||
:value="rightoperand" v-on:input="setRightOperandEnum" class="form-control">
|
||||
<option value="absent">{{ texts.status_absent }}</option>
|
||||
<option value="present">{{ texts.status_present }}</option>
|
||||
</select>
|
||||
<div class="checkin-rule-childrules" v-if="operator === 'or' || operator === 'and'">
|
||||
<div v-for="(op, opi) in operands">
|
||||
<checkin-rule :rule="op" :index="opi" :level="level + 1" v-if="typeof op === 'object'"></checkin-rule>
|
||||
@@ -312,6 +317,13 @@
|
||||
this.$set(this.rule[this.operator], 1, products);
|
||||
}
|
||||
},
|
||||
setRightOperandEnum: function (event) {
|
||||
if (this.rule[this.operator].length === 1) {
|
||||
this.rule[this.operator].push(event.target.value);
|
||||
} else {
|
||||
this.$set(this.rule[this.operator], 1, event.target.value);
|
||||
}
|
||||
},
|
||||
addOperand: function () {
|
||||
this.rule[this.operator].push({"": []});
|
||||
},
|
||||
|
||||
@@ -75,6 +75,17 @@
|
||||
{{ rightoperand.objectList.map((o) => o.lookup[2]).join(", ") }}
|
||||
</strong>
|
||||
</span>
|
||||
<span v-else-if="vardata && vardata.type === 'enum_entry_status'">
|
||||
<span class="fa fa-check-circle-o"></span>
|
||||
{{ vardata.label }}
|
||||
<span v-if="varresult !== null">
|
||||
({{varresult}})
|
||||
</span>
|
||||
<br>
|
||||
<strong>
|
||||
{{ op.label }} {{ rightoperand }}
|
||||
</strong>
|
||||
</span>
|
||||
</div>
|
||||
</foreignObject>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user