OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

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

Rev Author Line No. Line
18 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.core.sales.pos.model;
15
 
67 ilm 16
import org.openconcerto.erp.config.ComptaPropsConfiguration;
17
import org.openconcerto.erp.core.common.ui.TotalCalculator;
144 ilm 18
import org.openconcerto.erp.core.customerrelationship.customer.element.CompteClientTransactionSQLELement;
142 ilm 19
import org.openconcerto.erp.core.finance.accounting.element.ComptePCESQLElement;
20
import org.openconcerto.erp.core.finance.accounting.element.JournalSQLElement;
67 ilm 21
import org.openconcerto.erp.core.finance.tax.model.TaxeCache;
132 ilm 22
import org.openconcerto.erp.core.sales.pos.POSConfiguration;
18 ilm 23
import org.openconcerto.erp.core.sales.pos.io.DefaultTicketPrinter;
149 ilm 24
import org.openconcerto.erp.core.sales.pos.io.Printable;
18 ilm 25
import org.openconcerto.erp.core.sales.pos.io.TicketPrinter;
144 ilm 26
import org.openconcerto.erp.core.sales.pos.model.RegisterFiles.HashMode;
18 ilm 27
import org.openconcerto.erp.core.sales.pos.ui.TicketCellRenderer;
142 ilm 28
import org.openconcerto.erp.generationEcritures.GenerationEcritures;
29
import org.openconcerto.erp.generationEcritures.GenerationMvtVirement;
73 ilm 30
import org.openconcerto.erp.preferences.DefaultNXProps;
61 ilm 31
import org.openconcerto.sql.Configuration;
144 ilm 32
import org.openconcerto.sql.element.SQLElementDirectory;
142 ilm 33
import org.openconcerto.sql.model.SQLRow;
34
import org.openconcerto.sql.model.SQLRowAccessor;
67 ilm 35
import org.openconcerto.sql.model.SQLRowValues;
61 ilm 36
import org.openconcerto.sql.model.SQLTable;
142 ilm 37
import org.openconcerto.sql.utils.SQLUtils;
90 ilm 38
import org.openconcerto.utils.DecimalUtils;
18 ilm 39
import org.openconcerto.utils.Pair;
142 ilm 40
import org.openconcerto.utils.Tuple2;
144 ilm 41
import org.openconcerto.utils.XMLDateFormat;
18 ilm 42
 
43
import java.io.File;
44
import java.io.IOException;
67 ilm 45
import java.math.BigDecimal;
46
import java.math.RoundingMode;
144 ilm 47
import java.nio.file.Files;
48
import java.nio.file.Path;
142 ilm 49
import java.sql.SQLException;
18 ilm 50
import java.text.SimpleDateFormat;
51
import java.util.ArrayList;
52
import java.util.Calendar;
149 ilm 53
import java.util.Collections;
54
import java.util.Comparator;
18 ilm 55
import java.util.Date;
56
import java.util.List;
61 ilm 57
import java.util.Locale;
142 ilm 58
import java.util.Map;
18 ilm 59
 
132 ilm 60
import org.jdom2.Attribute;
61
import org.jdom2.Document;
62
import org.jdom2.Element;
18 ilm 63
 
149 ilm 64
public class Ticket implements Printable {
80 ilm 65
 
144 ilm 66
    private static final XMLDateFormat DATE_FMT = new XMLDateFormat();
67
 
18 ilm 68
    // Propre a ticket
144 ilm 69
    private final List<Paiement> paiements = new ArrayList<Paiement>();
18 ilm 70
    private final List<Pair<Article, Integer>> items = new ArrayList<Pair<Article, Integer>>();
80 ilm 71
    private Calendar creationCal;
142 ilm 72
    private Client client = Client.NONE;
144 ilm 73
    private final int number;
74
    private final String previousHash;
151 ilm 75
    private boolean additionnalCopyRequested = false;
18 ilm 76
 
77
    // Propre à la caisse
80 ilm 78
    private final int caisseNumber;
18 ilm 79
 
61 ilm 80
    private static final SQLTable tableArticle = Configuration.getInstance().getRoot().findTable("ARTICLE");
81
 
144 ilm 82
    public static Ticket getTicketFromCode(final String code, final RegisterFiles registerFiles) {
80 ilm 83
        try {
144 ilm 84
            final ReceiptCode receiptCode = new ReceiptCode(code);
85
            final Path receiptFile = registerFiles.getReceiptFile(receiptCode);
86
            if (receiptFile != null && Files.exists(receiptFile)) {
87
                return parseFile(receiptFile.toFile());
88
            } else {
89
                // old location
90
                return parseFile(receiptCode.getFile());
91
            }
92
        } catch (final Exception e) {
80 ilm 93
            return null;
18 ilm 94
        }
80 ilm 95
    }
18 ilm 96
 
144 ilm 97
    public void setClient(final Client client) {
142 ilm 98
        this.client = client;
99
    }
100
 
101
    public Client getClient() {
144 ilm 102
        return this.client;
142 ilm 103
    }
104
 
80 ilm 105
    public static Ticket parseFile(final File file) {
144 ilm 106
        return parseFile(file, HashMode.REQUIRED);
107
    }
108
 
109
    public static Ticket parseFile(final File file, final HashMode hashMode) {
80 ilm 110
        if (!file.exists()) {
111
            return null;
112
        }
113
 
18 ilm 114
        try {
115
            // XML Reading
116
 
144 ilm 117
            final Document document = RegisterFiles.parse(file.toPath(), hashMode);
18 ilm 118
            final Element root = document.getRootElement();
80 ilm 119
            final ReceiptCode receiptCode = new ReceiptCode(root.getAttributeValue("code"));
144 ilm 120
            final String creationDateAttr = root.getAttributeValue("creationDate");
121
            final Calendar c = (Calendar) receiptCode.getDay().clone();
122
            if (creationDateAttr == null) {
123
                final String h = root.getAttributeValue("hour");
124
                final String m = root.getAttributeValue("minute");
125
                c.set(Calendar.HOUR_OF_DAY, Integer.parseInt(h));
126
                c.set(Calendar.MINUTE, Integer.parseInt(m));
127
            } else {
128
                c.setTime((Date) DATE_FMT.parseObject(creationDateAttr));
129
            }
142 ilm 130
            final String client = root.getAttributeValue("clientID", "1");
144 ilm 131
            final Ticket t = new Ticket(receiptCode, c, root.getAttributeValue("previousHash"));
142 ilm 132
            t.setClient(new Client(Integer.parseInt(client), "", BigDecimal.ZERO));
80 ilm 133
 
18 ilm 134
            // article
80 ilm 135
            final List<Element> children = root.getChildren("article");
144 ilm 136
            for (final Element element : children) {
137
                final int qte = Integer.parseInt(element.getAttributeValue("qte"));
138
                final BigDecimal prix_unitaire_cents_ht = new BigDecimal(element.getAttributeValue("prixHT"));
139
                final int idTaxe = Integer.parseInt(element.getAttributeValue("idTaxe"));
140
                final BigDecimal prix_unitaire_cents = new BigDecimal(element.getAttributeValue("prix"));
141
                final String categorie = element.getAttributeValue("categorie");
142
                final String name = element.getValue();
143
                final String codebarre = element.getAttributeValue("codebarre");
144
                final String codeArt = element.getAttributeValue("code");
145
                final Categorie cat = new Categorie(categorie);
61 ilm 146
 
144 ilm 147
                final String valueID = element.getAttributeValue("id");
61 ilm 148
 
144 ilm 149
                final int id = valueID == null || valueID.trim().length() == 0 ? tableArticle.getUndefinedID() : Integer.parseInt(valueID);
150
                final Article art = new Article(cat, name, id);
132 ilm 151
                art.setPriceWithTax(prix_unitaire_cents);
25 ilm 152
                art.setCode(codeArt);
132 ilm 153
                art.setPriceWithoutTax(prix_unitaire_cents_ht);
18 ilm 154
                art.setIdTaxe(idTaxe);
132 ilm 155
                art.setBarCode(codebarre);
144 ilm 156
                final Pair<Article, Integer> line = new Pair<Article, Integer>(art, qte);
18 ilm 157
                t.items.add(line);
158
 
159
            }
160
            // paiement
80 ilm 161
            final List<Element> payChildren = root.getChildren("paiement");
144 ilm 162
            for (final Element element : payChildren) {
18 ilm 163
 
144 ilm 164
                final String type = element.getAttributeValue("type");
165
                final int montant_cents = Integer.parseInt(element.getAttributeValue("montant"));
151 ilm 166
                if (montant_cents != 0) {
18 ilm 167
                    int tp = Paiement.ESPECES;
168
                    if (type.equals("CB")) {
169
                        tp = Paiement.CB;
170
                    } else if (type.equals("CHEQUE")) {
171
                        tp = Paiement.CHEQUE;
172
                    } else if (type.equals("ESPECES")) {
173
                        tp = Paiement.ESPECES;
142 ilm 174
                    } else if (type.equals("SOLDE")) {
175
                        tp = Paiement.SOLDE;
18 ilm 176
                    }
144 ilm 177
                    final Paiement p = new Paiement(tp);
18 ilm 178
                    p.setMontantInCents(montant_cents);
179
                    t.paiements.add(p);
180
                }
181
            }
182
 
80 ilm 183
            return t;
144 ilm 184
        } catch (final Exception e) {
142 ilm 185
            System.err.println("Error with ticket : " + file + " : " + e.getMessage());
18 ilm 186
            e.printStackTrace();
187
            return null;
188
        }
189
    }
190
 
144 ilm 191
    // TODO receiptCode should be immutable and the day part should be the current accounting day
192
    // not the day of the present time. E.g. for work day beginning at 15:00, even at 3:00 the
193
    // receipt code should contain the previous day.
194
    // The creationCal should only be set once the ticket is done (i.e. no further modifications
195
    // should be allowed)
196
 
197
    // create new ticket, i.e. null creationDate since it's not done
198
    public Ticket(final int caisse, final int number, final String previousHash) {
199
        this(new ReceiptCode(caisse, Calendar.getInstance(), number), null, previousHash);
18 ilm 200
    }
201
 
144 ilm 202
    // re-create existing ticket
203
    public Ticket(final ReceiptCode code, final Calendar creationDate, final String previousHash) {
204
        this.caisseNumber = code.getCaisseNb();
205
        this.number = code.getDayIndex();
206
        this.previousHash = previousHash;
207
        this.setCreationCal(creationDate);
18 ilm 208
    }
209
 
144 ilm 210
    public final String getPreviousHash() {
211
        return this.previousHash;
18 ilm 212
    }
213
 
80 ilm 214
    public final ReceiptCode getReceiptCode() {
215
        // TODO replace our fields by one ReceiptCode
216
        return new ReceiptCode(this.getCaisseNumber(), this.getCreationCal(), this.getNumber());
18 ilm 217
    }
218
 
219
    public String getCode() {
80 ilm 220
        return getReceiptCode().getCode();
18 ilm 221
    }
222
 
223
    /**
224
     * Numero du ticket fait ce jour, compteur remis à 1 chaque jour
144 ilm 225
     *
226
     * @return the index of this ticket, inside its day.
18 ilm 227
     */
228
    public int getNumber() {
229
        return this.number;
230
    }
231
 
232
    /**
233
     * Numero de la caisse, de 1 à n
234
     */
80 ilm 235
    final int getCaisseNumber() {
18 ilm 236
        return this.caisseNumber;
237
    }
238
 
144 ilm 239
    public String save(final RegisterFiles files, final SQLElementDirectory dir) throws IOException, SQLException {
240
        final String fileHash = files.save(this);
241
        // FIXME handle failure
242
        this.handleSolde(dir);
243
        return fileHash;
244
    }
245
 
246
    final byte[] saveToFile(final Path f) throws IOException {
93 ilm 247
        final Calendar c = getCreationCal();
18 ilm 248
 
144 ilm 249
        final Element topLevel = new Element("ticket");
18 ilm 250
        topLevel.setAttribute(new Attribute("code", this.getCode()));
144 ilm 251
        if (this.getPreviousHash() != null)
252
            topLevel.setAttribute("previousHash", this.getPreviousHash());
253
        topLevel.setAttribute("creationDate", DATE_FMT.format(c.getTime()));
142 ilm 254
        topLevel.setAttribute("clientID", String.valueOf(this.client.getId()));
18 ilm 255
        // Articles
144 ilm 256
        for (final Pair<Article, Integer> item : this.items) {
257
            final Element e = new Element("article");
18 ilm 258
            e.setAttribute("qte", String.valueOf(item.getSecond()));
259
            // Prix unitaire
132 ilm 260
            e.setAttribute("prix", String.valueOf(item.getFirst().getPriceWithTax()));
261
            e.setAttribute("prixHT", String.valueOf(item.getFirst().getPriceWithoutTax()));
18 ilm 262
            e.setAttribute("idTaxe", String.valueOf(item.getFirst().getIdTaxe()));
263
            e.setAttribute("categorie", item.getFirst().getCategorie().getName());
25 ilm 264
            e.setAttribute("codebarre", item.getFirst().getBarCode());
265
            e.setAttribute("code", item.getFirst().getCode());
61 ilm 266
            e.setAttribute("id", String.valueOf(item.getFirst().getId()));
18 ilm 267
            e.setText(item.getFirst().getName());
268
            topLevel.addContent(e);
269
        }
270
        // Paiements
144 ilm 271
        for (final Paiement paiement : this.paiements) {
18 ilm 272
            final int montantInCents = paiement.getMontantInCents();
151 ilm 273
            if (montantInCents != 0) {
18 ilm 274
                final Element e = new Element("paiement");
275
                String type = "";
276
                if (paiement.getType() == Paiement.CB) {
277
                    type = "CB";
278
                } else if (paiement.getType() == Paiement.CHEQUE) {
279
                    type = "CHEQUE";
280
                } else if (paiement.getType() == Paiement.ESPECES) {
281
                    type = "ESPECES";
142 ilm 282
                } else if (paiement.getType() == Paiement.SOLDE) {
283
                    type = "SOLDE";
18 ilm 284
                }
285
                e.setAttribute("type", type);
286
                e.setAttribute("montant", String.valueOf(montantInCents));
287
                topLevel.addContent(e);
288
            }
289
        }
144 ilm 290
        return RegisterFiles.save(new Document(topLevel), f);
291
    }
142 ilm 292
 
144 ilm 293
    private final void handleSolde(final SQLElementDirectory dir) throws SQLException {
294
        final SQLTable table = dir.getElement(CompteClientTransactionSQLELement.class).getTable();
295
        final SQLTable tablePrefCompte = table.getDBRoot().findTable("PREFS_COMPTE");
296
        final SQLRow rowPrefsCompte = tablePrefCompte.getRow(2);
297
        final Calendar c = getCreationCal();
298
        SQLUtils.executeAtomic(table.getDBSystemRoot().getDataSource(), new SQLUtils.SQLFactory<Object>() {
299
            @Override
300
            public Object create() throws SQLException {
301
                for (final Paiement paiement : Ticket.this.paiements) {
302
                    final int montantInCents = paiement.getMontantInCents();
303
                    if (montantInCents > 0 && paiement.getType() == Paiement.SOLDE) {
304
                        final SQLRowValues rowValsTransact = new SQLRowValues(table);
305
                        rowValsTransact.put("ID_CLIENT", Ticket.this.client.getId());
142 ilm 306
 
144 ilm 307
                        rowValsTransact.put("DATE", c.getTime());
308
                        final BigDecimal amountTransaction = new BigDecimal(montantInCents).movePointLeft(2);
309
                        rowValsTransact.put("MONTANT", amountTransaction.negate());
142 ilm 310
 
144 ilm 311
                        SQLRow rowTransact = rowValsTransact.commit();
312
                        final GenerationEcritures ecr = new GenerationEcritures();
313
                        final int idMvt = ecr.getNewMouvement(table.getName(), rowTransact.getID(), 1, "Transact. " + Ticket.this.client.getFullName() + " Ticket " + getCode());
314
                        rowTransact = rowTransact.createEmptyUpdateRow().put("ID_MOUVEMENT", idMvt).commit();
142 ilm 315
 
144 ilm 316
                        // mise à jour du solde
317
                        final SQLTable tableClient = table.getForeignTable("ID_CLIENT");
318
                        final SQLRow row = tableClient.getRow(Ticket.this.client.getId());
319
                        final BigDecimal solde = row.getBigDecimal("SOLDE_COMPTE");
320
                        final BigDecimal nouveauSolde = solde.subtract(amountTransaction);
321
                        row.createEmptyUpdateRow().put("SOLDE_COMPTE", nouveauSolde).commit();
142 ilm 322
 
144 ilm 323
                        // Créeation des réglements et écritures
142 ilm 324
 
144 ilm 325
                        int idCompteAvanceClient = rowPrefsCompte.getInt("ID_COMPTE_PCE_AVANCE_CLIENT");
326
                        int idCompteClient = row.getInt("ID_COMPTE_PCE");
327
                        if (idCompteAvanceClient <= 1) {
328
                            idCompteAvanceClient = ComptePCESQLElement.getIdComptePceDefault("AvanceClients");
329
                        }
142 ilm 330
 
144 ilm 331
                        // compte Clients
332
                        if (idCompteClient <= 1) {
333
                            idCompteClient = rowPrefsCompte.getInt("ID_COMPTE_PCE_CLIENT");
334
                            if (idCompteClient <= 1) {
335
                                idCompteClient = ComptePCESQLElement.getIdComptePceDefault("Clients");
142 ilm 336
                            }
144 ilm 337
                        }
338
                        new GenerationMvtVirement(idCompteAvanceClient, idCompteClient, 0, montantInCents, "Utilisation compte client", c.getTime(), JournalSQLElement.VENTES, "Ticket N°" + getCode())
339
                                .genereMouvement();
142 ilm 340
                    }
341
                }
144 ilm 342
                return null;
142 ilm 343
            }
144 ilm 344
        });
18 ilm 345
    }
346
 
149 ilm 347
    @Override
144 ilm 348
    public void print(final TicketPrinter prt, final int ticketWidth) {
349
        final int maxWidth = ticketWidth;
350
        final int MAX_PRICE_WIDTH = 8;
351
        final int MAX_QTE_WIDTH = 5;
149 ilm 352
        prt.clearBuffer("receipt " + this.getCode());
144 ilm 353
        final List<TicketLine> headers = POSConfiguration.getInstance().getHeaderLines();
354
        for (final TicketLine line : headers) {
18 ilm 355
            prt.addToBuffer(line);
356
        }
357
 
358
        // Date
359
        prt.addToBuffer("");
144 ilm 360
        final SimpleDateFormat df = new SimpleDateFormat("EEEE d MMMM yyyy à HH:mm", Locale.FRENCH);
151 ilm 361
        prt.addToBuffer(DefaultTicketPrinter.formatCenter(maxWidth, "Le " + df.format(getCreationDate())));
18 ilm 362
        prt.addToBuffer("");
149 ilm 363
        List<Pair<Article, Integer>> itemsToPrint = new ArrayList<>(this.items);
364
        Collections.sort(itemsToPrint, new Comparator<Pair<Article, Integer>>() {
18 ilm 365
 
149 ilm 366
            @Override
367
            public int compare(Pair<Article, Integer> o1, Pair<Article, Integer> o2) {
368
                final Article p1 = o1.getFirst();
369
                final Article p2 = o2.getFirst();
370
                final Categorie c1 = p1.getCategorie();
371
                final Categorie c2 = p2.getCategorie();
372
                if (c1.equals(c2)) {
373
                    return p1.getName().compareTo(p2.getName());
374
                }
375
                // Unknown first
376
                if (c1.isUnknown()) {
377
                    return -1;
378
                }
379
                if (c2.isUnknown()) {
380
                    return 1;
381
                }
382
                // Sort by name
383
                return c1.getName().compareTo(c2.getName());
384
            }
385
        });
386
        Categorie currentCategorie = null;
144 ilm 387
        for (final Pair<Article, Integer> item : this.items) {
61 ilm 388
            final Article article = item.getFirst();
151 ilm 389
            if (currentCategorie == null || !currentCategorie.getName().equals(article.getCategorie().getName())) {
149 ilm 390
                // Print category name, except for unknown
391
                currentCategorie = article.getCategorie();
392
                if (!currentCategorie.isUnknown()) {
393
                    prt.addToBuffer(currentCategorie.getName(), TicketPrinter.BOLD);
394
                }
395
            }
61 ilm 396
            final Integer nb = item.getSecond();
144 ilm 397
            final Float tauxFromId = TaxeCache.getCache().getTauxFromId(article.getIdTaxe());
398
            final BigDecimal tauxTVA = new BigDecimal(tauxFromId).movePointLeft(2).add(BigDecimal.ONE);
151 ilm 399
            final BigDecimal unitPrice = article.getPriceWithoutTax().multiply(tauxTVA, DecimalUtils.HIGH_PRECISION);
144 ilm 400
            final BigDecimal multiply = article.getPriceWithoutTax().multiply(new BigDecimal(nb), DecimalUtils.HIGH_PRECISION).multiply(tauxTVA, DecimalUtils.HIGH_PRECISION);
67 ilm 401
 
151 ilm 402
            final String qtyString = DefaultTicketPrinter.formatRight(MAX_QTE_WIDTH, String.valueOf(nb));
403
            final String priceString = DefaultTicketPrinter.formatRight(MAX_PRICE_WIDTH, TicketCellRenderer.centsToString(multiply.movePointRight(2).setScale(0, RoundingMode.HALF_UP).intValue()));
404
            String unitPriceString = "";
405
            if (nb != 1) {
406
                unitPriceString = DefaultTicketPrinter.formatRight(MAX_PRICE_WIDTH, TicketCellRenderer.centsToString(unitPrice.movePointRight(2).setScale(0, RoundingMode.HALF_UP).intValue()));
407
            }
408
 
83 ilm 409
            if (article.getCode() != null && !article.getCode().isEmpty()) {
410
                // 2 lines
151 ilm 411
                final String codeString = DefaultTicketPrinter.formatLeft(maxWidth - 2 - MAX_PRICE_WIDTH - MAX_QTE_WIDTH - 1 - unitPriceString.length(), article.getCode());
412
                prt.addToBuffer(qtyString + " " + codeString + " " + unitPriceString + " " + priceString);
83 ilm 413
                final String nameString = DefaultTicketPrinter.formatLeft(maxWidth - MAX_QTE_WIDTH - 1, article.getName());
414
                prt.addToBuffer("      " + nameString);
415
            } else {
416
                // 1 line
151 ilm 417
                final String nameString = DefaultTicketPrinter.formatLeft(maxWidth - 2 - MAX_PRICE_WIDTH - MAX_QTE_WIDTH - 1 - unitPriceString.length(), article.getName());
418
                prt.addToBuffer(qtyString + " " + nameString + " " + unitPriceString + " " + priceString);
83 ilm 419
            }
420
 
18 ilm 421
        }
422
 
144 ilm 423
        final StringBuilder spacer = new StringBuilder();
18 ilm 424
        for (int i = 0; i <= MAX_QTE_WIDTH; i++) {
425
            spacer.append(' ');
426
        }
427
        for (int i = 0; i < maxWidth - MAX_QTE_WIDTH - 1; i++) {
428
            spacer.append('=');
429
        }
430
        prt.addToBuffer(spacer.toString());
83 ilm 431
 
432
        final TotalCalculator calc = getTotalCalculator();
144 ilm 433
        final int totalTTCInCents = calc.getTotalTTC().movePointRight(2).setScale(0, RoundingMode.HALF_UP).intValue();
83 ilm 434
 
132 ilm 435
        prt.addToBuffer(DefaultTicketPrinter.formatRight(maxWidth - MAX_PRICE_WIDTH, "MONTANT TOTAL TTC (Euros) : ")
144 ilm 436
                + DefaultTicketPrinter.formatRight(MAX_PRICE_WIDTH, TicketCellRenderer.centsToString(totalTTCInCents)), TicketPrinter.BOLD);
142 ilm 437
 
438
        final Map<SQLRowAccessor, Tuple2<BigDecimal, BigDecimal>> mapHtTVARowTaux = calc.getMapHtTVARowTaux();
144 ilm 439
        for (final SQLRowAccessor row : mapHtTVARowTaux.keySet()) {
440
            final Tuple2<BigDecimal, BigDecimal> htTVA = mapHtTVARowTaux.get(row);
441
            final float tvaTaux = TaxeCache.getCache().getTauxFromId(row.getID());
142 ilm 442
            final BigDecimal montantTVA = htTVA.get1();
443
            if (montantTVA != null && montantTVA.signum() != 0) {
444
                prt.addToBuffer(
445
                        DefaultTicketPrinter.formatRight(maxWidth - MAX_PRICE_WIDTH, "Dont TVA " + tvaTaux + "% : ")
446
                                + DefaultTicketPrinter.formatRight(MAX_PRICE_WIDTH, TicketCellRenderer.centsToString(montantTVA.movePointRight(2).setScale(0, RoundingMode.HALF_UP).intValue())),
144 ilm 447
                        TicketPrinter.NORMAL);
142 ilm 448
            }
449
        }
18 ilm 450
        prt.addToBuffer("");
451
        //
144 ilm 452
        for (final Paiement paiement : this.paiements) {
18 ilm 453
 
454
            String type = "";
144 ilm 455
            final int montantInCents = paiement.getMontantInCents();
151 ilm 456
            if (montantInCents != 0) {
457
                if (montantInCents > 0) {
458
                    type = "Paiement ";
459
                } else {
460
                    type = "Remboursement ";
461
                }
462
                if (paiement.getType() == Paiement.CB) {
463
                    type = "CB";
464
                } else if (paiement.getType() == Paiement.CHEQUE) {
465
                    type = "par chèque";
466
                } else if (paiement.getType() == Paiement.ESPECES) {
467
                    type = "en espèces";
468
                } else if (paiement.getType() == Paiement.SOLDE) {
469
                    type = "depuis solde";
470
                }
18 ilm 471
                type += " de " + TicketCellRenderer.centsToString(montantInCents);
472
                if (montantInCents > 100) {
473
                    type += " euros";
474
                } else {
475
                    type += " euro";
476
                }
477
                prt.addToBuffer(type);
478
            }
479
        }
480
        // Montant Rendu
83 ilm 481
        if (getTotalInCents() < getPaidTotal()) {
144 ilm 482
            final int montantInCents = getPaidTotal() - getTotalInCents();
18 ilm 483
            String type = "Rendu : " + TicketCellRenderer.centsToString(montantInCents);
484
            if (montantInCents > 100) {
485
                type += " euros";
486
            } else {
487
                type += " euro";
488
            }
489
            prt.addToBuffer(type);
490
        }
491
        prt.addToBuffer("");
492
        // Footer
144 ilm 493
        final List<TicketLine> footers = POSConfiguration.getInstance().getFooterLines();
494
        for (final TicketLine line : footers) {
18 ilm 495
            prt.addToBuffer(line);
496
        }
497
        prt.addToBuffer("");
144 ilm 498
        prt.addToBuffer(getCode(), TicketPrinter.BARCODE);
18 ilm 499
        prt.addToBuffer("");
83 ilm 500
        prt.addToBuffer("Ticket créé par l'ERP OpenConcerto.");
501
 
18 ilm 502
        try {
503
            prt.printBuffer();
144 ilm 504
        } catch (final Exception e) {
18 ilm 505
            e.printStackTrace();
506
        }
507
    }
508
 
509
    public Date getCreationDate() {
80 ilm 510
        return this.getCreationCal().getTime();
18 ilm 511
    }
512
 
80 ilm 513
    public Calendar getCreationCal() {
514
        return this.creationCal;
18 ilm 515
    }
516
 
80 ilm 517
    public void setCreationCal(final Calendar cal) {
144 ilm 518
        // FIXME date mismatch when ticket created one day and saved the next one
519
        this.creationCal = cal == null ? null : (Calendar) cal.clone();
18 ilm 520
    }
521
 
144 ilm 522
    public void addPaiement(final Paiement p1) {
18 ilm 523
        this.paiements.add(p1);
524
 
525
    }
526
 
151 ilm 527
    public boolean isAdditionnalCopyRequested() {
528
        return additionnalCopyRequested;
529
    }
530
 
144 ilm 531
    public void addArticle(final Article a) {
18 ilm 532
        boolean alreadyExist = false;
151 ilm 533
        if (a.isAdditionalCopyRequested()) {
534
            this.additionnalCopyRequested = true;
535
        }
144 ilm 536
        for (final Pair<Article, Integer> line : this.items) {
18 ilm 537
            if (line.getFirst().equals(a)) {
538
                alreadyExist = true;
539
                break;
540
            }
541
        }
151 ilm 542
 
18 ilm 543
        if (!alreadyExist) {
144 ilm 544
            final Pair<Article, Integer> line = new Pair<Article, Integer>(new Article(a), 1);
18 ilm 545
            this.items.add(line);
546
        }
547
 
548
    }
549
 
144 ilm 550
    public void incrementArticle(final Article a) {
18 ilm 551
        boolean alreadyExist = false;
144 ilm 552
        for (final Pair<Article, Integer> line : this.items) {
18 ilm 553
            if (line.getFirst().equals(a)) {
554
                alreadyExist = true;
555
                line.setSecond(line.getSecond() + 1);
556
                break;
557
            }
558
        }
559
        if (!alreadyExist) {
144 ilm 560
            final Pair<Article, Integer> line = new Pair<Article, Integer>(a, 1);
18 ilm 561
            this.items.add(line);
562
        }
563
 
564
    }
565
 
566
    public List<Paiement> getPaiements() {
567
        return this.paiements;
568
    }
569
 
83 ilm 570
    public int getTotalInCents() {
571
        final TotalCalculator calc = getTotalCalculator();
132 ilm 572
        final BigDecimal totalTTC = calc.getTotalTTC();
573
        return totalTTC.movePointRight(2).setScale(0, RoundingMode.HALF_UP).intValue();
83 ilm 574
    }
575
 
576
    public TotalCalculator getTotalCalculator() {
73 ilm 577
        final SQLTable tableElt = ((ComptaPropsConfiguration) Configuration.getInstance()).getRootSociete().findTable("SAISIE_VENTE_FACTURE_ELEMENT");
156 ilm 578
        final TotalCalculator calc = new TotalCalculator("T_PA_HT", "T_PV_HT", null, null);
73 ilm 579
        final String val = DefaultNXProps.getInstance().getStringProperty("ArticleService");
580
        final Boolean bServiceActive = Boolean.valueOf(val);
581
        calc.setServiceActive(bServiceActive != null && bServiceActive);
582
        final int size = this.items.size();
583
        for (int i = 0; i < size; i++) {
584
            final Pair<Article, Integer> line = this.items.get(i);
18 ilm 585
            final int count = line.getSecond();
73 ilm 586
            final Article art = line.getFirst();
587
            final SQLRowValues rowVals = new SQLRowValues(tableElt);
132 ilm 588
            rowVals.put("T_PV_HT", art.getPriceWithoutTax().multiply(new BigDecimal(count)));
67 ilm 589
            rowVals.put("QTE", count);
132 ilm 590
            rowVals.put("ID_TAXE", art.getIdTaxe());
67 ilm 591
            calc.addLine(rowVals, tableArticle.getRow(art.getId()), i, false);
73 ilm 592
 
18 ilm 593
        }
67 ilm 594
        calc.checkResult();
83 ilm 595
        return calc;
18 ilm 596
    }
597
 
598
    public List<Pair<Article, Integer>> getArticles() {
599
        return this.items;
600
    }
601
 
144 ilm 602
    public void clearArticle(final Article article) {
18 ilm 603
        Pair<Article, Integer> toRemove = null;
144 ilm 604
        for (final Pair<Article, Integer> line : this.items) {
18 ilm 605
            if (line.getFirst().equals(article)) {
606
                toRemove = line;
607
                break;
608
            }
609
        }
610
        if (toRemove != null) {
611
            this.items.remove(toRemove);
612
        }
613
    }
614
 
144 ilm 615
    public void setArticleCount(final Article article, final int count) {
151 ilm 616
        // TODO Allow only if annulation?
617
        // if (count <= 0) {
618
        // this.clearArticle(article);
619
        // return;
620
        // }
18 ilm 621
        Pair<Article, Integer> toModify = null;
144 ilm 622
        for (final Pair<Article, Integer> line : this.items) {
18 ilm 623
            if (line.getFirst().equals(article)) {
624
                toModify = line;
625
                break;
626
            }
627
        }
628
        if (toModify != null) {
629
            toModify.setSecond(count);
630
        }
631
 
632
    }
633
 
144 ilm 634
    public int getItemCount(final Article article) {
635
        for (final Pair<Article, Integer> line : this.items) {
18 ilm 636
            if (line.getFirst().equals(article)) {
637
                return line.getSecond();
638
            }
639
        }
640
        return 0;
641
    }
642
 
643
    public int getPaidTotal() {
644
        int paid = 0;
144 ilm 645
        for (final Paiement p : this.paiements) {
18 ilm 646
            paid += p.getMontantInCents();
647
        }
648
        return paid;
649
    }
650
 
144 ilm 651
    public void removeArticle(final Article a) {
18 ilm 652
        Pair<Article, Integer> lineToDelete = null;
144 ilm 653
        for (final Pair<Article, Integer> line : this.items) {
18 ilm 654
            if (line.getFirst().equals(a)) {
655
                final int count = line.getSecond() + 1;
656
                if (count <= 0) {
657
                    lineToDelete = line;
658
                }
659
                line.setSecond(count);
660
                break;
661
            }
662
        }
663
        if (lineToDelete != null) {
664
            this.items.remove(lineToDelete);
665
        }
666
 
667
    }
668
 
669
    @Override
670
    public String toString() {
671
        return "Ticket " + getCode();
672
    }
673
 
674
    @Override
144 ilm 675
    public boolean equals(final Object obj) {
18 ilm 676
        if (this == obj) {
677
            return true;
678
        } else if (obj instanceof Ticket) {
144 ilm 679
            final Ticket t = (Ticket) obj;
18 ilm 680
            return t.getCode().equals(getCode());
681
        }
682
        return false;
683
    }
684
 
685
    @Override
686
    public int hashCode() {
687
        return getCode().hashCode();
688
    }
689
 
144 ilm 690
    public void deleteTicket(final SQLElementDirectory dir) throws IOException {
691
        final SQLTable table = dir.getElement(CompteClientTransactionSQLELement.class).getTable();
692
 
693
        for (final Paiement paiement : this.paiements) {
142 ilm 694
            final int montantInCents = paiement.getMontantInCents();
695
            if (montantInCents > 0 && paiement.getType() == Paiement.SOLDE) {
696
                try {
144 ilm 697
                    SQLUtils.executeAtomic(table.getDBSystemRoot().getDataSource(), new SQLUtils.SQLFactory<Object>() {
142 ilm 698
                        @Override
699
                        public Object create() throws SQLException {
144 ilm 700
                            final SQLTable tablePrefCompte = table.getDBRoot().findTable("PREFS_COMPTE");
701
                            final SQLRow rowPrefsCompte = tablePrefCompte.getRow(2);
702
                            final SQLRowValues rowValsTransact = new SQLRowValues(table);
703
                            rowValsTransact.put("ID_CLIENT", Ticket.this.client.getId());
142 ilm 704
 
705
                            rowValsTransact.put("DATE", getCreationCal().getTime());
706
                            final BigDecimal amountTransaction = new BigDecimal(montantInCents).movePointLeft(2);
707
                            rowValsTransact.put("MONTANT", amountTransaction);
708
 
709
                            SQLRow rowTransact = rowValsTransact.commit();
144 ilm 710
                            final GenerationEcritures ecr = new GenerationEcritures();
711
                            final int idMvt = ecr.getNewMouvement(table.getName(), rowTransact.getID(), 1, "Annule Transact. " + Ticket.this.client.getFullName() + " Ticket " + getCode());
142 ilm 712
                            rowTransact = rowTransact.createEmptyUpdateRow().put("ID_MOUVEMENT", idMvt).commit();
713
 
714
                            // mise à jour du solde
144 ilm 715
                            final SQLTable tableClient = table.getForeignTable("ID_CLIENT");
716
                            final SQLRow row = tableClient.getRow(Ticket.this.client.getId());
717
                            final BigDecimal solde = row.getBigDecimal("SOLDE_COMPTE");
142 ilm 718
                            final BigDecimal nouveauSolde = solde.add(amountTransaction);
719
                            row.createEmptyUpdateRow().put("SOLDE_COMPTE", nouveauSolde).commit();
720
 
721
                            // Créeation des réglements et écritures
722
 
723
                            int idCompteAvanceClient = rowPrefsCompte.getInt("ID_COMPTE_PCE_AVANCE_CLIENT");
724
                            int idCompteClient = row.getInt("ID_COMPTE_PCE");
725
                            try {
726
                                if (idCompteAvanceClient <= 1) {
727
                                    idCompteAvanceClient = ComptePCESQLElement.getIdComptePceDefault("AvanceClients");
728
                                }
729
 
730
                                // compte Clients
731
                                if (idCompteClient <= 1) {
732
                                    idCompteClient = rowPrefsCompte.getInt("ID_COMPTE_PCE_CLIENT");
733
                                    if (idCompteClient <= 1) {
734
                                        idCompteClient = ComptePCESQLElement.getIdComptePceDefault("Clients");
735
                                    }
736
                                }
144 ilm 737
                            } catch (final Exception e) {
142 ilm 738
                                e.printStackTrace();
739
                                throw new SQLException(e);
740
                            }
144 ilm 741
                            new GenerationMvtVirement(idCompteAvanceClient, idCompteClient, montantInCents, 0L, "Annulation transaction compte client", getCreationCal().getTime(),
142 ilm 742
                                    JournalSQLElement.VENTES, "Ticket N°" + getCode()).genereMouvement();
743
                            return null;
744
                        }
745
                    });
144 ilm 746
                } catch (final SQLException e) {
142 ilm 747
                    e.printStackTrace();
748
                }
749
            }
750
        }
751
 
80 ilm 752
        getReceiptCode().markDeleted();
18 ilm 753
    }
754
}