ynab-amazon-categorizer 2.2.3__tar.gz → 2.2.4__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.
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/CHANGELOG.md +12 -0
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/DEVELOPMENT.md +1 -1
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/PKG-INFO +2 -2
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/README.md +1 -1
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/src/ynab_amazon_categorizer/_version.py +3 -3
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/src/ynab_amazon_categorizer/amazon_parser.py +2 -2
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/src/ynab_amazon_categorizer/cli.py +3 -6
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/src/ynab_amazon_categorizer.egg-info/PKG-INFO +2 -2
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/tests/test_amazon_parser.py +16 -0
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/tests/test_cli_functions.py +55 -0
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/.env.example +0 -0
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/.git_archival.txt +0 -0
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/.gitattributes +0 -0
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/.github/workflows/release.yml +0 -0
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/.gitignore +0 -0
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/.pre-commit-config.yaml +0 -0
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/AGENTS.md +0 -0
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/CLAUDE.md +0 -0
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/LICENSE +0 -0
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/pyproject.toml +0 -0
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/setup.cfg +0 -0
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/src/ynab_amazon_categorizer/__init__.py +0 -0
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/src/ynab_amazon_categorizer/__main__.py +0 -0
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/src/ynab_amazon_categorizer/config.py +0 -0
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/src/ynab_amazon_categorizer/exceptions.py +0 -0
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/src/ynab_amazon_categorizer/memo_generator.py +0 -0
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/src/ynab_amazon_categorizer/transaction_matcher.py +0 -0
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/src/ynab_amazon_categorizer/ynab_client.py +0 -0
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/src/ynab_amazon_categorizer.egg-info/SOURCES.txt +0 -0
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/src/ynab_amazon_categorizer.egg-info/dependency_links.txt +0 -0
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/src/ynab_amazon_categorizer.egg-info/entry_points.txt +0 -0
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/src/ynab_amazon_categorizer.egg-info/requires.txt +0 -0
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/src/ynab_amazon_categorizer.egg-info/top_level.txt +0 -0
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/tests/__init__.py +0 -0
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/tests/test_amazon_parser_real.py +0 -0
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/tests/test_config.py +0 -0
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/tests/test_memo_generator.py +0 -0
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/tests/test_transaction_matcher.py +0 -0
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/tests/test_ynab_client.py +0 -0
- {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/uv.lock +0 -0
|
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.2.4] - 2026-05-13
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Order totals with thousands separators (e.g. `$1,234.56`) are now parsed correctly
|
|
15
|
+
- Single-category matched orders now show all item names in the suggested memo, not just the first
|
|
16
|
+
- Transaction amount is displayed with the correct sign (no spurious negation)
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- Minimum supported Python version clarified to 3.12+ in README and DEVELOPMENT.md
|
|
21
|
+
|
|
10
22
|
## [2.2.3] - 2026-05-05
|
|
11
23
|
|
|
12
24
|
### Fixed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ynab-amazon-categorizer
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.4
|
|
4
4
|
Summary: Automatically categorize Amazon transactions in YNAB with rich item information
|
|
5
5
|
Author: dizzlkheinz
|
|
6
6
|
License: GPL-3.0
|
|
@@ -49,7 +49,7 @@ When you paste in the text from your Amazon order page:
|
|
|
49
49
|
|
|
50
50
|
## Prerequisites
|
|
51
51
|
|
|
52
|
-
- Python 3.12+
|
|
52
|
+
- Python 3.12+
|
|
53
53
|
- [uv](https://docs.astral.sh/uv/) (recommended) or standard Python tooling
|
|
54
54
|
- YNAB account with API access
|
|
55
55
|
|
|
@@ -15,7 +15,7 @@ When you paste in the text from your Amazon order page:
|
|
|
15
15
|
|
|
16
16
|
## Prerequisites
|
|
17
17
|
|
|
18
|
-
- Python 3.12+
|
|
18
|
+
- Python 3.12+
|
|
19
19
|
- [uv](https://docs.astral.sh/uv/) (recommended) or standard Python tooling
|
|
20
20
|
- YNAB account with API access
|
|
21
21
|
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '2.2.
|
|
22
|
-
__version_tuple__ = version_tuple = (2, 2,
|
|
21
|
+
__version__ = version = '2.2.4'
|
|
22
|
+
__version_tuple__ = version_tuple = (2, 2, 4)
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'g034b5e27c'
|
|
@@ -49,14 +49,14 @@ class AmazonParser:
|
|
|
49
49
|
orders = []
|
|
50
50
|
|
|
51
51
|
# Find all order blocks using regex
|
|
52
|
-
order_pattern = r"Order placed\s*([A-Za-z]+ \d+, \d{4})\s*Total\s*\$(
|
|
52
|
+
order_pattern = r"Order placed\s*([A-Za-z]+ \d+, \d{4})\s*Total\s*\$([0-9][0-9,]*(?:\.[0-9]{1,2})?)\s*.*?Order # (\d{3}-\d{7}-\d{7})"
|
|
53
53
|
order_matches = list(
|
|
54
54
|
re.finditer(order_pattern, orders_text, re.DOTALL | re.IGNORECASE)
|
|
55
55
|
)
|
|
56
56
|
|
|
57
57
|
for idx, match in enumerate(order_matches):
|
|
58
58
|
order_date = match.group(1).strip()
|
|
59
|
-
order_total = float(match.group(2))
|
|
59
|
+
order_total = float(match.group(2).replace(",", ""))
|
|
60
60
|
order_id = match.group(3)
|
|
61
61
|
|
|
62
62
|
# Find the content after this order until the next order or end
|
{ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/src/ynab_amazon_categorizer/cli.py
RENAMED
|
@@ -427,12 +427,9 @@ def resolve_memo(
|
|
|
427
427
|
if item_details:
|
|
428
428
|
if isinstance(item_details, dict) and "items" in item_details:
|
|
429
429
|
# Auto-matched order data - format as: Item Name\n Order Link
|
|
430
|
-
items_list = item_details["items"]
|
|
431
430
|
items_text = (
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
else "Amazon Purchase"
|
|
435
|
-
)
|
|
431
|
+
generate_split_summary_memo(matching_order) if matching_order else ""
|
|
432
|
+
) or "Amazon Purchase"
|
|
436
433
|
order_id_value = item_details["order_id"]
|
|
437
434
|
order_link = memo_generator.generate_amazon_order_link(
|
|
438
435
|
order_id_value if isinstance(order_id_value, str) else None
|
|
@@ -639,7 +636,7 @@ def process_transaction(
|
|
|
639
636
|
print(f" ID: {transaction_id}")
|
|
640
637
|
print(f" Date: {date}")
|
|
641
638
|
print(f" Payee: {payee}")
|
|
642
|
-
print(f" Amount: {
|
|
639
|
+
print(f" Amount: {amount_float:.2f}")
|
|
643
640
|
if original_memo:
|
|
644
641
|
print(f" Original Memo: {original_memo}")
|
|
645
642
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ynab-amazon-categorizer
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.4
|
|
4
4
|
Summary: Automatically categorize Amazon transactions in YNAB with rich item information
|
|
5
5
|
Author: dizzlkheinz
|
|
6
6
|
License: GPL-3.0
|
|
@@ -49,7 +49,7 @@ When you paste in the text from your Amazon order page:
|
|
|
49
49
|
|
|
50
50
|
## Prerequisites
|
|
51
51
|
|
|
52
|
-
- Python 3.12+
|
|
52
|
+
- Python 3.12+
|
|
53
53
|
- [uv](https://docs.astral.sh/uv/) (recommended) or standard Python tooling
|
|
54
54
|
- YNAB account with API access
|
|
55
55
|
|
|
@@ -24,6 +24,22 @@ def test_parse_simple_order() -> None:
|
|
|
24
24
|
assert "Fancy Feast" in order.items[0]
|
|
25
25
|
|
|
26
26
|
|
|
27
|
+
def test_parse_comma_formatted_total() -> None:
|
|
28
|
+
"""Totals with thousands separators are parsed as the full amount."""
|
|
29
|
+
order_text = """
|
|
30
|
+
Order placed January 1, 2026
|
|
31
|
+
Total $1,234.56
|
|
32
|
+
Order # 702-8237239-1234567
|
|
33
|
+
Premium Product Name With Enough Words To Parse
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
parser = AmazonParser()
|
|
37
|
+
orders = parser.parse_orders_page(order_text)
|
|
38
|
+
|
|
39
|
+
assert len(orders) == 1
|
|
40
|
+
assert orders[0].total == 1234.56
|
|
41
|
+
|
|
42
|
+
|
|
27
43
|
def test_parse_empty_order_text() -> None:
|
|
28
44
|
"""Test parsing empty order text returns empty list."""
|
|
29
45
|
parser = AmazonParser()
|
|
@@ -14,6 +14,8 @@ from ynab_amazon_categorizer.cli import (
|
|
|
14
14
|
fetch_amazon_transactions,
|
|
15
15
|
generate_split_summary_memo,
|
|
16
16
|
print_config_summary,
|
|
17
|
+
process_transaction,
|
|
18
|
+
resolve_memo,
|
|
17
19
|
)
|
|
18
20
|
from ynab_amazon_categorizer.config import Config
|
|
19
21
|
from ynab_amazon_categorizer.memo_generator import MemoGenerator
|
|
@@ -168,6 +170,23 @@ def test_build_split_payload_with_order() -> None:
|
|
|
168
170
|
assert "Widget B" in result["memo"]
|
|
169
171
|
|
|
170
172
|
|
|
173
|
+
def test_resolve_memo_keeps_all_matched_items(
|
|
174
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
175
|
+
) -> None:
|
|
176
|
+
"""Single-category matched orders keep all parsed items in the suggested memo."""
|
|
177
|
+
order = Order()
|
|
178
|
+
order.order_id = "702-1234567-7654321"
|
|
179
|
+
order.items = ["Widget A", "Widget B"]
|
|
180
|
+
|
|
181
|
+
monkeypatch.setattr("builtins.input", lambda _prompt: "")
|
|
182
|
+
|
|
183
|
+
result = resolve_memo(order, "", MemoGenerator("amazon.com"))
|
|
184
|
+
|
|
185
|
+
assert "Widget A" in result
|
|
186
|
+
assert "Widget B" in result
|
|
187
|
+
assert "702-1234567-7654321" in result
|
|
188
|
+
|
|
189
|
+
|
|
171
190
|
# --- print_config_summary tests ---
|
|
172
191
|
|
|
173
192
|
|
|
@@ -297,6 +316,42 @@ def test_fetch_amazon_transactions_includes_manual() -> None:
|
|
|
297
316
|
assert result[0]["id"] == "t1"
|
|
298
317
|
|
|
299
318
|
|
|
319
|
+
# --- process_transaction display tests ---
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def test_process_transaction_displays_inflow_amount_without_negating(
|
|
323
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
324
|
+
capsys: pytest.CaptureFixture[str],
|
|
325
|
+
) -> None:
|
|
326
|
+
"""Accepted inflows display with their actual positive sign."""
|
|
327
|
+
transaction = {
|
|
328
|
+
"id": "t1",
|
|
329
|
+
"date": "2025-01-15",
|
|
330
|
+
"payee_name": "Amazon",
|
|
331
|
+
"amount": 10000,
|
|
332
|
+
"memo": "",
|
|
333
|
+
}
|
|
334
|
+
responses = iter(["y", "s"])
|
|
335
|
+
monkeypatch.setattr("builtins.input", lambda _prompt: next(responses))
|
|
336
|
+
|
|
337
|
+
result = process_transaction(
|
|
338
|
+
transaction,
|
|
339
|
+
0,
|
|
340
|
+
1,
|
|
341
|
+
None,
|
|
342
|
+
MemoGenerator(),
|
|
343
|
+
Mock(),
|
|
344
|
+
Mock(),
|
|
345
|
+
{},
|
|
346
|
+
{},
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
captured = capsys.readouterr().out
|
|
350
|
+
assert result is True
|
|
351
|
+
assert "Found inflow transaction: Amazon $10.00" in captured
|
|
352
|
+
assert "Amount: 10.00" in captured
|
|
353
|
+
|
|
354
|
+
|
|
300
355
|
# --- generate_split_summary_memo tests ---
|
|
301
356
|
|
|
302
357
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/.github/workflows/release.yml
RENAMED
|
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
|
{ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/tests/test_amazon_parser_real.py
RENAMED
|
File without changes
|
|
File without changes
|
{ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/tests/test_memo_generator.py
RENAMED
|
File without changes
|
{ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/tests/test_transaction_matcher.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|