1 | """ |
||
2 | byceps.services.shop.order.action_service |
||
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 typing import Callable, Sequence |
|
11 | 1 | from uuid import UUID |
|
12 | |||
13 | 1 | from ....database import db |
|
14 | 1 | from ....typing import UserID |
|
15 | |||
16 | 1 | from ..article.transfer.models import ArticleNumber |
|
17 | |||
18 | 1 | from .actions.award_badge import award_badge |
|
19 | 1 | from .actions.create_ticket_bundles import create_ticket_bundles |
|
20 | 1 | from .actions.create_tickets import create_tickets |
|
21 | 1 | from .actions.revoke_ticket_bundles import revoke_ticket_bundles |
|
22 | 1 | from .actions.revoke_tickets import revoke_tickets |
|
23 | 1 | from .dbmodels.order_action import OrderAction as DbOrderAction |
|
24 | 1 | from .transfer.models import Action, ActionParameters, Order, PaymentState |
|
25 | |||
26 | |||
27 | 1 | OrderActionType = Callable[[Order, ArticleNumber, int, ActionParameters], None] |
|
28 | |||
29 | |||
30 | 1 | PROCEDURES_BY_NAME = { |
|
31 | 'award_badge': award_badge, |
||
32 | 'create_ticket_bundles': create_ticket_bundles, |
||
33 | 'revoke_ticket_bundles': revoke_ticket_bundles, |
||
34 | 'create_tickets': create_tickets, |
||
35 | 'revoke_tickets': revoke_tickets, |
||
36 | } |
||
37 | |||
38 | |||
39 | # -------------------------------------------------------------------- # |
||
40 | # creation/removal |
||
41 | |||
42 | |||
43 | 1 | def create_action( |
|
44 | article_number: ArticleNumber, |
||
45 | payment_state: PaymentState, |
||
46 | procedure_name: str, |
||
47 | parameters: ActionParameters, |
||
48 | ) -> None: |
||
49 | """Create an order action.""" |
||
50 | 1 | action = DbOrderAction( |
|
51 | article_number, payment_state, procedure_name, parameters |
||
52 | ) |
||
53 | |||
54 | 1 | db.session.add(action) |
|
55 | 1 | db.session.commit() |
|
56 | |||
57 | |||
58 | 1 | def delete_action(action_id: UUID) -> None: |
|
59 | """Delete the order action.""" |
||
60 | db.session.query(OrderAction) \ |
||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
Loading history...
|
|||
61 | .filter_by(id=action_id) \ |
||
62 | .delete() |
||
63 | |||
64 | db.session.commit() |
||
65 | |||
66 | |||
67 | 1 | def delete_actions_for_article(article_number: ArticleNumber) -> None: |
|
68 | """Delete all order actions for an article.""" |
||
69 | 1 | db.session.query(DbOrderAction) \ |
|
70 | .filter_by(article_number=article_number) \ |
||
71 | .delete() |
||
72 | |||
73 | 1 | db.session.commit() |
|
74 | |||
75 | |||
76 | 1 | def find_action(action_id: UUID) -> Optional[Action]: |
|
77 | """Return the action with that ID, if found.""" |
||
78 | action = DbOrderAction.query.get(action_id) |
||
79 | |||
80 | if action is None: |
||
81 | return |
||
82 | |||
83 | return _db_entity_to_action(action) |
||
84 | |||
85 | |||
86 | # -------------------------------------------------------------------- # |
||
87 | # retrieval |
||
88 | |||
89 | |||
90 | 1 | def get_actions_for_article(article_number: ArticleNumber) -> list[Action]: |
|
91 | """Return the order actions defined for that article.""" |
||
92 | actions = DbOrderAction.query \ |
||
93 | .filter_by(article_number=article_number) \ |
||
94 | .all() |
||
95 | |||
96 | return [_db_entity_to_action(action) for action in actions] |
||
97 | |||
98 | |||
99 | 1 | def _db_entity_to_action(action: DbOrderAction) -> Action: |
|
100 | 1 | return Action( |
|
101 | id=action.id, |
||
102 | article_number=action.article_number, |
||
103 | payment_state=action.payment_state, |
||
104 | procedure_name=action.procedure, |
||
105 | parameters=action.parameters, |
||
106 | ) |
||
107 | |||
108 | |||
109 | # -------------------------------------------------------------------- # |
||
110 | # execution |
||
111 | |||
112 | |||
113 | 1 | def execute_actions( |
|
114 | order: Order, payment_state: PaymentState, initiator_id: UserID |
||
115 | ) -> None: |
||
116 | """Execute relevant actions for this order in its new payment state.""" |
||
117 | 1 | article_numbers = {item.article_number for item in order.items} |
|
118 | |||
119 | 1 | if not article_numbers: |
|
120 | 1 | return |
|
121 | |||
122 | 1 | quantities_by_article_number = { |
|
123 | item.article_number: item.quantity for item in order.items |
||
124 | } |
||
125 | |||
126 | 1 | actions = _get_actions(article_numbers, payment_state) |
|
127 | |||
128 | 1 | for action in actions: |
|
129 | 1 | article_quantity = quantities_by_article_number[action.article_number] |
|
130 | |||
131 | 1 | _execute_procedure(order, action, article_quantity, initiator_id) |
|
132 | |||
133 | |||
134 | 1 | def _get_actions( |
|
135 | article_numbers: set[ArticleNumber], payment_state: PaymentState |
||
136 | ) -> Sequence[Action]: |
||
137 | """Return the order actions for those article numbers.""" |
||
138 | 1 | actions = DbOrderAction.query \ |
|
139 | .filter(DbOrderAction.article_number.in_(article_numbers)) \ |
||
140 | .filter_by(_payment_state=payment_state.name) \ |
||
141 | .all() |
||
142 | |||
143 | 1 | return [_db_entity_to_action(action) for action in actions] |
|
144 | |||
145 | |||
146 | 1 | def _execute_procedure( |
|
147 | order: Order, |
||
148 | action: Action, |
||
149 | article_quantity: int, |
||
150 | initiator_id: UserID, |
||
151 | ) -> None: |
||
152 | """Execute the procedure configured for that order action.""" |
||
153 | 1 | procedure = _get_procedure(action.procedure_name, action.article_number) |
|
154 | |||
155 | 1 | procedure( |
|
156 | order, |
||
157 | action.article_number, |
||
158 | article_quantity, |
||
159 | initiator_id, |
||
160 | action.parameters, |
||
161 | ) |
||
162 | |||
163 | |||
164 | 1 | def _get_procedure(name: str, article_number: ArticleNumber) -> OrderActionType: |
|
165 | """Return procedure with that name, or raise an exception if the |
||
166 | name is not registerd. |
||
167 | """ |
||
168 | 1 | procedure = PROCEDURES_BY_NAME.get(name) |
|
169 | |||
170 | 1 | if procedure is None: |
|
171 | raise Exception( |
||
172 | f"Unknown procedure '{name}' configured " |
||
173 | f"for article number '{article_number}'." |
||
174 | ) |
||
175 | |||
176 | return procedure |
||
177 |