Browse Source

implemented storing of customer settings when sending document

feature/document
Stephan Richter 4 months ago
parent
commit
67cb0b61b6
  1. 5
      core/src/main/java/de/srsoftware/umbrella/core/exceptions/UmbrellaException.java
  2. 2
      documents/build.gradle.kts
  3. 61
      documents/src/main/java/de/srsoftware/umbrella/documents/DocumentApi.java
  4. 2
      documents/src/main/java/de/srsoftware/umbrella/documents/DocumentDb.java
  5. 16
      documents/src/main/java/de/srsoftware/umbrella/documents/SqliteDb.java
  6. 1
      frontend/src/routes/document/Send.svelte

5
core/src/main/java/de/srsoftware/umbrella/core/exceptions/UmbrellaException.java

@ -23,6 +23,7 @@ public class UmbrellaException extends Exception{ @@ -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{ @@ -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);
}
}

2
documents/build.gradle.kts

@ -8,7 +8,7 @@ dependencies{ @@ -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")
}

61
documents/src/main/java/de/srsoftware/umbrella/documents/DocumentApi.java

@ -6,6 +6,7 @@ import static de.srsoftware.document.mustang.Constants.KEY_DATA; @@ -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.*; @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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<LineItem>();
for (var entry : document.positions().entrySet()){
@ -346,11 +345,12 @@ public class DocumentApi extends BaseHandler { @@ -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 { @@ -384,7 +384,7 @@ public class DocumentApi extends BaseHandler {
pdfData.put(USER,user);
pdfData.put(FIELD_PRICE_FORMAT, priceFormat(document.currency(),user.language()));
Map<String,Object> 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 { @@ -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 { @@ -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 { @@ -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 { @@ -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<Long,Object>();
var map = new HashMap<Long,Object>();
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 { @@ -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);
};
}

2
documents/src/main/java/de/srsoftware/umbrella/documents/DocumentDb.java

@ -41,6 +41,8 @@ public interface DocumentDb { @@ -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;
/**

16
documents/src/main/java/de/srsoftware/umbrella/documents/SqliteDb.java

@ -7,6 +7,7 @@ import static de.srsoftware.tools.jdbc.Condition.in; @@ -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) @@ -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 {

1
frontend/src/routes/document/Send.svelte

@ -67,5 +67,6 @@ @@ -67,5 +67,6 @@
</fieldset>
<fieldset>
<legend>{t('actions')}</legend>
<button onclick={() => router.navigate(`/document/${id}/view`)}>{t('abort')}</button>
<button onclick={doSend}>{t('do_send')}</button>
</fieldset>
Loading…
Cancel
Save