AhmadYarAI commited on
Commit
00a4c1d
Β·
1 Parent(s): 54839eb

Add the categories for the own users

Browse files
.DS_Store CHANGED
Binary files a/.DS_Store and b/.DS_Store differ
 
app/constants/categories.py CHANGED
@@ -1,14 +1,17 @@
1
  CATEGORIES = [
2
- {"name": "Groceries", "icon": "πŸ›’", "budget": 0, "spent": 0, "remaining": 0},
3
- {"name": "Food", "icon": "🍽", "budget": 0, "spent": 0, "remaining": 0},
4
- {"name": "Transport", "icon": "🚌", "budget": 0, "spent": 0, "remaining": 0},
5
- {"name": "Health", "icon": "πŸ’Š", "budget": 0, "spent": 0, "remaining": 0},
6
- {"name": "Gifts", "icon": "🎁", "budget": 0, "spent": 0, "remaining": 0},
7
- {"name": "Rent", "icon": "🏠", "budget": 0, "spent": 0, "remaining": 0},
8
- {"name": "Utilities", "icon": "⚑", "budget": 0, "spent": 0, "remaining": 0},
9
- {"name": "Entertainment", "icon": "πŸŽ‰", "budget": 0, "spent": 0, "remaining": 0},
10
- {"name": "Education", "icon": "πŸ“š", "budget": 0, "spent": 0, "remaining": 0},
11
- {"name": "Insurance", "icon": "πŸ›‘", "budget": 0, "spent": 0, "remaining": 0},
12
- ]
13
-
14
 
 
 
 
 
 
1
  CATEGORIES = [
2
+ # ---------- HEAD CATEGORIES ----------
3
+ {"name": "Groceries", "icon": "πŸ›’", "scope": "head"},
4
+ {"name": "Food", "icon": "🍽", "scope": "head"},
5
+ {"name": "Transport", "icon": "🚌", "scope": "head"},
6
+ {"name": "Health", "icon": "πŸ’Š", "scope": "head"},
7
+ {"name": "Gifts", "icon": "🎁", "scope": "head"},
8
+ {"name": "Rent", "icon": "🏠", "scope": "head"},
9
+ {"name": "Utilities", "icon": "⚑", "scope": "head"},
10
+ {"name": "Entertainment", "icon": "πŸŽ‰", "scope": "head"},
11
+ {"name": "Education", "icon": "πŸ“š", "scope": "head"},
12
+ {"name": "Insurance", "icon": "πŸ›‘", "scope": "head"},
 
13
 
14
+ # ---------- MEMBER CATEGORIES ----------
15
+ {"name": "Student Groceries", "icon": "πŸ›’", "scope": "member"},
16
+ {"name": "Food", "icon": "🍽", "scope": "member"},
17
+ ]
app/db/categories_budget.py CHANGED
@@ -1,17 +1,27 @@
1
- from sqlalchemy import Column, Integer, String, Float, DateTime, ForeignKey
2
  from sqlalchemy.orm import relationship
3
  from sqlalchemy.sql import func
4
  from app.db.database import Base
5
 
6
-
7
  class CategoryBudget(Base):
8
  __tablename__ = "category_budgets"
9
 
10
  id = Column(Integer, primary_key=True, index=True)
11
- family_code = Column(String(10), index=True, nullable=False)
12
 
 
13
  category_name = Column(String(50), nullable=False)
 
 
 
 
14
  budget = Column(Float, default=0.0)
15
  spent = Column(Float, default=0.0)
16
 
17
- created_at = Column(DateTime(timezone=True), server_default=func.now())
 
 
 
 
 
 
 
 
1
+ from sqlalchemy import Column, Integer, String, Float, DateTime, ForeignKey, UniqueConstraint
2
  from sqlalchemy.orm import relationship
3
  from sqlalchemy.sql import func
4
  from app.db.database import Base
5
 
 
6
  class CategoryBudget(Base):
7
  __tablename__ = "category_budgets"
8
 
9
  id = Column(Integer, primary_key=True, index=True)
 
10
 
11
+ family_code = Column(String(10), index=True, nullable=False)
12
  category_name = Column(String(50), nullable=False)
13
+
14
+ scope = Column(String(10), nullable=False) # "family" | "member"
15
+ owner_id = Column(Integer, nullable=True) # NULL for family, member_id for member
16
+
17
  budget = Column(Float, default=0.0)
18
  spent = Column(Float, default=0.0)
19
 
20
+ created_at = Column(DateTime(timezone=True), server_default=func.now())
21
+
22
+ __table_args__ = (
23
+ UniqueConstraint(
24
+ "family_code", "scope", "owner_id", "category_name",
25
+ name="uq_category_budget_scope"
26
+ ),
27
+ )
app/routers/expense.py CHANGED
@@ -13,7 +13,7 @@ from app.db.categories_budget import CategoryBudget
13
 
14
  router = APIRouter(prefix="/expense", tags=["expense"])
15
 
16
- CATEGORIES = [
17
  {"name": "Groceries", "icon": "πŸ›’", "budget": 0, "spent": 0, "remaining": 0},
18
  {"name": "Food", "icon": "🍽", "budget": 0, "spent": 0, "remaining": 0},
19
  {"name": "Transport", "icon": "🚌", "budget": 0, "spent": 0, "remaining": 0},
@@ -26,6 +26,14 @@ CATEGORIES = [
26
  {"name": "Insurance", "icon": "πŸ›‘", "budget": 0, "spent": 0, "remaining": 0},
27
  ]
28
 
 
 
 
 
 
 
 
 
29
 
30
  @router.get("/categories")
31
  def get_categories(
@@ -34,45 +42,60 @@ def get_categories(
34
  ):
35
  family_code = current_user.family_code
36
 
37
- # Fetch budget rows from DB
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  rows = db.query(CategoryBudget).filter(
39
- CategoryBudget.family_code == family_code
 
 
40
  ).all()
41
 
42
- # Map for quick lookup
43
  db_map = {row.category_name: row for row in rows}
44
 
 
45
  result = []
46
 
47
- for cat in CATEGORIES:
48
  name = cat["name"]
49
  icon = cat["icon"]
50
 
51
- # Read values from DB if available
52
- if name in db_map:
53
- row = db_map[name]
54
- budget = row.budget
55
- spent = row.spent
56
- remaining = budget - spent
57
- else:
58
- budget = 0
59
- spent = 0
60
- remaining = 0
61
 
62
  result.append({
63
  "name": name,
64
  "icon": icon,
65
  "budget": budget,
66
  "spent": spent,
67
- "remaining": remaining,
68
  })
69
 
70
  return {
71
  "status": True,
 
72
  "categories": result
73
  }
74
 
75
-
76
  @router.post("/add")
77
  def add_expense(
78
  payload: AddExpenseRequest,
 
13
 
14
  router = APIRouter(prefix="/expense", tags=["expense"])
15
 
16
+ HEAD_CATEGORIES = [
17
  {"name": "Groceries", "icon": "πŸ›’", "budget": 0, "spent": 0, "remaining": 0},
18
  {"name": "Food", "icon": "🍽", "budget": 0, "spent": 0, "remaining": 0},
19
  {"name": "Transport", "icon": "🚌", "budget": 0, "spent": 0, "remaining": 0},
 
26
  {"name": "Insurance", "icon": "πŸ›‘", "budget": 0, "spent": 0, "remaining": 0},
27
  ]
28
 
29
+ MEMBER_CATEGORIES = [
30
+ {"name": "Food", "icon": "🍽", "budget": 0, "spent": 0, "remaining": 0},
31
+ {"name": "Transport", "icon": "🚌", "budget": 0, "spent": 0, "remaining": 0},
32
+ {"name": "Entertainment", "icon": "πŸŽ‰", "budget": 0, "spent": 0, "remaining": 0},
33
+ {"name": "Education", "icon": "πŸ“š", "budget": 0, "spent": 0, "remaining": 0},
34
+ {"name": "Gifts", "icon": "🎁", "budget": 0, "spent": 0, "remaining": 0},
35
+ ]
36
+
37
 
38
  @router.get("/categories")
39
  def get_categories(
 
42
  ):
43
  family_code = current_user.family_code
44
 
45
+ # ---------------- ROLE CHECK ----------------
46
+ if current_user.role == "member":
47
+ fm = db.query(FamilyMember).filter(
48
+ FamilyMember.family_code == family_code,
49
+ FamilyMember.user_id == current_user.id
50
+ ).first()
51
+
52
+ if not fm:
53
+ raise HTTPException(400, "Member record not found")
54
+
55
+ scope = "member"
56
+ owner_id = fm.id
57
+ base_categories = MEMBER_CATEGORIES
58
+
59
+ else: # head
60
+ scope = "family"
61
+ owner_id = None
62
+ base_categories = HEAD_CATEGORIES
63
+
64
+ # ---------------- FETCH BUDGETS ----------------
65
  rows = db.query(CategoryBudget).filter(
66
+ CategoryBudget.family_code == family_code,
67
+ CategoryBudget.scope == scope,
68
+ CategoryBudget.owner_id == owner_id
69
  ).all()
70
 
71
+ # Safe lookup
72
  db_map = {row.category_name: row for row in rows}
73
 
74
+ # ---------------- BUILD RESPONSE ----------------
75
  result = []
76
 
77
+ for cat in base_categories:
78
  name = cat["name"]
79
  icon = cat["icon"]
80
 
81
+ row = db_map.get(name)
82
+ budget = row.budget if row else 0
83
+ spent = row.spent if row else 0
 
 
 
 
 
 
 
84
 
85
  result.append({
86
  "name": name,
87
  "icon": icon,
88
  "budget": budget,
89
  "spent": spent,
90
+ "remaining": max(budget - spent, 0),
91
  })
92
 
93
  return {
94
  "status": True,
95
+ "role": current_user.role,
96
  "categories": result
97
  }
98
 
 
99
  @router.post("/add")
100
  def add_expense(
101
  payload: AddExpenseRequest,