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