From 529b9c33206a9e103e0e0575ceb85fe2295d96e7 Mon Sep 17 00:00:00 2001 From: Jonny Date: Sun, 13 Sep 2026 19:56:40 +0200 Subject: [PATCH] feat: add Storniert status column to Bestellungen board Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01V57jSQPqwkGG8BuAXg59X5 --- Claude outputs/main.py | 12 +++++------ Claude outputs/tests/test_orders.py | 33 +++++++++++++++++++++++++---- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/Claude outputs/main.py b/Claude outputs/main.py index 67eaa6b..bd3ebf8 100644 --- a/Claude outputs/main.py +++ b/Claude outputs/main.py @@ -486,7 +486,7 @@ async def _deck_request(method: str, path: str, json_body: dict = None): _deck_cache: dict = {} _DECK_BOARD_TITLE = "Bestellungen" -_DECK_STACK_TITLES = ["Offen", "In Arbeit", "Erledigt"] +_DECK_STACK_TITLES = ["Offen", "In Arbeit", "Erledigt", "Storniert"] async def _ensure_deck_board() -> dict: @@ -830,15 +830,15 @@ ORDER_TOOLS = [ "properties": { "status": { "type": "string", - "description": "Filter by column: 'Offen', 'In Arbeit' or 'Erledigt'. Omit to list all.", - "enum": ["Offen", "In Arbeit", "Erledigt"], + "description": "Filter by column: 'Offen', 'In Arbeit', 'Erledigt' or 'Storniert'. Omit to list all.", + "enum": ["Offen", "In Arbeit", "Erledigt", "Storniert"], }, }, }, }, { "name": "update_order_status", - "description": "Move an order to a different status column (e.g. mark it 'In Arbeit' or 'Erledigt'). Call list_orders first if you don't already know the order's id.", + "description": "Move an order to a different status column (e.g. mark it 'In Arbeit', 'Erledigt' or 'Storniert'). Call list_orders first if you don't already know the order's id.", "input_schema": { "type": "object", "properties": { @@ -846,7 +846,7 @@ ORDER_TOOLS = [ "status": { "type": "string", "description": "New status column", - "enum": ["Offen", "In Arbeit", "Erledigt"], + "enum": ["Offen", "In Arbeit", "Erledigt", "Storniert"], }, }, "required": ["order_id", "status"], @@ -904,7 +904,7 @@ ORDER_ASSISTANT_INSTRUCTIONS = ( "Du hast ausserdem Zugriff auf Bestellungen ueber die Tools " "create_order, list_orders, update_order_status und update_order - sie " "leben als Karten im Nextcloud-Deck-Board 'Bestellungen' mit den " - "Spalten Offen, In Arbeit und Erledigt. update_order_status aendert " + "Spalten Offen, In Arbeit, Erledigt und Storniert. update_order_status aendert " "nur die Spalte/den Status; update_order aendert Kunde, Beschreibung " "und/oder Faelligkeitsdatum, ohne die Spalte zu wechseln. Du kannst " "alle vier Tools direkt aufrufen, sobald du die noetigen Angaben hast " diff --git a/Claude outputs/tests/test_orders.py b/Claude outputs/tests/test_orders.py index 8303354..1c86fd6 100644 --- a/Claude outputs/tests/test_orders.py +++ b/Claude outputs/tests/test_orders.py @@ -107,8 +107,8 @@ async def test_ensure_deck_board_creates_board_and_stacks_when_missing(): board = await main._ensure_deck_board() assert board["board_id"] == 10 - assert titles_created == ["Offen", "In Arbeit", "Erledigt"] - assert set(board["stacks"].keys()) == {"Offen", "In Arbeit", "Erledigt"} + assert titles_created == ["Offen", "In Arbeit", "Erledigt", "Storniert"] + assert set(board["stacks"].keys()) == {"Offen", "In Arbeit", "Erledigt", "Storniert"} @pytest.mark.asyncio @@ -121,20 +121,45 @@ async def test_ensure_deck_board_reuses_existing_board_and_stacks(): {"id": 1, "title": "Offen", "cards": []}, {"id": 2, "title": "In Arbeit", "cards": []}, {"id": 3, "title": "Erledigt", "cards": []}, + {"id": 4, "title": "Storniert", "cards": []}, ] raise AssertionError(f"unexpected call {method} {path}") with patch.object(main, "_deck_request", new=AsyncMock(side_effect=fake_deck_request)) as mock_req: board = await main._ensure_deck_board() - assert board == {"board_id": 10, "stacks": {"Offen": 1, "In Arbeit": 2, "Erledigt": 3}} + assert board == {"board_id": 10, "stacks": {"Offen": 1, "In Arbeit": 2, "Erledigt": 3, "Storniert": 4}} assert mock_req.call_count == 2 +@pytest.mark.asyncio +async def test_ensure_deck_board_creates_missing_stack_on_existing_board(): + """Regression: a board created before 'Storniert' existed only has the + original three stacks - the fourth must be added, not just assumed.""" + async def fake_deck_request(method, path, json_body=None): + if (method, path) == ("GET", "/boards"): + return [{"id": 10, "title": "Bestellungen"}] + if (method, path) == ("GET", "/boards/10/stacks"): + return [ + {"id": 1, "title": "Offen", "cards": []}, + {"id": 2, "title": "In Arbeit", "cards": []}, + {"id": 3, "title": "Erledigt", "cards": []}, + ] + if (method, path) == ("POST", "/boards/10/stacks"): + assert json_body["title"] == "Storniert" + return {"id": 4, "title": "Storniert"} + raise AssertionError(f"unexpected call {method} {path}") + + with patch.object(main, "_deck_request", new=AsyncMock(side_effect=fake_deck_request)): + board = await main._ensure_deck_board() + + assert board["stacks"] == {"Offen": 1, "In Arbeit": 2, "Erledigt": 3, "Storniert": 4} + + @pytest.mark.asyncio async def test_ensure_deck_board_caches_after_first_lookup(): main._deck_cache["board_id"] = 10 - main._deck_cache["stacks"] = {"Offen": 1, "In Arbeit": 2, "Erledigt": 3} + main._deck_cache["stacks"] = {"Offen": 1, "In Arbeit": 2, "Erledigt": 3, "Storniert": 4} with patch.object(main, "_deck_request", new=AsyncMock()) as mock_req: board = await main._ensure_deck_board()