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 @@