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

Java tutorial

Introduction

Here is the source code for org.openbravo.erpCommon.ad_forms.DocInvoice.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.math.MathContext;
import java.math.RoundingMode;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;

import javax.servlet.ServletException;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.openbravo.base.exception.OBException;
import org.openbravo.base.secureApp.VariablesSecureApp;
import org.openbravo.client.kernel.RequestContext;
import org.openbravo.dal.core.OBContext;
import org.openbravo.dal.service.OBDal;
import org.openbravo.data.FieldProvider;
import org.openbravo.database.ConnectionProvider;
import org.openbravo.erpCommon.utility.AccDefUtility;
import org.openbravo.erpCommon.utility.CashVATUtil;
import org.openbravo.erpCommon.utility.OBDateUtils;
import org.openbravo.erpCommon.utility.SequenceIdData;
import org.openbravo.erpCommon.utility.Utility;
import org.openbravo.model.common.currency.ConversionRateDoc;
import org.openbravo.model.common.currency.Currency;
import org.openbravo.model.common.invoice.Invoice;
import org.openbravo.model.common.invoice.InvoiceLine;
import org.openbravo.model.common.invoice.InvoiceTax;
import org.openbravo.model.financialmgmt.calendar.Period;

public class DocInvoice extends AcctServer {
    private static final long serialVersionUID = 1L;
    static Logger log4jDocInvoice = Logger.getLogger(DocInvoice.class);

    DocTax[] m_taxes = null;
    DocLine_FinPaymentSchedule[] m_payments = null;

    public DocLine_FinPaymentSchedule[] getM_payments() {
        return m_payments;
    }

    DocLine[] p_lines_taxes = null;

    boolean isCashVAT = false;
    String prepaymentamt;

    String SeqNo = "0";

    /**
     * Constructor
     * 
     * @param AD_Client_ID
     *          AD_Client_ID
     */
    public DocInvoice(String AD_Client_ID, String AD_Org_ID, ConnectionProvider connectionProvider) {
        super(AD_Client_ID, AD_Org_ID, connectionProvider);
    }

    public void loadObjectFieldProvider(ConnectionProvider conn, String stradClientId, String Id)
            throws ServletException {
        setObjectFieldProvider(DocInvoiceData.selectRegistro(conn, stradClientId, Id));
    }

    public boolean loadDocumentDetails(FieldProvider[] data, ConnectionProvider conn) {
        DateDoc = data[0].getField("DateInvoiced");
        TaxIncluded = data[0].getField("IsTaxIncluded");
        C_BPartner_Location_ID = data[0].getField("C_BPartner_Location_ID");
        // Amounts
        Amounts[AMTTYPE_Gross] = data[0].getField("GrandTotal");
        if (Amounts[AMTTYPE_Gross] == null)
            Amounts[AMTTYPE_Gross] = "0";
        Amounts[AMTTYPE_Net] = data[0].getField("TotalLines");
        if (Amounts[AMTTYPE_Net] == null)
            Amounts[AMTTYPE_Net] = "0";
        Amounts[AMTTYPE_Charge] = data[0].getField("ChargeAmt");
        if (Amounts[AMTTYPE_Charge] == null)
            Amounts[AMTTYPE_Charge] = "0";

        loadDocumentType(); // lines require doc type
        // Contained Objects
        p_lines = loadLines();
        m_taxes = loadTaxes();
        m_payments = loadPayments();
        m_debt_payments = loadDebtPayments();
        isCashVAT = StringUtils.equals("Y", data[0].getField("iscashvat"));
        prepaymentamt = data[0].getField("prepaymentamt");
        return true;

    }

    private DocLine[] loadLines() {
        ArrayList<Object> list = new ArrayList<Object>();
        DocLineInvoiceData[] data = null;
        try {
            log4jDocInvoice.debug("############### groupLines = " + groupLines);
            if (groupLines.equals("Y"))
                data = DocLineInvoiceData.selectTotal(connectionProvider, Record_ID);
            else
                data = DocLineInvoiceData.select(connectionProvider, Record_ID);
        } catch (ServletException e) {
            log4jDocInvoice.warn(e);
        }
        if (data == null || data.length == 0)
            return null;
        for (int i = 0; i < data.length; i++) {
            String Line_ID = data[i].cInvoicelineId;
            DocLine_Invoice docLine = new DocLine_Invoice(DocumentType, Record_ID, Line_ID);
            docLine.loadAttributes(data[i], this);
            String strQty = data[i].qtyinvoiced;
            docLine.setQty(strQty);
            String LineNetAmt = data[i].linenetamt;
            String PriceList = data[i].pricelist;
            docLine.setAmount(LineNetAmt, PriceList, strQty);
            // Accruals & Deferrals for revenue products
            docLine.setIsDeferred("Y".equals(data[i].isdeferred));
            docLine.setDefPlanType(data[i].defplantype);
            docLine.setPeriodNumber(!"".equals(data[i].periodnumber) ? new Integer(data[i].periodnumber) : 0);
            docLine.setStartingPeriodId(data[i].cPeriodId);

            list.add(docLine);
        }
        // Return Array
        DocLine[] dl = new DocLine[list.size()];
        list.toArray(dl);
        return dl;
    } // loadLines

    private DocTax[] loadTaxes() {
        ArrayList<Object> list = new ArrayList<Object>();
        DocInvoiceData[] data = null;
        try {
            data = DocInvoiceData.select(connectionProvider, Record_ID);
        } catch (ServletException e) {
            log4jDocInvoice.warn(e);
        }
        log4jDocInvoice.debug("############### Taxes.length = " + data.length);
        for (int i = 0; i < data.length; i++) {
            String C_Tax_ID = data[i].cTaxId;
            String name = data[i].name;
            String rate = data[i].rate;

            String taxBaseAmt = data[i].taxbaseamt;
            String amount = data[i].taxamt;
            boolean isTaxDeductable = false;
            boolean isTaxUndeductable = ("Y".equals(data[i].ratetaxundeductable))
                    || ("Y".equals(data[i].orgtaxundeductable));
            if ("Y".equals(data[i].orgtaxundeductable)) {
                /*
                 * If any tax line level has tax deductable flag then override isTaxUndeductable flag for
                 * intracommunity non tax deductible organization
                 */
                if ("Y".equals(data[i].istaxdeductable)) {
                    isTaxUndeductable = false;
                    isTaxDeductable = true;
                }
            } else {
                // configured for intracommunity with tax liability
                if ("Y".equals(data[i].istaxdeductable)) {
                    isTaxDeductable = true;
                }
            }

            boolean taxIsCashVAT = StringUtils.equals(data[i].iscashvat, "Y");

            DocTax taxLine = new DocTax(C_Tax_ID, name, rate, taxBaseAmt, amount, isTaxUndeductable,
                    isTaxDeductable, taxIsCashVAT);
            list.add(taxLine);
        }
        // Return Array
        DocTax[] tl = new DocTax[list.size()];
        list.toArray(tl);
        return tl;
    } // loadTaxes

    private DocLine_Payment[] loadDebtPayments() {
        ArrayList<Object> list = new ArrayList<Object>();
        DocInvoiceData[] data = null;
        try {
            data = DocInvoiceData.selectDebtPayments(connectionProvider, Record_ID);
            log4jDocInvoice.debug("############### DebtPayments.length = " + data.length);
            for (int i = 0; i < data.length; i++) {
                //
                String Line_ID = data[i].cDebtPaymentId;
                DocLine_Payment dpLine = new DocLine_Payment(DocumentType, Record_ID, Line_ID);
                log4jDocInvoice.debug(" dpLine.m_Record_Id2 = " + data[i].cDebtPaymentId);
                dpLine.m_Record_Id2 = data[i].cDebtPaymentId;
                dpLine.C_Currency_ID_From = data[i].cCurrencyId;
                dpLine.dpStatus = data[i].status;
                dpLine.isReceipt = data[i].isreceipt;
                dpLine.isPaid = data[i].ispaid;
                dpLine.isManual = data[i].ismanual;
                dpLine.WriteOffAmt = data[i].writeoffamt;
                dpLine.Amount = data[i].amount;
                list.add(dpLine);
            }
        } catch (ServletException e) {
            log4jDocInvoice.warn(e);
        }

        // Return Array
        DocLine_Payment[] tl = new DocLine_Payment[list.size()];
        list.toArray(tl);
        return tl;
    } // loadDebtPayments

    private DocLine_FinPaymentSchedule[] loadPayments() {
        ArrayList<Object> list = new ArrayList<Object>();
        DocInvoiceData[] data = null;
        try {
            data = DocInvoiceData.selectPayments(connectionProvider, Record_ID);
            log4jDocInvoice.debug("############### DebtPayments.length = " + data.length);
            for (int i = 0; i < data.length; i++) {
                //
                String Line_ID = data[i].finPaymentScheduleId;
                DocLine_FinPaymentSchedule dpLine = new DocLine_FinPaymentSchedule(DocumentType, Record_ID,
                        Line_ID);
                log4jDocInvoice.debug(" dpLine.m_Record_Id2 = " + data[i].finPaymentScheduleId);
                dpLine.m_Record_Id2 = data[i].finPaymentScheduleId;
                dpLine.C_Currency_ID_From = data[i].cCurrencyId;
                dpLine.isPaid = data[i].ispaid;
                dpLine.Amount = data[i].amount;
                dpLine.PrepaidAmount = data[i].prepaidamt;

                list.add(dpLine);
            }
        } catch (ServletException e) {
            log4jDocInvoice.warn(e);
        }

        // Return Array
        DocLine_FinPaymentSchedule[] tl = new DocLine_FinPaymentSchedule[list.size()];
        list.toArray(tl);
        return tl;
    } // loadPayments

    /**
     * Create Facts (the accounting logic) for ARI, ARC, ARF, API, APC.
     * 
     * <pre>
     *  ARI, ARF
     *      Receivables     DR
     *      Charge                  CR
     *      TaxDue                  CR
     *      Revenue                 CR
     *  ARC
     *      Receivables             CR
     *      Charge          DR
     *      TaxDue          DR
     *      Revenue         RR
     *  API
     *      Payables                CR
     *      Charge          DR
     *      TaxCredit       DR
     *      Expense         DR
     *  APC
     *      Payables        DR
     *      Charge                  CR
     *      TaxCredit               CR
     *      Expense                 CR
     * </pre>
     * 
     * @param as
     *          accounting schema
     * @return Fact
     */
    public Fact createFact(AcctSchema as, ConnectionProvider conn, Connection con, VariablesSecureApp vars)
            throws ServletException {
        // Select specific definition
        String strClassname = AcctServerData.selectTemplateDoc(conn, as.m_C_AcctSchema_ID, DocumentType);
        if (strClassname.equals(""))
            strClassname = AcctServerData.selectTemplate(conn, as.m_C_AcctSchema_ID, AD_Table_ID);
        if (!strClassname.equals("")) {
            try {
                DocInvoiceTemplate newTemplate = (DocInvoiceTemplate) Class.forName(strClassname).newInstance();
                return newTemplate.createFact(this, as, conn, con, vars);
            } catch (Exception e) {
                log4j.error("Error while creating new instance for DocInvoiceTemplate - " + e);
            }
        }
        log4jDocInvoice.debug("Starting create fact");
        // create Fact Header
        Fact fact = new Fact(this, as, Fact.POST_Actual);
        String Fact_Acct_Group_ID = SequenceIdData.getUUID();
        // Cash based accounting
        if (!as.isAccrual())
            return null;

        /** @todo Assumes TaxIncluded = N */

        // ARI, ARF, ARI_RM
        if (DocumentType.equals(AcctServer.DOCTYPE_ARInvoice) || DocumentType.equals(AcctServer.DOCTYPE_ARProForma)
                || DocumentType.equals(AcctServer.DOCTYPE_RMSalesInvoice)) {
            log4jDocInvoice.debug("Point 1");
            // Receivables DR
            if (m_payments == null || m_payments.length == 0)
                for (int i = 0; m_debt_payments != null && i < m_debt_payments.length; i++) {
                    if (m_debt_payments[i].isReceipt.equals("Y"))
                        fact.createLine(m_debt_payments[i],
                                getAccountBPartner(C_BPartner_ID, as, true, m_debt_payments[i].dpStatus, conn),
                                this.C_Currency_ID,
                                getConvertedAmt(m_debt_payments[i].Amount, m_debt_payments[i].C_Currency_ID_From,
                                        this.C_Currency_ID, DateAcct, "", AD_Client_ID, AD_Org_ID, conn),
                                "", Fact_Acct_Group_ID, nextSeqNo(SeqNo), DocumentType, conn);
                    else
                        fact.createLine(m_debt_payments[i],
                                getAccountBPartner(C_BPartner_ID, as, false, m_debt_payments[i].dpStatus, conn),
                                this.C_Currency_ID, "",
                                getConvertedAmt(m_debt_payments[i].Amount, m_debt_payments[i].C_Currency_ID_From,
                                        this.C_Currency_ID, DateAcct, "", AD_Client_ID, AD_Org_ID, conn),
                                Fact_Acct_Group_ID, nextSeqNo(SeqNo), DocumentType, conn);
                }
            else
                for (int i = 0; m_payments != null && i < m_payments.length; i++) {
                    fact.createLine(m_payments[i], getAccountBPartner(C_BPartner_ID, as, true, false, conn),
                            this.C_Currency_ID, m_payments[i].Amount, "", Fact_Acct_Group_ID, nextSeqNo(SeqNo),
                            DocumentType, conn);
                    if (m_payments[i].C_Currency_ID_From.equals(as.m_C_Currency_ID)
                            && new BigDecimal(m_payments[i].PrepaidAmount).compareTo(ZERO) != 0) {
                        fact.createLine(m_payments[i], getAccountBPartner(C_BPartner_ID, as, true, true, conn),
                                this.C_Currency_ID, m_payments[i].PrepaidAmount, "", Fact_Acct_Group_ID,
                                nextSeqNo(SeqNo), DocumentType, conn);
                    } else if (!m_payments[i].C_Currency_ID_From.equals(as.m_C_Currency_ID)) {
                        try {
                            DocInvoiceData[] prepayments = DocInvoiceData.selectPrepayments(connectionProvider,
                                    m_payments[i].Line_ID);
                            for (int j = 0; j < prepayments.length; j++) {
                                BigDecimal prePaymentAmt = convertAmount(new BigDecimal(prepayments[j].prepaidamt),
                                        true, DateAcct, TABLEID_Payment, prepayments[j].finPaymentId,
                                        m_payments[i].C_Currency_ID_From, as.m_C_Currency_ID, m_payments[i], as,
                                        fact, Fact_Acct_Group_ID, nextSeqNo(SeqNo), conn);
                                fact.createLine(m_payments[i],
                                        getAccountBPartner(C_BPartner_ID, as, true, true, conn),
                                        m_payments[i].C_Currency_ID_From, prePaymentAmt.toString(), "",
                                        Fact_Acct_Group_ID, nextSeqNo(SeqNo), DocumentType, conn);
                            }
                        } catch (ServletException e) {
                            log4jDocInvoice.warn(e);
                        }
                    }
                }
            if ((m_payments == null || m_payments.length == 0)
                    && (m_debt_payments == null || m_debt_payments.length == 0)) {
                BigDecimal grossamt = new BigDecimal(Amounts[AMTTYPE_Gross]);
                BigDecimal prepayment = new BigDecimal(prepaymentamt);
                BigDecimal difference = grossamt.abs().subtract(prepayment.abs());
                if (!prepaymentamt.equals("0")) {
                    if (grossamt.abs().compareTo(prepayment.abs()) > 0) {
                        if (IsReturn.equals("Y")) {
                            fact.createLine(null, getAccountBPartner(C_BPartner_ID, as, true, true, conn),
                                    this.C_Currency_ID, "", prepaymentamt, Fact_Acct_Group_ID, nextSeqNo(SeqNo),
                                    DocumentType, conn);
                            fact.createLine(null, getAccountBPartner(C_BPartner_ID, as, true, false, conn),
                                    this.C_Currency_ID, "", difference.toString(), Fact_Acct_Group_ID,
                                    nextSeqNo(SeqNo), DocumentType, conn);
                        } else {
                            fact.createLine(null, getAccountBPartner(C_BPartner_ID, as, true, true, conn),
                                    this.C_Currency_ID, prepaymentamt, "", Fact_Acct_Group_ID, nextSeqNo(SeqNo),
                                    DocumentType, conn);
                            fact.createLine(null, getAccountBPartner(C_BPartner_ID, as, true, false, conn),
                                    this.C_Currency_ID, difference.toString(), "", Fact_Acct_Group_ID,
                                    nextSeqNo(SeqNo), DocumentType, conn);
                        }
                    } else {
                        if (IsReturn.equals("Y")) {
                            fact.createLine(null, getAccountBPartner(C_BPartner_ID, as, true, true, conn),
                                    this.C_Currency_ID, "", prepaymentamt, Fact_Acct_Group_ID, nextSeqNo(SeqNo),
                                    DocumentType, conn);
                        } else {
                            fact.createLine(null, getAccountBPartner(C_BPartner_ID, as, true, true, conn),
                                    this.C_Currency_ID, prepaymentamt, "", Fact_Acct_Group_ID, nextSeqNo(SeqNo),
                                    DocumentType, conn);
                        }
                    }
                } else {
                    fact.createLine(null, getAccountBPartner(C_BPartner_ID, as, true, false, conn),
                            this.C_Currency_ID, Amounts[AMTTYPE_Gross], "", Fact_Acct_Group_ID, nextSeqNo(SeqNo),
                            DocumentType, conn);
                }

            }
            // Charge CR
            log4jDocInvoice.debug("The first create line");
            fact.createLine(null, getAccount(AcctServer.ACCTTYPE_Charge, as, conn), C_Currency_ID, "",
                    getAmount(AcctServer.AMTTYPE_Charge), Fact_Acct_Group_ID, nextSeqNo(SeqNo), DocumentType, conn);
            // TaxDue CR
            log4jDocInvoice.debug("m_taxes.length: " + m_taxes);
            BigDecimal grossamt = new BigDecimal(Amounts[AMTTYPE_Gross]);
            BigDecimal prepayment = new BigDecimal(prepaymentamt);

            for (int i = 0; m_taxes != null && i < m_taxes.length; i++) {
                // New docLine created to assign C_Tax_ID value to the entry
                DocLine docLine = new DocLine(DocumentType, Record_ID, "");
                docLine.m_C_Tax_ID = m_taxes[i].m_C_Tax_ID;

                BigDecimal percentageFinalAccount = CashVATUtil._100;
                final BigDecimal taxesAmountTotal = new BigDecimal(
                        StringUtils.isBlank(m_taxes[i].m_amount) ? "0" : m_taxes[i].m_amount);
                BigDecimal taxToTransAccount = BigDecimal.ZERO;
                int precission = 0;
                OBContext.setAdminMode(true);
                try {
                    Currency currency = OBDal.getInstance().get(Currency.class, C_Currency_ID);
                    precission = currency.getStandardPrecision().intValue();
                } finally {
                    OBContext.restorePreviousMode();
                }
                if (IsReversal.equals("Y")) {
                    if (isCashVAT && m_taxes[i].m_isCashVAT) {
                        if ((m_payments == null || m_payments.length == 0)
                                && (m_debt_payments == null || m_debt_payments.length == 0)
                                && (!prepaymentamt.equals("0"))) {
                            percentageFinalAccount = ((prepayment.multiply(new BigDecimal(100)))
                                    .divide(grossamt.abs(), precission, RoundingMode.HALF_UP));
                            taxToTransAccount = CashVATUtil.calculatePercentageAmount(
                                    CashVATUtil._100.subtract(percentageFinalAccount), taxesAmountTotal,
                                    C_Currency_ID);
                        } else {
                            percentageFinalAccount = CashVATUtil
                                    .calculatePrepaidPercentageForCashVATTax(m_taxes[i].m_C_Tax_ID, Record_ID);
                            taxToTransAccount = CashVATUtil.calculatePercentageAmount(
                                    CashVATUtil._100.subtract(percentageFinalAccount), taxesAmountTotal,
                                    C_Currency_ID);
                        }
                        fact.createLine(docLine, m_taxes[i].getAccount(DocTax.ACCTTYPE_TaxDue_Trans, as, conn),
                                C_Currency_ID, taxToTransAccount.toString(), "", Fact_Acct_Group_ID,
                                nextSeqNo(SeqNo), DocumentType, conn);
                    }
                    final BigDecimal taxToFinalAccount = taxesAmountTotal.subtract(taxToTransAccount);
                    fact.createLine(docLine, m_taxes[i].getAccount(DocTax.ACCTTYPE_TaxDue, as, conn), C_Currency_ID,
                            taxToFinalAccount.toString(), "", Fact_Acct_Group_ID, nextSeqNo(SeqNo), DocumentType,
                            conn);
                } else {
                    if (isCashVAT && m_taxes[i].m_isCashVAT) {
                        if ((m_payments == null || m_payments.length == 0)
                                && (m_debt_payments == null || m_debt_payments.length == 0)
                                && (!prepaymentamt.equals("0"))) {
                            percentageFinalAccount = ((prepayment.multiply(new BigDecimal(100)))
                                    .divide(grossamt.abs(), precission, RoundingMode.HALF_UP));
                            taxToTransAccount = CashVATUtil.calculatePercentageAmount(
                                    CashVATUtil._100.subtract(percentageFinalAccount), taxesAmountTotal,
                                    C_Currency_ID);
                        } else {
                            percentageFinalAccount = CashVATUtil
                                    .calculatePrepaidPercentageForCashVATTax(m_taxes[i].m_C_Tax_ID, Record_ID);
                            taxToTransAccount = CashVATUtil.calculatePercentageAmount(
                                    CashVATUtil._100.subtract(percentageFinalAccount), taxesAmountTotal,
                                    C_Currency_ID);
                        }
                        fact.createLine(docLine, m_taxes[i].getAccount(DocTax.ACCTTYPE_TaxDue_Trans, as, conn),
                                C_Currency_ID, "", taxToTransAccount.toString(), Fact_Acct_Group_ID,
                                nextSeqNo(SeqNo), DocumentType, conn);
                    }
                    final BigDecimal taxToFinalAccount = taxesAmountTotal.subtract(taxToTransAccount);
                    fact.createLine(docLine, m_taxes[i].getAccount(DocTax.ACCTTYPE_TaxDue, as, conn), C_Currency_ID,
                            "", taxToFinalAccount.toString(), Fact_Acct_Group_ID, nextSeqNo(SeqNo), DocumentType,
                            conn);
                }
            }
            // Revenue CR
            if (p_lines != null && p_lines.length > 0) {
                for (int i = 0; i < p_lines.length; i++) {
                    Account account = ((DocLine_Invoice) p_lines[i]).getAccount(ProductInfo.ACCTTYPE_P_Revenue, as,
                            conn);
                    if (DocumentType.equals(AcctServer.DOCTYPE_RMSalesInvoice)) {
                        Account accountReturnMaterial = ((DocLine_Invoice) p_lines[i])
                                .getAccount(ProductInfo.ACCTTYPE_P_RevenueReturn, as, conn);
                        if (accountReturnMaterial != null) {
                            account = accountReturnMaterial;
                        }
                    }
                    String amount = p_lines[i].getAmount();
                    String amountConverted = "";
                    ConversionRateDoc conversionRateCurrentDoc = getConversionRateDoc(TABLEID_Invoice, Record_ID,
                            C_Currency_ID, as.m_C_Currency_ID);
                    if (conversionRateCurrentDoc != null) {
                        amountConverted = applyRate(new BigDecimal(p_lines[i].getAmount()),
                                conversionRateCurrentDoc, true).setScale(2, BigDecimal.ROUND_HALF_UP).toString();
                    } else {
                        amountConverted = getConvertedAmt(p_lines[i].getAmount(), C_Currency_ID, as.m_C_Currency_ID,
                                DateAcct, "", AD_Client_ID, AD_Org_ID, conn);
                    }
                    if (((DocLine_Invoice) p_lines[i]).isDeferred()) {
                        amount = createAccDefRevenueFact(
                                fact, (DocLine_Invoice) p_lines[i], account, ((DocLine_Invoice) p_lines[i])
                                        .getAccount(ProductInfo.ACCTTYPE_P_DefRevenue, as, conn),
                                amountConverted, as.m_C_Currency_ID, conn);
                        if (IsReversal.equals("Y")) {
                            fact.createLine(p_lines[i], account, as.m_C_Currency_ID, amount, "", Fact_Acct_Group_ID,
                                    nextSeqNo(SeqNo), DocumentType, conn);
                        } else {
                            fact.createLine(p_lines[i], account, as.m_C_Currency_ID, "", amount, Fact_Acct_Group_ID,
                                    nextSeqNo(SeqNo), DocumentType, conn);
                        }
                    } else {
                        if (IsReversal.equals("Y")) {
                            fact.createLine(p_lines[i], account, this.C_Currency_ID, amount, "", Fact_Acct_Group_ID,
                                    nextSeqNo(SeqNo), DocumentType, conn);
                        } else {
                            fact.createLine(p_lines[i], account, this.C_Currency_ID, "", amount, Fact_Acct_Group_ID,
                                    nextSeqNo(SeqNo), DocumentType, conn);
                        }
                    }
                    // If revenue has been deferred
                    if (((DocLine_Invoice) p_lines[i]).isDeferred() && !amount.equals(amountConverted)) {
                        amount = new BigDecimal(amountConverted).subtract(new BigDecimal(amount)).toString();
                        if (IsReversal.equals("Y")) {
                            fact.createLine(p_lines[i],
                                    ((DocLine_Invoice) p_lines[i]).getAccount(ProductInfo.ACCTTYPE_P_DefRevenue, as,
                                            conn),
                                    as.m_C_Currency_ID, amount, "", Fact_Acct_Group_ID, nextSeqNo(SeqNo),
                                    DocumentType, conn);
                        } else {
                            fact.createLine(p_lines[i],
                                    ((DocLine_Invoice) p_lines[i]).getAccount(ProductInfo.ACCTTYPE_P_DefRevenue, as,
                                            conn),
                                    as.m_C_Currency_ID, "", amount, Fact_Acct_Group_ID, nextSeqNo(SeqNo),
                                    DocumentType, conn);
                        }
                    }
                }
            }
            // Set Locations
            FactLine[] fLines = fact.getLines();
            for (int i = 0; i < fLines.length; i++) {
                if (fLines[i] != null) {
                    fLines[i].setLocationFromOrg(fLines[i].getAD_Org_ID(conn), true, conn); // from Loc
                    fLines[i].setLocationFromBPartner(C_BPartner_Location_ID, false, conn); // to Loc
                }
            }
        }
        // ARC
        else if (this.DocumentType.equals(AcctServer.DOCTYPE_ARCredit)) {
            log4jDocInvoice.debug("Point 2");
            // Receivables CR
            if (m_payments == null || m_payments.length == 0)
                for (int i = 0; m_debt_payments != null && i < m_debt_payments.length; i++) {
                    BigDecimal amount = new BigDecimal(m_debt_payments[i].Amount);
                    // BigDecimal ZERO = BigDecimal.ZERO;
                    fact.createLine(m_debt_payments[i],
                            getAccountBPartner(C_BPartner_ID, as, true, m_debt_payments[i].dpStatus, conn),
                            this.C_Currency_ID, "",
                            getConvertedAmt(((amount.negate())).toPlainString(),
                                    m_debt_payments[i].C_Currency_ID_From, this.C_Currency_ID, DateAcct, "",
                                    AD_Client_ID, AD_Org_ID, conn),
                            Fact_Acct_Group_ID, nextSeqNo(SeqNo), DocumentType, conn);
                }
            else
                for (int i = 0; m_payments != null && i < m_payments.length; i++) {
                    BigDecimal amount = new BigDecimal(m_payments[i].Amount);
                    BigDecimal prepaidAmount = new BigDecimal(m_payments[i].PrepaidAmount);
                    fact.createLine(m_payments[i], getAccountBPartner(C_BPartner_ID, as, true, false, conn),
                            this.C_Currency_ID, "", amount.negate().toString(), Fact_Acct_Group_ID,
                            nextSeqNo(SeqNo), DocumentType, conn);
                    // Pre-payment: Probably not needed as at this point we can not generate pre-payments
                    // against ARC. Amount is negated
                    if (m_payments[i].C_Currency_ID_From.equals(as.m_C_Currency_ID)
                            && prepaidAmount.compareTo(ZERO) != 0) {
                        fact.createLine(m_payments[i], getAccountBPartner(C_BPartner_ID, as, true, true, conn),
                                this.C_Currency_ID, "", prepaidAmount.negate().toString(), Fact_Acct_Group_ID,
                                nextSeqNo(SeqNo), DocumentType, conn);
                    } else if (!m_payments[i].C_Currency_ID_From.equals(as.m_C_Currency_ID)) {
                        try {
                            DocInvoiceData[] prepayments = DocInvoiceData.selectPrepayments(connectionProvider,
                                    m_payments[i].Line_ID);
                            for (int j = 0; j < prepayments.length; j++) {
                                BigDecimal prePaymentAmt = convertAmount(
                                        new BigDecimal(prepayments[j].prepaidamt).negate(), true, DateAcct,
                                        TABLEID_Payment, prepayments[j].finPaymentId,
                                        m_payments[i].C_Currency_ID_From, as.m_C_Currency_ID, m_payments[i], as,
                                        fact, Fact_Acct_Group_ID, nextSeqNo(SeqNo), conn);
                                fact.createLine(m_payments[i],
                                        getAccountBPartner(C_BPartner_ID, as, true, true, conn),
                                        m_payments[i].C_Currency_ID_From, "", prePaymentAmt.toString(),
                                        Fact_Acct_Group_ID, nextSeqNo(SeqNo), DocumentType, conn);
                            }
                        } catch (ServletException e) {
                            log4jDocInvoice.warn(e);
                        }
                    }
                }
            if ((m_payments == null || m_payments.length == 0)
                    && (m_debt_payments == null || m_debt_payments.length == 0)) {
                fact.createLine(null, getAccountBPartner(C_BPartner_ID, as, true, false, conn), this.C_Currency_ID,
                        "", Amounts[AMTTYPE_Gross], Fact_Acct_Group_ID, nextSeqNo(SeqNo), DocumentType, conn);
            }
            // Charge DR
            fact.createLine(null, getAccount(AcctServer.ACCTTYPE_Charge, as, conn), this.C_Currency_ID,
                    getAmount(AcctServer.AMTTYPE_Charge), "", Fact_Acct_Group_ID, nextSeqNo(SeqNo), DocumentType,
                    conn);
            // TaxDue DR
            for (int i = 0; m_taxes != null && i < m_taxes.length; i++) {
                // New docLine created to assign C_Tax_ID value to the entry
                DocLine docLine = new DocLine(DocumentType, Record_ID, "");
                docLine.m_C_Tax_ID = m_taxes[i].m_C_Tax_ID;

                BigDecimal percentageFinalAccount = CashVATUtil._100;
                final BigDecimal taxesAmountTotal = new BigDecimal(
                        StringUtils.isBlank(m_taxes[i].getAmount()) ? "0" : m_taxes[i].getAmount());
                BigDecimal taxToTransAccount = BigDecimal.ZERO;
                if (isCashVAT && m_taxes[i].m_isCashVAT) {
                    percentageFinalAccount = CashVATUtil
                            .calculatePrepaidPercentageForCashVATTax(m_taxes[i].m_C_Tax_ID, Record_ID);
                    taxToTransAccount = CashVATUtil.calculatePercentageAmount(
                            CashVATUtil._100.subtract(percentageFinalAccount), taxesAmountTotal, C_Currency_ID);
                    fact.createLine(docLine, m_taxes[i].getAccount(DocTax.ACCTTYPE_TaxDue_Trans, as, conn),
                            this.C_Currency_ID, taxToTransAccount.toString(), "", Fact_Acct_Group_ID,
                            nextSeqNo(SeqNo), DocumentType, conn);
                }
                final BigDecimal taxToFinalAccount = taxesAmountTotal.subtract(taxToTransAccount);
                fact.createLine(docLine, m_taxes[i].getAccount(DocTax.ACCTTYPE_TaxDue, as, conn),
                        this.C_Currency_ID, taxToFinalAccount.toString(), "", Fact_Acct_Group_ID, nextSeqNo(SeqNo),
                        DocumentType, conn);
            }
            // Revenue CR
            for (int i = 0; p_lines != null && i < p_lines.length; i++) {
                String amount = p_lines[i].getAmount();
                String amountCoverted = "";
                ConversionRateDoc conversionRateCurrentDoc = getConversionRateDoc(TABLEID_Invoice, Record_ID,
                        C_Currency_ID, as.m_C_Currency_ID);
                if (conversionRateCurrentDoc != null) {
                    amountCoverted = applyRate(new BigDecimal(p_lines[i].getAmount()), conversionRateCurrentDoc,
                            true).setScale(2, BigDecimal.ROUND_HALF_UP).toString();
                } else {
                    amountCoverted = getConvertedAmt(p_lines[i].getAmount(), C_Currency_ID, as.m_C_Currency_ID,
                            DateAcct, "", AD_Client_ID, AD_Org_ID, conn);
                }
                Account account = ((DocLine_Invoice) p_lines[i]).getAccount(ProductInfo.ACCTTYPE_P_Revenue, as,
                        conn);
                if (((DocLine_Invoice) p_lines[i]).isDeferred()) {
                    amount = createAccDefRevenueFact(fact, (DocLine_Invoice) p_lines[i], account,
                            ((DocLine_Invoice) p_lines[i]).getAccount(ProductInfo.ACCTTYPE_P_DefRevenue, as, conn),
                            amountCoverted, as.m_C_Currency_ID, conn);
                    fact.createLine(p_lines[i], account, as.m_C_Currency_ID, amount, "", Fact_Acct_Group_ID,
                            nextSeqNo(SeqNo), DocumentType, conn);
                } else {
                    fact.createLine(p_lines[i], account, this.C_Currency_ID, amount, "", Fact_Acct_Group_ID,
                            nextSeqNo(SeqNo), DocumentType, conn);
                }
                // If revenue has been deferred
                if (((DocLine_Invoice) p_lines[i]).isDeferred() && !amount.equals(amountCoverted)) {
                    amount = new BigDecimal(amountCoverted).subtract(new BigDecimal(amount)).toString();
                    fact.createLine(p_lines[i],
                            ((DocLine_Invoice) p_lines[i]).getAccount(ProductInfo.ACCTTYPE_P_DefRevenue, as, conn),
                            as.m_C_Currency_ID, amount, "", Fact_Acct_Group_ID, nextSeqNo(SeqNo), DocumentType,
                            conn);
                }
            }
            // Set Locations
            FactLine[] fLines = fact.getLines();
            for (int i = 0; fLines != null && i < fLines.length; i++) {
                if (fLines[i] != null) {
                    fLines[i].setLocationFromOrg(fLines[i].getAD_Org_ID(conn), true, conn); // from Loc
                    fLines[i].setLocationFromBPartner(C_BPartner_Location_ID, false, conn); // to Loc
                }
            }
        }
        // API
        else if (this.DocumentType.equals(AcctServer.DOCTYPE_APInvoice)) {
            log4jDocInvoice.debug("Point 3");
            // Liability CR
            if (m_payments == null || m_payments.length == 0)
                for (int i = 0; m_debt_payments != null && i < m_debt_payments.length; i++) {
                    if (m_debt_payments[i].isReceipt.equals("Y"))
                        fact.createLine(m_debt_payments[i],
                                getAccountBPartner(C_BPartner_ID, as, true, m_debt_payments[i].dpStatus, conn),
                                this.C_Currency_ID,
                                getConvertedAmt(m_debt_payments[i].Amount, m_debt_payments[i].C_Currency_ID_From,
                                        this.C_Currency_ID, DateAcct, "", AD_Client_ID, AD_Org_ID, conn),
                                "", Fact_Acct_Group_ID, nextSeqNo(SeqNo), DocumentType, conn);
                    else
                        fact.createLine(m_debt_payments[i],
                                getAccountBPartner(C_BPartner_ID, as, false, m_debt_payments[i].dpStatus, conn),
                                this.C_Currency_ID, "",
                                getConvertedAmt(m_debt_payments[i].Amount, m_debt_payments[i].C_Currency_ID_From,
                                        this.C_Currency_ID, DateAcct, "", AD_Client_ID, AD_Org_ID, conn),
                                Fact_Acct_Group_ID, nextSeqNo(SeqNo), DocumentType, conn);
                }
            else
                for (int i = 0; m_payments != null && i < m_payments.length; i++) {
                    fact.createLine(m_payments[i], getAccountBPartner(C_BPartner_ID, as, false, false, conn),
                            this.C_Currency_ID, "", m_payments[i].Amount, Fact_Acct_Group_ID, nextSeqNo(SeqNo),
                            DocumentType, conn);
                    if (m_payments[i].C_Currency_ID_From.equals(as.m_C_Currency_ID)
                            && new BigDecimal(m_payments[i].PrepaidAmount).compareTo(ZERO) != 0) {
                        fact.createLine(m_payments[i], getAccountBPartner(C_BPartner_ID, as, false, true, conn),
                                this.C_Currency_ID, "", m_payments[i].PrepaidAmount, Fact_Acct_Group_ID,
                                nextSeqNo(SeqNo), DocumentType, conn);
                    } else if (!m_payments[i].C_Currency_ID_From.equals(as.m_C_Currency_ID)) {
                        try {
                            DocInvoiceData[] prepayments = DocInvoiceData.selectPrepayments(connectionProvider,
                                    m_payments[i].Line_ID);
                            for (int j = 0; j < prepayments.length; j++) {
                                BigDecimal prePaymentAmt = convertAmount(new BigDecimal(prepayments[j].prepaidamt),
                                        false, DateAcct, TABLEID_Payment, prepayments[j].finPaymentId,
                                        m_payments[i].C_Currency_ID_From, as.m_C_Currency_ID, m_payments[i], as,
                                        fact, Fact_Acct_Group_ID, nextSeqNo(SeqNo), conn);
                                fact.createLine(m_payments[i],
                                        getAccountBPartner(C_BPartner_ID, as, false, true, conn),
                                        m_payments[i].C_Currency_ID_From, "", prePaymentAmt.toString(),
                                        Fact_Acct_Group_ID, nextSeqNo(SeqNo), DocumentType, conn);
                            }
                        } catch (ServletException e) {
                            log4jDocInvoice.warn(e);
                        }
                    }
                }
            if ((m_payments == null || m_payments.length == 0)
                    && (m_debt_payments == null || m_debt_payments.length == 0)) {
                BigDecimal grossamt = new BigDecimal(Amounts[AMTTYPE_Gross]);
                BigDecimal prepayment = new BigDecimal(prepaymentamt);
                BigDecimal difference = grossamt.abs().subtract(prepayment.abs());
                if (!prepaymentamt.equals("0")) {
                    if (grossamt.abs().compareTo(prepayment.abs()) > 0) {
                        if (IsReturn.equals("Y")) {
                            fact.createLine(null, getAccountBPartner(C_BPartner_ID, as, false, true, conn),
                                    this.C_Currency_ID, prepaymentamt, "", Fact_Acct_Group_ID, nextSeqNo(SeqNo),
                                    DocumentType, conn);
                            fact.createLine(null, getAccountBPartner(C_BPartner_ID, as, false, false, conn),
                                    this.C_Currency_ID, difference.toString(), "", Fact_Acct_Group_ID,
                                    nextSeqNo(SeqNo), DocumentType, conn);
                        } else {
                            fact.createLine(null, getAccountBPartner(C_BPartner_ID, as, false, true, conn),
                                    this.C_Currency_ID, "", prepaymentamt, Fact_Acct_Group_ID, nextSeqNo(SeqNo),
                                    DocumentType, conn);
                            fact.createLine(null, getAccountBPartner(C_BPartner_ID, as, false, false, conn),
                                    this.C_Currency_ID, "", difference.toString(), Fact_Acct_Group_ID,
                                    nextSeqNo(SeqNo), DocumentType, conn);
                        }
                    } else {
                        if (IsReturn.equals("Y")) {
                            fact.createLine(null, getAccountBPartner(C_BPartner_ID, as, false, true, conn),
                                    this.C_Currency_ID, prepaymentamt, "", Fact_Acct_Group_ID, nextSeqNo(SeqNo),
                                    DocumentType, conn);
                        } else {
                            fact.createLine(null, getAccountBPartner(C_BPartner_ID, as, false, true, conn),
                                    this.C_Currency_ID, "", prepaymentamt, Fact_Acct_Group_ID, nextSeqNo(SeqNo),
                                    DocumentType, conn);
                        }
                    }
                } else {
                    fact.createLine(null, getAccountBPartner(C_BPartner_ID, as, false, false, conn),
                            this.C_Currency_ID, "", Amounts[AMTTYPE_Gross], Fact_Acct_Group_ID, nextSeqNo(SeqNo),
                            DocumentType, conn);
                }

            }
            // Charge DR
            fact.createLine(null, getAccount(AcctServer.ACCTTYPE_Charge, as, conn), this.C_Currency_ID,
                    getAmount(AcctServer.AMTTYPE_Charge), "", Fact_Acct_Group_ID, nextSeqNo(SeqNo), DocumentType,
                    conn);
            BigDecimal grossamt = new BigDecimal(Amounts[AMTTYPE_Gross]);
            BigDecimal prepayment = new BigDecimal(prepaymentamt);
            // TaxCredit DR
            for (int i = 0; m_taxes != null && i < m_taxes.length; i++) {
                // New docLine created to assign C_Tax_ID value to the entry
                DocLine docLine = new DocLine(DocumentType, Record_ID, "");
                docLine.m_C_Tax_ID = m_taxes[i].m_C_Tax_ID;
                OBContext.setAdminMode(true);
                int precission = 0;
                try {
                    Currency currency = OBDal.getInstance().get(Currency.class, C_Currency_ID);
                    precission = currency.getStandardPrecision().intValue();
                } finally {
                    OBContext.restorePreviousMode();
                }
                if (!m_taxes[i].m_isTaxUndeductable) {
                    BigDecimal percentageFinalAccount = CashVATUtil._100;
                    final BigDecimal taxesAmountTotal = new BigDecimal(
                            StringUtils.isBlank(m_taxes[i].getAmount()) ? "0" : m_taxes[i].getAmount());
                    BigDecimal taxToTransAccount = BigDecimal.ZERO;
                    if (IsReversal.equals("Y")) {
                        if (isCashVAT && m_taxes[i].m_isCashVAT) {
                            if ((m_payments == null || m_payments.length == 0)
                                    && (m_debt_payments == null || m_debt_payments.length == 0)
                                    && (!prepaymentamt.equals("0"))) {
                                percentageFinalAccount = ((prepayment.multiply(new BigDecimal(100)))
                                        .divide(grossamt.abs(), precission, RoundingMode.HALF_UP));
                                taxToTransAccount = CashVATUtil.calculatePercentageAmount(
                                        CashVATUtil._100.subtract(percentageFinalAccount), taxesAmountTotal,
                                        C_Currency_ID);
                            } else {
                                percentageFinalAccount = CashVATUtil
                                        .calculatePrepaidPercentageForCashVATTax(m_taxes[i].m_C_Tax_ID, Record_ID);
                                taxToTransAccount = CashVATUtil.calculatePercentageAmount(
                                        CashVATUtil._100.subtract(percentageFinalAccount), taxesAmountTotal,
                                        C_Currency_ID);
                            }
                            fact.createLine(docLine,
                                    m_taxes[i].getAccount(DocTax.ACCTTYPE_TaxCredit_Trans, as, conn),
                                    this.C_Currency_ID, "", taxToTransAccount.toString(), Fact_Acct_Group_ID,
                                    nextSeqNo(SeqNo), DocumentType, conn);
                        }
                        final BigDecimal taxToFinalAccount = taxesAmountTotal.subtract(taxToTransAccount);
                        fact.createLine(docLine, m_taxes[i].getAccount(DocTax.ACCTTYPE_TaxCredit, as, conn),
                                this.C_Currency_ID, "", taxToFinalAccount.toString(), Fact_Acct_Group_ID,
                                nextSeqNo(SeqNo), DocumentType, conn);
                    } else {
                        if (isCashVAT && m_taxes[i].m_isCashVAT) {
                            if ((m_payments == null || m_payments.length == 0)
                                    && (m_debt_payments == null || m_debt_payments.length == 0)
                                    && (!prepaymentamt.equals("0"))) {
                                percentageFinalAccount = ((prepayment.multiply(new BigDecimal(100)))
                                        .divide(grossamt.abs(), precission, RoundingMode.HALF_UP));
                                taxToTransAccount = CashVATUtil.calculatePercentageAmount(
                                        CashVATUtil._100.subtract(percentageFinalAccount), taxesAmountTotal,
                                        C_Currency_ID);
                            } else {
                                percentageFinalAccount = CashVATUtil
                                        .calculatePrepaidPercentageForCashVATTax(m_taxes[i].m_C_Tax_ID, Record_ID);
                                taxToTransAccount = CashVATUtil.calculatePercentageAmount(
                                        CashVATUtil._100.subtract(percentageFinalAccount), taxesAmountTotal,
                                        C_Currency_ID);
                            }
                            fact.createLine(docLine,
                                    m_taxes[i].getAccount(DocTax.ACCTTYPE_TaxCredit_Trans, as, conn),
                                    this.C_Currency_ID, taxToTransAccount.toString(), "", Fact_Acct_Group_ID,
                                    nextSeqNo(SeqNo), DocumentType, conn);
                        }
                        final BigDecimal taxToFinalAccount = taxesAmountTotal.subtract(taxToTransAccount);
                        fact.createLine(docLine, m_taxes[i].getAccount(DocTax.ACCTTYPE_TaxCredit, as, conn),
                                this.C_Currency_ID, taxToFinalAccount.toString(), "", Fact_Acct_Group_ID,
                                nextSeqNo(SeqNo), DocumentType, conn);
                    }

                } else {
                    DocLineInvoiceData[] data = null;
                    try {
                        data = DocLineInvoiceData.selectUndeductable(connectionProvider, Record_ID);
                    } catch (ServletException e) {
                        log4jDocInvoice.warn(e);
                    }

                    BigDecimal cumulativeTaxLineAmount = new BigDecimal(0);
                    BigDecimal taxAmount = new BigDecimal(0);
                    for (int j = 0; data != null && j < data.length; j++) {
                        DocLine docLine1 = new DocLine(DocumentType, Record_ID, "");
                        docLine1.m_C_Tax_ID = data[j].cTaxId;
                        docLine1.m_C_BPartner_ID = data[j].cBpartnerId;
                        docLine1.m_M_Product_ID = data[j].mProductId;
                        docLine1.m_C_Costcenter_ID = data[j].cCostcenterId;
                        docLine1.m_C_Project_ID = data[j].cProjectId;
                        docLine1.m_User1_ID = data[j].user1id;
                        docLine1.m_User2_ID = data[j].user2id;
                        docLine1.m_C_Activity_ID = data[j].cActivityId;
                        docLine1.m_C_Campaign_ID = data[j].cCampaignId;
                        docLine1.m_A_Asset_ID = data[j].aAssetId;
                        String strtaxAmount = null;

                        try {

                            DocInvoiceData[] dataEx = DocInvoiceData.selectProductAcct(conn,
                                    as.getC_AcctSchema_ID(), m_taxes[i].m_C_Tax_ID, Record_ID);
                            if (dataEx.length == 0) {
                                dataEx = DocInvoiceData.selectGLItemAcctForTaxLine(conn, as.getC_AcctSchema_ID(),
                                        m_taxes[i].m_C_Tax_ID, Record_ID);
                            }
                            strtaxAmount = m_taxes[i].getAmount();
                            taxAmount = new BigDecimal(strtaxAmount.equals("") ? "0.00" : strtaxAmount);
                            if (j == data.length - 1) {
                                data[j].taxamt = taxAmount.subtract(cumulativeTaxLineAmount).toPlainString();
                            }
                            try {

                                if (this.DocumentType.equals(AcctServer.DOCTYPE_APInvoice)) {
                                    if (IsReversal.equals("Y")) {
                                        fact.createLine(docLine1, Account.getAccount(conn, dataEx[0].pExpenseAcct),
                                                this.C_Currency_ID, "", data[j].taxamt, Fact_Acct_Group_ID,
                                                nextSeqNo(SeqNo), DocumentType, conn);

                                    } else {
                                        fact.createLine(docLine1, Account.getAccount(conn, dataEx[0].pExpenseAcct),
                                                this.C_Currency_ID, data[j].taxamt, "", Fact_Acct_Group_ID,
                                                nextSeqNo(SeqNo), DocumentType, conn);
                                    }
                                } else if (this.DocumentType.equals(AcctServer.DOCTYPE_APCredit)) {
                                    fact.createLine(docLine1, Account.getAccount(conn, dataEx[0].pExpenseAcct),
                                            this.C_Currency_ID, "", data[j].taxamt, Fact_Acct_Group_ID,
                                            nextSeqNo(SeqNo), DocumentType, conn);
                                }
                                cumulativeTaxLineAmount = cumulativeTaxLineAmount
                                        .add(new BigDecimal(data[j].taxamt));
                            } catch (ServletException e) {
                                log4jDocInvoice.error("Exception in createLineForTaxUndeductable method: " + e);
                            }
                        } catch (ServletException e) {
                            log4jDocInvoice.warn(e);
                        }
                    }
                }
            }
            // Expense DR
            for (int i = 0; p_lines != null && i < p_lines.length; i++) {
                String amount = p_lines[i].getAmount();
                String amountConverted = "";
                ConversionRateDoc conversionRateCurrentDoc = getConversionRateDoc(TABLEID_Invoice, Record_ID,
                        C_Currency_ID, as.m_C_Currency_ID);
                if (conversionRateCurrentDoc != null) {
                    amountConverted = applyRate(new BigDecimal(p_lines[i].getAmount()), conversionRateCurrentDoc,
                            true).setScale(2, BigDecimal.ROUND_HALF_UP).toString();
                } else {
                    amountConverted = getConvertedAmt(p_lines[i].getAmount(), C_Currency_ID, as.m_C_Currency_ID,
                            DateAcct, "", AD_Client_ID, AD_Org_ID, conn);
                }
                if (((DocLine_Invoice) p_lines[i]).isDeferred()) {
                    amount = createAccDefExpenseFact(fact, (DocLine_Invoice) p_lines[i],
                            ((DocLine_Invoice) p_lines[i]).getAccount(ProductInfo.ACCTTYPE_P_Expense, as, conn),
                            ((DocLine_Invoice) p_lines[i]).getAccount(ProductInfo.ACCTTYPE_P_DefExpense, as, conn),
                            amountConverted, as.m_C_Currency_ID, conn);
                    if (IsReversal.equals("Y")) {
                        fact.createLine(p_lines[i],
                                ((DocLine_Invoice) p_lines[i]).getAccount(ProductInfo.ACCTTYPE_P_Expense, as, conn),
                                as.m_C_Currency_ID, "", amount, Fact_Acct_Group_ID, nextSeqNo(SeqNo), DocumentType,
                                conn);
                    } else {
                        fact.createLine(p_lines[i],
                                ((DocLine_Invoice) p_lines[i]).getAccount(ProductInfo.ACCTTYPE_P_Expense, as, conn),
                                as.m_C_Currency_ID, amount, "", Fact_Acct_Group_ID, nextSeqNo(SeqNo), DocumentType,
                                conn);
                    }
                } else {
                    if (IsReversal.equals("Y")) {
                        fact.createLine(p_lines[i],
                                ((DocLine_Invoice) p_lines[i]).getAccount(ProductInfo.ACCTTYPE_P_Expense, as, conn),
                                this.C_Currency_ID, "", amount, Fact_Acct_Group_ID, nextSeqNo(SeqNo), DocumentType,
                                conn);
                    } else {
                        fact.createLine(p_lines[i],
                                ((DocLine_Invoice) p_lines[i]).getAccount(ProductInfo.ACCTTYPE_P_Expense, as, conn),
                                this.C_Currency_ID, amount, "", Fact_Acct_Group_ID, nextSeqNo(SeqNo), DocumentType,
                                conn);
                    }
                }
                // If expense has been deferred
                if (((DocLine_Invoice) p_lines[i]).isDeferred() && !amount.equals(amountConverted)) {
                    amount = new BigDecimal(amountConverted).subtract(new BigDecimal(amount)).toString();
                    if (IsReversal.equals("Y")) {
                        fact.createLine(p_lines[i],
                                ((DocLine_Invoice) p_lines[i]).getAccount(ProductInfo.ACCTTYPE_P_DefExpense, as,
                                        conn),
                                as.m_C_Currency_ID, "", amount, Fact_Acct_Group_ID, nextSeqNo(SeqNo), DocumentType,
                                conn);
                    } else {
                        fact.createLine(p_lines[i],
                                ((DocLine_Invoice) p_lines[i]).getAccount(ProductInfo.ACCTTYPE_P_DefExpense, as,
                                        conn),
                                as.m_C_Currency_ID, amount, "", Fact_Acct_Group_ID, nextSeqNo(SeqNo), DocumentType,
                                conn);
                    }
                }
            }
            // Set Locations
            FactLine[] fLines = fact.getLines();
            for (int i = 0; fLines != null && i < fLines.length; i++) {
                if (fLines[i] != null) {
                    fLines[i].setLocationFromBPartner(C_BPartner_Location_ID, true, conn); // from Loc
                    fLines[i].setLocationFromOrg(fLines[i].getAD_Org_ID(conn), false, conn); // to Loc
                }
            }
            updateProductInfo(as.getC_AcctSchema_ID(), conn, con); // only API
        }
        // APC
        else if (this.DocumentType.equals(AcctServer.DOCTYPE_APCredit)) {
            log4jDocInvoice.debug("Point 4");
            // Liability DR
            if (m_payments == null || m_payments.length == 0)
                for (int i = 0; m_debt_payments != null && i < m_debt_payments.length; i++) {
                    BigDecimal amount = new BigDecimal(m_debt_payments[i].Amount);
                    // BigDecimal ZERO = BigDecimal.ZERO;
                    fact.createLine(m_debt_payments[i],
                            getAccountBPartner(C_BPartner_ID, as, false, m_debt_payments[i].dpStatus, conn),
                            this.C_Currency_ID,
                            getConvertedAmt(((amount.negate())).toPlainString(),
                                    m_debt_payments[i].C_Currency_ID_From, this.C_Currency_ID, DateAcct, "",
                                    AD_Client_ID, AD_Org_ID, conn),
                            "", Fact_Acct_Group_ID, nextSeqNo(SeqNo), DocumentType, conn);
                }
            else
                for (int i = 0; m_payments != null && i < m_payments.length; i++) {
                    BigDecimal amount = new BigDecimal(m_payments[i].Amount);
                    BigDecimal prepaidAmount = new BigDecimal(m_payments[i].PrepaidAmount);
                    fact.createLine(m_payments[i], getAccountBPartner(C_BPartner_ID, as, false, false, conn),
                            this.C_Currency_ID, amount.negate().toString(), "", Fact_Acct_Group_ID,
                            nextSeqNo(SeqNo), DocumentType, conn);
                    // Pre-payment: Probably not needed as at this point we can not generate pre-payments
                    // against APC. Amount is negated
                    if (m_payments[i].C_Currency_ID_From.equals(as.m_C_Currency_ID)
                            && prepaidAmount.compareTo(ZERO) != 0) {
                        fact.createLine(m_payments[i], getAccountBPartner(C_BPartner_ID, as, false, true, conn),
                                this.C_Currency_ID, prepaidAmount.negate().toString(), "", Fact_Acct_Group_ID,
                                nextSeqNo(SeqNo), DocumentType, conn);
                    } else if (!m_payments[i].C_Currency_ID_From.equals(as.m_C_Currency_ID)) {
                        try {
                            DocInvoiceData[] prepayments = DocInvoiceData.selectPrepayments(connectionProvider,
                                    m_payments[i].Line_ID);
                            for (int j = 0; j < prepayments.length; j++) {
                                BigDecimal prePaymentAmt = convertAmount(
                                        new BigDecimal(prepayments[j].prepaidamt).negate(), false, DateAcct,
                                        TABLEID_Payment, prepayments[j].finPaymentId,
                                        m_payments[i].C_Currency_ID_From, as.m_C_Currency_ID, m_payments[i], as,
                                        fact, Fact_Acct_Group_ID, nextSeqNo(SeqNo), conn);
                                fact.createLine(m_payments[i],
                                        getAccountBPartner(C_BPartner_ID, as, false, true, conn),
                                        m_payments[i].C_Currency_ID_From, prePaymentAmt.toString(), "",
                                        Fact_Acct_Group_ID, nextSeqNo(SeqNo), DocumentType, conn);
                            }
                        } catch (ServletException e) {
                            log4jDocInvoice.warn(e);
                        }
                    }
                }
            if ((m_payments == null || m_payments.length == 0)
                    && (m_debt_payments == null || m_debt_payments.length == 0)) {
                fact.createLine(null, getAccountBPartner(C_BPartner_ID, as, false, false, conn), this.C_Currency_ID,
                        Amounts[AMTTYPE_Gross], "", Fact_Acct_Group_ID, nextSeqNo(SeqNo), DocumentType, conn);
            }
            // Charge CR
            fact.createLine(null, getAccount(AcctServer.ACCTTYPE_Charge, as, conn), this.C_Currency_ID, "",
                    getAmount(AcctServer.AMTTYPE_Charge), Fact_Acct_Group_ID, nextSeqNo(SeqNo), DocumentType, conn);
            // TaxCredit CR
            for (int i = 0; m_taxes != null && i < m_taxes.length; i++) {
                // New docLine created to assign C_Tax_ID value to the entry
                DocLine docLine = new DocLine(DocumentType, Record_ID, "");
                docLine.m_C_Tax_ID = m_taxes[i].m_C_Tax_ID;
                if (m_taxes[i].m_isTaxUndeductable) {
                    computeTaxUndeductableLine(conn, as, fact, docLine, Fact_Acct_Group_ID, m_taxes[i].m_C_Tax_ID,
                            m_taxes[i].getAmount());

                } else {
                    BigDecimal percentageFinalAccount = CashVATUtil._100;
                    final BigDecimal taxesAmountTotal = new BigDecimal(
                            StringUtils.isBlank(m_taxes[i].getAmount()) ? "0" : m_taxes[i].getAmount());
                    BigDecimal taxToTransAccount = BigDecimal.ZERO;
                    if (isCashVAT && m_taxes[i].m_isCashVAT) {
                        percentageFinalAccount = CashVATUtil
                                .calculatePrepaidPercentageForCashVATTax(m_taxes[i].m_C_Tax_ID, Record_ID);
                        taxToTransAccount = CashVATUtil.calculatePercentageAmount(
                                CashVATUtil._100.subtract(percentageFinalAccount), taxesAmountTotal, C_Currency_ID);
                        fact.createLine(docLine, m_taxes[i].getAccount(DocTax.ACCTTYPE_TaxCredit_Trans, as, conn),
                                this.C_Currency_ID, "", taxToTransAccount.toString(), Fact_Acct_Group_ID,
                                nextSeqNo(SeqNo), DocumentType, conn);
                    }
                    final BigDecimal taxToFinalAccount = taxesAmountTotal.subtract(taxToTransAccount);
                    fact.createLine(docLine, m_taxes[i].getAccount(DocTax.ACCTTYPE_TaxCredit, as, conn),
                            this.C_Currency_ID, "", taxToFinalAccount.toString(), Fact_Acct_Group_ID,
                            nextSeqNo(SeqNo), DocumentType, conn);
                }
            }
            // Expense CR
            for (int i = 0; p_lines != null && i < p_lines.length; i++) {
                String amount = p_lines[i].getAmount();
                String amountConverted = "";
                ConversionRateDoc conversionRateCurrentDoc = getConversionRateDoc(TABLEID_Invoice, Record_ID,
                        C_Currency_ID, as.m_C_Currency_ID);
                if (conversionRateCurrentDoc != null) {
                    amountConverted = applyRate(new BigDecimal(p_lines[i].getAmount()), conversionRateCurrentDoc,
                            true).setScale(2, BigDecimal.ROUND_HALF_UP).toString();
                } else {
                    amountConverted = getConvertedAmt(p_lines[i].getAmount(), C_Currency_ID, as.m_C_Currency_ID,
                            DateAcct, "", AD_Client_ID, AD_Org_ID, conn);
                }
                Account account = ((DocLine_Invoice) p_lines[i]).getAccount(ProductInfo.ACCTTYPE_P_Expense, as,
                        conn);
                if (((DocLine_Invoice) p_lines[i]).isDeferred()) {
                    amount = createAccDefExpenseFact(fact, (DocLine_Invoice) p_lines[i], account,
                            ((DocLine_Invoice) p_lines[i]).getAccount(ProductInfo.ACCTTYPE_P_DefExpense, as, conn),
                            amountConverted, as.m_C_Currency_ID, conn);
                    fact.createLine(p_lines[i], account, as.m_C_Currency_ID, "", amount, Fact_Acct_Group_ID,
                            nextSeqNo(SeqNo), DocumentType, conn);
                } else {
                    fact.createLine(p_lines[i], account, this.C_Currency_ID, "", amount, Fact_Acct_Group_ID,
                            nextSeqNo(SeqNo), DocumentType, conn);
                }
                // If expense has been deferred
                if (((DocLine_Invoice) p_lines[i]).isDeferred() && !amount.equals(amountConverted)) {
                    amount = new BigDecimal(amountConverted).subtract(new BigDecimal(amount)).toString();
                    fact.createLine(p_lines[i],
                            ((DocLine_Invoice) p_lines[i]).getAccount(ProductInfo.ACCTTYPE_P_DefExpense, as, conn),
                            as.m_C_Currency_ID, "", amount, Fact_Acct_Group_ID, nextSeqNo(SeqNo), DocumentType,
                            conn);
                }

            }
            // Set Locations
            FactLine[] fLines = fact.getLines();
            for (int i = 0; fLines != null && i < fLines.length; i++) {
                if (fLines[i] != null) {
                    fLines[i].setLocationFromBPartner(C_BPartner_Location_ID, true, conn); // from Loc
                    fLines[i].setLocationFromOrg(fLines[i].getAD_Org_ID(conn), false, conn); // to Loc
                }
            }
        } else {
            log4jDocInvoice.warn("Doc_Invoice - DocumentType unknown: " + this.DocumentType);
            fact = null;
        }
        SeqNo = "0";
        return fact;
    }// createFact

    String createAccDefRevenueFact(Fact fact, DocLine_Invoice line, Account prodRevAccount,
            Account prodDefRevAccount, String lineAmount, String strCurrencyId, ConnectionProvider conn) {
        BigDecimal amount = new BigDecimal(lineAmount);
        String Fact_Acct_Group_ID = SequenceIdData.getUUID();
        ArrayList<HashMap<String, String>> plan = new ArrayList<HashMap<String, String>>();
        Period startingPeriod = OBDal.getInstance().get(Period.class, line.getStartingPeriodId());
        plan = calculateAccDefPlan(startingPeriod, line.getPeriodNumber(), amount, strCurrencyId);
        for (HashMap<String, String> planLine : plan) {
            DocLine planDocLine = new DocLine(DocumentType, Record_ID, line.m_TrxLine_ID);
            planDocLine.copyInfo(line);
            planDocLine.m_DateAcct = planLine.get("date");
            if (IsReversal.equals("Y")) {
                // Revenue Account
                fact.createLine(planDocLine, prodRevAccount, strCurrencyId, planLine.get("amount"), "",
                        Fact_Acct_Group_ID, nextSeqNo(SeqNo), DocumentType, conn);
                // Deferred Revenue Account
                fact.createLine(planDocLine, prodDefRevAccount, strCurrencyId, "", planLine.get("amount"),
                        Fact_Acct_Group_ID, nextSeqNo(SeqNo), DocumentType, conn);
            } else {
                // Deferred Revenue Account
                fact.createLine(planDocLine, prodDefRevAccount, strCurrencyId, planLine.get("amount"), "",
                        Fact_Acct_Group_ID, nextSeqNo(SeqNo), DocumentType, conn);
                // Revenue Account
                fact.createLine(planDocLine, prodRevAccount, strCurrencyId, "", planLine.get("amount"),
                        Fact_Acct_Group_ID, nextSeqNo(SeqNo), DocumentType, conn);
            }
            amount = amount.subtract(new BigDecimal(planLine.get("amount")));
            Fact_Acct_Group_ID = SequenceIdData.getUUID();
        }

        return amount.toString();
    }

    private ArrayList<HashMap<String, String>> calculateAccDefPlan(Period startingPeriod, int periodNumber,
            BigDecimal amount, String strCurrencyId) {
        Period period = startingPeriod;
        Date date = period.getEndingDate();
        ArrayList<HashMap<String, String>> plan = new ArrayList<HashMap<String, String>>();
        int i = 1;
        BigDecimal total = BigDecimal.ZERO;
        int stdPrecision = 0;
        OBContext.setAdminMode(true);
        try {
            stdPrecision = OBDal.getInstance().get(Currency.class, this.C_Currency_ID).getStandardPrecision()
                    .intValue();
        } finally {
            OBContext.restorePreviousMode();
        }
        BigDecimal periodAmount = amount
                .divide(new BigDecimal(periodNumber), new MathContext(32, RoundingMode.HALF_UP))
                .setScale(stdPrecision, BigDecimal.ROUND_HALF_UP);

        while (i <= periodNumber) {
            if (!OBDateUtils.formatDate(date).equals(DateAcct)) {
                HashMap<String, String> hm = new HashMap<String, String>();
                hm.put("date", OBDateUtils.formatDate(date));
                hm.put("amount", i == periodNumber ? amount.subtract(total).toString() : periodAmount.toString());
                plan.add(hm);
            }
            try {
                AcctServerData[] data = AcctServerData.periodOpen(connectionProvider, AD_Client_ID, DocumentType,
                        AD_Org_ID, OBDateUtils.formatDate(period.getEndingDate()));
                if ("".equals(data[0].period)) {
                    setStatus(STATUS_PeriodClosed);
                    throw new OBException("@PeriodNotAvailable@");
                }
            } catch (ServletException e) {
                log4j.warn("DocInvoice - Error checking period open.", e);
                e.printStackTrace();
            }
            if (i < periodNumber) {
                period = AccDefUtility.getNextPeriod(period);
                date = period.getEndingDate();
            }
            total = total.add(periodAmount);
            i++;
        }
        return plan;
    }

    public String createAccDefExpenseFact(Fact fact, DocLine_Invoice line, Account prodExpAccount,
            Account prodDefExpAccount, String lineAmount, String strCurrencyId, ConnectionProvider conn) {
        BigDecimal amount = new BigDecimal(lineAmount);
        String Fact_Acct_Group_ID = SequenceIdData.getUUID();
        ArrayList<HashMap<String, String>> plan = new ArrayList<HashMap<String, String>>();
        Period startingPeriod = OBDal.getInstance().get(Period.class, line.getStartingPeriodId());
        plan = calculateAccDefPlan(startingPeriod, line.getPeriodNumber(), amount, strCurrencyId);
        for (HashMap<String, String> planLine : plan) {
            DocLine planDocLine = new DocLine(DocumentType, Record_ID, line.m_TrxLine_ID);
            planDocLine.copyInfo(line);
            planDocLine.m_DateAcct = planLine.get("date");
            if (IsReversal.equals("Y")) {
                // Expense Account
                fact.createLine(planDocLine, prodExpAccount, strCurrencyId, "", planLine.get("amount"),
                        Fact_Acct_Group_ID, nextSeqNo(SeqNo), DocumentType, conn);
                // Deferred Expense Account
                fact.createLine(planDocLine, prodDefExpAccount, strCurrencyId, planLine.get("amount"), "",
                        Fact_Acct_Group_ID, nextSeqNo(SeqNo), DocumentType, conn);
            } else {
                // Deferred Expense Account
                fact.createLine(planDocLine, prodDefExpAccount, strCurrencyId, "", planLine.get("amount"),
                        Fact_Acct_Group_ID, nextSeqNo(SeqNo), DocumentType, conn);
                // Expense Account
                fact.createLine(planDocLine, prodExpAccount, strCurrencyId, planLine.get("amount"), "",
                        Fact_Acct_Group_ID, nextSeqNo(SeqNo), DocumentType, conn);
            }
            amount = amount.subtract(new BigDecimal(planLine.get("amount")));
            Fact_Acct_Group_ID = SequenceIdData.getUUID();
        }
        return amount.toString();
    }

    /**
     * Update Product Info. - Costing (PriceLastInv) - PO (PriceLastInv)
     * 
     * @param C_AcctSchema_ID
     *          accounting schema
     */
    public void updateProductInfo(String C_AcctSchema_ID, ConnectionProvider conn, Connection con) {
        log4jDocInvoice.debug("updateProductInfo - C_Invoice_ID=" + this.Record_ID);

        /**
         * @todo Last.. would need to compare document/last updated date would need to maintain
         *       LastPriceUpdateDate on _PO and _Costing
         */

        // update Product PO info
        // should only be once, but here for every AcctSchema
        // ignores multiple lines with same product - just uses first
        int no = 0;
        try {
            no = DocInvoiceData.updateProductPO(con, conn, Record_ID);
            log4jDocInvoice.debug("M_Product_PO - Updated=" + no);

        } catch (ServletException e) {
            log4jDocInvoice.warn(e);
            if (e.getMessage().contains("@NoConversionRate@")) {
                setMessageResult(Utility.translateError(conn, RequestContext.get().getVariablesSecureApp(),
                        OBContext.getOBContext().getLanguage().getId(), e.getMessage()));
                throw new IllegalStateException();
            }
        }
    } // updateProductInfo

    /**
     * Get Source Currency Balance - subtracts line and tax amounts from total - no rounding
     * 
     * @return positive amount, if total invoice is bigger than lines
     */
    public BigDecimal getBalance() {
        // BigDecimal ZERO = new BigDecimal("0");
        BigDecimal retValue = ZERO;
        StringBuffer sb = new StringBuffer(" [");
        // Total
        retValue = retValue.add(new BigDecimal(getAmount(AcctServer.AMTTYPE_Gross)));
        sb.append(getAmount(AcctServer.AMTTYPE_Gross));
        // - Charge
        retValue = retValue.subtract(new BigDecimal(getAmount(AcctServer.AMTTYPE_Charge)));
        sb.append("-").append(getAmount(AcctServer.AMTTYPE_Charge));
        // - Tax
        for (int i = 0; i < m_taxes.length; i++) {
            retValue = retValue.subtract(new BigDecimal(m_taxes[i].getAmount()));
            sb.append("-").append(m_taxes[i].getAmount());
        }
        // - Lines
        for (int i = 0; p_lines != null && i < p_lines.length; i++) {
            retValue = retValue.subtract(new BigDecimal(p_lines[i].getAmount()));
            sb.append("-").append(p_lines[i].getAmount());
        }
        sb.append("]");
        //
        log4jDocInvoice.debug("Balance=" + retValue + sb.toString());
        return retValue;
    } // getBalance

    public String nextSeqNo(String oldSeqNo) {
        log4jDocInvoice.debug("DocInvoice - oldSeqNo = " + oldSeqNo);
        BigDecimal seqNo = new BigDecimal(oldSeqNo);
        SeqNo = (seqNo.add(new BigDecimal("10"))).toString();
        log4jDocInvoice.debug("DocInvoice - nextSeqNo = " + SeqNo);
        return SeqNo;
    }

    /**
     * Get the account for Accounting Schema
     * 
     * @param cBPartnerId
     *          business partner id
     * @param as
     *          accounting schema
     * @return Account
     */
    public final Account getAccountBPartner(String cBPartnerId, AcctSchema as, boolean isReceipt, String dpStatus,
            ConnectionProvider conn) {
        DocPaymentData[] data = null;
        try {
            if (log4j.isDebugEnabled())
                log4j.debug("DocInvoice - getAccountBPartner - DocumentType = " + DocumentType);
            if (isReceipt) {
                data = DocPaymentData.selectBPartnerCustomerAcct(conn, cBPartnerId, as.getC_AcctSchema_ID(),
                        dpStatus);
            } else {
                data = DocPaymentData.selectBPartnerVendorAcct(conn, cBPartnerId, as.getC_AcctSchema_ID(),
                        dpStatus);
            }
        } catch (ServletException e) {
            log4j.warn(e);
        }
        // Get Acct
        String Account_ID = "";
        if (data != null && data.length != 0) {
            Account_ID = data[0].accountId;
        } else
            return null;
        // No account
        if (Account_ID.equals("")) {
            log4j.warn("DocInvoice - getAccountBPartner - NO account BPartner=" + cBPartnerId + ", Record="
                    + Record_ID + ", status " + dpStatus);
            return null;
        }
        // Return Account
        Account acct = null;
        try {
            acct = Account.getAccount(conn, Account_ID);
        } catch (ServletException e) {
            log4j.warn(e);
        }
        return acct;
    } // getAccount

    /**
     * @return the log4jDocInvoice
     */
    public static Logger getLog4jDocInvoice() {
        return log4jDocInvoice;
    }

    /**
     * @param log4jDocInvoice
     *          the log4jDocInvoice to set
     */
    public static void setLog4jDocInvoice(Logger log4jDocInvoice) {
        DocInvoice.log4jDocInvoice = log4jDocInvoice;
    }

    /**
     * @return the m_taxes
     */
    public DocTax[] getM_taxes() {
        return m_taxes;
    }

    /**
     * @param m_taxes
     *          the m_taxes to set
     */
    public void setM_taxes(DocTax[] m_taxes) {
        this.m_taxes = m_taxes;
    }

    /**
     * @return the seqNo
     */
    public String getSeqNo() {
        return SeqNo;
    }

    /**
     * @param seqNo
     *          the seqNo to set
     */
    public void setSeqNo(String seqNo) {
        SeqNo = seqNo;
    }

    /**
     * @return the serialVersionUID
     */
    public static long getSerialVersionUID() {
        return serialVersionUID;
    }

    /**
     * Get Document Confirmation
     * 
     */
    public boolean getDocumentConfirmation(ConnectionProvider conn, String strRecordId) {
        DocInvoiceData[] data = null;
        FieldProvider dataFP[] = getObjectFieldProvider();
        if (ZERO.compareTo(new BigDecimal(dataFP[0].getField("GrandTotal"))) == 0) {
            Invoice invoice = OBDal.getInstance().get(Invoice.class, strRecordId);
            boolean zero = true;
            for (InvoiceLine invoiceline : invoice.getInvoiceLineList()) {
                if (ZERO.compareTo(invoiceline.getLineNetAmount()) != 0) {
                    zero = false;
                }
            }
            for (InvoiceTax invoiceTax : invoice.getInvoiceTaxList()) {
                if (ZERO.compareTo(invoiceTax.getTaxAmount()) != 0) {
                    zero = false;
                }
            }
            if (zero) {
                strMessage = "@TotalGrossIsZero@";
                setStatus(STATUS_DocumentDisabled);
                return false;
            }
        }
        try {
            data = DocInvoiceData.selectRegistro(conn, AD_Client_ID, strRecordId);
            AcctSchema[] m_acctSchemas = reloadLocalAcctSchemaArray(data[0].adOrgId);

            AcctSchema acct = null;
            for (int i = 0; i < m_acctSchemas.length; i++) {
                acct = m_acctSchemas[i];
                data = DocInvoiceData.selectFinInvCount(conn, strRecordId, acct.m_C_AcctSchema_ID);
                int countFinInv = Integer.parseInt(data[0].fininvcount);
                int countGLItemAcct = Integer.parseInt(data[0].finacctcount);
                // For any GL Item used in financial invoice lines debit/credit accounts must be defined
                if (countFinInv != 0 && (countFinInv != countGLItemAcct)) {
                    log4jDocInvoice.debug("DocInvoice - getDocumentConfirmation - GL Item used in financial "
                            + "invoice lines debit/credit accounts must be defined.");
                    setStatus(STATUS_InvalidAccount);
                    return false;
                }
            }
        } catch (Exception e) {
            log4jDocInvoice.error("Exception in getDocumentConfirmation method.", e);
        }

        return true;
    }

    private AcctSchema[] reloadLocalAcctSchemaArray(String adOrgId) throws ServletException {
        AcctSchema acct = null;
        ArrayList<Object> new_as = new ArrayList<Object>();
        // We reload again all the acct schemas of the client
        AcctSchema[] m_aslocal = AcctSchema.getAcctSchemaArray(connectionProvider, AD_Client_ID, adOrgId);
        // Filter the right acct schemas for the organization
        for (int i = 0; i < m_aslocal.length; i++) {
            acct = m_aslocal[i];
            if (AcctSchemaData.selectAcctSchemaTable(connectionProvider, acct.m_C_AcctSchema_ID, AD_Table_ID)) {
                new_as.add(new AcctSchema(connectionProvider, acct.m_C_AcctSchema_ID));
            }
        }
        AcctSchema[] retValue = new AcctSchema[new_as.size()];
        new_as.toArray(retValue);
        m_aslocal = retValue;
        return m_aslocal;
    }

    public String getServletInfo() {
        return "Servlet for the accounting";
    } // end of getServletInfo() method

    public void computeTaxUndeductableLine(ConnectionProvider conn, AcctSchema as, Fact fact, DocLine docLine,
            String Fact_Acct_Group_ID, String taxId, String strTaxAmount) {
        int invoiceLineTaxCount = 0;
        int totalInvoiceLineTax = getTaxLineCount(conn, taxId);
        BigDecimal cumulativeTaxLineAmount = new BigDecimal(0);
        BigDecimal taxAmount = new BigDecimal(strTaxAmount.equals("") ? "0.00" : strTaxAmount);
        DocInvoiceData[] data = null;
        try {

            // We can have some lines from product or some lines from general ledger
            data = DocInvoiceData.selectProductAcct(conn, as.getC_AcctSchema_ID(), taxId, Record_ID);
            cumulativeTaxLineAmount = createLineForTaxUndeductable(invoiceLineTaxCount, totalInvoiceLineTax,
                    cumulativeTaxLineAmount, taxAmount, data, conn, fact, docLine, Fact_Acct_Group_ID);
            invoiceLineTaxCount = data.length;
            // check whether gl item is selected instead of product in invoice line
            data = DocInvoiceData.selectGLItemAcctForTaxLine(conn, as.getC_AcctSchema_ID(), taxId, Record_ID);
            createLineForTaxUndeductable(invoiceLineTaxCount, totalInvoiceLineTax, cumulativeTaxLineAmount,
                    taxAmount, data, conn, fact, docLine, Fact_Acct_Group_ID);
        } catch (ServletException e) {
            log4jDocInvoice.error("Exception in computeTaxUndeductableLine method: " + e);
        }
    }

    private int getTaxLineCount(ConnectionProvider conn, String taxId) {
        DocInvoiceData[] data = null;
        try {
            data = DocInvoiceData.getTaxLineCount(conn, taxId, Record_ID);
        } catch (ServletException e) {
            log4jDocInvoice.error("Exception in getTaxLineCount method: " + e);
        }
        if (data.length > 0) {
            return Integer.parseInt(data[0].totallines);
        }
        return 0;
    }

    private BigDecimal createLineForTaxUndeductable(int invoiceLineTaxCount, int totalInvoiceLineTax,
            BigDecimal cumulativeTaxLineAmount, BigDecimal taxAmount, DocInvoiceData[] data,
            ConnectionProvider conn, Fact fact, DocLine docLine, String Fact_Acct_Group_ID) {
        for (int j = 0; j < data.length; j++) {
            invoiceLineTaxCount++;
            // We have to adjust the amount in last line of tax
            if (invoiceLineTaxCount == totalInvoiceLineTax) {
                data[j].taxamt = taxAmount.subtract(cumulativeTaxLineAmount).toPlainString();
            }
            try {
                // currently applicable for API and APC
                if (this.DocumentType.equals(AcctServer.DOCTYPE_APInvoice)) {
                    if (IsReversal.equals("Y")) {
                        fact.createLine(docLine, Account.getAccount(conn, data[j].pExpenseAcct), this.C_Currency_ID,
                                "", data[j].taxamt, Fact_Acct_Group_ID, nextSeqNo(SeqNo), DocumentType, conn);

                    } else {
                        fact.createLine(docLine, Account.getAccount(conn, data[j].pExpenseAcct), this.C_Currency_ID,
                                data[j].taxamt, "", Fact_Acct_Group_ID, nextSeqNo(SeqNo), DocumentType, conn);
                    }
                } else if (this.DocumentType.equals(AcctServer.DOCTYPE_APCredit)) {
                    fact.createLine(docLine, Account.getAccount(conn, data[j].pExpenseAcct), this.C_Currency_ID, "",
                            data[j].taxamt, Fact_Acct_Group_ID, nextSeqNo(SeqNo), DocumentType, conn);
                }
                cumulativeTaxLineAmount = cumulativeTaxLineAmount.add(new BigDecimal(data[j].taxamt));
            } catch (ServletException e) {
                log4jDocInvoice.error("Exception in createLineForTaxUndeductable method: " + e);
            }

        }

        return cumulativeTaxLineAmount;
    }
}