# Copyright (c) 2026 Patrick Motsch # All rights reserved. """Redmine node definitions - map to MethodRedmine actions.""" from modules.shared.i18nRegistry import t # Typed FeatureInstance binding (replaces legacy `string, hidden`). # - type FeatureInstanceRef[redmine] is filtered by the DataPicker. # - frontendType "featureInstance" is rendered by FeatureInstancePicker which # loads /options/feature.instance?featureCode=redmine for the current mandate. _REDMINE_INSTANCE_PARAM = { "name": "featureInstanceId", "type": "FeatureInstanceRef[redmine]", "required": True, "frontendType": "featureInstance", "frontendOptions": {"featureCode": "redmine"}, "description": t("Redmine-Mandant"), } REDMINE_NODES = [ { "id": "redmine.readTicket", "category": "redmine", "label": t("Ticket lesen"), "description": t("Einzelnes Redmine-Ticket aus dem Mirror laden."), "parameters": [ dict(_REDMINE_INSTANCE_PARAM), {"name": "ticketId", "type": "int", "required": True, "frontendType": "number", "description": t("Redmine-Ticket-ID")}, ], "inputs": 1, "outputs": 1, "inputPorts": {0: {"accepts": ["Transit"]}}, "outputPorts": {0: {"schema": "ActionResult"}}, "meta": {"icon": "mdi-ticket-outline", "color": "#4A6FA5", "usesAi": False}, "_method": "redmine", "_action": "readTicket", }, { "id": "redmine.listTickets", "category": "redmine", "label": t("Tickets auflisten"), "description": t("Tickets aus dem lokalen Mirror mit Filtern (Tracker, Status, Zeitraum, Zuweisung)."), "parameters": [ dict(_REDMINE_INSTANCE_PARAM), {"name": "trackerIds", "type": "str", "required": False, "frontendType": "text", "description": t("Tracker-IDs (Komma-separiert)"), "default": ""}, {"name": "status", "type": "str", "required": False, "frontendType": "text", "description": t("Status-Filter: open | closed | *"), "default": "*"}, {"name": "dateFrom", "type": "str", "required": False, "frontendType": "date", "description": t("Zeitraum ab (ISO-Datum)"), "default": ""}, {"name": "dateTo", "type": "str", "required": False, "frontendType": "date", "description": t("Zeitraum bis (ISO-Datum)"), "default": ""}, {"name": "assignedToId", "type": "int", "required": False, "frontendType": "number", "description": t("Nur Tickets dieses Benutzers (ID)")}, {"name": "limit", "type": "int", "required": False, "frontendType": "number", "description": t("Max. Anzahl Tickets (1-500)"), "default": 100}, ], "inputs": 1, "outputs": 1, "inputPorts": {0: {"accepts": ["Transit"]}}, "outputPorts": {0: {"schema": "ActionResult"}}, "meta": {"icon": "mdi-format-list-bulleted", "color": "#4A6FA5", "usesAi": False}, "_method": "redmine", "_action": "listTickets", }, { "id": "redmine.createTicket", "category": "redmine", "label": t("Ticket erstellen"), "description": t("Neues Ticket in Redmine anlegen. Mirror wird sofort aktualisiert."), "parameters": [ dict(_REDMINE_INSTANCE_PARAM), {"name": "subject", "type": "str", "required": True, "frontendType": "text", "description": t("Ticket-Titel")}, {"name": "trackerId", "type": "int", "required": True, "frontendType": "number", "description": t("Tracker-ID (Userstory, Feature, Task, ...)")}, {"name": "description", "type": "str", "required": False, "frontendType": "textarea", "description": t("Ticket-Beschreibung"), "default": ""}, {"name": "statusId", "type": "int", "required": False, "frontendType": "number", "description": t("Status-ID (optional)")}, {"name": "priorityId", "type": "int", "required": False, "frontendType": "number", "description": t("Prioritaet-ID (optional)")}, {"name": "assignedToId", "type": "int", "required": False, "frontendType": "number", "description": t("Zugewiesene Benutzer-ID (optional)")}, {"name": "parentIssueId", "type": "int", "required": False, "frontendType": "number", "description": t("Uebergeordnetes Ticket (optional)")}, {"name": "customFields", "type": "str", "required": False, "frontendType": "textarea", "description": t("Custom Fields als JSON {id: value}"), "default": ""}, ], "inputs": 1, "outputs": 1, "inputPorts": {0: {"accepts": ["Transit"]}}, "outputPorts": {0: {"schema": "ActionResult"}}, "meta": {"icon": "mdi-ticket-plus-outline", "color": "#4A6FA5", "usesAi": False}, "_method": "redmine", "_action": "createTicket", }, { "id": "redmine.updateTicket", "category": "redmine", "label": t("Ticket bearbeiten"), "description": t("Felder eines Redmine-Tickets aktualisieren. Nur gesetzte Felder werden uebertragen."), "parameters": [ dict(_REDMINE_INSTANCE_PARAM), {"name": "ticketId", "type": "int", "required": True, "frontendType": "number", "description": t("Ticket-ID")}, {"name": "subject", "type": "str", "required": False, "frontendType": "text", "description": t("Neuer Titel")}, {"name": "description", "type": "str", "required": False, "frontendType": "textarea", "description": t("Neue Beschreibung")}, {"name": "trackerId", "type": "int", "required": False, "frontendType": "number", "description": t("Neuer Tracker")}, {"name": "statusId", "type": "int", "required": False, "frontendType": "number", "description": t("Neuer Status")}, {"name": "priorityId", "type": "int", "required": False, "frontendType": "number", "description": t("Neue Prioritaet")}, {"name": "assignedToId", "type": "int", "required": False, "frontendType": "number", "description": t("Neue Zuweisung")}, {"name": "parentIssueId", "type": "int", "required": False, "frontendType": "number", "description": t("Neues Parent-Ticket")}, {"name": "notes", "type": "str", "required": False, "frontendType": "textarea", "description": t("Kommentar (Journal-Eintrag)"), "default": ""}, {"name": "customFields", "type": "str", "required": False, "frontendType": "textarea", "description": t("Custom Fields als JSON {id: value}"), "default": ""}, ], "inputs": 1, "outputs": 1, "inputPorts": {0: {"accepts": ["Transit"]}}, "outputPorts": {0: {"schema": "ActionResult"}}, "meta": {"icon": "mdi-ticket-confirmation-outline", "color": "#4A6FA5", "usesAi": False}, "_method": "redmine", "_action": "updateTicket", }, { "id": "redmine.getStats", "category": "redmine", "label": t("Statistik laden"), "description": t("Aggregierte Kennzahlen (KPIs, Durchsatz, Status-Verteilung, Backlog) aus dem Mirror."), "parameters": [ dict(_REDMINE_INSTANCE_PARAM), {"name": "dateFrom", "type": "str", "required": False, "frontendType": "date", "description": t("Zeitraum ab")}, {"name": "dateTo", "type": "str", "required": False, "frontendType": "date", "description": t("Zeitraum bis")}, {"name": "bucket", "type": "str", "required": False, "frontendType": "text", "description": t("Bucket: day | week | month"), "default": "week"}, {"name": "trackerIds", "type": "str", "required": False, "frontendType": "text", "description": t("Tracker-IDs (Komma-separiert)"), "default": ""}, ], "inputs": 1, "outputs": 1, "inputPorts": {0: {"accepts": ["Transit"]}}, "outputPorts": {0: {"schema": "ActionResult"}}, "meta": {"icon": "mdi-chart-bar", "color": "#4A6FA5", "usesAi": False}, "_method": "redmine", "_action": "getStats", }, { "id": "redmine.runSync", "category": "redmine", "label": t("Mirror synchronisieren"), "description": t("Tickets und Beziehungen aus Redmine in den lokalen Mirror uebernehmen."), "parameters": [ dict(_REDMINE_INSTANCE_PARAM), {"name": "force", "type": "bool", "required": False, "frontendType": "checkbox", "description": t("Vollsync erzwingen (ignoriert lastSyncAt)"), "default": False}, ], "inputs": 1, "outputs": 1, "inputPorts": {0: {"accepts": ["Transit"]}}, "outputPorts": {0: {"schema": "ActionResult"}}, "meta": {"icon": "mdi-database-sync", "color": "#4A6FA5", "usesAi": False}, "_method": "redmine", "_action": "runSync", }, ]