- •Курсовой проект (работа)
- •Задание на курсовой проект (работу)
- •Содержание
- •Введение
- •Планирование разработки проекта
- •Описание проектирования Диаграмма вариантов использования
- •Требования к функциям, выполняемые системой
- •Требования к надёжности
- •Методология
- •Стратегия Разработка общей модели
- •Составление списка необходимых функций системы
- •Планирование работы над каждой функцией
- •Проектирование функции
- •Реализация функции
- •Используемые языки
- •5. Тестирование дневника расходов и поступлений
- •Приложение 1. Код программы
- •Приложение 2. Скриншоты интерфейса
5. Тестирование дневника расходов и поступлений
Выполнение действий 4.1 и 4.2. Переход в дневник. Отображается сегодняшняя дата: 9.03.2015. Отображается действие 4.1, баланс: 1000, количество: 1. Смена даты на 10.03.2015. Отображается действие 3.2, баланс: 500, количество: 1.
Добавление платежа – сумма: -200, категория: «Eat». Платёж успешно добавлен, отображается баланс – 300, количество: 2.
6. Тестирование списка долгов и списка выплат
Переход на страницу списка долгов. Добавление долга – дата: 10.03.2015, окончание: 10.03.2016, сумма: 100000, сумма выплаты: 1000, частота выплат: 1 месяц, категория: «Relax». Долг успешно добавлен и отображён в списке, также успешно добавлены и отображены 12 выплат суммой по 1000.
Удаление долга – после нажатии кнопки удаления долг был успешно удалён.
7. Тестирование графика изменения баланса
Выполненение действий 4.1, 4.2, 5.2. Добавление долга – дата: 10.03.2015, окончание 10.03.2016, сумма: 10000, сумма выплаты: 2000, частота выплат: 1 день, категория: «Auto». На странице графика выбираем диапазон отображается следующая динамика баланса: 800, 10300, 8300, 6300, 4300, 2300.
8. Тестирование графика сравнения дней по сумме финансовых операций
Выполнение действий из пункта 7. Полученный график: 800, 9500, -2000, -2000, -2000, -2000.
9. Тестирование графика сравнения дней по сумме задолженностей
Выполнение действий 7.
10. Тестирование графика сравнения категорий по сумме финансовых операций
Выполнение действий 4.1, 4.2, 5.2. Полученный график: 1000, -500, -200.
Заключение
В ходе выполнения курсовой работы были использованы гибкие методологии разработки продукта, язык программирования – Java. На выходе получен продукт, удовлетворяющий потребности заказчика.
Выбранная методология позволила сформировать жесткие сроки на создание функционала программы, а также обеспечить достаточное количество времени на каждую итерацию. Причём все заданные функции выполнены в срок, без каких-либо накладок и выбора наиболее и наименее важных функций.
Процесс конструирования позволил избежать накладок при работе приложения(например, введения некорректных данных и “падения” программы), а также доработать диалоговые окна для взаимодействия с пользователем и протестировать корректность работы БД с помощью тестовых данных. Соответственно данные действия повысили качество программы.
Приложение 1. Код программы
BalanceBacking.java
package ru.ulstu.secretary.chart;
import java.util.Date;
import javax.faces.view.ViewScoped;
import javax.inject.Inject;
import javax.inject.Named;
import ru.ulstu.secretary.db.ActionByDateService;
@Named
@ViewScoped
public class BalanceBacking extends DateChartBacking {
private static final long serialVersionUID = 8470304964338154695L;
@Inject
private ActionByDateService actionByDateService;
protected double getNextY(double y, Date x) {
return y + actionByDateService.getList(x).stream().mapToDouble(task -> task.getBalance()).sum();
}
@Override
protected String getRedirect() {
return "diary";
}
}
CategoryBalanceBacking.cs
package ru.ulstu.secretary.chart;
import javax.faces.view.ViewScoped;
import javax.inject.Inject;
import javax.inject.Named;
import ru.ulstu.secretary.db.CategoryService;
import ru.ulstu.secretary.db.TaskByCategoryService;
import ru.ulstu.secretary.entity.Category;
@Named
@ViewScoped
public class CategoryBalanceBacking extends ChartBacking<Category> {
private static final long serialVersionUID = 1L;
@Inject
private CategoryService categoryService;
@Inject
private TaskByCategoryService taskByCategoryService;
@Override
protected Iterable<Category> getRangeOfX() {
return categoryService.getList();
}
@Override
protected double getNextY(double y, Category x) {
return taskByCategoryService.getList(x).stream().mapToDouble(task -> task.getBalance()).sum();
}
@Override
protected String getRedirect() {
return "";
}
@Override
protected void prepareRedirect(Category category) {
}
}
ChartBacking.java
package ru.ulstu.secretary.chart;
import java.io.Serializable;
import org.primefaces.model.chart.BarChartModel;
import org.primefaces.model.chart.ChartSeries;
public abstract class ChartBacking<X> implements Serializable {
private static final long serialVersionUID = 5722395681760535076L;
protected abstract Iterable<X> getRangeOfX();
protected abstract double getNextY(double y, X x);
public BarChartModel getModel() {
BarChartModel model = new BarChartModel();
ChartSeries series = new ChartSeries();
double y = 0;
for (X x : getRangeOfX()) {
y = getNextY(y, x);
series.set(x, y);
}
model.addSeries(series);
return model;
}
public X getMin() {
X result = null;
double resultValue = Double.MAX_VALUE;
double y = 0;
for (X x : getRangeOfX()) {
y = getNextY(y, x);
if (y <= resultValue) {
resultValue = y;
result = x;
}
}
return result;
}
public X getMax() {
X result = null;
double resultValue = Double.MIN_VALUE;
double y = 0;
for (X x : getRangeOfX()) {
y = getNextY(y, x);
if (y >= resultValue) {
resultValue = y;
result = x;
}
}
return result;
}
public double getMinValue() {
double resultValue = Double.MAX_VALUE;
double y = 0;
for (X x : getRangeOfX()) {
y = getNextY(y, x);
if (y <= resultValue) {
resultValue = y;
}
}
return resultValue;
}
public double getMaxValue() {
double resultValue = Double.MIN_VALUE;
double y = 0;
for (X x : getRangeOfX()) {
y = getNextY(y, x);
if (y >= resultValue) {
resultValue = y;
}
}
if (resultValue == Double.MIN_VALUE) {
return 0;
}
return resultValue;
}
protected abstract void prepareRedirect(X selected);
protected abstract String getRedirect();
public String redirect(X selected) {
prepareRedirect(selected);
String redirect = getRedirect() + "?faces-redirect=true";
return redirect;
}
}
DateChartBacking.java
package ru.ulstu.secretary.chart;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import ru.ulstu.secretary.grid.DateBacking;
import ru.ulstu.secretary.utils.DateUtil;
public abstract class DateChartBacking extends ChartBacking<Date> {
private static final long serialVersionUID = -5928036826842490016L;
private Date from;
private Date to;
@Inject
private DateBacking dateBacking;
@PostConstruct
private void init() {
from = DateUtil.getToday();
to = DateUtil.getToday();
change(-1, 1);
}
@Override
protected Iterable<Date> getRangeOfX() {
long dayCount = TimeUnit.DAYS.convert(to.getTime() - from.getTime(), TimeUnit.MILLISECONDS) + 1;
return () -> IntStream.iterate(0, i -> i + 1).limit(dayCount).mapToObj(i -> {
Calendar calendar = new GregorianCalendar();
calendar.setTime(from);
calendar.add(Calendar.DATE, i);
return calendar.getTime();
}).iterator();
}
public Date getFrom() {
return from;
}
public void setFrom(Date from) {
this.from = from;
}
public Date getTo() {
return to;
}
public void setTo(Date to) {
this.to = to;
}
public void change(int left, int right) {
Calendar calendar = new GregorianCalendar();
calendar.setTime(from);
calendar.add(Calendar.DATE, left);
from = calendar.getTime();
calendar.setTime(to);
calendar.add(Calendar.DATE, right);
to = calendar.getTime();
}
@Override
protected void prepareRedirect(Date selected) {
dateBacking.setDate(selected);
}
}
DayBacking.java
package ru.ulstu.secretary.chart;
import java.util.Date;
import javax.faces.view.ViewScoped;
import javax.inject.Inject;
import javax.inject.Named;
import ru.ulstu.secretary.db.ActionByDateService;
@Named
@ViewScoped
public class DayBacking extends DateChartBacking {
private static final long serialVersionUID = 8470304964338154695L;
@Inject
private ActionByDateService actionByDateService;
protected double getNextY(double y, Date x) {
return actionByDateService.getList(x).stream().mapToDouble(task -> task.getBalance()).sum();
}
@Override
protected String getRedirect() {
return "diary";
}
}
UnpaidBacking.java
package ru.ulstu.secretary.chart;
import java.util.Date;
import javax.faces.view.ViewScoped;
import javax.inject.Inject;
import javax.inject.Named;
import ru.ulstu.secretary.db.EntityService;
import ru.ulstu.secretary.entity.Debt;
@Named
@ViewScoped
public class UnpaidBacking extends DateChartBacking {
private static final long serialVersionUID = 981680502855109121L;
@Inject
private EntityService entityService;
@Override
protected double getNextY(double y, Date x) {
return entityService.getList(Debt.class).stream().mapToDouble(debt -> debt.getUnpaid(x)).sum();
}
@Override
protected String getRedirect() {
return "diary";
}
}
CriteriaService.java
package ru.ulstu.secretary.criteria;
import java.util.Collection;
import javax.annotation.PostConstruct;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
public abstract class CriteriaService<Entity> {
@PersistenceContext
private EntityManager entityManager;
private CriteriaBuilder builder;
private CriteriaQuery<Entity> query;
private Root<Entity> root;
@PostConstruct
private void initCriteriaService() {
builder = entityManager.getCriteriaBuilder();
query = builder.createQuery(getType());
root = query.from(getType());
}
protected abstract Class<Entity> getType();
protected CriteriaBuilder getBuilder() {
return builder;
}
protected CriteriaQuery<Entity> getQuery() {
return query;
}
protected Root<Entity> getRoot() {
return root;
}
protected Collection<Entity> getList(Predicate restriction) {
query.where(restriction);
return entityManager.createQuery(query).getResultList();
}
}
PropertyCriteriaService.java
package ru.ulstu.secretary.criteria;
import java.util.Collection;
public abstract class PropertyCriteriaService<Entity, Property> extends CriteriaService<Entity> {
public Collection<Entity> getList(Property property) {
return getList(getBuilder().equal(getRoot().get(getProperty()), property));
}
protected abstract String getProperty();
}
RangeCriteriaService.java
package ru.ulstu.secretary.criteria;
import java.util.Collection;
public abstract class RangeCriteriaService<Entity, Property extends Comparable<Property>> extends CriteriaService<Entity> {
public Collection<Entity> getAll(Property from, Property to) {
return getList(getBuilder().between(getRoot().get(getProperty()), from, to));
}
protected abstract String getProperty();
}
UpperLimitCriteriaService.java
package ru.ulstu.secretary.criteria;
import java.util.Collection;
public abstract class UpperLimitCriteriaService<X extends Comparable<X>, Y> extends CriteriaService<Y> {
public Collection<Y> getList(X x) {
return getList(getBuilder().lessThanOrEqualTo(getRoot().get(getProperty()), x));
}
protected abstract String getProperty();
}
ActionByDateService.java
package ru.ulstu.secretary.db;
import java.util.Date;
import javax.ejb.Stateless;
import ru.ulstu.secretary.criteria.PropertyCriteriaService;
import ru.ulstu.secretary.entity.Action;
@Stateless
public class ActionByDateService extends PropertyCriteriaService<Action, Date> {
@Override
protected String getProperty() {
return "date";
}
@Override
protected Class<Action> getType() {
return Action.class;
}
}
CategoryService.java
package ru.ulstu.secretary.db;
import java.util.Arrays;
import javax.ejb.Stateless;
import javax.inject.Named;
import ru.ulstu.secretary.entity.Category;
@Named
@Stateless
public class CategoryService {
public Iterable<Category> getList() {
return Arrays.asList(Category.values());
}
}
DebtByDateService.java
package ru.ulstu.secretary.db;
import java.util.Date;
import javax.ejb.Stateless;
import ru.ulstu.secretary.criteria.UpperLimitCriteriaService;
import ru.ulstu.secretary.entity.Debt;
@Stateless
public class DebtByDateService extends UpperLimitCriteriaService<Date, Debt> {
@Override
protected String getProperty() {
return "date";
}
@Override
protected Class<Debt> getType() {
return Debt.class;
}
}
EntityService.java
package ru.ulstu.secretary.db;
import java.util.Collection;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
@Stateless
public class EntityService {
@PersistenceContext
private EntityManager entityManager;
public <T> Collection<T> getList(Class<T> type) {
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<T> query = builder.createQuery(type);
query.from(type);
return entityManager.createQuery(query).getResultList();
}
public <T> T save(T entity) {
return entityManager.merge(entity);
}
public <T> void remove(T entity) {
entity = entityManager.merge(entity);
entityManager.remove(entity);
}
}
PayByDateService.java
package ru.ulstu.secretary.db;
import java.util.Date;
import javax.ejb.Stateless;
import ru.ulstu.secretary.criteria.PropertyCriteriaService;
import ru.ulstu.secretary.entity.Pay;
@Stateless
public class PayByDateService extends PropertyCriteriaService<Pay, Date> {
@Override
protected String getProperty() {
return "date";
}
@Override
protected Class<Pay> getType() {
return Pay.class;
}
}
RepayByDateService.java
package ru.ulstu.secretary.db;
import java.util.Date;
import javax.ejb.Stateless;
import ru.ulstu.secretary.criteria.PropertyCriteriaService;
import ru.ulstu.secretary.entity.Repay;
@Stateless
public class RepayByDateService extends PropertyCriteriaService<Repay, Date> {
@Override
protected String getProperty() {
return "date";
}
@Override
protected Class<Repay> getType() {
return Repay.class;
}
}
TaskByCategoryService.java
package ru.ulstu.secretary.db;
import javax.ejb.Stateless;
import ru.ulstu.secretary.criteria.PropertyCriteriaService;
import ru.ulstu.secretary.entity.Category;
import ru.ulstu.secretary.entity.Task;
@Stateless
public class TaskByCategoryService extends PropertyCriteriaService<Task, Category> {
@Override
protected String getProperty() {
return "category";
}
@Override
protected Class<Task> getType() {
return Task.class;
}
}
TaskByDateService.java
package ru.ulstu.secretary.db;
import java.util.Date;
import javax.ejb.Stateless;
import ru.ulstu.secretary.criteria.PropertyCriteriaService;
import ru.ulstu.secretary.entity.Task;
@Stateless
public class TaskByDateService extends PropertyCriteriaService<Task, Date> {
@Override
protected String getProperty() {
return "date";
}
@Override
protected Class<Task> getType() {
return Task.class;
}
}
UnitService.java
package ru.ulstu.secretary.db;
import javax.ejb.Stateless;
import javax.inject.Named;
import ru.ulstu.secretary.entity.Unit;
@Named
@Stateless
public class UnitService {
public Unit[] getList() {
return Unit.values();
}
}
Action.java
package ru.ulstu.secretary.entity;
import java.util.Date;
import javax.persistence.Entity;
@Entity
public class Action extends BaseEntity {
private Date date;
private Double balance;
private String comment;
private Category category;
private boolean complete;
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public Double getBalance() {
return balance;
}
public void setBalance(Double money) {
this.balance = money;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public Category getCategory() {
return category;
}
public void setCategory(Category category) {
this.category = category;
}
public boolean isComplete() {
return complete;
}
public void setComplete(boolean complete) {
this.complete = complete;
}
}
BaseEntity.java
package ru.ulstu.secretary.entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
@MappedSuperclass
public class BaseEntity {
@Id
@GeneratedValue
private int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
Category.java
package ru.ulstu.secretary.entity;
public enum Category {
AUTO("auto"),
HOME("home");
private final String i18n;
private Category(String i18n) {
this.i18n = i18n;
}
public String toString() {
return i18n;
}
}
Debt.java
package ru.ulstu.secretary.entity;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.OneToMany;
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Debt extends Action {
private Date deadline;
private Double repay;
private Integer frequency;
private Unit unit;
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<Repay> repays;
public Debt() {
unit = Unit.DAY;
repays = new ArrayList<>();
}
public Date getDeadline() {
return deadline;
}
public void setDeadline(Date deadline) {
this.deadline = deadline;
}
public Double getRepay() {
return repay;
}
public void setRepay(Double repay) {
this.repay = repay;
}
public Integer getFrequency() {
return frequency;
}
public void setFrequency(Integer frequency) {
this.frequency = frequency;
}
public Unit getUnit() {
return unit;
}
public void setUnit(Unit unit) {
this.unit = unit;
}
public List<Repay> getRepays() {
return repays;
}
public void setRepays(List<Repay> repays) {
this.repays = repays;
}
public double getUnpaid(Date moment) {
if (moment.before(getDate())) {
return 0;
}
double sum = repays.stream().filter(repay -> !repay.getDate().after(moment)).mapToDouble(repay -> repay.getBalance()).sum();
return Math.max(0, getBalance() + sum);
}
}
Pay.java
package ru.ulstu.secretary.entity;
import javax.persistence.Entity;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Pay extends Task {}
Repay.java
package ru.ulstu.secretary.entity;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.ManyToOne;
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Repay extends Task {
@ManyToOne(cascade = CascadeType.ALL)
private Debt debt;
public Debt getDebt() {
return debt;
}
public void setDebt(Debt debt) {
this.debt = debt;
}
}
Task.java
package ru.ulstu.secretary.entity;
import javax.persistence.Entity;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Task extends Action {}
Unit.java
package ru.ulstu.secretary.entity;
import java.util.Calendar;
public enum Unit {
YEAR("year", Calendar.YEAR),
MONTH("month", Calendar.MONTH),
WEEK("week", Calendar.WEEK_OF_MONTH),
DAY("day", Calendar.DAY_OF_WEEK);
private final String i18n;
private final int code;
private Unit(String i18n, int code) {
this.i18n = i18n;
this.code = code;
}
public int getCode() {
return code;
}
public String toString() {
return i18n;
}
}
DateBacking.java
package ru.ulstu.secretary.grid;
import java.io.Serializable;
import java.util.Date;
import javax.annotation.PostConstruct;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
import ru.ulstu.secretary.utils.DateUtil;
@Named
@SessionScoped
public class DateBacking implements Serializable {
private static final long serialVersionUID = -7664901585738595118L;
private Date date;
@PostConstruct
private void init() {
date = DateUtil.getToday();
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}
DebtBacking.java
package ru.ulstu.secretary.grid;
import java.util.Calendar;
import java.util.GregorianCalendar;
import javax.faces.view.ViewScoped;
import javax.inject.Named;
import ru.ulstu.secretary.entity.Debt;
import ru.ulstu.secretary.entity.Repay;
import ru.ulstu.secretary.utils.DateUtil;
@Named
@ViewScoped
public class DebtBacking extends GridBacking<Debt> {
private static final long serialVersionUID = 714120325390373523L;
private Debt debt;
@Override
protected Debt getNewBlank() {
Debt debt = new Debt();
debt.setDate(DateUtil.getToday());
return debt;
}
@Override
protected Class<Debt> getType() {
return Debt.class;
}
@Override
public void save() throws Exception {
Calendar calendar = new GregorianCalendar();
calendar.setTime(getBlank().getDate());
while (true) {
calendar.add(getBlank().getUnit().getCode(), 1);
if (calendar.getTime().after(getBlank().getDeadline())) {
break;
}
Repay repay = new Repay();
repay.setDate(calendar.getTime());
repay.setCategory(getBlank().getCategory());
repay.setBalance(-getBlank().getRepay());
getBlank().getRepays().add(repay);
}
debt = getBlank();
super.save();
}
public Debt getDebt() {
return debt;
}
public void setDebt(Debt debt) {
this.debt = debt;
}
}
DiaryPayBacking.java
package ru.ulstu.secretary.grid;
import java.util.Collection;
import javax.faces.view.ViewScoped;
import javax.inject.Inject;
import javax.inject.Named;
import ru.ulstu.secretary.db.ActionByDateService;
import ru.ulstu.secretary.db.PayByDateService;
import ru.ulstu.secretary.entity.Pay;
@Named
@ViewScoped
public class DiaryPayBacking extends GridBacking<Pay> {
private static final long serialVersionUID = -3370333139341916306L;
@Inject
private DateBacking dateBacking;
@Inject
private ActionByDateService actionByDateService;
@Inject
private PayByDateService payByDateService;
@Override
protected Class<Pay> getType() {
return Pay.class;
}
@Override
protected Pay getNewBlank() {
Pay pay = new Pay();
pay.setDate(dateBacking.getDate());
return pay;
}
@Override
public Collection<Pay> getList() {
return payByDateService.getList(dateBacking.getDate());
}
public double getBalance() {
return actionByDateService.getList(dateBacking.getDate()).stream().mapToDouble(task -> task.getBalance()).sum();
}
}
DiaryRepayBacking.java
package ru.ulstu.secretary.grid;
import java.util.Collection;
import javax.faces.view.ViewScoped;
import javax.inject.Inject;
import javax.inject.Named;
import ru.ulstu.secretary.db.RepayByDateService;
import ru.ulstu.secretary.entity.Repay;
@Named
@ViewScoped
public class DiaryRepayBacking extends GridBacking<Repay> {
private static final long serialVersionUID = 8966176107954231754L;
@Inject
private DateBacking dateBacking;
@Inject
private RepayByDateService repayByDateService;
@Override
protected Class<Repay> getType() {
return Repay.class;
}
public Collection<Repay> getList() {
return repayByDateService.getList(dateBacking.getDate());
}
}
GridBacking.java
package ru.ulstu.secretary.grid;
import java.io.Serializable;
import java.util.Collection;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import ru.ulstu.secretary.db.EntityService;
public abstract class GridBacking<Entity> implements Serializable {
private static final long serialVersionUID = -2639437488394357753L;
@Inject
private EntityService entityService;
private Entity blank;
@PostConstruct
private void init() throws Exception {
blank = getNewBlank();
}
protected abstract Class<Entity> getType();
protected Entity getNewBlank() throws Exception {
return getType().getConstructor().newInstance();
}
public Collection<Entity> getList() {
return entityService.getList(getType());
}
public Entity getBlank() {
return blank;
}
public void save() throws Exception {
entityService.save(blank);
blank = getNewBlank();
}
public void remove(Entity obj) {
entityService.remove(obj);
}
}
PayBacking.java
package ru.ulstu.secretary.grid;
import javax.faces.view.ViewScoped;
import javax.inject.Named;
import ru.ulstu.secretary.entity.Pay;
import static ru.ulstu.secretary.utils.DateUtil.getToday;
@Named
@ViewScoped
public class PayBacking extends GridBacking<Pay> {
private static final long serialVersionUID = -520158973748994729L;
@Override
protected Class<Pay> getType() {
return Pay.class;
}
protected Pay getNewBlank() {
Pay pay = new Pay();
pay.setDate(getToday());
return pay;
}
public int getCount() {
return getList().size();
}
public double getBalance() {
return getList().stream().mapToDouble(pay -> pay.getBalance()).sum();
}
}
RepayBacking.java
package ru.ulstu.secretary.grid;
import java.util.Collection;
import javax.faces.view.ViewScoped;
import javax.inject.Inject;
import javax.inject.Named;
import ru.ulstu.secretary.entity.Repay;
@Named
@ViewScoped
public class RepayBacking extends GridBacking<Repay> {
private static final long serialVersionUID = -5042825198701507477L;
@Inject
private DebtBacking debtBacking;
@Override
protected Class<Repay> getType() {
return Repay.class;
}
@Override
public Collection<Repay> getList() {
return debtBacking.getDebt() == null ? null : debtBacking.getDebt().getRepays();
}
}
TodayBacking.java
package ru.ulstu.secretary.grid;
import java.io.Serializable;
import javax.faces.view.ViewScoped;
import javax.inject.Inject;
import javax.inject.Named;
import ru.ulstu.secretary.db.TaskByDateService;
import ru.ulstu.secretary.utils.DateUtil;
@Named
@ViewScoped
public class TodayBacking implements Serializable {
private static final long serialVersionUID = -6983619217861517215L;
@Inject
private TaskByDateService taskByDateService;
public int getCount() {
return taskByDateService.getList(DateUtil.getToday()).size();
}
}
DateUtil.java
package ru.ulstu.secretary.utils;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
public class DateUtil {
public static Date getToday() {
Calendar calendar = new GregorianCalendar();
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
return calendar.getTime();
}
}
Balance.xhtml
<ui:composition template="/WEB-INF/tpl/base.xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:p="http://primefaces.org/ui" xmlns:s="http://java.sun.com/jsf/composite/secretary">
<ui:define name="title">#{i18n.balance}</ui:define>
<ui:define name="body">
<s:chart backing="#{balanceBacking}"/>
</ui:define>
</ui:composition>
Category.xhtml
<ui:composition template="/WEB-INF/tpl/base.xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:p="http://primefaces.org/ui" xmlns:s="http://java.sun.com/jsf/composite/secretary">
<ui:define name="title">#{i18n.category}</ui:define>
<ui:define name="body">
<s:chart backing="#{categoryBalanceBacking}" hasPeriod="false"/>
</ui:define>
</ui:composition>
Day.xhtml
<ui:composition template="/WEB-INF/tpl/base.xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:s="http://java.sun.com/jsf/composite/secretary">
<ui:define name="title">#{i18n.day}</ui:define>
<ui:define name="body">
<s:chart backing="#{dayBacking}"/>
</ui:define>
</ui:composition>
Debt.xhtml
<ui:composition template="/WEB-INF/tpl/base.xhtml" xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:p="http://primefaces.org/ui" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:a="http://xmlns.jcp.org/jsf/passthrough" xmlns:s="http://java.sun.com/jsf/composite/secretary">
<ui:define name="title">Debt</ui:define>
<ui:define name="body">
<s:grid name="debt" backing="#{debtBacking}"/>
<s:grid name="repay" backing="#{repayBacking}" editable="false"/>
</ui:define>
</ui:composition>
Diary.xhtml
<ui:composition template="/WEB-INF/tpl/base.xhtml" xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:p="http://primefaces.org/ui" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:a="http://xmlns.jcp.org/jsf/passthrough" xmlns:s="http://java.sun.com/jsf/composite/secretary">
<ui:define name="title">#{i18n.diary}</ui:define>
<ui:define name="body">
<div class="panel panel-info">
<div class="panel-heading"><span class="glyphicon glyphicon-calendar"/> #{i18n.date}</div>
<div class="panel-body">
<div class="form-inline">
<h:inputText styleClass="form-control" value="#{dateBacking.date}">
<f:convertDateTime pattern="yyyy-MM-dd"/>
</h:inputText>
<h:commandButton styleClass="btn btn-primary" value="#{i18n.see}"/>
</div>
</div>
</div>
<s:grid name="diary" backing="#{diaryPayBacking}"/>
<s:grid name="repay" backing="#{diaryRepayBacking}" editable="false"/>
<div class="panel panel-info">
<div class="panel-heading"><span class="glyphicon glyphicon-ok"/> #{i18n.total}</div>
<div class="panel-body">
<p><strong>#{i18n.balance}</strong>: #{diaryPayBacking.balance}</p>
</div>
</div>
</ui:define>
</ui:composition>
Pay.xhtml
<ui:composition template="/WEB-INF/tpl/base.xhtml" xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:p="http://primefaces.org/ui" xmlns:s="http://java.sun.com/jsf/composite/secretary">
<ui:define name="title">#{i18n.pay}</ui:define>
<ui:define name="body">
<s:grid name="pay" backing="#{payBacking}"/>
<div class="panel panel-info">
<div class="panel-heading"><span class="glyphicon glyphicon-ok"/> #{i18n.total}</div>
<div class="panel-body">
<p><strong>#{i18n.count}</strong>: #{payBacking.count}</p>
</div>
</div>
</ui:define>
</ui:composition>
Unpaid.xhtml
<ui:composition template="/WEB-INF/tpl/base.xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:s="http://java.sun.com/jsf/composite/secretary">
<ui:define name="title">#{i18n.unpaid}</ui:define>
<ui:define name="body">
<s:chart backing="#{unpaidBacking}"/>
</ui:define>
</ui:composition>
Chart.xhtml
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:c="http://java.sun.com/jsf/composite" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:p="http://primefaces.org/ui" xmlns:f="http://java.sun.com/jsf/core">
<c:interface>
<c:attribute name="backing"/>
<c:attribute name="hasPeriod" default="true"/>
</c:interface>
<c:implementation>
<ui:fragment rendered="#{cc.attrs.hasPeriod}">
<div class="panel panel-info">
<div class="panel-heading"><span class="glyphicon glyphicon-calendar"/> #{i18n.date}</div>
<div class="panel-body">
<div class="form-inline">
<h:inputText styleClass="form-control" value="#{cc.attrs.backing.from}">
<f:convertDateTime pattern="yyyy-MM-dd"/>
</h:inputText>
<h:inputText styleClass="form-control" value="#{cc.attrs.backing.to}">
<f:convertDateTime pattern="yyyy-MM-dd"/>
</h:inputText>
<p:commandButton styleClass="btn btn-primary" ajax="none" value="#{i18n.see}"/>
</div>
</div>
</div>
</ui:fragment>
<div class="panel panel-info" style="overflow: hidden">
<div class="panel-heading"><span class="glyphicon glyphicon-th-list"/> #{i18n.graphics}</div>
<div class="panel-body">
<p:chart type="bar" model="#{cc.attrs.backing.model}"/>
</div>
</div>
<div class="panel panel-info">
<div class="panel-heading"><span class="glyphicon glyphicon-ok"/> #{i18n.total}</div>
<div class="panel-body">
<p><p:commandButton styleClass="btn btn-primary" action="#{cc.attrs.backing.redirect(cc.attrs.backing.min)}" value="#{i18n.see}"/> #{i18n.min}: #{cc.attrs.backing.minValue}</p>
<p><p:commandButton styleClass="btn btn-primary" action="#{cc.attrs.backing.redirect(cc.attrs.backing.max)}" value="#{i18n.see}"/> #{i18n.max}: #{cc.attrs.backing.maxValue}</p>
</div>
</div>
</c:implementation>
</html>
Grid.xhtml
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:c="http://java.sun.com/jsf/composite" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:p="http://primefaces.org/ui" xmlns:f="http://java.sun.com/jsf/core">
<c:interface>
<c:attribute name="name"/>
<c:attribute name="backing"/>
<c:attribute name="icon" default="glyphicon glyphicon-th-list"/>
<c:attribute name="editable" default="true"/>
</c:interface>
<c:implementation>
<div class="panel panel-info">
<div class="panel-heading"><span class="#{cc.attrs.icon}"/> #{i18n[cc.attrs.name]}</div>
<p:dataTable tableStyleClass="table" value="#{cc.attrs.backing.list}" var="obj">
<ui:include src="/WEB-INF/grid/#{cc.attrs.name}.xhtml"/>
<p:column rendered="#{cc.attrs.editable}" style="width: 1%">
<p:commandButton style="display: inline-block" value="" ajax="none" icon="glyphicon glyphicon-remove" styleClass="btn btn-danger" action="#{cc.attrs.backing.remove(obj)}"/>
<f:facet name="footer">
<p:commandButton style="display: inline-block" value="" ajax="none" icon="glyphicon glyphicon-plus" styleClass="btn btn-primary" action="#{cc.attrs.backing.save}"/>
</f:facet>
</p:column>
</p:dataTable>
</div>
</c:implementation>
</html>
Debt.xhtml
<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:p="http://primefaces.org/ui" xmlns:h="http://java.sun.com/jsf/html" xmlns:a="http://xmlns.jcp.org/jsf/passthrough" xmlns:f="http://java.sun.com/jsf/core">
<ui:include src="/WEB-INF/grid/pay.xhtml"/>
<p:column headerText="#{i18n.deadline}">
<h:outputText value="#{obj.deadline}">
<f:convertDateTime pattern="yyyy-MM-dd"/>
</h:outputText>
<f:facet name="footer">
<h:inputText styleClass="form-control" value="#{cc.attrs.backing.blank.deadline}" a:placeholder="#{i18n.deadline}">
<f:convertDateTime pattern="yyyy-MM-dd"/>
</h:inputText>
</f:facet>
</p:column>
<p:column headerText="#{i18n.repay}">
#{obj.repay}
<f:facet name="footer">
<h:inputText styleClass="form-control" value="#{cc.attrs.backing.blank.repay}" a:placeholder="#{i18n.repay}"/>
</f:facet>
</p:column>
<p:column headerText="#{i18n.frequency}">
#{obj.frequency}
<f:facet name="footer">
<h:inputText styleClass="form-control" value="#{cc.attrs.backing.blank.frequency}" a:placeholder="#{i18n.frequency}"/>
</f:facet>
</p:column>
<p:column headerText="#{i18n.unit}">
#{i18n[obj.unit]}
<f:facet name="footer">
<h:selectOneMenu styleClass="form-control" value="#{cc.attrs.backing.blank.unit}">
<f:selectItems value="#{unitService.list}" var="unit" itemLabel="#{i18n[unit]}"/>
</h:selectOneMenu>
</f:facet>
</p:column>
</ui:composition>
Diary.xhtml
<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:p="http://primefaces.org/ui" xmlns:h="http://java.sun.com/jsf/html" xmlns:a="http://xmlns.jcp.org/jsf/passthrough" xmlns:f="http://java.sun.com/jsf/core">
<p:column headerText="#{i18n.balance}">
#{obj.balance}
<f:facet name="footer">
<h:inputText styleClass="form-control" value="#{cc.attrs.backing.blank.balance}" a:placeholder="#{i18n.balance}"/>
</f:facet>
</p:column>
<p:column headerText="#{i18n.category}">
#{i18n[obj.category]}
<f:facet name="footer">
<h:selectOneMenu styleClass="form-control" value="#{cc.attrs.backing.blank.category}">
<f:selectItems value="#{categoryService.list}" var="category" itemLabel="#{i18n[category]}"/>
</h:selectOneMenu>
</f:facet>
</p:column>
<p:column headerText="#{i18n.comment}">
#{obj.comment}
<f:facet name="footer">
<h:inputText styleClass="form-control" value="#{cc.attrs.backing.blank.comment}" a:placeholder="#{i18n.comment}"/>
</f:facet>
</p:column>
</ui:composition>
Pay.xhtml
<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:p="http://primefaces.org/ui" xmlns:h="http://java.sun.com/jsf/html" xmlns:a="http://xmlns.jcp.org/jsf/passthrough" xmlns:f="http://java.sun.com/jsf/core">
<p:column headerText="#{i18n.date}">
<h:outputText value="#{obj.date}">
<f:convertDateTime pattern="yyyy-MM-dd"/>
</h:outputText>
<f:facet name="footer">
<h:inputText styleClass="form-control" value="#{cc.attrs.backing.blank.date}" a:placeholder="#{i18n.date}">
<f:convertDateTime pattern="yyyy-MM-dd"/>
</h:inputText>
</f:facet>
</p:column>
<ui:include src="/WEB-INF/grid/diary.xhtml"/>
</ui:composition>
Repay.xhtml
<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:p="http://primefaces.org/ui" xmlns:h="http://java.sun.com/jsf/html" xmlns:a="http://xmlns.jcp.org/jsf/passthrough" xmlns:f="http://java.sun.com/jsf/core">
<p:column headerText="#{i18n.date}">
<h:outputText value="#{obj.date}">
<f:convertDateTime pattern="yyyy-MM-dd"/>
</h:outputText>
<ui:fragment rendered="#{cc.attrs.editable}">
<f:facet name="footer">
<h:inputText styleClass="form-control" value="#{cc.attrs.backing.blank.date}" a:placeholder="#{i18n.date}">
<f:convertDateTime pattern="yyyy-MM-dd"/>
</h:inputText>
</f:facet>
</ui:fragment>
</p:column>
<p:column headerText="#{i18n.balance}">
#{obj.balance}
<ui:fragment rendered="#{cc.attrs.editable}">
<f:facet name="footer">
<h:inputText rendered="#{cc.attrs.editable}" styleClass="form-control" value="#{cc.attrs.backing.blank.balance}" a:placeholder="#{i18n.balance}"/>
</f:facet>
</ui:fragment>
</p:column>
</ui:composition>
Base.xhtml
<!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:h="http://java.sun.com/jsf/html" xmlns:p="http://primefaces.org/ui" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<title>#{i18n.appTitle}</title>
<h:outputScript library="bootstrap" name="js/bootstrap.min.js" />
<link rel="stylesheet" href="#{request.contextPath}/resources/bootstrap/css/bootstrap.min.css"/>
</h:head>
<h:body>
<div class="container-fluid">
<div class="navbar navbar-default">
<ul class="nav navbar-nav">
<li>
<h:link outcome="diary" style="#{todayBacking.count > 0 ? 'color: white; background: #E74C3C' : ''}">
<span class="glyphicon glyphicon-exclamation-sign"/> #{todayBacking.count}
</h:link>
</li>
<li><h:link outcome="pay"><span class="glyphicon glyphicon-book"/> #{i18n.pay}</h:link></li>
<li><h:link outcome="debt"><span class="glyphicon glyphicon glyphicon-calendar"/> #{i18n.debt}</h:link></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><span class="glyphicon glyphicon-stats"/> #{i18n.graphics} <span class="caret"/></a>
<ul class="dropdown-menu" role="menu">
<li><h:link outcome="balance">#{i18n.balance}</h:link></li>
<li><h:link outcome="day">#{i18n.day}</h:link></li>
<li><h:link outcome="unpaid">#{i18n.unpaid}</h:link></li>
<li><h:link outcome="category">#{i18n.category}</h:link></li>
</ul>
</li>
</ul>
</div>
<div class="page-header">
<h1><ui:insert name="title"/></h1>
</div>
<h:form>
<p:messages/>
<ui:insert name="body"/>
</h:form>
</div>
</h:body>
</html>
Web.xml
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.xhtml</url-pattern>
</servlet-mapping>
<context-param>
<param-name>primefaces.THEME</param-name>
<param-value>none</param-value>
</context-param>
<context-param>
<param-name>javax.faces.DATETIMECONVERTER_DEFAULT_TIMEZONE_IS_SYSTEM_TIMEZONE</param-name>
<param-value>true</param-value>
</context-param>
</web-app>
Faces-config.xml
<faces-config xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd" version="2.2">
<application>
<locale-config>
<default-locale>en</default-locale>
</locale-config>
<resource-bundle>
<base-name>ru.ulstu.secretary.i18n</base-name>
<var>i18n</var>
</resource-bundle>
</application>
</faces-config>
Persistence.xml
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">
<persistence-unit name="secretaryPU" transaction-type="JTA">
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:test"/>
<property name="javax.persistence.jdbc.user" value="sa"/>
<property name="javax.persistence.schema-generation.database.action" value="create" />
<property name="hibernate.show_sql" value="true"/>
</properties>
</persistence-unit>
</persistence>
I18n.properties
appTitle=Home Secretary
pay=Pay
diary=Diary
debt=Debt
repay=Repay
grid=Data
total=Total
date=Date
balance=Sum
category=Category
comment=Comment
action=Action
delete=Delete
complete=Complete
count=Count
graphics=Graphic
day=Day
deadline=Deadline
required=Required
frequency=Frequency
unit=Unit
year=Year
month=Month
week=Week
unpaid=Unpaid
add=Add
see=See
auto=Auto
home=Home
min=Min
max=Max