1
|
|
|
""" |
2
|
|
|
byceps.blueprints.admin.ticketing.views |
3
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
4
|
|
|
|
5
|
|
|
:Copyright: 2014-2024 Jochen Kupperschmidt |
6
|
|
|
:License: Revised BSD (see `LICENSE` file for details) |
7
|
|
|
""" |
8
|
|
|
|
9
|
1 |
|
from flask import abort, g, request |
10
|
1 |
|
from flask_babel import gettext |
11
|
|
|
|
12
|
1 |
|
from byceps.services.party import party_service |
13
|
1 |
|
from byceps.services.shop.order import order_service |
14
|
1 |
|
from byceps.services.ticketing import ( |
15
|
|
|
ticket_bundle_service, |
16
|
|
|
ticket_category_service, |
17
|
|
|
ticket_service, |
18
|
|
|
ticket_user_management_service, |
19
|
|
|
) |
20
|
1 |
|
from byceps.services.ticketing.ticket_service import FilterMode |
21
|
1 |
|
from byceps.util.framework.blueprint import create_blueprint |
22
|
1 |
|
from byceps.util.framework.flash import flash_error, flash_success |
23
|
1 |
|
from byceps.util.framework.templating import templated |
24
|
1 |
|
from byceps.util.views import permission_required, redirect_to |
25
|
|
|
|
26
|
1 |
|
from . import service |
27
|
1 |
|
from .forms import SpecifyUserForm, UpdateCodeForm |
28
|
|
|
|
29
|
|
|
|
30
|
1 |
|
blueprint = create_blueprint('ticketing_admin', __name__) |
31
|
|
|
|
32
|
|
|
|
33
|
|
|
# -------------------------------------------------------------------- # |
34
|
|
|
# tickets |
35
|
|
|
|
36
|
|
|
|
37
|
1 |
|
@blueprint.get('/tickets/for_party/<party_id>', defaults={'page': 1}) |
38
|
1 |
|
@blueprint.get('/tickets/for_party/<party_id>/pages/<int:page>') |
39
|
1 |
|
@permission_required('ticketing.view') |
40
|
1 |
|
@templated |
41
|
1 |
|
def index_for_party(party_id, page): |
42
|
|
|
"""List tickets for that party.""" |
43
|
1 |
|
party = _get_party_or_404(party_id) |
44
|
|
|
|
45
|
1 |
|
per_page = request.args.get('per_page', type=int, default=15) |
46
|
|
|
|
47
|
1 |
|
search_term = request.args.get('search_term', default='').strip() |
48
|
|
|
|
49
|
1 |
|
filter_category_id = request.args.get('category') |
50
|
1 |
|
if filter_category_id: |
51
|
|
|
filter_category = ticket_category_service.find_category( |
52
|
|
|
filter_category_id |
53
|
|
|
) |
54
|
|
|
else: |
55
|
1 |
|
filter_category = None |
56
|
|
|
|
57
|
1 |
|
try: |
58
|
1 |
|
filter_revoked = FilterMode[request.args.get('revoked')] |
59
|
1 |
|
except KeyError: |
60
|
1 |
|
filter_revoked = None |
61
|
|
|
|
62
|
1 |
|
try: |
63
|
1 |
|
filter_checked_in = FilterMode[request.args.get('checked_in')] |
64
|
1 |
|
except KeyError: |
65
|
1 |
|
filter_checked_in = None |
66
|
|
|
|
67
|
1 |
|
filter_args = {} |
68
|
1 |
|
if filter_category is not None: |
69
|
|
|
filter_args['category'] = filter_category_id |
70
|
1 |
|
if filter_revoked is not None: |
71
|
|
|
filter_args['revoked'] = filter_revoked.name |
72
|
1 |
|
if filter_checked_in is not None: |
73
|
|
|
filter_args['checked_in'] = filter_checked_in.name |
74
|
|
|
|
75
|
1 |
|
def _get_filter_args(**kwargs) -> dict[str, str]: |
76
|
1 |
|
args = filter_args.copy() |
77
|
1 |
|
args.update(**kwargs) |
78
|
1 |
|
return args |
79
|
|
|
|
80
|
1 |
|
tickets = ticket_service.get_tickets_with_details_for_party_paginated( |
81
|
|
|
party.id, |
82
|
|
|
page, |
83
|
|
|
per_page, |
84
|
|
|
search_term=search_term, |
85
|
|
|
filter_category_id=filter_category.id |
86
|
|
|
if filter_category is not None |
87
|
|
|
else None, |
88
|
|
|
filter_revoked=filter_revoked, |
89
|
|
|
filter_checked_in=filter_checked_in, |
90
|
|
|
) |
91
|
|
|
|
92
|
1 |
|
categories = ticket_category_service.get_categories_for_party(party.id) |
93
|
|
|
|
94
|
1 |
|
return { |
95
|
|
|
'party': party, |
96
|
|
|
'per_page': per_page, |
97
|
|
|
'search_term': search_term, |
98
|
|
|
'tickets': tickets, |
99
|
|
|
'categories': categories, |
100
|
|
|
'filter_category': filter_category, |
101
|
|
|
'filter_revoked': filter_revoked, |
102
|
|
|
'filter_checked_in': filter_checked_in, |
103
|
|
|
'get_filter_args': _get_filter_args, |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
|
107
|
1 |
|
@blueprint.get('/tickets/<uuid:ticket_id>') |
108
|
1 |
|
@permission_required('ticketing.view') |
109
|
1 |
|
@templated |
110
|
1 |
|
def view_ticket(ticket_id): |
111
|
|
|
"""Show a ticket.""" |
112
|
1 |
|
ticket = ticket_service.get_ticket_with_details(ticket_id) |
113
|
1 |
|
if ticket is None: |
114
|
|
|
abort(404) |
115
|
|
|
|
116
|
1 |
|
party = party_service.get_party(ticket.category.party_id) |
117
|
|
|
|
118
|
1 |
|
if ticket.order_number: |
119
|
|
|
order = order_service.find_order_by_order_number(ticket.order_number) |
120
|
|
|
else: |
121
|
1 |
|
order = None |
122
|
|
|
|
123
|
1 |
|
log_entries = service.get_log_entries(ticket.id) |
124
|
|
|
|
125
|
1 |
|
return { |
126
|
|
|
'party': party, |
127
|
|
|
'ticket': ticket, |
128
|
|
|
'order': order, |
129
|
|
|
'log_entries': log_entries, |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
|
133
|
|
|
# -------------------------------------------------------------------- # |
134
|
|
|
# ticket code |
135
|
|
|
|
136
|
|
|
|
137
|
1 |
|
@blueprint.get('/tickets/<uuid:ticket_id>/code') |
138
|
1 |
|
@permission_required('ticketing.administrate') |
139
|
1 |
|
@templated |
140
|
1 |
|
def update_code_form(ticket_id, erroneous_form=None): |
141
|
|
|
"""Show a form to set a custom code for the ticket.""" |
142
|
|
|
ticket = _get_ticket_or_404(ticket_id) |
143
|
|
|
|
144
|
|
|
if ticket.user_checked_in: |
145
|
|
|
flash_error( |
146
|
|
|
gettext( |
147
|
|
|
'Code cannot be changed as somebody has already been checked in with this ticket.' |
148
|
|
|
) |
149
|
|
|
) |
150
|
|
|
return redirect_to('.view_ticket', ticket_id=ticket.id) |
151
|
|
|
|
152
|
|
|
party = party_service.get_party(ticket.party_id) |
153
|
|
|
|
154
|
|
|
form = erroneous_form if erroneous_form else UpdateCodeForm() |
155
|
|
|
|
156
|
|
|
return { |
157
|
|
|
'party': party, |
158
|
|
|
'ticket': ticket, |
159
|
|
|
'form': form, |
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
|
163
|
1 |
|
@blueprint.post('/tickets/<uuid:ticket_id>/code') |
164
|
1 |
|
@permission_required('ticketing.administrate') |
165
|
1 |
|
def update_code(ticket_id): |
166
|
|
|
"""Set a custom code for the ticket.""" |
167
|
|
|
ticket = _get_ticket_or_404(ticket_id) |
168
|
|
|
|
169
|
|
|
if ticket.user_checked_in: |
170
|
|
|
flash_error( |
171
|
|
|
gettext( |
172
|
|
|
'Code cannot be changed as somebody has already been checked in with this ticket.' |
173
|
|
|
) |
174
|
|
|
) |
175
|
|
|
return redirect_to('.view_ticket', ticket_id=ticket.id) |
176
|
|
|
|
177
|
|
|
form = UpdateCodeForm(request.form) |
178
|
|
|
if not form.validate(): |
179
|
|
|
return update_code_form(ticket.id, form) |
180
|
|
|
|
181
|
|
|
code = form.code.data |
182
|
|
|
manager = g.user |
183
|
|
|
|
184
|
|
|
ticket_service.update_ticket_code(ticket.id, code, manager.id) |
185
|
|
|
|
186
|
|
|
flash_success( |
187
|
|
|
gettext( |
188
|
|
|
'Code for ticket %(ticket_code)s has been updated.', |
189
|
|
|
ticket_code=ticket.code, |
190
|
|
|
) |
191
|
|
|
) |
192
|
|
|
|
193
|
|
|
return redirect_to('.view_ticket', ticket_id=ticket.id) |
194
|
|
|
|
195
|
|
|
|
196
|
|
|
# -------------------------------------------------------------------- # |
197
|
|
|
# user appointment |
198
|
|
|
|
199
|
|
|
|
200
|
1 |
|
@blueprint.get('/tickets/<uuid:ticket_id>/appoint_user') |
201
|
1 |
|
@permission_required('ticketing.checkin') |
202
|
1 |
|
@templated |
203
|
1 |
|
def appoint_user_form(ticket_id, erroneous_form=None): |
204
|
|
|
"""Show a form to select a user to appoint for the ticket.""" |
205
|
1 |
|
ticket = _get_ticket_or_404(ticket_id) |
206
|
|
|
|
207
|
1 |
|
party = party_service.get_party(ticket.party_id) |
208
|
|
|
|
209
|
1 |
|
form = erroneous_form if erroneous_form else SpecifyUserForm() |
210
|
|
|
|
211
|
1 |
|
return { |
212
|
|
|
'party': party, |
213
|
|
|
'ticket': ticket, |
214
|
|
|
'form': form, |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
|
218
|
1 |
|
@blueprint.post('/tickets/<uuid:ticket_id>/user') |
219
|
1 |
|
@permission_required('ticketing.checkin') |
220
|
1 |
|
def appoint_user(ticket_id): |
221
|
|
|
"""Appoint a user for the ticket.""" |
222
|
|
|
form = SpecifyUserForm(request.form) |
223
|
|
|
if not form.validate(): |
224
|
|
|
return appoint_user_form(ticket_id, form) |
225
|
|
|
|
226
|
|
|
ticket = _get_ticket_or_404(ticket_id) |
227
|
|
|
user = form.user.data |
228
|
|
|
manager = g.user |
229
|
|
|
|
230
|
|
|
result = ticket_user_management_service.appoint_user( |
231
|
|
|
ticket.id, user.id, manager.id |
232
|
|
|
) |
233
|
|
|
|
234
|
|
|
if result.is_err(): |
235
|
|
|
flash_error(result.unwrap_err().message) |
236
|
|
|
return redirect_to('.view_ticket', ticket_id=ticket.id) |
237
|
|
|
|
238
|
|
|
flash_success( |
239
|
|
|
gettext( |
240
|
|
|
'%(screen_name)s has been assigned as user of ticket %(ticket_code)s.', |
241
|
|
|
screen_name=user.screen_name, |
242
|
|
|
ticket_code=ticket.code, |
243
|
|
|
) |
244
|
|
|
) |
245
|
|
|
|
246
|
|
|
return redirect_to('.view_ticket', ticket_id=ticket.id) |
247
|
|
|
|
248
|
|
|
|
249
|
|
|
# -------------------------------------------------------------------- # |
250
|
|
|
# bundles |
251
|
|
|
|
252
|
|
|
|
253
|
1 |
|
@blueprint.get('/bundles/for_party/<party_id>', defaults={'page': 1}) |
254
|
1 |
|
@blueprint.get('/bundles/for_party/<party_id>/pages/<int:page>') |
255
|
1 |
|
@permission_required('ticketing.view') |
256
|
1 |
|
@templated |
257
|
1 |
|
def index_bundle_for_party(party_id, page): |
258
|
|
|
"""List ticket bundles for that party.""" |
259
|
1 |
|
party = _get_party_or_404(party_id) |
260
|
|
|
|
261
|
1 |
|
per_page = request.args.get('per_page', type=int, default=15) |
262
|
|
|
|
263
|
1 |
|
bundles = ticket_bundle_service.get_bundles_for_party_paginated( |
264
|
|
|
party.id, page, per_page |
265
|
|
|
) |
266
|
|
|
|
267
|
1 |
|
return { |
268
|
|
|
'party': party, |
269
|
|
|
'bundles': bundles, |
270
|
|
|
'per_page': per_page, |
271
|
|
|
} |
272
|
|
|
|
273
|
|
|
|
274
|
1 |
|
@blueprint.get('/bundles/<uuid:bundle_id>') |
275
|
1 |
|
@permission_required('ticketing.view') |
276
|
1 |
|
@templated |
277
|
1 |
|
def view_bundle(bundle_id): |
278
|
|
|
"""Show a ticket bundle.""" |
279
|
1 |
|
bundle = ticket_bundle_service.find_bundle(bundle_id) |
280
|
1 |
|
if bundle is None: |
281
|
|
|
abort(404) |
282
|
|
|
|
283
|
1 |
|
party = party_service.get_party(bundle.ticket_category.party_id) |
284
|
|
|
|
285
|
1 |
|
tickets = ticket_bundle_service.get_tickets_for_bundle(bundle.id) |
286
|
|
|
|
287
|
1 |
|
return { |
288
|
|
|
'party': party, |
289
|
|
|
'bundle': bundle, |
290
|
|
|
'tickets': tickets, |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
|
294
|
|
|
# -------------------------------------------------------------------- # |
295
|
|
|
# helpers |
296
|
|
|
|
297
|
|
|
|
298
|
1 |
|
def _get_party_or_404(party_id): |
299
|
1 |
|
party = party_service.find_party(party_id) |
300
|
|
|
|
301
|
1 |
|
if party is None: |
302
|
|
|
abort(404) |
303
|
|
|
|
304
|
1 |
|
return party |
305
|
|
|
|
306
|
|
|
|
307
|
1 |
|
def _get_ticket_or_404(ticket_id): |
308
|
1 |
|
ticket = ticket_service.find_ticket(ticket_id) |
309
|
|
|
|
310
|
1 |
|
if ticket is None: |
311
|
|
|
abort(404) |
312
|
|
|
|
313
|
|
|
return ticket |
314
|
|
|
|