1
|
|
|
""" |
2
|
|
|
byceps.services.shop.order.transfer.models |
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 dataclasses import dataclass |
11
|
1 |
|
from datetime import datetime, timedelta |
12
|
1 |
|
from decimal import Decimal |
13
|
1 |
|
from enum import Enum |
14
|
1 |
|
from typing import Any, Dict, NewType, Optional |
15
|
1 |
|
from uuid import UUID |
16
|
|
|
|
17
|
1 |
|
from .....typing import UserID |
18
|
|
|
|
19
|
1 |
|
from ...article.transfer.models import ArticleNumber |
20
|
1 |
|
from ...shop.transfer.models import ShopID |
21
|
|
|
|
22
|
|
|
|
23
|
1 |
|
OrderID = NewType('OrderID', UUID) |
24
|
|
|
|
25
|
|
|
|
26
|
1 |
|
OrderNumber = NewType('OrderNumber', str) |
27
|
|
|
|
28
|
|
|
|
29
|
1 |
|
OrderNumberSequenceID = NewType('OrderNumberSequenceID', UUID) |
30
|
|
|
|
31
|
|
|
|
32
|
1 |
|
@dataclass(frozen=True) |
33
|
|
|
class OrderNumberSequence: |
34
|
1 |
|
id: OrderNumberSequenceID |
35
|
1 |
|
shop_id: ShopID |
36
|
1 |
|
prefix: str |
37
|
1 |
|
value: int |
38
|
|
|
|
39
|
|
|
|
40
|
1 |
|
OrderState = Enum( |
41
|
|
|
'OrderState', |
42
|
|
|
[ |
43
|
|
|
'open', |
44
|
|
|
'canceled', |
45
|
|
|
'complete', |
46
|
|
|
], |
47
|
|
|
) |
48
|
|
|
|
49
|
|
|
|
50
|
1 |
|
PaymentMethod = Enum( |
51
|
|
|
'PaymentMethod', |
52
|
|
|
[ |
53
|
|
|
'bank_transfer', |
54
|
|
|
'cash', |
55
|
|
|
'direct_debit', |
56
|
|
|
'free', |
57
|
|
|
], |
58
|
|
|
) |
59
|
|
|
|
60
|
|
|
|
61
|
1 |
|
PaymentState = Enum( |
62
|
|
|
'PaymentState', |
63
|
|
|
[ |
64
|
|
|
'open', |
65
|
|
|
'canceled_before_paid', |
66
|
|
|
'paid', |
67
|
|
|
'canceled_after_paid', |
68
|
|
|
], |
69
|
|
|
) |
70
|
|
|
|
71
|
|
|
|
72
|
1 |
|
@dataclass(frozen=True) |
73
|
|
|
class Address: |
74
|
1 |
|
country: str |
75
|
1 |
|
zip_code: str |
76
|
1 |
|
city: str |
77
|
1 |
|
street: str |
78
|
|
|
|
79
|
|
|
|
80
|
1 |
|
@dataclass(frozen=True) |
81
|
|
|
class OrderItem: |
82
|
1 |
|
order_number: OrderNumber |
83
|
1 |
|
article_number: ArticleNumber |
84
|
1 |
|
description: str |
85
|
1 |
|
unit_price: Decimal |
86
|
1 |
|
tax_rate: Decimal |
87
|
1 |
|
quantity: int |
88
|
1 |
|
line_amount: Decimal |
89
|
|
|
|
90
|
|
|
|
91
|
1 |
|
OVERDUE_THRESHOLD = timedelta(days=14) |
92
|
|
|
|
93
|
|
|
|
94
|
1 |
|
@dataclass(frozen=True) |
95
|
|
|
class Order: |
96
|
1 |
|
id: OrderID |
97
|
1 |
|
shop_id: ShopID |
98
|
1 |
|
order_number: OrderNumber |
99
|
1 |
|
created_at: datetime |
100
|
1 |
|
placed_by_id: UserID |
101
|
1 |
|
first_names: str |
102
|
1 |
|
last_name: str |
103
|
1 |
|
address: Address |
104
|
1 |
|
total_amount: Decimal |
105
|
1 |
|
items: list[OrderItem] |
106
|
1 |
|
payment_method: Optional[PaymentMethod] |
107
|
1 |
|
payment_state: PaymentState |
108
|
1 |
|
state: OrderState |
109
|
1 |
|
is_open: bool |
110
|
1 |
|
is_canceled: bool |
111
|
1 |
|
is_paid: bool |
112
|
1 |
|
is_invoiced: bool |
113
|
1 |
|
is_shipping_required: bool |
114
|
1 |
|
is_shipped: bool |
115
|
1 |
|
cancelation_reason: Optional[str] |
116
|
|
|
|
117
|
1 |
|
@property |
118
|
|
|
def is_overdue(self) -> bool: |
119
|
|
|
"""Return `True` if payment of the order is overdue.""" |
120
|
1 |
|
if self.payment_state != PaymentState.open: |
121
|
|
|
return False |
122
|
|
|
|
123
|
1 |
|
return datetime.utcnow() > (self.created_at + OVERDUE_THRESHOLD) |
124
|
|
|
|
125
|
|
|
|
126
|
1 |
|
ActionParameters = Dict[str, Any] |
127
|
|
|
|
128
|
|
|
|
129
|
1 |
|
@dataclass(frozen=True) |
130
|
|
|
class Action: |
131
|
1 |
|
id: UUID |
132
|
1 |
|
article_number: ArticleNumber |
133
|
1 |
|
payment_state: PaymentState |
134
|
1 |
|
procedure_name: str |
135
|
|
|
parameters: ActionParameters |
136
|
|
|
|