# module_buchungen.R buchungenUI <- function(id) { ns <- NS(id) tagList( useShinyjs(), h3("Hauptbuchungen"), reactableOutput(ns("buchungen_table")), hr(), h3("Details / Gegenbuchungen"), reactableOutput(ns("details_table")), br(), actionBttn(ns("add_trans"), "Transaktion hinzu", size = "sm", style = "material-flat", color = "warning"), actionBttn(ns("del_trans"), "Transaktion Löschen", size = "sm", style = "material-flat", color = "danger"), actionBttn(ns("add_detail"), "Detail hinzu", size = "sm", style = "material-flat", color = "success"), # Das UI-Element des Moduls (auch wenn es leer ist) postingModuleUI(ns("posting_modal")) ) } buchungenServer <- function(id, conn, aktiver_filter = reactive("alle")) { moduleServer(id, function(input, output, session) { ns <- session$ns # * Reactive Variablen ---- postings_data <- reactiveVal(read_buch_tabelle(conn)) details_data <- reactiveVal(NULL) selected_trans_id <- reactiveVal(NULL) current_main_idx <- reactiveVal(NULL) reset_trigger <- reactiveVal(0) # ← neu: erzwingt Re-render mit Filter-Reset modal_trigger <- reactiveVal(list(post_id = NULL, counter = 0)) highlighted_valuta <- reactiveVal(NULL) update_db_trigger <- postingModuleServer("posting_modal", conn, selected_trans_id, modal_trigger) gefilterte_daten <- reactive({ d <- postings_data() switch(aktiver_filter(), "giro" = d |> filter(grepl("0130", account_name)), "monat" = d |> filter( floor_date(as.Date(valuta), "month") == floor_date(Sys.Date(), "month") ), d ) }) observeEvent(aktiver_filter(), { current_main_idx(NULL) selected_trans_id(NULL) details_data(NULL) }) # * Haupttabelle rendern ---- output$buchungen_table <- renderReactable({ reset_trigger() f_reactable( daten = gefilterte_daten(), # ← geändert coldefs = coldef_entries_tabelle, selection = "single", hoehe = "60vh", defaultSelected = current_main_idx(), highlight_valuta = highlighted_valuta() ) }) # * Details-Tabelle laden ---- sel_details <- reactive(getReactableState("buchungen_table", "selected")) observeEvent(sel_details(), ignoreInit = TRUE, { req(sel_details()) highlighted_valuta(gefilterte_daten()[sel_details(), "valuta"]) current_main_idx(sel_details()) t_id <- gefilterte_daten()[sel_details(), "entry_id"] |> pull() # ← geändert selected_trans_id(t_id) details_data(read_buch_tabelle(conn, trans_id = t_id)) }) output$details_table <- renderReactable({ req(details_data()) f_reactable( details_data(), coldefs = coldef_entries_tabelle, selection = "single", hoehe = NULL ) }) # * Detail hinzufügen ---- observeEvent(input$add_detail, { req(selected_trans_id()) modal_trigger(list(post_id = NULL, counter = modal_trigger()$counter + 1)) }) # * Detail editieren (Klick auf Detail-Tabelle) ---- sel_detail <- reactive(getReactableState("details_table", "selected")) observeEvent(sel_detail(), ignoreInit = TRUE, { p_id <- details_data()$id[sel_detail()] modal_trigger(list(post_id = p_id, counter = modal_trigger()$counter + 1)) }) # * DB-Update durch Modal ---- observeEvent(update_db_trigger(), ignoreInit = TRUE, { req(selected_trans_id()) details_data(read_buch_tabelle(conn, trans_id = selected_trans_id())) postings_data(read_buch_tabelle(conn)) # Kein Re-render nötig – Filter/Sortierung bleibt erhalten updateReactable("buchungen_table", data = postings_data()) }) # * Neue Transaktion ---- observeEvent(input$add_trans, { new_t_id <- max_id(conn, "entries") + 1 dbxInsert(conn, "entries", data.frame(id = new_t_id)) p_id1 <- max_id(conn, "postings") + 1 p_id2 <- p_id1 + 1 dbxInsert(conn, "postings", data.frame( id = c(p_id1, p_id2), entry_id = c(new_t_id, new_t_id), amount = c(0, 0), account_id = c(0, 0), valuta = c(Sys.Date(), Sys.Date()) )) ## ** Daten aktualisieren ---- postings_data(read_buch_tabelle(conn)) selected_trans_id(new_t_id) details_data(read_buch_tabelle(conn, trans_id = new_t_id)) ## ** Index in neuen Daten suchen ---- neue_zeile <- which(postings_data()$id == p_id1) current_main_idx(neue_zeile) ## ** Re-render erzwingen → Filter wird zurückgesetzt, neue Zeile selektiert ---- reset_trigger(isolate(reset_trigger()) + 1) scroll_to_row(ns("buchungen_table"), neue_zeile) }) # * Transaktion löschen ---- observeEvent(input$del_trans, ignoreInit = TRUE, { req(selected_trans_id()) dbxDelete(conn, "postings", where = data.frame(entry_id = selected_trans_id())) dbxDelete(conn, "entries", where = data.frame(id = selected_trans_id())) postings_data(read_buch_tabelle(conn)) current_main_idx(NULL) selected_trans_id(NULL) details_data(NULL) updateReactable("buchungen_table", data = postings_data()) # details_table re-rendert automatisch weil details_data(NULL) → req() schlägt fehl }) }) }