Commit e9b0113b by Tuomas Riihimäki

Merge branch 'product-billine-summary' into 'master'

Add per product bill summary page

See merge request !430
2 parents 327aa8cc 05ec88b2
...@@ -29,10 +29,7 @@ import javax.ejb.Local; ...@@ -29,10 +29,7 @@ import javax.ejb.Local;
import fi.codecrew.moya.bortal.views.BillSummary; import fi.codecrew.moya.bortal.views.BillSummary;
import fi.codecrew.moya.entitysearch.BillSearchQuery; import fi.codecrew.moya.entitysearch.BillSearchQuery;
import fi.codecrew.moya.exceptions.BillException; import fi.codecrew.moya.exceptions.BillException;
import fi.codecrew.moya.model.Bill; import fi.codecrew.moya.model.*;
import fi.codecrew.moya.model.EventUser;
import fi.codecrew.moya.model.FoodWave;
import fi.codecrew.moya.model.Product;
import fi.codecrew.moya.utilities.SearchResult; import fi.codecrew.moya.utilities.SearchResult;
@Local @Local
...@@ -70,4 +67,5 @@ public interface BillBeanLocal { ...@@ -70,4 +67,5 @@ public interface BillBeanLocal {
SearchResult<Bill> findUsers(BillSearchQuery q); SearchResult<Bill> findUsers(BillSearchQuery q);
List<BillLine> getBillLinesForProduct(Integer productId);
} }
...@@ -22,18 +22,27 @@ import java.io.Serializable; ...@@ -22,18 +22,27 @@ import java.io.Serializable;
import java.math.BigDecimal; import java.math.BigDecimal;
import fi.codecrew.moya.model.BillLine; import fi.codecrew.moya.model.BillLine;
import fi.codecrew.moya.model.Product;
public class BillSummary implements Serializable{ public class BillSummary implements Serializable {
public BillSummary(String name) { private final Product product;
this.name = name; private final String name;
}
private String name;
private BigDecimal active = BigDecimal.ZERO; private BigDecimal active = BigDecimal.ZERO;
private BigDecimal paid = BigDecimal.ZERO; private BigDecimal paid = BigDecimal.ZERO;
private BigDecimal expired = BigDecimal.ZERO; private BigDecimal expired = BigDecimal.ZERO;
public BillSummary(BillLine line) {
this.name = line.getName();
this.product = line.getLineProduct();
}
public BillSummary(Product product) {
this.name = product.getName();
this.product = product;
}
public String getName() { public String getName() {
return name; return name;
} }
...@@ -61,4 +70,7 @@ public class BillSummary implements Serializable{ ...@@ -61,4 +70,7 @@ public class BillSummary implements Serializable{
return expired; return expired;
} }
public Product getProduct() {
return product;
}
} }
...@@ -20,11 +20,7 @@ package fi.codecrew.moya.beans; ...@@ -20,11 +20,7 @@ package fi.codecrew.moya.beans;
import java.io.OutputStream; import java.io.OutputStream;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.ArrayList; import java.util.*;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import javax.annotation.security.DeclareRoles; import javax.annotation.security.DeclareRoles;
import javax.annotation.security.RolesAllowed; import javax.annotation.security.RolesAllowed;
...@@ -218,10 +214,14 @@ public class BillBean implements BillBeanLocal { ...@@ -218,10 +214,14 @@ public class BillBean implements BillBeanLocal {
@Override @Override
@RolesAllowed(BillPermission.S_READ_ALL) @RolesAllowed(BillPermission.S_READ_ALL)
public Collection<BillSummary> getBillLineSummary() { public Collection<BillSummary> getBillLineSummary() {
Collection<BillSummary> ret = billLineFacade.getLineSummary(eventbean
.getCurrentEvent());
return ret; Map<String, BillSummary> retmap = new HashMap<String, BillSummary>();
for (BillLine bl : billLineFacade.getAllLines(eventbean.getCurrentEvent())) {
retmap.computeIfAbsent(bl.getName(), (k) -> new BillSummary(bl)).addLine(bl);
}
return retmap.values();
} }
/** /**
...@@ -398,4 +398,11 @@ public class BillBean implements BillBeanLocal { ...@@ -398,4 +398,11 @@ public class BillBean implements BillBeanLocal {
return billFacade.find(q); return billFacade.find(q);
} }
@Override
@RolesAllowed(BillPermission.S_READ_ALL)
public List<BillLine> getBillLinesForProduct(Integer productId) {
return billLineFacade.getLinesForProduct(productBean.findById(productId));
}
} }
/* /*
* Copyright Codecrew Ry * Copyright Codecrew Ry
* *
* All rights reserved. * All rights reserved.
* *
* This license applies to any software containing a notice placed by the * This license applies to any software containing a notice placed by the
* copyright holder. Such software is herein referred to as the Software. * copyright holder. Such software is herein referred to as the Software.
* This license covers modification, distribution and use of the Software. * This license covers modification, distribution and use of the Software.
* *
* Any distribution and use in source and binary forms, with or without * Any distribution and use in source and binary forms, with or without
* modification is not permitted without explicit written permission from the * modification is not permitted without explicit written permission from the
* copyright owner. * copyright owner.
* *
* A non-exclusive royalty-free right is granted to the copyright owner of the * A non-exclusive royalty-free right is granted to the copyright owner of the
* Software to use, modify and distribute all modifications to the Software in * Software to use, modify and distribute all modifications to the Software in
* future versions of the Software. * future versions of the Software.
* *
*/ */
package fi.codecrew.moya.facade; package fi.codecrew.moya.facade;
import java.util.Collection;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import javax.ejb.EJB;
import javax.ejb.LocalBean; import javax.ejb.LocalBean;
import javax.ejb.Stateless; import javax.ejb.Stateless;
import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root; import javax.persistence.criteria.Root;
import fi.codecrew.moya.beans.EventBeanLocal;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -43,37 +42,25 @@ import fi.codecrew.moya.model.Product; ...@@ -43,37 +42,25 @@ import fi.codecrew.moya.model.Product;
@Stateless @Stateless
@LocalBean @LocalBean
public class BillLineFacade extends IntegerPkGenericFacade<BillLine> { public class BillLineFacade extends IntegerPkGenericFacade<BillLine> {
@SuppressWarnings("unused") @SuppressWarnings("unused")
private static final Logger logger = LoggerFactory.getLogger(BillLineFacade.class); private static final Logger logger = LoggerFactory.getLogger(BillLineFacade.class);
@EJB
private EventBeanLocal eventbean;
public BillLineFacade() { public BillLineFacade() {
super(BillLine.class); super(BillLine.class);
} }
// @Override public List<BillLine> getAllLines(LanEvent event) {
// public void create(BillLine entity) {
// super.create(entity);
// billfacade.evict(entity.getBill());
// }
public Collection<BillSummary> getLineSummary(LanEvent event) {
CriteriaBuilder cb = getEm().getCriteriaBuilder(); CriteriaBuilder cb = getEm().getCriteriaBuilder();
CriteriaQuery<BillLine> cq = cb.createQuery(BillLine.class); CriteriaQuery<BillLine> cq = cb.createQuery(BillLine.class);
Root<BillLine> root = cq.from(BillLine.class); Root<BillLine> root = cq.from(BillLine.class);
cq.where(cb.equal(root.get(BillLine_.bill).get(Bill_.event), event)); cq.where(cb.equal(root.get(BillLine_.bill).get(Bill_.event), event));
List<BillLine> lines = getEm().createQuery(cq).getResultList(); return getEm().createQuery(cq).getResultList();
Map<String, BillSummary> retmap = new HashMap<String, BillSummary>();
for (BillLine bl : lines) {
if (!retmap.containsKey(bl.getName())) {
retmap.put(bl.getName(), new BillSummary(bl.getName()));
}
retmap.get(bl.getName()).addLine(bl);
}
return retmap.values();
} }
public BillSummary getLineSummary(Product list, LanEvent event) { public BillSummary getLineSummary(Product list, LanEvent event) {
...@@ -81,10 +68,10 @@ public class BillLineFacade extends IntegerPkGenericFacade<BillLine> { ...@@ -81,10 +68,10 @@ public class BillLineFacade extends IntegerPkGenericFacade<BillLine> {
CriteriaQuery<BillLine> cq = cb.createQuery(BillLine.class); CriteriaQuery<BillLine> cq = cb.createQuery(BillLine.class);
Root<BillLine> root = cq.from(BillLine.class); Root<BillLine> root = cq.from(BillLine.class);
cq.where(cb.equal(root.get(BillLine_.bill).get(Bill_.event), event), cq.where(cb.equal(root.get(BillLine_.bill).get(Bill_.event), event),
cb.equal(root.get(BillLine_.lineProduct), list)); cb.equal(root.get(BillLine_.lineProduct), list));
List<BillLine> lines = getEm().createQuery(cq).getResultList(); List<BillLine> lines = getEm().createQuery(cq).getResultList();
BillSummary ret = new BillSummary(list.getName()); BillSummary ret = new BillSummary(list);
for (BillLine bl : lines) { for (BillLine bl : lines) {
ret.addLine(bl); ret.addLine(bl);
...@@ -92,17 +79,18 @@ public class BillLineFacade extends IntegerPkGenericFacade<BillLine> { ...@@ -92,17 +79,18 @@ public class BillLineFacade extends IntegerPkGenericFacade<BillLine> {
return ret; return ret;
} }
public BillSummary getLineSummary(Product list, LanEvent event, EventUser user) { public BillSummary getLineSummary(Product product, LanEvent event, EventUser user) {
CriteriaBuilder cb = getEm().getCriteriaBuilder(); CriteriaBuilder cb = getEm().getCriteriaBuilder();
CriteriaQuery<BillLine> cq = cb.createQuery(BillLine.class); CriteriaQuery<BillLine> cq = cb.createQuery(BillLine.class);
Root<BillLine> root = cq.from(BillLine.class); Root<BillLine> root = cq.from(BillLine.class);
cq.where(cb.equal(root.get(BillLine_.bill).get(Bill_.event), event), cq.where(cb.equal(root.get(BillLine_.bill).get(Bill_.event), event),
cb.equal(root.get(BillLine_.lineProduct), list), cb.equal(root.get(BillLine_.lineProduct), product),
cb.equal(root.get(BillLine_.bill).get(Bill_.user), user) cb.equal(root.get(BillLine_.bill).get(Bill_.user), user)
); );
List<BillLine> lines = getEm().createQuery(cq).getResultList(); List<BillLine> lines = getEm().createQuery(cq).getResultList();
BillSummary ret = new BillSummary(list.getName());
BillSummary ret = new BillSummary(product);
for (BillLine bl : lines) { for (BillLine bl : lines) {
ret.addLine(bl); ret.addLine(bl);
...@@ -110,4 +98,16 @@ public class BillLineFacade extends IntegerPkGenericFacade<BillLine> { ...@@ -110,4 +98,16 @@ public class BillLineFacade extends IntegerPkGenericFacade<BillLine> {
return ret; return ret;
} }
public List<BillLine> getLinesForProduct(Product product) {
if (product == null) {
return null;
}
CriteriaBuilder cb = getEm().getCriteriaBuilder();
CriteriaQuery<BillLine> cq = cb.createQuery(BillLine.class);
Root<BillLine> root = cq.from(BillLine.class);
cq.where(cb.equal(root.get(BillLine_.lineProduct), product),
cb.equal(root.get(BillLine_.bill).get(Bill_.event), eventbean.getCurrentEvent()));
return getEm().createQuery(cq).getResultList();
}
} }
<!DOCTYPE html <!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:bills="http://java.sun.com/jsf/composite/tools/bills" <html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"> xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core" xmlns:p="http://primefaces.org/ui">
<h:body> <h:body>
<ui:composition template="#{sessionHandler.template}"> <ui:composition template="#{sessionHandler.template}">
<f:metadata> <f:metadata>
<f:event type="preRenderView" listener="#{billListView.initSummaryView}" /> <f:event type="preRenderView" listener="#{billListView.initSummaryView}"/>
</f:metadata> </f:metadata>
<ui:define name="content"> <ui:define name="content">
<h:form id="billsummary" styleClass="moya_datatable2"> <h:form id="billsummary" styleClass="moya_datatable2">
<h:dataTable border="0" id="billSummary" value="#{billListView.billsummary}" var="sumline" styleClass="moya_datatable2"> <p:dataTable border="0" id="billSummary" value="#{billListView.billsummary}" var="sumline"
<h:column> styleClass="moya_datatable2">
<f:facet name="header"> <p:column headerText="${i18n['product.name']}">
<h:outputText value="${i18n['product.name']}" /> <h:outputText value="#{sumline.name}"/>
</f:facet> </p:column>
<h:outputText value="#{sumline.name}" /> <p:column headerText="${i18n['product.name']}">
</h:column> <p:link rendered="#{!empty sumline.product}" outcome="/bill/productSummary"
<h:column> value="#{sumline.product.name}">
<f:facet name="header"> <f:param name="productId" value="#{sumline.product.id}"/>
<h:outputText value="${i18n['product.boughtTotal']}" /> </p:link>
</f:facet> </p:column>
<p:column headerText="${i18n['product.boughtTotal']}">
<h:outputText value="#{sumline.active}"> <h:outputText value="#{sumline.active}">
<f:convertNumber /> <f:convertNumber/>
</h:outputText> </h:outputText>
</h:column> </p:column>
<h:column> <p:column headerText="${i18n['product.paid']}">
<f:facet name="header">
<h:outputText value="${i18n['product.paid']}" />
</f:facet>
<h:outputText value="#{sumline.paid}"> <h:outputText value="#{sumline.paid}">
<f:convertNumber /> <f:convertNumber/>
</h:outputText> </h:outputText>
</h:column> </p:column>
<h:column> <p:column headerText="${i18n['product.expired']}">
<f:facet name="header">
<h:outputText value="${i18n['product.expired']}" />
</f:facet>
<h:outputText value="#{sumline.expired}"> <h:outputText value="#{sumline.expired}">
<f:convertNumber /> <f:convertNumber/>
</h:outputText> </h:outputText>
</h:column> </p:column>
</h:dataTable> </p:dataTable>
</h:form> </h:form>
</ui:define> </ui:define>
</ui:composition> </ui:composition>
......
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core" xmlns:p="http://primefaces.org/ui">
<h:body>
<ui:composition template="#{sessionHandler.template}">
<f:metadata>
<f:viewParam value="#{productSummaryView.productId}" name="productId"/>
<f:event type="preRenderView" listener="#{productSummaryView.initProductSummary}"/>
</f:metadata>
<ui:define name="content">
<h:form id="billsummary" styleClass="moya_datatable2">
<p:dataTable id="lines" value="#{productSummaryView.billLines}" var="line" widgetVar="lineTable">
<p:column headerText="${i18n['bill.id_str']}" sortBy="#{line.bill.id }" sortable="true"
filterBy="#{line.bill.paid}" filterMatchMode="exact">
<f:facet name="filter">
<p:selectOneButton onchange="PF('lineTable').filter()" styleClass="custom-filter">
<f:converter converterId="javax.faces.Boolean"/>
<f:selectItem itemLabel="#{i18n['bill.filter_all']}" itemValue=""/>
<f:selectItem itemLabel="#{i18n['bill.isPaid']}" itemValue="true"/>
<f:selectItem itemLabel="#{i18n['bill.isNotPaid']}" itemValue="false"/>
</p:selectOneButton>
</f:facet>
<p:link outcome="/bill/edit" value="#{line.bill.id} (#{line.bill.paid?i18n['bill.isPaid']:i18n['bill.isNotPaid']})">
<f:param name="billid" value="#{line.bill.id}"/>
</p:link>
</p:column>
<p:column sortable="true" sortBy="#{line.bill.paidDate}" headerText="#{i18n['bill.paidDate']}">
<h:outputText value="#{line.bill.paidDate}">
<f:convertDateTime pattern="#{sessionHandler.datetimeFormat}"
timeZone="#{sessionHandler.timezone}"/>
</h:outputText>
</p:column>
<p:column headerText="Purchaser" sortBy="#{line.bill.user.wholeName}" sortable="true">
<p:link outcome="/user/edit" value="#{line.bill.user.wholeName}">
<f:param name="userid" value="#{line.bill.user.user.id}"/>
</p:link>
</p:column>
<p:column headerText="${i18n['billLine.product']}" sortBy="#{line.name}" sortable="true">
<p:link outcome="/product/edit" value="#{line.name}">
<f:param name="productid" value="#{line.lineProduct.id}"/>
</p:link>
</p:column>
<p:column headerText="${i18n['billLine.quantity']}" sortBy="#{line.quantity}" sortable="true">
<h:outputText value="#{line.quantity}">
<f:convertNumber/>
</h:outputText>
</p:column>
</p:dataTable>
</h:form>
</ui:define>
</ui:composition>
</h:body>
</html>
\ No newline at end of file
...@@ -57,3 +57,8 @@ ...@@ -57,3 +57,8 @@
100% { opacity: 1.0; } 100% { opacity: 1.0; }
} }
.ui-filter-column .ui-column-customfilter .custom-filter {
width: 100%;
box-sizing: border-box;
}
/*
* Copyright Codecrew Ry
*
* All rights reserved.
*
* This license applies to any software containing a notice placed by the
* copyright holder. Such software is herein referred to as the Software.
* This license covers modification, distribution and use of the Software.
*
* Any distribution and use in source and binary forms, with or without
* modification is not permitted without explicit written permission from the
* copyright owner.
*
* A non-exclusive royalty-free right is granted to the copyright owner of the
* Software to use, modify and distribute all modifications to the Software in
* future versions of the Software.
*
*/
package fi.codecrew.moya.web.cdiview.shop;
import fi.codecrew.moya.beans.BillBeanLocal;
import fi.codecrew.moya.beans.EventBeanLocal;
import fi.codecrew.moya.beans.VerkkomaksutFiBeanLocal;
import fi.codecrew.moya.bortal.views.BillSummary;
import fi.codecrew.moya.entitysearch.BillSearchQuery;
import fi.codecrew.moya.enums.apps.BillPermission;
import fi.codecrew.moya.exceptions.BillException;
import fi.codecrew.moya.model.Bill;
import fi.codecrew.moya.model.BillLine;
import fi.codecrew.moya.model.EventUser;
import fi.codecrew.moya.utilities.SearchQuery.QuerySortOrder;
import fi.codecrew.moya.utilities.SearchResult;
import fi.codecrew.moya.utilities.jsf.MessageHelper;
import fi.codecrew.moya.web.annotations.SelectedUser;
import fi.codecrew.moya.web.cdiview.GenericCDIView;
import fi.codecrew.moya.web.helpers.LazyEntityDataModel;
import org.primefaces.model.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.ejb.EJB;
import javax.enterprise.context.ConversationScoped;
import javax.inject.Inject;
import javax.inject.Named;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
@Named
@ConversationScoped
public class ProductSummaryView extends GenericCDIView {
private static final long serialVersionUID = -4668803787903428161L;
private static final Logger logger = LoggerFactory.getLogger(ProductSummaryView.class);
@EJB
private transient BillBeanLocal billbean;
private Integer productId;
private List<BillLine> billLines;
public void initProductSummary() {
if (super.requirePermissions(BillPermission.READ_ALL) && billLines == null) {
beginConversation();
logger.info("Initializing lazyList");
billLines = billbean.getBillLinesForProduct(productId);
}
}
public Integer getProductId() {
return productId;
}
public void setProductId(Integer productId) {
this.productId = productId;
}
public List<BillLine> getBillLines() {
return billLines;
}
public void setBillLines(List<BillLine> billLines) {
this.billLines = billLines;
}
}
...@@ -1943,4 +1943,6 @@ holderize.column = Column ...@@ -1943,4 +1943,6 @@ holderize.column = Column
holderize.row = Row holderize.row = Row
holderize.barcode = Barcode holderize.barcode = Barcode
holderize.nextCodeHeader = Next card position holderize.nextCodeHeader = Next card position
holderize.parametersHeader = Folder parameters holderize.parametersHeader = Folder parameters
\ No newline at end of file bill.id_str=Bill id
bill.filter_all=All
\ No newline at end of file
...@@ -1916,17 +1916,19 @@ role.source.ACCOUNTEVENT = Tilitapah ...@@ -1916,17 +1916,19 @@ role.source.ACCOUNTEVENT = Tilitapah
role.source.PLACE_PRODUCT = Tuote paikan kautta role.source.PLACE_PRODUCT = Tuote paikan kautta
role.source.EVENT_DEFAULT = Tapahtuman oletusrooli role.source.EVENT_DEFAULT = Tapahtuman oletusrooli
user.allroles = Kaikki k\u00E4ytt\u00E4j\u00E4n roolit user.allroles = Kaikki k\u00E4ytt\u00E4j\u00E4n roolit
voting.create.entrysubmitrole = Teosten lhettmiseen tarvittava rooli voting.create.entrysubmitrole = Teosten l\u00E4hett\u00E4miseen tarvittava rooli
compo.filetype.name = Tiedostotyypin nimi compo.filetype.name = Tiedostotyypin nimi
compo.filetype.sort = Jrjestysnumero compo.filetype.sort = J\u00E4rjestysnumero
compo.filetype.filetype = Tyyppi compo.filetype.filetype = Tyyppi
feature.name = Ominaisuus feature.name = Ominaisuus
feature.user_permission = Kyttj feature.user_permission = K\u00E4ytt\u00E4j\u00E4
feature.info_permission = Jrjestj feature.info_permission = J\u00E4rjest\u00E4j\u00E4
feature.admin_permission = Pkyttj feature.admin_permission = P\u00E4\u00E4k\u00E4ytt\u00E4j\u00E4
role.features = Tapahtuman ominaisuuksien oikeudet role.features = Tapahtuman ominaisuuksien oikeudet
bill.id_str=Laskun ID
bill.filter_all=Kaikki
holderize.header = Tulostettujen korttien kansiotus holderize.header = Tulostettujen korttien kansiotus
holderize.pageRows = Rivej sivulla holderize.pageRows = Rivej sivulla
holderize.pageColumns = Sarakkeita sivulla holderize.pageColumns = Sarakkeita sivulla
holderize.folderName = Kansion nimi holderize.folderName = Kansion nimi
holderize.page = Sivu holderize.page = Sivu
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!