preparation for postgresql database storage, some workflow corrections

master
Markus Kreth 4 years ago
parent d595ab7534
commit 8ffbf7a635
  1. 2
      .gitignore
  2. 4
      pom.xml
  3. 104
      sql/create_tables_postgresql.sql
  4. 6
      sql/drop_tables_postgresql.sql
  5. 10
      src/main/java/de/kreth/invoice/business/InvoiceBusiness.java
  6. 46
      src/main/java/de/kreth/invoice/data/InvoiceItem.java
  7. 2
      src/main/java/de/kreth/invoice/data/User.java
  8. 2
      src/main/java/de/kreth/invoice/views/View.java
  9. 4
      src/main/java/de/kreth/invoice/views/article/ArticleDialog.java
  10. 46
      src/main/java/de/kreth/invoice/views/invoice/InvoiceDialog.java
  11. 7
      src/main/java/de/kreth/invoice/views/invoice/InvoiceGrid.java
  12. 2
      src/main/java/de/kreth/invoice/views/invoiceitem/InvoiceItemDialog.java
  13. 4
      src/main/java/de/kreth/invoice/views/invoiceitem/InvoiceItemOverviewComponent.java
  14. 49
      src/main/java/de/kreth/invoice/views/invoiceitem/InvoiceOverviewComponent.java
  15. 22
      src/main/java/de/kreth/invoice/views/user/UserDetailsDialog.java
  16. 2
      src/main/resources/localization.properties

2
.gitignore vendored

@ -14,3 +14,5 @@ frontend/
node_modules/
data/
images/

@ -99,6 +99,10 @@
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>

@ -0,0 +1,104 @@
CREATE DATABASE trainer_abrechnungen
WITH
ENCODING = 'UTF8'
LC_COLLATE = 'German_Germany.1252'
LC_CTYPE = 'German_Germany.1252'
CONNECTION LIMIT = -1;
CREATE TABLE public.USERDATA
(
id bigint NOT NULL,
principal_id character varying(255) NOT NULL,
email character varying(255) NOT NULL,
family_name character varying(255) NOT NULL,
given_name character varying(255) NOT NULL,
updated timestamp without time zone,
created timestamp without time zone,
CONSTRAINT user_pkey PRIMARY KEY (id)
);
CREATE TABLE public.adress
(
user_id bigint NOT NULL,
adress1 character varying(255) NOT NULL,
adress2 character varying(255),
updated timestamp without time zone,
city character varying(155),
created timestamp without time zone,
zip character varying(45),
CONSTRAINT adress_pkey PRIMARY KEY (user_id),
CONSTRAINT adress_user_fk FOREIGN KEY (user_id) REFERENCES public.USERDATA(id)
);
CREATE TABLE public.article
(
id bigint NOT NULL,
updated timestamp without time zone,
created timestamp without time zone,
description character varying(255),
price numeric(19,2),
report_ressource character varying(255),
title character varying(50) NOT NULL,
user_id bigint NOT NULL,
CONSTRAINT article_pkey PRIMARY KEY (id),
CONSTRAINT article_user_fk FOREIGN KEY (user_id) REFERENCES public.USERDATA(id)
);
CREATE TABLE public.banking_connection
(
user_id bigint NOT NULL,
bank_name character varying(150) NOT NULL,
bic character varying(150),
updated timestamp without time zone,
created timestamp without time zone,
iban character varying(150) NOT NULL,
CONSTRAINT banking_connection_pkey PRIMARY KEY (user_id),
CONSTRAINT banking_connection_user_fk FOREIGN KEY (user_id) REFERENCES public.USERDATA(id)
);
CREATE TABLE public.invoice
(
id bigint NOT NULL,
updated timestamp without time zone,
created timestamp without time zone,
invoice_date timestamp without time zone,
invoiceid character varying(150) NOT NULL,
report_ressource character varying(255),
sign_image_path character varying(255),
user_id bigint NOT NULL,
CONSTRAINT invoice_pkey PRIMARY KEY (id),
CONSTRAINT invoice_user_fk FOREIGN KEY (user_id) REFERENCES public.USERDATA(id),
CONSTRAINT invoiceid_unique UNIQUE (user_id, invoiceid)
);
CREATE TABLE public.invoice_item
(
id bigint NOT NULL,
description character varying(255) NULL,
start_time timestamp without time zone,
end_time timestamp without time zone,
participants character varying(15),
price_per_hour numeric(19,2),
sum_price numeric(19,2),
article_id bigint NOT NULL,
invoice_id bigint NULL,
created timestamp without time zone,
updated timestamp without time zone,
CONSTRAINT invoice_item_pkey PRIMARY KEY (id),
CONSTRAINT invoice_item_article_fk FOREIGN KEY (article_id) REFERENCES public.article(id),
CONSTRAINT invoice_item_invoice_fk FOREIGN KEY (invoice_id) REFERENCES public.invoice(id)
);
ALTER TABLE IF EXISTS public.USERDATA
OWNER TO trainer;
ALTER TABLE IF EXISTS public.adress
OWNER TO trainer;
ALTER TABLE IF EXISTS public.article
OWNER TO trainer;
ALTER TABLE IF EXISTS public.banking_connection
OWNER TO trainer;
ALTER TABLE IF EXISTS public.invoice
OWNER TO trainer;
ALTER TABLE IF EXISTS public.invoice_item
OWNER TO trainer;

@ -0,0 +1,6 @@
DROP TABLE IF EXISTS public.invoice_item;
DROP TABLE IF EXISTS public.invoice;
DROP TABLE IF EXISTS public.adress;
DROP TABLE IF EXISTS public.banking_connection;
DROP TABLE IF EXISTS public.article;
DROP TABLE IF EXISTS public.USERDATA;

@ -38,6 +38,16 @@ public class InvoiceBusiness extends AbstractBusiness<Invoice> {
return save;
}
@Override
public boolean delete(Invoice obj) {
for (InvoiceItem i : obj.getItems()) {
i.setInvoice(null);
itemRepository.save(i);
}
return super.delete(obj);
}
public String createNextInvoiceId(List<Invoice> invoices, String pattern) {
Optional<Invoice> latest = invoices.stream()

@ -19,8 +19,8 @@ public class InvoiceItem extends BaseEntity {
private static final long serialVersionUID = 3142997452876778041L;
private LocalDateTime start;
private LocalDateTime end;
private LocalDateTime startTime;
private LocalDateTime endTime;
@Column(nullable = true, length = 15)
private String participants;
@Column(nullable = true, length = 255)
@ -44,20 +44,20 @@ public class InvoiceItem extends BaseEntity {
}
public LocalDateTime getStart() {
return start;
return startTime;
}
public void setStart(LocalDateTime start) {
this.start = start;
public void setStart(LocalDateTime startTime) {
this.startTime = startTime;
getSumPrice();
}
public LocalDateTime getEnd() {
return end;
return endTime;
}
public void setEnd(LocalDateTime end) {
this.end = end;
public void setEnd(LocalDateTime endTime) {
this.endTime = endTime;
getSumPrice();
}
@ -82,9 +82,9 @@ public class InvoiceItem extends BaseEntity {
}
public BigDecimal getSumPrice() {
if (article == null || start == null || end == null) {
if (article == null || startTime == null || endTime == null) {
sumPrice = null;
return null;
return BigDecimal.ZERO;
}
sumPrice = BigDecimal.valueOf(getDurationInMinutes())
@ -104,10 +104,10 @@ public class InvoiceItem extends BaseEntity {
}
public long getDurationInMinutes() {
if (start == null || end == null) {
if (startTime == null || endTime == null) {
return -1L;
}
return start.until(end, ChronoUnit.MINUTES);
return startTime.until(endTime, ChronoUnit.MINUTES);
}
public String getDescription() {
@ -123,13 +123,13 @@ public class InvoiceItem extends BaseEntity {
@Override
protected String getMediumRepresentation() {
return DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.SHORT).format(start);
return DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.SHORT).format(endTime);
}
@Override
public String toString() {
return "InvoiceItem [id=" + getId() + ", start=" + start + ", end="
+ end + ", article=" + article + "]";
return "InvoiceItem [id=" + getId() + ", start=" + startTime + ", end="
+ endTime + ", article=" + article + "]";
}
@Override
@ -137,8 +137,8 @@ public class InvoiceItem extends BaseEntity {
final int prime = 31;
int result = 1;
result = prime * result + ((article == null) ? 0 : article.hashCode());
result = prime * result + ((end == null) ? 0 : end.hashCode());
result = prime * result + ((start == null) ? 0 : start.hashCode());
result = prime * result + ((endTime == null) ? 0 : endTime.hashCode());
result = prime * result + ((startTime == null) ? 0 : endTime.hashCode());
return result;
}
@ -161,18 +161,18 @@ public class InvoiceItem extends BaseEntity {
} else if (!article.equals(other.article)) {
return false;
}
if (end == null) {
if (other.end != null) {
if (endTime == null) {
if (other.endTime != null) {
return false;
}
} else if (!end.equals(other.end)) {
} else if (!endTime.equals(other.endTime)) {
return false;
}
if (start == null) {
if (other.start != null) {
if (startTime == null) {
if (other.startTime != null) {
return false;
}
} else if (!start.equals(other.start)) {
} else if (!endTime.equals(other.endTime)) {
return false;
}
return true;

@ -14,7 +14,7 @@ import javax.persistence.Table;
import org.keycloak.representations.AccessToken;
@Entity
@Table(name = "USER")
@Table(name = "USERDATA")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class User extends BaseEntity {

@ -165,7 +165,7 @@ public class View extends VerticalLayout implements BeforeEnterObserver {
}
});
invoiceCompoent = new InvoiceOverviewComponent(invoiceRepository, user, itemsForInvoice);
invoiceCompoent.addInvoiceCreationListener(() -> invoiceItems.refreshData());
invoiceCompoent.addInvoiceCountChangeListener(() -> invoiceItems.refreshData());
layout.add(invoiceItems);
layout.add(invoiceCompoent);

@ -60,7 +60,7 @@ public class ArticleDialog extends Dialog {
description = new TextField();
description.setLabel("Beschreibung");
isTrainer = new Checkbox("als Trainer", false);
isTrainer = new Checkbox("mit Trainerlizenz", false);
FormLayout contentValues = new FormLayout();
contentValues.add(title, pricePerHour, description, isTrainer);
@ -229,7 +229,7 @@ public class ArticleDialog extends Dialog {
articleGrid.addColumn(Article::getDescription)
.setHeader("Beschreibung");
articleGrid.addColumn(this::reportToCheckbox)
.setHeader("Report");
.setHeader("Trainer Vorlage");
articleGrid.addSelectionListener(sel -> {
if (!binder.hasChanges()) {

@ -3,6 +3,7 @@ package de.kreth.invoice.views.invoice;
import static de.kreth.invoice.Application.getString;
import static de.kreth.invoice.Localization_Properties.CAPTION_INVOICE_PRINTSIGNATURE;
import static de.kreth.invoice.Localization_Properties.LABEL_CANCEL;
import static de.kreth.invoice.Localization_Properties.LABEL_CLOSE;
import static de.kreth.invoice.Localization_Properties.LABEL_OPEN;
import static de.kreth.invoice.Localization_Properties.LABEL_PREVIEW;
import static de.kreth.invoice.Localization_Properties.LABEL_STORE;
@ -14,7 +15,10 @@ import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@ -78,6 +82,10 @@ public class InvoiceDialog extends Dialog {
private Checkbox printSignature;
private final List<String> existingInvoiceNumbers = new ArrayList<>();
private Button deleteButton;
/**
* Initializes the Dialog with an empty {@link Invoice}.
* <p>
@ -118,18 +126,24 @@ public class InvoiceDialog extends Dialog {
}
});
okButton = new Button(getString(LABEL_STORE), ev -> close());
Button cancel = new Button(getString(LABEL_CANCEL), ev -> close());
deleteButton = new Button("Löschen");
String caption;
String previewCaption;
String closeCaption;
if (pdfOpenLabel == InvoiceMode.VIEW_ONLY) {
caption = getString(LABEL_OPEN);
previewCaption = getString(LABEL_OPEN);
closeCaption = getString(LABEL_CLOSE);
okButton.setVisible(false);
} else {
caption = getString(LABEL_PREVIEW);
previewCaption = getString(LABEL_PREVIEW);
closeCaption = getString(LABEL_CANCEL);
deleteButton.setVisible(false);
}
Button previewButton = new Button(caption, this::showPdf);
Button cancel = new Button(closeCaption, ev -> close());
Button previewButton = new Button(previewCaption, this::showPdf);
HorizontalLayout btnLayout = new HorizontalLayout();
btnLayout.add(okButton, cancel, previewButton);
btnLayout.add(okButton, cancel, previewButton, deleteButton);
VerticalLayout vLayout = new VerticalLayout();
@ -144,6 +158,14 @@ public class InvoiceDialog extends Dialog {
private void updateInvoiceNo(ValueChangeEvent<String> ev) {
if (invoice != null) {
invoice.setInvoiceId(ev.getValue());
if (existingInvoiceNumbers.contains(invoice.getInvoiceId())) {
invoiceNo.setErrorMessage("Die Rechnungsnummer existiert bereits. Sie muss eindeutig sein.");
invoiceNo.setInvalid(true);
okButton.setEnabled(false);
} else {
invoiceNo.setInvalid(false);
okButton.setEnabled(true);
}
}
}
@ -209,9 +231,9 @@ public class InvoiceDialog extends Dialog {
}
}
};
new StreamResource("invoice.pdf", inStream);
Anchor link = new Anchor(resourceFrame, "Download PDF");
// r("Download PDF", new FileResource(outFile));
link.addFocusListener(ev -> LOGGER.debug("Download link clicked."));
link.addAttachListener(ev -> LOGGER.debug("Download link attached."));
@ -234,8 +256,14 @@ public class InvoiceDialog extends Dialog {
return okButton.addClickListener(listener);
}
public void setInvoice(Invoice invoice) {
this.invoice = invoice;
public Registration addDeleteClickListener(ComponentEventListener<ClickEvent<Button>> listener) {
return deleteButton.addClickListener(listener);
}
public void setInvoice(Invoice invoice, List<String> existingInvoiceNumbers) {
this.invoice = Objects.requireNonNull(invoice);
this.existingInvoiceNumbers.clear();
this.existingInvoiceNumbers.addAll(existingInvoiceNumbers);
signature = new Signature(invoice.getUser());
invoiceNo.setValue(invoice.getInvoiceId());
invoiceDate.setValue(invoice.getInvoiceDate().toLocalDate());

@ -8,6 +8,7 @@ import java.util.List;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.data.provider.DataProvider;
import com.vaadin.flow.data.provider.ListDataProvider;
import com.vaadin.flow.data.renderer.LocalDateTimeRenderer;
import com.vaadin.flow.data.renderer.NumberRenderer;
@ -41,4 +42,10 @@ public class InvoiceGrid extends Grid<Invoice> {
this.items.addAll(items);
getDataProvider().refreshAll();
}
@SuppressWarnings("unchecked")
@Override
public ListDataProvider<Invoice> getDataProvider() {
return (ListDataProvider<Invoice>) super.getDataProvider();
}
}

@ -110,7 +110,7 @@ public class InvoiceItemDialog {
item.setStart(LocalDateTime.of(startDate.getValue(), startTime.getValue()));
item.setEnd(LocalDateTime.of(startDate.getValue(), endTime.getValue()));
item.setParticipants(participants.getValue());
item.getSumPrice();
}
public interface DialogCloseListener {

@ -105,7 +105,9 @@ public class InvoiceItemOverviewComponent extends VerticalLayout {
}
public void refreshData() {
grid.setItems(invoiceItemRepository.findByInvoiceIsNull(user));
List<InvoiceItem> findByInvoiceIsNull = invoiceItemRepository.findByInvoiceIsNull(user);
findByInvoiceIsNull.sort((i1, i2) -> i1.getStart().compareTo(i2.getStart()));
grid.setItems(findByInvoiceIsNull);
grid.deselectAll();
grid.getDataProvider().refreshAll();
ItemSelectionChangeEvent evt = new ItemSelectionChangeEvent(getAllItems());

@ -13,8 +13,6 @@ import com.vaadin.flow.component.ClickEvent;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.confirmdialog.ConfirmDialog;
import com.vaadin.flow.component.formlayout.FormLayout;
import com.vaadin.flow.component.grid.contextmenu.GridContextMenu;
import com.vaadin.flow.component.grid.contextmenu.GridContextMenu.GridContextMenuItemClickEvent;
import com.vaadin.flow.component.html.H3;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
@ -35,7 +33,8 @@ public class InvoiceOverviewComponent extends VerticalLayout {
private final InvoiceBusiness business;
private final User user;
private final List<InvoiceItem> itemsForInvoice;
private final List<InvoiceCreationListener> creationListener;
private final List<InvoiceCountChangeListener> creationListener;
private InvoiceDialog invoiceDialog;
public InvoiceOverviewComponent(InvoiceBusiness business, User user, List<InvoiceItem> itemsForInvoice) {
super();
@ -51,31 +50,34 @@ public class InvoiceOverviewComponent extends VerticalLayout {
addButton);
add(new VerticalLayout(titleComponent, grid));
grid.addItemClickListener(ev -> openDialog(ev.getItem(), InvoiceMode.VIEW_ONLY));
GridContextMenu<Invoice> menu = grid.addContextMenu();
menu.addItem("Löschen", this::delete);
}
private void delete(GridContextMenuItemClickEvent<Invoice> event) {
if (event.getItem().isPresent()) {
private void confirmAndExecuteDelete(Invoice item) {
ConfirmDialog dlg = new ConfirmDialog();
dlg.setHeader(getString(Localization_Properties.MESSAGE_DELETE_TITLE));
dlg.setText(MessageFormat.format(getString(Localization_Properties.MESSAGE_DELETE_TEXT),
event.getItem().get()));
item));
dlg.setCancelable(true);
dlg.setCancelText("Nicht " + getString(Localization_Properties.LABEL_DELETE));
dlg.setConfirmText(getString(Localization_Properties.LABEL_DELETE));
dlg.addConfirmListener(ev -> {
business.delete(event.getItem().get());
business.delete(item);
if (invoiceDialog != null) {
invoiceDialog.close();
}
refreshData();
for (InvoiceCountChangeListener invoiceCreationListener : creationListener) {
invoiceCreationListener.invoiceCreated();
}
});
dlg.open();
}
}
public void refreshData() {
List<Invoice> loadAll = business.loadAll(invoice -> user.equals(invoice.getUser()));
loadAll.sort((i1, i2) -> i1.getInvoiceDate().compareTo(i2.getInvoiceDate()));
grid.setItems(loadAll);
grid.getDataProvider().refreshAll();
}
@ -97,24 +99,35 @@ public class InvoiceOverviewComponent extends VerticalLayout {
}
private void openDialog(Invoice invoice, InvoiceMode mode) {
InvoiceDialog dlg = new InvoiceDialog(mode);
dlg.setInvoice(invoice);
dlg.addOkClickListener(evt -> {
invoiceDialog = new InvoiceDialog(mode);
List<String> invoiceIds = new ArrayList<>();
grid.getDataProvider().getItems().forEach(inv -> invoiceIds.add(inv.getInvoiceId().strip()));
invoiceDialog.setInvoice(invoice, invoiceIds);
invoiceDialog.addOkClickListener(evt -> {
business.save(invoice);
refreshData();
for (InvoiceCreationListener invoiceCreationListener : creationListener) {
for (InvoiceCountChangeListener invoiceCreationListener : creationListener) {
invoiceCreationListener.invoiceCreated();
}
});
dlg.open();
dlg.addOpenedChangeListener(evt -> refreshData());
invoiceDialog.addDeleteClickListener(evt -> {
confirmAndExecuteDelete(invoice);
});
invoiceDialog.open();
invoiceDialog.addOpenedChangeListener(evt -> refreshData());
invoiceDialog.addOpenedChangeListener(ev -> {
if (ev.isOpened() == false) {
invoiceDialog = null;
}
});
}
public void addInvoiceCreationListener(InvoiceCreationListener listener) {
public void addInvoiceCountChangeListener(InvoiceCountChangeListener listener) {
this.creationListener.add(listener);
}
public static interface InvoiceCreationListener {
public static interface InvoiceCountChangeListener {
void invoiceCreated();
}
}

@ -14,6 +14,7 @@ import org.slf4j.LoggerFactory;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.dialog.Dialog;
import com.vaadin.flow.component.formlayout.FormLayout;
import com.vaadin.flow.component.html.Hr;
import com.vaadin.flow.component.html.Image;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
@ -62,9 +63,7 @@ public class UserDetailsDialog extends Dialog {
private final Button okButton;
private final Image signatureImage;
//
// private final UserBankRepository bankRepository;
// private final UserAdressRepository adressRepository;
private final Upload upload;
private User user;
@ -127,11 +126,9 @@ public class UserDetailsDialog extends Dialog {
.bind(UserAdress::getCity, UserAdress::setCity);
signatureImage = new Image();
signatureImage.setWidth("192px");
signatureImage.setHeight("62px");
signatureImage.setAlt("Keine Unterschrift konfiguriert");
Upload upload = new Upload(this::receiveUpload);
upload = new Upload(this::receiveUpload);
upload.addFinishedListener(ev -> updateSignatureImage());
VerticalLayout layout = new VerticalLayout();
@ -142,7 +139,7 @@ public class UserDetailsDialog extends Dialog {
HorizontalLayout cityLayout = new HorizontalLayout();
cityLayout.add(zipCode, city);
layout.add(adress1, adress2, cityLayout, new HorizontalLayout(signatureImage, upload));
layout.add(adress1, adress2, cityLayout, new FormLayout(signatureImage, upload));
okButton = new Button("OK", ev -> {
BinderValidationStatus<UserBank> bankValidation = bankBinder.validate();
@ -191,7 +188,8 @@ public class UserDetailsDialog extends Dialog {
}
private void updateSignatureImage() {
if (user != null) {
if (user != null && user.getId() != null) {
upload.setUploadButton(null);
Signature signature = new Signature(user);
if (signature.isSignatureImageExists()) {
File signatureUrl = signature.getSignatureUrl();
@ -210,8 +208,16 @@ public class UserDetailsDialog extends Dialog {
}
}
});
signatureImage.setWidth("192px");
signatureImage.setHeight("62px");
signatureImage.setSrc(resource);
}
} else {
signatureImage.setWidth(null);
signatureImage.setHeight(null);
upload.setVisible(false);
signatureImage.setAlt(
"Eine Unterschrift kann konfiguriert werden, nachdem die Benutzerdaten gespeichert wurden.");
}
}

@ -62,7 +62,7 @@ label.logout = Abmelden
label.ok = OK
label.store = Speichern
label.preview = Vorschau
label.open = \u00D6ffnen
label.open = PDF \u00D6ffnen
label.user.register = Registrieren
message.article.priceerror = Bitte legen Sie den Preis fest.

Loading…
Cancel
Save