amazon-orders 3.2.0__tar.gz → 3.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {amazon_orders-3.2.0 → amazon_orders-3.2.2}/CHANGELOG.md +18 -1
- {amazon_orders-3.2.0/amazon_orders.egg-info → amazon_orders-3.2.2}/PKG-INFO +2 -2
- {amazon_orders-3.2.0 → amazon_orders-3.2.2/amazon_orders.egg-info}/PKG-INFO +2 -2
- {amazon_orders-3.2.0 → amazon_orders-3.2.2}/amazonorders/__init__.py +1 -1
- {amazon_orders-3.2.0 → amazon_orders-3.2.2}/amazonorders/orders.py +6 -0
- {amazon_orders-3.2.0 → amazon_orders-3.2.2}/amazonorders/selectors.py +40 -30
- {amazon_orders-3.2.0 → amazon_orders-3.2.2}/amazonorders/transactions.py +20 -40
- {amazon_orders-3.2.0 → amazon_orders-3.2.2}/tests/test_cli.py +2 -2
- {amazon_orders-3.2.0 → amazon_orders-3.2.2}/tests/test_orders.py +45 -0
- {amazon_orders-3.2.0 → amazon_orders-3.2.2}/tests/test_transactions.py +5 -5
- {amazon_orders-3.2.0 → amazon_orders-3.2.2}/tests/testcase.py +59 -7
- {amazon_orders-3.2.0 → amazon_orders-3.2.2}/LICENSE +0 -0
- {amazon_orders-3.2.0 → amazon_orders-3.2.2}/MANIFEST.in +0 -0
- {amazon_orders-3.2.0 → amazon_orders-3.2.2}/README.md +0 -0
- {amazon_orders-3.2.0 → amazon_orders-3.2.2}/amazon_orders.egg-info/SOURCES.txt +0 -0
- {amazon_orders-3.2.0 → amazon_orders-3.2.2}/amazon_orders.egg-info/dependency_links.txt +0 -0
- {amazon_orders-3.2.0 → amazon_orders-3.2.2}/amazon_orders.egg-info/entry_points.txt +0 -0
- {amazon_orders-3.2.0 → amazon_orders-3.2.2}/amazon_orders.egg-info/requires.txt +0 -0
- {amazon_orders-3.2.0 → amazon_orders-3.2.2}/amazon_orders.egg-info/top_level.txt +0 -0
- {amazon_orders-3.2.0 → amazon_orders-3.2.2}/amazonorders/banner.txt +0 -0
- {amazon_orders-3.2.0 → amazon_orders-3.2.2}/amazonorders/cli.py +0 -0
- {amazon_orders-3.2.0 → amazon_orders-3.2.2}/amazonorders/conf.py +0 -0
- {amazon_orders-3.2.0 → amazon_orders-3.2.2}/amazonorders/constants.py +0 -0
- {amazon_orders-3.2.0 → amazon_orders-3.2.2}/amazonorders/entity/__init__.py +0 -0
- {amazon_orders-3.2.0 → amazon_orders-3.2.2}/amazonorders/entity/item.py +0 -0
- {amazon_orders-3.2.0 → amazon_orders-3.2.2}/amazonorders/entity/order.py +0 -0
- {amazon_orders-3.2.0 → amazon_orders-3.2.2}/amazonorders/entity/parsable.py +0 -0
- {amazon_orders-3.2.0 → amazon_orders-3.2.2}/amazonorders/entity/recipient.py +0 -0
- {amazon_orders-3.2.0 → amazon_orders-3.2.2}/amazonorders/entity/seller.py +0 -0
- {amazon_orders-3.2.0 → amazon_orders-3.2.2}/amazonorders/entity/shipment.py +0 -0
- {amazon_orders-3.2.0 → amazon_orders-3.2.2}/amazonorders/entity/transaction.py +0 -0
- {amazon_orders-3.2.0 → amazon_orders-3.2.2}/amazonorders/exception.py +0 -0
- {amazon_orders-3.2.0 → amazon_orders-3.2.2}/amazonorders/forms.py +0 -0
- {amazon_orders-3.2.0 → amazon_orders-3.2.2}/amazonorders/session.py +0 -0
- {amazon_orders-3.2.0 → amazon_orders-3.2.2}/amazonorders/util.py +0 -0
- {amazon_orders-3.2.0 → amazon_orders-3.2.2}/pyproject.toml +0 -0
- {amazon_orders-3.2.0 → amazon_orders-3.2.2}/setup.cfg +0 -0
- {amazon_orders-3.2.0 → amazon_orders-3.2.2}/tests/test_conf.py +0 -0
- {amazon_orders-3.2.0 → amazon_orders-3.2.2}/tests/test_session.py +0 -0
- {amazon_orders-3.2.0 → amazon_orders-3.2.2}/tests/test_util.py +0 -0
|
@@ -4,7 +4,24 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
-
## [Unreleased](https://github.com/alexdlaird/amazon-orders/compare/3.2.
|
|
7
|
+
## [Unreleased](https://github.com/alexdlaird/amazon-orders/compare/3.2.2...HEAD)
|
|
8
|
+
|
|
9
|
+
## [3.2.2](https://github.com/alexdlaird/amazon-orders/compare/3.2.1...3.2.2) - 2025-01-28
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Stability improvements.
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- Broken parsing of Amazon Fresh orders, these are now skipped.
|
|
18
|
+
|
|
19
|
+
## [3.2.1](https://github.com/alexdlaird/amazon-orders/compare/3.2.0...3.2.1) - 2024-11-08
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- Issues with parsing Items and Shipments using the new `data-component`, selectors made more precise.
|
|
24
|
+
- Transactions use `util` selector methods, so consistent use of trying a list of selectors is maintained.
|
|
8
25
|
|
|
9
26
|
## [3.2.0](https://github.com/alexdlaird/amazon-orders/compare/3.1.0...3.2.0) - 2024-11-07
|
|
10
27
|
|
|
@@ -73,6 +73,12 @@ class AmazonOrders:
|
|
|
73
73
|
response_parsed = self.amazon_session.last_response_parsed
|
|
74
74
|
|
|
75
75
|
for order_tag in util.select(response_parsed, self.config.selectors.ORDER_HISTORY_ENTITY_SELECTOR):
|
|
76
|
+
# If we find a brand logo (for instance, Amazon Fresh), we don't know how to parse this. If we know how
|
|
77
|
+
# to do this in the future, we can implement it, but right now we have no reliable way, so skipping
|
|
78
|
+
# these orders.
|
|
79
|
+
if util.select(order_tag, self.config.selectors.ORDER_HISTORY_BRAND_SELECTOR):
|
|
80
|
+
continue
|
|
81
|
+
|
|
76
82
|
order: Order = self.config.order_cls(order_tag, self.config)
|
|
77
83
|
|
|
78
84
|
if full_details:
|
|
@@ -45,20 +45,31 @@ class Selectors:
|
|
|
45
45
|
# is passed.
|
|
46
46
|
##########################################################################
|
|
47
47
|
|
|
48
|
-
ORDER_HISTORY_ENTITY_SELECTOR = ["div.order-card",
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
ORDER_HISTORY_ENTITY_SELECTOR = ["div.order-card",
|
|
49
|
+
"div.order"]
|
|
50
|
+
ORDER_DETAILS_ENTITY_SELECTOR = ["div#orderDetails",
|
|
51
|
+
"div#ordersContainer"]
|
|
52
|
+
ITEM_ENTITY_SELECTOR = ["[data-component='purchasedItems'] .a-fixed-left-grid",
|
|
53
|
+
"div:has(> div.yohtmlc-item)",
|
|
54
|
+
".item-box"]
|
|
55
|
+
SHIPMENT_ENTITY_SELECTOR = ["[data-component='orderCard'] [data-component='shipments'] .a-box",
|
|
56
|
+
"div.shipment",
|
|
57
|
+
"div.delivery-box"]
|
|
58
|
+
ORDER_HISTORY_BRAND_SELECTOR = ".brand-info-box .brand-logo img"
|
|
53
59
|
|
|
54
60
|
#####################################
|
|
55
61
|
# CSS selectors for Item fields
|
|
56
62
|
#####################################
|
|
57
63
|
|
|
58
64
|
FIELD_ITEM_IMG_LINK_SELECTOR = "a img"
|
|
59
|
-
FIELD_ITEM_QUANTITY_SELECTOR = [".od-item-view-qty",
|
|
60
|
-
|
|
61
|
-
|
|
65
|
+
FIELD_ITEM_QUANTITY_SELECTOR = [".od-item-view-qty",
|
|
66
|
+
"span.item-view-qty",
|
|
67
|
+
"span.product-image__qty"]
|
|
68
|
+
FIELD_ITEM_TITLE_SELECTOR = ["[data-component='itemTitle']",
|
|
69
|
+
".yohtmlc-item a", ".yohtmlc-product-title"]
|
|
70
|
+
FIELD_ITEM_LINK_SELECTOR = ["[data-component='itemTitle'] a",
|
|
71
|
+
".yohtmlc-item a",
|
|
72
|
+
"a:has(> .yohtmlc-product-title)"]
|
|
62
73
|
FIELD_ITEM_TAG_ITERATOR_SELECTOR = [".yohtmlc-item div"]
|
|
63
74
|
FIELD_ITEM_PRICE_SELECTOR = ["[data-component='unitPrice'] .a-text-price :not(.a-offscreen)",
|
|
64
75
|
".yohtmlc-item .a-color-price"]
|
|
@@ -70,9 +81,13 @@ class Selectors:
|
|
|
70
81
|
#####################################
|
|
71
82
|
|
|
72
83
|
FIELD_ORDER_DETAILS_LINK_SELECTOR = "a.yohtmlc-order-details-link"
|
|
73
|
-
FIELD_ORDER_NUMBER_SELECTOR = [".order-date-invoice-item bdi[dir='ltr']",
|
|
74
|
-
|
|
75
|
-
|
|
84
|
+
FIELD_ORDER_NUMBER_SELECTOR = [".order-date-invoice-item bdi[dir='ltr']",
|
|
85
|
+
"bdi[dir='ltr']",
|
|
86
|
+
"span[dir='ltr']"]
|
|
87
|
+
FIELD_ORDER_GRAND_TOTAL_SELECTOR = ["div.yohtmlc-order-total span.value",
|
|
88
|
+
"div.order-header div.a-column.a-span2"]
|
|
89
|
+
FIELD_ORDER_PLACED_DATE_SELECTOR = ["span.order-date-invoice-item",
|
|
90
|
+
"div.a-span3"]
|
|
76
91
|
FIELD_ORDER_PAYMENT_METHOD_SELECTOR = "img.pmts-payment-credit-card-instrument-logo"
|
|
77
92
|
FIELD_ORDER_PAYMENT_METHOD_LAST_4_SELECTOR = "span:has(img.pmts-payment-credit-card-instrument-logo):last-child"
|
|
78
93
|
FIELD_ORDER_SUBTOTALS_TAG_ITERATOR_SELECTOR = ["[data-component='orderSubtotals'] div.a-row",
|
|
@@ -88,7 +103,8 @@ class Selectors:
|
|
|
88
103
|
# CSS selectors for Shipment fields
|
|
89
104
|
#####################################
|
|
90
105
|
|
|
91
|
-
FIELD_SHIPMENT_TRACKING_LINK_SELECTOR = ["span.track-package-button a",
|
|
106
|
+
FIELD_SHIPMENT_TRACKING_LINK_SELECTOR = ["span.track-package-button a",
|
|
107
|
+
"a[href*='ship-track?itemId=']"]
|
|
92
108
|
FIELD_SHIPMENT_DELIVERY_STATUS_SELECTOR = ["div.js-shipment-info-container div.a-row",
|
|
93
109
|
"span.delivery-box__primary-text",
|
|
94
110
|
".yohtmlc-shipment-status-primaryText",
|
|
@@ -122,25 +138,19 @@ class Selectors:
|
|
|
122
138
|
TRANSACTIONS_CONTAINER_SELECTOR = "div"
|
|
123
139
|
TRANSACTIONS_SELECTOR = "div.apx-transactions-line-item-component-container"
|
|
124
140
|
|
|
125
|
-
TRANSACTIONS_NEXT_PAGE_INPUT_SELECTOR =
|
|
126
|
-
"input[type='submit'][name^='ppw-widgetEvent:DefaultNextPageNavigationEvent']"
|
|
127
|
-
)
|
|
141
|
+
TRANSACTIONS_NEXT_PAGE_INPUT_SELECTOR = [
|
|
142
|
+
"input[type='submit'][name^='ppw-widgetEvent:DefaultNextPageNavigationEvent']"]
|
|
128
143
|
TRANSACTIONS_NEXT_PAGE_INPUT_STATE_SELECTOR = "input[name='ppw-widgetState']"
|
|
129
144
|
TRANSACTIONS_NEXT_PAGE_INPUT_IE_SELECTOR = "input[name='ie']"
|
|
130
145
|
|
|
131
146
|
FIELD_TRANSACTION_COMPLETED_DATE_SELECTOR = "span"
|
|
132
|
-
FIELD_TRANSACTION_PAYMENT_METHOD_SELECTOR =
|
|
133
|
-
"div.apx-transactions-line-item-component-container > div:nth-child(1) span.a-size-base"
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
"div.apx-transactions-line-item-component-container > div:nth-child(2) a.a-link-normal"
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
"div.apx-transactions-line-item-component-container > div:nth-child(2) a.a-link-normal"
|
|
143
|
-
)
|
|
144
|
-
FIELD_TRANSACTION_SELLER_NAME_SELECTOR = (
|
|
145
|
-
"div.apx-transactions-line-item-component-container > div:nth-child(3) span.a-size-base"
|
|
146
|
-
)
|
|
147
|
+
FIELD_TRANSACTION_PAYMENT_METHOD_SELECTOR = [
|
|
148
|
+
"div.apx-transactions-line-item-component-container > div:nth-child(1) span.a-size-base"]
|
|
149
|
+
FIELD_TRANSACTION_GRAND_TOTAL_SELECTOR = [
|
|
150
|
+
"div.apx-transactions-line-item-component-container > div:nth-child(1) span.a-size-base-plus"]
|
|
151
|
+
FIELD_TRANSACTION_ORDER_NUMBER_SELECTOR = [
|
|
152
|
+
"div.apx-transactions-line-item-component-container > div:nth-child(2) a.a-link-normal"]
|
|
153
|
+
FIELD_TRANSACTION_ORDER_LINK_SELECTOR = [
|
|
154
|
+
"div.apx-transactions-line-item-component-container > div:nth-child(2) a.a-link-normal"]
|
|
155
|
+
FIELD_TRANSACTION_SELLER_NAME_SELECTOR = [
|
|
156
|
+
"div.apx-transactions-line-item-component-container > div:nth-child(3) span.a-size-base"]
|
|
@@ -8,6 +8,7 @@ from typing import Dict, List, Optional, Tuple
|
|
|
8
8
|
from bs4 import Tag
|
|
9
9
|
from dateutil import parser
|
|
10
10
|
|
|
11
|
+
from amazonorders import util
|
|
11
12
|
from amazonorders.conf import AmazonOrdersConfig
|
|
12
13
|
from amazonorders.entity.transaction import Transaction
|
|
13
14
|
from amazonorders.exception import AmazonOrdersError
|
|
@@ -16,55 +17,36 @@ from amazonorders.session import AmazonSession
|
|
|
16
17
|
logger = logging.getLogger(__name__)
|
|
17
18
|
|
|
18
19
|
|
|
19
|
-
def
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def _parse_transaction_form_tag(
|
|
24
|
-
form_tag: Tag, config: AmazonOrdersConfig
|
|
25
|
-
) -> Tuple[List[Transaction], Optional[str], Optional[Dict[str, str]]]:
|
|
20
|
+
def _parse_transaction_form_tag(form_tag: Tag,
|
|
21
|
+
config: AmazonOrdersConfig) \
|
|
22
|
+
-> Tuple[List[Transaction], Optional[str], Optional[Dict[str, str]]]:
|
|
26
23
|
transactions = []
|
|
27
|
-
date_container_tags =
|
|
28
|
-
config.selectors.TRANSACTION_DATE_CONTAINERS_SELECTOR
|
|
29
|
-
)
|
|
24
|
+
date_container_tags = util.select(form_tag, config.selectors.TRANSACTION_DATE_CONTAINERS_SELECTOR)
|
|
30
25
|
for date_container_tag in date_container_tags:
|
|
31
|
-
date_tag =
|
|
32
|
-
config.selectors.FIELD_TRANSACTION_COMPLETED_DATE_SELECTOR
|
|
33
|
-
)
|
|
26
|
+
date_tag = util.select_one(date_container_tag, config.selectors.FIELD_TRANSACTION_COMPLETED_DATE_SELECTOR)
|
|
34
27
|
if not date_tag:
|
|
35
|
-
logger.warning("Could not find date tag in
|
|
28
|
+
logger.warning("Could not find date tag in Transaction form.")
|
|
36
29
|
continue
|
|
37
30
|
|
|
38
31
|
date_str = date_tag.text
|
|
39
32
|
date = parser.parse(date_str).date()
|
|
40
33
|
|
|
41
34
|
transactions_container_tag = date_container_tag.find_next_sibling(
|
|
42
|
-
config.selectors.TRANSACTIONS_CONTAINER_SELECTOR
|
|
43
|
-
)
|
|
35
|
+
config.selectors.TRANSACTIONS_CONTAINER_SELECTOR)
|
|
44
36
|
if not isinstance(transactions_container_tag, Tag):
|
|
45
|
-
logger.warning(
|
|
46
|
-
"Could not find transactions container tag in transaction form."
|
|
47
|
-
)
|
|
37
|
+
logger.warning("Could not find transactions container tag in Transaction form.")
|
|
48
38
|
continue
|
|
49
39
|
|
|
50
|
-
transaction_tags =
|
|
51
|
-
config.selectors.TRANSACTIONS_SELECTOR
|
|
52
|
-
)
|
|
40
|
+
transaction_tags = util.select(transactions_container_tag, config.selectors.TRANSACTIONS_SELECTOR)
|
|
53
41
|
for transaction_tag in transaction_tags:
|
|
54
42
|
transaction = Transaction(transaction_tag, config, date)
|
|
55
43
|
transactions.append(transaction)
|
|
56
44
|
|
|
57
|
-
form_state_input =
|
|
58
|
-
|
|
59
|
-
)
|
|
60
|
-
form_ie_input = form_tag.select_one(
|
|
61
|
-
config.selectors.TRANSACTIONS_NEXT_PAGE_INPUT_IE_SELECTOR
|
|
62
|
-
)
|
|
63
|
-
next_page_input = form_tag.select_one(
|
|
64
|
-
config.selectors.TRANSACTIONS_NEXT_PAGE_INPUT_SELECTOR
|
|
65
|
-
)
|
|
45
|
+
form_state_input = util.select_one(form_tag, config.selectors.TRANSACTIONS_NEXT_PAGE_INPUT_STATE_SELECTOR)
|
|
46
|
+
form_ie_input = util.select_one(form_tag, config.selectors.TRANSACTIONS_NEXT_PAGE_INPUT_IE_SELECTOR)
|
|
47
|
+
next_page_input = util.select_one(form_tag, config.selectors.TRANSACTIONS_NEXT_PAGE_INPUT_SELECTOR)
|
|
66
48
|
if not next_page_input or not form_state_input or not form_ie_input:
|
|
67
|
-
return
|
|
49
|
+
return transactions, None, None
|
|
68
50
|
|
|
69
51
|
next_page_post_url = str(form_tag["action"])
|
|
70
52
|
next_page_post_data = {
|
|
@@ -73,7 +55,7 @@ def _parse_transaction_form_tag(
|
|
|
73
55
|
str(next_page_input["name"]): "",
|
|
74
56
|
}
|
|
75
57
|
|
|
76
|
-
return
|
|
58
|
+
return transactions, next_page_post_url, next_page_post_data
|
|
77
59
|
|
|
78
60
|
|
|
79
61
|
class AmazonTransactions:
|
|
@@ -112,15 +94,14 @@ class AmazonTransactions:
|
|
|
112
94
|
if not self.amazon_session.is_authenticated:
|
|
113
95
|
raise AmazonOrdersError("Call AmazonSession.login() to authenticate first.")
|
|
114
96
|
|
|
115
|
-
min_date =
|
|
97
|
+
min_date = datetime.date.today() - datetime.timedelta(days=days)
|
|
116
98
|
|
|
117
99
|
self.amazon_session.get(self.config.constants.TRANSACTION_HISTORY_LANDING_URL)
|
|
118
100
|
if not self.amazon_session.last_response_parsed:
|
|
119
101
|
raise AmazonOrdersError("Could not get transaction history landing page.")
|
|
120
102
|
|
|
121
|
-
form_tag = self.amazon_session.last_response_parsed
|
|
122
|
-
|
|
123
|
-
)
|
|
103
|
+
form_tag = util.select_one(self.amazon_session.last_response_parsed,
|
|
104
|
+
self.config.selectors.TRANSACTION_HISTORY_FORM_SELECTOR)
|
|
124
105
|
|
|
125
106
|
transactions: List[Transaction] = []
|
|
126
107
|
while form_tag:
|
|
@@ -140,8 +121,7 @@ class AmazonTransactions:
|
|
|
140
121
|
if not self.amazon_session.last_response_parsed:
|
|
141
122
|
raise AmazonOrdersError("Could not get next transaction history page.")
|
|
142
123
|
|
|
143
|
-
form_tag = self.amazon_session.last_response_parsed
|
|
144
|
-
|
|
145
|
-
)
|
|
124
|
+
form_tag = util.select_one(self.amazon_session.last_response_parsed,
|
|
125
|
+
self.config.selectors.TRANSACTION_HISTORY_FORM_SELECTOR)
|
|
146
126
|
|
|
147
127
|
return transactions
|
|
@@ -86,10 +86,10 @@ class TestCli(UnitTestCase):
|
|
|
86
86
|
self.assertIn("Order #112-2961628-4757846", response.output)
|
|
87
87
|
|
|
88
88
|
@responses.activate
|
|
89
|
-
@patch("amazonorders.transactions.
|
|
89
|
+
@patch("amazonorders.transactions.datetime", wraps=datetime)
|
|
90
90
|
def test_transactions_command(self, mock_get_today: Mock):
|
|
91
91
|
# GIVEN
|
|
92
|
-
mock_get_today.return_value = datetime.date(2024, 10, 11)
|
|
92
|
+
mock_get_today.date.today.return_value = datetime.date(2024, 10, 11)
|
|
93
93
|
days = 1
|
|
94
94
|
self.given_login_responses_success()
|
|
95
95
|
with open(os.path.join(self.RESOURCES_DIR, "get-transactions.html"), "r", encoding="utf-8") as f:
|
|
@@ -103,6 +103,30 @@ class TestOrders(UnitTestCase):
|
|
|
103
103
|
self.assertEqual(1, resp2.call_count)
|
|
104
104
|
self.assertEqual(1, resp3.call_count)
|
|
105
105
|
|
|
106
|
+
@responses.activate
|
|
107
|
+
def test_get_order_history_skip_fresh(self):
|
|
108
|
+
# GIVEN
|
|
109
|
+
self.amazon_session.is_authenticated = True
|
|
110
|
+
year = 2024
|
|
111
|
+
start_index = 0
|
|
112
|
+
resp1 = self.given_order_history_landing_exists()
|
|
113
|
+
with open(os.path.join(self.RESOURCES_DIR, "order-history-fresh.html"), "r",
|
|
114
|
+
encoding="utf-8") as f:
|
|
115
|
+
resp2 = responses.add(
|
|
116
|
+
responses.GET,
|
|
117
|
+
self.test_config.constants.ORDER_HISTORY_URL,
|
|
118
|
+
body=f.read(),
|
|
119
|
+
status=200,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
# WHEN
|
|
123
|
+
orders = self.amazon_orders.get_order_history(year=year, start_index=start_index)
|
|
124
|
+
|
|
125
|
+
# THEN
|
|
126
|
+
self.assertEqual(9, len(orders))
|
|
127
|
+
self.assertEqual(1, resp1.call_count)
|
|
128
|
+
self.assertEqual(1, resp2.call_count)
|
|
129
|
+
|
|
106
130
|
@responses.activate
|
|
107
131
|
def test_get_order_history_full_details(self):
|
|
108
132
|
# GIVEN
|
|
@@ -308,6 +332,27 @@ class TestOrders(UnitTestCase):
|
|
|
308
332
|
self.assert_order_114_8722141_6545058_data_component_subscription(order, True)
|
|
309
333
|
self.assertEqual(1, resp1.call_count)
|
|
310
334
|
|
|
335
|
+
@responses.activate
|
|
336
|
+
def test_get_order_2024_data_component_multiple_shipments(self):
|
|
337
|
+
# GIVEN
|
|
338
|
+
self.amazon_session.is_authenticated = True
|
|
339
|
+
order_id = "111-6778632-7354601"
|
|
340
|
+
with open(os.path.join(self.RESOURCES_DIR, f"order-details-{order_id}.html"), "r",
|
|
341
|
+
encoding="utf-8") as f:
|
|
342
|
+
resp1 = responses.add(
|
|
343
|
+
responses.GET,
|
|
344
|
+
f"{self.test_config.constants.ORDER_DETAILS_URL}?orderID={order_id}",
|
|
345
|
+
body=f.read(),
|
|
346
|
+
status=200,
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
# WHEN
|
|
350
|
+
order = self.amazon_orders.get_order(order_id)
|
|
351
|
+
|
|
352
|
+
# THEN
|
|
353
|
+
self.assert_order_111_6778632_7354601_data_component_subscription(order, True)
|
|
354
|
+
self.assertEqual(1, resp1.call_count)
|
|
355
|
+
|
|
311
356
|
@unittest.skipIf(not os.path.exists(temp_order_history_file_path),
|
|
312
357
|
reason="Skipped, to debug an order history page, "
|
|
313
358
|
"place it at tests/output/temp-order-history.html")
|
|
@@ -31,16 +31,16 @@ class TestOrders(UnitTestCase):
|
|
|
31
31
|
self.amazon_transactions = AmazonTransactions(self.amazon_session)
|
|
32
32
|
|
|
33
33
|
@responses.activate
|
|
34
|
-
@patch("amazonorders.transactions.
|
|
34
|
+
@patch("amazonorders.transactions.datetime", wraps=datetime)
|
|
35
35
|
def test_transactions_command(self, mock_get_today: Mock):
|
|
36
36
|
# GIVEN
|
|
37
|
-
mock_get_today.return_value = datetime.date(2024, 10, 11)
|
|
37
|
+
mock_get_today.date.today.return_value = datetime.date(2024, 10, 11)
|
|
38
38
|
days = 1
|
|
39
39
|
self.amazon_session.is_authenticated = True
|
|
40
40
|
with open(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
os.path.join(self.RESOURCES_DIR, "get-transactions.html"),
|
|
42
|
+
"r",
|
|
43
|
+
encoding="utf-8",
|
|
44
44
|
) as f:
|
|
45
45
|
responses.add(
|
|
46
46
|
responses.GET,
|
|
@@ -217,8 +217,8 @@ class TestCase(unittest.TestCase):
|
|
|
217
217
|
self.assertIn("Delivered May 13", shipment.delivery_status)
|
|
218
218
|
self.assertTrue(found_kimoe)
|
|
219
219
|
self.assertTrue(found_amazon)
|
|
220
|
-
self.assertEqual(str(order.items),
|
|
221
|
-
str(order.shipments[0].items + order.shipments[1].items))
|
|
220
|
+
self.assertEqual(str(order.items.sort()),
|
|
221
|
+
str((order.shipments[0].items + order.shipments[1].items).sort()))
|
|
222
222
|
self.assertEqual(2, len(order.items))
|
|
223
223
|
found_kimoe = False
|
|
224
224
|
found_amazon = False
|
|
@@ -439,6 +439,51 @@ class TestCase(unittest.TestCase):
|
|
|
439
439
|
order.items[0].seller.name)
|
|
440
440
|
self.assertIsNone(order.items[0].seller.link)
|
|
441
441
|
|
|
442
|
+
def assert_order_111_6778632_7354601_data_component_subscription(self, order, full_details=False):
|
|
443
|
+
self.assertEqual("111-6778632-7354601", order.order_number)
|
|
444
|
+
self.assertEqual(60.88, order.grand_total)
|
|
445
|
+
self.assertIsNotNone(order.order_details_link)
|
|
446
|
+
self.assertEqual(date(2024, 9, 8), order.order_placed_date)
|
|
447
|
+
self.assertEqual("Name1 & Name2", order.recipient.name)
|
|
448
|
+
self.assertIn("Address2", order.recipient.address)
|
|
449
|
+
self.assertEqual(2, len(order.shipments))
|
|
450
|
+
self.assertEqual(str(order.items.sort()),
|
|
451
|
+
str((order.shipments[0].items + order.shipments[1].items).sort()))
|
|
452
|
+
self.assertEqual(3, len(order.shipments[0].items))
|
|
453
|
+
self.assertEqual(1, len(order.shipments[1].items))
|
|
454
|
+
self.assertEqual("Delivered September 9", order.shipments[0].delivery_status)
|
|
455
|
+
self.assertEqual("Delivered September 9", order.shipments[1].delivery_status)
|
|
456
|
+
self.assertEqual(4, len(order.items))
|
|
457
|
+
self.assertEqual(
|
|
458
|
+
"Dxhycc Satin Pirate Sash Pirate Medieval Renaissance Large Sash Halloween Costume Waist "
|
|
459
|
+
"Sash Belt, Red",
|
|
460
|
+
order.items[0].title)
|
|
461
|
+
self.assertEqual("Ziploc Paper Sandwich and Snack Bags, Recyclable & Sealable with Fun Designs, "
|
|
462
|
+
"150 Total Bags",
|
|
463
|
+
order.items[3].title)
|
|
464
|
+
self.assertIsNotNone(order.items[0].link)
|
|
465
|
+
self.assertIsNotNone(order.items[0].image_link)
|
|
466
|
+
self.assertIsNotNone(order.items[3].link)
|
|
467
|
+
self.assertIsNotNone(order.items[3].image_link)
|
|
468
|
+
|
|
469
|
+
self.assertEqual(order.full_details, full_details)
|
|
470
|
+
|
|
471
|
+
if full_details:
|
|
472
|
+
self.assertEqual("Prime Visa", order.payment_method)
|
|
473
|
+
self.assertEqual(1111, order.payment_method_last_4)
|
|
474
|
+
self.assertEqual(57.69, order.subtotal)
|
|
475
|
+
self.assertEqual(2.99, order.shipping_total)
|
|
476
|
+
self.assertEqual(57.69, order.total_before_tax)
|
|
477
|
+
self.assertEqual(3.19, order.estimated_tax)
|
|
478
|
+
self.assertEqual(date(2024, 10, 9), order.items[0].return_eligible_date)
|
|
479
|
+
self.assertEqual(date(2024, 10, 9), order.items[1].return_eligible_date)
|
|
480
|
+
self.assertEqual(date(2024, 10, 9), order.items[2].return_eligible_date)
|
|
481
|
+
self.assertEqual(date(2024, 10, 9), order.items[3].return_eligible_date)
|
|
482
|
+
self.assertEqual(7.49, order.items[0].price)
|
|
483
|
+
self.assertEqual(18.95, order.items[1].price)
|
|
484
|
+
self.assertEqual(9.98, order.items[2].price)
|
|
485
|
+
self.assertEqual(21.27, order.items[3].price)
|
|
486
|
+
|
|
442
487
|
def assert_populated_generic(self, order, full_details):
|
|
443
488
|
self.assertIsNotNone(order.order_number)
|
|
444
489
|
self.assertIsNotNone(order.grand_total)
|
|
@@ -448,10 +493,16 @@ class TestCase(unittest.TestCase):
|
|
|
448
493
|
self.assertIsNotNone(order.recipient.name)
|
|
449
494
|
self.assertIsNotNone(order.recipient.address)
|
|
450
495
|
self.assertGreaterEqual(len(order.shipments), 1)
|
|
451
|
-
|
|
496
|
+
shipment_items = []
|
|
497
|
+
for shipment in order.shipments:
|
|
498
|
+
shipment_items += shipment.items
|
|
499
|
+
self.assertGreaterEqual(len(shipment.items), 1)
|
|
500
|
+
self.assertIsNotNone(shipment.delivery_status)
|
|
501
|
+
self.assertEqual(str(order.items.sort()), str(shipment_items.sort()))
|
|
452
502
|
self.assertGreaterEqual(len(order.items), 1)
|
|
453
|
-
|
|
454
|
-
|
|
503
|
+
for item in order.items:
|
|
504
|
+
self.assertIsNotNone(item.title)
|
|
505
|
+
self.assertIsNotNone(item.link)
|
|
455
506
|
|
|
456
507
|
self.assertEqual(order.full_details, full_details)
|
|
457
508
|
|
|
@@ -464,5 +515,6 @@ class TestCase(unittest.TestCase):
|
|
|
464
515
|
self.assertIsNotNone(order.total_before_tax)
|
|
465
516
|
self.assertIsNotNone(order.estimated_tax)
|
|
466
517
|
if order.recipient:
|
|
467
|
-
|
|
468
|
-
|
|
518
|
+
for item in order.items:
|
|
519
|
+
self.assertIsNotNone(item.price)
|
|
520
|
+
self.assertIsNotNone(item.seller.name)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|