## ## FileMaker -> SQLite (canonical english schema) ## Keeps Hibiscus payload fields unchanged ## library(tidyverse) library(dbx) library(RJDBC) library(DBI) library(RSQLite) source("~/R/rfunc/fehler_add.R") ok <- TRUE error_f <- NULL # Settings ---- fm_host <- "localhost" fm_port <- 2399 fm_db <- "gemfin04" fm_user <- "admin" fm_pass <- "" fm_driver_class <- "com.filemaker.jdbc.Driver" fm_driver_jar <- "~/R/fmjdbc.jar" sqlite_path <- "db/development.sqlite" # Helpers ---- exec_sql <- function(sql) dbExecute(con_s, sql) f_anzahl <- function(tabelle){ dbxSelect(con_s, paste0("SELECt count(id) FROM ", tabelle)) %>% pull } # Connections ---- if (ok) { drv <- JDBC(fm_driver_class, fm_driver_jar) con_f <- dbConnect( drv, paste0("jdbc:filemaker://", fm_host, ":", fm_port, "/", fm_db), fm_user, fm_pass ) ok <- !con_f@jc$isClosed() error_f <- fehler_add("FileMaker connection established", ok, error_f) } if (ok) { if (file.exists(sqlite_path)) file.remove(sqlite_path) con_s <- dbConnect(SQLite(), sqlite_path, extended_types = TRUE) dbExecute(con_s, "PRAGMA foreign_keys = ON;") error_f <- fehler_add("SQLite connection established", TRUE, error_f) } ## * Transfer Accounts ---- if (ok) { exec_sql("DROP TABLE IF EXISTS konten;") exec_sql("CREATE TABLE konten ( id INTEGER PRIMARY KEY, kontoname TEXT NOT NULL, bank_name TEXT, hibiscus_konto_id INTEGER, budget_id INTEGER, wiso_kategorie TEXT, bank_konto_nr TEXT, ist_spende INTEGER, notiz TEXT, created_at TEXT, updated_at TEXT );") konten <- dbxSelect(con_f, " SELECT id, konto, budget_id, bankname, updated_at, created_at, konto_hibiscus, konto_wiso, kontonummer_bank, notizen, ist_spende FROM Konten ") %>% transmute( id = as.integer(id), kontoname = konto, bank_name = bankname, hibiscus_konto_id = as.integer(konto_hibiscus), budget_id = as.integer(budget_id), wiso_kategorie = konto_wiso, bank_konto_nr = kontonummer_bank, ist_spende = as.integer(ist_spende), notiz = notizen, created_at = created_at, updated_at = updated_at ) dbWriteTable(con_s, "konten", konten, append = TRUE) ok <- dbExistsTable(con_s, "konten") ok <- f_anzahl("konten") == nrow(konten) & ok error_f <- fehler_add("Konten übertragen", TRUE, error_f) } ## * Transfer Projekte ---- if (ok) { exec_sql("DROP TABLE IF EXISTS projekte;") exec_sql("CREATE TABLE projekte ( id INTEGER PRIMARY KEY, projektname TEXT NOT NULL, jahr INTEGER, projekt_wiso TEXT, bereich TEXT, notiz TEXT, created_at TEXT, updated_at TEXT );") projekte <- dbxSelect(con_f, " SELECT id, projektname, bereich, notiz, jahr, created_at, updated_at from Projekte") %>% transmute( id = as.integer(id), projektname = projektname, bereich = bereich, notiz = notiz, jahr = as.integer(jahr), created_at = created_at, updated_at = updated_at ) dbWriteTable(con_s, "projekte", projekte, append = TRUE) ok <- dbExistsTable(con_s, "projekte") ok <- f_anzahl("projekte") == nrow(projekte) & ok error_f <- fehler_add("Projekte übertragen", TRUE, error_f) } ## * Transfer Budgets ---- if (ok) { exec_sql("DROP TABLE IF EXISTS budgets;") exec_sql("CREATE TABLE budgets ( id INTEGER PRIMARY KEY, reihe INTEGER, bereich TEXT, bezeichnung TEXT, richtung TEXT, created_at TEXT, updated_at TEXT );") budgets <- dbxSelect(con_f, " SELECT id, richtung, bereich, bezeichnung, reihe FROM Budgets ") %>% transmute( id = as.integer(id), reihe = as.integer(reihe), bereich = bereich, bezeichnung = bezeichnung, richtung = richtung, created_at = format(Sys.time(), ""), updated_at = format(Sys.time(), "") ) dbWriteTable(con_s, "budgets", budgets, append = TRUE) ok <- dbExistsTable(con_s, "budgets") ok <- f_anzahl("budgets") == nrow(budgets) & ok error_f <- fehler_add("Budgets übertragen", TRUE, error_f) } ### ** Transfer Budget-Werte if(ok){ exec_sql("DROP TABLE IF EXISTS budget_werte;") exec_sql("CREATE TABLE budget_werte ( id INTEGER PRIMARY KEY, budget_id INTEGER, jahr INTEGER, betrag DECIMAL, budget_listen_nr INTEGER, created_at TEXT, updated_at TEXT );") budget_werte <- dbReadTable(con_f, "Budgets_werte") %>% transmute( id = as.integer(id), budget_id = as.integer(budget_id), jahr = as.integer(jahr), betrag = betrag, budget_listen_nr = as.integer(budget_listen_nr), created_at = format(Sys.time(), ""), updated_at = format(Sys.time(), "") ) dbWriteTable(con_s, "budget_werte", budget_werte, append = TRUE) ok <- dbExistsTable(con_s, "budget_werte") ok <- f_anzahl("budget_werte") == nrow(budget_werte) & ok error_f <- fehler_add("budget_werte übertragen", TRUE, error_f) } ## ------------------------------------------------------- 2026-06-19 08:52 ## * Transfer Adressen ---- if (ok) { exec_sql("DROP TABLE IF EXISTS adressen;") exec_sql("CREATE TABLE adressen ( id INTEGER PRIMARY KEY, vorname TEXT, nachname TEXT, strasse TEXT, plz TEXT, ort TEXT, land TEXT, telefon TEXT, mobil TEXT, email TEXT, anrede TEXT, geschlecht TEXT, ist_mitglied INTEGER, ist_firma INTEGER, quittung_anrede TEXT, quittung_name TEXT, display_name TEXT, partner_id INTEGER, notiz TEXT, created_at TEXT, updated_at TEXT );") adressen <- dbxSelect(con_f, " SELECT id, vorname, nachname, strasse, plz, ort, Land, telefon, mobil, email, anrede, geschlecht, mitglied, notiz, quittung_anrede, quittung_name, bezeichnung, partner_id, b_paar, b_firma FROM Adressen ") %>% as_tibble() %>% transmute( id = as.integer(id), vorname = vorname, nachname = nachname, strasse = strasse, plz = plz, ort = ort, land = Land, telefon = telefon, mobil = mobil, email = email, anrede = anrede, geschlecht = geschlecht, ist_mitglied = as.integer(mitglied), ist_firma = as.integer(b_firma), quittung_anrede = quittung_anrede, quittung_name = quittung_name, display_name = bezeichnung, partner_id = as.integer(partner_id), notiz = notiz, created_at = format(Sys.time(), ""), updated_at = format(Sys.time(), "") ) dbWriteTable(con_s, "adressen", adressen, append = TRUE) ok <- dbExistsTable(con_s, "adressen") ok <- f_anzahl("adressen") == nrow(adressen) & ok error_f <- fehler_add("adressen übertragen", TRUE, error_f) } ## * Transfer Transaktionen ---- if (ok) { exec_sql("DROP TABLE IF EXISTS transaktionen;") exec_sql("CREATE TABLE transaktionen ( id INTEGER PRIMARY KEY, adress_id INTEGER, vwz TEXT, notiz TEXT, bank_kontakt TEXT, created_at TEXT, updated_at TEXT );") transaktionen <- dbxSelect(con_f, " SELECT id, adress_id, verwendungszweck, notiz, kontakt, created_at, updated_at FROM trans ") %>% as_tibble() %>% transmute( id = as.integer(id), adress_id = as.integer(adress_id), vwz = verwendungszweck, notiz = notiz, bank_kontakt = kontakt, created_at = created_at, updated_at = updated_at ) dbWriteTable(con_s, "transaktionen", transaktionen, append = TRUE) ok <- dbExistsTable(con_s, "transaktionen") ok <- f_anzahl("transaktionen") == nrow(transaktionen) & ok error_f <- fehler_add("transaktionen übertragen", TRUE, error_f) } ## * Transfer Postings ---- if (ok) { exec_sql("DROP TABLE IF EXISTS buchungen;") exec_sql("CREATE TABLE buchungen ( id INTEGER PRIMARY KEY, trans_id INTEGER NOT NULL, konto_id INTEGER NOT NULL, valuta TEXT NOT NULL, buchungsdatum TEXT, betrag DECIMAL, rechnungsnummer TEXT, notiz TEXT, status TEXT, ist_verzicht INTEGER, umsatz_id INTEGER, wiso_id INTEGER, quittung_id INTEGER, projekt_id INTEGER, betrag_muenzen DECIMAL, created_at TEXT, updated_at TEXT );") buchungen <- dbxSelect(con_f, " SELECT id, trans_id, konto_id, wertstellung, buchungsdatum, betrag, rechnungsnummer, notiz, status, b_verzicht, umsatz_id, wiso_id, quittung_id, projekt_id, betrag_muenzen, created_at, updated_at FROM buchungen ") %>% as_tibble() buchungen <- buchungen %>% transmute( id = as.integer(id), trans_id = as.integer(trans_id), konto_id = as.integer(konto_id), valuta = as.character(wertstellung), buchungsdatum = as.character(buchungsdatum), betrag = betrag, rechnungsnummer = rechnungsnummer, notiz = notiz, status = status, ist_verzicht = as.integer(b_verzicht), umsatz_id = as.integer(umsatz_id), wiso_id = as.integer(wiso_id), quittung_id = as.integer(quittung_id), projekt_id = as.integer(projekt_id), betrag_muenzen = betrag_muenzen, created_at = created_at, updated_at = updated_at ) dbWriteTable(con_s, "buchungen", buchungen, append = TRUE) ok <- dbExistsTable(con_s, "buchungen") ok <- f_anzahl("buchungen") == nrow(buchungen) & ok error_f <- fehler_add("buchungen übertragen", TRUE, error_f) } ## * Transfer Attachments ---- if(ok){ exec_sql("DROP TABLE IF EXISTS attachments;") exec_sql("CREATE TABLE attachments ( id INTEGER PRIMARY KEY AUTOINCREMENT, trans_id INTEGER, quittung_id INTEGER, adress_id INTEGER, wiso_id INTEGER, btisch_id TEXT, original_name TEXT, kategorie TEXT, ext TEXT NOT NULL, notiz TEXT, created_at TEXT DEFAULT (datetime('now')), updated_at TEXT DEFAULT (datetime('now')) );") att <- dbxSelect(con_f, "SELECT id, trans_id, quittung_id, wiso_id, btisch_id, adress_id, ft_dateiname, beschreibung, created_at, updated_at, ft_extension FROM Dokumente") %>% transmute( id = as.integer(id), trans_id = as.integer(trans_id), quittung_id = as.integer(quittung_id), wiso_id = as.integer(wiso_id), btisch_id = as.integer(btisch_id), adress_id = as.integer(adress_id), created_at = as.character(created_at), updated_at = as.character(updated_at), original_name = ft_dateiname, ext = paste0(id,".", ft_extension) ) dbWriteTable(con_s, "attachments", att, append = T) ok <- dbExistsTable(con_s, "attachments") ok <- f_anzahl("attachments") == nrow(att) & ok error_f <- fehler_add("attachments übertragen", TRUE, error_f) } ## ------------------------------------------------------- 2026-03-19 16:38 ### ** Dateien übertragen ---- if(ok){ pfad <- "~/Insync/Projekte/Gemeindefinanzen/gemfin-fm/gemfin04/Dokumente/datei/" zielpfad <- "~/Documents/workspace/gemfin-shiny/www/documents/" vorhanden <- list.files(pfad, pattern = "pdf") eintraege <- att$original_name anz <- length(eintraege) for(ind in 1:length(eintraege)){ if( exists(paste0(zielpfad, eintraege[ind])) ){ cat(ind, " von ", anz, "\n") file.copy( from = paste0(pfad, vorhanden[ind]), to = "~/Documents/workspace/gemfin-shiny/www/documents/") } } neue_dateien <- list.files("~/Documents/workspace/gemfin-shiny/www/documents/") length(neue_dateien) eintraege <- att$original_name length(eintraege) nv <- att[-which(eintraege %in% neue_dateien),] error_f <- fehler_add("Alle Dateien übertragen", nrow(nv) == 0, error_f) } ## ------------------------------------------------------- 2026-04-28 18:40 ## * Transfer Bankverbindungen ---- if (ok) { exec_sql("DROP TABLE IF EXISTS bvb;") exec_sql("CREATE TABLE bvb ( id INTEGER PRIMARY KEY, adress_id INTEGER, remote_name TEXT, iban TEXT, bic TEXT, kreditinstitut TEXT, created_at TEXT, updated_at TEXT );") bvb <- dbxSelect(con_f, " SELECT id, adress_id, kontakt, iban, bic, kreditinstitut, created_at, updated_at FROM Bankverbindungen ") %>% transmute( id = as.integer(id), adress_id = as.integer(adress_id), remote_name = kontakt, iban = iban, bic = bic, kreditinstitut = kreditinstitut, created_at = as.character(created_at), updated_at = as.character(updated_at) ) dbWriteTable(con_s, "bvb", bvb, append = TRUE) ok <- dbExistsTable(con_s, "bvb") ok <- f_anzahl("bvb") == nrow(bvb) & ok error_f <- fehler_add("bvb übertragen", TRUE, error_f) } ## * Transfer Hibiscus ---- if(ok){ exec_sql("DROP TABLE IF EXISTS umsatz;") exec_sql("CREATE TABLE umsatz ( id INTEGER PRIMARY KEY, konto_id INTEGER, empfaenger_konto TEXT, empfaenger_blz TEXT, empfaenger_name TEXT, betrag REAL, zweck TEXT, zweck2 TEXT, zweck3 TEXT, datum TEXT, valuta TEXT, saldo REAL, customerref TEXT, kommentar TEXT, endtoendid TEXT );") umsatz <- dbReadTable(con_f, "Umsatz") %>% transmute( id = as.integer(id), konto_id = as.integer(konto_id), empfaenger_konto = empfaenger_konto, empfaenger_blz = empfaenger_blz, empfaenger_name = empfaenger_name, betrag = betrag, zweck = zweck, zweck2 = zweck2, zweck3 = zweck2, datum = datum, valuta = valuta, saldo = saldo, kommentar = kommentar, endtoendid = endtoendid ) dbWriteTable(con_s, "umsatz", umsatz, append = TRUE) ok <- sum(duplicated(umsatz$id)) == 0 ok <- dbExistsTable(con_s, "umsatz") & ok ok <- f_anzahl("umsatz") == nrow(umsatz) & ok error_f <- fehler_add("Tabelle Umsatz existiert", ok, error_f) } ## ------------------------------------------------------- 2026-06-19 08:34 # Close ---- dbListTables(con_s) dbDisconnect(con_s) dbDisconnect(con_f) print("Migration finished.")