remove lexoffice name in project
This commit is contained in:
parent
b93d9e4def
commit
f74f936d72
|
|
@ -19,7 +19,7 @@ DEFAULT_SETTINGS = {
|
||||||
"smtp_ssl": "starttls",
|
"smtp_ssl": "starttls",
|
||||||
"smtp_username": "",
|
"smtp_username": "",
|
||||||
"smtp_password": "",
|
"smtp_password": "",
|
||||||
"lexoffice_email": "",
|
"import_email": "",
|
||||||
"source_folder": "Rechnungen",
|
"source_folder": "Rechnungen",
|
||||||
"processed_folder": "Rechnungen/Verarbeitet",
|
"processed_folder": "Rechnungen/Verarbeitet",
|
||||||
"interval_minutes": "5",
|
"interval_minutes": "5",
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ def _build_forward_email(
|
||||||
msg["Subject"] = f"Belegimport: {original_subject}"
|
msg["Subject"] = f"Belegimport: {original_subject}"
|
||||||
|
|
||||||
body = (
|
body = (
|
||||||
f"Automatisch weitergeleitet von LexOffice Belegimport.\n"
|
f"Automatisch weitergeleitet von Belegimport.\n"
|
||||||
f"Original-Absender: {original_from}\n"
|
f"Original-Absender: {original_from}\n"
|
||||||
f"Original-Betreff: {original_subject}\n"
|
f"Original-Betreff: {original_subject}\n"
|
||||||
f"Anzahl Anhänge: {len(attachments)}"
|
f"Anzahl Anhänge: {len(attachments)}"
|
||||||
|
|
@ -121,13 +121,13 @@ def _move_email(conn: imaplib.IMAP4, msg_uid: bytes, dest_folder: str):
|
||||||
async def process_mailbox() -> dict:
|
async def process_mailbox() -> dict:
|
||||||
settings = await get_settings()
|
settings = await get_settings()
|
||||||
|
|
||||||
if not settings.get("imap_server") or not settings.get("lexoffice_email"):
|
if not settings.get("imap_server") or not settings.get("import_email"):
|
||||||
logger.warning("IMAP oder LexOffice-Email nicht konfiguriert")
|
logger.warning("IMAP oder Import-Email nicht konfiguriert")
|
||||||
return {"processed": 0, "skipped": 0, "errors": 0, "error": "Nicht konfiguriert"}
|
return {"processed": 0, "skipped": 0, "errors": 0, "error": "Nicht konfiguriert"}
|
||||||
|
|
||||||
source_folder = settings.get("source_folder", "INBOX")
|
source_folder = settings.get("source_folder", "INBOX")
|
||||||
processed_folder = settings.get("processed_folder", "INBOX/Verarbeitet")
|
processed_folder = settings.get("processed_folder", "INBOX/Verarbeitet")
|
||||||
lexoffice_email = settings["lexoffice_email"]
|
import_email = settings["import_email"]
|
||||||
smtp_from = settings.get("smtp_username", "")
|
smtp_from = settings.get("smtp_username", "")
|
||||||
|
|
||||||
processed = 0
|
processed = 0
|
||||||
|
|
@ -187,7 +187,7 @@ async def process_mailbox() -> dict:
|
||||||
|
|
||||||
forward_msg = _build_forward_email(
|
forward_msg = _build_forward_email(
|
||||||
from_addr=smtp_from,
|
from_addr=smtp_from,
|
||||||
to_addr=lexoffice_email,
|
to_addr=import_email,
|
||||||
original_subject=subject,
|
original_subject=subject,
|
||||||
original_from=from_addr,
|
original_from=from_addr,
|
||||||
attachments=attachments,
|
attachments=attachments,
|
||||||
|
|
@ -259,18 +259,18 @@ async def process_mailbox() -> dict:
|
||||||
async def send_test_email() -> dict:
|
async def send_test_email() -> dict:
|
||||||
settings = await get_settings()
|
settings = await get_settings()
|
||||||
|
|
||||||
if not settings.get("smtp_server") or not settings.get("lexoffice_email"):
|
if not settings.get("smtp_server") or not settings.get("import_email"):
|
||||||
return {"success": False, "error": "SMTP oder LexOffice-Email nicht konfiguriert"}
|
return {"success": False, "error": "SMTP oder Import-Email nicht konfiguriert"}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
smtp_conn = _connect_smtp(settings)
|
smtp_conn = _connect_smtp(settings)
|
||||||
|
|
||||||
msg = MIMEMultipart()
|
msg = MIMEMultipart()
|
||||||
msg["From"] = settings["smtp_username"]
|
msg["From"] = settings["smtp_username"]
|
||||||
msg["To"] = settings["lexoffice_email"]
|
msg["To"] = settings["import_email"]
|
||||||
msg["Subject"] = "LexOffice Belegimport - Test-Email"
|
msg["Subject"] = "Belegimport - Test-Email"
|
||||||
msg.attach(MIMEText(
|
msg.attach(MIMEText(
|
||||||
"Dies ist eine Test-Email vom LexOffice Belegimport Service.\n"
|
"Dies ist eine Test-Email vom Belegimport Service.\n"
|
||||||
"Wenn Sie diese Email erhalten, funktioniert die SMTP-Verbindung.",
|
"Wenn Sie diese Email erhalten, funktioniert die SMTP-Verbindung.",
|
||||||
"plain",
|
"plain",
|
||||||
"utf-8",
|
"utf-8",
|
||||||
|
|
|
||||||
10
app/main.py
10
app/main.py
|
|
@ -32,12 +32,12 @@ async def lifespan(app: FastAPI):
|
||||||
interval = int(settings.get("interval_minutes", 5))
|
interval = int(settings.get("interval_minutes", 5))
|
||||||
enabled = settings.get("scheduler_enabled", "false") == "true"
|
enabled = settings.get("scheduler_enabled", "false") == "true"
|
||||||
configure_job(interval, enabled)
|
configure_job(interval, enabled)
|
||||||
logger.info("LexOffice Belegimport gestartet")
|
logger.info("Belegimport gestartet")
|
||||||
yield
|
yield
|
||||||
logger.info("LexOffice Belegimport beendet")
|
logger.info("Belegimport beendet")
|
||||||
|
|
||||||
|
|
||||||
app = FastAPI(title="LexOffice Belegimport", lifespan=lifespan)
|
app = FastAPI(title="Belegimport", lifespan=lifespan)
|
||||||
app.mount("/static", StaticFiles(directory="app/static"), name="static")
|
app.mount("/static", StaticFiles(directory="app/static"), name="static")
|
||||||
templates = Jinja2Templates(directory="app/templates")
|
templates = Jinja2Templates(directory="app/templates")
|
||||||
|
|
||||||
|
|
@ -73,7 +73,7 @@ async def _save_form_settings(request: Request) -> dict:
|
||||||
"smtp_ssl": form.get("smtp_ssl", "starttls"),
|
"smtp_ssl": form.get("smtp_ssl", "starttls"),
|
||||||
"smtp_username": form.get("smtp_username", ""),
|
"smtp_username": form.get("smtp_username", ""),
|
||||||
"smtp_password": form.get("smtp_password") or current.get("smtp_password", ""),
|
"smtp_password": form.get("smtp_password") or current.get("smtp_password", ""),
|
||||||
"lexoffice_email": form.get("lexoffice_email", ""),
|
"import_email": form.get("import_email", ""),
|
||||||
"source_folder": form.get("source_folder", "Rechnungen"),
|
"source_folder": form.get("source_folder", "Rechnungen"),
|
||||||
"processed_folder": form.get("processed_folder", "Rechnungen/Verarbeitet"),
|
"processed_folder": form.get("processed_folder", "Rechnungen/Verarbeitet"),
|
||||||
"interval_minutes": form.get("interval_minutes", "5"),
|
"interval_minutes": form.get("interval_minutes", "5"),
|
||||||
|
|
@ -323,5 +323,5 @@ async def separator_pdf():
|
||||||
return Response(
|
return Response(
|
||||||
content=pdf_bytes,
|
content=pdf_bytes,
|
||||||
media_type="application/pdf",
|
media_type="application/pdf",
|
||||||
headers={"Content-Disposition": "attachment; filename=Trennseite_LexOffice.pdf"},
|
headers={"Content-Disposition": "attachment; filename=Trennseite.pdf"},
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ from app.mail_processor import _connect_smtp, _build_forward_email
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
SEPARATOR_QR_CONTENT = "LEXOFFICE-TRENNUNG"
|
SEPARATOR_QR_CONTENT = "BELEGIMPORT-TRENNUNG"
|
||||||
UPLOAD_DIR = Path(os.environ.get("UPLOAD_DIR", "/data/uploads"))
|
UPLOAD_DIR = Path(os.environ.get("UPLOAD_DIR", "/data/uploads"))
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -85,11 +85,11 @@ def split_pdf(pdf_path: str, separator_pages: list[int]) -> list[bytes]:
|
||||||
|
|
||||||
|
|
||||||
async def process_scanned_pdf(pdf_path: str, progress_callback=None) -> dict:
|
async def process_scanned_pdf(pdf_path: str, progress_callback=None) -> dict:
|
||||||
"""Full pipeline: detect separators, split, send each document to LexOffice."""
|
"""Full pipeline: detect separators, split, send each document via email."""
|
||||||
settings = await get_settings()
|
settings = await get_settings()
|
||||||
|
|
||||||
if not settings.get("smtp_server") or not settings.get("lexoffice_email"):
|
if not settings.get("smtp_server") or not settings.get("import_email"):
|
||||||
return {"error": "SMTP oder LexOffice-Email nicht konfiguriert", "total_pages": 0, "documents": 0, "sent": 0, "errors": 1}
|
return {"error": "SMTP oder Import-Email nicht konfiguriert", "total_pages": 0, "documents": 0, "sent": 0, "errors": 1}
|
||||||
|
|
||||||
# Step 1: Detect separator pages (CPU-bound, run in thread)
|
# Step 1: Detect separator pages (CPU-bound, run in thread)
|
||||||
if progress_callback:
|
if progress_callback:
|
||||||
|
|
@ -116,7 +116,7 @@ async def process_scanned_pdf(pdf_path: str, progress_callback=None) -> dict:
|
||||||
if not documents:
|
if not documents:
|
||||||
return {"error": "Keine Dokumente nach dem Splitting gefunden", "total_pages": total_pages, "documents": 0, "sent": 0, "errors": 1}
|
return {"error": "Keine Dokumente nach dem Splitting gefunden", "total_pages": total_pages, "documents": 0, "sent": 0, "errors": 1}
|
||||||
|
|
||||||
# Step 3: Send each document to LexOffice
|
# Step 3: Send each document via email
|
||||||
if progress_callback:
|
if progress_callback:
|
||||||
progress_callback("status", 0, 0, f"{len(documents)} Dokument(e) erkannt, starte Versand...")
|
progress_callback("status", 0, 0, f"{len(documents)} Dokument(e) erkannt, starte Versand...")
|
||||||
|
|
||||||
|
|
@ -135,7 +135,7 @@ async def process_scanned_pdf(pdf_path: str, progress_callback=None) -> dict:
|
||||||
filename = f"Scan_Dokument_{i + 1}.pdf"
|
filename = f"Scan_Dokument_{i + 1}.pdf"
|
||||||
msg = _build_forward_email(
|
msg = _build_forward_email(
|
||||||
from_addr=settings["smtp_username"],
|
from_addr=settings["smtp_username"],
|
||||||
to_addr=settings["lexoffice_email"],
|
to_addr=settings["import_email"],
|
||||||
original_subject=f"Scan-Upload Dokument {i + 1}/{len(documents)}",
|
original_subject=f"Scan-Upload Dokument {i + 1}/{len(documents)}",
|
||||||
original_from="Scan-Upload",
|
original_from="Scan-Upload",
|
||||||
attachments=[(filename, doc_bytes)],
|
attachments=[(filename, doc_bytes)],
|
||||||
|
|
@ -217,7 +217,7 @@ def generate_separator_pdf() -> bytes:
|
||||||
|
|
||||||
# Title text
|
# Title text
|
||||||
_centered_textbox(page, 120, "TRENNSEITE", 36, (0, 0, 0))
|
_centered_textbox(page, 120, "TRENNSEITE", 36, (0, 0, 0))
|
||||||
_centered_textbox(page, 170, "LexOffice Belegimport", 16, (0.4, 0.4, 0.4))
|
_centered_textbox(page, 170, "Belegimport", 16, (0.4, 0.4, 0.4))
|
||||||
|
|
||||||
# Insert QR code image centered
|
# Insert QR code image centered
|
||||||
qr_bytes = io.BytesIO()
|
qr_bytes = io.BytesIO()
|
||||||
|
|
|
||||||
|
|
@ -124,12 +124,12 @@ async def process_smb_share() -> dict:
|
||||||
if not settings.get("smb_server") or not settings.get("smb_share"):
|
if not settings.get("smb_server") or not settings.get("smb_share"):
|
||||||
return {"processed": 0, "skipped": 0, "errors": 0, "error": "SMB nicht konfiguriert"}
|
return {"processed": 0, "skipped": 0, "errors": 0, "error": "SMB nicht konfiguriert"}
|
||||||
|
|
||||||
if not settings.get("lexoffice_email"):
|
if not settings.get("import_email"):
|
||||||
return {"processed": 0, "skipped": 0, "errors": 0, "error": "LexOffice-Email nicht konfiguriert"}
|
return {"processed": 0, "skipped": 0, "errors": 0, "error": "Import-Email nicht konfiguriert"}
|
||||||
|
|
||||||
mode = settings.get("smb_mode", "forward")
|
mode = settings.get("smb_mode", "forward")
|
||||||
smtp_from = settings.get("smtp_username", "")
|
smtp_from = settings.get("smtp_username", "")
|
||||||
lexoffice_email = settings["lexoffice_email"]
|
import_email = settings["import_email"]
|
||||||
|
|
||||||
processed = 0
|
processed = 0
|
||||||
skipped = 0
|
skipped = 0
|
||||||
|
|
@ -180,7 +180,7 @@ async def process_smb_share() -> dict:
|
||||||
subject = f"SMB-Import: {filename} (Dokument {i + 1}/{len(documents)})"
|
subject = f"SMB-Import: {filename} (Dokument {i + 1}/{len(documents)})"
|
||||||
msg = _build_forward_email(
|
msg = _build_forward_email(
|
||||||
from_addr=smtp_from,
|
from_addr=smtp_from,
|
||||||
to_addr=lexoffice_email,
|
to_addr=import_email,
|
||||||
original_subject=subject,
|
original_subject=subject,
|
||||||
original_from="SMB-Import",
|
original_from="SMB-Import",
|
||||||
attachments=[(doc_filename, doc_bytes)],
|
attachments=[(doc_filename, doc_bytes)],
|
||||||
|
|
@ -200,7 +200,7 @@ async def process_smb_share() -> dict:
|
||||||
else:
|
else:
|
||||||
msg = _build_forward_email(
|
msg = _build_forward_email(
|
||||||
from_addr=smtp_from,
|
from_addr=smtp_from,
|
||||||
to_addr=lexoffice_email,
|
to_addr=import_email,
|
||||||
original_subject=f"SMB-Import: {filename}",
|
original_subject=f"SMB-Import: {filename}",
|
||||||
original_from="SMB-Import",
|
original_from="SMB-Import",
|
||||||
attachments=[(filename, pdf_data)],
|
attachments=[(filename, pdf_data)],
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,12 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>LexOffice Belegimport</title>
|
<title>Belegimport</title>
|
||||||
<link rel="stylesheet" href="/static/style.css">
|
<link rel="stylesheet" href="/static/style.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<nav>
|
<nav>
|
||||||
<div class="nav-brand">LexOffice Belegimport</div>
|
<div class="nav-brand">Belegimport</div>
|
||||||
<div class="nav-links">
|
<div class="nav-links">
|
||||||
<a href="/" class="{% if active_page == 'settings' %}active{% endif %}">Einstellungen</a>
|
<a href="/" class="{% if active_page == 'settings' %}active{% endif %}">Einstellungen</a>
|
||||||
<a href="/scan" class="{% if active_page == 'scan' %}active{% endif %}">Scan-Upload</a>
|
<a href="/scan" class="{% if active_page == 'scan' %}active{% endif %}">Scan-Upload</a>
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h2>Scan-Upload</h2>
|
<h2>Scan-Upload</h2>
|
||||||
<p class="text-muted" style="margin-bottom:1rem;">
|
<p class="text-muted" style="margin-bottom:1rem;">
|
||||||
Mehrseitige PDF hochladen. Trennseiten mit QR-Code werden automatisch erkannt und die einzelnen Dokumente an LexOffice gesendet.
|
Mehrseitige PDF hochladen. Trennseiten mit QR-Code werden automatisch erkannt und die einzelnen Dokumente gesendet.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<!-- Upload Zone -->
|
<!-- Upload Zone -->
|
||||||
|
|
@ -234,7 +234,7 @@ function listenProgress(uploadId) {
|
||||||
if (result.separator_pages > 0) {
|
if (result.separator_pages > 0) {
|
||||||
msg += ' (' + result.separator_pages + ' Trennseite(n))';
|
msg += ' (' + result.separator_pages + ' Trennseite(n))';
|
||||||
}
|
}
|
||||||
msg += ', ' + result.sent + ' an LexOffice gesendet';
|
msg += ', ' + result.sent + ' gesendet';
|
||||||
if (result.errors > 0) {
|
if (result.errors > 0) {
|
||||||
msg += ', ' + result.errors + ' Fehler';
|
msg += ', ' + result.errors + ' Fehler';
|
||||||
showResult(msg, 'warning');
|
showResult(msg, 'warning');
|
||||||
|
|
|
||||||
|
|
@ -77,12 +77,12 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h2>LexOffice & Ordner</h2>
|
<h2>Import & Ordner</h2>
|
||||||
<div class="form-grid">
|
<div class="form-grid">
|
||||||
<div class="form-group form-group-wide">
|
<div class="form-group form-group-wide">
|
||||||
<label for="lexoffice_email">LexOffice Import-Emailadresse</label>
|
<label for="import_email">Import-Emailadresse</label>
|
||||||
<input type="email" id="lexoffice_email" name="lexoffice_email"
|
<input type="email" id="import_email" name="import_email"
|
||||||
value="{{ settings.get('lexoffice_email', '') }}" placeholder="import-xyz@lexoffice.de">
|
value="{{ settings.get('import_email', '') }}" placeholder="import@example.com">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="source_folder">Eingangsordner (IMAP)</label>
|
<label for="source_folder">Eingangsordner (IMAP)</label>
|
||||||
|
|
@ -103,7 +103,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="form-actions">
|
<div class="form-actions">
|
||||||
<button type="button" class="btn btn-secondary" onclick="testEmail()">
|
<button type="button" class="btn btn-secondary" onclick="testEmail()">
|
||||||
<span class="btn-text">Test-Email an LexOffice senden</span>
|
<span class="btn-text">Test-Email senden</span>
|
||||||
<span class="btn-spinner" style="display:none;">Sende...</span>
|
<span class="btn-spinner" style="display:none;">Sende...</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -343,7 +343,7 @@ async function testEmail() {
|
||||||
const resp = await fetch('/api/test-email', { method: 'POST', body: getFormData() });
|
const resp = await fetch('/api/test-email', { method: 'POST', body: getFormData() });
|
||||||
const data = await resp.json();
|
const data = await resp.json();
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
const addr = document.getElementById('lexoffice_email').value;
|
const addr = document.getElementById('import_email').value;
|
||||||
showAlert('Test-Email erfolgreich an ' + addr + ' gesendet! Einstellungen gespeichert.', 'success');
|
showAlert('Test-Email erfolgreich an ' + addr + ' gesendet! Einstellungen gespeichert.', 'success');
|
||||||
} else {
|
} else {
|
||||||
showAlert('Test-Email fehlgeschlagen: ' + data.error, 'error');
|
showAlert('Test-Email fehlgeschlagen: ' + data.error, 'error');
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
services:
|
services:
|
||||||
belegimport:
|
belegimport:
|
||||||
build: .
|
build: .
|
||||||
container_name: lexoffice-belegimport
|
container_name: belegimport
|
||||||
ports:
|
ports:
|
||||||
- "8081:8000"
|
- "8081:8000"
|
||||||
volumes:
|
volumes:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue