From dc394dd9922911de59dc96a4423b5ff1ace3e179 Mon Sep 17 00:00:00 2001 From: Yaro Kasear Date: Mon, 7 Jul 2025 09:40:27 -0500 Subject: [PATCH] Add inventory item creation and update endpoints; enhance inventory template with form handling --- models/inventory.py | 18 +++++ routes.py | 74 +++++++++++++++++++ templates/fragments/_breadcrumb_fragment.html | 2 +- templates/inventory.html | 67 ++++++++++++++++- 4 files changed, 159 insertions(+), 2 deletions(-) diff --git a/models/inventory.py b/models/inventory.py index 2028d96..5498788 100644 --- a/models/inventory.py +++ b/models/inventory.py @@ -106,3 +106,21 @@ class Inventory(db.Model): 'barcode': self.barcode, 'shared': self.shared } + + @classmethod + def from_dict(cls, data: dict[str, Any]) -> "Inventory": + return cls( + timestamp=datetime.datetime.fromisoformat(data.get("timestamp")) if data.get("timestamp") else datetime.datetime.now(), + condition=data.get("condition", "Unverified"), + needed=data.get("needed", ""), + type_id=data["type_id"], + inventory_name=data.get("inventory_name"), + serial=data.get("serial"), + model=data.get("model"), + notes=data.get("notes"), + owner_id=data.get("owner_id"), + brand_id=data.get("brand_id"), + location_id=data.get("location_id"), + barcode=data.get("barcode"), + shared=bool(data.get("shared", False)) + ) diff --git a/routes.py b/routes.py index 45e8e1c..8dcdce2 100644 --- a/routes.py +++ b/routes.py @@ -8,6 +8,7 @@ from .utils.load import eager_load_user_relationships, eager_load_inventory_rela import pandas as pd import traceback import json +import datetime main = Blueprint('main', __name__) @@ -271,6 +272,79 @@ def inventory_item(id): types=types ) +@main.route("/inventory_item/new", methods=["GET"]) +def new_inventory_item(): + brands = db.session.query(Brand).all() + users = eager_load_user_relationships(db.session.query(User)).all() + rooms = eager_load_room_relationships(db.session.query(Room)).all() + types = db.session.query(Item).all() + + item = Inventory( + timestamp=datetime.datetime.now(), + condition="Unverified", + needed="", + type_id=None, + ) + + return render_template( + "inventory.html", + item=item, + brands=brands, + users=users, + rooms=rooms, + types=types, + worklog=[], + worklog_headers={}, + worklog_rows=[] + ) + +@main.route("/api/inventory", methods=["POST"]) +def create_inventory_item(): + try: + data = request.get_json(force=True) + + new_item = Inventory.from_dict(data) + + db.session.add(new_item) + db.session.commit() + + return jsonify({"success": True, "id": new_item.id}), 201 + + except Exception as e: + db.session.rollback() + return jsonify({"success": False, "error": str(e)}), 400 + +@main.route("/api/inventory/", methods=["PUT"]) +def update_inventory_item(id): + try: + data = request.get_json(force=True) + item = db.session.query(Inventory).get(id) + + if not item: + return jsonify({"success": False, "error": f"Inventory item with ID {id} not found."}), 404 + + item.timestamp = datetime.datetime.fromisoformat(data.get("timestamp")) if data.get("timestamp") else item.timestamp + item.condition = data.get("condition", item.condition) + item.needed = data.get("needed", item.needed) + item.type_id = data.get("type_id", item.type_id) + item.inventory_name = data.get("inventory_name", item.inventory_name) + item.serial = data.get("serial", item.serial) + item.model = data.get("model", item.model) + item.notes = data.get("notes", item.notes) + item.owner_id = data.get("owner_id", item.owner_id) + item.brand_id = data.get("brand_id", item.brand_id) + item.location_id = data.get("location_id", item.location_id) + item.barcode = data.get("barcode", item.barcode) + item.shared = bool(data.get("shared", item.shared)) + + db.session.commit() + + return jsonify({"success": True, "id": item.id}), 200 + + except Exception as e: + db.session.rollback() + return jsonify({"success": False, "error": str(e)}), 400 + @main.route("/users") def list_users(): query = eager_load_user_relationships(db.session.query(User)).order_by(User.last_name, User.first_name) diff --git a/templates/fragments/_breadcrumb_fragment.html b/templates/fragments/_breadcrumb_fragment.html index c197511..279d02e 100644 --- a/templates/fragments/_breadcrumb_fragment.html +++ b/templates/fragments/_breadcrumb_fragment.html @@ -27,7 +27,7 @@ {% if submit_button %}
- +
{% endif %} diff --git a/templates/inventory.html b/templates/inventory.html index f290792..a3359ad 100644 --- a/templates/inventory.html +++ b/templates/inventory.html @@ -11,6 +11,7 @@ breadcrumbs=[ title=title, submit_button=True) }} +
@@ -52,7 +53,7 @@ submit_button=True) }}
- +
@@ -124,4 +125,68 @@ submit_button=True) }} {% endif %}
+{% endblock %} + +{% block script %} + document.addEventListener("DOMContentLoaded", () => { + const submitButton = document.getElementById("saveButton"); + const toastData = localStorage.getItem("toastMessage"); + if (toastData) { + const { message, type } = JSON.parse(toastData); + renderToast({ message, type }); + localStorage.removeItem("toastMessage"); + } + + if (submitButton) { + submitButton.addEventListener("click", async (e) => { + e.preventDefault(); + + const payload = { + timestamp: document.querySelector("input[name='timestamp']").value, + condition: document.querySelector("select[name='condition']").value, + needed: "", // ← either add a field for this or drop it if obsolete + type_id: parseInt(document.querySelector("select[name='type']").value), + inventory_name: document.querySelector("input[name='inventory_name']").value || null, + serial: document.querySelector("input[name='serial']").value || null, + model: document.querySelector("input[name='model']").value || null, + notes: document.querySelector("textarea[name='notes']").value || null, + owner_id: parseInt(document.querySelector("select#userList").value) || null, + brand_id: parseInt(document.querySelector("select[name='brand']").value) || null, + location_id: parseInt(document.querySelector("select#room").value) || null, + barcode: document.querySelector("input[name='barcode']").value || null, + shared: document.querySelector("input[name='shared']").checked + }; + + try { + const id = document.querySelector("#inventoryId").value; + const isEdit = id && id !== "None"; + + const endpoint = isEdit ? `/api/inventory/${id}` : "/api/inventory" + const method = isEdit ? "PUT" : "POST"; + + const response = await fetch(endpoint, { + method, + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify(payload) + }); + + const result = await response.json(); + if(result.success) { + localStorage.setItem("toastMessage", JSON.stringify({ + message: isEdit ? "Inventory item updated!" : "Inventory item created!", + type: "success" + })); + + window.location.href = `/inventory_item/${result.id}`; + } else { + renderToast({message: `Error: ${result.error}`, type: "danger"}); + } + } catch (err) { + console.error(err); + } + }); + } + }); {% endblock %} \ No newline at end of file