OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 177 | 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.generationEcritures;

import org.openconcerto.sql.element.SQLElementDirectory;
import org.openconcerto.sql.model.ConnectionHandlerNoSetup;
import org.openconcerto.sql.model.DBRoot;
import org.openconcerto.sql.model.DBSystemRoot;
import org.openconcerto.sql.model.SQLDataSource;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLInsert;
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.SQLUpdate;
import org.openconcerto.sql.model.Where;
import org.openconcerto.sql.users.User;
import org.openconcerto.sql.utils.SQLUtils;
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.cc.ITransformer;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.dbutils.ResultSetHandler;

public class Exercice {
    private Date debut;
    private Date fin;
    private static final Logger LOGGER = Logger.getLogger(Exercice.class.getName());

    public Exercice() {

    }

    public Exercice(Date debut, Date fin) {
        if (debut != null && debut.after(fin)) {
            throw new IllegalArgumentException("date de fin invalide");
        }
        this.debut = debut;
        this.fin = fin;
    }

    public String toString() {
        return "Exercice " + this.debut + " -> " + this.fin;
    }

    public void insert(SQLElementDirectory directory, final DBRoot root, User user, List<Piece> pieces) throws SQLException {
        final DBSystemRoot sysRoot = root.getDBSystemRoot();
        final List<SQLInsert> insertsPiece = new ArrayList<>();
        LOGGER.log(Level.INFO, "insertion de {0} pièces comptables", pieces.size());
        for (Piece p : pieces) {
            // Pièces
            insertsPiece.add(p.createInsert(root));

            // Vérification des mouvements
            final List<Mouvement> mouvements = p.getMouvements();
            if (mouvements.isEmpty()) {
                throw new IllegalStateException("Piece vide : " + p);
            }

            for (Mouvement m : mouvements) {
                if (!m.isBalanced()) {
                    throw new IllegalStateException("Mouvement non balancé : " + m);
                }
                if (m.isEmpty()) {
                    throw new IllegalStateException("Mouvement vide : " + m);
                }

                for (Ecriture e : m.getEcritures()) {
                    if (this.debut != null && e.getDate().before(this.debut)) {
                        throw new IllegalStateException("Mouvement invalide : " + m + " : une écriture est définie avant la date de début d'exercice : " + e);
                    }
                    if (e.getNom() == null) {
                        throw new IllegalStateException("Ecriture sans nom : " + e);
                    }
                }

                if (this.fin != null) {
                    for (Ecriture e : m.getEcritures()) {
                        if (e.getDate().after(this.fin)) {
                            throw new IllegalStateException("Mouvement invalide : " + m + " : une écriture est définie après la date de fin d'exercice : " + e.getDate() + ">" + this.fin);
                        }

                    }
                }

            }
        }

        SQLUtils.executeAtomic(sysRoot.getDataSource(), new ConnectionHandlerNoSetup<Object, SQLException>() {
            @Override
            public Object handle(SQLDataSource ds) throws SQLException {
                // Insertion des journaux et comptes manquants et remplissage des champs journaux et
                // comptes des
                // écritures depuis les ids
                final Set<Journal> journauxACreerOuFetcher = new HashSet<>();
                final Set<Number> journauxAresoudre = new HashSet<>();
                final Set<Compte> comptesACreerOuFetcher = new HashSet<>();
                final Set<Number> comptesAresoudre = new HashSet<>();
                final List<Ecriture> ecrituresSansJournalID = new LinkedList<>();
                final List<Ecriture> ecrituresSansCompteID = new LinkedList<>();
                for (Piece p : pieces) {
                    for (Mouvement m : p.getMouvements()) {
                        for (Ecriture e : m.getEcritures()) {
                            if (e.getJournalID() == null) {
                                // Journal à creer
                                journauxACreerOuFetcher.add(new Journal(null, e.getJournalCode(), e.getJournalNom()));
                                ecrituresSansJournalID.add(e);
                            } else {
                                journauxAresoudre.add(e.getJournalID());
                            }
                            if (e.getCompteID() == null) {
                                // Compte à creer
                                comptesACreerOuFetcher.add(new Compte(null, e.getCompteNumero(), e.getCompteNom()));
                                ecrituresSansCompteID.add(e);
                            } else {
                                comptesAresoudre.add(e.getJournalID());
                            }
                        }

                    }
                }
                final Map<Long, Journal> mapJournaux = new HashMap<>();
                if (!journauxACreerOuFetcher.isEmpty()) {
                    if (LOGGER.isLoggable(Level.FINE)) {
                        LOGGER.fine(journauxACreerOuFetcher.size() + " journaux à créer : " + journauxACreerOuFetcher);
                    }
                    // On récupère tous les journaux car il y en a peu
                    final Map<String, Journal> codesDesJournauxExistants = getCodesJournaux(root);

                    final List<SQLInsert> insertsJournaux = new ArrayList<>();
                    final List<Journal> list = new ArrayList<>();
                    for (Journal journal : journauxACreerOuFetcher) {
                        // journal non déjà existant
                        if (codesDesJournauxExistants.get(journal.getCode().toLowerCase()) == null) {
                            list.add(journal);
                            insertsJournaux.add(journal.createInsert(root));
                        }
                    }
                    final List<Number> journauxIds = new ArrayList<>();
                    if (!insertsJournaux.isEmpty()) {
                        journauxIds.addAll(SQLInsert.executeSimilarInserts(sysRoot, insertsJournaux, true));
                        journauxAresoudre.addAll(journauxIds);
                    }
                    // Mise à jour de l'ID du journal pour les écritures dont le journal vient
                    // d'être créé
                    final int size = list.size();
                    for (int i = 0; i < size; i++) {
                        final String journalCode = list.get(i).getCode();
                        final Number journalID = journauxIds.get(i);
                        final Iterator<Ecriture> it = ecrituresSansJournalID.iterator();
                        while (it.hasNext()) {
                            final Ecriture e = it.next();

                            if (e.getJournalCode().equalsIgnoreCase(journalCode)) {
                                e.setJournalID(journalID);
                                it.remove();
                            }

                        }
                    }
                    final Iterator<Ecriture> it = ecrituresSansJournalID.iterator();
                    while (it.hasNext()) {
                        final Ecriture e = it.next();
                        final Journal journal = codesDesJournauxExistants.get(e.getJournalCode().toLowerCase());
                        e.setJournalID(journal.getId());
                        it.remove();
                    }
                    for (Journal journal : codesDesJournauxExistants.values()) {
                        mapJournaux.put(journal.getId(), journal);
                    }
                }
                final Map<Long, Compte> mapComptes = new HashMap<>();
                if (!comptesACreerOuFetcher.isEmpty()) {
                    if (LOGGER.isLoggable(Level.FINE)) {
                        LOGGER.fine(comptesACreerOuFetcher.size() + " comptes à créer ou fetcher: " + comptesACreerOuFetcher);
                    }
                    final Map<String, Compte> numerosDesComptesExistants = getNumeroDesComptes(root, pieces);

                    final List<SQLInsert> insertsComptes = new ArrayList<>();
                    final List<Compte> list = new ArrayList<>();
                    for (Compte c : comptesACreerOuFetcher) {
                        if (numerosDesComptesExistants.get(c.getNumero().toLowerCase()) == null) {
                            if (LOGGER.isLoggable(Level.FINE)) {
                                LOGGER.fine("création du compte : " + c.getNumero().toLowerCase());
                            }
                            list.add(c);
                            insertsComptes.add(c.createInsert(root));
                        }
                    }
                    List<Number> comptesIds = new ArrayList<>();
                    if (!insertsComptes.isEmpty()) {

                        final List<Number> insertedIDs = SQLInsert.executeSimilarInserts(sysRoot, insertsComptes, true);
                        comptesIds.addAll(insertedIDs);
                        if (LOGGER.isLoggable(Level.FINE)) {
                            LOGGER.fine("IDs des comptes créés : " + comptesIds);
                        }

                        comptesAresoudre.addAll(comptesIds);
                    }

                    // Mise à jour de l'ID du compte pour les écritures dont le compte vient
                    // d'être créé
                    final int size = list.size();
                    for (int i = 0; i < size; i++) {
                        final String compteCode = list.get(i).getNumero();
                        final Number compteID = comptesIds.get(i);
                        final Iterator<Ecriture> it = ecrituresSansCompteID.iterator();
                        while (it.hasNext()) {
                            final Ecriture e = it.next();
                            if (e.getCompteNumero().equalsIgnoreCase(compteCode)) {
                                if (LOGGER.isLoggable(Level.FINEST)) {
                                    LOGGER.finest("mise à jour de l'écriture " + e + " avec le compte d'ID " + compteID);
                                }
                                e.setCompteID(compteID);
                                it.remove();
                            }
                        }

                    }
                    final Iterator<Ecriture> it = ecrituresSansCompteID.iterator();
                    while (it.hasNext()) {
                        final Ecriture e = it.next();
                        Compte compte = numerosDesComptesExistants.get(e.getCompteNumero().toLowerCase());
                        if (compte != null) {
                            e.setCompteID(compte.getId());
                            it.remove();
                        }

                    }
                    for (Compte compte : numerosDesComptesExistants.values()) {
                        mapComptes.put(compte.getId(), compte);
                    }

                }

                // fetch journaux et comptes, update des code/numéro et nom
                final List<String> queries = new ArrayList<>();
                final List<ResultSetHandler> handlers = new ArrayList<>();

                if (!comptesAresoudre.isEmpty()) {
                    if (LOGGER.isLoggable(Level.FINE)) {
                        LOGGER.fine(comptesAresoudre.size() + " comptes à résoudre : ids " + comptesAresoudre);
                    }
                    final SQLTable tableCompte = root.getTable("COMPTE_PCE");
                    final SQLSelect selCompte = new SQLSelect();
                    selCompte.addSelect(tableCompte.getKey());
                    selCompte.addSelect(tableCompte.getField("NUMERO"));
                    selCompte.addSelect(tableCompte.getField("NOM"));
                    selCompte.setWhere(new Where(tableCompte.getKey(), comptesAresoudre));
                    queries.add(selCompte.asString());
                    handlers.add(new ResultSetHandler() {

                        @Override
                        public Object handle(ResultSet rs) throws SQLException {
                            while (rs.next()) {
                                final Long id = rs.getLong(1);
                                final String numero = rs.getString(2);
                                final String nom = rs.getString(3);
                                mapComptes.put(id, new Compte(id, numero, nom));

                            }
                            return null;
                        }
                    });
                }
                if (!journauxAresoudre.isEmpty()) {
                    if (LOGGER.isLoggable(Level.FINE)) {
                        LOGGER.fine(journauxAresoudre.size() + " journaux à résoudre ids : " + journauxAresoudre);
                    }
                    final SQLTable tableJournal = root.getTable("JOURNAL");
                    final SQLSelect selJournal = new SQLSelect();
                    selJournal.addSelect(tableJournal.getKey());
                    selJournal.addSelect(tableJournal.getField("CODE"));
                    selJournal.addSelect(tableJournal.getField("NOM"));
                    selJournal.setWhere(new Where(tableJournal.getKey(), journauxAresoudre));
                    queries.add(selJournal.asString());
                    handlers.add(new ResultSetHandler() {

                        @Override
                        public Object handle(ResultSet rs) throws SQLException {
                            while (rs.next()) {
                                final Long id = rs.getLong(1);
                                final String code = rs.getString(2);
                                final String nom = rs.getString(3);
                                mapJournaux.put(id, new Journal(id, code, nom));

                            }
                            return null;
                        }
                    });

                }

                SQLUtils.executeMultiple(sysRoot, queries, handlers);
                for (Piece p : pieces) {
                    for (Mouvement m : p.getMouvements()) {
                        for (Ecriture e : m.getEcritures()) {
                            Number compteID = e.getCompteID();
                            if (compteID == null) {
                                throw new IllegalStateException("pas d'ID compte dans l'écriture " + e);
                            }
                            final Long idCompte = compteID.longValue();
                            final Compte c = mapComptes.get(idCompte);
                            if (c == null) {
                                throw new IllegalStateException("pas de compte d'ID " + idCompte + " dans la map " + mapComptes.keySet());
                            }
                            e.setCompte(c.getNumero(), c.getNom());

                            final Long idJournal = e.getJournalID().longValue();
                            final Journal j = mapJournaux.get(idJournal);
                            if (j == null) {
                                throw new IllegalStateException("pas de journal d'ID " + idJournal + " dans la map " + mapJournaux.keySet());
                            }
                            e.setJournal(j.getCode(), j.getNom());

                        }
                    }
                }

                // Insertion des pieces
                final List<Number> idsPieces = SQLInsert.executeSimilarInserts(sysRoot, insertsPiece, true);

                // Creation des inserts des mouvements
                final List<SQLInsert> insertsMouvement = new ArrayList<>(insertsPiece.size() * 3);
                final List<Mouvement> listMvtWithoutIDs = new ArrayList<>(insertsPiece.size() * 3);
                for (int i = 0; i < pieces.size(); i++) {
                    Piece piece = pieces.get(i);
                    piece.setId(idsPieces.get(i));
                    for (Mouvement m : piece.getMouvements()) {
                        listMvtWithoutIDs.add(m);
                        insertsMouvement.add(m.createInsert(root));
                    }
                }

                // Insertion des mouvements
                final List<Number> idsMouvements = SQLInsert.executeSimilarInserts(sysRoot, insertsMouvement, true);

                // Mise à jour des numeros de mouvements
                SQLSelect sel = new SQLSelect();
                final SQLTable tableMvt = root.getTable("MOUVEMENT");
                sel.addSelect(tableMvt.getField("NUMERO"), "MAX");
                Number maxMvtNumber = (Number) sysRoot.getDataSource().executeScalar(sel.asString());
                int maxMvt = 1;
                if (maxMvtNumber != null) {
                    maxMvt = maxMvtNumber.intValue();
                }
                List<SQLUpdate> mvtUpdate = new ArrayList<>();
                for (int i = 0; i < idsMouvements.size(); i++) {
                    Number mvtId = idsMouvements.get(i);
                    SQLUpdate update = new SQLUpdate(new Where(tableMvt.getKey(), "=", mvtId));
                    update.add(tableMvt.getField("NUMERO"), maxMvt);
                    mvtUpdate.add(update);
                    maxMvt++;
                    listMvtWithoutIDs.get(i).setId(mvtId);
                }

                SQLUpdate.executeMultipleWithBatch(sysRoot, mvtUpdate);

                // Creation des inserts des écritures sans analytique
                final List<SQLInsert> insertsEcrituresSansAnalytique = new ArrayList<>(insertsMouvement.size() * 2);
                final List<SQLInsert> insertsEcrituresAvecAnalytique = new ArrayList<>(insertsMouvement.size() * 2);
                final List<Ecriture> ecrituresAvecAnalytique = new ArrayList<>(insertsEcrituresAvecAnalytique.size());
                for (Piece p : pieces) {
                    final List<Mouvement> mouvements = p.getMouvements();
                    final int stop = mouvements.size();
                    for (int i = 0; i < stop; i++) {
                        final Mouvement m = mouvements.get(i);
                        for (Ecriture e : m.getEcritures()) {
                            if (e.hasAnalytique()) {
                                insertsEcrituresAvecAnalytique.add(e.createInsert(root, user));
                                ecrituresAvecAnalytique.add(e);
                            } else {
                                insertsEcrituresSansAnalytique.add(e.createInsert(root, user));
                            }
                        }

                    }
                }
                // Insertions des écritures des mouvements
                SQLInsert.executeSimilarInserts(sysRoot, insertsEcrituresSansAnalytique, false);
                // Insertions des écritures avec analytique
                if (!ecrituresAvecAnalytique.isEmpty()) {
                    final List<Number> idsEcritues = SQLInsert.executeSimilarInserts(sysRoot, insertsEcrituresAvecAnalytique, true);
                    // Analytique
                    final List<SQLInsert> insertsAssociationAnalytique = new ArrayList<>(insertsEcrituresAvecAnalytique.size());
                    final int size = ecrituresAvecAnalytique.size();
                    for (int i = 0; i < size; i++) {
                        final Ecriture e = ecrituresAvecAnalytique.get(i);
                        e.setId(idsEcritues.get(i));
                        for (AssociationAnalytique a : e.getAssociationsAnalytiques()) {
                            insertsAssociationAnalytique.add(a.createInsert(root, user));
                        }
                    }
                    SQLInsert.executeSimilarInserts(sysRoot, insertsAssociationAnalytique, false);
                }
                for (Piece p : pieces) {
                    final List<Mouvement> mouvements = p.getMouvements();
                    for (Mouvement m : mouvements) {
                        if (m.getPostInsertionAction() != null) {
                            m.getPostInsertionAction().afterInsert(m);
                        }
                    }

                }

                return null;
            }
        });

    }

    /**
     * Map des numero de compte en minuscule <-> Compte
     * 
     * @param pieces pièces servant à déterminer sur quels numéros de compte on limite la requête
     */
    protected Map<String, Compte> getNumeroDesComptes(DBRoot root, List<Piece> pieces) {
        // tous les comptes dont le numero
        // est parmis celles des écritures des pièces
        final Set<String> numerosDesComptes = new HashSet<>();
        for (Piece p : pieces) {
            final List<Mouvement> mouvements = p.getMouvements();
            for (Mouvement m : mouvements) {
                for (Ecriture e : m.getEcritures()) {
                    numerosDesComptes.add(e.getCompteNumero().toLowerCase());
                }
            }
        }

        final Map<String, Compte> result = new HashMap<>();
        final SQLTable tableCompte = root.getTable("COMPTE_PCE");
        final SQLSelect selCompte = new SQLSelect();
        selCompte.addSelect(tableCompte.getKey());
        final SQLField fNumero = tableCompte.getField("NUMERO");
        selCompte.addSelect(fNumero);
        selCompte.addSelect(tableCompte.getField("NOM"));
        String numeros = CollectionUtils.join(numerosDesComptes, ",", new ITransformer<Object, String>() {
            @Override
            public String transformChecked(final Object input) {
                return fNumero.getField().getType().toString(input);
            }
        });
        final Where w = Where.createRaw("lower(" + fNumero.getFieldRef() + ") in (" + numeros + ")", fNumero);
        selCompte.setWhere(w);

        final ResultSetHandler resultSetHandler = new ResultSetHandler() {

            @Override
            public Object handle(ResultSet rs) throws SQLException {
                while (rs.next()) {
                    final Long id = rs.getLong(1);
                    final String numero = rs.getString(2);
                    final String nom = rs.getString(3);
                    result.put(numero.toLowerCase(), new Compte(id, numero, nom));
                }
                return null;
            }
        };

        root.getDBSystemRoot().getDataSource().execute(selCompte.asString(), resultSetHandler);
        return result;
    }

    /**
     * Map des codes en minuscule <-> Journal
     */
    protected Map<String, Journal> getCodesJournaux(DBRoot root) {
        final Map<String, Journal> result = new HashMap<>();
        final SQLTable tableJournal = root.getTable("JOURNAL");
        final SQLSelect selJournal = new SQLSelect();
        selJournal.addSelect(tableJournal.getKey());
        selJournal.addSelect(tableJournal.getField("CODE"));
        selJournal.addSelect(tableJournal.getField("NOM"));

        final ResultSetHandler resultSetHandler = new ResultSetHandler() {

            @Override
            public Object handle(ResultSet rs) throws SQLException {
                while (rs.next()) {
                    final Long id = rs.getLong(1);
                    final String code = rs.getString(2);
                    final String nom = rs.getString(3);
                    result.put(code.toLowerCase(), new Journal(id, code, nom));
                }
                return null;
            }
        };

        root.getDBSystemRoot().getDataSource().execute(selJournal.asString(), resultSetHandler);
        return result;
    }

}