summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-06-30 11:04:38 +0200
committerDanilo M. <danix@danix.xyz>2026-06-30 11:04:38 +0200
commit28bde5b10abf904212dfc3cae937112134293053 (patch)
treeb12aba30abeaba15a10458032e11e7d01cb69bd2
parent49c9af4c213d5f420a405d860c0454fade26c297 (diff)
downloadfirefly-cli-28bde5b10abf904212dfc3cae937112134293053.tar.gz
firefly-cli-28bde5b10abf904212dfc3cae937112134293053.zip
feat: account, category, tag, auth commands
-rw-r--r--firefly_cli/commands/__init__.py3
-rw-r--r--firefly_cli/commands/account.py28
-rw-r--r--firefly_cli/commands/auth.py21
-rw-r--r--firefly_cli/commands/category.py8
-rw-r--r--firefly_cli/commands/tag.py8
-rw-r--r--tests/unit/test_commands_account.py27
6 files changed, 95 insertions, 0 deletions
diff --git a/firefly_cli/commands/__init__.py b/firefly_cli/commands/__init__.py
new file mode 100644
index 0000000..67cab2e
--- /dev/null
+++ b/firefly_cli/commands/__init__.py
@@ -0,0 +1,3 @@
+# Copyright (C) 2026 Danilo M. <danix@danix.xyz> GPL-2.0-only
+# Importing each module runs its @registry.command decorators.
+from firefly_cli.commands import auth, account, category, tag # noqa: F401
diff --git a/firefly_cli/commands/account.py b/firefly_cli/commands/account.py
new file mode 100644
index 0000000..fa2dc65
--- /dev/null
+++ b/firefly_cli/commands/account.py
@@ -0,0 +1,28 @@
+# Copyright (C) 2026 Danilo M. <danix@danix.xyz> GPL-2.0-only
+from firefly_cli import registry, output
+
+def _list_args(p):
+ p.add_argument("--type", help="filter: asset, expense, revenue, liability, ...")
+
+@registry.command("account list", help="list accounts", args=_list_args)
+def cmd_list(args, ctx):
+ params = {"type": args.type} if getattr(args, "type", None) else None
+ resp = ctx.client.request("GET", "/api/v1/accounts", params=params)
+ output.emit(output.unwrap(resp), human=ctx.human)
+ return 0
+
+def _name_arg(p):
+ p.add_argument("account", help="account name or id")
+
+@registry.command("account get", help="show one account", args=_name_arg)
+def cmd_get(args, ctx):
+ acc = ctx.resolver.account(args.account)
+ output.emit(acc, human=ctx.human)
+ return 0
+
+@registry.command("account balance", help="show account balance", args=_name_arg)
+def cmd_balance(args, ctx):
+ acc = ctx.resolver.account(args.account)
+ output.emit({"id": acc["id"], "name": acc.get("name"),
+ "current_balance": acc.get("current_balance")}, human=ctx.human)
+ return 0
diff --git a/firefly_cli/commands/auth.py b/firefly_cli/commands/auth.py
new file mode 100644
index 0000000..d2319e9
--- /dev/null
+++ b/firefly_cli/commands/auth.py
@@ -0,0 +1,21 @@
+# Copyright (C) 2026 Danilo M. <danix@danix.xyz> GPL-2.0-only
+import getpass
+from firefly_cli import registry, output, config
+
+def _set_args(p):
+ p.add_argument("--url", help="Firefly III base URL")
+ p.add_argument("--token", help="Personal Access Token")
+
+@registry.command("auth set", help="write url+token to config", args=_set_args)
+def cmd_set(args, ctx):
+ url = args.url or input("Firefly III URL: ").strip()
+ token = args.token or getpass.getpass("Personal Access Token: ").strip()
+ path = config.write(url, token)
+ output.emit({"written": str(path)}, human=ctx.human)
+ return 0
+
+@registry.command("auth test", help="verify connectivity and token")
+def cmd_test(args, ctx):
+ resp = ctx.client.request("GET", "/api/v1/about")
+ output.emit(resp.get("data", resp), human=ctx.human)
+ return 0
diff --git a/firefly_cli/commands/category.py b/firefly_cli/commands/category.py
new file mode 100644
index 0000000..a7ddbe8
--- /dev/null
+++ b/firefly_cli/commands/category.py
@@ -0,0 +1,8 @@
+# Copyright (C) 2026 Danilo M. <danix@danix.xyz> GPL-2.0-only
+from firefly_cli import registry, output
+
+@registry.command("category list", help="list categories")
+def cmd_list(args, ctx):
+ resp = ctx.client.request("GET", "/api/v1/categories")
+ output.emit(output.unwrap(resp), human=ctx.human)
+ return 0
diff --git a/firefly_cli/commands/tag.py b/firefly_cli/commands/tag.py
new file mode 100644
index 0000000..668c4ae
--- /dev/null
+++ b/firefly_cli/commands/tag.py
@@ -0,0 +1,8 @@
+# Copyright (C) 2026 Danilo M. <danix@danix.xyz> GPL-2.0-only
+from firefly_cli import registry, output
+
+@registry.command("tag list", help="list tags")
+def cmd_list(args, ctx):
+ resp = ctx.client.request("GET", "/api/v1/tags")
+ output.emit(output.unwrap(resp), human=ctx.human)
+ return 0
diff --git a/tests/unit/test_commands_account.py b/tests/unit/test_commands_account.py
new file mode 100644
index 0000000..4d28802
--- /dev/null
+++ b/tests/unit/test_commands_account.py
@@ -0,0 +1,27 @@
+import unittest
+from unittest.mock import MagicMock
+from firefly_cli.commands import account as acct
+from firefly_cli.context import Context
+
+def make_ctx():
+ client = MagicMock()
+ resolver = MagicMock()
+ return Context(client=client, resolver=resolver, human=False), client, resolver
+
+class TestAccountCmd(unittest.TestCase):
+ def test_list_passes_type_filter(self):
+ ctx, client, _ = make_ctx()
+ client.request.return_value = {"data": []}
+ args = MagicMock(type="asset")
+ acct.cmd_list(args, ctx)
+ client.request.assert_called_once_with(
+ "GET", "/api/v1/accounts", params={"type": "asset"})
+
+ def test_balance_resolves_name_and_returns_balance(self):
+ ctx, client, resolver = make_ctx()
+ resolver.account.return_value = {"id": "3", "name": "Checking",
+ "current_balance": "100.00"}
+ args = MagicMock(account="Checking")
+ rc = acct.cmd_balance(args, ctx)
+ resolver.account.assert_called_once_with("Checking")
+ self.assertEqual(rc, 0)