Changes between Initial Version and Version 1 of UseCaseImplementationsFinal


Ignore:
Timestamp:
01/20/26 16:57:03 (5 days ago)
Author:
231104
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • UseCaseImplementationsFinal

    v1 v1  
     1= Финална имплементација на случаи на употреба
     2
     3== Трансфер на средства со email известување
     4
     5**User-to-User трансфер**
     6
     7Најавен корисник се навигира кон страницата за трансфер на средства и внесува email адреса на примачот.
     8
     9**Backend имплементација - views.py**
     10
     11При испраќање на трансфер преку POST барање, најпрво се валидира изворната сметка и балансот. Потоа се проверува дали примачот постои во системот.
     12
     13
     14{{{
     15
     16@api_view(['POST'])
     17@permission_classes([IsAuthenticated])
     18def transfer_funds(request):
     19    data = request.data
     20    source_account_id = data.get('source_account_id')
     21    recipient_email = data.get('recipient_email')
     22    amount = Decimal(str(data.get('amount', 0)))
     23    note = data.get('note', '')
     24
     25    # Validate source account
     26    source_account = Account.objects.get(id=source_account_id, user=request.user)
     27
     28    # Check balance
     29    if source_account.balance < amount:
     30        return Response({'error': 'Insufficient balance'})
     31
     32}}}
     33
     34По успешна валидација, се креираат две трансакции - една за испраќачот (debit) и една за примачот (credit):
     35
     36
     37{{{
     38
     39    with transaction.atomic():
     40        Transaction.objects.create(
     41            account=source_account,
     42            transaction_type='debit',
     43            amount=amount,
     44            description=f'Sent to {recipient_user.first_name}'
     45        )
     46
     47        Transaction.objects.create(
     48            account=recipient_account,
     49            transaction_type='credit',
     50            amount=amount,
     51            description=f'Received from {request.user.first_name}'
     52        )
     53
     54}}}
     55
     56**Email известување
     57**
     58
     59По завршување на трансферот, се праќаат два email-и преку функцијата send_transfer_email:
     60
     61
     62{{{
     63def send_transfer_email(user, transfer_type, amount, recipient_name,
     64                        reference_number, new_balance, is_sender=True, note=None):
     65    if is_sender:
     66        subject = f'Banker - Transfer Sent: ${amount}'
     67    else:
     68        subject = f'Banker - Payment Received: ${amount}'
     69
     70    send_mail(
     71        subject=subject,
     72        message=message,
     73        from_email=settings.DEFAULT_FROM_EMAIL,
     74        recipient_list=[user.email],
     75        html_message=html_message
     76    )
     77
     78}}}
     79
     80Email-от содржи: сума, име на примач/испраќач, референтен број, датум и време, нов баланс, и опционална порака (note) доколку е внесена при трансферот.
     81
     82**Internal трансфер (меѓу свои сметки)**
     83
     84Корисникот може да пренесе средства меѓу своите сметки.
     85
     86
     87При интерен трансфер, корисникот добива email со детали за двете сметки:
     88
     89{{{
     90def send_internal_transfer_email(user, amount, from_account, to_account,
     91                                  reference_number, from_balance, to_balance):
     92    subject = f'Banker - Internal Transfer Completed: ${amount}'
     93
     94    # HTML email содржи табела со:
     95    # - Amount
     96    # - From Account (име на изворна сметка)
     97    # - To Account (име на дестинациска сметка)
     98    # - Updated Balances за двете сметки
     99}}}
     100
     101== Најава преку Google OAuth
     102
     103**Ненајавен корисник**
     104
     105Ненајавениот корисник се навигира кон страницата за најава.
     106
     107
     108Корисникот го кликнува копчето за најава преку Google профил и го селектира посакуваниот профил.
     109
     110
     111По успешна најава, корисникот е пренасочен кон главната страница.
     112
     113**Frontend имплементација - OAuthButtons.tsx**
     114
     115Во frontend делот се иницијализира Google Sign-In SDK и се рендерира официјалното Google копче:
     116
     117{{{
     118useEffect(() => {
     119  if (googleLoaded && window.google) {
     120    window.google.accounts.id.initialize({
     121      client_id: GOOGLE_CLIENT_ID,
     122      callback: handleGoogleCallback,
     123      ux_mode: 'popup',
     124    });
     125
     126    window.google.accounts.id.renderButton(
     127      googleButtonRef.current,
     128      { theme: 'outline', size: 'large', text: 'signin_with' }
     129    );
     130  }
     131}, [googleLoaded]);
     132}}}
     133
     134По успешна автентикација, credential-от се праќа до backend-от:
     135
     136{{{
     137const handleGoogleCallback = async (response) => {
     138  const backendResponse = await fetch(
     139    'http://localhost:8000/api/auth/google/',
     140    {
     141      method: 'POST',
     142      headers: { 'Content-Type': 'application/json' },
     143      body: JSON.stringify({ credential: response.credential })
     144    }
     145  );
     146
     147  const data = await backendResponse.json();
     148  localStorage.setItem('access_token', data.access);
     149  localStorage.setItem('user', JSON.stringify(data.user));
     150};
     151}}}
     152
     153**Backend имплементација - oauth_views.py**
     154
     155Во backend-от се верифицира Google credential преку Google tokeninfo endpoint:
     156
     157{{{
     158@api_view(['POST'])
     159@permission_classes([AllowAny])
     160def google_login(request):
     161    credential = request.data.get('credential')
     162
     163    # Verify token with Google
     164    google_url = f'https://oauth2.googleapis.com/tokeninfo?id_token={credential}'
     165    response = requests.get(google_url)
     166    google_data = response.json()
     167
     168    # Verify audience matches our app
     169    if google_data.get('aud') != settings.GOOGLE_CLIENT_ID:
     170        return Response({'error': 'Token not for this app'})
     171
     172    email = google_data.get('email')
     173    first_name = google_data.get('given_name', '')
     174    last_name = google_data.get('family_name', '')
     175}}}
     176
     177Доколку корисникот не постои, се креира нов:
     178
     179{{{
     180    user, created = User.objects.get_or_create(
     181        email=email,
     182        defaults={
     183            'username': email,
     184            'first_name': first_name,
     185            'last_name': last_name,
     186        }
     187    )
     188
     189    # Generate JWT tokens
     190    refresh = RefreshToken.for_user(user)
     191
     192    # Send login notification email
     193    send_login_notification(user, 'Google Sign-in')
     194}}}
     195
     196**Email известување при најава**
     197
     198При секоја успешна најава преку Google, корисникот добива security email:
     199
     200{{{
     201def send_login_notification(user, login_method):
     202    subject = 'Banker - New Sign-in to Your Account'
     203
     204    # Email содржи:
     205    # - Account email
     206    # - Sign-in Method (Google Sign-in)
     207    # - Date & Time
     208    # - Security warning доколку не е корисникот
     209}}}
     210
     211== Support страница со email известување
     212
     213**Корисник испраќа support барање**
     214
     215Најавениот корисник се навигира кон Support страницата.
     216
     217Корисникот пополнува форма со категорија, приоритет, предмет и порака.
     218
     219По испраќање, корисникот добива потврда со ticket ID.
     220
     221**Frontend имплементација - support/page.tsx**
     222
     223Support формата содржи селекција за категорија и приоритет, како и полиња за предмет и порака:
     224
     225{{{
     226const categories = [
     227  { value: 'general', label: 'General Inquiry', icon: HelpCircle },
     228  { value: 'bug', label: 'Bug Report', icon: Bug },
     229  { value: 'feature', label: 'Feature Request', icon: Lightbulb },
     230  { value: 'billing', label: 'Billing Issue', icon: CreditCard },
     231];
     232
     233const priorities = [
     234  { value: 'low', label: 'Low', color: 'bg-gray-100' },
     235  { value: 'normal', label: 'Normal', color: 'bg-blue-100' },
     236  { value: 'high', label: 'High', color: 'bg-orange-100' },
     237  { value: 'urgent', label: 'Urgent', color: 'bg-red-100' },
     238];
     239}}}
     240
     241При submit, се праќа POST барање до backend-от:
     242
     243{{{
     244const handleSubmit = async (e) => {
     245  const response = await fetch('http://localhost:8000/api/banking/support/', {
     246    method: 'POST',
     247    headers: {
     248      'Authorization': `Bearer ${accessToken}`,
     249      'Content-Type': 'application/json',
     250    },
     251    body: JSON.stringify({ subject, message, category, priority }),
     252  });
     253};
     254}}}
     255
     256**Backend имплементација - contact_views.py**
     257
     258Backend-от го обработува support барањето и праќа два email-и:
     259
     260{{{
     261@api_view(['POST'])
     262@permission_classes([IsAuthenticated])
     263def send_support_message(request):
     264    data = request.data
     265    user = request.user
     266
     267    subject = data.get('subject', 'Support Request')
     268    message = data.get('message')
     269    category = data.get('category', 'general')
     270    priority = data.get('priority', 'normal')
     271
     272    # Generate unique ticket ID
     273    ticket_id = f'TKT-{user.id}-{int(time.time())}'
     274}}}
     275
     276
     277{{{
     278Се праќаат два email-и:
     279
     280    # Email 1: To support team
     281    send_mail(
     282        subject=f'[Banker Support - {priority.upper()}] {subject}',
     283        message=support_email_body,
     284        from_email=settings.DEFAULT_FROM_EMAIL,
     285        recipient_list=[settings.SUPPORT_EMAIL],
     286    )
     287
     288    # Email 2: Confirmation to user
     289    send_mail(
     290        subject=f'[Banker] Support Request Received - {ticket_id}',
     291        message=user_confirmation,
     292        from_email=settings.DEFAULT_FROM_EMAIL,
     293        recipient_list=[user.email],
     294    )
     295}}}
     296
     297== Бришење на банкарска сметка
     298
     299**Корисник брише сметка**
     300
     301Најавениот корисник се навигира кон My Banks страницата.
     302
     303Корисникот кликнува на копчето за бришење на сметка.
     304
     305Системот прво проверува дали сметката има баланс - доколку има, не дозволува бришење.
     306
     307**Backend имплементација - views.py**
     308
     309{{{
     310@api_view(['DELETE'])
     311@permission_classes([IsAuthenticated])
     312def delete_account(request, account_id):
     313    account = Account.objects.get(id=account_id, user=request.user)
     314
     315    # Check if account has balance
     316    if account.balance > 0:
     317        return Response({
     318            'error': f'Cannot delete account with balance. Current: ${account.balance}'
     319        }, status=status.HTTP_400_BAD_REQUEST)
     320
     321    # Delete all transactions for this account
     322    Transaction.objects.filter(account=account).delete()
     323
     324    # Delete the account
     325    account_name = f'{account.bank.name} {account.account_type.title()}'
     326    account.delete()
     327
     328    return Response({
     329        'message': f'Account "{account_name}" deleted successfully'
     330    })
     331}}}
     332
     333**URL конфигурација - urls.py**
     334
     335{{{
     336path('accounts/<uuid:account_id>/', views.delete_account, name='delete_account'),
     337}}}
     338
     339== Филтрирање на аналитика по категорија
     340
     341**Корисник филтрира аналитика**
     342
     343Најавениот корисник се навигира кон Analytics страницата.
     344
     345Корисникот може да филтрира по временски период и по категорија.
     346
     347**Backend имплементација - views.py**
     348
     349Analytics endpoint-от прима query параметри за филтрирање:
     350
     351{{{
     352@api_view(['GET'])
     353@permission_classes([IsAuthenticated])
     354def analytics_view(request):
     355    # Get query parameters
     356    date_range = request.GET.get('range', 'all')
     357    category_filter = request.GET.get('category', 'all')
     358
     359    # Filter transactions by date
     360    transactions = Transaction.objects.filter(account__in=user_accounts)
     361    if start_date:
     362        transactions = transactions.filter(created_at__gte=start_date)
     363
     364    # Filter by category if specified
     365    if category_filter and category_filter != 'all':
     366        transactions = transactions.filter(category__iexact=category_filter)
     367}}}
     368
     369
     370Филтрирањето се применува на сите калкулации - category breakdown, monthly trends и income vs expenses.
     371
     372Исто така се враќа листа на достапни категории за dropdown менито:
     373
     374
     375{{{
     376# Get all available categories for filter dropdown
     377    all_categories = list(Transaction.objects.filter(
     378        account__in=user_accounts
     379    ).values_list('category', flat=True).distinct())
     380
     381    return Response({
     382        'categoryBreakdown': category_breakdown,
     383        'monthlyTrends': monthly_trends,
     384        'incomeVsExpenses': income_vs_expenses,
     385        'availableCategories': all_categories
     386    })
     387}}}
     388
     389
     390== Конфигурација на системот
     391
     392**Django Settings - settings.py**
     393
     394Email конфигурација за Gmail SMTP:
     395
     396{{{
     397EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
     398EMAIL_HOST = 'smtp.gmail.com'
     399EMAIL_PORT = 587
     400EMAIL_USE_TLS = True
     401EMAIL_HOST_USER = 'your-email@gmail.com'
     402EMAIL_HOST_PASSWORD = 'your-app-password'
     403DEFAULT_FROM_EMAIL = 'Banker App <your-email@gmail.com>'
     404SUPPORT_EMAIL = 'your-email@gmail.com'
     405}}}
     406
     407Google OAuth конфигурација:
     408
     409{{{
     410GOOGLE_CLIENT_ID = 'your-google-client-id.apps.googleusercontent.com'
     411GOOGLE_CLIENT_SECRET = 'your-google-client-secret'
     412}}}
     413
     414**URL Routing - urls.py**
     415
     416Authentication endpoints:
     417
     418{{{
     419# authentication/urls.py
     420urlpatterns = [
     421    path('google/', google_login, name='google_login'),
     422    path('login/', login_view, name='login'),
     423    path('register/', register_view, name='register'),
     424]
     425}}}
     426
     427Banking endpoints:
     428
     429{{{
     430# banking/urls.py
     431urlpatterns = [
     432    path('transfer/', transfer_funds, name='transfer_funds'),
     433    path('support/', send_support_message, name='support'),
     434    path('analytics/', analytics_view, name='analytics'),
     435    path('accounts/<uuid:account_id>/', delete_account, name='delete_account'),
     436]
     437}}}
     438
     439
     440
     441
     442