Dépôt officiel du code source de l'ERP OpenConcerto
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;
}
}