@marcos_feitoza/personal-finance-backen-trades-assets 1.0.2 → 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,17 @@
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
+
8
+ ## [1.0.3](https://github.com/MarcosOps/personal-finance-backend-trades-assets/compare/v1.0.2...v1.0.3) (2025-11-11)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * crypto symbol working ([9c61bf9](https://github.com/MarcosOps/personal-finance-backend-trades-assets/commit/9c61bf91b382fc7563985c1973fc6a37e9e361c1))
14
+
1
15
  ## [1.0.2](https://github.com/MarcosOps/personal-finance-backend-trades-assets/compare/v1.0.1...v1.0.2) (2025-10-17)
2
16
 
3
17
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marcos_feitoza/personal-finance-backen-trades-assets",
3
- "version": "1.0.2",
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,32 +16,50 @@ 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")
21
- db_asset = models.Asset(**asset.dict())
28
+
29
+ # user_id is removed from Asset model
30
+ db_asset = models.Asset(
31
+ symbol=asset.symbol,
32
+ name=asset.name,
33
+ asset_type=asset.asset_type,
34
+ industry=asset.industry,
35
+ id_crypto=asset.id_crypto,
36
+ )
22
37
  db.add(db_asset)
23
38
  db.commit()
24
39
  db.refresh(db_asset)
25
40
  return db_asset
26
41
 
27
42
  @router.get("/", response_model=list[schemas.AssetResponse])
28
- def read_assets(investment_account: str = None, skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
29
- logger.info(f"Reading assets for investment account: {investment_account}")
30
- 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
+ )
31
56
 
32
57
  if investment_account:
33
- # Get asset_ids from trades for the given investment_account
34
- asset_ids_in_account = db.query(distinct(models.Trade.asset_id)).filter(
35
- models.Trade.investment_account == investment_account
36
- ).all()
37
- # Extract just the IDs from the list of tuples
38
- asset_ids = [id for (id,) in asset_ids_in_account]
39
-
40
- # Filter assets by these IDs
41
- 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()
42
62
 
43
63
  assets = query.offset(skip).limit(limit).all()
44
- logger.info(f"Found {len(assets)} assets")
64
+ logger.info(f"Found {len(assets)} assets for user {current_user.id}")
45
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,65 +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()
39
- if not db_asset and '.' not in trade_symbol:
40
- db_asset = db.query(models.Asset).filter(models.Asset.symbol == f"{trade_symbol}.TO").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()
41
49
 
42
50
  if not db_asset:
43
- logger.info(f"Asset with symbol {trade_symbol} not found. Fetching details...")
44
- asset_details = _get_asset_details_from_service(trade_symbol)
45
- canonical_symbol = asset_details.get("canonical_symbol", trade_symbol)
46
- db_asset = db.query(models.Asset).filter(models.Asset.symbol == canonical_symbol).first()
47
-
48
- if not db_asset:
49
- logger.info(f"Creating new asset with canonical symbol: {canonical_symbol}")
50
- db_asset = models.Asset(
51
- symbol=canonical_symbol,
52
- name=asset_details.get("name", canonical_symbol),
53
- asset_type=asset_details.get("asset_type", "Unknown"),
54
- industry=asset_details.get("industry", "N/A")
55
- )
56
- db.add(db_asset)
57
- db.commit()
58
- db.refresh(db_asset)
59
- logger.info(f"Created new asset with ID: {db_asset.id}")
60
- else:
61
- logger.info(f"Found existing asset with canonical symbol: {canonical_symbol}")
51
+ logger.info(f"Asset with symbol {trade_symbol} not found globally. Creating new asset...")
52
+ db_asset = models.Asset(
53
+ symbol=trade_symbol,
54
+ name=trade.name,
55
+ asset_type=trade.asset_type,
56
+ industry=trade.industry,
57
+ id_crypto=trade.id_crypto if hasattr(trade, 'id_crypto') else None,
58
+ # user_id is no longer part of Asset model
59
+ )
60
+ db.add(db_asset)
61
+ db.commit()
62
+ db.refresh(db_asset)
63
+ logger.info(f"Created new global asset with ID: {db_asset.id}")
62
64
  else:
63
- logger.info(f"Found existing asset with ID: {db_asset.id}")
65
+ logger.info(f"Found existing global asset with ID: {db_asset.id}")
64
66
 
65
67
  if trade.trade_type == 'buy':
66
68
  investment_account_name = trade.investment_account
67
69
  trade_cost = trade.shares * trade.price
68
70
  current_balance = 0
71
+ headers = {"Authorization": f"Bearer {token}"}
69
72
  try:
70
73
  async with httpx.AsyncClient() as client:
71
- 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)
72
75
  response.raise_for_status()
73
76
  data = response.json()
74
77
  current_balance = data['balance']
75
78
  logger.info(f"Calculated cash balance for '{investment_account_name}' is {current_balance}")
76
79
  except Exception as e:
77
- 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}")
78
81
 
79
82
  if float(current_balance) < float(trade_cost):
80
- 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}")
81
84
  raise HTTPException(
82
85
  status_code=400,
83
86
  detail=f"Insufficient cash balance in '{investment_account_name}' to complete trade."
84
87
  )
85
88
 
86
- trade_data = trade.dict(exclude={'symbol', 'name', 'asset_type', 'industry'})
87
- db_trade = models.Trade(**trade_data, asset_id=db_asset.id)
89
+ trade_data = trade.dict(exclude={'symbol', 'name', 'asset_type', 'industry', 'id_crypto'})
90
+ db_trade = models.Trade(**trade_data, asset_id=db_asset.id, user_id=current_user.id)
88
91
 
89
92
  if db_trade.trade_type == 'buy':
90
- 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.")
91
94
  elif db_trade.trade_type == 'sell':
92
- 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.")
93
96
 
94
97
  db.add(db_trade)
95
98
  db.commit()
@@ -101,13 +104,14 @@ def read_trades(
101
104
  investment_account: str = None,
102
105
  skip: int = 0,
103
106
  limit: int = 100,
104
- db: Session = Depends(get_db)
107
+ db: Session = Depends(get_db),
108
+ current_user: models.User = Depends(get_current_user)
105
109
  ):
106
- logger.info(f"Reading trades for account: {investment_account}")
107
- 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)
108
112
  if investment_account:
109
113
  query = query.filter(models.Trade.investment_account == investment_account)
110
114
 
111
115
  trades = query.order_by(models.Trade.date.desc()).offset(skip).limit(limit).all()
112
- logger.info(f"Found {len(trades)} trades")
116
+ logger.info(f"Found {len(trades)} trades for user {current_user.id}")
113
117
  return trades