OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 156 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 2011 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.Categorie;
import org.openconcerto.erp.core.sales.pos.model.RegisterFiles.DifferentDayException;
import org.openconcerto.erp.core.sales.pos.model.Ticket;
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.element.SQLElementDirectory;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowAccessor;
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.StringUtils;

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.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
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;

    public CaissePanel(final CaisseFrame caisseFrame) throws Exception {
        final SQLElementDirectory dir = caisseFrame.getConf().getDirectory();

        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);
        loadArticles(dir);
        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.controler), 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) {
                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);
                }

            }
        });
        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;
    }

    private void loadArticles(final SQLElementDirectory dir) {
        final SQLSelect selUniteVente = new SQLSelect();
        selUniteVente.addSelectStar(dir.getElement("UNITE_VENTE").getTable());
        final Map<Integer, String> mapUniteVenteName = new HashMap<>();
        for (SQLRow row : SQLRowListRSH.execute(selUniteVente)) {
            mapUniteVenteName.put(row.getID(), row.getString("CODE"));
        }

        final Set<Integer> favoriteProductsIds = this.controler.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"));
        selArticle.setWhere(new Where(tableArticle.getField("OBSOLETE"), "=", Boolean.FALSE).and(new Where(tableArticle.getField("MASQUE_CAISSE"), "=", Boolean.FALSE)));

        final Categorie cUnclassified = new Categorie("Non classés", true);
        cUnclassified.setUnknown();
        for (SQLRow row : SQLRowListRSH.execute(selArticle)) {
            final int idFamilleArticle = row.getInt("ID_FAMILLE_ARTICLE");
            Categorie s1 = categoriesMap.get(idFamilleArticle);
            if (s1 == null) {
                s1 = cUnclassified;
                categoriesMap.put(idFamilleArticle, cUnclassified);
            }
            final String name = row.getString("NOM").trim();
            if (name.length() > 0) {
                final Article a = new Article(s1, name, row.getID());
                final String barcode = row.getString("CODE_BARRE");
                final String code = row.getString("CODE");
                a.setBarCode(StringUtils.isEmpty(barcode, true) ? code : barcode);
                a.setCode(code);
                a.setIdTaxe(row.getInt("ID_TAXE"));
                a.setPriceWithoutTax(row.getBigDecimal("PV_HT"));
                a.setPriceWithTax(row.getBigDecimal("PV_TTC"));
                a.setAdditionalCopyRequested(row.getBoolean("ADDITIONAL_TICKET_COPY"));
                if (row.getInt("ID_UNITE_VENTE") != 2) {
                    a.setSalesUnit(mapUniteVenteName.get(row.getInt("ID_UNITE_VENTE")));
                }
                final Integer idProduct = a.getId();
                if (favoriteProductsIds.contains(idProduct)) {
                    favoriteProducts.add(a);
                }

            }
        }
        Categorie.setFavoriteProducts(favoriteProducts);
        initCacheArticleMap(dir);
    }

    private static Map<Integer, SQLRowAccessor> cacheArticle = new HashMap<>();

    public static SQLRowAccessor getArticleRowValuesFromCache(int id) {
        return cacheArticle.get(id);
    }

    private void initCacheArticleMap(final SQLElementDirectory dir) {
        final SQLTable tableArt = dir.getElement("ARTICLE").getTable();
        SQLRowValues rowValsArt = new SQLRowValues(tableArt);
        rowValsArt.putNulls(tableArt.getFieldsName());
        final SQLTable tableArtCatComptable = dir.getElement("ARTICLE_CATEGORIE_COMPTABLE").getTable();
        SQLRowValues rowValsArtCatComptable = new SQLRowValues(tableArtCatComptable);
        rowValsArtCatComptable.putNulls(tableArtCatComptable.getFieldsName());
        rowValsArtCatComptable.put("ID_ARTICLE", rowValsArt);
        final SQLRowValues rowValsCaCompt = rowValsArtCatComptable.putRowValues("ID_CATEGORIE_COMPTABLE");
        rowValsCaCompt.putNulls(rowValsCaCompt.getTable().getFieldsName());

        final SQLRowValues rowValsFam = rowValsArt.putRowValues("ID_FAMILLE_ARTICLE");
        rowValsFam.putNulls(rowValsFam.getTable().getFieldsName());
        final SQLRowValues rowValuesFamP = rowValsFam.putRowValues("ID_FAMILLE_ARTICLE_PERE");
        rowValuesFamP.putNulls(rowValuesFamP.getTable().getFieldsName());

        SQLRowValuesListFetcher fetcher = SQLRowValuesListFetcher.create(rowValsArt);
        List<SQLRowValues> res = fetcher.fetch();
        for (SQLRowValues sqlRowValues : res) {
            cacheArticle.put(sqlRowValues.getID(), sqlRowValues);
        }

    }

    @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 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é");
        }
    }
}