90 lines
3.4 KiB
Python
90 lines
3.4 KiB
Python
"""
|
|
Inventory Management Module
|
|
Handles reading and querying product metadata from stock.csv.
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import csv
|
|
from typing import Dict, Optional, Any, List
|
|
|
|
if sys.platform.startswith('win'):
|
|
try:
|
|
sys.stdout.reconfigure(encoding='utf-8')
|
|
except Exception:
|
|
pass
|
|
|
|
|
|
class InventoryManager:
|
|
def __init__(self, stock_dir: str = "stock", csv_filename: str = "stock.csv"):
|
|
self.stock_dir = stock_dir
|
|
self.csv_path = os.path.join(stock_dir, csv_filename)
|
|
self.products: Dict[str, Dict[str, Any]] = {}
|
|
self.load_inventory()
|
|
|
|
def load_inventory(self) -> None:
|
|
"""Loads inventory data from the CSV file."""
|
|
if not os.path.exists(self.csv_path):
|
|
print(f"[Warning] Inventory CSV not found at: {self.csv_path}")
|
|
return
|
|
|
|
with open(self.csv_path, mode="r", encoding="utf-8") as f:
|
|
reader = csv.DictReader(f)
|
|
for row in reader:
|
|
sku_id = row.get("SKU_ID", "").strip()
|
|
if not sku_id:
|
|
continue
|
|
|
|
# Locate the corresponding template image in stock_dir
|
|
local_image = self._find_template_image(sku_id)
|
|
|
|
self.products[sku_id] = {
|
|
"sku_id": sku_id,
|
|
"product_name": row.get("product_name", "").strip(),
|
|
"receipt_date": row.get("receipt_date", "").strip(),
|
|
"expiration_date": row.get("expiration_date", "").strip(),
|
|
"number_in": int(row.get("number_in", 0) or 0),
|
|
"number_out": int(row.get("number_out", 0) or 0),
|
|
"number_on": int(row.get("number_on", 0) or 0),
|
|
"price": float(row.get("price", 0) or 0),
|
|
"raw_image_link": row.get("image_link", "").strip(),
|
|
"local_image_path": local_image
|
|
}
|
|
|
|
def _find_template_image(self, sku_id: str) -> Optional[str]:
|
|
"""Finds the local image file for the given SKU_ID."""
|
|
extensions = [".jpeg", ".jpg", ".png"]
|
|
for ext in extensions:
|
|
candidate = os.path.join(self.stock_dir, f"{sku_id}{ext}")
|
|
if os.path.exists(candidate):
|
|
return candidate
|
|
return None
|
|
|
|
def get_product(self, sku_id: str) -> Optional[Dict[str, Any]]:
|
|
"""Retrieves product information for a given SKU_ID."""
|
|
return self.products.get(sku_id)
|
|
|
|
def list_all_skus(self) -> List[str]:
|
|
"""Returns all available SKU IDs."""
|
|
return list(self.products.keys())
|
|
|
|
def update_stock(self, sku_id: str, quantity_delta: int) -> bool:
|
|
"""Updates the on-shelf quantity (e.g., when purchased or restocked)."""
|
|
if sku_id in self.products:
|
|
current = self.products[sku_id]["number_on"]
|
|
new_val = max(0, current + quantity_delta)
|
|
self.products[sku_id]["number_on"] = new_val
|
|
if quantity_delta < 0:
|
|
self.products[sku_id]["number_out"] += abs(quantity_delta)
|
|
else:
|
|
self.products[sku_id]["number_in"] += quantity_delta
|
|
return True
|
|
return False
|
|
|
|
|
|
if __name__ == "__main__":
|
|
inv = InventoryManager("stock")
|
|
print(f"Loaded {len(inv.products)} products.")
|
|
for sku in inv.list_all_skus():
|
|
p = inv.get_product(sku)
|
|
print(f"[{sku}] {p['product_name']} | Price: {p['price']} | In-Stock: {p['number_on']} | Image: {p['local_image_path']}")
|