diff --git a/core/src/main/java/de/srsoftware/umbrella/core/exceptions/UmbrellaException.java b/core/src/main/java/de/srsoftware/umbrella/core/exceptions/UmbrellaException.java index 34552a9..e507788 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/exceptions/UmbrellaException.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/exceptions/UmbrellaException.java @@ -23,6 +23,7 @@ public class UmbrellaException extends Exception{ this.statusCode = statusCode; } + public UmbrellaException causedBy(Exception e) { initCause(e); return this; @@ -59,4 +60,8 @@ public class UmbrellaException extends Exception{ public int statusCode(){ return statusCode; } + + public static UmbrellaException unprocessable(String message, Object... fills) { + return new UmbrellaException(HTTP_UNPROCESSABLE,message,fills); + } } diff --git a/documents/build.gradle.kts b/documents/build.gradle.kts index aee7b1c..d0f8d16 100644 --- a/documents/build.gradle.kts +++ b/documents/build.gradle.kts @@ -8,7 +8,7 @@ dependencies{ implementation("de.srsoftware:document.api:2.0.0") implementation("de.srsoftware:document.file:1.0.1") implementation("de.srsoftware:document.processor:1.0.3") - implementation("de.srsoftware:document.zugferd:1.0.4") + implementation("de.srsoftware:document.zugferd:1.0.5") 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 5aca53f..cb962b2 100644 --- a/documents/src/main/java/de/srsoftware/umbrella/documents/DocumentApi.java +++ b/documents/src/main/java/de/srsoftware/umbrella/documents/DocumentApi.java @@ -6,6 +6,7 @@ import static de.srsoftware.document.mustang.Constants.KEY_DATA; import static de.srsoftware.document.mustang.Constants.KEY_PDF; import static de.srsoftware.document.mustang.Constants.KEY_PRODUCER; import static de.srsoftware.document.mustang.Constants.KEY_TEMPLATE; +import static de.srsoftware.document.zugferd.data.UnitCode.*; import static de.srsoftware.tools.MimeType.MIME_FORM_URL; import static de.srsoftware.tools.MimeType.MIME_PDF; import static de.srsoftware.tools.Optionals.isSet; @@ -15,8 +16,7 @@ import static de.srsoftware.umbrella.core.Constants.*; import static de.srsoftware.umbrella.core.Paths.LIST; import static de.srsoftware.umbrella.core.ResponseCode.HTTP_UNPROCESSABLE; import static de.srsoftware.umbrella.core.Util.request; -import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.forbidden; -import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingFieldException; +import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.*; import static de.srsoftware.umbrella.documents.Constants.*; import static de.srsoftware.umbrella.documents.model.Document.State.NEW; import static de.srsoftware.umbrella.documents.model.Document.State.SENT; @@ -202,14 +202,7 @@ public class DocumentApi extends BaseHandler { case LIST -> listCompaniesDocuments(ex,user.get(),token.orElse(null)); case TEMPLATES -> postTemplateList(ex,user.get()); case null -> postDocument(ex,user.get()); - default -> { - var docId = Long.parseLong(head); - yield switch (path.pop()){ - case null -> postToDocument(ex,path,user.get(),docId); - case PATH_SEND -> sendDocument(ex,path,user.get(),docId); - default -> super.doPost(path,ex); - }; - } + default -> postToDocument(ex,path,user.get(),Long.parseLong(head)); }; } catch (NumberFormatException ignored) { return super.doPost(path,ex); @@ -225,6 +218,12 @@ public class DocumentApi extends BaseHandler { if (!(json.has(EMAIL) && json.get(EMAIL) instanceof String email)) throw missingFieldException(EMAIL); if (!(json.has(SUBJECT) && json.get(SUBJECT) instanceof String subject)) throw missingFieldException(SUBJECT); if (!(json.has(CONTENT) && json.get(CONTENT) instanceof String content)) throw missingFieldException(CONTENT); + var settings = new CustomerSettings(doc.head(),doc.footer(),content); + try { + db.save(settings,doc); + } catch (UmbrellaException e) { + LOG.log(WARNING,e); + } LOG.log(WARNING,"Updating settings of company-customer-document combination not implemented!"); var attachment = new Attachment(doc.number()+".pdf",rendered.mimeType(),rendered.bytes()); var message = new Message(user,subject,content,null,List.of(attachment)); @@ -297,32 +296,32 @@ public class DocumentApi extends BaseHandler { return val -> message.format(new Object[]{val/100d}); } - private DocumentData convert(Document document) throws Converter.ConversionError { + private DocumentData convert(Document document) throws UmbrellaException { var currency = switch (document.currency()){ case "€" -> Currency.EUR; case "$" -> Currency.USD; - default -> throw new Converter.ConversionError("Unsupported currency: ",document.currency()); + default -> throw unprocessable("Unsupported currency: ",document.currency()); }; var typeCode = switch (document.type().name()){ case "invoice" -> TypeCode.HANDELSRECHNUNG; - default -> throw new Converter.ConversionError("Unsupported document type: ",document.type().name()); + default -> throw unprocessable("Unsupported document type: ",document.type().name()); }; var countryID = CountryCode.DE; LOG.log(WARNING,"countryID is hardcoded to be \"DE\", should be field of company!"); var sender = document.sender(); var match = POST_CODE.matcher(sender.name()); - if (!match.find()) throw new Converter.ConversionError(ERROR_ADDRESS_MISSING, SENDER); + if (!match.find()) throw unprocessable(ERROR_ADDRESS_MISSING, SENDER); var name = match.group(1).trim(); var streetAddress = match.group(2).trim(); var postCode = match.group(3); var city = match.group(4).trim(); var taxNumber = sender.taxNumber(); - if (!taxNumber.startsWith("DE")) throw new Converter.ConversionError("Invalid sender tax number ({0})!",taxNumber); + if (!taxNumber.startsWith("DE")) throw unprocessable("Invalid sender tax number ({0})!",taxNumber); var author = new Author(name,countryID,postCode,city,streetAddress,taxNumber); match = POST_CODE.matcher(document.customer().name()); - if (!match.find()) throw new Converter.ConversionError(ERROR_ADDRESS_MISSING,FIELD_CUSTOMER); + if (!match.find()) throw unprocessable(ERROR_ADDRESS_MISSING,FIELD_CUSTOMER); name = escapeHtmlEntities(match.group(1).trim()); streetAddress = escapeHtmlEntities(match.group(2).trim()); postCode = match.group(3); @@ -338,7 +337,7 @@ public class DocumentApi extends BaseHandler { try { deliveryDate = LocalDate.parse(document.delivery()); } catch (RuntimeException ex) { - throw new Converter.ConversionError("\"{0}\" is not a valid delivery date (cannot be parsed)!",document.delivery()); + throw unprocessable("\"{0}\" is not a valid delivery date (cannot be parsed)!",document.delivery()); } var lineItems = new ArrayList(); for (var entry : document.positions().entrySet()){ @@ -346,11 +345,12 @@ public class DocumentApi extends BaseHandler { var pos = entry.getValue(); UnitCode unit = switch (pos.unit()){ - case "jährlich" -> UnitCode.per_year; - case "pauschal" -> UnitCode.fixed; - case "h", "Stunden" -> UnitCode.hours; - case "Stück" -> UnitCode.pieces; - default -> throw new Converter.ConversionError("No unit code defined for {0}",pos.unit()); + case "d" -> per_day; + case "h", "Stunden" -> hours; + case "jährlich" -> per_year; + case "pauschal" -> fixed; + case "Stück" -> pieces; + default -> throw unprocessable("No unit code defined for {0}",pos.unit()); }; var percent = pos.tax(); var taxType = percent == 0 ? TaxType.Z : TaxType.S; @@ -384,7 +384,7 @@ public class DocumentApi extends BaseHandler { pdfData.put(USER,user); pdfData.put(FIELD_PRICE_FORMAT, priceFormat(document.currency(),user.language())); Map data = pdfData; - if (zugferd) try { // create additional data + if (zugferd) {// create additional data data = Map.of( KEY_XML,Map.of( KEY_PROFILE,"EN16931", @@ -396,10 +396,8 @@ public class DocumentApi extends BaseHandler { ), KEY_PRODUCER,"SRSoftware Umbrella" ); - } catch (Converter.ConversionError e){ - throw new UmbrellaException(500,"Failed to convert data: {0}",e.getMessage()).causedBy(e); } - var source = optDoc.get(); + var source = optDoc.get(); var rendered = source.render(data); if (rendered instanceof RenderError err) throw new UmbrellaException(500,"Failed to render {0}: {1}",source.name(),err.toString()); if (!(rendered instanceof Content content)) throw new UmbrellaException(500,"Unknown result type ({0}) returned from render process!",rendered.getClass().getSimpleName()); @@ -409,8 +407,8 @@ public class DocumentApi extends BaseHandler { } private boolean getRenderedDocument(HttpExchange ex, long docId, UmbrellaUser user) throws IOException, UmbrellaException { var document = getDocumentWithCompanyData(docId,user); - var content = renderDocument(document,user); - var headers = ex.getResponseHeaders(); + var content = renderDocument(document,user); + var headers = ex.getResponseHeaders(); headers.add(CONTENT_TYPE, MIME_PDF); headers.add(CONTENT_DISPOSITION,"attachment; filename=\""+document.number()+".pdf\""); return sendContent(ex,content.bytes()); @@ -418,7 +416,7 @@ public class DocumentApi extends BaseHandler { 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); + var resp = request(location, token.asMap(),MIME_FORM_URL,null); if (!(resp instanceof String s && s.startsWith("["))) throw new UmbrellaException(500,"{0} did not return JSON Array!",location); return new JSONArray(s); } @@ -428,10 +426,10 @@ public class DocumentApi extends BaseHandler { var json = json(ex); if (!json.has(COMPANY)) throw missingFieldException(COMPANY); long companyId = json.getLong(COMPANY); - var company = companies.get(companyId); + var company = companies.get(companyId); if (!companies.membership(companyId,user.id())) throw forbidden("You are mot a member of company {0}",company); var docs = db.listDocs(companyId); - var map = new HashMap(); + var map = new HashMap(); for (var entry : docs.entrySet()) map.put(entry.getKey(),entry.getValue().summary()); return sendContent(ex,new JSONObject(map).toString(2)); } catch (IOException e) { @@ -529,6 +527,7 @@ public class DocumentApi extends BaseHandler { var head = path.pop(); return switch (head){ case POSITION -> postDocumentPosition(docId,ex,user); + case PATH_SEND -> sendDocument(ex,path,user,docId); case null, default -> super.doPost(path,ex); }; } diff --git a/documents/src/main/java/de/srsoftware/umbrella/documents/DocumentDb.java b/documents/src/main/java/de/srsoftware/umbrella/documents/DocumentDb.java index 259d25b..4927c9d 100644 --- a/documents/src/main/java/de/srsoftware/umbrella/documents/DocumentDb.java +++ b/documents/src/main/java/de/srsoftware/umbrella/documents/DocumentDb.java @@ -41,6 +41,8 @@ public interface DocumentDb { Document save(Document document) throws UmbrellaException; + CustomerSettings save(CustomerSettings settings,Document doc) throws UmbrellaException; + CustomerSettings save(long companyId, Type docType, String customerId, CustomerSettings settings) throws UmbrellaException; /** diff --git a/documents/src/main/java/de/srsoftware/umbrella/documents/SqliteDb.java b/documents/src/main/java/de/srsoftware/umbrella/documents/SqliteDb.java index f600f74..24b8d11 100644 --- a/documents/src/main/java/de/srsoftware/umbrella/documents/SqliteDb.java +++ b/documents/src/main/java/de/srsoftware/umbrella/documents/SqliteDb.java @@ -7,6 +7,7 @@ import static de.srsoftware.tools.jdbc.Condition.in; import static de.srsoftware.tools.jdbc.Query.*; import static de.srsoftware.tools.jdbc.Query.SelectQuery.ALL; import static de.srsoftware.umbrella.core.Constants.*; +import static de.srsoftware.umbrella.core.ResponseCode.HTTP_SERVER_ERROR; import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.databaseException; import static de.srsoftware.umbrella.documents.Constants.*; import static de.srsoftware.umbrella.documents.model.Document.DEFAULT_THOUSANDS_SEPARATOR; @@ -409,6 +410,21 @@ CREATE TABLE IF NOT EXISTS {0} ( {1} VARCHAR(255) PRIMARY KEY, {2} VARCHAR(255) } } + @Override + public CustomerSettings save(CustomerSettings settings, Document doc) throws UmbrellaException { + var companyId = doc.companyId(); + var typeId = doc.type().id(); + var customerId = doc.customer().id(); + try{ + replaceInto(TABLE_CUSTOMER_SETTINGS, FIELD_COMPANY_ID, FIELD_DOC_TYPE_ID, FIELD_CUSTOMER_NUMBER, FIELD_DEFAULT_HEADER, FIELD_DEFAULT_FOOTER, FIELD_DEFAULT_MAIL) + .values(companyId, typeId, customerId, settings.header(), settings.footer(), settings.mailText()) + .execute(db); + return settings; + } catch (SQLException e){ + throw new UmbrellaException(HTTP_SERVER_ERROR,"Failed to update customer settings"); + } + } + @Override public Document save(Document doc) throws UmbrellaException { if (doc.isNew()) try { diff --git a/frontend/src/routes/document/Send.svelte b/frontend/src/routes/document/Send.svelte index be0f274..da7b2d4 100644 --- a/frontend/src/routes/document/Send.svelte +++ b/frontend/src/routes/document/Send.svelte @@ -67,5 +67,6 @@
{t('actions')} +
\ No newline at end of file