OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

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