1 | """ |
||
2 | byceps.blueprints.admin.shop.article.views |
||
3 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
||
4 | |||
5 | :Copyright: 2006-2021 Jochen Kupperschmidt |
||
6 | :License: Revised BSD (see `LICENSE` file for details) |
||
7 | """ |
||
8 | |||
9 | 1 | from __future__ import annotations |
|
10 | 1 | from datetime import datetime |
|
11 | 1 | from decimal import Decimal |
|
12 | |||
13 | 1 | from flask import abort, request |
|
14 | 1 | from flask_babel import gettext |
|
15 | |||
16 | 1 | from .....services.brand import service as brand_service |
|
17 | 1 | from .....services.party import service as party_service |
|
18 | 1 | from .....services.party.transfer.models import Party |
|
19 | 1 | from .....services.shop.article import ( |
|
20 | sequence_service as article_sequence_service, |
||
21 | service as article_service, |
||
22 | ) |
||
23 | 1 | from .....services.shop.order import ( |
|
24 | action_registry_service, |
||
25 | action_service, |
||
26 | ordered_articles_service, |
||
27 | service as order_service, |
||
28 | ) |
||
29 | 1 | from .....services.shop.order.transfer.models import PaymentState |
|
30 | 1 | from .....services.shop.shop import service as shop_service |
|
31 | 1 | from .....services.ticketing import category_service as ticket_category_service |
|
32 | 1 | from .....services.ticketing.transfer.models import TicketCategory |
|
33 | 1 | from .....services.user import service as user_service |
|
34 | 1 | from .....services.user_badge import badge_service |
|
35 | 1 | from .....typing import BrandID |
|
36 | 1 | from .....util.authorization import register_permission_enum |
|
37 | 1 | from .....util.framework.blueprint import create_blueprint |
|
38 | 1 | from .....util.framework.flash import flash_error, flash_success |
|
39 | 1 | from .....util.framework.templating import templated |
|
40 | 1 | from .....util.templatefilters import local_tz_to_utc, utc_to_local_tz |
|
41 | 1 | from .....util.views import permission_required, redirect_to, respond_no_content |
|
42 | |||
43 | 1 | from .authorization import ShopArticlePermission |
|
44 | 1 | from .forms import ( |
|
45 | ArticleCreateForm, |
||
46 | ArticleUpdateForm, |
||
47 | ArticleAttachmentCreateForm, |
||
48 | ArticleNumberSequenceCreateForm, |
||
49 | RegisterBadgeAwardingActionForm, |
||
50 | RegisterTicketBundlesCreationActionForm, |
||
51 | RegisterTicketsCreationActionForm, |
||
52 | ) |
||
53 | |||
54 | |||
55 | 1 | blueprint = create_blueprint('shop_article_admin', __name__) |
|
56 | |||
57 | |||
58 | 1 | register_permission_enum(ShopArticlePermission) |
|
59 | |||
60 | |||
61 | 1 | TAX_RATE_DISPLAY_FACTOR = Decimal('100') |
|
62 | |||
63 | |||
64 | 1 | @blueprint.get('/for_shop/<shop_id>', defaults={'page': 1}) |
|
65 | 1 | @blueprint.get('/for_shop/<shop_id>/pages/<int:page>') |
|
66 | 1 | @permission_required(ShopArticlePermission.view) |
|
67 | 1 | @templated |
|
68 | def index_for_shop(shop_id, page): |
||
69 | """List articles for that shop.""" |
||
70 | shop = _get_shop_or_404(shop_id) |
||
71 | |||
72 | brand = brand_service.get_brand(shop.brand_id) |
||
73 | |||
74 | per_page = request.args.get('per_page', type=int, default=15) |
||
75 | articles = article_service.get_articles_for_shop_paginated( |
||
76 | shop.id, page, per_page |
||
77 | ) |
||
78 | |||
79 | return { |
||
80 | 'shop': shop, |
||
81 | 'brand': brand, |
||
82 | 'articles': articles, |
||
83 | } |
||
84 | |||
85 | |||
86 | 1 | @blueprint.get('/<uuid:article_id>') |
|
87 | 1 | @permission_required(ShopArticlePermission.view) |
|
88 | 1 | @templated |
|
89 | def view(article_id): |
||
90 | """Show a single article.""" |
||
91 | article = article_service.find_article_with_details(article_id) |
||
92 | if article is None: |
||
93 | abort(404) |
||
94 | |||
95 | shop = shop_service.get_shop(article.shop_id) |
||
96 | |||
97 | brand = brand_service.get_brand(shop.brand_id) |
||
98 | |||
99 | totals = ordered_articles_service.count_ordered_articles( |
||
100 | article.item_number |
||
101 | ) |
||
102 | |||
103 | actions = action_service.get_actions_for_article(article.item_number) |
||
104 | actions.sort(key=lambda a: a.payment_state.name, reverse=True) |
||
105 | |||
106 | return { |
||
107 | 'article': article, |
||
108 | 'shop': shop, |
||
109 | 'brand': brand, |
||
110 | 'totals': totals, |
||
111 | 'PaymentState': PaymentState, |
||
112 | 'actions': actions, |
||
113 | } |
||
114 | |||
115 | |||
116 | 1 | @blueprint.get('/<uuid:article_id>/ordered') |
|
117 | 1 | @permission_required(ShopArticlePermission.view) |
|
118 | 1 | @templated |
|
119 | def view_ordered(article_id): |
||
120 | """List the people that have ordered this article, and the |
||
121 | corresponding quantities. |
||
122 | """ |
||
123 | article = _get_article_or_404(article_id) |
||
124 | |||
125 | shop = shop_service.get_shop(article.shop_id) |
||
126 | |||
127 | brand = brand_service.get_brand(shop.brand_id) |
||
128 | |||
129 | order_items = ordered_articles_service.get_order_items_for_article( |
||
130 | article.item_number |
||
131 | ) |
||
132 | |||
133 | quantity_total = sum(item.quantity for item in order_items) |
||
134 | |||
135 | order_numbers = {item.order_number for item in order_items} |
||
136 | orders = order_service.find_orders_by_order_numbers(order_numbers) |
||
137 | orders_by_order_numbers = {order.order_number: order for order in orders} |
||
138 | |||
139 | user_ids = {order.placed_by_id for order in orders} |
||
140 | users = user_service.find_users(user_ids, include_avatars=True) |
||
141 | users_by_id = {user.id: user for user in users} |
||
142 | |||
143 | def transform(order_item): |
||
144 | quantity = order_item.quantity |
||
145 | order = orders_by_order_numbers[order_item.order_number] |
||
146 | user = users_by_id[order.placed_by_id] |
||
147 | |||
148 | return quantity, order, user |
||
149 | |||
150 | quantities_orders_users = list(map(transform, order_items)) |
||
151 | |||
152 | return { |
||
153 | 'article': article, |
||
154 | 'shop': shop, |
||
155 | 'brand': brand, |
||
156 | 'quantity_total': quantity_total, |
||
157 | 'quantities_orders_users': quantities_orders_users, |
||
158 | 'now': datetime.utcnow(), |
||
159 | } |
||
160 | |||
161 | |||
162 | # -------------------------------------------------------------------- # |
||
163 | # create |
||
164 | |||
165 | |||
166 | 1 | @blueprint.get('/for_shop/<shop_id>/create') |
|
167 | 1 | @permission_required(ShopArticlePermission.create) |
|
168 | 1 | @templated |
|
169 | 1 | def create_form(shop_id, erroneous_form=None): |
|
170 | """Show form to create an article.""" |
||
171 | shop = _get_shop_or_404(shop_id) |
||
172 | |||
173 | brand = brand_service.get_brand(shop.brand_id) |
||
174 | |||
175 | article_number_sequences = ( |
||
176 | article_sequence_service.get_article_number_sequences_for_shop(shop.id) |
||
177 | ) |
||
178 | article_number_sequence_available = bool(article_number_sequences) |
||
179 | |||
180 | form = ( |
||
181 | erroneous_form |
||
182 | if erroneous_form |
||
183 | else ArticleCreateForm(price=Decimal('0.0'), tax_rate=Decimal('19.0')) |
||
184 | ) |
||
185 | form.set_article_number_sequence_choices(article_number_sequences) |
||
186 | |||
187 | return { |
||
188 | 'shop': shop, |
||
189 | 'brand': brand, |
||
190 | 'article_number_sequence_available': article_number_sequence_available, |
||
191 | 'form': form, |
||
192 | } |
||
193 | |||
194 | |||
195 | 1 | @blueprint.post('/for_shop/<shop_id>') |
|
196 | 1 | @permission_required(ShopArticlePermission.create) |
|
197 | def create(shop_id): |
||
198 | """Create an article.""" |
||
199 | shop = _get_shop_or_404(shop_id) |
||
200 | |||
201 | form = ArticleCreateForm(request.form) |
||
202 | |||
203 | article_number_sequences = ( |
||
204 | article_sequence_service.get_article_number_sequences_for_shop(shop.id) |
||
205 | ) |
||
206 | if not article_number_sequences: |
||
207 | flash_error( |
||
208 | gettext('No article number sequences are defined for this shop.') |
||
209 | ) |
||
210 | return create_form(shop_id, form) |
||
211 | |||
212 | form.set_article_number_sequence_choices(article_number_sequences) |
||
213 | if not form.validate(): |
||
214 | return create_form(shop_id, form) |
||
215 | |||
216 | article_number_sequence_id = form.article_number_sequence_id.data |
||
217 | if not article_number_sequence_id: |
||
218 | flash_error(gettext('No valid article number sequence was specified.')) |
||
219 | return create_form(shop_id, form) |
||
220 | |||
221 | article_number_sequence = ( |
||
222 | article_sequence_service.get_article_number_sequence( |
||
223 | article_number_sequence_id |
||
224 | ) |
||
225 | ) |
||
226 | if article_number_sequence.shop_id != shop.id: |
||
227 | flash_error(gettext('No valid article number sequence was specified.')) |
||
228 | return create_form(shop_id, form) |
||
229 | |||
230 | try: |
||
231 | item_number = article_sequence_service.generate_article_number( |
||
232 | article_number_sequence.id |
||
233 | ) |
||
234 | except article_sequence_service.ArticleNumberGenerationFailed as e: |
||
235 | abort(500, e.message) |
||
236 | |||
237 | description = form.description.data.strip() |
||
238 | price = form.price.data |
||
239 | tax_rate = form.tax_rate.data / TAX_RATE_DISPLAY_FACTOR |
||
240 | total_quantity = form.total_quantity.data |
||
241 | quantity = total_quantity |
||
242 | max_quantity_per_order = form.max_quantity_per_order.data |
||
243 | |||
244 | article = article_service.create_article( |
||
245 | shop.id, |
||
246 | item_number, |
||
247 | description, |
||
248 | price, |
||
249 | tax_rate, |
||
250 | total_quantity, |
||
251 | max_quantity_per_order, |
||
252 | ) |
||
253 | |||
254 | flash_success( |
||
255 | gettext( |
||
256 | 'Article "%(item_number)s" has been created.', |
||
257 | item_number=article.item_number, |
||
258 | ) |
||
259 | ) |
||
260 | return redirect_to('.view', article_id=article.id) |
||
261 | |||
262 | |||
263 | # -------------------------------------------------------------------- # |
||
264 | # update |
||
265 | |||
266 | |||
267 | 1 | @blueprint.get('/<uuid:article_id>/update') |
|
268 | 1 | @permission_required(ShopArticlePermission.update) |
|
269 | 1 | @templated |
|
270 | 1 | def update_form(article_id, erroneous_form=None): |
|
271 | """Show form to update an article.""" |
||
272 | article = _get_article_or_404(article_id) |
||
273 | |||
274 | shop = shop_service.get_shop(article.shop_id) |
||
275 | |||
276 | brand = brand_service.get_brand(shop.brand_id) |
||
277 | |||
278 | if article.available_from: |
||
279 | article.available_from = utc_to_local_tz(article.available_from) |
||
280 | if article.available_until: |
||
281 | article.available_until = utc_to_local_tz(article.available_until) |
||
282 | |||
283 | form = ( |
||
284 | erroneous_form |
||
285 | if erroneous_form |
||
286 | else ArticleUpdateForm( |
||
287 | obj=article, |
||
288 | available_from_date=article.available_from.date() |
||
289 | if article.available_from |
||
290 | else None, |
||
291 | available_from_time=article.available_from.time() |
||
292 | if article.available_from |
||
293 | else None, |
||
294 | available_until_date=article.available_until.date() |
||
295 | if article.available_until |
||
296 | else None, |
||
297 | available_until_time=article.available_until.time() |
||
298 | if article.available_until |
||
299 | else None, |
||
300 | ) |
||
301 | ) |
||
302 | form.tax_rate.data = article.tax_rate * TAX_RATE_DISPLAY_FACTOR |
||
303 | |||
304 | return { |
||
305 | 'article': article, |
||
306 | 'shop': shop, |
||
307 | 'brand': brand, |
||
308 | 'form': form, |
||
309 | } |
||
310 | |||
311 | |||
312 | 1 | @blueprint.post('/<uuid:article_id>') |
|
313 | 1 | @permission_required(ShopArticlePermission.update) |
|
314 | def update(article_id): |
||
315 | """Update an article.""" |
||
316 | article = _get_article_or_404(article_id) |
||
317 | |||
318 | form = ArticleUpdateForm(request.form) |
||
319 | if not form.validate(): |
||
320 | return update_form(article_id, form) |
||
321 | |||
322 | description = form.description.data.strip() |
||
323 | price = form.price.data |
||
324 | tax_rate = form.tax_rate.data / TAX_RATE_DISPLAY_FACTOR |
||
325 | if form.available_from_date.data and form.available_from_time.data: |
||
326 | available_from = local_tz_to_utc( |
||
327 | datetime.combine( |
||
328 | form.available_from_date.data, form.available_from_time.data |
||
329 | ) |
||
330 | ) |
||
331 | else: |
||
332 | available_from = None |
||
333 | if form.available_until_date.data and form.available_until_time.data: |
||
334 | available_until = local_tz_to_utc( |
||
335 | datetime.combine( |
||
336 | form.available_until_date.data, form.available_until_time.data |
||
337 | ) |
||
338 | ) |
||
339 | else: |
||
340 | available_until = None |
||
341 | total_quantity = form.total_quantity.data |
||
342 | max_quantity_per_order = form.max_quantity_per_order.data |
||
343 | not_directly_orderable = form.not_directly_orderable.data |
||
344 | requires_separate_order = form.requires_separate_order.data |
||
345 | shipping_required = form.shipping_required.data |
||
346 | |||
347 | article = article_service.update_article( |
||
348 | article.id, |
||
349 | description, |
||
350 | price, |
||
351 | tax_rate, |
||
352 | available_from, |
||
353 | available_until, |
||
354 | total_quantity, |
||
355 | max_quantity_per_order, |
||
356 | not_directly_orderable, |
||
357 | requires_separate_order, |
||
358 | shipping_required, |
||
359 | ) |
||
360 | |||
361 | flash_success( |
||
362 | gettext( |
||
363 | 'Article "%(description)s" has been updated.', |
||
364 | description=article.description, |
||
365 | ) |
||
366 | ) |
||
367 | return redirect_to('.view', article_id=article.id) |
||
368 | |||
369 | |||
370 | # -------------------------------------------------------------------- # |
||
371 | # article attachments |
||
372 | |||
373 | |||
374 | 1 | @blueprint.get('/<uuid:article_id>/attachments/create') |
|
375 | 1 | @permission_required(ShopArticlePermission.update) |
|
376 | 1 | @templated |
|
377 | 1 | def attachment_create_form(article_id, erroneous_form=None): |
|
378 | """Show form to attach an article to another article.""" |
||
379 | article = _get_article_or_404(article_id) |
||
380 | |||
381 | shop = shop_service.get_shop(article.shop_id) |
||
382 | |||
383 | brand = brand_service.get_brand(shop.brand_id) |
||
384 | |||
385 | attachable_articles = article_service.get_attachable_articles(article.id) |
||
386 | |||
387 | form = ( |
||
388 | erroneous_form |
||
389 | if erroneous_form |
||
390 | else ArticleAttachmentCreateForm(quantity=0) |
||
391 | ) |
||
392 | form.set_article_to_attach_choices(attachable_articles) |
||
393 | |||
394 | return { |
||
395 | 'article': article, |
||
396 | 'shop': shop, |
||
397 | 'brand': brand, |
||
398 | 'form': form, |
||
399 | } |
||
400 | |||
401 | |||
402 | 1 | @blueprint.post('/<uuid:article_id>/attachments') |
|
403 | 1 | @permission_required(ShopArticlePermission.update) |
|
404 | def attachment_create(article_id): |
||
405 | """Attach an article to another article.""" |
||
406 | article = _get_article_or_404(article_id) |
||
407 | |||
408 | attachable_articles = article_service.get_attachable_articles(article.id) |
||
409 | |||
410 | form = ArticleAttachmentCreateForm(request.form) |
||
411 | form.set_article_to_attach_choices(attachable_articles) |
||
412 | |||
413 | if not form.validate(): |
||
414 | return attachment_create_form(article_id, form) |
||
415 | |||
416 | article_to_attach_id = form.article_to_attach_id.data |
||
417 | article_to_attach = article_service.get_article(article_to_attach_id) |
||
418 | quantity = form.quantity.data |
||
419 | |||
420 | article_service.attach_article( |
||
421 | article_to_attach.item_number, quantity, article.item_number |
||
422 | ) |
||
423 | |||
424 | flash_success( |
||
425 | gettext( |
||
426 | 'Article "%(article_to_attach_item_number)s" has been attached %(quantity)s times to article "%(article_item_number)s".', |
||
427 | article_to_attach_item_number=article_to_attach.item_number, |
||
428 | quantity=quantity, |
||
429 | article_item_number=article.item_number, |
||
430 | ) |
||
431 | ) |
||
432 | return redirect_to('.view', article_id=article.id) |
||
433 | |||
434 | |||
435 | 1 | @blueprint.delete('/attachments/<uuid:article_id>') |
|
436 | 1 | @permission_required(ShopArticlePermission.update) |
|
437 | 1 | @respond_no_content |
|
438 | def attachment_remove(article_id): |
||
439 | """Remove the attachment link from one article to another.""" |
||
440 | attached_article = article_service.find_attached_article(article_id) |
||
441 | |||
442 | if attached_article is None: |
||
443 | abort(404) |
||
444 | |||
445 | article = attached_article.article |
||
446 | attached_to_article = attached_article.attached_to_article |
||
447 | |||
448 | article_service.unattach_article(attached_article.id) |
||
449 | |||
450 | flash_success( |
||
451 | gettext( |
||
452 | 'Article "%(article_item_number)s" is no longer attached to article "%(attached_to_article_item_number)s".', |
||
453 | article_item_number=article.item_number, |
||
454 | attached_to_article_item_number=attached_to_article.item_number, |
||
455 | ) |
||
456 | ) |
||
457 | |||
458 | |||
459 | # -------------------------------------------------------------------- # |
||
460 | # actions |
||
461 | |||
462 | |||
463 | 1 | @blueprint.get('/<uuid:article_id>/actions/badge_awarding/create') |
|
464 | 1 | @permission_required(ShopArticlePermission.update) |
|
465 | 1 | @templated |
|
466 | 1 | def action_create_form_for_badge_awarding(article_id, erroneous_form=None): |
|
467 | """Show form to register a badge awarding action for the article.""" |
||
468 | article = _get_article_or_404(article_id) |
||
469 | |||
470 | shop = shop_service.get_shop(article.shop_id) |
||
471 | brand = brand_service.get_brand(shop.brand_id) |
||
472 | |||
473 | badges = badge_service.get_all_badges() |
||
474 | |||
475 | form = ( |
||
476 | erroneous_form if erroneous_form else RegisterBadgeAwardingActionForm() |
||
477 | ) |
||
478 | form.set_badge_choices(badges) |
||
479 | |||
480 | return { |
||
481 | 'article': article, |
||
482 | 'shop': shop, |
||
483 | 'brand': brand, |
||
484 | 'form': form, |
||
485 | } |
||
486 | |||
487 | |||
488 | 1 | @blueprint.post('/<uuid:article_id>/actions/badge_awarding') |
|
489 | 1 | @permission_required(ShopArticlePermission.update) |
|
490 | def action_create_for_badge_awarding(article_id): |
||
491 | """Register a badge awarding action for the article.""" |
||
492 | article = _get_article_or_404(article_id) |
||
493 | |||
494 | badges = badge_service.get_all_badges() |
||
495 | |||
496 | form = RegisterBadgeAwardingActionForm(request.form) |
||
497 | form.set_badge_choices(badges) |
||
498 | |||
499 | if not form.validate(): |
||
500 | return action_create_form_for_badge_awarding(article_id, form) |
||
501 | |||
502 | badge_id = form.badge_id.data |
||
503 | badge = badge_service.get_badge(badge_id) |
||
504 | |||
505 | action_registry_service.register_badge_awarding( |
||
506 | article.item_number, badge.id |
||
507 | ) |
||
508 | |||
509 | flash_success(gettext('Action has been added.')) |
||
510 | |||
511 | return redirect_to('.view', article_id=article.id) |
||
512 | |||
513 | |||
514 | 1 | View Code Duplication | @blueprint.get('/<uuid:article_id>/actions/tickets_creation/create') |
1 ignored issue
–
show
Duplication
introduced
by
Loading history...
|
|||
515 | 1 | @permission_required(ShopArticlePermission.update) |
|
516 | 1 | @templated |
|
517 | 1 | def action_create_form_for_tickets_creation(article_id, erroneous_form=None): |
|
518 | """Show form to register a tickets creation action for the article.""" |
||
519 | article = _get_article_or_404(article_id) |
||
520 | |||
521 | shop = shop_service.get_shop(article.shop_id) |
||
522 | brand = brand_service.get_brand(shop.brand_id) |
||
523 | |||
524 | form = ( |
||
525 | erroneous_form |
||
526 | if erroneous_form |
||
527 | else RegisterTicketsCreationActionForm() |
||
528 | ) |
||
529 | form.set_category_choices(_get_categories_with_parties(brand.id)) |
||
530 | |||
531 | return { |
||
532 | 'article': article, |
||
533 | 'shop': shop, |
||
534 | 'brand': brand, |
||
535 | 'form': form, |
||
536 | } |
||
537 | |||
538 | |||
539 | 1 | View Code Duplication | @blueprint.post('/<uuid:article_id>/actions/tickets_creation') |
1 ignored issue
–
show
|
|||
540 | 1 | @permission_required(ShopArticlePermission.update) |
|
541 | def action_create_for_tickets_creation(article_id): |
||
542 | """Register a tickets creation action for the article.""" |
||
543 | article = _get_article_or_404(article_id) |
||
544 | |||
545 | shop = shop_service.get_shop(article.shop_id) |
||
546 | brand = brand_service.get_brand(shop.brand_id) |
||
547 | |||
548 | form = RegisterTicketsCreationActionForm(request.form) |
||
549 | form.set_category_choices(_get_categories_with_parties(brand.id)) |
||
550 | |||
551 | if not form.validate(): |
||
552 | return action_create_form_for_tickets_creation(article_id, form) |
||
553 | |||
554 | category_id = form.category_id.data |
||
555 | category = ticket_category_service.find_category(category_id) |
||
556 | if category is None: |
||
557 | raise ValueError(f'Unknown category ID "{category_id}"') |
||
558 | |||
559 | action_registry_service.register_tickets_creation( |
||
560 | article.item_number, category.id |
||
561 | ) |
||
562 | |||
563 | flash_success(gettext('Action has been added.')) |
||
564 | |||
565 | return redirect_to('.view', article_id=article.id) |
||
566 | |||
567 | |||
568 | 1 | View Code Duplication | @blueprint.get('/<uuid:article_id>/actions/ticket_bundles_creation/create') |
1 ignored issue
–
show
|
|||
569 | 1 | @permission_required(ShopArticlePermission.update) |
|
570 | 1 | @templated |
|
571 | 1 | def action_create_form_for_ticket_bundles_creation( |
|
572 | article_id, erroneous_form=None |
||
573 | ): |
||
574 | """Show form to register a ticket bundles creation action for the article.""" |
||
575 | article = _get_article_or_404(article_id) |
||
576 | |||
577 | shop = shop_service.get_shop(article.shop_id) |
||
578 | brand = brand_service.get_brand(shop.brand_id) |
||
579 | |||
580 | form = ( |
||
581 | erroneous_form |
||
582 | if erroneous_form |
||
583 | else RegisterTicketBundlesCreationActionForm() |
||
584 | ) |
||
585 | form.set_category_choices(_get_categories_with_parties(brand.id)) |
||
586 | |||
587 | return { |
||
588 | 'article': article, |
||
589 | 'shop': shop, |
||
590 | 'brand': brand, |
||
591 | 'form': form, |
||
592 | } |
||
593 | |||
594 | |||
595 | 1 | View Code Duplication | @blueprint.post('/<uuid:article_id>/actions/ticket_bundles_creation') |
1 ignored issue
–
show
|
|||
596 | 1 | @permission_required(ShopArticlePermission.update) |
|
597 | def action_create_for_ticket_bundles_creation(article_id): |
||
598 | """Register a ticket bundles creation action for the article.""" |
||
599 | article = _get_article_or_404(article_id) |
||
600 | |||
601 | shop = shop_service.get_shop(article.shop_id) |
||
602 | brand = brand_service.get_brand(shop.brand_id) |
||
603 | |||
604 | form = RegisterTicketBundlesCreationActionForm(request.form) |
||
605 | form.set_category_choices(_get_categories_with_parties(brand.id)) |
||
606 | |||
607 | if not form.validate(): |
||
608 | return action_create_form_for_ticket_bundles_creation(article_id, form) |
||
609 | |||
610 | category_id = form.category_id.data |
||
611 | category = ticket_category_service.find_category(category_id) |
||
612 | if category is None: |
||
613 | raise ValueError(f'Unknown category ID "{category_id}"') |
||
614 | |||
615 | ticket_quantity = form.ticket_quantity.data |
||
616 | |||
617 | action_registry_service.register_ticket_bundles_creation( |
||
618 | article.item_number, category.id, ticket_quantity |
||
619 | ) |
||
620 | |||
621 | flash_success(gettext('Action has been added.')) |
||
622 | |||
623 | return redirect_to('.view', article_id=article.id) |
||
624 | |||
625 | |||
626 | 1 | def _get_categories_with_parties( |
|
627 | brand_id: BrandID, |
||
628 | ) -> set[tuple[TicketCategory, Party]]: |
||
629 | return { |
||
630 | (category, party) |
||
631 | for party in party_service.get_active_parties(brand_id) |
||
632 | for category in ticket_category_service.get_categories_for_party( |
||
633 | party.id |
||
634 | ) |
||
635 | } |
||
636 | |||
637 | |||
638 | 1 | @blueprint.delete('/actions/<uuid:action_id>') |
|
639 | 1 | @permission_required(ShopArticlePermission.update) |
|
640 | 1 | @respond_no_content |
|
641 | def action_remove(action_id): |
||
642 | """Remove the action from the article.""" |
||
643 | action = action_service.find_action(action_id) |
||
644 | |||
645 | if action is None: |
||
646 | abort(404) |
||
647 | |||
648 | action_service.delete_action(action.id) |
||
649 | |||
650 | flash_success(gettext('Action has been removed.')) |
||
651 | |||
652 | |||
653 | # -------------------------------------------------------------------- # |
||
654 | # article number sequences |
||
655 | |||
656 | |||
657 | 1 | @blueprint.get('/number_sequences/for_shop/<shop_id>/create') |
|
658 | 1 | @permission_required(ShopArticlePermission.create) |
|
659 | 1 | @templated |
|
660 | 1 | def create_number_sequence_form(shop_id, erroneous_form=None): |
|
661 | """Show form to create an article number sequence.""" |
||
662 | shop = _get_shop_or_404(shop_id) |
||
663 | |||
664 | brand = brand_service.get_brand(shop.brand_id) |
||
665 | |||
666 | form = ( |
||
667 | erroneous_form if erroneous_form else ArticleNumberSequenceCreateForm() |
||
668 | ) |
||
669 | |||
670 | return { |
||
671 | 'shop': shop, |
||
672 | 'brand': brand, |
||
673 | 'form': form, |
||
674 | } |
||
675 | |||
676 | |||
677 | 1 | @blueprint.post('/number_sequences/for_shop/<shop_id>') |
|
678 | 1 | @permission_required(ShopArticlePermission.create) |
|
679 | def create_number_sequence(shop_id): |
||
680 | """Create an article number sequence.""" |
||
681 | shop = _get_shop_or_404(shop_id) |
||
682 | |||
683 | form = ArticleNumberSequenceCreateForm(request.form) |
||
684 | if not form.validate(): |
||
685 | return create_number_sequence_form(shop_id, form) |
||
686 | |||
687 | prefix = form.prefix.data.strip() |
||
688 | |||
689 | sequence_id = article_sequence_service.create_article_number_sequence( |
||
690 | shop.id, prefix |
||
691 | ) |
||
692 | if sequence_id is None: |
||
693 | flash_error( |
||
694 | gettext( |
||
695 | 'Article number sequence could not be created. ' |
||
696 | 'Is prefix "%(prefix)s" already defined?', |
||
697 | prefix=prefix, |
||
698 | ) |
||
699 | ) |
||
700 | return create_number_sequence_form(shop.id, form) |
||
701 | |||
702 | flash_success( |
||
703 | gettext( |
||
704 | 'Article number sequence with prefix "%(prefix)s" has been created.', |
||
705 | prefix=prefix, |
||
706 | ) |
||
707 | ) |
||
708 | return redirect_to('.index_for_shop', shop_id=shop.id) |
||
709 | |||
710 | |||
711 | # -------------------------------------------------------------------- # |
||
712 | # helpers |
||
713 | |||
714 | |||
715 | 1 | def _get_shop_or_404(shop_id): |
|
716 | shop = shop_service.find_shop(shop_id) |
||
717 | |||
718 | if shop is None: |
||
719 | abort(404) |
||
720 | |||
721 | return shop |
||
722 | |||
723 | |||
724 | 1 | def _get_article_or_404(article_id): |
|
725 | article = article_service.find_db_article(article_id) |
||
726 | |||
727 | if article is None: |
||
728 | abort(404) |
||
729 | |||
730 | return article |
||
731 |