~homeworkprod/byceps

ref: e6f5debad1efcca29947351164b9171c6fd60611 byceps/byceps/blueprints/site/shop/order/views.py -rw-r--r-- 8.6 KiB
e6f5deba — Jochen Kupperschmidt Wrap flash message texts in `gettext` calls for translation 10 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
"""
byceps.blueprints.site.shop.order.views
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

:Copyright: 2006-2021 Jochen Kupperschmidt
:License: Revised BSD (see `LICENSE` file for details)
"""

from decimal import Decimal

from flask import abort, g, request
from flask_babel import gettext

from .....services.country import service as country_service
from .....services.shop.article import service as article_service
from .....services.shop.article.models.compilation import ArticleCompilation
from .....services.shop.cart.models import Cart
from .....services.shop.order.email import service as order_email_service
from .....services.shop.order import service as order_service
from .....services.shop.shop import service as shop_service
from .....services.shop.storefront import service as storefront_service
from .....services.site import service as site_service
from .....services.user import service as user_service
from .....signals import shop as shop_signals
from .....util.framework.blueprint import create_blueprint
from .....util.framework.flash import flash_error, flash_notice, flash_success
from .....util.framework.templating import templated
from .....util.views import login_required, redirect_to

from .forms import assemble_articles_order_form, OrderForm


blueprint = create_blueprint('shop_order', __name__)


@blueprint.add_app_template_filter
def tax_rate_as_percentage(tax_rate) -> str:
    # Keep a digit after the decimal point in case
    # the tax rate is a fractional number.
    percentage = (tax_rate * 100).quantize(Decimal('.0'))
    return str(percentage).replace('.', ',')


@blueprint.route('/order')
@templated
def order_form(erroneous_form=None):
    """Show a form to order articles."""
    storefront = _get_storefront_or_404()
    shop = shop_service.get_shop(storefront.shop_id)

    if storefront.closed:
        flash_notice(gettext('Der Shop ist derzeit geschlossen.'))
        return {'article_compilation': None}

    article_compilation = article_service.get_article_compilation_for_orderable_articles(
        shop.id
    )

    if article_compilation.is_empty():
        flash_error(gettext('Es sind keine Artikel verfügbar.'))
        return {'article_compilation': None}

    is_logged_in = g.user.is_active
    if not is_logged_in:
        return list_articles(article_compilation)

    user = user_service.find_user_with_details(g.user.id)

    if erroneous_form:
        form = erroneous_form
    else:
        ArticlesOrderForm = assemble_articles_order_form(article_compilation)
        form = ArticlesOrderForm(obj=user.detail)

    country_names = country_service.get_country_names()

    return {
        'form': form,
        'country_names': country_names,
        'article_compilation': article_compilation,
    }


# No route registered. Intended to be called from another view function.
@templated
def list_articles(article_compilation):
    """List articles for anonymous users to view."""
    return {
        'article_compilation': article_compilation,
    }


@blueprint.route('/order', methods=['POST'])
@login_required
def order():
    """Order articles."""
    storefront = _get_storefront_or_404()
    shop = shop_service.get_shop(storefront.shop_id)

    if storefront.closed:
        flash_notice(gettext('Der Shop ist derzeit geschlossen.'))
        return order_form()

    article_compilation = article_service.get_article_compilation_for_orderable_articles(
        shop.id
    )

    if article_compilation.is_empty():
        flash_error(gettext('Es sind keine Artikel verfügbar.'))
        return order_form()

    ArticlesOrderForm = assemble_articles_order_form(article_compilation)
    form = ArticlesOrderForm(request.form)

    if not form.validate():
        return order_form(form)

    cart = form.get_cart(article_compilation)

    if cart.is_empty():
        flash_error(gettext('Es wurden keine Artikel ausgewählt.'))
        return order_form(form)

    orderer = form.get_orderer(g.user.id)

    try:
        order = _place_order(storefront.id, orderer, cart)
    except order_service.OrderFailed:
        flash_error(gettext('Die Bestellung ist fehlgeschlagen.'))
        return order_form(form)

    _flash_order_success(order)

    return redirect_to('shop_orders.view', order_id=order.id)


@blueprint.route('/order_single/<uuid:article_id>')
@login_required
@templated
def order_single_form(article_id, erroneous_form=None):
    """Show a form to order a single article."""
    article = _get_article_or_404(article_id)

    storefront = _get_storefront_or_404()
    shop = shop_service.get_shop(storefront.shop_id)

    user = user_service.find_user_with_details(g.user.id)

    form = erroneous_form if erroneous_form else OrderForm(obj=user.detail)

    if storefront.closed:
        flash_notice(gettext('Der Shop ist derzeit geschlossen.'))
        return {
            'form': form,
            'article': None,
        }

    article_compilation = article_service.get_article_compilation_for_single_article(
        article.id, fixed_quantity=1
    )

    country_names = country_service.get_country_names()

    if article.not_directly_orderable:
        flash_error(gettext('Der Artikel kann nicht direkt bestellt werden.'))
        return {
            'form': form,
            'article': None,
        }

    if order_service.has_user_placed_orders(user.id, shop.id):
        flash_error(gettext('Du kannst keine weitere Bestellung aufgeben.'))
        return {
            'form': form,
            'article': None,
        }

    if article.quantity < 1 or not article_service.is_article_available_now(
        article
    ):
        flash_error(gettext('Der Artikel ist nicht verfügbar.'))
        return {
            'form': form,
            'article': None,
        }

    return {
        'form': form,
        'country_names': country_names,
        'article': article,
        'article_compilation': article_compilation,
    }


@blueprint.route('/order_single/<uuid:article_id>', methods=['POST'])
@login_required
def order_single(article_id):
    """Order a single article."""
    article = _get_article_or_404(article_id)
    quantity = 1

    storefront = _get_storefront_or_404()
    shop = shop_service.get_shop(storefront.shop_id)

    if storefront.closed:
        flash_notice(gettext('Der Shop ist derzeit geschlossen.'))
        return order_single_form(article.id)

    if article.not_directly_orderable:
        flash_error(gettext('Der Artikel kann nicht direkt bestellt werden.'))
        return order_single_form(article.id)

    article_compilation = article_service.get_article_compilation_for_single_article(
        article.id, fixed_quantity=quantity
    )

    user = g.user

    if order_service.has_user_placed_orders(user.id, shop.id):
        flash_error(gettext('Du kannst keine weitere Bestellung aufgeben.'))
        return order_single_form(article.id)

    if article.quantity < 1 or not article_service.is_article_available_now(
        article
    ):
        flash_error(gettext('Der Artikel ist nicht verfügbar.'))
        return order_single_form(article.id)

    form = OrderForm(request.form)
    if not form.validate():
        return order_single_form(article.id, form)

    orderer = form.get_orderer(user.id)

    cart = _create_cart_from_article_compilation(article_compilation)

    try:
        order = _place_order(storefront.id, orderer, cart)
    except order_service.OrderFailed:
        flash_error(gettext('Die Bestellung ist fehlgeschlagen.'))
        return order_form(form)

    _flash_order_success(order)

    return redirect_to('shop_orders.view', order_id=order.id)


def _get_storefront_or_404():
    site = site_service.get_site(g.site_id)
    storefront_id = site.storefront_id
    if storefront_id is None:
        abort(404)

    return storefront_service.get_storefront(storefront_id)


def _get_article_or_404(article_id):
    article = article_service.find_db_article(article_id)

    if article is None:
        abort(404)

    return article


def _create_cart_from_article_compilation(
    article_compilation: ArticleCompilation,
) -> Cart:
    cart = Cart()

    for item in article_compilation:
        cart.add_item(item.article, item.fixed_quantity)

    return cart


def _place_order(storefront_id, orderer, cart):
    order, event = order_service.place_order(storefront_id, orderer, cart)

    order_email_service.send_email_for_incoming_order_to_orderer(order.id)

    shop_signals.order_placed.send(None, event=event)

    return order


def _flash_order_success(order):
    flash_success(
        gettext(
            'Deine Bestellung mit der '
            'Bestellnummer <strong>%(order_number)s</strong> '
            'wurde entgegengenommen. Vielen Dank!',
            order_number=order.order_number,
        ),
        text_is_safe=True,
    )