OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

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