570 lines
18 KiB
AutoHotkey
570 lines
18 KiB
AutoHotkey
/*
|
|
|
|
Dieses Script vereinfacht das Buchen mit der Sherpa, indem FiBu-Konten
|
|
schnell gesucht und ausgewählt werden können. Das Suchfenster wird dafür
|
|
mit der Tastenkombination STRG+ALT+k (K wie Konto) geöffnet.
|
|
|
|
Es basiert auf AutoHotKey v2 und ist deshalb nur für Windows verfügbar.
|
|
|
|
Installation:
|
|
1. AutoHotKey v2 herunterladen & installieren: https://www.autohotkey.com/
|
|
2. Dieses Script (FiBu-Konto.ahk) z. B. in den AutoHotKey-Ordner ablegen
|
|
(Dokumente -> AutoHotKey)
|
|
3. FiBu-Konto.ahk doppelklicken (ein neues System-Icon öffnet sich)
|
|
|
|
Einrichtung (Kontenplan aus Summen- und Saldenliste importieren):
|
|
- In den Sherpa-Einstellungen sicherstellen, dass der Export mit UTF-8-
|
|
Zeichensatz geschieht (Allgemein -> Finanzbuchhaltung -> Exportoptionen).
|
|
Diese Einstellung muss für jeden Kontext/Gliederung gesetzt werden.
|
|
- In Sherpa: Finanzbuchhaltung -> Summen- und Saldenliste -> Anzeigen
|
|
-> "Nur bebuchte Konten" ausschalten (alle anzeigen)
|
|
-> CSV-Datei exportieren
|
|
- System-Icon rechtsklicken, "Summen- und Saldenliste importieren",
|
|
Datei auswählen
|
|
- Die SuSa-Datei kann jetzt gelöscht werden
|
|
|
|
Benutzung:
|
|
- Cursor in der Sherpa in das Soll-/Haben-Eingabefeld setzen
|
|
- STRG+ALT+k öffnet das Fenster
|
|
- Enter-Taste schreibt das 4-stellige FiBu-Konto in das Soll-/Haben-Feld
|
|
- Escape-Taste schließt das Fenster
|
|
|
|
Tipps:
|
|
- Die Suche findet alle Konten, die die Zeichen in der richtigen Reihenfolge
|
|
enthalten, diese müssen aber nicht direkt aufeinander folgen. Ein Beispiel:
|
|
"vblkr" findet "Verbindlichkeiten an Kreisverbände"
|
|
- Das Script kann automatisch bei Systemstart ausgeführt werden, sodass das
|
|
Suchfenster immer verfügbar ist: Rechtsklick auf das System-Icon
|
|
-> "Bei Systemstart starten"
|
|
- Das Script kann nach der Eingabe des FiBu-Kontos Strg+S drücken, z. B. um
|
|
den Buchungsstapel regelmäßig zu speichern
|
|
- Außerdem kann das FiBu-Konto vorgelesen werden, um eine akustische Rück-
|
|
meldung zu erhalten und dadurch Fehler zu vermeiden
|
|
|
|
Lizenz: GPL v3
|
|
Ansprechperson: Florian Kürsch <florian.kuersch@hamburg.gruene.de>
|
|
Version: 0.1
|
|
Stand: 2024-10-23
|
|
*/
|
|
|
|
#Requires AutoHotkey v2.0
|
|
|
|
FileEncoding("UTF-8")
|
|
autostartShortcutPath := A_Startup . "\FiBu-Konto.lnk"
|
|
configDir := A_AppData . "\AHK-FiBu-Konto"
|
|
ktoListPath := configDir . "\fibu-konten.txt"
|
|
autosaveConfigPath := configDir . "\autosave_enabled"
|
|
voiceConfigPath := configDir . "\voice_enabled"
|
|
|
|
DirCreate(configDir)
|
|
|
|
defaultKtoList := [
|
|
"0000 Importiere die FiBu-Konten über die SuSa-Liste"
|
|
]
|
|
|
|
isActive := false
|
|
|
|
voice := ComObject("SAPI.SpVoice")
|
|
voice.Volume := 100 ; Volume from 0 to 100
|
|
voice.Rate := 0 ; Rate from -10 (slow) to 10 (fast)
|
|
|
|
A_TrayMenu.Delete()
|
|
A_TrayMenu.Insert("1&", "Konten-Suche öffnen (Strg+Alt+k)", (*) => open())
|
|
A_TrayMenu.Insert("2&") ; separator
|
|
A_TrayMenu.Insert("3&", "Summen- && Saldenliste importieren...", (*) => import_susa_file())
|
|
A_TrayMenu.Insert("4&", "Beim Systemstart starten", toggle_autostart)
|
|
if (is_autostart_enabled()) {
|
|
A_TrayMenu.Check("4&")
|
|
}
|
|
A_TrayMenu.Insert("5&", "Nach Eingabe des Kontos Strg+S drücken", toggle_autosave)
|
|
if (is_autosave_enabled()) {
|
|
A_TrayMenu.Check("5&")
|
|
}
|
|
A_TrayMenu.Insert("6&", "Konto vorlesen", toggle_voice)
|
|
if (is_voice_enabled()) {
|
|
A_TrayMenu.Check("6&")
|
|
}
|
|
A_TrayMenu.Insert("7&") ; separator
|
|
A_TrayMenu.Insert("8&", "Beenden", (*) => ExitApp())
|
|
|
|
myGui := Gui("+AlwaysOnTop -Caption +ToolWindow -MinimizeBox", "FiBu-Konto auswählen")
|
|
myGui.SetFont("s10")
|
|
myGui.OnEvent("Escape", on_escape_pressed)
|
|
guiHwnd := myGui.Hwnd
|
|
OnMessage(0x0006, handle_activation)
|
|
|
|
myEdit := myGui.Add("Edit", "w500 vMyEdit")
|
|
myEdit.OnEvent("Change", on_text_changed)
|
|
myListBox := myGui.Add("ListBox", "r15 w500 vmyListBox")
|
|
okButton := myGui.Add("Button", "Default w80", "OK")
|
|
okButton.OnEvent("Click", on_okBtn_clicked)
|
|
|
|
ktoList := read_array_from_file(ktoListPath, defaultKtoList)
|
|
apply_list(ktoList)
|
|
|
|
if (not is_kto_list_imported()) {
|
|
import_susa_file()
|
|
}
|
|
|
|
|
|
^!k:: ; Ctrl+Alt+k
|
|
{
|
|
open()
|
|
}
|
|
|
|
on_escape_pressed(*) {
|
|
close()
|
|
}
|
|
|
|
handle_activation(wParam, lParam, msg, hwnd) {
|
|
if (hwnd = guiHwnd && !wParam) {
|
|
on_focus_lost()
|
|
}
|
|
}
|
|
|
|
on_focus_lost() {
|
|
close()
|
|
}
|
|
|
|
on_okBtn_clicked(*) { ; auch wenn Enter gedrückt wird
|
|
if (myListBox.Value = 0) {
|
|
return
|
|
}
|
|
kto := SubStr(myListBox.Text, 1, 4) ; FiBu-Konten sind 4-stellig
|
|
close()
|
|
|
|
if(is_voice_enabled()) {
|
|
textToSpeak := RegExReplace(myListBox.Text, "^\d{4}\s+")
|
|
textToSpeak := RegExReplace(textToSpeak, "U)\s*\(.*\)$") ; U) ungreedy
|
|
textToSpeak := textToSpeak . ", " . RegExReplace(kto, "(.)", "$1 ")
|
|
SetTimer () => say(textToSpeak), -1 ; async
|
|
}
|
|
|
|
SendText(kto)
|
|
|
|
if(is_autosave_enabled()) {
|
|
SendInput "^s" ; ctrl + s
|
|
}
|
|
}
|
|
|
|
open() {
|
|
global isActive
|
|
isActive := true
|
|
show_gui_on_active_monitor()
|
|
myEdit.Focus()
|
|
}
|
|
|
|
close() {
|
|
global isActive
|
|
myGui.Hide()
|
|
isActive := false
|
|
}
|
|
|
|
; Wir wollen aus dem Textfeld heraus Elemente aus der Liste auswählen.
|
|
; Deshalb registrieren wir einen globalen Hotkey, der nur ausgeführt
|
|
; wird, wenn das Fenster aktiv ist (es gibt kein KeyPress-Event o. Ä.
|
|
; für das Textfeld).
|
|
#HotIf isActive
|
|
Up:: {
|
|
up_by(1)
|
|
}
|
|
PgUp:: {
|
|
up_by(10)
|
|
}
|
|
up_by(offset) {
|
|
val := myListBox.Value - offset
|
|
if (val < 1) {
|
|
val := 1
|
|
}
|
|
myListBox.Value := val
|
|
}
|
|
Down:: {
|
|
down_by(1)
|
|
}
|
|
PgDn:: {
|
|
down_by(10)
|
|
}
|
|
down_by(offset) {
|
|
val := myListBox.Value + offset
|
|
len := ControlGetItems(myListBox).Length
|
|
if (val > len) {
|
|
val := len
|
|
}
|
|
myListBox.Value := val
|
|
}
|
|
#HotIf
|
|
|
|
|
|
on_text_changed(*) {
|
|
do_search_delayed()
|
|
}
|
|
|
|
|
|
searchUpdateTimer := do_search
|
|
|
|
do_search_delayed() {
|
|
global searchUpdateTimer
|
|
if (searchUpdateTimer != 0) {
|
|
SetTimer searchUpdateTimer, 0 ; clear
|
|
}
|
|
searchUpdateTimer := do_search
|
|
SetTimer(searchUpdateTimer, -50) ; 50 ms delay / negative: exec once
|
|
}
|
|
|
|
|
|
do_search() {
|
|
searchText := myEdit.Text
|
|
filteredList := filter_list(ktoList, searchText)
|
|
apply_list(filteredList)
|
|
select_best_match(filteredList, searchText)
|
|
}
|
|
|
|
|
|
filter_list(originalList, searchText) {
|
|
filteredList := []
|
|
Loop originalList.Length {
|
|
if is_subsequence(originalList[A_Index], searchText) {
|
|
filteredList.Push(originalList[A_Index])
|
|
}
|
|
}
|
|
return filteredList
|
|
}
|
|
|
|
apply_list(newList) {
|
|
myListBox.Opt("-Redraw")
|
|
myListBox.Delete()
|
|
if(newList.Length = 0) {
|
|
okButton.Enabled := false
|
|
myListBox.Opt("+Redraw")
|
|
return
|
|
}
|
|
okButton.Enabled := true
|
|
myListBox.Add(newList)
|
|
myListBox.Opt("+Redraw")
|
|
}
|
|
|
|
select_best_match(filteredList, searchText) {
|
|
if (filteredList.Length = 0) {
|
|
return
|
|
}
|
|
if (searchText = "") {
|
|
myListBox.Choose(1)
|
|
return
|
|
}
|
|
exact_match := find_best_exact_match(filteredList, searchText)
|
|
if (exact_match > 0) {
|
|
myListBox.Choose(exact_match)
|
|
return
|
|
}
|
|
grouped_match := find_best_grouped_match(filteredList, searchText)
|
|
if (grouped_match > 0) {
|
|
myListBox.Choose(grouped_match)
|
|
return
|
|
}
|
|
subsequence_match := find_best_subsequence_match(filteredList, searchText)
|
|
if (subsequence_match > 0) {
|
|
myListBox.Choose(subsequence_match)
|
|
return
|
|
}
|
|
;myListBox.Choose(1) ; fallback
|
|
}
|
|
|
|
|
|
find_best_exact_match(list, searchText) {
|
|
/*
|
|
Beispiel: Suche nach "43"
|
|
- "4243 ..."
|
|
- "4300 ..." <= Selektion, weil die 43 am Anfang steht
|
|
- "4301 ..."
|
|
*/
|
|
return find_best_match(list, searchText, match_substring)
|
|
}
|
|
|
|
match_substring(sourceText, searchText) {
|
|
startPos := InStr(sourceText, searchText, false) ; false = case-insensitive
|
|
length := startPos = 0 ? 0 : StrLen(searchText)
|
|
return {startPos: startPos, length: length}
|
|
}
|
|
|
|
find_best_grouped_match(list, searchText) {
|
|
/*
|
|
Beispiel: Suche nach "pa ra"
|
|
- "1803 Forderungen Pfand (Ford Pfand)"
|
|
- "4301 PA Allg Raummiete & Nebenkosten (PA Allg MietRaum)" <= Selektion, weil "ra" in einem neuen Wort beginnt
|
|
- "4321 PA PT Raummiete & Nebenkosten (PA PT MietRaum)" <= nicht selektiert, obwohl der Match kürzer ist
|
|
*/
|
|
return find_best_match(list, searchText, match_group)
|
|
}
|
|
|
|
match_group(sourceText, searchText) {
|
|
pattern := "iU)" . RegExReplace(searchText, "\s+", ".* ")
|
|
startPos := RegExMatch(sourceText, pattern, &match)
|
|
return {startPos: startPos, length: 1} ; Länge wird ignoriert
|
|
}
|
|
|
|
find_best_subsequence_match(list, searchText) {
|
|
/*
|
|
Beispiel: Suche nach "para"
|
|
Hier wird die 3. Zeile selektiert, weil "p" weit links steht und das Match früher Endet als in der 2. Zeile
|
|
- "1650 Sparbücher (Sparbuch)"
|
|
- "4100 Personalausgaben (Ausg Personal) <= "p" steht an der gleichen Position, aber das Match ist deutlich länger
|
|
- "4301 PA Allg Raummiete & Nebenkosten (PA Allg MietRaum)"
|
|
*/
|
|
return find_best_match(list, searchText, match_subsequence)
|
|
}
|
|
|
|
match_subsequence(sourceText, searchText) {
|
|
pattern := "iU)" . RegExReplace(searchText, "(.)", "$1.*")
|
|
; i: case-insensitive / U: ungreedy
|
|
startPos := RegExMatch(sourceText, pattern, &match)
|
|
length := startPos = 0 ? 0 : match.Len
|
|
return {startPos: startPos, length: length}
|
|
}
|
|
|
|
find_best_match(list, searchText, match_func) {
|
|
if (list.Length = 0 or searchText = "") {
|
|
return 0
|
|
}
|
|
bestListIdx := 0
|
|
bestListIdxMatchPos := 0 ; 0 = not found / fist index = 1
|
|
bestListIdxMatchLength := 0
|
|
loop list.Length {
|
|
row := list[A_Index]
|
|
matchInfo := match_func(row, searchText)
|
|
matchPos := matchInfo.startPos
|
|
matchLength := matchInfo.length
|
|
if (matchPos = 0) {
|
|
continue
|
|
}
|
|
if (bestListIdxMatchPos = 0
|
|
or matchPos < bestListIdxMatchPos
|
|
or (matchPos = bestListIdxMatchPos and matchLength < bestListIdxMatchLength)) {
|
|
bestListIdx := A_Index
|
|
bestListIdxMatchPos := matchPos
|
|
bestListIdxMatchLength := matchLength
|
|
}
|
|
}
|
|
return bestListIdx
|
|
}
|
|
|
|
is_subsequence(sourceText, searchText) {
|
|
return match_subsequence(sourceText, searchText).startPos > 0 ? true : false
|
|
}
|
|
|
|
import_susa_file() {
|
|
MsgBox("
|
|
(
|
|
Das Suchfenster benötigt eine Liste aller FiBu-Konten. Am einfachsten geht das über die Summen- und Saldenliste:
|
|
|
|
1. In den Sherpa-Einstellungen sicherstellen, dass der Export mit UTF-8-
|
|
Zeichensatz geschieht (Allgemein => Finanzbuchhaltung => Exportoptionen).
|
|
Hinweis: Diese Einstellung gilt nur für die aktuelle Gliederung.
|
|
|
|
2. In Sherpa: Finanzbuchhaltung => Summen- und Saldenliste => Anzeigen
|
|
-> "Nur bebuchte Konten" ausschalten (alle anzeigen)
|
|
-> CSV-Datei exportieren
|
|
|
|
Wenn die SuSa-Liste bereit ist, klicke bitte OK.
|
|
)", "Summen- und Saldenliste importieren")
|
|
|
|
selectedFile := FileSelect("1", "", "Summen- und Saldenliste auswählen (UTF-8)", "CSV-Dateien (*.csv)")
|
|
; "1": File Must Exist
|
|
if (selectedFile = "") {
|
|
return
|
|
}
|
|
|
|
newKtoList := create_kto_list_from_susa_file(selectedFile)
|
|
write_array_to_file(newKtoList, ktoListPath)
|
|
|
|
global ktoList
|
|
ktoList := newKtoList
|
|
apply_list(ktoList)
|
|
|
|
MsgBox("
|
|
(
|
|
FiBu-Konten erfolgreich importiert. Du kannst die SuSa-Datei jetzt löschen.
|
|
|
|
Das Suchfenster kannst du mit STRG+ALT+k öffnen, nachdem du den Cursor in ein Textfeld gesetzt hast.
|
|
)", "Import erfolgreich")
|
|
}
|
|
|
|
is_kto_list_imported() {
|
|
return FileExist(ktoListPath) != ""
|
|
}
|
|
|
|
create_kto_list_from_susa_file(susaFile) {
|
|
newKtoList := []
|
|
|
|
Loop read, susaFile {
|
|
if (A_Index = 1) {
|
|
Continue ; erste Zeile überspringen
|
|
}
|
|
lineNumber := A_Index
|
|
; workaround: das AutoHotKey CSV-Parsing erwartet Komma, Sherpa exportiert Semikolon
|
|
; todo: prüfen, was mit Kommata in Feldern passiert
|
|
line := StrReplace(A_LoopReadLine, ";", ",")
|
|
newKtoLine := ""
|
|
Loop parse, line, "CSV" {
|
|
if (A_Index = 4) {
|
|
break ; nur die ersten 3 Spalten sind für uns interessant
|
|
}
|
|
field := A_LoopField
|
|
if (A_Index = 3) { ; Kurzname
|
|
field := "(" . field . ")"
|
|
}
|
|
field := Trim(field)
|
|
newKtoLine := newKtoLine . " " . field
|
|
}
|
|
newKtoList.Push(Trim(newKtoLine))
|
|
}
|
|
|
|
return newKtoList
|
|
}
|
|
|
|
write_array_to_file(myArray, filePath) {
|
|
file := FileOpen(filePath, "w")
|
|
for line in myArray {
|
|
file.WriteLine(line)
|
|
}
|
|
file.Close()
|
|
}
|
|
|
|
read_array_from_file(filePath, defaultArray) {
|
|
if ( FileExist(filePath) = "" ) {
|
|
return defaultArray
|
|
}
|
|
myArray := []
|
|
Loop Read, filePath {
|
|
myArray.Push(A_LoopReadLine)
|
|
}
|
|
return myArray
|
|
}
|
|
|
|
toggle_autostart(itemName, itemPos, myMenu) {
|
|
if (is_autostart_enabled()) {
|
|
FileDelete(autostartShortcutPath)
|
|
myMenu.Uncheck(itemName)
|
|
} else {
|
|
FileCreateShortcut(A_ScriptFullPath, autostartShortcutPath)
|
|
myMenu.Check(itemName)
|
|
}
|
|
}
|
|
|
|
is_autostart_enabled() {
|
|
return FileExist(autostartShortcutPath) != ""
|
|
}
|
|
|
|
toggle_autosave(itemName, itemPos, myMenu) {
|
|
if (is_autosave_enabled()) {
|
|
FileDelete(autosaveConfigPath)
|
|
myMenu.Uncheck(itemName)
|
|
} else {
|
|
FileAppend "", autosaveConfigPath
|
|
myMenu.Check(itemName)
|
|
}
|
|
}
|
|
|
|
is_autosave_enabled() {
|
|
return FileExist(autosaveConfigPath) != ""
|
|
}
|
|
|
|
|
|
toggle_voice(itemName, itemPos, myMenu) {
|
|
if (is_voice_enabled()) {
|
|
FileDelete(voiceConfigPath)
|
|
myMenu.Uncheck(itemName)
|
|
} else {
|
|
FileAppend "", voiceConfigPath
|
|
myMenu.Check(itemName)
|
|
}
|
|
}
|
|
|
|
is_voice_enabled() {
|
|
return FileExist(voiceConfigPath) != ""
|
|
}
|
|
|
|
|
|
show_gui_on_active_monitor() {
|
|
activeWin := WinExist("A")
|
|
if (not activeWin) {
|
|
myGui.Show()
|
|
return
|
|
}
|
|
WinGetPos(&winX, &winY, &winWidth, &winHeight, activeWin)
|
|
winCenterX := winX + winWidth // 2
|
|
winCenterY := winY + winHeight // 2
|
|
|
|
myGui.Show()
|
|
; GetPos ist erst verfügbar, wenn das Fenster sichtbar ist
|
|
myGui.GetPos(&guiX, &guiY, &guiWidth, &guiHeight)
|
|
|
|
monitorIndex := get_monitor_index_from_window(winCenterX, winCenterY)
|
|
monitorInfo := get_monitor_info(monitorIndex)
|
|
guiX := monitorInfo.left + (monitorInfo.right - monitorInfo.left) // 2 - guiWidth // 2
|
|
guiY := monitorInfo.top + (monitorInfo.bottom - monitorInfo.top) // 2 - guiHeight // 2
|
|
|
|
myGui.Show(Format("x{} y{}", guiX, guiY))
|
|
}
|
|
|
|
get_monitor_index_from_window(x, y) {
|
|
monitorCount := MonitorGetCount()
|
|
Loop monitorCount {
|
|
monitorInfo := get_monitor_info(A_Index)
|
|
if (x >= monitorInfo.left && x < monitorInfo.right && y >= monitorInfo.top && y < monitorInfo.bottom) {
|
|
return A_Index
|
|
}
|
|
}
|
|
return 1
|
|
}
|
|
|
|
get_monitor_info(monitorIndex) {
|
|
MonitorGet(monitorIndex, &left, &top, &right, &bottom)
|
|
MonitorGetWorkArea(monitorIndex, &workLeft, &workTop, &workRight, &workBottom)
|
|
return {left: workLeft, top: workTop, right: workRight, bottom: workBottom}
|
|
}
|
|
|
|
say(textToSpeak) {
|
|
; todo: diese Liste als CSV-Datei speichern/laden
|
|
voice.Skip("Sentence", 1)
|
|
textToSpeak := StrReplace(textToSpeak, "Pers Net ", "Personal Netto: ", true)
|
|
textToSpeak := StrReplace(textToSpeak, "Pers SV ", "Personal Sozialversicherung: ", true)
|
|
textToSpeak := StrReplace(textToSpeak, "Pers FA ", "Personal Finanzamt: ", true)
|
|
textToSpeak := StrReplace(textToSpeak, "Pers ", "Personal ", true)
|
|
textToSpeak := StrReplace(textToSpeak, "GB Inv ", "Geschäftsbetrieb Inventar: ", true)
|
|
textToSpeak := StrReplace(textToSpeak, "GB Bü ", "Geschäftsbetrieb Büro: ", true)
|
|
textToSpeak := StrReplace(textToSpeak, "GB DL ", "Geschäftsbetrieb Dienstleistungen: ", true)
|
|
textToSpeak := StrReplace(textToSpeak, "GB Sonst ", "Geschäftsbetrieb Sonstiges: ", true)
|
|
textToSpeak := StrReplace(textToSpeak, "GB ", "Geschäftsbetrieb ", true)
|
|
textToSpeak := StrReplace(textToSpeak, "PA Allg ", "Politische Arbeit allgemein: ", true)
|
|
textToSpeak := StrReplace(textToSpeak, "PA PT ", "Politische Arbeit Parteitage: ", true)
|
|
textToSpeak := StrReplace(textToSpeak, "PA VA ", "Politische Arbeit Veranstaltungen: ", true)
|
|
textToSpeak := StrReplace(textToSpeak, "PA Gre ", "Politische Arbeit Gremien: ", true)
|
|
textToSpeak := StrReplace(textToSpeak, "PA ", "Politische Arbeit ", true)
|
|
textToSpeak := StrReplace(textToSpeak, "WK EU ", "Wahlkampf Europa-Wahl: ", true)
|
|
textToSpeak := StrReplace(textToSpeak, "WK BTW ", "Wahlkampf Bundestagswahl: ", true)
|
|
textToSpeak := StrReplace(textToSpeak, "WK Bü ", "Wahlkampf Bürgerschaftswahl: ", true)
|
|
textToSpeak := StrReplace(textToSpeak, "WK BV ", "Wahlkampf Bezirkswahl: ", true)
|
|
textToSpeak := StrReplace(textToSpeak, "WK ", "Wahlkampf ", true)
|
|
textToSpeak := StrReplace(textToSpeak, "VZ GB ", "Verrechnungszuschuss eingehend, Geschäftsbetrieb: ", true)
|
|
textToSpeak := StrReplace(textToSpeak, "VZ PA ", "Verrechnungszuschuss eingehend, Politische Arbeit: ", true)
|
|
textToSpeak := StrReplace(textToSpeak, "VZ WK ", "Verrechnungszuschuss eingehend, Wahlkampf: ", true)
|
|
textToSpeak := StrReplace(textToSpeak, "VZ ", "Verrechnungszuschuss eingehend ", true)
|
|
textToSpeak := StrReplace(textToSpeak, "vz GB ", "Verrechnungszuschuss ausgehend, Geschäftsbetrieb: ", true)
|
|
textToSpeak := StrReplace(textToSpeak, "vz PA ", "Verrechnungszuschuss ausgehend, Politische Arbeit: ", true)
|
|
textToSpeak := StrReplace(textToSpeak, "vz WK ", "Verrechnungszuschuss ausgehend, Wahlkampf: ", true)
|
|
textToSpeak := StrReplace(textToSpeak, "vz ", "Verrechnungszuschuss ausgehend ", true)
|
|
textToSpeak := StrReplace(textToSpeak, "EZ ", "Echter Zuschuss eingehend ", true)
|
|
textToSpeak := StrReplace(textToSpeak, "ez ", "Echter Zuschuss ausgehend ", true)
|
|
textToSpeak := StrReplace(textToSpeak, "GBV", "Geschäftsbesorgungsvertrag", true)
|
|
textToSpeak := StrReplace(textToSpeak, "auß ", "außerhalb ", true)
|
|
textToSpeak := StrReplace(textToSpeak, "HH", "Hamburg", true)
|
|
textToSpeak := StrReplace(textToSpeak, "BV", "Bundesverband", true)
|
|
textToSpeak := StrReplace(textToSpeak, "LV", "Landesverband", true)
|
|
textToSpeak := StrReplace(textToSpeak, "KV", "Kreisverband", true)
|
|
textToSpeak := StrReplace(textToSpeak, "GJ", "Grüne Jugend", true)
|
|
textToSpeak := StrReplace(textToSpeak, "Gl.", "Gliederung", true)
|
|
textToSpeak := StrReplace(textToSpeak, "Fr ", "Fraktion ", true)
|
|
textToSpeak := StrReplace(textToSpeak, "FiBu", "Finanzbuchhaltung", true)
|
|
textToSpeak := StrReplace(textToSpeak, "USt", "Umsatzsteuer", true)
|
|
voice.Speak(textToSpeak, 0x1)
|
|
}
|