org.efaps.esjp.accounting.transaction.evaluation.DocumentInfo_Base.java Source code

Java tutorial

Introduction

Here is the source code for org.efaps.esjp.accounting.transaction.evaluation.DocumentInfo_Base.java

Source

/*
 * Copyright 2003 - 2017 The eFaps Team
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package org.efaps.esjp.accounting.transaction.evaluation;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.text.StrSubstitutor;
import org.efaps.admin.dbproperty.DBProperties;
import org.efaps.admin.event.Parameter;
import org.efaps.admin.program.esjp.EFapsApplication;
import org.efaps.admin.program.esjp.EFapsUUID;
import org.efaps.admin.program.esjp.Listener;
import org.efaps.db.CachedMultiPrintQuery;
import org.efaps.db.CachedPrintQuery;
import org.efaps.db.Context;
import org.efaps.db.Instance;
import org.efaps.db.MultiPrintQuery;
import org.efaps.db.PrintQuery;
import org.efaps.db.QueryBuilder;
import org.efaps.db.SelectBuilder;
import org.efaps.esjp.accounting.Account2CaseInfo;
import org.efaps.esjp.accounting.Case;
import org.efaps.esjp.accounting.Period;
import org.efaps.esjp.accounting.listener.IOnDocumentInfo;
import org.efaps.esjp.accounting.transaction.AccountInfo;
import org.efaps.esjp.accounting.util.Accounting;
import org.efaps.esjp.accounting.util.Accounting.Account2Case4AmountConfig;
import org.efaps.esjp.accounting.util.Accounting.ExchangeConfig;
import org.efaps.esjp.accounting.util.Accounting.SummarizeConfig;
import org.efaps.esjp.accounting.util.Accounting.SummarizeCriteria;
import org.efaps.esjp.accounting.util.AccountingSettings;
import org.efaps.esjp.ci.CIAccounting;
import org.efaps.esjp.ci.CIERP;
import org.efaps.esjp.ci.CISales;
import org.efaps.esjp.db.InstanceUtils;
import org.efaps.esjp.erp.Currency;
import org.efaps.esjp.erp.CurrencyInst;
import org.efaps.esjp.erp.NumberFormatter;
import org.efaps.esjp.erp.RateInfo;
import org.efaps.esjp.sales.tax.xml.TaxEntry;
import org.efaps.esjp.sales.tax.xml.Taxes;
import org.efaps.ui.wicket.util.DateUtil;
import org.efaps.util.EFapsException;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

/**
 * TODO comment!
 *
 * @author The eFaps Team
 */
@EFapsUUID("f6508096-b3a3-4b19-9b10-0290ad0571a6")
@EFapsApplication("eFapsApp-Accounting")
public abstract class DocumentInfo_Base {

    /**
     * Instance of this Document.
     */
    private Instance instance;

    /**
     * Instance of the applied Case.
     */
    private Instance caseInst;

    /**
     * Is this a Stock moving Document.
     */
    private boolean stockDoc;

    /**
     * Did the Document pass the validation of the cost. (Every product has its
     * cost assigned)
     */
    private boolean costValidated;

    /**
     * Is this a document containing sums.
     */
    private boolean sumsDoc;

    /**
     * Is this a document containing sums.
     */
    private boolean paymentDoc;

    /**
     * Date of this Document.
     */
    private DateTime date;

    /**
     * Date used for rateevaluation.
     */
    private DateTime rateDate;

    /** The rate prop key. */
    private ExchangeConfig exchangeConfig;

    /**
     * List of TargetAccounts for debit.
     */
    private final Set<AccountInfo> debitAccounts = new LinkedHashSet<>();

    /**
     * List of TargetAccounts for credit.
     */
    private final Set<AccountInfo> creditAccounts = new LinkedHashSet<>();

    /**
     * Amount of this Account.
     */
    private BigDecimal amount;

    /**
     * Formater.
     */
    private DecimalFormat formater;

    /**
     * Account for the Debtor.
     */
    private AccountInfo debtorAccount;

    /**
     * RateInfo of the Document.
     */
    private RateInfo rateInfo;

    /**
     * Invert this document. (Means change the map for debit and credit).
     */
    private boolean invert;

    /**
     * Summarize or not.
     */
    private SummarizeConfig config = SummarizeConfig.NONE;

    /**
     * Summarize or not.
     */
    private SummarizeCriteria summarizeCriteria = SummarizeCriteria.ACCOUNT;

    /** The doc insts. */
    private final Set<Instance> docInsts = new HashSet<>();

    /** The product2 amount. */
    private Map<Instance, Map<String, BigDecimal>> product2Amount;

    /** The key2 amount. */
    private Map<String, Map<String, BigDecimal>> key2Amount;

    /** The amounts. */
    private Map<String, BigDecimal> amounts;

    /**
     * Constructor.
     */
    protected DocumentInfo_Base() {
        this(null);
    }

    /**
     * @param _instance Instance of the Document
     */
    protected DocumentInfo_Base(final Instance _instance) {
        setInstance(_instance);
    }

    /**
     * Gets the product2 amount.
     *
     * DOES NOT EVALUATE TAXES!!!
     *
     * @return the product2 amount
     * @throws EFapsException on error
     */
    public Map<Instance, Map<String, BigDecimal>> getProduct2Amount() throws EFapsException {
        final Map<Instance, Map<String, BigDecimal>> ret;
        if (isSumsDoc()) {
            if (this.product2Amount == null) {
                this.product2Amount = new HashMap<>();
                final QueryBuilder queryBldr = new QueryBuilder(CISales.PositionAbstract);
                queryBldr.addWhereAttrEqValue(CISales.PositionAbstract.DocumentAbstractLink, this.instance);
                final MultiPrintQuery multi = queryBldr.getPrint();
                final SelectBuilder selInst = new SelectBuilder().linkto(CISales.PositionAbstract.Product)
                        .instance();
                multi.addSelect(selInst);
                multi.addAttribute(CISales.PositionSumAbstract.RateNetPrice,
                        CISales.PositionSumAbstract.RateCrossPrice, CISales.PositionSumAbstract.Tax);
                multi.execute();
                while (multi.next()) {
                    final BigDecimal netAmount = multi.getAttribute(CISales.PositionSumAbstract.RateNetPrice);
                    final BigDecimal crossAmount = multi.getAttribute(CISales.PositionSumAbstract.RateCrossPrice);
                    final Instance prodInst = multi.getSelect(selInst);
                    final Map<String, BigDecimal> tmpMap;
                    if (this.product2Amount.containsKey(prodInst)) {
                        tmpMap = this.product2Amount.get(prodInst);
                        tmpMap.put(Account2Case4AmountConfig.NET.name(),
                                tmpMap.get(Account2Case4AmountConfig.NET.name()).add(netAmount));
                        tmpMap.put(Account2Case4AmountConfig.CROSS.name(),
                                tmpMap.get(Account2Case4AmountConfig.CROSS.name()).add(crossAmount));
                    } else {
                        tmpMap = new HashMap<>();
                        tmpMap.put(Account2Case4AmountConfig.NET.name(), netAmount);
                        tmpMap.put(Account2Case4AmountConfig.CROSS.name(), crossAmount);
                    }
                    this.product2Amount.put(prodInst, tmpMap);
                }
            }
            ret = this.product2Amount;
        } else {
            ret = Collections.emptyMap();
        }
        return ret;
    }

    /**
     * Gets the key2 amount.
     *
     * @param _parameter Parameter as passed by the eFaps API
     * @return the key2 amount
     * @throws EFapsException on error
     */
    public Map<String, Map<String, BigDecimal>> getKey2Amount(final Parameter _parameter) throws EFapsException {
        final Map<String, Map<String, BigDecimal>> ret;
        if (isSumsDoc()) {
            if (this.key2Amount == null) {
                this.key2Amount = new HashMap<>();
                // let others participate
                for (final IOnDocumentInfo listener : Listener.get()
                        .<IOnDocumentInfo>invoke(IOnDocumentInfo.class)) {
                    for (final Entry<String, Map<String, BigDecimal>> entry : listener.getKey2Amount(this.instance)
                            .entrySet()) {
                        if (this.key2Amount.containsKey(entry.getKey())) {
                            if (entry.getValue() == null) {
                                this.key2Amount.remove(entry.getKey());
                            } else {
                                this.key2Amount.put(entry.getKey(), this.key2Amount.get(entry.getKey()));
                            }
                        } else {
                            this.key2Amount.put(entry.getKey(), entry.getValue());
                        }
                    }
                }

                final String[] swaps = _parameter.getParameterValues("swapInstance");
                if (ArrayUtils.isNotEmpty(swaps)) {
                    // find out in which of the documents are we right now
                    String[] docOids = _parameter.getParameterValues("document");
                    if (docOids == null) {
                        docOids = (String[]) Context.getThreadContext().getSessionAttribute("docOids");
                    }
                    int idx = 0;
                    for (int i = 0; i < docOids.length; i++) {
                        if (i > 0 && i % 2 == 0) {
                            idx++;
                        }
                        if (getInstance().getOid().equals(docOids[i])) {
                            break;
                        }
                    }
                    final Instance swapInstance = Instance.get(swaps[idx]);
                    if (swapInstance.isValid()) {
                        final PrintQuery print = CachedPrintQuery.get4Request(swapInstance);
                        final SelectBuilder selFromInst = SelectBuilder.get()
                                .linkto(CISales.Document2Document4Swap.FromLink).instance();
                        final SelectBuilder selToInst = SelectBuilder.get()
                                .linkto(CISales.Document2Document4Swap.ToLink).instance();
                        print.addSelect(selFromInst, selToInst);
                        print.execute();
                        if (getInstance().equals(print.getSelect(selFromInst))) {
                            this.key2Amount.put("From", getAmounts());
                        } else if (getInstance().equals(print.getSelect(selToInst))) {
                            this.key2Amount.put("To", getAmounts());
                        }
                    }
                }
            }
            ret = this.key2Amount;
        } else {
            ret = Collections.emptyMap();
        }
        return ret;
    }

    /**
     * Getter method for the instance variable {@link #invert}.
     *
     * @return value of instance variable {@link #invert}
     */
    public boolean isInvert() {
        return this.invert;
    }

    /**
     * Setter method for instance variable {@link #invert}.
     *
     * @param _invert value for instance variable {@link #invert}
     */

    public void setInvert(final boolean _invert) {
        this.invert = _invert;
    }

    /**
     * Getter method for the instance variable {@link #costValidated}.
     *
     * @return value of instance variable {@link #costValidated}
     */
    public boolean isCostValidated() {
        return this.costValidated;
    }

    /**
     * Setter method for instance variable {@link #costValidated}.
     *
     * @param _costValidated value for instance variable {@link #costValidated}
     */

    public void setCostValidated(final boolean _costValidated) {
        this.costValidated = _costValidated;
    }

    /**
     * Getter method for the instance variable {@link #formater}.
     *
     * @return value of instance variable {@link #formater}
     * @throws EFapsException on error
     */
    public DecimalFormat getFormater() throws EFapsException {
        if (this.formater == null) {
            this.formater = NumberFormatter.get().getTwoDigitsFormatter();
        }
        return this.formater;
    }

    /**
     * Setter method for instance variable {@link #formater}.
     *
     * @param _formater value for instance variable {@link #formater}
     */
    public void setFormater(final DecimalFormat _formater) {
        this.formater = _formater;
    }

    /**
     * Gets the amounts.
     *
     * @return the amounts
     * @throws EFapsException on error
     */
    public Map<String, BigDecimal> getAmounts() throws EFapsException {
        if (this.amounts == null) {
            this.amounts = new HashMap<>();
            if (isSumsDoc()) {
                final PrintQuery print = CachedPrintQuery.get4Request(this.instance);
                print.addAttribute(CISales.DocumentSumAbstract.RateNetTotal,
                        CISales.DocumentSumAbstract.RateCrossTotal, CISales.DocumentSumAbstract.RateTaxes);
                print.execute();
                this.amounts.put(Account2Case4AmountConfig.NET.name(),
                        print.getAttribute(CISales.DocumentSumAbstract.RateNetTotal));
                this.amounts.put(Account2Case4AmountConfig.CROSS.name(),
                        print.getAttribute(CISales.DocumentSumAbstract.RateCrossTotal));
                final Taxes taxes = print.getAttribute(CISales.DocumentSumAbstract.RateTaxes);
                if (taxes != null) {
                    for (final TaxEntry entry : taxes.getEntries()) {
                        this.amounts.put(entry.getUUID().toString(), entry.getAmount());
                    }
                }
            } else if (isPaymentDoc()) {
                final PrintQuery print = CachedPrintQuery.get4Request(this.instance);
                print.addAttribute(CISales.PaymentDocumentAbstract.Amount);
                print.execute();
                this.amounts.put(Account2Case4AmountConfig.NET.name(),
                        print.getAttribute(CISales.PaymentDocumentAbstract.Amount));
                this.amounts.put(Account2Case4AmountConfig.CROSS.name(),
                        print.getAttribute(CISales.PaymentDocumentAbstract.Amount));

            }
        }
        return this.amounts;
    }

    /**
     * Gets the amount.
     *
     * @param _acc2case the acc 2 case
     * @return the amount
     * @throws EFapsException on error
     */
    public BigDecimal getAmount(final Account2CaseInfo _acc2case) throws EFapsException {
        return getAmount(_acc2case.getAmountConfig(), _acc2case.getAccountInstance());
    }

    /**
     * Getter method for the instance variable {@link #amount}.
     *
     * @param _amountConfig the amount config
     * @param _accInst the acc inst
     * @return value of instance variable {@link #amount}
     * @throws EFapsException on error
     */
    public BigDecimal getAmount(final Account2Case4AmountConfig _amountConfig, final Instance _accInst)
            throws EFapsException {
        final BigDecimal ret;
        if (this.amount != null) {
            ret = this.amount;
        } else {
            ret = DocumentInfo_Base.getAmount4Map(getAmounts(), _amountConfig, _accInst);
        }
        return ret;
    }

    /**
     * Setter method for instance variable {@link #amount}.
     *
     * @param _amount value for instance variable {@link #amount}
     * @return the document info
     */
    public DocumentInfo setAmount(final BigDecimal _amount) {
        this.amount = _amount;
        return (DocumentInfo) this;
    }

    /**
     * Getter method for the instance variable {@link #debitAccounts}.
     *
     * @return value of instance variable {@link #debitAccounts}
     */
    public Set<AccountInfo> getDebitAccounts() {
        return this.invert ? this.creditAccounts : this.debitAccounts;
    }

    /**
     * @param _accInfo acinfo
     * @return this
     * @throws EFapsException on error
     */
    public DocumentInfo addDebit(final AccountInfo _accInfo) throws EFapsException {
        if (InstanceUtils.isValid(_accInfo.getInstance())) {
            add(this.debitAccounts, _accInfo, true);
        }
        return (DocumentInfo) this;
    }

    /**
     * Getter method for the instance variable {@link #creditAccounts}.
     *
     * @return value of instance variable {@link #creditAccounts}
     */
    public Set<AccountInfo> getCreditAccounts() {
        return this.invert ? this.debitAccounts : this.creditAccounts;
    }

    /**
     * @param _accInfo acinfo
     * @return this
     * @throws EFapsException on error
     */
    public DocumentInfo addCredit(final AccountInfo _accInfo) throws EFapsException {
        if (InstanceUtils.isValid(_accInfo.getInstance())) {
            add(this.creditAccounts, _accInfo, false);
        }
        return (DocumentInfo) this;
    }

    /**
     * Adds the.
     *
     * @param _accounts accounts to add to
     * @param _accInfo the new account
     * @param _debit the debit
     * @throws EFapsException on error
     */
    protected void add(final Set<AccountInfo> _accounts, final AccountInfo _accInfo, final boolean _debit)
            throws EFapsException {
        if (_accInfo.getRateInfo() == null) {
            if (getRateInfo() != null) {
                _accInfo.setRateInfo(getRateInfo(), getRatePropKey());
            } else {
                _accInfo.setRateInfo(RateInfo.getDummyRateInfo(), getRatePropKey());
            }
        }
        boolean add = true;
        if (SummarizeConfig.BOTH.equals(getSummarizeConfig())
                || SummarizeConfig.DEBIT.equals(getSummarizeConfig()) && _debit
                || SummarizeConfig.CREDIT.equals(getSummarizeConfig()) && !_debit) {
            _accInfo.setAmountRate(null); // reset the amount rate
            for (final AccountInfo acc : _accounts) {
                switch (getSummarizeCriteria()) {
                case LABEL:
                    if (acc.getInstance().equals(_accInfo.getInstance())
                            && acc.getRateInfo().getCurrencyInstance()
                                    .equals(_accInfo.getRateInfo().getCurrencyInstance())
                            && (acc.getLabelInst() == null && _accInfo.getLabelInst() == null
                                    || acc.getLabelInst() != null
                                            && acc.getLabelInst().equals(_accInfo.getLabelInst()))) {
                        acc.addAmount(_accInfo.getAmount());
                        add = false;
                        break;
                    }
                    break;
                case ALL:
                    if (acc.getInstance().equals(_accInfo.getInstance())
                            && acc.getRateInfo().getCurrencyInstance()
                                    .equals(_accInfo.getRateInfo().getCurrencyInstance())
                            && (acc.getLabelInst() == null && _accInfo.getLabelInst() == null
                                    || acc.getLabelInst() != null
                                            && acc.getLabelInst().equals(_accInfo.getLabelInst()))
                            && (acc.getRemark() == null && _accInfo.getRemark() == null
                                    || acc.getRemark() != null && acc.getRemark().equals(_accInfo.getRemark()))) {
                        acc.addAmount(_accInfo.getAmount());
                        add = false;
                        break;
                    }
                    break;
                case ACCOUNT:
                default:
                    if (acc.getInstance().equals(_accInfo.getInstance()) && acc.getRateInfo().getCurrencyInstance()
                            .equals(_accInfo.getRateInfo().getCurrencyInstance())) {
                        acc.addAmount(_accInfo.getAmount());
                        add = false;
                        break;
                    }
                    break;
                }
            }
        }
        if (add) {
            _accounts.add(_accInfo);
        }
    }

    /**
     * Setter method for instance variable {@link #debtorAccount}.
     *
     * @param _debtorAccount value for instance variable {@link #debtorAccount}
     */

    public void setDebtorAccount(final AccountInfo _debtorAccount) {
        this.debtorAccount = _debtorAccount;
    }

    /**
     * Getter method for the instance variable {@link #debtorAccount}.
     *
     * @return value of instance variable {@link #debtorAccount}
     */
    public AccountInfo getDebtorAccount() {
        return this.debtorAccount;
    }

    /**
     * Getter method for the instance variable {@link #currSymbol}.
     *
     * @return value of instance variable {@link #currSymbol}
     * @throws EFapsException on error
     */
    public String getCurrSymbol() throws EFapsException {
        return this.rateInfo.getCurrencyInstObj().getSymbol();
    }

    /**
     * Getter method for the instance variable {@link #date}.
     *
     * @return value of instance variable {@link #date}
     */
    public DateTime getDate() {
        return this.date;
    }

    /**
     * @param _date date for this document
     */
    public void setDate(final DateTime _date) {
        this.date = _date;
    }

    /**
     * Gets the date used for rateevaluation.
     *
     * @return the date used for rateevaluation
     */
    public DateTime getRateDate() {
        if (this.rateDate == null) {
            this.rateDate = getDate();
        }
        return this.rateDate;
    }

    /**
     * Sets the date used for rateevaluation.
     *
     * @param _rateDate the new date used for rateevaluation
     */
    public void setRateDate(final DateTime _rateDate) {
        this.rateDate = _rateDate;
    }

    /**
     * @return Date as String
     * @throws EFapsException on error
     */
    public String getDateString() throws EFapsException {
        final DateTimeFormatter formatter = DateTimeFormat.mediumDate();
        return this.date.withChronology(Context.getThreadContext().getChronology())
                .toString(formatter.withLocale(Context.getThreadContext().getLocale()));
    }

    /**
     * @return Instance of the Rate Currency
     */
    public Instance getRateCurrInst() {
        return this.rateInfo.getCurrencyInstance();
    }

    /**
     * Getter method for the instance variable {@link #stockDoc}.
     *
     * @return value of instance variable {@link #stockDoc}
     */
    public boolean isStockDoc() {
        return this.stockDoc;
    }

    /**
     * Getter method for the instance variable {@link #sumsDoc}.
     *
     * @return value of instance variable {@link #sumsDoc}
     */
    public boolean isSumsDoc() {
        return this.sumsDoc;
    }

    /**
     * Getter method for the instance variable {@link #instance}.
     *
     * @return value of instance variable {@link #instance}
     */
    public Instance getInstance() {
        return this.instance;
    }

    /**
     * Setter method for instance variable {@link #instance}.
     *
     * @param _instance value for instance variable {@link #instance}
     */

    public void setInstance(final Instance _instance) {
        this.instance = _instance;
        if (_instance != null && _instance.isValid()) {
            setSumsDoc(_instance.getType().isKindOf(CISales.DocumentSumAbstract.getType()));
            setPaymentDoc(_instance.getType().isKindOf(CISales.PaymentDocumentIOAbstract.getType()));
            setStockDoc(_instance.getType().isKindOf(CISales.DocumentStockAbstract.getType()));
        }
    }

    /**
     * @return the sum in the rate currency
     */
    public BigDecimal getRateCreditSum() {
        BigDecimal ret = BigDecimal.ZERO;
        for (final AccountInfo account : getCreditAccounts()) {
            ret = ret.add(account.getAmount().setScale(2, BigDecimal.ROUND_HALF_UP));
        }
        return ret;
    }

    /**
     * Gets the credit sum.
     *
     * @param _parameter Parameter as passed by the eFaps API
     * @return the sum of credit accounts in base currency
     * @throws EFapsException on error
     */
    public BigDecimal getCreditSum(final Parameter _parameter) throws EFapsException {
        BigDecimal ret = BigDecimal.ZERO;
        for (final AccountInfo account : getCreditAccounts()) {
            ret = ret.add(account.getAmountRate(_parameter).setScale(2, BigDecimal.ROUND_HALF_UP));
        }
        return ret;
    }

    /**
     * Gets the credit sum formated.
     *
     * @param _parameter Parameter as passed by the eFaps API
     * @return the sum of credit accounts formated
     * @throws EFapsException on error
     */
    public String getCreditSumFormated(final Parameter _parameter) throws EFapsException {
        return getFormater().format(getCreditSum(_parameter));
    }

    /**
     * @return the sum in the rate currency
     */
    public BigDecimal getRateDebitSum() {
        BigDecimal ret = BigDecimal.ZERO;
        for (final AccountInfo account : getDebitAccounts()) {
            ret = ret.add(account.getAmount().setScale(2, BigDecimal.ROUND_HALF_UP));
        }
        return ret;
    }

    /**
     * Gets the debit sum.
     *
     * @param _parameter Parameter as passed by the eFaps API
     * @return the sum of all debit accounts in base currency
     * @throws EFapsException on error
     */
    public BigDecimal getDebitSum(final Parameter _parameter) throws EFapsException {
        BigDecimal ret = BigDecimal.ZERO;
        for (final AccountInfo account : getDebitAccounts()) {
            ret = ret.add(account.getAmountRate(_parameter).setScale(2, BigDecimal.ROUND_HALF_UP));
        }
        return ret;
    }

    /**
     * Gets the debit sum formated.
     *
     * @param _parameter Parameter as passed by the eFaps API
     * @return the sum of all debit accounts formated
     * @throws EFapsException on error
     */
    public String getDebitSumFormated(final Parameter _parameter) throws EFapsException {
        return getFormater().format(getDebitSum(_parameter));
    }

    /**
     * Gets the difference.
     *
     * @param _parameter Parameter as passed by the eFaps API
     * @return the difference between the sum of debit accounts and the sum of
     *         credit accounts
     * @throws EFapsException on error
     */
    public BigDecimal getDifference(final Parameter _parameter) throws EFapsException {
        return getDebitSum(_parameter).subtract(getCreditSum(_parameter)).abs();
    }

    /**
     * Gets the difference formated.
     *
     * @param _parameter Parameter as passed by the eFaps API
     * @return the difference between the sum of debit accounts and the sum of
     *         credit accounts formated
     * @throws EFapsException on error
     */
    public String getDifferenceFormated(final Parameter _parameter) throws EFapsException {
        return getFormater().format(getDifference(_parameter));
    }

    /**
     * Getter method for the instance variable {@link #rateInfo}.
     *
     * @return value of instance variable {@link #rateInfo}
     */
    public RateInfo getRateInfo() {
        return this.rateInfo;
    }

    /**
     * Setter method for instance variable {@link #rateInfo}.
     *
     * @param _rateInfo value for instance variable {@link #rateInfo}
     */
    public void setRateInfo(final RateInfo _rateInfo) {
        this.rateInfo = _rateInfo;
    }

    /**
     * Getter method for the instance variable {@link #paymentDoc}.
     *
     * @return value of instance variable {@link #paymentDoc}
     */
    public boolean isPaymentDoc() {
        return this.paymentDoc;
    }

    /**
     * Setter method for instance variable {@link #paymentDoc}.
     *
     * @param _paymentDoc value for instance variable {@link #paymentDoc}
     */
    public void setPaymentDoc(final boolean _paymentDoc) {
        this.paymentDoc = _paymentDoc;
    }

    /**
     * Setter method for instance variable {@link #stockDoc}.
     *
     * @param _stockDoc value for instance variable {@link #stockDoc}
     */
    public void setStockDoc(final boolean _stockDoc) {
        this.stockDoc = _stockDoc;
    }

    /**
     * Setter method for instance variable {@link #sumsDoc}.
     *
     * @param _sumsDoc value for instance variable {@link #sumsDoc}
     */
    public void setSumsDoc(final boolean _sumsDoc) {
        this.sumsDoc = _sumsDoc;
    }

    /**
     * Valid means not zero and equal.
     *
     * @param _parameter Parameter as passed by the eFaps API
     * @return true if valid, else false
     * @throws EFapsException on error
     */
    public boolean isValid(final Parameter _parameter) throws EFapsException {
        return getDebitSum(_parameter).compareTo(BigDecimal.ZERO) != 0
                && getDebitSum(_parameter).compareTo(getCreditSum(_parameter)) == 0;
    }

    /**
     * Getter method for the instance variable {@link #summarize}.
     *
     * @return value of instance variable {@link #summarize}
     */
    public SummarizeConfig getSummarizeConfig() {
        return this.config;
    }

    /**
     * Setter method for instance variable {@link #summarize}.
     *
     * @param _config the new summarize config
     */
    public void setSummarizeConfig(final SummarizeConfig _config) {
        this.config = _config;
    }

    /**
     * Gets the description.
     *
     * @param _parameter Parameter as passed by the eFaps API
     * @return the description
     * @throws EFapsException on error
     */
    public String getDescription(final Parameter _parameter) throws EFapsException {
        String labelStr = "";
        final Instance caseInstTmp = getCaseInst();
        if (caseInstTmp != null && caseInstTmp.isValid()) {
            final PrintQuery print = new CachedPrintQuery(caseInstTmp, Case.CACHEKEY);
            print.addAttribute(CIAccounting.CaseAbstract.Label);
            print.execute();
            labelStr = print.<String>getAttribute(CIAccounting.CaseAbstract.Label);
        } else {
            labelStr = DBProperties.getProperty(DocumentInfo.class.getName() + "."
                    + (InstanceUtils.isValid(getInstance()) ? getInstance().getType().getName() : "Default")
                    + ".description");
        }
        final StrSubstitutor sub = new StrSubstitutor(getMap4Substitutor(_parameter));
        return sub.replace(labelStr);
    }

    /**
     * Getter method for the instance variable {@link #caseInst}.
     *
     * @return value of instance variable {@link #caseInst}
     */
    public Instance getCaseInst() {
        return this.caseInst;
    }

    /**
     * Setter method for instance variable {@link #caseInst}.
     *
     * @param _caseInst value for instance variable {@link #caseInst}
     */
    public void setCaseInst(final Instance _caseInst) {
        this.caseInst = _caseInst;
    }

    /**
     * Adds the doc inst.
     *
     * @param _docInst the doc inst
     */
    public void addDocInst(final Instance _docInst) {
        this.docInsts.add(_docInst);
    }

    /**
     * Gets the doc insts.
     *
     * @param _include the include
     * @return array of instances to be connected
     */
    public Instance[] getDocInsts(final boolean _include) {
        Instance[] ret = this.docInsts.toArray(new Instance[this.docInsts.size()]);
        if (_include) {
            ret = ArrayUtils.add(ret, this.instance);
        }
        return ret;
    }

    /**
     * Gets the amount4 doc.
     *
     * @param _docInst the doc inst
     * @return the amount4 doc
     */
    public BigDecimal getAmount4Doc(final Instance _docInst) {
        BigDecimal ret = BigDecimal.ZERO;
        for (final AccountInfo accInfo : getDebitAccounts()) {
            if (_docInst.equals(accInfo.getDocLink())) {
                ret = ret.add(accInfo.getAmount());
            }
        }
        for (final AccountInfo accInfo : getCreditAccounts()) {
            if (_docInst.equals(accInfo.getDocLink())) {
                ret = ret.add(accInfo.getAmount());
            }
        }
        return ret;
    }

    /**
     * Gets the rate.
     *
     * @param _parameter Parameter as passed by the eFaps API
     * @return the rate
     * @throws EFapsException on error
     */
    public BigDecimal getRate(final Parameter _parameter) throws EFapsException {
        return RateInfo.getRate(_parameter, getRateInfo(), getRatePropKey());
    }

    /**
     * Gets the rate prop key.
     *
     * @return the rate prop key
     */
    public String getRatePropKey() {
        String ret = "";
        if (this.exchangeConfig != null) {
            ret = this.exchangeConfig.name();
        } else if (getInstance() != null && getInstance().isValid()) {
            ret = getInstance().getType().getName();
        }
        return ret;
    }

    /**
     * Setter method for instance variable {@link #rounding}.
     *
     * @param _parameter Parameter as passed by the eFaps API
     * @throws EFapsException on error
     */
    public void applyRounding(final Parameter _parameter) throws EFapsException {
        if (!isValid(_parameter)) {
            final Period period = new Period();
            final Instance periodInst = period.evaluateCurrentPeriod(_parameter);
            // is does not sum to 0 but is less then the max defined
            final Properties props = Accounting.getSysConfig().getObjectAttributeValueAsProperties(periodInst);
            final BigDecimal diffMax = new BigDecimal(
                    props.getProperty(AccountingSettings.PERIOD_ROUNDINGMAXAMOUNT, "0"));
            final BigDecimal diff = getDebitSum(_parameter).subtract(getCreditSum(_parameter));
            if (diffMax.compareTo(diff.abs()) > 0) {
                final boolean debit = diff.compareTo(BigDecimal.ZERO) < 0;
                final AccountInfo accInfo;
                if (debit) {
                    accInfo = AccountInfo.get4Config(_parameter, AccountingSettings.PERIOD_ROUNDINGDEBIT);
                } else {
                    accInfo = AccountInfo.get4Config(_parameter, AccountingSettings.PERIOD_ROUNDINGCREDIT);
                }
                if (accInfo != null) {
                    accInfo.setAmount(diff.abs());
                    accInfo.setAmountRate(diff.abs());
                    final CurrencyInst currInst = period.getCurrency(periodInst);
                    accInfo.setCurrInstance(currInst.getInstance());
                    accInfo.setRateInfo(RateInfo.getDummyRateInfo(), "");
                    if (debit) {
                        addDebit(accInfo);
                    } else {
                        addCredit(accInfo);
                    }
                }
            }
        }
    }

    /**
     * Apply exchange gain loss.
     *
     * @param _parameter Parameter as passed by the eFaps API
     * @throws EFapsException on error
     */
    public void applyExchangeGainLoss(final Parameter _parameter) throws EFapsException {
        final AccountInfo gainAcc = AccountInfo.get4Config(_parameter, AccountingSettings.PERIOD_EXCHANGEGAIN);
        final AccountInfo lossAcc = AccountInfo.get4Config(_parameter, AccountingSettings.PERIOD_EXCHANGELOSS);

        if (gainAcc != null && lossAcc != null) {
            final QueryBuilder queryBldr = new QueryBuilder(CISales.Payment);
            queryBldr.addWhereAttrEqValue(CISales.Payment.TargetDocument, getInstance());
            final MultiPrintQuery multi = queryBldr.getPrint();
            final SelectBuilder selDocInst = new SelectBuilder().linkto(CISales.Payment.FromAbstractLink)
                    .instance();
            final SelectBuilder selCurInst = new SelectBuilder().linkto(CISales.Payment.CurrencyLink).instance();
            multi.addSelect(selDocInst, selCurInst);
            multi.addAttribute(CISales.Payment.Amount, CISales.Payment.Date);
            multi.execute();
            while (multi.next()) {
                final Instance docInst = multi.getSelect(selDocInst);
                final PrintQuery print = new PrintQuery(docInst);
                final SelectBuilder selDocCurInst = new SelectBuilder()
                        .linkto(CISales.DocumentSumAbstract.RateCurrencyId).instance();
                print.addSelect(selDocCurInst);
                print.addAttribute(CIERP.DocumentAbstract.Date);
                print.execute();
                final Instance curInst = multi.getSelect(selCurInst);
                final Instance docCurInst = print.getSelect(selDocCurInst);
                final DateTime docDate = print.getAttribute(CIERP.DocumentAbstract.Date);
                final DateTime dateTmp = multi.getAttribute(CISales.Payment.Date);
                final BigDecimal amountTmp = multi.getAttribute(CISales.Payment.Amount);

                if (!curInst.equals(Currency.getBaseCurrency()) || !docCurInst.equals(Currency.getBaseCurrency())) {
                    final Currency currency = new Currency();
                    final RateInfo[] rateInfos1 = currency.evaluateRateInfos(_parameter, dateTmp, curInst,
                            docCurInst);
                    final RateInfo[] rateInfos2 = currency.evaluateRateInfos(_parameter, docDate, curInst,
                            docCurInst);
                    final int idx;
                    // payment in BaseCurreny ==> Document was not BaseCurrency therefore current against target
                    if (curInst.equals(Currency.getBaseCurrency())) {
                        idx = 2;
                        // Document in  BaseCurrency ==> payment was not BaseCurrency therefore current against base
                    } else if (docCurInst.equals(Currency.getBaseCurrency())) {
                        idx = 0;
                        // neither Document nor payment are BaseCurrency but are the same
                    } else if (curInst.equals(docCurInst)) {
                        idx = 0;
                    } else {
                        idx = 0;
                    }

                    final BigDecimal rate1 = RateInfo.getRate(_parameter, rateInfos1[idx],
                            docInst.getType().getName());
                    final BigDecimal rate2 = RateInfo.getRate(_parameter, rateInfos2[idx],
                            docInst.getType().getName());
                    if (rate1.compareTo(rate2) != 0) {
                        final BigDecimal amount1 = amountTmp.divide(rate1, BigDecimal.ROUND_HALF_UP);
                        final BigDecimal amount2 = amountTmp.divide(rate2, BigDecimal.ROUND_HALF_UP);
                        BigDecimal gainLoss = amount1.subtract(amount2);
                        if (idx == 2) {
                            gainLoss = gainLoss.multiply(rate1);
                        }
                        if (gainLoss.compareTo(BigDecimal.ZERO) != 0) {
                            final boolean out = getInstance().getType()
                                    .isKindOf(CISales.PaymentDocumentOutAbstract);
                            if (out) {
                                final boolean gain = gainLoss.compareTo(BigDecimal.ZERO) > 0;
                                for (final AccountInfo accinfo : getCreditAccounts()) {
                                    if (accinfo.getDocLink() != null && accinfo.getDocLink().equals(docInst)) {
                                        final BigDecimal accAmount;
                                        if (accinfo.getRateInfo().getCurrencyInstance()
                                                .equals(Currency.getBaseCurrency())) {
                                            accAmount = gainLoss;
                                        } else {
                                            accAmount = gainLoss.multiply(accinfo.getRate(_parameter));
                                        }
                                        accinfo.addAmount(accAmount.negate());
                                    }
                                }
                                if (gain) {
                                    gainAcc.setAmount(gainLoss.abs()).setRateInfo(RateInfo.getDummyRateInfo(),
                                            getInstance().getType().getName());
                                    addCredit(gainAcc);
                                } else {
                                    lossAcc.setAmount(gainLoss.abs()).setRateInfo(RateInfo.getDummyRateInfo(),
                                            getInstance().getType().getName());
                                    addDebit(lossAcc);
                                }
                            } else {
                                final boolean gain = gainLoss.compareTo(BigDecimal.ZERO) < 0;
                                for (final AccountInfo accinfo : getDebitAccounts()) {
                                    if (accinfo.getDocLink() != null && accinfo.getDocLink().equals(docInst)) {
                                        final BigDecimal accAmount;
                                        if (!accinfo.getRateInfo().getCurrencyInstance()
                                                .equals(Currency.getBaseCurrency())) {
                                            accAmount = gainLoss;
                                        } else {
                                            accAmount = gainLoss.multiply(accinfo.getRate(_parameter));
                                        }
                                        accinfo.addAmount(accAmount);
                                    }
                                }
                                if (gain) {
                                    gainAcc.setAmount(gainLoss.abs()).setRateInfo(RateInfo.getDummyRateInfo(),
                                            getInstance().getType().getName());
                                    addDebit(gainAcc);
                                } else {
                                    lossAcc.setAmount(gainLoss.abs()).setRateInfo(RateInfo.getDummyRateInfo(),
                                            getInstance().getType().getName());
                                    addCredit(lossAcc);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * Getter method for the instance variable {@link #summarizeCriteria}.
     *
     * @return value of instance variable {@link #summarizeCriteria}
     */
    public SummarizeCriteria getSummarizeCriteria() {
        return this.summarizeCriteria;
    }

    /**
     * Setter method for instance variable {@link #summarizeCriteria}.
     *
     * @param _summarizeCriteria value for instance variable {@link #summarizeCriteria}
     */
    public void setSummarizeCriteria(final SummarizeCriteria _summarizeCriteria) {
        this.summarizeCriteria = _summarizeCriteria;
    }

    /**
     * Gets the rate prop key.
     *
     * @return the rate prop key
     */
    public ExchangeConfig getExchangeConfig() {
        return this.exchangeConfig;
    }

    /**
     * Sets the rate prop key.
     *
     * @param _exchangeConfig the new rate prop key
     */
    public void setExchangeConfig(final ExchangeConfig _exchangeConfig) {
        this.exchangeConfig = _exchangeConfig;
    }

    /**
     * Gets the combined.
     *
     * @param _docInfos the doc infos
     * @param _config the config
     * @param _criteria the criteria
     * @return the combined
     * @throws EFapsException on error
     */
    protected static DocumentInfo getCombined(final Collection<DocumentInfo> _docInfos,
            final SummarizeConfig _config, final SummarizeCriteria _criteria) throws EFapsException {
        final DocumentInfo ret;
        if (_docInfos.size() == 1) {
            ret = _docInfos.iterator().next();
            for (final AccountInfo accInfo : ret.getCreditAccounts()) {
                accInfo.setDocLink(ret.getInstance());
            }
            for (final AccountInfo accInfo : ret.getDebitAccounts()) {
                accInfo.setDocLink(ret.getInstance());
            }
        } else {
            ret = new DocumentInfo();
            ret.setRateInfo(_docInfos.iterator().next().getRateInfo());
            ret.setSummarizeConfig(_config);
            ret.setSummarizeCriteria(_criteria);
            ret.setCaseInst(_docInfos.iterator().next().getCaseInst());
            for (final DocumentInfo documentInfo : _docInfos) {
                for (final AccountInfo accInfo : documentInfo.getCreditAccounts()) {
                    ret.add(ret.getCreditAccounts(), accInfo, false);
                    if (!SummarizeConfig.BOTH.equals(_config) && !SummarizeConfig.CREDIT.equals(_config)) {
                        accInfo.setDocLink(documentInfo.getInstance());
                    }
                }
                for (final AccountInfo accInfo : documentInfo.getDebitAccounts()) {
                    ret.add(ret.getDebitAccounts(), accInfo, true);
                    if (!SummarizeConfig.BOTH.equals(_config) && !SummarizeConfig.DEBIT.equals(_config)) {
                        accInfo.setDocLink(documentInfo.getInstance());
                    }
                }
            }
        }
        return ret;
    }

    /**
     * Gets the map4 substitutor.
     *
     * @param _parameter Parameter as passed by the eFaps API
     * @return the map4 substitutor
     * @throws EFapsException on error
     */
    public final Map<String, String> getMap4Substitutor(final Parameter _parameter) throws EFapsException {
        final Map<String, String> ret = new HashMap<>();
        final String transdateStr = _parameter.getParameterValue("date");
        final DateTime transdate;
        if (transdateStr == null) {
            if (_parameter.getParameterValue("date_eFapsDate") != null) {
                transdate = DateUtil.getDateFromParameter(_parameter.getParameterValue("date_eFapsDate"));
            } else {
                transdate = new DateTime();
            }
        } else {
            transdate = new DateTime(transdateStr);
        }

        ret.put(Accounting.SubstitutorKeys.TRANSACTION_DATE.name(),
                transdate.toString(DateTimeFormat.mediumDate().withLocale(Context.getThreadContext().getLocale())));

        ret.put(Accounting.SubstitutorKeys.DOCUMENT_DATE.name(),
                getDate() == null ? ""
                        : getDate().toString(
                                DateTimeFormat.mediumDate().withLocale(Context.getThreadContext().getLocale())));

        if (getInstance() != null && getInstance().isValid()) {
            ret.put(Accounting.SubstitutorKeys.DOCUMENT_TYPE.name(), getInstance().getType().getLabel());
            if (getInstance().getType().isKindOf(CIERP.DocumentAbstract)) {
                final PrintQuery print = new PrintQuery(getInstance());
                print.addAttribute(CIERP.DocumentAbstract.Name);
                print.executeWithoutAccessCheck();
                ret.put(Accounting.SubstitutorKeys.DOCUMENT_NAME.name(),
                        print.<String>getAttribute(CIERP.DocumentAbstract.Name));
            }
        } else {
            ret.put(Accounting.SubstitutorKeys.DOCUMENT_TYPE.name(), "");
            ret.put(Accounting.SubstitutorKeys.DOCUMENT_NAME.name(), "");
        }
        return ret;
    }

    /**
     * Gets the amount for map.
     *
     * @param _map the map
     * @param _amountConfig the amount config
     * @param _accInst the acc inst
     * @return the amount for map
     * @throws EFapsException on error
     */
    public static BigDecimal getAmount4Map(final Map<String, BigDecimal> _map,
            final Account2Case4AmountConfig _amountConfig, final Instance _accInst) throws EFapsException {
        BigDecimal ret = BigDecimal.ZERO;
        if (_amountConfig != null) {
            switch (_amountConfig) {
            case TAX:
                final QueryBuilder attrQueryBldr = new QueryBuilder(CIAccounting.AccountBalanceSheetLiability2Tax);
                attrQueryBldr.addWhereAttrEqValue(CIAccounting.AccountBalanceSheetLiability2Tax.FromAccountLink,
                        _accInst);
                final QueryBuilder queryBldr = new QueryBuilder(CISales.Tax);
                queryBldr.addWhereAttrInQuery(CISales.Tax.ID,
                        attrQueryBldr.getAttributeQuery(CIAccounting.AccountBalanceSheetLiability2Tax.ToTaxLink));
                final CachedMultiPrintQuery multi = queryBldr.getCachedPrint4Request();
                multi.addAttribute(CISales.Tax.UUID);
                multi.execute();
                while (multi.next()) {
                    final String uuid = multi.getAttribute(CISales.Tax.UUID);
                    if (_map.containsKey(uuid)) {
                        ret = ret.add(_map.get(uuid));
                    }
                }
                break;
            default:
                ret = _map.get(_amountConfig.name());
                break;
            }
        }
        return ret;
    }
}