usage cost page using tab styling (#1)
* usage cost page using tab styling * refactor admin usage cost page
This commit is contained in:
parent
c5e8b01793
commit
15a794fe97
7 changed files with 479 additions and 4 deletions
|
@ -0,0 +1,114 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% block title %}{% trans "Usage Cost" %}{% endblock %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
{% include "admin/price_configuration/missing_prices.html" %}
|
||||||
|
{% if invoice %}
|
||||||
|
|
||||||
|
<div id="usage_cost">
|
||||||
|
<div class="row" style="margin-bottom: 15px;">
|
||||||
|
<div class="col-md-6">
|
||||||
|
Invoice Month <b>{{ invoice.start_date|date:"M Y" }}</b>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<span class="pull-right" data-html2canvas-ignore="true">
|
||||||
|
{% include 'admin/projects_invoice/manage_invoice_modal.html' with invoice=invoice project_balance_amount=project_balance_amount %}
|
||||||
|
<button onclick="javascript:downloadPdf();" class="btn btn-default">Download PDF</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<h3 class="panel-title">Invoice State</h3>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<h2> {{ invoice.state_text }} </h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<h3 class="panel-title">Subtotal</h3>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<h2>{{ invoice.subtotal_money }}</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if invoice.state != 1 %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<h3 class="panel-title">Tax</h3>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<h2> {{ invoice.tax_money }} </h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<h3 class="panel-title">Total</h3>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<h2>{{ invoice.total_money }}</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12">
|
||||||
|
{{ tab_group.render }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<h1>Billing not enabled or you don't have any usage yet</h1> <br/>
|
||||||
|
{% endif %}
|
||||||
|
{% 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() {
|
||||||
|
let opt = {
|
||||||
|
filename: 'usage_cost.pdf',
|
||||||
|
margin: [16, 16],
|
||||||
|
enableLinks: true,
|
||||||
|
image: {type: 'jpeg', quality: 0.98},
|
||||||
|
pagebreak: { mode: 'avoid-all', },
|
||||||
|
jsPDF: {unit: 'mm', format: 'a4', orientation: 'portrait'},
|
||||||
|
}
|
||||||
|
|
||||||
|
// clone element to modify
|
||||||
|
const ucElement = document.getElementById('usage_cost');
|
||||||
|
const ucElementClone = ucElement.cloneNode(true);
|
||||||
|
|
||||||
|
// modify element
|
||||||
|
const navTabs = ucElementClone.querySelector('.nav-tabs');
|
||||||
|
navTabs.style.display = 'none';
|
||||||
|
const tabContent = ucElementClone.querySelectorAll('.tab-pane');
|
||||||
|
tabContent.forEach(content => {
|
||||||
|
content.classList.remove("tab-pane");
|
||||||
|
});
|
||||||
|
|
||||||
|
html2pdf().set(opt).from(ucElementClone).save();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
|
@ -14,10 +14,11 @@ from django.conf.urls import url
|
||||||
|
|
||||||
from openstack_dashboard.dashboards.yuyu.admin.projects_invoice import views
|
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/detail/(?P<project_id>[^/]+)/(?P<id>[^/]+)/$', views.InvoiceView.as_view(), name='invoice_detail'),
|
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.UsageCostTabView.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(),
|
||||||
name='rollback_to_unpaid'),
|
name='rollback_to_unpaid'),
|
||||||
|
|
|
@ -24,6 +24,8 @@ from djmoney.money import Money
|
||||||
from horizon import exceptions
|
from horizon import exceptions
|
||||||
from horizon import tables
|
from horizon import tables
|
||||||
from horizon import views
|
from horizon import views
|
||||||
|
from horizon import tabs
|
||||||
|
|
||||||
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
|
||||||
|
@ -31,6 +33,7 @@ from ...cases.balance_use_case import BalanceUseCase
|
||||||
from ...cases.setting_use_case import SettingUseCase
|
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.usage_cost.tabs import UsageCostTabs
|
||||||
from ...core.utils.invoice_utils import state_to_text
|
from ...core.utils.invoice_utils import state_to_text
|
||||||
|
|
||||||
|
|
||||||
|
@ -106,6 +109,31 @@ class InvoiceView(views.APIView):
|
||||||
return sum(instance_prices)
|
return sum(instance_prices)
|
||||||
|
|
||||||
|
|
||||||
|
class UsageCostTabView(tabs.TabbedTableView):
|
||||||
|
tab_group_class = UsageCostTabs
|
||||||
|
page_title = _("Usage Cost")
|
||||||
|
template_name = "admin/projects_invoice/cost_tabs.html"
|
||||||
|
|
||||||
|
invoice_uc = InvoiceUseCase()
|
||||||
|
balance_uc = BalanceUseCase()
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
request.invoice = self.invoice_uc.get_invoice(self.request, self.kwargs['id'],
|
||||||
|
tenant_id=self.kwargs['project_id'])
|
||||||
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
balance = self.balance_uc.retrieve_by_project(self.request, self.kwargs['project_id'])
|
||||||
|
context['invoice'] = self.request.invoice
|
||||||
|
context['project_balance_amount'] = Money(amount=balance['amount'],
|
||||||
|
currency=balance['amount_currency']) if balance else 0
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
class UsageCostView(tables.MultiTableView):
|
class UsageCostView(tables.MultiTableView):
|
||||||
table_classes = (
|
table_classes = (
|
||||||
InstanceCostTable, VolumeCostTable, FloatingIpCostTable, RouterCostTable, SnapshotCostTable, ImageCostTable)
|
InstanceCostTable, VolumeCostTable, FloatingIpCostTable, RouterCostTable, SnapshotCostTable, ImageCostTable)
|
||||||
|
|
181
yuyu/core/usage_cost/tabs.py
Normal file
181
yuyu/core/usage_cost/tabs.py
Normal file
|
@ -0,0 +1,181 @@
|
||||||
|
import dateutil.parser
|
||||||
|
from django.utils.timesince import timesince
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from djmoney.money import Money
|
||||||
|
|
||||||
|
from horizon import exceptions, tabs
|
||||||
|
from openstack_dashboard import api
|
||||||
|
from openstack_dashboard.dashboards.yuyu.core.usage_cost.tables import (
|
||||||
|
InstanceCostTable, VolumeCostTable, FloatingIpCostTable, RouterCostTable, SnapshotCostTable, ImageCostTable
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class InstanceTab(tabs.TableTab):
|
||||||
|
table_classes = (InstanceCostTable,)
|
||||||
|
name = _("Instance")
|
||||||
|
slug = "instance"
|
||||||
|
template_name = 'horizon/common/_detail_table.html'
|
||||||
|
|
||||||
|
def _get_flavor_name(self, flavor_id):
|
||||||
|
try:
|
||||||
|
return api.nova.flavor_get(self.request, flavor_id).name
|
||||||
|
except Exception:
|
||||||
|
return 'Invalid Flavor'
|
||||||
|
|
||||||
|
def get_instance_cost_data(self):
|
||||||
|
try:
|
||||||
|
datas = map(lambda x: {
|
||||||
|
"id": x['id'],
|
||||||
|
"name": x['name'],
|
||||||
|
"flavor": self._get_flavor_name(x['flavor_id']),
|
||||||
|
"usage": timesince(
|
||||||
|
dateutil.parser.isoparse(x['start_date']),
|
||||||
|
dateutil.parser.isoparse(x['adjusted_end_date'])
|
||||||
|
),
|
||||||
|
"cost": Money(amount=x['price_charged'], currency=x['price_charged_currency'])
|
||||||
|
}, self.request.invoice.get('instances', []))
|
||||||
|
return datas
|
||||||
|
except Exception:
|
||||||
|
error_message = _('Unable to get instance cost')
|
||||||
|
exceptions.handle(self.request, error_message)
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
class VolumeTab(tabs.TableTab):
|
||||||
|
table_classes = (VolumeCostTable,)
|
||||||
|
name = _("Volume")
|
||||||
|
slug = "volume"
|
||||||
|
template_name = 'horizon/common/_detail_table.html'
|
||||||
|
|
||||||
|
def _get_volume_name(self, volume_type_id):
|
||||||
|
try:
|
||||||
|
return api.cinder.volume_type_get(self.request, volume_type_id).name
|
||||||
|
except Exception:
|
||||||
|
return 'Invalid Volume'
|
||||||
|
|
||||||
|
def get_volume_cost_data(self):
|
||||||
|
try:
|
||||||
|
datas = map(lambda x: {
|
||||||
|
"id": x['id'],
|
||||||
|
"name": x['volume_name'],
|
||||||
|
"usage": timesince(
|
||||||
|
dateutil.parser.isoparse(x['start_date']),
|
||||||
|
dateutil.parser.isoparse(x['adjusted_end_date'])
|
||||||
|
),
|
||||||
|
'type': self._get_volume_name(x['volume_type_id']),
|
||||||
|
'size': x['space_allocation_gb'],
|
||||||
|
"cost": Money(amount=x['price_charged'], currency=x['price_charged_currency'])
|
||||||
|
}, self.request.invoice.get('volumes', []))
|
||||||
|
return datas
|
||||||
|
except Exception:
|
||||||
|
error_message = _('Unable to get volume cost')
|
||||||
|
exceptions.handle(self.request, error_message)
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
class FloatingIpTab(tabs.TableTab):
|
||||||
|
table_classes = (FloatingIpCostTable,)
|
||||||
|
name = _("Floating IP")
|
||||||
|
slug = "floating_ip"
|
||||||
|
template_name = 'horizon/common/_detail_table.html'
|
||||||
|
|
||||||
|
def get_floating_ip_cost_data(self):
|
||||||
|
try:
|
||||||
|
datas = map(lambda x: {
|
||||||
|
"id": x['id'],
|
||||||
|
"name": x['ip'],
|
||||||
|
"usage": timesince(
|
||||||
|
dateutil.parser.isoparse(x['start_date']),
|
||||||
|
dateutil.parser.isoparse(x['adjusted_end_date'])
|
||||||
|
),
|
||||||
|
"cost": Money(amount=x['price_charged'], currency=x['price_charged_currency'])
|
||||||
|
}, self.request.invoice.get('floating_ips', []))
|
||||||
|
|
||||||
|
return datas
|
||||||
|
except Exception:
|
||||||
|
error_message = _('Unable to get floating ip cost')
|
||||||
|
exceptions.handle(self.request, error_message)
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
class RouterTab(tabs.TableTab):
|
||||||
|
table_classes = (RouterCostTable,)
|
||||||
|
name = _("Router")
|
||||||
|
slug = "router"
|
||||||
|
template_name = 'horizon/common/_detail_table.html'
|
||||||
|
|
||||||
|
def get_router_cost_data(self):
|
||||||
|
try:
|
||||||
|
datas = map(lambda x: {
|
||||||
|
"id": x['id'],
|
||||||
|
"name": x['name'],
|
||||||
|
"usage": timesince(
|
||||||
|
dateutil.parser.isoparse(x['start_date']),
|
||||||
|
dateutil.parser.isoparse(x['adjusted_end_date'])
|
||||||
|
),
|
||||||
|
"cost": Money(amount=x['price_charged'], currency=x['price_charged_currency'])
|
||||||
|
}, self.request.invoice.get('routers', []))
|
||||||
|
|
||||||
|
return datas
|
||||||
|
except Exception:
|
||||||
|
error_message = _('Unable to get router cost')
|
||||||
|
exceptions.handle(self.request, error_message)
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
class SnapshotTab(tabs.TableTab):
|
||||||
|
table_classes = (SnapshotCostTable,)
|
||||||
|
name = _("Snapshot")
|
||||||
|
slug = "snapshot"
|
||||||
|
template_name = 'horizon/common/_detail_table.html'
|
||||||
|
|
||||||
|
def get_snapshot_cost_data(self):
|
||||||
|
try:
|
||||||
|
datas = map(lambda x: {
|
||||||
|
"id": x['id'],
|
||||||
|
"name": x['name'],
|
||||||
|
'size': x['space_allocation_gb'],
|
||||||
|
"usage": timesince(
|
||||||
|
dateutil.parser.isoparse(x['start_date']),
|
||||||
|
dateutil.parser.isoparse(x['adjusted_end_date'])
|
||||||
|
),
|
||||||
|
"cost": Money(amount=x['price_charged'], currency=x['price_charged_currency'])
|
||||||
|
}, self.request.invoice.get('snapshots', []))
|
||||||
|
|
||||||
|
return datas
|
||||||
|
except Exception:
|
||||||
|
error_message = _('Unable to get snapshot cost')
|
||||||
|
exceptions.handle(self.request, error_message)
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
class ImageTab(tabs.TableTab):
|
||||||
|
table_classes = (ImageCostTable,)
|
||||||
|
name = _("Image")
|
||||||
|
slug = "image"
|
||||||
|
template_name = 'horizon/common/_detail_table.html'
|
||||||
|
|
||||||
|
def get_image_cost_data(self):
|
||||||
|
try:
|
||||||
|
datas = map(lambda x: {
|
||||||
|
"id": x['id'],
|
||||||
|
"name": x['name'],
|
||||||
|
'size': x['space_allocation_gb'],
|
||||||
|
"usage": timesince(
|
||||||
|
dateutil.parser.isoparse(x['start_date']),
|
||||||
|
dateutil.parser.isoparse(x['adjusted_end_date'])
|
||||||
|
),
|
||||||
|
"cost": Money(amount=x['price_charged'], currency=x['price_charged_currency'])
|
||||||
|
}, self.request.invoice.get('images', []))
|
||||||
|
|
||||||
|
return datas
|
||||||
|
except Exception:
|
||||||
|
error_message = _('Unable to get images cost')
|
||||||
|
exceptions.handle(self.request, error_message)
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
class UsageCostTabs(tabs.TabGroup):
|
||||||
|
slug = "usage_cost"
|
||||||
|
tabs = (InstanceTab, VolumeTab, FloatingIpTab, RouterTab, SnapshotTab, ImageTab, )
|
||||||
|
sticky = True
|
122
yuyu/project/usage_cost/templates/usage_cost/index_tabs.html
Normal file
122
yuyu/project/usage_cost/templates/usage_cost/index_tabs.html
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% block title %}{% trans "Usage Cost" %}{% endblock %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
{% include "admin/price_configuration/missing_prices.html" %}
|
||||||
|
{% if invoice %}
|
||||||
|
<div id="usage_cost">
|
||||||
|
<div class="row" style="margin-bottom: 15px;">
|
||||||
|
<div class="col-md-6">
|
||||||
|
|
||||||
|
Invoice Month
|
||||||
|
<select id="invoice_select" onchange="onInvoiceChange(this.value)">
|
||||||
|
{% for i in invoice_list %}
|
||||||
|
<option value="{{ i.id }}" {% if i.id == invoice.id %}
|
||||||
|
selected {% endif %}>{{ i.start_date|date:"M Y" }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<button onclick="javascript:downloadPdf();" class="btn btn-default pull-right" data-html2canvas-ignore="true">Download PDF</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<h3 class="panel-title">Invoice State</h3>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<h2> {{ invoice.state_text }} </h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<h3 class="panel-title">Subtotal</h3>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<h2>{{ invoice.subtotal_money }}</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if invoice.state != 1 %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<h3 class="panel-title">Tax</h3>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<h2> {{ invoice.tax_money }} </h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<h3 class="panel-title">Total</h3>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<h2>{{ invoice.total_money }}</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12">
|
||||||
|
{{ tab_group.render }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<h1>You don't have any usage yet</h1> <br/>
|
||||||
|
{% endif %}
|
||||||
|
{% 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 onInvoiceChange(val) {
|
||||||
|
var search = "?invoice_id=" + val;
|
||||||
|
window.location.href = window.location.protocol + "//" + window.location.host + window.location.pathname + search;
|
||||||
|
}
|
||||||
|
|
||||||
|
function downloadPdf() {
|
||||||
|
let opt = {
|
||||||
|
filename: 'usage_cost.pdf',
|
||||||
|
margin: [16, 16],
|
||||||
|
enableLinks: true,
|
||||||
|
image: {type: 'jpeg', quality: 0.98},
|
||||||
|
pagebreak: { mode: 'avoid-all', },
|
||||||
|
jsPDF: {unit: 'mm', format: 'a4', orientation: 'portrait'},
|
||||||
|
}
|
||||||
|
|
||||||
|
// clone element to modify
|
||||||
|
const ucElement = document.getElementById('usage_cost');
|
||||||
|
const ucElementClone = ucElement.cloneNode(true);
|
||||||
|
|
||||||
|
// modify element
|
||||||
|
const navTabs = ucElementClone.querySelector('.nav-tabs');
|
||||||
|
navTabs.style.display = 'none';
|
||||||
|
const tabContent = ucElementClone.querySelectorAll('.tab-pane');
|
||||||
|
tabContent.forEach(content => {
|
||||||
|
content.classList.remove("tab-pane");
|
||||||
|
});
|
||||||
|
|
||||||
|
html2pdf().set(opt).from(ucElementClone).save();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
|
@ -16,5 +16,5 @@ from openstack_dashboard.dashboards.yuyu.project.usage_cost import views
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^$', views.IndexView.as_view(), name='index'),
|
url(r'^$', views.IndexViewTab.as_view(), name='index'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -14,12 +14,41 @@ from django.utils.timesince import timesince
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from djmoney.money import Money
|
from djmoney.money import Money
|
||||||
|
|
||||||
from horizon import exceptions
|
from horizon import exceptions, tables, tabs
|
||||||
from horizon import tables
|
|
||||||
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 openstack_dashboard.dashboards.yuyu.core.usage_cost.tables import InstanceCostTable, VolumeCostTable, \
|
from openstack_dashboard.dashboards.yuyu.core.usage_cost.tables import InstanceCostTable, VolumeCostTable, \
|
||||||
FloatingIpCostTable, RouterCostTable, SnapshotCostTable, ImageCostTable
|
FloatingIpCostTable, RouterCostTable, SnapshotCostTable, ImageCostTable
|
||||||
|
from openstack_dashboard.dashboards.yuyu.core.usage_cost.tabs import UsageCostTabs
|
||||||
|
|
||||||
|
|
||||||
|
class IndexViewTab(tabs.TabbedTableView):
|
||||||
|
|
||||||
|
tab_group_class = UsageCostTabs
|
||||||
|
page_title = _("Usage Cost")
|
||||||
|
template_name = "project/usage_cost/index_tabs.html"
|
||||||
|
|
||||||
|
invoice_uc = InvoiceUseCase()
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
request.invoice_list = self.invoice_uc.get_simple_list(self.request)
|
||||||
|
request.has_invoice = len(request.invoice_list) != 0
|
||||||
|
|
||||||
|
invoice_id = self.request.GET.get('invoice_id', None)
|
||||||
|
if not invoice_id and request.has_invoice:
|
||||||
|
invoice_id = request.invoice_list[0]['id']
|
||||||
|
|
||||||
|
request.invoice = self.invoice_uc.get_invoice(self.request, invoice_id) if request.has_invoice else {}
|
||||||
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context['invoice_list'] = self.request.invoice_list
|
||||||
|
context['invoice'] = self.request.invoice
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
class IndexView(tables.MultiTableView):
|
class IndexView(tables.MultiTableView):
|
||||||
|
|
Loading…
Add table
Reference in a new issue