Abhyudit22 commited on
Commit
72d90c8
·
1 Parent(s): a034b0a

dashboard

Browse files
backend/crud/analytics_crud.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sqlalchemy.orm import Session
2
+ from sqlalchemy import func, desc
3
+ from models import Product, Sale
4
+
5
+
6
+ def get_total_products_count(db: Session):
7
+
8
+ return db.query(Product).count()
9
+
10
+
11
+ def get_total_sales_count(db: Session):
12
+
13
+ return db.query(Sale).count()
14
+
15
+
16
+ def get_total_revenue(db: Session):
17
+
18
+ result = db.query(func.sum(Sale.total_amount)).first()
19
+ # result[0] is the sum value, or None if no sales exist
20
+ if result[0] is None:
21
+ return 0.0
22
+ return float(result[0])
23
+
24
+
25
+ def get_total_profit(db: Session):
26
+
27
+ result = db.query(func.sum(Sale.profit)).first()
28
+ if result[0] is None:
29
+ return 0.0
30
+ return float(result[0])
31
+
32
+
33
+ def get_low_stock_products(db: Session, threshold: int = 10):
34
+
35
+ products = db.query(Product).filter(Product.quantity_left < threshold).order_by(Product.quantity_left.asc()).all()
36
+ return products
37
+
38
+
39
+ def get_top_selling_products(db: Session, limit: int = 5):
40
+
41
+ results = db.query(
42
+ Sale.product_id,
43
+ Product.name.label("product_name"),
44
+ func.sum(Sale.quantity).label("total_quantity_sold"),
45
+ func.sum(Sale.total_amount).label("total_revenue")
46
+ ).join(Product, Sale.product_id == Product.id) \
47
+ .group_by(Sale.product_id, Product.name) \
48
+ .order_by(desc("total_quantity_sold")) \
49
+ .limit(limit) \
50
+ .all()
51
+
52
+ return results
53
+
54
+
55
+ def get_recent_sales(db: Session, limit: int = 5):
56
+
57
+ results = db.query(
58
+ Sale.id.label("sale_id"),
59
+ Product.name.label("product_name"),
60
+ Sale.quantity,
61
+ Sale.total_amount,
62
+ Sale.sale_date
63
+ ).join(Product, Sale.product_id == Product.id) \
64
+ .order_by(desc(Sale.sale_date)) \
65
+ .limit(limit) \
66
+ .all()
67
+
68
+ return results
backend/models.py CHANGED
@@ -42,3 +42,10 @@ class Sale(Base):
42
 
43
  # Relationship
44
  product = relationship("Product", back_populates="sales")
 
 
 
 
 
 
 
 
42
 
43
  # Relationship
44
  product = relationship("Product", back_populates="sales")
45
+
46
+ @property
47
+ def product_name(self):
48
+ return self.product.name
49
+ def __init_subclass__(cls):
50
+ from sqlalchemy.orm import class_mapper
51
+ super().__init_subclass__()
backend/routes/analytics_routes.py ADDED
File without changes
backend/routes/sale_routes.py CHANGED
@@ -4,33 +4,54 @@ from database import get_db
4
  from crud import sale_crud
5
  from schemas import SaleCreate, SaleResponse
6
 
7
-
8
  router = APIRouter(prefix="/sales", tags=["Sales"])
9
 
10
 
11
  @router.post("/", response_model=SaleResponse)
12
  def create_sale(sale: SaleCreate, db: Session = Depends(get_db)):
13
-
14
  result = sale_crud.create_sale(db, sale)
15
 
16
-
17
  if result is None:
18
  raise HTTPException(status_code=404, detail="Product not found")
19
 
20
  if result == "not_enough_stock":
21
  raise HTTPException(status_code=400, detail="Not enough stock available")
22
 
23
- return result
 
 
 
 
 
 
 
 
 
 
 
 
24
 
25
 
26
  @router.get("/", response_model=list[SaleResponse])
27
  def get_all_sales(db: Session = Depends(get_db)):
28
  sales = sale_crud.get_all_sales(db)
29
-
 
30
  for sale in sales:
31
- sale.product_name = sale.product.name
 
 
 
 
 
 
 
 
 
 
 
32
 
33
- return sales
34
 
35
 
36
  @router.get("/{sale_id}", response_model=SaleResponse)
@@ -40,16 +61,37 @@ def get_sale_by_id(sale_id: int, db: Session = Depends(get_db)):
40
  if sale is None:
41
  raise HTTPException(status_code=404, detail="Sale not found")
42
 
43
- sale.product_name = sale.product.name
44
-
45
- return sale
 
 
 
 
 
 
 
 
 
46
 
47
 
48
  @router.get("/product/{product_id}", response_model=list[SaleResponse])
49
  def get_sales_by_product(product_id: int, db: Session = Depends(get_db)):
50
  sales = sale_crud.get_sales_by_product(db, product_id)
51
 
 
52
  for sale in sales:
53
- sale.product_name = sale.product.name
 
 
 
 
 
 
 
 
 
 
 
54
 
55
- return sales
 
4
  from crud import sale_crud
5
  from schemas import SaleCreate, SaleResponse
6
 
 
7
  router = APIRouter(prefix="/sales", tags=["Sales"])
8
 
9
 
10
  @router.post("/", response_model=SaleResponse)
11
  def create_sale(sale: SaleCreate, db: Session = Depends(get_db)):
 
12
  result = sale_crud.create_sale(db, sale)
13
 
 
14
  if result is None:
15
  raise HTTPException(status_code=404, detail="Product not found")
16
 
17
  if result == "not_enough_stock":
18
  raise HTTPException(status_code=400, detail="Not enough stock available")
19
 
20
+ # Manual conversion for response
21
+ return {
22
+ "id": result.id,
23
+ "product_id": result.product_id,
24
+ "product_name": result.product.name,
25
+ "quantity": result.quantity,
26
+ "selling_price_at_time": result.selling_price_at_time,
27
+ "cost_price_at_time": result.cost_price_at_time,
28
+ "total_amount": result.total_amount,
29
+ "profit": result.profit,
30
+ "customer_name": result.customer_name,
31
+ "sale_date": result.sale_date
32
+ }
33
 
34
 
35
  @router.get("/", response_model=list[SaleResponse])
36
  def get_all_sales(db: Session = Depends(get_db)):
37
  sales = sale_crud.get_all_sales(db)
38
+
39
+ result = []
40
  for sale in sales:
41
+ result.append({
42
+ "id": sale.id,
43
+ "product_id": sale.product_id,
44
+ "product_name": sale.product.name,
45
+ "quantity": sale.quantity,
46
+ "selling_price_at_time": sale.selling_price_at_time,
47
+ "cost_price_at_time": sale.cost_price_at_time,
48
+ "total_amount": sale.total_amount,
49
+ "profit": sale.profit,
50
+ "customer_name": sale.customer_name,
51
+ "sale_date": sale.sale_date
52
+ })
53
 
54
+ return result
55
 
56
 
57
  @router.get("/{sale_id}", response_model=SaleResponse)
 
61
  if sale is None:
62
  raise HTTPException(status_code=404, detail="Sale not found")
63
 
64
+ return {
65
+ "id": sale.id,
66
+ "product_id": sale.product_id,
67
+ "product_name": sale.product.name,
68
+ "quantity": sale.quantity,
69
+ "selling_price_at_time": sale.selling_price_at_time,
70
+ "cost_price_at_time": sale.cost_price_at_time,
71
+ "total_amount": sale.total_amount,
72
+ "profit": sale.profit,
73
+ "customer_name": sale.customer_name,
74
+ "sale_date": sale.sale_date
75
+ }
76
 
77
 
78
  @router.get("/product/{product_id}", response_model=list[SaleResponse])
79
  def get_sales_by_product(product_id: int, db: Session = Depends(get_db)):
80
  sales = sale_crud.get_sales_by_product(db, product_id)
81
 
82
+ result = []
83
  for sale in sales:
84
+ result.append({
85
+ "id": sale.id,
86
+ "product_id": sale.product_id,
87
+ "product_name": sale.product.name,
88
+ "quantity": sale.quantity,
89
+ "selling_price_at_time": sale.selling_price_at_time,
90
+ "cost_price_at_time": sale.cost_price_at_time,
91
+ "total_amount": sale.total_amount,
92
+ "profit": sale.profit,
93
+ "customer_name": sale.customer_name,
94
+ "sale_date": sale.sale_date
95
+ })
96
 
97
+ return result
backend/schemas.py CHANGED
@@ -3,7 +3,8 @@ from decimal import Decimal
3
  from typing import Optional
4
  from pydantic import Field
5
  from datetime import datetime
6
-
 
7
 
8
 
9
  class ProductBase(BaseModel):
@@ -63,7 +64,6 @@ class SaleCreate(BaseModel):
63
  class SaleResponse(BaseModel):
64
  id: int
65
  product_id: int
66
- product_name: str
67
  quantity: int
68
  selling_price_at_time: float
69
  cost_price_at_time: float
@@ -74,3 +74,34 @@ class SaleResponse(BaseModel):
74
 
75
  class Config:
76
  from_attributes = True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  from typing import Optional
4
  from pydantic import Field
5
  from datetime import datetime
6
+ from typing import List, Optional
7
+ from pydantic import BaseModel, Field
8
 
9
 
10
  class ProductBase(BaseModel):
 
64
  class SaleResponse(BaseModel):
65
  id: int
66
  product_id: int
 
67
  quantity: int
68
  selling_price_at_time: float
69
  cost_price_at_time: float
 
74
 
75
  class Config:
76
  from_attributes = True
77
+
78
+ class LowStockProduct(BaseModel):
79
+ id: int
80
+ name: str
81
+ sku: str
82
+ quantity_left: int
83
+
84
+
85
+ class TopSellingProduct(BaseModel):
86
+ product_id: int
87
+ product_name: str
88
+ total_quantity_sold: int
89
+ total_revenue: float
90
+
91
+
92
+ class RecentSaleInfo(BaseModel):
93
+ sale_id: int
94
+ product_name: str
95
+ quantity: int
96
+ total_amount: float
97
+ sale_date: datetime
98
+
99
+
100
+ class DashboardResponse(BaseModel):
101
+ total_products: int
102
+ total_sales_count: int
103
+ total_revenue: float
104
+ total_profit: float
105
+ low_stock_products: List[LowStockProduct]
106
+ top_selling_products: List[TopSellingProduct]
107
+ recent_sales: List[RecentSaleInfo]