L6 Benachrichtigungen & Audit: Empfängerregeln, In-App-Benachrichtigungen und Craftvia-Mails
- handleEvent (Signatur unverändert) löst alle 17 Domain-Events auf: Team/Assignees,
Backoffice (read_all + report:approve), Teamleiter-Freigaben, Ersteller, Abrechnung,
Notdienst, Import, Sync; Akteur ausgenommen, alle IDs über ctx.db neu aufgelöst.
- In-App-Notification über ctx.db (ungelesene gleiche Meldung wird aufgefrischt),
E-Mail über enqueueMail mit dedupeKey event:entity:user (+ optional occurrenceId),
Opt-out je Typ, Notdienst als Pflichtmail, feste Empfänger ohne Doppelmail.
- Mail-Templates craftvia_* (de/en) inkl. Notdienst-Format Spec §19.4 und Craftvia-Fußzeile.
- Migration tenant_mail_settings: mailFromName, mailReplyTo, emergencyRecipients,
billingRecipients an tenant_settings (keine neue Tabelle).
- Texte aus messages/{de,en}/notifications.json.
- Test scripts/test-benachrichtigungen-events.ts (63 Prüfungen).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,183 @@
|
||||
{
|
||||
"bell": {
|
||||
"label": "Notifications",
|
||||
"unreadCount": "{count, plural, =0 {No unread} one {# unread} other {# unread}}",
|
||||
"empty": "No notifications.",
|
||||
"markAllRead": "Mark all read",
|
||||
"showAll": "Show all"
|
||||
},
|
||||
"list": {
|
||||
"crumb": "Overview",
|
||||
"title": "Notifications",
|
||||
"sub": "Assignments, reports, emergency call-outs and errors – newest first.",
|
||||
"filterStatus": "Status",
|
||||
"filterType": "Type",
|
||||
"statusAll": "All",
|
||||
"statusUnread": "Unread",
|
||||
"statusRead": "Read",
|
||||
"typeAll": "All types",
|
||||
"apply": "Filter",
|
||||
"reset": "Reset",
|
||||
"empty": "No notifications for this selection.",
|
||||
"unread": "New",
|
||||
"read": "Read",
|
||||
"open": "Open",
|
||||
"markRead": "Mark as read",
|
||||
"markAllRead": "Mark all as read",
|
||||
"previous": "Previous",
|
||||
"next": "Next",
|
||||
"page": "Page {page} of {pages}"
|
||||
},
|
||||
"types": {
|
||||
"work_order_assigned": "Team assignment",
|
||||
"work_order_changed": "Work order changed",
|
||||
"work_order_cancelled": "Work order cancelled",
|
||||
"work_order_started": "Work order started",
|
||||
"work_order_daily_report_created": "Daily report created",
|
||||
"work_order_technically_completed": "Technically completed",
|
||||
"work_order_signature_missing": "Signature missing",
|
||||
"work_order_missing_required": "Required information missing",
|
||||
"work_order_released_for_billing": "Released for billing",
|
||||
"report_submitted": "Report for review",
|
||||
"report_approved": "Report approved",
|
||||
"report_rejected": "Report rejected",
|
||||
"emergency_created": "Emergency call-out created",
|
||||
"emergency_completed": "Emergency call-out completed",
|
||||
"import_ready_for_review": "Import ready for review",
|
||||
"import_failed": "Document processing failed",
|
||||
"sync_failed": "Sync failed"
|
||||
},
|
||||
"events": {
|
||||
"work_order_assigned": { "title": "New work order {number}", "message": "{title} – {customer} was assigned to your team." },
|
||||
"work_order_changed": { "title": "Work order {number} changed", "message": "{title} – {customer}: details were updated." },
|
||||
"work_order_cancelled": { "title": "Work order {number} cancelled", "message": "{title} – {customer} was cancelled." },
|
||||
"work_order_started": { "title": "Work order {number} started", "message": "{actor} started work at {customer}." },
|
||||
"work_order_daily_report_created": { "title": "Daily report for {number}", "message": "{actor} created a daily report for {customer}." },
|
||||
"work_order_technically_completed": { "title": "Work order {number} technically completed", "message": "{title} – {customer}: on-site work finished." },
|
||||
"work_order_signature_missing": { "title": "Signature missing: {number}", "message": "{title} – {customer}: no customer signature." },
|
||||
"work_order_missing_required": { "title": "Required information missing: {number}", "message": "{title} – {customer}: documentation incomplete." },
|
||||
"work_order_released_for_billing": { "title": "Work order {number} ready for billing", "message": "{title} – {customer} is ready for billing." },
|
||||
"report_submitted": { "title": "Report for review: {number}", "message": "{actor} submitted a report for {title}." },
|
||||
"report_approved": { "title": "Report approved: {number}", "message": "The report for {title} was approved." },
|
||||
"report_rejected": { "title": "Report rejected: {number}", "message": "The report for {title} needs a correction." },
|
||||
"emergency_created": { "title": "Emergency call-out {number} created", "message": "{actor} created an emergency call-out at {customer}." },
|
||||
"emergency_completed": { "title": "Emergency call-out {number} completed", "message": "{customer}: ready for review and billing." },
|
||||
"import_ready_for_review": { "title": "Import ready for review", "message": "The data from {fileName} is ready for review." },
|
||||
"import_failed": { "title": "Document processing failed", "message": "{fileName} could not be processed." },
|
||||
"sync_failed": { "title": "Sync failed", "message": "A change could not be applied ({reason})." }
|
||||
},
|
||||
"fallback": {
|
||||
"unknown": "unknown",
|
||||
"system": "System",
|
||||
"document": "document"
|
||||
},
|
||||
"preferences": {
|
||||
"title": "Notifications",
|
||||
"sub": "You receive all notifications in the app. Choose which ones are also sent by e-mail.",
|
||||
"email": "E-mail",
|
||||
"mandatory": "Mandatory – cannot be turned off",
|
||||
"save": "Save",
|
||||
"saved": "Settings saved."
|
||||
},
|
||||
"mailSettings": {
|
||||
"crumb": "Settings",
|
||||
"title": "E-mail delivery",
|
||||
"sub": "Sender, reply-to address and fixed recipients for emergency call-outs and billing.",
|
||||
"back": "Back to settings",
|
||||
"sender": "Sender",
|
||||
"fromName": "Sender name",
|
||||
"fromNameHint": "Shown as the name in the inbox. Empty = company name.",
|
||||
"fromAddress": "Sender address",
|
||||
"fromAddressHint": "The address stays {address} so that mail is delivered (SPF/DKIM).",
|
||||
"replyTo": "Reply-to address",
|
||||
"replyToHint": "Replies to notifications go to this address.",
|
||||
"recipients": "Fixed recipients",
|
||||
"emergencyRecipients": "Emergency recipients",
|
||||
"emergencyHint": "One address per line. Receive every new and completed emergency call-out.",
|
||||
"billingRecipients": "Billing recipients",
|
||||
"billingHint": "One address per line. Receive work orders released for billing. Empty = back office.",
|
||||
"save": "Save",
|
||||
"saved": "Settings saved.",
|
||||
"invalid": "Please check your input: {detail}"
|
||||
},
|
||||
"audit": {
|
||||
"crumb": "Settings",
|
||||
"title": "Audit log",
|
||||
"sub": "Who changed what and when – read only.",
|
||||
"back": "Back to settings",
|
||||
"from": "From",
|
||||
"to": "To",
|
||||
"actor": "User",
|
||||
"actorAll": "All users",
|
||||
"action": "Action",
|
||||
"actionAll": "All actions",
|
||||
"entity": "Object type",
|
||||
"entityAll": "All object types",
|
||||
"entityId": "Object ID",
|
||||
"apply": "Filter",
|
||||
"reset": "Reset",
|
||||
"time": "Time",
|
||||
"object": "Object",
|
||||
"result": "Result",
|
||||
"resultOk": "Succeeded",
|
||||
"resultDenied": "Denied",
|
||||
"details": "Details",
|
||||
"empty": "No entries for this selection.",
|
||||
"total": "{count, plural, one {# entry} other {# entries}}",
|
||||
"previous": "Previous",
|
||||
"next": "Next",
|
||||
"page": "Page {page} of {pages}",
|
||||
"system": "System",
|
||||
"platform": "Platform",
|
||||
"detailTitle": "Audit log entry",
|
||||
"close": "Close",
|
||||
"field": "Field",
|
||||
"before": "Before",
|
||||
"after": "After",
|
||||
"changed": "changed",
|
||||
"noValues": "No values recorded.",
|
||||
"ip": "IP address",
|
||||
"userAgent": "User agent",
|
||||
"notCaptured": "not recorded",
|
||||
"actions": {
|
||||
"create": "Created",
|
||||
"update": "Updated",
|
||||
"delete": "Deleted",
|
||||
"login": "Sign-in",
|
||||
"logout": "Sign-out",
|
||||
"denied": "Denied",
|
||||
"export": "Export",
|
||||
"import": "Import",
|
||||
"provision": "Provisioned",
|
||||
"approve": "Approved",
|
||||
"reject": "Rejected"
|
||||
},
|
||||
"entities": {
|
||||
"user": "User",
|
||||
"role": "Role",
|
||||
"tenant": "Tenant",
|
||||
"tenant_settings": "Settings",
|
||||
"tenant_mail_settings": "E-mail delivery",
|
||||
"tenant_module": "Module",
|
||||
"module": "Module",
|
||||
"session": "Session",
|
||||
"account_inactive": "Inactive account",
|
||||
"work_order": "Work order",
|
||||
"customer": "Customer",
|
||||
"site": "Site",
|
||||
"team": "Team",
|
||||
"report": "Report",
|
||||
"signature": "Signature",
|
||||
"document": "Document",
|
||||
"import_job": "Work order import",
|
||||
"material_usage": "Material",
|
||||
"time_entry": "Working time",
|
||||
"work_session": "Work session",
|
||||
"photo": "Photo",
|
||||
"voice_note": "Voice note",
|
||||
"notification": "Notification",
|
||||
"notification_settings": "Notification settings",
|
||||
"sync_operation": "Sync"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user