summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid McMackins II <contact@mcmackins.org>2016-09-10 18:31:13 -0500
committerDavid McMackins II <contact@mcmackins.org>2016-09-10 18:31:13 -0500
commit36b78e77945fa0b948765e50457d685d820c6e89 (patch)
tree7902f174a99374e3a1dc2203860613881ff88523
parentf64e456379ec43ccf87d88ee4371da312d70378b (diff)
Add main table
-rw-r--r--src/com/delwink/icebox/Config.java4
-rw-r--r--src/com/delwink/icebox/DataDir.java2
-rw-r--r--src/com/delwink/icebox/Inventory.java213
-rw-r--r--src/com/delwink/icebox/InventoryItem.java98
-rw-r--r--src/com/delwink/icebox/MainWindow.java29
-rw-r--r--src/com/delwink/icebox/Order.java85
-rw-r--r--src/com/delwink/icebox/QuantityUpdate.java117
-rw-r--r--src/com/delwink/icebox/lang/en_US.lang2
-rw-r--r--src/com/delwink/icebox/table/MainWindowTableModel.java80
9 files changed, 623 insertions, 7 deletions
diff --git a/src/com/delwink/icebox/Config.java b/src/com/delwink/icebox/Config.java
index 66ae602..d571415 100644
--- a/src/com/delwink/icebox/Config.java
+++ b/src/com/delwink/icebox/Config.java
@@ -45,8 +45,8 @@ public final class Config {
PROPERTIES.setProperty("setlaf", "y");
if (CONFIG_FILE.exists()) {
- try {
- PROPERTIES.load(new FileInputStream(CONFIG_FILE));
+ try (FileInputStream stream = new FileInputStream(CONFIG_FILE)) {
+ PROPERTIES.load(stream);
} catch (IOException ex) {
System.err.println("Failed to load config file");
}
diff --git a/src/com/delwink/icebox/DataDir.java b/src/com/delwink/icebox/DataDir.java
index acf61f4..e1986ad 100644
--- a/src/com/delwink/icebox/DataDir.java
+++ b/src/com/delwink/icebox/DataDir.java
@@ -25,6 +25,8 @@ import java.io.FileNotFoundException;
* @author David McMackins II
*/
public final class DataDir {
+ public static final File INVENTORY_FILE = getDataFile("inventory.xml");
+
private static File rootDir = null;
/**
diff --git a/src/com/delwink/icebox/Inventory.java b/src/com/delwink/icebox/Inventory.java
new file mode 100644
index 0000000..f4472c9
--- /dev/null
+++ b/src/com/delwink/icebox/Inventory.java
@@ -0,0 +1,213 @@
+/*
+ * IceBox - inventory management software for restaurants
+ * Copyright (C) 2016 Delwink, LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, version 3 only.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.delwink.icebox;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+/**
+ * An inventory of items.
+ * @author David McMackins II
+ */
+public class Inventory {
+ protected final List<Order> ORDERS;
+ protected final List<QuantityUpdate> UPDATES;
+ protected final Set<InventoryItem> ITEMS;
+
+ /**
+ * Creates a new empty inventory.
+ */
+ public Inventory() {
+ ORDERS = new ArrayList<>();
+ UPDATES = new ArrayList<>();
+ ITEMS = new TreeSet<>();
+ }
+
+ /**
+ * Loads an inventory from an XML input stream.
+ * @param input An XML input stream.
+ */
+ public Inventory(InputStream input)
+ throws ParserConfigurationException, SAXException, IOException {
+ this();
+
+ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+ DocumentBuilder builder = dbf.newDocumentBuilder();
+ Document doc = builder.parse(input);
+
+ Element root = doc.getDocumentElement();
+ root.normalize();
+
+ { // load item definitions
+ NodeList items = root.getElementsByTagName("item");
+ for (int i = 0; i < items.getLength(); ++i) {
+ Element item = (Element) items.item(i);
+
+ int id = Integer.parseInt(item.getAttribute("id"));
+ int reorderAt = Integer.parseInt(item.getAttribute("reorder"));
+ String name = item.getAttribute("name");
+ String unit = item.getAttribute("unit");
+
+ addNewItem(new InventoryItem(id, name, unit, reorderAt));
+ }
+ }
+
+ { // load orders
+ NodeList orders = root.getElementsByTagName("order");
+ for (int i = 0; i < orders.getLength(); ++i) {
+ Element order = (Element) orders.item(i);
+
+ String orderNumber = order.getAttribute("num");
+ Date orderDate = new Date(Long.parseLong(order.getAttribute("date")));
+ Order newOrder = new Order(orderNumber, orderDate);
+
+ NodeList items = order.getElementsByTagName("item");
+ for (int j = 0; j < items.getLength(); ++j) {
+ Element item = (Element) items.item(j);
+
+ int id = Integer.parseInt(item.getAttribute("id"));
+ int qty = Integer.parseInt(item.getAttribute("qty"));
+
+ newOrder.addItem(id, qty);
+ }
+
+ addOrder(newOrder);
+ }
+ }
+
+ { // load updates
+ NodeList updates = root.getElementsByTagName("update");
+ for (int i = 0; i < updates.getLength(); ++i) {
+ Element update = (Element) updates.item(i);
+
+ Date date = new Date(Long.parseLong(update.getAttribute("date")));
+ QuantityUpdate newUpdate = new QuantityUpdate(date);
+
+ NodeList records = update.getElementsByTagName("record");
+ for (int j = 0; j < records.getLength(); ++j) {
+ Element record = (Element) records.item(j);
+
+ int id = Integer.parseInt(record.getAttribute("item"));
+ int sold = Integer.parseInt(record.getAttribute("sold"));
+ int waste = Integer.parseInt(record.getAttribute("waste"));
+
+ newUpdate.addItem(id, sold, waste);
+ }
+
+ addUpdate(newUpdate);
+ }
+ }
+ }
+
+ /**
+ * Outputs the inventory data as XML.
+ * @param output The output target.
+ */
+ public void saveXml(OutputStream output) {
+ PrintWriter writer = new PrintWriter(output);
+
+ writer.println("<!-- Generated by IceBox. DO NOT EDIT! -->");
+ writer.println("<inventory>");
+
+ for (InventoryItem item : ITEMS)
+ writer.println(" " + item);
+
+ writer.println();
+
+ for (Order order : ORDERS) {
+ writer.println(" <order num=\"" + order.getOrderNumber() + "\" "
+ + "date=\"" + order.getOrderDate().getTime() + "\">");
+
+ for (Integer itemID : order) {
+ writer.println(" <item id=\"" + itemID + "\" "
+ + "qty=\"" + order.getQuantityByID(itemID) + "\"/>");
+ }
+
+ writer.println(" </order>");
+ }
+
+ for (QuantityUpdate update : UPDATES) {
+ writer.println(" <update date=\"" + update.getDate().getTime() + "\">");
+
+ for (QuantityUpdate.Record record : update) {
+ writer.println(" <record item=\"" + record.getItemID() + "\" "
+ + "sold=\"" + record.getSold() + "\" "
+ + "waste=\"" + record.getWaste() + "\"/>");
+ }
+
+ writer.println(" </update>");
+ }
+
+ writer.println("</inventory>");
+ writer.flush();
+ }
+
+ public final void addOrder(Order order) {
+ ORDERS.add(order);
+
+ for (int item : order) {
+ InventoryItem affectedItem = getItemByID(item);
+ if (affectedItem == null)
+ throw new IllegalStateException("Order has an undefined item " + item);
+
+ affectedItem.addStock(order.getQuantityByID(item));
+ }
+ }
+
+ public final void addUpdate(QuantityUpdate update) {
+ UPDATES.add(update);
+
+ for (QuantityUpdate.Record record : update) {
+ InventoryItem affectedItem = getItemByID(record.getItemID());
+ if (affectedItem == null)
+ throw new IllegalStateException("Update has an undefined item " + record.getItemID());
+
+ affectedItem.addStock(-(record.getSold() + record.getWaste()));
+ }
+ }
+
+ public final void addNewItem(InventoryItem item) {
+ ITEMS.add(item);
+ }
+
+ public Set<InventoryItem> getItems() {
+ return ITEMS;
+ }
+
+ public InventoryItem getItemByID(int id) {
+ for (InventoryItem item : ITEMS)
+ if (item.getID() == id)
+ return item;
+
+ return null;
+ }
+}
diff --git a/src/com/delwink/icebox/InventoryItem.java b/src/com/delwink/icebox/InventoryItem.java
new file mode 100644
index 0000000..066ad8e
--- /dev/null
+++ b/src/com/delwink/icebox/InventoryItem.java
@@ -0,0 +1,98 @@
+/*
+ * IceBox - inventory management software for restaurants
+ * Copyright (C) 2016 Delwink, LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, version 3 only.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.delwink.icebox;
+
+/**
+ * An tracked inventory item.
+ * @author David McMackins II
+ */
+public class InventoryItem implements Comparable<InventoryItem> {
+ protected final int ID;
+ protected int inStock, reorderAt;
+ protected String name, unit;
+
+ /**
+ * Creates a new inventory item.
+ * @param id A unique numeric ID for this item.
+ */
+ public InventoryItem(int id) {
+ this(id, "Item", "units", 0);
+ }
+
+ /**
+ * Creates a new inventory item with known properties.
+ * @param id A unique numeric ID for this item.
+ * @param name A name for this item.
+ * @param unit Unit per quantity.
+ * @param reorderAt Quantity at which this item should be reordered.
+ */
+ public InventoryItem(int id, String name, String unit, int reorderAt) {
+ ID = id;
+ inStock = 0;
+ this.name = name;
+ this.unit = unit;
+ this.reorderAt = reorderAt;
+ }
+
+ public int getID() {
+ return ID;
+ }
+
+ public int getStock() {
+ return inStock;
+ }
+
+ public void addStock(int n) {
+ inStock += n;
+ }
+
+ public int getReorderAt() {
+ return reorderAt;
+ }
+
+ public void setReorderAt(int reorderAt) {
+ this.reorderAt = reorderAt;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getUnit() {
+ return unit;
+ }
+
+ public void setUnit(String unit) {
+ this.unit = unit;
+ }
+
+ @Override
+ public int compareTo(InventoryItem item) {
+ return getName().compareTo(item.getName());
+ }
+
+ @Override
+ public String toString() {
+ return "<item id=\"" + getID() + "\" name=\"" + getName() + "\" "
+ + "unit=\"" + getUnit() + "\" reorder=\"" + getReorderAt() + "/>";
+ }
+}
diff --git a/src/com/delwink/icebox/MainWindow.java b/src/com/delwink/icebox/MainWindow.java
index c1460c0..8d7e134 100644
--- a/src/com/delwink/icebox/MainWindow.java
+++ b/src/com/delwink/icebox/MainWindow.java
@@ -18,13 +18,18 @@
package com.delwink.icebox;
import com.delwink.icebox.lang.Lang;
+import com.delwink.icebox.table.MainWindowTableModel;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
+import java.io.File;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
@@ -36,12 +41,15 @@ import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
+import javax.xml.parsers.ParserConfigurationException;
+import org.xml.sax.SAXException;
/**
* The main IceBox hub window.
* @author David McMackins II
*/
public class MainWindow extends JFrame {
+ protected final Inventory INVENTORY;
protected final JButton ITEMS_BUTTON, ORDERS_BUTTON, UPDATE_BUTTON;
protected final JCheckBox REORDER_ONLY;
protected final JMenu REPORT_MENU, SESSION_MENU;
@@ -50,9 +58,11 @@ public class MainWindow extends JFrame {
/**
* Creates a new main IceBox window.
+ * @param inventory The inventory tracked in this window.
*/
- public MainWindow() {
+ public MainWindow(Inventory inventory) {
super(Lang.get("MainWindow.title"));
+ INVENTORY = inventory;
// menus
MENU_BAR = new JMenuBar();
@@ -107,8 +117,7 @@ public class MainWindow extends JFrame {
optionBox.add(REORDER_ONLY);
// center section
- INVENTORY_TABLE = new JTable();
-
+ INVENTORY_TABLE = new JTable(new MainWindowTableModel(INVENTORY));
JScrollPane inventoryBox = new JScrollPane(INVENTORY_TABLE);
// bottom section
@@ -177,7 +186,7 @@ public class MainWindow extends JFrame {
return (getExtendedState() & MAXIMIZED_BOTH) == MAXIMIZED_BOTH;
}
- public static void main(String[] args) {
+ public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
try {
Lang.setLang(Config.get("lang") + ".lang");
} catch (FileNotFoundException ignored) {
@@ -191,7 +200,17 @@ public class MainWindow extends JFrame {
}
}
- MainWindow mainWindow = new MainWindow();
+ Inventory inventory;
+ File inventoryFile = DataDir.INVENTORY_FILE;
+ if (inventoryFile.exists()) {
+ try (FileInputStream stream = new FileInputStream(inventoryFile)) {
+ inventory = new Inventory(stream);
+ }
+ } else {
+ inventory = new Inventory();
+ }
+
+ MainWindow mainWindow = new MainWindow(inventory);
mainWindow.setVisible(true);
}
}
diff --git a/src/com/delwink/icebox/Order.java b/src/com/delwink/icebox/Order.java
new file mode 100644
index 0000000..1609484
--- /dev/null
+++ b/src/com/delwink/icebox/Order.java
@@ -0,0 +1,85 @@
+/*
+ * IceBox - inventory management software for restaurants
+ * Copyright (C) 2016 Delwink, LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, version 3 only.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.delwink.icebox;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * An order of inventory items.
+ * @author David McMackins II
+ */
+public class Order implements Iterable<Integer> {
+ protected final Map<Integer, Integer> ITEMS;
+ protected Date orderDate;
+ protected String orderNumber;
+
+ /**
+ * Creates a new empty order.
+ * @param orderNumber Identifier for this order.
+ * @param orderDate The date of this order.
+ */
+ public Order(String orderNumber, Date orderDate) {
+ ITEMS = new HashMap<>();
+ this.orderDate = orderDate;
+ this.orderNumber = orderNumber;
+ }
+
+ /**
+ * Creates a new empty order.
+ */
+ public Order() {
+ this("", new Date());
+ }
+
+ /**
+ * Adds an item to this order.
+ * @param itemID ID of the item to be added.
+ * @param qty Quantity of the item to be added.
+ */
+ public void addItem(int itemID, int qty) {
+ ITEMS.put(itemID, qty);
+ }
+
+ public int getQuantityByID(int itemID) {
+ return ITEMS.get(itemID);
+ }
+
+ public Date getOrderDate() {
+ return orderDate;
+ }
+
+ public void setOrderDate(Date orderDate) {
+ this.orderDate = orderDate;
+ }
+
+ public String getOrderNumber() {
+ return orderNumber;
+ }
+
+ public void setOrderNumber(String orderNumber) {
+ this.orderNumber = orderNumber;
+ }
+
+ @Override
+ public Iterator<Integer> iterator() {
+ return ITEMS.keySet().iterator();
+ }
+}
diff --git a/src/com/delwink/icebox/QuantityUpdate.java b/src/com/delwink/icebox/QuantityUpdate.java
new file mode 100644
index 0000000..34995ed
--- /dev/null
+++ b/src/com/delwink/icebox/QuantityUpdate.java
@@ -0,0 +1,117 @@
+/*
+ * IceBox - inventory management software for restaurants
+ * Copyright (C) 2016 Delwink, LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, version 3 only.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.delwink.icebox;
+
+import com.delwink.icebox.QuantityUpdate.Record;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * An update to inventory quantity.
+ * @author David McMackins II
+ */
+public class QuantityUpdate implements Iterable<Record> {
+ protected final List<Record> RECORDS;
+ protected Date updateDate;
+
+ public QuantityUpdate(Date date) {
+ RECORDS = new ArrayList<>();
+ updateDate = date;
+ }
+
+ public Record getItemByID(int itemID) {
+ for (Record r : RECORDS)
+ if (r.getItemID() == itemID)
+ return r;
+
+ return null;
+ }
+
+ public void addItem(int itemID, int sold, int waste) {
+ Record r = getItemByID(itemID);
+ if (r == null) {
+ RECORDS.add(new Record(itemID, sold, waste));
+ } else {
+ r.setSold(r.getSold() + sold);
+ r.setWaste(r.getWaste() + waste);
+ }
+ }
+
+ public Date getDate() {
+ return updateDate;
+ }
+
+ public void setDate(Date updateDate) {
+ this.updateDate = updateDate;
+ }
+
+ @Override
+ public Iterator<Record> iterator() {
+ return RECORDS.iterator();
+ }
+
+ /**
+ * A single record of this quantity update.
+ */
+ public class Record {
+ protected final int itemID;
+ protected int sold, waste;
+
+ /**
+ * Creates a new record with unknown quantities.
+ * @param itemID The ID of this record's item.
+ */
+ public Record(int itemID) {
+ this(itemID, 0, 0);
+ }
+
+ /**
+ * Creates a new record with known quantities.
+ * @param itemID The ID of this record's item.
+ * @param sold The number of units sold.
+ * @param waste The number of units wasted.
+ */
+ public Record(int itemID, int sold, int waste) {
+ this.itemID = itemID;
+ this.sold = sold;
+ this.waste = waste;
+ }
+
+ public int getItemID() {
+ return itemID;
+ }
+
+ public int getSold() {
+ return sold;
+ }
+
+ public void setSold(int sold) {
+ this.sold = sold;
+ }
+
+ public int getWaste() {
+ return waste;
+ }
+
+ public void setWaste(int waste) {
+ this.waste = waste;
+ }
+ }
+}
diff --git a/src/com/delwink/icebox/lang/en_US.lang b/src/com/delwink/icebox/lang/en_US.lang
index e2047b2..5f740fe 100644
--- a/src/com/delwink/icebox/lang/en_US.lang
+++ b/src/com/delwink/icebox/lang/en_US.lang
@@ -4,6 +4,8 @@ ok=OK
save=Save
cancel=Cancel
+MainWindow.column0=Item
+MainWindow.column1=# in Stock
MainWindow.help=Help
MainWindow.items=Items
MainWindow.orders=Orders
diff --git a/src/com/delwink/icebox/table/MainWindowTableModel.java b/src/com/delwink/icebox/table/MainWindowTableModel.java
new file mode 100644
index 0000000..b02644a
--- /dev/null
+++ b/src/com/delwink/icebox/table/MainWindowTableModel.java
@@ -0,0 +1,80 @@
+/*
+ * IceBox - inventory management software for restaurants
+ * Copyright (C) 2016 Delwink, LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, version 3 only.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.delwink.icebox.table;
+
+import com.delwink.icebox.Inventory;
+import com.delwink.icebox.InventoryItem;
+import com.delwink.icebox.lang.Lang;
+import javax.swing.table.AbstractTableModel;
+
+/**
+ * Table model designed for the IceBox main window.
+ * @author David McMackins II
+ */
+public class MainWindowTableModel extends AbstractTableModel {
+ protected final Inventory INVENTORY;
+
+ public MainWindowTableModel(Inventory inventory) {
+ INVENTORY = inventory;
+ }
+
+ @Override
+ public int getRowCount() {
+ return INVENTORY.getItems().size();
+ }
+
+ @Override
+ public int getColumnCount() {
+ return 2;
+ }
+
+ @Override
+ public String getColumnName(int columnIndex) {
+ return Lang.get("MainWindow.column" + columnIndex);
+ }
+
+ @Override
+ public Class<?> getColumnClass(int columnIndex) {
+ return String.class;
+ }
+
+ @Override
+ public Object getValueAt(int i, int j) {
+ InventoryItem item = null;
+ for (InventoryItem temp : INVENTORY.getItems()) {
+ if (i-- == 0) {
+ item = temp;
+ break;
+ }
+ }
+
+ if (item == null)
+ throw new IndexOutOfBoundsException();
+
+ switch (j) {
+ case 0:
+ return item.getName();
+
+ case 1:
+ return String.valueOf(item.getStock());
+
+ default:
+ return new IndexOutOfBoundsException();
+ }
+ }
+}