Browse Source

preparing document submission

Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
feature/document
Stephan Richter 4 months ago
parent
commit
93449e4bad
  1. 1
      core/src/main/java/de/srsoftware/umbrella/core/api/UserService.java
  2. 1
      documents/src/main/java/de/srsoftware/umbrella/documents/Constants.java
  3. 53
      documents/src/main/java/de/srsoftware/umbrella/documents/DocumentApi.java
  4. 10
      documents/src/main/java/de/srsoftware/umbrella/documents/model/CustomerSettings.java
  5. 1
      documents/src/main/java/de/srsoftware/umbrella/documents/model/Document.java
  6. 51
      frontend/src/routes/document/Send.svelte
  7. 1
      frontend/src/translations.svelte.js
  8. 7
      translations/src/main/resources/de.json

1
core/src/main/java/de/srsoftware/umbrella/core/api/UserService.java

@ -11,4 +11,5 @@ public interface UserService { @@ -11,4 +11,5 @@ public interface UserService {
UmbrellaUser loadUser(long userId) throws UmbrellaException;
Optional<UmbrellaUser> loadUser(Optional<Token> sessionToken) throws UmbrellaException;
Optional<UmbrellaUser> loadUser(HttpExchange ex) throws UmbrellaException;
PostBox postBox();
}

1
documents/src/main/java/de/srsoftware/umbrella/documents/Constants.java

@ -18,6 +18,7 @@ public class Constants { @@ -18,6 +18,7 @@ public class Constants {
public static final String CONFIG_DATABASE = "umbrella.modules.document.database";
public static final String CONFIG_TEMPLATES = "umbrella.modules.document.templates";
public static final String CONTACTS = "contacts";
public static final String CONTENT = "content";
public static final String CONTENT_DISPOSITION = "Content-Disposition";
public static final String CUSTOMERS = "customers";

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

@ -7,6 +7,7 @@ import static de.srsoftware.document.mustang.Constants.KEY_PDF; @@ -7,6 +7,7 @@ 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.tools.MimeType.MIME_FORM_URL;
import static de.srsoftware.tools.MimeType.MIME_PDF;
import static de.srsoftware.tools.Optionals.isSet;
import static de.srsoftware.tools.Strings.escapeHtmlEntities;
import static de.srsoftware.umbrella.core.ConnectionProvider.connect;
@ -28,6 +29,7 @@ import de.srsoftware.configuration.Configuration; @@ -28,6 +29,7 @@ import de.srsoftware.configuration.Configuration;
import de.srsoftware.document.api.Content;
import de.srsoftware.document.api.DocumentRegistry;
import de.srsoftware.document.api.RenderError;
import de.srsoftware.document.api.RenderResult;
import de.srsoftware.document.files.DocumentDirectory;
import de.srsoftware.document.processor.latex.LatexFactory;
import de.srsoftware.document.processor.weasyprint.WeasyFactory;
@ -68,6 +70,7 @@ public class DocumentApi extends BaseHandler { @@ -68,6 +70,7 @@ public class DocumentApi extends BaseHandler {
private final DocumentDb db;
private final UserService users;
private final Translator translator;
private final PostBox messages;
public DocumentApi(CompanyService companyService, Translator translator, Configuration config) throws UmbrellaException {
this.config = config;
@ -76,6 +79,7 @@ public class DocumentApi extends BaseHandler { @@ -76,6 +79,7 @@ public class DocumentApi extends BaseHandler {
db = new SqliteDb(connect(dbFile));
companies = companyService;
users = companyService.userService();
messages = users.postBox();
Optional<String> templates = config.get(CONFIG_TEMPLATES);
if (templates.isEmpty()) throw missingFieldException(CONFIG_TEMPLATES);
@ -145,6 +149,7 @@ public class DocumentApi extends BaseHandler { @@ -145,6 +149,7 @@ public class DocumentApi extends BaseHandler {
yield switch (head){
case null -> getDocument(ex,docId,user.get());
case PATH_PDF -> getRenderedDocument(ex,docId,user.get());
case SETTINGS -> getDocumentSettings(ex,docId,user.get());
default -> super.doGet(path,ex);
};
@ -214,7 +219,16 @@ public class DocumentApi extends BaseHandler { @@ -214,7 +219,16 @@ public class DocumentApi extends BaseHandler {
}
}
private boolean sendDocument(HttpExchange ex, Path path, UmbrellaUser umbrellaUser, long docId) throws IOException {
private boolean sendDocument(HttpExchange ex, Path path, UmbrellaUser user, long docId) throws IOException, UmbrellaException {
var doc = getDocument(docId,user).a;
var rendered = renderDocument(doc,user);
var json = json(ex);
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);
postBox.send()
return sendEmptyResponse(HTTP_NOT_IMPLEMENTED,ex);
}
@ -249,13 +263,13 @@ public class DocumentApi extends BaseHandler { @@ -249,13 +263,13 @@ public class DocumentApi extends BaseHandler {
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;
}
@ -264,6 +278,14 @@ public class DocumentApi extends BaseHandler { @@ -264,6 +278,14 @@ public class DocumentApi extends BaseHandler {
return sendContent(ex,doc.renderToMap());
}
private boolean getDocumentSettings(HttpExchange ex, long docId, UmbrellaUser user) throws IOException, UmbrellaException {
var tuple = getDocument(docId,user);
var doc = tuple.a;
var company = tuple.b;
var settings = db.getCustomerSettings(company.id(),doc.type(),doc.customer().id());
return sendContent(ex,settings);
}
private PriceFormat priceFormat(String currency, String language) {
var pattern = switch (currency){
case "$" -> "$&nbsp;{0,number,#,###.00}";
@ -343,9 +365,7 @@ public class DocumentApi extends BaseHandler { @@ -343,9 +365,7 @@ public class DocumentApi extends BaseHandler {
return new DocumentData(document.number(),currency,typeCode,document.date(),author,customer,notes,deliveryDate,null,terms,lineItems);
}
private boolean getRenderedDocument(HttpExchange ex, long docId, UmbrellaUser user) throws IOException, UmbrellaException {
var document = getDocumentWithCompanyData(docId,user);
private Content renderDocument(Document document, UmbrellaUser user) throws UmbrellaException {
var template = document.template().name();
var templateName = template+".html.pdf";
var type = document.type().name();
@ -377,19 +397,22 @@ public class DocumentApi extends BaseHandler { @@ -377,19 +397,22 @@ public class DocumentApi extends BaseHandler {
} catch (Converter.ConversionError e){
throw new UmbrellaException(500,"Failed to convert data: {0}",e.getMessage()).causedBy(e);
}
var rendered = optDoc.get().render(data);
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) {
var headers = ex.getResponseHeaders();
headers.add(CONTENT_TYPE, source.mimeType());
headers.add(CONTENT_DISPOSITION,"attachment; filename=\""+document.number()+".pdf\"");
return sendContent(ex,content.bytes());
}
throw new UmbrellaException(500,"Unknown result type ({0}) returned from render process!",rendered.getClass().getSimpleName());
if (!(rendered instanceof Content content)) throw new UmbrellaException(500,"Unknown result type ({0}) returned from render process!",rendered.getClass().getSimpleName());
return content;
}
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();
headers.add(CONTENT_TYPE, MIME_PDF);
headers.add(CONTENT_DISPOSITION,"attachment; filename=\""+document.number()+".pdf\"");
return sendContent(ex,content.bytes());
}
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!"));

10
documents/src/main/java/de/srsoftware/umbrella/documents/model/CustomerSettings.java

@ -2,16 +2,24 @@ @@ -2,16 +2,24 @@
package de.srsoftware.umbrella.documents.model;
import de.srsoftware.tools.Mappable;
import static de.srsoftware.umbrella.documents.Constants.*;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;
public record CustomerSettings(String header, String footer, String mailText) {
public record CustomerSettings(String header, String footer, String mailText) implements Mappable {
public static CustomerSettings of(ResultSet rs) throws SQLException {
var header = rs.getString(FIELD_DEFAULT_HEADER);
var footer = rs.getString(FIELD_DEFAULT_FOOTER);
var mailText = rs.getString(FIELD_DEFAULT_MAIL);
return new CustomerSettings(header,footer,mailText);
}
@Override
public Map<String, Object> toMap() {
return Map.of(FIELD_FOOTER,footer,FIELD_HEAD,header,CONTENT,mailText);
}
}

1
documents/src/main/java/de/srsoftware/umbrella/documents/model/Document.java

@ -62,6 +62,7 @@ public final class Document implements Mappable { @@ -62,6 +62,7 @@ public final class Document implements Mappable {
private final PositionList positions;
private final Set<String> dirtyFields = new HashSet<>();
public Document(long id, long companyId, String number, Type type, LocalDate date, State state, Template template, String delivery, String head, String footer, String currency, String thousandsSeparator, Sender sender, Customer customer, PositionList positions) {
this.id = id;
this.companyId = companyId;

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

@ -7,6 +7,49 @@ @@ -7,6 +7,49 @@
let { id } = $props();
let error = $state(null);
let doc = $state(null);
let email = $state(null);
let content = $state(null);
let subject = $state(null);
async function loadDoc(){
let url = `${location.protocol}//${location.host.replace('5173','8080')}/api/document/${id}`;
let resp = await fetch(url,{credentials:'include'});
if (resp.ok){
doc = await resp.json();
email = doc.customer.email;
subject = t('new_document_from',t(doc.type),doc.company.name,doc.number),
error = null;
} else {
error = await resp.text();
return;
}
url = `${location.protocol}//${location.host.replace('5173','8080')}/api/document/${id}/settings`;
resp = await fetch(url,{credentials:'include'});
if (resp.ok){
const settings = await resp.json();
content = settings.content;
} else {
error = await resp.text();
}
}
async function doSend(){
var data = {email:email,subject:subject,content:content};
const url = `${location.protocol}//${location.host.replace('5173','8080')}/api/document/${id}/send`;
const resp = fetch(url,{
credentials:'include',
method: 'POST',
body: JSON.stringify(data)
});
if (resp.ok){
router.navigate('/document');
} else {
error = await resp.text();
}
}
onMount(loadDoc);
</script>
{#if error}
@ -14,11 +57,15 @@ @@ -14,11 +57,15 @@
{/if}
<fieldset>
<legend>{t('customer_email')}</legend>
<legend>{t('customer_email')} / {t('subject')}</legend>
<input type="text" bind:value={email} />
<input type="text" bind:value={subject} />
</fieldset>
<fieldset>
<legend>{t('content')}</legend>
<legend>{t('message')}</legend>
<textarea bind:value={content}></textarea>
</fieldset>
<fieldset>
<legend>{t('actions')}</legend>
<button onclick={doSend}>{t('do_send')}</button>
</fieldset>

1
frontend/src/translations.svelte.js

@ -8,6 +8,7 @@ export async function loadTranslation(lang){ @@ -8,6 +8,7 @@ export async function loadTranslation(lang){
}
export function t(key,...args){
if (key === undefined) return "";
if (key instanceof Response) key = 'status.'+key.status;
let set = translations.values;
let keys = key.split('.');

7
translations/src/main/resources/de.json

@ -37,7 +37,8 @@ @@ -37,7 +37,8 @@
"description": "Beschreibung",
"document": "Dokumente",
"documents": "Dokumente",
"do_login" : "anmelden",
"do_login" : "anmelden",
"do_send" : "versenden",
"edit": "Bearbeiten",
"editing": "Nutzer {0} bearbeiten",
"edit_password": "Passwort ändern",
@ -81,6 +82,7 @@ @@ -81,6 +82,7 @@
"logout": "Abmelden",
"MANAGE_LOGIN_SERVICES": "Login-Services verwalten",
"message": "Nachricht",
"messages": "Benachrichtigungen",
"model": "Modelle",
"mismatch": "ungleich",
@ -90,6 +92,7 @@ @@ -90,6 +92,7 @@
"net_price": "Nettopreis",
"net_sum": "Netto-Summe",
"new_password": "neues Passwort",
"new_document_from": "{2} / neues {0}s-Dokument von {1}",
"notes": "Notizen",
"number": "Nummer",
@ -114,6 +117,7 @@ @@ -114,6 +117,7 @@
"search": "Suche",
"select_company" : "Wählen Sie eine ihrer Firmen:",
"select_customer": "Kunde auswählen",
"send_document": "Dokument versenden",
"sender": "Absender",
"sender_bank_account": "Bankverbindung",
"sender_local_court": "Amtsgericht",
@ -135,6 +139,7 @@ @@ -135,6 +139,7 @@
"501": "Nicht implementiert"
},
"stock": "Inventar",
"subject": "Betreff",
"task": "Aufgaben",
"tax_id": "Steuernummer",

Loading…
Cancel
Save