PDF-Upload aus der Rechnungsmaske entfernt; Rechnungen entstehen in der UI nur noch aus Templates

Die REST-API (/api/invoices/sign, /validate) nimmt unverändert PDFs entgegen
und bietet weiterhin keinen Template-Zugriff.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-17 10:31:29 +02:00
co-authored by Claude Fable 5
parent fa0d9da2b1
commit fd71c272fa
2 changed files with 12 additions and 51 deletions
@@ -23,8 +23,6 @@ import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.BigDecimalField;
import com.vaadin.flow.component.textfield.EmailField;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.component.upload.Upload;
import com.vaadin.flow.component.upload.receivers.MemoryBuffer;
import com.vaadin.flow.data.renderer.ComponentRenderer;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
@@ -47,8 +45,6 @@ import de.assecutor.pdftool.zugferd.ZugferdResult;
import de.assecutor.pdftool.zugferd.ZugferdService;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.math.BigDecimal;
import java.text.NumberFormat;
import java.time.LocalDate;
@@ -72,10 +68,7 @@ public class MainView extends VerticalLayout {
private final InvoiceTemplateService invoiceTemplateService;
private final TemplatePdfService templatePdfService;
private final MemoryBuffer uploadBuffer = new MemoryBuffer();
private byte[] uploadedPdf;
/** Alternative zum PDF-Upload: ein gespeichertes Rechnungstemplate. */
/** Grundlage der Rechnung: ein gespeichertes Rechnungstemplate. */
private final ComboBox<String> templateSelect = new ComboBox<>("Rechnungstemplate");
/** Vorbelegung der Rechnungssteller-Felder aus dem Adressbuch. */
@@ -119,8 +112,8 @@ public class MainView extends VerticalLayout {
private final Anchor downloadLink = new Anchor();
/**
* Alle Controls unterhalb der Quellenwahl; sie sind erst aktiv, wenn ein PDF
* hochgeladen oder ein Template ausgewählt wurde.
* Alle Controls unterhalb der Template-Auswahl; sie sind erst aktiv, wenn
* ein Template ausgewählt wurde.
*/
private final List<com.vaadin.flow.component.HasEnabled> sourceDependentControls = new ArrayList<>();
@@ -136,10 +129,9 @@ public class MainView extends VerticalLayout {
setMaxWidth("960px");
add(new Paragraph("Laden Sie ein Rechnungs-PDF hoch oder wählen Sie ein gespeichertes "
+ "Rechnungstemplate und ergänzen Sie die Rechnungsdaten. Die Rechnung wird als "
+ "rechtskonforme ZUGFeRD-Rechnung (PDF/A-3 mit eingebettetem EN16931-XML) erzeugt "
+ "und als ZIP-Datei bereitgestellt."));
add(new Paragraph("Wählen Sie ein gespeichertes Rechnungstemplate und ergänzen Sie die "
+ "Rechnungsdaten. Die Rechnung wird als rechtskonforme ZUGFeRD-Rechnung "
+ "(PDF/A-3 mit eingebettetem EN16931-XML) erzeugt und als ZIP-Datei bereitgestellt."));
// Eingaben unter einem Namen speichern bzw. gespeicherte Eingaben laden.
draftSelect.setPlaceholder("Vorbelegung laden");
@@ -174,34 +166,12 @@ public class MainView extends VerticalLayout {
});
add(draftSelect);
Upload upload = new Upload(uploadBuffer);
upload.setAcceptedFileTypes("application/pdf", ".pdf");
upload.setMaxFiles(1);
upload.setMaxFileSize(25 * 1024 * 1024);
upload.addSucceededListener(event -> {
try {
uploadedPdf = uploadBuffer.getInputStream().readAllBytes();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
// Entweder PDF oder Template: hochgeladenes PDF hebt die Template-Auswahl auf.
templateSelect.clear();
updateControlsEnabled();
});
upload.addFileRemovedListener(event -> {
uploadedPdf = null;
updateControlsEnabled();
});
templateSelect.setItems(invoiceTemplateService.templateNames());
templateSelect.setPlaceholder("Template auswählen");
templateSelect.setClearButtonVisible(true);
templateSelect.setWidth("300px");
templateSelect.addValueChangeListener(event -> {
if (event.isFromClient() && event.getValue() != null) {
// Entweder PDF oder Template: Auswahl entfernt ein hochgeladenes PDF.
upload.clearFileList();
uploadedPdf = null;
// Rechnungssteller-Felder nur bei der ersten Template-Auswahl
// leer beginnen lassen; beim Wechsel zwischen Templates bleiben
// die Eingaben erhalten (sonst wäre u. a. der Speichern-Button
@@ -213,12 +183,7 @@ public class MainView extends VerticalLayout {
}
updateControlsEnabled();
});
HorizontalLayout sourceLayout = new HorizontalLayout(upload, templateSelect);
sourceLayout.setAlignItems(Alignment.CENTER);
// Das Label der ComboBox ausgleichen, damit das Eingabefeld selbst
// mittig zur Upload-Fläche steht.
templateSelect.getStyle().set("margin-top", "-1.25em");
add(new H3("1. Rechnungs-PDF hochladen oder Template auswählen"), sourceLayout);
add(new H3("1. Template auswählen"), templateSelect);
invoiceNumber.setRequired(true);
invoiceNumber.setErrorMessage("Rechnungsnummer ist ein Pflichtfeld.");
@@ -368,7 +333,7 @@ public class MainView extends VerticalLayout {
.addThemeVariants(NotificationVariant.LUMO_SUCCESS);
}
/** Sammelt den aktuellen Formularstand; ein hochgeladenes PDF wird nicht mitgespeichert. */
/** Sammelt den aktuellen Formularstand. */
private InvoiceDraft buildDraft(String name) {
return new InvoiceDraft(null, name,
templateSelect.getValue(),
@@ -398,8 +363,7 @@ public class MainView extends VerticalLayout {
/** Überträgt gespeicherte Eingaben in das Formular. */
private void applyDraft(InvoiceDraft draft) {
// Die Template-Auswahl wiederherstellen, sofern das Template noch existiert;
// ein hochgeladenes PDF ist nicht Teil der gespeicherten Eingaben.
// Die Template-Auswahl wiederherstellen, sofern das Template noch existiert.
if (draft.templateName() != null && !draft.templateName().isBlank()
&& invoiceTemplateService.templateExists(draft.templateName())) {
templateSelect.setValue(draft.templateName());
@@ -625,10 +589,10 @@ public class MainView extends VerticalLayout {
/**
* Das Rechnungsformular und der Erzeugen-Button sind nur aktiv, wenn ein
* PDF hochgeladen oder ein Template ausgewählt wurde.
* Template ausgewählt wurde.
*/
private void updateControlsEnabled() {
boolean sourceSelected = uploadedPdf != null || templateSelect.getValue() != null;
boolean sourceSelected = templateSelect.getValue() != null;
sourceDependentControls.forEach(control -> control.setEnabled(sourceSelected));
}
@@ -638,10 +602,6 @@ public class MainView extends VerticalLayout {
showErrors(errors);
return;
}
if (uploadedPdf != null) {
convertAndOfferDownload(uploadedPdf);
return;
}
String templateName = templateSelect.getValue();
Optional<String> templateData = invoiceTemplateService.loadTemplate(templateName);
if (templateData.isEmpty()) {
+1
View File
@@ -22,6 +22,7 @@
"noUnusedParameters": false,
"experimentalDecorators": true,
"useDefineForClassFields": false,
"ignoreDeprecations": "6.0",
"baseUrl": "src/main/frontend",
"paths": {
"@vaadin/flow-frontend": ["generated/jar-resources"],