/**
* This file is part of Pau's Asset Manager Project.
*
* Pau's Asset Manager Project is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Pau's Asset Manager Project is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Pau's Asset Manager Project. If not, see <http://www.gnu.org/licenses/>.
*/
package org.pau.assetmanager.viewmodel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.pau.assetmanager.business.BooksBusiness;
import org.pau.assetmanager.business.ClientsBusiness;
import org.pau.assetmanager.business.DaoFunction;
import org.pau.assetmanager.business.PropertiesBusiness;
import org.pau.assetmanager.entities.Book;
import org.pau.assetmanager.entities.Client;
import org.pau.assetmanager.entities.GeneralBook;
import org.pau.assetmanager.entities.Property;
import org.pau.assetmanager.entities.PropertyBook;
import org.pau.assetmanager.entities.StocksBook;
import org.pau.assetmanager.entities.User;
import org.pau.assetmanager.utils.AssetManagerRuntimeException;
import org.pau.assetmanager.utils.UserUtil;
import org.pau.assetmanager.viewmodel.type.BookSelectionType;
import org.pau.assetmanager.viewmodel.type.PropertyType;
import org.pau.assetmanager.viewmodel.utils.BookSelection;
import org.zkoss.bind.BindUtils;
import org.zkoss.bind.annotation.BindingParam;
import org.zkoss.bind.annotation.Command;
import org.zkoss.bind.annotation.ContextParam;
import org.zkoss.bind.annotation.ContextType;
import org.zkoss.bind.annotation.DependsOn;
import org.zkoss.bind.annotation.GlobalCommand;
import org.zkoss.bind.annotation.Init;
import org.zkoss.bind.annotation.NotifyChange;
import org.zkoss.zk.ui.event.CheckEvent;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.InputEvent;
import org.zkoss.zk.ui.util.Clients;
import org.zkoss.zul.Radio;
import com.google.common.base.Optional;
/**
* This class is the ViewModel of the main UI component that allows CRUD
* operations on clients and books. The ZUL field to which the ViewModel is
* linked to is 'index.zul'
*
* Furthermore, it allows the selection of a book which will be used by the
* 'tabbed UI' subcomponents to display different details about its annotations
* (edition, filtering, reporting, tax computation...)
*
* @author Pau Carré Cardona
*
*/
public class BookViewModel {
// static references to the detail reports that deal with the annotations of
// the
// book selection
private static final String SRC_FOR_PROPERTY_INCOME = "property_income_annotations.zul";
private static final String SRC_FOR_STOCKS_INCOME = "stocks_income_annotations.zul";
private static final String SRC_FOR_GENERAL_INCOME = "general_income_annotations.zul";
private static final String SRC_FOR_PROPERTY_EXPENSES = "property_expenses_annotations.zul";
private static final String SRC_FOR_STOCKS_EXPENSES = "stocks_expenses_annotations.zul";
private static final String SRC_FOR_GENERAL_EXPENSES = "general_expenses_annotations.zul";
private static final String SRC_FOR_MOVEMENT = "movement_annotations.zul";
private static final String SRC_FOR_MONTHLY_REPORT = "grouping_model_view_month.zul";
private static final String SRC_FOR_QUARTERLY_REPORT = "grouping_model_view_quarter.zul";
private static final String SRC_FOR_YEARLY_REPORT = "grouping_model_view_yearly.zul";
private static final String SRC_FOR_STOCKS_YEARLY_REPORT = "stocks_grouping_model_view_yearly.zul";
// static reference to the indexes of the tab index/number that deal with
// the annotations of the
// book selection
public static final Integer TAB_INDEX_FOR_INCOME = 0;
public static final Integer TAB_INDEX_FOR_EXPENSES = 1;
public static final Integer TAB_INDEX_FOR_MOVEMENT = 2;
public static final Integer TAB_INDEX_FOR_MONTHLY_REPORT = 3;
public static final Integer TAB_INDEX_FOR_QUARTERLY_REPORT = 4;
public static final Integer TAB_INDEX_FOR_YEARLY_REPORT = 5;
public static final Integer TAB_INDEX_FOR_STOCKS_YEARLY_REPORT = 6;
// selected book
private Book selectedBook = null;
// selected client
private Client selectedClient = null;
// book selection type
// there are two types of selections: the book or all the books from the
// client
private BookSelectionType bookSelectionType;
// holds the state in which the bookSelectionType is
// 'BookSelectionType.SINGLE_BOOK'
private Boolean singleBookSelection;
// the current selected tab index
private Integer currentSelectedTabIndex = TAB_INDEX_FOR_INCOME;
// references of the sources for each one the tabs
// the sources can change depending on the book type and the book selection
// type
private String srcForIncome = null;
private String srcForExpenses = null;
private String srcForMonthlyReport = SRC_FOR_MONTHLY_REPORT;
private String srcForQuarterlyReport = SRC_FOR_QUARTERLY_REPORT;
private String srcForYearlyReport = null;
private String srcForMovement = SRC_FOR_MOVEMENT;
private String srcForStocksYearlyReport = null;
private static final Logger logger = LogManager
.getLogger(BookViewModel.class);
@Init
public void init() {
bookSelectionType = BookSelectionType.SINGLE_BOOK;
List<Client> clients = getClients();
if (clients.size() > 0) {
selectedClient = clients.iterator().next();
}
}
@DependsOn("bookSelectionType")
public Boolean getSingleBookSelection() {
singleBookSelection = bookSelectionType
.equals(BookSelectionType.SINGLE_BOOK);
return singleBookSelection;
}
public User getExecutionUser() {
return UserUtil.getExecutionUser();
}
private static List<BookSelectionType> bookSelectionTypes = new ArrayList<BookSelectionType>(
Arrays.asList(BookSelectionType.values()));
public List<BookSelectionType> getBookSelectionTypes() {
return bookSelectionTypes;
}
public BookSelectionType getBookSelectionType() {
return bookSelectionType;
}
// WATCH OUT: do not notify, the refreshBookSelectionType command will do
// that
public void setBookSelectionType(BookSelectionType bookSelectionType) {
this.bookSelectionType = bookSelectionType;
}
@NotifyChange({ "bookSelectionType", "currentSelectedTabIndex" })
@Command
public void refreshBookSelectionType(
@BindingParam("radioEvent") CheckEvent radioEvent) {
Clients.clearBusy();
this.bookSelectionType = ((Radio) radioEvent.getTarget()).getValue();
if (bookSelectionType.equals(BookSelectionType.ALL_BOOKS)) {
currentSelectedTabIndex = TAB_INDEX_FOR_MONTHLY_REPORT;
}
notifyGlobalUpdateBookSelection();
}
@NotifyChange({ "clients", "selectedClient" })
@Command
public void removeClient() {
ClientsBusiness.removeClient(selectedClient);
selectedClient = null;
selectedBook = null;
}
@NotifyChange("books")
@Command
public void saveBook(@BindingParam("book") Book book) {
if (book.getId() != null && getSingleBookSelection()) {
DaoFunction.mergeFunction().apply(book);
notifyGlobalUpdateBookSelection();
}
}
private void notifyGlobalUpdateBookSelection() {
Map<String, Object> data = new HashMap<String, Object>();
data.put("bookSelection", new BookSelection(selectedBook,
bookSelectionType, selectedClient));
BindUtils.postGlobalCommand(null, null, "updateBookSelection", data);
}
@Command
public void saveProperty(@BindingParam("property") Property property) {
if (property.getId() != null) {
DaoFunction.mergeFunction().apply(property);
notifyGlobalUpdateBookSelection();
}
}
@NotifyChange("clients")
@Command
public void saveClient(@BindingParam("client") Client client) {
if (client.getId() != null) {
DaoFunction.<Client> mergeFunction().apply(client);
}
}
public List<PropertyType> getPropertyTypes() {
return Arrays.asList(PropertyType.values());
}
private List<Book> getBooksFromClient(Client client) {
List<Book> books = BooksBusiness.getBooksFromClient(client);
return books;
}
public List<Client> getClients() {
return ClientsBusiness.getClients();
}
@DependsOn("clients")
public Client getSelectedClient() {
if (selectedClient == null) {
List<Client> clients = ClientsBusiness.getClients();
if (clients != null && clients.size() > 0) {
selectedClient = clients.iterator().next();
}
}
return selectedClient;
}
@NotifyChange({ "books" })
@Command
public void importMovements() {
}
@NotifyChange({ "books" })
@Command
public void refreshBooks() {
selectedBook = null;
}
public void setSelectedClient(Client selectedClient) {
this.selectedClient = selectedClient;
}
@DependsOn("selectedBook")
public PropertyBook getSelectedPropertyBook() {
if (selectedBook != null && selectedBook instanceof PropertyBook) {
return (PropertyBook) selectedBook;
}
return null;
}
@DependsOn("selectedBook")
public StocksBook getSelectedStocksBook() {
if (selectedBook != null && selectedBook instanceof StocksBook) {
return (StocksBook) selectedBook;
}
return null;
}
@DependsOn("selectedBook")
public String getSelectedBookTypeName() {
if (selectedBook == null) {
return "";
}
if (selectedBook instanceof PropertyBook) {
return "Propiedad";
} else if (selectedBook instanceof StocksBook) {
return "Cartera de valores";
} else if (selectedBook instanceof GeneralBook) {
return "Gastos generales";
}
return "";
}
@DependsOn("selectedBook")
public String getIncomeLabel() {
if (selectedBook == null) {
return "";
}
if (selectedBook instanceof StocksBook) {
return "Ventas";
}
return "Ingresos";
}
@DependsOn("selectedBook")
public String getExpensesLabel() {
if (selectedBook == null) {
return "";
}
if (selectedBook instanceof StocksBook) {
return "Compras";
}
return "Gastos";
}
@DependsOn({ "selectedBook", "currentSelectedTabIndex" })
public String getSrcForYearlyReport() {
if (selectedBook != null
&& (selectedBook instanceof PropertyBook
|| (!getSingleBookSelection()) || selectedBook instanceof StocksBook)) {
if (currentSelectedTabIndex.equals(TAB_INDEX_FOR_YEARLY_REPORT)) {
if (!(selectedBook instanceof StocksBook)) {
srcForYearlyReport = SRC_FOR_YEARLY_REPORT;
return srcForYearlyReport;
}
}
}
return null;
}
@DependsOn({ "selectedBook", "currentSelectedTabIndex" })
public String getSrcForStocksYearlyReport() {
if (selectedBook != null && selectedBook instanceof StocksBook) {
if (currentSelectedTabIndex
.equals(TAB_INDEX_FOR_STOCKS_YEARLY_REPORT)) {
srcForStocksYearlyReport = SRC_FOR_STOCKS_YEARLY_REPORT;
} else {
srcForStocksYearlyReport = null;
}
} else {
srcForStocksYearlyReport = null;
}
return srcForStocksYearlyReport;
}
@DependsOn({ "selectedBook", "bookSelectionType" })
public String getSrcForIncome() {
if (bookSelectionType != null
&& bookSelectionType.equals(BookSelectionType.ALL_BOOKS)) {
srcForIncome = "";
} else {
if (selectedBook != null) {
if (selectedBook instanceof StocksBook) {
srcForIncome = SRC_FOR_STOCKS_INCOME;
} else if (selectedBook instanceof PropertyBook) {
srcForIncome = SRC_FOR_PROPERTY_INCOME;
} else if (selectedBook instanceof GeneralBook) {
srcForIncome = SRC_FOR_GENERAL_INCOME;
} else {
srcForIncome = "";
}
} else {
srcForIncome = "";
}
}
return srcForIncome;
}
@DependsOn({ "selectedBook", "bookSelectionType" })
public String getSrcForExpenses() {
if (bookSelectionType != null
&& bookSelectionType.equals(BookSelectionType.ALL_BOOKS)) {
srcForIncome = "";
} else {
if (selectedBook != null) {
if (selectedBook instanceof StocksBook) {
srcForExpenses = SRC_FOR_STOCKS_EXPENSES;
} else if (selectedBook instanceof PropertyBook) {
srcForExpenses = SRC_FOR_PROPERTY_EXPENSES;
} else if (selectedBook instanceof GeneralBook) {
srcForExpenses = SRC_FOR_GENERAL_EXPENSES;
} else {
srcForExpenses = "";
}
} else {
srcForExpenses = "";
}
}
return srcForExpenses;
}
@DependsOn({"selectedBook", "bookSelectionType"} )
public String getSrcForMovementExpenses() {
if (selectedBook != null
&& !bookSelectionType.equals(BookSelectionType.ALL_BOOKS)) {
srcForMovement = SRC_FOR_MOVEMENT;
} else {
srcForMovement = "";
}
return srcForMovement;
}
@DependsOn({ "currentSelectedTabIndex" })
public String getSrcForMonthlyReport() {
if (currentSelectedTabIndex.equals(TAB_INDEX_FOR_MONTHLY_REPORT)) {
return srcForMonthlyReport;
}
return null;
}
@NotifyChange({ "currentSelectedTabIndex", "srcForIncome",
"srcForExpenses", "srcForMonthlyReport", "srcForQuarterlyReport",
"srcForYearlyReport", "srcForMovementExpenses" })
@Command
public void refreshTabs() {
if (selectedBook != null && (!getSingleBookSelection())) {
setCurrentSelectedTabIndex(TAB_INDEX_FOR_MONTHLY_REPORT);
}
}
@DependsOn({ "selectedBook", "currentSelectedTabIndex" })
public String getSrcForQuarterlyReport() {
if (selectedBook != null
&& (selectedBook instanceof PropertyBook || (!getSingleBookSelection()))) {
if (currentSelectedTabIndex.equals(TAB_INDEX_FOR_QUARTERLY_REPORT)) {
return srcForQuarterlyReport;
}
}
return null;
}
public Integer getCurrentSelectedTabIndex() {
return currentSelectedTabIndex;
}
@NotifyChange({ "currentSelectedTabIndex" })
public void setCurrentSelectedTabIndex(Integer currentSelectedTabIndex) {
this.currentSelectedTabIndex = currentSelectedTabIndex;
}
@DependsOn({ "selectedClient", "books" })
public Book getSelectedBook() {
if (selectedBook == null) {
List<Book> books = getBooksFromClient(selectedClient);
if (books != null && books.size() > 0) {
setSelectedBook(books.iterator().next());
}
}
return selectedBook;
}
@Command
@NotifyChange("books")
public void updateSelectedBook(
@ContextParam(ContextType.TRIGGER_EVENT) InputEvent event) {
String newBookName = event.getValue();
selectedBook.setName(newBookName);
}
@GlobalCommand
@NotifyChange({ "clients" })
public void updateSelectedClientData() {
}
public void setSelectedBook(Book selectedBook) {
this.selectedBook = selectedBook;
setCurrentSelectedTabIndex(TAB_INDEX_FOR_INCOME);
notifyGlobalUpdateBookSelection();
}
@DependsOn({ "selectedClient" })
public List<Book> getBooks() {
if (selectedClient == null) {
return new LinkedList<Book>();
}
List<Book> books = getBooksFromClient(selectedClient);
return books;
}
@Command
@NotifyChange({ "clients", "selectedClient" })
public void notifyAddClientRequest(
@ContextParam(ContextType.TRIGGER_EVENT) Event event) {
String clientName = (String) event.getData();
User user = UserUtil.getExecutionUser();
ClientsBusiness.addClientByClientName(user, clientName);
Optional<Client> optionalClient = ClientsBusiness
.getClientByName(clientName);
if (optionalClient.isPresent()) {
setSelectedClient(optionalClient.get());
} else {
String message = "Client with name '" + clientName + "' not found.";
logger.error(message);
throw new AssetManagerRuntimeException(message);
}
}
@Command
@NotifyChange({ "books", "selectedBook" })
public void notifyAddBookRequest(@BindingParam("book") Book book) {
book = BooksBusiness.addBook(book);
setSelectedBook(book);
PropertiesBusiness
.removePropertiesOfUsersWithoutBooksByClient(selectedClient);
}
@Command
@NotifyChange({ "books", "selectedBook" })
public void removeBook() {
BooksBusiness.removeBook(selectedBook);
selectedBook = null;
}
}