@@ -18,6 +18,8 @@ class ExpenseSplit(BaseModel):
1818 amount : float = Field (..., gt = 0 )
1919 type : SplitType = SplitType .EQUAL
2020
21+ model_config = {"populate_by_name" : True }
22+
2123class ExpenseCreateRequest (BaseModel ):
2224 description : str = Field (..., min_length = 1 , max_length = 500 )
2325 amount : float = Field (..., gt = 0 )
@@ -27,13 +29,23 @@ class ExpenseCreateRequest(BaseModel):
2729 receiptUrls : Optional [List [str ]] = []
2830
2931 @validator ('splits' )
30- def validate_splits_sum (cls , v , values ):
31- if 'amount' in values :
32- total_split = sum (split .amount for split in v )
33- if abs (total_split - values ['amount' ]) > 0.01 : # Allow small floating point differences
34- raise ValueError ('Split amounts must sum to total expense amount' )
32+ def validate_splits_sum (cls , v , values , ** kwargs ):
33+ # Always validate splits if provided
34+ if v is not None :
35+ # Use the provided amount if present, else try to get from instance (for partial update)
36+ amount = values .get ('amount' )
37+ if amount is None :
38+ instance = kwargs .get ('instance' )
39+ if instance is not None :
40+ amount = getattr (instance , 'amount' , None )
41+ if amount is not None :
42+ total_split = sum (split .amount for split in v )
43+ if abs (total_split - amount ) > 0.01 :
44+ raise ValueError ('Split amounts must sum to total expense amount' )
3545 return v
3646
47+ model_config = {"populate_by_name" : True }
48+
3749class ExpenseUpdateRequest (BaseModel ):
3850 description : Optional [str ] = Field (None , min_length = 1 , max_length = 500 )
3951 amount : Optional [float ] = Field (None , gt = 0 )
@@ -50,9 +62,7 @@ def validate_splits_sum(cls, v, values):
5062 raise ValueError ('Split amounts must sum to total expense amount' )
5163 return v
5264
53- class Config :
54- # Allow validation to work with partial updates
55- validate_assignment = True
65+ model_config = {"populate_by_name" : True , "validate_assignment" : True }
5666
5767class ExpenseComment (BaseModel ):
5868 id : str = Field (alias = "_id" )
@@ -113,38 +123,52 @@ class OptimizedSettlement(BaseModel):
113123 amount : float
114124 consolidatedExpenses : Optional [List [str ]] = []
115125
126+ model_config = {"populate_by_name" : True }
127+
116128class GroupSummary (BaseModel ):
117129 totalExpenses : float
118130 totalSettlements : int
119131 optimizedSettlements : List [OptimizedSettlement ]
120132
133+ model_config = {"populate_by_name" : True }
134+
121135class ExpenseCreateResponse (BaseModel ):
122136 expense : ExpenseResponse
123137 settlements : List [Settlement ]
124138 groupSummary : GroupSummary
125139
140+ model_config = {"populate_by_name" : True }
141+
126142class ExpenseListResponse (BaseModel ):
127143 expenses : List [ExpenseResponse ]
128144 pagination : Dict [str , Any ]
129145 summary : Dict [str , Any ]
130146
147+ model_config = {"populate_by_name" : True }
148+
131149class SettlementCreateRequest (BaseModel ):
132150 payer_id : str
133151 payee_id : str
134152 amount : float = Field (..., gt = 0 )
135153 description : Optional [str ] = None
136154 paidAt : Optional [datetime ] = None
137155
156+ model_config = {"populate_by_name" : True }
157+
138158class SettlementUpdateRequest (BaseModel ):
139159 status : SettlementStatus
140160 paidAt : Optional [datetime ] = None
141161
162+ model_config = {"populate_by_name" : True }
163+
142164class SettlementListResponse (BaseModel ):
143165 settlements : List [Settlement ]
144166 optimizedSettlements : List [OptimizedSettlement ]
145167 summary : Dict [str , Any ]
146168 pagination : Dict [str , Any ]
147169
170+ model_config = {"populate_by_name" : True }
171+
148172class UserBalance (BaseModel ):
149173 userId : str
150174 userName : str
@@ -155,12 +179,16 @@ class UserBalance(BaseModel):
155179 pendingSettlements : List [Settlement ] = []
156180 recentExpenses : List [Dict [str , Any ]] = []
157181
182+ model_config = {"populate_by_name" : True }
183+
158184class FriendBalanceBreakdown (BaseModel ):
159185 groupId : str
160186 groupName : str
161187 balance : float
162188 owesYou : bool
163189
190+ model_config = {"populate_by_name" : True }
191+
164192class FriendBalance (BaseModel ):
165193 userId : str
166194 userName : str
@@ -170,17 +198,23 @@ class FriendBalance(BaseModel):
170198 breakdown : List [FriendBalanceBreakdown ]
171199 lastActivity : datetime
172200
201+ model_config = {"populate_by_name" : True }
202+
173203class FriendsBalanceResponse (BaseModel ):
174204 friendsBalance : List [FriendBalance ]
175205 summary : Dict [str , Any ]
176206
207+ model_config = {"populate_by_name" : True }
208+
177209class BalanceSummaryResponse (BaseModel ):
178210 totalOwedToYou : float
179211 totalYouOwe : float
180212 netBalance : float
181213 currency : str = "USD"
182214 groupsSummary : List [Dict [str , Any ]]
183215
216+ model_config = {"populate_by_name" : True }
217+
184218class ExpenseAnalytics (BaseModel ):
185219 period : str
186220 totalExpenses : float
@@ -190,10 +224,16 @@ class ExpenseAnalytics(BaseModel):
190224 memberContributions : List [Dict [str , Any ]]
191225 expenseTrends : List [Dict [str , Any ]]
192226
227+ model_config = {"populate_by_name" : True }
228+
193229class AttachmentUploadResponse (BaseModel ):
194230 attachment_key : str
195231 url : str
196232
233+ model_config = {"populate_by_name" : True }
234+
197235class OptimizedSettlementsResponse (BaseModel ):
198236 optimizedSettlements : List [OptimizedSettlement ]
199237 savings : Dict [str , Any ]
238+
239+ model_config = {"populate_by_name" : True }
0 commit comments