OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 174 | Rev 182 | 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.ui;
15
 
132 ilm 16
import org.openconcerto.erp.core.sales.pos.POSConfiguration;
18 ilm 17
import org.openconcerto.erp.core.sales.pos.io.BarcodeReader;
132 ilm 18
import org.openconcerto.erp.core.sales.pos.io.ConcertProtocol;
19
import org.openconcerto.erp.core.sales.pos.io.ESCSerialDisplay;
18 ilm 20
import org.openconcerto.erp.core.sales.pos.io.TicketPrinter;
21
import org.openconcerto.erp.core.sales.pos.model.Article;
142 ilm 22
import org.openconcerto.erp.core.sales.pos.model.Client;
18 ilm 23
import org.openconcerto.erp.core.sales.pos.model.Paiement;
144 ilm 24
import org.openconcerto.erp.core.sales.pos.model.ReceiptCode;
25
import org.openconcerto.erp.core.sales.pos.model.RegisterFiles;
26
import org.openconcerto.erp.core.sales.pos.model.RegisterLog;
27
import org.openconcerto.erp.core.sales.pos.model.RegisterLogEntry.ReceiptEntry;
28
import org.openconcerto.erp.core.sales.pos.model.RegisterState.Status;
18 ilm 29
import org.openconcerto.erp.core.sales.pos.model.Ticket;
174 ilm 30
import org.openconcerto.erp.core.sales.pos.model.TicketItem;
149 ilm 31
import org.openconcerto.erp.preferences.TemplateNXProps;
144 ilm 32
import org.openconcerto.sql.element.SQLElementDirectory;
149 ilm 33
import org.openconcerto.utils.FileUtils;
34
import org.openconcerto.utils.StringUtils;
18 ilm 35
 
36
import java.awt.event.KeyEvent;
149 ilm 37
import java.io.File;
38
import java.io.FileOutputStream;
144 ilm 39
import java.io.IOException;
142 ilm 40
import java.math.BigDecimal;
132 ilm 41
import java.math.RoundingMode;
144 ilm 42
import java.sql.SQLException;
43
import java.text.ParseException;
18 ilm 44
import java.util.ArrayList;
93 ilm 45
import java.util.Calendar;
149 ilm 46
import java.util.HashSet;
18 ilm 47
import java.util.List;
149 ilm 48
import java.util.Set;
132 ilm 49
import java.util.Timer;
50
import java.util.TimerTask;
18 ilm 51
 
132 ilm 52
import javax.swing.JOptionPane;
53
 
144 ilm 54
import org.jdom2.JDOMException;
55
 
18 ilm 56
public class CaisseControler implements BarcodeListener {
57
 
58
    private Article articleSelected;
59
    private Paiement paiementSelected;
60
    private Ticket t;
142 ilm 61
    private Client client = Client.NONE;
174 ilm 62
    private List<CaisseListener> listeners = new ArrayList<>();
18 ilm 63
 
132 ilm 64
    private final BarcodeReader r;
18 ilm 65
    private Paiement p1 = new Paiement(Paiement.ESPECES);
66
    private Paiement p2 = new Paiement(Paiement.CB);
67
    private Paiement p3 = new Paiement(Paiement.CHEQUE);
132 ilm 68
    private final CaisseFrame caisseFrame;
142 ilm 69
    private final POSDisplay lcd;
174 ilm 70
    private TicketItem ticketItemSelected;
18 ilm 71
 
144 ilm 72
    public CaisseControler(CaisseFrame caisseFrame) throws ParseException, JDOMException, IOException {
18 ilm 73
        this.caisseFrame = caisseFrame;
144 ilm 74
        final RegisterLog lastLog = caisseFrame.getFiles().getLastLog();
75
        if (lastLog == null || lastLog.getRegisterState().getStatus() != Status.OPEN)
76
            throw new IllegalStateException("Not open");
77
        final String lastHash = lastLog.getLastReceiptHash();
18 ilm 78
 
144 ilm 79
        final ReceiptEntry lastReceiptEvent = lastLog.getLastReceiptCreationEvent();
80
        final int dayIndex;
81
        if (lastReceiptEvent == null) {
82
            dayIndex = 1;
83
        } else {
84
            final ReceiptCode lastCode = lastReceiptEvent.getCode();
85
            dayIndex = lastCode.getDayIndex() + 1;
86
        }
156 ilm 87
        final POSConfiguration posConf = getPOSConf();
88
        this.t = new Ticket(posConf.getPosID(), dayIndex, lastHash);
144 ilm 89
 
18 ilm 90
        this.t.addPaiement(this.p1);
91
        this.t.addPaiement(this.p2);
92
        this.t.addPaiement(this.p3);
93
 
156 ilm 94
        final int scanDelay = posConf.getScanDelay();
144 ilm 95
        this.r = new BarcodeReader(scanDelay);
18 ilm 96
        this.r.start();
97
        this.r.addBarcodeListener(this);
156 ilm 98
        if (posConf.getLCDType().equals("serial")) {
174 ilm 99
            this.lcd = new ESCSerialDisplay(posConf.getLCDPort());
142 ilm 100
        } else {
174 ilm 101
            this.lcd = new PrinterPOSDisplay(posConf.getLCDPort());
142 ilm 102
        }
132 ilm 103
        this.setLCDDefaultDisplay(0);
18 ilm 104
    }
105
 
149 ilm 106
    public final CaisseFrame getCaisseFrame() {
107
        return this.caisseFrame;
108
    }
109
 
156 ilm 110
    protected final POSConfiguration getPOSConf() {
111
        return getCaisseFrame().getPOSConf();
112
    }
113
 
18 ilm 114
    public Article getArticleSelected() {
115
        return this.articleSelected;
116
    }
117
 
118
    public Paiement getPaiementSelected() {
119
        return this.paiementSelected;
120
    }
121
 
122
    void setArticleSelected(Article a) {
174 ilm 123
        if (a != this.articleSelected) {
65 ilm 124
            this.articleSelected = a;
125
            this.paiementSelected = null;
126
            fire();
127
        }
18 ilm 128
    }
129
 
130
    void setPaiementSelected(Paiement p) {
131
        this.paiementSelected = p;
132
        this.articleSelected = null;
174 ilm 133
        this.ticketItemSelected = null;
18 ilm 134
        fire();
135
    }
136
 
137
    // Listeners
138
    private void fire() {
139
        int stop = this.listeners.size();
140
        for (int i = 0; i < stop; i++) {
141
            this.listeners.get(i).caisseStateChanged();
142
        }
143
    }
144
 
145
    void addCaisseListener(CaisseListener l) {
146
        this.listeners.add(l);
147
    }
148
 
142 ilm 149
    // Customer
150
    public void setClient(Client client) {
151
        if (client == null) {
152
            throw new IllegalArgumentException("Customer cannot be null");
153
        }
154
        this.client = client;
155
        this.t.setClient(client);
156
        fire();
157
    }
158
 
159
    public boolean isClientDefined() {
160
        return this.client != Client.NONE;
161
    }
162
 
163
    public Client getClient() {
174 ilm 164
        return this.client;
142 ilm 165
    }
166
 
174 ilm 167
    public void setCodePostal(String codePostal) {
168
        this.t.setCodePostal(codePostal);
169
    }
170
 
18 ilm 171
    // Articles
151 ilm 172
    public void addArticle(Article a) {
18 ilm 173
        this.t.addArticle(a);
174
        fire();
132 ilm 175
        String price = TicketCellRenderer.centsToString(a.getPriceWithTax().movePointRight(2).setScale(0, RoundingMode.HALF_UP).intValue());
176
        this.setLCD(a.getName(), price, 0);
177
        this.setLCDDefaultDisplay(2);
18 ilm 178
    }
179
 
180
    void incrementArticle(Article a) {
181
        this.t.incrementArticle(a);
182
        fire();
132 ilm 183
        String price = TicketCellRenderer.centsToString(a.getPriceWithTax().movePointRight(2).setScale(0, RoundingMode.HALF_UP).intValue());
184
        this.setLCD(a.getName(), price, 0);
185
        this.setLCDDefaultDisplay(2);
18 ilm 186
    }
187
 
188
    void removeArticle(Article a) {
189
        this.t.removeArticle(a);
190
        fire();
191
    }
192
 
193
    // Paiements
194
    public List<Paiement> getPaiements() {
195
        return this.t.getPaiements();
196
    }
197
 
198
    public void addPaiement(Paiement p) {
199
        this.t.addPaiement(p);
200
        fire();
201
    }
202
 
203
    public void clearPaiement(Paiement paiement) {
204
        if (this.p1.equals(paiement) || this.p2.equals(paiement) || this.p3.equals(paiement)) {
205
            paiement.setMontantInCents(0);
206
        }
207
        fire();
208
    }
209
 
132 ilm 210
    public void setPaiementValue(Paiement p, int v) {
142 ilm 211
        if (p.getType() == Paiement.SOLDE) {
212
            int soldeInCents = getClient().getSolde().movePointRight(2).intValue();
213
            if (v > soldeInCents) {
214
                v = soldeInCents;
215
            }
216
        }
132 ilm 217
        p.setMontantInCents(v);
18 ilm 218
        fire();
132 ilm 219
        this.setLCD("Paiement " + p.getTypeAsString().replace('è', 'e').replace('é', 'e'), TicketCellRenderer.centsToString(p.getMontantInCents()), 0);
220
        this.setLCDDefaultDisplay(3);
18 ilm 221
    }
222
 
223
    // Totaux
224
    public int getTotal() {
83 ilm 225
        return this.t.getTotalInCents();
18 ilm 226
    }
227
 
228
    public int getPaidTotal() {
229
        return this.t.getPaidTotal();
230
    }
231
 
232
    //
233
 
174 ilm 234
    public List<TicketItem> getItems() {
235
        return this.t.getItems();
18 ilm 236
    }
237
 
174 ilm 238
    public BigDecimal getItemCount(Article article) {
18 ilm 239
        return this.t.getItemCount(article);
240
    }
241
 
242
    public void clearArticle(Article article) {
243
        this.t.clearArticle(article);
174 ilm 244
        this.setTicketItemSelected(null);
18 ilm 245
    }
246
 
174 ilm 247
    public void setArticleCount(Article article, BigDecimal count) {
18 ilm 248
        this.t.setArticleCount(article, count);
249
        this.setArticleSelected(null);
250
    }
251
 
252
    @Override
253
    public void barcodeRead(String code) {
144 ilm 254
        System.err.println("CaisseControler.barcodeRead() barcode : " + code);
18 ilm 255
        if (code.equalsIgnoreCase("especes")) {
256
            autoFillPaiement(this.p1);
257
 
258
        } else if (code.equalsIgnoreCase("cb")) {
259
            autoFillPaiement(this.p2);
260
 
261
        } else if (code.equalsIgnoreCase("cheque")) {
262
            autoFillPaiement(this.p3);
263
 
264
        } else if (code.equalsIgnoreCase("annuler")) {
265
            if (this.articleSelected != null) {
266
                this.clearArticle(this.articleSelected);
174 ilm 267
            } else if (this.paiementSelected != null) {
268
                this.paiementSelected.setMontantInCents(0);
269
                fire();
18 ilm 270
            }
271
        } else if (code.equalsIgnoreCase("valider")) {
272
 
273
        } else if (code.equalsIgnoreCase("facture")) {
274
 
275
        } else if (code.equalsIgnoreCase("ticket")) {
276
 
277
        } else {
278
            Article a = Article.getArticleFromBarcode(code);
279
            if (a != null) {
144 ilm 280
                System.err.println("CaisseControler.barcodeRead() barcode : " + code + " : product found : " + a.getName());
18 ilm 281
                this.incrementArticle(a);
282
                this.setArticleSelected(a);
80 ilm 283
            } else {
144 ilm 284
                System.err.println("CaisseControler.barcodeRead() barcode : " + code + " : no product found");
285
                Ticket t = Ticket.getTicketFromCode(code, this.caisseFrame.getFiles());
83 ilm 286
                if (t != null) {
144 ilm 287
                    System.err.println("CaisseControler.barcodeRead() barcode : " + code + " : receipt found :" + t.getNumber());
174 ilm 288
                    this.caisseFrame.showTickets(t);
83 ilm 289
                }
18 ilm 290
            }
291
        }
292
 
293
    }
294
 
295
    void autoFillPaiement(Paiement p) {
142 ilm 296
        int nouveauMontant = getTotal() - getPaidTotal() + p.getMontantInCents();
297
        if (p.getType() == Paiement.SOLDE) {
298
            int soldeInCents = getClient().getSolde().movePointRight(2).intValue();
299
            if (nouveauMontant > soldeInCents) {
300
                nouveauMontant = soldeInCents;
301
            }
302
        }
303
        p.setMontantInCents(nouveauMontant);
18 ilm 304
        setPaiementSelected(p);
132 ilm 305
        this.setLCD("Paiement " + p.getTypeAsString(), TicketCellRenderer.centsToString(p.getMontantInCents()), 0);
306
        this.setLCDDefaultDisplay(3);
18 ilm 307
    }
308
 
309
    void addBarcodeListener(BarcodeListener l) {
310
        this.r.addBarcodeListener(l);
311
    }
312
 
313
    public boolean canAddPaiement(int type) {
314
        final int paiementCount = this.t.getPaiements().size();
315
        if (paiementCount >= 6)
316
            return false;
317
        for (int i = 0; i < paiementCount; i++) {
318
            Paiement p = this.t.getPaiements().get(i);
151 ilm 319
            if (p.getType() == type && p.getMontantInCents() != 0) {
18 ilm 320
                return false;
321
            }
322
        }
323
 
324
        return true;
325
    }
326
 
327
    @Override
328
    public void keyReceived(KeyEvent ee) {
174 ilm 329
        // nothing
18 ilm 330
    }
331
 
332
    public static String getCents(int cents) {
151 ilm 333
        String s = String.valueOf(Math.abs(cents) % 100);
18 ilm 334
        if (s.length() < 2) {
335
            s = "0" + s;
336
        }
337
        return s;
338
    }
339
 
340
    public static String getEuros(int cents) {
174 ilm 341
        return String.valueOf(cents / 100);
18 ilm 342
    }
343
 
149 ilm 344
    public Ticket saveAndClearTicket(final RegisterFiles files, final SQLElementDirectory dir) throws IOException, SQLException {
345
        if (!this.isTicketValid())
346
            return null;
144 ilm 347
        this.t.setCreationCal(Calendar.getInstance());
149 ilm 348
        final String fileHash = this.t.save(files, dir);
349
        final Ticket res = this.t;
350
        final int newIndex = this.t.getNumber() + 1;
174 ilm 351
        this.t = new Ticket(getPOSConf().getPosID(), newIndex, fileHash);
352
        this.p1 = new Paiement(Paiement.ESPECES);
353
        this.p2 = new Paiement(Paiement.CB);
354
        this.p3 = new Paiement(Paiement.CHEQUE);
149 ilm 355
        this.t.addPaiement(this.p1);
356
        this.t.addPaiement(this.p2);
357
        this.t.addPaiement(this.p3);
358
        this.setPaiementSelected(null);
359
        this.setArticleSelected(null);
174 ilm 360
        this.client = Client.NONE;
149 ilm 361
        return res;
144 ilm 362
    }
363
 
18 ilm 364
    public int getTicketNumber() {
365
        return this.t.getNumber();
366
    }
367
 
368
    public void openDrawer() {
369
        try {
156 ilm 370
            final TicketPrinter prt = getPOSConf().getTicketPrinterConfiguration1().createTicketPrinter();
18 ilm 371
            prt.openDrawer();
372
        } catch (Exception e) {
132 ilm 373
            e.printStackTrace();
18 ilm 374
        }
375
 
376
    }
83 ilm 377
 
378
    public void switchListMode() {
174 ilm 379
        this.caisseFrame.mainPanel.switchListMode();
83 ilm 380
 
381
    }
132 ilm 382
 
383
    public void setLCD(final String line1, final String line2, final int delay) {
384
        final TimerTask task = new TimerTask() {
385
            @Override
386
            public void run() {
387
                try {
174 ilm 388
                    CaisseControler.this.lcd.setMessage(line1, line2);
132 ilm 389
                } catch (Exception e) {
390
                    e.printStackTrace();
391
                }
392
            }
393
        };
394
        final Timer timer = new Timer("LCD : " + line1, true);
174 ilm 395
        timer.schedule(task, delay * 1000L);
132 ilm 396
 
397
    }
398
 
399
    public void setLCDDefaultDisplay(int delay) {
174 ilm 400
        if (this.t.getTotalInCents() > 0) {
132 ilm 401
            int count = 0;
174 ilm 402
            final List<TicketItem> articles = this.t.getItems();
403
            for (TicketItem pair : articles) {
404
                Article a = pair.getArticle();
405
                if (a.getSalesUnit() == null) {
406
                    count += pair.getQty().intValue();
407
                } else {
408
                    count++;
409
                }
132 ilm 410
            }
411
            String line1;
412
            if (count == 1) {
413
                line1 = "1 article";
414
            } else {
415
                line1 = count + " articles";
416
            }
174 ilm 417
            int cents = this.t.getTotalInCents();
132 ilm 418
            setLCD(line1, "Total : " + TicketCellRenderer.centsToString(cents), delay);
419
        } else {
156 ilm 420
            setLCD(getPOSConf().getLCDLine1(), getPOSConf().getLCDLine2(), delay);
132 ilm 421
        }
422
    }
423
 
424
    public void sendCBRequest(final Paiement p) {
425
 
156 ilm 426
        final String creditCardPort = getPOSConf().getCreditCardPort();
132 ilm 427
        if (creditCardPort != null && creditCardPort.trim().length() > 2) {
174 ilm 428
            final Thread thread = new Thread(new Runnable() {
132 ilm 429
 
430
                @Override
431
                public void run() {
432
                    try {
433
                        ConcertProtocol cp = new ConcertProtocol(creditCardPort);
434
                        boolean ok = cp.sendCardPayment(p.getMontantInCents(), ConcertProtocol.CURRENCY_EUR);
435
                        if (ok) {
436
                            JOptionPane.showMessageDialog(null, "Paiement CB OK");
437
                        } else {
438
                            JOptionPane.showMessageDialog(null, "Erreur paiement CB");
439
                        }
440
                    } catch (Throwable ex) {
441
                        JOptionPane.showMessageDialog(null, "Erreur terminal CB");
442
                    }
443
                }
444
            });
174 ilm 445
            thread.setDaemon(true);
446
            thread.start();
132 ilm 447
 
448
        }
449
 
450
    }
142 ilm 451
 
174 ilm 452
    public void setArticleHT(TicketItem item, BigDecimal ht) {
453
        final Article newArticle = new Article(item.getArticle());
454
        newArticle.updatePriceWithoutTax(ht);
455
        item.setArticle(newArticle);
456
        fire();
142 ilm 457
    }
458
 
174 ilm 459
    public void openPriceEditor(TicketItem item) {
460
        this.caisseFrame.showPriceEditor(item, this);
142 ilm 461
    }
462
 
463
    public void enableBarcodeReader() {
464
        this.r.setEnabled(true);
465
    }
466
 
467
    public void disableBarcodeReader() {
468
        this.r.setEnabled(false);
469
    }
144 ilm 470
 
471
    public boolean isTicketValid() {
174 ilm 472
        return (!this.t.getItems().isEmpty()) && ((this.getTotal() >= 0 && this.getPaidTotal() >= this.getTotal()) || (this.getTotal() < 0 && this.getPaidTotal() == this.getTotal()));
144 ilm 473
    }
149 ilm 474
 
475
    public Set<Integer> loadFavoriteProductsIds() {
476
        final TemplateNXProps nxprops = (TemplateNXProps) TemplateNXProps.getInstance();
477
        final File f = new File(nxprops.getDefaultStringValue(), "favorites.txt");
478
        System.out.println("CaisseControler.saveFavoriteProductsIds() loading favorites from " + f.getAbsolutePath());
479
        final Set<Integer> result = new HashSet<>();
480
        if (f.exists()) {
481
            try {
482
                String s = FileUtils.read(f);
483
                List<String> sIds = StringUtils.fastSplit(s, ',');
484
                for (String string : sIds) {
485
                    if (!string.isEmpty()) {
486
                        result.add(Integer.parseInt(string));
487
                    }
488
                }
489
            } catch (IOException e) {
490
                e.printStackTrace();
491
            }
492
        }
493
        return result;
494
    }
495
 
496
    public void saveFavoriteProductsIds(List<Article> products) {
497
        final TemplateNXProps nxprops = (TemplateNXProps) TemplateNXProps.getInstance();
498
        final File f = new File(nxprops.getDefaultStringValue(), "favorites.txt");
499
        System.out.println("CaisseControler.saveFavoriteProductsIds() saving favorites to " + f.getAbsolutePath());
500
        try (FileOutputStream fOut = new FileOutputStream(f);) {
501
            for (Article product : products) {
502
                fOut.write(String.valueOf(product.getId()).getBytes());
503
                fOut.write(',');
504
            }
505
            fOut.flush();
506
        } catch (IOException e) {
507
            e.printStackTrace();
508
        }
509
    }
510
 
174 ilm 511
    public void setTicketItemSelected(TicketItem item) {
512
        this.ticketItemSelected = item;
513
        if (item == null) {
514
            this.articleSelected = null;
515
        } else {
516
            this.articleSelected = item.getArticle();
517
            this.paiementSelected = null;
518
        }
519
        fire();
520
 
521
    }
522
 
523
    public TicketItem getTicketItemSelected() {
524
        return this.ticketItemSelected;
525
    }
526
 
527
    public void removeTicketItem(TicketItem item) {
528
        this.t.removeTicketItem(item);
529
        this.setTicketItemSelected(null);
530
    }
531
 
532
    public void cancel(Ticket ticket) {
533
        this.t.clear();
534
        // Annulation du ticket
535
        for (TicketItem a : ticket.getItems()) {
536
            final Article article = a.getArticle();
537
            TicketItem item = new TicketItem(article, a.getQty().multiply(new BigDecimal(-1)));
538
            this.t.addItem(item);
539
        }
180 ilm 540
        // Annulation de chaque paiement
541
        final List<Paiement> typesAdded = new ArrayList<>();
174 ilm 542
        for (Paiement p : ticket.getPaiements()) {
543
            final Paiement paiement = new Paiement(p.getType());
544
            paiement.setMontantInCents(-1 * p.getMontantInCents());
545
            this.t.addPaiement(paiement);
180 ilm 546
            typesAdded.add(p);
174 ilm 547
        }
180 ilm 548
        // On complete avec les autres types
549
        final List<Paiement> types = new ArrayList<>();
550
        types.add(new Paiement(Paiement.CB));
551
        types.add(new Paiement(Paiement.CHEQUE));
552
        types.add(new Paiement(Paiement.ESPECES));
553
        for (Paiement paiement : types) {
554
            boolean typeFound = false;
555
            for (Paiement p : typesAdded) {
556
                if (paiement.getType() == p.getType()) {
557
                    typeFound = true;
558
                }
559
            }
560
            if (!typeFound) {
561
                this.t.addPaiement(paiement);
562
            }
563
        }
174 ilm 564
 
565
        this.caisseFrame.showCaisse();
566
        fire();
567
    }
568
 
18 ilm 569
}