org.openbravo.erpCommon.ad_forms.Fact.java Source code

Java tutorial

Introduction

Here is the source code for org.openbravo.erpCommon.ad_forms.Fact.java

Source

/*
 ******************************************************************************
 * The contents of this file are subject to the   Compiere License  Version 1.1
 * ("License"); You may not use this file except in compliance with the License
 * You may obtain a copy of the License at http://www.compiere.org/license.html
 * Software distributed under the License is distributed on an  "AS IS"  basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
 * the specific language governing rights and limitations under the License.
 * The Original Code is                  Compiere  ERP & CRM  Business Solution
 * The Initial Developer of the Original Code is Jorg Janke  and ComPiere, Inc.
 * Portions created by Jorg Janke are Copyright (C) 1999-2001 Jorg Janke, parts
 * created by ComPiere are Copyright (C) ComPiere, Inc.;   All Rights Reserved.
 * Contributor(s): Openbravo SLU
 * Contributions are Copyright (C) 2001-2015 Openbravo S.L.U.
 ******************************************************************************
 */
package org.openbravo.erpCommon.ad_forms;

import java.math.BigDecimal;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

import javax.servlet.ServletException;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.openbravo.base.secureApp.VariablesSecureApp;
import org.openbravo.database.ConnectionProvider;

public class Fact {
    static Logger log4jFact = Logger.getLogger(Fact.class);

    /** Document */
    private AcctServer m_doc = null;

    public AcctServer getM_doc() {
        return m_doc;
    }

    /** Accounting Schema */
    private AcctSchema m_acctSchema = null;

    /** Posting Type */
    private String m_postingType = null;

    public final BigDecimal ZERO = new BigDecimal("0");

    /** Actual Balance Type */
    public static final String POST_Actual = "A";
    /** Budget Balance Type */
    public static final String POST_Budget = "B";
    /** Encumbrance Posting */
    public static final String POST_Commitment = "C";

    /** Lines */
    private ArrayList<Object> m_lines = new ArrayList<Object>();

    @Deprecated
    // Use TABLEID_Invoice instead
    public static final String EXCHANGE_DOCTYPE_Invoice = "318";
    @Deprecated
    // Use TABLEID_Payment instead
    public static final String EXCHANGE_DOCTYPE_Payment = "D1A97202E832470285C9B1EB026D54E2";
    @Deprecated
    // Use TABLEID_Transaction instead
    public static final String EXCHANGE_DOCTYPE_Transaction = "4D8C3B3C31D1410DA046140C9F024D17";

    /**
     * Constructor
     * 
     * @param document
     *          pointer to document
     * @param acctSchema
     *          Account Schema to create accounts
     * @param defaultPostingType
     *          the default Posting type (actual,..) for this posting
     */
    public Fact(AcctServer document, AcctSchema acctSchema, String defaultPostingType) {
        m_doc = document;
        m_acctSchema = acctSchema;
        m_postingType = defaultPostingType;
        //
        log4jFact.debug("Fact[" + m_doc.DocumentNo + "," + "AcctSchema[" + m_acctSchema.m_C_AcctSchema_ID + "-"
                + m_acctSchema.m_Name + ",PostType=" + m_postingType + "]");
    } // Fact

    /**
     * Dispose
     */
    public void dispose() {
        for (int i = 0; i < m_lines.size(); i++)
            ((FactLine) m_lines.get(i)).dispose();
        m_lines.clear();
        m_lines = null;
    } // dispose

    /**
     * Create and convert Fact Line. Used to create a DR and/or CR entry
     * 
     * @param docLine
     *          the document line or null
     * @param account
     *          if null, line is not created
     * @param C_Currency_ID
     *          the currency
     * @param debitAmt
     *          debit amount, can be null
     * @param creditAmt
     *          credit amount, can be null
     * @return Fact Line
     */
    public FactLine createLine(DocLine docLine, Account account, String C_Currency_ID, String debitAmt,
            String creditAmt, String Fact_Acct_Group_ID, String SeqNo, String DocBaseType,
            ConnectionProvider conn) {
        return createLine(docLine, account, C_Currency_ID, debitAmt, creditAmt, Fact_Acct_Group_ID, SeqNo,
                DocBaseType, m_doc.DateAcct, null, conn);
    }

    /**
     * Create and convert Fact Line using a specified conversion date. Used to create a DR and/or CR
     * entry
     * 
     * @param docLine
     *          the document line or null
     * @param account
     *          if null, line is not created
     * @param C_Currency_ID
     *          the currency
     * @param debitAmt
     *          debit amount, can be null
     * @param creditAmt
     *          credit amount, can be null
     * @param Fact_Acct_Group_ID
     * 
     * @param SeqNo
     * 
     * @param DocBaseType
     * 
     * @param conversionDate
     *          Date to convert currencies if required
     * @return Fact Line
     */
    public FactLine createLine(DocLine docLine, Account account, String C_Currency_ID, String debitAmt,
            String creditAmt, String Fact_Acct_Group_ID, String SeqNo, String DocBaseType, String conversionDate,
            ConnectionProvider conn) {
        return createLine(docLine, account, C_Currency_ID, debitAmt, creditAmt, Fact_Acct_Group_ID, SeqNo,
                DocBaseType, conversionDate, null, conn);
    }

    /**
     * Create and convert Fact Line using a specified conversion date. Used to create a DR and/or CR
     * entry
     * 
     * @param docLine
     *          the document line or null
     * @param account
     *          if null, line is not created
     * @param C_Currency_ID
     *          the currency
     * @param debitAmt
     *          debit amount, can be null
     * @param creditAmt
     *          credit amount, can be null
     * @param Fact_Acct_Group_ID
     * 
     * @param SeqNo
     * 
     * @param DocBaseType
     * 
     * @param conversionDate
     *          Date to convert currencies if required
     * @param conversionRate
     *          The rate to use to convert from source amount to account amount. May be null
     * @return Fact Line
     */
    public FactLine createLine(DocLine docLine, Account account, String C_Currency_ID, String debitAmt,
            String creditAmt, String Fact_Acct_Group_ID, String SeqNo, String DocBaseType, String conversionDate,
            BigDecimal conversionRate, ConnectionProvider conn) {

        String strNegate = "";
        try {
            strNegate = AcctServerData.selectNegate(conn, m_acctSchema.m_C_AcctSchema_ID, DocBaseType);
            if (strNegate.equals(""))
                strNegate = AcctServerData.selectDefaultNegate(conn, m_acctSchema.m_C_AcctSchema_ID);
        } catch (ServletException e) {
        }
        if (strNegate.equals(""))
            strNegate = "Y";
        BigDecimal DebitAmt = new BigDecimal(debitAmt.equals("") ? "0.00" : debitAmt);
        BigDecimal CreditAmt = new BigDecimal(creditAmt.equals("") ? "0.00" : creditAmt);
        if (DebitAmt.compareTo(BigDecimal.ZERO) == 0 && CreditAmt.compareTo(BigDecimal.ZERO) == 0) {
            return null;
        }
        if (strNegate.equals("N") && (DebitAmt.compareTo(ZERO) < 0 || CreditAmt.compareTo(ZERO) < 0)) {
            BigDecimal convertedDebitAmt = StringUtils.isBlank(docLine.m_AmtAcctDr) ? ZERO
                    : new BigDecimal(docLine.m_AmtAcctDr);
            BigDecimal convertedCreditAmt = StringUtils.isBlank(docLine.m_AmtAcctCr) ? ZERO
                    : new BigDecimal(docLine.m_AmtAcctCr);

            if (DebitAmt.compareTo(ZERO) < 0) {
                CreditAmt = CreditAmt.add(DebitAmt.abs());
                creditAmt = CreditAmt.toString();
                DebitAmt = BigDecimal.ZERO;
                debitAmt = DebitAmt.toString();
                convertedCreditAmt = convertedCreditAmt.add(convertedDebitAmt.abs());
                convertedDebitAmt = BigDecimal.ZERO;
            }
            if (CreditAmt.compareTo(ZERO) < 0) {
                DebitAmt = DebitAmt.add(CreditAmt.abs());
                debitAmt = DebitAmt.toString();
                CreditAmt = BigDecimal.ZERO;
                creditAmt = CreditAmt.toString();
                convertedDebitAmt = convertedDebitAmt.add(convertedCreditAmt.abs());
                convertedCreditAmt = BigDecimal.ZERO;
            }

            // If this is a manual entry then we need to recompute Amounts which were set in loadLines for
            // GL Journal Document
            if ("GLJ".equals(DocBaseType)) {
                docLine.setConvertedAmt(docLine.m_C_AcctSchema_ID, convertedDebitAmt.toString(),
                        convertedCreditAmt.toString());
            }

            if (strNegate.equals("N") && (DebitAmt.compareTo(ZERO) < 0 || CreditAmt.compareTo(ZERO) < 0)) {
                return createLine(docLine, account, C_Currency_ID, CreditAmt.abs().toString(),
                        DebitAmt.abs().toString(), Fact_Acct_Group_ID, SeqNo, DocBaseType, conn);
            }
        }

        log4jFact.debug("createLine - " + account + " - Dr=" + debitAmt + ", Cr=" + creditAmt);
        log4jFact.debug("Starting createline");
        // Data Check
        if (account == null) {
            log4jFact.debug("end of create line");
            m_doc.setStatus(AcctServer.STATUS_InvalidAccount);
            return null;
        }
        //
        log4jFact.debug("createLine - Fact_Acct_Group_ID = " + Fact_Acct_Group_ID);
        FactLine line = new FactLine(m_doc.AD_Table_ID, m_doc.Record_ID,
                docLine == null ? "" : docLine.m_TrxLine_ID, Fact_Acct_Group_ID, SeqNo, DocBaseType);
        log4jFact.debug("createLine - line.m_Fact_Acct_Group_ID = " + line.m_Fact_Acct_Group_ID);
        log4jFact.debug("Object created");
        line.setDocumentInfo(m_doc, docLine);
        line.setAD_Org_ID(m_doc.AD_Org_ID);
        // if (docLine!=null) line.setAD_Org_ID(docLine.m_AD_Org_ID);
        log4jFact.debug("document info set");
        line.setAccount(m_acctSchema, account);
        log4jFact.debug("account set");

        log4jFact.debug(
                "C_Currency_ID: " + C_Currency_ID + " - debitAmt: " + debitAmt + " - creditAmt: " + creditAmt);
        // Amounts - one needs to be both not zero
        if (!line.setAmtSource(C_Currency_ID, debitAmt, creditAmt))
            return null;
        if (conversionDate == null || conversionDate.isEmpty()) {
            conversionDate = m_doc.DateAcct;
        }
        log4jFact.debug("C_Currency_ID: " + m_acctSchema.getC_Currency_ID() + " - ConversionDate: " + conversionDate
                + " - CurrencyRateType: " + m_acctSchema.getCurrencyRateType());
        // Convert
        if (conversionRate != null) {
            line.convertByRate(m_acctSchema.getC_Currency_ID(), conversionRate);
        } else {
            line.convert(m_acctSchema.getC_Currency_ID(), conversionDate, m_acctSchema.getCurrencyRateType(), conn);
        }
        // Optionally overwrite Acct Amount
        if (docLine != null && !docLine.m_AmtAcctDr.equals("") && !docLine.m_AmtAcctCr.equals(""))
            line.setAmtAcct(docLine.m_AmtAcctDr, docLine.m_AmtAcctCr);
        // Info
        line.setJournalInfo(m_doc.GL_Category_ID);
        line.setPostingType(m_postingType);
        // Set Info
        line.setDocumentInfo(m_doc, docLine);
        //
        log4jFact.debug("createLine - " + m_doc.DocumentNo);
        log4jFact.debug("********************* Fact - createLine - DocumentNo - " + m_doc.DocumentNo
                + " -  m_lines.size() - " + m_lines.size());

        line.roundToCurrencyPrecision();

        m_lines.add(line);
        return line;
    } // createLine

    /**
     * Add Fact Line
     * 
     * @param line
     *          fact line
     */
    void add(FactLine line) {
        m_lines.add(line);
    } // add

    /**
     * Create and convert Fact Line. Used to create either a DR or CR entry
     * 
     * @param docLine
     *          Document Line or null
     * @param accountDr
     *          Account to be used if Amt is DR balance
     * @param accountCr
     *          Account to be used if Amt is CR balance
     * @param C_Currency_ID
     *          Currency
     * @param Amt
     *          if negative Cr else Dr
     * @return FactLine
     */
    public FactLine createLine(DocLine docLine, Account accountDr, Account accountCr, String C_Currency_ID,
            String Amt, String Fact_Acct_Group_ID, String SeqNo, String DocBaseType, ConnectionProvider conn) {
        BigDecimal m_Amt = ZERO;
        try {
            if (!Amt.equals(""))
                m_Amt = new BigDecimal(Amt);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        if (m_Amt.compareTo(ZERO) < 0)
            return createLine(docLine, accountCr, C_Currency_ID, "", m_Amt.abs().toString(), Fact_Acct_Group_ID,
                    SeqNo, DocBaseType, conn);
        else
            return createLine(docLine, accountDr, C_Currency_ID, m_Amt.toString(), "", Fact_Acct_Group_ID, SeqNo,
                    DocBaseType, conn);
    } // createLine

    /**
     * Create and convert Fact Line. Used to create either a DR or CR entry
     * 
     * @param docLine
     *          Document line or null
     * @param account
     *          Account to be used
     * @param C_Currency_ID
     *          Currency
     * @param Amt
     *          if negative Cr else Dr
     * @return FactLine
     */
    public FactLine createLine(DocLine docLine, Account account, String C_Currency_ID, String Amt,
            String Fact_Acct_Group_ID, String SeqNo, String DocBaseType, ConnectionProvider conn) {
        BigDecimal m_Amt = ZERO;
        try {
            if (!Amt.equals(""))
                m_Amt = new BigDecimal(Amt);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        if (m_Amt.compareTo(ZERO) < 0)
            return createLine(docLine, account, C_Currency_ID, "", m_Amt.abs().toString(), Fact_Acct_Group_ID,
                    SeqNo, DocBaseType, conn);
        else
            return createLine(docLine, account, C_Currency_ID, Amt.toString(), "", Fact_Acct_Group_ID, SeqNo,
                    DocBaseType, conn);
    } // createLine

    /**
     * Are the lines Source Balanced
     * 
     * @return true if source lines balanced
     */
    public boolean isSourceBalanced() {
        log4jFact.debug("Starting isSourceBalanced");
        // No lines -> balanded
        if (m_lines == null || m_lines.size() == 0)
            return true;
        BigDecimal balance = getSourceBalance();
        boolean retValue = balance.compareTo(ZERO) == 0;
        if (retValue)
            log4jFact.debug("isSourceBalanced - ");
        else
            log4jFact.warn("isSourceBalanced NO - Balance=" + balance);
        return retValue;
    } // isSourceBalanced

    /**
     * Return Source Balance
     * 
     * @return source balance
     */
    protected BigDecimal getSourceBalance() {
        BigDecimal result = new BigDecimal("0");
        for (int i = 0; i < m_lines.size(); i++) {
            FactLine line = (FactLine) m_lines.get(i);
            result = result.add(line.getSourceBalance());
        }
        // log.debug ("getSourceBalance - " + result.toString());
        return result;
    } // getSourceBalance

    /**
     * Create Source Line for Suspense Balancing. Only if Suspense Balancing is enabled and not a
     * multi-currency document (double check as otherwise the rule should not have fired) If not
     * balanced create balancing entry in currency of the document
     * 
     * @return FactLine
     */
    public FactLine balanceSource(ConnectionProvider conn) {
        if (!m_acctSchema.isSuspenseBalancing() || m_doc.MultiCurrency)
            return null;
        if (m_lines.size() == 0) {
            log4jFact.error("balanceSouce failed.");
            return null;
        }
        FactLine fl = (FactLine) m_lines.get(0);
        BigDecimal diff = getSourceBalance();
        log4jFact.debug("balanceSource = " + diff);
        // new line
        FactLine line = new FactLine(m_doc.AD_Table_ID, m_doc.Record_ID, "", fl.m_Fact_Acct_Group_ID, fl.m_SeqNo,
                fl.m_DocBaseType);// antes
        // "0".
        line.setDocumentInfo(m_doc, null);
        line.setJournalInfo(m_doc.GL_Category_ID);
        line.setPostingType(m_postingType);
        // Amount
        if (diff.compareTo(ZERO) < 0) // negative balance => DR
            line.setAmtSource(m_doc.C_Currency_ID, diff.abs().toString(), ZERO.toString());
        else
            // positive balance => CR
            line.setAmtSource(m_doc.C_Currency_ID, ZERO.toString(), diff.toString());
        // Convert
        line.convert(m_acctSchema.getC_Currency_ID(), m_doc.DateAcct, m_acctSchema.getCurrencyRateType(), conn);
        line.setAccount(m_acctSchema, m_acctSchema.getSuspenseBalancing_Acct());
        //
        log4jFact.debug("balanceSource - ");
        log4jFact.debug("****************** fact - balancesource -  m_lines.size() - " + m_lines.size());
        m_lines.add(line);
        return line;
    } // balancingSource

    /**
     * Get Lines
     * 
     * @return FactLine Array
     */
    public FactLine[] getLines() {
        FactLine[] temp = new FactLine[m_lines.size()];
        m_lines.toArray(temp);
        return temp;
    } // getLines

    /**
     * Save Fact
     * 
     * @param con
     *          connection
     * @return true if all lines were saved
     */
    public boolean save(Connection con, ConnectionProvider conn, VariablesSecureApp vars) throws ServletException {
        // save Lines
        log4jFact.debug(" Fact - save() - m_lines.size - " + m_lines.size());
        if (m_lines.size() == 0)
            return true;
        for (int i = 0; i < m_lines.size(); i++) {
            FactLine fl = (FactLine) m_lines.get(i);
            if (!fl.save(con, conn, vars)) { // abort on first error
                log4jFact.warn("Save (fact): aborted. i=" + i);
                return false;
            }
        }
        return true;
    } // commit

    /**
     * Are all segments balanced
     * 
     * @return true if segments are balanced
     */
    public boolean isSegmentBalanced(ConnectionProvider conn) {
        if (m_lines.size() == 0)
            return true;

        ArrayList<Object> elementList = m_acctSchema.m_elementList;
        int size = elementList.size();
        // check all balancing segments
        for (int i = 0; i < size; i++) {
            AcctSchemaElement ase = (AcctSchemaElement) elementList.get(i);
            if (ase.m_balanced.equals("Y") && !isSegmentBalanced(ase.m_segmentType, conn))
                return false;
        }
        return true;
    } // isSegmentBalanced

    /**
     * Is Source Segment balanced.
     * 
     * @param segmentType
     *          - see AcctSchemaElement.SEGMENT_* Implemented only for Org Other sensible candidates
     *          are Project, User1/2
     * @return true if segments are balanced
     */
    public boolean isSegmentBalanced(String segmentType, ConnectionProvider conn) {
        if (segmentType.equals(AcctSchemaElement.SEGMENT_Org)) {
            log4jFact.debug("Starting isSegmentBalanced");
            HashMap<String, BigDecimal> map = new HashMap<String, BigDecimal>();
            // Add up values by key
            for (int i = 0; i < m_lines.size(); i++) {
                FactLine line = (FactLine) m_lines.get(i);
                String key = line.getAD_Org_ID(conn);
                BigDecimal bal = line.getSourceBalance();
                BigDecimal oldBal = map.get(key);
                if (oldBal != null)
                    bal = bal.add(oldBal);
                map.put(key, bal);
                // log4jFact.debug("Add Key=" + key + ", Bal=" + bal + " <- " +
                // line);
            }
            // check if all keys are zero
            Iterator<BigDecimal> values = map.values().iterator();
            while (values.hasNext()) {
                BigDecimal bal = values.next();
                if (bal.compareTo(ZERO) != 0) {
                    map.clear();
                    log4jFact.warn(
                            "isSegmentBalanced (" + segmentType + ") NO - " + toString() + ", Balance=" + bal);
                    return false;
                }
            }
            map.clear();
            log4jFact.debug("isSegmentBalanced (" + segmentType + ") - " + toString());
            return true;
        }
        log4jFact.debug("isSegmentBalanced (" + segmentType + ") (not checked) - " + toString());
        return true;
    } // isSegmentBalanced

    /**
     * Balance all segments. - For all balancing segments - For all segment values - If balance <> 0
     * create dueTo/dueFrom line overwriting the segment value
     */
    public void balanceSegments(ConnectionProvider conn) {
        log4jFact.debug("balanceSegments");
        //
        ArrayList<Object> elementList = m_acctSchema.m_elementList;
        int size = elementList.size();
        // check all balancing segments
        for (int i = 0; i < size; i++) {
            AcctSchemaElement ase = (AcctSchemaElement) elementList.get(i);
            if (ase.m_balanced.equals("Y"))
                balanceSegment(ase.m_segmentType, conn);
        }
    } // balanceSegments

    /**
     * Balance Source Segment
     * 
     * @param segmentType
     *          segment type
     */
    private void balanceSegment(String segmentType, ConnectionProvider conn) {
        // no lines -> balanced
        if (m_lines.size() == 0)
            return;
        log4jFact.debug("balanceSegment (" + segmentType + ") - ");
        // Org
        if (segmentType.equals(AcctSchemaElement.SEGMENT_Org)) {
            HashMap<String, BigDecimal> map = new HashMap<String, BigDecimal>();
            // Add up values by key
            for (int i = 0; i < m_lines.size(); i++) {
                FactLine line = (FactLine) m_lines.get(i);
                String key = line.getAD_Org_ID(conn);
                BigDecimal bal = line.getSourceBalance();
                BigDecimal oldBal = map.get(key);
                if (oldBal != null)
                    bal = bal.add(oldBal);
                map.put(key, bal);
            }
            // Create entry for non-zero element
            Iterator<String> keys = map.keySet().iterator();
            while (keys.hasNext()) {
                String key = keys.next();
                BigDecimal diff = map.get(key);
                //
                if (diff.compareTo(ZERO) != 0) {
                    // Create Balancing Entry
                    if (m_lines.size() == 0) {
                        log4jFact.error("balanceSegment failed.");
                        return;
                    }
                    FactLine fl = (FactLine) m_lines.get(0);
                    FactLine line = new FactLine(m_doc.AD_Table_ID, m_doc.Record_ID, "", fl.m_Fact_Acct_Group_ID,
                            fl.m_SeqNo, fl.m_DocBaseType);
                    line.setDocumentInfo(m_doc, null);
                    line.setJournalInfo(m_doc.GL_Category_ID);
                    line.setPostingType(m_postingType);
                    // Amount & Account
                    if (diff.compareTo(ZERO) < 0) {
                        line.setAmtSource(m_doc.C_Currency_ID, diff.abs().toString(), ZERO.toString());
                        line.setAccount(m_acctSchema, m_acctSchema.m_DueFrom_Acct);
                    } else {
                        line.setAmtSource(m_doc.C_Currency_ID, ZERO.toString(), diff.abs().toString());
                        line.setAccount(m_acctSchema, m_acctSchema.m_DueTo_Acct);
                    }
                    line.convert(m_acctSchema.getC_Currency_ID(), m_doc.DateAcct,
                            m_acctSchema.getCurrencyRateType(), conn);
                    line.setAD_Org_ID(key);
                    log4jFact.debug("balanceSegment (" + segmentType + ") - ");
                    log4jFact.debug("************* fact - balanceSegment - m_lines.size() - " + m_lines.size()
                            + " - line.ad_org_id - " + line.getAD_Org_ID(conn));
                    m_lines.add(line);
                }
            }
            map.clear();
        }
    } // balanceSegment

    /**
     * Are the lines Accounting Balanced
     * 
     * @return true if accounting lines are balanced
     */
    public boolean isAcctBalanced() {
        // no lines -> balanced
        if (m_lines == null || m_lines.size() == 0)
            return true;
        BigDecimal balance = getAcctBalance();
        boolean retValue = balance.compareTo(ZERO) == 0;
        if (retValue)
            log4jFact.debug("isAcctBalanced - ");
        else
            log4jFact.warn("isAcctBalanced NO - Balance=" + balance);
        return retValue;
    } // isAcctBalanced

    /**
     * Return Accounting Balance
     * 
     * @return true if accounting lines are balanced
     */
    protected BigDecimal getAcctBalance() {
        BigDecimal result = ZERO;
        for (int i = 0; i < m_lines.size(); i++) {
            FactLine line = (FactLine) m_lines.get(i);
            BigDecimal balance = line.getAccountingBalance();
            result = result.add(balance);
        }
        return result;
    } // getAcctBalance

    /**
     * Balance Accounting Currency. If the accounting currency is not balanced, if Currency balancing
     * is enabled create a new line using the currency balancing account with zero source balance or
     * adjust the line with the largest balance sheet account or if no balance sheet account exist,
     * the line with the largest amount
     * 
     * @return FactLine
     */
    public FactLine balanceAccounting(ConnectionProvider conn) {
        BigDecimal diff = getAcctBalance();
        log4jFact.debug("balanceAccounting - Balance=" + diff);
        FactLine line = null;
        // Create Currency Entry
        if (m_acctSchema.isCurrencyBalancing()) {
            if (m_lines.size() == 0) {
                log4jFact.error("balanceAccounting failed.");
                return null;
            }
            FactLine fl = (FactLine) m_lines.get(0);
            line = new FactLine(m_doc.AD_Table_ID, m_doc.Record_ID, "", fl.m_Fact_Acct_Group_ID, fl.m_SeqNo,
                    fl.m_DocBaseType);
            line.setDocumentInfo(m_doc, null);
            line.setJournalInfo(m_doc.GL_Category_ID);
            line.setPostingType(m_postingType);

            // Amount
            line.setAmtSource(m_doc.C_Currency_ID, ZERO.toString(), ZERO.toString());
            line.convert(m_acctSchema.getC_Currency_ID(), m_doc.DateAcct, m_acctSchema.getCurrencyRateType(), conn);
            if (diff.compareTo(ZERO) < 0)
                line.setAmtAcct(diff.abs().toString(), ZERO.toString());
            else
                line.setAmtAcct(ZERO.toString(), diff.abs().toString());
            line.setAccount(m_acctSchema, m_acctSchema.getCurrencyBalancing_Acct());
            log4jFact.debug("balanceAccounting - " + line.toString());
            log4jFact.debug("************* fact - balanceAccounting - m_lines.size() - " + m_lines.size());
            m_lines.add(line);
        } else { // Adjust biggest (Balance Sheet) line amount
            BigDecimal BSamount = ZERO;
            FactLine BSline = null;
            BigDecimal PLamount = ZERO;
            FactLine PLline = null;
            int signum = diff.signum();
            // Find line
            for (int i = 0; i < m_lines.size(); i++) {
                FactLine l = (FactLine) m_lines.get(i);
                BigDecimal amt = l.getAccountingBalance();
                // amt = amt.abs();
                if (l.isBalanceSheet() && ((amt.compareTo(BSamount) > 0 && signum != 1))
                        || ((amt.compareTo(BSamount) < 0 && signum == 1))) {
                    BSamount = amt;
                    BSline = l;
                } else if (!l.isBalanceSheet() && ((amt.compareTo(BSamount) > 0 && signum != 1))
                        || ((amt.compareTo(BSamount) < 0 && signum == 1))) {
                    PLamount = amt;
                    PLline = l;
                }
            }
            if (BSline != null)
                line = BSline;
            else
                line = PLline;

            if (line == null)
                log4jFact.error("balanceAccounting - No Line found");
            else {
                log4jFact.debug("Adjusting Amt=" + diff.toString() + "; Line=" + line.toString());
                line.currencyCorrect(diff);
                log4jFact.debug("balanceAccounting - " + line.toString());
            }
        } // correct biggest amount

        // Debug info only
        this.isAcctBalanced();

        return line;
    } // balanceAccounting

    public AcctSchema getM_acctSchema() {
        return m_acctSchema;
    }

    public void setM_acctSchema(AcctSchema schema) {
        m_acctSchema = schema;
    }

    boolean isMulticurrencyDocument() {
        boolean isMultiCurrency = false;
        for (int i = 0; i < m_lines.size(); i++) {
            FactLine factLine = (FactLine) m_lines.get(i);
            if (!factLine.getCurrency().equals(getM_acctSchema().m_C_Currency_ID)) {
                return true;
            }
        }
        return isMultiCurrency;
    }
}