diff --git a/backend/src/controllers/portfolio.go b/backend/src/controllers/portfolio.go index d2747d2c..bd4534ec 100644 --- a/backend/src/controllers/portfolio.go +++ b/backend/src/controllers/portfolio.go @@ -4,7 +4,11 @@ import ( "backend/src/services" "net/http" + // "fmt" + "github.com/gin-gonic/gin" + + "backend/src/models" ) type PortfolioController struct { @@ -21,20 +25,26 @@ func NewPortfolioController(portfolioService *services.PortfolioService, etradeS // CopyPortfolio copies the target user's portfolio to the current user's portfolio func (etc *PortfolioController) CopyPortfolio(c *gin.Context) { - targetUserID := c.Param("target_user_id") - currentUserID := c.Param("current_user_id") + currentUserID := c.Query("current_user_id") + targetUserID := c.Query("target_user_id") - // get current user's portfolio. If it doesn't exist, return error + // get current user's portfolio. If it doesn't exist, create empty portfolio currentUserPortfolio, err := etc.portfolioService.GetUserPortfolio(currentUserID) if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"message": "Current User does not have an existing portfolio"}) - return + // create empty portfolio for the user + emptyPortfolio := &models.UserPortfolio{} + newPortfolio, createErr := etc.portfolioService.CreateUserPortfolio(currentUserID, emptyPortfolio) + currentUserPortfolio = newPortfolio + if createErr != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create empty portfolio", "errorMessage": createErr.Error()}) + return + } } // get target user's portfolio. If it doesn't exist, return error targetPortfolio, err := etc.portfolioService.GetUserPortfolio(targetUserID) if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"message": "Target User does not have portfolio to copy"}) + c.JSON(http.StatusBadRequest, gin.H{"message": "Target User does not have portfolio to copy", "errorMessage": err.Error()}) return } @@ -59,6 +69,7 @@ func (etc *PortfolioController) CopyPortfolio(c *gin.Context) { } c.JSON(http.StatusOK, updatedPortfolio) + // c.JSON(http.StatusOK, copiedPortfolio) } // GetUserPortfolio returns the user's portfolio diff --git a/backend/src/routes/portfolio.go b/backend/src/routes/portfolio.go index a102542d..2ebfad57 100644 --- a/backend/src/routes/portfolio.go +++ b/backend/src/routes/portfolio.go @@ -15,7 +15,7 @@ func SetupPortfolioRoutes(router *gin.Engine, db *gorm.DB) { portfolioRoutes := router.Group("/portfolio") { - portfolioRoutes.PUT("/:target_user_id/:current_user_id", portfolioController.CopyPortfolio) + portfolioRoutes.PUT("", portfolioController.CopyPortfolio) /* different than getportfolio in etrade, returns single object for simplicity keep etrade getportfolio route to match actual Etrade data use either call depending on what you need diff --git a/backend/src/services/portfolio.go b/backend/src/services/portfolio.go index 70de204b..065a6c78 100644 --- a/backend/src/services/portfolio.go +++ b/backend/src/services/portfolio.go @@ -22,6 +22,24 @@ func (os *PortfolioService) CopyPortfolio(currentUserPortfolio models.UserPortfo var newPositions []models.Position targetPositions := targetPortfolio.Positions for _, position := range targetPositions { + // check if position exists in currentUserPortfolio.positions (matching by position.Ticker) + positionExists := false + var matchingPosition models.Position + for i, p := range currentUserPortfolio.Positions { + if p.Ticker == position.Ticker { + positionExists = true + matchingPosition = currentUserPortfolio.Positions[i] + break + } + } + + // if position exists, update the quantity -> skip to next position + if positionExists { + matchingPosition.Quantity += position.Quantity + continue + } + + // if position doesn't already exist, copy the position newPosition := models.Position{ UserPortfolioID: currentUserPortfolio.ID, PositionID: position.PositionID, @@ -66,3 +84,13 @@ func (os *PortfolioService) GetUserPortfolio(userID string) (*models.UserPortfol } return &portfolio, nil } + +// CreateUserPortfolio creates a new portfolio for the user. +func (os *PortfolioService) CreateUserPortfolio(userID string, portfolio *models.UserPortfolio) (*models.UserPortfolio, error) { + portfolio.UserID = userID + err := os.DB.Create(portfolio).Error + if err != nil { + return nil, err + } + return portfolio, nil +} diff --git a/frontend/pages/Copy/CopyTradesPage.tsx b/frontend/pages/Copy/CopyTradesPage.tsx index aa67bbbb..0fd839c8 100644 --- a/frontend/pages/Copy/CopyTradesPage.tsx +++ b/frontend/pages/Copy/CopyTradesPage.tsx @@ -16,8 +16,7 @@ import { AuthNavigationProp, } from '../../types/navigationTypes'; import { CopyRouteParams } from '../../types/types'; -// import { User } from '../../types/types'; -// import { copyTrades } from '../../services/copy'; +import { copyTrades } from '../../services/copy'; function CopyTradesPage() { const { session } = useSession(); @@ -38,7 +37,8 @@ function CopyTradesPage() { } try { - // await copyTrades(session?.user.id as string, user.username); + await copyTrades(session?.user.id as string, user?.id); + // Alert.alert(`session?.user.id: ${session?.user.id} | user.id: ${user?.id}`); } catch (error) { Alert.alert('Error', 'Failed to copy trades'); return; @@ -69,10 +69,16 @@ function CopyTradesPage() { Investment Amount { + if (text.startsWith('$')) { + text = text.slice(1); + } + text = text.replace(/[^0-9]/g, ''); + setInvestmentAmount(text); + }} + style={styles.input} /> This investment will proportionally copy this investor’s portfolio. @@ -92,10 +98,16 @@ function CopyTradesPage() { Investment Falls Below { + if (text.startsWith('$')) { + text = text.slice(1); + } + text = text.replace(/[^0-9]/g, ''); + setStopLossAmount(text); + }} style={styles.input} - keyboardType="numeric" /> )} diff --git a/frontend/reducers/onboarding/onboardingReducer.ts b/frontend/reducers/onboarding/onboardingReducer.ts index bb6dc5b7..d0150442 100644 --- a/frontend/reducers/onboarding/onboardingReducer.ts +++ b/frontend/reducers/onboarding/onboardingReducer.ts @@ -13,7 +13,7 @@ const onboardingSlice = createSlice({ financialGoalsShortTerm: [], financialGoalsLongTerm: [], financialLiteracy: [], - isOnboarding: 'normal', // 'onboarding', 'normal', 'makingPost' + isOnboarding: 'onboarding', // 'onboarding', 'normal', 'makingPost' }, reducers: { updateFirstName(state, action) { diff --git a/frontend/services/copy.ts b/frontend/services/copy.ts index 7131449f..a99e7ca2 100644 --- a/frontend/services/copy.ts +++ b/frontend/services/copy.ts @@ -4,7 +4,7 @@ import { Redirect } from '../types/types'; export const copyTrades = async (currentUserId: string, targetUserId: string) => { const response: AxiosResponse = await axios.post( - `http://${API_LINK}/portfolio/${currentUserId}/${targetUserId}` + `http://${API_LINK}/portfolio?current_user_id=${currentUserId}&target_user_id=${targetUserId}` ); return response.data; };