diff --git a/.gitignore b/.gitignore
index c2a137f..d543419 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,5 @@ buildNumber.properties
frontend/
node_modules/
+
+data/
diff --git a/.project b/.project
index 1330282..bd288b1 100644
--- a/.project
+++ b/.project
@@ -11,12 +11,12 @@
- org.eclipse.m2e.core.maven2Builder
+ org.springframework.ide.eclipse.boot.validation.springbootbuilder
- org.springframework.ide.eclipse.boot.validation.springbootbuilder
+ org.eclipse.m2e.core.maven2Builder
diff --git a/pom.xml b/pom.xml
index c9f439b..bc6a829 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,6 +1,4 @@
-
-
+
4.0.0
de.kreth.invoice
@@ -163,6 +161,23 @@
5.0.3
test
+
+ net.sf.jasperreports
+ jasperreports
+ 6.19.1
+
+
+
+ com.lowagie
+ itext
+ 2.1.7
+
+
diff --git a/src/main/java/de/kreth/invoice/data/BaseEntity.java b/src/main/java/de/kreth/invoice/data/BaseEntity.java
index 78d8a6e..15178e2 100644
--- a/src/main/java/de/kreth/invoice/data/BaseEntity.java
+++ b/src/main/java/de/kreth/invoice/data/BaseEntity.java
@@ -5,22 +5,26 @@ import java.time.LocalDateTime;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
-import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.PrePersist;
+import org.hibernate.annotations.CreationTimestamp;
+import org.hibernate.annotations.UpdateTimestamp;
+
@MappedSuperclass
public class BaseEntity implements Serializable {
private static final long serialVersionUID = 6953593432069408729L;
@Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @GeneratedValue
private int id;
@Column(name = "created")
+ @CreationTimestamp
private LocalDateTime createdDate;
@Column(name = "updated")
+ @UpdateTimestamp
private LocalDateTime changeDate;
public int getId() {
@@ -70,20 +74,26 @@ public class BaseEntity implements Serializable {
@Override
public boolean equals(Object obj) {
- if (this == obj)
+ if (this == obj) {
return true;
- if (obj == null)
+ }
+ if (obj == null) {
return false;
- if (getClass() != obj.getClass())
+ }
+ if (getClass() != obj.getClass()) {
return false;
+ }
BaseEntity other = (BaseEntity) obj;
if (createdDate == null) {
- if (other.createdDate != null)
+ if (other.createdDate != null) {
return false;
- } else if (!createdDate.equals(other.createdDate))
+ }
+ } else if (!createdDate.equals(other.createdDate)) {
return false;
- if (id != other.id)
+ }
+ if (id != other.id) {
return false;
+ }
return true;
}
diff --git a/src/main/java/de/kreth/invoice/data/Invoice.java b/src/main/java/de/kreth/invoice/data/Invoice.java
index b7d21d7..275fd79 100644
--- a/src/main/java/de/kreth/invoice/data/Invoice.java
+++ b/src/main/java/de/kreth/invoice/data/Invoice.java
@@ -6,8 +6,11 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
@@ -21,11 +24,13 @@ public class Invoice extends BaseEntity {
private String invoiceId;
private LocalDateTime invoiceDate;
+ private String signImagePath;
+
@OneToMany(mappedBy = "invoice")
private List items;
-// @ManyToOne(cascade = CascadeType.REFRESH)
-// @JoinColumn(name = "user_id", nullable = false, updatable = false)
+ @ManyToOne(cascade = CascadeType.REFRESH)
+ @JoinColumn(name = "user_id", nullable = false, updatable = false)
private User user;
public String getInvoiceId() {
@@ -68,6 +73,10 @@ public class Invoice extends BaseEntity {
BigDecimal::add);
}
+ public String getSignImagePath() {
+ return signImagePath;
+ }
+
@Override
public String toString() {
return "Invoice [invoiceId=" + invoiceId + ", itemscount="
diff --git a/src/main/java/de/kreth/invoice/data/InvoiceItem.java b/src/main/java/de/kreth/invoice/data/InvoiceItem.java
index 1189b81..19117f9 100644
--- a/src/main/java/de/kreth/invoice/data/InvoiceItem.java
+++ b/src/main/java/de/kreth/invoice/data/InvoiceItem.java
@@ -21,6 +21,8 @@ public class InvoiceItem extends BaseEntity {
private LocalDateTime end;
@Column(nullable = true, length = 15)
private String participants;
+ @Column(nullable = true, length = 255)
+ private String description;
@ManyToOne(optional = false)
@JoinColumn(name = "article_id", nullable = false, updatable = false)
@@ -33,6 +35,8 @@ public class InvoiceItem extends BaseEntity {
@Column(name = "sum_price")
private BigDecimal sumPrice;
+ private BigDecimal pricePerHour;
+
public String getTitle() {
return getArticle().getTitle();
}
@@ -101,6 +105,14 @@ public class InvoiceItem extends BaseEntity {
return start.until(end, ChronoUnit.MINUTES);
}
+ public String getDescription() {
+ return description;
+ }
+
+ public BigDecimal getPricePerHour() {
+ return pricePerHour;
+ }
+
@Override
public String toString() {
return "InvoiceItem [id=" + getId() + ", start=" + start + ", end="
@@ -119,28 +131,37 @@ public class InvoiceItem extends BaseEntity {
@Override
public boolean equals(Object obj) {
- if (this == obj)
+ if (this == obj) {
return true;
- if (obj == null)
+ }
+ if (obj == null) {
return false;
- if (getClass() != obj.getClass())
+ }
+ if (getClass() != obj.getClass()) {
return false;
+ }
InvoiceItem other = (InvoiceItem) obj;
if (article == null) {
- if (other.article != null)
+ if (other.article != null) {
return false;
- } else if (!article.equals(other.article))
+ }
+ } else if (!article.equals(other.article)) {
return false;
+ }
if (end == null) {
- if (other.end != null)
+ if (other.end != null) {
return false;
- } else if (!end.equals(other.end))
+ }
+ } else if (!end.equals(other.end)) {
return false;
+ }
if (start == null) {
- if (other.start != null)
+ if (other.start != null) {
return false;
- } else if (!start.equals(other.start))
+ }
+ } else if (!start.equals(other.start)) {
return false;
+ }
return true;
}
diff --git a/src/main/java/de/kreth/invoice/data/User.java b/src/main/java/de/kreth/invoice/data/User.java
index 2b5ad4d..478fca5 100644
--- a/src/main/java/de/kreth/invoice/data/User.java
+++ b/src/main/java/de/kreth/invoice/data/User.java
@@ -2,10 +2,13 @@ package de.kreth.invoice.data;
import java.util.Objects;
+import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
+import javax.persistence.FetchType;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
+import javax.persistence.OneToOne;
import javax.persistence.Table;
import org.keycloak.representations.AccessToken;
@@ -21,6 +24,12 @@ public class User extends BaseEntity {
private String familyName;
private String email;
+ @OneToOne(fetch = FetchType.LAZY, mappedBy = "user", cascade = CascadeType.ALL)
+ private UserBank bank;
+
+ @OneToOne(fetch = FetchType.LAZY, mappedBy = "user", cascade = CascadeType.ALL)
+ private UserAdress adress;
+
@Column(name = "PRINCIPAL_ID", nullable = false, length = 40, updatable = false, insertable = true, unique = true)
public String getPrincipalId() {
return principalId;
@@ -55,4 +64,11 @@ public class User extends BaseEntity {
return email;
}
+ public UserBank getBank() {
+ return bank;
+ }
+
+ public UserAdress getAdress() {
+ return adress;
+ }
}
diff --git a/src/main/java/de/kreth/invoice/report/InvoiceReportSource.java b/src/main/java/de/kreth/invoice/report/InvoiceReportSource.java
new file mode 100644
index 0000000..5ae8209
--- /dev/null
+++ b/src/main/java/de/kreth/invoice/report/InvoiceReportSource.java
@@ -0,0 +1,229 @@
+package de.kreth.invoice.report;
+
+import java.math.BigDecimal;
+import java.nio.file.Path;
+import java.time.LocalDateTime;
+import java.util.Iterator;
+import java.util.List;
+
+import de.kreth.invoice.data.Invoice;
+import de.kreth.invoice.data.InvoiceItem;
+import net.sf.jasperreports.engine.JRDataSource;
+import net.sf.jasperreports.engine.JRDataSourceProvider;
+import net.sf.jasperreports.engine.JRException;
+import net.sf.jasperreports.engine.JRField;
+import net.sf.jasperreports.engine.JasperReport;
+import net.sf.jasperreports.engine.base.JRBaseField;
+
+public class InvoiceReportSource implements JRDataSource, JRDataSourceProvider {
+
+ public static final String FIELD_INVOICE_NO = "INVOICE_NO";
+
+ public static final String FIELD_INVOICE_DATE = "INVOICE_DATE";
+
+ public static final String FIELD_INVOICE_SUM = "INVOICE_SUM";
+
+ public static final String FIELD_USER_PRENAME = "USER_PRENAME";
+
+ public static final String FIELD_USER_SURNAME = "USER_SURNAME";
+
+ public static final String FIELD_USER_ADRESS1 = "USER_ADRESS1";
+
+ public static final String FIELD_USER_ADRESS2 = "USER_ADRESS2";
+
+ public static final String FIELD_USER_ZIP = "USER_ZIPCODE";
+
+ public static final String FIELD_USER_CITY = "USER_CITY";
+
+ public static final String FIELD_BANK_NAME = "BANK_NAME";
+
+ public static final String FIELD_BANK_IBAN = "BANK_IBAN";
+
+ public static final String FIELD_BANK_BIC = "BANK_BIC";
+
+ public static final String FIELD_SIGNATURE_PATH = "FIELD_SIGNATURE_PATH";
+
+ public static final String FIELD_ARTICLE_TITLE = "ARTICLE_TITLE";
+
+ public static final String FIELD_ARTICLE_DESCRIPTION = "ARTICLE_DESCRIPTION";
+
+ public static final String FIELD_ARTICLE_PRICE_PER_HOUR = "ARTICLE_PRICE_PER_HOUR";
+
+ public static final String FIELD_ITEM_START = "ITEM_START";
+
+ public static final String FIELD_ITEM_END = "ITEM_END";
+
+ public static final String FIELD_ITEM_PARTICIPANTS = "FIELD_ITEM_PARTICIPANTS";
+
+ public static final String FIELD_ITEM_DURATION_MINUTES = "ITEM_DURATION_MINUTES";
+
+ public static final String FIELD_ITEM_SUM = "ITEM_SUM";
+
+ private Invoice invoice;
+
+ private Iterator itemIterator;
+
+ private InvoiceItem currentItem;
+
+ public InvoiceReportSource() {
+ }
+
+ public void setInvoice(Invoice invoice) {
+ this.invoice = invoice;
+ List items = invoice.getItems();
+ items.sort(this::compare);
+ itemIterator = items.iterator();
+ }
+
+ private int compare(InvoiceItem i1, InvoiceItem i2) {
+ return i1.getStart().compareTo(i2.getStart());
+ }
+
+ @Override
+ public boolean next() throws JRException {
+ if (itemIterator.hasNext() == false) {
+ currentItem = null;
+ return false;
+ }
+ currentItem = itemIterator.next();
+ return true;
+ }
+
+ @Override
+ public Object getFieldValue(JRField jrField) throws JRException {
+ switch (jrField.getName()) {
+ case FIELD_INVOICE_NO:
+ return invoice.getInvoiceId();
+ case FIELD_INVOICE_DATE:
+ return invoice.getInvoiceDate();
+ case FIELD_INVOICE_SUM:
+ return invoice.getSum();
+ case FIELD_USER_PRENAME:
+ return invoice.getUser().getGivenName();
+ case FIELD_USER_SURNAME:
+ return invoice.getUser().getFamilyName();
+
+ case FIELD_BANK_NAME:
+ return invoice.getUser().getBank().getBankName();
+ case FIELD_BANK_IBAN:
+ return invoice.getUser().getBank().getIban();
+ case FIELD_BANK_BIC:
+ return invoice.getUser().getBank().getBic();
+ case FIELD_USER_ADRESS1:
+ return invoice.getUser().getAdress().getAdress1();
+ case FIELD_USER_ADRESS2:
+ return invoice.getUser().getAdress().getAdress2();
+ case FIELD_USER_ZIP:
+ return invoice.getUser().getAdress().getZip();
+ case FIELD_USER_CITY:
+ return invoice.getUser().getAdress().getCity();
+
+ case FIELD_SIGNATURE_PATH:
+ return invoice.getSignImagePath();
+ default:
+ break;
+ }
+
+ if (currentItem != null) {
+
+ switch (jrField.getName()) {
+ case FIELD_ARTICLE_TITLE:
+ return currentItem.getTitle();
+ case FIELD_ARTICLE_DESCRIPTION:
+ return currentItem.getDescription();
+ case FIELD_ARTICLE_PRICE_PER_HOUR:
+ return currentItem.getPricePerHour();
+ case FIELD_ITEM_START:
+ return currentItem.getStart();
+ case FIELD_ITEM_END:
+ return currentItem.getEnd();
+ case FIELD_ITEM_DURATION_MINUTES:
+ return currentItem.getDurationInMinutes();
+ case FIELD_ITEM_SUM:
+ return currentItem.getSumPrice();
+ case FIELD_ITEM_PARTICIPANTS:
+ return currentItem.getParticipants();
+
+ default:
+ break;
+ }
+ }
+
+ return null;
+ }
+
+ public static JRDataSource getDataSource() {
+ return new InvoiceReportSource();
+ }
+
+ @Override
+ public boolean supportsGetFieldsOperation() {
+ return true;
+ }
+
+ @Override
+ public JRField[] getFields(JasperReport report)
+ throws JRException, UnsupportedOperationException {
+ JRField[] fields = {
+ new InternalField(FIELD_INVOICE_NO, "Invoice No", String.class),
+ new InternalField(FIELD_INVOICE_DATE, "Invoice date",
+ LocalDateTime.class),
+ new InternalField(FIELD_INVOICE_SUM, "Invoice sum",
+ BigDecimal.class),
+ new InternalField(FIELD_USER_PRENAME, "User Prename",
+ String.class),
+ new InternalField(FIELD_USER_SURNAME, "User Surname",
+ String.class),
+
+ new InternalField(FIELD_USER_ADRESS1, "User Adress1",
+ String.class),
+ new InternalField(FIELD_USER_ADRESS2, "User Âdress2",
+ String.class),
+ new InternalField(FIELD_USER_ZIP, "User Zipcode", String.class),
+ new InternalField(FIELD_USER_CITY, "User City", String.class),
+
+ new InternalField(FIELD_BANK_NAME, "Bank Name", String.class),
+ new InternalField(FIELD_BANK_IBAN, "Bank IBAN", String.class),
+ new InternalField(FIELD_BANK_BIC, "Bank Bic", String.class),
+
+ new InternalField(FIELD_ARTICLE_TITLE, "Article Title",
+ String.class),
+ new InternalField(FIELD_ARTICLE_DESCRIPTION,
+ "Article Description", String.class),
+ new InternalField(FIELD_ARTICLE_PRICE_PER_HOUR,
+ "Article Price per Hour", BigDecimal.class),
+ new InternalField(FIELD_ITEM_START, "Item Start",
+ LocalDateTime.class),
+ new InternalField(FIELD_ITEM_END, "Item End",
+ LocalDateTime.class),
+ new InternalField(FIELD_ITEM_DURATION_MINUTES,
+ "Item Duration in Minutes", Long.class),
+ new InternalField(FIELD_ITEM_PARTICIPANTS, "Item Participants",
+ String.class),
+ new InternalField(FIELD_ITEM_SUM, "Item Sum",
+ BigDecimal.class),
+ new InternalField(FIELD_SIGNATURE_PATH, "Signature Image Path",
+ Path.class) };
+ return fields;
+ }
+
+ @Override
+ public JRDataSource create(JasperReport report) throws JRException {
+ return this;
+ }
+
+ @Override
+ public void dispose(JRDataSource dataSource) throws JRException {
+ }
+
+ static class InternalField extends JRBaseField {
+ private static final long serialVersionUID = -495777796541981790L;
+
+ InternalField(String name, String desc, Class> clazz) {
+ this.name = name;
+ this.description = desc;
+ this.valueClass = clazz;
+ this.valueClassName = clazz.getName();
+ }
+ }
+}
diff --git a/src/main/java/de/kreth/invoice/report/Signature.java b/src/main/java/de/kreth/invoice/report/Signature.java
new file mode 100644
index 0000000..9acca86
--- /dev/null
+++ b/src/main/java/de/kreth/invoice/report/Signature.java
@@ -0,0 +1,59 @@
+package de.kreth.invoice.report;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Objects;
+
+import org.apache.commons.io.FilenameUtils;
+
+import de.kreth.invoice.data.User;
+
+public class Signature {
+
+ private final User user;
+
+ public Signature(User user) {
+ super();
+ this.user = Objects.requireNonNull(user);
+ }
+
+ public OutputStream createOutputStream(String fileName) throws IOException {
+ File dir = new File("images");
+ dir.mkdirs();
+ return new FileOutputStream(new File(dir, user.getId() + "." + FilenameUtils.getExtension(fileName)));
+ }
+
+ public boolean isSignatureImageExists() {
+ File[] listFiles = new File("images").listFiles(new FileFilter() {
+
+ @Override
+ public boolean accept(File pathname) {
+ return pathname.getName().startsWith(user.getId() + ".");
+ }
+ });
+ return listFiles != null && listFiles.length > 0;
+ }
+
+ /**
+ * Check with {@link #isSignatureImageExists()} existence first.
+ *
+ * @return
+ * @throws NullPointerException if image does not exist.
+ */
+ public File getSignatureUrl() {
+ File[] listFiles = new File("images").listFiles(new FileFilter() {
+
+ @Override
+ public boolean accept(File pathname) {
+ return pathname.getName().startsWith(user.getId() + ".");
+ }
+ });
+ if (listFiles == null || listFiles.length == 0) {
+ throw new NullPointerException("Image file does not exist");
+ }
+ return listFiles[0];
+ }
+}
diff --git a/src/main/java/de/kreth/invoice/security/UserManager.java b/src/main/java/de/kreth/invoice/security/UserManager.java
index 2089a64..d16ac43 100644
--- a/src/main/java/de/kreth/invoice/security/UserManager.java
+++ b/src/main/java/de/kreth/invoice/security/UserManager.java
@@ -49,7 +49,7 @@ public class UserManager {
User user = new User();
AccessToken accessToken = getAccessToken();
user.setPrincipal(accessToken);
- return user;
+ return save(user);
}
}
diff --git a/src/main/java/de/kreth/invoice/views/UserDetailsDialog.java b/src/main/java/de/kreth/invoice/views/UserDetailsDialog.java
new file mode 100644
index 0000000..7641b82
--- /dev/null
+++ b/src/main/java/de/kreth/invoice/views/UserDetailsDialog.java
@@ -0,0 +1,227 @@
+package de.kreth.invoice.views;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UncheckedIOException;
+import java.util.Objects;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.vaadin.flow.component.button.Button;
+import com.vaadin.flow.component.dialog.Dialog;
+import com.vaadin.flow.component.html.Hr;
+import com.vaadin.flow.component.html.Image;
+import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
+import com.vaadin.flow.component.orderedlayout.VerticalLayout;
+import com.vaadin.flow.component.textfield.TextField;
+import com.vaadin.flow.component.upload.Upload;
+import com.vaadin.flow.data.binder.Binder;
+import com.vaadin.flow.data.binder.BinderValidationStatus;
+import com.vaadin.flow.server.AbstractStreamResource;
+import com.vaadin.flow.server.InputStreamFactory;
+import com.vaadin.flow.server.StreamResource;
+
+import de.kreth.invoice.data.Adress;
+import de.kreth.invoice.data.User;
+import de.kreth.invoice.data.UserAdress;
+import de.kreth.invoice.data.UserBank;
+import de.kreth.invoice.persistence.UserAdressRepository;
+import de.kreth.invoice.persistence.UserBankRepository;
+import de.kreth.invoice.report.Signature;
+
+public class UserDetailsDialog extends Dialog {
+
+ private static final long serialVersionUID = -6255487997073609068L;
+
+ private Logger logger = LoggerFactory.getLogger(getClass());
+
+ private final Binder bankBinder = new Binder<>();
+ private final Binder adressBinder = new Binder<>();
+
+ private final TextField loginName;
+
+ private final TextField prename;
+
+ private final TextField surname;
+
+ private final TextField bankName;
+
+ private final TextField iban;
+
+ private final TextField bic;
+
+ private final TextField adress1;
+
+ private final TextField adress2;
+
+ private final TextField zipCode;
+
+ private final TextField city;
+
+ private final Button okButton;
+
+ private final Image signatureImage;
+
+ private final UserBankRepository bankRepository;
+ private final UserAdressRepository adressRepository;
+
+ private User user;
+
+ private UserBank bank;
+
+ private UserAdress adress;
+
+ public UserDetailsDialog(UserBankRepository bankRepository, UserAdressRepository adressRepository) {
+ this.bankRepository = bankRepository;
+ this.adressRepository = adressRepository;
+
+ loginName = new TextField();
+ loginName.setLabel("Login Name");
+ loginName.setEnabled(false);
+
+ prename = new TextField();
+ prename.setLabel("Vorname");
+ prename.setEnabled(false);
+
+ surname = new TextField();
+ surname.setLabel("Nachname");
+
+ bankName = new TextField();
+ bankName.setLabel("Name der Bank");
+ bankBinder.forField(bankName)
+ .asRequired("Der BankName darf nicht leer sein.")
+ .bind(UserBank::getBankName, UserBank::setBankName);
+
+ iban = new TextField();
+ iban.setLabel("IBAN");
+ bankBinder.forField(iban)
+ .asRequired("Die IBAN darf nicht leer sein.")
+ .bind(UserBank::getIban, UserBank::setIban);
+
+ bic = new TextField();
+ bic.setLabel("BIC");
+ bankBinder.forField(bic).bind(UserBank::getBic, UserBank::setBic);
+
+ adress1 = new TextField();
+ adress1.setLabel("Straße");
+ adressBinder.forField(adress1)
+ .asRequired("Die Straße muss angegeben sein.")
+ .bind(Adress::getAdress1, Adress::setAdress1);
+
+ adress2 = new TextField();
+ adress2.setLabel("Adresszusatz");
+ adressBinder.forField(adress2)
+ .bind(Adress::getAdress2, Adress::setAdress2);
+
+ zipCode = new TextField();
+ zipCode.setLabel("Postleitzahl");
+ adressBinder.forField(zipCode)
+ .asRequired("Die Postleitzahl muss angegeben sein.")
+ .bind(Adress::getZip, Adress::setZip);
+
+ city = new TextField();
+ city.setLabel("Ort");
+ adressBinder.forField(city)
+ .asRequired("Der Ort muss angegeben sein.")
+ .bind(Adress::getCity, Adress::setCity);
+
+ signatureImage = new Image();
+ signatureImage.setWidth("192px");
+ signatureImage.setHeight("62px");
+ signatureImage.setAlt("Keine Unterschrift konfiguriert");
+
+ Upload upload = new Upload(this::receiveUpload);
+ upload.addFinishedListener(ev -> updateSignatureImage());
+
+ VerticalLayout layout = new VerticalLayout();
+ layout.add(loginName, prename, surname);
+ layout.add(new Hr());
+ layout.add(bankName, iban, bic);
+ layout.add(new Hr());
+ HorizontalLayout cityLayout = new HorizontalLayout();
+ cityLayout.add(zipCode, city);
+
+ layout.add(adress1, adress2, cityLayout, new HorizontalLayout(signatureImage, upload));
+
+ okButton = new Button("OK", ev -> {
+ BinderValidationStatus bankValidation = bankBinder.validate();
+
+ BinderValidationStatus adressValidation = adressBinder.validate();
+ if (bankValidation.isOk() && adressValidation.isOk()) {
+ close();
+ }
+ });
+
+ Button cancel = new Button("Abbrechen", ev -> close());
+
+ HorizontalLayout buttons = new HorizontalLayout();
+ buttons.add(okButton, cancel);
+ layout.add(buttons);
+
+ add(layout);
+ }
+
+ private OutputStream receiveUpload(String filename, String mimeType) {
+
+ Signature signature = new Signature(user);
+ try {
+ return signature.createOutputStream(filename);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+//
+// public Registration addOkClickListener(ClickListener listener) {
+// return okButton.addClickListener(listener);
+// }
+
+ public void setUser(User user) {
+ this.user = Objects.requireNonNull(user);
+ this.bank = bankRepository.findByUser(user);
+ this.adress = adressRepository.findByUser(user);
+ if (this.bank == null) {
+ this.bank = new UserBank();
+ }
+ if (this.adress == null) {
+ this.adress = new UserAdress();
+ }
+ updateSignatureImage();
+ }
+
+ private void updateSignatureImage() {
+ if (user != null) {
+ Signature signature = new Signature(user);
+ if (signature.isSignatureImageExists()) {
+ File signatureUrl = signature.getSignatureUrl();
+ logger.info("Showing signature: {}", signatureUrl);
+
+ StreamResource resource = new StreamResource(getAriaLabelString(), new InputStreamFactory() {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public InputStream createInputStream() {
+ try {
+ return new FileInputStream(signatureUrl);
+ } catch (FileNotFoundException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+ });
+ signatureImage.setSrc(resource);
+ } else {
+ signatureImage.setSrc((AbstractStreamResource) null);
+ }
+ }
+ }
+
+ public User getUser() {
+ return user;
+ }
+
+}
diff --git a/src/main/resources/reports/mtv_gross_buchholz.jrxml b/src/main/resources/reports/mtv_gross_buchholz.jrxml
new file mode 100644
index 0000000..4fedf13
--- /dev/null
+++ b/src/main/resources/reports/mtv_gross_buchholz.jrxml
@@ -0,0 +1,586 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/reports/mtv_gross_buchholz_trainer.jrxml b/src/main/resources/reports/mtv_gross_buchholz_trainer.jrxml
new file mode 100644
index 0000000..dfeea7e
--- /dev/null
+++ b/src/main/resources/reports/mtv_gross_buchholz_trainer.jrxml
@@ -0,0 +1,782 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql
new file mode 100644
index 0000000..c5c91aa
--- /dev/null
+++ b/src/main/resources/schema.sql
@@ -0,0 +1,71 @@
+
+CREATE TABLE USER (
+ ID INT(11) NOT NULL,
+ EMAIL VARCHAR(45) NOT NULL,
+ GIVEN_NAME VARCHAR(45) NOT NULL,
+ FAMILY_NAME VARCHAR(45) NOT NULL,
+ PRINCIPAL_ID VARCHAR(45) NOT NULL,
+ UPDATED TIMESTAMP NOT NULL,
+ CREATED TIMESTAMP NOT NULL
+);
+
+CREATE TABLE ADRESS (
+ ID INT(11) NOT NULL,
+ ADRESS_TYPE VARCHAR(31) NOT NULL,
+ ADRESS1 VARCHAR(255) DEFAULT NULL,
+ ADRESS2 VARCHAR(255) DEFAULT NULL,
+ ZIP VARCHAR(45) DEFAULT NULL,
+ CITY VARCHAR(155) DEFAULT NULL,
+ UPDATED TIMESTAMP NOT NULL,
+ CREATED TIMESTAMP NOT NULL
+);
+
+CREATE TABLE ARTICLE (
+ ID INT(11) NOT NULL,
+ PRICE DOUBLE NOT NULL,
+ TITLE VARCHAR(50) NOT NULL,
+ DESCRIPTION VARCHAR(255) DEFAULT NULL,
+ USER_ID INT(11) NOT NULL,
+ REPORT VARCHAR(45) NOT NULL DEFAULT '/REPORTS/MTV_GROSS_BUCHHOLZ.JRXML',
+ UPDATED TIMESTAMP NOT NULL,
+ CREATED TIMESTAMP NOT NULL
+);
+
+CREATE TABLE BANKING_CONNECTION (
+ ID INT(11) NOT NULL,
+ OWNER_TYPE VARCHAR(31) NOT NULL,
+ BANKNAME VARCHAR(255) DEFAULT NULL,
+ BIC VARCHAR(255) DEFAULT NULL,
+ IBAN VARCHAR(255) DEFAULT NULL,
+ UPDATED TIMESTAMP NOT NULL,
+ CREATED TIMESTAMP NOT NULL
+);
+
+CREATE TABLE INVOICE (
+ ID INT(11) NOT NULL,
+ INVOICE_DATE DATETIME NOT NULL,
+ INVOICE_ID VARCHAR(150) NOT NULL,
+ USER_ID INT(11) NOT NULL,
+ SIGN_IMAGE_PATH VARCHAR(255) DEFAULT NULL,
+ UPDATED TIMESTAMP NOT NULL,
+ CREATED TIMESTAMP NOT NULL
+);
+
+CREATE TABLE INVOICE_ITEM (
+ ID INT(11) NOT NULL,
+ START DATETIME NOT NULL,
+ END VARCHAR(45) NOT NULL,
+ ARTICLE_ID INT(11) NOT NULL,
+ PARTICIPANTS VARCHAR(15) DEFAULT NULL,
+ SUM_PRICE DECIMAL(7,2) NOT NULL,
+ RECHNUNG_ID INT(11) DEFAULT NULL,
+ INVOICE_ID INT(11) DEFAULT NULL,
+ UPDATED TIMESTAMP NOT NULL,
+ CREATED TIMESTAMP NOT NULL,
+ TITLE VARCHAR(100) NOT NULL,
+ DESCRIPTION VARCHAR(255) DEFAULT NULL,
+ USER_ID INT(11) NOT NULL,
+ PRICEPERHOUR DECIMAL(7,2) NOT NULL,
+ REPORT VARCHAR(255) DEFAULT NULL
+);
+