feat(accounting): re-viewable incoming invoices + expense invoiced/paid lifecycle UI

#1 Incoming invoices are re-viewable: extracted a reusable rasterised
DocumentPreview (last page = QR-bill), added a click-to-view ViewModal on
every row, and embedded the preview in the mark-paid dialog.

#2/#3 Expenses ledger:
- invoiced badge (links to the client invoice) + paid toggle (manual,
  independent of invoiced)
- edit until invoiced (ExpenseFormModal now does create + edit; locked
  rows show a Lock chip instead of edit/add-to-invoice)
- 'Add to invoice' action (re-bill via customer picker + markup) and a
  'Mark paid' dialog
- service: Expense gains invoiced/billedInvoiceId/paid/paidAt fields +
  invoiceExpense() and markExpensePaid()

en/de translations added.
This commit is contained in:
Luca
2026-06-11 21:19:06 +02:00
parent 545ef334f4
commit 727bdab6e0
5 changed files with 326 additions and 74 deletions
+13
View File
@@ -3474,6 +3474,7 @@
"triageTitle": "Dokument kategorisieren",
"saveCategorize": "Speichern",
"categorize": "Kategorisieren",
"view": "Ansehen",
"empty": "Noch keine Dokumente — oben eines erfassen.",
"untitled": "Unbenanntes Dokument",
"noAmount": "Betrag nicht erfasst",
@@ -3485,6 +3486,7 @@
"nextPage": "Weiter",
"pageOf": "Seite {{n}} / {{total}}",
"status": {
"label": "Status",
"unsorted": "Neu",
"categorized": "Kategorisiert",
"declined": "Abgelehnt",
@@ -3545,6 +3547,7 @@
"whoHint": "z. B. Mitarbeitername oder Geschäft",
"proof": "Beleg",
"proofRequired": "Ein Beleg ist erforderlich.",
"proofExisting": "Es ist bereits ein Beleg angehängt — neuen hochladen, um ihn zu ersetzen.",
"viewProof": "Beleg"
},
"expenseKind": {
@@ -3582,6 +3585,16 @@
"unpaidToast": "Als nicht bezahlt markiert.",
"addExpense": "Aufwand hinzufügen",
"addTitle": "Aufwand hinzufügen",
"editTitle": "Aufwand bearbeiten",
"updatedToast": "Aufwand aktualisiert.",
"payTitle": "Aufwand als bezahlt markieren",
"invoiced": "Verrechnet",
"invoicedToast": "Zu einer Kundenrechnung hinzugefügt.",
"invoiceTitle": "Zur Kundenrechnung hinzufügen",
"invoiceHint": "Erstellt eine verrechenbare Position auf der nächsten geplanten Rechnung des Kunden und sperrt den Aufwand für weitere Änderungen.",
"addToInvoice": "Zur Rechnung hinzufügen",
"locked": "Gesperrt",
"lockedHint": "Gesperrt — dieser Aufwand ist auf einer Kundenrechnung.",
"description": "Beschreibung",
"descriptionHint": "z. B. Kilometer, Spesenpauschale, Barbeleg",
"createdToast": "Aufwand hinzugefügt."
+13
View File
@@ -3474,6 +3474,7 @@
"triageTitle": "Categorize document",
"saveCategorize": "Save",
"categorize": "Categorize",
"view": "View",
"empty": "No documents yet — capture one above.",
"untitled": "Untitled document",
"noAmount": "amount not entered",
@@ -3485,6 +3486,7 @@
"nextPage": "Next",
"pageOf": "Page {{n}} / {{total}}",
"status": {
"label": "Status",
"unsorted": "New",
"categorized": "Categorized",
"declined": "Declined",
@@ -3545,6 +3547,7 @@
"whoHint": "e.g. coworker name or shop",
"proof": "Proof",
"proofRequired": "A proof file is required.",
"proofExisting": "A proof file is already attached — upload a new one to replace it.",
"viewProof": "Proof"
},
"expenseKind": {
@@ -3582,6 +3585,16 @@
"unpaidToast": "Marked as not paid.",
"addExpense": "Add expense",
"addTitle": "Add expense",
"editTitle": "Edit expense",
"updatedToast": "Expense updated.",
"payTitle": "Mark expense paid",
"invoiced": "Invoiced",
"invoicedToast": "Added to a client invoice.",
"invoiceTitle": "Add to client invoice",
"invoiceHint": "This creates a billable line on the clients next scheduled invoice and locks the expense from further edits.",
"addToInvoice": "Add to invoice",
"locked": "Locked",
"lockedHint": "Locked — this expense is on a client invoice.",
"description": "Description",
"descriptionHint": "e.g. mileage, per-diem, cash receipt",
"createdToast": "Expense added."