Files
enchiriadis 7a6a7751f6 Helpers/scripts/_belegversand_setup.applescript aktualisiert
Erweiterungen der Funktionalität auf die Bedürfnisse von DATEV:
Belegversand.conf siehe jetzt so aus:

## Benutzer-Einstellungen - bitte individuell anpassen.
## Eine Kurz-Doku findest du in der Datei README-Belegversand.md

APP_VERSION=1.5
MAIL_TO_DEFAULT=echiriadis@netcologne.de
MAIL_CC_TO_DEFAULT=aischa.abdulaziz@gmail.de
MAIL_SUBJECT_PREFIX=Unterlagen

## Versandmodus: true = ein ZIP-Archiv (optional mit Passwort),
##               false = Belege einzeln anhängen (z.B. für DATEV)
ZIP_ARCHIVE=false
ZIP_PASSWORD_DEFAULT=

## Maximale Mailgröße in MB. Größere Sendungen werden auf mehrere Mails verteilt.
MAX_MAIL_SIZE_MB=20

## Erlaubte Dateitypen, durch Komma getrennt. Andere Belege werden
## vor dem Versand gemeldet (bitte vorher in PDF konvertieren).
SUPPORTED_FILE_TYPES_FOR_SENDING=pdf, tif, tiff

LOG_SENT_GROUP_NAME=Versendet
WRITE_DEBUG_LOG=true
SHOW_README=false
MAILBODY_FOLDER_NAME=mail_vorlagen
MAILBODY_RECORD_NAME=belegversand
SENT_TAG_FOR_DOCS=gesendet
PATH_TO_DRYRUN_PREVIEW=/tmp/DT-Belegversand-preview.txt

## Die folgenden Werte werden von der Software geschrieben.
## Bitte nur manuell editieren, wenn du weisst, was du tust!

BELEGE_ROOT_UUID=CBCEA210-6C76-4DB5-9195-2295354C6080
README_UUID=D5B05DFD-710E-4577-9808-E544741F2049
2026-10-12 20:18:02 +00:00

736 lines
32 KiB
AppleScript
Raw Permalink 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.
(*
_belegversand_setup.scpt – Version 1.5
Rolle dieses Scripts
--------------------
Dieses Script hat eine Doppelfunktion:
1. Es prüft, ob relativ zur aktuellen DEVONthink-Datenbank bereits eine
gültige Konfiguration für den Belegversand existiert.
Falls ja, startet es direkt das Hauptscript _belegversand.scpt.
2. Falls noch keine gültige Konfiguration existiert, führt es die
Ersteinrichtung durch:
- Belege-Hauptgruppe bestimmen
- _config-Struktur anlegen
- Konfigurationsdatei schreiben
- README-Datei anlegen/aktualisieren
- Mailbody-Datei anlegen/aktualisieren
- globale /Versendet_Outsider-Gruppe anlegen
- Gruppe /Originale für umgewandelte Fotos anlegen
Zielstruktur in DEVONthink
--------------------------
DB-Wurzel
├── Originale (Original-Fotos nach PDF-Umwandlung per Smart Rule)
├── Versendet_Outsider
│ ├── !Ergebnis
│ ├── sentlog_outsiders
│ └── sentlog_outsiders.ndjson
└── _config
├── belegversand.conf
├── README-Belegversand
└── mail_vorlagen
└── belegversand
Wichtig:
--------
Dieses Script ist der eigentliche "Dispatcher" zwischen
- Einrichtung
- und normalem Versand.
Version 1.4:
-----------
- MAIL_CC_TO_DEFAULT in Config hinzugefügt
- Globale Versendet_Outsider-Gruppe wird automatisch angelegt
Version 1.5:
-----------
- Neue Config-Schlüssel:
ZIP_ARCHIVE, MAX_MAIL_SIZE_MB, SUPPORTED_FILE_TYPES_FOR_SENDING
- Setup fragt den Versandmodus ab (ZIP oder Einzeldateien);
ZIP-Passwort wird nur noch im ZIP-Modus abgefragt
- Fix: Jahres-/Monatsordner werden nicht mehr doppelt angelegt,
wenn sie bereits existieren
- Fix: Keine verschachtelte Gruppe Belege/Belege mehr, wenn die
ausgewählte Hauptgruppe bereits "Belege" heißt
- Fix: Jahresordner-Loggruppe nutzt den gewählten Namen
(LOG_SENT_GROUP_NAME) statt fest "Versendet"
- Aufgeräumt: doppelter debugInfoText-Block und überflüssige
basePath-Zuweisung entfernt
- Neu: Gruppe /Originale auf DB-Wurzel-Ebene (außerhalb der Belege,
damit die Smart Rule ihre eigenen Originale nicht erneut erfasst)
- Neu: Anleitung für die Smart Rule "Fotos → PDF" als Hinweisdialog
am Ende des Setups und im README (Smart Rules lassen sich per
AppleScript nicht anlegen)
*)
-- Name der DEVONthink-Konfigurationsgruppe.
property configGroupName : "_config"
-- Name der Konfigurationsdatei.
property configRecordName : "belegversand.conf"
-- Name des Hilfetext-Records in DEVONthink.
property readmeRecordName : "README-Belegversand"
-- Name des Unterordners innerhalb von _config, in dem Mail-Vorlagen liegen.
property defaultMailBodyFolderName : "mail_vorlagen"
-- Name des Standard-Mailbody-Records.
property defaultMailBodyRecordName : "belegversand"
-- Schlüsselfeld, an dem eine Konfiguration als "gültig" erkannt wird.
-- Wenn BELEGE_ROOT_UUID fehlt, gilt die Config als unvollständig.
property requiredConfigKey : "BELEGE_ROOT_UUID="
-- Name des eigentlichen Hauptscripts.
property mainScriptName : "_belegversand.scpt"
-- Versionsinformation für README und Hinweise.
property appVersion : "1.5"
property basePath : "Library/Application Scripts/com.devon-technologies.think/Menu/Belegversand"
-- Default-Werte für die Erstkonfiguration.
property defaultMailTo : "steuer@name.de"
property defaultSubjectPrefix : "Unterlagen"
property defaultZipPassword : ""
property defaultLogSentGroupName : "Versendet"
property defaultSentTagForDocs : "gesendet"
property defaultWriteDebugLog : "false"
property defaultShowReadme : "true"
property defaultDryRunPreviewPath : "/tmp/DT-Belegversand-preview.txt"
-- Neu in 1.5
property defaultZipArchive : "true"
property defaultMaxMailSizeMB : "20"
property defaultSupportedFileTypes : "pdf, tif, tiff"
-- Gruppe für Original-Fotos nach der PDF-Umwandlung (auf DB-Wurzel-Ebene)
property originalsGroupName : "Originale"
-- Monatsordner im Jahreszyklus
property monthNames : {"01_Januar", "02_Februar", "03_März", "04_April", "05_Mai", "06_Juni", "07_Juli", "08_August", "09_September", "10_Oktober", "11_November", "12_Dezember"}
(*
runSiblingScript(scriptFileName)
--------------------------------
Lädt und startet ein weiteres Script aus Helpers/scripts/.
Parameter:
- scriptFileName : Name der Scriptdatei, die gestartet werden soll
Verwendung:
- bei vorhandener Konfiguration wird das Hauptscript _belegversand.scpt gestartet
*)
on runSiblingScript(scriptFileName)
set expandedPath to (do shell script "echo ~") & "/" & basePath
set targetPath to expandedPath & "/Helpers/scripts/" & scriptFileName
try
set targetAlias to POSIX file targetPath as alias
on error
display dialog "Hinweis: Scriptdatei nicht gefunden:" & return & targetPath buttons {"OK"} default button "OK"
return
end try
try
set loadedScript to load script targetAlias
run loadedScript
on error errMsg number errNum
display dialog "Hinweis: Konnte Script nicht starten:" & return & scriptFileName & return & return & errMsg buttons {"OK"} default button "OK"
end try
end runSiblingScript
(*
recordText(theRecord)
---------------------
Liest den Textinhalt eines DEVONthink-Records.
Versucht zuerst:
- plain text
und fällt bei Bedarf zurück auf:
- content
*)
on recordText(theRecord)
tell application id "DNtp"
try
set t to plain text of theRecord
if t is missing value then set t to ""
return t as rich text
on error
try
set t to content of theRecord
if t is missing value then set t to ""
return t as rich text
on error
return ""
end try
end try
end tell
end recordText
(*
isGroupByRecord(theRecord)
--------------------------
Prüft, ob ein DEVONthink-Record eine Gruppe ist.
*)
on isGroupByRecord(theRecord)
tell application id "DNtp"
try
return ((type of theRecord) is group)
on error
return false
end try
end tell
end isGroupByRecord
(*
findChildByName(parentRecord, childName)
----------------------------------------
Sucht innerhalb einer DEVONthink-Gruppe ein direktes Kind
mit einem bestimmten Namen.
Rückgabe:
- den gefundenen Record
- oder missing value
*)
on findChildByName(parentRecord, childName)
tell application id "DNtp"
try
set kids to children of parentRecord
repeat with i from 1 to count of kids
set c to item i of kids
if (name of c as rich text) is childName then return c
end repeat
end try
end tell
return missing value
end findChildByName
(*
getOrCreateChildGroup(parentRecord, groupName)
----------------------------------------------
Sucht unter parentRecord eine Gruppe groupName.
Falls sie nicht existiert, wird sie angelegt.
Beispiele:
- _config
- mail_vorlagen
- Versendet_Outsider
- Jahres- und Monatsordner
*)
on getOrCreateChildGroup(parentRecord, groupName)
set existingGroup to my findChildByName(parentRecord, groupName)
if existingGroup is not missing value then return existingGroup
tell application id "DNtp"
return create record with {name:groupName, type:group} in parentRecord
end tell
end getOrCreateChildGroup
(*
getOrCreateTextRecord(parentRecord, recordName, initialText)
------------------------------------------------------------
Sucht unter parentRecord einen Textrecord mit gegebenem Namen.
Falls er nicht existiert, wird er mit initialText angelegt.
*)
on getOrCreateTextRecord(parentRecord, recordName, initialText)
set existingRecord to my findChildByName(parentRecord, recordName)
if existingRecord is not missing value then return existingRecord
tell application id "DNtp"
return create record with {name:recordName, type:txt, content:initialText} in parentRecord
end tell
end getOrCreateTextRecord
(*
getOrCreateMarkdownRecord(parentRecord, recordName, initialText)
----------------------------------------------------------------
Wie getOrCreateTextRecord, aber für Markdown-Records.
*)
on getOrCreateMarkdownRecord(parentRecord, recordName, initialText)
set existingRecord to my findChildByName(parentRecord, recordName)
if existingRecord is not missing value then return existingRecord
tell application id "DNtp"
return create record with {name:recordName, type:markdown, content:initialText} in parentRecord
end tell
end getOrCreateMarkdownRecord
(*
buildConfigText(...)
--------------------
Baut den vollständigen Inhalt der Konfigurationsdatei belegversand.conf.
Die Parser im Hauptscript lesen nur Zeilen, die wirklich mit KEY= beginnen.
Kommentarzeilen (##) sind daher unproblematisch und dienen als Kurzhilfe.
*)
on buildConfigText(mailToDefault, mailCcToDefault, subjectPrefix, zipArchiveValue, zipPasswordDefault, maxMailSizeMB, supportedFileTypes, logSentGroupName, belegeRootUUID, writeDebugLogValue, showReadmeValue, readmeUUID, mailBodyFolderName, mailBodyRecordName, sentTagForDocs, dryRunPreviewPath)
return "## Benutzer-Einstellungen - bitte individuell anpassen." & return & ¬
"## Eine Kurz-Doku findest du in der Datei README-Belegversand.md" & return & return & ¬
"APP_VERSION=" & appVersion & return & ¬
"MAIL_TO_DEFAULT=" & mailToDefault & return & ¬
"MAIL_CC_TO_DEFAULT=" & mailCcToDefault & return & ¬
"MAIL_SUBJECT_PREFIX=" & subjectPrefix & return & return & ¬
"## Versandmodus: true = ein ZIP-Archiv (optional mit Passwort)," & return & ¬
"## false = Belege einzeln anhängen (z.B. für DATEV)" & return & ¬
"ZIP_ARCHIVE=" & zipArchiveValue & return & ¬
"ZIP_PASSWORD_DEFAULT=" & zipPasswordDefault & return & return & ¬
"## Maximale Mailgröße in MB. Größere Sendungen werden auf mehrere Mails verteilt." & return & ¬
"MAX_MAIL_SIZE_MB=" & maxMailSizeMB & return & return & ¬
"## Erlaubte Dateitypen, durch Komma getrennt. Andere Belege werden" & return & ¬
"## vor dem Versand gemeldet (bitte vorher in PDF konvertieren)." & return & ¬
"SUPPORTED_FILE_TYPES_FOR_SENDING=" & supportedFileTypes & return & return & ¬
"LOG_SENT_GROUP_NAME=" & logSentGroupName & return & ¬
"WRITE_DEBUG_LOG=" & writeDebugLogValue & return & ¬
"SHOW_README=" & showReadmeValue & return & ¬
"MAILBODY_FOLDER_NAME=" & mailBodyFolderName & return & ¬
"MAILBODY_RECORD_NAME=" & mailBodyRecordName & return & ¬
"SENT_TAG_FOR_DOCS=" & sentTagForDocs & return & ¬
"PATH_TO_DRYRUN_PREVIEW=" & dryRunPreviewPath & return & return & ¬
"## Die folgenden Werte werden von der Software geschrieben." & return & ¬
"## Bitte nur manuell editieren, wenn du weisst, was du tust!" & return & return & ¬
"BELEGE_ROOT_UUID=" & belegeRootUUID & return & ¬
"README_UUID=" & readmeUUID & return
end buildConfigText
(*
buildReadmeText()
-----------------
Erzeugt den Inhalt der internen README-Datei in DEVONthink.
Bewusst kurz & prägnant — Links verweisen auf ausführliche Doku.
*)
on buildReadmeText()
return "# Belegversand v" & appVersion & return & return & ¬
"Automatisiertes Dokumenten-Versand-System für DEVONthink-Datenbanken." & return & return & ¬
"## Installation" & return & return & ¬
"```bash" & return & ¬
"cd ~/Library/Application\\ Scripts/com.devon-technologies.think/Menu/" & return & ¬
"git clone https://gitea.tuxi.ddnss.de/enchiriadis/DEVONthink-Belegversand-Automatisation.git Belegversand" & return & ¬
"cd Belegversand && ./compile.sh" & return & ¬
"```" & return & return & ¬
"## Dokumentation" & return & return & ¬
"- **Ausführliche Anleitung & Setup-Guide:** " & return & ¬
" https://wiki.tuxi.ddnss.de/wiki/Belegversand" & return & ¬
"- **Konfiguration:** _config/belegversand.conf" & return & ¬
"- **Mail-Templates:** _config/mail_vorlagen/" & return & return & ¬
"## Erste Schritte" & return & return & ¬
"1. Belege in DEVONthink auswählen" & return & ¬
"2. Scripts → START aufrufen" & return & ¬
"3. Dialog folgen → Versand vorbereiten" & return & return & ¬
"## Versandmodus" & return & return & ¬
"- `ZIP_ARCHIVE=true`: alle Belege in einem ZIP-Archiv (optional mit Passwort)" & return & ¬
"- `ZIP_ARCHIVE=false`: Belege einzeln angehängt (z.B. für DATEV)" & return & ¬
"- Ohne ZIP gehen die Belege unverschlüsselt raus (nur TLS beim Transport)." & return & ¬
"- Nur Dateitypen aus `SUPPORTED_FILE_TYPES_FOR_SENDING` werden versendet." & return & ¬
" Fotos (JPG/HEIC/PNG) vorher in PDF umwandeln – am besten automatisch (siehe unten)." & return & return & ¬
"## Fotos automatisch in PDF umwandeln (Smart Rule)" & return & return & ¬
"Smart Rules lassen sich nicht per Script anlegen – bitte einmalig von Hand einrichten:" & return & return & my buildSmartRuleSteps("Belege") & return & ¬
"Hinweise:" & return & return & ¬
"- Die Gruppe `" & originalsGroupName & "` liegt bewusst auf oberster Ebene, also außerhalb der Belege. So erfasst die Regel ihre eigenen Originale nicht erneut." & return & ¬
"- Smart Rules gehören zur DEVONthink-Installation, nicht zur Datenbank. Auf einem weiteren Mac muss die Regel dort erneut angelegt werden." & return & ¬
"- Mit einem einzelnen Test-Foto prüfen, ob danach das PDF in der Belege-Gruppe und das Foto in `" & originalsGroupName & "` liegt." & return & return & ¬
"Hinweis: Detaillierte Dokumentation immer im Repository (siehe oben)."
end buildReadmeText
(*
buildSmartRuleSteps(belegeGroupName)
------------------------------------
Schritt-für-Schritt-Anleitung für die Smart Rule "Fotos → PDF".
Wird im README und im Abschlussdialog des Setups verwendet.
Die Bezeichnungen der Menüs/Aktionen können je nach DEVONthink-Version
leicht abweichen.
*)
on buildSmartRuleSteps(belegeGroupName)
return "1. In DEVONthink eine neue intelligente Regel (Smart Rule) anlegen." & return & ¬
"2. Name: z.B. „Belege: Fotos → PDF“." & return & ¬
"3. Suchen in: Gruppe „" & belegeGroupName & "“ (inkl. Untergruppen)." & return & ¬
"4. Bedingung: Art ist Bild." & return & ¬
"5. Ausführen: beim Importieren (optional zusätzlich: beim Hinzufügen)." & return & ¬
"6. Aktion: in durchsuchbares PDF umwandeln (OCR) – oder, falls OCR nicht verfügbar: in PDF konvertieren." & return & ¬
"7. Aktion: Original verschieben nach Gruppe „" & originalsGroupName & "“." & return
end buildSmartRuleSteps
(*
buildDefaultMailBodyText()
--------------------------
Erzeugt den Standardtext für die Mailbody-Datei.
*)
on buildDefaultMailBodyText()
return "Sehr geehrte/r Frau/Herr <Name>," & return & return & ¬
"beiliegend erhalten Sie die aktuellen Belege und Quittungen." & return & return & ¬
"Mit freundlichen Grüßen" & return & return & ¬
"Unterschrift" & return
end buildDefaultMailBodyText
(*
getTopParentGroup(theRecord)
----------------------------
Ermittelt die oberste Gruppe der Datenbankstruktur relativ zu einem Record.
*)
on getTopParentGroup(theRecord)
tell application id "DNtp"
set currentRecord to theRecord
repeat
try
set p to parents of currentRecord
if p is {} then return currentRecord
set currentRecord to item 1 of p
on error
return currentRecord
end try
end repeat
end tell
end getTopParentGroup
(*
findConfigRecordInDBRoot(dbRootRecord)
--------------------------------------
Prüft, ob in der gegebenen DB-Wurzel bereits eine gültige Konfiguration existiert.
*)
on findConfigRecordInDBRoot(dbRootRecord)
set configGroupRecord to my findChildByName(dbRootRecord, configGroupName)
if configGroupRecord is missing value then return missing value
set configRecord to my findChildByName(configGroupRecord, configRecordName)
if configRecord is missing value then return missing value
set cfgText to my recordText(configRecord)
if cfgText does not contain requiredConfigKey then return missing value
return configRecord
end findConfigRecordInDBRoot
(*
getCurrentYear()
----------------
Ermittelt das aktuelle Jahr als 4-stelligen String (z.B. "2026").
*)
on getCurrentYear()
return do shell script "date '+%Y'"
end getCurrentYear
(*
getCurrentMonth()
-----------------
Ermittelt den aktuellen Monat als Zahl (1-12).
*)
on getCurrentMonth()
set monthStr to do shell script "date '+%m'"
return (monthStr as integer)
end getCurrentMonth
(*
isNovemberOrDecember()
----------------------
Prüft, ob der aktuelle Monat November (11) oder Dezember (12) ist.
*)
on isNovemberOrDecember()
set currentMonth to my getCurrentMonth()
return (currentMonth = 11 or currentMonth = 12)
end isNovemberOrDecember
(*
createYearCycleInGroup(parentGroup, yearString, logGroupName)
-------------------------------------------------------------
Erstellt einen Jahresordner (z.B. "2026") unter parentGroup
und erzeugt die Monatsordner (01_Januar bis 12_Dezember) darin,
dazu die Loggruppe (LOG_SENT_GROUP_NAME) und "Provisionsabrechnungen".
Bereits vorhandene Ordner werden wiederverwendet, nicht dupliziert.
(DEVONthink legt bei "create record" sonst stillschweigend Duplikate an.)
*)
on createYearCycleInGroup(parentGroup, yearString, logGroupName)
set yearGroupRecord to my getOrCreateChildGroup(parentGroup, yearString)
repeat with monthName in my monthNames
my getOrCreateChildGroup(yearGroupRecord, monthName as text)
end repeat
my getOrCreateChildGroup(yearGroupRecord, logGroupName)
my getOrCreateChildGroup(yearGroupRecord, "Provisionsabrechnungen")
end createYearCycleInGroup
(*
yearCycleStatus(belegeRootRecord, belegeRootName, yearString)
-------------------------------------------------------------
Prüft, ob der Jahreszyklus für ein Jahr bereits existiert.
Rückgabe:
- "missing" : Jahresordner (oder Gruppe Belege) fehlt
- "partial" : Jahresordner vorhanden, aber Monatsordner fehlen
- "complete" : Jahresordner mit allen 12 Monatsordnern vorhanden
*)
on yearCycleStatus(belegeRootRecord, belegeRootName, yearString)
if belegeRootName is "Belege" then
set belegeGroup to belegeRootRecord
else
set belegeGroup to my findChildByName(belegeRootRecord, "Belege")
if belegeGroup is missing value then return "missing"
end if
set yearGroup to my findChildByName(belegeGroup, yearString)
if yearGroup is missing value then return "missing"
repeat with monthName in my monthNames
if my findChildByName(yearGroup, monthName as text) is missing value then return "partial"
end repeat
return "complete"
end yearCycleStatus
(*
run
---
Haupteinstiegspunkt des Setup-Scripts.
*)
on run
with timeout of 3600 seconds
tell application id "DNtp"
set sel to selection
if sel is {} then
display dialog "Belegversand – nichts ausgewählt" & return & return & ¬
"Bitte wähle in DEVONthink zuerst etwas in der Datenbank aus, mit der du arbeiten möchtest – am einfachsten die Gruppe „Belege“ oder einen beliebigen Beleg darin." & return & return & ¬
"Warum?" & return & ¬
"Das Script erkennt an deiner Auswahl, um welche Datenbank es geht, und sucht dort nach der Konfiguration (_config/belegversand.conf):" & return & return & ¬
"• Konfiguration gefunden → der Belegversand startet." & return & ¬
"• Keine Konfiguration → die Ersteinrichtung startet." & return & return & ¬
"Danach das Script einfach noch einmal aufrufen." buttons {"OK"} default button "OK" with title "Belegversand " & appVersion
return
end if
set selectedRecord to item 1 of sel
end tell
set dbRootRecord to my getTopParentGroup(selectedRecord)
set existingConfigRecord to my findConfigRecordInDBRoot(dbRootRecord)
if existingConfigRecord is not missing value then
my runSiblingScript(mainScriptName)
return
end if
-- ─────────────────────────────────────────────
-- Ersteinrichtung: Belege-Hauptgruppe bestimmen
-- ─────────────────────────────────────────────
tell application id "DNtp"
set sel to selection
if sel is not {} then
set selectedRecord to item 1 of sel
try
set selectedName to name of selectedRecord as rich text
on error
set selectedName to ""
end try
try
set selectedIsGroup to ((type of selectedRecord) is group)
on error
set selectedIsGroup to false
end try
else
set selectedName to ""
set selectedIsGroup to false
end if
end tell
set setupIntroText to "In dieser Datenbank ist der Belegversand noch nicht eingerichtet." & return & return & ¬
"Die Einrichtung legt an:" & return & ¬
"• _config – Konfiguration, README und Mail-Vorlagen" & return & ¬
"• Versendet_Outsider – Log für Belege außerhalb von Jahresordnern" & return & ¬
"• optional: Jahresordner mit Monatsordnern (01_Januar … 12_Dezember)" & return & return & ¬
"Bestehende Belege werden dabei nicht verändert." & return & return
if selectedIsGroup and selectedName is "Belege" then
display dialog setupIntroText & ¬
"Als Belege-Hauptgruppe wird die bereits ausgewählte Gruppe „Belege“ verwendet." buttons {"Abbrechen", "Einrichtung starten"} default button "Einrichtung starten" cancel button "Abbrechen" with title "Belegversand " & appVersion & " – Einrichtung"
else
display dialog setupIntroText & ¬
"Wähle jetzt in DEVONthink die Belege-Hauptgruppe aus – also die Gruppe, in der alle Belege und Quittungen liegen (üblicherweise „Belege“)." & return & return & ¬
"Lass diesen Dialog dabei offen und klicke erst danach auf „Weiter“." buttons {"Abbrechen", "Weiter"} default button "Weiter" cancel button "Abbrechen" with title "Belegversand " & appVersion & " – Einrichtung"
end if
tell application id "DNtp"
set sel to selection
if sel is {} then
display dialog "Bitte zuerst die Belege-Hauptgruppe in DEVONthink auswählen." buttons {"OK"} default button "OK"
return
end if
if (count of sel) is not 1 then
display dialog "Bitte genau eine Gruppe auswählen: die Belege-Hauptgruppe." buttons {"OK"} default button "OK"
return
end if
set belegeRootRecord to item 1 of sel
set belegeRootName to name of belegeRootRecord as rich text
set belegeRootUUID to uuid of belegeRootRecord as rich text
try
set belegeRootType to type of belegeRootRecord as rich text
on error
set belegeRootType to "unbekannt"
end try
set isRootGroup to my isGroupByRecord(belegeRootRecord)
end tell
if isRootGroup is false then
display dialog "Das ausgewählte Objekt ist keine Gruppe." & return & return & ¬
"Name: " & belegeRootName & return & ¬
"Typ: " & belegeRootType & return & return & ¬
"Bitte den Ordner Belege auswählen, bzw. den Ordner, in dem alle Belege und Quittungen liegen." buttons {"OK"} default button "OK"
return
end if
if belegeRootName is not "Belege" then
set confirmChoice to button returned of (display dialog "Die ausgewählte Gruppe heißt nicht Belege, sondern " & belegeRootName & "." & return & return & "Soll diese Gruppe wirklich als Belege-Hauptgruppe in die Config-Datei eingetragen werden?" buttons {"Abbrechen", "Trotzdem verwenden"} default button "Abbrechen")
if confirmChoice is "Abbrechen" then return
end if
-- Jahreszyklus: erst prüfen, was schon da ist, dann passend fragen
set currentYear to my getCurrentYear()
set yearsToCheck to {currentYear}
if my isNovemberOrDecember() then set end of yearsToCheck to (((currentYear as integer) + 1) as text)
set allComplete to true
repeat with y in yearsToCheck
if my yearCycleStatus(belegeRootRecord, belegeRootName, y as text) is not "complete" then set allComplete to false
end repeat
set currentYearStatus to my yearCycleStatus(belegeRootRecord, belegeRootName, currentYear)
if allComplete then
-- Alles vorhanden: keine Frage nötig. Der Zyklus läuft trotzdem
-- (still), damit die Loggruppe mit dem gewählten Namen existiert.
set yearCycleChoice to "Ja"
else if currentYearStatus is "missing" then
set yearCycleChoice to button returned of (display dialog "Soll ein Ordner Belege mit Jahresordner und Monatszyklen angelegt werden?" buttons {"Nein", "Ja"} default button "Ja" with title "Belegversand " & appVersion & " – Einrichtung")
else
set yearCycleChoice to button returned of (display dialog "Jahresordner " & currentYear & " ist bereits vorhanden." & return & return & ¬
"Vorhandene Monatsordner bleiben unverändert." & return & ¬
"Fehlende Monatsordner werden ergänzt." buttons {"Überspringen", "Ergänzen"} default button "Ergänzen" with title "Belegversand " & appVersion & " – Einrichtung")
if yearCycleChoice is "Ergänzen" then
set yearCycleChoice to "Ja"
else
set yearCycleChoice to "Nein"
end if
end if
set dbRootRecord to my getTopParentGroup(belegeRootRecord)
tell application id "DNtp"
set dbRootName to name of dbRootRecord as rich text
end tell
-- ─────────────────────────────────────────────
-- Benutzer-Einstellungen abfragen
-- ─────────────────────────────────────────────
set mailToDefault to text returned of (display dialog "Standard-Empfängeradresse:" default answer defaultMailTo)
set mailCcToDefault to text returned of (display dialog "Standard-CC-Adresse (optional):" default answer "")
set subjectPrefix to text returned of (display dialog "Standard-Betreff-Präfix:" default answer defaultSubjectPrefix)
set zipChoice to button returned of (display dialog "Wie sollen die Belege verschickt werden?" & return & return & ¬
"ZIP-Archiv: alle Belege in einem (optional passwortgeschützten) ZIP." & return & return & ¬
"Einzeldateien: jeder Beleg als eigener Anhang – z.B. für DATEV." & return & return & ¬
"(Später jederzeit in der belegversand.conf über ZIP_ARCHIVE änderbar.)" buttons {"Einzeldateien", "ZIP-Archiv"} default button "ZIP-Archiv")
if zipChoice is "ZIP-Archiv" then
set zipArchiveValue to "true"
set zipPasswordDefault to text returned of (display dialog "Standard-ZIP-Passwort (optional):" default answer defaultZipPassword with hidden answer)
else
set zipArchiveValue to "false"
set zipPasswordDefault to ""
end if
set logSentGroupName to text returned of (display dialog "Name der Versand-Loggruppe:" default answer defaultLogSentGroupName)
set dbgChoice to button returned of (display dialog "Debug-Logging standardmäßig aktivieren?" buttons {"Nein", "Ja"} default button "Nein")
if dbgChoice is "Ja" then
set writeDebugLogValue to "true"
else
set writeDebugLogValue to "false"
end if
set showReadmeValue to defaultShowReadme
set mailBodyFolderName to defaultMailBodyFolderName
set mailBodyRecordName to defaultMailBodyRecordName
set sentTagForDocs to defaultSentTagForDocs
set dryRunPreviewPath to defaultDryRunPreviewPath
set maxMailSizeMB to defaultMaxMailSizeMB
set supportedFileTypes to defaultSupportedFileTypes
-- ─────────────────────────────────────────────
-- Jahres-/Monatsstruktur (erst jetzt, damit der
-- gewählte Loggruppen-Name verwendet werden kann)
-- ─────────────────────────────────────────────
if yearCycleChoice is "Ja" then
-- Heißt die gewählte Gruppe schon "Belege", direkt verwenden –
-- sonst entstünde Belege/Belege.
if belegeRootName is "Belege" then
set belegeGroup to belegeRootRecord
else
set belegeGroup to my getOrCreateChildGroup(belegeRootRecord, "Belege")
end if
tell application id "DNtp"
set belegeRootUUID to uuid of belegeGroup as rich text
set belegeRootName to name of belegeGroup as rich text
end tell
set currentYear to my getCurrentYear()
my createYearCycleInGroup(belegeGroup, currentYear, logSentGroupName)
if my isNovemberOrDecember() then
set nextYear to (currentYear as integer) + 1
my createYearCycleInGroup(belegeGroup, (nextYear as text), logSentGroupName)
end if
end if
-- ─────────────────────────────────────────────
-- _config-Struktur anlegen und Dateien schreiben
-- ─────────────────────────────────────────────
set configGroupRecord to my getOrCreateChildGroup(dbRootRecord, configGroupName)
set readmeRecord to my getOrCreateMarkdownRecord(configGroupRecord, readmeRecordName, my buildReadmeText())
set mailBodyFolderRecord to my getOrCreateChildGroup(configGroupRecord, mailBodyFolderName)
set mailBodyRecord to my getOrCreateTextRecord(mailBodyFolderRecord, mailBodyRecordName, my buildDefaultMailBodyText())
set outsiderLogGroupRecord to my getOrCreateChildGroup(dbRootRecord, "Versendet_Outsider")
set originalsGroupRecord to my getOrCreateChildGroup(dbRootRecord, originalsGroupName)
tell application id "DNtp"
set readmeUUID to uuid of readmeRecord as rich text
set outsiderLogGroupUUID to uuid of outsiderLogGroupRecord as rich text
end tell
set configRecord to my getOrCreateTextRecord(configGroupRecord, configRecordName, "")
set configText to my buildConfigText(mailToDefault, mailCcToDefault, subjectPrefix, zipArchiveValue, zipPasswordDefault, maxMailSizeMB, supportedFileTypes, logSentGroupName, belegeRootUUID, writeDebugLogValue, showReadmeValue, readmeUUID, mailBodyFolderName, mailBodyRecordName, sentTagForDocs, dryRunPreviewPath)
tell application id "DNtp"
try
set content of configRecord to configText
on error
set plain text of configRecord to configText
end try
try
set content of readmeRecord to my buildReadmeText()
on error
set plain text of readmeRecord to my buildReadmeText()
end try
try
set content of mailBodyRecord to my buildDefaultMailBodyText()
on error
set plain text of mailBodyRecord to my buildDefaultMailBodyText()
end try
set configUUID to uuid of configRecord as rich text
set configGroupUUID to uuid of configGroupRecord as rich text
set mailBodyFolderUUID to uuid of mailBodyFolderRecord as rich text
set mailBodyUUID to uuid of mailBodyRecord as rich text
end tell
if writeDebugLogValue is "true" then
set debugInfoText to return & return & "Debug-Log: /tmp/DT-Belegversand-debug.log"
else
set debugInfoText to ""
end if
if zipArchiveValue is "true" then
set modeInfoText to "ZIP-Archiv"
else
set modeInfoText to "Einzeldateien"
end if
display dialog "Belegversand wurde eingerichtet." & return & return & ¬
"Datenbank-Wurzel: " & dbRootName & return & ¬
"Belege-Gruppe: " & belegeRootName & return & ¬
"BELEGE_ROOT_UUID: " & belegeRootUUID & return & return & ¬
"Versandmodus: " & modeInfoText & return & ¬
"Max. Mailgröße: " & maxMailSizeMB & " MB" & return & ¬
"Erlaubte Dateitypen: " & supportedFileTypes & return & return & ¬
"Config-Gruppe: " & configGroupName & return & ¬
"CONFIG_GROUP_UUID: " & configGroupUUID & return & ¬
"Config-Datei: " & configRecordName & return & ¬
"CONFIG_RECORD_UUID: " & configUUID & return & return & ¬
"README-Datei: " & readmeRecordName & return & ¬
"README_UUID: " & readmeUUID & return & return & ¬
"Mailbody-Ordner: " & mailBodyFolderName & return & ¬
"MAILBODY_FOLDER_UUID: " & mailBodyFolderUUID & return & return & ¬
"Mailbody-Datei: " & mailBodyRecordName & return & ¬
"MAILBODY_UUID: " & mailBodyUUID & return & return & ¬
"Globale Log-Gruppe (Outsider): Versendet_Outsider" & return & ¬
"OUTSIDER_LOG_UUID: " & outsiderLogGroupUUID & return & return & ¬
"Gruppe für Original-Fotos: " & originalsGroupName & debugInfoText buttons {"OK"} default button "OK"
-- ─────────────────────────────────────────────
-- Empfehlung: Smart Rule für Fotos → PDF
-- (lässt sich per AppleScript nicht anlegen)
-- ─────────────────────────────────────────────
display dialog "Empfehlung: Fotos automatisch in PDF umwandeln" & return & return & ¬
"Versendet werden nur PDF- und TIFF-Dateien. Damit Fotos von Quittungen (JPG, HEIC, PNG) gar nicht erst stören, kann DEVONthink sie beim Import automatisch umwandeln." & return & return & ¬
"Diese Regel lässt sich nicht per Script anlegen – bitte einmalig selbst einrichten:" & return & return & my buildSmartRuleSteps(belegeRootName) & return & ¬
"Tipp: Mit einem einzelnen Test-Foto prüfen, ob danach das PDF bei den Belegen und das Foto in „" & originalsGroupName & "“ liegt." & return & return & ¬
"Diese Anleitung steht auch im README (_config/README-Belegversand)." buttons {"OK"} default button "OK" with title "Belegversand " & appVersion & " – Empfehlung"
end timeout
end run