From 52d9030c39c2d437c1b96a4b58cb39c3be2cbd2e Mon Sep 17 00:00:00 2001 From: Klaus-Uwe Mitterer Date: Sun, 26 Jan 2020 11:53:25 +0100 Subject: [PATCH] More files. --- .gitignore | 2 +- auction/forms.py | 8 + auction/migrations/0006_auto_20200123_1701.py | 23 + auction/migrations/0007_auto_20200123_1753.py | 19 + auction/migrations/0008_inquiry_contact.py | 20 + auction/migrations/0009_inquiry_currency.py | 19 + auction/models.py | 18 +- auction/urls.py | 11 + auction/views.py | 114 +++- frontend/admin.py | 4 + .../migrations/0002_auto_20200125_1023.py | 18 + frontend/migrations/0003_testimonial_stars.py | 20 + frontend/models.py | 8 +- frontend/templatetags/mapimage.py | 27 + .../{hoteloptions.py => offeroptions.py} | 2 +- frontend/templatetags/testimonials.py | 4 +- frontend/views.py | 13 +- payment/migrations/0001_initial.py | 21 +- payment/migrations/0002_dummypayment.py | 25 - payment/migrations/0003_auto_20200122_1308.py | 27 - .../migrations/0004_remove_payment_inquiry.py | 17 - payment/migrations/0005_auto_20200122_1310.py | 18 - payment/models.py | 63 ++- payment/urls.py | 11 + payment/views.py | 50 ++ .../migrations/0002_auto_20200124_1150.py | 23 + .../migrations/0003_auto_20200125_1023.py | 30 + .../migrations/0004_contactprofile_email.py | 19 + .../migrations/0005_auto_20200125_1134.py | 33 ++ .../migrations/0006_auto_20200125_1216.py | 23 + .../migrations/0007_contactprofile_user.py | 22 + profiles/models.py | 19 +- profiles/views.py | 12 +- requirements.txt | 2 + static/frontend/js/custom-autocomplete.js | 6 +- static/frontend/js/custom-checkout.js | 11 + static/frontend/js/custom-date-picker.js | 4 +- .../auction/{orderform.html => payment.html} | 255 ++++++--- templates/frontend/base.html | 535 +++++++++++++++++- templates/frontend/error.html | 80 +++ templates/frontend/footer.html | 163 ------ templates/frontend/header.html | 370 ------------ templates/frontend/index.html | 68 +-- templates/payment/redirect.html | 11 + templates/payment/redirect_stripe.js | 7 + templates/payment/status.html | 39 ++ templates/profiles/base.html | 35 ++ urlaubsauktion/database.dist.py | 5 + urlaubsauktion/settings.py | 27 +- urlaubsauktion/urls.py | 7 +- 50 files changed, 1575 insertions(+), 793 deletions(-) create mode 100644 auction/forms.py create mode 100644 auction/migrations/0006_auto_20200123_1701.py create mode 100644 auction/migrations/0007_auto_20200123_1753.py create mode 100644 auction/migrations/0008_inquiry_contact.py create mode 100644 auction/migrations/0009_inquiry_currency.py create mode 100644 auction/urls.py create mode 100644 frontend/migrations/0002_auto_20200125_1023.py create mode 100644 frontend/migrations/0003_testimonial_stars.py create mode 100644 frontend/templatetags/mapimage.py rename frontend/templatetags/{hoteloptions.py => offeroptions.py} (77%) delete mode 100644 payment/migrations/0002_dummypayment.py delete mode 100644 payment/migrations/0003_auto_20200122_1308.py delete mode 100644 payment/migrations/0004_remove_payment_inquiry.py delete mode 100644 payment/migrations/0005_auto_20200122_1310.py create mode 100644 payment/urls.py create mode 100644 profiles/migrations/0002_auto_20200124_1150.py create mode 100644 profiles/migrations/0003_auto_20200125_1023.py create mode 100644 profiles/migrations/0004_contactprofile_email.py create mode 100644 profiles/migrations/0005_auto_20200125_1134.py create mode 100644 profiles/migrations/0006_auto_20200125_1216.py create mode 100644 profiles/migrations/0007_contactprofile_user.py create mode 100644 static/frontend/js/custom-checkout.js rename templates/auction/{orderform.html => payment.html} (58%) create mode 100644 templates/frontend/error.html delete mode 100644 templates/frontend/footer.html delete mode 100644 templates/frontend/header.html create mode 100644 templates/payment/redirect.html create mode 100644 templates/payment/redirect_stripe.js create mode 100644 templates/payment/status.html create mode 100644 templates/profiles/base.html create mode 100644 urlaubsauktion/database.dist.py diff --git a/.gitignore b/.gitignore index cd2dd3a..2f20ea3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -db.sqlite3 +urlaubsauktion/database.py *.swp *.pyc __pycache__/ diff --git a/auction/forms.py b/auction/forms.py new file mode 100644 index 0000000..f6564b6 --- /dev/null +++ b/auction/forms.py @@ -0,0 +1,8 @@ +from django.forms import Form, ModelForm, CharField + +from profiles.models import ContactProfile + +class PostPaymentForm(ModelForm): + class Meta: + model = ContactProfile + fields = ["first_name", "last_name", "address", "address2", "zipcode", "city", "country", "phone", "email"] diff --git a/auction/migrations/0006_auto_20200123_1701.py b/auction/migrations/0006_auto_20200123_1701.py new file mode 100644 index 0000000..6154c0d --- /dev/null +++ b/auction/migrations/0006_auto_20200123_1701.py @@ -0,0 +1,23 @@ +# Generated by Django 2.2.6 on 2020-01-23 16:01 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('auction', '0005_auto_20200122_1802'), + ] + + operations = [ + migrations.RemoveField( + model_name='inquiry', + name='id', + ), + migrations.AddField( + model_name='inquiry', + name='uuid', + field=models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False), + ), + ] diff --git a/auction/migrations/0007_auto_20200123_1753.py b/auction/migrations/0007_auto_20200123_1753.py new file mode 100644 index 0000000..34adcdc --- /dev/null +++ b/auction/migrations/0007_auto_20200123_1753.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2.6 on 2020-01-23 16:53 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('auction', '0006_auto_20200123_1701'), + ] + + operations = [ + migrations.AlterField( + model_name='inquiry', + name='user', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='profiles.ClientProfile'), + ), + ] diff --git a/auction/migrations/0008_inquiry_contact.py b/auction/migrations/0008_inquiry_contact.py new file mode 100644 index 0000000..fa0c94f --- /dev/null +++ b/auction/migrations/0008_inquiry_contact.py @@ -0,0 +1,20 @@ +# Generated by Django 2.2.6 on 2020-01-25 09:45 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('profiles', '0004_contactprofile_email'), + ('auction', '0007_auto_20200123_1753'), + ] + + operations = [ + migrations.AddField( + model_name='inquiry', + name='contact', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='profiles.ContactProfile'), + ), + ] diff --git a/auction/migrations/0009_inquiry_currency.py b/auction/migrations/0009_inquiry_currency.py new file mode 100644 index 0000000..f5dc004 --- /dev/null +++ b/auction/migrations/0009_inquiry_currency.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2.6 on 2020-01-25 10:03 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('auction', '0008_inquiry_contact'), + ] + + operations = [ + migrations.AddField( + model_name='inquiry', + name='currency', + field=models.CharField(choices=[('EUR', 'Euro')], default='eur', max_length=3), + preserve_default=False, + ), + ] diff --git a/auction/models.py b/auction/models.py index f547dcc..fb9e353 100644 --- a/auction/models.py +++ b/auction/models.py @@ -1,15 +1,25 @@ -from django.db.models import Model, ForeignKey, DateTimeField, DecimalField, PositiveIntegerField, DateField, CharField, ForeignKey, CASCADE +from django.db.models import Model, ForeignKey, UUIDField, DateTimeField, DecimalField, PositiveIntegerField, DateField, CharField, ForeignKey, CASCADE, SET_NULL from django.contrib.gis.db.models import PointField +from django.urls import reverse_lazy +from django.conf import settings -from profiles.models import ClientProfile +from uuid import uuid4 + +from profiles.models import ClientProfile, ContactProfile class Inquiry(Model): - user = ForeignKey(ClientProfile, on_delete=CASCADE) + uuid = UUIDField(default=uuid4, primary_key=True) + user = ForeignKey(ClientProfile, null=True, on_delete=CASCADE) amount = DecimalField(max_digits=25, decimal_places=2) + currency = CharField(max_length=3, choices=settings.CURRENCIES) first_date = DateField() last_date = DateField() destination_name = CharField(max_length=128) destination_geo = PointField() adults = PositiveIntegerField() children = PositiveIntegerField(default=0) - posted = DateTimeField(auto_now_add=True) \ No newline at end of file + posted = DateTimeField(auto_now_add=True) + contact = ForeignKey(ContactProfile, null=True, on_delete=SET_NULL) + + def get_absolute_url(self): + return reverse_lazy("auction:payment", kwargs={'pk': self.uuid}) \ No newline at end of file diff --git a/auction/urls.py b/auction/urls.py new file mode 100644 index 0000000..9212566 --- /dev/null +++ b/auction/urls.py @@ -0,0 +1,11 @@ +from django.urls import path + +from auction.views import InquiryView, PaymentView, PostPaymentView + +app_name = "auction" + +urlpatterns = [ + path('create_inquiry/', InquiryView.as_view(), name="create_inquiry"), + path('/payment/', PaymentView.as_view(), name="payment"), + path('/post_payment/', PostPaymentView.as_view(), name="post_payment") +] diff --git a/auction/views.py b/auction/views.py index 0ac796d..a03f69e 100644 --- a/auction/views.py +++ b/auction/views.py @@ -1,9 +1,117 @@ -from django.shortcuts import render -from django.views.generic import CreateView +from django.shortcuts import render, redirect +from django.views.generic import CreateView, DetailView, FormView +from django.urls import reverse_lazy +from django.contrib.gis.geos import Point +from django.contrib.messages import error +from django.conf import settings + +from geopy.geocoders import Nominatim from auction.models import Inquiry +from profiles.models import ClientProfile, ContactProfile +from auction.forms import PostPaymentForm +from payment.models import KlarnaPayment, PaypalPayment, StripePayment, DummyPayment # Create your views here. class InquiryView(CreateView): - model = Inquiry \ No newline at end of file + model = Inquiry + fields = ["amount", "first_date", "last_date", "destination_name", "adults", "children"] + + def get(self, request, *args, **kwargs): + return redirect(reverse_lazy("frontend:index")) + + def form_invalid(self, form): + #for field in form.errors.keys(): + # print('ValidationError: %s[%s] <- "%s" %s' % ( + # type(self), + # field, + # form.data.get(field, False), + # form.errors.get(field, False).as_text() + # )) + return redirect(reverse_lazy("frontend:index") + "?invalid=true") + + def form_valid(self, form): + try: + form.instance.user = ClientProfile.objects.get(user=self.request.user) + except ClientProfile.DoesNotExist: # pylint: disable=no-member + form.instance.user = None + + form.instance.currency = "eur" + + lat, lon = self.request.POST.get("destination_lat", None), self.request.POST.get("destination_lon", None) + + if (not lat) or (not lon): + location = Nominatim(user_agent="UrlaubsAuktion 1.0").geocode(form.instance.destination_name, country_codes="at") + lat, lon = location.latitude, location.longitude + + form.instance.destination_geo = Point(lon, lat) + return super().form_valid(form) + +class PaymentView(DetailView): + model = Inquiry + template_name = "auction/payment.html" + +class PostPaymentView(FormView): + form_class = PostPaymentForm + + def form_invalid(self, form): + #super().form_invalid(form) + for _dumbo, errormsg in form.errors: + error(self.request, errormsg) + return redirect(reverse_lazy("auction:payment", kwargs={'pk': self.kwargs["pk"]})) + + def form_valid(self, form): + #super().form_valid(form) + + # ClientProfile + try: + client = ClientProfile.objects.get(user=self.request.user) + except ClientProfile.DoesNotExist: # pylint: disable=no-member + client = ClientProfile.objects.create( + user = self.request.user, + first_name = form.cleaned_data["first_name"], + last_name = form.cleaned_data["last_name"], + address = form.cleaned_data["address"], + address2 = form.cleaned_data["address2"], + zipcode = form.cleaned_data["zipcode"], + city = form.cleaned_data["city"], + country = form.cleaned_data["country"], + phone = form.cleaned_data["phone"] + ) + self.request.user.email = form.cleaned_data["email"] + self.request.user.save() + + # ContactProfile + contact = ContactProfile.objects.create( + user = self.request.user, + first_name = form.cleaned_data["first_name"], + last_name = form.cleaned_data["last_name"], + address = form.cleaned_data["address"], + address2 = form.cleaned_data["address2"], + zipcode = form.cleaned_data["zipcode"], + city = form.cleaned_data["city"], + country = form.cleaned_data["country"], + phone = form.cleaned_data["phone"], + email = form.cleaned_data["email"] + ) + + # Inquiry + inquiry = Inquiry.objects.get(uuid=self.kwargs["pk"]) # pylint: disable=no-member + inquiry.user = client + inquiry.contact = contact + inquiry.save() + + # Payment + gateway = self.request.POST.get("gateway").lower() + if gateway == "paypal": + handler = PaypalPayment + elif gateway == "dummy" and settings.DEBUG: + handler = DummyPayment + elif gateway == "klarna": + handler = KlarnaPayment + else: + handler = StripePayment + + payment = handler.objects.create(invoice=inquiry) + return payment.start() \ No newline at end of file diff --git a/frontend/admin.py b/frontend/admin.py index 8c38f3f..62beb52 100644 --- a/frontend/admin.py +++ b/frontend/admin.py @@ -1,3 +1,7 @@ from django.contrib import admin +from frontend.models import Testimonial + # Register your models here. + +admin.site.register(Testimonial) diff --git a/frontend/migrations/0002_auto_20200125_1023.py b/frontend/migrations/0002_auto_20200125_1023.py new file mode 100644 index 0000000..de32744 --- /dev/null +++ b/frontend/migrations/0002_auto_20200125_1023.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.6 on 2020-01-25 09:23 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('frontend', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='testimonial', + name='language', + field=models.CharField(choices=[('de', 'Deutsch'), ('en', 'Englisch')], max_length=12), + ), + ] diff --git a/frontend/migrations/0003_testimonial_stars.py b/frontend/migrations/0003_testimonial_stars.py new file mode 100644 index 0000000..4196d4f --- /dev/null +++ b/frontend/migrations/0003_testimonial_stars.py @@ -0,0 +1,20 @@ +# Generated by Django 2.2.6 on 2020-01-25 12:35 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('frontend', '0002_auto_20200125_1023'), + ] + + operations = [ + migrations.AddField( + model_name='testimonial', + name='stars', + field=models.PositiveIntegerField(default=5, validators=[django.core.validators.MaxValueValidator(5), django.core.validators.MinValueValidator(1)]), + preserve_default=False, + ), + ] diff --git a/frontend/models.py b/frontend/models.py index 5ef31d5..ff5fbc6 100644 --- a/frontend/models.py +++ b/frontend/models.py @@ -1,10 +1,16 @@ -from django.db.models import Model, CharField, TextField, ImageField, BooleanField +from django.db.models import Model, CharField, TextField, ImageField, BooleanField, PositiveIntegerField from django.conf import settings +from django.core.validators import MaxValueValidator, MinValueValidator # Create your models here. class Testimonial(Model): name = CharField(max_length=128) text = TextField() + stars = PositiveIntegerField( + validators=[ + MaxValueValidator(5), + MinValueValidator(1) + ]) language = CharField(max_length=12, choices=settings.LANGUAGES) public = BooleanField(default=False) \ No newline at end of file diff --git a/frontend/templatetags/mapimage.py b/frontend/templatetags/mapimage.py new file mode 100644 index 0000000..1b85098 --- /dev/null +++ b/frontend/templatetags/mapimage.py @@ -0,0 +1,27 @@ +from django import template + +import requests +import base64 + +from dbsettings.models import Setting + +register = template.Library() + +@register.simple_tag +def mapimage(location, zoom=8, width=348, height=250): + payload = { + 'center': location, + 'zoom': str(zoom), + "size": "%ix%i" % (width, height), + 'sensor': "false", + 'key': Setting.objects.get(key="google.api.key").value # pylint: disable=no-member + } + r = requests.get('https://maps.googleapis.com/maps/api/staticmap', params=payload) + image = r.content + data_uri = 'data:image/jpg;base64,' + data_uri += base64.b64encode(image).decode().replace('\n', '') + return data_uri + +@register.simple_tag +def mapimage_coords(lat, lon, zoom=8, width=348, height=250): + return mapimage("%s,%s" % (str(lat), str(lon)), zoom, width, height) \ No newline at end of file diff --git a/frontend/templatetags/hoteloptions.py b/frontend/templatetags/offeroptions.py similarity index 77% rename from frontend/templatetags/hoteloptions.py rename to frontend/templatetags/offeroptions.py index 9e5c44f..992984a 100644 --- a/frontend/templatetags/hoteloptions.py +++ b/frontend/templatetags/offeroptions.py @@ -12,7 +12,7 @@ def stars(number): output = '
' for i in range(5): - output += '/status/', StatusView.as_view(), name="status"), + path('/redirect_stripe/', StripeRedirectView.as_view(), name="redirect_stripe"), + path('/redirect_stripe/redirect.js', StripeRedirectJSView.as_view(), name="redirect_stripe_js") +] diff --git a/payment/views.py b/payment/views.py index 91ea44a..912e251 100644 --- a/payment/views.py +++ b/payment/views.py @@ -1,3 +1,53 @@ from django.shortcuts import render +from django.views.generic import DetailView, TemplateView +from django.http import HttpResponse +from django.views.decorators.csrf import csrf_exempt + +from payment.models import Payment +from dbsettings.models import Setting + +import stripe # Create your views here. + + +class StatusView(DetailView): + model = Payment + template_name = "payment/status.html" + + +class StripeRedirectView(TemplateView): + template_name = "payment/redirect.html" + + +class StripeRedirectJSView(DetailView): + model = Payment + template_name = "payment/redirect_stripe.js" + + +@csrf_exempt +def stripe_webhook(request): + endpoint_secret = Setting.objects.get(key="stripe.webhook.secret").value # pylint: disable=no-member + payload = request.body + sig_header = request.META['HTTP_STRIPE_SIGNATURE'] + event = None + + try: + event = stripe.Webhook.construct_event( + payload, sig_header, endpoint_secret + ) + except ValueError: + # Invalid payload + return HttpResponse(status=400) + except stripe.error.SignatureVerificationError: + # Invalid signature + return HttpResponse(status=400) + + # Handle the checkout.session.completed event + if event['type'] == 'checkout.session.completed': + session = event['data']['object'] + payment = Payment.objects.get(session=session["id"]) + payment.session_status = event["type"] + + + return HttpResponse(status=200) diff --git a/profiles/migrations/0002_auto_20200124_1150.py b/profiles/migrations/0002_auto_20200124_1150.py new file mode 100644 index 0000000..fcad428 --- /dev/null +++ b/profiles/migrations/0002_auto_20200124_1150.py @@ -0,0 +1,23 @@ +# Generated by Django 2.2.6 on 2020-01-24 10:50 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('profiles', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='clientprofile', + name='picture', + field=models.ImageField(blank=True, null=True, upload_to=''), + ), + migrations.AddField( + model_name='partnerprofile', + name='logo', + field=models.ImageField(blank=True, null=True, upload_to=''), + ), + ] diff --git a/profiles/migrations/0003_auto_20200125_1023.py b/profiles/migrations/0003_auto_20200125_1023.py new file mode 100644 index 0000000..7f966ea --- /dev/null +++ b/profiles/migrations/0003_auto_20200125_1023.py @@ -0,0 +1,30 @@ +# Generated by Django 2.2.6 on 2020-01-25 09:23 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('profiles', '0002_auto_20200124_1150'), + ] + + operations = [ + migrations.CreateModel( + name='ContactProfile', + fields=[ + ('profile_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='profiles.Profile')), + ], + options={ + 'abstract': False, + 'base_manager_name': 'objects', + }, + bases=('profiles.profile',), + ), + migrations.AlterField( + model_name='profile', + name='country', + field=models.CharField(max_length=128), + ), + ] diff --git a/profiles/migrations/0004_contactprofile_email.py b/profiles/migrations/0004_contactprofile_email.py new file mode 100644 index 0000000..36791e9 --- /dev/null +++ b/profiles/migrations/0004_contactprofile_email.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2.6 on 2020-01-25 09:29 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('profiles', '0003_auto_20200125_1023'), + ] + + operations = [ + migrations.AddField( + model_name='contactprofile', + name='email', + field=models.EmailField(default=None, max_length=254), + preserve_default=False, + ), + ] diff --git a/profiles/migrations/0005_auto_20200125_1134.py b/profiles/migrations/0005_auto_20200125_1134.py new file mode 100644 index 0000000..27e4e24 --- /dev/null +++ b/profiles/migrations/0005_auto_20200125_1134.py @@ -0,0 +1,33 @@ +# Generated by Django 2.2.6 on 2020-01-25 10:34 + +from django.conf import settings +from django.db import migrations, models +from django.contrib.auth.models import User +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('profiles', '0004_contactprofile_email'), + ] + + operations = [ + migrations.RemoveField( + model_name='profile', + name='user', + ), + migrations.AddField( + model_name='clientprofile', + name='creator', + field=models.OneToOneField(default=User.objects.all()[0].id, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + preserve_default=False, + ), + migrations.AddField( + model_name='partnerprofile', + name='creator', + field=models.OneToOneField(default=User.objects.all()[0].id, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + preserve_default=False, + ), + ] diff --git a/profiles/migrations/0006_auto_20200125_1216.py b/profiles/migrations/0006_auto_20200125_1216.py new file mode 100644 index 0000000..8d00f1f --- /dev/null +++ b/profiles/migrations/0006_auto_20200125_1216.py @@ -0,0 +1,23 @@ +# Generated by Django 2.2.6 on 2020-01-25 11:16 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('profiles', '0005_auto_20200125_1134'), + ] + + operations = [ + migrations.RenameField( + model_name='clientprofile', + old_name='creator', + new_name='user', + ), + migrations.RenameField( + model_name='partnerprofile', + old_name='creator', + new_name='user', + ), + ] diff --git a/profiles/migrations/0007_contactprofile_user.py b/profiles/migrations/0007_contactprofile_user.py new file mode 100644 index 0000000..d3e8ef4 --- /dev/null +++ b/profiles/migrations/0007_contactprofile_user.py @@ -0,0 +1,22 @@ +# Generated by Django 2.2.6 on 2020-01-25 13:17 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('profiles', '0006_auto_20200125_1216'), + ] + + operations = [ + migrations.AddField( + model_name='contactprofile', + name='user', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + preserve_default=False, + ), + ] diff --git a/profiles/models.py b/profiles/models.py index 9101e17..58ccfd0 100644 --- a/profiles/models.py +++ b/profiles/models.py @@ -1,4 +1,4 @@ -from django.db.models import Model, CharField, ForeignKey, DecimalField, OneToOneField, BooleanField, CASCADE +from django.db.models import Model, EmailField, CharField, ForeignKey, DecimalField, OneToOneField, BooleanField, ImageField, CASCADE from django.contrib.auth.models import User from django.db.models.signals import post_save from django.dispatch import receiver @@ -9,26 +9,25 @@ from phonenumber_field.modelfields import PhoneNumberField from vies.models import VATINField class Profile(PolymorphicModel): - user = OneToOneField(User, on_delete=CASCADE) first_name = CharField(max_length=128) last_name = CharField(max_length=128) address = CharField(max_length=128) address2 = CharField(max_length=128, blank=True, null=True) zipcode = CharField(max_length=15) city = CharField(max_length=128) - country = CountryField() + country = CharField(max_length=128) phone = PhoneNumberField() class ClientProfile(Profile): - pass + user = OneToOneField(User, on_delete=CASCADE) + picture = ImageField(null=True, blank=True) class PartnerProfile(Profile): + user = OneToOneField(User, on_delete=CASCADE) company = CharField(max_length=128, blank=True, null=True) vatid = VATINField(blank=True, null=True) + logo = ImageField(null=True, blank=True) -def create_client_profile(user): - ClientProfile.objects.create(user=user) - -def create_partner_profile(user): - PartnerProfile.objects.create(user=user) - +class ContactProfile(Profile): + user = ForeignKey(User, on_delete=CASCADE) + email = EmailField() \ No newline at end of file diff --git a/profiles/views.py b/profiles/views.py index 0dc73cd..07c0997 100644 --- a/profiles/views.py +++ b/profiles/views.py @@ -6,15 +6,16 @@ from django.shortcuts import redirect class RegistrationView(FormView): template_name = 'profiles/register.html' form_class = UserCreationForm - success_url = '/?registered=true' + success_url = "/" def form_valid(self, form): - res = super().form_valid(form) + super().form_valid(form) username = form.cleaned_data.get('username') password = form.cleaned_data.get('password1') user = authenticate(username=username, password=password) login(self.request, user) - return res + + return redirect(self.request.GET.get("next", "/") + "?registered=true") class LoginView(FormView): template_name = 'profiles/register.html' @@ -22,12 +23,13 @@ class LoginView(FormView): success_url = "/" def form_valid(self, form): - res = super().form_valid(form) + super().form_valid(form) username = form.cleaned_data.get('username') password = form.cleaned_data.get('password') user = authenticate(username=username, password=password) login(self.request, user) - return res + + return redirect(self.request.GET.get("next", "/")) def user_logout(request): logout(request) diff --git a/requirements.txt b/requirements.txt index 1f5576c..f5141ce 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,3 +10,5 @@ mysqlclient django-cookie-law django-bootstrap-form django-multiselectfield +geopy +stripe diff --git a/static/frontend/js/custom-autocomplete.js b/static/frontend/js/custom-autocomplete.js index a847325..9497843 100644 --- a/static/frontend/js/custom-autocomplete.js +++ b/static/frontend/js/custom-autocomplete.js @@ -1,4 +1,4 @@ -var input = document.getElementById('destination'); +var input = document.getElementById('id_destination_name'); var options = { types: ['(cities)'], componentRestrictions: {country: 'at'}, @@ -12,7 +12,7 @@ autocomplete.addListener('place_changed', fillInAddress); function fillInAddress() { // Get the place details from the autocomplete object. var place = autocomplete.getPlace(); - - alert(place.geometry.location.lat()); + $("#id_destination_lat").val(place.geometry.location.lat); + $("#id_destination_lon").val(place.geometry.location.lon); } \ No newline at end of file diff --git a/static/frontend/js/custom-checkout.js b/static/frontend/js/custom-checkout.js new file mode 100644 index 0000000..d097340 --- /dev/null +++ b/static/frontend/js/custom-checkout.js @@ -0,0 +1,11 @@ +$(document).ready(function(){ + if($('#login-tabs').length){ + $("#frm_booking :input").prop("disabled", true); + $('#login-tabs :input').prop("disabled", false); + } else { + $("#id_gateway").val("credit-card"); + $("#payment-tabs ul.nav.nav-tabs li.nav-item").click(function(event) { + $("#id_gateway").val(event.target.href.slice(event.target.href.indexOf("#tab-") + "#-tab".length)); + }); + }; +}); \ No newline at end of file diff --git a/static/frontend/js/custom-date-picker.js b/static/frontend/js/custom-date-picker.js index 34ba6e5..3e83a71 100644 --- a/static/frontend/js/custom-date-picker.js +++ b/static/frontend/js/custom-date-picker.js @@ -13,6 +13,7 @@ var now = new Date(nowTemp.getFullYear(), nowTemp.getMonth(), nowTemp.getDate(), 0, 0, 0, 0); var checkin = date1.datepicker({ + format: 'dd.mm.yyyy', onRender: function(date) { return date.valueOf() < now.valueOf() ? 'disabled' : ''; } @@ -29,6 +30,7 @@ }).data('datepicker'); var checkout = date2.datepicker({ + format: 'dd.mm.yyyy', onRender: function(date) { return date.valueOf() <= checkin.date.valueOf() ? 'disabled' : ''; } @@ -38,7 +40,7 @@ }).data('datepicker'); date3.datepicker({ - format: 'mm-dd-yyyy' + format: 'dd.mm.yyyy' }); })(jQuery); \ No newline at end of file diff --git a/templates/auction/orderform.html b/templates/auction/payment.html similarity index 58% rename from templates/auction/orderform.html rename to templates/auction/payment.html index ca2a1c4..17a140d 100644 --- a/templates/auction/orderform.html +++ b/templates/auction/payment.html @@ -1,6 +1,8 @@ {% extends "frontend/base.html" %} {% load i18n %} {% load static %} +{% load mapimage %} +{% load dbsetting %} {% block "content" %}
@@ -22,6 +24,14 @@
+ {% if messages %} + {% for message in messages %} + + {% endfor %} + {% endif %}
+{% endblock %} +{% block "scripts" %} + {% endblock %} \ No newline at end of file diff --git a/templates/frontend/base.html b/templates/frontend/base.html index c2b06fa..59f9b34 100644 --- a/templates/frontend/base.html +++ b/templates/frontend/base.html @@ -1,4 +1,535 @@ -{% include "frontend/header.html" %} +{% load static %} +{% load i18n %} +{% load dbsetting %} + + + + Urlaubsauktion - {{ title }} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ × +
+ + +
+
+ + +
+
+ + +
+
+ + + +
+
+
+
+
+
    +
  • Kumi Systems e.U., Sternäckerweg 51a/2, 8041 Graz
  • +
  • +43 800 093004
  • +
+
+
+
+ +
+
+
+
+ + + + {% block "content" %} {% endblock %} -{% include "frontend/footer.html" %} \ No newline at end of file +{% if not request.user.is_authenticated %} + + + {% endif %} + + + + + + + +
+ + + + + + + + + + + + + + + + + + {% block "scripts" %} + {% endblock %} + + + + diff --git a/templates/frontend/error.html b/templates/frontend/error.html new file mode 100644 index 0000000..1d3179f --- /dev/null +++ b/templates/frontend/error.html @@ -0,0 +1,80 @@ +{% load i18n %} +{% load static %} + + + + UrlaubsAuktion - {% trans "Seite nicht gefunden" %} + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+
+
+
+
+
+
+

UrlaubsAuktion

+
+ +
+ +
+

{{ error_code }}

+
+ +
+
+

UrlaubsAuktion

+
+ +

{% trans "Ein Fehler ist aufgetreten!" %}

+

{% trans "Es tut uns leid," %}{% if error_code == 404 %}{% trans "diese Seite konnte nicht gefunden werden." %}{% else %}{% trans "es ist ein interner Fehler aufgetreten." %}{% endif %}

+

{% trans "Wir werden diesen Fehler schnellstmöglich beheben. Bitte versuchen Sie es später nochmals." %}

+ {% trans "Zurück zur Startseite" %} +
+
+
+
+
+
+
+
+ + + + + + + + + + diff --git a/templates/frontend/footer.html b/templates/frontend/footer.html deleted file mode 100644 index 9fc4e7c..0000000 --- a/templates/frontend/footer.html +++ /dev/null @@ -1,163 +0,0 @@ -{% load static %} -{% load i18n %} -{% load dbsetting %} -{% load cookielaw_tags %} -{% if not request.user.is_authenticated %} - - - {% endif %} - - - - - - - -
- - {% cookielaw_banner %} - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/templates/frontend/header.html b/templates/frontend/header.html deleted file mode 100644 index 0b90a38..0000000 --- a/templates/frontend/header.html +++ /dev/null @@ -1,370 +0,0 @@ -{% load static %} -{% load i18n %} -{% load dbsetting %} - - - - Urlaubsauktion - {{ title }} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- - - -
- × -
- -
-
-
- - -
-
-
- -
-
- - - -
-
-
-
-
-
    -
  • Kumi Systems e.U., Sternäckerweg 51a/2, 8041 Graz
  • -
  • +43 800 093004
  • -
-
-
-
- -
-
-
-
- - - diff --git a/templates/frontend/index.html b/templates/frontend/index.html index b56263e..8f19d94 100644 --- a/templates/frontend/index.html +++ b/templates/frontend/index.html @@ -2,6 +2,8 @@ {% load static %} {% load i18n %} {% load testimonials %} +{% load dbsetting %} +{% load offeroptions %} {% block "content" %}
@@ -48,7 +50,7 @@
-
+ {% csrf_token %}
@@ -57,14 +59,16 @@
- + + +
- +
@@ -77,14 +81,14 @@
- +
- +
@@ -96,27 +100,27 @@
- + + + + + +
- + + + + + + +
@@ -285,14 +289,14 @@
- +
- +
@@ -305,14 +309,14 @@
- +
- +
@@ -532,16 +536,10 @@ {% for t in test %} {% endfor %} @@ -560,3 +558,7 @@
{% endblock %} +{% block "scripts" %} + + +{% endblock %} \ No newline at end of file diff --git a/templates/payment/redirect.html b/templates/payment/redirect.html new file mode 100644 index 0000000..666802b --- /dev/null +++ b/templates/payment/redirect.html @@ -0,0 +1,11 @@ +{% load i18n %} + + + Urlaubsauktion - {% trans "Weiterleitung" %} + + +

{% trans "Sie werden zu unserem Zahlungsdienstleister weitergeleitet. Wir bitten um einen Augenblick Geduld." %}

+ + + + \ No newline at end of file diff --git a/templates/payment/redirect_stripe.js b/templates/payment/redirect_stripe.js new file mode 100644 index 0000000..66e6579 --- /dev/null +++ b/templates/payment/redirect_stripe.js @@ -0,0 +1,7 @@ +{% load dbsetting %} +var stripe = Stripe('{% dbsetting "stripe.key.public" %}'); +stripe.redirectToCheckout({ + sessionId: '{{ object.session }}' + }).then(function (result) { + alert(result.error.message); + }); \ No newline at end of file diff --git a/templates/payment/status.html b/templates/payment/status.html new file mode 100644 index 0000000..aa4c7e4 --- /dev/null +++ b/templates/payment/status.html @@ -0,0 +1,39 @@ +{% extends "frontend/base.html" %} +{% block "content" %} + +
+
+
+
+
+
+

{% trans "Zahlung" %} {% if object.status == 0 %}{% trans "erfolgreich" %}{% elif object.status == -1 %}{% trans "autorisiert" %}{% endif %}

+

{% blocktrans with currency=object.invoice.currency|upper, amount=object.invoice.amount %}Die Zahlung über {{ currency }} {{ amount }} wurde{% endblocktrans %}{% if object.status == 0 %}{% trans "erfolgreich durchgeführt" %}{% elif object.status == -1 %}{% trans "autorisiert" %}{% endif %}

+ + +
+ + + + + + + + + + + + + + + +
{% trans "Kategorie" %}{% trans "Beschreibung" %}{% trans "Preis" %}
{% trans "Gebot" %}{{ object.invoice.destination_name }}{{ object.first_date|date:"SHORT_DATE_FORMAT" }} – {{ object.last_date|date:"SHORT_DATE_FORMAT" }}{{ object.invoice.currency|upper }} {{ object.invoice.amount }}
+
+

{% trans "Die Buchungsdaten wurden auch per E-Mail versendet." %}

+
+
+
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/templates/profiles/base.html b/templates/profiles/base.html new file mode 100644 index 0000000..1ba97f9 --- /dev/null +++ b/templates/profiles/base.html @@ -0,0 +1,35 @@ +{% extends "frontend/base.html" %} +{% block "content" %} + +
+
+
+
+
+
+

UrlaubsAuktion

+

Hallo {{ object.first_name }}, willkommen bei der Urlaubsauktion!

+

+
+ +
+
+ + + {% block "profilepage" %} + {% endblock %} +
+
+
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/urlaubsauktion/database.dist.py b/urlaubsauktion/database.dist.py new file mode 100644 index 0000000..f66d459 --- /dev/null +++ b/urlaubsauktion/database.dist.py @@ -0,0 +1,5 @@ +DATABASE_HOST = "localhost" +DATABASE_PORT = 3306 +DATABASE_USER = "urlaubsauktion" +DATABASE_PASS = "secret" +DATABASE_NAME = "urlaubsauktion" diff --git a/urlaubsauktion/settings.py b/urlaubsauktion/settings.py index 5cf0f1b..9026607 100644 --- a/urlaubsauktion/settings.py +++ b/urlaubsauktion/settings.py @@ -1,6 +1,9 @@ import os from django.utils.translation import gettext_lazy as _ +from django.contrib.messages import constants as messages + +from urlaubsauktion.database import * # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -15,7 +18,7 @@ SECRET_KEY = 'g$_(44hzu#_utld_hn@yw$-x_1l-0pf2+f-3#$^5f2c(p=)nq3' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True -ALLOWED_HOSTS = [] +ALLOWED_HOSTS = ["*"] # Application definition @@ -36,7 +39,6 @@ INSTALLED_APPS = [ 'django_countries', 'phonenumber_field', 'bootstrapform', - 'cookielaw', 'multiselectfield', ] @@ -78,10 +80,11 @@ WSGI_APPLICATION = 'urlaubsauktion.wsgi.application' DATABASES = { 'default': { 'ENGINE': 'django.contrib.gis.db.backends.mysql', - 'NAME': 'urlaubsauktion', - 'USER': 'urlaubsauktion', - 'PASSWORD': 'ooJ3ooPu', - 'HOST': 'localhost', + 'NAME': DATABASE_NAME, + 'USER': DATABASE_USER, + 'PASSWORD': DATABASE_PASS, + 'HOST': DATABASE_HOST, + 'PORT': DATABASE_PORT, } } @@ -111,8 +114,12 @@ AUTH_PASSWORD_VALIDATORS = [ LANGUAGE_CODE = 'de' LANGUAGES = [ - ('de', _('German')), - ('en', _('English')), + ('de', _('Deutsch')), + ('en', _('Englisch')), +] + +CURRENCIES = [ + ('EUR', _("Euro")) ] TIME_ZONE = 'Europe/Vienna' @@ -132,3 +139,7 @@ STATIC_URL = '/static/' STATICFILES_DIRS = [ os.path.join(BASE_DIR, "static"), ] + +MESSAGE_TAGS = { + messages.ERROR: 'danger' +} diff --git a/urlaubsauktion/urls.py b/urlaubsauktion/urls.py index 618b1cb..fe4131d 100644 --- a/urlaubsauktion/urls.py +++ b/urlaubsauktion/urls.py @@ -16,8 +16,13 @@ Including another URLconf from django.contrib import admin from django.urls import path, include +handler404 = 'frontend.views.handler404' +handler500 = 'frontend.views.handler500' + urlpatterns = [ path('admin/', admin.site.urls), path('', include('frontend.urls')), - path('profiles/', include('profiles.urls')) + path('profiles/', include('profiles.urls')), + path('auction/', include('auction.urls')), + path('payment/', include('payment.urls')), ]