summaryrefslogtreecommitdiffstats
path: root/firefly_cli/commands
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-07-01 12:13:42 +0200
committerDanilo M. <danix@danix.xyz>2026-07-01 12:13:42 +0200
commit941ccb2cb34944e1321b3dc23731bfa93018d74f (patch)
tree72f5ccb1c9c0d90c320aa17dda4ddb4b8a444f40 /firefly_cli/commands
parent60e15f9ced98c270a48d58ac000738afb78c2d7e (diff)
downloadfirefly-cli-0.3.6.tar.gz
firefly-cli-0.3.6.zip
feat: tx list --flat, account create --if-not-exists, --since/--until doc (v0.3.6)v0.3.6
Three smaller ISSUES.md items, one PATCH (two optional flags + a doc fix; no existing caller or JSON shape changes). - tx list --flat: emit one top-level object per split (journal id repeated), dropping the transactions[] nesting so single-split journals script cleanly. JSON-only; --human already explodes splits into a table. - account create --if-not-exists: resolve the name first; on a clash return the existing account with "existed": true (exit 0) instead of surfacing Firefly's 422, so import scripts are idempotent. Detects via resolver, not by parsing the error string. - SKILL.md documents that --since/--until filter on the transaction date (the value date); Firefly journals have a single date field, no separate book date (verified against firefly-iii TimeCollection setRange). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Diffstat (limited to 'firefly_cli/commands')
-rw-r--r--firefly_cli/commands/account.py13
-rw-r--r--firefly_cli/commands/transaction.py13
2 files changed, 24 insertions, 2 deletions
diff --git a/firefly_cli/commands/account.py b/firefly_cli/commands/account.py
index d78f8fc..81c2e1d 100644
--- a/firefly_cli/commands/account.py
+++ b/firefly_cli/commands/account.py
@@ -23,6 +23,8 @@ def _create_args(p):
p.add_argument("--opening-balance", dest="opening_balance", default=None,
help="initial balance (asset accounts); dated today")
p.add_argument("--currency", default=None, help="currency code, e.g. EUR")
+ p.add_argument("--if-not-exists", dest="if_not_exists", action="store_true",
+ help="if an account with this name exists, return it (existed:true) instead of erroring")
@registry.command("account create", help="create an asset, expense, or revenue account", args=_create_args)
def cmd_create(args, ctx):
@@ -30,6 +32,17 @@ def cmd_create(args, ctx):
raise FireflyError(
f'Unsupported account type "{args.type}". '
f'Use one of: {", ".join(_CREATE_TYPES)}.')
+ if getattr(args, "if_not_exists", False):
+ # Resolve by name to detect existence; avoids parsing Firefly's 422
+ # "name already in use" error string. Missing = ResolutionError -> create.
+ from firefly_cli.errors import ResolutionError
+ try:
+ existing = ctx.resolver.account(args.name)
+ except ResolutionError:
+ existing = None
+ if existing is not None:
+ output.emit({**existing, "existed": True}, human=ctx.human)
+ return 0
body = {"name": args.name, "type": args.type}
if args.type == "asset":
body["account_role"] = "defaultAsset"
diff --git a/firefly_cli/commands/transaction.py b/firefly_cli/commands/transaction.py
index df1ffc4..714d2d4 100644
--- a/firefly_cli/commands/transaction.py
+++ b/firefly_cli/commands/transaction.py
@@ -139,6 +139,8 @@ def _list_args(p):
p.add_argument("--limit", type=int, default=20)
p.add_argument("--all", action="store_true",
help="fetch every page (ignores --limit truncation)")
+ p.add_argument("--flat", action="store_true",
+ help="one flat object per split; drop the transactions[] nesting (JSON only)")
@registry.command("tx list", help="list recent transactions (newest first)", args=_list_args)
def cmd_list(args, ctx):
@@ -162,7 +164,7 @@ def cmd_list(args, ctx):
if page >= (pg.get("total_pages") or 1):
break
page += 1
- output.emit(rows, human=ctx.human)
+ output.emit(_maybe_flat(rows, args, ctx), human=ctx.human)
return 0
resp = ctx.client.request("GET", path, params=params)
@@ -171,9 +173,16 @@ def cmd_list(args, ctx):
if total is not None and count is not None and count < total:
import sys
print(f"showing {count} of {total} (use --all for all)", file=sys.stderr)
- output.emit(output.unwrap(resp), human=ctx.human)
+ output.emit(_maybe_flat(output.unwrap(resp), args, ctx), human=ctx.human)
return 0
+# --flat is a JSON-only convenience; --human already explodes splits into a
+# table, so leave its nested rows alone.
+def _maybe_flat(rows, args, ctx):
+ if getattr(args, "flat", False) and not ctx.human:
+ return output.flatten_tx(rows)
+ return rows
+
def _id_arg(p):
p.add_argument("id")