@marcos_feitoza/personal-finance-backen-trades-assets 1.1.0 → 1.1.1

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.1.1](https://github.com/MarcosOps/personal-finance-backend-trades-assets/compare/v1.1.0...v1.1.1) (2025-11-26)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * update logs ([5bfcc96](https://github.com/MarcosOps/personal-finance-backend-trades-assets/commit/5bfcc965be907ec04583dc86e8a04caeb1ef8cb5))
7
+
1
8
  # [1.1.0](https://github.com/MarcosOps/personal-finance-backend-trades-assets/compare/v1.0.4...v1.1.0) (2025-11-25)
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.1.0",
3
+ "version": "1.1.1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -3,15 +3,13 @@ from sqlalchemy.orm import Session
3
3
  from datetime import datetime
4
4
  from personal_finance_shared import models, schemas, crud
5
5
  from personal_finance_shared.database import get_db
6
- import logging
7
6
  import requests
8
7
  import os
9
8
  import httpx
10
9
  from personal_finance_shared.dependencies import get_current_user, get_current_token
10
+ from personal_finance_shared.logging_utils import get_logger, REQUEST_ID_CTX
11
11
 
12
-
13
- logging.basicConfig(level=logging.INFO)
14
- logger = logging.getLogger(__name__)
12
+ logger = get_logger("personal_finance_trades_assets.routers.trades")
15
13
 
16
14
  router = APIRouter(
17
15
  prefix="/api/trades",
@@ -21,18 +19,6 @@ router = APIRouter(
21
19
  MARKET_DATA_SERVICE_URL = os.getenv("MARKET_DATA_SERVICE_URL", "http://personal-finance-backend-market-data.app.svc.cluster.local:8000")
22
20
  BALANCE_SERVICE_URL = os.getenv("BALANCE_SERVICE_URL", "http://personal-finance-backend-balance-service.app.svc.cluster.local:8000")
23
21
 
24
- def _get_asset_details_from_service(symbol: str) -> dict:
25
- """Fetches asset details from the market-data service."""
26
- try:
27
- url = f"{MARKET_DATA_SERVICE_URL}/api/market-data/details/{symbol.upper()}"
28
- logger.info(f"Fetching details from: {url}")
29
- response = requests.post(url, timeout=10)
30
- response.raise_for_status()
31
- return response.json()
32
- except requests.exceptions.RequestException as e:
33
- logger.error(f"Error calling market-data service for {symbol}: {e}")
34
- return {}
35
-
36
22
  @router.post("/", response_model=schemas.TradeResponse)
37
23
  async def create_trade(
38
24
  trade: schemas.TradeCreate,
@@ -40,64 +26,72 @@ async def create_trade(
40
26
  current_user: models.User = Depends(get_current_user),
41
27
  token: str = Depends(get_current_token)
42
28
  ):
29
+ correlation_id = REQUEST_ID_CTX.get()
43
30
  trade_symbol = trade.symbol.upper()
44
- logger.info(f"Processing trade for user {current_user.id}, symbol: {trade_symbol}")
31
+ logger.info("Processing trade", extra={'user_id': current_user.id, 'symbol': trade_symbol, 'trade_data': trade.dict()})
45
32
 
46
- # Search for the asset globally, not by user_id
47
33
  db_asset = db.query(models.Asset).filter(
48
34
  models.Asset.symbol == trade_symbol
49
35
  ).first()
50
36
 
51
37
  if not db_asset:
52
- logger.info(f"Asset with symbol {trade_symbol} not found globally. Creating new asset...")
38
+ logger.info("Asset not found globally, creating new asset", extra={'user_id': current_user.id, 'symbol': trade_symbol})
53
39
  db_asset = models.Asset(
54
40
  symbol=trade_symbol,
55
41
  name=trade.name,
56
42
  asset_type=trade.asset_type,
57
43
  industry=trade.industry,
58
44
  id_crypto=trade.id_crypto if hasattr(trade, 'id_crypto') else None,
59
- # user_id is no longer part of Asset model
60
45
  )
61
46
  db.add(db_asset)
62
47
  db.commit()
63
48
  db.refresh(db_asset)
64
- logger.info(f"Created new global asset with ID: {db_asset.id}")
49
+ logger.info("Created new global asset", extra={'user_id': current_user.id, 'asset_id': db_asset.id, 'symbol': db_asset.symbol})
65
50
  else:
66
- logger.info(f"Found existing global asset with ID: {db_asset.id}")
51
+ logger.info("Found existing global asset", extra={'user_id': current_user.id, 'asset_id': db_asset.id, 'symbol': db_asset.symbol})
67
52
 
68
53
  if trade.trade_type == 'buy':
69
54
  investment_account_name = trade.investment_account
70
55
  trade_cost = trade.shares * trade.price
71
56
  current_balance = 0
72
57
  headers = {"Authorization": f"Bearer {token}"}
58
+ if correlation_id:
59
+ headers["X-Correlation-ID"] = correlation_id
73
60
  try:
74
61
  async with httpx.AsyncClient() as client:
75
62
  response = await client.get(f"{BALANCE_SERVICE_URL}/api/balance/{investment_account_name}", headers=headers)
76
63
  response.raise_for_status()
77
64
  data = response.json()
78
65
  current_balance = data['balance']
79
- logger.info(f"Calculated cash balance for '{investment_account_name}' is {current_balance}")
66
+ logger.info("Calculated cash balance for account", extra={'user_id': current_user.id, 'account': investment_account_name, 'balance': current_balance})
80
67
  except Exception as e:
81
- logger.error(f"Could not retrieve balance for validation for user {current_user.id}: {e}")
68
+ logger.error("Could not retrieve balance for validation", extra={'user_id': current_user.id, 'error': str(e), 'account': investment_account_name})
82
69
 
83
70
  if float(current_balance) < float(trade_cost):
84
- logger.warning(f"Insufficient funds in '{investment_account_name}' for user {current_user.id}. Balance: {current_balance}, Required: {trade_cost}")
71
+ error_detail = f"Insufficient cash balance in '{investment_account_name}'. Balance: {current_balance}, Required: {trade_cost}"
72
+ logger.warning("Insufficient funds for trade", extra={'user_id': current_user.id, 'account': investment_account_name, 'balance': current_balance, 'required': trade_cost})
85
73
  raise HTTPException(
86
74
  status_code=400,
87
- detail=f"Insufficient cash balance in '{investment_account_name}' to complete trade."
75
+ detail=error_detail
88
76
  )
89
77
 
90
78
  trade_data = trade.dict(exclude={'symbol', 'name', 'asset_type', 'industry', 'id_crypto'})
91
79
  db_trade = models.Trade(**trade_data, asset_id=db_asset.id, user_id=current_user.id)
92
80
 
93
81
  if db_trade.trade_type == 'buy':
94
- 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
+ logger.info("STOCK PURCHASE recorded", extra={'user_id': current_user.id, 'shares': db_trade.shares, 'symbol': trade_symbol, 'price': db_trade.price})
95
83
  elif db_trade.trade_type == 'sell':
96
- 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
+ logger.info("STOCK SALE recorded", extra={'user_id': current_user.id, 'shares': db_trade.shares, 'symbol': trade_symbol, 'price': db_trade.price})
97
85
 
98
86
  db.add(db_trade)
99
87
  db.commit()
100
88
  db.refresh(db_trade)
89
+ logger.info("Trade created successfully", extra={'user_id': current_user.id, 'trade_id': db_trade.id})
90
+
91
+ # Get and log asset summary after trade
92
+ account_summary = crud.get_account_summary(db=db, user_id=current_user.id, account_name=trade.investment_account)
93
+ logger.info("Account asset summary after trade", extra={'user_id': current_user.id, 'account': trade.investment_account, 'summary': account_summary})
94
+
101
95
  return db_trade
102
96
 
103
97
  @router.get("/", response_model=list[schemas.TradeResponse])
@@ -108,16 +102,19 @@ def read_trades(
108
102
  db: Session = Depends(get_db),
109
103
  current_user: models.User = Depends(get_current_user)
110
104
  ):
111
- logger.info(f"Reading trades for user {current_user.id}, account: {investment_account}")
105
+ logger.info("Reading trades", extra={'user_id': current_user.id, 'account': investment_account, 'skip': skip, 'limit': limit})
112
106
  query = db.query(models.Trade).filter(
107
+
113
108
  models.Trade.user_id == current_user.id,
114
109
  models.Trade.deleted_at.is_(None) # Filter out soft-deleted trades
110
+ ,
111
+ models.Trade.deleted_at.is_(None) # Filter out soft-deleted trades
115
112
  )
116
113
  if investment_account:
117
114
  query = query.filter(models.Trade.investment_account == investment_account)
118
115
 
119
116
  trades = query.order_by(models.Trade.date.desc()).offset(skip).limit(limit).all()
120
- logger.info(f"Found {len(trades)} trades for user {current_user.id}")
117
+ logger.info("Trades found", extra={'user_id': current_user.id, 'count': len(trades), 'account': investment_account})
121
118
  return trades
122
119
 
123
120
  @router.delete("/{trade_id}", response_model=schemas.TradeResponse)
@@ -142,4 +139,4 @@ def delete_trade(
142
139
  db.commit()
143
140
  db.refresh(db_trade)
144
141
  logger.info(f"Successfully soft-deleted trade ID: {trade_id} for user: {current_user.id}")
145
- return db_trade
142
+ return db_trade