From 07ef1682269de828d6c319ebc12753e396005770 Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Wed, 16 Jul 2025 08:55:08 +0200 Subject: [PATCH] preparing to render document --- .../de/srsoftware/umbrella/core/Tuple.java | 17 +++++ documents/build.gradle.kts | 6 ++ .../umbrella/documents/DocumentApi.java | 66 ++++++++++++++----- frontend/src/routes/document/View.svelte | 11 ++++ 4 files changed, 83 insertions(+), 17 deletions(-) create mode 100644 core/src/main/java/de/srsoftware/umbrella/core/Tuple.java diff --git a/core/src/main/java/de/srsoftware/umbrella/core/Tuple.java b/core/src/main/java/de/srsoftware/umbrella/core/Tuple.java new file mode 100644 index 0000000..a1a7225 --- /dev/null +++ b/core/src/main/java/de/srsoftware/umbrella/core/Tuple.java @@ -0,0 +1,17 @@ +package de.srsoftware.umbrella.core; + +import de.srsoftware.umbrella.core.model.Company; + +public class Tuple{ + public final A a; + public final B b; + + public Tuple(A a, B b){ + this.a = a; + this.b = b; + } + + public static Tuple of(A a, B b) { + return new Tuple<>(a,b); + } +} diff --git a/documents/build.gradle.kts b/documents/build.gradle.kts index 6552745..08d06aa 100644 --- a/documents/build.gradle.kts +++ b/documents/build.gradle.kts @@ -3,5 +3,11 @@ description = "Umbrella : Documents" dependencies{ implementation(project(":company")) implementation(project(":core")) + + implementation("de.srsoftware:document.api:1.0.1") + implementation("de.srsoftware:document.file:1.0.0") + implementation("de.srsoftware:document.processor:1.0.2") + implementation("de.srsoftware:document.zugferd:1.0.3") + implementation("de.srsoftware:tools.mime:1.1.2") } \ No newline at end of file diff --git a/documents/src/main/java/de/srsoftware/umbrella/documents/DocumentApi.java b/documents/src/main/java/de/srsoftware/umbrella/documents/DocumentApi.java index 9037d37..555872e 100644 --- a/documents/src/main/java/de/srsoftware/umbrella/documents/DocumentApi.java +++ b/documents/src/main/java/de/srsoftware/umbrella/documents/DocumentApi.java @@ -19,10 +19,12 @@ import static java.util.stream.Collectors.toMap; import com.sun.net.httpserver.HttpExchange; import de.srsoftware.configuration.Configuration; +import de.srsoftware.document.api.DocumentRegistry; import de.srsoftware.tools.Pair; import de.srsoftware.tools.Path; import de.srsoftware.tools.SessionToken; import de.srsoftware.umbrella.core.BaseHandler; +import de.srsoftware.umbrella.core.Tuple; import de.srsoftware.umbrella.core.api.CompanyService; import de.srsoftware.umbrella.core.api.UserService; import de.srsoftware.umbrella.core.exceptions.UmbrellaException; @@ -35,12 +37,14 @@ import java.time.LocalDate; import java.util.HashMap; import java.util.Map; import java.util.Optional; -import java.util.stream.Collectors; +import java.util.function.Predicate; import java.util.stream.Stream; import org.json.JSONArray; import org.json.JSONObject; public class DocumentApi extends BaseHandler { + private static final Predicate ZUGFERD_FILTER = document -> document.id().equals("Zugferd"); + private final DocumentRegistry registry = new DocumentRegistry(); private final CompanyService companies; private final Configuration config; @@ -110,18 +114,25 @@ public class DocumentApi extends BaseHandler { case STATES -> getDocStates(ex); case null -> super.doGet(path,ex); default -> { - try { - var docId = Long.parseLong(head); - yield getDocument(ex,docId,user.get()); - } catch (NumberFormatException ignored) {} - yield super.doGet(path,ex); + var docId = Long.parseLong(head); + head = path.pop(); + yield switch (head){ + case null -> getDocument(ex,docId,user.get()); + case PATH_PDF -> getRenderedDocument(ex,docId,user.get()); + default -> super.doGet(path,ex); + }; + } }; + } catch (NumberFormatException ignored) { + return super.doGet(path,ex); } catch (UmbrellaException e) { return send(ex,e); } } + + @Override public boolean doOptions(Path path, HttpExchange ex) throws IOException { return sendEmptyResponse(HTTP_OK,addCors(ex)); @@ -197,18 +208,46 @@ public class DocumentApi extends BaseHandler { return sendContent(ex,map); } - private boolean getDocument(HttpExchange ex, long docId, UmbrellaUser user) throws IOException, UmbrellaException { + private Tuple getDocument(long docId, UmbrellaUser user) throws UmbrellaException { var doc = db.loadDoc(docId); var companyId = doc.companyId(); var company = companies.get(companyId); if (!companies.membership(companyId,user.id())) throw forbidden("You are mot a member of company {0}",company.name()); + return Tuple.of(doc,company); + } + + private Document getDocumentWithCompanyData(long docId, UmbrellaUser user) throws UmbrellaException { + var tuple = getDocument(docId,user); + + var company = tuple.b; var sep = company.decimalSeparator(); + + var doc = tuple.a; if (sep != null) doc.setDecimalSeparator(sep); doc.setCompanyName(company.name()); + return doc; + } + private boolean getDocument(HttpExchange ex, long docId, UmbrellaUser user) throws IOException, UmbrellaException { + var doc = getDocumentWithCompanyData(docId,user); return sendContent(ex,doc.renderToMap()); } + private boolean getRenderedDocument(HttpExchange ex, long docId, UmbrellaUser user) throws IOException, UmbrellaException { + var document = getDocumentWithCompanyData(docId,user); + var template = document.template().name(); + var templateName = template+".html.pdf"; + var type = document.type().name(); + var zugferd = "invoice".equals(type); + Predicate filter = zugferd ? ZUGFERD_FILTER : doc -> doc.name().equals(templateName); + var optDoc = registry.documents() + .filter(filter) + .findAny(); + if (optDoc.isEmpty()) throw new UmbrellaException(404,"Cannot render {0} {1}: Missing template \"{2}\"",type,document.number(),template); + + return sendContent(ex,document.renderToMap()); + } + private JSONArray getLegacyContacts(HttpExchange ex, UmbrellaUser umbrellaUser, Token token) throws IOException, UmbrellaException { var location = config.get("umbrella.modules.contact.baseUrl").map(s -> s+"/json").orElseThrow(() -> new UmbrellaException(500,"umbrella.modules.contact.baseUrl not configured!")); var resp = request(location, token.asMap(),MIME_FORM_URL,null); @@ -234,9 +273,7 @@ public class DocumentApi extends BaseHandler { } private boolean patchDocument(long docId, UmbrellaUser user, HttpExchange ex) throws UmbrellaException, IOException { - var doc = db.loadDoc(docId); - var companyId = doc.companyId(); - if (!companies.membership(companyId,user.id())) throw forbidden("You are mot a member of company {0}",doc.companyId()); + var doc = getDocument(docId,user).a; doc.patch(json(ex)); db.save(doc); return ok(ex); @@ -247,9 +284,7 @@ public class DocumentApi extends BaseHandler { } private boolean patchDocumentPosition(long docId, UmbrellaUser user, HttpExchange ex) throws UmbrellaException, IOException { - var doc = db.loadDoc(docId); - var companyId = doc.companyId(); - if (!companies.membership(companyId,user.id())) throw forbidden("You are mot a member of company {0}",doc.companyId()); + var doc = getDocument(docId,user).a; if (doc.state() != NEW) throw forbidden("Document has already been send and is write-protected!"); var json = json(ex); var step = json.has(MOVE) && json.get(MOVE) instanceof Number num ? num.intValue() : 0; @@ -295,10 +330,7 @@ public class DocumentApi extends BaseHandler { } private boolean postDocumentPosition(long docId, HttpExchange ex, UmbrellaUser user) throws IOException, UmbrellaException { - var doc = db.loadDoc(docId); - var companyId = doc.companyId(); - var company = companies.get(companyId); - if (!companies.membership(companyId,user.id())) throw forbidden("You are mot a member of company {0}",company.name()); + var doc = getDocument(docId,user).a; var json = json(ex); if (!(json.has(FIELD_AMOUNT) && json.get(FIELD_AMOUNT) instanceof Number amount)) throw missingFieldException(FIELD_AMOUNT); diff --git a/frontend/src/routes/document/View.svelte b/frontend/src/routes/document/View.svelte index 5024e9c..5f7cfcc 100644 --- a/frontend/src/routes/document/View.svelte +++ b/frontend/src/routes/document/View.svelte @@ -79,6 +79,16 @@ } } + async function render(){ + const url = `${location.protocol}//${location.host.replace('5173','8080')}/api/document/${doc.id}/pdf`; + const resp = fetch(url,{credentials:'include'}); + if (resp.ok){ + error = null; + } else { + error = await resp.text(); + } + } + onMount(loadDoc); @@ -199,6 +209,7 @@
{t('document.actions')} +
TODO