Show invoices directly from stripe

This commit is contained in:
PCoder 2020-01-20 12:07:32 +05:30
parent fd4f61bc5c
commit a00a9f6ff0
5 changed files with 139 additions and 77 deletions

View file

@ -1,6 +1,11 @@
import datetime
from django import template from django import template
from django.core.urlresolvers import resolve, reverse 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() register = template.Library()
@ -52,3 +57,58 @@ def escaped_line_break(value):
:return: :return:
""" """
return value.replace("\\n", "\n") 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 &mdash; %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 ""

View file

@ -29,7 +29,7 @@
<img class="svg-img" src="{% static 'hosting/img/key.svg' %}"> <img class="svg-img" src="{% static 'hosting/img/key.svg' %}">
</div> </div>
</a> </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> <h2>{% trans "My Bills" %}</h2>
<div class="hosting-dashboard-image"> <div class="hosting-dashboard-image">
<img class="svg-img" src="{% static 'hosting/img/billing.svg' %}"> <img class="svg-img" src="{% static 'hosting/img/billing.svg' %}">

View file

@ -26,36 +26,46 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for invoice in invoices %} {% for inv_data in invs %}
<tr> <tr>
<td class="xs-td-inline" data-header="{% trans 'VM ID' %}">{{ invoice.order.vm_id }}</td> {{ inv_data | get_line_item_from_stripe_invoice }}
<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' }} &mdash; {{ 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>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
{% if invs.has_other_pages %}
{% if is_paginated %} <ul class="pagination">
<div class="pagination"> {% if invs.has_previous %}
<span class="page-links"> {% if user_email %}
{% if page_obj.has_previous %} <li><a href="?page={{ invs.previous_page_number }}&user_email={{user_email}}">&laquo;</a></li>
<a href="{{request.path}}?page={{ page_obj.previous_page_number }}">{% trans "previous" %}</a> {% else %}
{% endif %} <li><a href="?page={{ invs.previous_page_number }}">&laquo;</a></li>
<span class="page-current"> {% endif %}
{% trans "Page" %} {{ page_obj.number }} {% trans "of" %} {{ page_obj.paginator.num_pages }}. {% else %}
</span> <li class="disabled"><span>&laquo;</span></li>
{% if page_obj.has_next %}
<a href="{{request.path}}?page={{ page_obj.next_page_number }}">{% trans "next" %}</a>
{% endif %}
</span>
</div>
{% endif %} {% 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}}">&raquo;</a></li>
{% else %}
<li><a href="?page={{ invs.next_page_number }}">&raquo;</a></li>
{% endif %}
{% else %}
<li class="disabled"><span>&raquo;</span></li>
{% endif %}
</ul>
{% endif %}
</div> </div>
{% endblock %} {% endblock %}

View file

@ -3,6 +3,7 @@ import uuid
from datetime import datetime from datetime import datetime
from time import sleep from time import sleep
import stripe
from django import forms from django import forms
from django.conf import settings from django.conf import settings
from django.contrib import messages 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.contrib.auth.tokens import default_token_generator
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.core.files.base import ContentFile 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.core.urlresolvers import reverse_lazy, reverse
from django.http import ( from django.http import (
Http404, HttpResponseRedirect, HttpResponse, JsonResponse Http404, HttpResponseRedirect, HttpResponse, JsonResponse
@ -40,7 +42,6 @@ from datacenterlight.models import VMTemplate, VMPricing
from datacenterlight.utils import ( from datacenterlight.utils import (
create_vm, get_cms_integration, check_otp, validate_vat_number 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 hosting.models import UserCardDetail
from membership.models import CustomUser, StripeCustomer from membership.models import CustomUser, StripeCustomer
from opennebula_api.models import OpenNebulaManager 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_vm_price_with_vat, get_vm_price_for_given_vat, HostingUtils,
get_vat_rate_for_country get_vat_rate_for_country
) )
from utils.ldap_manager import LdapManager
from utils.mailer import BaseEmail from utils.mailer import BaseEmail
from utils.stripe_utils import StripeUtils from utils.stripe_utils import StripeUtils
from utils.tasks import send_plain_email_task from utils.tasks import send_plain_email_task
from utils.ldap_manager import LdapManager
from utils.views import ( from utils.views import (
PasswordResetViewMixin, PasswordResetConfirmViewMixin, LoginViewMixin, PasswordResetViewMixin, PasswordResetConfirmViewMixin, LoginViewMixin,
ResendActivationLinkViewMixin ResendActivationLinkViewMixin
@ -73,7 +73,7 @@ from .forms import (
from .mixins import ProcessVMSelectionMixin, HostingContextMixin from .mixins import ProcessVMSelectionMixin, HostingContextMixin
from .models import ( from .models import (
HostingOrder, HostingBill, HostingPlan, UserHostingKey, VMDetail, HostingOrder, HostingBill, HostingPlan, UserHostingKey, VMDetail,
GenericProduct, MonthlyHostingBill, HostingBillLineItem GenericProduct, MonthlyHostingBill
) )
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -1240,7 +1240,7 @@ class OrdersHostingListView(LoginRequiredMixin, ListView):
return super(OrdersHostingListView, self).get(request, *args, **kwargs) return super(OrdersHostingListView, self).get(request, *args, **kwargs)
class InvoiceListView(LoginRequiredMixin, ListView): class InvoiceListView(LoginRequiredMixin, TemplateView):
template_name = "hosting/invoices.html" template_name = "hosting/invoices.html"
login_url = reverse_lazy('hosting:login') login_url = reverse_lazy('hosting:login')
context_object_name = "invoices" context_object_name = "invoices"
@ -1248,10 +1248,13 @@ class InvoiceListView(LoginRequiredMixin, ListView):
ordering = '-created' ordering = '-created'
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
page = self.request.GET.get('page', 1)
context = super(InvoiceListView, self).get_context_data(**kwargs) context = super(InvoiceListView, self).get_context_data(**kwargs)
invs_page = None
if ('user_email' in self.request.GET if ('user_email' in self.request.GET
and self.request.user.email == settings.ADMIN_EMAIL): and self.request.user.email == settings.ADMIN_EMAIL):
user_email = self.request.GET['user_email'] user_email = self.request.GET['user_email']
context['user_email'] = user_email
logger.debug( logger.debug(
"user_email = {}".format(user_email) "user_email = {}".format(user_email)
) )
@ -1260,54 +1263,34 @@ class InvoiceListView(LoginRequiredMixin, ListView):
except CustomUser.DoesNotExist as dne: except CustomUser.DoesNotExist as dne:
logger.debug("User does not exist") logger.debug("User does not exist")
cu = self.request.user cu = self.request.user
mhbs = MonthlyHostingBill.objects.filter(customer__user=cu) invs = stripe.Invoice.list(customer=cu.stripecustomer.stripe_id,
else: count=100)
mhbs = MonthlyHostingBill.objects.filter( paginator = Paginator(invs.data, 10)
customer__user=self.request.user
)
ips_dict = {}
line_item_period_dict = {}
for mhb in mhbs:
try: try:
vm_detail = VMDetail.objects.get(vm_id=mhb.order.vm_id) invs_page = paginator.page(page)
ips_dict[mhb.invoice_number] = [vm_detail.ipv6, vm_detail.ipv4] except PageNotAnInteger:
all_line_items = HostingBillLineItem.objects.filter(monthly_hosting_bill=mhb) invs_page = paginator.page(1)
for line_item in all_line_items: except EmptyPage:
if line_item.get_item_detail_str() != "": invs_page = paginator.page(paginator.num_pages)
line_item_period_dict[mhb.invoice_number] = { else:
"period_start": line_item.period_start, try:
"period_end": line_item.period_end invs = stripe.Invoice.list(
} customer=self.request.user.stripecustomer.stripe_id,
break count=100
except VMDetail.DoesNotExist as dne: )
ips_dict[mhb.invoice_number] = ['--'] paginator = Paginator(invs.data, 10)
logger.debug("VMDetail for {} doesn't exist".format( try:
mhb.order.vm_id invs_page = paginator.page(page)
)) except PageNotAnInteger:
context['ips'] = ips_dict invs_page = paginator.page(1)
context['period'] = line_item_period_dict 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 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) @method_decorator(decorators)
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
return super(InvoiceListView, self).get(request, *args, **kwargs) return super(InvoiceListView, self).get(request, *args, **kwargs)

View file

@ -199,6 +199,15 @@ def get_vat_rate_for_country(country):
return 0 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: class HostingUtils:
@staticmethod @staticmethod
def clear_items_from_list(from_list, items_list): def clear_items_from_list(from_list, items_list):