forked from CGM_Public/pretix_original
290 lines
8.0 KiB
JavaScript
290 lines
8.0 KiB
JavaScript
$(function () {
|
||
var TYPEOPS = {
|
||
// Every change to our supported JSON logic must be done
|
||
// * in pretix.base.services.checkin
|
||
// * in pretix.base.models.checkin
|
||
// * in pretix.helpers.jsonlogic_boolalg
|
||
// * in checkinrules.js
|
||
// * in libpretixsync
|
||
// * in pretixscan-ios
|
||
'product': {
|
||
'inList': {
|
||
'label': gettext('is one of'),
|
||
'cardinality': 2,
|
||
}
|
||
},
|
||
'variation': {
|
||
'inList': {
|
||
'label': gettext('is one of'),
|
||
'cardinality': 2,
|
||
}
|
||
},
|
||
'gate': {
|
||
'inList': {
|
||
'label': gettext('is one of'),
|
||
'cardinality': 2,
|
||
}
|
||
},
|
||
'datetime': {
|
||
'isBefore': {
|
||
'label': gettext('is before'),
|
||
'cardinality': 2,
|
||
},
|
||
'isAfter': {
|
||
'label': gettext('is after'),
|
||
'cardinality': 2,
|
||
},
|
||
},
|
||
'enum_entry_status': {
|
||
'==': {
|
||
'label': gettext('='),
|
||
'cardinality': 2,
|
||
},
|
||
},
|
||
'int_by_datetime': {
|
||
'<': {
|
||
'label': '<',
|
||
'cardinality': 2,
|
||
},
|
||
'<=': {
|
||
'label': '≤',
|
||
'cardinality': 2,
|
||
},
|
||
'>': {
|
||
'label': '>',
|
||
'cardinality': 2,
|
||
},
|
||
'>=': {
|
||
'label': '≥',
|
||
'cardinality': 2,
|
||
},
|
||
'==': {
|
||
'label': '=',
|
||
'cardinality': 2,
|
||
},
|
||
'!=': {
|
||
'label': '≠',
|
||
'cardinality': 2,
|
||
},
|
||
},
|
||
'int': {
|
||
'<': {
|
||
'label': '<',
|
||
'cardinality': 2,
|
||
},
|
||
'<=': {
|
||
'label': '≤',
|
||
'cardinality': 2,
|
||
},
|
||
'>': {
|
||
'label': '>',
|
||
'cardinality': 2,
|
||
},
|
||
'>=': {
|
||
'label': '≥',
|
||
'cardinality': 2,
|
||
},
|
||
'==': {
|
||
'label': '=',
|
||
'cardinality': 2,
|
||
},
|
||
'!=': {
|
||
'label': '≠',
|
||
'cardinality': 2,
|
||
},
|
||
},
|
||
};
|
||
var VARS = {
|
||
'product': {
|
||
'label': gettext('Product'),
|
||
'type': 'product',
|
||
},
|
||
'variation': {
|
||
'label': gettext('Product variation'),
|
||
'type': 'variation',
|
||
},
|
||
'gate': {
|
||
'label': gettext('Gate'),
|
||
'type': 'gate',
|
||
},
|
||
'now': {
|
||
'label': gettext('Current date and time'),
|
||
'type': 'datetime',
|
||
},
|
||
'now_isoweekday': {
|
||
'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',
|
||
},
|
||
'entries_today': {
|
||
'label': gettext('Number of previous entries since midnight'),
|
||
'type': 'int',
|
||
},
|
||
'entries_since': {
|
||
'label': gettext('Number of previous entries since'),
|
||
'type': 'int_by_datetime',
|
||
},
|
||
'entries_before': {
|
||
'label': gettext('Number of previous entries before'),
|
||
'type': 'int_by_datetime',
|
||
},
|
||
'entries_days': {
|
||
'label': gettext('Number of days with a previous entry'),
|
||
'type': 'int',
|
||
},
|
||
'entries_days_since': {
|
||
'label': gettext('Number of days with a previous entry since'),
|
||
'type': 'int_by_datetime',
|
||
},
|
||
'entries_days_before': {
|
||
'label': gettext('Number of days with a previous entry before'),
|
||
'type': 'int_by_datetime',
|
||
},
|
||
'minutes_since_last_entry': {
|
||
'label': gettext('Minutes since last entry (-1 on first entry)'),
|
||
'type': 'int',
|
||
},
|
||
'minutes_since_first_entry': {
|
||
'label': gettext('Minutes since first entry (-1 on first entry)'),
|
||
'type': 'int',
|
||
},
|
||
};
|
||
|
||
var components = {
|
||
CheckinRulesVisualization: CheckinRulesVisualization.default,
|
||
}
|
||
if (typeof CheckinRule !== "undefined") {
|
||
Vue.component('checkin-rule', CheckinRule.default);
|
||
components = {
|
||
CheckinRulesEditor: CheckinRulesEditor.default,
|
||
CheckinRulesVisualization: CheckinRulesVisualization.default,
|
||
}
|
||
}
|
||
var app = new Vue({
|
||
el: '#rules-editor',
|
||
components: components,
|
||
data: function () {
|
||
return {
|
||
rules: {},
|
||
items: [],
|
||
all_products: false,
|
||
limit_products: [],
|
||
TYPEOPS: TYPEOPS,
|
||
VARS: VARS,
|
||
texts: {
|
||
and: gettext('All of the conditions below (AND)'),
|
||
or: gettext('At least one of the conditions below (OR)'),
|
||
date_from: gettext('Event start'),
|
||
date_to: gettext('Event end'),
|
||
date_admission: gettext('Event admission'),
|
||
date_custom: gettext('custom date and time'),
|
||
date_customtime: gettext('custom time'),
|
||
date_tolerance: gettext('Tolerance (minutes)'),
|
||
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,
|
||
};
|
||
},
|
||
computed: {
|
||
missingItems: function () {
|
||
// This computed property contains list of item or variation names that
|
||
// a) Are allowed on the checkin list according to all_products or include_products
|
||
// b) Are not matched by ANY logical branch of the rule.
|
||
// The list will be empty if there is a "catch-all" rule.
|
||
var products_seen = {};
|
||
var variations_seen = {};
|
||
var rules = convert_to_dnf(this.rules);
|
||
var branch_without_product_filter = false;
|
||
|
||
if (!rules["or"]) {
|
||
rules = {"or": [rules]}
|
||
}
|
||
|
||
for (var part of rules["or"]) {
|
||
if (!part["and"]) {
|
||
part = {"and": [part]}
|
||
}
|
||
var this_branch_without_product_filter = true;
|
||
for (var subpart of part["and"]) {
|
||
if (subpart["inList"]) {
|
||
if (subpart["inList"][0]["var"] === "product" && subpart["inList"][1]) {
|
||
this_branch_without_product_filter = false;
|
||
for (var listentry of subpart["inList"][1]["objectList"]) {
|
||
products_seen[parseInt(listentry["lookup"][1])] = true
|
||
}
|
||
} else if (subpart["inList"][0]["var"] === "variation" && subpart["inList"][1]) {
|
||
this_branch_without_product_filter = false;
|
||
for (var listentry_ of subpart["inList"][1]["objectList"]) {
|
||
variations_seen[parseInt(listentry_["lookup"][1])] = true
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (this_branch_without_product_filter) {
|
||
branch_without_product_filter = true;
|
||
break;
|
||
}
|
||
}
|
||
if (branch_without_product_filter || (!Object.keys(products_seen).length && !Object.keys(variations_seen).length)) {
|
||
// At least one branch with no product filters at all – that's fine.
|
||
return [];
|
||
}
|
||
|
||
var missing = [];
|
||
for (var item of this.items) {
|
||
if (products_seen[item.id]) continue;
|
||
if (!this.all_products && !this.limit_products.includes(item.id)) continue;
|
||
if (item.variations.length > 0) {
|
||
for (var variation of item.variations) {
|
||
if (variations_seen[variation.id]) continue;
|
||
missing.push(item.name + " – " + variation.name)
|
||
}
|
||
} else {
|
||
missing.push(item.name)
|
||
}
|
||
}
|
||
return missing;
|
||
}
|
||
},
|
||
created: function () {
|
||
this.rules = JSON.parse($("#id_rules").val());
|
||
if ($("#items").length) {
|
||
this.items = JSON.parse($("#items").html());
|
||
|
||
var root = this.$root
|
||
|
||
function _update() {
|
||
root.all_products = $("#id_all_products").prop("checked")
|
||
root.limit_products = $("input[name=limit_products]:checked").map(function () {
|
||
return parseInt($(this).val())
|
||
}).toArray()
|
||
}
|
||
|
||
$("#id_all_products, input[name=limit_products]").on("change", function () {
|
||
_update();
|
||
})
|
||
_update()
|
||
}
|
||
},
|
||
watch: {
|
||
rules: {
|
||
deep: true,
|
||
handler: function (newval) {
|
||
$("#id_rules").val(JSON.stringify(newval));
|
||
}
|
||
},
|
||
}
|
||
})
|
||
});
|