@marcos_feitoza/personal-finance-backen-trades-assets 1.0.3 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## [1.0.4](https://github.com/MarcosOps/personal-finance-backend-trades-assets/compare/v1.0.3...v1.0.4) (2025-11-21)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * update trades and assets to support current user ([7a976a5](https://github.com/MarcosOps/personal-finance-backend-trades-assets/commit/7a976a576eea4ab9758e5fa20d5b1d314ae72912))
7
+
1
8
  ## [1.0.3](https://github.com/MarcosOps/personal-finance-backend-trades-assets/compare/v1.0.2...v1.0.3) (2025-11-11)
2
9
 
3
10
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marcos_feitoza/personal-finance-backen-trades-assets",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -4,6 +4,8 @@ from sqlalchemy import distinct
4
4
  from personal_finance_shared import models, schemas
5
5
  from personal_finance_shared.database import get_db
6
6
  import logging
7
+ from personal_finance_shared.dependencies import get_current_user
8
+
7
9
 
8
10
  logging.basicConfig(level=logging.INFO)
9
11
  logger = logging.getLogger(__name__)
@@ -14,16 +16,23 @@ router = APIRouter(
14
16
  )
15
17
 
16
18
  @router.post("/", response_model=schemas.AssetResponse)
17
- def create_asset(asset: schemas.AssetCreate, db: Session = Depends(get_db)):
19
+ def create_asset(
20
+ asset: schemas.AssetCreate,
21
+ db: Session = Depends(get_db),
22
+ current_user: models.User = Depends(get_current_user) # Keep for authorization if needed
23
+ ):
24
+ # Assets are global now, check for symbol existence globally
18
25
  db_asset = db.query(models.Asset).filter(models.Asset.symbol == asset.symbol).first()
19
26
  if db_asset:
20
27
  raise HTTPException(status_code=400, detail="Asset with this symbol already exists")
28
+
29
+ # user_id is removed from Asset model
21
30
  db_asset = models.Asset(
22
31
  symbol=asset.symbol,
23
32
  name=asset.name,
24
33
  asset_type=asset.asset_type,
25
34
  industry=asset.industry,
26
- id_crypto=asset.id_crypto
35
+ id_crypto=asset.id_crypto,
27
36
  )
28
37
  db.add(db_asset)
29
38
  db.commit()
@@ -31,21 +40,26 @@ def create_asset(asset: schemas.AssetCreate, db: Session = Depends(get_db)):
31
40
  return db_asset
32
41
 
33
42
  @router.get("/", response_model=list[schemas.AssetResponse])
34
- def read_assets(investment_account: str = None, skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
35
- logger.info(f"Reading assets for investment account: {investment_account}")
36
- query = db.query(models.Asset)
43
+ def read_assets(
44
+ investment_account: str = None,
45
+ skip: int = 0,
46
+ limit: int = 100,
47
+ db: Session = Depends(get_db),
48
+ current_user: models.User = Depends(get_current_user)
49
+ ):
50
+ logger.info(f"Reading assets for user {current_user.id}, investment account: {investment_account}")
51
+
52
+ # The query should join Trades and Assets to find assets owned by the user.
53
+ query = db.query(models.Asset).join(models.Trade).filter(
54
+ models.Trade.user_id == current_user.id
55
+ )
37
56
 
38
57
  if investment_account:
39
- # Get asset_ids from trades for the given investment_account
40
- asset_ids_in_account = db.query(distinct(models.Trade.asset_id)).filter(
41
- models.Trade.investment_account == investment_account
42
- ).all()
43
- # Extract just the IDs from the list of tuples
44
- asset_ids = [id for (id,) in asset_ids_in_account]
45
-
46
- # Filter assets by these IDs
47
- query = query.filter(models.Asset.id.in_(asset_ids))
58
+ query = query.filter(models.Trade.investment_account == investment_account)
59
+
60
+ # Use distinct to avoid duplicate assets if a user traded the same asset multiple times
61
+ query = query.distinct()
48
62
 
49
63
  assets = query.offset(skip).limit(limit).all()
50
- logger.info(f"Found {len(assets)} assets")
64
+ logger.info(f"Found {len(assets)} assets for user {current_user.id}")
51
65
  return assets
@@ -6,6 +6,8 @@ import logging
6
6
  import requests
7
7
  import os
8
8
  import httpx
9
+ from personal_finance_shared.dependencies import get_current_user, get_current_token
10
+
9
11
 
10
12
  logging.basicConfig(level=logging.INFO)
11
13
  logger = logging.getLogger(__name__)
@@ -31,56 +33,66 @@ def _get_asset_details_from_service(symbol: str) -> dict:
31
33
  return {}
32
34
 
33
35
  @router.post("/", response_model=schemas.TradeResponse)
34
- async def create_trade(trade: schemas.TradeCreate, db: Session = Depends(get_db)):
36
+ async def create_trade(
37
+ trade: schemas.TradeCreate,
38
+ db: Session = Depends(get_db),
39
+ current_user: models.User = Depends(get_current_user),
40
+ token: str = Depends(get_current_token)
41
+ ):
35
42
  trade_symbol = trade.symbol.upper()
36
- logger.info(f"Processing trade for symbol: {trade_symbol}")
43
+ logger.info(f"Processing trade for user {current_user.id}, symbol: {trade_symbol}")
37
44
 
38
- db_asset = db.query(models.Asset).filter(models.Asset.symbol == trade_symbol).first()
45
+ # Search for the asset globally, not by user_id
46
+ db_asset = db.query(models.Asset).filter(
47
+ models.Asset.symbol == trade_symbol
48
+ ).first()
39
49
 
40
50
  if not db_asset:
41
- logger.info(f"Asset with symbol {trade_symbol} not found. Creating new asset...")
51
+ logger.info(f"Asset with symbol {trade_symbol} not found globally. Creating new asset...")
42
52
  db_asset = models.Asset(
43
53
  symbol=trade_symbol,
44
54
  name=trade.name,
45
55
  asset_type=trade.asset_type,
46
56
  industry=trade.industry,
47
- id_crypto=trade.id_crypto if hasattr(trade, 'id_crypto') else None
57
+ id_crypto=trade.id_crypto if hasattr(trade, 'id_crypto') else None,
58
+ # user_id is no longer part of Asset model
48
59
  )
49
60
  db.add(db_asset)
50
61
  db.commit()
51
62
  db.refresh(db_asset)
52
- logger.info(f"Created new asset with ID: {db_asset.id}")
63
+ logger.info(f"Created new global asset with ID: {db_asset.id}")
53
64
  else:
54
- logger.info(f"Found existing asset with ID: {db_asset.id}")
65
+ logger.info(f"Found existing global asset with ID: {db_asset.id}")
55
66
 
56
67
  if trade.trade_type == 'buy':
57
68
  investment_account_name = trade.investment_account
58
69
  trade_cost = trade.shares * trade.price
59
70
  current_balance = 0
71
+ headers = {"Authorization": f"Bearer {token}"}
60
72
  try:
61
73
  async with httpx.AsyncClient() as client:
62
- response = await client.get(f"{BALANCE_SERVICE_URL}/api/balance/{investment_account_name}")
74
+ response = await client.get(f"{BALANCE_SERVICE_URL}/api/balance/{investment_account_name}", headers=headers)
63
75
  response.raise_for_status()
64
76
  data = response.json()
65
77
  current_balance = data['balance']
66
78
  logger.info(f"Calculated cash balance for '{investment_account_name}' is {current_balance}")
67
79
  except Exception as e:
68
- logger.error(f"Could not retrieve balance for validation: {e}")
80
+ logger.error(f"Could not retrieve balance for validation for user {current_user.id}: {e}")
69
81
 
70
82
  if float(current_balance) < float(trade_cost):
71
- logger.warning(f"Insufficient funds in '{investment_account_name}'. Balance: {current_balance}, Required: {trade_cost}")
83
+ logger.warning(f"Insufficient funds in '{investment_account_name}' for user {current_user.id}. Balance: {current_balance}, Required: {trade_cost}")
72
84
  raise HTTPException(
73
85
  status_code=400,
74
86
  detail=f"Insufficient cash balance in '{investment_account_name}' to complete trade."
75
87
  )
76
88
 
77
89
  trade_data = trade.dict(exclude={'symbol', 'name', 'asset_type', 'industry', 'id_crypto'})
78
- db_trade = models.Trade(**trade_data, asset_id=db_asset.id)
90
+ db_trade = models.Trade(**trade_data, asset_id=db_asset.id, user_id=current_user.id)
79
91
 
80
92
  if db_trade.trade_type == 'buy':
81
- logger.info(f"STOCK PURCHASE: Bought {db_trade.shares} shares of {trade_symbol} at ${db_trade.price:.2f} each.")
93
+ logger.info(f"STOCK PURCHASE for user {current_user.id}: Bought {db_trade.shares} shares of {trade_symbol} at ${db_trade.price:.2f} each.")
82
94
  elif db_trade.trade_type == 'sell':
83
- logger.info(f"STOCK SALE: Sold {db_trade.shares} shares of {trade_symbol} at ${db_trade.price:.2f} each.")
95
+ logger.info(f"STOCK SALE for user {current_user.id}: Sold {db_trade.shares} shares of {trade_symbol} at ${db_trade.price:.2f} each.")
84
96
 
85
97
  db.add(db_trade)
86
98
  db.commit()
@@ -92,13 +104,14 @@ def read_trades(
92
104
  investment_account: str = None,
93
105
  skip: int = 0,
94
106
  limit: int = 100,
95
- db: Session = Depends(get_db)
107
+ db: Session = Depends(get_db),
108
+ current_user: models.User = Depends(get_current_user)
96
109
  ):
97
- logger.info(f"Reading trades for account: {investment_account}")
98
- query = db.query(models.Trade)
110
+ logger.info(f"Reading trades for user {current_user.id}, account: {investment_account}")
111
+ query = db.query(models.Trade).filter(models.Trade.user_id == current_user.id)
99
112
  if investment_account:
100
113
  query = query.filter(models.Trade.investment_account == investment_account)
101
114
 
102
115
  trades = query.order_by(models.Trade.date.desc()).offset(skip).limit(limit).all()
103
- logger.info(f"Found {len(trades)} trades")
116
+ logger.info(f"Found {len(trades)} trades for user {current_user.id}")
104
117
  return trades