create_number_sequence()   A
last analyzed

Complexity

Conditions 3

Size

Total Lines 32
Code Lines 21

Duplication

Lines 32
Ratio 100 %

Code Coverage

Tests 3
CRAP Score 7.3652

Importance

Changes 0
Metric Value
cc 3
eloc 21
nop 1
dl 32
loc 32
ccs 3
cts 14
cp 0.2143
crap 7.3652
rs 9.376
c 0
b 0
f 0
1
"""
2
byceps.blueprints.admin.shop.order.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, Response
10 1
from flask_babel import gettext
11
12 1
from byceps.services.brand import brand_service
13 1
from byceps.services.shop.order import (
14
    order_log_service,
15
    order_sequence_service,
16
    order_service,
17
)
18 1
from byceps.services.shop.order.email import order_email_service
19 1
from byceps.services.shop.order.errors import (
20
    OrderAlreadyCanceledError,
21
    OrderAlreadyMarkedAsPaidError,
22
)
23 1
from byceps.services.shop.order.export import order_export_service
24 1
from byceps.services.shop.order.models.order import PaymentState
25 1
from byceps.services.shop.shop import shop_service
26 1
from byceps.services.ticketing import ticket_service
27 1
from byceps.signals import shop as shop_signals
28 1
from byceps.util.framework.blueprint import create_blueprint
29 1
from byceps.util.framework.flash import flash_error, flash_notice, flash_success
30 1
from byceps.util.framework.templating import templated
31 1
from byceps.util.views import (
32
    permission_required,
33
    redirect_to,
34
    respond_no_content,
35
)
36
37 1
from . import service
38 1
from .forms import (
39
    AddNoteForm,
40
    CancelForm,
41
    MarkAsPaidForm,
42
    OrderNumberSequenceCreateForm,
43
)
44 1
from .models import OrderStateFilter
45
46
47 1
blueprint = create_blueprint('shop_order_admin', __name__)
48
49
50 1
@blueprint.get('/for_shop/<shop_id>', defaults={'page': 1})
51 1
@blueprint.get('/for_shop/<shop_id>/pages/<int:page>')
52 1
@permission_required('shop_order.view')
53 1
@templated
54 1
def index_for_shop(shop_id, page):
55
    """List orders for that shop."""
56
    shop = _get_shop_or_404(shop_id)
57
58
    brand = brand_service.get_brand(shop.brand_id)
59
60
    per_page = request.args.get('per_page', type=int, default=15)
61
62
    search_term = request.args.get('search_term', default='').strip()
63
64
    only_payment_state = request.args.get(
65
        'only_payment_state', type=PaymentState.__members__.get
66
    )
67
68
    def _str_to_bool(value):
69
        valid_values = {
70
            'true': True,
71
            'false': False,
72
        }
73
        return valid_values.get(value, False)
74
75
    only_overdue = request.args.get('only_overdue', type=_str_to_bool)
76
    only_processed = request.args.get('only_processed', type=_str_to_bool)
77
78
    order_state_filter = OrderStateFilter.find(
79
        only_payment_state, only_overdue, only_processed
80
    )
81
82
    orders = order_service.get_orders_for_shop_paginated(
83
        shop.id,
84
        page,
85
        per_page,
86
        search_term=search_term,
87
        only_payment_state=only_payment_state,
88
        only_overdue=only_overdue,
89
        only_processed=only_processed,
90
    )
91
92
    return {
93
        'shop': shop,
94
        'brand': brand,
95
        'per_page': per_page,
96
        'search_term': search_term,
97
        'PaymentState': PaymentState,
98
        'only_payment_state': only_payment_state,
99
        'only_overdue': only_overdue,
100
        'only_processed': only_processed,
101
        'OrderStateFilter': OrderStateFilter,
102
        'order_state_filter': order_state_filter,
103
        'orders': orders,
104
    }
105
106
107 1
@blueprint.get('/<uuid:order_id>')
108 1
@permission_required('shop_order.view')
109 1
@templated
110 1
def view(order_id):
111
    """Show a single order."""
112
    order = order_service.find_order_with_details_for_admin(order_id)
113
    if order is None:
114
        abort(404)
115
116
    shop = shop_service.get_shop(order.shop_id)
117
118
    brand = brand_service.get_brand(shop.brand_id)
119
120
    log_entries = service.get_enriched_log_entry_data_for_order(order.id)
121
122
    tickets = ticket_service.get_tickets_created_by_order(order.order_number)
123
124
    return {
125
        'shop': shop,
126
        'brand': brand,
127
        'order': order,
128
        'log_entries': log_entries,
129
        'PaymentState': PaymentState,
130
        'tickets': tickets,
131
        'render_order_payment_method': _find_order_payment_method_label,
132
    }
133
134
135
# -------------------------------------------------------------------- #
136
# export
137
138
139 1
@blueprint.get('/<uuid:order_id>/export')
140 1
@permission_required('shop_order.view')
141 1
def export(order_id):
142
    """Export the order as an XML document."""
143 1
    xml_export = order_export_service.export_order_as_xml(order_id)
144
145 1
    if xml_export is None:
146 1
        abort(404)
147
148 1
    return Response(
149
        xml_export['content'], content_type=xml_export['content_type']
150
    )
151
152
153
# -------------------------------------------------------------------- #
154
# notes
155
156
157 1
@blueprint.get('/<uuid:order_id>/notes/create')
158 1
@permission_required('shop_order.update')
159 1
@templated
160 1
def add_note_form(order_id, erroneous_form=None):
161
    """Show form to add a note to the order."""
162
    order = _get_order_or_404(order_id)
163
164
    shop = shop_service.get_shop(order.shop_id)
165
166
    brand = brand_service.get_brand(shop.brand_id)
167
168
    form = erroneous_form if erroneous_form else AddNoteForm()
169
170
    return {
171
        'shop': shop,
172
        'brand': brand,
173
        'order': order,
174
        'form': form,
175
    }
176
177
178 1
@blueprint.post('/<uuid:order_id>/notes')
179 1
@permission_required('shop_order.update')
180 1
def add_note(order_id):
181
    """Add a note to the order."""
182
    order = _get_order_or_404(order_id)
183
184
    form = AddNoteForm(request.form)
185
    if not form.validate():
186
        return add_note_form(order_id, form)
187
188
    text = form.text.data.strip()
189
190
    order_service.add_note(order, g.user, text)
191
192
    flash_success(gettext('Note has been added.'))
193
194
    return redirect_to('.view', order_id=order.id)
195
196
197
# -------------------------------------------------------------------- #
198
# flags
199
200
201 1
@blueprint.post('/<uuid:order_id>/flags/shipped')
202 1
@permission_required('shop_order.update')
203 1
@respond_no_content
204 1
def set_shipped_flag(order_id):
205
    """Mark the order as shipped."""
206
    order = _get_order_or_404(order_id)
207
    initiator = g.user
208
209
    result = order_service.set_shipped_flag(order, initiator)
210
211
    if result.is_err():
212
        flash_error(result.unwrap_err())
213
        return
214
215
    flash_success(
216
        gettext(
217
            'Order %(order_number)s has been marked as shipped.',
218
            order_number=order.order_number,
219
        )
220
    )
221
222
223 1
@blueprint.delete('/<uuid:order_id>/flags/shipped')
224 1
@permission_required('shop_order.update')
225 1
@respond_no_content
226 1
def unset_shipped_flag(order_id):
227
    """Mark the order as not shipped."""
228
    order = _get_order_or_404(order_id)
229
    initiator = g.user
230
231
    result = order_service.unset_shipped_flag(order, initiator)
232
233
    if result.is_err():
234
        flash_error(result.unwrap_err())
235
        return
236
237
    flash_success(
238
        gettext(
239
            'Order %(order_number)s has been marked as not shipped.',
240
            order_number=order.order_number,
241
        )
242
    )
243
244
245
# -------------------------------------------------------------------- #
246
# cancel
247
248
249 1
@blueprint.get('/<uuid:order_id>/cancel')
250 1
@permission_required('shop_order.cancel')
251 1
@templated
252 1
def cancel_form(order_id, erroneous_form=None):
253
    """Show form to cancel an order."""
254
    order = _get_order_or_404(order_id)
255
256
    if order.is_canceled:
257
        flash_error(
258
            gettext(
259
                'The order has already been canceled. '
260
                'The payment state cannot be changed anymore.'
261
            )
262
        )
263
        return redirect_to('.view', order_id=order.id)
264
265
    shop = shop_service.get_shop(order.shop_id)
266
267
    brand = brand_service.get_brand(shop.brand_id)
268
269
    form = erroneous_form if erroneous_form else CancelForm()
270
271
    return {
272
        'shop': shop,
273
        'brand': brand,
274
        'order': order,
275
        'form': form,
276
    }
277
278
279 1
@blueprint.post('/<uuid:order_id>/cancel')
280 1
@permission_required('shop_order.cancel')
281 1
def cancel(order_id):
282
    """Set the payment state of a single order to 'canceled' and
283
    release the respective article quantities.
284
    """
285 1
    order = _get_order_or_404(order_id)
286
287 1
    form = CancelForm(request.form)
288 1
    if not form.validate():
289
        return cancel_form(order_id, form)
290
291 1
    reason = form.reason.data.strip()
292 1
    send_email = form.send_email.data
293
294 1
    cancellation_result = order_service.cancel_order(order.id, g.user, reason)
295 1
    if cancellation_result.is_err():
296
        err = cancellation_result.unwrap_err()
297
        if isinstance(err, OrderAlreadyCanceledError):
298
            flash_error(
299
                gettext(
300
                    'The order has already been canceled. '
301
                    'The payment state cannot be changed anymore.'
302
                )
303
            )
304
        else:
305
            flash_error(gettext('An unexpected error occurred.'))
306
        return redirect_to('.view', order_id=order.id)
307
308 1
    canceled_order, event = cancellation_result.unwrap()
309
310 1
    flash_success(
311
        gettext(
312
            'The order has been canceled and the corresponding articles '
313
            'have been made available again.'
314
        )
315
    )
316
317 1
    if send_email:
318 1
        order_email_service.send_email_for_canceled_order_to_orderer(
319
            canceled_order
320
        )
321
    else:
322 1
        flash_notice(gettext('No email has been sent to the orderer.'))
323
324 1
    shop_signals.order_canceled.send(None, event=event)
325
326 1
    return redirect_to('.view', order_id=canceled_order.id)
327
328
329
# -------------------------------------------------------------------- #
330
# mark as paid
331
332
333 1
@blueprint.get('/<uuid:order_id>/mark_as_paid')
334 1
@permission_required('shop_order.mark_as_paid')
335 1
@templated
336 1
def mark_as_paid_form(order_id, erroneous_form=None):
337
    """Show form to mark an order as paid."""
338
    order = _get_order_or_404(order_id)
339
340
    if order.is_paid:
341
        flash_error(gettext('Order is already marked as paid.'))
342
        return redirect_to('.view', order_id=order.id)
343
344
    shop = shop_service.get_shop(order.shop_id)
345
346
    brand = brand_service.get_brand(shop.brand_id)
347
348
    form = erroneous_form if erroneous_form else MarkAsPaidForm()
349
    form.set_payment_method_choices()
350
351
    return {
352
        'shop': shop,
353
        'brand': brand,
354
        'order': order,
355
        'form': form,
356
    }
357
358
359 1
@blueprint.post('/<uuid:order_id>/mark_as_paid')
360 1
@permission_required('shop_order.mark_as_paid')
361 1
def mark_as_paid(order_id):
362
    """Set the payment state of a single order to 'paid'."""
363 1
    order = _get_order_or_404(order_id)
364
365 1
    form = MarkAsPaidForm(request.form)
366 1
    form.set_payment_method_choices()
367 1
    if not form.validate():
368
        return mark_as_paid_form(order_id, form)
369
370 1
    payment_method = form.payment_method.data
371 1
    initiator = g.user
372
373 1
    mark_as_paid_result = order_service.mark_order_as_paid(
374
        order.id, payment_method, initiator
375
    )
376 1
    if mark_as_paid_result.is_err():
377
        err = mark_as_paid_result.unwrap_err()
378
        if isinstance(err, OrderAlreadyMarkedAsPaidError):
379
            flash_error(gettext('Order is already marked as paid.'))
380
        else:
381
            flash_error(gettext('An unexpected error occurred.'))
382
        return redirect_to('.view', order_id=order.id)
383
384 1
    paid_order, event = mark_as_paid_result.unwrap()
385
386 1
    flash_success(gettext('Order has been marked as paid.'))
387
388 1
    order_email_service.send_email_for_paid_order_to_orderer(paid_order)
389
390 1
    shop_signals.order_paid.send(None, event=event)
391
392 1
    return redirect_to('.view', order_id=paid_order.id)
393
394
395
# -------------------------------------------------------------------- #
396
# email
397
398
399 1
@blueprint.post('/<uuid:order_id>/resend_incoming_order_email')
400 1
@permission_required('shop_order.update')
401 1
@respond_no_content
402 1
def resend_email_for_incoming_order_to_orderer(order_id):
403
    """Resend the e-mail to the orderer to confirm that the order was placed."""
404
    order = _get_order_or_404(order_id)
405
406
    initiator_id = g.user.id
407
408
    order_email_service.send_email_for_incoming_order_to_orderer(order)
409
410
    order_log_service.create_db_entry(
411
        'order-placed-confirmation-email-resent',
412
        order.id,
413
        {
414
            'initiator_id': str(initiator_id),
415
        },
416
    )
417
418
    flash_success(
419
        gettext('Email confirmation for placed order has been sent again.')
420
    )
421
422
423
# -------------------------------------------------------------------- #
424
# order number sequences
425
426
427 1
@blueprint.get('/number_sequences/for_shop/<shop_id>/create')
428 1
@permission_required('shop.update')
429 1
@templated
430 1
def create_number_sequence_form(shop_id, erroneous_form=None):
431
    """Show form to create an order number sequence."""
432
    shop = _get_shop_or_404(shop_id)
433
434
    brand = brand_service.get_brand(shop.brand_id)
435
436
    form = erroneous_form if erroneous_form else OrderNumberSequenceCreateForm()
437
438
    return {
439
        'shop': shop,
440
        'brand': brand,
441
        'form': form,
442
    }
443
444
445 1 View Code Duplication
@blueprint.post('/number_sequences/for_shop/<shop_id>')
446 1
@permission_required('shop.update')
447 1
def create_number_sequence(shop_id):
448
    """Create an order number sequence."""
449
    shop = _get_shop_or_404(shop_id)
450
451
    form = OrderNumberSequenceCreateForm(request.form)
452
    if not form.validate():
453
        return create_number_sequence_form(shop_id, form)
454
455
    prefix = form.prefix.data.strip()
456
457
    creation_result = order_sequence_service.create_order_number_sequence(
458
        shop.id, prefix
459
    )
460
    if creation_result.is_err():
461
        flash_error(
462
            gettext(
463
                'Order number sequence could not be created. '
464
                'Is the prefix "%(prefix)s" already defined?',
465
                prefix=prefix,
466
            )
467
        )
468
        return create_number_sequence_form(shop.id, form)
469
470
    flash_success(
471
        gettext(
472
            'Order number sequence with prefix "%(prefix)s" has been created.',
473
            prefix=prefix,
474
        )
475
    )
476
    return redirect_to('.index_for_shop', shop_id=shop.id)
477
478
479
# -------------------------------------------------------------------- #
480
# helpers
481
482
483 1
def _get_shop_or_404(shop_id):
484
    shop = shop_service.find_shop(shop_id)
485
486
    if shop is None:
487
        abort(404)
488
489
    return shop
490
491
492 1
def _get_order_or_404(order_id):
493 1
    order = order_service.find_order(order_id)
494
495 1
    if order is None:
496
        abort(404)
497
498 1
    return order
499
500
501 1
def _find_order_payment_method_label(payment_method):
502
    return order_service.find_payment_method_label(payment_method)
503