]> git.atlas4tour.it Git - pia_atlas.git/commitdiff
- Implementato pagamento PayPal con storage transazione nel DB
authorVincenzo Carbonara <vincenzo.carbonara@dyrecta.com>
Tue, 8 Oct 2024 13:05:53 +0000 (15:05 +0200)
committerVincenzo Carbonara <vincenzo.carbonara@dyrecta.com>
Tue, 8 Oct 2024 13:05:53 +0000 (15:05 +0200)
- Creata Sezione I Tuoi Percorsi
- Implementato filtro nome e descrizione per sezioni Percorsi.

12 files changed:
.idea/socoin_atlas.iml
pagamenti/templates/pagamenti_checkout.html [new file with mode: 0644]
pagamenti/templates/payment_success.html [new file with mode: 0644]
pagamenti/urls.py [new file with mode: 0644]
sistema/models.py
sistema/templates/base.html
sistema/templates/percorsi_cliente_home.html
sistema/templates/tuoi_percorsi_cliente.html [new file with mode: 0644]
sistema/urls.py
sistema/views.py
socoin_atlas/settings.py
socoin_atlas/urls.py

index 0220ca76843f5d4264d714ea5048b95a1c676569..740ea5433bb00a2a3e8814240759493aef143e81 100644 (file)
@@ -16,7 +16,7 @@
     <content url="file://$MODULE_DIR$">
       <excludeFolder url="file://$MODULE_DIR$/env" />
     </content>
-    <orderEntry type="jdk" jdkName="Python 3.7 (socoin_atlas)" jdkType="Python SDK" />
+    <orderEntry type="jdk" jdkName="Python 3.11 (pythonProject3)" jdkType="Python SDK" />
     <orderEntry type="sourceFolder" forTests="false" />
   </component>
   <component name="TemplatesService">
diff --git a/pagamenti/templates/pagamenti_checkout.html b/pagamenti/templates/pagamenti_checkout.html
new file mode 100644 (file)
index 0000000..bac5968
--- /dev/null
@@ -0,0 +1,11 @@
+{% extends 'base.html' %}
+{% block content %}
+<div class="container">
+    <h1>Checkout per {{ percorso.nome }}</h1>
+    <p>Prezzo: {{ percorso.prezzo|floatformat:2 }} €</p>
+    <form method="POST">
+        {% csrf_token %}
+        <button type="submit" class="btn btn-success">Procedi al pagamento</button>
+    </form>
+</div>
+{% endblock %}
\ No newline at end of file
diff --git a/pagamenti/templates/payment_success.html b/pagamenti/templates/payment_success.html
new file mode 100644 (file)
index 0000000..1a20014
--- /dev/null
@@ -0,0 +1,7 @@
+{% extends 'base.html' %}
+{% block content %}
+<div class="container">
+    <h1>Pagamento riuscito!</h1>
+    <p>Grazie per il tuo acquisto.</p>
+</div>
+{% endblock %}
\ No newline at end of file
diff --git a/pagamenti/urls.py b/pagamenti/urls.py
new file mode 100644 (file)
index 0000000..416ec6f
--- /dev/null
@@ -0,0 +1,10 @@
+from django.urls import path
+from .views import CheckoutView, PaymentSuccessView, PaymentCancelView
+
+app_name = 'pagamenti'
+
+urlpatterns = [
+    path('checkout/<int:percorso_id>/', CheckoutView.as_view(), name='checkout'),  # Checkout page
+    path('success/', PaymentSuccessView.as_view(), name='payment_success'),  # Payment success
+    path('cancel/', PaymentCancelView.as_view(), name='payment_cancel'),  # Payment canceled
+]
index d4cee6ba159b4e582c6ebcf628b3b002c4ed35fc..9263d873bca9a3d3433fa09a5a912ade821dab4f 100644 (file)
@@ -44,6 +44,12 @@ class Percorso(models.Model):
     is_active = models.BooleanField(default=True)
 
 
+class Pagamenti(models.Model):
+    user = models.ForeignKey(User, on_delete=models.CASCADE)
+    percorso = models.ForeignKey(Percorso, on_delete=models.CASCADE)
+    data_acquisto = models.DateTimeField(auto_now_add=True)
+
+
 class Tappa(models.Model):
     percorso = models.ForeignKey(Percorso, on_delete=models.DO_NOTHING)
     poi = models.ForeignKey(PointOfInterest, on_delete=models.DO_NOTHING)
index 95650d9db8f4c93a5465eda8c3932e4a15667f18..05c8639da7bf2055cf503421a1907bb25b400802 100644 (file)
                             <i class="fas fa-route" style="padding: 6px 28px 7px 6px;"></i><span>Percorsi</span></a>
                     </li>
 
+                   <li class="dropdown voce_menu"> <!--  id="tuoi-percorsi" -->
+                        <a href="{% url 'sistema:tuoi_percorsi_list' %}" class="nav-link">
+                            <i class="fas fa-route" style="padding: 6px 28px 7px 6px;"></i><span>Tuoi Percorsi</span></a>
+                    </li>
+
+
                 </ul>
             </aside>
         </div>
index ca950e0b3b37f2c50f30d3eac79fe9a0d7ca5c18..b5e632318a5943ee45b2e978ef2adde023835cc6 100644 (file)
@@ -5,14 +5,36 @@
 <div class="row">
     <div class="col-12 col-md-12 col-lg-12">
         <h1 class="line-listini"><i class="fa-solid fa-route clr-listini mr-2" aria-hidden="true"></i> Dettaglio percorso </h1>
+
+        <!-- Campo di ricerca -->
+        <div class="form-group">
+            <input type="text" id="search-filter" class="form-control" placeholder="Cerca tra i percorsi">
+        </div>
+
         <div class="card">
             <div class="card-header">
                 <h4 class="clr-config">I tuoi percorsi</h4>
             </div>
 
             <div class="card-body">
-                <div class="row">
-
+                <div class="row" id="paid-routes">
+                    {% for percorso_pagato in list_pagati %}
+                        <div class="card p-3 route__filter" style="width: 18rem;">
+                            <img class="card-img-top" src="{% static 'assets/img/logo.png' %}">
+                            <div class="card-body">
+                                <h5 class="card-title route__text">{{ percorso_pagato.nome}}</h5>
+                                <p class="card-text route__text">{{ percorso_pagato.descrizione }}</p>
+                            </div>
+                            <div class="card-footer">
+                                <div class="d-flex justify-content-between">
+                                    <h4>{{ percorso_pagato.prezzo|floatformat:2 }} €</h4>
+                                </div>
+                            </div>
+                            <a href="{% url 'sistema:percorso_info' percorso_pagato.pk %}" class="btn btn-warning rounded-4">Dettaglio percorso</a>
+                        </div>
+                    {% empty %}
+                        <p>Non possiedi ancora alcun Percorso.</p>
+                    {% endfor %}
                 </div>
             </div>
         </div>
                 <h4 class="clr-config">Percorsi che puoi acquistare</h4>
             </div>
             <div class="card-body">
-                <div class="row">
+                <div class="row" id="available-routes">
                     {% for percorso in list_percorsi %}
-                        <div class="card" style="width: 18rem;">
+                        <div class="card p-3 route__filter" style="width: 18rem;">
                             <img class="card-img-top" src="{% static 'assets/img/logo.png' %}">
                             <div class="card-body">
-                                <h5 class="card-title">{{ percorso.nome}}</h5>
-                                <p class="card-text">{{ percorso.descrizione }}</p>
+                                <h5 class="card-title route__text">{{ percorso.nome}}</h5>
+                                <p class="card-text route__text">{{ percorso.descrizione }}</p>
                             </div>
                             <div class="card-footer">
                                 <div class="d-flex justify-content-between">
                                     <h4>{{ percorso.prezzo|floatformat:2 }} €</h4>
-                                    <a href="#" class="btn btn-success">Compralo subito</a>
+                                    <a href="{% url 'pagamenti:checkout' percorso.pk %}" class="btn btn-success">Acquista ora</a>
                                 </div>
                             </div>
-                        <a href="{% url 'sistema:percorso_info' percorso.pk %}" class="stretched-link"></a>
+                            <a href="{% url 'sistema:percorso_info' percorso.pk %}" class="btn btn-warning rounded-4">Dettaglio percorso</a>
                         </div>
+                    {% empty %}
+                        <p>Non ci sono percorsi disponibili per l'acquisto.</p>
                     {% endfor %}
                 </div>
             </div>
     </div>
 </div>
 
+
+
+
+<script>
+document.addEventListener('DOMContentLoaded', () => {
+    // Assicurati che il DOM sia completamente caricato prima di eseguire il codice
+    const input = document.getElementById('search-filter');
+
+    // Verifica che l'elemento di input esista nel DOM
+    if (input) {
+        // Aggiungi un listener sull'input per avviare la ricerca durante la digitazione
+        input.addEventListener('input', search);
+    } else {
+        // Se non viene trovato, segnala in console
+        console.error("Input non trovato");
+    }
+});
+
+const search = (e) => {
+    // Ottieni il valore attuale dell'input e convertilo in minuscolo
+    const inputValue = e.target.value.toLowerCase();
+
+    // Seleziona i contenitori delle card per i percorsi pagati e disponibili
+    const paidRoutesContainer = document.getElementById('paid-routes');
+    const availableRoutesContainer = document.getElementById('available-routes');
+
+    // Verifica che i contenitori esistano prima di procedere
+    if (!paidRoutesContainer || !availableRoutesContainer) {
+        console.error("Contenitori di percorsi non trovati");
+        return;
+    }
+
+    // Seleziona le card individuali delle due sezioni
+    const paidRoutes = [...paidRoutesContainer.getElementsByClassName('route__filter')];
+    const availableRoutes = [...availableRoutesContainer.getElementsByClassName('route__filter')];
+
+    // Funzione per filtrare le card in base al testo di ricerca
+    const filterCards = (routes) => {
+        for (let i = 0; i < routes.length; i++) {
+            // Prendi il testo da h5 (titolo) e p (descrizione)
+            let searchTerm = [...routes[i].querySelectorAll("h5.card-title, p.card-text")];
+            searchTerm = searchTerm.map(item => item.innerHTML.toLowerCase());
+
+            // Verifica se l'input di ricerca è contenuto nel titolo o nella descrizione della card
+            if (searchTerm.map(text => text.includes(inputValue)).some(val => val)) {
+                // Mostra la card se il testo combacia
+                routes[i].style.display = 'block';
+            } else {
+                // Nascondi la card se il testo non combacia
+                routes[i].style.display = 'none';
+            }
+        }
+    }
+
+    // Applica il filtro alle due sezioni
+    filterCards(paidRoutes);
+    filterCards(availableRoutes);
+}
+</script>
+
+
+
 {% endblock %}
\ No newline at end of file
diff --git a/sistema/templates/tuoi_percorsi_cliente.html b/sistema/templates/tuoi_percorsi_cliente.html
new file mode 100644 (file)
index 0000000..680eb72
--- /dev/null
@@ -0,0 +1,101 @@
+{% extends 'base.html' %}
+{% load static %}
+{% block content %}
+
+<div class="row">
+    <div class="col-12 col-md-12 col-lg-12">
+        <h1 class="line-listini"><i class="fa-solid fa-route clr-listini mr-2" aria-hidden="true"></i> Dettaglio percorsi acquistati </h1>
+
+        <div class="form-group">
+            <input type="text" id="search-filter" class="form-control" placeholder="Cerca tra i percorsi">
+        </div>
+
+        <div class="card">
+            <div class="card-header">
+                <h4 class="clr-config">I tuoi percorsi</h4>
+            </div>
+
+            <div class="card-body m-3">
+                <div class="row">
+                    {% for percorso in list_percorsi_pagati %}
+                        <div class="card p-3 m-2" style="width: 18rem;">
+                            <img class="card-img-top" src="{% static 'assets/img/logo.png' %}">
+                            <div class="card-body">
+                                <h5 class="card-title">{{ percorso.nome}}</h5>
+                                <p class="card-text">{{ percorso.descrizione }}</p>
+                            </div>
+                            <div class="card-footer">
+                                <div class="d-flex justify-content-between">
+                                    <h4>{{ percorso.prezzo|floatformat:2 }} €</h4>
+<!--                                <a href="{% url 'pagamenti:checkout' percorso.pk %}" class="btn btn-success">Acquista ora</a>   -->
+                                </div>
+                            </div>
+                            <a href="{% url 'sistema:percorso_info' percorso.pk %}" class="btn btn-warning rounded-4">Dettaglio percorso</a>
+                        </div>
+                    {% endfor %}
+                </div>
+            </div>
+        </div>
+
+    </div>
+</div>
+
+
+<script>
+    document.addEventListener('DOMContentLoaded', () => {
+    // Assicurati che il DOM sia completamente caricato prima di eseguire il codice
+    const input = document.getElementById('search-filter');
+
+    // Verifica che l'elemento di input esista nel DOM
+    if (input) {
+        // Aggiungi un listener sull'input per avviare la ricerca durante la digitazione
+        input.addEventListener('input', search);
+    } else {
+        console.error("Input non trovato");
+    }
+});
+
+const search = (e) => {
+    // Ottieni il valore attuale dell'input e convertilo in minuscolo
+    const inputValue = e.target.value.toLowerCase();
+
+    // Seleziona il contenitore delle card per i percorsi pagati
+    const paidRoutesContainer = document.querySelector('.card-body .row');
+
+    // Verifica che il contenitore esista prima di procedere
+    if (!paidRoutesContainer) {
+        console.error("Contenitore di percorsi non trovato");
+        return;
+    }
+
+    // Seleziona le card individuali della sezione
+    const paidRoutes = [...paidRoutesContainer.getElementsByClassName('card')];
+
+    // Funzione per filtrare le card in base al testo di ricerca
+    const filterCards = (routes) => {
+        for (let i = 0; i < routes.length; i++) {
+            // Prendi il testo da h5 (titolo) e p (descrizione)
+            let searchTerm = [
+                routes[i].querySelector("h5.card-title"),
+                routes[i].querySelector("p.card-text")
+            ];
+            searchTerm = searchTerm.map(item => item ? item.innerHTML.toLowerCase() : '');
+
+            // Verifica se l'input di ricerca è contenuto nel titolo o nella descrizione della card
+            if (searchTerm.map(text => text.includes(inputValue)).some(val => val)) {
+                // Mostra la card se il testo combacia
+                routes[i].style.display = 'block';
+            } else {
+                // Nascondi la card se il testo non combacia
+                routes[i].style.display = 'none';
+            }
+        }
+    }
+
+    // Applica il filtro alle card
+    filterCards(paidRoutes);
+}
+</script>
+
+
+{% endblock %}
\ No newline at end of file
index fd37ad9c37b652096d0dbd5c2b894c485ae8b670..0916665c912e71ad737d6065f1a7f67f6a23498b 100644 (file)
@@ -3,7 +3,7 @@ from django.urls import path
 from sistema.datatables import LocalitaDatatables, MultimediaDatatables, TipoMultimediaDatatables, PercorsoDatatables, \
     PoiDatatables, FeedbackDatatables, GestisceLocalitaDatatables
 from sistema.views import Home, LocalitaListView, MultimediaListView, PuntiInteresseListView, \
-    TipologiaMultimediaListView, PercorsiListView, LocalitaView, TipoMultimediaView, PoiView, PercorsoView, FeedbackView, PercorsoInfo
+    TipologiaMultimediaListView, PercorsiListView, TuoiPercorsiListView, LocalitaView, TipoMultimediaView, PoiView, PercorsoView, FeedbackView, PercorsoInfo
 
 urlpatterns = [
     path('', Home.as_view(), name='home'),
@@ -14,6 +14,7 @@ urlpatterns = [
     path('poi_list/', PuntiInteresseListView.as_view(), name='poi_list'),
     path('tipo_multimedia_list/', TipologiaMultimediaListView.as_view(), name='tipo_multimedia_list'),
     path('percorsi_list/', PercorsiListView.as_view(), name='percorsi_list'),
+    path('tuoi_percorsi_list/', TuoiPercorsiListView.as_view(), name='tuoi_percorsi_list'),
 
     ## DATATABLES ##
     path('localita_datatables/', LocalitaDatatables.as_view(), name='localita_datatables'),
index 06d9af3ec7ae4f1ef101a86ebe4d86f73ca19b8b..eac79514378414abe5d64f20aeb4b7f41387437f 100644 (file)
@@ -9,8 +9,9 @@ from django.views import View
 from django.views.generic import TemplateView
 from rest_framework import status
 
+from pagamenti.views import CheckoutView, GetPayPalToken
 from sistema.forms import LocalitaForm, TipoMultimediaForm, PoiForm, PercorsoForm, MultimediaForm
-from sistema.models import Localita, TipologiaMultimedia, PointOfInterest, Percorso, Tappa, TappaSerializer, Multimedia, \
+from sistema.models import Localita, TipologiaMultimedia, Pagamenti, PointOfInterest, Percorso, Tappa, TappaSerializer, Multimedia, \
     Feedback, FeedbackSerializer
 from socoin_atlas import settings
 from socoin_atlas.settings import MEDIA_ROOT
@@ -46,10 +47,34 @@ class MultimediaListView(TemplateView):#PermissionRequiredMixin
 class PercorsiListView(View):
     def get(self, request):
         if request.session['roles'] == settings.CLIENTI_GROUPS:
-            list_percorsi = Percorso.objects.filter(is_active=True)
-            return render(request, 'percorsi_cliente_home.html', {'list_percorsi': list_percorsi})
+            # Ottieni gli ID dei percorsi già pagati
+            percorsi_pagati_id = Pagamenti.objects.filter(user=request.user).values_list('percorso_id', flat=True)
+
+            # Ottieni i percorsi pagati
+            list_pagati = Percorso.objects.filter(id__in=percorsi_pagati_id)
+
+            # Ottieni i percorsi disponibili (non pagati)
+            list_percorsi = Percorso.objects.exclude(id__in=percorsi_pagati_id)
+
+            return render(request, 'percorsi_cliente_home.html', {
+                'list_pagati': list_pagati,
+                'list_percorsi': list_percorsi,
+            })
+        else:
+            percorsi_disponibili = Percorso.objects.all()
+            return render(request, 'percorsi_list.html', {'percorsi': percorsi_disponibili})
+
+
+class TuoiPercorsiListView(View):
+    def get(self, request):
+        if request.session['roles'] == settings.CLIENTI_GROUPS:
+            user_id = request.user.id
+            pagamenti_utente = Pagamenti.objects.filter(user_id=user_id)
+            id_percorsi_pagati = pagamenti_utente.values_list('percorso_id', flat=True)
+            list_percorsi_pagati = Percorso.objects.filter(id__in=id_percorsi_pagati, is_active=True)
+            return render(request, 'tuoi_percorsi_cliente.html', {'list_percorsi_pagati': list_percorsi_pagati})
         else:
-            return render(request, 'percorsi_list.html', {})
+            return render(request, 'percorsi_cliente_home.html', {})
 
 
 class Home(CustomLoginRequiredMixin, View):  # CustomLoginRequiredMixin
index 747dcb0385b6f9c1111d6b16fdf1b8d3671dd981..567eb043e8728ef74598240522011e1664f4b7f6 100644 (file)
@@ -12,6 +12,7 @@ https://docs.djangoproject.com/en/3.2/ref/settings/
 import datetime
 import os
 from pathlib import Path
+from decouple import config
 
 # Build paths inside the project like this: BASE_DIR / 'subdir'.
 BASE_DIR = Path(__file__).resolve().parent.parent
@@ -42,7 +43,8 @@ INSTALLED_APPS = [
     'sistema',
     'utenti',
     'api',
-    'rest_framework'
+    'rest_framework',
+    'pagamenti',
 
 ]
 
@@ -104,7 +106,7 @@ DATABASES = {
         'ENGINE': 'django.db.backends.mysql',
         'NAME': 'socoin_atlas',
         'USER': 'root',
-        'PASSWORD': "password",
+        'PASSWORD': "",
         'HOST': '127.0.0.1',  # Or an IP Address that your DB is hosted on
         'PORT': '3306',
         'AUTOCOMMIT': True,
@@ -170,4 +172,10 @@ ADMIN_GROUPS = 'ADMIN'
 '''
 PER INTEGRARE I PAGAMENTI CON PAYPAL
 https://www.youtube.com/watch?v=8rMfW4wO-vU&ab_channel=DennisIvy
-'''
\ No newline at end of file
+
+'''
+
+# Credenziali PayPal
+PAYPAL_ID='Af2rjE-cXvWU6Wv0vKVkP_vtl2K3d4Te-geFeHkybXqbISX8VKeLZ4to2M-1xo5UR9FbqUxX81olDJAg'
+PAYPAL_SECRET='EJ3lZQRK44HWdo2HprwUjm0wov-8Pc4IQZW3hFb_z647qNC-dtLpLkEUrvYXCA2WVHB8LkMfgT7QJDqH'
+PAYPAL_BASE_URL='https://sandbox.paypal.com'
\ No newline at end of file
index 37061b89512b3d67dc456260b337c9bc42ac856f..a271c2d284b1f70465c4587ef92d17bc2db5d880 100644 (file)
@@ -26,4 +26,5 @@ urlpatterns = [
     path('', include(('sistema.urls', 'sistema'), namespace='sistema')),
     path('utenti/', include(('utenti.urls', 'utenti'), namespace='utenti')),
     path('api/', include(('api.urls', 'api'), namespace='api')),
+    path('pagamenti/', include(('pagamenti.urls', 'pagamenti'), namespace='pagamenti')),
 ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + staticfiles_urlpatterns()