OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 174 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
174 ilm 1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
5
 *
6
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
7
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
8
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
9
 * language governing permissions and limitations under the License.
10
 *
11
 * When distributing the software, include this License Header Notice in each file.
12
 */
13
 
14
 package org.openconcerto.erp.generationEcritures;
15
 
16
import org.openconcerto.sql.element.SQLElementDirectory;
17
import org.openconcerto.sql.model.ConnectionHandlerNoSetup;
18
import org.openconcerto.sql.model.DBRoot;
19
import org.openconcerto.sql.model.DBSystemRoot;
20
import org.openconcerto.sql.model.SQLDataSource;
21
import org.openconcerto.sql.model.SQLField;
22
import org.openconcerto.sql.model.SQLInsert;
23
import org.openconcerto.sql.model.SQLSelect;
24
import org.openconcerto.sql.model.SQLTable;
25
import org.openconcerto.sql.model.SQLUpdate;
26
import org.openconcerto.sql.model.Where;
27
import org.openconcerto.sql.users.User;
28
import org.openconcerto.sql.utils.SQLUtils;
29
import org.openconcerto.utils.CollectionUtils;
30
import org.openconcerto.utils.cc.ITransformer;
31
 
32
import java.sql.ResultSet;
33
import java.sql.SQLException;
34
import java.util.ArrayList;
35
import java.util.Date;
36
import java.util.HashMap;
37
import java.util.HashSet;
38
import java.util.Iterator;
39
import java.util.LinkedList;
40
import java.util.List;
41
import java.util.Map;
42
import java.util.Set;
43
import java.util.logging.Level;
44
import java.util.logging.Logger;
45
 
46
import org.apache.commons.dbutils.ResultSetHandler;
47
 
48
public class Exercice {
49
    private Date debut;
50
    private Date fin;
51
    private static final Logger LOGGER = Logger.getLogger(Exercice.class.getName());
52
 
53
    public Exercice() {
54
 
55
    }
56
 
57
    public Exercice(Date debut, Date fin) {
58
        if (debut != null && debut.after(fin)) {
59
            throw new IllegalArgumentException("date de fin invalide");
60
        }
61
        this.debut = debut;
62
        this.fin = fin;
63
    }
64
 
65
    public String toString() {
66
        return "Exercice " + this.debut + " -> " + this.fin;
67
    }
68
 
69
    public void insert(SQLElementDirectory directory, final DBRoot root, User user, List<Piece> pieces) throws SQLException {
70
        final DBSystemRoot sysRoot = root.getDBSystemRoot();
71
        final List<SQLInsert> insertsPiece = new ArrayList<>();
72
        LOGGER.log(Level.INFO, "insertion de {0} pièces comptables", pieces.size());
73
        for (Piece p : pieces) {
74
            // Pièces
177 ilm 75
            insertsPiece.add(p.createInsert(root, user));
174 ilm 76
 
77
            // Vérification des mouvements
78
            final List<Mouvement> mouvements = p.getMouvements();
79
            if (mouvements.isEmpty()) {
80
                throw new IllegalStateException("Piece vide : " + p);
81
            }
82
 
83
            for (Mouvement m : mouvements) {
84
                if (!m.isBalanced()) {
85
                    throw new IllegalStateException("Mouvement non balancé : " + m);
86
                }
87
                if (m.isEmpty()) {
88
                    throw new IllegalStateException("Mouvement vide : " + m);
89
                }
90
 
91
                for (Ecriture e : m.getEcritures()) {
92
                    if (this.debut != null && e.getDate().before(this.debut)) {
93
                        throw new IllegalStateException("Mouvement invalide : " + m + " : une écriture est définie avant la date de début d'exercice : " + e);
94
                    }
95
                    if (e.getNom() == null) {
96
                        throw new IllegalStateException("Ecriture sans nom : " + e);
97
                    }
98
                }
99
 
100
                if (this.fin != null) {
101
                    for (Ecriture e : m.getEcritures()) {
102
                        if (e.getDate().after(this.fin)) {
103
                            throw new IllegalStateException("Mouvement invalide : " + m + " : une écriture est définie après la date de fin d'exercice : " + e.getDate() + ">" + this.fin);
104
                        }
105
 
106
                    }
107
                }
108
 
109
            }
110
        }
111
 
112
        SQLUtils.executeAtomic(sysRoot.getDataSource(), new ConnectionHandlerNoSetup<Object, SQLException>() {
113
            @Override
114
            public Object handle(SQLDataSource ds) throws SQLException {
115
                // Insertion des journaux et comptes manquants et remplissage des champs journaux et
116
                // comptes des
117
                // écritures depuis les ids
118
                final Set<Journal> journauxACreerOuFetcher = new HashSet<>();
119
                final Set<Number> journauxAresoudre = new HashSet<>();
120
                final Set<Compte> comptesACreerOuFetcher = new HashSet<>();
121
                final Set<Number> comptesAresoudre = new HashSet<>();
122
                final List<Ecriture> ecrituresSansJournalID = new LinkedList<>();
123
                final List<Ecriture> ecrituresSansCompteID = new LinkedList<>();
124
                for (Piece p : pieces) {
125
                    for (Mouvement m : p.getMouvements()) {
126
                        for (Ecriture e : m.getEcritures()) {
127
                            if (e.getJournalID() == null) {
128
                                // Journal à creer
129
                                journauxACreerOuFetcher.add(new Journal(null, e.getJournalCode(), e.getJournalNom()));
130
                                ecrituresSansJournalID.add(e);
131
                            } else {
132
                                journauxAresoudre.add(e.getJournalID());
133
                            }
134
                            if (e.getCompteID() == null) {
135
                                // Compte à creer
136
                                comptesACreerOuFetcher.add(new Compte(null, e.getCompteNumero(), e.getCompteNom()));
137
                                ecrituresSansCompteID.add(e);
138
                            } else {
139
                                comptesAresoudre.add(e.getJournalID());
140
                            }
141
                        }
142
 
143
                    }
144
                }
145
                final Map<Long, Journal> mapJournaux = new HashMap<>();
146
                if (!journauxACreerOuFetcher.isEmpty()) {
147
                    if (LOGGER.isLoggable(Level.FINE)) {
148
                        LOGGER.fine(journauxACreerOuFetcher.size() + " journaux à créer : " + journauxACreerOuFetcher);
149
                    }
150
                    // On récupère tous les journaux car il y en a peu
151
                    final Map<String, Journal> codesDesJournauxExistants = getCodesJournaux(root);
152
 
153
                    final List<SQLInsert> insertsJournaux = new ArrayList<>();
154
                    final List<Journal> list = new ArrayList<>();
155
                    for (Journal journal : journauxACreerOuFetcher) {
156
                        // journal non déjà existant
157
                        if (codesDesJournauxExistants.get(journal.getCode().toLowerCase()) == null) {
158
                            list.add(journal);
177 ilm 159
                            insertsJournaux.add(journal.createInsert(root, user));
174 ilm 160
                        }
161
                    }
162
                    final List<Number> journauxIds = new ArrayList<>();
163
                    if (!insertsJournaux.isEmpty()) {
164
                        journauxIds.addAll(SQLInsert.executeSimilarInserts(sysRoot, insertsJournaux, true));
165
                        journauxAresoudre.addAll(journauxIds);
166
                    }
167
                    // Mise à jour de l'ID du journal pour les écritures dont le journal vient
168
                    // d'être créé
169
                    final int size = list.size();
170
                    for (int i = 0; i < size; i++) {
171
                        final String journalCode = list.get(i).getCode();
172
                        final Number journalID = journauxIds.get(i);
173
                        final Iterator<Ecriture> it = ecrituresSansJournalID.iterator();
174
                        while (it.hasNext()) {
175
                            final Ecriture e = it.next();
176
 
177
                            if (e.getJournalCode().equalsIgnoreCase(journalCode)) {
178
                                e.setJournalID(journalID);
179
                                it.remove();
180
                            }
181
 
182
                        }
183
                    }
184
                    final Iterator<Ecriture> it = ecrituresSansJournalID.iterator();
185
                    while (it.hasNext()) {
186
                        final Ecriture e = it.next();
187
                        final Journal journal = codesDesJournauxExistants.get(e.getJournalCode().toLowerCase());
188
                        e.setJournalID(journal.getId());
189
                        it.remove();
190
                    }
191
                    for (Journal journal : codesDesJournauxExistants.values()) {
192
                        mapJournaux.put(journal.getId(), journal);
193
                    }
194
                }
195
                final Map<Long, Compte> mapComptes = new HashMap<>();
196
                if (!comptesACreerOuFetcher.isEmpty()) {
197
                    if (LOGGER.isLoggable(Level.FINE)) {
198
                        LOGGER.fine(comptesACreerOuFetcher.size() + " comptes à créer ou fetcher: " + comptesACreerOuFetcher);
199
                    }
200
                    final Map<String, Compte> numerosDesComptesExistants = getNumeroDesComptes(root, pieces);
201
 
202
                    final List<SQLInsert> insertsComptes = new ArrayList<>();
203
                    final List<Compte> list = new ArrayList<>();
204
                    for (Compte c : comptesACreerOuFetcher) {
205
                        if (numerosDesComptesExistants.get(c.getNumero().toLowerCase()) == null) {
206
                            if (LOGGER.isLoggable(Level.FINE)) {
207
                                LOGGER.fine("création du compte : " + c.getNumero().toLowerCase());
208
                            }
209
                            list.add(c);
177 ilm 210
                            insertsComptes.add(c.createInsert(root, user));
174 ilm 211
                        }
212
                    }
213
                    List<Number> comptesIds = new ArrayList<>();
214
                    if (!insertsComptes.isEmpty()) {
215
 
216
                        final List<Number> insertedIDs = SQLInsert.executeSimilarInserts(sysRoot, insertsComptes, true);
217
                        comptesIds.addAll(insertedIDs);
218
                        if (LOGGER.isLoggable(Level.FINE)) {
219
                            LOGGER.fine("IDs des comptes créés : " + comptesIds);
220
                        }
221
 
222
                        comptesAresoudre.addAll(comptesIds);
223
                    }
224
 
225
                    // Mise à jour de l'ID du compte pour les écritures dont le compte vient
226
                    // d'être créé
227
                    final int size = list.size();
228
                    for (int i = 0; i < size; i++) {
229
                        final String compteCode = list.get(i).getNumero();
230
                        final Number compteID = comptesIds.get(i);
231
                        final Iterator<Ecriture> it = ecrituresSansCompteID.iterator();
232
                        while (it.hasNext()) {
233
                            final Ecriture e = it.next();
234
                            if (e.getCompteNumero().equalsIgnoreCase(compteCode)) {
235
                                if (LOGGER.isLoggable(Level.FINEST)) {
236
                                    LOGGER.finest("mise à jour de l'écriture " + e + " avec le compte d'ID " + compteID);
237
                                }
238
                                e.setCompteID(compteID);
239
                                it.remove();
240
                            }
241
                        }
242
 
243
                    }
244
                    final Iterator<Ecriture> it = ecrituresSansCompteID.iterator();
245
                    while (it.hasNext()) {
246
                        final Ecriture e = it.next();
247
                        Compte compte = numerosDesComptesExistants.get(e.getCompteNumero().toLowerCase());
248
                        if (compte != null) {
249
                            e.setCompteID(compte.getId());
250
                            it.remove();
251
                        }
252
 
253
                    }
254
                    for (Compte compte : numerosDesComptesExistants.values()) {
255
                        mapComptes.put(compte.getId(), compte);
256
                    }
257
 
258
                }
259
 
260
                // fetch journaux et comptes, update des code/numéro et nom
261
                final List<String> queries = new ArrayList<>();
262
                final List<ResultSetHandler> handlers = new ArrayList<>();
263
 
264
                if (!comptesAresoudre.isEmpty()) {
265
                    if (LOGGER.isLoggable(Level.FINE)) {
266
                        LOGGER.fine(comptesAresoudre.size() + " comptes à résoudre : ids " + comptesAresoudre);
267
                    }
268
                    final SQLTable tableCompte = root.getTable("COMPTE_PCE");
269
                    final SQLSelect selCompte = new SQLSelect();
270
                    selCompte.addSelect(tableCompte.getKey());
271
                    selCompte.addSelect(tableCompte.getField("NUMERO"));
272
                    selCompte.addSelect(tableCompte.getField("NOM"));
273
                    selCompte.setWhere(new Where(tableCompte.getKey(), comptesAresoudre));
274
                    queries.add(selCompte.asString());
275
                    handlers.add(new ResultSetHandler() {
276
 
277
                        @Override
278
                        public Object handle(ResultSet rs) throws SQLException {
279
                            while (rs.next()) {
280
                                final Long id = rs.getLong(1);
281
                                final String numero = rs.getString(2);
282
                                final String nom = rs.getString(3);
283
                                mapComptes.put(id, new Compte(id, numero, nom));
284
 
285
                            }
286
                            return null;
287
                        }
288
                    });
289
                }
290
                if (!journauxAresoudre.isEmpty()) {
291
                    if (LOGGER.isLoggable(Level.FINE)) {
292
                        LOGGER.fine(journauxAresoudre.size() + " journaux à résoudre ids : " + journauxAresoudre);
293
                    }
294
                    final SQLTable tableJournal = root.getTable("JOURNAL");
295
                    final SQLSelect selJournal = new SQLSelect();
296
                    selJournal.addSelect(tableJournal.getKey());
297
                    selJournal.addSelect(tableJournal.getField("CODE"));
298
                    selJournal.addSelect(tableJournal.getField("NOM"));
299
                    selJournal.setWhere(new Where(tableJournal.getKey(), journauxAresoudre));
300
                    queries.add(selJournal.asString());
301
                    handlers.add(new ResultSetHandler() {
302
 
303
                        @Override
304
                        public Object handle(ResultSet rs) throws SQLException {
305
                            while (rs.next()) {
306
                                final Long id = rs.getLong(1);
307
                                final String code = rs.getString(2);
308
                                final String nom = rs.getString(3);
309
                                mapJournaux.put(id, new Journal(id, code, nom));
310
 
311
                            }
312
                            return null;
313
                        }
314
                    });
315
 
316
                }
317
 
318
                SQLUtils.executeMultiple(sysRoot, queries, handlers);
319
                for (Piece p : pieces) {
320
                    for (Mouvement m : p.getMouvements()) {
321
                        for (Ecriture e : m.getEcritures()) {
322
                            Number compteID = e.getCompteID();
323
                            if (compteID == null) {
324
                                throw new IllegalStateException("pas d'ID compte dans l'écriture " + e);
325
                            }
326
                            final Long idCompte = compteID.longValue();
327
                            final Compte c = mapComptes.get(idCompte);
328
                            if (c == null) {
329
                                throw new IllegalStateException("pas de compte d'ID " + idCompte + " dans la map " + mapComptes.keySet());
330
                            }
331
                            e.setCompte(c.getNumero(), c.getNom());
332
 
333
                            final Long idJournal = e.getJournalID().longValue();
334
                            final Journal j = mapJournaux.get(idJournal);
335
                            if (j == null) {
336
                                throw new IllegalStateException("pas de journal d'ID " + idJournal + " dans la map " + mapJournaux.keySet());
337
                            }
338
                            e.setJournal(j.getCode(), j.getNom());
339
 
340
                        }
341
                    }
342
                }
343
 
344
                // Insertion des pieces
345
                final List<Number> idsPieces = SQLInsert.executeSimilarInserts(sysRoot, insertsPiece, true);
346
 
347
                // Creation des inserts des mouvements
348
                final List<SQLInsert> insertsMouvement = new ArrayList<>(insertsPiece.size() * 3);
349
                final List<Mouvement> listMvtWithoutIDs = new ArrayList<>(insertsPiece.size() * 3);
350
                for (int i = 0; i < pieces.size(); i++) {
351
                    Piece piece = pieces.get(i);
352
                    piece.setId(idsPieces.get(i));
353
                    for (Mouvement m : piece.getMouvements()) {
354
                        listMvtWithoutIDs.add(m);
177 ilm 355
                        insertsMouvement.add(m.createInsert(root, user));
174 ilm 356
                    }
357
                }
358
 
359
                // Insertion des mouvements
360
                final List<Number> idsMouvements = SQLInsert.executeSimilarInserts(sysRoot, insertsMouvement, true);
361
 
362
                // Mise à jour des numeros de mouvements
363
                SQLSelect sel = new SQLSelect();
364
                final SQLTable tableMvt = root.getTable("MOUVEMENT");
365
                sel.addSelect(tableMvt.getField("NUMERO"), "MAX");
366
                Number maxMvtNumber = (Number) sysRoot.getDataSource().executeScalar(sel.asString());
367
                int maxMvt = 1;
368
                if (maxMvtNumber != null) {
369
                    maxMvt = maxMvtNumber.intValue();
370
                }
371
                List<SQLUpdate> mvtUpdate = new ArrayList<>();
372
                for (int i = 0; i < idsMouvements.size(); i++) {
177 ilm 373
                    maxMvt++;
174 ilm 374
                    Number mvtId = idsMouvements.get(i);
375
                    SQLUpdate update = new SQLUpdate(new Where(tableMvt.getKey(), "=", mvtId));
376
                    update.add(tableMvt.getField("NUMERO"), maxMvt);
377
                    mvtUpdate.add(update);
378
                    listMvtWithoutIDs.get(i).setId(mvtId);
379
                }
380
 
381
                SQLUpdate.executeMultipleWithBatch(sysRoot, mvtUpdate);
382
 
383
                // Creation des inserts des écritures sans analytique
384
                final List<SQLInsert> insertsEcrituresSansAnalytique = new ArrayList<>(insertsMouvement.size() * 2);
385
                final List<SQLInsert> insertsEcrituresAvecAnalytique = new ArrayList<>(insertsMouvement.size() * 2);
386
                final List<Ecriture> ecrituresAvecAnalytique = new ArrayList<>(insertsEcrituresAvecAnalytique.size());
387
                for (Piece p : pieces) {
388
                    final List<Mouvement> mouvements = p.getMouvements();
389
                    final int stop = mouvements.size();
390
                    for (int i = 0; i < stop; i++) {
391
                        final Mouvement m = mouvements.get(i);
392
                        for (Ecriture e : m.getEcritures()) {
393
                            if (e.hasAnalytique()) {
394
                                insertsEcrituresAvecAnalytique.add(e.createInsert(root, user));
395
                                ecrituresAvecAnalytique.add(e);
396
                            } else {
397
                                insertsEcrituresSansAnalytique.add(e.createInsert(root, user));
398
                            }
399
                        }
400
 
401
                    }
402
                }
403
                // Insertions des écritures des mouvements
404
                SQLInsert.executeSimilarInserts(sysRoot, insertsEcrituresSansAnalytique, false);
405
                // Insertions des écritures avec analytique
406
                if (!ecrituresAvecAnalytique.isEmpty()) {
407
                    final List<Number> idsEcritues = SQLInsert.executeSimilarInserts(sysRoot, insertsEcrituresAvecAnalytique, true);
408
                    // Analytique
409
                    final List<SQLInsert> insertsAssociationAnalytique = new ArrayList<>(insertsEcrituresAvecAnalytique.size());
410
                    final int size = ecrituresAvecAnalytique.size();
411
                    for (int i = 0; i < size; i++) {
412
                        final Ecriture e = ecrituresAvecAnalytique.get(i);
413
                        e.setId(idsEcritues.get(i));
414
                        for (AssociationAnalytique a : e.getAssociationsAnalytiques()) {
415
                            insertsAssociationAnalytique.add(a.createInsert(root, user));
416
                        }
417
                    }
418
                    SQLInsert.executeSimilarInserts(sysRoot, insertsAssociationAnalytique, false);
419
                }
420
                for (Piece p : pieces) {
421
                    final List<Mouvement> mouvements = p.getMouvements();
422
                    for (Mouvement m : mouvements) {
423
                        if (m.getPostInsertionAction() != null) {
424
                            m.getPostInsertionAction().afterInsert(m);
425
                        }
426
                    }
427
 
428
                }
429
 
430
                return null;
431
            }
432
        });
433
 
434
    }
435
 
436
    /**
437
     * Map des numero de compte en minuscule <-> Compte
438
     *
439
     * @param pieces pièces servant à déterminer sur quels numéros de compte on limite la requête
440
     */
441
    protected Map<String, Compte> getNumeroDesComptes(DBRoot root, List<Piece> pieces) {
442
        // tous les comptes dont le numero
443
        // est parmis celles des écritures des pièces
444
        final Set<String> numerosDesComptes = new HashSet<>();
445
        for (Piece p : pieces) {
446
            final List<Mouvement> mouvements = p.getMouvements();
447
            for (Mouvement m : mouvements) {
448
                for (Ecriture e : m.getEcritures()) {
449
                    numerosDesComptes.add(e.getCompteNumero().toLowerCase());
450
                }
451
            }
452
        }
453
 
454
        final Map<String, Compte> result = new HashMap<>();
455
        final SQLTable tableCompte = root.getTable("COMPTE_PCE");
456
        final SQLSelect selCompte = new SQLSelect();
457
        selCompte.addSelect(tableCompte.getKey());
458
        final SQLField fNumero = tableCompte.getField("NUMERO");
459
        selCompte.addSelect(fNumero);
460
        selCompte.addSelect(tableCompte.getField("NOM"));
461
        String numeros = CollectionUtils.join(numerosDesComptes, ",", new ITransformer<Object, String>() {
462
            @Override
463
            public String transformChecked(final Object input) {
464
                return fNumero.getField().getType().toString(input);
465
            }
466
        });
467
        final Where w = Where.createRaw("lower(" + fNumero.getFieldRef() + ") in (" + numeros + ")", fNumero);
468
        selCompte.setWhere(w);
469
 
470
        final ResultSetHandler resultSetHandler = new ResultSetHandler() {
471
 
472
            @Override
473
            public Object handle(ResultSet rs) throws SQLException {
474
                while (rs.next()) {
475
                    final Long id = rs.getLong(1);
476
                    final String numero = rs.getString(2);
477
                    final String nom = rs.getString(3);
478
                    result.put(numero.toLowerCase(), new Compte(id, numero, nom));
479
                }
480
                return null;
481
            }
482
        };
483
 
484
        root.getDBSystemRoot().getDataSource().execute(selCompte.asString(), resultSetHandler);
485
        return result;
486
    }
487
 
488
    /**
489
     * Map des codes en minuscule <-> Journal
490
     */
491
    protected Map<String, Journal> getCodesJournaux(DBRoot root) {
492
        final Map<String, Journal> result = new HashMap<>();
493
        final SQLTable tableJournal = root.getTable("JOURNAL");
494
        final SQLSelect selJournal = new SQLSelect();
495
        selJournal.addSelect(tableJournal.getKey());
496
        selJournal.addSelect(tableJournal.getField("CODE"));
497
        selJournal.addSelect(tableJournal.getField("NOM"));
498
 
499
        final ResultSetHandler resultSetHandler = new ResultSetHandler() {
500
 
501
            @Override
502
            public Object handle(ResultSet rs) throws SQLException {
503
                while (rs.next()) {
504
                    final Long id = rs.getLong(1);
505
                    final String code = rs.getString(2);
506
                    final String nom = rs.getString(3);
507
                    result.put(code.toLowerCase(), new Journal(id, code, nom));
508
                }
509
                return null;
510
            }
511
        };
512
 
513
        root.getDBSystemRoot().getDataSource().execute(selJournal.asString(), resultSetHandler);
514
        return result;
515
    }
516
 
517
}