Generalize email system with invoice workflow and Stiftungsgeschichte category

- Rename DestinataerEmailEingang → EmailEingang with category support
  (destinataer, rechnung, land_pacht, stiftungsgeschichte, allgemein)
- Add invoice capture workflow: create Verwaltungskosten from email,
  link DMS documents as invoice attachments, track payment status
- Add Stiftungsgeschichte email category with auto-detection patterns
  (Ahnenforschung, Genealogie, Chronik, etc.) and DMS integration
- Update poll_emails task with category detection and DMS context mapping
- Show available history documents in Geschichte editor sidebar
- Consolidate DMS views, remove legacy dokument templates
- Update all detail/form templates for DMS document linking
- Add deploy.sh script and streamline compose.yml

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
SysAdmin Agent
2026-03-12 10:17:14 +00:00
parent f4fc512ad3
commit e6f4c5ba1b
44 changed files with 1076 additions and 3428 deletions

View File

@@ -0,0 +1,33 @@
# Generated by Django 5.0.6 on 2026-03-12 08:11
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('stiftung', '0048_phase3_dms_dokument_datei'),
]
operations = [
migrations.AddField(
model_name='destinataeremaileingang',
name='dokument_dateien',
field=models.ManyToManyField(blank=True, help_text='Automatisch befüllte Anhänge als Django-DMS-Dateien.', related_name='email_eingaenge', to='stiftung.dokumentdatei', verbose_name='DMS-Dokumente (Anhänge)'),
),
migrations.AlterField(
model_name='appconfiguration',
name='category',
field=models.CharField(choices=[('paperless', 'Paperless Integration'), ('email', 'E-Mail / IMAP'), ('general', 'General Settings'), ('corporate', 'Corporate Identity'), ('notifications', 'Notifications'), ('system', 'System Settings')], default='general', max_length=50, verbose_name='Category'),
),
migrations.AlterField(
model_name='appconfiguration',
name='setting_type',
field=models.CharField(choices=[('text', 'Text'), ('password', 'Password'), ('number', 'Number'), ('boolean', 'Boolean'), ('url', 'URL'), ('tag', 'Tag Name'), ('tag_id', 'Tag ID')], default='text', max_length=20, verbose_name='Type'),
),
migrations.AlterField(
model_name='destinataeremaileingang',
name='paperless_dokument_ids',
field=models.JSONField(blank=True, default=list, help_text='Veraltet wird nach vollständiger Migration entfernt. Neue Anhänge in dokument_dateien.', verbose_name='Paperless Dokument-IDs (Anhänge, veraltet)'),
),
]

View File

@@ -0,0 +1,132 @@
# Phase 4: Generalize EmailEingang + Rechnungsworkflow
# - Rename DestinataerEmailEingang → EmailEingang
# - Add kategorie, verwaltungskosten FK, land FK, verpachtung FK
# - Expand status choices (rechnung_erfasst, zahlung_gebucht)
# - Add verwaltungskosten FK to DokumentDatei
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('stiftung', '0049_phase3_email_dms_m2m'),
]
operations = [
# 1. Rename model (preserves DB table, updates Django state)
migrations.RenameModel(
old_name='DestinataerEmailEingang',
new_name='EmailEingang',
),
# 2. Add kategorie field to EmailEingang
migrations.AddField(
model_name='emaileingang',
name='kategorie',
field=models.CharField(
choices=[
('destinataer', 'Destinataer'),
('rechnung', 'Rechnung'),
('land_pacht', 'Grundstueck / Pacht'),
('allgemein', 'Allgemein'),
],
default='allgemein',
max_length=20,
verbose_name='Kategorie',
),
),
# 3. Add verwaltungskosten FK to EmailEingang
migrations.AddField(
model_name='emaileingang',
name='verwaltungskosten',
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name='email_eingaenge',
to='stiftung.verwaltungskosten',
verbose_name='Verwaltungskosten / Rechnung',
),
),
# 4. Add land FK to EmailEingang
migrations.AddField(
model_name='emaileingang',
name='land',
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name='email_eingaenge',
to='stiftung.land',
verbose_name='Laenderei',
),
),
# 5. Add verpachtung FK to EmailEingang
migrations.AddField(
model_name='emaileingang',
name='verpachtung',
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name='email_eingaenge',
to='stiftung.landverpachtung',
verbose_name='Verpachtung',
),
),
# 6. Update status choices on EmailEingang
migrations.AlterField(
model_name='emaileingang',
name='status',
field=models.CharField(
choices=[
('neu', 'Neu / Unbearbeitet'),
('zugewiesen', 'Destinataer zugewiesen'),
('verarbeitet', 'Verarbeitet'),
('rechnung_erfasst', 'Rechnung erfasst'),
('zahlung_gebucht', 'Zahlung gebucht'),
('unbekannt', 'Unbekannter Absender'),
('fehler', 'Fehler bei Verarbeitung'),
],
default='neu',
max_length=20,
verbose_name='Status',
),
),
# 7. Update Meta on EmailEingang
migrations.AlterModelOptions(
name='emaileingang',
options={
'ordering': ['-eingangsdatum'],
'verbose_name': 'E-Mail-Eingang',
'verbose_name_plural': 'E-Mail-Eingaenge',
},
),
# 8. Set kategorie='destinataer' for existing emails that have a destinataer FK
migrations.RunSQL(
sql="UPDATE stiftung_emaileingang SET kategorie = 'destinataer' WHERE destinataer_id IS NOT NULL;",
reverse_sql=migrations.RunSQL.noop,
),
# 9. Add verwaltungskosten FK to DokumentDatei
migrations.AddField(
model_name='dokumentdatei',
name='verwaltungskosten',
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name='dms_dokumente',
to='stiftung.verwaltungskosten',
verbose_name='Verwaltungskosten / Rechnung',
),
),
]

View File

@@ -0,0 +1,29 @@
# Generated by Django 5.0.6 on 2026-03-12 09:45
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('stiftung', '0050_generalize_email_rechnungsworkflow'),
]
operations = [
migrations.AlterField(
model_name='emaileingang',
name='destinataer',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='email_eingaenge', to='stiftung.destinataer', verbose_name='Destinataer'),
),
migrations.AlterField(
model_name='emaileingang',
name='dokument_dateien',
field=models.ManyToManyField(blank=True, help_text='Automatisch befuellte Anhaenge als Django-DMS-Dateien.', related_name='email_eingaenge', to='stiftung.dokumentdatei', verbose_name='DMS-Dokumente (Anhaenge)'),
),
migrations.AlterField(
model_name='emaileingang',
name='paperless_dokument_ids',
field=models.JSONField(blank=True, default=list, help_text='Veraltet wird nach vollstaendiger Migration entfernt. Neue Anhaenge in dokument_dateien.', verbose_name='Paperless Dokument-IDs (Anhaenge, veraltet)'),
),
]

View File

@@ -0,0 +1,23 @@
# Generated by Django 5.0.6 on 2026-03-12 10:07
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('stiftung', '0051_alter_emaileingang_destinataer_and_more'),
]
operations = [
migrations.AlterField(
model_name='dokumentdatei',
name='kontext',
field=models.CharField(choices=[('pachtvertrag', 'Pachtvertrag'), ('antrag', 'Antrag / Förderantrag'), ('verwendungsnachweis', 'Verwendungsnachweis'), ('studiennachweis', 'Studiennachweis'), ('rechnung', 'Rechnung'), ('vertrag', 'Vertrag'), ('bericht', 'Bericht'), ('landkarte', 'Landkarte / Kataster'), ('korrespondenz', 'Korrespondenz / Brief'), ('bescheid', 'Bescheid / Behörde'), ('stiftungsgeschichte', 'Stiftungsgeschichte / Archiv'), ('anderes', 'Sonstiges')], default='anderes', max_length=30, verbose_name='Dokumententyp'),
),
migrations.AlterField(
model_name='emaileingang',
name='kategorie',
field=models.CharField(choices=[('destinataer', 'Destinataer'), ('rechnung', 'Rechnung'), ('land_pacht', 'Grundstueck / Pacht'), ('stiftungsgeschichte', 'Stiftungsgeschichte'), ('allgemein', 'Allgemein')], default='allgemein', max_length=20, verbose_name='Kategorie'),
),
]