|
|
|
|
@ -28,6 +28,7 @@ import static de.srsoftware.umbrella.documents.Constants.*;
@@ -28,6 +28,7 @@ import static de.srsoftware.umbrella.documents.Constants.*;
|
|
|
|
|
import static java.lang.System.Logger.Level.DEBUG; |
|
|
|
|
import static java.lang.System.Logger.Level.WARNING; |
|
|
|
|
import static java.net.HttpURLConnection.*; |
|
|
|
|
import static java.util.function.Predicate.not; |
|
|
|
|
import static java.util.stream.Collectors.toMap; |
|
|
|
|
|
|
|
|
|
import com.sun.net.httpserver.HttpExchange; |
|
|
|
|
@ -41,10 +42,7 @@ import de.srsoftware.document.processor.weasyprint.WeasyFactory;
@@ -41,10 +42,7 @@ import de.srsoftware.document.processor.weasyprint.WeasyFactory;
|
|
|
|
|
import de.srsoftware.document.zugferd.ZugferdFactory; |
|
|
|
|
import de.srsoftware.document.zugferd.data.*; |
|
|
|
|
import de.srsoftware.document.zugferd.data.Currency; |
|
|
|
|
import de.srsoftware.tools.Pair; |
|
|
|
|
import de.srsoftware.tools.Path; |
|
|
|
|
import de.srsoftware.tools.SessionToken; |
|
|
|
|
import de.srsoftware.tools.Tuple; |
|
|
|
|
import de.srsoftware.tools.*; |
|
|
|
|
import de.srsoftware.umbrella.core.BaseHandler; |
|
|
|
|
import de.srsoftware.umbrella.core.ModuleRegistry; |
|
|
|
|
import de.srsoftware.umbrella.core.Paths; |
|
|
|
|
@ -85,25 +83,75 @@ public class DocumentApi extends BaseHandler implements DocumentService {
@@ -85,25 +83,75 @@ public class DocumentApi extends BaseHandler implements DocumentService {
|
|
|
|
|
this.registry.documents().forEach(d -> LOG.log(DEBUG,"found template: {0}",d)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public boolean doDelete(Path path, HttpExchange ex) throws IOException { |
|
|
|
|
addCors(ex); |
|
|
|
|
private DocumentData convert(Document document) throws UmbrellaException { |
|
|
|
|
var currency = switch (document.currency()){ |
|
|
|
|
case "€" -> Currency.EUR; |
|
|
|
|
case "$" -> Currency.USD; |
|
|
|
|
default -> throw unprocessable("Unsupported currency: ",document.currency()); |
|
|
|
|
}; |
|
|
|
|
var typeCode = switch (document.type().name()){ |
|
|
|
|
case "invoice" -> TypeCode.HANDELSRECHNUNG; |
|
|
|
|
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 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 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 unprocessable(ERROR_ADDRESS_MISSING,CUSTOMER); |
|
|
|
|
name = escapeHtmlEntities(match.group(1).trim()); |
|
|
|
|
streetAddress = escapeHtmlEntities(match.group(2).trim()); |
|
|
|
|
postCode = match.group(3); |
|
|
|
|
city = match.group(4).trim(); |
|
|
|
|
var customer = new de.srsoftware.document.zugferd.data.Customer(name,countryID,postCode,streetAddress,city); |
|
|
|
|
var footer = document.footer(); |
|
|
|
|
var head = document.head(); |
|
|
|
|
|
|
|
|
|
var notes = new ArrayList<String>(); |
|
|
|
|
if (!head.isBlank()) notes.add(head); |
|
|
|
|
if (!footer.isBlank()) notes.add(footer); |
|
|
|
|
LocalDate deliveryDate; |
|
|
|
|
try { |
|
|
|
|
Optional<Token> token = SessionToken.from(ex).map(Token::of); |
|
|
|
|
var user = userService().loadUser(token); |
|
|
|
|
if (user.isEmpty()) return unauthorized(ex); |
|
|
|
|
var head = path.pop(); |
|
|
|
|
long docId = Long.parseLong(head); |
|
|
|
|
return switch (path.pop()){ |
|
|
|
|
case POSITION -> deletePosition(ex,docId,user.get()); |
|
|
|
|
case null -> deleteDocument(ex,docId,user.get()); |
|
|
|
|
default -> super.doDelete(path,ex); |
|
|
|
|
deliveryDate = LocalDate.parse(document.delivery()); |
|
|
|
|
} catch (RuntimeException ex) { |
|
|
|
|
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()){ |
|
|
|
|
var number = entry.getKey(); |
|
|
|
|
var pos = entry.getValue(); |
|
|
|
|
|
|
|
|
|
UnitCode unit = switch (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()); |
|
|
|
|
}; |
|
|
|
|
} catch (NumberFormatException ignored) { |
|
|
|
|
return super.doDelete(path,ex); |
|
|
|
|
} catch (UmbrellaException e) { |
|
|
|
|
return send(ex,e); |
|
|
|
|
var percent = pos.tax(); |
|
|
|
|
var taxType = percent == 0 ? TaxType.Z : TaxType.S; |
|
|
|
|
LOG.log(WARNING,"tax type is hardcoded to be \"{0}\", should be field of document / document position!",taxType.name()); |
|
|
|
|
|
|
|
|
|
var taxSet = new LineItemTaxSet(percent*100L,taxType); |
|
|
|
|
// bei Gutschriften soll die Menge negativ sein, nicht aber der Einheitspreis. Deshalb wird das ggf. invertiert.
|
|
|
|
|
var neg = pos.unitPrice() < 0; |
|
|
|
|
var unitPrice = (neg ? -1 : 1) * pos.unitPrice(); |
|
|
|
|
var amount = (neg ? -1 : 1) * pos.amount(); |
|
|
|
|
lineItems.add(new LineItem(number,pos.itemCode(),pos.title(),pos.description(),null,unitPrice,unit,amount,taxSet)); |
|
|
|
|
} |
|
|
|
|
String terms = footer; |
|
|
|
|
return new DocumentData(document.number(),currency,typeCode,document.date(),author,customer,notes,deliveryDate,null,terms,lineItems); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private boolean deleteDocument(HttpExchange ex, long docId, UmbrellaUser user) throws IOException, UmbrellaException { |
|
|
|
|
@ -125,6 +173,32 @@ public class DocumentApi extends BaseHandler implements DocumentService {
@@ -125,6 +173,32 @@ public class DocumentApi extends BaseHandler implements DocumentService {
|
|
|
|
|
return send(ex,db.loadDoc(docId).positions()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public Map<Long, Map<Long, String>> docsReferencedByTimes(Set<Long> timeIds) throws UmbrellaException { |
|
|
|
|
return db.docReferencedByTimes(timeIds); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public boolean doDelete(Path path, HttpExchange ex) throws IOException { |
|
|
|
|
addCors(ex); |
|
|
|
|
try { |
|
|
|
|
Optional<Token> token = SessionToken.from(ex).map(Token::of); |
|
|
|
|
var user = userService().loadUser(token); |
|
|
|
|
if (user.isEmpty()) return unauthorized(ex); |
|
|
|
|
var head = path.pop(); |
|
|
|
|
long docId = Long.parseLong(head); |
|
|
|
|
return switch (path.pop()){ |
|
|
|
|
case POSITION -> deletePosition(ex,docId,user.get()); |
|
|
|
|
case null -> deleteDocument(ex,docId,user.get()); |
|
|
|
|
default -> super.doDelete(path,ex); |
|
|
|
|
}; |
|
|
|
|
} catch (NumberFormatException ignored) { |
|
|
|
|
return super.doDelete(path,ex); |
|
|
|
|
} catch (UmbrellaException e) { |
|
|
|
|
return send(ex,e); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public boolean doGet(Path path, HttpExchange ex) throws IOException { |
|
|
|
|
addCors(ex); |
|
|
|
|
@ -204,14 +278,17 @@ public class DocumentApi extends BaseHandler implements DocumentService {
@@ -204,14 +278,17 @@ public class DocumentApi extends BaseHandler implements DocumentService {
|
|
|
|
|
return sendContent(ex,map); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private boolean getDocTypes(HttpExchange ex) throws UmbrellaException, IOException { |
|
|
|
|
var types = db.listTypes(); |
|
|
|
|
var map = types.values().stream().collect(toMap(Type::id, Type::name)); |
|
|
|
|
return sendContent(ex,map); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private boolean getDocument(HttpExchange ex, long docId, UmbrellaUser user) throws IOException, UmbrellaException { |
|
|
|
|
var doc = getDocumentWithCompanyData(docId,user); |
|
|
|
|
return sendContent(ex,doc.renderToMap()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private Tuple<Document,Company> getDocument(long docId, UmbrellaUser user) throws UmbrellaException { |
|
|
|
|
var doc = db.loadDoc(docId); |
|
|
|
|
var companyId = doc.companyId(); |
|
|
|
|
@ -220,6 +297,14 @@ public class DocumentApi extends BaseHandler implements DocumentService {
@@ -220,6 +297,14 @@ public class DocumentApi extends BaseHandler implements DocumentService {
|
|
|
|
|
return Tuple.of(doc,company); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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 Document getDocumentWithCompanyData(long docId, UmbrellaUser user) throws UmbrellaException { |
|
|
|
|
var tuple = getDocument(docId,user); |
|
|
|
|
var company = tuple.b; |
|
|
|
|
@ -232,88 +317,49 @@ public class DocumentApi extends BaseHandler implements DocumentService {
@@ -232,88 +317,49 @@ public class DocumentApi extends BaseHandler implements DocumentService {
|
|
|
|
|
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 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 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); |
|
|
|
|
public Map<Long, Document> list(long companyId) throws UmbrellaException{ |
|
|
|
|
return db.listDocs(companyId); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private DocumentData convert(Document document) throws UmbrellaException { |
|
|
|
|
var currency = switch (document.currency()){ |
|
|
|
|
case "€" -> Currency.EUR; |
|
|
|
|
case "$" -> Currency.USD; |
|
|
|
|
default -> throw unprocessable("Unsupported currency: ",document.currency()); |
|
|
|
|
}; |
|
|
|
|
var typeCode = switch (document.type().name()){ |
|
|
|
|
case "invoice" -> TypeCode.HANDELSRECHNUNG; |
|
|
|
|
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 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 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 unprocessable(ERROR_ADDRESS_MISSING,CUSTOMER); |
|
|
|
|
name = escapeHtmlEntities(match.group(1).trim()); |
|
|
|
|
streetAddress = escapeHtmlEntities(match.group(2).trim()); |
|
|
|
|
postCode = match.group(3); |
|
|
|
|
city = match.group(4).trim(); |
|
|
|
|
var customer = new de.srsoftware.document.zugferd.data.Customer(name,countryID,postCode,streetAddress,city); |
|
|
|
|
var footer = document.footer(); |
|
|
|
|
var head = document.head(); |
|
|
|
|
|
|
|
|
|
var notes = new ArrayList<String>(); |
|
|
|
|
if (!head.isBlank()) notes.add(head); |
|
|
|
|
if (!footer.isBlank()) notes.add(footer); |
|
|
|
|
LocalDate deliveryDate; |
|
|
|
|
private boolean listCompaniesDocuments(HttpExchange ex, UmbrellaUser user) throws UmbrellaException { |
|
|
|
|
try { |
|
|
|
|
deliveryDate = LocalDate.parse(document.delivery()); |
|
|
|
|
} catch (RuntimeException ex) { |
|
|
|
|
throw unprocessable("\"{0}\" is not a valid delivery date (cannot be parsed)!",document.delivery()); |
|
|
|
|
var json = json(ex); |
|
|
|
|
if (!json.has(COMPANY)) throw missingFieldException(COMPANY); |
|
|
|
|
long companyId = json.getLong(COMPANY); |
|
|
|
|
var company = companyService().get(companyId); |
|
|
|
|
if (!companyService().membership(companyId,user.id())) throw forbidden("You are mot a member of company {0}",company); |
|
|
|
|
var docs = list(companyId); |
|
|
|
|
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) { |
|
|
|
|
LOG.log(WARNING,"Failed to parse JSON data from request",e); |
|
|
|
|
throw new UmbrellaException( 500,"Failed to parse JSON data from request").causedBy(e); |
|
|
|
|
} |
|
|
|
|
var lineItems = new ArrayList<LineItem>(); |
|
|
|
|
for (var entry : document.positions().entrySet()){ |
|
|
|
|
var number = entry.getKey(); |
|
|
|
|
var pos = entry.getValue(); |
|
|
|
|
|
|
|
|
|
UnitCode unit = switch (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; |
|
|
|
|
LOG.log(WARNING,"tax type is hardcoded to be \"{0}\", should be field of document / document position!",taxType.name()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var taxSet = new LineItemTaxSet(percent*100L,taxType); |
|
|
|
|
// bei Gutschriften soll die Menge negativ sein, nicht aber der Einheitspreis. Deshalb wird das ggf. invertiert.
|
|
|
|
|
var neg = pos.unitPrice() < 0; |
|
|
|
|
var unitPrice = (neg ? -1 : 1) * pos.unitPrice(); |
|
|
|
|
var amount = (neg ? -1 : 1) * pos.amount(); |
|
|
|
|
lineItems.add(new LineItem(number,pos.itemCode(),pos.title(),pos.description(),null,unitPrice,unit,amount,taxSet)); |
|
|
|
|
private boolean patchDocument(long docId, UmbrellaUser user, HttpExchange ex) throws UmbrellaException, IOException { |
|
|
|
|
var doc = getDocument(docId,user).a; |
|
|
|
|
var data = json(ex); |
|
|
|
|
doc.patch(data); |
|
|
|
|
if (doc.isDirty(FOOTER,HEAD)) { |
|
|
|
|
var settings = db.getCustomerSettings(doc.companyId(),doc.type(),doc.customer().id()); |
|
|
|
|
if (settings == null) settings = CustomerSettings.empty(); |
|
|
|
|
db.save(doc.companyId(),doc.type(),doc.customer().id(), settings.patch(data)); |
|
|
|
|
} |
|
|
|
|
String terms = footer; |
|
|
|
|
return new DocumentData(document.number(),currency,typeCode,document.date(),author,customer,notes,deliveryDate,null,terms,lineItems); |
|
|
|
|
var stateChanged = doc.isDirty(STATE); |
|
|
|
|
db.save(doc); |
|
|
|
|
if (stateChanged) updateTimes(doc); |
|
|
|
|
return ok(ex); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private Content renderDocument(Document document, UmbrellaUser user) throws UmbrellaException { |
|
|
|
|
@ -355,54 +401,6 @@ public class DocumentApi extends BaseHandler implements DocumentService {
@@ -355,54 +401,6 @@ public class DocumentApi extends BaseHandler implements DocumentService {
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public Map<Long, Document> list(long companyId) throws UmbrellaException{ |
|
|
|
|
return db.listDocs(companyId); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private boolean listCompaniesDocuments(HttpExchange ex, UmbrellaUser user) throws UmbrellaException { |
|
|
|
|
try { |
|
|
|
|
var json = json(ex); |
|
|
|
|
if (!json.has(COMPANY)) throw missingFieldException(COMPANY); |
|
|
|
|
long companyId = json.getLong(COMPANY); |
|
|
|
|
var company = companyService().get(companyId); |
|
|
|
|
if (!companyService().membership(companyId,user.id())) throw forbidden("You are mot a member of company {0}",company); |
|
|
|
|
var docs = list(companyId); |
|
|
|
|
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) { |
|
|
|
|
LOG.log(WARNING,"Failed to parse JSON data from request",e); |
|
|
|
|
throw new UmbrellaException( 500,"Failed to parse JSON data from request").causedBy(e); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public Map<Long, Map<Long, String>> docsReferencedByTimes(Set<Long> timeIds) throws UmbrellaException { |
|
|
|
|
return db.docReferencedByTimes(timeIds); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private boolean patchDocument(long docId, UmbrellaUser user, HttpExchange ex) throws UmbrellaException, IOException { |
|
|
|
|
var doc = getDocument(docId,user).a; |
|
|
|
|
var data = json(ex); |
|
|
|
|
doc.patch(data); |
|
|
|
|
if (doc.isDirty(FOOTER,HEAD)) { |
|
|
|
|
var settings = db.getCustomerSettings(doc.companyId(),doc.type(),doc.customer().id()); |
|
|
|
|
if (settings == null) settings = CustomerSettings.empty(); |
|
|
|
|
db.save(doc.companyId(),doc.type(),doc.customer().id(), settings.patch(data)); |
|
|
|
|
} |
|
|
|
|
db.save(doc); |
|
|
|
|
return ok(ex); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private boolean send(HttpExchange ex,PositionList positions) throws IOException { |
|
|
|
|
return sendContent(ex,positions.entrySet().stream().collect(toMap(Map.Entry::getKey,entry -> entry.getValue().renderToMap()))); |
|
|
|
|
} |
|
|
|
|
@ -567,6 +565,18 @@ public class DocumentApi extends BaseHandler implements DocumentService {
@@ -567,6 +565,18 @@ public class DocumentApi extends BaseHandler implements DocumentService {
|
|
|
|
|
var envelope = new Envelope(message,new User(doc.customer().shortName(),new EmailAddress(email),doc.customer().language())); |
|
|
|
|
postBox().send(envelope); |
|
|
|
|
db.save(doc.set(SENT)); |
|
|
|
|
updateTimes(doc); |
|
|
|
|
return ok(ex); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void updateTimes(Document doc) { |
|
|
|
|
var timeState = switch (doc.state()){ |
|
|
|
|
case DELAYED, SENT -> Time.State.Pending; |
|
|
|
|
case ERROR -> Time.State.Open; |
|
|
|
|
case DECLINED, NEW -> Time.State.Open; |
|
|
|
|
case PAYED -> Time.State.Complete; |
|
|
|
|
}; |
|
|
|
|
var timeIds = doc.positions().values().stream().map(Position::timeId).filter(not(Optionals::is0)).toList(); |
|
|
|
|
timeService().updateStates(timeIds,timeState); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|