/* * 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.Collection; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; 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 Map ITEMS; /** * Creates a new empty inventory. */ public Inventory() { ORDERS = new ArrayList<>(); UPDATES = new ArrayList<>(); ITEMS = new TreeMap<>(); } /** * 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); if (item.getParentNode().isSameNode(root)) { // make sure it's top-level 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(this, 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.setItem(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 (Integer id : ITEMS.keySet()) writer.println(" " + ITEMS.get(id)); 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 void refreshQuantities() { for (InventoryItem item : ITEMS.values()) item.addStock(-item.getStock()); // clear stock before refreshing List orders = new ArrayList<>(ORDERS); ORDERS.clear(); for (Order order : orders) addOrder(order); List updates = new ArrayList<>(UPDATES); UPDATES.clear(); for (QuantityUpdate update : updates) addUpdate(update); } 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 List getOrders() { return ORDERS; } 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 List getUpdates() { return UPDATES; } public final void addNewItem(InventoryItem item) { ITEMS.put(item.getID(), item); } public void deleteItem(int id) { ITEMS.remove(id); } public Collection getItems() { return ITEMS.values(); } public InventoryItem getItemByID(int id) { return ITEMS.get(id); } public int getNextID() { Set keys = ITEMS.keySet(); for (int i = 0; i <= keys.size(); ++i) if (!ITEMS.containsKey(i)) return i; if (ITEMS.isEmpty()) return 0; throw new IllegalStateException("Could not find next item ID"); } }