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