update invoice template
This commit is contained in:
parent
e70423ce86
commit
b6fda8e7f9
18 changed files with 441 additions and 275 deletions
|
@ -13,7 +13,7 @@ class SettingForm(forms.SelfHandlingForm):
|
||||||
company_logo = forms.URLField(label=_("COMPANY LOGO URL"),
|
company_logo = forms.URLField(label=_("COMPANY LOGO URL"),
|
||||||
required=False)
|
required=False)
|
||||||
company_address = forms.CharField(label=_("COMPANY ADDRESS"),
|
company_address = forms.CharField(label=_("COMPANY ADDRESS"),
|
||||||
required=False)
|
required=False, widget=forms.Textarea())
|
||||||
email_admin = forms.EmailField(label=_("EMAIL ADMIN"),
|
email_admin = forms.EmailField(label=_("EMAIL ADMIN"),
|
||||||
required=True)
|
required=True)
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ from django.urls import reverse
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from horizon import tables
|
from horizon import tables
|
||||||
|
from horizon.utils import filters as utils_filters
|
||||||
|
|
||||||
|
|
||||||
class NotificationFilterAction(tables.FilterAction):
|
class NotificationFilterAction(tables.FilterAction):
|
||||||
|
@ -15,7 +16,8 @@ class NotificationTable(tables.DataTable):
|
||||||
recipient = tables.Column("recipient", verbose_name=_("Recipient"))
|
recipient = tables.Column("recipient", verbose_name=_("Recipient"))
|
||||||
sent_status = tables.Column("sent_status", verbose_name=_("Sent Status"))
|
sent_status = tables.Column("sent_status", verbose_name=_("Sent Status"))
|
||||||
is_read = tables.Column("is_read", verbose_name=_("Is Read"))
|
is_read = tables.Column("is_read", verbose_name=_("Is Read"))
|
||||||
created_at = tables.Column("created_at", verbose_name=_("Created At"))
|
created_at = tables.Column("created_at", verbose_name=_("Date Time"), filters=(utils_filters.parse_isotime,),
|
||||||
|
sortable=True)
|
||||||
|
|
||||||
def get_object_id(self, obj):
|
def get_object_id(self, obj):
|
||||||
return obj["id"]
|
return obj["id"]
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
<dt>Project: </dt>
|
<dt>Project: </dt>
|
||||||
<dd>
|
<dd>
|
||||||
<select id="selection" onchange="onSelection(this.value)">
|
<select id="selection" onchange="onSelection(this.value)">
|
||||||
<option value='{}'
|
<option value='null'
|
||||||
{% if not current_tenant_id %}selected {% endif %}
|
{% if not current_tenant_id %}selected {% endif %}
|
||||||
>All</option>
|
>All</option>
|
||||||
|
|
||||||
|
|
|
@ -5,10 +5,8 @@
|
||||||
{% block main %}
|
{% block main %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12 text-right">
|
<div class="col-sm-12 text-right">
|
||||||
{% if not notification.sent_status %}
|
|
||||||
<a href="{% url 'horizon:admin:notification_center:resend' notification.id %}"
|
<a href="{% url 'horizon:admin:notification_center:resend' notification.id %}"
|
||||||
class="btn btn-primary" style="margin-bottom: 8px">Resend Notification</a>
|
class="btn btn-primary" style="margin-bottom: 8px">Resend Notification</a>
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
|
@ -9,7 +9,7 @@ class InvoiceAction(tables.LinkAction):
|
||||||
verbose_name = "Invoice"
|
verbose_name = "Invoice"
|
||||||
|
|
||||||
def get_link_url(self, datum=None, *args, **kwargs):
|
def get_link_url(self, datum=None, *args, **kwargs):
|
||||||
return reverse("horizon:admin:projects_invoice:download_pdf", kwargs={
|
return reverse("horizon:admin:projects_invoice:invoice_detail", kwargs={
|
||||||
"id": datum['id'],
|
"id": datum['id'],
|
||||||
"project_id": datum['project_id'],
|
"project_id": datum['project_id'],
|
||||||
})
|
})
|
||||||
|
|
|
@ -0,0 +1,155 @@
|
||||||
|
<style>
|
||||||
|
.invoice {
|
||||||
|
padding: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoice h2 {
|
||||||
|
margin-top: 0px;
|
||||||
|
line-height: 0.8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoice .small {
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoice hr {
|
||||||
|
margin-top: 10px;
|
||||||
|
border-color: #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoice .table tr.line {
|
||||||
|
border-bottom: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoice .table td {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoice .identity {
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 1.1em;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoice .identity strong {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.grid {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
background: #fff;
|
||||||
|
color: #666666;
|
||||||
|
border-radius: 2px;
|
||||||
|
margin-bottom: 25px;
|
||||||
|
box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div id="invoice">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<!-- BEGIN INVOICE -->
|
||||||
|
<div class="col-xs-12">
|
||||||
|
<div class="grid invoice">
|
||||||
|
<div class="grid-body">
|
||||||
|
<div class="invoice-title">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12">
|
||||||
|
<img src="{{ setting.company_logo }}" alt="" height="50">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12">
|
||||||
|
<h2>invoice<br>
|
||||||
|
<span class="small">order #{{ invoice.id }}</span></h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-6">
|
||||||
|
<address style="max-width: 30%;">
|
||||||
|
{{ setting.company_name }} <br/>
|
||||||
|
{{ setting.company_address }}
|
||||||
|
</address>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-6 text-right">
|
||||||
|
<address>
|
||||||
|
<strong>Invoice Month:</strong><br>
|
||||||
|
{{ invoice.start_date|date:"M Y" }}
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<strong>Invoice State:</strong><br>
|
||||||
|
{{ invoice.state_text }}
|
||||||
|
</address>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<h3>ORDER SUMMARY</h3>
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr class="line">
|
||||||
|
<td><strong>#</strong></td>
|
||||||
|
<td width="70%"><strong>COMPONENT</strong></td>
|
||||||
|
<td class="text-right"><strong>TOTAL COST</strong></td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>1</td>
|
||||||
|
<td><strong>Instance</strong></td>
|
||||||
|
<td class="text-right">{{ instance_cost }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>2</td>
|
||||||
|
<td><strong>Volume</strong></td>
|
||||||
|
<td class="text-right">{{ volume_cost }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>3</td>
|
||||||
|
<td><strong>Floating IP</strong></td>
|
||||||
|
<td class="text-right">{{ fip_cost }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>4</td>
|
||||||
|
<td><strong>Router</strong></td>
|
||||||
|
<td class="text-right">{{ router_cost }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>5</td>
|
||||||
|
<td><strong>Snapshot</strong></td>
|
||||||
|
<td class="text-right">{{ snapshot_cost }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="line">
|
||||||
|
<td>6</td>
|
||||||
|
<td><strong>Image</strong></td>
|
||||||
|
<td class="text-right">{{ image_cost }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" class="text-right"><strong>Subtotal</strong></td>
|
||||||
|
<td class="text-right"><strong>{{ invoice.subtotal_money }}</strong></td>
|
||||||
|
</tr>
|
||||||
|
{% if invoice.state != 1 %}
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" class="text-right"><strong>Tax</strong></td>
|
||||||
|
<td class="text-right"><strong>{{ invoice.tax_money }}</strong></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" class="text-right"><strong>Total</strong></td>
|
||||||
|
<td class="text-right"><strong>{{ invoice.total_money }}</strong></td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- END INVOICE -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -1,123 +0,0 @@
|
||||||
{% extends 'base.html' %}
|
|
||||||
{% block title %}{{ page_title }}{% endblock %}
|
|
||||||
{% block main %}
|
|
||||||
<button onclick="javascript:downloadPdf();" class="btn btn-default">Download PDF</button>
|
|
||||||
{% if invoice.state == 2 %}
|
|
||||||
<a class="btn btn-primary" href="{% url 'horizon:admin:projects_invoice:finish_invoice' invoice.id %}?next={{ request.path }}">Set to Finished</a>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if invoice.state == 100 %}
|
|
||||||
<a class="btn btn-danger" href="{% url 'horizon:admin:projects_invoice:rollback_to_unpaid' invoice.id %}?next={{ request.path }}">Rollback to Unpaid</a>
|
|
||||||
{% endif %}
|
|
||||||
<br/>
|
|
||||||
<br/>
|
|
||||||
|
|
||||||
<div id="invoice">
|
|
||||||
<div>
|
|
||||||
<dl>
|
|
||||||
<dt>Invoice Month</dt>
|
|
||||||
<dd>
|
|
||||||
<h3>{{ invoice.start_date|date:"M Y" }}</h3>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
<h5>Invoice State: {{ invoice.state_text }}</h5>
|
|
||||||
</div>
|
|
||||||
<table class="table table-striped">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Component</th>
|
|
||||||
<th>Total Cost</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>Instance</td>
|
|
||||||
<td>{{ instance_cost }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Volume</td>
|
|
||||||
<td>{{ volume_cost }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Floating IP</td>
|
|
||||||
<td>{{ fip_cost }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Router</td>
|
|
||||||
<td>{{ router_cost }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Snapshot</td>
|
|
||||||
<td>{{ snapshot_cost }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Image</td>
|
|
||||||
<td>{{ image_cost }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td></td>
|
|
||||||
<td><b>Subtotal:</b> {{ invoice.subtotal_money }}</td>
|
|
||||||
</tr>
|
|
||||||
{% if invoice.state != 1 %}
|
|
||||||
<tr>
|
|
||||||
<td></td>
|
|
||||||
<td><b>Tax:</b> {{ invoice.tax_money }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td></td>
|
|
||||||
<td><b>Total:</b> {{ invoice.total_money }}</td>
|
|
||||||
</tr>
|
|
||||||
{% endif %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
{% block js %}
|
|
||||||
{{ block.super }}
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.3.2/jspdf.min.js"></script>
|
|
||||||
<script type="text/javascript">
|
|
||||||
function downloadPdf() {
|
|
||||||
var pdf = new jsPDF('p', 'pt', 'letter');
|
|
||||||
// source can be HTML-formatted string, or a reference
|
|
||||||
// to an actual DOM element from which the text will be scraped.
|
|
||||||
source = $('#invoice')[0];
|
|
||||||
|
|
||||||
// we support special element handlers. Register them with jQuery-style
|
|
||||||
// ID selector for either ID or node name. ("#iAmID", "div", "span" etc.)
|
|
||||||
// There is no support for any other type of selectors
|
|
||||||
// (class, of compound) at this time.
|
|
||||||
specialElementHandlers = {
|
|
||||||
// element with id of "bypass" - jQuery style selector
|
|
||||||
'#bypassme': function (element, renderer) {
|
|
||||||
// true = "handled elsewhere, bypass text extraction"
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
};
|
|
||||||
margins = {
|
|
||||||
top: 80,
|
|
||||||
bottom: 60,
|
|
||||||
left: 40,
|
|
||||||
width: 522
|
|
||||||
};
|
|
||||||
// all coords and widths are in jsPDF instance's declared units
|
|
||||||
// 'inches' in this case
|
|
||||||
pdf.fromHTML(
|
|
||||||
source, // HTML string or DOM elem ref.
|
|
||||||
margins.left, // x coord
|
|
||||||
margins.top, { // y coord
|
|
||||||
'width': margins.width, // max width of content on PDF
|
|
||||||
'elementHandlers': specialElementHandlers
|
|
||||||
},
|
|
||||||
|
|
||||||
function (dispose) {
|
|
||||||
// dispose: object with X, Y of the last line add to the PDF
|
|
||||||
// this allow the insertion of new lines after html
|
|
||||||
pdf.save('invoice.pdf');
|
|
||||||
}, margins);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% block title %}{{ page_title }}{% endblock %}
|
||||||
|
{% block main %}
|
||||||
|
<a class="btn btn-default" href="?print=true" target="_blank">Download PDF</a>
|
||||||
|
{% if invoice.state == 2 %}
|
||||||
|
<a class="btn btn-primary"
|
||||||
|
href="{% url 'horizon:admin:projects_invoice:finish_invoice' invoice.id %}?next={{ request.path }}">Set to
|
||||||
|
Finished</a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if invoice.state == 100 %}
|
||||||
|
<a class="btn btn-danger"
|
||||||
|
href="{% url 'horizon:admin:projects_invoice:rollback_to_unpaid' invoice.id %}?next={{ request.path }}">Rollback
|
||||||
|
to Unpaid</a>
|
||||||
|
{% endif %}
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
{% include 'admin/projects_invoice/base_invoice.html' %}
|
||||||
|
{% endblock %}
|
||||||
|
{% block js %}
|
||||||
|
{{ block.super }}
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"
|
||||||
|
integrity="sha512-GsLlZN/3F2ErC5ifS5QtgpiJtWd43JWSuIgh7mbzZ8zBps+dvLusV+eNQATqgA/HdeKFVgA5v3S/cIrLF7QnIg=="
|
||||||
|
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
function downloadPdf() {
|
||||||
|
window.print();
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,13 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Invoice Download</title>
|
||||||
|
{% include "_stylesheets.html" %}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{% include 'admin/projects_invoice/base_invoice.html' %}
|
||||||
|
{% include "horizon/_scripts.html" %}
|
||||||
|
<script type="text/javascript">
|
||||||
|
window.print();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -16,7 +16,7 @@ from openstack_dashboard.dashboards.yuyu.admin.projects_invoice import views
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^$', views.IndexView.as_view(), name='index'),
|
url(r'^$', views.IndexView.as_view(), name='index'),
|
||||||
url(r'^invoice/pdf/(?P<project_id>[^/]+)/(?P<id>[^/]+)/$', views.InvoiceView.as_view(), name='download_pdf'),
|
url(r'^invoice/detail/(?P<project_id>[^/]+)/(?P<id>[^/]+)/$', views.InvoiceView.as_view(), name='invoice_detail'),
|
||||||
url(r'^invoice/usage/(?P<project_id>[^/]+)/(?P<id>[^/]+)/$', views.UsageCostView.as_view(), name='usage_cost'),
|
url(r'^invoice/usage/(?P<project_id>[^/]+)/(?P<id>[^/]+)/$', views.UsageCostView.as_view(), name='usage_cost'),
|
||||||
url(r'^invoice/finish/(?P<id>[^/]+)/$', views.FinishInvoice.as_view(), name='finish_invoice'),
|
url(r'^invoice/finish/(?P<id>[^/]+)/$', views.FinishInvoice.as_view(), name='finish_invoice'),
|
||||||
url(r'^invoice/rollback_to_unpaid/(?P<id>[^/]+)/$', views.RollbackToUnpaidInvoice.as_view(),
|
url(r'^invoice/rollback_to_unpaid/(?P<id>[^/]+)/$', views.RollbackToUnpaidInvoice.as_view(),
|
||||||
|
|
|
@ -27,6 +27,7 @@ from horizon import views
|
||||||
from openstack_dashboard import api
|
from openstack_dashboard import api
|
||||||
from openstack_dashboard.dashboards.yuyu.cases.invoice_use_case import InvoiceUseCase
|
from openstack_dashboard.dashboards.yuyu.cases.invoice_use_case import InvoiceUseCase
|
||||||
from .tables import InvoiceTable
|
from .tables import InvoiceTable
|
||||||
|
from ...cases.setting_use_case import SettingUseCase
|
||||||
from ...core.usage_cost.tables import InstanceCostTable, VolumeCostTable, FloatingIpCostTable, RouterCostTable, \
|
from ...core.usage_cost.tables import InstanceCostTable, VolumeCostTable, FloatingIpCostTable, RouterCostTable, \
|
||||||
SnapshotCostTable, ImageCostTable
|
SnapshotCostTable, ImageCostTable
|
||||||
from ...core.utils.invoice_utils import state_to_text
|
from ...core.utils.invoice_utils import state_to_text
|
||||||
|
@ -67,14 +68,21 @@ class IndexView(tables.DataTableView):
|
||||||
|
|
||||||
class InvoiceView(views.APIView):
|
class InvoiceView(views.APIView):
|
||||||
page_title = _("Invoice")
|
page_title = _("Invoice")
|
||||||
template_name = "admin/projects_invoice/download_pdf.html"
|
|
||||||
|
|
||||||
invoice_uc = InvoiceUseCase()
|
invoice_uc = InvoiceUseCase()
|
||||||
|
setting_uc = SettingUseCase()
|
||||||
|
|
||||||
|
def get_template_names(self):
|
||||||
|
if self.request.GET.get('print', None):
|
||||||
|
return ['admin/projects_invoice/invoice_download.html']
|
||||||
|
else:
|
||||||
|
return ['admin/projects_invoice/invoice.html']
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
invoice = self.invoice_uc.get_invoice(self.request, self.kwargs['id'], tenant_id=self.kwargs['project_id'])
|
invoice = self.invoice_uc.get_invoice(self.request, self.kwargs['id'], tenant_id=self.kwargs['project_id'])
|
||||||
context['invoice'] = invoice
|
context['invoice'] = invoice
|
||||||
|
context['setting'] = self.setting_uc.get_settings(self.request)
|
||||||
context['instance_cost'] = self.get_sum_price(invoice, 'instances')
|
context['instance_cost'] = self.get_sum_price(invoice, 'instances')
|
||||||
context['volume_cost'] = self.get_sum_price(invoice, 'volumes')
|
context['volume_cost'] = self.get_sum_price(invoice, 'volumes')
|
||||||
context['fip_cost'] = self.get_sum_price(invoice, 'floating_ips')
|
context['fip_cost'] = self.get_sum_price(invoice, 'floating_ips')
|
||||||
|
|
|
@ -10,7 +10,7 @@ class DetailAction(tables.LinkAction):
|
||||||
|
|
||||||
def get_link_url(self, datum=None, *args, **kwargs):
|
def get_link_url(self, datum=None, *args, **kwargs):
|
||||||
print(datum, args, kwargs)
|
print(datum, args, kwargs)
|
||||||
return reverse("horizon:project:invoice:download_pdf", kwargs={"id": datum['id']})
|
return reverse("horizon:project:invoice:invoice_detail", kwargs={"id": datum['id']})
|
||||||
|
|
||||||
|
|
||||||
class InvoiceTable(tables.DataTable):
|
class InvoiceTable(tables.DataTable):
|
||||||
|
|
155
yuyu/project/invoice/templates/invoice/base_invoice.html
Normal file
155
yuyu/project/invoice/templates/invoice/base_invoice.html
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
<style>
|
||||||
|
.invoice {
|
||||||
|
padding: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoice h2 {
|
||||||
|
margin-top: 0px;
|
||||||
|
line-height: 0.8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoice .small {
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoice hr {
|
||||||
|
margin-top: 10px;
|
||||||
|
border-color: #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoice .table tr.line {
|
||||||
|
border-bottom: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoice .table td {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoice .identity {
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 1.1em;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoice .identity strong {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.grid {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
background: #fff;
|
||||||
|
color: #666666;
|
||||||
|
border-radius: 2px;
|
||||||
|
margin-bottom: 25px;
|
||||||
|
box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div id="invoice">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<!-- BEGIN INVOICE -->
|
||||||
|
<div class="col-xs-12">
|
||||||
|
<div class="grid invoice">
|
||||||
|
<div class="grid-body">
|
||||||
|
<div class="invoice-title">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12">
|
||||||
|
<img src="{{ setting.company_logo }}" alt="" height="50">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12">
|
||||||
|
<h2>invoice<br>
|
||||||
|
<span class="small">order #{{ invoice.id }}</span></h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-6">
|
||||||
|
<address style="max-width: 30%;">
|
||||||
|
{{ setting.company_name }} <br/>
|
||||||
|
{{ setting.company_address }}
|
||||||
|
</address>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-6 text-right">
|
||||||
|
<address>
|
||||||
|
<strong>Invoice Month:</strong><br>
|
||||||
|
{{ invoice.start_date|date:"M Y" }}
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<strong>Invoice State:</strong><br>
|
||||||
|
{{ invoice.state_text }}
|
||||||
|
</address>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<h3>ORDER SUMMARY</h3>
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr class="line">
|
||||||
|
<td><strong>#</strong></td>
|
||||||
|
<td width="70%"><strong>COMPONENT</strong></td>
|
||||||
|
<td class="text-right"><strong>TOTAL COST</strong></td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>1</td>
|
||||||
|
<td><strong>Instance</strong></td>
|
||||||
|
<td class="text-right">{{ instance_cost }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>2</td>
|
||||||
|
<td><strong>Volume</strong></td>
|
||||||
|
<td class="text-right">{{ volume_cost }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>3</td>
|
||||||
|
<td><strong>Floating IP</strong></td>
|
||||||
|
<td class="text-right">{{ fip_cost }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>4</td>
|
||||||
|
<td><strong>Router</strong></td>
|
||||||
|
<td class="text-right">{{ router_cost }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>5</td>
|
||||||
|
<td><strong>Snapshot</strong></td>
|
||||||
|
<td class="text-right">{{ snapshot_cost }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="line">
|
||||||
|
<td>6</td>
|
||||||
|
<td><strong>Image</strong></td>
|
||||||
|
<td class="text-right">{{ image_cost }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" class="text-right"><strong>Subtotal</strong></td>
|
||||||
|
<td class="text-right"><strong>{{ invoice.subtotal_money }}</strong></td>
|
||||||
|
</tr>
|
||||||
|
{% if invoice.state != 1 %}
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" class="text-right"><strong>Tax</strong></td>
|
||||||
|
<td class="text-right"><strong>{{ invoice.tax_money }}</strong></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" class="text-right"><strong>Total</strong></td>
|
||||||
|
<td class="text-right"><strong>{{ invoice.total_money }}</strong></td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- END INVOICE -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -1,116 +0,0 @@
|
||||||
{% extends 'base.html' %}
|
|
||||||
{% block title %}{{ page_title }}{% endblock %}
|
|
||||||
{% block main %}
|
|
||||||
<button onclick="javascript:downloadPdf();" class="btn btn-default">Download PDF</button>
|
|
||||||
<br/>
|
|
||||||
<br/>
|
|
||||||
|
|
||||||
<div id="invoice">
|
|
||||||
<div>
|
|
||||||
<dl>
|
|
||||||
<dt>Invoice Month</dt>
|
|
||||||
<dd>
|
|
||||||
<h3>{{ invoice.start_date|date:"M Y" }}</h3>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
<h5>Invoice State: {{ invoice.state_text }}</h5>
|
|
||||||
</div>
|
|
||||||
<table class="table table-striped">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Component</th>
|
|
||||||
<th>Total Cost</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>Instance</td>
|
|
||||||
<td>{{ instance_cost }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Volume</td>
|
|
||||||
<td>{{ volume_cost }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Floating IP</td>
|
|
||||||
<td>{{ fip_cost }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Router</td>
|
|
||||||
<td>{{ router_cost }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Snapshot</td>
|
|
||||||
<td>{{ snapshot_cost }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Image</td>
|
|
||||||
<td>{{ image_cost }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td></td>
|
|
||||||
<td><b>Subtotal:</b> {{ invoice.subtotal_money }}</td>
|
|
||||||
</tr>
|
|
||||||
{% if invoice.state != 1 %}
|
|
||||||
<tr>
|
|
||||||
<td></td>
|
|
||||||
<td><b>Tax:</b> {{ invoice.tax_money }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td></td>
|
|
||||||
<td><b>Total:</b> {{ invoice.total_money }}</td>
|
|
||||||
</tr>
|
|
||||||
{% endif %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
{% block js %}
|
|
||||||
{{ block.super }}
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.3.2/jspdf.min.js"></script>
|
|
||||||
<script type="text/javascript">
|
|
||||||
function downloadPdf() {
|
|
||||||
var pdf = new jsPDF('p', 'pt', 'letter');
|
|
||||||
// source can be HTML-formatted string, or a reference
|
|
||||||
// to an actual DOM element from which the text will be scraped.
|
|
||||||
source = $('#invoice')[0];
|
|
||||||
|
|
||||||
// we support special element handlers. Register them with jQuery-style
|
|
||||||
// ID selector for either ID or node name. ("#iAmID", "div", "span" etc.)
|
|
||||||
// There is no support for any other type of selectors
|
|
||||||
// (class, of compound) at this time.
|
|
||||||
specialElementHandlers = {
|
|
||||||
// element with id of "bypass" - jQuery style selector
|
|
||||||
'#bypassme': function (element, renderer) {
|
|
||||||
// true = "handled elsewhere, bypass text extraction"
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
};
|
|
||||||
margins = {
|
|
||||||
top: 80,
|
|
||||||
bottom: 60,
|
|
||||||
left: 40,
|
|
||||||
width: 522
|
|
||||||
};
|
|
||||||
// all coords and widths are in jsPDF instance's declared units
|
|
||||||
// 'inches' in this case
|
|
||||||
pdf.fromHTML(
|
|
||||||
source, // HTML string or DOM elem ref.
|
|
||||||
margins.left, // x coord
|
|
||||||
margins.top, { // y coord
|
|
||||||
'width': margins.width, // max width of content on PDF
|
|
||||||
'elementHandlers': specialElementHandlers
|
|
||||||
},
|
|
||||||
|
|
||||||
function (dispose) {
|
|
||||||
// dispose: object with X, Y of the last line add to the PDF
|
|
||||||
// this allow the insertion of new lines after html
|
|
||||||
pdf.save('invoice.pdf');
|
|
||||||
}, margins);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
21
yuyu/project/invoice/templates/invoice/invoice.html
Normal file
21
yuyu/project/invoice/templates/invoice/invoice.html
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% block title %}{{ page_title }}{% endblock %}
|
||||||
|
{% block main %}
|
||||||
|
<a class="btn btn-default" href="?print=true" target="_blank">Download PDF</a>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
{% include 'admin/projects_invoice/base_invoice.html' %}
|
||||||
|
{% endblock %}
|
||||||
|
{% block js %}
|
||||||
|
{{ block.super }}
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"
|
||||||
|
integrity="sha512-GsLlZN/3F2ErC5ifS5QtgpiJtWd43JWSuIgh7mbzZ8zBps+dvLusV+eNQATqgA/HdeKFVgA5v3S/cIrLF7QnIg=="
|
||||||
|
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
function downloadPdf() {
|
||||||
|
window.print();
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
13
yuyu/project/invoice/templates/invoice/invoice_download.html
Normal file
13
yuyu/project/invoice/templates/invoice/invoice_download.html
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Invoice Download</title>
|
||||||
|
{% include "_stylesheets.html" %}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{% include 'project/invoice/base_invoice.html' %}
|
||||||
|
{% include "horizon/_scripts.html" %}
|
||||||
|
<script type="text/javascript">
|
||||||
|
window.print();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -17,5 +17,5 @@ from openstack_dashboard.dashboards.yuyu.project.invoice import views
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^$', views.IndexView.as_view(), name='index'),
|
url(r'^$', views.IndexView.as_view(), name='index'),
|
||||||
url(r'^invoice/pdf/(?P<id>[^/]+)/$', views.InvoiceView.as_view(), name='download_pdf'),
|
url(r'^invoice/detail/(?P<id>[^/]+)/$', views.InvoiceView.as_view(), name='invoice_detail'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -25,6 +25,7 @@ from horizon import views
|
||||||
from openstack_dashboard import api
|
from openstack_dashboard import api
|
||||||
from openstack_dashboard.dashboards.yuyu.cases.invoice_use_case import InvoiceUseCase
|
from openstack_dashboard.dashboards.yuyu.cases.invoice_use_case import InvoiceUseCase
|
||||||
from .tables import InvoiceTable
|
from .tables import InvoiceTable
|
||||||
|
from ...cases.setting_use_case import SettingUseCase
|
||||||
from ...core.utils.invoice_utils import state_to_text
|
from ...core.utils.invoice_utils import state_to_text
|
||||||
|
|
||||||
|
|
||||||
|
@ -55,15 +56,22 @@ class IndexView(tables.DataTableView):
|
||||||
|
|
||||||
class InvoiceView(views.APIView):
|
class InvoiceView(views.APIView):
|
||||||
page_title = _("Invoice")
|
page_title = _("Invoice")
|
||||||
template_name = "project/invoice/download_pdf.html"
|
|
||||||
|
|
||||||
invoice_uc = InvoiceUseCase()
|
invoice_uc = InvoiceUseCase()
|
||||||
|
setting_uc = SettingUseCase()
|
||||||
|
|
||||||
|
def get_template_names(self):
|
||||||
|
if self.request.GET.get('print', None):
|
||||||
|
return ['project/invoice/invoice_download.html']
|
||||||
|
else:
|
||||||
|
return ['project/invoice/invoice.html']
|
||||||
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
invoice = self.invoice_uc.get_invoice(self.request, self.kwargs['id'])
|
invoice = self.invoice_uc.get_invoice(self.request, self.kwargs['id'])
|
||||||
context['invoice'] = invoice
|
context['invoice'] = invoice
|
||||||
|
context['setting'] = self.setting_uc.get_settings(self.request)
|
||||||
context['instance_cost'] = self.get_sum_price(invoice, 'instances')
|
context['instance_cost'] = self.get_sum_price(invoice, 'instances')
|
||||||
context['volume_cost'] = self.get_sum_price(invoice, 'volumes')
|
context['volume_cost'] = self.get_sum_price(invoice, 'volumes')
|
||||||
context['fip_cost'] = self.get_sum_price(invoice, 'floating_ips')
|
context['fip_cost'] = self.get_sum_price(invoice, 'floating_ips')
|
||||||
|
|
Loading…
Add table
Reference in a new issue