From 1f0b9eb2147c27be5e9ff998970a41225208b482 Mon Sep 17 00:00:00 2001 From: Christian Oswald Date: Tue, 28 Apr 2026 17:37:29 +0200 Subject: [PATCH] =?UTF-8?q?Buchungs-Seite=20aufger=C3=A4umt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- R/postings/buchungen_mod.R | 200 +++++++++++++++++++++---------------- 1 file changed, 112 insertions(+), 88 deletions(-) diff --git a/R/postings/buchungen_mod.R b/R/postings/buchungen_mod.R index 4b406e9..95cbd3c 100755 --- a/R/postings/buchungen_mod.R +++ b/R/postings/buchungen_mod.R @@ -5,19 +5,39 @@ buchungenUI <- function(id) { tagList( useShinyjs(), h3("Hauptbuchungen"), - reactableOutput(ns("buchungen_table")), + fluidRow( + column(8, reactableOutput(ns("buchungen_table"))), + + column(4, + selectizeInput(ns("contact"), label = "Kontakt", choices = get_contact_choices(conn), + selected = NA, width = "100%"), + textAreaInput(ns("note"), label = "Verwendungszweck", value = NA, width = "100%"), + h3("Dateien"), + uiOutput(ns("attachments_ui")), + fileInput(ns("anhang"), NULL, multiple = TRUE, + accept = c(".pdf", ".jpg", ".png", ".xlsx", ".docx"), + width = "100%"), + div( + style = "display: flex; flex-direction: column; gap: 10px; align-items: flex-start;", + + actionBttn(ns("add_detail"), "Detail hinzu", + size = "sm", style = "material-flat", color = "default", block = TRUE), + actionBttn(ns("del_detail"), "Detail Löschen", + size = "sm", style = "material-flat", color = "danger", block = TRUE), + actionBttn(ns("add_trans"), "Transaktion hinzu", + size = "sm", style = "material-flat", color = "default", block = TRUE), + + actionBttn(ns("del_trans"), "Transaktion Löschen", + size = "sm", style = "material-flat", color = "danger", block = TRUE), + actionBttn(ns("save_trans"), "Transaktion speichern", + size = "sm", style = "material-flat", color = "success", block = TRUE, icon = icon("floppy-disk")), + ) + ) + ), + hr(), h3("Details / Gegenbuchungen"), uiOutput(ns("details_table")) , - h3("Anhänge"), - uiOutput(ns("attachments_ui")), - fileInput(ns("anhang"), NULL, multiple = TRUE, - accept = c(".pdf", ".jpg", ".png", ".xlsx", ".docx"), - width = "100%"), - 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"), @@ -34,9 +54,9 @@ buchungenServer <- function(id, conn, r_global) { selected_trans_id <- reactiveVal(NULL) reset_trigger <- reactiveVal(NULL) current_main_idx <- reactiveVal(NULL) - - + + # ── Filter ---- aktiver_filter <- reactive(r_global$buchungen_filter) @@ -70,98 +90,102 @@ buchungenServer <- function(id, conn, r_global) { sel <- reactive(getReactableState("buchungen_table", "selected")) observeEvent(sel(), ignoreInit = T, { - - output$details_table <- renderUI({ - sel_details(read_buch_entries(conn, gefilterte_daten()$entry_id[sel()])) - rows <- lapply(1:nrow(sel_details()), function(ind) { + req(sel()) + # Daten laden + df <- read_buch_entries(conn, gefilterte_daten()$entry_id[sel()]) + sel_details(df) + + rows <- lapply(1:nrow(df), function(ind) { fluidRow(class = "details-row", - column(1, numericInput(ns("id"), label = NULL, width = "100%", value = sel_details()$id[ind])), - column(2, dateInput( ns("valuta"), label = NULL, width = "100%",value = sel_details()$valuta[ind])), - column(3, - selectizeInput(ns("account"), label = NULL, - choices = get_account_choices(conn), - selected = sel_details()$account_id[ind], - width = "100%") - ), - column(3, - selectizeInput(ns("projekt"), label = NULL, - choices = get_project_choices(conn), - selected = sel_details()$projekt_id[ind], - width = "100%") - ), - column(2, numericInput(ns("betrag"), label = NULL, width = "100%",value = sel_details()$amount[ind])) + # HIER: Index an die ID hängen! + column(1, numericInput(ns(paste0("id_", ind)), label = NULL, width = "100%", value = df$id[ind])), + column(2, dateInput(ns(paste0("valuta_", ind)), label = NULL, width = "100%", value = df$valuta[ind])), + column(3, selectizeInput(ns(paste0("account_", ind)), label = NULL, + choices = get_account_choices(conn), + selected = df$account_id[ind], width = "100%")), + column(3, selectizeInput(ns(paste0("projekt_", ind)), label = NULL, + choices = get_project_choices(conn), + selected = df$projekt_id[ind], width = "100%")), + column(2, numericInput(ns(paste0("betrag_", ind)), label = NULL, width = "100%", value = df$amount[ind])) ) }) - - tagList( - rows) + tagList(rows) }) }) - observe({ + observeEvent(input$save_trans, { req(sel_details()) + n_rows <- nrow(sel_details()) - lapply(1:nrow(sel_details()), function(ind) { - - # Alle Input-IDs dieser Zeile - ids <- c(paste0("id_", ind), - paste0("valuta_", ind), - paste0("betrag_", ind), - paste0("account_", ind), - paste0("projekt_", ind)) - - # Observer nur für diese Zeile - observeEvent( - lapply(ids, function(nm) input[[nm]]), # Trigger: irgendein Input dieser Zeile - ignoreInit = TRUE, { - - record <- list( - id = input[[paste0("id_", ind)]], - valuta = input[[paste0("valuta_", ind)]], - betrag = input[[paste0("betrag_", ind)]], - account = input[[paste0("account_", ind)]], - projekt = input[[paste0("projekt_", ind)]] - ) - - print( record) - } + # Alle Zeilen in einer Liste sammeln + all_records <- lapply(1:n_rows, function(ind) { + data.frame( + id = as.integer(input[[paste0("id_", ind)]]), + valuta = as.Date(input[[paste0("valuta_", ind)]]), + amount = as.numeric(input[[paste0("betrag_", ind)]]), + account_id = input[[paste0("account_", ind)]], + projekt_id = input[[paste0("projekt_", ind)]], + stringsAsFactors = FALSE ) }) + + # Zu einem großen Dataframe zusammenfügen + final_df <- do.call(rbind, all_records) + browser() + + # Datenbank-Update-Logik + tryCatch({ + # Beispiel: Für jede Zeile ein Update ausführen + for(i in 1:nrow(final_df)) { + update_buchung_detail(conn, final_df[i, ]) # Deine DB-Funktion + } + + showNotification("Daten erfolgreich gespeichert", type = "message") + + # Optional: Haupttabelle aktualisieren, falls sich Summen geändert haben + # trigger_refresh(trigger_refresh() + 1) + + }, error = function(e) { + showNotification(paste("Fehler beim Speichern:", e$message), type = "error") + }) }) # ── Anhänge ---- - # output$attachments_ui <- renderUI({ - # req(selected_trans_id()) - # att <- dbxSelect(conn, paste0( - # "SELECT * FROM attachments WHERE entry_id = ", selected_trans_id() - # )) - # if (nrow(att) == 0) return(p("Keine Anhänge vorhanden.")) - # - # tagList(lapply(seq_len(nrow(att)), function(i) { - # ext <- tools::file_ext(att$original_name[i]) - # filename <- paste0("attachments/", att$id[i], ".", ext) - # - # observeEvent(input[[paste0("open_att_", att$id[i])]], { - # showModal(modalDialog( - # tags$iframe(src = filename, width = "100%", height = "600px", - # style = "border:none;"), - # title = att$original_name[i], - # size = "l", - # easyClose = TRUE, - # footer = modalButton("Schließen") - # )) - # }, ignoreInit = TRUE, once = FALSE) - # - # div( - # actionLink(ns(paste0("open_att_", att$id[i])), att$original_name[i]), - # actionLink(ns(paste0("del_att_", att$id[i])), "✕", - # style = "color:red; margin-left:8px;") - # ) - # })) - # }) + output$attachments_ui <- renderUI({ + req(sel_details()) + entry_id <- + dbxSelect(conn, paste0("SELECT entry_id FROM postings WHERE id=", + sel_details()$id[1])) %>% pull + att <- dbxSelect(conn, paste0( + "SELECT * FROM attachments WHERE entry_id = ", entry_id + )) + if (nrow(att) == 0) return(p("Keine Anhänge vorhanden.")) + + tagList(lapply(seq_len(nrow(att)), function(i) { + ext <- tools::file_ext(att$original_name[i]) + filename <- paste0("attachments/", att$id[i], ".", ext) + + observeEvent(input[[paste0("open_att_", att$id[i])]], { + showModal(modalDialog( + tags$iframe(src = filename, width = "100%", height = "600px", + style = "border:none;"), + title = att$original_name[i], + size = "l", + easyClose = TRUE, + footer = modalButton("Schließen") + )) + }, ignoreInit = TRUE, once = FALSE) + + div( + actionLink(ns(paste0("open_att_", att$id[i])), att$original_name[i]), + actionLink(ns(paste0("del_att_", att$id[i])), "✕", + style = "color:red; margin-left:8px;") + ) + })) + }) }) } \ No newline at end of file