Přidaná agenda pro nastavení limitů komisí. Změněn způsob hlídání limitů při zadávání nového požadavku- limit se bere z nové agendy.

closes #263
closes #264
master
Josef Rokos 9 years ago
parent cd6b8c01dc
commit cbb81657c8

@ -15,6 +15,7 @@ import info.bukova.isspst.services.addressbook.AdbService;
import info.bukova.isspst.services.buildings.BuildingService;
import info.bukova.isspst.services.fulltext.FullTextService;
import info.bukova.isspst.services.invoicing.InvoicingService;
import info.bukova.isspst.services.limits.LimitService;
import info.bukova.isspst.services.munits.MUnitService;
import info.bukova.isspst.services.orders.ApprovedService;
import info.bukova.isspst.services.orders.OrderService;
@ -88,6 +89,7 @@ public class Constants {
public final static String MOD_INVOICING = "INVOICING";
public final static String MOD_SEARCH = "SEARCH";
public final static String MOD_SIGNEDDOCS = "SIGNEDDOCS";
public final static String MOD_LIMITS = "LIMITS";
public final static Module MODULES[] = {
new Module(MOD_USERS, "Uživatelé", UserService.class),
new Module(MOD_PERMISSIONS, "Práva", RoleService.class),
@ -106,6 +108,7 @@ public class Constants {
new Module(MOD_INVOICING, "Fakturace požadavků", InvoicingService.class),
new Module(MOD_SEARCH, "Fulltextové vyhledávání", FullTextService.class, true, false),
new Module(MOD_SIGNEDDOCS, "Podepsané dokumenty", SignedDocumentService.class, true, false),
new Module(MOD_LIMITS, "Limity komisi", LimitService.class),
};
public final static String PERM_APPROVE = "PERM_APPROVE";

@ -0,0 +1,10 @@
package info.bukova.isspst.dao;
import info.bukova.isspst.data.Limit;
/**
* @author Pepa Rokos
*/
public interface LimitDao extends BaseDao<Limit> {
}

@ -0,0 +1,11 @@
package info.bukova.isspst.dao.jpa;
import info.bukova.isspst.dao.LimitDao;
import info.bukova.isspst.data.Limit;
/**
* @author Pepa Rokos
*/
public class LimitDaoJPA extends BaseDaoJPA<Limit> implements LimitDao {
}

@ -0,0 +1,50 @@
package info.bukova.isspst.data;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import java.math.BigDecimal;
/**
* @author Pepa Rokos
*/
@Entity
@Table(name = "LIMITS")
public class Limit extends BaseData implements SeasonsAware {
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "WORKGROUP_ID")
private Workgroup workgroup;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "SEASON_ID")
private Season season;
@Column(name = "REQ_LIMIT", precision = 15, scale = 4)
private BigDecimal limit;
public Workgroup getWorkgroup() {
return workgroup;
}
public void setWorkgroup(Workgroup workgroup) {
this.workgroup = workgroup;
}
public Season getSeason() {
return season;
}
public void setSeason(Season season) {
this.season = season;
}
public BigDecimal getLimit() {
return limit;
}
public void setLimit(BigDecimal limit) {
this.limit = limit;
}
}

@ -2,6 +2,7 @@ package info.bukova.isspst.services.invoicing;
import info.bukova.isspst.data.Invoicing;
import info.bukova.isspst.data.Requirement;
import info.bukova.isspst.data.Season;
import info.bukova.isspst.data.Workgroup;
import info.bukova.isspst.services.Service;
@ -12,6 +13,8 @@ public interface InvoicingService extends Service<Invoicing> {
public BigDecimal totalInvoicedForWorkgroup(Workgroup workgroup);
public BigDecimal totalInvoicedForWorkgroupAndSeason(Workgroup workgroup, Season season);
public void loadReqItems(Invoicing invoicing);
public void loadItems(Invoicing invoicing);

@ -4,11 +4,14 @@ import info.bukova.isspst.Constants;
import info.bukova.isspst.data.Invoicing;
import info.bukova.isspst.data.InvoicingItem;
import info.bukova.isspst.data.Requirement;
import info.bukova.isspst.data.Season;
import info.bukova.isspst.data.Workgroup;
import info.bukova.isspst.services.AbstractOwnedService;
import info.bukova.isspst.services.LazyLoader;
import info.bukova.isspst.services.settings.SeasonService;
import org.hibernate.Hibernate;
import org.hibernate.Query;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.transaction.annotation.Transactional;
@ -18,13 +21,23 @@ import java.util.List;
public class InvoicingServiceImpl extends AbstractOwnedService<Invoicing> implements
InvoicingService {
@Autowired
private SeasonService seasonService;
@Override
@Transactional
public BigDecimal totalInvoicedForWorkgroup(Workgroup workgroup) {
return totalInvoicedForWorkgroupAndSeason(workgroup, seasonService.getActive());
}
@Override
@Transactional
public BigDecimal totalInvoicedForWorkgroupAndSeason(Workgroup workgroup, Season season) {
Query q = dao.getQuery("select sum(inv.totalInvoiced) "
+ "from Invoicing inv join inv.requirement rq join rq.workgroup w "
+ "where w = :workgroup ");
+ "where w = :workgroup and rq.season = :season");
q.setParameter("workgroup", workgroup);
q.setParameter("season", season);
return (BigDecimal) q.uniqueResult();
}

@ -0,0 +1,14 @@
package info.bukova.isspst.services.limits;
import info.bukova.isspst.services.IsspstException;
/**
* @author Pepa Rokos
*/
public class LimitException extends IsspstException {
public LimitException(String reason) {
setReason(reason);
}
}

@ -0,0 +1,17 @@
package info.bukova.isspst.services.limits;
import info.bukova.isspst.data.Limit;
import info.bukova.isspst.data.Season;
import info.bukova.isspst.data.Workgroup;
import info.bukova.isspst.services.Service;
import java.util.List;
/**
* @author Pepa Rokos
*/
public interface LimitService extends Service<Limit> {
List<Limit> getForSeason(Season season);
Limit getForWorkgroupAndSeason(Workgroup wg, Season season);
Limit getForWorkgroup(Workgroup wg);
}

@ -0,0 +1,62 @@
package info.bukova.isspst.services.limits;
import info.bukova.isspst.data.Limit;
import info.bukova.isspst.data.Season;
import info.bukova.isspst.data.Workgroup;
import info.bukova.isspst.services.AbstractOwnedService;
import info.bukova.isspst.services.settings.SeasonService;
import org.hibernate.Query;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* @author Pepa Rokos
*/
public class LimitServiceImpl extends AbstractOwnedService<Limit> implements LimitService {
@Autowired
private SeasonService seasonService;
@Override
@Transactional
public List<Limit> getForSeason(Season season) {
Query q = dao.getQuery("from Limit where season = :season");
q.setParameter("season", season);
return q.list();
}
@Override
@Transactional
public Limit getForWorkgroupAndSeason(Workgroup wg, Season season) {
Query q = dao.getQuery("from Limit where workgroup = :wg and season = :season");
q.setParameter("wg", wg);
q.setParameter("season", season);
return (Limit) q.uniqueResult();
}
@Override
@Transactional
public Limit getForWorkgroup(Workgroup wg) {
return getForWorkgroupAndSeason(wg, seasonService.getActive());
}
@Override
@Transactional
@PreAuthorize("hasPermission(this, 'PERM_ADD')")
public void add(Limit entity) {
if (getForWorkgroupAndSeason(entity.getWorkgroup(),entity.getSeason()) != null) {
throw new LimitException("LimitExists");
}
super.add(entity);
}
@Override
protected Limit createEntity() {
Limit limit = new Limit();
limit.setSeason(seasonService.getActive());
return limit;
}
}

@ -2,6 +2,7 @@ package info.bukova.isspst.services.requirement;
import info.bukova.isspst.Constants;
import info.bukova.isspst.data.Invoicing;
import info.bukova.isspst.data.Limit;
import info.bukova.isspst.data.Requirement;
import info.bukova.isspst.data.RequirementItem;
import info.bukova.isspst.data.RequirementState;
@ -10,6 +11,7 @@ import info.bukova.isspst.data.User;
import info.bukova.isspst.data.Workflow;
import info.bukova.isspst.services.LazyLoader;
import info.bukova.isspst.services.invoicing.InvoicingService;
import info.bukova.isspst.services.limits.LimitService;
import org.hibernate.Hibernate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
@ -33,6 +35,8 @@ public class RequirementServiceImpl extends RequirementBaseServiceImpl<Requireme
private RequirementTypeService reqTypeService;
@Autowired
private InvoicingService invoicingService;
@Autowired
private LimitService limitService;
@Override
protected Requirement createEntity()
@ -80,26 +84,25 @@ public class RequirementServiceImpl extends RequirementBaseServiceImpl<Requireme
@Override
protected boolean canAdd(Requirement entity)
{
if (entity.getWorkgroup() != null && entity.getWorkgroup().getLimit() != null && entity.getWorkgroup().getLimit().compareTo(BigDecimal.ZERO) != 0)
{
if (entity.getWorkgroup() != null) {
Limit limit = limitService.getForWorkgroup(entity.getWorkgroup());
if (limit == null) {
return true;
}
BigDecimal total = invoicingService.totalInvoicedForWorkgroup(entity.getWorkgroup());
if (total == null)
{
if (total == null) {
total = BigDecimal.ZERO;
}
total = total.add(entity.getSumTotal());
if (total.compareTo(entity.getWorkgroup().getLimit()) <= 0)
{
if (total.compareTo(limit.getLimit()) <= 0) {
return true;
}
}
else
{
else {
return true;
}

@ -5,10 +5,6 @@ import info.bukova.isspst.data.DataModel;
import info.bukova.isspst.services.IsspstException;
import info.bukova.isspst.services.Service;
import info.bukova.isspst.services.ValidationException;
import java.sql.SQLException;
import java.util.Map;
import org.hibernate.exception.ConstraintViolationException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.security.access.AccessDeniedException;
@ -20,12 +16,15 @@ import org.zkoss.bind.annotation.NotifyChange;
import org.zkoss.zul.Messagebox;
import org.zkoss.zul.Window;
import java.sql.SQLException;
import java.util.Map;
public class FormViewModel<T extends DataModel> extends BindingViewModel<T>
{
private T dataBean;
private Map<String, String> errMessages;
private Service<T> service;
private boolean newRec;
protected boolean newRec;
private ServiceConstraint<T> constraint;
@Init

@ -4,9 +4,12 @@ import info.bukova.isspst.data.Role;
import info.bukova.isspst.data.User;
import info.bukova.isspst.data.Workgroup;
import info.bukova.isspst.services.invoicing.InvoicingService;
import info.bukova.isspst.services.limits.LimitService;
import info.bukova.isspst.services.users.UserService;
import info.bukova.isspst.services.workgroups.WorkgroupService;
import info.bukova.isspst.ui.DocumentViewModel;
import org.zkoss.bind.annotation.Init;
import org.zkoss.zk.ui.select.annotation.WireVariable;
import java.math.BigDecimal;
import java.util.ArrayList;
@ -14,9 +17,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.zkoss.bind.annotation.Init;
import org.zkoss.zk.ui.select.annotation.WireVariable;
public class DashBoardVM extends DocumentViewModel {
@WireVariable
@ -25,9 +25,12 @@ public class DashBoardVM extends DocumentViewModel {
private UserService userService;
@WireVariable
private InvoicingService invoicingService;
@WireVariable
private LimitService limitService;
private User user;
private Map<Workgroup, List<Role>> groupRoles;
private Map<Workgroup, BigDecimal> workgroupSpent;
private Map<Workgroup, BigDecimal> workgroupLimit;
@Init(superclass = true)
public void init() {
@ -35,6 +38,7 @@ public class DashBoardVM extends DocumentViewModel {
groupRoles = new HashMap<Workgroup, List<Role>>();
workgroupSpent = new HashMap<Workgroup, BigDecimal>();
workgroupLimit = new HashMap<Workgroup, BigDecimal>();
List<Workgroup> wg = new ArrayList<Workgroup>();
if (workgroupService.getUserCentres(user) != null) {
@ -48,6 +52,9 @@ public class DashBoardVM extends DocumentViewModel {
List<Role> r = workgroupService.getUserWorkgroupRoles(w, user);
groupRoles.put(w, r);
workgroupSpent.put(w, invoicingService.totalInvoicedForWorkgroup(w));
if (limitService.getForWorkgroup(w) != null) {
workgroupLimit.put(w, limitService.getForWorkgroup(w).getLimit());
}
}
}
@ -71,4 +78,8 @@ public class DashBoardVM extends DocumentViewModel {
return workgroupSpent;
}
public Map<Workgroup, BigDecimal> getWorkgroupLimit() {
return workgroupLimit;
}
}

@ -0,0 +1,43 @@
package info.bukova.isspst.ui.limits;
import info.bukova.isspst.data.Limit;
import info.bukova.isspst.data.Workgroup;
import info.bukova.isspst.services.limits.LimitService;
import info.bukova.isspst.services.settings.SeasonService;
import info.bukova.isspst.services.workgroups.WorkgroupService;
import info.bukova.isspst.ui.FormViewModel;
import org.zkoss.bind.annotation.Init;
import org.zkoss.zk.ui.select.annotation.WireVariable;
import java.util.List;
public class LimitsForm extends FormViewModel<Limit> {
List<Workgroup> workgroups;
@WireVariable
WorkgroupService workgroupService;
@WireVariable
LimitService limitService;
@WireVariable
SeasonService seasonService;
@Init(superclass = true)
public void init() {
workgroups = workgroupService.getWorkgroups();
if (getDataBean().getWorkgroup() == null) {
List<Limit> limits = limitService.getForSeason(seasonService.getActive());
for (Limit l : limits) {
workgroups.remove(l.getWorkgroup());
}
}
}
public List<Workgroup> getWorkgroups() {
return workgroups;
}
public boolean isNewRec() {
return newRec;
}
}

@ -0,0 +1,132 @@
package info.bukova.isspst.ui.limits;
import info.bukova.isspst.StringUtils;
import info.bukova.isspst.data.Limit;
import info.bukova.isspst.data.Workgroup;
import info.bukova.isspst.services.invoicing.InvoicingService;
import info.bukova.isspst.services.limits.LimitService;
import info.bukova.isspst.services.workgroups.WorkgroupService;
import info.bukova.isspst.ui.ListViewModel;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.BarRenderer3D;
import org.jfree.chart.title.LegendTitle;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.ui.RectangleEdge;
import org.zkoss.bind.annotation.GlobalCommand;
import org.zkoss.bind.annotation.Init;
import org.zkoss.bind.annotation.NotifyChange;
import org.zkoss.zk.ui.select.annotation.WireVariable;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.RenderedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
public class LimitsList extends ListViewModel<Limit> {
@WireVariable
private LimitService limitService;
@WireVariable
private InvoicingService invoicingService;
@WireVariable
private WorkgroupService workgroupService;
private Map<Limit, BigDecimal> spent;
private RenderedImage chart;
@Init(superclass = true)
public void init() {
service = limitService;
dataClass = Limit.class;
formZul = "limitForm.zul";
//dataFilter = new MUnitFilter(getFilterTemplate());
genSpent();
}
public Map<Limit, BigDecimal> getSpent() {
if (spent != null && !getDataList().isEmpty()) {
for (Limit l : spent.keySet()) {
if (!l.getSeason().equals(getDataList().get(0).getSeason())) {
spent = null;
}
break;
}
}
if (spent != null && spent.keySet().size() < getDataList().size()) {
spent = null;
}
if (spent != null) {
return spent;
}
spent = new HashMap<Limit, BigDecimal>();
for (Limit l : getDataList()) {
spent.put(l, invoicingService.totalInvoicedForWorkgroupAndSeason(l.getWorkgroup(), getSelSeason()));
}
return spent;
}
private void genSpent() {
spent = new HashMap<Limit, BigDecimal>();
for (Limit l : getDataList()) {
spent.put(l, invoicingService.totalInvoicedForWorkgroupAndSeason(l.getWorkgroup(), getSelSeason()));
}
}
private void genCharts() {
DefaultCategoryDataset dataSet = new DefaultCategoryDataset();
for (Workgroup wg : workgroupService.getWorkgroups()) {
Limit limit = null;
for (Limit l : getDataList()) {
if (l.getWorkgroup().equals(wg)) {
limit = l;
break;
}
}
dataSet.addValue(limit == null ? null : limit.getLimit(), StringUtils.localize("LimitsLimit"), wg.getFullName());
dataSet.addValue(invoicingService.totalInvoicedForWorkgroupAndSeason(wg, getSelSeason()), StringUtils.localize("LimitsSpent"), wg.getFullName());
}
JFreeChart jfChart = ChartFactory.createBarChart3D(StringUtils.localize("LimitsChartTitle"), StringUtils.localize("LimitsWorkgroup"),
StringUtils.localize("LimitsAmount"), dataSet, PlotOrientation.HORIZONTAL, true, false, false);
CategoryPlot plot = (CategoryPlot) jfChart.getPlot();
BarRenderer3D renderer = (BarRenderer3D) plot.getRenderer();
renderer.setSeriesPaint(0, Color.GREEN);
renderer.setSeriesPaint(1, Color.RED);
LegendTitle legend = jfChart.getLegend();
legend.setPosition(RectangleEdge.RIGHT);
try {
chart = ImageIO.read(new ByteArrayInputStream(ChartUtilities.encodeAsPNG(jfChart.createBufferedImage(1000, 400))));
} catch (IOException e) {
chart = null;
e.printStackTrace();
}
}
public RenderedImage getChart() {
genCharts();
return chart;
}
@Override
@GlobalCommand
@NotifyChange({ "dataList", "dataBean", "ableToDelete", "chart", "spent" })
public void refresh() {
genSpent();
super.refresh();
}
}

@ -39,5 +39,6 @@
<mapping class="info.bukova.isspst.data.SignedDocument"></mapping>
<mapping class="info.bukova.isspst.data.SignedDocumentItem"></mapping>
<mapping class="info.bukova.isspst.data.Season"></mapping>
<mapping class="info.bukova.isspst.data.Limit"></mapping>
</session-factory>
</hibernate-configuration>

@ -445,3 +445,12 @@ SeasonsPrefix = Prefix
SeasonsNewSeasonDialog = Opravdu chcete založit nové období?
Active_season_not_set = Není nastaveno žádné aktivní období.
LimitsAgName = Limity komisí
LimitsLimit = Limit
LimitsWorkgroup = Komise
LimitsSpent = Vyčerpáno
LimitsChartTitle = Vyčerpané částky
LimitsAmount = Částka

@ -289,6 +289,10 @@
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean id="limitDao" class="info.bukova.isspst.dao.jpa.LimitDaoJPA">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<!-- Business logic -->
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
@ -478,4 +482,8 @@
<bean id="seasonService" class="info.bukova.isspst.services.settings.SeasonServiceImpl">
<property name="dao" ref="seasonDao"/>
</bean>
<bean id="limitService" class="info.bukova.isspst.services.limits.LimitServiceImpl">
<property name="dao" ref="limitDao"/>
</bean>
</beans>

@ -0,0 +1,14 @@
<?page title="${labels.LimitsAgName}" contentType="text/html;charset=UTF-8"?>
<zk xmlns="http://www.zkoss.org/2005/zul"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.zkoss.org/2005/zul http://www.zkoss.org/2005/zul/zul.xsd">
<?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>
<zscript>
String gridZul = "limits.zul";
</zscript>
<include src="../../app/template.zhtml"/>
</zk>

@ -0,0 +1,46 @@
<?page title="${labels.TravelOrdersFormTitle}" contentType="text/html;charset=UTF-8"?>
<zk xmlns="http://www.zkoss.org/2005/zul"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.zkoss.org/2005/zul http://www.zkoss.org/2005/zul/zul.xsd">
<?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>
<window
id="editWin"
closable="true"
border="normal"
width="600px"
position="center"
apply="org.zkoss.bind.BindComposer"
viewModel="@id('vm') @init('info.bukova.isspst.ui.limits.LimitsForm')">
<caption
image="/img/invoicing-032.png"
zclass="form-caption"
label="${labels.LimitsLimit}" />
<grid>
<columns>
<column/>
<column/>
</columns>
<rows>
<row>
<label value="${labels.LimitsWorkgroup}"/>
<combobox
disabled="@load(not vm.newRec)"
width="150px"
model="@load(vm.workgroups)"
readonly="true"
selectedItem="@bind(vm.dataBean.workgroup)">
<template name="model">
<comboitem label="@load(each)" />
</template>
</combobox>
</row>
<row>
<label value="${labels.LimitsLimit}"/>
<textbox value="@bind(vm.dataBean.limit) @converter(vm.standardBigDecimalConverter)"/>
</row>
</rows>
</grid>
<include src="/app/formButtons.zul"/>
</window>
</zk>

@ -0,0 +1,53 @@
<?page title="${labels.LimitsAgName}" contentType="text/html;charset=UTF-8"?>
<zk xmlns="http://www.zkoss.org/2005/zul"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.zkoss.org/2005/zul http://www.zkoss.org/2005/zul/zul.xsd">
<?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>
<window
vflex="1"
border="normal"
apply="org.zkoss.bind.BindComposer"
viewModel="@id('vm') @init('info.bukova.isspst.ui.limits.LimitsList')">
<caption
src="/img/invoicing-032.png"
zclass="form-caption"
label="${labels.LimitsAgName}" />
<include src="/app/toolbar.zul" />
<listbox
vflex="1"
model="@load(vm.dataList)"
selectedItem="@bind(vm.dataBean)">
<listhead menupopup="auto">
<listheader
label="${labels.LimitsWorkgroup}"
sort="czech(name)"
width="30%" />
<listheader
label="${labels.LimitsLimit}"
sort="czech(description)"
align="right"
width="70%" />
<listheader
label="${labels.LimitsSpent}"
sort="czech(description)"
align="right"
width="70%" />
</listhead>
<template name="model">
<listitem>
<listcell label="@load(each.workgroup)" />
<listcell label="@load(each.limit) @converter(vm.standardBigDecimalConverter)" />
<listcell label="@load(vm.spent[each]) @converter(vm.standardBigDecimalConverter)"/>
</listitem>
</template>
</listbox>
<div align="center" vflex="1">
<image height="100%" content="@load(vm.chart)"/>
</div>
</window>
</zk>

@ -103,7 +103,7 @@
</listhead>
<listitem>
<listcell label="${labels.Limit}" />
<listcell label="@load(each.limit) @converter(vm.standardBigDecimalConverter)" />
<listcell label="@load(vm.workgroupLimit[each]) @converter(vm.standardBigDecimalConverter)" />
</listitem>
<listitem>
<listcell label="${labels.Spent}" />

@ -130,6 +130,10 @@
label="${labels.AgendaWorkgroups}"
href="/admin/workgroups"
disabled="${not sec:isAllGranted('PERM_READ_WORKGROUPS')}" />
<menuitem
label="${labels.LimitsAgName}"
href="/admin/limits"
disabled="${not sec:isAllGranted('PERM_READ_LIMITS')}" />
</menupopup>
</menu>
</menupopup>

Loading…
Cancel
Save