Files
gemfin-shiny/R/buchungen_mod.R
T

136 lines
4.9 KiB
R
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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) {
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))
update_db_trigger <- postingModuleServer("posting_modal", conn, selected_trans_id, modal_trigger)
# * Haupttabelle rendern ----
output$buchungen_table <- renderReactable({
reset_trigger() # Abhängigkeit bei Increment wird neu gerendert
f_reactable(
daten = postings_data(),
coldefs = coldef_entries_tabelle,
selection = "single",
hoehe = "60vh",
defaultSelected = current_main_idx()
)
})
# * Details-Tabelle laden ----
sel_details <- reactive(getReactableState("buchungen_table", "selected"))
observeEvent(sel_details(), ignoreInit = TRUE, {
req(sel_details())
current_main_idx(sel_details())
t_id <- postings_data()[sel_details(), "entry_id"] |> pull()
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)
showNotification(
"Filter zurückgesetzt neue Transaktion ist jetzt sichtbar.",
type = "message",
duration = 3
)
})
# * 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
})
})
}