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.
Files changed (40) hide show
  1. {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/CHANGELOG.md +12 -0
  2. {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/DEVELOPMENT.md +1 -1
  3. {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/PKG-INFO +2 -2
  4. {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/README.md +1 -1
  5. {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/src/ynab_amazon_categorizer/_version.py +3 -3
  6. {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/src/ynab_amazon_categorizer/amazon_parser.py +2 -2
  7. {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/src/ynab_amazon_categorizer/cli.py +3 -6
  8. {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/src/ynab_amazon_categorizer.egg-info/PKG-INFO +2 -2
  9. {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/tests/test_amazon_parser.py +16 -0
  10. {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/tests/test_cli_functions.py +55 -0
  11. {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/.env.example +0 -0
  12. {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/.git_archival.txt +0 -0
  13. {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/.gitattributes +0 -0
  14. {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/.github/workflows/release.yml +0 -0
  15. {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/.gitignore +0 -0
  16. {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/.pre-commit-config.yaml +0 -0
  17. {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/AGENTS.md +0 -0
  18. {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/CLAUDE.md +0 -0
  19. {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/LICENSE +0 -0
  20. {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/pyproject.toml +0 -0
  21. {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/setup.cfg +0 -0
  22. {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/src/ynab_amazon_categorizer/__init__.py +0 -0
  23. {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/src/ynab_amazon_categorizer/__main__.py +0 -0
  24. {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/src/ynab_amazon_categorizer/config.py +0 -0
  25. {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/src/ynab_amazon_categorizer/exceptions.py +0 -0
  26. {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/src/ynab_amazon_categorizer/memo_generator.py +0 -0
  27. {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/src/ynab_amazon_categorizer/transaction_matcher.py +0 -0
  28. {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/src/ynab_amazon_categorizer/ynab_client.py +0 -0
  29. {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/src/ynab_amazon_categorizer.egg-info/SOURCES.txt +0 -0
  30. {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/src/ynab_amazon_categorizer.egg-info/dependency_links.txt +0 -0
  31. {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/src/ynab_amazon_categorizer.egg-info/entry_points.txt +0 -0
  32. {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/src/ynab_amazon_categorizer.egg-info/requires.txt +0 -0
  33. {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/src/ynab_amazon_categorizer.egg-info/top_level.txt +0 -0
  34. {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/tests/__init__.py +0 -0
  35. {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/tests/test_amazon_parser_real.py +0 -0
  36. {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/tests/test_config.py +0 -0
  37. {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/tests/test_memo_generator.py +0 -0
  38. {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/tests/test_transaction_matcher.py +0 -0
  39. {ynab_amazon_categorizer-2.2.3 → ynab_amazon_categorizer-2.2.4}/tests/test_ynab_client.py +0 -0
  40. {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
@@ -4,7 +4,7 @@ Practical commands and conventions for working on this repository.
4
4
 
5
5
  ## Prerequisites
6
6
 
7
- - Python 3.10+
7
+ - Python 3.12+
8
8
  - [uv](https://docs.astral.sh/uv/)
9
9
 
10
10
  ## Setup
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ynab-amazon-categorizer
3
- Version: 2.2.3
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+ (recommended; 3.10+ supported)
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+ (recommended; 3.10+ supported)
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.3'
22
- __version_tuple__ = version_tuple = (2, 2, 3)
21
+ __version__ = version = '2.2.4'
22
+ __version_tuple__ = version_tuple = (2, 2, 4)
23
23
 
24
- __commit_id__ = commit_id = 'gb865ea2d2'
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*\$(\d+\.?\d*)\s*.*?Order # (\d{3}-\d{7}-\d{7})"
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
@@ -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
- items_list[0]
433
- if isinstance(items_list, list) and items_list
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: {-amount_float:.2f}")
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
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+ (recommended; 3.10+ supported)
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