From 36b78e77945fa0b948765e50457d685d820c6e89 Mon Sep 17 00:00:00 2001 From: David McMackins II Date: Sat, 10 Sep 2016 18:31:13 -0500 Subject: Add main table --- src/com/delwink/icebox/Config.java | 4 +- src/com/delwink/icebox/DataDir.java | 2 + src/com/delwink/icebox/Inventory.java | 213 +++++++++++++++++++++ src/com/delwink/icebox/InventoryItem.java | 98 ++++++++++ src/com/delwink/icebox/MainWindow.java | 29 ++- src/com/delwink/icebox/Order.java | 85 ++++++++ src/com/delwink/icebox/QuantityUpdate.java | 117 +++++++++++ src/com/delwink/icebox/lang/en_US.lang | 2 + .../delwink/icebox/table/MainWindowTableModel.java | 80 ++++++++ 9 files changed, 623 insertions(+), 7 deletions(-) create mode 100644 src/com/delwink/icebox/Inventory.java create mode 100644 src/com/delwink/icebox/InventoryItem.java create mode 100644 src/com/delwink/icebox/Order.java create mode 100644 src/com/delwink/icebox/QuantityUpdate.java create mode 100644 src/com/delwink/icebox/table/MainWindowTableModel.java 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 . + */ + +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 ORDERS; + protected final List UPDATES; + protected final Set 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(""); + writer.println(""); + + for (InventoryItem item : ITEMS) + writer.println(" " + item); + + writer.println(); + + for (Order order : ORDERS) { + writer.println(" "); + + for (Integer itemID : order) { + writer.println(" "); + } + + writer.println(" "); + } + + for (QuantityUpdate update : UPDATES) { + writer.println(" "); + + for (QuantityUpdate.Record record : update) { + writer.println(" "); + } + + writer.println(" "); + } + + writer.println(""); + 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 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 . + */ + +package com.delwink.icebox; + +/** + * An tracked inventory item. + * @author David McMackins II + */ +public class InventoryItem implements Comparable { + 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 ""; + } +} 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 . + */ + +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 { + protected final Map 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 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 . + */ + +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 { + protected final List 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 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 . + */ + +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(); + } + } +} -- cgit v1.2.3