OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Rev 174 | Rev 180 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
 * 
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each file.
 */
 
 package org.openconcerto.erp.core.common.ui;

import org.openconcerto.erp.core.finance.accounting.element.ComptePCESQLElement;
import org.openconcerto.erp.core.finance.tax.model.TaxeCache;
import org.openconcerto.erp.preferences.GestionCommercialeGlobalPreferencePanel;
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.model.SQLBackgroundTableCache;
import org.openconcerto.sql.model.SQLBackgroundTableCacheItem;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.preferences.SQLPreferences;
import org.openconcerto.utils.DecimalUtils;
import org.openconcerto.utils.Tuple2;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class TotalCalculator {

    private static String FIELD_SERVICE = "SERVICE";
    private static String FIELD_POIDS = "T_POIDS";
    private final String fieldHT, fieldHA, fieldDevise;
    private boolean imputEcart = true;
    private SQLRowAccessor rowDefaultCptProduit, rowDefaultCptService, rowDefaultCptTVACollecte, rowDefaultCptTVADeductible, rowDefaultCptAchat;

    private final SQLRowAccessor rowCatComptable;

    private SQLRowAccessor rowDefaultCptProduitStandard;

    private double totalPoids;

    private BigDecimal totalDevise, totalDeviseSel;
    private BigDecimal totalHA, totalHASel;
    private BigDecimal totalEco, totalEcoSel;
    private BigDecimal totalService, totalServiceSel;
    private BigDecimal totalHTSansFActurable, totalTTC, totalTTCSel;
    private long remiseHT, remiseRestante;
    private final boolean achat;

    // Total des HT par comptes
    private Map<SQLRowAccessor, BigDecimal> mapHt = new HashMap<SQLRowAccessor, BigDecimal>();
    private Map<SQLRowAccessor, BigDecimal> mapHtSel = new HashMap<SQLRowAccessor, BigDecimal>();

    // Total des TVA par comptes
    private Map<SQLRowAccessor, BigDecimal> mapHtTVA = new HashMap<SQLRowAccessor, BigDecimal>();
    private Map<SQLRowAccessor, Map<SQLRowAccessor, BigDecimal>> mapHtTaxeCompl = new HashMap<SQLRowAccessor, Map<SQLRowAccessor, BigDecimal>>();
    private Map<SQLRowAccessor, BigDecimal> mapHtTVAIntra = new HashMap<SQLRowAccessor, BigDecimal>();
    private Map<SQLRowAccessor, BigDecimal> mapHtTVASel = new HashMap<SQLRowAccessor, BigDecimal>();

    // Total HT par TVA
    private Map<SQLRowAccessor, Tuple2<BigDecimal, BigDecimal>> mapHtTVARowTaux = new HashMap<SQLRowAccessor, Tuple2<BigDecimal, BigDecimal>>();
    private int[] selectedRows;

    private Boolean bServiceActive;
    private BigDecimal totalHTAvantRemise;
    private boolean intraComm = false;

    public TotalCalculator(String fieldHA, String fieldHT, String fieldDeviseTotal, SQLRowAccessor rowCatComptable) {
        this(fieldHA, fieldHT, fieldDeviseTotal, false, null, rowCatComptable);
    }

    public void setRowDefaultCptService(SQLRowAccessor rowDefaultCptService) {
        this.rowDefaultCptService = rowDefaultCptService;
    }

    public void setIntraComm(boolean intraComm) {
        this.intraComm = intraComm;
    }

    public TotalCalculator(String fieldHA, String fieldHT, String fieldDeviseTotal, boolean achat, SQLRowAccessor defaultCompte, SQLRowAccessor rowCatComptable) {

        this.achat = achat;
        initValues();

        this.fieldDevise = fieldDeviseTotal;
        this.fieldHA = fieldHA;
        this.fieldHT = fieldHT;
        final SQLTable tablePrefCompte = Configuration.getInstance().getRoot().findTable("PREFS_COMPTE");
        final SQLBackgroundTableCacheItem cacheForTablePref = SQLBackgroundTableCache.getInstance().getCacheForTable(tablePrefCompte);
        final SQLBackgroundTableCacheItem cacheForTableCompte = SQLBackgroundTableCache.getInstance().getCacheForTable(tablePrefCompte.getTable("COMPTE_PCE"));
        final SQLRow rowPrefsCompte = cacheForTablePref.getRowFromId(2);

        SQLPreferences prefs = SQLPreferences.getMemCached(tablePrefCompte.getDBRoot());
        this.imputEcart = prefs.getBoolean(GestionCommercialeGlobalPreferencePanel.IMPUT_ECART, true);

        // FIXME faire un fetcher pour ne pas faire 5 requetes (1 par getForeign)
        // Comptes par défaut
        this.rowDefaultCptService = cacheForTableCompte.getRowFromId(rowPrefsCompte.getForeignID("ID_COMPTE_PCE_VENTE_SERVICE"));
        if (this.rowDefaultCptService == null || this.rowDefaultCptService.isUndefined()) {
            try {
                this.rowDefaultCptService = ComptePCESQLElement.getRowComptePceDefault("VentesServices");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        this.rowCatComptable = rowCatComptable;

        if (defaultCompte == null || defaultCompte.isUndefined()) {

            if (rowCatComptable != null && !rowCatComptable.isUndefined() && !rowCatComptable.isForeignEmpty("ID_COMPTE_PCE_VENTE")) {
                this.rowDefaultCptProduit = cacheForTableCompte.getRowFromId(rowCatComptable.getForeignID("ID_COMPTE_PCE_VENTE"));
            } else {
                this.rowDefaultCptProduit = cacheForTableCompte.getRowFromId(rowPrefsCompte.getForeignID("ID_COMPTE_PCE_VENTE_PRODUIT"));
                if (this.rowDefaultCptProduit == null || this.rowDefaultCptProduit.isUndefined()) {
                    try {
                        this.rowDefaultCptProduit = ComptePCESQLElement.getRowComptePceDefault("VentesProduits");
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        } else {
            this.rowDefaultCptProduit = defaultCompte;
        }
        this.rowDefaultCptProduitStandard = this.rowDefaultCptProduit;

        this.rowDefaultCptTVACollecte = cacheForTableCompte.getRowFromId(rowPrefsCompte.getForeignID("ID_COMPTE_PCE_TVA_VENTE"));
        if (this.rowDefaultCptTVACollecte == null || this.rowDefaultCptTVACollecte.isUndefined()) {
            try {
                this.rowDefaultCptTVACollecte = ComptePCESQLElement.getRowComptePceDefault("TVACollectee");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        this.rowDefaultCptTVADeductible = cacheForTableCompte.getRowFromId(rowPrefsCompte.getForeignID("ID_COMPTE_PCE_TVA_ACHAT"));
        if (this.rowDefaultCptTVADeductible == null || this.rowDefaultCptTVADeductible.isUndefined()) {
            try {
                this.rowDefaultCptTVADeductible = ComptePCESQLElement.getRowComptePceDefault("TVADeductible");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        if (defaultCompte == null || defaultCompte.isUndefined()) {
            if (rowCatComptable != null && !rowCatComptable.isUndefined() && !rowCatComptable.isForeignEmpty("ID_COMPTE_PCE_ACHAT")) {
                this.rowDefaultCptProduit = cacheForTableCompte.getRowFromId(rowCatComptable.getForeignID("ID_COMPTE_PCE_ACHAT"));
            } else {
                this.rowDefaultCptAchat = cacheForTableCompte.getRowFromId(rowPrefsCompte.getForeignID("ID_COMPTE_PCE_ACHAT"));
                if (this.rowDefaultCptAchat == null || this.rowDefaultCptAchat.isUndefined()) {
                    try {
                        this.rowDefaultCptAchat = ComptePCESQLElement.getRowComptePceDefault("Achats");
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        } else {
            this.rowDefaultCptAchat = defaultCompte;
        }

    }

    public void setRowDefaultCptProduit(SQLRowAccessor rowDefaultCptProduit) {
        this.rowDefaultCptProduit = rowDefaultCptProduit;
    }

    public void retoreRowDefaultCptProduit() {
        this.rowDefaultCptProduit = this.rowDefaultCptProduitStandard;
    }

    /**
     * Définition d'une remise HT à appliquer
     * 
     * @param remiseHT montant de la remise en cents
     * @param totalHTAvantRemise montant de la facture avant remise
     */
    public void setRemise(long remiseHT, BigDecimal totalHTAvantRemise) {
        this.remiseHT = remiseHT;
        this.remiseRestante = remiseHT;
        this.totalHTAvantRemise = totalHTAvantRemise;
    }

    public void addTVAAdjust(BigDecimal tvaFix) {

        final SQLBackgroundTableCacheItem cacheForTableCompte = SQLBackgroundTableCache.getInstance().getCacheForTable(this.compteTable);

        SQLRowAccessor tva = TaxeCache.getCache().getFirstTaxe();

        SQLRowAccessor rowCptTva;
        if (this.intraComm) {

            // Intra comm TTC=HT et solde de TVA
            rowCptTva = cacheForTableCompte.getRowFromId(tva.getForeignID("ID_COMPTE_PCE_DED_INTRA"));
            if (rowCptTva == null || rowCptTva.isUndefined()) {
                rowCptTva = this.rowDefaultCptTVADeductible;
            }

            SQLRowAccessor rowCptTvaIntra = cacheForTableCompte.getRowFromId(tva.getForeignID("ID_COMPTE_PCE_COLLECTE_INTRA"));
            if (rowCptTvaIntra == null || rowCptTvaIntra.isUndefined()) {
                rowCptTvaIntra = this.rowDefaultCptTVADeductible;
            }

            if (this.mapHtTVAIntra.get(rowCptTvaIntra) == null) {
                this.mapHtTVAIntra.put(rowCptTvaIntra, tvaFix);
            } else {
                BigDecimal l = this.mapHtTVAIntra.get(rowCptTvaIntra);
                this.mapHtTVAIntra.put(rowCptTvaIntra, l.add(tvaFix));
            }

        } else if (this.achat) {
            rowCptTva = cacheForTableCompte.getRowFromId(tva.getForeignID("ID_COMPTE_PCE_DED"));
            if (rowCptTva == null || rowCptTva.isUndefined()) {
                rowCptTva = this.rowDefaultCptTVADeductible;
            }
        } else {
            rowCptTva = cacheForTableCompte.getRowFromId(tva.getForeignID("ID_COMPTE_PCE_COLLECTE"));
            if (rowCptTva == null || rowCptTva.isUndefined()) {
                rowCptTva = this.rowDefaultCptTVACollecte;
            }
        }
        if (this.mapHtTVA.get(rowCptTva) == null) {
            this.mapHtTVA.put(rowCptTva, tvaFix);
        } else {
            BigDecimal l = this.mapHtTVA.get(rowCptTva);
            this.mapHtTVA.put(rowCptTva, l.add(tvaFix));
        }

        this.totalTTC = this.totalTTC.add(tvaFix);

    }

    /**
     * Gestion de la vente de service
     * 
     * @param b
     */
    public void setServiceActive(boolean b) {
        this.bServiceActive = b;
    }

    /**
     * Remise à zéro des valeurs de calcul
     */
    public void initValues() {
        this.remiseHT = 0;
        this.remiseRestante = 0;
        this.totalHTAvantRemise = BigDecimal.ZERO;

        this.selectedRows = null;

        this.totalHTSansFActurable = BigDecimal.ZERO;
        this.totalTTC = BigDecimal.ZERO;
        this.totalTTCSel = BigDecimal.ZERO;

        this.totalEco = BigDecimal.ZERO;
        this.totalEcoSel = BigDecimal.ZERO;

        this.totalHA = BigDecimal.ZERO;
        this.totalHASel = BigDecimal.ZERO;

        this.totalService = BigDecimal.ZERO;
        this.totalServiceSel = BigDecimal.ZERO;

        this.totalDeviseSel = BigDecimal.ZERO;
        this.totalDevise = BigDecimal.ZERO;

        this.totalPoids = 0;

        // Total des HT par comptes
        this.mapHt.clear();
        this.mapHtSel.clear();

        // Total des TVA par comptes
        this.mapHtTVA.clear();
        this.mapHtTaxeCompl.clear();
        this.mapHtTVAIntra.clear();
        this.mapHtTVARowTaux.clear();
        this.mapHtTVASel.clear();

    }

    public void setSelectedRows(int[] selectedRows) {
        this.selectedRows = selectedRows;
    }

    public void addEchantillon(BigDecimal ht, SQLRowAccessor tva) {
        addHT(ht, ht, tva, this.rowDefaultCptProduit, false);
    }

    // Cache de TVA
    private static Map<Integer, SQLRowAccessor> mapTVA;
    private static long lastTVAfetchedTime = 0;

    final SQLTable tvaTable = Configuration.getInstance().getRoot().findTable("TAXE");
    final SQLTable compteTable = Configuration.getInstance().getRoot().findTable("COMPTE_PCE");

    /**
     * Mise en cache des comptes de TVA
     */
    private void fetchTVA() {
        mapTVA = new HashMap<Integer, SQLRowAccessor>();
        Set<SQLRowAccessor> taxes = TaxeCache.getCache().getAllTaxe();
        for (SQLRowAccessor sqlRowAccessor : taxes) {
            mapTVA.put(sqlRowAccessor.getID(), sqlRowAccessor);
        }
        // SQLRowValues rowVals = new SQLRowValues(this.tvaTable);
        // SQLRowValues rowValsC1 = new SQLRowValues(this.compteTable);
        // rowValsC1.put("NUMERO", null);
        // rowValsC1.put("ID", null);
        //
        // SQLRowValues rowValsC2 = new SQLRowValues(this.compteTable);
        // rowValsC2.put("NUMERO", null);
        // rowValsC2.put("ID", null);
        //
        // SQLRowValues rowValsC3 = new SQLRowValues(this.compteTable);
        // rowValsC3.put("NUMERO", null);
        // rowValsC3.put("ID", null);
        // SQLRowValues rowValsC4 = new SQLRowValues(this.compteTable);
        // rowValsC4.put("NUMERO", null);
        // rowValsC4.put("ID", null);
        //
        // rowVals.put(this.tvaTable.getKey().getName(), null);
        // rowVals.put("ID_COMPTE_PCE_COLLECTE", rowValsC1);
        // rowVals.put("ID_COMPTE_PCE_DED", rowValsC2);
        // rowVals.put("ID_COMPTE_PCE_VENTE", rowValsC3);
        // rowVals.put("ID_COMPTE_PCE_VENTE_SERVICE", rowValsC4);
        //
        // if (this.tvaTable.contains("ID_COMPTE_PCE_COLLECTE_INTRA")) {
        // SQLRowValues rowValsC1Intra = new SQLRowValues(this.compteTable);
        // rowValsC1Intra.put("NUMERO", null);
        // rowValsC1Intra.put("ID", null);
        // rowVals.put("ID_COMPTE_PCE_COLLECTE_INTRA", rowValsC1Intra);
        // SQLRowValues rowValsC2Intra = new SQLRowValues(this.compteTable);
        // rowValsC2Intra.put("NUMERO", null);
        // rowValsC2Intra.put("ID", null);
        // rowVals.put("ID_COMPTE_PCE_DED_INTRA", rowValsC2Intra);
        // }
        // SQLRowValuesListFetcher fetch = SQLRowValuesListFetcher.create(rowVals);
        // List<SQLRowValues> rowValsList = fetch.fetch();
        //
        // for (SQLRowValues sqlRowValues : rowValsList) {
        // mapTVA.put(sqlRowValues.getID(), sqlRowValues);
        // }
        lastTVAfetchedTime = System.currentTimeMillis();
    }

    private void addHT(BigDecimal ht, BigDecimal htSansFacturable, SQLRowAccessor tva, SQLRowAccessor cptArticle, boolean selection) {

        BigDecimal ttc;
        BigDecimal totalTVA;
        final SQLBackgroundTableCacheItem cacheForTableCompte = SQLBackgroundTableCache.getInstance().getCacheForTable(cptArticle.getTable());

        if (tva == null || tva.isUndefined()) {
            ttc = ht;
            totalTVA = BigDecimal.ZERO;
        } else {
            BigDecimal tauxTVA = BigDecimal.valueOf(TaxeCache.getCache().getTauxFromId(tva.getID())).movePointLeft(2);
            ttc = tauxTVA.add(BigDecimal.ONE).multiply(ht, DecimalUtils.HIGH_PRECISION);
            totalTVA = ttc.subtract(ht);
        }

        if (tva != null && !tva.isUndefined()) {
            SQLRowAccessor rowCptTva;
            if (this.intraComm) {

                // Intra comm TTC=HT et solde de TVA
                rowCptTva = cacheForTableCompte.getRowFromId(tva.getForeignID("ID_COMPTE_PCE_DED_INTRA"));
                if (rowCptTva == null || rowCptTva.isUndefined()) {
                    rowCptTva = this.rowDefaultCptTVADeductible;
                }

                SQLRowAccessor rowCptTvaIntra = cacheForTableCompte.getRowFromId(tva.getForeignID("ID_COMPTE_PCE_COLLECTE_INTRA"));
                if (rowCptTvaIntra == null || rowCptTvaIntra.isUndefined()) {
                    rowCptTvaIntra = this.rowDefaultCptTVADeductible;
                }

                if (this.mapHtTVAIntra.get(rowCptTvaIntra) == null) {
                    this.mapHtTVAIntra.put(rowCptTvaIntra, totalTVA);
                } else {
                    BigDecimal l = this.mapHtTVAIntra.get(rowCptTvaIntra);
                    this.mapHtTVAIntra.put(rowCptTvaIntra, l.add(totalTVA));
                }

                ttc = ht;
            } else if (this.achat) {
                rowCptTva = cacheForTableCompte.getRowFromId(tva.getForeignID("ID_COMPTE_PCE_DED"));
                if (rowCptTva == null || rowCptTva.isUndefined()) {
                    rowCptTva = this.rowDefaultCptTVADeductible;
                }
            } else {
                rowCptTva = cacheForTableCompte.getRowFromId(tva.getForeignID("ID_COMPTE_PCE_COLLECTE"));
                if (rowCptTva == null || rowCptTva.isUndefined()) {
                    rowCptTva = this.rowDefaultCptTVACollecte;
                }
            }
            if (this.mapHtTVA.get(rowCptTva) == null) {
                this.mapHtTVA.put(rowCptTva, totalTVA);
            } else {
                BigDecimal l = this.mapHtTVA.get(rowCptTva);
                this.mapHtTVA.put(rowCptTva, l.add(totalTVA));
            }
            if (ht.signum() != 0) {
                if (this.mapHtTVARowTaux.get(tva) == null) {
                    this.mapHtTVARowTaux.put(tva, Tuple2.create(ht, totalTVA));
                } else {
                    Tuple2<BigDecimal, BigDecimal> l = this.mapHtTVARowTaux.get(tva);
                    this.mapHtTVARowTaux.put(tva, Tuple2.create(ht.add(l.get0()), l.get1().add(totalTVA)));
                }
            }
            if (selection) {
                if (this.mapHtTVASel.get(rowCptTva) == null) {
                    this.mapHtTVASel.put(rowCptTva, totalTVA);
                } else {
                    BigDecimal l = this.mapHtTVASel.get(rowCptTva);
                    this.mapHtTVASel.put(rowCptTva, l.add(totalTVA));
                }
            }
        }

        if (this.mapHt.get(cptArticle) == null) {
            this.mapHt.put(cptArticle, ht);
        } else {
            BigDecimal l = this.mapHt.get(cptArticle);
            this.mapHt.put(cptArticle, l.add(ht));
        }

        this.totalTTC = this.totalTTC.add(ttc);
        this.totalHTSansFActurable = this.totalHTSansFActurable.add(htSansFacturable);
        if (selection) {

            if (this.mapHtSel.get(cptArticle) == null) {
                this.mapHtSel.put(cptArticle, ht);
            } else {
                BigDecimal l = this.mapHtSel.get(cptArticle);
                this.mapHtSel.put(cptArticle, l.add(ht));
            }
            this.totalTTCSel = this.totalTTCSel.add(ttc);
        }
    }

    private static boolean containsInt(int[] tab, int i) {
        if (tab == null) {
            return false;
        }

        for (int j = 0; j < tab.length; j++) {
            if (tab[j] == i) {
                return true;
            }
        }
        return false;
    }

    public void addLine(SQLRowAccessor rowAccessorLine, SQLRowAccessor article, int lineNumber, boolean last) {

        if (rowAccessorLine.getFields().contains("NIVEAU") && rowAccessorLine.getInt("NIVEAU") != 1) {
            return;
        }

        // Total HT de la ligne
        BigDecimal totalLineHT = rowAccessorLine.getObject(this.fieldHT) == null ? BigDecimal.ZERO : (BigDecimal) rowAccessorLine.getObject(this.fieldHT);
        BigDecimal totalLineEco = rowAccessorLine.getObject("T_ECO_CONTRIBUTION") == null ? BigDecimal.ZERO : (BigDecimal) rowAccessorLine.getObject("T_ECO_CONTRIBUTION");

        BigDecimal totalLineHTSansFacturable = totalLineHT;
        if (!this.achat) {
            totalLineHTSansFacturable = rowAccessorLine.getObject("PV_HT") == null ? BigDecimal.ZERO : (BigDecimal) rowAccessorLine.getObject("PV_HT");
            BigDecimal qteUV = rowAccessorLine.getObject("QTE_UNITAIRE") == null ? BigDecimal.ZERO : (BigDecimal) rowAccessorLine.getObject("QTE_UNITAIRE");
            int qte = rowAccessorLine.getInt("QTE");
            totalLineHTSansFacturable = totalLineHTSansFacturable.multiply(qteUV).multiply(new BigDecimal(qte));
        }
        // Prix Unitaire de la ligne
        // TODO voir pour passer le prix total et non le prix unitaire
        BigDecimal totalHALigne = rowAccessorLine.getObject(this.fieldHA) == null ? BigDecimal.ZERO : (BigDecimal) rowAccessorLine.getObject(this.fieldHA);

        Boolean service = rowAccessorLine.getBoolean(FIELD_SERVICE);

        BigDecimal totalLineDevise = (this.fieldDevise == null || rowAccessorLine.getObject(this.fieldDevise) == null) ? BigDecimal.ZERO : (BigDecimal) rowAccessorLine.getObject(this.fieldDevise);

        Number nPoids = (Number) rowAccessorLine.getObject(FIELD_POIDS);

        // Si il y a une remise à appliquer
        if (this.remiseHT != 0 && this.remiseRestante > 0 && this.totalHTAvantRemise != null && this.totalHTAvantRemise.signum() != 0) {

            // Si c'est la derniere ligne, on applique le restant de la remise
            if (last) {
                totalLineHT = totalLineHT.subtract(new BigDecimal(this.remiseRestante).movePointLeft(2));
                totalLineHTSansFacturable = totalLineHTSansFacturable.subtract(new BigDecimal(this.remiseRestante).movePointLeft(2));
                this.remiseRestante = 0;
            } else {
                BigDecimal percent = totalLineHT.divide(this.totalHTAvantRemise, DecimalUtils.HIGH_PRECISION);

                BigDecimal remiseApply = percent.multiply(new BigDecimal(this.remiseHT), DecimalUtils.HIGH_PRECISION).setScale(0, RoundingMode.HALF_UP);
                totalLineHT = totalLineHT.subtract(remiseApply.movePointLeft(2));
                totalLineHTSansFacturable = totalLineHTSansFacturable.subtract(remiseApply.movePointLeft(2));
                this.remiseRestante -= remiseApply.longValue();
            }
        }

        // TODO : generaliser le cache
        if (mapTVA == null || lastTVAfetchedTime + 30 * 1000 < System.currentTimeMillis()) {
            fetchTVA();
        }
        Integer idTVA = null;
        if (rowAccessorLine.getObject("ID_TAXE") != null && !rowAccessorLine.isForeignEmpty("ID_TAXE")) {
            idTVA = rowAccessorLine.getForeignID("ID_TAXE");
        } else {
            final SQLRowAccessor foreignTVA = rowAccessorLine.getForeign("ID_TAXE");
            if (foreignTVA != null) {
                idTVA = foreignTVA.getID();
            }
        }
        SQLRowAccessor tva = mapTVA.get(idTVA);

        SQLRowAccessor cpt = (this.achat ? this.rowDefaultCptAchat : this.rowDefaultCptProduit);
        final SQLBackgroundTableCacheItem cacheForTableCompte = SQLBackgroundTableCache.getInstance().getCacheForTable(cpt.getTable());

        if (!this.achat) {
            // Total Service
            if (this.bServiceActive != null && this.bServiceActive && service != null && service.booleanValue()) {
                this.totalService = this.totalService.add(totalLineHT);
                cpt = this.rowDefaultCptService;
                if (tva != null && !tva.isForeignEmpty("ID_COMPTE_PCE_VENTE_SERVICE")) {
                    cpt = cacheForTableCompte.getRowFromId(tva.getForeignID("ID_COMPTE_PCE_VENTE_SERVICE"));
                }
            } else {
                // Compte defini par défaut dans la TVA
                if (tva != null && !tva.isForeignEmpty("ID_COMPTE_PCE_VENTE")) {
                    cpt = cacheForTableCompte.getRowFromId(tva.getForeignID("ID_COMPTE_PCE_VENTE"));
                }

            }
        }

        // Compte PCE défini directement sur la ligne
        boolean compteSpec = false;
        if (rowAccessorLine.getTable().contains("ID_COMPTE_PCE") && rowAccessorLine.getObject("ID_COMPTE_PCE") != null && !rowAccessorLine.isForeignEmpty("ID_COMPTE_PCE")) {
            compteSpec = true;
            cpt = cacheForTableCompte.getRowFromId(rowAccessorLine.getForeignID("ID_COMPTE_PCE"));
        }
        if (article != null && !article.isUndefined()) {
            if (!compteSpec) {
                SQLRowAccessor cptCatComptable = null;
                // TODO Optimiser les requetes
                if (this.rowCatComptable != null && !this.rowCatComptable.isUndefined()) {
                    String suffix = (this.achat ? "_ACHAT" : "_VENTE");
                    if (!this.rowCatComptable.isForeignEmpty("ID_COMPTE_PCE" + suffix)) {
                        cptCatComptable = cacheForTableCompte.getRowFromId(this.rowCatComptable.getForeignID("ID_COMPTE_PCE" + suffix));
                    }
                    Collection<? extends SQLRowAccessor> rows = article.getReferentRows(this.compteTable.getTable("ARTICLE_CATEGORIE_COMPTABLE"));
                    for (SQLRowAccessor sqlRowAccessor : rows) {
                        if (sqlRowAccessor.getForeignID("ID_CATEGORIE_COMPTABLE") == this.rowCatComptable.getID()) {
                            if (!sqlRowAccessor.isForeignEmpty("ID_COMPTE_PCE" + suffix)) {
                                cptCatComptable = cacheForTableCompte.getRowFromId(sqlRowAccessor.getForeignID("ID_COMPTE_PCE" + suffix));
                            }
                        }
                    }
                }
                if (cptCatComptable == null) {
                    String suffix = (this.achat ? "_ACHAT" : "");
                    SQLRowAccessor compteArticle = cacheForTableCompte.getRowFromId(article.getForeignID("ID_COMPTE_PCE" + suffix));
                    if (compteArticle != null && !compteArticle.isUndefined()) {
                        cpt = compteArticle;
                    } else {
                        SQLRowAccessor familleArticle = article.getForeign("ID_FAMILLE_ARTICLE");
                        Set<SQLRowAccessor> unique = new HashSet<SQLRowAccessor>();
                        while (familleArticle != null && !familleArticle.isUndefined() && !unique.contains(familleArticle)) {

                            unique.add(familleArticle);
                            if (familleArticle.getObject("ID_COMPTE_PCE" + suffix) != null && !familleArticle.isForeignEmpty("ID_COMPTE_PCE" + suffix)) {
                                SQLRowAccessor compteFamilleArticle = cacheForTableCompte.getRowFromId(familleArticle.getForeignID("ID_COMPTE_PCE" + suffix));
                                if (compteFamilleArticle != null && !compteFamilleArticle.isUndefined()) {
                                    cpt = compteFamilleArticle;
                                    break;
                                }
                            }
                            familleArticle = familleArticle.getForeign("ID_FAMILLE_ARTICLE_PERE");
                        }
                    }
                } else {
                    cpt = cptCatComptable;
                }
            }
            if (!this.achat) {
                SQLRowAccessor taxeCompl = (article.getFields().contains("ID_TAXE_COMPLEMENTAIRE") && article.getObject("ID_TAXE_COMPLEMENTAIRE") != null
                        && !article.isForeignEmpty("ID_TAXE_COMPLEMENTAIRE") ? article.getForeign("ID_TAXE_COMPLEMENTAIRE") : null);
                if (taxeCompl != null && !taxeCompl.isUndefined()) {

                    Map<SQLRowAccessor, BigDecimal> mapCptTaxeValue = this.mapHtTaxeCompl.get(cpt);
                    if (mapCptTaxeValue == null) {
                        mapCptTaxeValue = new HashMap<SQLRowAccessor, BigDecimal>();
                        this.mapHtTaxeCompl.put(cpt, mapCptTaxeValue);
                    }
                    BigDecimal b = mapCptTaxeValue.get(taxeCompl);
                    if (b == null) {
                        b = BigDecimal.ZERO;
                    }
                    b = b.add(totalLineHT);
                    mapCptTaxeValue.put(taxeCompl, b);
                }
            }
        }

        if (!compteSpec) {
            // Total Service
            if (this.bServiceActive != null && this.bServiceActive) {
                if (service != null && service.booleanValue()) {
                    this.totalService = this.totalService.add(totalLineHT);
                    cpt = this.rowDefaultCptService;
                }
            }
        }

        // Total HA
        this.totalHA = this.totalHA.add(totalHALigne);

        // Total Devise
        if (totalLineDevise != null) {
            this.totalDevise = this.totalDevise.add(totalLineDevise);
        }

        // Total Poids

        this.totalPoids += nPoids == null ? 0 : nPoids.doubleValue();

        // Eco-contribution
        this.totalEco = this.totalEco.add(totalLineEco);

        // Calcul total sélectionné
        boolean selection = containsInt(this.selectedRows, lineNumber);
        if (selection) {

            this.totalHASel = this.totalHASel.add(totalHALigne);

            if (this.bServiceActive != null && this.bServiceActive) {
                if (service != null && service.booleanValue()) {
                    this.totalServiceSel = this.totalServiceSel.add(totalLineHT);
                }
            }
            this.totalEcoSel = this.totalEcoSel.add(totalLineEco);

            if (totalLineDevise != null) {
                this.totalDeviseSel = this.totalDeviseSel.add(totalLineDevise);
            }
        }

        addHT(totalLineHT, totalLineHTSansFacturable, tva, cpt, selection);
    }

    /**
     * Vérifie si ht + tva = ttc
     */
    public void checkResult() {
        BigDecimal ht = getTotalHT();
        BigDecimal tva = getTotalTVA();
        BigDecimal totalTTC2 = getTotalTTC();
        BigDecimal reste = totalTTC2.subtract(ht.add(tva));
        if (!this.intraComm && reste.compareTo(BigDecimal.ZERO) != 0) {
            System.err.print("Ecarts: " + reste + "(HT:" + ht);
            System.err.print(" TVA:" + tva);
            System.err.println(" TTC:" + totalTTC2);

            // TODO Check if row already exist in MAP ??
            if (!this.imputEcart) {
                for (SQLRowAccessor rHT : this.mapHt.keySet()) {
                    BigDecimal input = this.mapHt.get(rHT);
                    if (input != null && input.signum() != 0) {
                        this.mapHt.put(rHT, input.add(reste));
                        break;
                    }
                }
            } else {
                SQLRow row = ComptePCESQLElement.getRow("758", "Ecarts arrondis");
                this.mapHt.put(row, reste);
            }
        }
    }

    public BigDecimal getTotalDevise() {
        return this.totalDevise;
    }

    public BigDecimal getTotalDeviseSel() {
        return this.totalDeviseSel;
    }

    public BigDecimal getTotalHA() {
        return this.totalHA;
    }

    public BigDecimal getTotalHASel() {
        return this.totalHASel;
    }

    public double getTotalPoids() {
        return this.totalPoids;
    }

    public BigDecimal getTotalService() {
        return this.totalService;
    }

    public BigDecimal getTotalServiceSel() {
        return this.totalServiceSel;
    }

    public Map<SQLRowAccessor, Map<SQLRowAccessor, BigDecimal>> getMapHtTaxeCompl() {
        return this.mapHtTaxeCompl;
    }

    public BigDecimal getTotalHT() {
        BigDecimal ht = BigDecimal.ZERO;
        for (SQLRowAccessor row : this.mapHt.keySet()) {
            ht = ht.add(this.mapHt.get(row).setScale(2, RoundingMode.HALF_UP));
        }

        return ht;
    }

    public BigDecimal getTotalTVA() {
        BigDecimal tva = BigDecimal.ZERO;
        for (SQLRowAccessor row : this.mapHtTVA.keySet()) {
            tva = tva.add(this.mapHtTVA.get(row).setScale(2, RoundingMode.HALF_UP));
        }
        return tva;
    }

    public BigDecimal getTotalEco() {
        return this.totalEco.setScale(2, RoundingMode.HALF_UP);
    }

    public BigDecimal getTotalEcoSel() {
        return this.totalEcoSel.setScale(2, RoundingMode.HALF_UP);
    }

    public BigDecimal getTotalHTSansFActurable() {
        return this.totalHTSansFActurable;
    }

    public BigDecimal getTotalTTC() {
        return this.totalTTC.setScale(2, RoundingMode.HALF_UP);
    }

    public BigDecimal getTotalHTSel() {
        BigDecimal ht = BigDecimal.ZERO;
        for (SQLRowAccessor row : this.mapHtSel.keySet()) {
            ht = ht.add(this.mapHtSel.get(row).setScale(2, RoundingMode.HALF_UP));
        }

        return ht;
    }

    public BigDecimal getTotalTVASel() {
        BigDecimal tva = BigDecimal.ZERO;
        for (SQLRowAccessor row : this.mapHtTVASel.keySet()) {
            tva = tva.add(this.mapHtTVASel.get(row).setScale(2, RoundingMode.HALF_UP));
        }
        return tva;
    }

    public BigDecimal getTotalTTCSel() {

        return this.totalTTCSel.setScale(2, RoundingMode.HALF_UP);
    }

    public Map<SQLRowAccessor, BigDecimal> getMapHt() {
        return this.mapHt;
    }

    public Map<SQLRowAccessor, Tuple2<BigDecimal, BigDecimal>> getMapHtTVARowTaux() {
        return this.mapHtTVARowTaux;
    }

    public Map<SQLRowAccessor, BigDecimal> getMapHtTVA() {
        return this.mapHtTVA;
    }

    public Map<SQLRowAccessor, BigDecimal> getMapHtTVAIntra() {
        return this.mapHtTVAIntra;
    }
}