11import { useContext , useEffect , useState } from 'react' ;
22import { Alert , KeyboardAvoidingView , Platform , StyleSheet , View } from 'react-native' ;
3- import { ActivityIndicator , Appbar , Button , Checkbox , Menu , Paragraph , SegmentedButtons , Text , TextInput , Title } from 'react-native-paper' ;
3+ import { ActivityIndicator , Button , Checkbox , Menu , Paragraph , SegmentedButtons , Text , TextInput , Title } from 'react-native-paper' ;
44import { createExpense , getGroupMembers } from '../api/groups' ;
55import { AuthContext } from '../context/AuthContext' ;
66
@@ -13,7 +13,7 @@ const AddExpenseScreen = ({ route, navigation }) => {
1313 const [ isLoading , setIsLoading ] = useState ( true ) ;
1414 const [ isSubmitting , setIsSubmitting ] = useState ( false ) ;
1515 const [ splitMethod , setSplitMethod ] = useState ( 'equal' ) ;
16- const [ payerId , setPayerId ] = useState ( user . _id ) ;
16+ const [ payerId , setPayerId ] = useState ( null ) ; // Initialize as null until members are loaded
1717 const [ menuVisible , setMenuVisible ] = useState ( false ) ;
1818
1919 // State for different split methods
@@ -51,6 +51,14 @@ const AddExpenseScreen = ({ route, navigation }) => {
5151 setPercentages ( initialPercentages ) ;
5252 setExactAmounts ( initialExactAmounts ) ;
5353 setSelectedMembers ( initialSelectedMembers ) ;
54+
55+ // Set default payer to current user if they're a member
56+ const currentUserMember = response . data . find ( member => member . userId === user . _id ) ;
57+ if ( currentUserMember ) {
58+ setPayerId ( user . _id ) ;
59+ } else if ( response . data . length > 0 ) {
60+ setPayerId ( response . data [ 0 ] . userId ) ;
61+ }
5462 } catch ( error ) {
5563 console . error ( 'Failed to fetch members:' , error ) ;
5664 Alert . alert ( 'Error' , 'Failed to fetch group members.' ) ;
@@ -68,6 +76,10 @@ const AddExpenseScreen = ({ route, navigation }) => {
6876 Alert . alert ( 'Error' , 'Please fill in all fields.' ) ;
6977 return ;
7078 }
79+ if ( ! payerId ) {
80+ Alert . alert ( 'Error' , 'Please select who paid for this expense.' ) ;
81+ return ;
82+ }
7183 const numericAmount = parseFloat ( amount ) ;
7284 if ( isNaN ( numericAmount ) || numericAmount <= 0 ) {
7385 Alert . alert ( 'Error' , 'Please enter a valid amount.' ) ;
@@ -87,9 +99,13 @@ const AddExpenseScreen = ({ route, navigation }) => {
8799 throw new Error ( 'You must select at least one member for the split.' ) ;
88100 }
89101 const splitAmount = Math . round ( ( numericAmount / includedMembers . length ) * 100 ) / 100 ;
90- splits = includedMembers . map ( userId => ( {
102+ // Calculate remainder to handle rounding
103+ const totalSplitAmount = splitAmount * includedMembers . length ;
104+ const remainder = Math . round ( ( numericAmount - totalSplitAmount ) * 100 ) / 100 ;
105+
106+ splits = includedMembers . map ( ( userId , index ) => ( {
91107 userId,
92- amount : splitAmount ,
108+ amount : index === 0 ? splitAmount + remainder : splitAmount , // Add remainder to first member
93109 type : 'equal'
94110 } ) ) ;
95111 splitType = 'equal' ;
@@ -120,17 +136,32 @@ const AddExpenseScreen = ({ route, navigation }) => {
120136 } ) ) ;
121137 splitType = 'percentage' ;
122138 } else if ( splitMethod === 'shares' ) {
123- const totalShares = Object . values ( shares ) . reduce ( ( sum , val ) => sum + parseInt ( val || '0' , 10 ) , 0 ) ;
139+ const nonZeroShares = Object . entries ( shares ) . filter ( ( [ userId , value ] ) => parseInt ( value || '0' , 10 ) > 0 ) ;
140+ const totalShares = nonZeroShares . reduce ( ( sum , [ , value ] ) => sum + parseInt ( value || '0' , 10 ) , 0 ) ;
141+
124142 if ( totalShares === 0 ) {
125143 throw new Error ( 'Total shares cannot be zero.' ) ;
126144 }
127- splits = Object . entries ( shares )
128- . filter ( ( [ userId , value ] ) => parseInt ( value || '0' , 10 ) > 0 )
129- . map ( ( [ userId , value ] ) => ( {
145+
146+ // Calculate amounts with proper rounding
147+ const amounts = nonZeroShares . map ( ( [ userId , value ] ) => {
148+ const shareRatio = parseInt ( value , 10 ) / totalShares ;
149+ return {
130150 userId,
131- amount : Math . round ( ( numericAmount * ( parseInt ( value , 10 ) / totalShares ) ) * 100 ) / 100 ,
151+ amount : Math . round ( ( numericAmount * shareRatio ) * 100 ) / 100 ,
132152 type : 'unequal'
133- } ) ) ;
153+ } ;
154+ } ) ;
155+
156+ // Adjust for rounding errors
157+ const totalCalculated = amounts . reduce ( ( sum , item ) => sum + item . amount , 0 ) ;
158+ const difference = Math . round ( ( numericAmount - totalCalculated ) * 100 ) / 100 ;
159+
160+ if ( Math . abs ( difference ) > 0 ) {
161+ amounts [ 0 ] . amount = Math . round ( ( amounts [ 0 ] . amount + difference ) * 100 ) / 100 ;
162+ }
163+
164+ splits = amounts ;
134165 splitType = 'unequal' ; // Backend uses 'unequal' for shares
135166 }
136167
@@ -143,6 +174,7 @@ const AddExpenseScreen = ({ route, navigation }) => {
143174 tags : [ ]
144175 } ;
145176
177+ console . log ( 'Expense data being sent:' , JSON . stringify ( expenseData , null , 2 ) ) ;
146178 await createExpense ( token , groupId , expenseData ) ;
147179 Alert . alert ( 'Success' , 'Expense added successfully.' ) ;
148180 navigation . goBack ( ) ;
@@ -244,20 +276,14 @@ const AddExpenseScreen = ({ route, navigation }) => {
244276 ) ;
245277 }
246278
247- const selectedPayerName = members . find ( m => m . userId === payerId ) ?. user . name || 'Select Payer' ;
279+ const selectedPayerName = payerId ? ( members . find ( m => m . userId === payerId ) ?. user . name || 'Select Payer' ) : 'Select Payer' ;
248280
249281 return (
250- < View style = { styles . container } >
251- < Appbar . Header >
252- < Appbar . BackAction onPress = { ( ) => navigation . goBack ( ) } />
253- < Appbar . Content title = "Add Expense" />
254- </ Appbar . Header >
255- < KeyboardAvoidingView
256- behavior = { Platform . OS === 'ios' ? 'padding' : 'height' }
257- style = { styles . keyboardAvoidingView }
258- >
259- < View style = { styles . content } >
260- < Title > New Expense Details</ Title >
282+ < KeyboardAvoidingView
283+ behavior = { Platform . OS === 'ios' ? 'padding' : 'height' }
284+ style = { styles . container }
285+ >
286+ < View style = { styles . content } >
261287 < TextInput
262288 label = "Description"
263289 value = { description }
@@ -341,18 +367,14 @@ const AddExpenseScreen = ({ route, navigation }) => {
341367 Add Expense
342368 </ Button >
343369 </ View >
344- </ KeyboardAvoidingView >
345- </ View >
370+ </ KeyboardAvoidingView >
346371 ) ;
347372} ;
348373
349374const styles = StyleSheet . create ( {
350375 container : {
351376 flex : 1 ,
352377 } ,
353- keyboardAvoidingView : {
354- flex : 1 ,
355- } ,
356378 content : {
357379 flex : 1 ,
358380 padding : 16 ,
0 commit comments