diff options
| author | Danilo M. <danix@danix.xyz> | 2026-07-03 17:49:20 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-07-03 17:49:20 +0200 |
| commit | e30b3f203059d8d116f77185f0764c391cae07dc (patch) | |
| tree | e7a322c63be7100897f778ed9be680677fa0c69f | |
| parent | e0843da28eaa5828665a700b8a06f7fe1e27d2b8 (diff) | |
| download | firefly-cli-e30b3f203059d8d116f77185f0764c391cae07dc.tar.gz firefly-cli-e30b3f203059d8d116f77185f0764c391cae07dc.zip | |
feat(budget): limit list and limit set
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
| -rw-r--r-- | firefly_cli/commands/budget.py | 28 | ||||
| -rw-r--r-- | tests/unit/test_commands_budget.py | 39 |
2 files changed, 67 insertions, 0 deletions
diff --git a/firefly_cli/commands/budget.py b/firefly_cli/commands/budget.py index 3994958..733d092 100644 --- a/firefly_cli/commands/budget.py +++ b/firefly_cli/commands/budget.py @@ -105,3 +105,31 @@ def cmd_enable(args, ctx): @registry.command("budget disable", help="mark a budget inactive", args=_ref_arg) def cmd_disable(args, ctx): return _set_active(ctx, args.ref, False) + + +@registry.command("budget limit list", help="list a budget's limits (name or id)", args=_ref_arg) +def cmd_limit_list(args, ctx): + b = ctx.resolver.budget(args.ref) + resp = ctx.client.request("GET", f"/api/v1/budgets/{b['id']}/limits") + output.emit(output.unwrap(resp), human=ctx.human) + return 0 + +def _limit_set_args(p): + _ref_arg(p) + p.add_argument("--amount", required=True, help="limit amount (positive)") + p.add_argument("--start", default=None, help="YYYY-MM-DD (default: 1st of this month)") + p.add_argument("--end", default=None, help="YYYY-MM-DD (default: last of this month)") + p.add_argument("--currency", default=None, help="currency code, e.g. EUR") + +@registry.command("budget limit set", help="set (create) a spending limit for a budget over a period", args=_limit_set_args) +def cmd_limit_set(args, ctx): + b = ctx.resolver.budget(args.ref) + first, last = _current_month() + body = {"amount": str(args.amount), + "start": args.start or first, + "end": args.end or last} + if args.currency: + body["currency_code"] = args.currency + resp = ctx.client.request("POST", f"/api/v1/budgets/{b['id']}/limits", body=body) + output.emit(output.unwrap(resp), human=ctx.human) + return 0 diff --git a/tests/unit/test_commands_budget.py b/tests/unit/test_commands_budget.py index 0b1d89c..15e89c4 100644 --- a/tests/unit/test_commands_budget.py +++ b/tests/unit/test_commands_budget.py @@ -119,5 +119,44 @@ class TestBudgetLifecycle(unittest.TestCase): self.assertFalse(client.request.call_args[1]["body"]["active"]) +class TestBudgetLimit(unittest.TestCase): + def _resolver(self): + r = MagicMock() + r.budget.return_value = {"id": "5", "name": "Rent"} + return r + + def test_limit_list(self): + client = MagicMock() + client.request.return_value = {"data": []} + args = SimpleNamespace(ref="Rent") + rc = budget.cmd_limit_list(args, _ctx(client=client, resolver=self._resolver())) + self.assertEqual(rc, 0) + client.request.assert_called_with("GET", "/api/v1/budgets/5/limits") + + def test_limit_set_default_month(self): + client = MagicMock() + client.request.return_value = {"data": {"id": "1", "attributes": {}}} + args = SimpleNamespace(ref="Rent", amount="800", start=None, end=None, + currency=None) + budget.cmd_limit_set(args, _ctx(client=client, resolver=self._resolver())) + method, path = client.request.call_args[0][:2] + body = client.request.call_args[1]["body"] + self.assertEqual((method, path), ("POST", "/api/v1/budgets/5/limits")) + self.assertEqual(body["amount"], "800") + self.assertTrue(body["start"].endswith("-01")) + self.assertEqual(body["start"][:7], body["end"][:7]) + + def test_limit_set_explicit_range_and_currency(self): + client = MagicMock() + client.request.return_value = {"data": {"id": "1", "attributes": {}}} + args = SimpleNamespace(ref="Rent", amount="800", start="2026-02-01", + end="2026-02-28", currency="EUR") + budget.cmd_limit_set(args, _ctx(client=client, resolver=self._resolver())) + body = client.request.call_args[1]["body"] + self.assertEqual(body["start"], "2026-02-01") + self.assertEqual(body["end"], "2026-02-28") + self.assertEqual(body["currency_code"], "EUR") + + if __name__ == "__main__": unittest.main() |
