feat: E-Rechnungs-Backend, Pflichtangaben-Validator, Nummern-Audit und DATEV-Export
Größerer Wurf rund um Rechnungen: das vollständige E-Rechnungs- und Lifecycle-Backend wird eingecheckt, die UI-seitige Rechnungserstellung wird zugunsten eines DATEV-Exports zurückgebaut, und die Test-Infrastruktur wird auf JDK 25 angehoben. E-Rechnung & Signatur - ZUGFeRD/Factur-X/XRechnung-Anreicherung via Mustang (EInvoiceService). - PAdES-Detached-Signatur via PDFBox + BouncyCastle, System- und Nutzer- Keystore-Pfad, Nutzer-Keystores AES-GCM-verschlüsselt (AesGcmCipher, SigningCredentialsService, UserSigningCredentials). - Konfiguration via EInvoiceProperties + EInvoiceSecurityInitializer. - Approval-Workflow für kritische Rechnungsvorgänge (Storno, Berichtigung): InvoiceApprovalService, InvoiceApprovalRequest, ApprovalsView, InvoicePermissionService, InvoiceRoles. Lifecycle & Audit - InvoiceStatus DRAFT/ISSUED/SENT/CANCELLED/CORRECTED, InvoiceType INVOICE/CORRECTION/CANCELLATION, PaymentStatus, lückenloser Audit-Trail via InvoiceAuditEntry/Action. - InvoiceLifecycleService verwaltet alle Übergänge inklusive Storno- und Berichtigungsbelegen mit Querverweis zur Originalrechnung. - InvoiceLifecycleMigration zieht Bestandsdaten in das neue Lifecycle-Modell. - InvoiceExportService bündelt Original + Folgebelege als ZIP für die Auslieferung an den Steuerberater. Pflichtangaben-Validator (§ 14 UStG) - InvoiceComplianceValidator + Exception sammeln alle Verstöße in einem Lauf (Pflichtfelder, Adressen, Steuernummer/USt-IdNr, Items, Betrags- konsistenz, Hinweispflicht bei 0 % USt). - Wird vor jedem DRAFT-→-ISSUED-Übergang im Lifecycle aufgerufen, sodass festgeschriebene Rechnungen keine Pflichtfeld-Lücken mehr haben können. Rechnungsnummer-Audit (§ 14 Abs. 4 Nr. 4 UStG / GoBD) - InvoiceNumberReservation + Status RESERVED/USED/VOIDED protokollieren jede aus dem Counter gezogene Nummer. - UserInvoiceDataService schreibt bei Vergabe ein RESERVED-Audit, der Lifecycle setzt nach Festschreiben USED bzw. nach Löschen eines Entwurfs VOIDED — Lücken im Nummernkreis sind dadurch erklärbar. - InvoiceNumberAuditService liefert markUsed/markVoided/findUnused für Folge-UI und Betriebsprüfungs-Reports. UI-Rückbau und DATEV-Export - Routen für CreateInvoiceView, InvoicesView, MyInvoicesView auskommentiert (Code bleibt erhalten, Reaktivierung dokumentiert). - Rechnungs-Buttons aus ShowJobsView entfernt, Nav-Eintrag „Rechnungen" durch „DATEV-Export" ersetzt. - DatevExportService erzeugt einen DATEV-EXTF-Buchungsstapel (Version 7, Windows-1252, CRLF) mit SKR03-Erlöskonten (8400/8300/8125), Sammel- debitor 10000 und korrektem S/H-Verhalten für Stornorechnungen. - DatevExportView mit Zeitraum-Picker und Auto-Download. - i18n-Keys (de/en) für nav.datev.export und datev.export.*. Tests & Build - EInvoiceServiceTest (Signatur-Pfade), EInvoiceServiceDssValidationTest (PAdES-Profil via EU DSS 6.2 — dokumentiert PKCS7-B als Ist-Stand), InvoiceComplianceValidatorTest (26 Cases als Spezifikation der Pflichtangaben), InvoiceNumberAuditServiceTest, DatevExportServiceTest. - Mockito 5.18 + ByteBuddy 1.17.5 in <dependencyManagement> gepinnt; die Spring-Boot-3.4.3-Defaults (Mockito 5.14.2 / ByteBuddy 1.15.11) konnten den Inline-Mock-Maker auf JDK 25 nicht laden, weshalb die beiden DemoModeServiceTests vorher rot waren. - DSS-Test-Dependencies (dss-pades-pdfbox, dss-validation, dss-utils-apache-commons, dss-crl-parser-x509crl 6.2) im Test-Scope. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,17 @@ nav.customers=Address Book
|
||||
nav.appusers=App Users
|
||||
nav.statistics=Statistics
|
||||
nav.invoices=Invoices
|
||||
nav.datev.export=DATEV Export
|
||||
nav.approvals=Approvals
|
||||
datev.export.title=DATEV Export
|
||||
datev.export.description=Downloads a DATEV-compatible booking batch containing all finalized invoices for the selected period. The file can be imported into DATEV Unternehmen Online as well as DATEV-compatible third-party tools.
|
||||
datev.export.from=From
|
||||
datev.export.to=To
|
||||
datev.export.button=Export invoices
|
||||
datev.export.success=Export created: {0}
|
||||
datev.export.error.dates=Please pick both From and To dates.
|
||||
datev.export.error.range=To date must not be before From date.
|
||||
datev.export.error.user=Could not determine the current user.
|
||||
nav.messages=Messages
|
||||
nav.profile=My Profile
|
||||
nav.myinvoices=Invoices
|
||||
@@ -47,6 +58,36 @@ profile.settings.digitalprocess.info=Jobs are processed digitally via the app
|
||||
profile.settings.locateappuser=Locate App Users
|
||||
profile.settings.locateappuser.info=App user location is transmitted regularly
|
||||
profile.settings.vatrate=VAT rate
|
||||
profile.settings.einvoice=Create ZUGFeRD e-invoice
|
||||
profile.settings.einvoice.helper=Generates PDF/A-3 with embedded XRechnung/ZUGFeRD XML (when enabled system-wide).
|
||||
profile.settings.signinvoices=Digitally sign invoices
|
||||
profile.settings.signinvoices.helper=Adds a PAdES signature using the configured certificate. Saving fails if no active certificate is available.
|
||||
profile.signing.title=Signing certificate
|
||||
profile.signing.hint=Upload your own PKCS#12 certificate (.p12/.pfx) so invoices are signed with your signature. The private key is stored encrypted in the database.
|
||||
profile.signing.masterkey.missing=Note: The server master key is not configured. Ask your administrator to set votianlt.einvoice.signing.master-key before uploading a certificate.
|
||||
profile.signing.none=No personal signing certificate stored yet. The system-wide key will be used when signing.
|
||||
profile.signing.metadata.alias=Alias
|
||||
profile.signing.metadata.subject=Subject
|
||||
profile.signing.metadata.issuer=Issuer
|
||||
profile.signing.metadata.serial=Serial number
|
||||
profile.signing.metadata.validity=Valid
|
||||
profile.signing.expired=Certificate expired
|
||||
profile.signing.expiring=Expires within the next 30 days
|
||||
profile.signing.enabled=Use my certificate for signing
|
||||
profile.signing.toggle.saved=Setting saved.
|
||||
profile.signing.delete=Remove certificate
|
||||
profile.signing.deleted=Signing certificate removed.
|
||||
profile.signing.upload.title=Upload certificate
|
||||
profile.signing.upload.drop=Drop your PKCS#12 file here or click to upload
|
||||
profile.signing.upload.received=File received — please provide alias and password.
|
||||
profile.signing.upload.required=Please upload a certificate file first.
|
||||
profile.signing.upload.save=Save
|
||||
profile.signing.alias=Key alias
|
||||
profile.signing.alias.required=Please provide the key alias.
|
||||
profile.signing.password=Keystore password
|
||||
profile.signing.password.required=Please provide the keystore password.
|
||||
profile.signing.saved=Signing certificate saved.
|
||||
profile.signing.error=Save failed
|
||||
profile.account=Account
|
||||
profile.security=Security
|
||||
profile.security.twofactor=Two-Factor Authentication
|
||||
@@ -706,6 +747,96 @@ invoices.column.amount=Amount
|
||||
invoices.column.description=Description
|
||||
invoices.empty=No invoices have been created yet.
|
||||
invoices.notification.pdf.missing=No PDF is stored for this invoice.
|
||||
invoices.notification.pdf.error=Failed to display PDF: {0}
|
||||
invoices.column.status=Status
|
||||
invoices.column.type=Type
|
||||
invoices.column.actions=Actions
|
||||
invoices.disclaimer=Note: Legal retention obligations remain with the issuer. An already issued invoice is never overwritten — corrections are made through a correction document or cancellation invoice that explicitly references the original.
|
||||
invoices.status.draft=Draft
|
||||
invoices.status.issued=Issued
|
||||
invoices.status.sent=Sent
|
||||
invoices.status.cancelled=Cancelled
|
||||
invoices.status.corrected=Corrected
|
||||
invoices.type.invoice=Invoice
|
||||
invoices.type.cancellation=Cancellation invoice
|
||||
invoices.type.correction=Correction document
|
||||
invoices.action.view=View PDF
|
||||
invoices.action.history=History
|
||||
invoices.action.marksent=Mark as sent
|
||||
invoices.action.correct=Correct
|
||||
invoices.action.cancel=Cancel
|
||||
invoices.notification.sent=Invoice marked as sent.
|
||||
invoices.notification.cancelled=Cancellation document {0} created.
|
||||
invoices.notification.corrected=Correction document {0} created.
|
||||
invoices.notification.error=Action failed: {0}
|
||||
invoices.cancel.title=Cancel invoice {0}
|
||||
invoices.cancel.hint=The original invoice remains visible. A separate cancellation document with its own number will be created, explicitly referencing the original invoice.
|
||||
invoices.cancel.reason=Reason for cancellation
|
||||
invoices.cancel.reason.required=Please provide a reason.
|
||||
invoices.cancel.confirm=Create cancellation document
|
||||
invoices.correct.title=Correct invoice {0}
|
||||
invoices.correct.hint=A correction document is intended for formal errors only (e.g. address, delivery date). The original invoice remains visible and the correction document refers to it explicitly.
|
||||
invoices.correct.fields=Corrected information
|
||||
invoices.correct.fields.helper=Describe which fields are added or replaced.
|
||||
invoices.correct.fields.required=Please describe the corrected information.
|
||||
invoices.correct.reason=Reason for correction
|
||||
invoices.correct.confirm=Create correction document
|
||||
invoices.history.title=History for invoice {0}
|
||||
invoices.history.log=Audit log
|
||||
invoices.history.empty=No entries available.
|
||||
invoices.history.original=Original invoice
|
||||
invoices.history.cancellation=Cancellation document
|
||||
invoices.history.correction=Correction document
|
||||
invoices.history.replacement=Replacement invoice
|
||||
invoices.audit.action.created_draft=Draft created
|
||||
invoices.audit.action.updated_draft=Draft updated
|
||||
invoices.audit.action.issued=Issued
|
||||
invoices.audit.action.sent=Sent
|
||||
invoices.audit.action.cancelled=Cancelled
|
||||
invoices.audit.action.corrected=Corrected
|
||||
invoices.audit.action.replaced=Replaced by new invoice
|
||||
invoices.audit.action.deleted_draft=Draft deleted
|
||||
invoices.audit.action.payment_recorded=Payment recorded
|
||||
invoices.audit.resulting=Resulting document: {0}
|
||||
invoices.column.payment=Payment
|
||||
invoices.column.outstanding=Outstanding
|
||||
invoices.payment.unpaid=Open
|
||||
invoices.payment.partially_paid=Partially paid
|
||||
invoices.payment.paid=Paid
|
||||
invoices.payment.overpaid=Overpaid
|
||||
invoices.payment.refund_due=Refund due
|
||||
invoices.action.payment=Record payment
|
||||
invoices.action.export=Export
|
||||
invoices.payment.title=Payment for invoice {0}
|
||||
invoices.payment.hint=Outstanding balance: {0}. Negative amounts can be recorded for corrections.
|
||||
invoices.payment.amount=Amount
|
||||
invoices.payment.amount.required=Please enter a non-zero amount.
|
||||
invoices.payment.reference=Payment reference (e.g. statement, booking ID)
|
||||
invoices.payment.reason=Note
|
||||
invoices.payment.confirm=Record payment
|
||||
invoices.notification.payment=Payment recorded.
|
||||
invoices.einvoice.tooltip=PDF/A-3 with embedded ZUGFeRD/XRechnung XML
|
||||
invoices.einvoice.signed=Signed
|
||||
invoices.action.cancel.request=Request cancellation
|
||||
invoices.action.correct.request=Request correction
|
||||
invoices.notification.requested=Approval request created. Please wait for approval.
|
||||
approvals.title=Approvals
|
||||
approvals.no.permission=You do not have permission to handle approvals.
|
||||
approvals.column.requested=Requested at
|
||||
approvals.column.requester=Requested by
|
||||
approvals.column.invoice=Invoice
|
||||
approvals.column.action=Action
|
||||
approvals.column.reason=Reason
|
||||
approvals.action.approve=Approve
|
||||
approvals.action.reject=Reject
|
||||
approvals.confirm.approve.title=Approve request for invoice {0}
|
||||
approvals.confirm.reject.title=Reject request for invoice {0}
|
||||
approvals.review.fields=Corrected information
|
||||
approvals.review.reason=Reason
|
||||
approvals.review.comment=Comment (optional)
|
||||
approvals.notification.approved=Request approved — follow-up document created.
|
||||
approvals.notification.rejected=Request rejected.
|
||||
page.title.approvals=Approvals
|
||||
|
||||
# My Invoices
|
||||
myinvoices.title=Invoices
|
||||
|
||||
Reference in New Issue
Block a user