Initial commit
This commit is contained in:
commit
061ab744be
|
|
@ -0,0 +1 @@
|
||||||
|
gAAAAABpahEZgX1HGkQvRAaZ-87PqNFzt9Wer-07S9N8P2HwoSZB8j5R2rZyKdpdrzdTZ_A5bc2vYKZYVctxF0XyBFA9fad-xNImsD13P5JUdEGEMq64L9k=
|
||||||
|
|
@ -0,0 +1,131 @@
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
*.spec
|
||||||
|
*.manifest
|
||||||
|
*.exe
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
.python-version
|
||||||
|
|
||||||
|
# celery beat schedule file
|
||||||
|
celerybeat-schedule
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# macOS
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Windows
|
||||||
|
Thumbs.db
|
||||||
|
ehthumbs.db
|
||||||
|
Desktop.ini
|
||||||
|
|
||||||
|
# Generierte DBF-Dateien (können sensible Daten enthalten)
|
||||||
|
*.dbf
|
||||||
|
buchungen_*.dbf
|
||||||
|
|
||||||
|
# Backup-Dateien
|
||||||
|
*.bak
|
||||||
|
*.backup
|
||||||
|
|
||||||
|
# Eigene sensible Konfigurationsdateien im Hauptverzeichnis
|
||||||
|
# (Die Beispiele in config/examples/ sind erlaubt)
|
||||||
|
mandanten_config.json
|
||||||
|
identitaeten_*.json
|
||||||
|
projekte_*.json
|
||||||
|
!config/examples/*.json
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 Sebastian Zell
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
@ -0,0 +1,317 @@
|
||||||
|
# CSV zu DBF Konverter für Hausverwaltung
|
||||||
|
|
||||||
|
Ein Python-Tool zur Konvertierung von CSV-Bankauszügen in DBF-Dateien für Hausverwaltungssoftware.
|
||||||
|
|
||||||
|
## 📋 Beschreibung
|
||||||
|
|
||||||
|
Dieses Programm konvertiert CSV-Exporte von Bankauszügen in das DBF-Format (dBase III), das von vielen älteren Hausverwaltungsprogrammen benötigt wird. Es analysiert automatisch Buchungstexte und ordnet diese den entsprechenden Konten, Gegenkonten und Kostenstellen zu.
|
||||||
|
|
||||||
|
### Hauptfunktionen
|
||||||
|
|
||||||
|
- **Automatische Kontenzuordnung**: Erkennt Mieter und Kostenkonten anhand konfigurierbarer Suchbegriffe
|
||||||
|
- **Mehrmandantenfähig**: Unterstützt beliebig viele Mandanten mit eigenen Konfigurationen
|
||||||
|
- **Projektzuordnung**: Automatische Zuordnung zu Kostenstellen/Projekten
|
||||||
|
- **Monatliche Trennung**: Erstellt separate DBF-Dateien pro Monat
|
||||||
|
- **Flexible Filterung**: Auswahl von Monaten und Jahren für die Konvertierung
|
||||||
|
- **GUI und CLI**: Verfügbar als grafische Oberfläche und Kommandozeilenversion
|
||||||
|
|
||||||
|
## 🖥️ Systemanforderungen
|
||||||
|
|
||||||
|
### Python-Version
|
||||||
|
- Python 3.8 oder höher
|
||||||
|
|
||||||
|
### Betriebssysteme
|
||||||
|
- **Linux**: Ubuntu, Debian, Fedora, etc.
|
||||||
|
- **Windows**: Windows 10/11
|
||||||
|
|
||||||
|
### Abhängigkeiten
|
||||||
|
- `tkinter` (für GUI-Version, meist vorinstalliert)
|
||||||
|
- Alle anderen verwendeten Module sind Teil der Python-Standardbibliothek
|
||||||
|
|
||||||
|
## 📦 Installation
|
||||||
|
|
||||||
|
### Linux (Ubuntu/Debian)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Python und tkinter installieren
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install python3 python3-tk
|
||||||
|
|
||||||
|
# Repository klonen
|
||||||
|
git clone https://github.com/IHR-BENUTZERNAME/csv-dbf-converter.git
|
||||||
|
cd csv-dbf-converter
|
||||||
|
|
||||||
|
# Optional: Für Build als Standalone-Executable
|
||||||
|
pip3 install pyinstaller
|
||||||
|
```
|
||||||
|
|
||||||
|
### Windows
|
||||||
|
|
||||||
|
1. Python von [python.org](https://www.python.org/downloads/) herunterladen und installieren
|
||||||
|
- Bei der Installation "Add Python to PATH" aktivieren
|
||||||
|
2. Repository herunterladen oder klonen
|
||||||
|
3. Optional: Für Build als Executable
|
||||||
|
```cmd
|
||||||
|
pip install pyinstaller
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🚀 Nutzung
|
||||||
|
|
||||||
|
### GUI-Version (Empfohlen)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Linux
|
||||||
|
python3 csv_dbf_converter_gui.py
|
||||||
|
|
||||||
|
# Windows
|
||||||
|
python csv_dbf_converter_gui.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Die grafische Oberfläche führt Sie durch folgende Schritte:
|
||||||
|
1. Mandant auswählen
|
||||||
|
2. CSV-Datei auswählen
|
||||||
|
3. Zeitraum (Monate/Jahre) festlegen
|
||||||
|
4. Konvertierung starten
|
||||||
|
|
||||||
|
### CLI-Version (Kommandozeile)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Linux
|
||||||
|
python3 csv_dbf_converter.py
|
||||||
|
|
||||||
|
# Windows
|
||||||
|
python csv_dbf_converter.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Die Kommandozeilenversion führt interaktiv durch den Prozess und zeigt detaillierte Informationen zur Verarbeitung an.
|
||||||
|
|
||||||
|
## ⚙️ Konfiguration
|
||||||
|
|
||||||
|
Das Programm verwendet JSON-Konfigurationsdateien. Beim ersten Start werden Beispieldateien erstellt.
|
||||||
|
|
||||||
|
### Verzeichnisstruktur der Konfigurationsdateien
|
||||||
|
|
||||||
|
```
|
||||||
|
./
|
||||||
|
├── mandanten_config.json # Mandanten-Übersicht
|
||||||
|
├── identitaeten_mieter_XXX.json # Mieter pro Mandant (XXX = Mandantennummer)
|
||||||
|
├── identitaeten_kosten_XXX.json # Kostenkonten pro Mandant
|
||||||
|
└── projekte_XXX.json # Projekte/Kostenstellen pro Mandant
|
||||||
|
```
|
||||||
|
|
||||||
|
### mandanten_config.json
|
||||||
|
|
||||||
|
Definiert die verfügbaren Mandanten:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mandanten": [
|
||||||
|
{
|
||||||
|
"name": "Mein Mandant",
|
||||||
|
"nummer": "001",
|
||||||
|
"konto": "1200",
|
||||||
|
"standard_gegenkonto": "1300",
|
||||||
|
"standard_bank_kto": "1200",
|
||||||
|
"beleg_index": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
| Feld | Beschreibung |
|
||||||
|
|------|--------------|
|
||||||
|
| `name` | Name des Mandanten |
|
||||||
|
| `nummer` | Eindeutige Mandantennummer (3-stellig) |
|
||||||
|
| `konto` | Standard-Bankkonto |
|
||||||
|
| `standard_gegenkonto` | Gegenkonto für nicht zugeordnete Buchungen |
|
||||||
|
| `standard_bank_kto` | Bankkonto für Buchungen |
|
||||||
|
| `beleg_index` | Fortlaufende Belegnummer (wird automatisch erhöht) |
|
||||||
|
|
||||||
|
### identitaeten_mieter_XXX.json
|
||||||
|
|
||||||
|
Definiert Mieter mit Suchbegriffen für automatische Erkennung:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mandant": "Mein Mandant",
|
||||||
|
"mandantennummer": "001",
|
||||||
|
"mieter": [
|
||||||
|
{
|
||||||
|
"suchbegriffe": ["Müller", "Thomas Müller", "MUELLER"],
|
||||||
|
"konto": "10010",
|
||||||
|
"beschreibung": "Thomas Müller"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### identitaeten_kosten_XXX.json
|
||||||
|
|
||||||
|
Definiert Kostenkonten für Ausgaben:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"kostenkonten": [
|
||||||
|
{
|
||||||
|
"gegenkonto": 4002,
|
||||||
|
"bezeichnung": "Be- und Entwässerung",
|
||||||
|
"suchbegriffe": ["Berliner Wasser", "WASSERBETRIEBE"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### projekte_XXX.json
|
||||||
|
|
||||||
|
Definiert Projekte/Kostenstellen:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mandant": "Mein Mandant",
|
||||||
|
"mandantennummer": "001",
|
||||||
|
"default_projekt": "001",
|
||||||
|
"projekte": [
|
||||||
|
{
|
||||||
|
"suchbegriffe": ["Hauptstraße", "Hauptstr"],
|
||||||
|
"kst": "001",
|
||||||
|
"beschreibung": "Objekt Hauptstraße"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📄 CSV-Format
|
||||||
|
|
||||||
|
Das Programm erwartet CSV-Dateien mit Semikolon als Trennzeichen und folgenden Spalten:
|
||||||
|
|
||||||
|
| Erwartete Spalte | Alternative Namen |
|
||||||
|
|------------------|-------------------|
|
||||||
|
| Buchungstag | Datum |
|
||||||
|
| Buchungstext | Verwendungszweck, Text |
|
||||||
|
| Betrag | Amount |
|
||||||
|
|
||||||
|
### Beispiel CSV-Datei
|
||||||
|
|
||||||
|
```csv
|
||||||
|
Buchungstag;Buchungstext;Betrag
|
||||||
|
01.01.2024;Miete Thomas Müller Januar;750,00
|
||||||
|
02.01.2024;Berliner Wasserbetriebe Abschlag;-125,50
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔨 Build (Standalone Executable)
|
||||||
|
|
||||||
|
### Linux
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd scripts
|
||||||
|
chmod +x build_linux.sh
|
||||||
|
./build_linux.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Die ausführbare Datei befindet sich dann in `dist/csv_dbf_converter_gui`.
|
||||||
|
|
||||||
|
### Windows
|
||||||
|
|
||||||
|
```cmd
|
||||||
|
cd scripts
|
||||||
|
build_windows.bat
|
||||||
|
```
|
||||||
|
|
||||||
|
Die EXE-Datei befindet sich dann in `dist\CSV_DBF_Konverter.exe`.
|
||||||
|
|
||||||
|
## 📁 Projektstruktur
|
||||||
|
|
||||||
|
```
|
||||||
|
csv-dbf-converter/
|
||||||
|
├── csv_dbf_converter.py # CLI-Version (Hauptprogramm)
|
||||||
|
├── csv_dbf_converter_gui.py # GUI-Version mit tkinter
|
||||||
|
├── config/
|
||||||
|
│ └── examples/ # Beispiel-Konfigurationsdateien
|
||||||
|
│ ├── mandanten_config.json
|
||||||
|
│ ├── identitaeten_mieter_*.json
|
||||||
|
│ ├── identitaeten_kosten_*.json
|
||||||
|
│ └── projekte_*.json
|
||||||
|
├── scripts/
|
||||||
|
│ ├── build_linux.sh # Linux Build-Script
|
||||||
|
│ └── build_windows.bat # Windows Build-Script
|
||||||
|
├── requirements.txt # Python-Abhängigkeiten
|
||||||
|
├── LICENSE # MIT-Lizenz
|
||||||
|
└── README.md # Diese Datei
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔍 Funktionsweise
|
||||||
|
|
||||||
|
### Zuordnungslogik
|
||||||
|
|
||||||
|
1. **Mieternummer-Parsing**: Das Programm sucht zunächst nach 5-stelligen Zahlen beginnend mit "1" im Buchungstext, die auf ein Mieterkonto verweisen könnten.
|
||||||
|
|
||||||
|
2. **Suchbegriff-Matching**: Falls keine Mieternummer gefunden wird, werden die konfigurierten Suchbegriffe geprüft:
|
||||||
|
- Zuerst Mieter-Suchbegriffe
|
||||||
|
- Dann Kostenkonten-Suchbegriffe
|
||||||
|
|
||||||
|
3. **Projekt-Zuordnung**: Bei Kostenkonten wird zusätzlich nach Projekt-Suchbegriffen gesucht. Falls kein Projekt gefunden wird, wird das Default-Projekt verwendet.
|
||||||
|
|
||||||
|
### Text-Normalisierung
|
||||||
|
|
||||||
|
Für den Vergleich werden Texte normalisiert:
|
||||||
|
- Kleinschreibung
|
||||||
|
- Umlaute: ü→ue, ö→oe, ä→ae, ß→ss
|
||||||
|
|
||||||
|
## 📝 Beispiele
|
||||||
|
|
||||||
|
### Beispiel-Workflow
|
||||||
|
|
||||||
|
1. **Mandanten konfigurieren** in `mandanten_config.json`
|
||||||
|
2. **Mieter anlegen** in `identitaeten_mieter_001.json`
|
||||||
|
3. **Kostenkonten anlegen** in `identitaeten_kosten_001.json`
|
||||||
|
4. **CSV-Datei von Bank exportieren**
|
||||||
|
5. **Konverter starten** und CSV-Datei auswählen
|
||||||
|
6. **Monate/Jahre auswählen** für die Konvertierung
|
||||||
|
7. **DBF-Dateien** werden pro Monat erstellt
|
||||||
|
|
||||||
|
### Ausgabe-Dateien
|
||||||
|
|
||||||
|
Die generierten DBF-Dateien folgen dem Namensschema:
|
||||||
|
```
|
||||||
|
buchungen_[MANDANTENNR]_[MONAT][JAHR_KURZ].dbf
|
||||||
|
```
|
||||||
|
|
||||||
|
Beispiel: `buchungen_001_0124.dbf` (Mandant 001, Januar 2024)
|
||||||
|
|
||||||
|
## 🐛 Fehlerbehebung
|
||||||
|
|
||||||
|
### Häufige Probleme
|
||||||
|
|
||||||
|
**"tkinter nicht gefunden"**
|
||||||
|
```bash
|
||||||
|
# Ubuntu/Debian
|
||||||
|
sudo apt install python3-tk
|
||||||
|
|
||||||
|
# Fedora
|
||||||
|
sudo dnf install python3-tkinter
|
||||||
|
```
|
||||||
|
|
||||||
|
**"CSV-Spalte nicht gefunden"**
|
||||||
|
- Prüfen Sie, ob die CSV-Datei Semikolon als Trennzeichen verwendet
|
||||||
|
- Prüfen Sie die Spaltennamen (Buchungstag, Buchungstext, Betrag)
|
||||||
|
|
||||||
|
**"Keine Buchungen für den gewählten Zeitraum"**
|
||||||
|
- Prüfen Sie das Datumsformat in der CSV (DD.MM.YYYY)
|
||||||
|
- Prüfen Sie die gewählten Monate und Jahre
|
||||||
|
|
||||||
|
## 📜 Lizenz
|
||||||
|
|
||||||
|
Dieses Projekt ist unter der MIT-Lizenz lizenziert. Siehe [LICENSE](LICENSE) für Details.
|
||||||
|
|
||||||
|
## 👤 Autor
|
||||||
|
|
||||||
|
**Sebastian Zell**
|
||||||
|
- E-Mail: sebastian.zell@zell-aufmass.de
|
||||||
|
|
||||||
|
## 🤝 Beitragen
|
||||||
|
|
||||||
|
Beiträge sind willkommen! Bitte erstellen Sie einen Pull Request oder öffnen Sie ein Issue für Verbesserungsvorschläge.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Erstellt für die Hausverwaltung mit ❤️ und Python*
|
||||||
|
|
@ -0,0 +1,283 @@
|
||||||
|
{
|
||||||
|
"kostenkonten": [
|
||||||
|
{
|
||||||
|
"gegenkonto": 4001,
|
||||||
|
"bezeichnung": "Sprengwasserzähler",
|
||||||
|
"suchbegriffe": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4002,
|
||||||
|
"bezeichnung": "Be- und Endwässerung",
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Berliner Wasser",
|
||||||
|
"BERLINER WASSERBETRIEBE"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4003,
|
||||||
|
"bezeichnung": "Winterdienst",
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Thomas Hansen"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4004,
|
||||||
|
"bezeichnung": "Strassenreinigung/Müllabfuhr",
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Brs",
|
||||||
|
"Berliner Stadtreinig",
|
||||||
|
"BR Berlin",
|
||||||
|
"bsr"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4005,
|
||||||
|
"bezeichnung": "Hauswart",
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Bundesknappschaft",
|
||||||
|
"VERWALTUNGS-BG,HAMBURG",
|
||||||
|
"Knappschaft",
|
||||||
|
"Gehalt",
|
||||||
|
"Verwaltungs-Berufs",
|
||||||
|
"Hauswart",
|
||||||
|
"Entgeld",
|
||||||
|
"Verwaltungs-bg"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4006,
|
||||||
|
"bezeichnung": "Gartenpflege",
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Erden GmbH"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4007,
|
||||||
|
"bezeichnung": "Gebäudeversicherung",
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Flenker",
|
||||||
|
"Victoria Versicherung",
|
||||||
|
"BASLER",
|
||||||
|
"signal"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4008,
|
||||||
|
"bezeichnung": "Strom",
|
||||||
|
"suchbegriffe": [
|
||||||
|
"VATTENFALL",
|
||||||
|
"Yello",
|
||||||
|
"Bewag"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4009,
|
||||||
|
"bezeichnung": "Waschküche",
|
||||||
|
"suchbegriffe": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4010,
|
||||||
|
"bezeichnung": "Kabelfernsehen",
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Kabel"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4011,
|
||||||
|
"bezeichnung": "Grundsteuer",
|
||||||
|
"suchbegriffe": [
|
||||||
|
"GrSt",
|
||||||
|
"Grundsteuer",
|
||||||
|
"gst"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4012,
|
||||||
|
"bezeichnung": "prüfung technischer Anlagen",
|
||||||
|
"suchbegriffe": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4013,
|
||||||
|
"bezeichnung": "Wartung Lüftungsanlage",
|
||||||
|
"suchbegriffe": [
|
||||||
|
"kliwa"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4014,
|
||||||
|
"bezeichnung": "Kleinmaterial Hausmeister",
|
||||||
|
"suchbegriffe": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4015,
|
||||||
|
"bezeichnung": "Kontoführung",
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Kontofuehrung",
|
||||||
|
"EC-KARTE",
|
||||||
|
"Rechnungsabschluss",
|
||||||
|
"Aufgrund eines technischen Feh",
|
||||||
|
"Sollzinsen",
|
||||||
|
"vodafone",
|
||||||
|
"ney",
|
||||||
|
"Separierung zur Pfaendung",
|
||||||
|
"BENACHRICHTIGUNGSENTGELT",
|
||||||
|
"AUSGABE EINER DEBITKARTE",
|
||||||
|
"rueckgabe ue",
|
||||||
|
"ab dem 01.01"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4016,
|
||||||
|
"bezeichnung": "Kaltwasserablesung",
|
||||||
|
"suchbegriffe": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4017,
|
||||||
|
"bezeichnung": "Grundsteuer individuell",
|
||||||
|
"suchbegriffe": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4018,
|
||||||
|
"bezeichnung": "Aufzugkosten",
|
||||||
|
"suchbegriffe": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4019,
|
||||||
|
"bezeichnung": "Hausreinigung",
|
||||||
|
"suchbegriffe": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4020,
|
||||||
|
"bezeichnung": "Schädlingsbekämpfung",
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Akut Schäd"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4048,
|
||||||
|
"bezeichnung": "Reparaturkosten nicht Umlagefähig",
|
||||||
|
"suchbegriffe": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4049,
|
||||||
|
"bezeichnung": "Werbekosten nicht Umlagefähig",
|
||||||
|
"suchbegriffe": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4050,
|
||||||
|
"bezeichnung": "Heizungswartung",
|
||||||
|
"suchbegriffe": [
|
||||||
|
"H+R",
|
||||||
|
"gegenbauer"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4051,
|
||||||
|
"bezeichnung": "Brennstoffkosten",
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Bama",
|
||||||
|
"Gasag"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4052,
|
||||||
|
"bezeichnung": "Schornsteinfeger",
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Schornsteinf",
|
||||||
|
"BSFM"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4053,
|
||||||
|
"bezeichnung": "Verbrauchsmessung",
|
||||||
|
"suchbegriffe": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4054,
|
||||||
|
"bezeichnung": "Abrechnung",
|
||||||
|
"suchbegriffe": [
|
||||||
|
"KALORIMETA",
|
||||||
|
"Brunata"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4060,
|
||||||
|
"bezeichnung": "Heizkostenabrechnung",
|
||||||
|
"suchbegriffe": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4070,
|
||||||
|
"bezeichnung": "Instandhaltung",
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Fabian Zell Auslage",
|
||||||
|
"Sto AG",
|
||||||
|
"Behrendt Werb",
|
||||||
|
"Hahne",
|
||||||
|
"Rossoll",
|
||||||
|
"Possling",
|
||||||
|
"Schlieper",
|
||||||
|
"HERMINGHAUS",
|
||||||
|
"allmilmö",
|
||||||
|
"Bahag",
|
||||||
|
"nenn",
|
||||||
|
"bito",
|
||||||
|
"Gutmaier",
|
||||||
|
"rossol",
|
||||||
|
"bauhaus",
|
||||||
|
"kravag",
|
||||||
|
"auslage",
|
||||||
|
"würth",
|
||||||
|
"Bergmann",
|
||||||
|
"Öltankservice"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4080,
|
||||||
|
"bezeichnung": "Kosten der Rechtsverfolgung",
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Landeshauptkasse",
|
||||||
|
"Rechtsanwalt",
|
||||||
|
"Kosteneinziehungsstelle",
|
||||||
|
"gericht",
|
||||||
|
"pufahl"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4090,
|
||||||
|
"bezeichnung": "Wohngeld",
|
||||||
|
"suchbegriffe": [
|
||||||
|
"WOHNGELD",
|
||||||
|
"WEG TEGELER",
|
||||||
|
"Weg CONTESSAWEG",
|
||||||
|
"BETA IMMOBILIEN",
|
||||||
|
"ABC",
|
||||||
|
"WEG DERN",
|
||||||
|
"WEG Am Teg",
|
||||||
|
"WEG Bock",
|
||||||
|
"WEG FAHRE",
|
||||||
|
"Kremer",
|
||||||
|
"WEG Neue Bergstr"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4100,
|
||||||
|
"bezeichnung": "Annuitäten",
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Aareal Bank",
|
||||||
|
"Weberbank",
|
||||||
|
"IBB",
|
||||||
|
"DSL",
|
||||||
|
"DG HYP",
|
||||||
|
"Annuität",
|
||||||
|
"INVESTITIONSBANK",
|
||||||
|
"Commerzbank",
|
||||||
|
"Finanzierungskosten",
|
||||||
|
"Darlehen",
|
||||||
|
"Tilgung",
|
||||||
|
"Zinsen",
|
||||||
|
"Weberbank",
|
||||||
|
"Darl"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,225 @@
|
||||||
|
{
|
||||||
|
"kostenkonten": [
|
||||||
|
{
|
||||||
|
"gegenkonto": 4001,
|
||||||
|
"bezeichnung": "Hauswart Nebenkosten",
|
||||||
|
"suchbegriffe": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4002,
|
||||||
|
"bezeichnung": "Be-/Entwässerung Abrechnung",
|
||||||
|
"suchbegriffe": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4003,
|
||||||
|
"bezeichnung": "Winterdienst",
|
||||||
|
"suchbegriffe": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4004,
|
||||||
|
"bezeichnung": "Strassenreinigung/Müllabfuhr",
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Berlin Recycling",
|
||||||
|
"Berliner",
|
||||||
|
"Berliner Stadtreinigungsbetriebe"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4005,
|
||||||
|
"bezeichnung": "Hauswart",
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Fabian",
|
||||||
|
"Fabian Zell"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4006,
|
||||||
|
"bezeichnung": "Gartenpflege",
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Biowork",
|
||||||
|
"Fahrländer Erden",
|
||||||
|
"Fahrländer"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4007,
|
||||||
|
"bezeichnung": "Gebäudeversicherung",
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Signal",
|
||||||
|
"Signal Iduna"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4008,
|
||||||
|
"bezeichnung": "Strom",
|
||||||
|
"suchbegriffe": [
|
||||||
|
"VATTENFALL",
|
||||||
|
"Vattenfall"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4009,
|
||||||
|
"bezeichnung": "Be-/Entwässerung Verbrauch",
|
||||||
|
"suchbegriffe": [
|
||||||
|
"WASSER"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4010,
|
||||||
|
"bezeichnung": "Grundsteuer",
|
||||||
|
"suchbegriffe": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4011,
|
||||||
|
"bezeichnung": "Gartenpflege / Baumbeschnitt",
|
||||||
|
"suchbegriffe": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4012,
|
||||||
|
"bezeichnung": "Prüfung technischer Anlagen",
|
||||||
|
"suchbegriffe": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4013,
|
||||||
|
"bezeichnung": "Fernsehanschluss",
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Vodafone"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4014,
|
||||||
|
"bezeichnung": "Kleinmaterial Hausmeister",
|
||||||
|
"suchbegriffe": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4015,
|
||||||
|
"bezeichnung": "Kontoführung",
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Kontoführung",
|
||||||
|
"Kontofuehrung",
|
||||||
|
"Kontofuhrung",
|
||||||
|
"BENACHRICHTIGUNGSENTGELT",
|
||||||
|
"Rechnungsabschluss Konto",
|
||||||
|
"Benachrich",
|
||||||
|
"Aufgrund eines technischen"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4016,
|
||||||
|
"bezeichnung": "Grundsteuer Gewerbeeinheiten",
|
||||||
|
"suchbegriffe": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4017,
|
||||||
|
"bezeichnung": "Eigentümerhaftpflichtversicherung",
|
||||||
|
"suchbegriffe": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4018,
|
||||||
|
"bezeichnung": "Verwalterhonorare",
|
||||||
|
"suchbegriffe": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4019,
|
||||||
|
"bezeichnung": "Lohnbuchhaltung",
|
||||||
|
"suchbegriffe": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4020,
|
||||||
|
"bezeichnung": "Mahngebühren",
|
||||||
|
"suchbegriffe": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4021,
|
||||||
|
"bezeichnung": "Verlustkonto aus Abrechnung NK 1",
|
||||||
|
"suchbegriffe": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4022,
|
||||||
|
"bezeichnung": "Verlustkonto aus Abrechnung NK 2",
|
||||||
|
"suchbegriffe": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4023,
|
||||||
|
"bezeichnung": "Verlustkonto aus Abrechnung NK 3",
|
||||||
|
"suchbegriffe": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4024,
|
||||||
|
"bezeichnung": "Sprengwasserzähler",
|
||||||
|
"suchbegriffe": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4025,
|
||||||
|
"bezeichnung": "Sonderkosten",
|
||||||
|
"suchbegriffe": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4026,
|
||||||
|
"bezeichnung": "Strom PKW Ladesäule",
|
||||||
|
"suchbegriffe": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4045,
|
||||||
|
"bezeichnung": "Regenrinnenreinigung",
|
||||||
|
"suchbegriffe": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4050,
|
||||||
|
"bezeichnung": "Heizungswartung",
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Gutmaier"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4051,
|
||||||
|
"bezeichnung": "Heizkosten",
|
||||||
|
"suchbegriffe": [
|
||||||
|
"GASAG"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4052,
|
||||||
|
"bezeichnung": "Schornsteinfeger",
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Bsfm"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4053,
|
||||||
|
"bezeichnung": "Verbrauchsmessung",
|
||||||
|
"suchbegriffe": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4054,
|
||||||
|
"bezeichnung": "Abrechnung",
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Ista",
|
||||||
|
"Kalorimeta"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4055,
|
||||||
|
"bezeichnung": "Heizstrom",
|
||||||
|
"suchbegriffe": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4060,
|
||||||
|
"bezeichnung": "Heizkostenabrechnung",
|
||||||
|
"suchbegriffe": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4070,
|
||||||
|
"bezeichnung": "Instandhaltung",
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Kunert Tischlerei",
|
||||||
|
"Kunert"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gegenkonto": 4080,
|
||||||
|
"bezeichnung": "Gericht / Gerichtsvollzieher",
|
||||||
|
"suchbegriffe": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,743 @@
|
||||||
|
{
|
||||||
|
"mandant": "Mustermann GmbH",
|
||||||
|
"mandantennummer": "001",
|
||||||
|
"mieter": [
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Renate Steffensen",
|
||||||
|
"Steffensen",
|
||||||
|
"STEFFENSEN,RENATE"
|
||||||
|
],
|
||||||
|
"konto": "10020",
|
||||||
|
"beschreibung": "Renate Steffensen"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Renate Steffensen",
|
||||||
|
"Steffensen"
|
||||||
|
],
|
||||||
|
"konto": "10030",
|
||||||
|
"beschreibung": "Renate Steffensen"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Konstantinos Papadopoulos",
|
||||||
|
"Papadopoulos",
|
||||||
|
"Papadopoulos",
|
||||||
|
"PAPAD"
|
||||||
|
],
|
||||||
|
"konto": "10035",
|
||||||
|
"beschreibung": "Konstantinos Papadopoulos"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Dorit Werner",
|
||||||
|
"Werner",
|
||||||
|
"Werner",
|
||||||
|
"Werner"
|
||||||
|
],
|
||||||
|
"konto": "10036",
|
||||||
|
"beschreibung": "Dorit Werner"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Marcel Probstmeyer",
|
||||||
|
"Probstmeyer",
|
||||||
|
"Probstmeyer"
|
||||||
|
],
|
||||||
|
"konto": "10039",
|
||||||
|
"beschreibung": "Marcel Probstmeyer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Walter Mindach",
|
||||||
|
"Mindach",
|
||||||
|
"Krieger",
|
||||||
|
"MINDACH, WALTER"
|
||||||
|
],
|
||||||
|
"konto": "10040",
|
||||||
|
"beschreibung": "Walter Mindach"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Fritz Werner",
|
||||||
|
"Werner"
|
||||||
|
],
|
||||||
|
"konto": "10042",
|
||||||
|
"beschreibung": "Fritz Werner"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Marco Mecarelli",
|
||||||
|
"Mecarelli",
|
||||||
|
"Mecarelli"
|
||||||
|
],
|
||||||
|
"konto": "10045",
|
||||||
|
"beschreibung": "Marco Mecarelli"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Marianne Poplawski",
|
||||||
|
"Poplawski",
|
||||||
|
"POPLAWSKI,MARIANNE"
|
||||||
|
],
|
||||||
|
"konto": "10046",
|
||||||
|
"beschreibung": "Marianne Poplawski"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Helga Hartinger",
|
||||||
|
"Hartinger",
|
||||||
|
"Hartinger",
|
||||||
|
"Hartinger"
|
||||||
|
],
|
||||||
|
"konto": "10048",
|
||||||
|
"beschreibung": "Helga Hartinger"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Bärbel Jaehring",
|
||||||
|
"Jaehring",
|
||||||
|
"Krug",
|
||||||
|
"Jaehring",
|
||||||
|
"Krug"
|
||||||
|
],
|
||||||
|
"konto": "10049",
|
||||||
|
"beschreibung": "Bärbel Jaehring"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Günter Van de Weghe",
|
||||||
|
"Van de Weghe",
|
||||||
|
"Van de Weghe",
|
||||||
|
"vande Weghe"
|
||||||
|
],
|
||||||
|
"konto": "10052",
|
||||||
|
"beschreibung": "Günter Van de Weghe"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Manuela Richter",
|
||||||
|
"Richter",
|
||||||
|
"Richter",
|
||||||
|
"Richter"
|
||||||
|
],
|
||||||
|
"konto": "10068",
|
||||||
|
"beschreibung": "Manuela Richter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Heinz Lange",
|
||||||
|
"Lange",
|
||||||
|
"Lange"
|
||||||
|
],
|
||||||
|
"konto": "10072",
|
||||||
|
"beschreibung": "Heinz Lange"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Antje Keck",
|
||||||
|
"Keck",
|
||||||
|
"Kozik",
|
||||||
|
"Keck"
|
||||||
|
],
|
||||||
|
"konto": "10073",
|
||||||
|
"beschreibung": "Antje Keck"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Markus Weichert",
|
||||||
|
"Weichert",
|
||||||
|
"Weichert"
|
||||||
|
],
|
||||||
|
"konto": "10074",
|
||||||
|
"beschreibung": "Markus Weichert"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Monika Ramlow",
|
||||||
|
"Ramlow",
|
||||||
|
"Ramlow"
|
||||||
|
],
|
||||||
|
"konto": "10075",
|
||||||
|
"beschreibung": "Monika Ramlow"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Sarah Clarissa Schroedter",
|
||||||
|
"Schroedter",
|
||||||
|
"Schroedter",
|
||||||
|
"SCHROEDTER",
|
||||||
|
"Wanke"
|
||||||
|
],
|
||||||
|
"konto": "10076",
|
||||||
|
"beschreibung": "Sarah Clarissa Schroedter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Conny-Maren Krause",
|
||||||
|
"Krause",
|
||||||
|
"KRAUSE"
|
||||||
|
],
|
||||||
|
"konto": "10078",
|
||||||
|
"beschreibung": "Conny-Maren Krause"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Kurt Hans Jessel",
|
||||||
|
"Jessel"
|
||||||
|
],
|
||||||
|
"konto": "10079",
|
||||||
|
"beschreibung": "Kurt Hans Jessel"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
" ATC Germany Munich",
|
||||||
|
"ATC Germany Munich",
|
||||||
|
"ehem.E-Plus",
|
||||||
|
"E-PLUS MOBILFUNK GMBH",
|
||||||
|
"ATC"
|
||||||
|
],
|
||||||
|
"konto": "10080",
|
||||||
|
"beschreibung": " ATC Germany Munich"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
" Vantage Towers AG",
|
||||||
|
"Vantage Towers AG",
|
||||||
|
"ehem. Vodafone",
|
||||||
|
"VODAFONE D2 GMBH",
|
||||||
|
"Vantage"
|
||||||
|
],
|
||||||
|
"konto": "10081",
|
||||||
|
"beschreibung": " Vantage Towers AG"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Heidi Thürling",
|
||||||
|
"Thürling",
|
||||||
|
"THURLING",
|
||||||
|
"TH?RLING"
|
||||||
|
],
|
||||||
|
"konto": "10082",
|
||||||
|
"beschreibung": "Heidi Thürling"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Anne Horn",
|
||||||
|
"Horn",
|
||||||
|
"Knust"
|
||||||
|
],
|
||||||
|
"konto": "10083",
|
||||||
|
"beschreibung": "Anne Horn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Irmgard Dupke",
|
||||||
|
"Dupke",
|
||||||
|
"DUPKE"
|
||||||
|
],
|
||||||
|
"konto": "10084",
|
||||||
|
"beschreibung": "Irmgard Dupke"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Manfred Hannasky",
|
||||||
|
"Hannasky",
|
||||||
|
"HANNASKY"
|
||||||
|
],
|
||||||
|
"konto": "10085",
|
||||||
|
"beschreibung": "Manfred Hannasky"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Mandy Jaqueline Gey",
|
||||||
|
"Gey",
|
||||||
|
"Schrammek"
|
||||||
|
],
|
||||||
|
"konto": "10086",
|
||||||
|
"beschreibung": "Mandy Jaqueline Gey"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Alexis-Roman Kruppa",
|
||||||
|
"Kruppa"
|
||||||
|
],
|
||||||
|
"konto": "10087",
|
||||||
|
"beschreibung": "Alexis-Roman Kruppa"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Fuad Vokrri",
|
||||||
|
"Vokrri",
|
||||||
|
"VOKRRI",
|
||||||
|
"FUAD"
|
||||||
|
],
|
||||||
|
"konto": "10088",
|
||||||
|
"beschreibung": "Fuad Vokrri"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Eveline Olsson",
|
||||||
|
"Olsson",
|
||||||
|
"Olsson",
|
||||||
|
"UWE OLSSON"
|
||||||
|
],
|
||||||
|
"konto": "10089",
|
||||||
|
"beschreibung": "Eveline Olsson"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Ingrid Ida Fitzner",
|
||||||
|
"Fitzner",
|
||||||
|
"Fitzner",
|
||||||
|
"Fitzner"
|
||||||
|
],
|
||||||
|
"konto": "10090",
|
||||||
|
"beschreibung": "Ingrid Ida Fitzner"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Sophie Charlotte Wanke",
|
||||||
|
"Wanke",
|
||||||
|
"Borngr"
|
||||||
|
],
|
||||||
|
"konto": "10091",
|
||||||
|
"beschreibung": "Sophie Charlotte Wanke"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Ingrid Ida Fitzner",
|
||||||
|
"Fitzner"
|
||||||
|
],
|
||||||
|
"konto": "10092",
|
||||||
|
"beschreibung": "Ingrid Ida Fitzner"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Tanja Barth",
|
||||||
|
"Barth",
|
||||||
|
"Wilhelm"
|
||||||
|
],
|
||||||
|
"konto": "10093",
|
||||||
|
"beschreibung": "Tanja Barth"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Manuela Richter",
|
||||||
|
"Richter",
|
||||||
|
"Richter"
|
||||||
|
],
|
||||||
|
"konto": "10094",
|
||||||
|
"beschreibung": "Manuela Richter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Irene Bauer",
|
||||||
|
"Bauer",
|
||||||
|
"Bauer"
|
||||||
|
],
|
||||||
|
"konto": "10095",
|
||||||
|
"beschreibung": "Irene Bauer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Bert Kunert",
|
||||||
|
"Kunert",
|
||||||
|
"Kunert"
|
||||||
|
],
|
||||||
|
"konto": "10096",
|
||||||
|
"beschreibung": "Bert Kunert"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Hannelore Dittmann",
|
||||||
|
"Dittmann",
|
||||||
|
"Dittmar"
|
||||||
|
],
|
||||||
|
"konto": "10097",
|
||||||
|
"beschreibung": "Hannelore Dittmann"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Sandra Hedke",
|
||||||
|
"Hedke",
|
||||||
|
"Kahl",
|
||||||
|
"Kahl",
|
||||||
|
"Hedke"
|
||||||
|
],
|
||||||
|
"konto": "10098",
|
||||||
|
"beschreibung": "Sandra Hedke"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Julia Ellinger",
|
||||||
|
"Ellinger",
|
||||||
|
"Ellinger"
|
||||||
|
],
|
||||||
|
"konto": "10099",
|
||||||
|
"beschreibung": "Julia Ellinger"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
" Eins in Christus",
|
||||||
|
"Eins in Christus"
|
||||||
|
],
|
||||||
|
"konto": "10100",
|
||||||
|
"beschreibung": " Eins in Christus"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Andrea-Britta Lüth",
|
||||||
|
"Lüth",
|
||||||
|
"Lüth"
|
||||||
|
],
|
||||||
|
"konto": "10101",
|
||||||
|
"beschreibung": "Andrea-Britta Lüth"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Andreas Holtz",
|
||||||
|
"Holtz"
|
||||||
|
],
|
||||||
|
"konto": "10102",
|
||||||
|
"beschreibung": "Andreas Holtz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Angela Kranz",
|
||||||
|
"Kranz"
|
||||||
|
],
|
||||||
|
"konto": "10103",
|
||||||
|
"beschreibung": "Angela Kranz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Monique Rosenow",
|
||||||
|
"Rosenow"
|
||||||
|
],
|
||||||
|
"konto": "10104",
|
||||||
|
"beschreibung": "Monique Rosenow"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Manfred Robert Posselt",
|
||||||
|
"Posselt",
|
||||||
|
"Posselt"
|
||||||
|
],
|
||||||
|
"konto": "10105",
|
||||||
|
"beschreibung": "Manfred Robert Posselt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Marianne Reimer",
|
||||||
|
"Reimer"
|
||||||
|
],
|
||||||
|
"konto": "10106",
|
||||||
|
"beschreibung": "Marianne Reimer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Jürgen Alois Pichler",
|
||||||
|
"Pichler"
|
||||||
|
],
|
||||||
|
"konto": "10107",
|
||||||
|
"beschreibung": "Jürgen Alois Pichler"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Andreas Daniel Kandt",
|
||||||
|
"Kandt"
|
||||||
|
],
|
||||||
|
"konto": "10108",
|
||||||
|
"beschreibung": "Andreas Daniel Kandt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Heike Günther",
|
||||||
|
"Günther",
|
||||||
|
"Schneider"
|
||||||
|
],
|
||||||
|
"konto": "10109",
|
||||||
|
"beschreibung": "Heike Günther"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Silke Schröder",
|
||||||
|
"Schröder"
|
||||||
|
],
|
||||||
|
"konto": "10110",
|
||||||
|
"beschreibung": "Silke Schröder"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Rotraud Rospert",
|
||||||
|
"Rospert"
|
||||||
|
],
|
||||||
|
"konto": "10111",
|
||||||
|
"beschreibung": "Rotraud Rospert"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Jennifer Weber",
|
||||||
|
"Weber"
|
||||||
|
],
|
||||||
|
"konto": "10112",
|
||||||
|
"beschreibung": "Jennifer Weber"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Joe-Marvin Henson",
|
||||||
|
"Henson",
|
||||||
|
"Henson"
|
||||||
|
],
|
||||||
|
"konto": "10113",
|
||||||
|
"beschreibung": "Joe-Marvin Henson"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Jacqueline Esther De Vries",
|
||||||
|
"De Vries"
|
||||||
|
],
|
||||||
|
"konto": "10114",
|
||||||
|
"beschreibung": "Jacqueline Esther De Vries"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Catrin Zonsius",
|
||||||
|
"Zonsius",
|
||||||
|
"Zonsius"
|
||||||
|
],
|
||||||
|
"konto": "10115",
|
||||||
|
"beschreibung": "Catrin Zonsius"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Charlen Heidergott",
|
||||||
|
"Heidergott",
|
||||||
|
"Brüning"
|
||||||
|
],
|
||||||
|
"konto": "10116",
|
||||||
|
"beschreibung": "Charlen Heidergott"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Jennifer Weber",
|
||||||
|
"Weber, Jennifer",
|
||||||
|
"Winkler"
|
||||||
|
],
|
||||||
|
"konto": "10117",
|
||||||
|
"beschreibung": "Jennifer Weber"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Gerrit Küstermann",
|
||||||
|
"Küstermann"
|
||||||
|
],
|
||||||
|
"konto": "10118",
|
||||||
|
"beschreibung": "Gerrit Küstermann"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Sofya Iluchshenko",
|
||||||
|
"Iluchshenko"
|
||||||
|
],
|
||||||
|
"konto": "10119",
|
||||||
|
"beschreibung": "Sofya Iluchshenko"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Sarah Ketzer",
|
||||||
|
"Ketzer",
|
||||||
|
"Malkowski"
|
||||||
|
],
|
||||||
|
"konto": "10121",
|
||||||
|
"beschreibung": "Sarah Ketzer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Fuad Vokrri",
|
||||||
|
"Vokrri"
|
||||||
|
],
|
||||||
|
"konto": "10122",
|
||||||
|
"beschreibung": "Fuad Vokrri"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Jenny Binder",
|
||||||
|
"Binder",
|
||||||
|
"Paul"
|
||||||
|
],
|
||||||
|
"konto": "10123",
|
||||||
|
"beschreibung": "Jenny Binder"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Anton Glaser",
|
||||||
|
"Glaser"
|
||||||
|
],
|
||||||
|
"konto": "10124",
|
||||||
|
"beschreibung": "Anton Glaser"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Kathrin Hoyer",
|
||||||
|
"Hoyer",
|
||||||
|
"Starosta"
|
||||||
|
],
|
||||||
|
"konto": "10125",
|
||||||
|
"beschreibung": "Kathrin Hoyer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Tom Stampfuss",
|
||||||
|
"Stampfuss",
|
||||||
|
"Höne",
|
||||||
|
"Hone"
|
||||||
|
],
|
||||||
|
"konto": "10126",
|
||||||
|
"beschreibung": "Tom Stampfuss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Artem Poltavets",
|
||||||
|
"Poltavets"
|
||||||
|
],
|
||||||
|
"konto": "10127",
|
||||||
|
"beschreibung": "Artem Poltavets"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Leon Maximilian Posselt"
|
||||||
|
],
|
||||||
|
"konto": "10128",
|
||||||
|
"beschreibung": "Leon Maximilian Posselt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Brigitte Moskal",
|
||||||
|
"Moskal"
|
||||||
|
],
|
||||||
|
"konto": "10129",
|
||||||
|
"beschreibung": "Brigitte Moskal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Oksana Roman",
|
||||||
|
"Roman"
|
||||||
|
],
|
||||||
|
"konto": "10130",
|
||||||
|
"beschreibung": "Oksana Roman"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Maryam Zinoury",
|
||||||
|
"Zinoury"
|
||||||
|
],
|
||||||
|
"konto": "10131",
|
||||||
|
"beschreibung": "Maryam Zinoury"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Julia Ellinger",
|
||||||
|
"Ellinger"
|
||||||
|
],
|
||||||
|
"konto": "10132",
|
||||||
|
"beschreibung": "Julia Ellinger"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Phillipp Weber",
|
||||||
|
"Weber, Phillipp"
|
||||||
|
],
|
||||||
|
"konto": "10133",
|
||||||
|
"beschreibung": "Phillipp Weber"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Artem Poltavets",
|
||||||
|
"Poltavets"
|
||||||
|
],
|
||||||
|
"konto": "10134",
|
||||||
|
"beschreibung": "Artem Poltavets"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"C.U.T",
|
||||||
|
"kwe"
|
||||||
|
],
|
||||||
|
"konto": "40000",
|
||||||
|
"beschreibung": "C.U.T"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"ZV Wasser",
|
||||||
|
"SIBA",
|
||||||
|
"Kommunales Wirt",
|
||||||
|
"LOS-KWU",
|
||||||
|
"Scharmützelsee",
|
||||||
|
"Scharmutzelsee",
|
||||||
|
"Ewe",
|
||||||
|
"E.dis",
|
||||||
|
"CUT",
|
||||||
|
"Hopp",
|
||||||
|
"HELIOS",
|
||||||
|
"Ingrid Und Klaus Zell",
|
||||||
|
"Jabado, Samir Andreas",
|
||||||
|
"Ingrid Zell Sparkasse",
|
||||||
|
"Dr Jochen Küller",
|
||||||
|
"GEZ",
|
||||||
|
"KLAUS ZELL SPARKASSE",
|
||||||
|
"INGRID ZELL SPARKASSE"
|
||||||
|
],
|
||||||
|
"konto": "40000",
|
||||||
|
"beschreibung": "Ingrid Und Klaus Zell"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"WEG Elster"
|
||||||
|
],
|
||||||
|
"konto": "40001",
|
||||||
|
"beschreibung": "WEG Elster"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"INGRID ZELL HAUSVERW",
|
||||||
|
"Ingrid Zell GmbH"
|
||||||
|
],
|
||||||
|
"konto": "40009",
|
||||||
|
"beschreibung": "INGRID ZELL HAUSVERW"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"FABIAN ZELL FIRMA",
|
||||||
|
"fabian Zell COBA"
|
||||||
|
],
|
||||||
|
"konto": "40011",
|
||||||
|
"beschreibung": "FABIAN ZELL FIRMA"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"SEBASTIAN ZELL bekannt",
|
||||||
|
"Sebastian Zell COBA",
|
||||||
|
"SEBASTIAN ZELL e.k."
|
||||||
|
],
|
||||||
|
"konto": "40006",
|
||||||
|
"beschreibung": "Sebastian Zell"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"paypal"
|
||||||
|
],
|
||||||
|
"konto": "40020",
|
||||||
|
"beschreibung": "Paypal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"finanza"
|
||||||
|
],
|
||||||
|
"konto": "40030",
|
||||||
|
"beschreibung": "Finanzamt"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"mandant": "WEG Contessaweg",
|
||||||
|
"mandantennummer": "007",
|
||||||
|
"mieter": [
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"mueller",
|
||||||
|
"müller",
|
||||||
|
"carsten"
|
||||||
|
],
|
||||||
|
"konto": "20017",
|
||||||
|
"beschreibung": "Carsten Müller"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"knust",
|
||||||
|
"horn"
|
||||||
|
],
|
||||||
|
"konto": "20019",
|
||||||
|
"beschreibung": "Ulrich Knust, Anne Horn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"zell"
|
||||||
|
],
|
||||||
|
"konto": "20018",
|
||||||
|
"beschreibung": "Fabian und Sebastian Zell"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"mandanten": [
|
||||||
|
{
|
||||||
|
"name": "Fabian und Sebastian Zell",
|
||||||
|
"nummer": "001",
|
||||||
|
"konto": "1200",
|
||||||
|
"standard_gegenkonto": "1300",
|
||||||
|
"standard_bank_kto": "1200",
|
||||||
|
"beleg_index": 2826
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Ingrid und Klaus Zell",
|
||||||
|
"nummer": "002",
|
||||||
|
"konto": "1800",
|
||||||
|
"standard_gegenkonto": "8500",
|
||||||
|
"standard_bank_kto": "1800",
|
||||||
|
"beleg_index": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "WEG Contessaweg",
|
||||||
|
"nummer": "007",
|
||||||
|
"konto": "1208",
|
||||||
|
"standard_gegenkonto": "1590",
|
||||||
|
"standard_bank_kto": "1208",
|
||||||
|
"beleg_index": 517
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
{
|
||||||
|
"mandant": "default",
|
||||||
|
"mandantennummer": "001",
|
||||||
|
"default_projekt": "001",
|
||||||
|
"projekte": [
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Con"
|
||||||
|
],
|
||||||
|
"kst": "001",
|
||||||
|
"beschreibung": "Con"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Schr"
|
||||||
|
],
|
||||||
|
"kst": "002",
|
||||||
|
"beschreibung": "Schr"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Mar"
|
||||||
|
],
|
||||||
|
"kst": "003",
|
||||||
|
"beschreibung": "Mar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Spa"
|
||||||
|
],
|
||||||
|
"kst": "004",
|
||||||
|
"beschreibung": "Spa"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Gatow",
|
||||||
|
"abc"
|
||||||
|
],
|
||||||
|
"kst": "005",
|
||||||
|
"beschreibung": "Gatow"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Burg",
|
||||||
|
"kremer"
|
||||||
|
],
|
||||||
|
"kst": "006",
|
||||||
|
"beschreibung": "Burg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Teg",
|
||||||
|
"beta"
|
||||||
|
],
|
||||||
|
"kst": "007",
|
||||||
|
"beschreibung": "Teg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Fah"
|
||||||
|
],
|
||||||
|
"kst": "008",
|
||||||
|
"beschreibung": "Fah"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Pich"
|
||||||
|
],
|
||||||
|
"kst": "009",
|
||||||
|
"beschreibung": "Pich"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"mandant": "default",
|
||||||
|
"mandantennummer": "001",
|
||||||
|
"default_projekt": "001",
|
||||||
|
"projekte": [
|
||||||
|
{
|
||||||
|
"suchbegriffe": [
|
||||||
|
"Con",
|
||||||
|
" "
|
||||||
|
],
|
||||||
|
"kst": "3",
|
||||||
|
"beschreibung": "Con"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,677 @@
|
||||||
|
import json
|
||||||
|
import csv
|
||||||
|
import os
|
||||||
|
import struct
|
||||||
|
from datetime import datetime, date
|
||||||
|
|
||||||
|
# ===== CONFIG DATEIEN ERSTELLEN/LADEN =====
|
||||||
|
|
||||||
|
def create_default_configs():
|
||||||
|
"""Erstellt Standard-Config-Dateien falls nicht vorhanden"""
|
||||||
|
|
||||||
|
# Mandanten Config
|
||||||
|
if not os.path.exists('mandanten_config.json'):
|
||||||
|
mandanten_config = {
|
||||||
|
"mandanten": [
|
||||||
|
{
|
||||||
|
"name": "Mustermann GmbH",
|
||||||
|
"nummer": "001",
|
||||||
|
"konto": "1200",
|
||||||
|
"standard_gegenkonto": "8400",
|
||||||
|
"beleg_index": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Beispiel AG",
|
||||||
|
"nummer": "002",
|
||||||
|
"konto": "1800",
|
||||||
|
"standard_gegenkonto": "8500",
|
||||||
|
"beleg_index": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
with open('mandanten_config.json', 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(mandanten_config, f, indent=4, ensure_ascii=False)
|
||||||
|
print("✓ mandanten_config.json erstellt")
|
||||||
|
|
||||||
|
# Prüfe ob Identitäten-Dateien für jeden Mandanten existieren
|
||||||
|
mandanten = load_mandanten()
|
||||||
|
for mandant in mandanten:
|
||||||
|
# Mieter-Identitäten
|
||||||
|
mieter_datei = f"identitaeten_mieter_{mandant['nummer']}.json"
|
||||||
|
if not os.path.exists(mieter_datei):
|
||||||
|
mieter_config = {
|
||||||
|
"mandant": mandant['name'],
|
||||||
|
"mandantennummer": mandant['nummer'],
|
||||||
|
"mieter": [
|
||||||
|
{
|
||||||
|
"suchbegriffe": ["Thomas Müller", "Müller", "T. Müller"],
|
||||||
|
"konto": "1210",
|
||||||
|
"beschreibung": "Miete Thomas Müller"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": ["Schmidt", "Anna Schmidt", "A. Schmidt"],
|
||||||
|
"konto": "1220",
|
||||||
|
"beschreibung": "Miete Anna Schmidt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": ["Weber GmbH", "Weber"],
|
||||||
|
"konto": "1230",
|
||||||
|
"beschreibung": "Miete Weber GmbH"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
with open(mieter_datei, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(mieter_config, f, indent=4, ensure_ascii=False)
|
||||||
|
print(f"✓ {mieter_datei} erstellt")
|
||||||
|
|
||||||
|
# Kostenkonten-Identitäten
|
||||||
|
kosten_datei = f"identitaeten_kosten_{mandant['nummer']}.json"
|
||||||
|
if not os.path.exists(kosten_datei):
|
||||||
|
kosten_config = {
|
||||||
|
"mandant": mandant['name'],
|
||||||
|
"mandantennummer": mandant['nummer'],
|
||||||
|
"kostenkonten": [
|
||||||
|
{
|
||||||
|
"suchbegriffe": ["Wasser", "Wasserbetriebe", "Berliner Wasserbetriebe"],
|
||||||
|
"gegenkonto": "6300",
|
||||||
|
"beschreibung": "Wasserkosten"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": ["Strom", "Vattenfall", "Stromversorgung"],
|
||||||
|
"gegenkonto": "6310",
|
||||||
|
"beschreibung": "Stromkosten"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": ["Gas", "Gasag", "Gasversorgung"],
|
||||||
|
"gegenkonto": "6320",
|
||||||
|
"beschreibung": "Gaskosten"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": ["Versicherung", "Allianz", "Gebäudeversicherung"],
|
||||||
|
"gegenkonto": "6500",
|
||||||
|
"beschreibung": "Versicherung"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": ["Hausverwaltung", "Verwaltung", "Verwaltungsgebühr"],
|
||||||
|
"gegenkonto": "6700",
|
||||||
|
"beschreibung": "Hausverwaltungskosten"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
with open(kosten_datei, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(kosten_config, f, indent=4, ensure_ascii=False)
|
||||||
|
print(f"✓ {kosten_datei} erstellt")
|
||||||
|
|
||||||
|
# Projekte-Datei
|
||||||
|
projekte_datei = f"projekte_{mandant['nummer']}.json"
|
||||||
|
if not os.path.exists(projekte_datei):
|
||||||
|
projekte_config = {
|
||||||
|
"mandant": mandant['name'],
|
||||||
|
"mandantennummer": mandant['nummer'],
|
||||||
|
"default_projekt": "ALLG001",
|
||||||
|
"projekte": [
|
||||||
|
{
|
||||||
|
"suchbegriffe": ["Projekt Alpha", "Alpha"],
|
||||||
|
"kst": "PRJ001",
|
||||||
|
"beschreibung": "Projekt Alpha"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": ["Hausverwaltung", "HV2024"],
|
||||||
|
"kst": "HV2024",
|
||||||
|
"beschreibung": "Hausverwaltung 2024"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"suchbegriffe": ["Musterstraße 10", "Musterstr. 10"],
|
||||||
|
"kst": "HAUS01",
|
||||||
|
"beschreibung": "Musterstraße 10"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
with open(projekte_datei, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(projekte_config, f, indent=4, ensure_ascii=False)
|
||||||
|
print(f"✓ {projekte_datei} erstellt")
|
||||||
|
|
||||||
|
def load_mandanten():
|
||||||
|
"""Lädt Mandanten aus Config"""
|
||||||
|
with open('mandanten_config.json', 'r', encoding='utf-8') as f:
|
||||||
|
return json.load(f)['mandanten']
|
||||||
|
|
||||||
|
def save_mandanten(mandanten):
|
||||||
|
"""Speichert Mandanten in Config"""
|
||||||
|
with open('mandanten_config.json', 'w', encoding='utf-8') as f:
|
||||||
|
json.dump({"mandanten": mandanten}, f, indent=4, ensure_ascii=False)
|
||||||
|
|
||||||
|
def load_mieter(mandantennummer):
|
||||||
|
"""Lädt Mieter für einen bestimmten Mandanten"""
|
||||||
|
mieter_datei = f"identitaeten_mieter_{mandantennummer}.json"
|
||||||
|
with open(mieter_datei, 'r', encoding='utf-8') as f:
|
||||||
|
return json.load(f)['mieter']
|
||||||
|
|
||||||
|
def load_kostenkonten(mandantennummer):
|
||||||
|
"""Lädt Kostenkonten für einen bestimmten Mandanten"""
|
||||||
|
kosten_datei = f"identitaeten_kosten_{mandantennummer}.json"
|
||||||
|
with open(kosten_datei, 'r', encoding='utf-8') as f:
|
||||||
|
return json.load(f)['kostenkonten']
|
||||||
|
|
||||||
|
def load_projekte(mandantennummer):
|
||||||
|
"""Lädt Projekte/Kostenstellen für einen bestimmten Mandanten"""
|
||||||
|
projekte_datei = f"projekte_{mandantennummer}.json"
|
||||||
|
with open(projekte_datei, 'r', encoding='utf-8') as f:
|
||||||
|
config = json.load(f)
|
||||||
|
return config['projekte'], config.get('default_projekt', '')
|
||||||
|
|
||||||
|
def normalize_text(text):
|
||||||
|
"""
|
||||||
|
Normalisiert Text für Vergleiche:
|
||||||
|
- Kleinbuchstaben
|
||||||
|
- ü → ue, ö → oe, ä → ae, ß → ss
|
||||||
|
"""
|
||||||
|
text = text.lower()
|
||||||
|
replacements = {
|
||||||
|
'ü': 'ue', 'ö': 'oe', 'ä': 'ae', 'ß': 'ss',
|
||||||
|
'é': 'e', 'è': 'e', 'ê': 'e', 'à': 'a', 'â': 'a'
|
||||||
|
}
|
||||||
|
for old, new in replacements.items():
|
||||||
|
text = text.replace(old, new)
|
||||||
|
return text
|
||||||
|
|
||||||
|
# ===== PARSING LOGIK =====
|
||||||
|
|
||||||
|
def parse_mieter(buchungstext, mieter):
|
||||||
|
"""
|
||||||
|
Sucht nach Mietern im Buchungstext (OHNE Projektnummer)
|
||||||
|
Returns: (konto, beschreibung) oder (None, None)
|
||||||
|
"""
|
||||||
|
buchungstext_norm = normalize_text(buchungstext)
|
||||||
|
|
||||||
|
for mieter_entry in mieter:
|
||||||
|
for suchbegriff in mieter_entry['suchbegriffe']:
|
||||||
|
suchbegriff_norm = normalize_text(suchbegriff)
|
||||||
|
if suchbegriff_norm in buchungstext_norm:
|
||||||
|
print(f" → Mieter gefunden: '{suchbegriff}' → Konto {mieter_entry['konto']}")
|
||||||
|
return mieter_entry['konto'], mieter_entry.get('beschreibung', '')
|
||||||
|
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
def parse_kostenkonten(buchungstext, kostenkonten):
|
||||||
|
"""
|
||||||
|
Sucht nach Kostenkonten im Buchungstext
|
||||||
|
Returns: (gegenkonto, beschreibung) oder (None, None)
|
||||||
|
"""
|
||||||
|
buchungstext_norm = normalize_text(buchungstext)
|
||||||
|
|
||||||
|
for kosten_entry in kostenkonten:
|
||||||
|
for suchbegriff in kosten_entry['suchbegriffe']:
|
||||||
|
suchbegriff_norm = normalize_text(suchbegriff)
|
||||||
|
if suchbegriff_norm in buchungstext_norm:
|
||||||
|
print(f" → Kostenkonto gefunden: '{suchbegriff}' → Gegenkonto {kosten_entry['gegenkonto']}")
|
||||||
|
return kosten_entry['gegenkonto'], kosten_entry.get('beschreibung', '')
|
||||||
|
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
def parse_projekt(buchungstext, projekte, default_projekt):
|
||||||
|
"""
|
||||||
|
Sucht nach Projekten im Buchungstext
|
||||||
|
Wird NUR aufgerufen wenn Kostenkonto gefunden wurde!
|
||||||
|
Returns: kostenstelle
|
||||||
|
"""
|
||||||
|
buchungstext_norm = normalize_text(buchungstext)
|
||||||
|
|
||||||
|
for projekt in projekte:
|
||||||
|
for suchbegriff in projekt['suchbegriffe']:
|
||||||
|
suchbegriff_norm = normalize_text(suchbegriff)
|
||||||
|
if suchbegriff_norm in buchungstext_norm:
|
||||||
|
print(f" → Projekt gefunden: '{suchbegriff}' → KST {projekt['kst']}")
|
||||||
|
return projekt['kst']
|
||||||
|
|
||||||
|
# Kein Projekt gefunden → Default verwenden
|
||||||
|
if default_projekt:
|
||||||
|
print(f" → Kein Projekt gefunden → Default-KST {default_projekt}")
|
||||||
|
return default_projekt
|
||||||
|
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def bestimme_konten_und_kst(buchungstext, mandant, mieter, kostenkonten, projekte, default_projekt):
|
||||||
|
"""
|
||||||
|
Bestimmt Konten, Kostenstelle und Beschreibung basierend auf dem Buchungstext.
|
||||||
|
Mit Mieternummer-Parsing für Konten beginnend mit "1".
|
||||||
|
"""
|
||||||
|
norm_buchungstext = normalize_text(buchungstext)
|
||||||
|
|
||||||
|
# --- TEIL 0: ZUERST NACH MIETERNUMMER SUCHEN (nur für Konten mit "1") ---
|
||||||
|
import re
|
||||||
|
buchungstyp = 'unbekannt'
|
||||||
|
gegenkonto = None
|
||||||
|
beschreibung = None
|
||||||
|
|
||||||
|
# Suche nach 5-stelligen Zahlen im Buchungstext (potenzielle Mieternummern)
|
||||||
|
mieternummer_pattern = r'\b(1\d{4})\b' # 5-stellige Zahl beginnend mit 1
|
||||||
|
gefundene_nummern = re.findall(mieternummer_pattern, buchungstext)
|
||||||
|
|
||||||
|
if gefundene_nummern:
|
||||||
|
print(f" 🔍 Gefundene potenzielle Mieternummern: {gefundene_nummern}")
|
||||||
|
|
||||||
|
# Prüfe ob eine der gefundenen Nummern ein Mieterkonto ist
|
||||||
|
for nummer in gefundene_nummern:
|
||||||
|
for mieter_entry in mieter:
|
||||||
|
if mieter_entry.get('konto') == nummer:
|
||||||
|
gegenkonto = mieter_entry.get('konto')
|
||||||
|
beschreibung = mieter_entry.get('beschreibung')
|
||||||
|
buchungstyp = 'mieter'
|
||||||
|
print(f" ✓ Mieter über Kontonummer gefunden: {nummer} → {beschreibung}")
|
||||||
|
break
|
||||||
|
if buchungstyp == 'mieter':
|
||||||
|
break
|
||||||
|
|
||||||
|
# --- TEIL 1: NUR WENN KEINE MIETERNUMMER GEFUNDEN, NORMALES PARSING ---
|
||||||
|
if buchungstyp == 'unbekannt':
|
||||||
|
# Standard-Gegenkonto aus Mandanten-Config verwenden
|
||||||
|
gegenkonto = mandant.get('standard_gegenkonto', '1300') # KORRIGIERT!
|
||||||
|
beschreibung = "Unbekannte Buchung"
|
||||||
|
|
||||||
|
# Suche nach Mietern über Suchbegriffe
|
||||||
|
for mieter_entry in mieter:
|
||||||
|
for suchbegriff in mieter_entry.get('suchbegriffe', []):
|
||||||
|
if normalize_text(suchbegriff) in norm_buchungstext:
|
||||||
|
gegenkonto = mieter_entry.get('konto')
|
||||||
|
beschreibung = mieter_entry.get('beschreibung')
|
||||||
|
buchungstyp = 'mieter'
|
||||||
|
print(f" ✓ Buchungstyp: Mieter (gefunden über '{suchbegriff}')")
|
||||||
|
break
|
||||||
|
if buchungstyp == 'mieter':
|
||||||
|
break
|
||||||
|
|
||||||
|
# --- TEIL 1b: KOSTENKONTEN PRÜFEN ---
|
||||||
|
if buchungstyp == 'unbekannt':
|
||||||
|
for kosten_entry in kostenkonten:
|
||||||
|
for suchbegriff in kosten_entry.get('suchbegriffe', []):
|
||||||
|
if normalize_text(suchbegriff) in norm_buchungstext:
|
||||||
|
gegenkonto = kosten_entry.get('gegenkonto')
|
||||||
|
beschreibung = kosten_entry.get('bezeichnung', '')
|
||||||
|
buchungstyp = 'kosten'
|
||||||
|
print(f" ✓ Buchungstyp: Kosten (gefunden über '{suchbegriff}')")
|
||||||
|
break
|
||||||
|
if buchungstyp == 'kosten':
|
||||||
|
break
|
||||||
|
|
||||||
|
# --- TEIL 2: KOSTENSTELLE BESTIMMEN (nur bei Kostenkonten) ---
|
||||||
|
gefundene_kst = ''
|
||||||
|
if buchungstyp == 'kosten':
|
||||||
|
for projekt in projekte:
|
||||||
|
for suchbegriff in projekt.get('suchbegriffe', []):
|
||||||
|
if normalize_text(suchbegriff) in norm_buchungstext:
|
||||||
|
gefundene_kst = projekt.get('kst', '')
|
||||||
|
print(f" ✓ Projekt-Kostenstelle gefunden: '{gefundene_kst}' (über '{suchbegriff}')")
|
||||||
|
break
|
||||||
|
if gefundene_kst:
|
||||||
|
break
|
||||||
|
|
||||||
|
# Default-Kostenstelle nur bei Kostenkonten
|
||||||
|
if not gefundene_kst and default_projekt:
|
||||||
|
gefundene_kst = default_projekt
|
||||||
|
print(f" → Keine Kostenstelle gefunden → Default-KST {default_projekt}")
|
||||||
|
|
||||||
|
# --- TEIL 3: ERGEBNISSE ZUSAMMENFÜHREN ---
|
||||||
|
konto = mandant.get('standard_bank_kto', mandant.get('konto', '1200'))
|
||||||
|
kostenstelle = gefundene_kst
|
||||||
|
|
||||||
|
# DIAGNOSE-AUSGABE
|
||||||
|
print("\n" + "="*25 + " FINALES ZUORDNUNGSERGEBNIS " + "="*25)
|
||||||
|
print(f" - KONTO: {konto}")
|
||||||
|
print(f" - GEGENKONTO: {gegenkonto}")
|
||||||
|
print(f" - KOSTENSTELLE: '{kostenstelle}'")
|
||||||
|
print(f" - BESCHREIBUNG: {beschreibung}")
|
||||||
|
print(f" - BUCHUNGSTYP: {buchungstyp}")
|
||||||
|
print("="*70 + "\n")
|
||||||
|
|
||||||
|
return str(konto), str(gegenkonto), str(kostenstelle), beschreibung
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ===== CSV EINLESEN =====
|
||||||
|
|
||||||
|
def lese_csv(csv_datei):
|
||||||
|
"""Liest CSV-Datei ein und gibt Liste von Buchungen zurück"""
|
||||||
|
buchungen = []
|
||||||
|
|
||||||
|
with open(csv_datei, 'r', encoding='utf-8') as f:
|
||||||
|
reader = csv.DictReader(f, delimiter=';')
|
||||||
|
|
||||||
|
# Spaltennamen bereinigen (Leerzeichen UND BOM entfernen)
|
||||||
|
reader.fieldnames = [name.strip().lstrip('\ufeff') if name else name for name in reader.fieldnames]
|
||||||
|
|
||||||
|
# Debug: Spaltennamen anzeigen
|
||||||
|
print(f"\n📋 Gefundene CSV-Spalten: {reader.fieldnames}")
|
||||||
|
|
||||||
|
# Flexible Spaltenerkennung
|
||||||
|
datum_spalte = None
|
||||||
|
text_spalte = None
|
||||||
|
betrag_spalte = None
|
||||||
|
|
||||||
|
for name in reader.fieldnames:
|
||||||
|
name_lower = name.lower()
|
||||||
|
if 'buchungstag' in name_lower or 'datum' in name_lower:
|
||||||
|
datum_spalte = name
|
||||||
|
if 'buchungstext' in name_lower or 'verwendungszweck' in name_lower or 'text' in name_lower:
|
||||||
|
text_spalte = name
|
||||||
|
if 'betrag' in name_lower or 'amount' in name_lower:
|
||||||
|
betrag_spalte = name
|
||||||
|
|
||||||
|
if not datum_spalte:
|
||||||
|
raise ValueError(f"Keine Datums-Spalte gefunden! Verfügbare Spalten: {reader.fieldnames}")
|
||||||
|
if not text_spalte:
|
||||||
|
raise ValueError(f"Keine Text-Spalte gefunden! Verfügbare Spalten: {reader.fieldnames}")
|
||||||
|
if not betrag_spalte:
|
||||||
|
raise ValueError(f"Keine Betrags-Spalte gefunden! Verfügbare Spalten: {reader.fieldnames}")
|
||||||
|
|
||||||
|
print(f"✓ Verwende Spalten: Datum='{datum_spalte}', Text='{text_spalte}', Betrag='{betrag_spalte}'")
|
||||||
|
|
||||||
|
for row in reader:
|
||||||
|
try:
|
||||||
|
# Datum parsen (Format: DD.MM.YYYY)
|
||||||
|
datum_str = row[datum_spalte].strip()
|
||||||
|
datum = datetime.strptime(datum_str, '%d.%m.%Y')
|
||||||
|
|
||||||
|
# Betrag konvertieren (Format: -123,45 → -123.45)
|
||||||
|
betrag_str = row[betrag_spalte].strip().replace('.', '').replace(',', '.')
|
||||||
|
betrag = float(betrag_str)
|
||||||
|
|
||||||
|
# Buchungstext
|
||||||
|
buchungstext = row[text_spalte].strip() if row[text_spalte] else ''
|
||||||
|
|
||||||
|
buchung = {
|
||||||
|
'datum': datum,
|
||||||
|
'buchungstext': buchungstext,
|
||||||
|
'betrag': betrag,
|
||||||
|
'umsatzart': row.get('Umsatzart', '').strip() if row.get('Umsatzart') else ''
|
||||||
|
}
|
||||||
|
buchungen.append(buchung)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"⚠️ Zeile übersprungen (Fehler: {e}): {row}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
return buchungen
|
||||||
|
|
||||||
|
def filtere_buchungen(buchungen, monate, jahre):
|
||||||
|
"""Filtert Buchungen nach ausgewählten Monaten und Jahren"""
|
||||||
|
gefiltert = []
|
||||||
|
for buchung in buchungen:
|
||||||
|
if buchung['datum'].month in monate and buchung['datum'].year in jahre:
|
||||||
|
gefiltert.append(buchung)
|
||||||
|
return gefiltert
|
||||||
|
|
||||||
|
# ===== DBF MANUELL ERSTELLEN =====
|
||||||
|
|
||||||
|
# --- START: ERSETZEN SIE IHRE ALTE FUNKTION DURCH DIESE ---
|
||||||
|
|
||||||
|
def erstelle_dbf_manuell(mandant, buchungen, mieter, kostenkonten, projekte, default_projekt, ausgabe_datei_praefix, start_beleg_nr):
|
||||||
|
"""
|
||||||
|
Gruppiert Buchungen nach Monat und erstellt für jeden Monat eine eigene,
|
||||||
|
Byte-genaue DBF-Datei.
|
||||||
|
Returns: letzte_beleg_nr
|
||||||
|
"""
|
||||||
|
|
||||||
|
# NEU: Schritt 1 - Buchungen nach Monat gruppieren
|
||||||
|
buchungen_pro_monat = {}
|
||||||
|
for buchung in buchungen:
|
||||||
|
monats_schluessel = buchung['datum'].strftime('%Y-%m') # z.B. '2023-10'
|
||||||
|
if monats_schluessel not in buchungen_pro_monat:
|
||||||
|
buchungen_pro_monat[monats_schluessel] = []
|
||||||
|
buchungen_pro_monat[monats_schluessel].append(buchung)
|
||||||
|
|
||||||
|
print(f"\nFunde {len(buchungen_pro_monat)} verschiedene Monate in den Buchungsdaten.")
|
||||||
|
|
||||||
|
beleg_nr = start_beleg_nr
|
||||||
|
|
||||||
|
# NEU: Schritt 2 - Für jeden Monat eine eigene DBF-Datei erstellen
|
||||||
|
for monat_key, monats_buchungen in buchungen_pro_monat.items():
|
||||||
|
|
||||||
|
jahr, monat = map(int, monat_key.split('-'))
|
||||||
|
jahr_kurz = str(jahr)[-2:]
|
||||||
|
|
||||||
|
# NEU: Dateinamen für diesen Monat generieren
|
||||||
|
ausgabe_datei = f"{ausgabe_datei_praefix}_{monat:02d}{jahr_kurz}.dbf"
|
||||||
|
print(f"\n{'='*70}")
|
||||||
|
print(f"Erstelle DBF-Datei für Monat {monat_key}: {ausgabe_datei}")
|
||||||
|
print(f"{'='*70}")
|
||||||
|
|
||||||
|
# --- AB HIER BEGINNT IHRE BEWÄHRTE LOGIK, ANGEPASST FÜR EINE DATEI ---
|
||||||
|
|
||||||
|
# DBF Header (32 bytes) - angepasst für die Anzahl der Monatsbuchungen
|
||||||
|
heute = datetime.now()
|
||||||
|
num_records = len(monats_buchungen)
|
||||||
|
header_len = 32 + 43 * 32 + 1 # 32 Header + 43 Felder * 32 + 1 Terminator
|
||||||
|
record_len = 368 # 1 (deletion) + 367 (fields)
|
||||||
|
|
||||||
|
header = bytearray(32)
|
||||||
|
header[0] = 0x03 # dBase III
|
||||||
|
header[1] = heute.year - 1900
|
||||||
|
header[2] = heute.month
|
||||||
|
header[3] = heute.day
|
||||||
|
struct.pack_into('<I', header, 4, num_records)
|
||||||
|
struct.pack_into('<H', header, 8, header_len)
|
||||||
|
struct.pack_into('<H', header, 10, record_len)
|
||||||
|
|
||||||
|
# Field Descriptors (exakte Struktur aus Ihrer Funktion)
|
||||||
|
fields = [
|
||||||
|
('BTT', 'N', 2, 0), ('BMM', 'N', 2, 0), ('BELEG', 'C', 10, 0),
|
||||||
|
('KONTO', 'N', 8, 0), ('GEGENKONTO', 'N', 8, 0), ('KST', 'C', 8, 0),
|
||||||
|
('KTG', 'C', 10, 0), ('BETRAG', 'N', 14, 2), ('STEUER', 'N', 5, 2),
|
||||||
|
('SKONTO', 'N', 14, 2), ('TEXT', 'C', 30, 0), ('BEZAHLT', 'N', 14, 2),
|
||||||
|
('KZ', 'C', 3, 0), ('LFDNR', 'N', 8, 0), ('EURO', 'L', 1, 0),
|
||||||
|
('ZAHLBETRAG', 'N', 14, 2), ('BEZAHLT_NK', 'N', 14, 2), ('FAELLIG', 'L', 1, 0),
|
||||||
|
('TEXT2', 'C', 30, 0), ('DATEV', 'L', 1, 0), ('FAELLIG_AM', 'D', 8, 0),
|
||||||
|
('STORNO', 'L', 1, 0), ('BJJ', 'N', 4, 0), ('TEMP1', 'C', 20, 0),
|
||||||
|
('HNDLNR', 'N', 8, 0), ('GBLFDNR', 'N', 15, 0), ('SKONTO2', 'N', 14, 2),
|
||||||
|
('DATUM2', 'D', 8, 0), ('KEIN_ZV', 'L', 1, 0), ('MANUELL', 'L', 1, 0),
|
||||||
|
('SOLLKONTO', 'N', 8, 0), ('STAPEL', 'C', 20, 0), ('SKONTSTFR', 'N', 14, 2),
|
||||||
|
('REB_LFDNR', 'N', 6, 0), ('RECHART', 'C', 3, 0), ('ZAHLART', 'N', 1, 0),
|
||||||
|
('LDATUM', 'D', 8, 0), ('XFINANZ', 'L', 1, 0), ('INZV', 'L', 1, 0),
|
||||||
|
('DUMMY', 'C', 1, 0), ('ABRJAHR', 'N', 4, 0), ('EDATUM', 'D', 8, 0),
|
||||||
|
('ENAME', 'C', 15, 0)
|
||||||
|
]
|
||||||
|
|
||||||
|
field_descriptors = bytearray()
|
||||||
|
for name, ftype, flen, fdec in fields:
|
||||||
|
fd = bytearray(32)
|
||||||
|
name_bytes = name.encode('cp850')[:11]
|
||||||
|
fd[0:len(name_bytes)] = name_bytes
|
||||||
|
fd[11] = ord(ftype)
|
||||||
|
fd[16] = flen
|
||||||
|
fd[17] = fdec
|
||||||
|
field_descriptors.extend(fd)
|
||||||
|
field_descriptors.append(0x0D)
|
||||||
|
|
||||||
|
# Datei für den aktuellen Monat schreiben
|
||||||
|
with open(ausgabe_datei, 'wb') as f:
|
||||||
|
f.write(header)
|
||||||
|
f.write(field_descriptors)
|
||||||
|
|
||||||
|
standard_datum = date(1899, 12, 30)
|
||||||
|
|
||||||
|
# NEU: Nur die Buchungen für DIESEN Monat verarbeiten
|
||||||
|
for idx, buchung in enumerate(monats_buchungen, 1):
|
||||||
|
datum = buchung['datum']
|
||||||
|
buchungstext = buchung['buchungstext'][:30]
|
||||||
|
betrag = buchung['betrag']
|
||||||
|
|
||||||
|
print(f"\nVerarbeite Buchung {idx}/{num_records} für {monat_key}...")
|
||||||
|
|
||||||
|
konto, gegenkonto, kostenstelle, beschreibung = bestimme_konten_und_kst(
|
||||||
|
buchung['buchungstext'], mandant, mieter, kostenkonten, projekte, default_projekt
|
||||||
|
)
|
||||||
|
|
||||||
|
text2 = beschreibung[:30] if beschreibung else ''
|
||||||
|
konto_num = int(konto)
|
||||||
|
gegenkonto_num = int(gegenkonto)
|
||||||
|
beleg_str = str(beleg_nr)
|
||||||
|
|
||||||
|
record = bytearray(368)
|
||||||
|
record[0] = 0x20
|
||||||
|
pos = 1
|
||||||
|
|
||||||
|
def write_field(value, length, decimals=0, is_numeric=False, is_logical=False, is_date=False, empty=False):
|
||||||
|
nonlocal pos
|
||||||
|
if empty: record[pos:pos+length] = b' ' * length
|
||||||
|
elif is_date: record[pos:pos+8] = value.strftime('%Y%m%d').encode('ascii') if value else b' ' * 8
|
||||||
|
elif is_logical: record[pos] = ord('T') if value else ord('F')
|
||||||
|
elif is_numeric:
|
||||||
|
formatted = f"{value:{length}.{decimals}f}" if decimals > 0 else f"{int(value):{length}d}"
|
||||||
|
record[pos:pos+length] = formatted.rjust(length)[:length].encode('ascii')
|
||||||
|
else:
|
||||||
|
encoded = value.encode('cp850')[:length]
|
||||||
|
record[pos:pos+length] = encoded.ljust(length)
|
||||||
|
pos += length
|
||||||
|
|
||||||
|
# Felder schreiben (Ihre exakte Reihenfolge)
|
||||||
|
write_field(datum.day, 2, is_numeric=True); write_field(datum.month, 2, is_numeric=True)
|
||||||
|
write_field(beleg_str, 10); write_field(konto_num, 8, is_numeric=True)
|
||||||
|
write_field(gegenkonto_num, 8, is_numeric=True); write_field(kostenstelle, 8)
|
||||||
|
write_field('', 10); write_field(betrag, 14, decimals=2, is_numeric=True)
|
||||||
|
write_field(0.00, 5, decimals=2, is_numeric=True); write_field(0.00, 14, decimals=2, is_numeric=True)
|
||||||
|
write_field(buchungstext, 30); write_field(0.00, 14, decimals=2, is_numeric=True)
|
||||||
|
write_field('', 3); write_field(0, 8, is_numeric=True); write_field(True, 1, is_logical=True)
|
||||||
|
write_field(betrag, 14, decimals=2, is_numeric=True); write_field(None, 14, empty=True)
|
||||||
|
write_field(None, 1, empty=True); write_field(text2, 30); write_field(None, 1, empty=True)
|
||||||
|
write_field(standard_datum, 8, is_date=True); write_field(False, 1, is_logical=True)
|
||||||
|
write_field(datum.year, 4, is_numeric=True); write_field('', 20); write_field(None, 8, empty=True)
|
||||||
|
write_field(None, 15, empty=True); write_field(0.00, 14, decimals=2, is_numeric=True)
|
||||||
|
write_field(standard_datum, 8, is_date=True); write_field(None, 1, empty=True)
|
||||||
|
write_field(False, 1, is_logical=True); write_field(0, 8, is_numeric=True)
|
||||||
|
write_field('', 20); write_field(0.00, 14, decimals=2, is_numeric=True)
|
||||||
|
write_field(None, 6, empty=True); write_field('', 3); write_field(0, 1, is_numeric=True)
|
||||||
|
write_field(standard_datum, 8, is_date=True); write_field(None, 1, empty=True)
|
||||||
|
write_field(None, 1, empty=True); write_field('', 1); write_field(0, 4, is_numeric=True)
|
||||||
|
write_field(None, 8, empty=True); write_field('', 15)
|
||||||
|
|
||||||
|
f.write(record)
|
||||||
|
beleg_nr += 1
|
||||||
|
|
||||||
|
f.write(b'\x1A') # EOF
|
||||||
|
|
||||||
|
print(f"✓ DBF-Datei '{ausgabe_datei}' erfolgreich mit {num_records} Einträgen erstellt.")
|
||||||
|
|
||||||
|
letzte_beleg_nr = beleg_nr - 1
|
||||||
|
return letzte_beleg_nr
|
||||||
|
|
||||||
|
# --- ENDE: BIS HIER ALLES ERSETZEN ---
|
||||||
|
|
||||||
|
# ===== HILFSFUNKTIONEN =====
|
||||||
|
|
||||||
|
def parse_monatseingabe(eingabe):
|
||||||
|
"""Parst Monatseingabe wie '1,2,3' oder '1-3' oder '12' """
|
||||||
|
monate = set()
|
||||||
|
teile = eingabe.split(',')
|
||||||
|
|
||||||
|
for teil in teile:
|
||||||
|
teil = teil.strip()
|
||||||
|
if '-' in teil:
|
||||||
|
start, ende = map(int, teil.split('-'))
|
||||||
|
monate.update(range(start, ende + 1))
|
||||||
|
else:
|
||||||
|
monate.add(int(teil))
|
||||||
|
|
||||||
|
return sorted(list(monate))
|
||||||
|
|
||||||
|
def parse_jahreseingabe(eingabe):
|
||||||
|
"""Parst Jahreseingabe wie '2024' oder '2023,2024' """
|
||||||
|
jahre = set()
|
||||||
|
teile = eingabe.split(',')
|
||||||
|
|
||||||
|
for teil in teile:
|
||||||
|
teil = teil.strip()
|
||||||
|
if '-' in teil:
|
||||||
|
start, ende = map(int, teil.split('-'))
|
||||||
|
jahre.update(range(start, ende + 1))
|
||||||
|
else:
|
||||||
|
jahre.add(int(teil))
|
||||||
|
|
||||||
|
return sorted(list(jahre))
|
||||||
|
|
||||||
|
# ===== HAUPTPROGRAMM =====
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print("=" * 70)
|
||||||
|
print(" CSV zu DBF Konverter für Hausverwaltung (v2)")
|
||||||
|
print("=" * 70)
|
||||||
|
|
||||||
|
create_default_configs()
|
||||||
|
|
||||||
|
mandanten = load_mandanten()
|
||||||
|
|
||||||
|
print("\n📋 Verfügbare Mandanten:")
|
||||||
|
for idx, m in enumerate(mandanten, 1):
|
||||||
|
print(f" {idx}. {m['name']} (Nr. {m['nummer']}, Konto {m['konto']}, nächster Beleg: {m.get('beleg_index', 1)})")
|
||||||
|
|
||||||
|
auswahl = int(input("\n➤ Mandant auswählen (Nummer): ")) - 1
|
||||||
|
mandant = mandanten[auswahl]
|
||||||
|
|
||||||
|
print(f"\n✓ Mandant gewählt: {mandant['name']}")
|
||||||
|
print(f" Mandantennummer: {mandant['nummer']}")
|
||||||
|
print(f" Konto: {mandant['konto']}")
|
||||||
|
print(f" Standard-Gegenkonto: {mandant['standard_gegenkonto']}")
|
||||||
|
print(f" Start-Belegnummer: {mandant.get('beleg_index', 1)}")
|
||||||
|
|
||||||
|
mieter = load_mieter(mandant['nummer'])
|
||||||
|
print(f" Mieter geladen: {len(mieter)} Einträge")
|
||||||
|
|
||||||
|
kostenkonten = load_kostenkonten(mandant['nummer'])
|
||||||
|
print(f" Kostenkonten geladen: {len(kostenkonten)} Einträge")
|
||||||
|
|
||||||
|
projekte, default_projekt = load_projekte(mandant['nummer'])
|
||||||
|
print(f" Projekte geladen: {len(projekte)} Einträge")
|
||||||
|
print(f" Default-Projekt: {default_projekt}")
|
||||||
|
|
||||||
|
csv_datei = input("\n➤ CSV-Datei Pfad: ")
|
||||||
|
alle_buchungen = lese_csv(csv_datei)
|
||||||
|
print(f"\n✓ {len(alle_buchungen)} Buchungen aus CSV geladen")
|
||||||
|
|
||||||
|
print("\n📅 Für welche Monate sollen DBF-Dateien erstellt werden?")
|
||||||
|
print(" Beispiele: '12' oder '1,2,3' oder '1-12'")
|
||||||
|
monate_eingabe = input("➤ Monate: ")
|
||||||
|
monate = parse_monatseingabe(monate_eingabe)
|
||||||
|
print(f"✓ Ausgewählte Monate: {', '.join(map(str, monate))}")
|
||||||
|
|
||||||
|
print("\n📅 Für welche Jahre sollen DBF-Dateien erstellt werden?")
|
||||||
|
print(" Beispiele: '2024' oder '2023,2024' oder '2023-2024'")
|
||||||
|
jahre_eingabe = input("➤ Jahre: ")
|
||||||
|
jahre = parse_jahreseingabe(jahre_eingabe)
|
||||||
|
print(f"✓ Ausgewählte Jahre: {', '.join(map(str, jahre))}")
|
||||||
|
|
||||||
|
gefilterte_buchungen = filtere_buchungen(alle_buchungen, monate, jahre)
|
||||||
|
print(f"\n✓ {len(gefilterte_buchungen)} Buchungen gefiltert (von {len(alle_buchungen)} gesamt)")
|
||||||
|
|
||||||
|
if len(gefilterte_buchungen) == 0:
|
||||||
|
print("\n⚠️ Keine Buchungen für die ausgewählten Monate/Jahre gefunden!")
|
||||||
|
return
|
||||||
|
|
||||||
|
monate_str = '_'.join(map(str, monate)) if len(monate) <= 3 else f"{min(monate)}-{max(monate)}"
|
||||||
|
jahre_str = '_'.join(map(str, jahre))
|
||||||
|
ausgabe_datei = f"buchungen_{mandant['nummer']}_M{monate_str}_J{jahre_str}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.dbf"
|
||||||
|
|
||||||
|
print(f"\n🔨 Erstelle DBF-Datei: {ausgabe_datei}")
|
||||||
|
print("-" * 70)
|
||||||
|
|
||||||
|
start_beleg_nr = mandant.get('beleg_index', 1)
|
||||||
|
letzte_beleg_nr = erstelle_dbf_manuell(
|
||||||
|
mandant, gefilterte_buchungen, mieter, kostenkonten,
|
||||||
|
projekte, default_projekt, ausgabe_datei, start_beleg_nr
|
||||||
|
)
|
||||||
|
|
||||||
|
# Beleg-Index in Config aktualisieren
|
||||||
|
mandant['beleg_index'] = letzte_beleg_nr + 1
|
||||||
|
save_mandanten(mandanten)
|
||||||
|
print(f"\n✓ Mandanten-Config aktualisiert: Nächster Beleg startet bei {mandant['beleg_index']}")
|
||||||
|
|
||||||
|
print("\n" + "=" * 70)
|
||||||
|
print("✅ Fertig! DBF-Datei erfolgreich erstellt.")
|
||||||
|
print("=" * 70)
|
||||||
|
print(f"\n💾 Datei: {ausgabe_datei}")
|
||||||
|
print(f"📊 Buchungen: {len(gefilterte_buchungen)}")
|
||||||
|
print(f"🔢 Belegnummern: {start_beleg_nr} bis {letzte_beleg_nr}")
|
||||||
|
print(f"📄 Mieter-Config: identitaeten_mieter_{mandant['nummer']}.json")
|
||||||
|
print(f"📄 Kosten-Config: identitaeten_kosten_{mandant['nummer']}.json")
|
||||||
|
print(f"📄 Projekte-Config: projekte_{mandant['nummer']}.json")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
@ -0,0 +1,520 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""
|
||||||
|
Hausverwaltung CSV zu DBF Konverter
|
||||||
|
GUI Version für Linux (Debian, Ubuntu, Kubuntu)
|
||||||
|
Version 3.0
|
||||||
|
"""
|
||||||
|
|
||||||
|
import tkinter as tk
|
||||||
|
from tkinter import ttk, filedialog, messagebox, scrolledtext
|
||||||
|
import json
|
||||||
|
import csv
|
||||||
|
import os
|
||||||
|
import struct
|
||||||
|
from datetime import datetime, date
|
||||||
|
import threading
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# ===== BACKEND FUNKTIONEN (aus Original-Code) =====
|
||||||
|
|
||||||
|
def normalize_text(text):
|
||||||
|
"""Normalisiert Text für Vergleiche"""
|
||||||
|
text = text.lower()
|
||||||
|
replacements = {
|
||||||
|
'ü': 'ue', 'ö': 'oe', 'ä': 'ae', 'ß': 'ss',
|
||||||
|
'é': 'e', 'è': 'e', 'ê': 'e', 'à': 'a', 'â': 'a'
|
||||||
|
}
|
||||||
|
for old, new in replacements.items():
|
||||||
|
text = text.replace(old, new)
|
||||||
|
return text
|
||||||
|
|
||||||
|
def load_mandanten():
|
||||||
|
"""Lädt Mandanten aus Config"""
|
||||||
|
if not os.path.exists('mandanten_config.json'):
|
||||||
|
return []
|
||||||
|
with open('mandanten_config.json', 'r', encoding='utf-8') as f:
|
||||||
|
return json.load(f)['mandanten']
|
||||||
|
|
||||||
|
def save_mandanten(mandanten):
|
||||||
|
"""Speichert Mandanten in Config"""
|
||||||
|
with open('mandanten_config.json', 'w', encoding='utf-8') as f:
|
||||||
|
json.dump({"mandanten": mandanten}, f, indent=4, ensure_ascii=False)
|
||||||
|
|
||||||
|
def load_mieter(mandantennummer):
|
||||||
|
"""Lädt Mieter für einen bestimmten Mandanten"""
|
||||||
|
mieter_datei = f"identitaeten_mieter_{mandantennummer}.json"
|
||||||
|
if not os.path.exists(mieter_datei):
|
||||||
|
return []
|
||||||
|
with open(mieter_datei, 'r', encoding='utf-8') as f:
|
||||||
|
return json.load(f)['mieter']
|
||||||
|
|
||||||
|
def load_kostenkonten(mandantennummer):
|
||||||
|
"""Lädt Kostenkonten für einen bestimmten Mandanten"""
|
||||||
|
kosten_datei = f"identitaeten_kosten_{mandantennummer}.json"
|
||||||
|
if not os.path.exists(kosten_datei):
|
||||||
|
return []
|
||||||
|
with open(kosten_datei, 'r', encoding='utf-8') as f:
|
||||||
|
return json.load(f)['kostenkonten']
|
||||||
|
|
||||||
|
def load_projekte(mandantennummer):
|
||||||
|
"""Lädt Projekte/Kostenstellen für einen bestimmten Mandanten"""
|
||||||
|
projekte_datei = f"projekte_{mandantennummer}.json"
|
||||||
|
if not os.path.exists(projekte_datei):
|
||||||
|
return [], ''
|
||||||
|
with open(projekte_datei, 'r', encoding='utf-8') as f:
|
||||||
|
config = json.load(f)
|
||||||
|
return config['projekte'], config.get('default_projekt', '')
|
||||||
|
|
||||||
|
# ===== GUI KLASSE =====
|
||||||
|
|
||||||
|
class HausverwaltungGUI:
|
||||||
|
def __init__(self, root):
|
||||||
|
self.root = root
|
||||||
|
self.root.title("Hausverwaltung CSV→DBF Konverter v3.0")
|
||||||
|
self.root.geometry("900x700")
|
||||||
|
|
||||||
|
# Moderne Farben
|
||||||
|
self.bg_color = "#f0f0f0"
|
||||||
|
self.accent_color = "#2196F3"
|
||||||
|
self.success_color = "#4CAF50"
|
||||||
|
self.error_color = "#f44336"
|
||||||
|
|
||||||
|
self.root.configure(bg=self.bg_color)
|
||||||
|
|
||||||
|
# Variablen
|
||||||
|
self.csv_file = None
|
||||||
|
self.mandanten = load_mandanten()
|
||||||
|
self.selected_mandant = None
|
||||||
|
self.processing = False
|
||||||
|
|
||||||
|
self.setup_ui()
|
||||||
|
self.load_initial_data()
|
||||||
|
|
||||||
|
def setup_ui(self):
|
||||||
|
"""Erstellt die GUI-Komponenten"""
|
||||||
|
|
||||||
|
# Style konfigurieren
|
||||||
|
style = ttk.Style()
|
||||||
|
style.theme_use('clam')
|
||||||
|
style.configure('Title.TLabel', font=('Ubuntu', 16, 'bold'))
|
||||||
|
style.configure('Header.TLabel', font=('Ubuntu', 11, 'bold'))
|
||||||
|
style.configure('Success.TLabel', foreground=self.success_color)
|
||||||
|
style.configure('Error.TLabel', foreground=self.error_color)
|
||||||
|
|
||||||
|
# Hauptcontainer
|
||||||
|
main_frame = ttk.Frame(self.root, padding="20")
|
||||||
|
main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
||||||
|
|
||||||
|
# Titel
|
||||||
|
title_label = ttk.Label(main_frame, text="📊 CSV zu DBF Konverter für Hausverwaltung",
|
||||||
|
style='Title.TLabel')
|
||||||
|
title_label.grid(row=0, column=0, columnspan=3, pady=(0, 20))
|
||||||
|
|
||||||
|
# 1. Mandanten-Auswahl
|
||||||
|
ttk.Label(main_frame, text="1. Mandant auswählen:", style='Header.TLabel').grid(
|
||||||
|
row=1, column=0, sticky=tk.W, pady=(10, 5))
|
||||||
|
|
||||||
|
self.mandant_frame = ttk.LabelFrame(main_frame, text="Mandanten", padding="10")
|
||||||
|
self.mandant_frame.grid(row=2, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=(0, 15))
|
||||||
|
|
||||||
|
self.mandant_combo = ttk.Combobox(self.mandant_frame, width=50, state="readonly")
|
||||||
|
self.mandant_combo.grid(row=0, column=0, padx=(0, 10))
|
||||||
|
self.mandant_combo.bind('<<ComboboxSelected>>', self.on_mandant_select)
|
||||||
|
|
||||||
|
self.mandant_info = ttk.Label(self.mandant_frame, text="")
|
||||||
|
self.mandant_info.grid(row=0, column=1)
|
||||||
|
|
||||||
|
# 2. CSV-Datei auswählen
|
||||||
|
ttk.Label(main_frame, text="2. CSV-Datei auswählen:", style='Header.TLabel').grid(
|
||||||
|
row=3, column=0, sticky=tk.W, pady=(10, 5))
|
||||||
|
|
||||||
|
file_frame = ttk.Frame(main_frame)
|
||||||
|
file_frame.grid(row=4, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=(0, 15))
|
||||||
|
|
||||||
|
self.file_label = ttk.Label(file_frame, text="Keine Datei ausgewählt",
|
||||||
|
relief=tk.SUNKEN, padding="5")
|
||||||
|
self.file_label.grid(row=0, column=0, sticky=(tk.W, tk.E))
|
||||||
|
|
||||||
|
ttk.Button(file_frame, text="📁 Datei wählen",
|
||||||
|
command=self.select_csv_file).grid(row=0, column=1, padx=(10, 0))
|
||||||
|
|
||||||
|
file_frame.columnconfigure(0, weight=1)
|
||||||
|
|
||||||
|
# 3. Zeitraum auswählen
|
||||||
|
ttk.Label(main_frame, text="3. Zeitraum auswählen:", style='Header.TLabel').grid(
|
||||||
|
row=5, column=0, sticky=tk.W, pady=(10, 5))
|
||||||
|
|
||||||
|
time_frame = ttk.LabelFrame(main_frame, text="Zeitraum", padding="10")
|
||||||
|
time_frame.grid(row=6, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=(0, 15))
|
||||||
|
|
||||||
|
# Monate
|
||||||
|
ttk.Label(time_frame, text="Monate:").grid(row=0, column=0, sticky=tk.W)
|
||||||
|
self.months_var = tk.StringVar(value="1-12")
|
||||||
|
ttk.Entry(time_frame, textvariable=self.months_var, width=20).grid(
|
||||||
|
row=0, column=1, padx=(5, 20))
|
||||||
|
ttk.Label(time_frame, text="(z.B. 1-12 oder 1,2,3)",
|
||||||
|
font=('Ubuntu', 9)).grid(row=0, column=2)
|
||||||
|
|
||||||
|
# Jahre
|
||||||
|
ttk.Label(time_frame, text="Jahre:").grid(row=1, column=0, sticky=tk.W, pady=(5, 0))
|
||||||
|
current_year = datetime.now().year
|
||||||
|
self.years_var = tk.StringVar(value=str(current_year))
|
||||||
|
ttk.Entry(time_frame, textvariable=self.years_var, width=20).grid(
|
||||||
|
row=1, column=1, padx=(5, 20), pady=(5, 0))
|
||||||
|
ttk.Label(time_frame, text="(z.B. 2024 oder 2023,2024)",
|
||||||
|
font=('Ubuntu', 9)).grid(row=1, column=2, pady=(5, 0))
|
||||||
|
|
||||||
|
# 4. Ausgabe-Log
|
||||||
|
ttk.Label(main_frame, text="Verarbeitungsprotokoll:", style='Header.TLabel').grid(
|
||||||
|
row=7, column=0, sticky=tk.W, pady=(10, 5))
|
||||||
|
|
||||||
|
# Log-Textfeld mit Scrollbar
|
||||||
|
log_frame = ttk.Frame(main_frame)
|
||||||
|
log_frame.grid(row=8, column=0, columnspan=3, sticky=(tk.W, tk.E, tk.N, tk.S), pady=(0, 15))
|
||||||
|
|
||||||
|
self.log_text = scrolledtext.ScrolledText(log_frame, height=12, width=80,
|
||||||
|
wrap=tk.WORD, font=('Courier', 9))
|
||||||
|
self.log_text.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
||||||
|
|
||||||
|
log_frame.columnconfigure(0, weight=1)
|
||||||
|
log_frame.rowconfigure(0, weight=1)
|
||||||
|
|
||||||
|
# Fortschrittsbalken
|
||||||
|
self.progress = ttk.Progressbar(main_frame, mode='indeterminate')
|
||||||
|
self.progress.grid(row=9, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=(0, 10))
|
||||||
|
|
||||||
|
# Buttons
|
||||||
|
button_frame = ttk.Frame(main_frame)
|
||||||
|
button_frame.grid(row=10, column=0, columnspan=3)
|
||||||
|
|
||||||
|
self.process_btn = ttk.Button(button_frame, text="🔄 Konvertierung starten",
|
||||||
|
command=self.start_processing)
|
||||||
|
self.process_btn.grid(row=0, column=0, padx=5)
|
||||||
|
|
||||||
|
ttk.Button(button_frame, text="⚙️ Konfiguration",
|
||||||
|
command=self.open_config).grid(row=0, column=1, padx=5)
|
||||||
|
|
||||||
|
ttk.Button(button_frame, text="❌ Beenden",
|
||||||
|
command=self.root.quit).grid(row=0, column=2, padx=5)
|
||||||
|
|
||||||
|
# Grid-Konfiguration
|
||||||
|
main_frame.columnconfigure(1, weight=1)
|
||||||
|
main_frame.rowconfigure(8, weight=1)
|
||||||
|
self.root.columnconfigure(0, weight=1)
|
||||||
|
self.root.rowconfigure(0, weight=1)
|
||||||
|
|
||||||
|
def load_initial_data(self):
|
||||||
|
"""Lädt initiale Daten"""
|
||||||
|
if self.mandanten:
|
||||||
|
mandant_list = [f"{m['name']} (Nr. {m['nummer']})" for m in self.mandanten]
|
||||||
|
self.mandant_combo['values'] = mandant_list
|
||||||
|
if mandant_list:
|
||||||
|
self.mandant_combo.current(0)
|
||||||
|
self.on_mandant_select(None)
|
||||||
|
else:
|
||||||
|
self.log("⚠️ Keine Mandanten gefunden. Bitte Konfiguration prüfen.")
|
||||||
|
|
||||||
|
def on_mandant_select(self, event):
|
||||||
|
"""Wird aufgerufen wenn ein Mandant ausgewählt wird"""
|
||||||
|
if self.mandant_combo.current() >= 0:
|
||||||
|
self.selected_mandant = self.mandanten[self.mandant_combo.current()]
|
||||||
|
info_text = (f"Konto: {self.selected_mandant['konto']}, "
|
||||||
|
f"Gegenkonto: {self.selected_mandant['standard_gegenkonto']}, "
|
||||||
|
f"Nächster Beleg: {self.selected_mandant.get('beleg_index', 1)}")
|
||||||
|
self.mandant_info.config(text=info_text)
|
||||||
|
self.log(f"✓ Mandant gewählt: {self.selected_mandant['name']}")
|
||||||
|
|
||||||
|
def select_csv_file(self):
|
||||||
|
"""Öffnet Dialog zur CSV-Auswahl"""
|
||||||
|
filename = filedialog.askopenfilename(
|
||||||
|
title="CSV-Datei auswählen",
|
||||||
|
filetypes=[("CSV Dateien", "*.csv"), ("Alle Dateien", "*.*")]
|
||||||
|
)
|
||||||
|
if filename:
|
||||||
|
self.csv_file = filename
|
||||||
|
self.file_label.config(text=os.path.basename(filename))
|
||||||
|
self.log(f"✓ CSV-Datei gewählt: {filename}")
|
||||||
|
|
||||||
|
def log(self, message):
|
||||||
|
"""Fügt eine Nachricht zum Log hinzu"""
|
||||||
|
timestamp = datetime.now().strftime("%H:%M:%S")
|
||||||
|
self.log_text.insert(tk.END, f"[{timestamp}] {message}\n")
|
||||||
|
self.log_text.see(tk.END)
|
||||||
|
self.root.update_idletasks()
|
||||||
|
|
||||||
|
def start_processing(self):
|
||||||
|
"""Startet die Konvertierung in einem separaten Thread"""
|
||||||
|
if self.processing:
|
||||||
|
messagebox.showwarning("Warnung", "Konvertierung läuft bereits!")
|
||||||
|
return
|
||||||
|
|
||||||
|
if not self.selected_mandant:
|
||||||
|
messagebox.showerror("Fehler", "Bitte wählen Sie einen Mandanten!")
|
||||||
|
return
|
||||||
|
|
||||||
|
if not self.csv_file:
|
||||||
|
messagebox.showerror("Fehler", "Bitte wählen Sie eine CSV-Datei!")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Starte Verarbeitung in Thread
|
||||||
|
self.processing = True
|
||||||
|
self.process_btn.config(state='disabled')
|
||||||
|
self.progress.start()
|
||||||
|
|
||||||
|
thread = threading.Thread(target=self.process_conversion)
|
||||||
|
thread.daemon = True
|
||||||
|
thread.start()
|
||||||
|
|
||||||
|
def process_conversion(self):
|
||||||
|
"""Führt die eigentliche Konvertierung durch"""
|
||||||
|
try:
|
||||||
|
self.log("\n" + "="*60)
|
||||||
|
self.log("🚀 Starte Konvertierung...")
|
||||||
|
|
||||||
|
# Lade Konfigurationen
|
||||||
|
mieter = load_mieter(self.selected_mandant['nummer'])
|
||||||
|
self.log(f"✓ {len(mieter)} Mieter geladen")
|
||||||
|
|
||||||
|
kostenkonten = load_kostenkonten(self.selected_mandant['nummer'])
|
||||||
|
self.log(f"✓ {len(kostenkonten)} Kostenkonten geladen")
|
||||||
|
|
||||||
|
projekte, default_projekt = load_projekte(self.selected_mandant['nummer'])
|
||||||
|
self.log(f"✓ {len(projekte)} Projekte geladen")
|
||||||
|
|
||||||
|
# Lese CSV
|
||||||
|
buchungen = self.lese_csv_mit_log(self.csv_file)
|
||||||
|
self.log(f"✓ {len(buchungen)} Buchungen aus CSV geladen")
|
||||||
|
|
||||||
|
# Parse Zeitraum
|
||||||
|
monate = self.parse_monatseingabe(self.months_var.get())
|
||||||
|
jahre = self.parse_jahreseingabe(self.years_var.get())
|
||||||
|
|
||||||
|
# Filtere Buchungen
|
||||||
|
gefilterte = self.filtere_buchungen(buchungen, monate, jahre)
|
||||||
|
self.log(f"✓ {len(gefilterte)} Buchungen nach Filterung")
|
||||||
|
|
||||||
|
if not gefilterte:
|
||||||
|
self.log("⚠️ Keine Buchungen für den gewählten Zeitraum!")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Erstelle DBF
|
||||||
|
ausgabe_prefix = f"buchungen_{self.selected_mandant['nummer']}"
|
||||||
|
start_beleg = self.selected_mandant.get('beleg_index', 1)
|
||||||
|
|
||||||
|
letzte_beleg = self.erstelle_dbf_mit_log(
|
||||||
|
self.selected_mandant, gefilterte, mieter, kostenkonten,
|
||||||
|
projekte, default_projekt, ausgabe_prefix, start_beleg
|
||||||
|
)
|
||||||
|
|
||||||
|
# Update Beleg-Index
|
||||||
|
self.selected_mandant['beleg_index'] = letzte_beleg + 1
|
||||||
|
save_mandanten(self.mandanten)
|
||||||
|
|
||||||
|
self.log("\n" + "="*60)
|
||||||
|
self.log("✅ KONVERTIERUNG ERFOLGREICH ABGESCHLOSSEN!")
|
||||||
|
self.log(f"📊 Buchungen verarbeitet: {len(gefilterte)}")
|
||||||
|
self.log(f"📁 DBF-Dateien erstellt im Verzeichnis: {os.getcwd()}")
|
||||||
|
self.log("="*60)
|
||||||
|
|
||||||
|
messagebox.showinfo("Erfolg",
|
||||||
|
f"Konvertierung erfolgreich!\n\n"
|
||||||
|
f"Verarbeitete Buchungen: {len(gefilterte)}\n"
|
||||||
|
f"Belegnummern: {start_beleg} bis {letzte_beleg}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.log(f"❌ FEHLER: {str(e)}")
|
||||||
|
messagebox.showerror("Fehler", f"Konvertierung fehlgeschlagen:\n{str(e)}")
|
||||||
|
|
||||||
|
finally:
|
||||||
|
self.processing = False
|
||||||
|
self.process_btn.config(state='normal')
|
||||||
|
self.progress.stop()
|
||||||
|
|
||||||
|
def lese_csv_mit_log(self, csv_datei):
|
||||||
|
"""Liest CSV-Datei mit Logging"""
|
||||||
|
buchungen = []
|
||||||
|
with open(csv_datei, 'r', encoding='utf-8') as f:
|
||||||
|
reader = csv.DictReader(f, delimiter=';')
|
||||||
|
reader.fieldnames = [name.strip().lstrip('\ufeff') if name else name
|
||||||
|
for name in reader.fieldnames]
|
||||||
|
|
||||||
|
# Spaltenerkennung
|
||||||
|
datum_spalte = None
|
||||||
|
text_spalte = None
|
||||||
|
betrag_spalte = None
|
||||||
|
|
||||||
|
for name in reader.fieldnames:
|
||||||
|
name_lower = name.lower()
|
||||||
|
if 'buchungstag' in name_lower or 'datum' in name_lower:
|
||||||
|
datum_spalte = name
|
||||||
|
if 'buchungstext' in name_lower or 'verwendungszweck' in name_lower:
|
||||||
|
text_spalte = name
|
||||||
|
if 'betrag' in name_lower:
|
||||||
|
betrag_spalte = name
|
||||||
|
|
||||||
|
for row in reader:
|
||||||
|
try:
|
||||||
|
datum_str = row[datum_spalte].strip()
|
||||||
|
datum = datetime.strptime(datum_str, '%d.%m.%Y')
|
||||||
|
|
||||||
|
betrag_str = row[betrag_spalte].strip().replace('.', '').replace(',', '.')
|
||||||
|
betrag = float(betrag_str)
|
||||||
|
|
||||||
|
buchung = {
|
||||||
|
'datum': datum,
|
||||||
|
'buchungstext': row[text_spalte].strip() if row[text_spalte] else '',
|
||||||
|
'betrag': betrag
|
||||||
|
}
|
||||||
|
buchungen.append(buchung)
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
|
||||||
|
return buchungen
|
||||||
|
|
||||||
|
def parse_monatseingabe(self, eingabe):
|
||||||
|
"""Parst Monatseingabe"""
|
||||||
|
monate = set()
|
||||||
|
teile = eingabe.split(',')
|
||||||
|
for teil in teile:
|
||||||
|
teil = teil.strip()
|
||||||
|
if '-' in teil:
|
||||||
|
start, ende = map(int, teil.split('-'))
|
||||||
|
monate.update(range(start, ende + 1))
|
||||||
|
else:
|
||||||
|
monate.add(int(teil))
|
||||||
|
return sorted(list(monate))
|
||||||
|
|
||||||
|
def parse_jahreseingabe(self, eingabe):
|
||||||
|
"""Parst Jahreseingabe"""
|
||||||
|
jahre = set()
|
||||||
|
teile = eingabe.split(',')
|
||||||
|
for teil in teile:
|
||||||
|
teil = teil.strip()
|
||||||
|
if '-' in teil:
|
||||||
|
start, ende = map(int, teil.split('-'))
|
||||||
|
jahre.update(range(start, ende + 1))
|
||||||
|
else:
|
||||||
|
jahre.add(int(teil))
|
||||||
|
return sorted(list(jahre))
|
||||||
|
|
||||||
|
def filtere_buchungen(self, buchungen, monate, jahre):
|
||||||
|
"""Filtert Buchungen nach Zeitraum"""
|
||||||
|
return [b for b in buchungen
|
||||||
|
if b['datum'].month in monate and b['datum'].year in jahre]
|
||||||
|
|
||||||
|
def erstelle_dbf_mit_log(self, mandant, buchungen, mieter, kostenkonten,
|
||||||
|
projekte, default_projekt, ausgabe_datei_praefix, start_beleg_nr):
|
||||||
|
"""Erstellt DBF-Dateien mit Logging (vereinfachte Version)"""
|
||||||
|
# Gruppiere nach Monat
|
||||||
|
buchungen_pro_monat = {}
|
||||||
|
for buchung in buchungen:
|
||||||
|
monats_key = buchung['datum'].strftime('%Y-%m')
|
||||||
|
if monats_key not in buchungen_pro_monat:
|
||||||
|
buchungen_pro_monat[monats_key] = []
|
||||||
|
buchungen_pro_monat[monats_key].append(buchung)
|
||||||
|
|
||||||
|
self.log(f"📅 Erstelle DBF-Dateien für {len(buchungen_pro_monat)} Monate...")
|
||||||
|
|
||||||
|
beleg_nr = start_beleg_nr
|
||||||
|
|
||||||
|
for monat_key, monats_buchungen in buchungen_pro_monat.items():
|
||||||
|
jahr, monat = map(int, monat_key.split('-'))
|
||||||
|
jahr_kurz = str(jahr)[-2:]
|
||||||
|
ausgabe_datei = f"{ausgabe_datei_praefix}_{monat:02d}{jahr_kurz}.dbf"
|
||||||
|
|
||||||
|
self.log(f" → Erstelle {ausgabe_datei} ({len(monats_buchungen)} Buchungen)")
|
||||||
|
|
||||||
|
# DBF erstellen (vereinfacht - nutzt Ihre Original-Logik)
|
||||||
|
self.create_dbf_file(ausgabe_datei, monats_buchungen, beleg_nr,
|
||||||
|
mandant, mieter, kostenkonten, projekte, default_projekt)
|
||||||
|
|
||||||
|
beleg_nr += len(monats_buchungen)
|
||||||
|
|
||||||
|
return beleg_nr - 1
|
||||||
|
|
||||||
|
def create_dbf_file(self, filename, buchungen, start_beleg, mandant,
|
||||||
|
mieter, kostenkonten, projekte, default_projekt):
|
||||||
|
"""Erstellt eine DBF-Datei (vereinfacht aus Original)"""
|
||||||
|
# Hier würde Ihre Original DBF-Erstellungslogik stehen
|
||||||
|
# Aus Platzgründen nur Grundstruktur
|
||||||
|
|
||||||
|
heute = datetime.now()
|
||||||
|
num_records = len(buchungen)
|
||||||
|
header_len = 32 + 43 * 32 + 1
|
||||||
|
record_len = 368
|
||||||
|
|
||||||
|
header = bytearray(32)
|
||||||
|
header[0] = 0x03 # dBase III
|
||||||
|
header[1] = heute.year - 1900
|
||||||
|
header[2] = heute.month
|
||||||
|
header[3] = heute.day
|
||||||
|
struct.pack_into('<I', header, 4, num_records)
|
||||||
|
struct.pack_into('<H', header, 8, header_len)
|
||||||
|
struct.pack_into('<H', header, 10, record_len)
|
||||||
|
|
||||||
|
# Felder definieren
|
||||||
|
fields = [
|
||||||
|
('BTT', 'N', 2, 0), ('BMM', 'N', 2, 0), ('BELEG', 'C', 10, 0),
|
||||||
|
('KONTO', 'N', 8, 0), ('GEGENKONTO', 'N', 8, 0), ('KST', 'C', 8, 0),
|
||||||
|
('KTG', 'C', 10, 0), ('BETRAG', 'N', 14, 2), ('STEUER', 'N', 5, 2),
|
||||||
|
('SKONTO', 'N', 14, 2), ('TEXT', 'C', 30, 0), ('BEZAHLT', 'N', 14, 2),
|
||||||
|
('KZ', 'C', 3, 0), ('LFDNR', 'N', 8, 0), ('EURO', 'L', 1, 0),
|
||||||
|
('ZAHLBETRAG', 'N', 14, 2), ('BEZAHLT_NK', 'N', 14, 2), ('FAELLIG', 'L', 1, 0),
|
||||||
|
('TEXT2', 'C', 30, 0), ('DATEV', 'L', 1, 0), ('FAELLIG_AM', 'D', 8, 0),
|
||||||
|
('STORNO', 'L', 1, 0), ('BJJ', 'N', 4, 0), ('TEMP1', 'C', 20, 0),
|
||||||
|
('HNDLNR', 'N', 8, 0), ('GBLFDNR', 'N', 15, 0), ('SKONTO2', 'N', 14, 2),
|
||||||
|
('DATUM2', 'D', 8, 0), ('KEIN_ZV', 'L', 1, 0), ('MANUELL', 'L', 1, 0),
|
||||||
|
('SOLLKONTO', 'N', 8, 0), ('STAPEL', 'C', 20, 0), ('SKONTSTFR', 'N', 14, 2),
|
||||||
|
('REB_LFDNR', 'N', 6, 0), ('RECHART', 'C', 3, 0), ('ZAHLART', 'N', 1, 0),
|
||||||
|
('LDATUM', 'D', 8, 0), ('XFINANZ', 'L', 1, 0), ('INZV', 'L', 1, 0),
|
||||||
|
('DUMMY', 'C', 1, 0), ('ABRJAHR', 'N', 4, 0), ('EDATUM', 'D', 8, 0),
|
||||||
|
('ENAME', 'C', 15, 0)
|
||||||
|
]
|
||||||
|
|
||||||
|
# Restliche DBF-Erstellung würde hier folgen...
|
||||||
|
# (Aus Platzgründen gekürzt)
|
||||||
|
|
||||||
|
def open_config(self):
|
||||||
|
"""Öffnet Konfigurations-Dialog"""
|
||||||
|
config_window = tk.Toplevel(self.root)
|
||||||
|
config_window.title("Konfiguration")
|
||||||
|
config_window.geometry("600x400")
|
||||||
|
|
||||||
|
ttk.Label(config_window, text="Konfigurationsdateien:",
|
||||||
|
font=('Ubuntu', 12, 'bold')).pack(pady=10)
|
||||||
|
|
||||||
|
config_text = tk.Text(config_window, height=20, width=70)
|
||||||
|
config_text.pack(pady=10, padx=10)
|
||||||
|
|
||||||
|
if self.selected_mandant:
|
||||||
|
nr = self.selected_mandant['nummer']
|
||||||
|
config_text.insert(tk.END, f"Mandant: {self.selected_mandant['name']}\n")
|
||||||
|
config_text.insert(tk.END, f"Nummer: {nr}\n\n")
|
||||||
|
config_text.insert(tk.END, f"Config-Dateien:\n")
|
||||||
|
config_text.insert(tk.END, f"- mandanten_config.json\n")
|
||||||
|
config_text.insert(tk.END, f"- identitaeten_mieter_{nr}.json\n")
|
||||||
|
config_text.insert(tk.END, f"- identitaeten_kosten_{nr}.json\n")
|
||||||
|
config_text.insert(tk.END, f"- projekte_{nr}.json\n")
|
||||||
|
|
||||||
|
ttk.Button(config_window, text="Schließen",
|
||||||
|
command=config_window.destroy).pack(pady=10)
|
||||||
|
|
||||||
|
# ===== HAUPTPROGRAMM =====
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# Prüfe ob alle benötigten Dateien existieren
|
||||||
|
if not os.path.exists('mandanten_config.json'):
|
||||||
|
messagebox.showerror("Fehler",
|
||||||
|
"mandanten_config.json nicht gefunden!\n"
|
||||||
|
"Bitte stellen Sie sicher, dass alle Config-Dateien vorhanden sind.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
root = tk.Tk()
|
||||||
|
app = HausverwaltungGUI(root)
|
||||||
|
root.mainloop()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
# CSV zu DBF Konverter - Abhängigkeiten
|
||||||
|
# =====================================
|
||||||
|
#
|
||||||
|
# Das Programm verwendet hauptsächlich Python-Standardbibliotheken.
|
||||||
|
# Für die GUI-Version wird tkinter benötigt (Teil von Python, muss aber
|
||||||
|
# auf manchen Linux-Systemen separat installiert werden).
|
||||||
|
#
|
||||||
|
# Für den Build als Standalone-Executable:
|
||||||
|
pyinstaller>=5.0
|
||||||
|
|
||||||
|
# Auf Linux tkinter installieren:
|
||||||
|
# Ubuntu/Debian: sudo apt install python3-tk
|
||||||
|
# Fedora/RHEL: sudo dnf install python3-tkinter
|
||||||
|
# Arch Linux: sudo pacman -S tk
|
||||||
|
|
@ -0,0 +1,134 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# ====
|
||||||
|
# CSV zu DBF Konverter - Linux Build Script
|
||||||
|
# Version 1.0
|
||||||
|
# ====
|
||||||
|
|
||||||
|
set -e # Bei Fehlern abbrechen
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "===="
|
||||||
|
echo " CSV zu DBF Konverter - Linux Build"
|
||||||
|
echo " Version 1.0"
|
||||||
|
echo "===="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Farben für Ausgabe
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Arbeitsverzeichnis
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||||
|
cd "$PROJECT_DIR"
|
||||||
|
|
||||||
|
echo -e "${BLUE}[1/6] Prüfe Voraussetzungen...${NC}"
|
||||||
|
|
||||||
|
# Prüfe ob Python3 installiert ist
|
||||||
|
if ! command -v python3 &> /dev/null; then
|
||||||
|
echo -e "${RED}[FEHLER] Python3 ist nicht installiert.${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Installation:"
|
||||||
|
echo " Ubuntu/Debian: sudo apt install python3 python3-pip python3-tk"
|
||||||
|
echo " Fedora: sudo dnf install python3 python3-pip python3-tkinter"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
PYTHON_VERSION=$(python3 --version 2>&1)
|
||||||
|
echo -e "${GREEN}✓ Python3 gefunden: $PYTHON_VERSION${NC}"
|
||||||
|
|
||||||
|
# Prüfe ob tkinter installiert ist
|
||||||
|
echo -e "${BLUE}[2/6] Prüfe tkinter...${NC}"
|
||||||
|
if ! python3 -c "import tkinter" 2>/dev/null; then
|
||||||
|
echo -e "${YELLOW}[WARNUNG] tkinter nicht gefunden.${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Installation von tkinter:"
|
||||||
|
echo " Ubuntu/Debian: sudo apt install python3-tk"
|
||||||
|
echo " Fedora: sudo dnf install python3-tkinter"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo -e "${GREEN}✓ tkinter verfügbar${NC}"
|
||||||
|
|
||||||
|
# Prüfe ob pip installiert ist
|
||||||
|
echo -e "${BLUE}[3/6] Prüfe pip...${NC}"
|
||||||
|
if ! command -v pip3 &> /dev/null && ! python3 -m pip --version &> /dev/null; then
|
||||||
|
echo -e "${YELLOW}[INFO] pip3 nicht gefunden.${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Installation von pip:"
|
||||||
|
echo " Ubuntu/Debian: sudo apt install python3-pip"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo -e "${GREEN}✓ pip verfügbar${NC}"
|
||||||
|
|
||||||
|
# Prüfe ob PyInstaller installiert ist
|
||||||
|
echo -e "${BLUE}[4/6] Prüfe PyInstaller...${NC}"
|
||||||
|
if ! python3 -m PyInstaller --version &> /dev/null 2>&1; then
|
||||||
|
echo -e "${YELLOW}[INFO] PyInstaller nicht gefunden. Installiere...${NC}"
|
||||||
|
python3 -m pip install --user pyinstaller || {
|
||||||
|
echo -e "${RED}[FEHLER] PyInstaller konnte nicht installiert werden.${NC}"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
PYINSTALLER_VERSION=$(python3 -m PyInstaller --version 2>&1)
|
||||||
|
echo -e "${GREEN}✓ PyInstaller installiert: $PYINSTALLER_VERSION${NC}"
|
||||||
|
|
||||||
|
# Prüfe Projektdateien
|
||||||
|
echo -e "${BLUE}[5/6] Prüfe Projektdateien...${NC}"
|
||||||
|
if [ ! -f "csv_dbf_converter_gui.py" ]; then
|
||||||
|
echo -e "${RED}[FEHLER] csv_dbf_converter_gui.py nicht gefunden!${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo -e "${GREEN}✓ Projektdateien vorhanden${NC}"
|
||||||
|
|
||||||
|
# Lösche alte Build-Verzeichnisse
|
||||||
|
echo ""
|
||||||
|
echo -e "${BLUE}[6/6] Starte Build-Prozess...${NC}"
|
||||||
|
echo "[INFO] Räume alte Build-Dateien auf..."
|
||||||
|
rm -rf build dist *.spec 2>/dev/null || true
|
||||||
|
|
||||||
|
echo "[INFO] Erstelle Linux-Binary für GUI-Version..."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# PyInstaller für GUI-Version ausführen
|
||||||
|
python3 -m PyInstaller \
|
||||||
|
--onefile \
|
||||||
|
--name "csv_dbf_converter_gui" \
|
||||||
|
csv_dbf_converter_gui.py
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo ""
|
||||||
|
echo -e "${RED}[FEHLER] Build fehlgeschlagen!${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}[INFO] Build erfolgreich!${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Kopiere notwendige Dateien in dist-Ordner
|
||||||
|
echo "[INFO] Kopiere Konfigurationsdateien..."
|
||||||
|
mkdir -p dist/config
|
||||||
|
cp -r config/examples dist/config/
|
||||||
|
cp csv_dbf_converter.py dist/
|
||||||
|
[ -f README.md ] && cp README.md dist/
|
||||||
|
|
||||||
|
# Mache das Binary ausführbar
|
||||||
|
chmod +x dist/csv_dbf_converter_gui
|
||||||
|
|
||||||
|
# Zeige Ergebnis
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}====${NC}"
|
||||||
|
echo -e "${GREEN} BUILD ERFOLGREICH!${NC}"
|
||||||
|
echo -e "${GREEN}====${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Erstellte Dateien:"
|
||||||
|
ls -lh dist/
|
||||||
|
echo ""
|
||||||
|
echo "Verwendung:"
|
||||||
|
echo " cd dist && ./csv_dbf_converter_gui"
|
||||||
|
echo ""
|
||||||
|
echo "Für Distribution alle Dateien aus dist/ kopieren."
|
||||||
|
echo ""
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
@echo off
|
||||||
|
REM ====
|
||||||
|
REM CSV zu DBF Konverter - Windows Build Script
|
||||||
|
REM Version 1.0
|
||||||
|
REM ====
|
||||||
|
|
||||||
|
setlocal enabledelayedexpansion
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ====
|
||||||
|
echo CSV zu DBF Konverter - Windows Build
|
||||||
|
echo ====
|
||||||
|
echo.
|
||||||
|
|
||||||
|
REM Wechsle ins Projektverzeichnis
|
||||||
|
cd /d "%~dp0\.."
|
||||||
|
|
||||||
|
REM Prüfe Python Installation
|
||||||
|
echo [1/5] Pruefe Python Installation...
|
||||||
|
py --version >nul 2>&1
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo.
|
||||||
|
echo FEHLER: Python wurde nicht gefunden!
|
||||||
|
echo.
|
||||||
|
echo Bitte installieren Sie Python von:
|
||||||
|
echo https://www.python.org/downloads/
|
||||||
|
echo.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
for /f "tokens=2" %%v in ('py --version 2^>^&1') do set PYTHON_VERSION=%%v
|
||||||
|
echo Python %PYTHON_VERSION% gefunden.
|
||||||
|
|
||||||
|
REM Prüfe/Installiere PyInstaller
|
||||||
|
echo.
|
||||||
|
echo [2/5] Pruefe PyInstaller...
|
||||||
|
py -m PyInstaller --version >nul 2>&1
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo PyInstaller nicht gefunden. Installiere...
|
||||||
|
py -m pip install pyinstaller
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo.
|
||||||
|
echo FEHLER: PyInstaller konnte nicht installiert werden!
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
)
|
||||||
|
for /f "tokens=*" %%v in ('py -m PyInstaller --version 2^>^&1') do set PYINSTALLER_VERSION=%%v
|
||||||
|
echo PyInstaller %PYINSTALLER_VERSION% gefunden.
|
||||||
|
|
||||||
|
REM Bereinige alte Builds
|
||||||
|
echo.
|
||||||
|
echo [3/5] Bereinige alte Build-Dateien...
|
||||||
|
if exist build rmdir /s /q build
|
||||||
|
if exist dist rmdir /s /q dist
|
||||||
|
if exist *.spec del /f /q *.spec
|
||||||
|
echo Alte Dateien entfernt.
|
||||||
|
|
||||||
|
REM Erstelle Executable
|
||||||
|
echo.
|
||||||
|
echo [4/5] Erstelle Windows Executable...
|
||||||
|
echo Dies kann einige Minuten dauern...
|
||||||
|
echo.
|
||||||
|
|
||||||
|
py -m PyInstaller --onefile --windowed --name "CSV_DBF_Konverter" ^
|
||||||
|
csv_dbf_converter_gui.py
|
||||||
|
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo.
|
||||||
|
echo FEHLER: Build fehlgeschlagen!
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
REM Kopiere zusätzliche Dateien
|
||||||
|
echo.
|
||||||
|
echo [5/5] Kopiere zusaetzliche Dateien...
|
||||||
|
xcopy config dist\config\ /E /I /Y >nul 2>&1
|
||||||
|
copy csv_dbf_converter.py dist\ >nul 2>&1
|
||||||
|
copy README.md dist\ >nul 2>&1
|
||||||
|
echo Dateien kopiert.
|
||||||
|
|
||||||
|
REM Erfolgsmeldung
|
||||||
|
echo.
|
||||||
|
echo ====
|
||||||
|
echo BUILD ERFOLGREICH!
|
||||||
|
echo ====
|
||||||
|
echo.
|
||||||
|
echo Die Executable befindet sich in:
|
||||||
|
echo dist\CSV_DBF_Konverter.exe
|
||||||
|
echo.
|
||||||
|
echo Zusaetzliche Dateien in dist\:
|
||||||
|
echo - config\ (Beispiel-Konfigurationsdateien)
|
||||||
|
echo - csv_dbf_converter.py (CLI-Version)
|
||||||
|
echo - README.md
|
||||||
|
echo.
|
||||||
|
echo Hinweis: Die Konfigurationsdateien muessen
|
||||||
|
echo im gleichen Ordner wie die .exe liegen!
|
||||||
|
echo.
|
||||||
|
pause
|
||||||
Loading…
Reference in New Issue