implemented seach in documents
This commit is contained in:
@@ -22,9 +22,9 @@ import static de.srsoftware.umbrella.core.Constants.FIELD_PRICE_FORMAT;
|
|||||||
import static de.srsoftware.umbrella.core.Constants.FIELD_TIME_ID;
|
import static de.srsoftware.umbrella.core.Constants.FIELD_TIME_ID;
|
||||||
import static de.srsoftware.umbrella.core.Constants.FIELD_TYPE;
|
import static de.srsoftware.umbrella.core.Constants.FIELD_TYPE;
|
||||||
import static de.srsoftware.umbrella.core.Constants.FIELD_UNIT;
|
import static de.srsoftware.umbrella.core.Constants.FIELD_UNIT;
|
||||||
import static de.srsoftware.umbrella.core.Paths.LIST;
|
import static de.srsoftware.umbrella.core.Paths.*;
|
||||||
import static de.srsoftware.umbrella.core.Paths.STATES;
|
|
||||||
import static de.srsoftware.umbrella.core.ResponseCode.HTTP_UNPROCESSABLE;
|
import static de.srsoftware.umbrella.core.ResponseCode.HTTP_UNPROCESSABLE;
|
||||||
|
import static de.srsoftware.umbrella.core.Util.mapValues;
|
||||||
import static de.srsoftware.umbrella.core.Util.request;
|
import static de.srsoftware.umbrella.core.Util.request;
|
||||||
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.*;
|
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.*;
|
||||||
import static de.srsoftware.umbrella.core.model.Document.State.NEW;
|
import static de.srsoftware.umbrella.core.model.Document.State.NEW;
|
||||||
@@ -52,6 +52,7 @@ import de.srsoftware.tools.SessionToken;
|
|||||||
import de.srsoftware.tools.Tuple;
|
import de.srsoftware.tools.Tuple;
|
||||||
import de.srsoftware.umbrella.core.BaseHandler;
|
import de.srsoftware.umbrella.core.BaseHandler;
|
||||||
import de.srsoftware.umbrella.core.ModuleRegistry;
|
import de.srsoftware.umbrella.core.ModuleRegistry;
|
||||||
|
import de.srsoftware.umbrella.core.Paths;
|
||||||
import de.srsoftware.umbrella.core.api.*;
|
import de.srsoftware.umbrella.core.api.*;
|
||||||
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
|
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
|
||||||
import de.srsoftware.umbrella.core.model.*;
|
import de.srsoftware.umbrella.core.model.*;
|
||||||
@@ -148,7 +149,7 @@ public class DocumentApi extends BaseHandler implements DocumentService {
|
|||||||
yield switch (head){
|
yield switch (head){
|
||||||
case null -> getDocument(ex,docId,user.get());
|
case null -> getDocument(ex,docId,user.get());
|
||||||
case PATH_PDF -> getRenderedDocument(ex,docId,user.get());
|
case PATH_PDF -> getRenderedDocument(ex,docId,user.get());
|
||||||
case SETTINGS -> getDocumentSettings(ex,docId,user.get());
|
case Paths.SETTINGS -> getDocumentSettings(ex,docId,user.get());
|
||||||
default -> super.doGet(path,ex);
|
default -> super.doGet(path,ex);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -191,7 +192,8 @@ public class DocumentApi extends BaseHandler implements DocumentService {
|
|||||||
if (user.isEmpty()) return unauthorized(ex);
|
if (user.isEmpty()) return unauthorized(ex);
|
||||||
var head = path.pop();
|
var head = path.pop();
|
||||||
return switch (head){
|
return switch (head){
|
||||||
case LIST -> listCompaniesDocuments(ex,user.get(),token.orElse(null));
|
case LIST -> listCompaniesDocuments(ex,user.get());
|
||||||
|
case SEARCH -> postSearch(ex,user.get());
|
||||||
case TEMPLATES -> postTemplateList(ex,user.get());
|
case TEMPLATES -> postTemplateList(ex,user.get());
|
||||||
case null -> postDocument(ex,user.get());
|
case null -> postDocument(ex,user.get());
|
||||||
default -> postToDocument(ex,path,user.get(),Long.parseLong(head));
|
default -> postToDocument(ex,path,user.get(),Long.parseLong(head));
|
||||||
@@ -203,27 +205,6 @@ public class DocumentApi extends BaseHandler implements DocumentService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean sendDocument(HttpExchange ex, Path path, UmbrellaUser user, long docId) throws IOException, UmbrellaException {
|
|
||||||
var doc = getDocumentWithCompanyData(docId,user);
|
|
||||||
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);
|
|
||||||
var settings = new CustomerSettings(doc.head(),doc.footer(),content);
|
|
||||||
try {
|
|
||||||
db.save(settings,doc);
|
|
||||||
} catch (UmbrellaException e) {
|
|
||||||
LOG.log(WARNING,e);
|
|
||||||
}
|
|
||||||
var attachment = new Attachment(doc.number()+".pdf",rendered.mimeType(),rendered.bytes());
|
|
||||||
var message = new Message(user,subject,content,null,List.of(attachment));
|
|
||||||
var envelope = new Envelope(message,new User(doc.customer().shortName(),new EmailAddress(email),doc.customer().language()));
|
|
||||||
postBox().send(envelope);
|
|
||||||
db.save(doc.set(SENT));
|
|
||||||
return ok(ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean getContacts(HttpExchange ex, UmbrellaUser user, Token token) throws IOException, UmbrellaException {
|
private boolean getContacts(HttpExchange ex, UmbrellaUser user, Token token) throws IOException, UmbrellaException {
|
||||||
return sendContent(ex,getLegacyContacts(ex,user,token));
|
return sendContent(ex,getLegacyContacts(ex,user,token));
|
||||||
}
|
}
|
||||||
@@ -274,15 +255,6 @@ public class DocumentApi extends BaseHandler implements DocumentService {
|
|||||||
return sendContent(ex,settings);
|
return sendContent(ex,settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
private PriceFormat priceFormat(String currency, String language) {
|
|
||||||
var pattern = switch (currency){
|
|
||||||
case "$" -> "$ {0,number,#,###.00}";
|
|
||||||
default -> "{0,number,#,###.00} "+currency;
|
|
||||||
};
|
|
||||||
var message = new MessageFormat(pattern, "de".equals(language)? Locale.GERMAN:Locale.US);
|
|
||||||
return val -> message.format(new Object[]{val/100d});
|
|
||||||
}
|
|
||||||
|
|
||||||
private DocumentData convert(Document document) throws UmbrellaException {
|
private DocumentData convert(Document document) throws UmbrellaException {
|
||||||
var currency = switch (document.currency()){
|
var currency = switch (document.currency()){
|
||||||
case "€" -> Currency.EUR;
|
case "€" -> Currency.EUR;
|
||||||
@@ -392,6 +364,7 @@ public class DocumentApi extends BaseHandler implements DocumentService {
|
|||||||
return content;
|
return content;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean getRenderedDocument(HttpExchange ex, long docId, UmbrellaUser user) throws IOException, UmbrellaException {
|
private boolean getRenderedDocument(HttpExchange ex, long docId, UmbrellaUser user) throws IOException, UmbrellaException {
|
||||||
var document = getDocumentWithCompanyData(docId,user);
|
var document = getDocumentWithCompanyData(docId,user);
|
||||||
var content = renderDocument(document,user);
|
var content = renderDocument(document,user);
|
||||||
@@ -412,8 +385,7 @@ public class DocumentApi extends BaseHandler implements DocumentService {
|
|||||||
return db.listDocs(companyId);
|
return db.listDocs(companyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean listCompaniesDocuments(HttpExchange ex, UmbrellaUser user) throws UmbrellaException {
|
||||||
private boolean listCompaniesDocuments(HttpExchange ex, UmbrellaUser user, Token token) throws UmbrellaException {
|
|
||||||
try {
|
try {
|
||||||
var json = json(ex);
|
var json = json(ex);
|
||||||
if (!json.has(COMPANY)) throw missingFieldException(COMPANY);
|
if (!json.has(COMPANY)) throw missingFieldException(COMPANY);
|
||||||
@@ -556,6 +528,17 @@ public class DocumentApi extends BaseHandler implements DocumentService {
|
|||||||
return sendContent(ex,templates.stream().map(Template::toMap));
|
return sendContent(ex,templates.stream().map(Template::toMap));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean postSearch(HttpExchange ex, UmbrellaUser user) throws IOException {
|
||||||
|
var json = json(ex);
|
||||||
|
if (!(json.has(KEY) && json.get(KEY) instanceof String key)) throw missingFieldException(KEY);
|
||||||
|
var keys = Arrays.asList(key.split(" "));
|
||||||
|
var fulltext = json.has(FULLTEXT) && json.get(FULLTEXT) instanceof Boolean val && val;
|
||||||
|
|
||||||
|
var userCompanyIds = companyService().listCompaniesOf(user).keySet();
|
||||||
|
|
||||||
|
var documents = db.find(userCompanyIds,keys,fulltext);
|
||||||
|
return sendContent(ex,mapValues(documents));
|
||||||
|
}
|
||||||
|
|
||||||
private boolean postToDocument(HttpExchange ex, Path path, UmbrellaUser user, long docId) throws IOException, UmbrellaException {
|
private boolean postToDocument(HttpExchange ex, Path path, UmbrellaUser user, long docId) throws IOException, UmbrellaException {
|
||||||
var head = path.pop();
|
var head = path.pop();
|
||||||
@@ -566,4 +549,34 @@ public class DocumentApi extends BaseHandler implements DocumentService {
|
|||||||
case null, default -> super.doPost(path,ex);
|
case null, default -> super.doPost(path,ex);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private PriceFormat priceFormat(String currency, String language) {
|
||||||
|
var pattern = switch (currency){
|
||||||
|
case "$" -> "$ {0,number,#,###.00}";
|
||||||
|
default -> "{0,number,#,###.00} "+currency;
|
||||||
|
};
|
||||||
|
var message = new MessageFormat(pattern, "de".equals(language)? Locale.GERMAN:Locale.US);
|
||||||
|
return val -> message.format(new Object[]{val/100d});
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean sendDocument(HttpExchange ex, Path path, UmbrellaUser user, long docId) throws IOException, UmbrellaException {
|
||||||
|
var doc = getDocumentWithCompanyData(docId,user);
|
||||||
|
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);
|
||||||
|
var settings = new CustomerSettings(doc.head(),doc.footer(),content);
|
||||||
|
try {
|
||||||
|
db.save(settings,doc);
|
||||||
|
} catch (UmbrellaException e) {
|
||||||
|
LOG.log(WARNING,e);
|
||||||
|
}
|
||||||
|
var attachment = new Attachment(doc.number()+".pdf",rendered.mimeType(),rendered.bytes());
|
||||||
|
var message = new Message(user,subject,content,null,List.of(attachment));
|
||||||
|
var envelope = new Envelope(message,new User(doc.customer().shortName(),new EmailAddress(email),doc.customer().language()));
|
||||||
|
postBox().send(envelope);
|
||||||
|
db.save(doc.set(SENT));
|
||||||
|
return ok(ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,8 @@ import de.srsoftware.umbrella.core.model.Document;
|
|||||||
import de.srsoftware.umbrella.core.model.Template;
|
import de.srsoftware.umbrella.core.model.Template;
|
||||||
import de.srsoftware.umbrella.core.model.Type;
|
import de.srsoftware.umbrella.core.model.Type;
|
||||||
import de.srsoftware.umbrella.documents.model.*;
|
import de.srsoftware.umbrella.documents.model.*;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashMap;
|
import java.util.*;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public interface DocumentDb {
|
public interface DocumentDb {
|
||||||
Long dropPosition(long documentId, long pos) throws UmbrellaException;
|
Long dropPosition(long documentId, long pos) throws UmbrellaException;
|
||||||
@@ -22,6 +20,10 @@ public interface DocumentDb {
|
|||||||
*/
|
*/
|
||||||
String deleteDoc(Long docId) throws UmbrellaException;
|
String deleteDoc(Long docId) throws UmbrellaException;
|
||||||
|
|
||||||
|
public Map<Long, Map<Long, String>> docReferencedByTimes(Set<Long> timeIds) throws UmbrellaException;
|
||||||
|
|
||||||
|
Map<Long, Document> find(Collection<Long> companyIds, List<String> keys, boolean fulltext);
|
||||||
|
|
||||||
Long getCustomerPrice(long company, String id, String itemCode) throws UmbrellaException;
|
Long getCustomerPrice(long company, String id, String itemCode) throws UmbrellaException;
|
||||||
|
|
||||||
CustomerSettings getCustomerSettings(long companyId, Type docType, String customerId) throws UmbrellaException;
|
CustomerSettings getCustomerSettings(long companyId, Type docType, String customerId) throws UmbrellaException;
|
||||||
@@ -32,8 +34,6 @@ public interface DocumentDb {
|
|||||||
|
|
||||||
Type getType(int typeId) throws UmbrellaException;
|
Type getType(int typeId) throws UmbrellaException;
|
||||||
|
|
||||||
public Map<Long, Map<Long, String>> docReferencedByTimes(Set<Long> timeIds) throws UmbrellaException;
|
|
||||||
|
|
||||||
Map<Long, Document> listDocs(long companyId) throws UmbrellaException;
|
Map<Long, Document> listDocs(long companyId) throws UmbrellaException;
|
||||||
|
|
||||||
HashMap<Integer, Type> listTypes() throws UmbrellaException;
|
HashMap<Integer, Type> listTypes() throws UmbrellaException;
|
||||||
|
|||||||
@@ -2,8 +2,7 @@
|
|||||||
package de.srsoftware.umbrella.documents;
|
package de.srsoftware.umbrella.documents;
|
||||||
|
|
||||||
|
|
||||||
import static de.srsoftware.tools.jdbc.Condition.equal;
|
import static de.srsoftware.tools.jdbc.Condition.*;
|
||||||
import static de.srsoftware.tools.jdbc.Condition.in;
|
|
||||||
import static de.srsoftware.tools.jdbc.Query.*;
|
import static de.srsoftware.tools.jdbc.Query.*;
|
||||||
import static de.srsoftware.tools.jdbc.Query.SelectQuery.ALL;
|
import static de.srsoftware.tools.jdbc.Query.SelectQuery.ALL;
|
||||||
import static de.srsoftware.umbrella.core.Constants.*;
|
import static de.srsoftware.umbrella.core.Constants.*;
|
||||||
@@ -31,12 +30,8 @@ public class SqliteDb implements DocumentDb{
|
|||||||
private static final System.Logger LOG = System.getLogger(SqliteDb.class.getSimpleName());
|
private static final System.Logger LOG = System.getLogger(SqliteDb.class.getSimpleName());
|
||||||
private final Connection db;
|
private final Connection db;
|
||||||
private static final String DB_VERSION = "message_db_version";
|
private static final String DB_VERSION = "message_db_version";
|
||||||
|
|
||||||
|
|
||||||
private static final int INITIAL_DB_VERSION = 1;
|
private static final int INITIAL_DB_VERSION = 1;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public SqliteDb(Connection conn){
|
public SqliteDb(Connection conn){
|
||||||
db = conn;
|
db = conn;
|
||||||
init();
|
init();
|
||||||
@@ -334,6 +329,36 @@ CREATE TABLE IF NOT EXISTS {0} ( {1} VARCHAR(255) PRIMARY KEY, {2} VARCHAR(255)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<Long, Document> find(Collection<Long> companyIds, List<String> keys, boolean fulltext) {
|
||||||
|
try {
|
||||||
|
var rs = Query.select(ALL).from(TABLE_DOCUMENT_TYPES).exec(db);
|
||||||
|
var types = new HashMap<Integer,Type>();
|
||||||
|
while (rs.next()) types.put(rs.getInt(ID),toType(rs));
|
||||||
|
rs.close();
|
||||||
|
|
||||||
|
var query = Query.select(ALL).from(TABLE_DOCUMENTS).where(COMPANY_ID,in(companyIds.toArray()));
|
||||||
|
for (var key : keys) query.where(format("CONCAT({0},\" \",{1},\" \",{2},\" \",{3},\" \",{4},\" \",{5},\" \",{6},\" \",{7},\" \",{8},\" \",{9})",NUMBER,FIELD_HEAD,FIELD_FOOTER,SENDER,FIELD_TAX_NUMBER,FIELD_BANK_ACCOUNT,FIELD_CUSTOMER,FIELD_CUSTOMER_NUMBER,FIELD_CUSTOMER_TAX_NUMBER,FIELD_CUSTOMER_EMAIL),like("%"+key+"%"));
|
||||||
|
rs = query.exec(db);
|
||||||
|
var map = new HashMap<Long,Document>();
|
||||||
|
while (rs.next()) map.put(rs.getLong(ID),toDoc(rs,types));
|
||||||
|
rs.close();
|
||||||
|
|
||||||
|
rs = Query.select(ALL).from(TABLE_POSITIONS).where(FIELD_DOCUMENT_ID,in(map.keySet().toArray())).exec(db);
|
||||||
|
while (rs.next()){
|
||||||
|
var docId = rs.getLong(FIELD_DOCUMENT_ID);
|
||||||
|
var position = toPosition(rs);
|
||||||
|
map.get(docId).positions().add(position);
|
||||||
|
position.clean();
|
||||||
|
}
|
||||||
|
rs.close();
|
||||||
|
return map;
|
||||||
|
} catch (SQLException e) {
|
||||||
|
LOG.log(WARNING,"Failed to search list of documents.");
|
||||||
|
throw new UmbrellaException(500,"Failed to search list of documents.").causedBy(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<Long, Document> listDocs(long companyId) throws UmbrellaException {
|
public Map<Long, Document> listDocs(long companyId) throws UmbrellaException {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
const router = useTinyRouter();
|
const router = useTinyRouter();
|
||||||
let bookmarks = $state(null);
|
let bookmarks = $state(null);
|
||||||
let companies = $state(null);
|
let companies = $state(null);
|
||||||
|
let documents = $state(null);
|
||||||
let error = $state(null);
|
let error = $state(null);
|
||||||
let fulltext = false;
|
let fulltext = false;
|
||||||
let key = $state(router.getQueryParam('key'));
|
let key = $state(router.getQueryParam('key'));
|
||||||
@@ -43,6 +44,7 @@
|
|||||||
};
|
};
|
||||||
fetch(api('bookmark/search'),options).then(handleBookmarks);
|
fetch(api('bookmark/search'),options).then(handleBookmarks);
|
||||||
fetch(api('company/search'),options).then(handleCompanies);
|
fetch(api('company/search'),options).then(handleCompanies);
|
||||||
|
fetch(api('document/search'),options).then(handleDocuments);
|
||||||
fetch(api('notes/search'),options).then(handleNotes);
|
fetch(api('notes/search'),options).then(handleNotes);
|
||||||
fetch(api('project/search'),options).then(handleProjects);
|
fetch(api('project/search'),options).then(handleProjects);
|
||||||
fetch(api('task/search'),options).then(handleTasks);
|
fetch(api('task/search'),options).then(handleTasks);
|
||||||
@@ -76,6 +78,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleDocuments(resp){
|
||||||
|
if (resp.ok){
|
||||||
|
const json = await resp.json();
|
||||||
|
documents = Object.keys(json).length ? json : null;
|
||||||
|
} else {
|
||||||
|
error = await resp.text();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function handleNotes(resp){
|
async function handleNotes(resp){
|
||||||
if (resp.ok){
|
if (resp.ok){
|
||||||
const json = await resp.json();
|
const json = await resp.json();
|
||||||
@@ -212,7 +223,7 @@
|
|||||||
{#if times}
|
{#if times}
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>
|
<legend>
|
||||||
{t('timetracks')}
|
{t('timetracking')}
|
||||||
</legend>
|
</legend>
|
||||||
<ul>
|
<ul>
|
||||||
{#each Object.values(times) as time}
|
{#each Object.values(times) as time}
|
||||||
@@ -227,3 +238,23 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
{/if}
|
{/if}
|
||||||
|
{#if documents}
|
||||||
|
<fieldset>
|
||||||
|
<legend>
|
||||||
|
{t('documents')}
|
||||||
|
</legend>
|
||||||
|
<ul>
|
||||||
|
{#each Object.values(documents) as document}
|
||||||
|
<li>
|
||||||
|
<a href="/document/{document.id}/view" {onclick} >
|
||||||
|
{document.number}
|
||||||
|
({document.date})
|
||||||
|
{document.sender.name.split('\n')[0]}
|
||||||
|
→
|
||||||
|
{document.customer.name.split('\n')[0]}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
</fieldset>
|
||||||
|
{/if}
|
||||||
|
|||||||
Reference in New Issue
Block a user