Completed
Branch master (c65a32)
by Jochen
02:08
created

_create_cart_from_article_compilation()   A

Complexity

Conditions 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 6
nop 1
dl 0
loc 9
rs 10
c 0
b 0
f 0
1
"""
2
byceps.blueprints.shop.order.views
3
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4
5
:Copyright: 2006-2019 Jochen Kupperschmidt
6
:License: Modified BSD, see LICENSE for details.
7
"""
8
9
from flask import abort, g, request
10
11
from ....services.country import service as country_service
12
from ....services.party import service as party_service
13
from ....services.shop.article import service as article_service
14
from ....services.shop.article.models.compilation import ArticleCompilation
15
from ....services.shop.cart.models import Cart
16
from ....services.shop.order.email import service as order_email_service
17
from ....services.shop.order import service as order_service
18
from ....services.shop.order.transfer.models import PaymentMethod
19
from ....services.shop.shop import service as shop_service
20
from ....services.user import service as user_service
21
from ....util.framework.blueprint import create_blueprint
22
from ....util.framework.flash import flash_error, flash_notice, flash_success
23
from ....util.framework.templating import templated
24
from ....util.views import redirect_to
25
26
from ...authentication.decorators import login_required
27
28
from .forms import assemble_articles_order_form, OrderForm
29
from . import signals
30
31
32
blueprint = create_blueprint('shop_order', __name__)
33
34
35
@blueprint.route('/order')
36
@templated
37
def order_form(erroneous_form=None):
38
    """Show a form to order articles."""
39
    shop = _get_shop_or_404()
40
41
    if shop.closed:
42
        flash_notice('Der Shop ist derzeit geschlossen.')
43
        return {'article_compilation': None}
44
45
    article_compilation = article_service.get_article_compilation_for_orderable_articles(
46
        shop.id
47
    )
48
49
    if article_compilation.is_empty():
50
        flash_error('Es sind keine Artikel verfügbar.')
51
        return {'article_compilation': None}
52
53
    is_logged_in = g.current_user.is_active
54
    if not is_logged_in:
55
        return list_articles(article_compilation)
56
57
    user = user_service.find_user_with_details(g.current_user.id)
58
59
    if erroneous_form:
60
        form = erroneous_form
61
    else:
62
        ArticlesOrderForm = assemble_articles_order_form(article_compilation)
63
        form = ArticlesOrderForm(obj=user.detail)
64
65
    country_names = country_service.get_country_names()
66
67
    return {
68
        'form': form,
69
        'country_names': country_names,
70
        'article_compilation': article_compilation,
71
    }
72
73
74
# No route registered. Intended to be called from another view function.
75
@templated
76
def list_articles(article_compilation):
77
    """List articles for anonymous users to view."""
78
    return {
79
        'article_compilation': article_compilation,
80
    }
81
82
83
@blueprint.route('/order', methods=['POST'])
84
@login_required
85
def order():
86
    """Order articles."""
87
    shop = _get_shop_or_404()
88
89
    if shop.closed:
90
        flash_notice('Der Shop ist derzeit geschlossen.')
91
        return order_form()
92
93
    article_compilation = article_service.get_article_compilation_for_orderable_articles(
94
        shop.id
95
    )
96
97
    if article_compilation.is_empty():
98
        flash_error('Es sind keine Artikel verfügbar.')
99
        return order_form()
100
101
    ArticlesOrderForm = assemble_articles_order_form(article_compilation)
102
    form = ArticlesOrderForm(request.form)
103
104
    if not form.validate():
105
        return order_form(form)
106
107
    cart = form.get_cart(article_compilation)
108
109
    if cart.is_empty():
110
        flash_error('Es wurden keine Artikel ausgewählt.')
111
        return order_form(form)
112
113
    orderer = form.get_orderer(g.current_user.id)
114
115
    try:
116
        order = _place_order(shop.id, orderer, cart)
117
    except order_service.OrderFailed:
118
        flash_error('Die Bestellung ist fehlgeschlagen.')
119
        return order_form(form)
120
121
    _flash_order_success(order)
122
123
    return redirect_to('shop_orders.view', order_id=order.id)
124
125
126
@blueprint.route('/order_single/<uuid:article_id>')
127
@login_required
128
@templated
129
def order_single_form(article_id, erroneous_form=None):
130
    """Show a form to order a single article."""
131
    article = _get_article_or_404(article_id)
132
133
    shop = _get_shop_or_404()
134
135
    user = user_service.find_user_with_details(g.current_user.id)
136
137
    form = erroneous_form if erroneous_form else OrderForm(obj=user.detail)
138
139
    if shop.closed:
140
        flash_notice('Der Shop ist derzeit geschlossen.')
141
        return {
142
            'form': form,
143
            'article': None,
144
        }
145
146
    article_compilation = article_service.get_article_compilation_for_single_article(
147
        article, fixed_quantity=1
148
    )
149
150
    country_names = country_service.get_country_names()
151
152
    if article.not_directly_orderable:
153
        flash_error('Der Artikel kann nicht direkt bestellt werden.')
154
        return {
155
            'form': form,
156
            'article': None,
157
        }
158
159
    if order_service.has_user_placed_orders(user.id, shop.id):
160
        flash_error('Du kannst keine weitere Bestellung aufgeben.')
161
        return {
162
            'form': form,
163
            'article': None,
164
        }
165
166
    if article.quantity < 1 or not article.is_available:
167
        flash_error('Der Artikel ist nicht verfügbar.')
168
        return {
169
            'form': form,
170
            'article': None,
171
        }
172
173
    return {
174
        'form': form,
175
        'country_names': country_names,
176
        'article': article,
177
        'article_compilation': article_compilation,
178
    }
179
180
181
@blueprint.route('/order_single/<uuid:article_id>', methods=['POST'])
182
@login_required
183
def order_single(article_id):
184
    """Order a single article."""
185
    article = _get_article_or_404(article_id)
186
    quantity = 1
187
188
    shop = _get_shop_or_404()
189
190
    if shop.closed:
191
        flash_notice('Der Shop ist derzeit geschlossen.')
192
        return order_single_form(article.id)
193
194
    if article.not_directly_orderable:
195
        flash_error('Der Artikel kann nicht direkt bestellt werden.')
196
        return order_single_form(article.id)
197
198
    article_compilation = article_service.get_article_compilation_for_single_article(
199
        article, fixed_quantity=quantity
200
    )
201
202
    user = g.current_user
203
204
    if order_service.has_user_placed_orders(user.id, shop.id):
205
        flash_error('Du kannst keine weitere Bestellung aufgeben.')
206
        return order_single_form(article.id)
207
208
    if article.quantity < 1 or not article.is_available:
209
        flash_error('Der Artikel ist nicht verfügbar.')
210
        return order_single_form(article.id)
211
212
    form = OrderForm(request.form)
213
    if not form.validate():
214
        return order_single_form(article.id, form)
215
216
    orderer = form.get_orderer(user.id)
217
218
    cart = _create_cart_from_article_compilation(article_compilation)
219
220
    try:
221
        order = _place_order(shop.id, orderer, cart)
222
    except order_service.OrderFailed:
223
        flash_error('Die Bestellung ist fehlgeschlagen.')
224
        return order_form(form)
225
226
    _flash_order_success(order)
227
228
    return redirect_to('shop_orders.view', order_id=order.id)
229
230
231
def _get_shop_or_404():
232
    if g.party_id is None:
233
        # No party is configured for the current site.
234
        abort(404)
235
236
    party = party_service.get_party(g.party_id)
237
238
    if party.shop_id is None:
239
        abort(404)
240
241
    return shop_service.get_shop(party.shop_id)
242
243
244
def _get_article_or_404(article_id):
245
    article = article_service.find_article(article_id)
246
247
    if article is None:
248
        abort(404)
249
250
    return article
251
252
253
def _create_cart_from_article_compilation(
254
    article_compilation: ArticleCompilation
255
) -> Cart:
256
    cart = Cart()
257
258
    for item in article_compilation:
259
        cart.add_item(item.article, item.fixed_quantity)
260
261
    return cart
262
263
264
def _place_order(shop_id, orderer, cart):
265
    payment_method = PaymentMethod.bank_transfer
266
267
    order, event = order_service.place_order(
268
        shop_id, orderer, payment_method, cart
269
    )
270
271
    order_email_service.send_email_for_incoming_order_to_orderer(order.id)
272
273
    signals.order_placed.send(None, event=event)
274
275
    return order
276
277
278
def _flash_order_success(order):
279
    flash_success(
280
        'Deine Bestellung mit der '
281
        f'Bestellnummer <strong>{order.order_number}</strong> '
282
        'wurde entgegen genommen. Vielen Dank!',
283
        text_is_safe=True,
284
    )
285