parent
013df232ec
commit
d595ab7534
@ -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. |
||||
* <p> |
||||
* e.g. <code>Localization_Properties.getString(resBundle::getString)</code> |
||||
* @param resourceFunction {@link Properties#getProperty(String)} or {@link ResourceBundle#getString(String)} |
||||
* @return |
||||
*/ |
||||
public String getString(UnaryOperator<String> resourceFunction) { |
||||
return resourceFunction.apply(value); |
||||
} |
||||
} |
||||
@ -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<T extends BaseEntity> implements Business<T> { |
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass()); |
||||
|
||||
private final Class<T> itemClass; |
||||
|
||||
private CrudRepository<T, Long> repository; |
||||
|
||||
public AbstractBusiness(CrudRepository<T, Long> repository, Class<T> 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<T> loadAll() { |
||||
List<T> list = new ArrayList<T>(); |
||||
repository.findAll().forEach(list::add); |
||||
logger.trace("Loaded {} of {}", list.size(), itemClass.getSimpleName()); |
||||
return list; |
||||
} |
||||
|
||||
public List<T> loadAll(Predicate<T> predicate) { |
||||
List<T> loadAll = loadAll(); |
||||
List<T> result = loadAll.stream().filter(predicate).collect(Collectors.toList()); |
||||
logger.trace("Filtered {} of {} total {}", result.size(), loadAll.size(), itemClass.getSimpleName()); |
||||
return result; |
||||
} |
||||
|
||||
} |
||||
@ -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<Article> { |
||||
|
||||
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<InvoiceItem> items = invoiceItemRepository.findByArticle(current); |
||||
int size = items.size(); |
||||
return size > 0; |
||||
} |
||||
|
||||
public List<Article> findByUserId(Long id) { |
||||
return articleRepository.findByUserId(id); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,13 @@ |
||||
package de.kreth.invoice.business; |
||||
|
||||
import java.util.List; |
||||
|
||||
public interface Business<T> { |
||||
|
||||
boolean save(T obj); |
||||
|
||||
boolean delete(T obj); |
||||
|
||||
List<T> loadAll(); |
||||
|
||||
} |
||||
@ -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<Invoice> { |
||||
|
||||
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<Invoice> invoices, String pattern) { |
||||
|
||||
Optional<Invoice> 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<Invoice> findByUserId(long userId) { |
||||
return invoiceRepository.findByUserId(userId); |
||||
} |
||||
|
||||
} |
||||
@ -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<InvoiceItem> { |
||||
|
||||
private final InvoiceItemRepository repository; |
||||
|
||||
public InvoiceItemBusiness(InvoiceItemRepository invoiceItemRepository) { |
||||
super(invoiceItemRepository, InvoiceItem.class); |
||||
this.repository = invoiceItemRepository; |
||||
} |
||||
|
||||
public List<InvoiceItem> findByInvoiceIsNull(User user) { |
||||
List<InvoiceItem> findByInvoiceIsNull = repository.findByInvoiceIsNull(); |
||||
for (Iterator<InvoiceItem> iterator = findByInvoiceIsNull.iterator(); iterator.hasNext();) { |
||||
InvoiceItem invoiceItem = iterator.next(); |
||||
if (invoiceItem.getArticle().getUserId() != user.getId()) { |
||||
iterator.remove(); |
||||
} |
||||
} |
||||
return findByInvoiceIsNull; |
||||
} |
||||
|
||||
} |
||||
@ -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<InvoiceItem> { |
||||
|
||||
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<InvoiceItem> items = new ArrayList<>(); |
||||
|
||||
private InvoiceItemRepository repository; |
||||
|
||||
public InvoiceItemGrid(InvoiceItemRepository invoiceItemRepository) { |
||||
|
||||
this.repository = invoiceItemRepository; |
||||
addClassName("bordered"); |
||||
Column<InvoiceItem> titleCol = addColumn(InvoiceItem::getTitle); |
||||
titleCol.setId("Article"); |
||||
titleCol.setHeader("Artikel"); |
||||
|
||||
Column<InvoiceItem> dateColumn = addColumn(new LocalDateTimeRenderer<>(InvoiceItem::getStart, |
||||
DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM))); |
||||
dateColumn.setId("Date"); |
||||
dateColumn.setHeader("Datum"); |
||||
|
||||
Column<InvoiceItem> startColumn = addColumn(new LocalDateTimeRenderer<>(InvoiceItem::getStart, |
||||
DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT))); |
||||
startColumn.setId("Start"); |
||||
startColumn.setHeader("Beginn"); |
||||
|
||||
Column<InvoiceItem> endColumn = addColumn(new LocalDateTimeRenderer<>(InvoiceItem::getEnd, |
||||
DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT))); |
||||
endColumn.setId("Ende"); |
||||
endColumn.setHeader("Ende"); |
||||
|
||||
Column<InvoiceItem> participantColumn = addColumn(InvoiceItem::getParticipants); |
||||
participantColumn.setHeader("Teilnehmer"); |
||||
|
||||
Column<InvoiceItem> 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<InvoiceItem> dataProvider = new ListDataProvider<InvoiceItem>(items); |
||||
setDataProvider(dataProvider); |
||||
dataProvider.addDataProviderListener(new InnerDataProviderListener()); |
||||
} |
||||
|
||||
public void refreshData() { |
||||
items.clear(); |
||||
items.addAll(repository.findByInvoiceIsNull()); |
||||
} |
||||
|
||||
@Override |
||||
public GridSelectionModel<InvoiceItem> setSelectionMode(SelectionMode selectionMode) { |
||||
GridSelectionModel<InvoiceItem> setSelectionMode = super.setSelectionMode(selectionMode); |
||||
// setSelectionMode.addSelectionListener(this::selectionChanged);
|
||||
return setSelectionMode; |
||||
} |
||||
|
||||
// @SuppressWarnings("unchecked")
|
||||
// private void selectionChanged(SelectionEvent<T> event) {
|
||||
// if (event.getAllSelectedItems().isEmpty()) {
|
||||
// updateFooterWith(((ListDataProvider<T>) getDataProvider()).getItems());
|
||||
// } else {
|
||||
// updateFooterWith(event.getAllSelectedItems());
|
||||
// }
|
||||
// }
|
||||
|
||||
protected void internalSetDataProvider(DataProvider<InvoiceItem, ?> dataProvider) { |
||||
|
||||
if (!(dataProvider instanceof ListDataProvider)) { |
||||
throw new IllegalArgumentException("dataProvider must be an instance of ListDataProvider"); |
||||
} |
||||
// super.internalSetDataProvider(dataProvider);
|
||||
dataProvider.addDataProviderListener(new InnerDataProviderListener()); |
||||
updateFooterWith(((ListDataProvider<InvoiceItem>) getDataProvider()).getItems()); |
||||
} |
||||
|
||||
private void updateFooterWith(Collection<InvoiceItem> 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<InvoiceItem> { |
||||
|
||||
private static final long serialVersionUID = -6094992880488082586L; |
||||
|
||||
@Override |
||||
public void onDataChange(DataChangeEvent<InvoiceItem> event) { |
||||
if (event.getSource() == getDataProvider()) { |
||||
@SuppressWarnings("unchecked") |
||||
ListDataProvider<InvoiceItem> provider = (ListDataProvider<InvoiceItem>) getDataProvider(); |
||||
updateFooterWith(provider.getItems()); |
||||
} |
||||
} |
||||
|
||||
} |
||||
} |
||||
@ -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<Button> ev) { |
||||
InvoiceItem item = new InvoiceItem(); |
||||
List<Article> articles = articleRepository.findByUserId(user.getId()); |
||||
InvoiceItemDialog dialog = new InvoiceItemDialog(articles, dlg -> { |
||||
if (dlg.isClosedWithOk()) { |
||||
dlg.writeTo(item); |
||||
invoiceItemRepository.save(item); |
||||
} |
||||
}); |
||||
dialog.readFrom(item); |
||||
dialog.setVisible(); |
||||
|
||||
} |
||||
} |
||||
@ -1,18 +0,0 @@ |
||||
package de.kreth.invoice.components; |
||||
|
||||
import com.vaadin.flow.component.html.H2; |
||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout; |
||||
|
||||
public class InvoiceOverviewComponent extends VerticalLayout { |
||||
|
||||
private static final long serialVersionUID = 1067257075519373200L; |
||||
private final InvoiceGrid grid; |
||||
|
||||
public InvoiceOverviewComponent() { |
||||
|
||||
addClassName("bordered"); |
||||
|
||||
this.grid = new InvoiceGrid(); |
||||
add(new H2("Rechnungen"), grid); |
||||
} |
||||
} |
||||
@ -1,112 +0,0 @@ |
||||
package de.kreth.invoice.data; |
||||
|
||||
import javax.persistence.Column; |
||||
import javax.persistence.DiscriminatorColumn; |
||||
import javax.persistence.DiscriminatorType; |
||||
import javax.persistence.Entity; |
||||
import javax.persistence.Inheritance; |
||||
import javax.persistence.InheritanceType; |
||||
import javax.persistence.Table; |
||||
|
||||
@Entity |
||||
@Table(name = "ADRESS") |
||||
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) |
||||
@DiscriminatorColumn(name = "adress_type", discriminatorType = DiscriminatorType.STRING) |
||||
public class Adress extends BaseEntity { |
||||
|
||||
private static final long serialVersionUID = 8331249424121577387L; |
||||
@Column(nullable = false, length = 255) |
||||
private String adress1; |
||||
@Column(nullable = true, length = 255) |
||||
private String adress2; |
||||
@Column(nullable = true, length = 45) |
||||
private String zip; |
||||
@Column(nullable = true, length = 155) |
||||
private String city; |
||||
|
||||
public String getAdress1() { |
||||
return adress1; |
||||
} |
||||
|
||||
public void setAdress1(String adress1) { |
||||
this.adress1 = adress1; |
||||
} |
||||
|
||||
public String getAdress2() { |
||||
return adress2; |
||||
} |
||||
|
||||
public void setAdress2(String adress2) { |
||||
this.adress2 = adress2; |
||||
} |
||||
|
||||
public String getZip() { |
||||
return zip; |
||||
} |
||||
|
||||
public void setZip(String zip) { |
||||
this.zip = zip; |
||||
} |
||||
|
||||
public String getCity() { |
||||
return city; |
||||
} |
||||
|
||||
public void setCity(String city) { |
||||
this.city = city; |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return "Adress [adress1=" + adress1 + ", adress2=" + adress2 + ", zip=" |
||||
+ zip + ", city=" + city + "]"; |
||||
} |
||||
|
||||
public boolean isValid() { |
||||
return adress1 != null && !adress1.isBlank(); |
||||
} |
||||
|
||||
@Override |
||||
public int hashCode() { |
||||
final int prime = 31; |
||||
int result = 1; |
||||
result = prime * result + ((adress1 == null) ? 0 : adress1.hashCode()); |
||||
result = prime * result + ((adress2 == null) ? 0 : adress2.hashCode()); |
||||
result = prime * result + ((city == null) ? 0 : city.hashCode()); |
||||
result = prime * result + ((zip == null) ? 0 : zip.hashCode()); |
||||
return result; |
||||
} |
||||
|
||||
@Override |
||||
public boolean equals(Object obj) { |
||||
if (this == obj) |
||||
return true; |
||||
if (obj == null) |
||||
return false; |
||||
if (getClass() != obj.getClass()) |
||||
return false; |
||||
Adress other = (Adress) obj; |
||||
if (adress1 == null) { |
||||
if (other.adress1 != null) |
||||
return false; |
||||
} else if (!adress1.equals(other.adress1)) |
||||
return false; |
||||
if (adress2 == null) { |
||||
if (other.adress2 != null) |
||||
return false; |
||||
} else if (!adress2.equals(other.adress2)) |
||||
return false; |
||||
if (city == null) { |
||||
if (other.city != null) |
||||
return false; |
||||
} else if (!city.equals(other.city)) |
||||
return false; |
||||
if (zip == null) { |
||||
if (other.zip != null) |
||||
return false; |
||||
} else if (!zip.equals(other.zip)) |
||||
return false; |
||||
return true; |
||||
} |
||||
|
||||
} |
||||
@ -1,83 +0,0 @@ |
||||
package de.kreth.invoice.data; |
||||
|
||||
import javax.persistence.Column; |
||||
import javax.persistence.DiscriminatorColumn; |
||||
import javax.persistence.DiscriminatorType; |
||||
import javax.persistence.Entity; |
||||
import javax.persistence.Inheritance; |
||||
import javax.persistence.InheritanceType; |
||||
import javax.persistence.Table; |
||||
|
||||
@Entity |
||||
@Table(name = "BANKING_CONNECTION") |
||||
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) |
||||
@DiscriminatorColumn(name = "owner_type", discriminatorType = DiscriminatorType.STRING) |
||||
public class BankingConnection extends BaseEntity { |
||||
|
||||
private static final long serialVersionUID = -6168631092559375156L; |
||||
@Column(nullable = false, length = 150) |
||||
private String bankName; |
||||
@Column(nullable = false, length = 150) |
||||
private String iban; |
||||
@Column(nullable = true, length = 150) |
||||
private String bic; |
||||
|
||||
public String getBankName() { |
||||
return bankName; |
||||
} |
||||
|
||||
public void setBankName(String bankName) { |
||||
this.bankName = bankName; |
||||
} |
||||
|
||||
public String getIban() { |
||||
return iban; |
||||
} |
||||
|
||||
public void setIban(String iban) { |
||||
this.iban = iban; |
||||
} |
||||
|
||||
public String getBic() { |
||||
return bic; |
||||
} |
||||
|
||||
public void setBic(String bic) { |
||||
this.bic = bic; |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return iban; |
||||
} |
||||
|
||||
public boolean isValid() { |
||||
return iban != null; |
||||
} |
||||
|
||||
@Override |
||||
public int hashCode() { |
||||
final int prime = 31; |
||||
int result = 1; |
||||
result = prime * result + ((iban == null) ? 0 : iban.hashCode()); |
||||
return result; |
||||
} |
||||
|
||||
@Override |
||||
public boolean equals(Object obj) { |
||||
if (this == obj) |
||||
return true; |
||||
if (obj == null) |
||||
return false; |
||||
if (getClass() != obj.getClass()) |
||||
return false; |
||||
BankingConnection other = (BankingConnection) obj; |
||||
if (iban == null) { |
||||
if (other.iban != null) |
||||
return false; |
||||
} else if (!iban.equals(other.iban)) |
||||
return false; |
||||
return true; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,12 @@ |
||||
package de.kreth.invoice.persistence; |
||||
|
||||
import java.util.List; |
||||
|
||||
import org.springframework.data.repository.CrudRepository; |
||||
|
||||
import de.kreth.invoice.data.Invoice; |
||||
|
||||
public interface InvoiceRepository extends CrudRepository<Invoice, Long> { |
||||
|
||||
List<Invoice> findByUserId(long userId); |
||||
} |
||||
@ -0,0 +1,7 @@ |
||||
package de.kreth.invoice.views; |
||||
|
||||
public enum DataFormat { |
||||
DEBUG, |
||||
MEDIUM, |
||||
SMALL |
||||
} |
||||
@ -0,0 +1,34 @@ |
||||
package de.kreth.invoice.views; |
||||
|
||||
import com.vaadin.flow.component.Text; |
||||
import com.vaadin.flow.component.button.Button; |
||||
import com.vaadin.flow.component.button.ButtonVariant; |
||||
import com.vaadin.flow.component.html.Div; |
||||
import com.vaadin.flow.component.icon.Icon; |
||||
import com.vaadin.flow.component.notification.Notification; |
||||
import com.vaadin.flow.component.notification.NotificationVariant; |
||||
import com.vaadin.flow.component.orderedlayout.FlexComponent.Alignment; |
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout; |
||||
|
||||
public final class ErrorNotification { |
||||
|
||||
public static void showError(String message) { |
||||
Notification notification = new Notification(); |
||||
notification.addThemeVariants(NotificationVariant.LUMO_ERROR); |
||||
|
||||
Div text = new Div(new Text(message)); |
||||
|
||||
Button closeButton = new Button(new Icon("lumo", "cross")); |
||||
closeButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY_INLINE); |
||||
closeButton.getElement().setAttribute("aria-label", "Close"); |
||||
closeButton.addClickListener(event -> { |
||||
notification.close(); |
||||
}); |
||||
|
||||
HorizontalLayout layout = new HorizontalLayout(text, closeButton); |
||||
layout.setAlignItems(Alignment.CENTER); |
||||
|
||||
notification.add(layout); |
||||
notification.open(); |
||||
} |
||||
} |
||||
@ -0,0 +1,78 @@ |
||||
package de.kreth.invoice.views; |
||||
|
||||
import java.io.IOException; |
||||
import java.net.URL; |
||||
import java.util.Properties; |
||||
|
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
|
||||
import com.vaadin.flow.component.Text; |
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout; |
||||
|
||||
public class FooterComponent extends HorizontalLayout { |
||||
|
||||
private static final long serialVersionUID = 4845822203421115202L; |
||||
|
||||
private static final Logger LOGGER = LoggerFactory |
||||
.getLogger(FooterComponent.class); |
||||
|
||||
private static final Properties VERSION = new Properties(); |
||||
static { |
||||
String path = "/../version.properties"; |
||||
try { |
||||
recursivelyLoadPropFromPath(FooterComponent.class, path, 0); |
||||
} catch (Exception e) { |
||||
LOGGER.error("Error loading version properties file = " + path |
||||
+ ", cause: " + e.getMessage()); |
||||
} |
||||
} |
||||
|
||||
private static void recursivelyLoadPropFromPath( |
||||
Class<FooterComponent> thisClass, String path, int level) |
||||
throws IOException { |
||||
|
||||
URL resource = thisClass.getResource(path); |
||||
if (resource != null) { |
||||
VERSION.load(resource.openStream()); |
||||
LOGGER.info("Successfully loaded version info from " + resource); |
||||
} else if (level < 4) { |
||||
recursivelyLoadPropFromPath(thisClass, "/.." + path, level + 1); |
||||
} else { |
||||
throw new IOException("File not Found in any subdir of " + path); |
||||
} |
||||
|
||||
} |
||||
|
||||
public FooterComponent() { |
||||
|
||||
Text copyright = new Text("© Markus Kreth"); |
||||
add(copyright); |
||||
|
||||
// if (propertiesLoaded()) {
|
||||
//
|
||||
// String dateTimeProperty = Version_Properties.BUILD_DATETIME.getString(VERSION::getProperty);
|
||||
// SimpleDateFormat sourceFormat = new SimpleDateFormat(
|
||||
// "yyyy-MM-dd HH:mm:ss");
|
||||
// sourceFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
// try {
|
||||
// Date date = sourceFormat.parse(dateTimeProperty);
|
||||
// dateTimeProperty = DateFormat.getDateTimeInstance(
|
||||
// DateFormat.MEDIUM, DateFormat.SHORT).format(date);
|
||||
// } catch (ParseException e) {
|
||||
// LOGGER.warn(
|
||||
// "Unable to parse dateTimeProperty=" + dateTimeProperty,
|
||||
// e);
|
||||
// }
|
||||
// Label vers = new Label(
|
||||
// "Version: " + Version_Properties.PROJECT_VERSION.getString(VERSION::getProperty));
|
||||
// Label buildTime = new Label("Build: " + dateTimeProperty);
|
||||
// adds(vers, buildTime);
|
||||
// }
|
||||
} |
||||
|
||||
// private boolean propertiesLoaded() {
|
||||
// return Version_Properties.BUILD_DATETIME.getString(VERSION::getProperty) != null
|
||||
// && Version_Properties.BUILD_DATETIME.getString(VERSION::getProperty).trim().isEmpty() == false;
|
||||
// }
|
||||
} |
||||
@ -0,0 +1,69 @@ |
||||
package de.kreth.invoice.views; |
||||
|
||||
import java.math.BigDecimal; |
||||
import java.text.NumberFormat; |
||||
import java.text.ParseException; |
||||
import java.util.Locale; |
||||
|
||||
import com.vaadin.flow.data.binder.ErrorMessageProvider; |
||||
import com.vaadin.flow.data.binder.Result; |
||||
import com.vaadin.flow.data.binder.ValueContext; |
||||
import com.vaadin.flow.data.converter.AbstractStringToNumberConverter; |
||||
|
||||
public class PriceConverter extends AbstractStringToNumberConverter<BigDecimal> { |
||||
|
||||
private static final long serialVersionUID = -2627857228509459543L; |
||||
private final NumberFormat formatter; |
||||
private final NumberFormat backupFormat; |
||||
|
||||
public PriceConverter(BigDecimal emptyValue, ErrorMessageProvider errorMessageProvider) { |
||||
this(emptyValue, errorMessageProvider, Locale.getDefault()); |
||||
} |
||||
|
||||
public PriceConverter(BigDecimal emptyValue, String errorMessage) { |
||||
this(emptyValue, errorMessage, Locale.getDefault()); |
||||
} |
||||
|
||||
public PriceConverter(BigDecimal emptyValue, ErrorMessageProvider errorMessageProvider, Locale inLocale) { |
||||
super(emptyValue, errorMessageProvider); |
||||
formatter = NumberFormat.getCurrencyInstance(inLocale); |
||||
backupFormat = NumberFormat.getInstance(inLocale); |
||||
} |
||||
|
||||
public PriceConverter(BigDecimal emptyValue, String errorMessage, Locale inLocale) { |
||||
super(emptyValue, errorMessage); |
||||
formatter = NumberFormat.getCurrencyInstance(inLocale); |
||||
backupFormat = NumberFormat.getInstance(inLocale); |
||||
} |
||||
|
||||
@Override |
||||
public Result<BigDecimal> convertToModel(String value, ValueContext context) { |
||||
if (value == null) { |
||||
return Result.ok(BigDecimal.ZERO); |
||||
} |
||||
try { |
||||
Number numValue = formatter.parse(value); |
||||
return Result.ok(BigDecimal.valueOf(numValue.doubleValue())); |
||||
} catch (ParseException e) { |
||||
try { |
||||
Number numValue = backupFormat.parse(value.trim()); |
||||
return Result.ok(BigDecimal.valueOf(numValue.doubleValue())); |
||||
} catch (ParseException e2) { |
||||
return Result.error(e.getMessage()); |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
protected NumberFormat getFormat(Locale locale) { |
||||
return formatter; |
||||
} |
||||
|
||||
@Override |
||||
public String convertToPresentation(BigDecimal value, ValueContext context) { |
||||
if (value == null) { |
||||
return null; |
||||
} |
||||
return formatter.format(value.doubleValue()); |
||||
} |
||||
} |
||||
@ -0,0 +1,269 @@ |
||||
package de.kreth.invoice.views.article; |
||||
|
||||
import static de.kreth.invoice.Application.getString; |
||||
|
||||
import java.math.BigDecimal; |
||||
import java.util.List; |
||||
import java.util.Optional; |
||||
|
||||
import com.vaadin.flow.component.Unit; |
||||
import com.vaadin.flow.component.button.Button; |
||||
import com.vaadin.flow.component.checkbox.Checkbox; |
||||
import com.vaadin.flow.component.dialog.Dialog; |
||||
import com.vaadin.flow.component.formlayout.FormLayout; |
||||
import com.vaadin.flow.component.grid.Grid; |
||||
import com.vaadin.flow.component.textfield.TextField; |
||||
import com.vaadin.flow.data.binder.Binder; |
||||
|
||||
import de.kreth.invoice.Localization_Properties; |
||||
import de.kreth.invoice.business.ArticleBusiness; |
||||
import de.kreth.invoice.data.Article; |
||||
import de.kreth.invoice.data.ReportLicense; |
||||
import de.kreth.invoice.data.User; |
||||
import de.kreth.invoice.views.PriceConverter; |
||||
|
||||
public class ArticleDialog extends Dialog { |
||||
|
||||
private static final long serialVersionUID = 2516636187686436452L; |
||||
|
||||
private final ArticleBusiness business; |
||||
|
||||
private TextField title; |
||||
|
||||
private TextField pricePerHour; |
||||
|
||||
private TextField description; |
||||
|
||||
private Grid<Article> articleGrid; |
||||
|
||||
private User user; |
||||
|
||||
private Article current = null; |
||||
|
||||
private Binder<Article> binder; |
||||
|
||||
private Button discartButton; |
||||
|
||||
private Button storeButton; |
||||
|
||||
private Checkbox isTrainer; |
||||
|
||||
private Button deleteButton; |
||||
|
||||
public ArticleDialog(ArticleBusiness articleBusiness, User user) { |
||||
this.business = articleBusiness; |
||||
title = new TextField(); |
||||
title.setLabel("Artikel"); |
||||
pricePerHour = new TextField(); |
||||
pricePerHour.setLabel("Preis Einzel"); |
||||
|
||||
description = new TextField(); |
||||
description.setLabel("Beschreibung"); |
||||
|
||||
isTrainer = new Checkbox("als Trainer", false); |
||||
|
||||
FormLayout contentValues = new FormLayout(); |
||||
contentValues.add(title, pricePerHour, description, isTrainer); |
||||
|
||||
Button addArticle = new Button("Artikel hinzufügen"); |
||||
|
||||
addArticle.addClickListener(ev -> { |
||||
current = createNewArticle(); |
||||
binder.setBean(current); |
||||
toggleEditFields(); |
||||
}); |
||||
|
||||
discartButton = new Button("Abbrechen", e -> { |
||||
binder.readBean(null); |
||||
current = null; |
||||
toggleEditFields(); |
||||
}); |
||||
discartButton.setVisible(false); |
||||
|
||||
storeButton = new Button("Speichern", e -> { |
||||
if (binder.validate().isOk()) { |
||||
business.save(current); |
||||
reloadItems(); |
||||
toggleEditFields(); |
||||
} |
||||
}); |
||||
|
||||
Button closeButton = new Button(getString(Localization_Properties.LABEL_CLOSE), |
||||
ev -> close()); |
||||
|
||||
deleteButton = new Button(getString(Localization_Properties.LABEL_DELETE), ev -> { |
||||
business.delete(current); |
||||
current = null; |
||||
binder.setBean(null); |
||||
reloadItems(); |
||||
toggleEditFields(); |
||||
}); |
||||
|
||||
FormLayout contentButtons = new FormLayout(); |
||||
contentButtons.add(storeButton, discartButton, addArticle, deleteButton, closeButton); |
||||
|
||||
setupArticleGrid(); |
||||
|
||||
setupBinder(); |
||||
|
||||
FormLayout content = new FormLayout(); |
||||
content.add(contentValues, articleGrid, contentButtons); |
||||
add(content); |
||||
setWidth(80.0f, Unit.PERCENTAGE); |
||||
setModal(true); |
||||
setUser(user); |
||||
} |
||||
|
||||
private void setUser(User user) { |
||||
this.user = user; |
||||
if (user != null) { |
||||
List<Article> loadAll = reloadItems(); |
||||
|
||||
if (loadAll.isEmpty()) { |
||||
current = createNewArticle(); |
||||
} else { |
||||
current = loadAll.get(0); |
||||
articleGrid.select(current); |
||||
} |
||||
|
||||
binder.setBean(current); |
||||
toggleEditFields(); |
||||
} else { |
||||
close(); |
||||
} |
||||
} |
||||
|
||||
private void toggleEditFields() { |
||||
if (current == null || binder.getBean() == null) { |
||||
title.setEnabled(false); |
||||
pricePerHour.setEnabled(false); |
||||
description.setEnabled(false); |
||||
isTrainer.setEnabled(false); |
||||
discartButton.setVisible(false); |
||||
storeButton.setVisible(false); |
||||
deleteButton.setVisible(false); |
||||
} else { |
||||
discartButton.setVisible(true); |
||||
storeButton.setVisible(true); |
||||
deleteButton.setVisible(true); |
||||
storeButton.setEnabled(binder.validate().isOk()); |
||||
title.setEnabled(true); |
||||
pricePerHour.setEnabled(true); |
||||
description.setEnabled(true); |
||||
isTrainer.setEnabled(true); |
||||
} |
||||
} |
||||
|
||||
private Article createNewArticle() { |
||||
Article article = new Article(); |
||||
article.setTitle(""); |
||||
article.setDescription(""); |
||||
article.setPricePerHour(BigDecimal.ZERO); |
||||
article.setReport(ReportLicense.ASSISTANT.getRessource()); |
||||
if (user != null) { |
||||
article.setUserId(user.getId()); |
||||
} else { |
||||
article.setUserId(-1); |
||||
} |
||||
return article; |
||||
} |
||||
|
||||
private List<Article> reloadItems() { |
||||
List<Article> loadAll = business.loadAll(a -> a.getUserId() == user.getId()); |
||||
articleGrid.setItems(loadAll); |
||||
return loadAll; |
||||
} |
||||
|
||||
private void setupBinder() { |
||||
binder = new Binder<>(Article.class); |
||||
binder.forField(title).asRequired().withNullRepresentation("").bind(Article::getTitle, Article::setTitle); |
||||
PriceConverter converter = new PriceConverter(BigDecimal.ZERO, "Ungültiger Preis"); |
||||
|
||||
binder.forField(pricePerHour).asRequired() |
||||
.withNullRepresentation("") |
||||
.withConverter(converter) |
||||
.withValidator(t -> t != null && t.doubleValue() > 0, "Der Preis muss größer 0 sein!") |
||||
.bind(Article::getPricePerHour, Article::setPricePerHour); |
||||
|
||||
binder.forField(description).withNullRepresentation("").bind(Article::getDescription, Article::setDescription); |
||||
|
||||
binder.forField(isTrainer).bind(this::reportToCheckbox, this::checkboxToReportLicense); |
||||
|
||||
binder.addValueChangeListener(changeEv -> { |
||||
toggleEditFields(); |
||||
}); |
||||
} |
||||
|
||||
private boolean reportToCheckbox(Article source) { |
||||
if (source == null || source.getReport() == null) { |
||||
return false; |
||||
} |
||||
return source.getReport().contentEquals(ReportLicense.TRAINER.getRessource()); |
||||
} |
||||
|
||||
private void checkboxToReportLicense(Article bean, Boolean fieldvalue) { |
||||
if (bean != null && fieldvalue != null) { |
||||
if (fieldvalue) { |
||||
bean.setReport(ReportLicense.TRAINER.getRessource()); |
||||
} else { |
||||
bean.setReport(ReportLicense.ASSISTANT.getRessource()); |
||||
} |
||||
} |
||||
} |
||||
|
||||
private void setupArticleGrid() { |
||||
articleGrid = new Grid<>(); |
||||
|
||||
articleGrid.addColumn(Article::getTitle).setHeader("Titel"); |
||||
|
||||
// ValueProvider<BigDecimal, String> currencyProvider = new ValueProvider<BigDecimal, String>() {
|
||||
// private static final long serialVersionUID = -6305095230785149948L;
|
||||
// private final NumberFormat formatter = NumberFormat.getCurrencyInstance();
|
||||
//
|
||||
// @Override
|
||||
// public String apply(BigDecimal source) {
|
||||
// return formatter.format(source.doubleValue());
|
||||
// }
|
||||
// };
|
||||
// articleGrid.addColumn(Article::getPricePerHour, currencyProvider);
|
||||
articleGrid.addColumn(Article::getDescription) |
||||
.setHeader("Beschreibung"); |
||||
articleGrid.addColumn(this::reportToCheckbox) |
||||
.setHeader("Report"); |
||||
|
||||
articleGrid.addSelectionListener(sel -> { |
||||
if (!binder.hasChanges()) { |
||||
|
||||
Optional<Article> selected = sel.getFirstSelectedItem(); |
||||
if (selected.isPresent()) { |
||||
current = selected.get(); |
||||
binder.setBean(current); |
||||
|
||||
boolean hasItems = business.hasInvoiceItem(current); |
||||
deleteButton.setEnabled(!hasItems); |
||||
|
||||
if (hasItems) { |
||||
// deleteButton.setT
|
||||
} else { |
||||
// deleteButton.setDescription(
|
||||
// "Es existieren Einträge zu diesem Artikel. Er kann nicht gelöscht werden.");
|
||||
} |
||||
|
||||
// if (hasInvoice) {
|
||||
// String errorMessage = getString(MESSAGE_ARTICLE_ERROR_INVOICEEXISTS);
|
||||
// title.setDescription(errorMessage);
|
||||
// pricePerHour.setDescription(errorMessage);
|
||||
// description.setDescription(errorMessage);
|
||||
//
|
||||
// } else {
|
||||
// title.setDescription("");
|
||||
// pricePerHour.setDescription("");
|
||||
// description.setDescription("");
|
||||
// }
|
||||
} |
||||
} |
||||
toggleEditFields(); |
||||
}); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,30 @@ |
||||
package de.kreth.invoice.views.article; |
||||
|
||||
import java.text.NumberFormat; |
||||
import java.util.ResourceBundle; |
||||
|
||||
import com.vaadin.flow.component.grid.Grid; |
||||
import com.vaadin.flow.data.renderer.NumberRenderer; |
||||
|
||||
import de.kreth.invoice.data.Article; |
||||
|
||||
public class ArticleGrid extends Grid<Article> { |
||||
|
||||
private static final long serialVersionUID = 4063720728031721955L; |
||||
|
||||
public ArticleGrid(ResourceBundle resBundle) { |
||||
|
||||
addClassName("bordered"); |
||||
|
||||
addColumn(Article::getTitle).setHeader("Titel"); |
||||
|
||||
NumberRenderer<Article> re = new NumberRenderer<Article>(a -> a.getPricePerHour().doubleValue(), |
||||
NumberFormat.getCurrencyInstance()); |
||||
|
||||
addColumn(re) |
||||
.setHeader("Preis"); |
||||
addColumn(Article::getDescription).setHeader("Beschreibung"); |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,255 @@ |
||||
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_OPEN; |
||||
import static de.kreth.invoice.Localization_Properties.LABEL_PREVIEW; |
||||
import static de.kreth.invoice.Localization_Properties.LABEL_STORE; |
||||
|
||||
import java.io.File; |
||||
import java.io.FileInputStream; |
||||
import java.io.IOException; |
||||
import java.io.OutputStream; |
||||
import java.io.PipedInputStream; |
||||
import java.io.PipedOutputStream; |
||||
import java.nio.file.Files; |
||||
import java.util.HashMap; |
||||
import java.util.concurrent.ExecutorService; |
||||
import java.util.concurrent.Executors; |
||||
|
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
|
||||
import com.vaadin.flow.component.ClickEvent; |
||||
import com.vaadin.flow.component.Component; |
||||
import com.vaadin.flow.component.ComponentEventListener; |
||||
import com.vaadin.flow.component.HasValue.ValueChangeEvent; |
||||
import com.vaadin.flow.component.Unit; |
||||
import com.vaadin.flow.component.button.Button; |
||||
import com.vaadin.flow.component.checkbox.Checkbox; |
||||
import com.vaadin.flow.component.datepicker.DatePicker; |
||||
import com.vaadin.flow.component.dialog.Dialog; |
||||
import com.vaadin.flow.component.html.Anchor; |
||||
import com.vaadin.flow.component.orderedlayout.FlexComponent.Alignment; |
||||
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.dom.Element; |
||||
import com.vaadin.flow.server.StreamResource; |
||||
import com.vaadin.flow.server.StreamResourceWriter; |
||||
import com.vaadin.flow.server.VaadinSession; |
||||
import com.vaadin.flow.shared.Registration; |
||||
|
||||
import de.kreth.invoice.data.Invoice; |
||||
import de.kreth.invoice.data.InvoiceItem; |
||||
import de.kreth.invoice.report.InvoiceReportSource; |
||||
import de.kreth.invoice.report.Signature; |
||||
import de.kreth.invoice.views.invoiceitem.InvoiceItemGrid; |
||||
import net.sf.jasperreports.engine.JRException; |
||||
import net.sf.jasperreports.engine.JasperCompileManager; |
||||
import net.sf.jasperreports.engine.JasperExportManager; |
||||
import net.sf.jasperreports.engine.JasperFillManager; |
||||
import net.sf.jasperreports.engine.JasperPrint; |
||||
import net.sf.jasperreports.engine.JasperReport; |
||||
|
||||
public class InvoiceDialog extends Dialog { |
||||
|
||||
private static final long serialVersionUID = -8997281625128779760L; |
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(InvoiceDialog.class); |
||||
|
||||
public enum InvoiceMode { |
||||
CREATE, |
||||
VIEW_ONLY |
||||
} |
||||
|
||||
private TextField invoiceNo; |
||||
|
||||
private DatePicker invoiceDate; |
||||
|
||||
private InvoiceItemGrid<InvoiceItem> itemGrid; |
||||
|
||||
private Button okButton; |
||||
|
||||
private Invoice invoice; |
||||
|
||||
private Signature signature; |
||||
|
||||
private Checkbox printSignature; |
||||
|
||||
/** |
||||
* Initializes the Dialog with an empty {@link Invoice}. |
||||
* <p> |
||||
* Be sure to set an {@link Invoice} with at least 1 Item with |
||||
* {@link #setInvoice(Invoice)}. |
||||
* <p> |
||||
* |
||||
* @param resBundle |
||||
* @param pdfOpenLabel |
||||
*/ |
||||
public InvoiceDialog(InvoiceMode pdfOpenLabel) { |
||||
setWidth(200, Unit.EM); |
||||
|
||||
invoiceNo = new TextField(); |
||||
invoiceNo.setLabel("Rechn.Nr."); |
||||
if (InvoiceMode.VIEW_ONLY == pdfOpenLabel) { |
||||
invoiceNo.setReadOnly(true); |
||||
} else { |
||||
invoiceNo.addValueChangeListener(this::updateInvoiceNo); |
||||
} |
||||
|
||||
invoiceDate = new DatePicker(); |
||||
invoiceDate.setLabel("Rechnung Datum"); |
||||
invoiceDate.setEnabled(false); |
||||
|
||||
itemGrid = new InvoiceItemGrid<>(); |
||||
|
||||
printSignature = new Checkbox(getString(CAPTION_INVOICE_PRINTSIGNATURE)); |
||||
if (InvoiceMode.VIEW_ONLY == pdfOpenLabel) { |
||||
printSignature.setEnabled(false); |
||||
} |
||||
|
||||
printSignature.addValueChangeListener(ev -> { |
||||
if (printSignature.getValue() == Boolean.TRUE) { |
||||
invoice.setSignImagePath(signature.getSignatureUrl().getAbsolutePath()); |
||||
} else { |
||||
invoice.setSignImagePath(null); |
||||
} |
||||
}); |
||||
okButton = new Button(getString(LABEL_STORE), ev -> close()); |
||||
Button cancel = new Button(getString(LABEL_CANCEL), ev -> close()); |
||||
|
||||
String caption; |
||||
if (pdfOpenLabel == InvoiceMode.VIEW_ONLY) { |
||||
caption = getString(LABEL_OPEN); |
||||
} else { |
||||
caption = getString(LABEL_PREVIEW); |
||||
} |
||||
Button previewButton = new Button(caption, this::showPdf); |
||||
|
||||
HorizontalLayout btnLayout = new HorizontalLayout(); |
||||
btnLayout.add(okButton, cancel, previewButton); |
||||
|
||||
VerticalLayout vLayout = new VerticalLayout(); |
||||
|
||||
vLayout.add(invoiceNo, invoiceDate, itemGrid, printSignature); |
||||
|
||||
vLayout.add(btnLayout); |
||||
vLayout.setHorizontalComponentAlignment(Alignment.BASELINE, btnLayout); |
||||
|
||||
add(vLayout); |
||||
} |
||||
|
||||
private void updateInvoiceNo(ValueChangeEvent<String> ev) { |
||||
if (invoice != null) { |
||||
invoice.setInvoiceId(ev.getValue()); |
||||
} |
||||
} |
||||
|
||||
private void showPdf(ClickEvent<Button> ev) { |
||||
try { |
||||
JasperPrint print = createJasperPrint(); |
||||
LOGGER.debug("Created JasperPrint"); |
||||
showInWebWindow(print, ev); |
||||
} catch (JRException | IOException e) { |
||||
throw new RuntimeException(e); |
||||
} |
||||
} |
||||
|
||||
private void showInWebWindow(JasperPrint print, ClickEvent<Button> ev) throws IOException, JRException { |
||||
|
||||
Dialog window = new Dialog(); |
||||
// window.setCaption("View PDF");
|
||||
Component e = createEmbedded(print); |
||||
window.add(e); |
||||
window.setModal(true); |
||||
// window.setSizeFull();
|
||||
window.setWidth("95%"); |
||||
window.setHeight("99%"); |
||||
window.open(); |
||||
} |
||||
|
||||
private Component createEmbedded(JasperPrint print) throws IOException, JRException { |
||||
|
||||
PipedInputStream inFrame = new PipedInputStream(); |
||||
|
||||
final StreamResource resourceFrame = new StreamResource("invoice.pdf", () -> inFrame); |
||||
resourceFrame.setHeader("Content-Type", "application/pdf"); |
||||
|
||||
Element image = new Element("object"); |
||||
image.setAttribute("type", "application/pdf"); |
||||
image.getStyle().set("display", "block"); |
||||
image.setAttribute("data", resourceFrame); |
||||
image.setAttribute("width", "100%"); |
||||
image.setAttribute("height", "100%"); |
||||
|
||||
ExecutorService exec = Executors.newSingleThreadExecutor(); |
||||
File outFile = Files.createTempFile("invoice", ".pdf").toFile(); |
||||
exec.execute(() -> { |
||||
try (PipedOutputStream out1 = new PipedOutputStream(inFrame)) { |
||||
JasperExportManager.exportReportToPdfStream(print, out1); |
||||
} catch (JRException | IOException e) { |
||||
LOGGER.error("Error exporting Report to Browser Window", e); |
||||
} |
||||
}); |
||||
exec.shutdown(); |
||||
|
||||
JasperExportManager.exportReportToPdfFile(print, outFile.getAbsolutePath()); |
||||
LOGGER.info("PDF File written: {}", outFile.getAbsolutePath()); |
||||
|
||||
StreamResourceWriter inStream = new StreamResourceWriter() { |
||||
|
||||
private static final long serialVersionUID = -3847642428860682957L; |
||||
|
||||
@Override |
||||
public void accept(OutputStream stream, VaadinSession session) throws IOException { |
||||
try (FileInputStream in = new FileInputStream(outFile)) { |
||||
in.transferTo(stream); |
||||
} |
||||
} |
||||
}; |
||||
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.")); |
||||
VerticalLayout layout = new VerticalLayout(); |
||||
layout.add(link); |
||||
layout.getElement().appendChild(image); |
||||
layout.setSizeFull(); |
||||
return layout; |
||||
} |
||||
|
||||
private JasperPrint createJasperPrint() throws JRException { |
||||
InvoiceReportSource source = new InvoiceReportSource(); |
||||
source.setInvoice(invoice); |
||||
JasperReport report = JasperCompileManager |
||||
.compileReport(getClass().getResourceAsStream(invoice.getReportRessource())); |
||||
return JasperFillManager.fillReport(report, new HashMap<>(), source); |
||||
} |
||||
|
||||
public Registration addOkClickListener(ComponentEventListener<ClickEvent<Button>> listener) { |
||||
return okButton.addClickListener(listener); |
||||
} |
||||
|
||||
public void setInvoice(Invoice invoice) { |
||||
this.invoice = invoice; |
||||
signature = new Signature(invoice.getUser()); |
||||
invoiceNo.setValue(invoice.getInvoiceId()); |
||||
invoiceDate.setValue(invoice.getInvoiceDate().toLocalDate()); |
||||
itemGrid.setItems(invoice.getItems()); |
||||
printSignature.setVisible(signature.isSignatureImageExists()); |
||||
if (printSignature.isEnabled()) { |
||||
printSignature.setValue(signature.isSignatureImageExists()); |
||||
} else { |
||||
printSignature.setValue(invoice.getSignImagePath() != null); |
||||
} |
||||
} |
||||
|
||||
public void setOkVisible(boolean visible) { |
||||
okButton.setVisible(visible); |
||||
} |
||||
|
||||
} |
||||
@ -1,4 +1,4 @@ |
||||
package de.kreth.invoice.components; |
||||
package de.kreth.invoice.views.invoiceitem; |
||||
|
||||
import java.time.LocalDate; |
||||
import java.time.LocalDateTime; |
||||
@ -0,0 +1,179 @@ |
||||
package de.kreth.invoice.views.invoiceitem; |
||||
|
||||
import static de.kreth.invoice.Application.getString; |
||||
import static de.kreth.invoice.Localization_Properties.CAPTION_INVOICEITEM_DATE; |
||||
import static de.kreth.invoice.Localization_Properties.CAPTION_INVOICEITEM_END; |
||||
import static de.kreth.invoice.Localization_Properties.CAPTION_INVOICEITEM_NAME; |
||||
import static de.kreth.invoice.Localization_Properties.CAPTION_INVOICEITEM_PARTICIPANTS; |
||||
import static de.kreth.invoice.Localization_Properties.CAPTION_INVOICEITEM_START; |
||||
import static de.kreth.invoice.Localization_Properties.CAPTION_INVOICEITEM_SUMPRICE; |
||||
|
||||
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.component.grid.GridSortOrder; |
||||
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 com.vaadin.flow.data.selection.SelectionEvent; |
||||
|
||||
import de.kreth.invoice.data.InvoiceItem; |
||||
|
||||
public class InvoiceItemGrid<T extends InvoiceItem> extends Grid<T> { |
||||
|
||||
private static final long serialVersionUID = -8653320112619816426L; |
||||
|
||||
private final DateTimeFormatter ofLocalizedDateFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM); |
||||
|
||||
private final List<T> items = new ArrayList<>(); |
||||
private FooterCell priceSumCell; |
||||
|
||||
private FooterCell countCell; |
||||
|
||||
private FooterCell dateSpan; |
||||
|
||||
public InvoiceItemGrid() { |
||||
|
||||
Column<T> articleColumn = addColumn(InvoiceItem::getTitle) |
||||
.setHeader(getString(CAPTION_INVOICEITEM_NAME)); |
||||
|
||||
LocalDateTimeRenderer<T> renderer = new LocalDateTimeRenderer<>(InvoiceItem::getStart, |
||||
DateTimeFormatter.ofPattern("EEE, dd.MM.yyyy")); |
||||
Column<T> dateColumn = addColumn(renderer).setHeader(getString(CAPTION_INVOICEITEM_DATE)); |
||||
dateColumn.setId("Date"); |
||||
|
||||
Column<T> startColumn = addColumn(new LocalDateTimeRenderer<>(InvoiceItem::getStart, |
||||
DateTimeFormatter.ofPattern("HH:mm"))) |
||||
.setHeader(getString(CAPTION_INVOICEITEM_START)); |
||||
|
||||
Column<T> endColumn = addColumn(new LocalDateTimeRenderer<>(InvoiceItem::getEnd, |
||||
DateTimeFormatter.ofPattern("HH:mm"))) |
||||
.setHeader(getString(CAPTION_INVOICEITEM_END)); |
||||
addColumn(InvoiceItem::getParticipants) |
||||
.setHeader(getString(CAPTION_INVOICEITEM_PARTICIPANTS)); |
||||
|
||||
Column<T> priceColumn = addColumn( |
||||
new NumberRenderer<>(InvoiceItem::getSumPrice, NumberFormat.getCurrencyInstance())) |
||||
.setHeader(getString(CAPTION_INVOICEITEM_SUMPRICE)); |
||||
|
||||
FooterRow footer = appendFooterRow(); |
||||
footer = appendFooterRow(); |
||||
|
||||
priceSumCell = footer.getCell(priceColumn); |
||||
dateSpan = footer.join(dateColumn, startColumn, endColumn); |
||||
countCell = footer.getCell(articleColumn); |
||||
|
||||
addSelectionListener(this::selectionChanged); |
||||
|
||||
setItems(DataProvider.ofCollection(items)); |
||||
this.sort(GridSortOrder.asc(dateColumn).thenAsc(startColumn).build()); |
||||
|
||||
getDataProvider().addDataProviderListener(new InnerDataProviderListener()); |
||||
} |
||||
|
||||
@Override |
||||
public ListDataProvider<T> getDataProvider() { |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
ListDataProvider<T> dataProvider = (ListDataProvider<T>) super.getDataProvider(); |
||||
return dataProvider; |
||||
} |
||||
|
||||
public void setItems(List<T> collection) { |
||||
items.clear(); |
||||
items.addAll(collection); |
||||
getDataProvider().refreshAll(); |
||||
} |
||||
|
||||
@Override |
||||
public GridSelectionModel<T> setSelectionMode(SelectionMode selectionMode) { |
||||
GridSelectionModel<T> setSelectionMode = super.setSelectionMode(selectionMode); |
||||
setSelectionMode.addSelectionListener(this::selectionChanged); |
||||
return setSelectionMode; |
||||
} |
||||
|
||||
private void selectionChanged(SelectionEvent<Grid<T>, T> event) { |
||||
if (event.getAllSelectedItems().isEmpty()) { |
||||
updateFooterWith(getDataProvider().getItems()); |
||||
} else { |
||||
updateFooterWith(event.getAllSelectedItems()); |
||||
} |
||||
} |
||||
//
|
||||
// @SuppressWarnings("unchecked")
|
||||
// @Override
|
||||
// protected void internalSetDataProvider(DataProvider<T, ?> dataProvider) {
|
||||
//
|
||||
// if (!(dataProvider instanceof ListDataProvider)) {
|
||||
// throw new IllegalArgumentException("dataProvider must be an instance of ListDataProvider");
|
||||
// }
|
||||
// super.internalSetDataProvider(dataProvider);
|
||||
// dataProvider.addDataProviderListener(new InnerDataProviderListener());
|
||||
// updateFooterWith(((ListDataProvider<T>) getDataProvider()).getItems());
|
||||
// }
|
||||
|
||||
private void updateFooterWith(Collection<T> selected) { |
||||
BigDecimal priceSum = BigDecimal.ZERO; |
||||
LocalDate min = null; |
||||
LocalDate max = null; |
||||
|
||||
for (T 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<T> { |
||||
|
||||
private static final long serialVersionUID = -6094992880488082586L; |
||||
|
||||
@Override |
||||
public void onDataChange(DataChangeEvent<T> event) { |
||||
if (event.getSource() == getDataProvider()) { |
||||
ListDataProvider<T> provider = getDataProvider(); |
||||
updateFooterWith(provider.getItems()); |
||||
} |
||||
} |
||||
|
||||
} |
||||
} |
||||
@ -0,0 +1,162 @@ |
||||
package de.kreth.invoice.views.invoiceitem; |
||||
|
||||
import static de.kreth.invoice.Application.getString; |
||||
import static de.kreth.invoice.Localization_Properties.CAPTION_INVOICEITEMS; |
||||
import static de.kreth.invoice.Localization_Properties.CAPTION_INVOICEITEM_ADD; |
||||
|
||||
import java.text.MessageFormat; |
||||
import java.util.ArrayList; |
||||
import java.util.Collection; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
import java.util.Set; |
||||
|
||||
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.grid.Grid; |
||||
import com.vaadin.flow.component.grid.Grid.SelectionMode; |
||||
import com.vaadin.flow.component.grid.ItemClickEvent; |
||||
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.orderedlayout.HorizontalLayout; |
||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout; |
||||
import com.vaadin.flow.data.selection.SelectionEvent; |
||||
import com.vaadin.flow.data.selection.SelectionListener; |
||||
|
||||
import de.kreth.invoice.Localization_Properties; |
||||
import de.kreth.invoice.business.ArticleBusiness; |
||||
import de.kreth.invoice.business.InvoiceItemBusiness; |
||||
import de.kreth.invoice.data.Article; |
||||
import de.kreth.invoice.data.InvoiceItem; |
||||
import de.kreth.invoice.data.User; |
||||
|
||||
public class InvoiceItemOverviewComponent extends VerticalLayout { |
||||
|
||||
private static final long serialVersionUID = -4486121981960039L; |
||||
private final InvoiceItemGrid<InvoiceItem> grid; |
||||
private final InvoiceItemBusiness invoiceItemRepository; |
||||
private final ArticleBusiness articleRepository; |
||||
private final User user; |
||||
private final List<ItemSelectionChangeListener> selectListener; |
||||
|
||||
public InvoiceItemOverviewComponent(InvoiceItemBusiness invoiceItemRepository, |
||||
ArticleBusiness articleRepository, User user) { |
||||
this.invoiceItemRepository = invoiceItemRepository; |
||||
this.articleRepository = articleRepository; |
||||
this.user = user; |
||||
this.selectListener = new ArrayList<>(); |
||||
|
||||
Button addButton = new Button(getString(CAPTION_INVOICEITEM_ADD), this::createNewitem); |
||||
H3 title = new H3(getString(CAPTION_INVOICEITEMS)); |
||||
|
||||
HorizontalLayout horizontalLayout = new HorizontalLayout(title, addButton); |
||||
|
||||
add(horizontalLayout); |
||||
|
||||
grid = new InvoiceItemGrid<>(); |
||||
grid.addItemClickListener(this::itemClicked); |
||||
grid.setSelectionMode(SelectionMode.MULTI); |
||||
GridContextMenu<InvoiceItem> contextMenu = grid.addContextMenu(); |
||||
contextMenu.addItem(getString(Localization_Properties.LABEL_DELETE), this::deleteEvent); |
||||
|
||||
add(grid); |
||||
} |
||||
|
||||
private void deleteEvent(GridContextMenuItemClickEvent<InvoiceItem> event) { |
||||
if (event.getItem().isPresent()) { |
||||
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())); |
||||
dlg.setCancelable(true); |
||||
dlg.setCancelText("Nicht " + getString(Localization_Properties.LABEL_DELETE)); |
||||
dlg.setConfirmText(getString(Localization_Properties.LABEL_DELETE)); |
||||
dlg.addConfirmListener(ev -> { |
||||
invoiceItemRepository.delete(event.getItem().get()); |
||||
refreshData(); |
||||
}); |
||||
dlg.open(); |
||||
} |
||||
} |
||||
|
||||
public void addSeelctionListener(ItemSelectionChangeListener listener) { |
||||
selectListener.add(listener); |
||||
|
||||
grid.addSelectionListener(new SelectionListener<Grid<InvoiceItem>, InvoiceItem>() { |
||||
|
||||
private static final long serialVersionUID = 597901593658493167L; |
||||
|
||||
@Override |
||||
public void selectionChange(SelectionEvent<Grid<InvoiceItem>, InvoiceItem> event) { |
||||
|
||||
ItemSelectionChangeEvent ev; |
||||
|
||||
Set<InvoiceItem> selected = event.getAllSelectedItems(); |
||||
if (selected.isEmpty()) { |
||||
ev = new ItemSelectionChangeEvent(getAllItems()); |
||||
} else { |
||||
ev = new ItemSelectionChangeEvent(selected); |
||||
} |
||||
listener.itemSelectionChanged(ev); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
public void refreshData() { |
||||
grid.setItems(invoiceItemRepository.findByInvoiceIsNull(user)); |
||||
grid.deselectAll(); |
||||
grid.getDataProvider().refreshAll(); |
||||
ItemSelectionChangeEvent evt = new ItemSelectionChangeEvent(getAllItems()); |
||||
for (ItemSelectionChangeListener itemSelectionChangeListener : selectListener) { |
||||
itemSelectionChangeListener.itemSelectionChanged(evt); |
||||
} |
||||
} |
||||
|
||||
private void itemClicked(ItemClickEvent<InvoiceItem> event) { |
||||
InvoiceItem item = event.getItem(); |
||||
if (event.getButton() == 0) { |
||||
editItem(item); |
||||
} |
||||
} |
||||
|
||||
private void createNewitem(ClickEvent<Button> ev) { |
||||
InvoiceItem item = new InvoiceItem(); |
||||
editItem(item); |
||||
|
||||
} |
||||
|
||||
private void editItem(InvoiceItem item) { |
||||
List<Article> articles = articleRepository.findByUserId(user.getId()); |
||||
InvoiceItemDialog dialog = new InvoiceItemDialog(articles, dlg -> { |
||||
if (dlg.isClosedWithOk()) { |
||||
dlg.writeTo(item); |
||||
invoiceItemRepository.save(item); |
||||
refreshData(); |
||||
} |
||||
}); |
||||
dialog.readFrom(item); |
||||
dialog.setVisible(); |
||||
} |
||||
|
||||
public List<InvoiceItem> getAllItems() { |
||||
return new ArrayList<>(grid.getDataProvider().getItems()); |
||||
} |
||||
|
||||
public static interface ItemSelectionChangeListener { |
||||
void itemSelectionChanged(ItemSelectionChangeEvent event); |
||||
} |
||||
|
||||
public static class ItemSelectionChangeEvent { |
||||
private List<InvoiceItem> values; |
||||
|
||||
public ItemSelectionChangeEvent(Collection<InvoiceItem> values) { |
||||
this.values = Collections.unmodifiableList(new ArrayList<>(values)); |
||||
} |
||||
|
||||
public List<InvoiceItem> getValues() { |
||||
return values; |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,120 @@ |
||||
package de.kreth.invoice.views.invoiceitem; |
||||
|
||||
import static de.kreth.invoice.Application.getString; |
||||
|
||||
import java.text.MessageFormat; |
||||
import java.time.LocalDateTime; |
||||
import java.time.format.TextStyle; |
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
import java.util.Locale; |
||||
|
||||
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; |
||||
|
||||
import de.kreth.invoice.Localization_Properties; |
||||
import de.kreth.invoice.business.InvoiceBusiness; |
||||
import de.kreth.invoice.data.Invoice; |
||||
import de.kreth.invoice.data.InvoiceItem; |
||||
import de.kreth.invoice.data.User; |
||||
import de.kreth.invoice.views.invoice.InvoiceDialog; |
||||
import de.kreth.invoice.views.invoice.InvoiceDialog.InvoiceMode; |
||||
import de.kreth.invoice.views.invoice.InvoiceGrid; |
||||
|
||||
public class InvoiceOverviewComponent extends VerticalLayout { |
||||
|
||||
private static final long serialVersionUID = 7784319346191720329L; |
||||
private final InvoiceGrid grid; |
||||
private final InvoiceBusiness business; |
||||
private final User user; |
||||
private final List<InvoiceItem> itemsForInvoice; |
||||
private final List<InvoiceCreationListener> creationListener; |
||||
|
||||
public InvoiceOverviewComponent(InvoiceBusiness business, User user, List<InvoiceItem> itemsForInvoice) { |
||||
super(); |
||||
this.business = business; |
||||
this.user = user; |
||||
this.itemsForInvoice = itemsForInvoice; |
||||
this.grid = new InvoiceGrid(); |
||||
this.creationListener = new ArrayList<>(); |
||||
|
||||
Button addButton = new Button(getString(Localization_Properties.CAPTION_INVOICE_CREATE), |
||||
this::createNewRechnung); |
||||
FormLayout titleComponent = new FormLayout(new H3(getString(Localization_Properties.CAPTION_INVOICES)), |
||||
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()) { |
||||
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())); |
||||
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()); |
||||
refreshData(); |
||||
}); |
||||
dlg.open(); |
||||
} |
||||
} |
||||
|
||||
public void refreshData() { |
||||
|
||||
List<Invoice> loadAll = business.loadAll(invoice -> user.equals(invoice.getUser())); |
||||
grid.setItems(loadAll); |
||||
grid.getDataProvider().refreshAll(); |
||||
} |
||||
|
||||
private void createNewRechnung(ClickEvent<Button> ev) { |
||||
if (itemsForInvoice.isEmpty()) { |
||||
Notification.show("Es kann keine Abrechnung ohne Positionen erzeugt werden."); |
||||
return; |
||||
} |
||||
Invoice invoice = new Invoice(); |
||||
invoice.setUser(user); |
||||
invoice.setItems(itemsForInvoice); |
||||
invoice.setInvoiceId(itemsForInvoice.get(0).getStart().getYear() + "-" + |
||||
itemsForInvoice.get(0).getStart().getMonth().getDisplayName(TextStyle.FULL, Locale.GERMANY)); |
||||
invoice.setInvoiceDate(LocalDateTime.now()); |
||||
// invoice.setInvoiceId(business.createNextInvoiceId(business.findByUserId(user.getId()), "Rechnung_{0}"));
|
||||
invoice.setReportRessource(itemsForInvoice.get(0).getArticle().getReport()); |
||||
openDialog(invoice, InvoiceMode.CREATE); |
||||
} |
||||
|
||||
private void openDialog(Invoice invoice, InvoiceMode mode) { |
||||
InvoiceDialog dlg = new InvoiceDialog(mode); |
||||
dlg.setInvoice(invoice); |
||||
dlg.addOkClickListener(evt -> { |
||||
business.save(invoice); |
||||
refreshData(); |
||||
for (InvoiceCreationListener invoiceCreationListener : creationListener) { |
||||
invoiceCreationListener.invoiceCreated(); |
||||
} |
||||
}); |
||||
dlg.open(); |
||||
dlg.addOpenedChangeListener(evt -> refreshData()); |
||||
} |
||||
|
||||
public void addInvoiceCreationListener(InvoiceCreationListener listener) { |
||||
this.creationListener.add(listener); |
||||
} |
||||
|
||||
public static interface InvoiceCreationListener { |
||||
void invoiceCreated(); |
||||
} |
||||
} |
||||
@ -0,0 +1,79 @@ |
||||
#Generated by Eclipse Messages Editor (Eclipse Babel) |
||||
|
||||
caption.adress.city = Ort |
||||
caption.adress.street1 = Adresse |
||||
caption.adress.street2 = Adresse |
||||
caption.adress.zipcode = Postleitzahl |
||||
caption.article = Artikel |
||||
caption.article.description = Beschreibung |
||||
caption.article.price = Stundenpreis |
||||
caption.article.title = Titel |
||||
caption.article.report = Mit Trainer-Lizenz |
||||
caption.article.type.trainer = Trainer |
||||
caption.article.type.assistant = Übungsleiter |
||||
caption.articles = Artikel |
||||
caption.bank.bic = BIC |
||||
caption.bank.iban = IBAN |
||||
caption.bank.name = Bankname |
||||
caption.invoice.create = Rechnung erstellen |
||||
caption.invoice.invoicedate = Rechnungsdatum |
||||
caption.invoice.invoiceno = Rechnungsnummer |
||||
caption.invoice.pattern = Rechnung-{0} |
||||
caption.invoice.sum = Summe |
||||
caption.invoice.printsignature = Unterschrift drucken |
||||
caption.invoiceitem = |
||||
caption.invoiceitem.add = Neuer Posten |
||||
caption.invoiceitem.date = Datum |
||||
caption.invoiceitem.end = Ende |
||||
caption.invoiceitem.name = Rechnungsposition |
||||
caption.invoiceitem.participants = Teilnehmer |
||||
caption.invoiceitem.start = Beginn |
||||
caption.invoiceitem.sumprice = Betrag |
||||
caption.invoiceitems = Rechnungspositionen |
||||
caption.invoices = Rechnungen |
||||
caption.user.details = Benutzer Details |
||||
caption.user.login = Anmelden |
||||
caption.user.loginname = Anmeldename: |
||||
caption.user.password = Ihr Password: |
||||
caption.user.passwordconfirmation = Password best\u00E4tigen: |
||||
caption.user.prename = Vorname: |
||||
caption.user.surname = Nachname: |
||||
|
||||
error.invoice.text.noitems = Bitte Posten f\u00FCr Rechnung \ |
||||
ausw\u00E4hlen. |
||||
error.invoice.title.noitems = Leere Abrechnung nicht erlaubt. |
||||
error.article.undefined = Bitte Artikel anlegen. |
||||
error.userdetails.adress_empty = Adresse darf nicht leer sein. |
||||
error.userdetails.bankname_empty = Bankname darf nicht leer sein. |
||||
error.userdetails.city_empty = Ort darf nicht leer sein. |
||||
error.userdetails.iban_empty = Iban darf nicht leer sein. |
||||
error.userdetails.prename_empty = Vorname darf nicht leer sein. |
||||
error.userdetails.surname_empty = Nachname darf nicht leer sein. |
||||
error.userdetails.username_empty = Anmeldename darf nicht leer sein. |
||||
error.userdetails.zip_empty = Postleitzahl darf nicht leer sein. |
||||
|
||||
label.addarticle = Neuer Artikel |
||||
label.cancel = Abbrechen |
||||
label.close = Schlie\u00DFen |
||||
label.delete = L\u00F6schen |
||||
label.discart = Verwerfen |
||||
label.loggedin = Angemeldet: |
||||
label.logout = Abmelden |
||||
label.ok = OK |
||||
label.store = Speichern |
||||
label.preview = Vorschau |
||||
label.open = \u00D6ffnen |
||||
label.user.register = Registrieren |
||||
|
||||
message.article.priceerror = Bitte legen Sie den Preis fest. |
||||
message.article.error.invoiceexists = Kann nicht geändert werden, da bereits Rechnungen bestehen. Bitte neuen Artikel anlegen. |
||||
message.delete.text = Soll {0} wirklich gel\u00F6scht werden? |
||||
message.delete.title = Wirklich l\u00F6schen? |
||||
message.invoiceitem.allfieldsmustbeset = Start, Ende und Artikel m\u00FCssen \ |
||||
gesetzt sein! |
||||
message.invoiceitem.startbeforeend = Ende darf nicht vor Start liegen. |
||||
message.user.create.success = {0} erstellt! |
||||
message.user.create.failure = Fehler beim Erstellen von Benutzer {0}! Ändern Sie den Benutzernamen oder fragen Sie nach dem Passwort. Detail: {1} |
||||
message.user.loginfailure = Anmeldefehler! Falscher Name oder \ |
||||
Passwort? |
||||
message.user.passwordmissmatch = Passworter stimmen nicht \u00FCberein! |
||||
@ -0,0 +1,83 @@ |
||||
package de.kreth.invoice.business; |
||||
|
||||
import static org.junit.Assert.assertEquals; |
||||
import static org.junit.Assert.assertFalse; |
||||
import static org.junit.Assert.assertTrue; |
||||
|
||||
import java.util.Arrays; |
||||
import java.util.Collections; |
||||
|
||||
import org.junit.jupiter.api.BeforeEach; |
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import de.kreth.invoice.data.Invoice; |
||||
|
||||
public class InvoiceBusinessTest { |
||||
|
||||
private InvoiceBusiness business; |
||||
|
||||
@BeforeEach |
||||
void initBusiness() { |
||||
business = new InvoiceBusiness(null, null); |
||||
} |
||||
|
||||
@Test |
||||
void testFirstRechnungNoCreation() { |
||||
String invoiceId = business.createNextInvoiceId(Collections.emptyList(), "Rechnung_{0}"); |
||||
assertEquals("Rechnung_1", invoiceId); |
||||
} |
||||
|
||||
@Test |
||||
void testSimpleIncrementRechnungNoCreation() { |
||||
Invoice invoice1 = new Invoice(); |
||||
invoice1.setInvoiceId("Rechnung_1"); |
||||
|
||||
Invoice invoice2 = new Invoice(); |
||||
invoice2.setInvoiceId("Rechnung_2"); |
||||
|
||||
String invoiceId = business.createNextInvoiceId(Arrays.asList(invoice1, invoice2), "Rechnung_{0}"); |
||||
assertEquals("Rechnung_3", invoiceId); |
||||
} |
||||
|
||||
@Test |
||||
void testIgnoreNotMatching() { |
||||
|
||||
Invoice invoice1 = new Invoice(); |
||||
invoice1.setInvoiceId("Rechnung_1"); |
||||
|
||||
Invoice invoice2 = new Invoice(); |
||||
invoice2.setInvoiceId("Rechnung_2"); |
||||
|
||||
Invoice invoice3 = new Invoice(); |
||||
invoice3.setInvoiceId("Rechnung_2_zusatz"); |
||||
|
||||
String invoiceId = business.createNextInvoiceId(Arrays.asList(invoice1, invoice2, invoice3), "Rechnung_{0}"); |
||||
assertEquals("Rechnung_3", invoiceId); |
||||
} |
||||
|
||||
@Test |
||||
void testFilterMatching() { |
||||
String invoiceNo = "Rechnung_324"; |
||||
assertTrue(business.filter(invoiceNo, "Rechnung_{0}")); |
||||
} |
||||
|
||||
@Test |
||||
void testFilterMatchingSuffix() { |
||||
String invoiceNo = "Rechnung_324_Test"; |
||||
assertTrue(business.filter(invoiceNo, "Rechnung_{0}_Test")); |
||||
} |
||||
|
||||
@Test |
||||
void testFilterSuffixNotMatching() { |
||||
|
||||
String invoiceNo = "Rechnung_3_sonder"; |
||||
assertFalse(business.filter(invoiceNo, "Rechnung_{0}")); |
||||
} |
||||
|
||||
@Test |
||||
void testFilterWrongSuffixNotMatching() { |
||||
|
||||
String invoiceNo = "Rechnung_3_sonder"; |
||||
assertFalse(business.filter(invoiceNo, "Rechnung_{0}_Test")); |
||||
} |
||||
} |
||||
Loading…
Reference in new issue