Merge pull request #83 from wahyd4/feature/invest-portfolio

fix: API bugs in invest portfolio
This commit is contained in:
2026-04-18 23:42:21 +10:00
committed by GitHub
+22 -107
View File
@@ -71,6 +71,28 @@ class TransactionViewSet(viewsets.ModelViewSet):
qs = qs.filter(stock_code=stock_code.upper())
return qs.order_by('-date', '-created_at')
def create(self, request, *args, **kwargs):
"""Create a new transaction."""
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
data = serializer.validated_data
portfolio = data['portfolio']
try:
tx = Transaction.objects.create(
portfolio=portfolio,
action=data['action'],
stock_code=data['stock_code'].upper(),
quantity=data['quantity'],
date=data['date'],
)
except Exception as exc:
return Response({'error': str(exc)}, status=status.HTTP_400_BAD_REQUEST)
out = TransactionSerializer(tx)
return Response(out.data, status=status.HTTP_201_CREATED)
class AIUpdateView(APIView):
"""
@@ -98,110 +120,3 @@ class AIUpdateView(APIView):
return Response(result, status=status.HTTP_200_OK)
logger = logging.getLogger(__name__)
class PortfolioViewSet(viewsets.ModelViewSet):
queryset = Portfolio.objects.prefetch_related('stocks').all()
def get_serializer_class(self):
if self.action == 'list':
return PortfolioListSerializer
return PortfolioSerializer
@action(detail=True, methods=['get'], url_path='holdings')
def holdings(self, request, pk=None):
"""Return holdings with real-time prices from Yahoo Finance."""
portfolio = self.get_object()
try:
data = get_portfolio_holdings(portfolio)
return Response(data)
except Exception as exc:
logger.error("get_portfolio_holdings failed for %s: %s", portfolio.id, exc, exc_info=True)
return Response({'error': str(exc)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
@action(detail=True, methods=['get'], url_path='transactions')
def transactions(self, request, pk=None):
"""List all transactions for this portfolio."""
portfolio = self.get_object()
txs = portfolio.transactions.all().order_by('-date', '-created_at')
serializer = TransactionSerializer(txs, many=True)
return Response(serializer.data)
class StockViewSet(viewsets.ModelViewSet):
queryset = Stock.objects.select_related('portfolio').all()
serializer_class = StockSerializer
def get_queryset(self):
qs = super().get_queryset()
portfolio_id = self.request.query_params.get('portfolio')
if portfolio_id:
qs = qs.filter(portfolio_id=portfolio_id)
return qs
class TransactionViewSet(viewsets.ModelViewSet):
queryset = Transaction.objects.select_related('portfolio').all()
serializer_class = TransactionSerializer
def get_queryset(self):
qs = super().get_queryset()
portfolio_id = self.request.query_params.get('portfolio')
if portfolio_id:
qs = qs.filter(portfolio_id=portfolio_id)
stock_code = self.request.query_params.get('stock_code')
if stock_code:
qs = qs.filter(stock_code=stock_code.upper())
return qs.order_by('-date', '-created_at')
def create(self, request, *args, **kwargs):
"""Create a new transaction."""
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
data = serializer.validated_data
portfolio = data['portfolio']
try:
tx = add_transaction(
portfolio=portfolio,
action=data['action'],
stock_code=data['stock_code'],
quantity=data['quantity'],
price_per_share=data['price_per_share'],
date=data['date'],
)
except Exception as exc:
return Response({'error': str(exc)}, status=status.HTTP_400_BAD_REQUEST)
out = TransactionSerializer(tx)
return Response(out.data, status=status.HTTP_201_CREATED)
class AIUpdateView(APIView):
"""
POST /api/invest/ai-update/
AI updates portfolio holdings with a simplified payload.
"""
def post(self, request):
serializer = AIUpdateSerializer(data=request.data)
if not serializer.is_valid():
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
data = serializer.validated_data
portfolio = get_object_or_404(Portfolio, pk=data['portfolio_id'])
try:
result = ai_update_holdings(
portfolio=portfolio,
holdings=data['holdings'],
reset=data['reset'],
)
except Exception as exc:
logger.error("ai_update_holdings failed: %s", exc, exc_info=True)
return Response({'error': str(exc)}, status=status.HTTP_400_BAD_REQUEST)
return Response(result, status=status.HTTP_200_OK)