Added price status and information

This commit is contained in:
2026-08-27 00:41:28 +03:00
parent e532335b1b
commit 6df56c3ffb
6 changed files with 49 additions and 20 deletions
+39 -11
View File
@@ -14,6 +14,7 @@ app = Flask(__name__)
DATABASE_PATH = os.environ.get("DATABASE_PATH", str(Path(__file__).with_name("inventory.db")))
UPLOAD_FOLDER = Path(os.environ.get("UPLOAD_FOLDER", str(Path(DATABASE_PATH).parent / "uploads")))
ALLOWED_IMAGE_EXTENSIONS = {"jpg", "jpeg", "png", "gif", "webp"}
ALLOWED_STATUSES = {"Available", "Trashed", "Sold"}
app.config["MAX_CONTENT_LENGTH"] = 250 * 1024 * 1024
@@ -40,6 +41,8 @@ def initialize_db():
asset_tag TEXT UNIQUE,
status TEXT NOT NULL DEFAULT 'Available',
location TEXT,
destination TEXT,
price REAL,
purchase_date TEXT,
notes TEXT,
cpu TEXT,
@@ -67,6 +70,10 @@ def initialize_db():
columns = {row[1] for row in connection.execute("PRAGMA table_info(inventory_items)")}
if "image_filename" not in columns:
connection.execute("ALTER TABLE inventory_items ADD COLUMN image_filename TEXT")
if "price" not in columns:
connection.execute("ALTER TABLE inventory_items ADD COLUMN price REAL")
if "destination" not in columns:
connection.execute("ALTER TABLE inventory_items ADD COLUMN destination TEXT")
connection.execute(
"""INSERT INTO inventory_images (item_id, filename, original_name, created_at)
SELECT id, image_filename, image_filename, COALESCE(updated_at, datetime('now'))
@@ -81,6 +88,19 @@ def clean(value):
def item_payload(data):
price_value = clean(data.get("price"))
if price_value:
try:
price = round(float(price_value), 2)
except ValueError:
raise ValueError("Price must be a valid number.")
if price < 0:
raise ValueError("Price cannot be negative.")
else:
price = None
status = clean(data.get("status")) or "Available"
if status not in ALLOWED_STATUSES:
raise ValueError("Status must be Available, Trashed, or Sold.")
return {
"name": clean(data.get("name")),
"item_type": clean(data.get("item_type")),
@@ -88,8 +108,10 @@ def item_payload(data):
"model": clean(data.get("model")),
"serial_number": clean(data.get("serial_number")) or None,
"asset_tag": clean(data.get("asset_tag")) or None,
"status": clean(data.get("status")) or "Available",
"status": status,
"location": clean(data.get("location")),
"destination": clean(data.get("destination")),
"price": price,
"purchase_date": clean(data.get("purchase_date")) or None,
"notes": clean(data.get("notes")),
"cpu": clean(data.get("cpu")),
@@ -212,8 +234,8 @@ def list_items():
clauses = []
params = []
if query:
clauses.append("(name LIKE ? OR item_type LIKE ? OR brand LIKE ? OR model LIKE ? OR serial_number LIKE ? OR asset_tag LIKE ? OR location LIKE ?)")
params.extend([f"%{query}%"] * 7)
clauses.append("(name LIKE ? OR item_type LIKE ? OR brand LIKE ? OR model LIKE ? OR serial_number LIKE ? OR asset_tag LIKE ? OR location LIKE ? OR destination LIKE ?)")
params.extend([f"%{query}%"] * 8)
if item_type:
clauses.append("item_type = ?")
params.append(item_type)
@@ -231,9 +253,9 @@ def summary():
with get_db() as connection:
total = connection.execute("SELECT COUNT(*) FROM inventory_items").fetchone()[0]
available = connection.execute("SELECT COUNT(*) FROM inventory_items WHERE status = 'Available'").fetchone()[0]
checked_out = connection.execute("SELECT COUNT(*) FROM inventory_items WHERE status = 'Checked out'").fetchone()[0]
maintenance = connection.execute("SELECT COUNT(*) FROM inventory_items WHERE status = 'Maintenance'").fetchone()[0]
return jsonify({"total": total, "available": available, "checked_out": checked_out, "maintenance": maintenance})
trashed = connection.execute("SELECT COUNT(*) FROM inventory_items WHERE status = 'Trashed'").fetchone()[0]
sold = connection.execute("SELECT COUNT(*) FROM inventory_items WHERE status = 'Sold'").fetchone()[0]
return jsonify({"total": total, "available": available, "trashed": trashed, "sold": sold})
@app.get("/api/backup")
@@ -270,7 +292,10 @@ def restore():
@app.post("/api/items")
def create_item():
data = item_payload(request_data())
try:
data = item_payload(request_data())
except ValueError as error:
return jsonify({"error": str(error)}), 400
if not data["name"] or not data["item_type"]:
return jsonify({"error": "Name and type are required."}), 400
selected_files = [image for image in files_from_request() if image and image.filename]
@@ -285,8 +310,8 @@ def create_item():
with get_db() as connection:
cursor = connection.execute(
"""INSERT INTO inventory_items
(name, item_type, brand, model, serial_number, asset_tag, status, location, purchase_date, notes, cpu, ram, gpu, storage, media_format, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
(name, item_type, brand, model, serial_number, asset_tag, status, location, destination, price, purchase_date, notes, cpu, ram, gpu, storage, media_format, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
(*data.values(), now, now),
)
for filename, original_name in images:
@@ -302,7 +327,10 @@ def create_item():
@app.put("/api/items/<int:item_id>")
def update_item(item_id):
data = item_payload(request_data())
try:
data = item_payload(request_data())
except ValueError as error:
return jsonify({"error": str(error)}), 400
if not data["name"] or not data["item_type"]:
return jsonify({"error": "Name and type are required."}), 400
data["updated_at"] = datetime.now(timezone.utc).isoformat()
@@ -318,7 +346,7 @@ def update_item(item_id):
return jsonify({"error": "Each inventory item can have up to 5 pictures."}), 400
saved_images = save_images(new_images)
result = connection.execute(
"""UPDATE inventory_items SET name=?, item_type=?, brand=?, model=?, serial_number=?, asset_tag=?, status=?, location=?, purchase_date=?, notes=?, cpu=?, ram=?, gpu=?, storage=?, media_format=?, updated_at=? WHERE id=?""",
"""UPDATE inventory_items SET name=?, item_type=?, brand=?, model=?, serial_number=?, asset_tag=?, status=?, location=?, destination=?, price=?, purchase_date=?, notes=?, cpu=?, ram=?, gpu=?, storage=?, media_format=?, updated_at=? WHERE id=?""",
(*data.values(), item_id),
)
for filename, original_name in saved_images: