Show invoices directly from stripe
This commit is contained in:
		
					parent
					
						
							
								fd4f61bc5c
							
						
					
				
			
			
				commit
				
					
						a00a9f6ff0
					
				
			
		
					 5 changed files with 139 additions and 77 deletions
				
			
		|  | @ -1,6 +1,11 @@ | |||
| import datetime | ||||
| 
 | ||||
| from django import template | ||||
| from django.core.urlresolvers import resolve, reverse | ||||
| from django.utils.translation import activate, get_language | ||||
| from django.utils.safestring import mark_safe | ||||
| from django.utils.translation import activate, get_language, ugettext_lazy as _ | ||||
| 
 | ||||
| from utils.hosting_utils import get_ip_addresses | ||||
| 
 | ||||
| register = template.Library() | ||||
| 
 | ||||
|  | @ -52,3 +57,58 @@ def escaped_line_break(value): | |||
|     :return: | ||||
|     """ | ||||
|     return value.replace("\\n", "\n") | ||||
| 
 | ||||
| 
 | ||||
| @register.filter('get_line_item_from_stripe_invoice') | ||||
| def get_line_item_from_stripe_invoice(invoice): | ||||
|     """ | ||||
|     Returns ready-to-use "html" line item to be shown for an invoice in the | ||||
|     invoice list page | ||||
| 
 | ||||
|     :param invoice: the stripe Invoice object | ||||
|     :return: | ||||
|     """ | ||||
|     start_date = 0 | ||||
|     end_date = 0 | ||||
|     is_first = True | ||||
|     vm_id = -1 | ||||
|     for line_data in invoice["lines"]["data"]: | ||||
|         if is_first: | ||||
|             start_date = line_data.period.start | ||||
|             end_date = line_data.period.end | ||||
|             is_first = False | ||||
|             if hasattr(line_data.metadata, "VM_ID"): | ||||
|                 vm_id = line_data.metadata.VM_ID | ||||
|         else: | ||||
|             if line_data.period.start < start_date: | ||||
|                 start_date = line_data.period.start | ||||
|             if line_data.period.end > end_date: | ||||
|                 end_date = line_data.period.end | ||||
|             if hasattr(line_data.metadata, "VM_ID"): | ||||
|                 vm_id = line_data.metadata.VM_ID | ||||
| 
 | ||||
|     try: | ||||
|         vm_id = int(vm_id) | ||||
|     except ValueError as ve: | ||||
|         print(str(ve)) | ||||
|     if invoice["lines"]["data"]: | ||||
|         return mark_safe(""" | ||||
|                     <td class="xs-td-inline">{vm_id}</td> | ||||
|                     <td class="xs-td-inline">{ip_addresses}</td> | ||||
|                     <td class="xs-td-inline">{period}</td> | ||||
|                     <td class="xs-td-inline">{total}</td> | ||||
|                     <td class="text-right last-td"> | ||||
|                         <a class="btn btn-order-detail" href="{stripe_invoice_url}" target="_blank">{see_invoice_text}</a> | ||||
|                     </td> | ||||
|         """.format( | ||||
|             vm_id=vm_id if vm_id > 0 else "", | ||||
|             ip_addresses=mark_safe(get_ip_addresses(vm_id)) if vm_id > 0 else "", | ||||
|             period = mark_safe("%s — %s" % ( | ||||
|                 datetime.datetime.fromtimestamp(start_date).strftime('%Y-%m-%d'), | ||||
|                 datetime.datetime.fromtimestamp(end_date).strftime('%Y-%m-%d'))), | ||||
|             total=invoice.total/100, | ||||
|             stripe_invoice_url=invoice.hosted_invoice_url, | ||||
|             see_invoice_text=_("See Invoice") | ||||
|         )) | ||||
|     else: | ||||
|         return "" | ||||
|  |  | |||
|  | @ -29,7 +29,7 @@ | |||
|                         <img class="svg-img" src="{% static 'hosting/img/key.svg' %}"> | ||||
|                     </div> | ||||
|                 </a> | ||||
|                 <a href="{% if has_invoices %}{% url 'hosting:invoices' %}{% else %}{% url 'hosting:orders' %}{% endif %}" class="hosting-dashboard-item"> | ||||
|                 <a href="{% url 'hosting:invoices' %}" class="hosting-dashboard-item"> | ||||
|                     <h2>{% trans "My Bills" %}</h2> | ||||
|                     <div class="hosting-dashboard-image"> | ||||
|                         <img class="svg-img" src="{% static 'hosting/img/billing.svg' %}"> | ||||
|  |  | |||
|  | @ -26,36 +26,46 @@ | |||
|             </tr> | ||||
|         </thead> | ||||
|         <tbody> | ||||
|             {% for invoice in invoices %} | ||||
|             {% for inv_data in invs %} | ||||
|                 <tr> | ||||
|                     <td class="xs-td-inline" data-header="{% trans 'VM ID' %}">{{ invoice.order.vm_id }}</td> | ||||
|                     <td class="xs-td-inline" data-header="{% trans 'IP Address' %}">{{ ips|get_value_from_dict:invoice.invoice_number|join:"<br/>" }}</td> | ||||
|                     {% with period|get_value_from_dict:invoice.invoice_number as period_to_show %} | ||||
|                     <td class="xs-td-inline" data-header="{% trans 'Period' %}">{{ period_to_show.period_start | date:'Y-m-d' }} — {{ period_to_show.period_end | date:'Y-m-d' }}</td> | ||||
|                     {% endwith %} | ||||
|                     <td class="xs-td-inline" data-header="{% trans 'Amount' %}">{{ invoice.total_in_chf|floatformat:2|intcomma }}</td> | ||||
|                     <td class="text-right last-td"> | ||||
|                         <a class="btn btn-order-detail" href="{% url 'hosting:invoices' invoice.invoice_number %}">{% trans 'See Invoice' %}</a> | ||||
|                     </td> | ||||
|                     {{ inv_data | get_line_item_from_stripe_invoice }} | ||||
|                 </tr> | ||||
|             {% endfor %} | ||||
|         </tbody> | ||||
|     </table> | ||||
| {% if invs.has_other_pages %} | ||||
|   <ul class="pagination"> | ||||
|     {% if invs.has_previous %} | ||||
|       {% if user_email %} | ||||
|         <li><a href="?page={{ invs.previous_page_number }}&user_email={{user_email}}">«</a></li> | ||||
|       {% else %} | ||||
|         <li><a href="?page={{ invs.previous_page_number }}">«</a></li> | ||||
|       {% endif %} | ||||
|     {% else %} | ||||
|       <li class="disabled"><span>«</span></li> | ||||
|     {% endif %} | ||||
|     {% for i in invs.paginator.page_range %} | ||||
|       {% if invs.number == i %} | ||||
|         <li class="active"><span>{{ i }} <span class="sr-only">(current)</span></span></li> | ||||
|       {% else %} | ||||
|           {% if user_email %} | ||||
|               <li><a href="?page={{ i }}&user_email={{user_email}}">{{ i }}</a></li> | ||||
|           {% else %} | ||||
|               <li><a href="?page={{ i }}">{{ i }}</a></li> | ||||
|           {% endif %} | ||||
|       {% endif %} | ||||
|     {% endfor %} | ||||
|     {% if invs.has_next %} | ||||
|       {% if user_email %} | ||||
|         <li><a href="?page={{ invs.next_page_number }}&user_email={{user_email}}">»</a></li> | ||||
|       {% else %} | ||||
|         <li><a href="?page={{ invs.next_page_number }}">»</a></li> | ||||
|       {% endif %} | ||||
|     {% else %} | ||||
|       <li class="disabled"><span>»</span></li> | ||||
|     {% endif %} | ||||
|   </ul> | ||||
| {% endif %} | ||||
| 
 | ||||
|     {% if is_paginated %} | ||||
|         <div class="pagination"> | ||||
|             <span class="page-links"> | ||||
|                 {% if page_obj.has_previous %} | ||||
|                     <a href="{{request.path}}?page={{ page_obj.previous_page_number }}">{% trans "previous" %}</a> | ||||
|                 {% endif %} | ||||
|                 <span class="page-current"> | ||||
|                     {% trans "Page" %} {{ page_obj.number }} {% trans "of" %} {{ page_obj.paginator.num_pages }}. | ||||
|                 </span> | ||||
|                 {% if page_obj.has_next %} | ||||
|                     <a href="{{request.path}}?page={{ page_obj.next_page_number }}">{% trans "next" %}</a> | ||||
|                 {% endif %} | ||||
|             </span> | ||||
|         </div> | ||||
|     {% endif %} | ||||
| </div> | ||||
| {% endblock %} | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ import uuid | |||
| from datetime import datetime | ||||
| from time import sleep | ||||
| 
 | ||||
| import stripe | ||||
| from django import forms | ||||
| from django.conf import settings | ||||
| from django.contrib import messages | ||||
|  | @ -10,6 +11,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin | |||
| from django.contrib.auth.tokens import default_token_generator | ||||
| from django.core.exceptions import ValidationError | ||||
| from django.core.files.base import ContentFile | ||||
| from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger | ||||
| from django.core.urlresolvers import reverse_lazy, reverse | ||||
| from django.http import ( | ||||
|     Http404, HttpResponseRedirect, HttpResponse, JsonResponse | ||||
|  | @ -40,7 +42,6 @@ from datacenterlight.models import VMTemplate, VMPricing | |||
| from datacenterlight.utils import ( | ||||
|     create_vm, get_cms_integration, check_otp, validate_vat_number | ||||
| ) | ||||
| from dynamicweb.settings.base import DCL_ERROR_EMAILS_TO_LIST | ||||
| from hosting.models import UserCardDetail | ||||
| from membership.models import CustomUser, StripeCustomer | ||||
| from opennebula_api.models import OpenNebulaManager | ||||
|  | @ -57,11 +58,10 @@ from utils.hosting_utils import ( | |||
|     get_vm_price_with_vat, get_vm_price_for_given_vat, HostingUtils, | ||||
|     get_vat_rate_for_country | ||||
| ) | ||||
| from utils.ldap_manager import LdapManager | ||||
| from utils.mailer import BaseEmail | ||||
| from utils.stripe_utils import StripeUtils | ||||
| from utils.tasks import send_plain_email_task | ||||
| from utils.ldap_manager import LdapManager | ||||
| 
 | ||||
| from utils.views import ( | ||||
|     PasswordResetViewMixin, PasswordResetConfirmViewMixin, LoginViewMixin, | ||||
|     ResendActivationLinkViewMixin | ||||
|  | @ -73,7 +73,7 @@ from .forms import ( | |||
| from .mixins import ProcessVMSelectionMixin, HostingContextMixin | ||||
| from .models import ( | ||||
|     HostingOrder, HostingBill, HostingPlan, UserHostingKey, VMDetail, | ||||
|     GenericProduct, MonthlyHostingBill, HostingBillLineItem | ||||
|     GenericProduct, MonthlyHostingBill | ||||
| ) | ||||
| 
 | ||||
| logger = logging.getLogger(__name__) | ||||
|  | @ -1240,7 +1240,7 @@ class OrdersHostingListView(LoginRequiredMixin, ListView): | |||
|         return super(OrdersHostingListView, self).get(request, *args, **kwargs) | ||||
| 
 | ||||
| 
 | ||||
| class InvoiceListView(LoginRequiredMixin, ListView): | ||||
| class InvoiceListView(LoginRequiredMixin, TemplateView): | ||||
|     template_name = "hosting/invoices.html" | ||||
|     login_url = reverse_lazy('hosting:login') | ||||
|     context_object_name = "invoices" | ||||
|  | @ -1248,10 +1248,13 @@ class InvoiceListView(LoginRequiredMixin, ListView): | |||
|     ordering = '-created' | ||||
| 
 | ||||
|     def get_context_data(self, **kwargs): | ||||
|         page = self.request.GET.get('page', 1) | ||||
|         context = super(InvoiceListView, self).get_context_data(**kwargs) | ||||
|         invs_page = None | ||||
|         if ('user_email' in self.request.GET | ||||
|             and self.request.user.email == settings.ADMIN_EMAIL): | ||||
|             user_email = self.request.GET['user_email'] | ||||
|             context['user_email'] = user_email | ||||
|             logger.debug( | ||||
|                 "user_email = {}".format(user_email) | ||||
|             ) | ||||
|  | @ -1260,54 +1263,34 @@ class InvoiceListView(LoginRequiredMixin, ListView): | |||
|             except CustomUser.DoesNotExist as dne: | ||||
|                 logger.debug("User does not exist") | ||||
|                 cu = self.request.user | ||||
|             mhbs = MonthlyHostingBill.objects.filter(customer__user=cu) | ||||
|         else: | ||||
|             mhbs = MonthlyHostingBill.objects.filter( | ||||
|                 customer__user=self.request.user | ||||
|             ) | ||||
|         ips_dict = {} | ||||
|         line_item_period_dict = {} | ||||
|         for mhb in mhbs: | ||||
|             invs = stripe.Invoice.list(customer=cu.stripecustomer.stripe_id, | ||||
|                                        count=100) | ||||
|             paginator = Paginator(invs.data, 10) | ||||
|             try: | ||||
|                 vm_detail = VMDetail.objects.get(vm_id=mhb.order.vm_id) | ||||
|                 ips_dict[mhb.invoice_number] = [vm_detail.ipv6, vm_detail.ipv4] | ||||
|                 all_line_items = HostingBillLineItem.objects.filter(monthly_hosting_bill=mhb) | ||||
|                 for line_item in all_line_items: | ||||
|                     if line_item.get_item_detail_str() != "": | ||||
|                         line_item_period_dict[mhb.invoice_number] = { | ||||
|                             "period_start": line_item.period_start, | ||||
|                             "period_end": line_item.period_end | ||||
|                         } | ||||
|                         break | ||||
|             except VMDetail.DoesNotExist as dne: | ||||
|                 ips_dict[mhb.invoice_number] = ['--'] | ||||
|                 logger.debug("VMDetail for {} doesn't exist".format( | ||||
|                     mhb.order.vm_id | ||||
|                 )) | ||||
|         context['ips'] = ips_dict | ||||
|         context['period'] = line_item_period_dict | ||||
|                 invs_page = paginator.page(page) | ||||
|             except PageNotAnInteger: | ||||
|                 invs_page = paginator.page(1) | ||||
|             except EmptyPage: | ||||
|                 invs_page = paginator.page(paginator.num_pages) | ||||
|         else: | ||||
|             try: | ||||
|                 invs = stripe.Invoice.list( | ||||
|                     customer=self.request.user.stripecustomer.stripe_id, | ||||
|                     count=100 | ||||
|                 ) | ||||
|                 paginator = Paginator(invs.data, 10) | ||||
|                 try: | ||||
|                     invs_page = paginator.page(page) | ||||
|                 except PageNotAnInteger: | ||||
|                     invs_page = paginator.page(1) | ||||
|                 except EmptyPage: | ||||
|                     invs_page = paginator.page(paginator.num_pages) | ||||
|             except Exception as ex: | ||||
|                 logger.error(str(ex)) | ||||
|                 invs_page = None | ||||
|         context["invs"] = invs_page | ||||
|         return context | ||||
| 
 | ||||
|     def get_queryset(self): | ||||
|         user = self.request.user | ||||
|         if ('user_email' in self.request.GET | ||||
|             and self.request.user.email == settings.ADMIN_EMAIL): | ||||
|             user_email = self.request.GET['user_email'] | ||||
|             logger.debug( | ||||
|                 "user_email = {}".format(user_email) | ||||
|             ) | ||||
|             try: | ||||
|                 cu = CustomUser.objects.get(email=user_email) | ||||
|             except CustomUser.DoesNotExist as dne: | ||||
|                 logger.debug("User does not exist") | ||||
|                 cu = self.request.user | ||||
|             self.queryset = MonthlyHostingBill.objects.filter(customer__user=cu) | ||||
|         else: | ||||
|             self.queryset = MonthlyHostingBill.objects.filter( | ||||
|                 customer__user=self.request.user | ||||
|             ) | ||||
|         return super(InvoiceListView, self).get_queryset() | ||||
| 
 | ||||
|     @method_decorator(decorators) | ||||
|     def get(self, request, *args, **kwargs): | ||||
|         return super(InvoiceListView, self).get(request, *args, **kwargs) | ||||
|  |  | |||
|  | @ -199,6 +199,15 @@ def get_vat_rate_for_country(country): | |||
|         return 0 | ||||
| 
 | ||||
| 
 | ||||
| def get_ip_addresses(vm_id): | ||||
|     try: | ||||
|         vm_detail = VMDetail.objects.get(vm_id=vm_id) | ||||
|         return "%s <br/>%s" % (vm_detail.ipv6, vm_detail.ipv4) | ||||
|     except VMDetail.DoesNotExist as dne: | ||||
|         logger.error(str(dne)) | ||||
|         logger.error("VMDetail for %s does not exist" % vm_id) | ||||
|         return "--" | ||||
| 
 | ||||
| class HostingUtils: | ||||
|     @staticmethod | ||||
|     def clear_items_from_list(from_list, items_list): | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue