/*----------------------------------------------------------------------------
Copyright (c)2000 TogetherSoft LLC. Patents pending. All rights reserved.
----------------------------------------------------------------------------*/
package UserInterface;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;
import javax.swing.table.TableColumn;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Vector;
import ProblemDomain.CashSale; // PD Object
import ProblemDomain.CashSaleDetail; // PD Object
import ProblemDomain.ProductDesc; // PD Object
import ProblemDomain.InsuffPaymentException;
/**
* This is a sample that was based on the JMCU CashSale component built into the Together tool. This presents a simple GUI and
* shows how we can tie it into the PD classes.
* @athor Jon Kern
*/
public class POSFrame extends JFrame {
// Problem Domain Object
CashSale currentSale = new CashSale();
// Dummy list of store items
ProductDesc[] products;
// Sale Detail Table Column Header
private final String[] colNames = {"Item", "Name", "Unit", "Qty", "Price"};
private final static int ADDED_DETAIL = 0;
private final static int REMOVED_DETAIL = 1;
// Dummy list of cashiers
private final static String[] cashiers =
{ "Jim","Lucy","Steve","Sarah","Jon","Buddy","Bettie","Sue","John","Ted"};
NumberFormat currencyFormat = NumberFormat.getCurrencyInstance();
JMenuBar menuBar1 = new JMenuBar();
JMenu menuFile = new JMenu();
JMenuItem menuFileExit = new JMenuItem();
JMenu menuHelp = new JMenu();
JMenuItem menuHelpAbout = new JMenuItem();
JToolBar toolBar = new JToolBar();
JButton jButtonHelp = new JButton();
ImageIcon imageHelp;
JLabel statusBar = new JLabel();
BorderLayout borderLayout1 = new BorderLayout();
JScrollPane jScrollPaneDetails = new JScrollPane();
JTable jTableSaleDetails = new JTable();
JPanel jPanel1 = new JPanel();
BorderLayout borderLayout2 = new BorderLayout();
JPanel jPanelScan = new JPanel();
JButton jButtonScan = new JButton();
JTextField jTextFieldItem = new JTextField();
JPanel jPanelDetails = new JPanel();
JButton jButtonNewSale = new JButton();
JButton jButtonCancel = new JButton();
JPanel jPanelCompleteSale = new JPanel();
JButton jButtonPayment = new JButton();
JButton jButtonTotal = new JButton();
BorderLayout borderLayout3 = new BorderLayout();
JTextField jTextFieldSubTotal = new JTextField();
GridLayout gridLayout1 = new GridLayout();
JLabel jLabel1 = new JLabel();
JLabel jLabel2 = new JLabel();
JTextField jTextFieldTax = new JTextField();
JLabel jLabel3 = new JLabel();
JTextField jTextFieldPayment = new JTextField();
JTextField jTextFieldTotal = new JTextField();
JTextField jTextFieldChange = new JTextField();
JLabel jLabel4 = new JLabel();
JLabel jLabelCashier = new JLabel();
JLabel jLabel6 = new JLabel();
JLabel jLabelTimeStamp = new JLabel();
JLabel jLabel8 = new JLabel();
//Construct the frame
public POSFrame() {
setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); //do not hide the frame, maybe the user will press "Cancel"
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
fileExit_actionPerformed(null);
}
});
try {
setUpProducts();
jbInit();
}
catch(Exception e) {
e.printStackTrace();
}
}
// Set up the product "database"
private void setUpProducts() {
products = new ProductDesc[10];
products[0] = new ProductDesc("1", "Pepsi 24-pack", "Pepsi 24", new BigDecimal(3.99));
products[1] = new ProductDesc("2", "Lays Ridges", "Lays", new BigDecimal(1.99));
products[2] = new ProductDesc("3", "Vienna Sausages", "Vienna Sausages", new BigDecimal(2.99));
products[3] = new ProductDesc("4", "White Popcorn", "White Popcorn", new BigDecimal(1.29));
products[4] = new ProductDesc("5", "Soy Burgers", "Soy Burger", new BigDecimal(5.99));
products[5] = new ProductDesc("6", "Cat Chow", "Cat Chow", new BigDecimal(9.99));
products[6] = new ProductDesc("7", "Puppy Chow", "Puppy Chow", new BigDecimal(12.99));
products[7] = new ProductDesc("8", "Finch Food", "Finch Food", new BigDecimal(1.59));
products[8] = new ProductDesc("9", "Rice Krispies", "Rice Krispies", new BigDecimal(3.29));
products[9] = new ProductDesc("10", "Fruit Loops", "Fruit Loops", new BigDecimal(3.49));
}
//Component initialization
private void jbInit() throws Exception {
try {
imageHelp = new ImageIcon(UserInterface.POSFrame.class.getResource("/UserInterface/help.gif"));
}
catch(Exception e) {
}
this.getContentPane().setLayout(borderLayout1);
this.setSize(new Dimension(459, 396));
this.setTitle("Point Of Sale");
statusBar.setText(" ");
menuFile.setText("File");
menuFileExit.setText("Exit");
menuFileExit.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
fileExit_actionPerformed(e);
}
});
menuHelp.setText("Help");
menuHelpAbout.setText("About");
menuHelpAbout.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
helpAbout_actionPerformed(e);
}
});
if (imageHelp != null)
jButtonHelp.setIcon(imageHelp);
jButtonHelp.setToolTipText("Help");
jPanel1.setLayout(borderLayout2);
jButtonScan.setToolTipText("Scan next item");
jButtonScan.setMnemonic('S');
jButtonScan.setText("Scan");
jButtonScan.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
jButtonScan_actionPerformed(e);
}
});
jTextFieldItem.setPreferredSize(new Dimension(182, 21));
jTextFieldItem.setText("<item>");
jPanelScan.setBorder(BorderFactory.createRaisedBevelBorder());
jTableSaleDetails.setBorder(BorderFactory.createLineBorder(Color.black));
jButtonNewSale.setToolTipText("Start a new sale");
jButtonNewSale.setMnemonic('N');
jButtonNewSale.setText("New Sale");
jButtonNewSale.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
jButtonNewSale_actionPerformed(e);
}
});
jButtonCancel.setToolTipText("Cancel current sale");
jButtonCancel.setMnemonic('A');
jButtonCancel.setText("Cancel");
jButtonCancel.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
jButtonCancel_actionPerformed(e);
}
});
jButtonPayment.setFont(new java.awt.Font("Dialog", 1, 12));
jButtonPayment.setToolTipText("Enter payment amount");
jButtonPayment.setMnemonic('P');
jButtonPayment.setText("Payment");
jButtonPayment.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
jButtonPayment_actionPerformed(e);
}
});
jButtonTotal.setFont(new java.awt.Font("Dialog", 1, 12));
jButtonTotal.setToolTipText("Calculate Total");
jButtonTotal.setMnemonic('T');
jButtonTotal.setText("Total");
jButtonTotal.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
jButtonTotal_actionPerformed(e);
}
});
jPanelDetails.setLayout(borderLayout3);
jTextFieldSubTotal.setText("0.00");
jTextFieldSubTotal.setHorizontalAlignment(SwingConstants.RIGHT);
jPanelCompleteSale.setLayout(gridLayout1);
gridLayout1.setColumns(2);
gridLayout1.setHgap(3);
gridLayout1.setRows(5);
jLabel1.setFont(new java.awt.Font("Dialog", 1, 12));
jLabel1.setHorizontalAlignment(SwingConstants.RIGHT);
jLabel1.setText("SUBTOTAL:");
jLabel2.setFont(new java.awt.Font("Dialog", 1, 12));
jLabel2.setHorizontalAlignment(SwingConstants.RIGHT);
jLabel2.setText("Tax:");
jLabel3.setText(" ");
jTextFieldTax.setText("0.00");
jTextFieldTax.setHorizontalAlignment(SwingConstants.RIGHT);
jTextFieldPayment.setText("0.00");
jTextFieldPayment.setHorizontalAlignment(SwingConstants.RIGHT);
jTextFieldTotal.setText("0.00");
jTextFieldTotal.setHorizontalAlignment(SwingConstants.RIGHT);
jTextFieldChange.setText("0.00");
jTextFieldChange.setHorizontalAlignment(SwingConstants.RIGHT);
jLabel4.setText(" ");
jLabelCashier.setText(" ");
jLabel6.setText(" ");
jLabelTimeStamp.setText(" ");
jLabel8.setText("Change:");
jLabel8.setFont(new java.awt.Font("Dialog", 1, 12));
jLabel8.setHorizontalAlignment(SwingConstants.RIGHT);
jScrollPaneDetails.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
jScrollPaneDetails.setBorder(BorderFactory.createLineBorder(Color.black));
jPanelDetails.setBackground(Color.yellow);
toolBar.add(jButtonNewSale);
toolBar.add(jButtonCancel);
toolBar.add(jButtonHelp);
menuFile.add(menuFileExit);
menuHelp.add(menuHelpAbout);
menuBar1.add(menuFile);
menuBar1.add(menuHelp);
this.setJMenuBar(menuBar1);
this.getContentPane().add(toolBar, BorderLayout.NORTH);
this.getContentPane().add(statusBar, BorderLayout.SOUTH);
this.getContentPane().add(jPanel1, BorderLayout.CENTER);
jPanel1.add(jPanelScan, BorderLayout.NORTH);
jPanelScan.add(jButtonScan, null);
jPanelScan.add(jTextFieldItem, null);
jPanel1.add(jPanelDetails, BorderLayout.CENTER);
jPanelDetails.add(jScrollPaneDetails, BorderLayout.NORTH);
jScrollPaneDetails.getViewport().add(jTableSaleDetails, null);
jPanel1.add(jPanelCompleteSale, BorderLayout.SOUTH);
jPanelCompleteSale.add(jLabel3, null);
jPanelCompleteSale.add(jLabel1, null);
jPanelCompleteSale.add(jTextFieldSubTotal, null);
jPanelCompleteSale.add(jLabel4, null);
jPanelCompleteSale.add(jLabel2, null);
jPanelCompleteSale.add(jTextFieldTax, null);
jPanelCompleteSale.add(jLabel6, null);
jPanelCompleteSale.add(jButtonTotal, null);
jPanelCompleteSale.add(jTextFieldTotal, null);
jPanelCompleteSale.add(jLabelCashier, null);
jPanelCompleteSale.add(jButtonPayment, null);
jPanelCompleteSale.add(jTextFieldPayment, null);
jPanelCompleteSale.add(jLabelTimeStamp, null);
jPanelCompleteSale.add(jLabel8, null);
jPanelCompleteSale.add(jTextFieldChange, null);
// Initialize Detail Table
initDetailTable();
// Initialize widgets with data
loadGUIWithPD();
// Keep buttons properly grayed out at the start
initButtons();
this.toolBar.getRootPane().setDefaultButton(jButtonScan);
}
/** Keep buttons properly grayed out at the start. */
private void initButtons() {
jButtonNewSale.setEnabled(true);
jButtonCancel.setEnabled(false);
jButtonScan.setEnabled(true);
jButtonPayment.setEnabled(false);
jButtonTotal.setEnabled(false);
} // END initButtons()
private void loadGUIWithPD() {
jTextFieldItem.setText("");
jLabelTimeStamp.setText(DateFormat.getDateInstance().format(currentSale.getTime()));
jLabelCashier.setText(cashiers[(int)(Math.random() * 10.0)]);
jTextFieldSubTotal.setText("");
jTextFieldTax.setText("");
jTextFieldTotal.setText("");
jTextFieldPayment.setText("");
jTextFieldChange.setText("");
jTextFieldChange.setForeground(Color.black);
} // END loadGUIWithPD()
private void initDetailTable() {
TableDataModel dataModel = new TableDataModel();
jTableSaleDetails.setModel(dataModel);
jTableSaleDetails.setPreferredScrollableViewportSize(new Dimension(300, 70));
TableColumn column = null;
// {"Item", "Name", "Unit", "Qty", "Price"};
for (int i = 0; i < jTableSaleDetails.getColumnCount(); ++i) {
column = jTableSaleDetails.getColumnModel().getColumn(i);
switch (i) {
case 0: // Item
column.setPreferredWidth(20);
break;
case 1: // Name
column.setPreferredWidth(100);
break;
case 2: // Unit
column.setPreferredWidth(30);
break;
case 3: // Qty
column.setPreferredWidth(20);
break;
case 4: // Price
column.setPreferredWidth(30);
break;
default:
column.setPreferredWidth(50);
break;
} // ENDSWITCH (col)
} // ENDFOR (i)
}
//File | Exit action performed
public void fileExit_actionPerformed(ActionEvent e) {
if (preCancelCheck() == JOptionPane.OK_OPTION) {
System.exit(0);
}
}
//Help | About action performed
public void helpAbout_actionPerformed(ActionEvent e) {
POSFrame_AboutBox dlg = new POSFrame_AboutBox(this);
Dimension dlgSize = dlg.getPreferredSize();
Dimension frmSize = getSize();
Point loc = getLocation();
dlg.setLocation((frmSize.width - dlgSize.width) / 2 + loc.x, (frmSize.height - dlgSize.height) / 2 + loc.y);
dlg.setModal(true);
dlg.show();
}
//=================================================
/**
* Create a model of the data. NOTE: We could have built our own local vector to store
* the sale detail objects, but we are using the GUI object's own instance of a CashSale object to access
* the information pertaining to its collection of SaleDetails.
*/
class TableDataModel extends AbstractTableModel {
// These 3 methods always need to be implemented.
public int getColumnCount() {
return colNames.length;
}
public int getRowCount() {
Vector detailList = currentSale.getDetailList();
if (detailList != null) {
return detailList.size();
}
else {
return 0;
}
} // END getRowCount()
public Object getValueAt(int row, int col) {
// First get the detail item "row"
CashSaleDetail detail = (CashSaleDetail)currentSale.getDetailList().elementAt(row);
ProductDesc product = detail.getProductDesc();
Object data; // Lets table auto-format native data types
// Now do the grunt work to map a column to an attribute
// {"Item", "Name", "Unit", "Qty", "Price"};
switch (col) {
case 0: // Item
data = product.getItemNumber();
break;
case 1: // Name
data = product.getName();
break;
case 2: // Unit Price
data = currencyFormat.format(product.getPrice());
break;
case 3: // Qty
data = new Integer(detail.getQty());
break;
case 4: // Price
data = currencyFormat.format(product.calcPriceForQty(detail.getQty()));
break;
default:
data = "Unknown!";
break;
} // ENDSWITCH
return data;
} // END getValueAt()
// The default implementations of these methods in
// AbstractTableModel would work, but we can refine them.
public String getColumnName(int column) {
return colNames[column];
}
// Don't allow any editing
public boolean isCellEditable(int row, int col) {
return false;
}
public void setValueAt(Object aValue, int row, int column) {
// data[row][column] = aValue;
}
/** Update the table with the changed data model. */
public void dataChanged(int changeType, int index) {
if (changeType == ADDED_DETAIL) {
// Paint the part of the table that changed
fireTableDataChanged();
// fireTableRowsInserted(index, index);
}
else {
// Use this to redo the whole table
// since all rows are effectively deleted!
fireTableDataChanged();
}
} // END dataChanged()
/** JTable uses this method to determine the default renderer/ editor for each cell to improve the display. */
public Class getColumnClass(int c) {
return getValueAt(0, c).getClass();
}
}; // ENDCLASS TableDataModel
//=================================================
private int preCancelCheck() {
if (currentSale.isPending()) {
return JOptionPane.showConfirmDialog(null, "OK to Cancel Current Sale?", "Sale Pending",
JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE);
}
else {
return JOptionPane.OK_OPTION;
}
} // END preCancelCheck()
private void cancelSale() {
newSale();
/*
if ( preCancelCheck() == JOptionPane.OK_OPTION )
{
newSale();
}
else
{
// do nothing
}*/
} // END cancelSale()
/**
* Start off with a fresh CashSale object for the transaction. It will reflect the current timestamp and have no details.
*/
private void newSale() {
if (!currentSale.isCompleted()) {
if (preCancelCheck() != JOptionPane.OK_OPTION) {
return;
}
}
currentSale = new CashSale();
TableDataModel model = (TableDataModel)jTableSaleDetails.getModel();
model.dataChanged(REMOVED_DETAIL, 0);
// Reset widgets
loadGUIWithPD();
initButtons();
} // END newSale()
private void makePayment() {
String paymentValue = JOptionPane.showInputDialog("Please enter payment");
// See if input is valid number
try {
NumberFormat numberFormat = NumberFormat.getInstance();
numberFormat.setMinimumFractionDigits(2);
double n = numberFormat.parse(paymentValue).doubleValue();
// Compute change due
BigDecimal change = currentSale.makeCashSale(new BigDecimal(paymentValue));
jTextFieldChange.setText(currencyFormat.format(change));
jTextFieldChange.setForeground(Color.red);
jTextFieldPayment.setText(currencyFormat.format(new BigDecimal(paymentValue)));
// Once payment is made, you can no
// longer cancel or hit payment.
jButtonPayment.setEnabled(false);
jButtonCancel.setEnabled(false);
this.getRootPane().setDefaultButton(jButtonNewSale);
}
catch(ParseException pe) {
JOptionPane.showMessageDialog(null, pe.getMessage(), "Illegal Text Format", JOptionPane.ERROR_MESSAGE);
makePayment();
}
catch(InsuffPaymentException ipe) {
JOptionPane.showMessageDialog(null, ipe.getMessage(), "Insufficient Payment", JOptionPane.ERROR_MESSAGE);
}
} // END makePayment()
private void totalSale() {
jTextFieldSubTotal.setText(currencyFormat.format(currentSale.calcSubtotal()));
jTextFieldTax.setText(currencyFormat.format(currentSale.calcTax()));
jTextFieldTotal.setText(currencyFormat.format(currentSale.calcTotal()));
// Once you total the sale, you can
// then make a payment. But we'll disable
// the ability to scan in more or re-calc
// total (maybe a bit unrealistic).
jButtonPayment.setEnabled(true);
jButtonTotal.setEnabled(false);
jButtonScan.setEnabled(false);
} // END totalSale()
/** Use this to simulate scanning items */
private void scan() {
int pseudoIndex = (int)(Math.random() * 10.0);
ProductDesc prod = products[pseudoIndex];
jTextFieldItem.setText(prod.getDescription());
CashSaleDetail detail = new CashSaleDetail(prod);
// Now add it to the current sale
currentSale.addSaleDetail(detail);
// And notify the table
addDetailToTable();
jButtonCancel.setEnabled(true);
jButtonTotal.setEnabled(true);
} // END scan()
private void addDetailToTable() {
int index = currentSale.getDetailList().size() - 1;
TableDataModel model = (TableDataModel)jTableSaleDetails.getModel();
model.dataChanged(ADDED_DETAIL, index);
} // END addDetailToTable()
/* =======================
* BUTTON EVENTS
* ======================= */
void jButtonNewSale_actionPerformed(ActionEvent e) {
newSale();
}
void jButtonCancel_actionPerformed(ActionEvent e) {
cancelSale();
}
void jButtonPayment_actionPerformed(ActionEvent e) {
makePayment();
}
void jButtonTotal_actionPerformed(ActionEvent e) {
totalSale();
}
void jButtonScan_actionPerformed(ActionEvent e) {
scan();
}
} // ENDCLASS POSFrame
|