From d595ab7534a6054556da8d651cc0aca69b832a79 Mon Sep 17 00:00:00 2001 From: Markus Kreth Date: Tue, 24 May 2022 23:24:57 +0200 Subject: [PATCH] Runnable port with keycloak authentication --- pom.xml | 461 +++++++++--------- .../java/de/kreth/invoice/Application.java | 19 + .../invoice/Localization_Properties.java | 324 ++++++++++++ .../invoice/business/AbstractBusiness.java | 57 +++ .../invoice/business/ArticleBusiness.java | 34 ++ .../de/kreth/invoice/business/Business.java | 13 + .../invoice/business/InvoiceBusiness.java | 84 ++++ .../invoice/business/InvoiceItemBusiness.java | 33 ++ .../invoice/components/InvoiceItemGrid.java | 177 ------- .../InvoiceItemOverviewComponent.java | 53 -- .../components/InvoiceOverviewComponent.java | 18 - .../java/de/kreth/invoice/data/Adress.java | 112 ----- .../java/de/kreth/invoice/data/Article.java | 63 ++- .../kreth/invoice/data/BankingConnection.java | 83 ---- .../de/kreth/invoice/data/BaseEntity.java | 29 +- .../java/de/kreth/invoice/data/Invoice.java | 23 +- .../de/kreth/invoice/data/InvoiceItem.java | 13 + src/main/java/de/kreth/invoice/data/User.java | 29 +- .../de/kreth/invoice/data/UserAdress.java | 124 ++++- .../java/de/kreth/invoice/data/UserBank.java | 113 ++++- .../persistence/ArticleRepository.java | 4 +- .../persistence/InvoiceItemRepository.java | 6 +- .../persistence/InvoiceRepository.java | 12 + .../persistence/UserAdressRepository.java | 2 +- .../persistence/UserBankRepository.java | 2 +- .../invoice/report/InvoiceReportSource.java | 19 + .../kreth/invoice/security/UserManager.java | 18 +- .../de/kreth/invoice/views/DataFormat.java | 7 + .../invoice/views/ErrorNotification.java | 34 ++ .../kreth/invoice/views/FooterComponent.java | 78 +++ .../kreth/invoice/views/PriceConverter.java | 69 +++ .../java/de/kreth/invoice/views/View.java | 167 ++++--- .../invoice/views/article/ArticleDialog.java | 269 ++++++++++ .../invoice/views/article/ArticleGrid.java | 30 ++ .../invoice/views/invoice/InvoiceDialog.java | 255 ++++++++++ .../invoice}/InvoiceGrid.java | 16 +- .../invoiceitem}/InvoiceItemDialog.java | 2 +- .../views/invoiceitem/InvoiceItemGrid.java | 179 +++++++ .../InvoiceItemOverviewComponent.java | 162 ++++++ .../invoiceitem/InvoiceOverviewComponent.java | 120 +++++ .../views/{ => user}/UserDetailsDialog.java | 73 ++- src/main/resources/localization.properties | 79 +++ src/main/resources/schema.sql | 18 +- .../invoice/business/InvoiceBusinessTest.java | 83 ++++ 44 files changed, 2741 insertions(+), 825 deletions(-) create mode 100644 src/main/java/de/kreth/invoice/Localization_Properties.java create mode 100644 src/main/java/de/kreth/invoice/business/AbstractBusiness.java create mode 100644 src/main/java/de/kreth/invoice/business/ArticleBusiness.java create mode 100644 src/main/java/de/kreth/invoice/business/Business.java create mode 100644 src/main/java/de/kreth/invoice/business/InvoiceBusiness.java create mode 100644 src/main/java/de/kreth/invoice/business/InvoiceItemBusiness.java delete mode 100644 src/main/java/de/kreth/invoice/components/InvoiceItemGrid.java delete mode 100644 src/main/java/de/kreth/invoice/components/InvoiceItemOverviewComponent.java delete mode 100644 src/main/java/de/kreth/invoice/components/InvoiceOverviewComponent.java delete mode 100644 src/main/java/de/kreth/invoice/data/Adress.java delete mode 100644 src/main/java/de/kreth/invoice/data/BankingConnection.java create mode 100644 src/main/java/de/kreth/invoice/persistence/InvoiceRepository.java create mode 100644 src/main/java/de/kreth/invoice/views/DataFormat.java create mode 100644 src/main/java/de/kreth/invoice/views/ErrorNotification.java create mode 100644 src/main/java/de/kreth/invoice/views/FooterComponent.java create mode 100644 src/main/java/de/kreth/invoice/views/PriceConverter.java create mode 100644 src/main/java/de/kreth/invoice/views/article/ArticleDialog.java create mode 100644 src/main/java/de/kreth/invoice/views/article/ArticleGrid.java create mode 100644 src/main/java/de/kreth/invoice/views/invoice/InvoiceDialog.java rename src/main/java/de/kreth/invoice/{components => views/invoice}/InvoiceGrid.java (70%) rename src/main/java/de/kreth/invoice/{components => views/invoiceitem}/InvoiceItemDialog.java (98%) create mode 100644 src/main/java/de/kreth/invoice/views/invoiceitem/InvoiceItemGrid.java create mode 100644 src/main/java/de/kreth/invoice/views/invoiceitem/InvoiceItemOverviewComponent.java create mode 100644 src/main/java/de/kreth/invoice/views/invoiceitem/InvoiceOverviewComponent.java rename src/main/java/de/kreth/invoice/views/{ => user}/UserDetailsDialog.java (77%) create mode 100644 src/main/resources/localization.properties create mode 100644 src/test/java/de/kreth/invoice/business/InvoiceBusinessTest.java diff --git a/pom.xml b/pom.xml index bc6a829..99764d9 100644 --- a/pom.xml +++ b/pom.xml @@ -1,78 +1,78 @@ - 4.0.0 - - de.kreth.invoice - trainerinvoice - Übungsleiterabrechnungen - 1.0.0-SNAPSHOT - jar + 4.0.0 + + de.kreth.invoice + trainerinvoice + Übungsleiterabrechnungen + 1.0.0-SNAPSHOT + jar - - 11 - 23.0.8 + + 11 + 23.0.8 11.0.1 - + - - org.springframework.boot - spring-boot-starter-parent - 2.6.7 - + + org.springframework.boot + spring-boot-starter-parent + 2.6.7 + - - + + - - - central - https://repo.maven.apache.org/maven2 - - false - - - - vaadin-prereleases - + + + central + https://repo.maven.apache.org/maven2 + + false + + + + vaadin-prereleases + https://maven.vaadin.com/vaadin-prereleases/ - - - - Vaadin Directory - https://maven.vaadin.com/vaadin-addons - - false - - - + + + + Vaadin Directory + https://maven.vaadin.com/vaadin-addons + + false + + + - - - - central - https://repo.maven.apache.org/maven2 - - false - - - - vaadin-prereleases - + + + + central + https://repo.maven.apache.org/maven2 + + false + + + + vaadin-prereleases + https://maven.vaadin.com/vaadin-prereleases/ - - + + + + + + + com.vaadin + vaadin-bom + ${vaadin.version} + pom + import + - - - - com.vaadin - vaadin-bom - ${vaadin.version} - pom - import - - org.keycloak.bom keycloak-adapter-bom @@ -80,41 +80,36 @@ pom import - - + + - - - com.vaadin - - vaadin - - - com.vaadin - vaadin-spring-boot-starter - - - com.h2database - h2 - runtime - + + + com.vaadin + + vaadin + + + com.vaadin + vaadin-spring-boot-starter + + + + com.h2database + h2 + runtime + - - com.vaadin - exampledata - 4.1.4 - + + org.springframework.boot + spring-boot-starter-data-jpa + - - org.springframework.boot - spring-boot-starter-data-jpa - + + org.springframework.boot + spring-boot-starter-validation + - - org.springframework.boot - spring-boot-starter-validation - - org.springframework.boot spring-boot-starter-security @@ -128,163 +123,163 @@ keycloak-spring-security-adapter - - org.springframework.boot - spring-boot-devtools - true - - - org.springframework.boot - spring-boot-starter-test - test - - - com.vaadin - vaadin-testbench - test - - - - org.junit.vintage - junit-vintage-engine - test - - - org.hamcrest - hamcrest-core - - - - - io.github.bonigarcia - webdrivermanager - 5.0.3 - test - - net.sf.jasperreports - jasperreports - 6.19.1 + org.springframework.boot + spring-boot-devtools + true + + + org.springframework.boot + spring-boot-starter-test + test + + + com.vaadin + vaadin-testbench + test + + + + org.junit.vintage + junit-vintage-engine + test + + + org.hamcrest + hamcrest-core + + + + + io.github.bonigarcia + webdrivermanager + 5.0.3 + test + + + net.sf.jasperreports + jasperreports + 6.19.1 - - com.lowagie - itext - 2.1.7 + com.lowagie + itext + 2.1.7 - + - - spring-boot:run - - - org.springframework.boot - spring-boot-maven-plugin - - - 500 - 240 - - + + 500 + 240 + + - - - com.vaadin - vaadin-maven-plugin - ${vaadin.version} - - - - prepare-frontend - - - - - - + + com.vaadin + vaadin-maven-plugin + ${vaadin.version} + + + + prepare-frontend + + + + + + - - - - production - - - - com.vaadin - vaadin-maven-plugin - ${vaadin.version} - - - - build-frontend - - compile - - - - true - - - - - + + + + production + + + + com.vaadin + vaadin-maven-plugin + ${vaadin.version} + + + + build-frontend + + compile + + + + true + + + + + - - it - - - - org.springframework.boot - spring-boot-maven-plugin - - - start-spring-boot - pre-integration-test - - start - - - - stop-spring-boot - post-integration-test - - stop - - - - + + it + + + + org.springframework.boot + spring-boot-maven-plugin + + + start-spring-boot + pre-integration-test + + start + + + + stop-spring-boot + post-integration-test + + stop + + + + - - - org.apache.maven.plugins - maven-failsafe-plugin - - - - integration-test - verify - - - - - false - true - - - - - + + + org.apache.maven.plugins + maven-failsafe-plugin + + + + integration-test + verify + + + + + false + true + + + + + - - + + \ No newline at end of file diff --git a/src/main/java/de/kreth/invoice/Application.java b/src/main/java/de/kreth/invoice/Application.java index e0f1f3c..b60d628 100644 --- a/src/main/java/de/kreth/invoice/Application.java +++ b/src/main/java/de/kreth/invoice/Application.java @@ -1,8 +1,12 @@ package de.kreth.invoice; +import java.util.PropertyResourceBundle; +import java.util.ResourceBundle; + import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; +import org.springframework.context.annotation.Bean; import com.vaadin.flow.component.dependency.NpmPackage; import com.vaadin.flow.component.page.AppShellConfigurator; @@ -23,9 +27,24 @@ import com.vaadin.flow.theme.Theme; public class Application extends SpringBootServletInitializer implements AppShellConfigurator { private static final long serialVersionUID = 8632833774084603989L; + private static ResourceBundle bundle = null; public static void main(String[] args) { SpringApplication.run(Application.class, args); } + public static String getString(Localization_Properties property) { + if (bundle == null) { + bundle = resourceBundle(); + } + + return property.getString(bundle::getString); + } + + @Bean + static ResourceBundle resourceBundle() { + + ResourceBundle bundle = PropertyResourceBundle.getBundle("localization"); + return bundle; + } } diff --git a/src/main/java/de/kreth/invoice/Localization_Properties.java b/src/main/java/de/kreth/invoice/Localization_Properties.java new file mode 100644 index 0000000..1fffc4b --- /dev/null +++ b/src/main/java/de/kreth/invoice/Localization_Properties.java @@ -0,0 +1,324 @@ +package de.kreth.invoice; + +import java.util.Properties; +import java.util.ResourceBundle; +import java.util.function.UnaryOperator; + +import javax.annotation.processing.Generated; + +/** + * Property keys from localization.properties + */ +@Generated(date = "22.05.2022, 19:41:47", value = "de.kreth.property2java.Generator") +public enum Localization_Properties { + + /** + * caption.invoiceitem.date = "Datum" + */ + CAPTION_INVOICEITEM_DATE ("caption.invoiceitem.date"), + /** + * caption.user.login = "Anmelden" + */ + CAPTION_USER_LOGIN ("caption.user.login"), + /** + * error.invoice.title.noitems = "Leere Abrechnung nicht erlaubt." + */ + ERROR_INVOICE_TITLE_NOITEMS ("error.invoice.title.noitems"), + /** + * caption.invoice.sum = "Summe" + */ + CAPTION_INVOICE_SUM ("caption.invoice.sum"), + /** + * caption.invoiceitem.participants = "Teilnehmer" + */ + CAPTION_INVOICEITEM_PARTICIPANTS ("caption.invoiceitem.participants"), + /** + * caption.invoiceitem.add = "Neuer Posten" + */ + CAPTION_INVOICEITEM_ADD ("caption.invoiceitem.add"), + /** + * caption.invoice.pattern = "Rechnung-{0}" + */ + CAPTION_INVOICE_PATTERN ("caption.invoice.pattern"), + /** + * caption.invoiceitem.start = "Beginn" + */ + CAPTION_INVOICEITEM_START ("caption.invoiceitem.start"), + /** + * caption.article.report = "Mit Trainer-Lizenz" + */ + CAPTION_ARTICLE_REPORT ("caption.article.report"), + /** + * caption.invoice.invoiceno = "Rechnungsnummer" + */ + CAPTION_INVOICE_INVOICENO ("caption.invoice.invoiceno"), + /** + * caption.invoiceitems = "Rechnungspositionen" + */ + CAPTION_INVOICEITEMS ("caption.invoiceitems"), + /** + * message.user.passwordmissmatch = "Passworter stimmen nicht überein!" + */ + MESSAGE_USER_PASSWORDMISSMATCH ("message.user.passwordmissmatch"), + /** + * label.delete = "Löschen" + */ + LABEL_DELETE ("label.delete"), + /** + * message.user.loginfailure = "Anmeldefehler! Falscher Name oder Passwort?" + */ + MESSAGE_USER_LOGINFAILURE ("message.user.loginfailure"), + /** + * caption.article.type.trainer = "Trainer" + */ + CAPTION_ARTICLE_TYPE_TRAINER ("caption.article.type.trainer"), + /** + * caption.invoiceitem.sumprice = "Betrag" + */ + CAPTION_INVOICEITEM_SUMPRICE ("caption.invoiceitem.sumprice"), + /** + * message.delete.text = "Soll {0} wirklich gelöscht werden?" + */ + MESSAGE_DELETE_TEXT ("message.delete.text"), + /** + * label.user.register = "Registrieren" + */ + LABEL_USER_REGISTER ("label.user.register"), + /** + * error.userdetails.bankname_empty = "Bankname darf nicht leer sein." + */ + ERROR_USERDETAILS_BANKNAME_EMPTY ("error.userdetails.bankname_empty"), + /** + * label.ok = "OK" + */ + LABEL_OK ("label.ok"), + /** + * label.open = "Öffnen" + */ + LABEL_OPEN ("label.open"), + /** + * label.discart = "Verwerfen" + */ + LABEL_DISCART ("label.discart"), + /** + * caption.article = "Artikel" + */ + CAPTION_ARTICLE ("caption.article"), + /** + * message.article.priceerror = "Bitte legen Sie den Preis fest." + */ + MESSAGE_ARTICLE_PRICEERROR ("message.article.priceerror"), + /** + * error.userdetails.iban_empty = "Iban darf nicht leer sein." + */ + ERROR_USERDETAILS_IBAN_EMPTY ("error.userdetails.iban_empty"), + /** + * message.user.create.success = "{0} erstellt!" + */ + MESSAGE_USER_CREATE_SUCCESS ("message.user.create.success"), + /** + * error.userdetails.prename_empty = "Vorname darf nicht leer sein." + */ + ERROR_USERDETAILS_PRENAME_EMPTY ("error.userdetails.prename_empty"), + /** + * error.invoice.text.noitems = "Bitte Posten für Rechnung auswählen." + */ + ERROR_INVOICE_TEXT_NOITEMS ("error.invoice.text.noitems"), + /** + * caption.invoiceitem.end = "Ende" + */ + CAPTION_INVOICEITEM_END ("caption.invoiceitem.end"), + /** + * caption.invoiceitem = "" + */ + CAPTION_INVOICEITEM ("caption.invoiceitem"), + /** + * caption.adress.zipcode = "Postleitzahl" + */ + CAPTION_ADRESS_ZIPCODE ("caption.adress.zipcode"), + /** + * caption.user.password = "Ihr Password:" + */ + CAPTION_USER_PASSWORD ("caption.user.password"), + /** + * caption.invoiceitem.name = "Rechnungsposition" + */ + CAPTION_INVOICEITEM_NAME ("caption.invoiceitem.name"), + /** + * message.delete.title = "Wirklich löschen?" + */ + MESSAGE_DELETE_TITLE ("message.delete.title"), + /** + * error.userdetails.zip_empty = "Postleitzahl darf nicht leer sein." + */ + ERROR_USERDETAILS_ZIP_EMPTY ("error.userdetails.zip_empty"), + /** + * message.invoiceitem.allfieldsmustbeset = "Start, Ende und Artikel müssen gesetzt sein!" + */ + MESSAGE_INVOICEITEM_ALLFIELDSMUSTBESET ("message.invoiceitem.allfieldsmustbeset"), + /** + * caption.invoices = "Rechnungen" + */ + CAPTION_INVOICES ("caption.invoices"), + /** + * caption.user.passwordconfirmation = "Password bestätigen:" + */ + CAPTION_USER_PASSWORDCONFIRMATION ("caption.user.passwordconfirmation"), + /** + * caption.adress.city = "Ort" + */ + CAPTION_ADRESS_CITY ("caption.adress.city"), + /** + * caption.invoice.printsignature = "Unterschrift drucken" + */ + CAPTION_INVOICE_PRINTSIGNATURE ("caption.invoice.printsignature"), + /** + * caption.article.title = "Titel" + */ + CAPTION_ARTICLE_TITLE ("caption.article.title"), + /** + * caption.article.price = "Stundenpreis" + */ + CAPTION_ARTICLE_PRICE ("caption.article.price"), + /** + * error.userdetails.adress_empty = "Adresse darf nicht leer sein." + */ + ERROR_USERDETAILS_ADRESS_EMPTY ("error.userdetails.adress_empty"), + /** + * caption.bank.iban = "IBAN" + */ + CAPTION_BANK_IBAN ("caption.bank.iban"), + /** + * caption.invoice.create = "Rechnung erstellen" + */ + CAPTION_INVOICE_CREATE ("caption.invoice.create"), + /** + * error.article.undefined = "Bitte Artikel anlegen." + */ + ERROR_ARTICLE_UNDEFINED ("error.article.undefined"), + /** + * caption.bank.bic = "BIC" + */ + CAPTION_BANK_BIC ("caption.bank.bic"), + /** + * caption.article.type.assistant = "Übungsleiter" + */ + CAPTION_ARTICLE_TYPE_ASSISTANT ("caption.article.type.assistant"), + /** + * error.userdetails.city_empty = "Ort darf nicht leer sein." + */ + ERROR_USERDETAILS_CITY_EMPTY ("error.userdetails.city_empty"), + /** + * label.cancel = "Abbrechen" + */ + LABEL_CANCEL ("label.cancel"), + /** + * message.user.create.failure = "Fehler beim Erstellen von Benutzer {0}! Ändern Sie den Benutzernamen oder fragen Sie nach dem Passwort. Detail: {1}" + */ + MESSAGE_USER_CREATE_FAILURE ("message.user.create.failure"), + /** + * caption.article.description = "Beschreibung" + */ + CAPTION_ARTICLE_DESCRIPTION ("caption.article.description"), + /** + * caption.user.loginname = "Anmeldename:" + */ + CAPTION_USER_LOGINNAME ("caption.user.loginname"), + /** + * message.article.error.invoiceexists = "Kann nicht geändert werden, da bereits Rechnungen bestehen. Bitte neuen Artikel anlegen." + */ + MESSAGE_ARTICLE_ERROR_INVOICEEXISTS ("message.article.error.invoiceexists"), + /** + * error.userdetails.surname_empty = "Nachname darf nicht leer sein." + */ + ERROR_USERDETAILS_SURNAME_EMPTY ("error.userdetails.surname_empty"), + /** + * label.close = "Schließen" + */ + LABEL_CLOSE ("label.close"), + /** + * caption.adress.street2 = "Adresse" + */ + CAPTION_ADRESS_STREET2 ("caption.adress.street2"), + /** + * caption.adress.street1 = "Adresse" + */ + CAPTION_ADRESS_STREET1 ("caption.adress.street1"), + /** + * caption.user.surname = "Nachname:" + */ + CAPTION_USER_SURNAME ("caption.user.surname"), + /** + * label.loggedin = "Angemeldet:" + */ + LABEL_LOGGEDIN ("label.loggedin"), + /** + * label.logout = "Abmelden" + */ + LABEL_LOGOUT ("label.logout"), + /** + * caption.user.prename = "Vorname:" + */ + CAPTION_USER_PRENAME ("caption.user.prename"), + /** + * caption.bank.name = "Bankname" + */ + CAPTION_BANK_NAME ("caption.bank.name"), + /** + * label.addarticle = "Neuer Artikel" + */ + LABEL_ADDARTICLE ("label.addarticle"), + /** + * label.preview = "Vorschau" + */ + LABEL_PREVIEW ("label.preview"), + /** + * message.invoiceitem.startbeforeend = "Ende darf nicht vor Start liegen." + */ + MESSAGE_INVOICEITEM_STARTBEFOREEND ("message.invoiceitem.startbeforeend"), + /** + * caption.articles = "Artikel" + */ + CAPTION_ARTICLES ("caption.articles"), + /** + * caption.invoice.invoicedate = "Rechnungsdatum" + */ + CAPTION_INVOICE_INVOICEDATE ("caption.invoice.invoicedate"), + /** + * caption.user.details = "Benutzer Details" + */ + CAPTION_USER_DETAILS ("caption.user.details"), + /** + * error.userdetails.username_empty = "Anmeldename darf nicht leer sein." + */ + ERROR_USERDETAILS_USERNAME_EMPTY ("error.userdetails.username_empty"), + /** + * label.store = "Speichern" + */ + LABEL_STORE ("label.store"); + + private final String value; + + private Localization_Properties (String value) { + this.value = value; + } + + /** + * Represented Key in property File. + * @return key + */ + public String getValue() { + return value; + } + + /** + * Resolves the value for this key from the parameter function. + *

+ * e.g. Localization_Properties.getString(resBundle::getString) + * @param resourceFunction {@link Properties#getProperty(String)} or {@link ResourceBundle#getString(String)} + * @return + */ + public String getString(UnaryOperator resourceFunction) { + return resourceFunction.apply(value); + } +} diff --git a/src/main/java/de/kreth/invoice/business/AbstractBusiness.java b/src/main/java/de/kreth/invoice/business/AbstractBusiness.java new file mode 100644 index 0000000..e29b0cc --- /dev/null +++ b/src/main/java/de/kreth/invoice/business/AbstractBusiness.java @@ -0,0 +1,57 @@ +package de.kreth.invoice.business; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.repository.CrudRepository; + +import de.kreth.invoice.data.BaseEntity; + +public abstract class AbstractBusiness implements Business { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private final Class itemClass; + + private CrudRepository repository; + + public AbstractBusiness(CrudRepository repository, Class itemClass) { + super(); + this.repository = repository; + this.itemClass = itemClass; + } + + @Override + public boolean save(T obj) { + repository.save(obj); + logger.debug("Stored {}", obj); + return true; + } + + @Override + public boolean delete(T obj) { + repository.delete(obj); + logger.info("Deleted {}", obj); + return true; + } + + @Override + public List loadAll() { + List list = new ArrayList(); + repository.findAll().forEach(list::add); + logger.trace("Loaded {} of {}", list.size(), itemClass.getSimpleName()); + return list; + } + + public List loadAll(Predicate predicate) { + List loadAll = loadAll(); + List result = loadAll.stream().filter(predicate).collect(Collectors.toList()); + logger.trace("Filtered {} of {} total {}", result.size(), loadAll.size(), itemClass.getSimpleName()); + return result; + } + +} diff --git a/src/main/java/de/kreth/invoice/business/ArticleBusiness.java b/src/main/java/de/kreth/invoice/business/ArticleBusiness.java new file mode 100644 index 0000000..825d1c2 --- /dev/null +++ b/src/main/java/de/kreth/invoice/business/ArticleBusiness.java @@ -0,0 +1,34 @@ +package de.kreth.invoice.business; + +import java.util.List; + +import org.springframework.stereotype.Component; + +import de.kreth.invoice.data.Article; +import de.kreth.invoice.data.InvoiceItem; +import de.kreth.invoice.persistence.ArticleRepository; +import de.kreth.invoice.persistence.InvoiceItemRepository; + +@Component +public class ArticleBusiness extends AbstractBusiness

{ + + private final ArticleRepository articleRepository; + private final InvoiceItemRepository invoiceItemRepository; + + public ArticleBusiness(ArticleRepository articleRepository, InvoiceItemRepository invoiceItemRepository) { + super(articleRepository, Article.class); + this.articleRepository = articleRepository; + this.invoiceItemRepository = invoiceItemRepository; + } + + public boolean hasInvoiceItem(Article current) { + List items = invoiceItemRepository.findByArticle(current); + int size = items.size(); + return size > 0; + } + + public List
findByUserId(Long id) { + return articleRepository.findByUserId(id); + } + +} diff --git a/src/main/java/de/kreth/invoice/business/Business.java b/src/main/java/de/kreth/invoice/business/Business.java new file mode 100644 index 0000000..3929a12 --- /dev/null +++ b/src/main/java/de/kreth/invoice/business/Business.java @@ -0,0 +1,13 @@ +package de.kreth.invoice.business; + +import java.util.List; + +public interface Business { + + boolean save(T obj); + + boolean delete(T obj); + + List loadAll(); + +} \ No newline at end of file diff --git a/src/main/java/de/kreth/invoice/business/InvoiceBusiness.java b/src/main/java/de/kreth/invoice/business/InvoiceBusiness.java new file mode 100644 index 0000000..acd397f --- /dev/null +++ b/src/main/java/de/kreth/invoice/business/InvoiceBusiness.java @@ -0,0 +1,84 @@ +package de.kreth.invoice.business; + +import java.text.MessageFormat; +import java.util.List; +import java.util.Optional; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import de.kreth.invoice.data.Invoice; +import de.kreth.invoice.data.InvoiceItem; +import de.kreth.invoice.persistence.InvoiceItemRepository; +import de.kreth.invoice.persistence.InvoiceRepository; + +@Component +public class InvoiceBusiness extends AbstractBusiness { + + private final InvoiceRepository invoiceRepository; + private final InvoiceItemRepository itemRepository; + + @Autowired + public InvoiceBusiness(InvoiceRepository invoiceRepository, InvoiceItemRepository itemRepository) { + super(invoiceRepository, Invoice.class); + this.invoiceRepository = invoiceRepository; + this.itemRepository = itemRepository; + } + + @Override + public boolean save(Invoice obj) { + + for (InvoiceItem i : obj.getItems()) { + i.setInvoice(obj); + } + boolean save = super.save(obj); + for (InvoiceItem i : obj.getItems()) { + itemRepository.save(i); + } + return save; + } + + public String createNextInvoiceId(List invoices, String pattern) { + + Optional latest = invoices.stream() + .filter(i -> filter(i.getInvoiceId(), pattern)) + .max((o1, o2) -> { + return o1.getInvoiceId().compareTo(o2.getInvoiceId()); + }); + + int lastInvoiceId = 0; + + if (latest.isPresent()) { + String old = latest.get().getInvoiceId(); + int start = pattern.indexOf("{0}"); + String substring = old.substring(start); + if (substring.matches("[0-9]+")) { + lastInvoiceId = Integer.parseInt(substring); + } else { + lastInvoiceId++; + } + } + + lastInvoiceId++; + String invoiceNo = MessageFormat.format(pattern, lastInvoiceId); + return invoiceNo; + } + + boolean filter(String invoiceId, String pattern) { + + int start = Math.min(pattern.indexOf("{0}"), invoiceId.length() - 1); + int end = start + 1; + while (end < invoiceId.length() && Character.isDigit(invoiceId.charAt(end))) { + end++; + } + + String strippedPattern = pattern.substring(0, start) + pattern.substring(start + 3); + String strippedIId = invoiceId.substring(0, start) + invoiceId.substring(end); + return strippedIId.contentEquals(strippedPattern); + } + + public List findByUserId(long userId) { + return invoiceRepository.findByUserId(userId); + } + +} diff --git a/src/main/java/de/kreth/invoice/business/InvoiceItemBusiness.java b/src/main/java/de/kreth/invoice/business/InvoiceItemBusiness.java new file mode 100644 index 0000000..b75cab8 --- /dev/null +++ b/src/main/java/de/kreth/invoice/business/InvoiceItemBusiness.java @@ -0,0 +1,33 @@ +package de.kreth.invoice.business; + +import java.util.Iterator; +import java.util.List; + +import org.springframework.stereotype.Component; + +import de.kreth.invoice.data.InvoiceItem; +import de.kreth.invoice.data.User; +import de.kreth.invoice.persistence.InvoiceItemRepository; + +@Component +public class InvoiceItemBusiness extends AbstractBusiness { + + private final InvoiceItemRepository repository; + + public InvoiceItemBusiness(InvoiceItemRepository invoiceItemRepository) { + super(invoiceItemRepository, InvoiceItem.class); + this.repository = invoiceItemRepository; + } + + public List findByInvoiceIsNull(User user) { + List findByInvoiceIsNull = repository.findByInvoiceIsNull(); + for (Iterator iterator = findByInvoiceIsNull.iterator(); iterator.hasNext();) { + InvoiceItem invoiceItem = iterator.next(); + if (invoiceItem.getArticle().getUserId() != user.getId()) { + iterator.remove(); + } + } + return findByInvoiceIsNull; + } + +} diff --git a/src/main/java/de/kreth/invoice/components/InvoiceItemGrid.java b/src/main/java/de/kreth/invoice/components/InvoiceItemGrid.java deleted file mode 100644 index 372eb01..0000000 --- a/src/main/java/de/kreth/invoice/components/InvoiceItemGrid.java +++ /dev/null @@ -1,177 +0,0 @@ -package de.kreth.invoice.components; - -import java.math.BigDecimal; -import java.text.NumberFormat; -import java.time.LocalDate; -import java.time.format.DateTimeFormatter; -import java.time.format.FormatStyle; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import com.vaadin.flow.component.grid.FooterRow; -import com.vaadin.flow.component.grid.FooterRow.FooterCell; -import com.vaadin.flow.component.grid.Grid; -import com.vaadin.flow.component.grid.GridSelectionModel; -import com.vaadin.flow.data.provider.DataChangeEvent; -import com.vaadin.flow.data.provider.DataProvider; -import com.vaadin.flow.data.provider.DataProviderListener; -import com.vaadin.flow.data.provider.ListDataProvider; -import com.vaadin.flow.data.renderer.LocalDateTimeRenderer; -import com.vaadin.flow.data.renderer.NumberRenderer; - -import de.kreth.invoice.data.InvoiceItem; -import de.kreth.invoice.persistence.InvoiceItemRepository; - -class InvoiceItemGrid extends Grid { - - private static final long serialVersionUID = -8653320112619816426L; - - private final DateTimeFormatter ofLocalizedDateFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM); - - private FooterCell priceSumCell; - - private FooterCell countCell; - - private FooterCell dateSpan; - - private final List items = new ArrayList<>(); - - private InvoiceItemRepository repository; - - public InvoiceItemGrid(InvoiceItemRepository invoiceItemRepository) { - - this.repository = invoiceItemRepository; - addClassName("bordered"); - Column titleCol = addColumn(InvoiceItem::getTitle); - titleCol.setId("Article"); - titleCol.setHeader("Artikel"); - - Column dateColumn = addColumn(new LocalDateTimeRenderer<>(InvoiceItem::getStart, - DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM))); - dateColumn.setId("Date"); - dateColumn.setHeader("Datum"); - - Column startColumn = addColumn(new LocalDateTimeRenderer<>(InvoiceItem::getStart, - DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT))); - startColumn.setId("Start"); - startColumn.setHeader("Beginn"); - - Column endColumn = addColumn(new LocalDateTimeRenderer<>(InvoiceItem::getEnd, - DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT))); - endColumn.setId("Ende"); - endColumn.setHeader("Ende"); - - Column participantColumn = addColumn(InvoiceItem::getParticipants); - participantColumn.setHeader("Teilnehmer"); - - Column sumPriceColumn = addColumn( - new NumberRenderer<>(InvoiceItem::getSumPrice, NumberFormat.getCurrencyInstance())); - sumPriceColumn.setId("price"); - sumPriceColumn.setHeader("Betrag"); - -// setSortOrder(GridSortOrder.asc(dateColumn).thenAsc(startColumn)); - FooterRow footer = appendFooterRow(); - - priceSumCell = footer.getCell(sumPriceColumn); -// dateSpan = footer.join(dateColumn, startColumn, endColumn); - dateSpan = footer.getCell(dateColumn); - countCell = footer.getCell(titleCol); - -// addSelectionListener(this::selectionChanged); - - items.addAll(repository.findByInvoiceIsNull()); - - ListDataProvider dataProvider = new ListDataProvider(items); - setDataProvider(dataProvider); - dataProvider.addDataProviderListener(new InnerDataProviderListener()); - } - - public void refreshData() { - items.clear(); - items.addAll(repository.findByInvoiceIsNull()); - } - - @Override - public GridSelectionModel setSelectionMode(SelectionMode selectionMode) { - GridSelectionModel setSelectionMode = super.setSelectionMode(selectionMode); -// setSelectionMode.addSelectionListener(this::selectionChanged); - return setSelectionMode; - } - -// @SuppressWarnings("unchecked") -// private void selectionChanged(SelectionEvent event) { -// if (event.getAllSelectedItems().isEmpty()) { -// updateFooterWith(((ListDataProvider) getDataProvider()).getItems()); -// } else { -// updateFooterWith(event.getAllSelectedItems()); -// } -// } - - protected void internalSetDataProvider(DataProvider dataProvider) { - - if (!(dataProvider instanceof ListDataProvider)) { - throw new IllegalArgumentException("dataProvider must be an instance of ListDataProvider"); - } -// super.internalSetDataProvider(dataProvider); - dataProvider.addDataProviderListener(new InnerDataProviderListener()); - updateFooterWith(((ListDataProvider) getDataProvider()).getItems()); - } - - private void updateFooterWith(Collection selected) { - BigDecimal priceSum = BigDecimal.ZERO; - LocalDate min = null; - LocalDate max = null; - - for (InvoiceItem t : selected) { - priceSum = priceSum.add(t.getSumPrice()); - min = getMin(min, t.getStart().toLocalDate()); - max = getMax(max, t.getEnd().toLocalDate()); - } - - priceSumCell.setText(NumberFormat.getCurrencyInstance().format(priceSum)); - if (min != null && max != null) { - dateSpan.setText(min.format(ofLocalizedDateFormatter) + " - " + max.format(ofLocalizedDateFormatter)); - } else { - dateSpan.setText(""); - } - countCell.setText("Anzahl: " + selected.size()); - } - - private LocalDate getMax(LocalDate max, LocalDate localDate) { - if (max == null) { - max = localDate; - } else { - if (max.isBefore(localDate)) { - max = localDate; - } - } - return max; - } - - private LocalDate getMin(LocalDate min, LocalDate localDate) { - if (min == null) { - min = localDate; - } else { - if (min.isAfter(localDate)) { - min = localDate; - } - } - return min; - } - - private class InnerDataProviderListener implements DataProviderListener { - - private static final long serialVersionUID = -6094992880488082586L; - - @Override - public void onDataChange(DataChangeEvent event) { - if (event.getSource() == getDataProvider()) { - @SuppressWarnings("unchecked") - ListDataProvider provider = (ListDataProvider) getDataProvider(); - updateFooterWith(provider.getItems()); - } - } - - } -} diff --git a/src/main/java/de/kreth/invoice/components/InvoiceItemOverviewComponent.java b/src/main/java/de/kreth/invoice/components/InvoiceItemOverviewComponent.java deleted file mode 100644 index 7e5eb09..0000000 --- a/src/main/java/de/kreth/invoice/components/InvoiceItemOverviewComponent.java +++ /dev/null @@ -1,53 +0,0 @@ -package de.kreth.invoice.components; - -import java.util.List; - -import com.vaadin.flow.component.ClickEvent; -import com.vaadin.flow.component.button.Button; -import com.vaadin.flow.component.html.H3; -import com.vaadin.flow.component.orderedlayout.HorizontalLayout; -import com.vaadin.flow.component.orderedlayout.VerticalLayout; - -import de.kreth.invoice.data.Article; -import de.kreth.invoice.data.InvoiceItem; -import de.kreth.invoice.data.User; -import de.kreth.invoice.persistence.ArticleRepository; -import de.kreth.invoice.persistence.InvoiceItemRepository; - -public class InvoiceItemOverviewComponent extends VerticalLayout { - - private static final long serialVersionUID = -4486121981960039L; - private final InvoiceItemGrid grid; - private final InvoiceItemRepository invoiceItemRepository; - private final ArticleRepository articleRepository; - private final User user; - - public InvoiceItemOverviewComponent(InvoiceItemRepository invoiceItemRepository, - ArticleRepository articleRepository, User user) { - this.invoiceItemRepository = invoiceItemRepository; - this.articleRepository = articleRepository; - this.user = user; - Button addButton = new Button("Hinzufügen", this::createNewitem); - add(new HorizontalLayout(new H3("Rechnungspositionen"), addButton)); - grid = new InvoiceItemGrid(invoiceItemRepository); - add(grid); - } - - public void refreshData() { - grid.refreshData(); - } - - private void createNewitem(ClickEvent