OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 177 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved.
 * 
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each file.
 */
 
 package org.openconcerto.erp.core.sales.pos.ui;

import org.openconcerto.erp.core.sales.pos.model.Article;
import org.openconcerto.erp.core.sales.pos.model.ArticleCache;
import org.openconcerto.erp.core.sales.pos.model.Categorie;
import org.openconcerto.erp.core.sales.pos.model.RegisterFiles.DifferentDayException;
import org.openconcerto.erp.core.sales.pos.model.TarifQuantite;
import org.openconcerto.erp.core.sales.pos.model.Ticket;
import org.openconcerto.erp.core.sales.pos.model.TicketItem;
import org.openconcerto.erp.core.supplychain.stock.element.StockSQLElement;
import org.openconcerto.erp.preferences.TemplateNXProps;
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.element.SQLElementDirectory;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowListRSH;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLRowValuesListFetcher;
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.SQLTable.VirtualFields;
import org.openconcerto.sql.model.Where;
import org.openconcerto.utils.ExceptionHandler;
import org.openconcerto.utils.FileUtils;
import org.openconcerto.utils.StringUtils;
import org.openconcerto.utils.cc.ITransformer;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Rectangle2D;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.swing.JOptionPane;
import javax.swing.JPanel;

public class CaissePanel extends JPanel implements CaisseListener {

    public static final Color LIGHT_BLUE = new Color(83, 129, 172);
    public static final Color DARK_BLUE = new Color(0, 98, 159);

    private CaisseControler controler;

    private StatusBar st;
    private ArticleSelectorPanel articleSelectorPanel;
    private ArticleSearchPanel articleSearchPanel;

    private JPanel selector;
    final SQLElementDirectory dir;

    public CaissePanel(final CaisseFrame caisseFrame) throws Exception {
        this.dir = caisseFrame.getConf().getDirectory();
        loadArticles(this.dir);
        this.setLayout(new GridBagLayout());
        this.setBackground(Color.WHITE);
        this.setOpaque(isOpaque());
        GridBagConstraints c = new GridBagConstraints();

        // Column 1
        c.gridx = 0;
        c.gridy = 0;
        c.weightx = 0;
        c.weighty = 0;
        this.controler = new CaisseControler(caisseFrame);
        c.fill = GridBagConstraints.HORIZONTAL;
        this.st = createStatusBar(caisseFrame);
        this.add(this.st, c);

        TicketPanel t = new TicketPanel(this.controler);

        c.gridy++;
        c.weighty = 1;
        c.gridwidth = 1;
        c.anchor = GridBagConstraints.SOUTHWEST;
        c.fill = GridBagConstraints.NONE;
        this.add(t, c);
        // Column 2
        c.fill = GridBagConstraints.BOTH;
        c.gridx++;
        c.gridy = 0;
        c.weightx = 1;
        c.gridheight = 2;
        this.articleSelectorPanel = new ArticleSelectorPanel(this.controler);
        this.articleSearchPanel = new ArticleSearchPanel(this.controler);
        this.selector = this.articleSelectorPanel;
        this.add(this.selector, c);
        // Column 3
        c.gridx++;
        c.weightx = 0;
        this.add(new PaiementPanel(this), c);
        this.controler.addCaisseListener(this);
    }

    private StatusBar createStatusBar(final CaisseFrame caisseFrame) {
        final StatusBar s = new StatusBar();
        s.setLayout(new GridBagLayout());
        final GridBagConstraints c = new GridBagConstraints();
        if (this.controler.getPOSConf().getScreenWidth() < 1280) {
            c.insets = new Insets(0, 2, 0, 2);
        } else {
            c.insets = new Insets(0, 10, 0, 10);
        }
        c.gridx = 0;
        c.gridy = 0;
        c.fill = GridBagConstraints.NONE;
        c.anchor = GridBagConstraints.CENTER;
        c.weightx = 0;
        final POSButton bValidate = new POSButton("Valider");
        bValidate.setForeground(Color.WHITE);
        bValidate.setBackground(DARK_BLUE);
        s.add(bValidate, c);
        c.weightx = 1;
        c.gridx++;
        final POSButton bClients = new POSButton("Clients");
        bClients.setForeground(Color.WHITE);
        bClients.setBackground(DARK_BLUE);
        s.add(bClients, c);
        c.gridx++;
        final POSButton bMenu = new POSButton("Menu");
        bMenu.setForeground(Color.WHITE);
        bMenu.setBackground(DARK_BLUE);
        s.add(bMenu, c);
        bValidate.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                checkStock(getControler().getPOSConf().getDepotID(), new Runnable() {

                    @Override
                    public void run() {
                        if (caisseFrame.getPOSConf().askPostalCode()) {
                            caisseFrame.showPostalCodeFrame(CaissePanel.this);
                        } else {
                            validateTicket(caisseFrame);
                        }

                    }
                });

            }
        });
        bClients.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                // Clients
                try {
                    caisseFrame.showClients();
                } catch (Throwable ex) {
                    ExceptionHandler.handle("Erreur d'affichage du menu", ex);
                }

            }
        });

        bMenu.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                // Menu
                try {
                    caisseFrame.showMenu();
                } catch (Throwable ex) {
                    ExceptionHandler.handle("Erreur d'affichage du menu", ex);
                }

            }
        });

        return s;
    }

    protected void checkStock(int idDepot, Runnable runnable) {
        final List<TicketItem> items = this.controler.getItems();
        final List<Integer> articleIds = new ArrayList<>();
        final Map<Integer, TicketItem> mapTicketItem = new HashMap<>();
        for (TicketItem item : items) {
            articleIds.add(item.getArticle().getId());
            mapTicketItem.put(item.getArticle().getId(), item);
        }

        final SQLTable stockTable = this.dir.getElement(StockSQLElement.class).getTable();

        final SQLSelect selStock = new SQLSelect();
        selStock.addSelect(stockTable.getField("ID"));
        selStock.addSelect(stockTable.getField("ID_DEPOT_STOCK"));
        selStock.addSelect(stockTable.getField("QTE_REEL"));
        selStock.addSelect(stockTable.getField("ID_ARTICLE"));
        selStock.setWhere(Where.inValues(stockTable.getField("ID_ARTICLE"), articleIds).and(new Where(stockTable.getField("ID_DEPOT_STOCK"), "=", idDepot)));
        Map<TicketItem, Integer> missingQty = new HashMap<>();
        for (SQLRow row : SQLRowListRSH.execute(selStock)) {
            int idArticle = row.getInt("ID_ARTICLE");
            int qte = (int) Math.round(row.getFloat("QTE_REEL"));
            TicketItem item = mapTicketItem.get(idArticle);
            if (item == null) {
                System.err.println("Pas d'entrée dans STOCK pour l'article " + idArticle);
            } else {
                if (qte < item.getQty().intValue()) {
                    int delta = item.getQty().intValue() - qte;
                    missingQty.put(item, delta);
                }
            }

        }
        if (missingQty.isEmpty()) {
            runnable.run();
        } else {
            // Transfert de stock, Annulation ou bypass
            getControler().openStockErrorPanel(missingQty, runnable);
        }
    }

    public Set<Integer> loadFavoriteProductsIds() {
        final TemplateNXProps nxprops = (TemplateNXProps) TemplateNXProps.getInstance();
        final File f = new File(nxprops.getDefaultStringValue(), "favorites.txt");
        System.out.println("CaisseControler.saveFavoriteProductsIds() loading favorites from " + f.getAbsolutePath());
        final Set<Integer> result = new HashSet<>();
        if (f.exists()) {
            try {
                String s = FileUtils.read(f);
                List<String> sIds = StringUtils.fastSplit(s, ',');
                for (String string : sIds) {
                    if (!string.isEmpty()) {
                        result.add(Integer.parseInt(string));
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return result;
    }

    public void saveFavoriteProductsIds(List<Article> products) {
        final TemplateNXProps nxprops = (TemplateNXProps) TemplateNXProps.getInstance();
        final File f = new File(nxprops.getDefaultStringValue(), "favorites.txt");
        System.out.println("CaisseControler.saveFavoriteProductsIds() saving favorites to " + f.getAbsolutePath());
        try (FileOutputStream fOut = new FileOutputStream(f);) {
            for (Article product : products) {
                fOut.write(String.valueOf(product.getId()).getBytes());
                fOut.write(',');
            }
            fOut.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private List<Article> loadArticles(final SQLElementDirectory dir) {
        long t1 = System.currentTimeMillis();
        final SQLSelect selUniteVente = new SQLSelect();
        final SQLTable tableUniteVente = dir.getElement("UNITE_VENTE").getTable();
        selUniteVente.addSelect(tableUniteVente.getKey());
        selUniteVente.addSelect(tableUniteVente.getField("CODE"));
        final Map<Integer, String> mapUniteVenteName = new HashMap<>();
        for (SQLRow row : SQLRowListRSH.execute(selUniteVente)) {
            mapUniteVenteName.put(row.getID(), row.getString("CODE"));
        }

        final Set<Integer> favoriteProductsIds = loadFavoriteProductsIds();
        final List<Article> favoriteProducts = new ArrayList<>();

        final Map<Integer, Categorie> categoriesMap = new HashMap<>();

        SQLElement eltFam = dir.getElement("FAMILLE_ARTICLE");
        SQLElement eltArticle = dir.getElement("ARTICLE");

        final SQLSelect selFamille = new SQLSelect();
        selFamille.addSelectStar(eltFam.getTable());
        selFamille.addFieldOrder(eltFam.getTable().getField("CODE"));

        for (SQLRow row : SQLRowListRSH.execute(selFamille)) {
            // Map id -> Category
            final Categorie cP = categoriesMap.get(row.getInt("ID_FAMILLE_ARTICLE_PERE"));
            Categorie c;
            if (cP != null) {
                c = new Categorie(row.getString("NOM"));
                cP.add(c);
            } else {
                c = new Categorie(row.getString("NOM"), true);
            }

            categoriesMap.put(row.getID(), c);
        }

        final SQLSelect selArticle = new SQLSelect();
        final SQLTable tableArticle = eltArticle.getTable();
        selArticle.addAllSelect(tableArticle.getFields(VirtualFields.PRIMARY_KEY.union(VirtualFields.ARCHIVE)));
        selArticle.addAllSelect(tableArticle,
                Arrays.asList("ID_FAMILLE_ARTICLE", "NOM", "CODE", "CODE_BARRE", "ID_TAXE", "PV_HT", "PV_TTC", "ADDITIONAL_TICKET_COPY", "ID_UNITE_VENTE", "ID_ECO_CONTRIBUTION"));
        selArticle.setWhere(new Where(tableArticle.getField("OBSOLETE"), "=", Boolean.FALSE).and(new Where(tableArticle.getField("MASQUE_CAISSE"), "=", Boolean.FALSE)));
        selArticle.andWhere(new Where(tableArticle.getField("VIRTUEL"), "=", Boolean.FALSE));

        List<String> tablesDeclinaisons = new ArrayList<>();
        List<String> declinaisonsFieldNames = new ArrayList<>();
        for (SQLField f : tableArticle.getFields()) {
            if (f.getName().startsWith("ID_ARTICLE_DECLINAISON_")) {
                selArticle.addSelect(f);
                declinaisonsFieldNames.add(f.getName());
                tablesDeclinaisons.add(f.getName().substring("ID_".length()));
            }
        }

        final Categorie cUnclassified = new Categorie("Non classés", true);
        cUnclassified.setUnknown();

        // Fetch des declinaisons :
        // "ID_ARTICLE_DECLINAISON_COULEUR" : { {3 , "noir"] } ,
        // "ID_ARTICLE_DECLINAISON_TAILLE" : { {2 ,"XL"},{3,"XXL"}};
        Map<String, Map<Integer, String>> mapDeclinaisons = new HashMap<>();
        for (String table : tablesDeclinaisons) {
            SQLTable t = eltArticle.getTable().getTable(table);
            final SQLSelect selDecl = new SQLSelect();
            selDecl.addSelect(t.getKey());
            selDecl.addSelect(t.getField("NOM"));
            Map<Integer, String> m = new HashMap<>();
            mapDeclinaisons.put("ID_" + table, m);
            for (SQLRow row : SQLRowListRSH.execute(selDecl)) {
                m.put(row.getID(), row.getString("NOM"));
            }
        }
        // Fetch des articles
        final List<SQLRow> rArticles = SQLRowListRSH.execute(selArticle);
        List<Integer> idsArticles = new ArrayList<>(rArticles.size());
        for (SQLRow r : rArticles) {
            idsArticles.add(r.getID());
        }

        // Recuperation des promos
        final SQLTable tableArticleTairdPromotion = tableArticle.getTable("ARTICLE_TARIF_PROMOTION");
        final SQLTable tableTarifPromotion = tableArticle.getTable("TARIF_PROMOTION");
        SQLRowValues rTarifPromotion = new SQLRowValues(tableArticleTairdPromotion);
        rTarifPromotion.putNulls(tableArticleTairdPromotion.getFieldsName());
        rTarifPromotion.putRowValues("ID_TARIF_PROMOTION").putNulls("START", "END");
        rTarifPromotion.putRowValues("ID_ARTICLE").putNulls("ID_TAXE");
        Where where = new Where(tableArticleTairdPromotion.getField("ID_ARTICLE"), idsArticles);
        Calendar today = Calendar.getInstance();

        final SQLRowValuesListFetcher fetcher = SQLRowValuesListFetcher.create(rTarifPromotion);
        fetcher.appendSelTransf(new ITransformer<SQLSelect, SQLSelect>() {

            @Override
            public SQLSelect transformChecked(SQLSelect input) {
                input.andWhere(new Where(input.getAlias(tableTarifPromotion.getField("START")), "<", today));
                input.andWhere(new Where(input.getAlias(tableTarifPromotion.getField("END")), ">", today));
                return input;
            }
        });
        List<SQLRowValues> rPromotions = fetcher.fetch(where);
        long t1p = System.currentTimeMillis();
        // Tarifs : id article <-> liste des tarifs par quantité
        Map<Integer, List<TarifQuantite>> mapTarif = new HashMap<>();
        System.err.println("CaissePanel.loadArticles()" + rPromotions.size() + " promotions");
        for (SQLRowValues r : rPromotions) {
            final Integer foreignID = ((Integer) r.getObjectNoCheck("ID_ARTICLE"));
            List<TarifQuantite> list = mapTarif.get(foreignID);
            if (list == null) {
                list = new ArrayList<>();
                mapTarif.put(foreignID, list);
            }
            TarifQuantite t = new TarifQuantite(foreignID, r.getInt("QTE"), r.getBigDecimal("PV_HT"), r.getBigDecimal("PV_TTC"), r.getForeign("ID_ARTICLE").getInt("ID_TAXE"));
            list.add(t);
        }
        final List<Article> res = new ArrayList<>(rArticles.size());
        for (SQLRow row : rArticles) {
            final Integer idFamilleArticle = ((Integer) row.getObjectNoCheck("ID_FAMILLE_ARTICLE"));
            Categorie s1 = categoriesMap.get(idFamilleArticle);
            if (s1 == null) {
                s1 = cUnclassified;
                categoriesMap.put(idFamilleArticle, cUnclassified);
            }
            final String name = ((String) row.getObjectNoCheck("NOM")).trim();
            if (name.length() > 0) {
                final Article a = new Article(s1, name, row.getID());
                final String barcode = (String) row.getObjectNoCheck("CODE_BARRE");
                final String code = (String) row.getObjectNoCheck("CODE");
                a.setBarCode(StringUtils.isEmpty(barcode, true) ? code : barcode);
                a.setCode(code);
                a.setIdTaxe(((Integer) row.getObjectNoCheck("ID_TAXE")));
                a.setPriceWithoutTax((BigDecimal) row.getObjectNoCheck("PV_HT"));
                a.setPriceWithTax((BigDecimal) row.getObjectNoCheck("PV_TTC"));
                a.setAdditionalCopyRequested((Boolean) row.getObjectNoCheck("ADDITIONAL_TICKET_COPY"));
                final Integer idEcoContribution = ((Integer) row.getObjectNoCheck("ID_ECO_CONTRIBUTION"));
                if (idEcoContribution > 1)
                    a.setEcoTaxe((BigDecimal) row.getForeign("ID_ECO_CONTRIBUTION").getObjectNoCheck("TAUX"));
                final Integer idUniteVente = ((Integer) row.getObjectNoCheck("ID_UNITE_VENTE"));
                if (idUniteVente != 2) {
                    a.setSalesUnit(mapUniteVenteName.get(idUniteVente));
                }

                final Integer idProduct = a.getId();
                if (favoriteProductsIds.contains(idProduct)) {
                    favoriteProducts.add(a);
                }

                final List<TarifQuantite> promotions = mapTarif.get(row.getID());
                if (promotions != null) {
                    a.setTarifsPromotion(promotions);
                    for (TarifQuantite t : promotions) {
                        if (t.getQuantite() == 1) {
                            a.setIdTaxe(t.getIdTaxe());
                            a.setPriceWithoutTax(t.getPrixHT());
                            a.setPriceWithTax(t.getPrixTTC());
                            break;
                        }
                    }

                }

                for (String f : declinaisonsFieldNames) {
                    Integer idArtDeclinaison = (Integer) row.getObjectNoCheck(f);
                    if (idArtDeclinaison != null && idArtDeclinaison > 1) {
                        Map<Integer, String> mm = mapDeclinaisons.get(f);
                        if (mm != null) {
                            String v = mm.get(idArtDeclinaison);
                            a.addDeclinaison(f.substring("ID_ARTICLE_DECLINAISON_".length()), v);
                        }
                    }
                }

                res.add(a);

            }
        }
        long t2p = System.currentTimeMillis();

        Categorie.setFavoriteProducts(favoriteProducts);
        ArticleCache.initCache(dir);
        ArticleCache.getInstance().preloadCacheArticleMap(new ArrayList<>(favoriteProductsIds));
        long t2 = System.currentTimeMillis();
        System.err.println("CaissePanel.loadArticles() " + res.size() + " in " + (t2 - t1) + " ms : process: " + (t2p - t1p) + " ms");
        return res;
    }

    @Override
    public void paint(Graphics g) {
        System.err.println("CaissePanel.paint()" + this.getWidth() + " x " + this.getHeight());
        super.paint(g);
        Graphics2D g2 = (Graphics2D) g;
        g.setFont(new Font("Arial", Font.PLAIN, 32));
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        if (this.controler.isClientDefined()) {
            g.setColor(CaissePanel.DARK_BLUE);
            g.setFont(new Font("Arial", Font.PLAIN, 28));
            g.drawString(this.controler.getClient().getFullName(), 20, 75);
            g.setColor(Color.GRAY);
            g.setFont(g.getFont().deriveFont(18f));
            g.drawString("Solde : " + new DecimalFormat("#0.00").format(this.controler.getClient().getSolde()), 20, 120);

        }
        int xPos = 300;
        if (this.controler.getPOSConf().getScreenWidth() < 1280) {
            xPos = 270;
        }
        // Prix
        int x = xPos;
        int y = 110;
        String euros;
        String cents;
        Rectangle2D r;
        g.setColor(Color.BLACK);
        if (this.controler.isClientDefined()) {
            g.setFont(g.getFont().deriveFont(46f));
            y += 10;
        } else {
            g.setFont(g.getFont().deriveFont(66f));
        }
        final int total = this.controler.getTotal();
        euros = CaisseControler.getEuros(total) + ".";
        cents = CaisseControler.getCents(total);
        r = g.getFontMetrics().getStringBounds(euros, g);
        x = x - (int) r.getWidth();
        g.drawString(euros, x, y);
        g.setFont(g.getFont().deriveFont(40f));
        g.drawString(cents, x + (int) r.getWidth(), y);
        // Paiement
        y += 40;
        x = xPos;
        final int paye = this.controler.getPaidTotal();
        euros = CaisseControler.getEuros(paye) + ".";
        cents = CaisseControler.getCents(paye);

        g.setFont(g.getFont().deriveFont(18f));
        Rectangle2D r2 = g.getFontMetrics().getStringBounds("Payé", g);
        if (paye >= total) {
            g.setColor(Color.DARK_GRAY);
        } else {
            g.setColor(Color.ORANGE);
        }
        g.setFont(g.getFont().deriveFont(32f));
        r = g.getFontMetrics().getStringBounds(euros, g);
        g.drawString(euros, x - (int) r.getWidth(), y);
        g.setFont(g.getFont().deriveFont(24f));
        g.drawString(cents, x, y);
        g.setFont(g.getFont().deriveFont(18f));
        g.setColor(Color.GRAY);
        g.drawString("Payé", x - (int) r2.getWidth() - (int) r.getWidth() - 10, y);
        // A rendre
        final boolean minimalHeight = this.getHeight() < 750;
        if (!minimalHeight) {
            y += 40;
            x = xPos;
        } else {
            x = 140;
        }
        int aRendre = paye - total;
        if (aRendre != 0) {
            String label;
            if (aRendre > 0) {
                label = "Rendu";
            } else {
                if (!minimalHeight) {
                    label = "Reste à payer";
                } else {
                    label = "Doit";
                }
                aRendre = -aRendre;
            }

            euros = CaisseControler.getEuros(aRendre) + ".";
            cents = CaisseControler.getCents(aRendre);

            g.setFont(g.getFont().deriveFont(18f));
            Rectangle2D r3 = g.getFontMetrics().getStringBounds(label, g);

            g.setColor(Color.DARK_GRAY);
            g.setFont(g.getFont().deriveFont(32f));
            r = g.getFontMetrics().getStringBounds(euros, g);
            g.drawString(euros, x - (int) r.getWidth(), y);
            g.setFont(g.getFont().deriveFont(24f));
            g.drawString(cents, x, y);
            g.setFont(g.getFont().deriveFont(18f));
            g.setColor(Color.GRAY);
            g.drawString(label, x - (int) r3.getWidth() - (int) r.getWidth() - 10, y);

        }

    }

    @Override
    public void caisseStateChanged() {
        repaint();
    }

    public void switchListMode() {

        GridBagConstraints c = ((GridBagLayout) this.getLayout()).getConstraints(this.selector);
        this.remove(this.selector);

        if (this.selector == this.articleSearchPanel) {
            this.selector = this.articleSelectorPanel;
        } else {
            this.selector = this.articleSearchPanel;
        }
        System.err.println("CaissePanel.switchListMode()" + this.selector.getMinimumSize() + " " + this.selector.getPreferredSize() + " " + this.selector.getMaximumSize());
        this.add(this.selector, c);
        this.validate();
        this.repaint();

    }

    public boolean isModeSearch() {
        return this.selector == this.articleSearchPanel;
    }

    public CaisseControler getControler() {
        return this.controler;
    }

    public void validateTicket(final CaisseFrame caisseFrame) {
        final Ticket savedReceipt;
        try {
            savedReceipt = CaissePanel.this.controler.saveAndClearTicket(caisseFrame.getFiles(), caisseFrame.getConf().getDirectory());
        } catch (DifferentDayException ex) {
            JOptionPane.showMessageDialog(CaissePanel.this, "Impossible de laisser la caisse ouverte plusieurs jours. Veuillez la clôturer pour pouvoir faire de nouveaux tickets.", "Erreur",
                    JOptionPane.ERROR_MESSAGE);
            return;
        } catch (Throwable ex) {
            ExceptionHandler.handle(CaissePanel.this, "Erreur de sauvegarde des informations du ticket", ex);
            return;
        }
        if (savedReceipt != null) {
            // Valider
            CaissePanel.this.controler.setLCD("Impression de", "votre ticket...", 0);
            try {
                caisseFrame.getPOSConf().print(savedReceipt, (savedReceipt.isAdditionnalCopyRequested() ? 1 : 0));
            } catch (UnsatisfiedLinkError ex) {
                JOptionPane.showMessageDialog(CaissePanel.this, "Erreur de configuration de la liaison à l'imprimante");
            } catch (Throwable ex) {
                ex.printStackTrace();
                JOptionPane.showMessageDialog(CaissePanel.this, "Erreur d'impression du ticket");
            }

            CaissePanel.this.controler.setLCDDefaultDisplay(2);
        } else {
            System.err.println("CaissePanel.validateTicket() ticket non sauvé");
        }
    }
}