fix: resolve open risks — invoice race condition, SMTP config, workflow seeds

- billing/service.py: pg_advisory_xact_lock on invoice_number_seq per year
  → prevents duplicate INV-YYYY-NNNN under concurrent requests
- admin.py: SMTP settings in system_settings (smtp_host/port/user/password/
  from_address/enabled) with GET+PUT support; seed-workflows endpoint creates
  4 standard workflow definitions (still-cycles, still-eevee, turntable,
  multi-angle) idempotently
- notifications/service.py: send_email_notification_stub now sends real
  SMTP email via smtplib when smtp_enabled=true in system_settings
- Admin.tsx: SMTP settings panel (host/port/user/password/from + enable
  toggle, save button); Seed Standard Workflows maintenance button
- Upload.tsx: fix TS error — title→aria-label on Lucide icons
- Admin.tsx Settings type: add render_backend/flamenco_* fields (TS fix)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-06 18:15:45 +01:00
parent f19a6ccde8
commit c0ea60d984
5 changed files with 289 additions and 10 deletions
+4 -4
View File
@@ -453,9 +453,9 @@ export default function UploadPage() {
</td>
<td className="px-4 py-2 text-center">
{!hasId ? null : row.has_step ? (
<CheckCircle size={14} className="text-green-500 mx-auto" title="STEP file linked" />
<CheckCircle size={14} className="text-green-500 mx-auto" aria-label="STEP file linked" />
) : (
<X size={14} className="text-red-400 mx-auto" title="No STEP file" />
<X size={14} className="text-red-400 mx-auto" aria-label="No STEP file" />
)}
</td>
</tr>
@@ -598,9 +598,9 @@ export default function UploadPage() {
<td className="px-4 py-2">{row.gewaehltes_produkt || row.produkt_baureihe || '\u2014'}</td>
<td className="px-4 py-2 text-center">
{row.has_step ? (
<CheckCircle size={14} className="text-green-500 mx-auto" title="STEP file linked" />
<CheckCircle size={14} className="text-green-500 mx-auto" aria-label="STEP file linked" />
) : (
<X size={14} className="text-red-400 mx-auto" title="No STEP file" />
<X size={14} className="text-red-400 mx-auto" aria-label="No STEP file" />
)}
</td>
{outputTypes.map((ot) => (