Automated Action a17fe518a9 Implement Small Business Inventory Management System
This commit includes:
- Project structure setup with FastAPI and SQLite
- Database models and schemas for inventory management
- CRUD operations for all entities
- API endpoints for product, category, supplier, and inventory management
- User authentication with JWT tokens
- Initial database migration
- Comprehensive README with setup instructions
2025-06-17 19:02:35 +00:00

172 lines
6.2 KiB
Python

from typing import List, Optional, Dict, Any
from datetime import datetime
from sqlalchemy.orm import Session
from app.crud.base import CRUDBase
from app.models.inventory import Inventory, InventoryTransaction
from app.models.product import Product
from app.schemas.inventory import (
InventoryCreate, InventoryUpdate,
InventoryTransactionCreate, InventoryTransactionUpdate
)
class CRUDInventory(CRUDBase[Inventory, InventoryCreate, InventoryUpdate]):
def get_by_product_id(self, db: Session, *, product_id: int) -> Optional[Inventory]:
return db.query(Inventory).filter(Inventory.product_id == product_id).first()
def get_by_location(
self, db: Session, *, location: str, skip: int = 0, limit: int = 100
) -> List[Inventory]:
return db.query(Inventory).filter(Inventory.location == location).offset(skip).limit(limit).all()
def get_low_stock(self, db: Session, *, skip: int = 0, limit: int = 100) -> List[Dict[str, Any]]:
"""Get products where inventory is below minimum stock level."""
query = (
db.query(
Inventory,
Product.name,
Product.sku,
Product.min_stock_level
)
.join(Product)
.filter(Inventory.quantity <= Product.min_stock_level)
.offset(skip)
.limit(limit)
)
result = []
for inventory, name, sku, min_stock_level in query.all():
result.append({
"product_id": inventory.product_id,
"product_name": name,
"sku": sku,
"current_stock": inventory.quantity,
"min_stock_level": min_stock_level,
"is_low_stock": True
})
return result
def get_inventory_summary(self, db: Session, *, skip: int = 0, limit: int = 100) -> List[Dict[str, Any]]:
"""Get inventory summary for all products."""
query = (
db.query(
Inventory,
Product.name,
Product.sku,
Product.min_stock_level
)
.join(Product)
.offset(skip)
.limit(limit)
)
result = []
for inventory, name, sku, min_stock_level in query.all():
result.append({
"product_id": inventory.product_id,
"product_name": name,
"sku": sku,
"current_stock": inventory.quantity,
"min_stock_level": min_stock_level,
"is_low_stock": inventory.quantity <= min_stock_level
})
return result
def update_stock(
self, db: Session, *, product_id: int, quantity_change: int, user_id: Optional[int] = None,
transaction_type: str = "adjustment", reference: Optional[str] = None,
unit_price: Optional[float] = None, notes: Optional[str] = None
) -> Dict[str, Any]:
"""Update stock level and create a transaction record."""
inventory = self.get_by_product_id(db=db, product_id=product_id)
if not inventory:
# Create new inventory record if it doesn't exist
inventory_in = InventoryCreate(
product_id=product_id,
quantity=quantity_change if quantity_change > 0 else 0, # Don't allow negative initial stock
last_counted_at=datetime.now()
)
inventory = super().create(db=db, obj_in=inventory_in)
else:
# Update existing inventory
new_quantity = inventory.quantity + quantity_change
if new_quantity < 0:
new_quantity = 0 # Don't allow negative stock
inventory_in = InventoryUpdate(
product_id=product_id,
quantity=new_quantity,
last_counted_at=datetime.now()
)
inventory = super().update(db=db, db_obj=inventory, obj_in=inventory_in)
# Create transaction record
transaction = InventoryTransaction(
product_id=product_id,
quantity=quantity_change,
transaction_type=transaction_type,
reference=reference,
unit_price=unit_price,
notes=notes,
transaction_date=datetime.now(),
user_id=user_id
)
db.add(transaction)
db.commit()
db.refresh(transaction)
return {
"inventory": inventory,
"transaction": transaction
}
class CRUDInventoryTransaction(CRUDBase[InventoryTransaction, InventoryTransactionCreate, InventoryTransactionUpdate]):
def get_by_product_id(
self, db: Session, *, product_id: int, skip: int = 0, limit: int = 100
) -> List[InventoryTransaction]:
return (
db.query(InventoryTransaction)
.filter(InventoryTransaction.product_id == product_id)
.order_by(InventoryTransaction.transaction_date.desc())
.offset(skip)
.limit(limit)
.all()
)
def get_by_type(
self, db: Session, *, transaction_type: str, skip: int = 0, limit: int = 100
) -> List[InventoryTransaction]:
return (
db.query(InventoryTransaction)
.filter(InventoryTransaction.transaction_type == transaction_type)
.order_by(InventoryTransaction.transaction_date.desc())
.offset(skip)
.limit(limit)
.all()
)
def get_by_date_range(
self, db: Session, *, start_date: datetime, end_date: datetime,
skip: int = 0, limit: int = 100
) -> List[InventoryTransaction]:
return (
db.query(InventoryTransaction)
.filter(
InventoryTransaction.transaction_date >= start_date,
InventoryTransaction.transaction_date <= end_date
)
.order_by(InventoryTransaction.transaction_date.desc())
.offset(skip)
.limit(limit)
.all()
)
inventory = CRUDInventory(Inventory)
inventory_transaction = CRUDInventoryTransaction(InventoryTransaction)