From 4e073859495239c624d9a26ddf8a0177612ac8b6 Mon Sep 17 00:00:00 2001 From: Levi Date: Fri, 29 Jul 2016 00:17:34 -0500 Subject: [PATCH] Added command make_donations_charges in order to create stripe current monthly donations from all donators. Now the user can logout using navbar. Added restriction to user in order to make a donation when he has an active monthly donation . Added donations view where the user can view their recents donations. Now users receive an email after making his first donation. --- hosting/views.py | 2 - .../commands/make_donation_charges.py | 89 +++++++----- nosystemd/models.py | 7 + nosystemd/templates/nosystemd/base.html | 7 +- nosystemd/templates/nosystemd/donation.html | 2 +- .../templates/nosystemd/donation_detail.html | 4 +- nosystemd/templates/nosystemd/donations.html | 60 ++++++++ .../nosystemd/emails/donation_charge.html | 136 ++++++++++++++++++ .../nosystemd/emails/donation_charge.txt | 136 ++++++++++++++++++ nosystemd/templates/nosystemd/landing.html | 4 +- nosystemd/templates/nosystemd/login.html | 1 + nosystemd/templates/nosystemd/signup.html | 1 + nosystemd/urls.py | 6 +- nosystemd/views.py | 62 +++++++- 14 files changed, 467 insertions(+), 50 deletions(-) create mode 100644 nosystemd/templates/nosystemd/donations.html create mode 100644 nosystemd/templates/nosystemd/emails/donation_charge.html create mode 100644 nosystemd/templates/nosystemd/emails/donation_charge.txt diff --git a/hosting/views.py b/hosting/views.py index acd53d0c..1a6cbeb8 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -347,8 +347,6 @@ class PaymentVMView(LoginRequiredMixin, FormView): order.set_approved() # Send notification to ungleich as soon as VM has been booked - # TODO send email using celery - context = { 'vm': plan, 'order': order, diff --git a/nosystemd/management/commands/make_donation_charges.py b/nosystemd/management/commands/make_donation_charges.py index 210b04be..70f95d64 100644 --- a/nosystemd/management/commands/make_donation_charges.py +++ b/nosystemd/management/commands/make_donation_charges.py @@ -1,51 +1,72 @@ from django.core.management.base import BaseCommand -from nosystem.models import DonatorStatus, Donation from datetime import datetime from utils.stripe_utils import StripeUtils -from .forms import DonationForm + +from nosystemd.models import DonatorStatus, Donation +from nosystemd.forms import DonationForm class Command(BaseCommand): help = 'Make the monthly stripe charge to all donators' + CURRENCY = 'usd' def handle(self, *args, **options): donators = DonatorStatus.objects.filter(status=DonatorStatus.ACTIVE) current_month = datetime.now().month current_year = datetime.now().year - for donator in donators: - current_month_donation = Donation.objects.get(created_at__month=current_month, - created_at__year=current_year) - if not current_month_donation: - last_donation = Donation.objects.filter(donator=donator).last() - donation_amount = last_donation.donation - # Make stripe charge to a customer - stripe_utils = StripeUtils() - stripe_utils.CURRENCY = 'usd' - charge_response = stripe_utils.make_charge(amount=donation_amount, - customer=donator.stripe_id) - charge = charge_response.get('response_object') + print("--------- STARTING DONATIONS SCRIPT ---------") + print("Donations date: %s-%s" % (current_month, current_year)) - # Check if the payment was approved - if not charge: - # TODO save error - context = { - 'paymentError': charge_response.get('error'), + for donator_status in donators: + donator = donator_status.user.stripecustomer + try: + Donation.objects.get(created_at__month=current_month, + created_at__year=current_year, + donator=donator) + except Donation.DoesNotExist: + try: + # Get donator last donation amount + last_donation = Donation.objects.filter(donator=donator).last() + donation_amount = last_donation.donation + + # Make stripe charge to a customer + stripe_utils = StripeUtils() + stripe_utils.CURRENCY = self.CURRENCY + charge_response = stripe_utils.make_charge(amount=donation_amount, + customer=donator.stripe_id) + charge = charge_response.get('response_object') + + # Check if the payment was approved + if not charge: + # There is an error trying to creating the stripe charge + context = { + 'paymentError': charge_response.get('error'), + } + print("--------- STRIPE PAYMENT ERROR ---------") + print(context) + print("-------------------------") + # Create a donation + charge = charge_response.get('response_object') + donation_data = { + 'cc_brand': charge.source.brand, + 'stripe_charge_id': charge.id, + 'last4': charge.source.last4, + 'billing_address': last_donation.billing_address.id, + 'donator': donator.id, + 'donation': donation_amount } - print(context) - # Create a donation - charge = charge_response.get('response_object') - donation_data = { - 'cc_brand': charge.source.brand, - 'stripe_charge_id': charge.id, - 'last4': charge.source.last4, - 'billing_address': last_donation.billing_address.id, - 'donator': donator.id, - 'donation': donation_amount - } - donation_form = DonationForm(donation_data) - if donation_form.is_valid(): - donation = donation_form.save() - print(donation) + donation_form = DonationForm(donation_data) + if donation_form.is_valid(): + donation = donation_form.save() + print("--------- PAYMENT DONATION SUCCESSFULL ---------") + print("Donator: %s" % donation.donator.user.email) + print("Amount: %s %s" % (donation.donation, self.CURRENCY)) + print("-----------------------------------------------") + except Exception as e: + print("--------- ERROR ---------") + print(e) + print("-------------------------") + continue diff --git a/nosystemd/models.py b/nosystemd/models.py index 45c3948a..786eb8d7 100644 --- a/nosystemd/models.py +++ b/nosystemd/models.py @@ -14,6 +14,9 @@ class DonatorStatus(models.Model): user = models.OneToOneField(CustomUser) status = models.CharField(choices=STATUS_CHOICES, max_length=10, default=ACTIVE) + def __str__(self): + return "%s - %s " % (self.user.email, self.status) + @classmethod def create(cls, user): cls.objects.get_or_create(user=user) @@ -34,6 +37,10 @@ class Donation(models.Model): obj = cls.objects.create(**data) return obj + @classmethod + def get_total_donations_amount(cls): + return sum(cls.objects.all().values_list('donation', flat=True)) + def set_stripe_charge(self, stripe_charge): self.stripe_charge_id = stripe_charge.id self.last4 = stripe_charge.source.last4 diff --git a/nosystemd/templates/nosystemd/base.html b/nosystemd/templates/nosystemd/base.html index b9691186..4bd841f4 100644 --- a/nosystemd/templates/nosystemd/base.html +++ b/nosystemd/templates/nosystemd/base.html @@ -10,7 +10,7 @@ - Creative - Start Bootstrap Theme + NOSYSTEMD @@ -44,7 +44,7 @@ - nosystemd + nosystemd @@ -64,8 +64,9 @@ {{request.user.name}} {% else %} diff --git a/nosystemd/templates/nosystemd/donation.html b/nosystemd/templates/nosystemd/donation.html index f78eb39e..864a98e9 100644 --- a/nosystemd/templates/nosystemd/donation.html +++ b/nosystemd/templates/nosystemd/donation.html @@ -8,7 +8,7 @@
-
+

Monthly Donation

diff --git a/nosystemd/templates/nosystemd/donation_detail.html b/nosystemd/templates/nosystemd/donation_detail.html index f103b461..c4c838c0 100644 --- a/nosystemd/templates/nosystemd/donation_detail.html +++ b/nosystemd/templates/nosystemd/donation_detail.html @@ -60,13 +60,13 @@

- {% trans "Thanks for you donation, you can cancel your monthly donation at any time going to profile > cancel danation"%} + {% trans "Thanks for you donation, you can cancel your monthly donation at any time going to profile > subscription "%}
diff --git a/nosystemd/templates/nosystemd/donations.html b/nosystemd/templates/nosystemd/donations.html new file mode 100644 index 00000000..06749c28 --- /dev/null +++ b/nosystemd/templates/nosystemd/donations.html @@ -0,0 +1,60 @@ +{% extends "nosystemd/base.html" %} +{% load staticfiles bootstrap3 i18n %} + +{% block content %} + +
+
+
+
+ +

{% trans "Donations"%}

+
+ + + + + + + + + + {% for donation in donations %} + + + + + + + {% endfor %} + +
#{% trans "Donation"%}{% trans "Date" %}
{{ donation.id }}{{ donation.donation }} USD{{ donation.created_at|date:"M Y" }} + +
+ + {% if is_paginated %} + + {% endif %} + +
+ +
+
+ +
+ +{% endblock %} \ No newline at end of file diff --git a/nosystemd/templates/nosystemd/emails/donation_charge.html b/nosystemd/templates/nosystemd/emails/donation_charge.html new file mode 100644 index 00000000..569ccba1 --- /dev/null +++ b/nosystemd/templates/nosystemd/emails/donation_charge.html @@ -0,0 +1,136 @@ +{% load static from staticfiles %} + + + + + + +Oxygen Invoice + + + + + + + + + + + + + +
+
+ + +
+ +
+ + + +
+ logo + +
+
+ +
+
+
+
+ + + + + + + + + + +
+ Thank you for you support. +
+ Your monthly donation for {{donation.donation}} USD has been charged.
you can view your invoice clicking on the button below. +
+ +
+
+
+
+ + +
+ ungleich
+
+
+
+ + + diff --git a/nosystemd/templates/nosystemd/emails/donation_charge.txt b/nosystemd/templates/nosystemd/emails/donation_charge.txt new file mode 100644 index 00000000..6e81f644 --- /dev/null +++ b/nosystemd/templates/nosystemd/emails/donation_charge.txt @@ -0,0 +1,136 @@ +{% load static from staticfiles %} + + + + + + +Oxygen Invoice + + + + + + + + + + + + + +
+
+ + +
+ +
+ + + +
+ logo + +
+
+ +
+
+
+
+ + + + + + + + + + +
+ Thank you for you support. +
+ Your monthly donation for {{donation.donation}} has been charged. Thank you for your support.
you can view your invoice clicking on the button below. +
+ +
+
+
+
+ + +
+ ungleich
+
+
+
+ + + diff --git a/nosystemd/templates/nosystemd/landing.html b/nosystemd/templates/nosystemd/landing.html index d9093b4f..4bfb38ec 100755 --- a/nosystemd/templates/nosystemd/landing.html +++ b/nosystemd/templates/nosystemd/landing.html @@ -8,7 +8,9 @@

No more SYSTEMD


We want to remove systemd from the famous linux distros and create a good replacement

- DONATE NOW + DONATE NOW +

We have collected {{total_donations_amount}} USD at time. Thanks you all.

+

diff --git a/nosystemd/templates/nosystemd/login.html b/nosystemd/templates/nosystemd/login.html index 5c34138c..e24e6f62 100644 --- a/nosystemd/templates/nosystemd/login.html +++ b/nosystemd/templates/nosystemd/login.html @@ -30,6 +30,7 @@ {% csrf_token %} + {% for field in form %} {% bootstrap_field field show_label=False type='fields'%} {% endfor %} diff --git a/nosystemd/templates/nosystemd/signup.html b/nosystemd/templates/nosystemd/signup.html index 05f83b91..67aecf41 100644 --- a/nosystemd/templates/nosystemd/signup.html +++ b/nosystemd/templates/nosystemd/signup.html @@ -17,6 +17,7 @@

{% trans "Sign up"%}

+ {% csrf_token %} {% for field in form %} {% bootstrap_field field show_label=False %} diff --git a/nosystemd/urls.py b/nosystemd/urls.py index 0b4b8d53..d95127da 100644 --- a/nosystemd/urls.py +++ b/nosystemd/urls.py @@ -2,7 +2,7 @@ from django.conf.urls import url from .views import LandingView, LoginView, SignupView, PasswordResetView,\ PasswordResetConfirmView, DonationView, DonationDetailView, ChangeDonatorStatusDetailView,\ - DonatorStatusDetailView + DonatorStatusDetailView, DonationListView urlpatterns = [ url(r'^$', LandingView.as_view(), name='landing'), @@ -13,9 +13,9 @@ urlpatterns = [ url(r'reset-password/?$', PasswordResetView.as_view(), name='reset_password'), url(r'reset-password-confirm/(?P[0-9A-Za-z]+)-(?P.+)/$', PasswordResetConfirmView.as_view(), name='reset_password_confirm'), - - url(r'^donations/?$', DonationView.as_view(), name='donations'), + url(r'^donations/?$', DonationListView.as_view(), name='donations'), url(r'donations/(?P\d+)/?$', DonationDetailView.as_view(), name='donations'), + url(r'^make_donation/?$', DonationView.as_view(), name='make_donation'), url(r'donations/status/?$', DonatorStatusDetailView.as_view(), name='donator_status'), url(r'donations/status/(?P\d+)/?$', ChangeDonatorStatusDetailView.as_view(), diff --git a/nosystemd/views.py b/nosystemd/views.py index ee5a91e8..67046a9a 100644 --- a/nosystemd/views.py +++ b/nosystemd/views.py @@ -1,4 +1,5 @@ -from django.views.generic import TemplateView, CreateView, FormView, DetailView, UpdateView +from django.views.generic import TemplateView, CreateView, FormView, DetailView, UpdateView,\ + ListView from django.http import HttpResponseRedirect from django.shortcuts import render from django.core.urlresolvers import reverse_lazy, reverse @@ -12,6 +13,7 @@ from membership.models import CustomUser, StripeCustomer from utils.stripe_utils import StripeUtils from utils.views import PasswordResetViewMixin, PasswordResetConfirmViewMixin from utils.forms import PasswordResetRequestForm +from utils.mailer import BaseEmail from .forms import LoginForm, SignupForm, DonationForm, DonationBillingForm from .models import Donation, DonatorStatus @@ -20,12 +22,23 @@ from .models import Donation, DonatorStatus class LandingView(TemplateView): template_name = "nosystemd/landing.html" + def get_context_data(self, *args, **kwargs): + total_donations_amount = Donation.get_total_donations_amount() + context = { + 'total_donations_amount': total_donations_amount + } + return context + class LoginView(FormView): template_name = "nosystemd/login.html" form_class = LoginForm success_url = reverse_lazy('nosystemd:landing') + def get_success_url(self): + next_url = self.request.session.get('next', self.success_url) + return next_url + def form_valid(self, form): email = form.cleaned_data.get('email') password = form.cleaned_data.get('password') @@ -38,8 +51,10 @@ class LoginView(FormView): return HttpResponseRedirect(self.get_success_url()) def get(self, request, *args, **kwargs): + if self.request.user.is_authenticated(): return HttpResponseRedirect(reverse('nosystemd:landing')) + return super(LoginView, self).get(request, *args, **kwargs) @@ -49,7 +64,7 @@ class SignupView(CreateView): form_class = SignupForm def get_success_url(self): - next_url = self.request.session.get('next', reverse_lazy('nosystemd:signup')) + next_url = self.request.POST.get('next', reverse('nosystemd:login')) return next_url def form_valid(self, form): @@ -78,9 +93,12 @@ class PasswordResetConfirmView(PasswordResetConfirmViewMixin): class DonationView(LoginRequiredMixin, FormView): template_name = 'nosystemd/donation.html' - login_url = reverse_lazy('nosystemd:login') form_class = DonationBillingForm - success_url = reverse_lazy('nosystemd:donations') + success_url = reverse_lazy('nosystemd:make_donation') + + def get_login_url(self): + return "%s?next=%s" % (reverse('nosystemd:signup'), + reverse('nosystemd:make_donation')) def get_context_data(self, **kwargs): context = super(DonationView, self).get_context_data(**kwargs) @@ -90,6 +108,14 @@ class DonationView(LoginRequiredMixin, FormView): return context + def get(self, request, *args, **kwargs): + + if DonatorStatus.objects.filter(user=self.request.user).exists(): + messages.success(self.request, 'Your already are a monthly contributor') + return HttpResponseRedirect(reverse_lazy('nosystemd:donator_status')) + + return self.render_to_response(self.get_context_data()) + def post(self, request, *args, **kwargs): form = self.get_form() @@ -137,6 +163,22 @@ class DonationView(LoginRequiredMixin, FormView): donation_form = DonationForm(donation_data) if donation_form.is_valid(): donation = donation_form.save() + + context = { + 'donation': donation, + 'base_url': "{0}://{1}".format(request.scheme, request.get_host()) + + } + email_data = { + 'subject': 'Your donation have been charged', + 'to': request.user.email, + 'context': context, + 'template_name': 'donation_charge', + 'template_path': 'nosystemd/emails/' + } + email = BaseEmail(**email_data) + email.send() + return HttpResponseRedirect(reverse('nosystemd:donations', kwargs={'pk': donation.id})) else: @@ -153,6 +195,18 @@ class DonationDetailView(LoginRequiredMixin, DetailView): model = Donation +class DonationListView(LoginRequiredMixin, ListView): + template_name = "nosystemd/donations.html" + context_object_name = "donations" + login_url = reverse_lazy('nosystemd:login') + model = Donation + + def get_queryset(self): + queryset = super(DonationListView, self).get_queryset() + queryset = queryset.filter(donator__user=self.request.user) + return queryset + + class DonatorStatusDetailView(LoginRequiredMixin, TemplateView): template_name = "nosystemd/donator_status.html" login_url = reverse_lazy('nosystemd:login')