aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--firefly_cli/__init__.py2
-rw-r--r--firefly_cli/commands/transaction.py2
-rw-r--r--firefly_cli/resolver.py5
-rw-r--r--pyproject.toml2
-rw-r--r--tests/unit/test_commands_transaction.py8
5 files changed, 15 insertions, 4 deletions
diff --git a/firefly_cli/__init__.py b/firefly_cli/__init__.py
index 1ed8352..66300ca 100644
--- a/firefly_cli/__init__.py
+++ b/firefly_cli/__init__.py
@@ -2,4 +2,4 @@
# Copyright (C) 2026 Danilo M. <danix@danix.xyz>
# Licensed under the GNU General Public License v2.0 only.
-__version__ = "0.3.7"
+__version__ = "0.3.8"
diff --git a/firefly_cli/commands/transaction.py b/firefly_cli/commands/transaction.py
index 3ce20f3..60af6f4 100644
--- a/firefly_cli/commands/transaction.py
+++ b/firefly_cli/commands/transaction.py
@@ -39,7 +39,6 @@ def _add_args(p):
p.add_argument("--skip-dupes", dest="skip_dupes", action="store_true",
help="skip if a tx with same amount+date+source+destination exists")
-@registry.command("tx add", help="record a transaction; source/destination resolve to accounts, category/tags auto-create", args=_add_args)
def _resolve_side(ctx, name, acc_id, side):
# Exactly one of name/id per side (mutually exclusive). id path (ISSUES.md
# #2) fetches by id, validating existence; name path resolves as before.
@@ -49,6 +48,7 @@ def _resolve_side(ctx, name, acc_id, side):
f"(got name={name!r}, id={acc_id!r})")
return ctx.resolver.account_by_id(acc_id) if acc_id else ctx.resolver.account(name)
+@registry.command("tx add", help="record a transaction; source/destination resolve to accounts, category/tags auto-create", args=_add_args)
def cmd_add(args, ctx):
src = _resolve_side(ctx, args.source, args.source_id, "from")
dst = _resolve_side(ctx, args.dest, args.dest_id, "to")
diff --git a/firefly_cli/resolver.py b/firefly_cli/resolver.py
index 2a73e79..7296aad 100644
--- a/firefly_cli/resolver.py
+++ b/firefly_cli/resolver.py
@@ -30,7 +30,10 @@ class Resolver:
def account_by_id(self, acc_id):
# Escape hatch for same-name accounts (ISSUES.md #2): GET the account
- # directly; a bad id 404s and client.request surfaces a FireflyError.
+ # directly; a bad id errors and client.request surfaces a FireflyError.
+ # ponytail: Firefly returns 401 (not 404) for an unknown account id, so
+ # the message reads "Unauthenticated"; the write is still blocked, which
+ # is what matters. Remap to "account not found" only if it confuses users.
item = self.client.request("GET", f"/api/v1/accounts/{acc_id}")["data"]
return {"id": item["id"], **item.get("attributes", {})}
diff --git a/pyproject.toml b/pyproject.toml
index a356bd8..ae35bd7 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "firefly-iii-agent"
-version = "0.3.7"
+version = "0.3.8"
description = "CLI tool for agent interaction with Firefly III"
readme = "README.md"
requires-python = ">=3.11"
diff --git a/tests/unit/test_commands_transaction.py b/tests/unit/test_commands_transaction.py
index 5f455cb..61a09ab 100644
--- a/tests/unit/test_commands_transaction.py
+++ b/tests/unit/test_commands_transaction.py
@@ -192,6 +192,14 @@ class TestTxAdd(unittest.TestCase):
self.assertEqual(rc, 0)
client.request.assert_not_called() # dry-run wins: no search, no write
+ def test_tx_add_handler_registered_is_cmd_add(self):
+ # Guard the decorator placement: the @registry.command must wrap cmd_add,
+ # not a helper defined between the decorator and the def (a misplacement
+ # unit tests calling cmd_add directly would miss, but CLI dispatch hits).
+ from firefly_cli import registry
+ h = next(c.handler for c in registry.all_commands() if c.name == "tx add")
+ self.assertIs(h, tx.cmd_add)
+
def test_from_id_resolves_by_id_not_name(self):
# --to-id targets an ambiguous account by numeric id (ISSUES.md #2).
ctx, client, resolver = make_ctx()