Completed
Branch main (de2ccb)
by Jochen
108:27 queued 104:49
created

create_number_sequence_form()   A

Complexity

Conditions 2

Size

Total Lines 12
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2.3149

Importance

Changes 0
Metric Value
cc 2
eloc 9
nop 2
dl 0
loc 12
ccs 4
cts 7
cp 0.5714
crap 2.3149
rs 9.95
c 0
b 0
f 0
1
"""
2
byceps.blueprints.admin.shop.order.views
3
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4
5
:Copyright: 2006-2020 Jochen Kupperschmidt
6
:License: Modified BSD, see LICENSE for details.
7
"""
8
9 1
from flask import abort, g, request, Response
10
11 1
from .....services.shop.order import (
12
    sequence_service as order_sequence_service,
13
    service as order_service,
14
)
15 1
from .....services.shop.order.email import service as order_email_service
16 1
from .....services.shop.order.export import service as order_export_service
17 1
from .....services.shop.order.transfer.models import PaymentMethod, PaymentState
18 1
from .....services.shop.shop import service as shop_service
19 1
from .....services.ticketing import ticket_service
20 1
from .....services.user import service as user_service
21 1
from .....signals import shop as shop_signals
22 1
from .....util.framework.blueprint import create_blueprint
23 1
from .....util.framework.flash import flash_error, flash_notice, flash_success
24 1
from .....util.framework.templating import templated
25 1
from .....util.views import redirect_to, respond_no_content
26
27 1
from ....common.authorization.decorators import permission_required
28 1
from ....common.authorization.registry import permission_registry
29
30 1
from ..shop.authorization import ShopPermission
31 1
from .authorization import ShopOrderPermission
32 1
from .forms import CancelForm, MarkAsPaidForm, OrderNumberSequenceCreateForm
33 1
from .models import OrderStateFilter
34 1
from . import service
35
36
37 1
blueprint = create_blueprint('shop_order_admin', __name__)
38
39
40 1
permission_registry.register_enum(ShopOrderPermission)
41
42
43 1
@blueprint.route('/for_shop/<shop_id>', defaults={'page': 1})
44 1
@blueprint.route('/for_shop/<shop_id>/pages/<int:page>')
45 1
@permission_required(ShopOrderPermission.view)
46 1
@templated
47
def index_for_shop(shop_id, page):
48
    """List orders for that shop."""
49
    shop = _get_shop_or_404(shop_id)
50
51
    per_page = request.args.get('per_page', type=int, default=15)
52
53
    search_term = request.args.get('search_term', default='').strip()
54
55
    only_payment_state = request.args.get(
56
        'only_payment_state', type=PaymentState.__members__.get
57
    )
58
59
    def _str_to_bool(value):
60
        valid_values = {
61
            'true': True,
62
            'false': False,
63
        }
64
        return valid_values.get(value, False)
65
66
    only_shipped = request.args.get('only_shipped', type=_str_to_bool)
67
68
    order_state_filter = OrderStateFilter.find(only_payment_state, only_shipped)
69
70
    orders = order_service.get_orders_for_shop_paginated(
71
        shop.id,
72
        page,
73
        per_page,
74
        search_term=search_term,
75
        only_payment_state=only_payment_state,
76
        only_shipped=only_shipped,
77
    )
78
79
    orders.items = list(service.extend_order_tuples_with_orderer(orders.items))
80
81
    return {
82
        'shop': shop,
83
        'search_term': search_term,
84
        'PaymentState': PaymentState,
85
        'only_payment_state': only_payment_state,
86
        'only_shipped': only_shipped,
87
        'OrderStateFilter': OrderStateFilter,
88
        'order_state_filter': order_state_filter,
89
        'orders': orders,
90
        'render_order_payment_method': _find_order_payment_method_label,
91
    }
92
93
94 1
@blueprint.route('/<uuid:order_id>')
95 1
@permission_required(ShopOrderPermission.view)
96 1
@templated
97
def view(order_id):
98
    """Show a single order."""
99
    order = order_service.find_order_with_details(order_id)
100
    if order is None:
101
        abort(404)
102
103
    placed_by = user_service.get_user(order.placed_by_id, include_avatar=True)
104
105
    shop = shop_service.get_shop(order.shop_id)
106
107
    articles_by_item_number = service.get_articles_by_item_number(order)
108
109
    events = service.get_events(order.id)
110
111
    tickets = ticket_service.find_tickets_created_by_order(order.order_number)
112
113
    return {
114
        'shop': shop,
115
        'order': order,
116
        'placed_by': placed_by,
117
        'articles_by_item_number': articles_by_item_number,
118
        'events': events,
119
        'PaymentMethod': PaymentMethod,
120
        'PaymentState': PaymentState,
121
        'tickets': tickets,
122
        'render_order_payment_method': _find_order_payment_method_label,
123
    }
124
125
126
# -------------------------------------------------------------------- #
127
# export
128
129
130 1
@blueprint.route('/<uuid:order_id>/export')
131 1
@permission_required(ShopOrderPermission.view)
132
def export(order_id):
133
    """Export the order as an XML document."""
134 1
    xml_export = order_export_service.export_order_as_xml(order_id)
135
136 1
    if xml_export is None:
137 1
        abort(404)
138
139 1
    return Response(
140
        xml_export['content'], content_type=xml_export['content_type']
141
    )
142
143
144
# -------------------------------------------------------------------- #
145
# flags
146
147
148 1
@blueprint.route('/<uuid:order_id>/flags/invoiced', methods=['POST'])
149 1
@permission_required(ShopOrderPermission.update)
150 1
@respond_no_content
151
def set_invoiced_flag(order_id):
152
    """Mark the order as invoiced."""
153
    order = _get_order_or_404(order_id)
154
    initiator_id = g.current_user.id
155
156
    order_service.set_invoiced_flag(order.id, initiator_id)
157
158
    flash_success(
159
        f'Bestellung {order.order_number} wurde '
160
        'als in Rechnung gestellt markiert.'
161
    )
162
163
164 1
@blueprint.route('/<uuid:order_id>/flags/invoiced', methods=['DELETE'])
165 1
@permission_required(ShopOrderPermission.update)
166 1
@respond_no_content
167
def unset_invoiced_flag(order_id):
168
    """Mark the order as not invoiced."""
169
    order = _get_order_or_404(order_id)
170
    initiator_id = g.current_user.id
171
172
    order_service.unset_invoiced_flag(order.id, initiator_id)
173
174
    flash_success(
175
        f'Bestellung {order.order_number} wurde '
176
        'als nicht in Rechnung gestellt markiert.'
177
    )
178
179
180 1
@blueprint.route('/<uuid:order_id>/flags/shipped', methods=['POST'])
181 1
@permission_required(ShopOrderPermission.update)
182 1
@respond_no_content
183
def set_shipped_flag(order_id):
184
    """Mark the order as shipped."""
185
    order = _get_order_or_404(order_id)
186
    initiator_id = g.current_user.id
187
188
    order_service.set_shipped_flag(order.id, initiator_id)
189
190
    flash_success(
191
        f'Bestellung {order.order_number} wurde als verschickt markiert.'
192
    )
193
194
195 1
@blueprint.route('/<uuid:order_id>/flags/shipped', methods=['DELETE'])
196 1
@permission_required(ShopOrderPermission.update)
197 1
@respond_no_content
198
def unset_shipped_flag(order_id):
199
    """Mark the order as not shipped."""
200
    order = _get_order_or_404(order_id)
201
    initiator_id = g.current_user.id
202
203
    order_service.unset_shipped_flag(order.id, initiator_id)
204
205
    flash_success(
206
        f'Bestellung {order.order_number} wurde als nicht verschickt markiert.'
207
    )
208
209
210
# -------------------------------------------------------------------- #
211
# cancel
212
213
214 1
@blueprint.route('/<uuid:order_id>/cancel')
215 1
@permission_required(ShopOrderPermission.cancel)
216 1
@templated
217 1
def cancel_form(order_id, erroneous_form=None):
218
    """Show form to cancel an order."""
219
    order = _get_order_or_404(order_id)
220
221
    if order.is_canceled:
222
        flash_error(
223
            'Die Bestellung ist bereits storniert worden; '
224
            'der Bezahlstatus kann nicht mehr geändert werden.'
225
        )
226
        return redirect_to('.view', order_id=order.id)
227
228
    shop = shop_service.get_shop(order.shop_id)
229
230
    form = erroneous_form if erroneous_form else CancelForm()
231
232
    return {
233
        'shop': shop,
234
        'order': order,
235
        'form': form,
236
    }
237
238
239 1
@blueprint.route('/<uuid:order_id>/cancel', methods=['POST'])
240 1
@permission_required(ShopOrderPermission.cancel)
241
def cancel(order_id):
242
    """Set the payment status of a single order to 'canceled' and
243
    release the respective article quantities.
244
    """
245 1
    order = _get_order_or_404(order_id)
246
247 1
    form = CancelForm(request.form)
248 1
    if not form.validate():
249
        return cancel_form(order_id, form)
250
251 1
    reason = form.reason.data.strip()
252 1
    send_email = form.send_email.data
253
254 1
    try:
255 1
        event = order_service.cancel_order(order.id, g.current_user.id, reason)
256
    except order_service.OrderAlreadyCanceled:
257
        flash_error(
258
            'Die Bestellung ist bereits storniert worden; '
259
            'der Bezahlstatus kann nicht mehr geändert werden.'
260
        )
261
        return redirect_to('.view', order_id=order.id)
262
263 1
    flash_success(
264
        'Die Bestellung wurde als storniert markiert und die betroffenen '
265
        'Artikel in den entsprechenden Stückzahlen wieder zur Bestellung '
266
        'freigegeben.'
267
    )
268
269 1
    if send_email:
270 1
        order_email_service.send_email_for_canceled_order_to_orderer(order.id)
271
    else:
272 1
        flash_notice(
273
            'Es wurde keine E-Mail an den/die Auftraggeber/in versendet.'
274
        )
275
276 1
    shop_signals.order_canceled.send(None, event=event)
277
278 1
    return redirect_to('.view', order_id=order.id)
279
280
281
# -------------------------------------------------------------------- #
282
# mark as paid
283
284
285 1
@blueprint.route('/<uuid:order_id>/mark_as_paid')
286 1
@permission_required(ShopOrderPermission.mark_as_paid)
287 1
@templated
288 1
def mark_as_paid_form(order_id, erroneous_form=None):
289
    """Show form to mark an order as paid."""
290
    order = _get_order_or_404(order_id)
291
292
    if order.is_paid:
293
        flash_error('Die Bestellung ist bereits als bezahlt markiert worden.')
294
        return redirect_to('.view', order_id=order.id)
295
296
    shop = shop_service.get_shop(order.shop_id)
297
298
    form = erroneous_form if erroneous_form else MarkAsPaidForm()
299
300
    return {
301
        'shop': shop,
302
        'order': order,
303
        'form': form,
304
    }
305
306
307 1
@blueprint.route('/<uuid:order_id>/mark_as_paid', methods=['POST'])
308 1
@permission_required(ShopOrderPermission.mark_as_paid)
309
def mark_as_paid(order_id):
310
    """Set the payment status of a single order to 'paid'."""
311 1
    order = _get_order_or_404(order_id)
312
313 1
    form = MarkAsPaidForm(request.form)
314 1
    if not form.validate():
315
        return mark_as_paid_form(order_id, form)
316
317 1
    payment_method = PaymentMethod[form.payment_method.data]
318 1
    updated_by_id = g.current_user.id
319
320 1
    try:
321 1
        event = order_service.mark_order_as_paid(
322
            order.id, payment_method, updated_by_id
323
        )
324
    except order_service.OrderAlreadyMarkedAsPaid:
325
        flash_error('Die Bestellung ist bereits als bezahlt markiert worden.')
326
        return redirect_to('.view', order_id=order.id)
327
328 1
    flash_success('Die Bestellung wurde als bezahlt markiert.')
329
330 1
    order_email_service.send_email_for_paid_order_to_orderer(order.id)
331
332 1
    shop_signals.order_paid.send(None, event=event)
333
334 1
    return redirect_to('.view', order_id=order.id)
335
336
337
# -------------------------------------------------------------------- #
338
# email
339
340
341 1
@blueprint.route(
342
    '/<uuid:order_id>/resend_incoming_order_email', methods=['POST']
343
)
344 1
@permission_required(ShopOrderPermission.update)
345 1
@respond_no_content
346
def resend_email_for_incoming_order_to_orderer(order_id):
347
    """Resend the e-mail to the orderer to confirm that the order was placed."""
348
    order = _get_order_or_404(order_id)
349
350
    order_email_service.send_email_for_incoming_order_to_orderer(order.id)
351
352
    flash_success('Die E-Mail-Eingangsbestätigung wurde erneut versendet.')
353
354
355
# -------------------------------------------------------------------- #
356
# order number sequences
357
358
359 1
@blueprint.route('/number_sequences/for_shop/<shop_id>/create')
360 1
@permission_required(ShopPermission.update)
361 1
@templated
362 1
def create_number_sequence_form(shop_id, erroneous_form=None):
363
    """Show form to create an order number sequence."""
364
    shop = _get_shop_or_404(shop_id)
365
366
    form = erroneous_form if erroneous_form else OrderNumberSequenceCreateForm()
367
368
    return {
369
        'shop': shop,
370
        'form': form,
371
    }
372
373
374 1
@blueprint.route('/number_sequences/for_shop/<shop_id>', methods=['POST'])
375 1
@permission_required(ShopPermission.update)
376
def create_number_sequence(shop_id):
377
    """Create an order number sequence."""
378
    shop = _get_shop_or_404(shop_id)
379
380
    form = OrderNumberSequenceCreateForm(request.form)
381
    if not form.validate():
382
        return create_number_sequence_form(shop_id, form)
383
384
    prefix = form.prefix.data.strip()
385
386
    sequence_id = order_sequence_service.create_order_number_sequence(
387
        shop.id, prefix
388
    )
389
    if sequence_id is None:
390
        flash_error(
391
            'Die Bestellnummer-Sequenz konnte nicht angelegt werden. '
392
            f'Ist das Präfix "{prefix}" bereits definiert?'
393
        )
394
        return create_number_sequence_form(shop.id, form)
395
396
    flash_success(
397
        f'Die Bestellnummer-Sequenz mit dem Präfix "{prefix}" wurde angelegt.'
398
    )
399
    return redirect_to('.index_for_shop', shop_id=shop.id)
400
401
402
# -------------------------------------------------------------------- #
403
# helpers
404
405
406 1
def _get_shop_or_404(shop_id):
407
    shop = shop_service.find_shop(shop_id)
408
409
    if shop is None:
410
        abort(404)
411
412
    return shop
413
414
415 1
def _get_order_or_404(order_id):
416 1
    order = order_service.find_order(order_id)
417
418 1
    if order is None:
419
        abort(404)
420
421 1
    return order
422
423
424 1
def _find_order_payment_method_label(payment_method):
425
    return order_service.find_payment_method_label(payment_method)
426