Dépôt officiel du code source de l'ERP OpenConcerto
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é");
}
}
}