org.jpos.qi.minigl.AccountsView.java Source code

Java tutorial

Introduction

Here is the source code for org.jpos.qi.minigl.AccountsView.java

Source

/*
 * jPOS Project [http://jpos.org]
 * Copyright (C) 2000-2018 jPOS Software SRL
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.jpos.qi.minigl;

import com.vaadin.data.ValidationResult;
import com.vaadin.data.Validator;
import com.vaadin.icons.VaadinIcons;
import com.vaadin.shared.ui.ContentMode;
import com.vaadin.ui.*;
import com.vaadin.server.FontAwesome;
import com.vaadin.ui.Button;
import com.vaadin.ui.Label;
import com.vaadin.ui.Panel;
import com.vaadin.ui.components.grid.FooterCell;
import com.vaadin.ui.components.grid.FooterRow;
import com.vaadin.ui.components.grid.HeaderCell;
import com.vaadin.ui.components.grid.HeaderRow;
import com.vaadin.ui.renderers.DateRenderer;
import com.vaadin.ui.themes.ValoTheme;
import org.jpos.ee.DB;
import org.jpos.gl.*;
import org.jpos.qi.components.DateRange;
import org.jpos.qi.components.DateRangeComponent;
import org.jpos.qi.QIEntityView;
import org.jpos.qi.QIHelper;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.*;

import static org.jpos.util.QIUtils.createAmountRenderer;
import static org.jpos.util.QIUtils.getCaptionFromId;

public class AccountsView extends QIEntityView<Account> {

    private ComboBox<Journal> journals;
    private Object lastSelectedJournal;
    private DateRangeComponent dateRangeComponent;
    private CheckBoxGroup<Layer> layersCheckBox;
    private EntryGrid entryGrid;
    private Label rangeLabelTitle;
    private Account account;
    private List<Account> expandedItems;

    public AccountsView() {
        super(Account.class, "accounts");
        expandedItems = new ArrayList<>();
    }

    @Override
    public void showSpecificView(String parameter) {
        String[] params = parameter.split("/");
        if (params.length > 0 && "expand".equals(params[0])) {
            setGeneralView(true);
            showGeneralView();
            Account a = (Account) getEntityByParam(params[1]);
            expand(a);
        } else {
            super.showSpecificView(parameter);
        }
    }

    @Override
    public Grid createGrid() {
        TreeGrid<Account> tree = new TreeGrid();
        tree.setSizeFull();
        tree.setSelectionMode(Grid.SelectionMode.SINGLE);
        tree.addItemClickListener(event -> {
            //do nothing if it's root
            if (event.getItem().getRoot().getId() != event.getItem().getId()) {
                String url = getGeneralRoute() + "/" + getHelper().getItemId(event.getItem());
                getApp().getNavigator().navigateTo(url);
            }
        });
        tree.setItemCollapseAllowedProvider(account -> account.getParent() != null);
        tree.addExpandListener(listener -> {
            if (listener.getExpandedItem().getParent() != null)
                expandedItems.add(listener.getExpandedItem());
        });
        tree.addCollapseListener(listener -> expandedItems.remove(listener.getCollapsedItem()));
        return tree;
    }

    public void formatGrid() {
        setGridGetters();

        TreeGrid<Account> grid = (TreeGrid) getGrid();

        //Delete not visible columns
        //Use columnId as caption
        //Set sorting for every column.
        DecimalFormat nf = new DecimalFormat();
        nf.setGroupingUsed(false);

        grid.setStyleGenerator(styleGen -> {
            if (styleGen.isDebit()) {
                return "debit-color";
            } else {
                return "credit-color";
            }
        });

        Iterator<Grid.Column<Account, ?>> it = grid.getColumns().iterator();
        while (it.hasNext()) {
            Grid.Column c = it.next();
            String columnId = c.getId();
            if (!Arrays.asList(getVisibleColumns()).contains(columnId)) {
                grid.removeColumn(columnId);
            } else {
                c.setCaption(getCaptionFromId(columnId)).setSortProperty(columnId).setSortable(false)
                        .setHidable(false);
                c.setStyleGenerator(obj -> {
                    Object value = c.getValueProvider().apply(obj);
                    if (value instanceof BigDecimal) {
                        return "align-right";
                    }
                    return null;
                });
            }

        }
        //fix for when a manual resize is done, the last column takes the empty space.
        grid.addColumnResizeListener(event -> {
            int lastColumnIndex = grid.getColumns().size() - 1;
            ((Grid.Column) grid.getColumns().get(lastColumnIndex)).setWidth(1500);
        });
        //expand root account
        Account a = (Account) getEntityByParam("5");
        grid.expand(a);
    }

    @Override
    protected HorizontalLayout createHeader(String title) {
        HorizontalLayout header = super.createHeader(title);
        if (isGeneralView()) {
            Label refDebit = new Label(FontAwesome.SQUARE.getHtml() + " DEBIT accounts");
            Label refCredit = new Label(FontAwesome.SQUARE.getHtml() + " CREDIT accounts");
            refDebit.setContentMode(ContentMode.HTML);
            refCredit.setContentMode(ContentMode.HTML);
            refDebit.setStyleName("debit-color");
            refCredit.setStyleName("credit-color");
            refDebit.addStyleName(ValoTheme.LABEL_SMALL);
            refCredit.addStyleName(ValoTheme.LABEL_SMALL);
            Button collapse = new Button(getApp().getMessage("collapseAll"), event -> {
                ((TreeGrid) getGrid()).collapse(expandedItems.toArray());

            });
            collapse.setStyleName(ValoTheme.BUTTON_LINK);
            collapse.addStyleName(ValoTheme.BUTTON_SMALL);
            HorizontalLayout l = new HorizontalLayout(refDebit, refCredit, collapse);
            l.setSpacing(true);
            l.setComponentAlignment(refDebit, Alignment.BOTTOM_CENTER);
            l.setComponentAlignment(refCredit, Alignment.BOTTOM_CENTER);
            l.setComponentAlignment(collapse, Alignment.BOTTOM_CENTER);
            header.addComponent(l);
            header.setComponentAlignment(l, Alignment.MIDDLE_RIGHT);
        }
        return header;
    }

    @Override
    public void setGridGetters() {
        TreeGrid<Account> grid = (TreeGrid<Account>) getGrid();
        grid.addColumn(account -> account.getCode() + ": " + account.getDescription()).setId("id");
        grid.addColumn(Account::getCode).setId("code");
        grid.addColumn(Account::getDescription).setId("description");
        grid.addColumn(Account::getCurrencyCode).setId("currencyCode");
        grid.addColumn(Account::getCreated).setId("created");
        grid.addColumn(Account::getExpiration).setId("expiration");
        grid.addColumn(account -> account.getTags() != null ? account.getTags().toString() : "").setId("tags");
        grid.addColumn(account -> account.getParent() != null ? account.getParent().getCode() : "").setId("parent");
    }

    @Override
    protected Component buildAndBindCustomComponent(String propertyId) {
        if ("created".equalsIgnoreCase(propertyId)) {
            return buildAndBindDateField(propertyId);
        }
        if ("expiration".equalsIgnoreCase(propertyId)) {
            return buildAndBindDateField(propertyId);
        }
        if ("parent".equalsIgnoreCase(propertyId)) {
            ComboBox<Account> parentCombo = new ComboBox<>(getCaptionFromId(propertyId));
            parentCombo.setDataProvider(((AccountsHelper) getHelper()).getParentDataProvider());
            parentCombo.setItemCaptionGenerator(parent -> parent.getCode() + " - " + parent.getDescription());
            formatField(propertyId, parentCombo).bind(propertyId);
            return parentCombo;
        }
        if ("type".equalsIgnoreCase(propertyId)) {
            ComboBox<Integer> typeCombo = new ComboBox<>(getCaptionFromId(propertyId));
            typeCombo.setItems(Account.CHART, Account.DEBIT, Account.CREDIT);
            typeCombo.setItemCaptionGenerator(type -> getApp().getMessage("account." + type).toUpperCase());
            formatField(propertyId, typeCombo).bind(propertyId);
            return typeCombo;
        }
        if ("entries".equalsIgnoreCase(propertyId)) {
            //todo: should bind?
            return createEntriesPanel();
        }
        return null;
    }

    @Override
    protected List<Validator> getValidators(String propertyId) {
        List<Validator> validators = super.getValidators(propertyId);
        if ("currencyCode".equalsIgnoreCase(propertyId)) {
            Validator<String> currencyCodeValidator = (Validator<String>) (value, context) -> {
                Account parentAccount = getInstance().getParent();
                if (parentAccount != null && parentAccount.getCurrencyCode() != null
                        && !parentAccount.getCurrencyCode().equals(value)) {
                    return ValidationResult.error(getApp().getMessage("errorMessage.currencyCodeMismatch"));
                }
                return ValidationResult.ok();
            };
            validators.add(currencyCodeValidator);
        }
        return validators;
    }

    public void formatEntriesGrid() {
        String pattern = getApp().getMessage("datetime");
        SimpleDateFormat dateFormat = new SimpleDateFormat(pattern);
        entryGrid.getColumn("account").setHidden(true);
        entryGrid.addColumn(glEntry -> glEntry.getTransaction().getTimestamp()).setId("timestamp")
                .setRenderer(new DateRenderer(dateFormat)).setCaption(getCaptionFromId("timestamp"))
                .setSortable(true).setHidable(false);
        entryGrid.addColumn(GLEntry::getBalance).setId("balance").setRenderer(createAmountRenderer())
                .setStyleGenerator(cellStyle -> "align-right").setCaption(getCaptionFromId("balance"))
                .setSortable(true).setHidable(false);
        entryGrid.sort("timestamp");
        entryGrid.setColumnOrder("timestamp", "detail", "tags", "layer", "debit", "credit", "balance");
        entryGrid.setHeightByRows(10);

        //HEADER
        entryGrid.getDefaultHeaderRow().getCell("balance").setStyleName("align-right");
        HeaderRow initialBalanceHeader = entryGrid.appendHeaderRow();
        HeaderCell label = initialBalanceHeader.join("timestamp", "detail", "tags", "layer", "credit", "debit");
        label.setText("Initial Balance");
        HeaderCell value = initialBalanceHeader.getCell("balance");
        value.setStyleName("amount-header");

        //FOOTER
        FooterRow footer = entryGrid.appendFooterRow();
        FooterCell totalCell = footer.join("timestamp", "detail", "tags", "layer");
        totalCell.setHtml("<strong>TOTAL</strong>");
        FooterCell debitTotal = footer.getCell("debit");
        debitTotal.setStyleName("align-right");
        FooterCell creditTotal = footer.getCell("credit");
        creditTotal.setStyleName("align-right");
        FooterCell balanceTotal = footer.getCell("balance");
        balanceTotal.setStyleName("align-right");
    }

    private void updateHeaderAndFooter(AccountDetail detail) {
        entryGrid.getHeaderRow(1).getCell("balance").setText(detail.getInitialBalance().toString());
        entryGrid.getFooterRow(0).getCell("credit").setText(detail.getCredits().toString());
        entryGrid.getFooterRow(0).getCell("debit").setText(detail.getDebits().toString());
        entryGrid.getFooterRow(0).getCell("balance").setText(detail.getFinalBalance().toString());
    }

    @Override
    public QIHelper createHelper() {
        return new AccountsHelper();
    }

    @Override
    public Object getEntity(Object entity) {
        if (!(entity instanceof Account))
            return null;
        Account account = (Account) entity;
        AccountsHelper helper = (AccountsHelper) getHelper();
        return helper.getEntityByParam(String.valueOf(account.getId()));
    }

    @Override
    public String getHeaderSpecificTitle(Object entity) {
        if (!(entity instanceof Account))
            return "";
        Account account = (Account) entity;
        if (account.getId() == 0)
            return getApp().getMessage("new");
        return account.getId() + " - " + account.getCode();
    }

    private void refreshDetails() {
        try {
            if (lastSelectedJournal != journals.getValue()) {
                lastSelectedJournal = journals.getValue();
                updateLayers(journals.getValue());
            }
            DB.exec(db -> {
                GLSession session = new GLSession(db, "admin");
                DateRange range = dateRangeComponent.getValue();
                db.session().refresh(getInstance());
                short[] s = convertLayersToShortArray(layersCheckBox.getSelectedItems());
                AccountDetail detail = session.getAccountDetail(journals.getValue(), account, range.getStart(),
                        range.getEnd(), s);
                rangeLabelTitle.setValue(
                        "Showing entries from: " + getApp().getMessage(dateRangeComponent.getValue().toString()));
                updateHeaderAndFooter(detail);
                this.entryGrid.setItems(detail.getEntries());
                return null;
            });
        } catch (Exception e) {
            getApp().getLog().error(e);
            getApp().displayNotification(getApp().getMessage("errorMessage.unexpected"));
        }
    }

    private short[] convertLayersToShortArray(Set<Layer> layers) {
        short[] shorts = new short[layers.size()];
        int index = 0;
        for (Layer l : layers) {
            shorts[index] = l.getId();
            index++;
        }
        return shorts;
    }

    private Panel createEntriesPanel() {
        Panel entriesPanel = new Panel(getCaptionFromId("entries"));
        entriesPanel.setIcon(VaadinIcons.EXCHANGE);
        entriesPanel.addStyleName("color1");
        entriesPanel.addStyleName("margin-top-panel");

        VerticalLayout layout = new VerticalLayout();
        layout.setMargin(true);
        layout.setSpacing(true);
        Panel filterPanel = new Panel();
        filterPanel.addStyleName("v-panel-well");

        journals = new JournalsCombo(true);

        rangeLabelTitle = new Label();
        rangeLabelTitle.addStyleName(ValoTheme.LABEL_BOLD);
        dateRangeComponent = new DateRangeComponent(DateRange.ALL_TIME, true) {
            @Override
            protected Button.ClickListener createRefreshListener() {
                return event -> {
                    refreshDetails();
                };
            }
        };
        VerticalLayout detailsLayout = new VerticalLayout();

        entryGrid = new EntryGrid(null, false);
        formatEntriesGrid();
        detailsLayout.addComponent(entryGrid);
        detailsLayout.setMargin(false);

        HorizontalLayout layersLayout = new HorizontalLayout();
        layersLayout.setSizeFull();
        layersLayout.setSpacing(true);

        layersCheckBox = new CheckBoxGroup<>(getCaptionFromId("layers").toUpperCase());
        layersCheckBox.setItemCaptionGenerator(item -> item.getId() + " - " + item.getName());
        layersCheckBox.addValueChangeListener(listener -> refreshDetails());

        layersLayout.addComponentsAndExpand(layersCheckBox);
        VerticalLayout vbar = new VerticalLayout(journals, dateRangeComponent, layersLayout);
        vbar.setSpacing(true);
        vbar.setMargin(true);
        filterPanel.setContent(vbar);
        layout.addComponents(rangeLabelTitle, detailsLayout, filterPanel);
        entriesPanel.setContent(layout);
        refreshDetails();
        return entriesPanel;

    }

    private void updateLayers(Journal j) {
        List<Layer> items = ((AccountsHelper) getHelper()).getLayers(j);
        if (items != null) {
            Collections.sort(items);
            layersCheckBox.setItems(items);
            //select all items
            layersCheckBox.select(items.toArray(new Layer[items.size()]));
        }
    }

    private void expand(Account a) {
        TreeGrid tree = (TreeGrid) getGrid();
        if (a.getParent() != null) {
            expand(a.getParent());
        }
        tree.expand(a);
    }

    @Override
    public Layout createForm(final Object entity, String[] params, boolean isNew) {
        account = (Account) entity;
        return super.createForm(entity, params, isNew);
    }

    @Override
    public String getGeneralRoute() {
        return "/accounts".equals(super.getGeneralRoute()) && !isGeneralView() && account != null
                ? super.getGeneralRoute() + "/expand/" + account.getId()
                : super.getGeneralRoute();
    }

    @Override
    public boolean canEdit() {
        return true;
    }
}