Compare commits
6 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
8ab7beb0be | ||
![]() |
4448b6b28f | ||
![]() |
fabe9094a2 | ||
![]() |
3b50557a6a | ||
![]() |
8799790cfd | ||
![]() |
fb2455247a |
92 changed files with 2380 additions and 317 deletions
19
remove_yuyu_demo.sh
Executable file
19
remove_yuyu_demo.sh
Executable file
|
@ -0,0 +1,19 @@
|
|||
#!/bin/bash
|
||||
|
||||
echo "Specify Horizon Location (ex: /etc/horizon): "
|
||||
read horizon_path
|
||||
root=`pwd -P`
|
||||
|
||||
echo "Removing yuyu demo Symlink"
|
||||
rm $horizon_path/openstack_dashboard/dashboards/yuyu_demo
|
||||
rm $horizon_path/openstack_dashboard/local/enabled/_6600_yuyu.py
|
||||
rm $horizon_path/openstack_dashboard/local/enabled/_6601_admin_billing_panel_group.py
|
||||
rm $horizon_path/openstack_dashboard/local/enabled/_6602_admin_billing_overview.py
|
||||
rm $horizon_path/openstack_dashboard/local/enabled/_6603_admin_billing_price_configuration.py
|
||||
rm $horizon_path/openstack_dashboard/local/enabled/_6604_admin_billing_setting.py
|
||||
rm $horizon_path/openstack_dashboard/local/enabled/_6604_admin_billing_setting.py
|
||||
rm $horizon_path/openstack_dashboard/local/enabled/_6605_admin_billing_projects_invoice.py
|
||||
rm $horizon_path/openstack_dashboard/local/enabled/_6606_admin_notification_center.py
|
||||
rm $horizon_path/openstack_dashboard/local/enabled/_6607_admin_billing_projects_balance.py
|
||||
|
||||
echo "Yuyu Demo Removal Done"
|
20
setup_yuyu_demo.sh
Executable file
20
setup_yuyu_demo.sh
Executable file
|
@ -0,0 +1,20 @@
|
|||
#!/bin/bash
|
||||
|
||||
echo "Specify Horizon Location (ex: /etc/horizon): "
|
||||
read horizon_path
|
||||
root=`pwd -P`
|
||||
|
||||
echo "Creating Symlink"
|
||||
ln -sf $root/yuyu_demo $horizon_path/openstack_dashboard/dashboards
|
||||
ln -sf $root/yuyu_demo/local/enabled/_6600_yuyu_demo.py $horizon_path/openstack_dashboard/local/enabled/_6600_yuyu_demo.py
|
||||
ln -sf $root/yuyu_demo/local/enabled/_6601_admin_billing_panel_group.py $horizon_path/openstack_dashboard/local/enabled/_6601_admin_billing_panel_group.py
|
||||
ln -sf $root/yuyu_demo/local/enabled/_6602_admin_billing_overview.py $horizon_path/openstack_dashboard/local/enabled/_6602_admin_billing_overview.py
|
||||
ln -sf $root/yuyu_demo/local/enabled/_6603_admin_billing_price_configuration.py $horizon_path/openstack_dashboard/local/enabled/_6603_admin_billing_price_configuration.py
|
||||
ln -sf $root/yuyu_demo/local/enabled/_6604_admin_billing_setting.py $horizon_path/openstack_dashboard/local/enabled/_6604_admin_billing_setting.py
|
||||
ln -sf $root/yuyu_demo/local/enabled/_6604_admin_billing_setting.py $horizon_path/openstack_dashboard/local/enabled/_6604_admin_billing_setting.py
|
||||
ln -sf $root/yuyu_demo/local/enabled/_6605_admin_billing_projects_invoice.py $horizon_path/openstack_dashboard/local/enabled/_6605_admin_billing_projects_invoice.py
|
||||
ln -sf $root/yuyu_demo/local/enabled/_6606_admin_notification_center.py $horizon_path/openstack_dashboard/local/enabled/_6605_admin_notification_center.py
|
||||
ln -sf $root/yuyu_demo/local/enabled/_6607_admin_billing_projects_balance.py $horizon_path/openstack_dashboard/local/enabled/_6607_admin_billing_projects_balance.py
|
||||
|
||||
echo "Symlink Creation Done"
|
||||
echo "Now you can configure and use yuyu dashboard"
|
|
@ -28,8 +28,7 @@
|
|||
<div class="panel-body">
|
||||
<p>Please make sure all price is already configured before enable billing</p>
|
||||
<a href="{% url 'horizon:admin:billing_setting:enable_billing' %}" class="btn btn-primary {% if missing_price.has_missing or missing_setting.has_missing %} disabled {% endif %}">Enable</a>
|
||||
<a href="{% url 'horizon:admin:billing_setting:reset_billing' %}" class="btn btn-danger">Reset All Billing Data</a>
|
||||
<a href="{% url 'horizon:admin:billing_setting:reset_transaction' %}" class="btn btn-danger">Reset Transaction Data</a>
|
||||
<a href="{% url 'horizon:admin:billing_setting:reset_billing' %}" class="btn btn-danger">Reset Billing Data</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
|
|
@ -19,7 +19,6 @@ urlpatterns = [
|
|||
url(r'^enable_billing$', views.EnableBillingView.as_view(), name='enable_billing'),
|
||||
url(r'^disable_billing$', views.DisableBillingView.as_view(), name='disable_billing'),
|
||||
url(r'^reset_billing$', views.ResetBillingView.as_view(), name='reset_billing'),
|
||||
url(r'^reset_transaction$', views.ResetTransactionView.as_view(), name='reset_transaction'),
|
||||
url(r'^update_setting/$',
|
||||
views.UpdateSettingView.as_view(), name='update_setting'),
|
||||
]
|
||||
|
|
|
@ -111,15 +111,3 @@ class ResetBillingView(views.APIView):
|
|||
exceptions.handle(self.request,
|
||||
_("Unable to reset billing"))
|
||||
return shortcuts.redirect("horizon:admin:billing_setting:index")
|
||||
|
||||
class ResetTransactionView(views.APIView):
|
||||
invoice_uc = InvoiceUseCase()
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
try:
|
||||
self.invoice_uc.reset_transaction(request)
|
||||
messages.success(request, _("Data successfully Reset."))
|
||||
except Exception:
|
||||
exceptions.handle(self.request,
|
||||
_("Unable to reset transaction"))
|
||||
return shortcuts.redirect("horizon:admin:billing_setting:index")
|
|
@ -41,7 +41,7 @@ class IndexView(tables.DataTableView):
|
|||
if hasattr(self, "table"):
|
||||
context[self.context_object_name] = self.table
|
||||
|
||||
context['select_list'], _ = api.keystone.tenant_list(self.request)
|
||||
context['select_list'], _ = api.keystone.tenant_list(self.request, user=self.request.user.id)
|
||||
context['current_tenant_id'] = self.request.GET.get('tenant_id', None)
|
||||
|
||||
return context
|
||||
|
@ -67,7 +67,6 @@ class ReadAllView(views.APIView):
|
|||
try:
|
||||
notifications = self.notification_uc.get_list(self.request,
|
||||
filter_selection=self.kwargs['selection'])
|
||||
|
||||
for n in notifications:
|
||||
if not n["is_read"]:
|
||||
self.notification_uc.set_read(request, n['id'])
|
||||
|
|
|
@ -34,7 +34,7 @@ class IndexView(tables.DataTableView):
|
|||
def get_data(self):
|
||||
try:
|
||||
data = []
|
||||
project_list, has_more = api.keystone.tenant_list(self.request)
|
||||
project_list, has_more = api.keystone.tenant_list(self.request, user=self.request.user.id)
|
||||
for d in self.balance_uc.list(self.request):
|
||||
project = list(filter(lambda x: x.id == d['project']['tenant_id'], project_list))
|
||||
project_name = 'Unknown Project'
|
||||
|
|
|
@ -1,139 +0,0 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Invoice Download</title>
|
||||
{% include '_stylesheets.html' %}
|
||||
</head>
|
||||
<body>
|
||||
<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">{{ setting.company_logo }}</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 }}
|
||||
<br />
|
||||
<br />
|
||||
<strong>Subtotal</strong><br />
|
||||
{{ invoice.subtotal_money }}
|
||||
<br />
|
||||
<br />
|
||||
{% if invoice.state != 1 %}
|
||||
<strong>Tax</strong><br />
|
||||
{{ invoice.tax_money }}
|
||||
<br />
|
||||
<br />
|
||||
<strong>Total</strong><br />
|
||||
{{ invoice.total_money }}
|
||||
<br />
|
||||
<br />
|
||||
{% endif %}
|
||||
</address>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h3>ORDER SUMMARY</h3>
|
||||
<br />
|
||||
<br />
|
||||
<div id="usage_cost">
|
||||
<div id="instance-cost">{{ instance_cost_table.render }}</div>
|
||||
|
||||
<div id="volume-cost">{{ volume_cost_table.render }}</div>
|
||||
<div id="floating-ip-cost">{{ floating_ip_cost_table.render }}</div>
|
||||
<div id="router-cost">{{ router_cost_table.render }}</div>
|
||||
<div id="snapshot-cost">{{ snapshot_cost_table.render }}</div>
|
||||
<div id="image-cost">{{ image_cost_table.render }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- END INVOICE -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% include 'horizon/_scripts.html' %}
|
||||
<script type="text/javascript">
|
||||
window.print()
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -14,7 +14,7 @@
|
|||
<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 %}
|
||||
<a href="{% url "horizon:admin:projects_invoice:usage_cost_print" project_id invoice.id %}" target="_blank" class="btn btn-default">Download PDF</a>
|
||||
<button onclick="javascript:downloadPdf();" class="btn btn-default">Download PDF</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -79,3 +79,36 @@
|
|||
{% 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 %}
|
||||
|
|
|
@ -7,4 +7,17 @@
|
|||
<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 %}
|
|
@ -19,7 +19,6 @@ urlpatterns = [
|
|||
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/usage/(?P<project_id>[^/]+)/(?P<id>[^/]+)/$', views.UsageCostTabView.as_view(), name='usage_cost'),
|
||||
url(r'^invoice/usage/print/(?P<project_id>[^/]+)/(?P<id>[^/]+)/$', views.UsageCostView.as_view(), name='usage_cost_print'),
|
||||
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(),
|
||||
name='rollback_to_unpaid'),
|
||||
|
|
|
@ -46,7 +46,7 @@ class IndexView(tables.DataTableView):
|
|||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['project_list'], _ = api.keystone.tenant_list(self.request)
|
||||
context['project_list'], _ = api.keystone.tenant_list(self.request, user=self.request.user.id)
|
||||
context['current_project_id'] = self.request.GET.get('project_id', self.request.user.project_id)
|
||||
context['current_project_name'] = self.request.GET.get('project_name', self.request.user.project_id)
|
||||
return context
|
||||
|
@ -129,7 +129,6 @@ class UsageCostTabView(tabs.TabbedTableView):
|
|||
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_id'] = self.kwargs['project_id']
|
||||
context['project_balance_amount'] = Money(amount=balance['amount'],
|
||||
currency=balance['amount_currency']) if balance else 0
|
||||
return context
|
||||
|
@ -139,12 +138,10 @@ class UsageCostView(tables.MultiTableView):
|
|||
table_classes = (
|
||||
InstanceCostTable, VolumeCostTable, FloatingIpCostTable, RouterCostTable, SnapshotCostTable, ImageCostTable)
|
||||
page_title = _("Usage Cost")
|
||||
template_name = "admin/projects_invoice/cost_download.html"
|
||||
template_name = "admin/projects_invoice/cost_tables.html"
|
||||
|
||||
invoice_uc = InvoiceUseCase()
|
||||
balance_uc = BalanceUseCase()
|
||||
setting_uc = SettingUseCase()
|
||||
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
@ -157,7 +154,6 @@ class UsageCostView(tables.MultiTableView):
|
|||
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['setting'] = self.setting_uc.get_settings(self.request)
|
||||
context['invoice'] = self.request.invoice
|
||||
context['project_balance_amount'] = Money(amount=balance['amount'],
|
||||
currency=balance['amount_currency']) if balance else 0
|
||||
|
|
|
@ -109,9 +109,6 @@ class InvoiceUseCase:
|
|||
|
||||
def reset_billing(self, request):
|
||||
yuyu_client.post(request, f"invoice/reset_billing/", {})
|
||||
|
||||
def reset_transaction(self, request):
|
||||
yuyu_client.post(request, f"invoice/reset_transaction_data/", {})
|
||||
|
||||
def finish_invoice(self, request, id, skip_balance):
|
||||
response = yuyu_client.get(request, f"invoice/{id}/finish/?skip_balance={skip_balance}")
|
||||
|
|
|
@ -3,7 +3,7 @@ from openstack_dashboard.dashboards.yuyu.core import yuyu_client
|
|||
|
||||
class NotificationCenterUseCase:
|
||||
def get_list(self, request, filter_selection=None):
|
||||
if filter_selection is None or filter_selection == 'None':
|
||||
if filter_selection is None:
|
||||
return yuyu_client.get(request, f"notification/").json()
|
||||
|
||||
return yuyu_client.get(request, f"notification/?tenant_id={filter_selection}").json()
|
||||
|
|
|
@ -1,139 +0,0 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Invoice Download</title>
|
||||
{% include '_stylesheets.html' %}
|
||||
</head>
|
||||
<body>
|
||||
<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">{{ setting.company_logo }}</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 }}
|
||||
<br />
|
||||
<br />
|
||||
<strong>Subtotal</strong><br />
|
||||
{{ invoice.subtotal_money }}
|
||||
<br />
|
||||
<br />
|
||||
{% if invoice.state != 1 %}
|
||||
<strong>Tax</strong><br />
|
||||
{{ invoice.tax_money }}
|
||||
<br />
|
||||
<br />
|
||||
<strong>Total</strong><br />
|
||||
{{ invoice.total_money }}
|
||||
<br />
|
||||
<br />
|
||||
{% endif %}
|
||||
</address>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h3>ORDER SUMMARY</h3>
|
||||
<br />
|
||||
<br />
|
||||
<div id="usage_cost">
|
||||
<div id="instance-cost">{{ instance_cost_table.render }}</div>
|
||||
|
||||
<div id="volume-cost">{{ volume_cost_table.render }}</div>
|
||||
<div id="floating-ip-cost">{{ floating_ip_cost_table.render }}</div>
|
||||
<div id="router-cost">{{ router_cost_table.render }}</div>
|
||||
<div id="snapshot-cost">{{ snapshot_cost_table.render }}</div>
|
||||
<div id="image-cost">{{ image_cost_table.render }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- END INVOICE -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% include 'horizon/_scripts.html' %}
|
||||
<script type="text/javascript">
|
||||
window.print()
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -18,7 +18,7 @@
|
|||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<a href="{% url "horizon:project:usage_cost:download" %}?invoice_id={{ invoice.id }}" target="_blank" class="btn btn-default pull-right">Download PDF</a>
|
||||
<button onclick="javascript:downloadPdf();" class="btn btn-default pull-right" data-html2canvas-ignore="true">Download PDF</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -81,3 +81,42 @@
|
|||
<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 %}
|
||||
|
|
|
@ -17,5 +17,4 @@ from openstack_dashboard.dashboards.yuyu.project.usage_cost import views
|
|||
|
||||
urlpatterns = [
|
||||
url(r'^$', views.IndexViewTab.as_view(), name='index'),
|
||||
url(r'^download/$', views.DownloadView.as_view(), name='download'),
|
||||
]
|
||||
|
|
|
@ -17,7 +17,6 @@ from djmoney.money import Money
|
|||
from horizon import exceptions, tables, tabs
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard.dashboards.yuyu.cases.invoice_use_case import InvoiceUseCase
|
||||
from openstack_dashboard.dashboards.yuyu.cases.setting_use_case import SettingUseCase
|
||||
from openstack_dashboard.dashboards.yuyu.core.usage_cost.tables import InstanceCostTable, VolumeCostTable, \
|
||||
FloatingIpCostTable, RouterCostTable, SnapshotCostTable, ImageCostTable
|
||||
from openstack_dashboard.dashboards.yuyu.core.usage_cost.tabs import UsageCostTabs
|
||||
|
@ -52,14 +51,13 @@ class IndexViewTab(tabs.TabbedTableView):
|
|||
return context
|
||||
|
||||
|
||||
class DownloadView(tables.MultiTableView):
|
||||
class IndexView(tables.MultiTableView):
|
||||
table_classes = (
|
||||
InstanceCostTable, VolumeCostTable, FloatingIpCostTable, RouterCostTable, SnapshotCostTable, ImageCostTable)
|
||||
page_title = _("Usage Cost")
|
||||
template_name = "project/usage_cost/cost_download.html"
|
||||
template_name = "project/usage_cost/cost_tables.html"
|
||||
|
||||
invoice_uc = InvoiceUseCase()
|
||||
setting_uc = SettingUseCase()
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
@ -73,14 +71,12 @@ class DownloadView(tables.MultiTableView):
|
|||
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(DownloadView, self).get(request, *args, **kwargs)
|
||||
return super(IndexView, self).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
|
||||
context['setting'] = self.setting_uc.get_settings(self.request)
|
||||
|
||||
return context
|
||||
|
||||
def _get_flavor_name(self, flavor_id):
|
||||
|
|
0
yuyu_demo/__init__.py
Normal file
0
yuyu_demo/__init__.py
Normal file
0
yuyu_demo/admin/billing_overview_demo/__init__.py
Normal file
0
yuyu_demo/admin/billing_overview_demo/__init__.py
Normal file
21
yuyu_demo/admin/billing_overview_demo/panel.py
Normal file
21
yuyu_demo/admin/billing_overview_demo/panel.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
import horizon
|
||||
|
||||
|
||||
class BillingOverviewDemo(horizon.Panel):
|
||||
name = _("Billing Overview")
|
||||
slug = "billing_overview_demo"
|
||||
|
19
yuyu_demo/admin/billing_overview_demo/urls.py
Normal file
19
yuyu_demo/admin/billing_overview_demo/urls.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from django.conf.urls import url
|
||||
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', views.IndexView.as_view(), name='index'),
|
||||
]
|
17
yuyu_demo/admin/billing_overview_demo/views.py
Normal file
17
yuyu_demo/admin/billing_overview_demo/views.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
import json
|
||||
|
||||
from ....yuyu.admin.billing_overview import views
|
||||
|
||||
class IndexView(views.IndexView):
|
||||
pass
|
0
yuyu_demo/admin/billing_setting_demo/__init__.py
Normal file
0
yuyu_demo/admin/billing_setting_demo/__init__.py
Normal file
6
yuyu_demo/admin/billing_setting_demo/forms.py
Normal file
6
yuyu_demo/admin/billing_setting_demo/forms.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
from ....yuyu.admin.billing_setting import forms
|
||||
|
||||
|
||||
class SettingForm(forms.SettingForm):
|
||||
def handle(self, request, data):
|
||||
return
|
21
yuyu_demo/admin/billing_setting_demo/panel.py
Normal file
21
yuyu_demo/admin/billing_setting_demo/panel.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
import horizon
|
||||
|
||||
|
||||
class BillingSettingDemo(horizon.Panel):
|
||||
name = _("Billing Setting")
|
||||
slug = "billing_setting_demo"
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
{% block form_attrs %}enctype="multipart/form-data"{% endblock %}
|
||||
|
||||
{% block modal-footer %}
|
||||
{% if cancel_url %}
|
||||
<a href="{% block cancel_url %}{{ cancel_url }}{% endblock %}"
|
||||
class="btn btn-default cancel">
|
||||
{{ cancel_label }}
|
||||
</a>
|
||||
{% endif %}
|
||||
<input class="btn btn-primary" type="submit" disabled value="{{ submit_label }}">
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-body-right %}
|
||||
<h3>{% trans "Description:" %}</h3>
|
||||
<p>{% trans '<b>Company Name</b> : Your Company Name that will be included in invoice' %}</p>
|
||||
<p>{% trans '<b>Company Logo</b> : A logo that will be used in invoice' %}</p>
|
||||
<p>{% trans '<b>Email Admin</b> : Used to send a notification related invoice and error. You can put multiple email here separated by `,` (comma). Example: admin1@company.com,admin2@company.com' %}</p>
|
||||
<p>{% trans '<b>Invoice Tax</b> : Tax that will be calculated for each invoice' %}</p>
|
||||
<p>{% trans '<b>Invoice Auto Deduct Balance</b> : Automatically deduct project balance and finish invoice every end of the month if project balance is sufficient.' %}</p>
|
||||
<p>{% trans '<b>How To Top Up</b> : Instruction how to top up project balance that can be seen by project user' %}</p>
|
||||
{% endblock %}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans "Update Billing Setting" %}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% include "yuyu_demo/billing_setting_demo/_form_setting.html" %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,39 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Billing Setting" %}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% include "admin/price_configuration/missing_prices.html" %}
|
||||
{% include "admin/billing_setting/missing_billing_setting.html" %}
|
||||
|
||||
{# Form #}
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
{{ table.render }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
{% if setting.billing_enabled %}
|
||||
<div class="panel panel-success">
|
||||
<div class="panel-heading">Status: Billing Enabled</div>
|
||||
<div class="panel-body">
|
||||
<button class="btn btn-danger" data-toggle="modal" data-target="#modal_disable_billing">Disable Billing</button>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="panel panel-danger">
|
||||
<div class="panel-heading">Status: Billing Disabled</div>
|
||||
<div class="panel-body">
|
||||
<p>Please make sure all price is already configured before enable billing</p>
|
||||
<a href="{% url 'horizon:yuyu_demo:billing_setting_demo:enable_billing' %}" class="btn btn-primary {% if missing_price.has_missing or missing_setting.has_missing %} disabled {% endif %}">Enable</a>
|
||||
<a href="{% url 'horizon:yuyu_demo:billing_setting_demo:reset_billing' %}" class="btn btn-danger">Reset Billing Data</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% include "admin/billing_setting/modal_disable_billing.html" with modal_backdrop='static' hide=True %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,57 @@
|
|||
|
||||
{% if missing_setting.company_name %}
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="alert alert-danger alert-dismissible" role="alert">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
Company Name not complete
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if missing_setting.company_address %}
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="alert alert-danger alert-dismissible" role="alert">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
Company Address not complete
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if missing_setting.company_logo %}
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="alert alert-danger alert-dismissible" role="alert">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
Company Logo not complete
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% if missing_setting.invoice_tax %}
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="alert alert-danger alert-dismissible" role="alert">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
Tax not complete
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% if missing_setting.email_admin %}
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="alert alert-danger alert-dismissible" role="alert">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
Email Admin not complete
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
|
@ -0,0 +1,15 @@
|
|||
{% extends "horizon/common/_modal.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block modal_id %}modal_disable_billing{% endblock %}
|
||||
{% block modal_backdrop %}static{% endblock %}
|
||||
{% block modal-header %}{% trans "Confirmation" %}{% endblock %}
|
||||
|
||||
{% block modal-body %}
|
||||
<p>{% trans "Are you sure to disable billing?" %}</p>
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-footer %}
|
||||
<a href='#' class='btn btn-danger cancel' data-dismiss='modal'>{% trans "No" %}</a>
|
||||
<a href="{% url 'horizon:admin:billing_setting:disable_billing' %}" class="btn btn-primary">{% trans "Yes" %}</a>
|
||||
{% endblock %}
|
25
yuyu_demo/admin/billing_setting_demo/urls.py
Normal file
25
yuyu_demo/admin/billing_setting_demo/urls.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from django.conf.urls import url
|
||||
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', views.IndexView.as_view(), name='index'),
|
||||
url(r'^enable_billing$', views.EnableBillingView.as_view(), name='enable_billing'),
|
||||
url(r'^disable_billing$', views.DisableBillingView.as_view(), name='disable_billing'),
|
||||
url(r'^reset_billing$', views.ResetBillingView.as_view(), name='reset_billing'),
|
||||
url(r'^update_setting/$',
|
||||
views.UpdateSettingView.as_view(), name='update_setting'),
|
||||
]
|
||||
|
41
yuyu_demo/admin/billing_setting_demo/views.py
Normal file
41
yuyu_demo/admin/billing_setting_demo/views.py
Normal file
|
@ -0,0 +1,41 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from django import shortcuts
|
||||
|
||||
from ....yuyu.admin.billing_setting import views
|
||||
from . import forms
|
||||
|
||||
class IndexView(views.IndexView):
|
||||
pass
|
||||
|
||||
|
||||
class UpdateSettingView(views.UpdateSettingView):
|
||||
form_class = forms.SettingForm
|
||||
submit_url = ''
|
||||
success_url = ''
|
||||
template_name = 'yuyu_demo/billing_setting_demo/form_setting.html'
|
||||
|
||||
|
||||
|
||||
class EnableBillingView(views.EnableBillingView):
|
||||
def get(self, request, *args, **kwargs):
|
||||
return shortcuts.redirect("horizon:yuyu_demo:billing_setting_demo:index")
|
||||
|
||||
|
||||
class DisableBillingView(views.DisableBillingView):
|
||||
def get(self, request, *args, **kwargs):
|
||||
return shortcuts.redirect("horizon:yuyu_demo:billing_setting_demo:index")
|
||||
|
||||
|
||||
class ResetBillingView(views.ResetBillingView):
|
||||
def get(self, request, *args, **kwargs):
|
||||
return shortcuts.redirect("horizon:yuyu_demo:billing_setting_demo:index")
|
0
yuyu_demo/admin/notification_center_demo/__init__.py
Normal file
0
yuyu_demo/admin/notification_center_demo/__init__.py
Normal file
21
yuyu_demo/admin/notification_center_demo/panel.py
Normal file
21
yuyu_demo/admin/notification_center_demo/panel.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
import horizon
|
||||
|
||||
|
||||
class NotificationCenterDemo(horizon.Panel):
|
||||
name = _("Notification Center")
|
||||
slug = "notification_center_demo"
|
||||
|
27
yuyu_demo/admin/notification_center_demo/urls.py
Normal file
27
yuyu_demo/admin/notification_center_demo/urls.py
Normal file
|
@ -0,0 +1,27 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from django.conf.urls import url
|
||||
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', views.IndexView.as_view(), name='index'),
|
||||
url(r'^(?P<notification_id>[^/]+)/$',
|
||||
views.DetailView.as_view(),
|
||||
name='detail'),
|
||||
url(r'^resend/(?P<notification_id>[^/]+)/$',
|
||||
views.ResendView.as_view(),
|
||||
name='resend'),
|
||||
url(r'^read_all/(?P<selection>[^/]+)$', views.ReadAllView.as_view(), name='read_all'),
|
||||
]
|
||||
|
31
yuyu_demo/admin/notification_center_demo/views.py
Normal file
31
yuyu_demo/admin/notification_center_demo/views.py
Normal file
|
@ -0,0 +1,31 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from django import shortcuts
|
||||
from ....yuyu.admin.notification_center import views
|
||||
|
||||
|
||||
class IndexView(views.IndexView):
|
||||
pass
|
||||
|
||||
|
||||
class DetailView(views.DetailView):
|
||||
pass
|
||||
|
||||
class ReadAllView(views.ReadAllView):
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
return shortcuts.redirect("horizon:yuyu_demo:notification_center_demo:index")
|
||||
|
||||
|
||||
class ResendView(views.ResendView):
|
||||
def get(self, request, *args, **kwargs):
|
||||
return shortcuts.redirect("horizon:yuyu_demo:notification_center_demo:index")
|
0
yuyu_demo/admin/price_configuration_demo/__init__.py
Normal file
0
yuyu_demo/admin/price_configuration_demo/__init__.py
Normal file
34
yuyu_demo/admin/price_configuration_demo/forms.py
Normal file
34
yuyu_demo/admin/price_configuration_demo/forms.py
Normal file
|
@ -0,0 +1,34 @@
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import forms
|
||||
|
||||
from ....yuyu.admin.price_configuration import forms
|
||||
|
||||
class FlavorPriceForm(forms.FlavorPriceForm):
|
||||
def handle(self, request, data):
|
||||
return
|
||||
|
||||
|
||||
|
||||
class VolumePriceForm(forms.VolumePriceForm):
|
||||
def handle(self, request, data):
|
||||
return
|
||||
|
||||
|
||||
class FloatingIpPriceForm(forms.FloatingIpPriceForm):
|
||||
def handle(self, request, data):
|
||||
return
|
||||
|
||||
|
||||
class RouterPriceForm(forms.RouterPriceForm):
|
||||
def handle(self, request, data):
|
||||
return
|
||||
|
||||
|
||||
class SnapshotPriceForm(forms.SnapshotPriceForm):
|
||||
def handle(self, request, data):
|
||||
return
|
||||
|
||||
class ImagePriceForm(forms.SnapshotPriceForm):
|
||||
def handle(self, request, data):
|
||||
return
|
21
yuyu_demo/admin/price_configuration_demo/panel.py
Normal file
21
yuyu_demo/admin/price_configuration_demo/panel.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
import horizon
|
||||
|
||||
|
||||
class PriceConfigurationDemo(horizon.Panel):
|
||||
name = _("Price Configuration")
|
||||
slug = "price_configuration_demo"
|
||||
|
267
yuyu_demo/admin/price_configuration_demo/tables.py
Normal file
267
yuyu_demo/admin/price_configuration_demo/tables.py
Normal file
|
@ -0,0 +1,267 @@
|
|||
from django.utils.translation import ugettext_lazy as _, ungettext_lazy
|
||||
|
||||
from horizon import tables
|
||||
from ....yuyu.cases.flavor_price_use_case import FlavorPriceUseCase
|
||||
from ....yuyu.cases.floating_ip_price_use_case import FloatingIpPriceUseCase
|
||||
from ....yuyu.cases.volume_price_use_case import VolumePriceUseCase
|
||||
from ....yuyu.cases.router_price_use_case import RouterPriceUseCase
|
||||
from ....yuyu.cases.snapshot_price_use_case import SnapshotPriceUseCase
|
||||
from ....yuyu.cases.image_price_use_case import ImagePriceUseCase
|
||||
from ....yuyu.core.pricing_admin.tables import BasePriceTable, BaseCreatePrice, \
|
||||
BaseEditPrice
|
||||
|
||||
|
||||
class BaseDeletePrice(tables.DeleteAction):
|
||||
use_case = None
|
||||
single_action_label = None
|
||||
plural_action_label = None
|
||||
|
||||
def action_present(self, count):
|
||||
return ungettext_lazy(
|
||||
"Delete " + self.single_action_label,
|
||||
"Delete " + self.plural_action_label,
|
||||
count
|
||||
)
|
||||
|
||||
def action_past(self, count):
|
||||
return ungettext_lazy(
|
||||
"Deleted " + self.single_action_label,
|
||||
"Deleted " + self.plural_action_label,
|
||||
count
|
||||
)
|
||||
|
||||
def delete(self, request, obj_id):
|
||||
return
|
||||
|
||||
def create_create_action(type_name, **kwargs):
|
||||
return type(type_name, (BaseCreatePrice,), kwargs)
|
||||
|
||||
|
||||
def create_filter_action(type_name, **kwargs):
|
||||
return type(type_name, (tables.FilterAction,), kwargs)
|
||||
|
||||
|
||||
def create_edit_action(type_name, **kwargs):
|
||||
return type(type_name, (BaseEditPrice,), kwargs)
|
||||
|
||||
|
||||
def create_delete_action(type_name, **kwargs):
|
||||
return type(type_name, (BaseDeletePrice,), kwargs)
|
||||
|
||||
|
||||
class FlavorPriceTable(BasePriceTable):
|
||||
name = tables.WrappingColumn('name', verbose_name=_('Flavor Name'))
|
||||
|
||||
def get_object_display(self, datum):
|
||||
return datum['name']
|
||||
|
||||
class Meta(object):
|
||||
name = "flavor_price"
|
||||
verbose_name = _("Flavor Price")
|
||||
table_actions = (
|
||||
create_filter_action(
|
||||
type_name="FlavorPriceFilter",
|
||||
name="flavor_price_filter"
|
||||
),
|
||||
create_create_action(
|
||||
type_name="FlavorPriceCreate",
|
||||
name="flavor_price_create",
|
||||
verbose_name=_("Create Flavor Price"),
|
||||
url="horizon:yuyu_demo:price_configuration_demo:flavor_price_create"
|
||||
)
|
||||
)
|
||||
row_actions = (
|
||||
create_edit_action(
|
||||
type_name="FlavorPriceUpdate",
|
||||
name="flavor_price_edit",
|
||||
verbose_name=_("Edit Flavor Price"),
|
||||
url="horizon:yuyu_demo:price_configuration_demo:flavor_price_update"
|
||||
),
|
||||
create_delete_action(
|
||||
type_name="FlavorPriceDelete",
|
||||
use_case=FlavorPriceUseCase(),
|
||||
single_action_label="Flavor Price",
|
||||
plural_action_label="Flavor Prices"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class VolumePriceTable(BasePriceTable):
|
||||
name = tables.WrappingColumn('name', verbose_name=_('Volume Type Name'))
|
||||
|
||||
def get_object_display(self, datum):
|
||||
return datum['name']
|
||||
|
||||
class Meta(object):
|
||||
name = "volume_price"
|
||||
verbose_name = _("Volume Price")
|
||||
table_actions = (
|
||||
create_filter_action(
|
||||
type_name="VolumePriceFilter",
|
||||
name="volume_price_filter"
|
||||
),
|
||||
create_create_action(
|
||||
type_name="VolumePriceCreate",
|
||||
name="create_volume_price",
|
||||
verbose_name=_("Create Volume Price"),
|
||||
url="horizon:yuyu_demo:price_configuration_demo:volume_price_create"
|
||||
)
|
||||
)
|
||||
row_actions = (
|
||||
create_edit_action(
|
||||
type_name="VolumePriceEdit",
|
||||
name="update_volume_price",
|
||||
verbose_name=_("Edit Volume Price"),
|
||||
url="horizon:yuyu_demo:price_configuration_demo:volume_price_update"
|
||||
),
|
||||
create_delete_action(
|
||||
type_name="VolumePriceDelete",
|
||||
use_case=VolumePriceUseCase(),
|
||||
single_action_label="Volume Price",
|
||||
plural_action_label="Volume Prices"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class FloatingIpPriceTable(BasePriceTable):
|
||||
def get_object_display(self, datum):
|
||||
return datum['id']
|
||||
|
||||
class Meta(object):
|
||||
name = "floating_ip_price"
|
||||
verbose_name = _("Floating IP Price")
|
||||
table_actions = (
|
||||
create_filter_action(
|
||||
type_name="FloatingIpPriceFilter",
|
||||
name="floating_ip_price_filter"
|
||||
),
|
||||
create_create_action(
|
||||
type_name="FloatingIpPriceCreate",
|
||||
name="create_floating_ip_price",
|
||||
verbose_name=_("Create Floating IP Price"),
|
||||
url="horizon:yuyu_demo:price_configuration_demo:floating_ip_price_create",
|
||||
single_data=True,
|
||||
)
|
||||
)
|
||||
row_actions = (
|
||||
create_edit_action(
|
||||
type_name="FloatingIpPriceEdit",
|
||||
name="update_floating_ip_price",
|
||||
verbose_name=_("Edit Floating IP Price"),
|
||||
url="horizon:yuyu_demo:price_configuration_demo:floating_ip_price_update"
|
||||
),
|
||||
create_delete_action(
|
||||
type_name="FloatingIpPriceDelete",
|
||||
use_case=FloatingIpPriceUseCase(),
|
||||
single_action_label="Floating IP Price",
|
||||
plural_action_label="Floating IP Prices"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class RouterPriceTable(BasePriceTable):
|
||||
def get_object_display(self, datum):
|
||||
return datum['id']
|
||||
|
||||
class Meta(object):
|
||||
name = "router_price"
|
||||
verbose_name = _("Router Price")
|
||||
table_actions = (
|
||||
create_filter_action(
|
||||
type_name="RouterPriceFilter",
|
||||
name="router_price_filter"
|
||||
),
|
||||
create_create_action(
|
||||
type_name="RouterPriceCreate",
|
||||
name="create_router_price",
|
||||
verbose_name=_("Create Router Price"),
|
||||
url="horizon:yuyu_demo:price_configuration_demo:router_price_create",
|
||||
single_data=True,
|
||||
)
|
||||
)
|
||||
row_actions = (
|
||||
create_edit_action(
|
||||
type_name="RouterPriceEdit",
|
||||
name="update_router_price",
|
||||
verbose_name=_("Edit Router Price"),
|
||||
url="horizon:yuyu_demo:price_configuration_demo:router_price_update"
|
||||
),
|
||||
create_delete_action(
|
||||
type_name="RouterPriceDelete",
|
||||
use_case=RouterPriceUseCase(),
|
||||
single_action_label="Router Price",
|
||||
plural_action_label="Router Prices"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class SnapshotPriceTable(BasePriceTable):
|
||||
def get_object_display(self, datum):
|
||||
return datum['id']
|
||||
|
||||
class Meta(object):
|
||||
name = "snapshot_price"
|
||||
verbose_name = _("Snapshot Price")
|
||||
table_actions = (
|
||||
create_filter_action(
|
||||
type_name="SnapshotFilter",
|
||||
name="snapshot_price_filter"
|
||||
),
|
||||
create_create_action(
|
||||
type_name="SnapshotCreate",
|
||||
name="create_snapshot_price",
|
||||
verbose_name=_("Create Snapshot Price"),
|
||||
url="horizon:yuyu_demo:price_configuration_demo:snapshot_price_create",
|
||||
single_data=True,
|
||||
)
|
||||
)
|
||||
row_actions = (
|
||||
create_edit_action(
|
||||
type_name="SnapshotEdit",
|
||||
name="update_snapshot_price",
|
||||
verbose_name=_("Edit Snapshot Price"),
|
||||
url="horizon:yuyu_demo:price_configuration_demo:snapshot_price_update"
|
||||
),
|
||||
create_delete_action(
|
||||
type_name="SnapshotDelete",
|
||||
use_case=SnapshotPriceUseCase(),
|
||||
single_action_label="Snapshot Price",
|
||||
plural_action_label="Snapshot Prices"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class ImagePriceTable(BasePriceTable):
|
||||
def get_object_display(self, datum):
|
||||
return datum['id']
|
||||
|
||||
class Meta(object):
|
||||
name = "image_price"
|
||||
verbose_name = _("Image Price")
|
||||
table_actions = (
|
||||
create_filter_action(
|
||||
type_name="ImageFilter",
|
||||
name="image_price_filter"
|
||||
),
|
||||
create_create_action(
|
||||
type_name="ImageCreate",
|
||||
name="create_image_price",
|
||||
verbose_name=_("Create Image Price"),
|
||||
url="horizon:yuyu_demo:price_configuration_demo:image_price_create",
|
||||
single_data=True,
|
||||
)
|
||||
)
|
||||
row_actions = (
|
||||
create_edit_action(
|
||||
type_name="ImageEdit",
|
||||
name="update_image_price",
|
||||
verbose_name=_("Edit Image Price"),
|
||||
url="horizon:yuyu_demo:price_configuration_demo:image_price_update"
|
||||
),
|
||||
create_delete_action(
|
||||
type_name="ImageDelete",
|
||||
use_case=ImagePriceUseCase(),
|
||||
single_action_label="Image Price",
|
||||
plural_action_label="Image Prices"
|
||||
)
|
||||
)
|
130
yuyu_demo/admin/price_configuration_demo/tabs.py
Normal file
130
yuyu_demo/admin/price_configuration_demo/tabs.py
Normal file
|
@ -0,0 +1,130 @@
|
|||
from horizon import exceptions, tables, tabs
|
||||
from .tables import FlavorPriceTable, VolumePriceTable, \
|
||||
FloatingIpPriceTable, RouterPriceTable, SnapshotPriceTable, ImagePriceTable
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from openstack_dashboard.dashboards.yuyu.cases.flavor_price_use_case import FlavorPriceUseCase
|
||||
from openstack_dashboard.dashboards.yuyu.cases.floating_ip_price_use_case import FloatingIpPriceUseCase
|
||||
from openstack_dashboard.dashboards.yuyu.cases.volume_price_use_case import VolumePriceUseCase
|
||||
from openstack_dashboard.dashboards.yuyu.cases.router_price_use_case import RouterPriceUseCase
|
||||
from openstack_dashboard.dashboards.yuyu.cases.snapshot_price_use_case import SnapshotPriceUseCase
|
||||
from openstack_dashboard.dashboards.yuyu.cases.image_price_use_case import ImagePriceUseCase
|
||||
|
||||
|
||||
class FlavorTab(tabs.TableTab):
|
||||
table_classes = (FlavorPriceTable,)
|
||||
name = _("Flavor")
|
||||
slug = "flavor"
|
||||
template_name = 'horizon/common/_detail_table.html'
|
||||
|
||||
flavor_price_uc = FlavorPriceUseCase()
|
||||
|
||||
def get_flavor_price_data(self):
|
||||
try:
|
||||
data = self.flavor_price_uc.list(self.request)
|
||||
return data
|
||||
except Exception:
|
||||
error_message = _('Unable to get flavor prices')
|
||||
exceptions.handle(self.request, error_message)
|
||||
|
||||
return []
|
||||
|
||||
|
||||
class VolumeTab(tabs.TableTab):
|
||||
table_classes = (VolumePriceTable,)
|
||||
name = _("Volume")
|
||||
slug = "volume"
|
||||
template_name = 'horizon/common/_detail_table.html'
|
||||
|
||||
volume_price_uc = VolumePriceUseCase()
|
||||
|
||||
def get_volume_price_data(self):
|
||||
try:
|
||||
data = self.volume_price_uc.list(self.request)
|
||||
return data
|
||||
except Exception:
|
||||
error_message = _('Unable to get volume prices')
|
||||
exceptions.handle(self.request, error_message)
|
||||
|
||||
return []
|
||||
|
||||
|
||||
class FloatingIpTab(tabs.TableTab):
|
||||
table_classes = (FloatingIpPriceTable,)
|
||||
name = _("Floating IP")
|
||||
slug = "floating_ip"
|
||||
template_name = 'horizon/common/_detail_table.html'
|
||||
|
||||
fip_price_uc = FloatingIpPriceUseCase()
|
||||
|
||||
def get_floating_ip_price_data(self):
|
||||
try:
|
||||
data = self.fip_price_uc.list(self.request)
|
||||
return data
|
||||
except Exception:
|
||||
error_message = _('Unable to get floating IP prices')
|
||||
exceptions.handle(self.request, error_message)
|
||||
|
||||
return []
|
||||
|
||||
|
||||
class RouterTab(tabs.TableTab):
|
||||
table_classes = (RouterPriceTable,)
|
||||
name = _("Router")
|
||||
slug = "router"
|
||||
template_name = 'horizon/common/_detail_table.html'
|
||||
|
||||
price_uc = RouterPriceUseCase()
|
||||
|
||||
def get_router_price_data(self):
|
||||
try:
|
||||
data = self.price_uc.list(self.request)
|
||||
return data
|
||||
except Exception:
|
||||
error_message = _('Unable to get router prices')
|
||||
exceptions.handle(self.request, error_message)
|
||||
|
||||
return []
|
||||
|
||||
|
||||
class SnapshotTab(tabs.TableTab):
|
||||
table_classes = (SnapshotPriceTable,)
|
||||
name = _("Snapshot")
|
||||
slug = "snapshot"
|
||||
template_name = 'horizon/common/_detail_table.html'
|
||||
|
||||
price_uc = SnapshotPriceUseCase()
|
||||
|
||||
def get_snapshot_price_data(self):
|
||||
try:
|
||||
data = self.price_uc.list(self.request)
|
||||
return data
|
||||
except Exception:
|
||||
error_message = _('Unable to get snapshot prices')
|
||||
exceptions.handle(self.request, error_message)
|
||||
|
||||
return []
|
||||
|
||||
class ImageTab(tabs.TableTab):
|
||||
table_classes = (ImagePriceTable,)
|
||||
name = _("Image")
|
||||
slug = "image"
|
||||
template_name = 'horizon/common/_detail_table.html'
|
||||
|
||||
price_uc = ImagePriceUseCase()
|
||||
|
||||
def get_image_price_data(self):
|
||||
try:
|
||||
data = self.price_uc.list(self.request)
|
||||
return data
|
||||
except Exception:
|
||||
error_message = _('Unable to get image prices')
|
||||
exceptions.handle(self.request, error_message)
|
||||
|
||||
return []
|
||||
|
||||
|
||||
class PriceConfigurationTabs(tabs.TabGroup):
|
||||
slug = "price_config"
|
||||
tabs = (FlavorTab, VolumeTab, FloatingIpTab, RouterTab, SnapshotTab, ImageTab, )
|
||||
sticky = True
|
|
@ -0,0 +1,18 @@
|
|||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block modal-body-right %}
|
||||
<h3>{% trans "Description:" %}</h3>
|
||||
<p>{% trans 'Create/update a flavor price.' %}</p>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block modal-footer %}
|
||||
{% if cancel_url %}
|
||||
<a href="{% block cancel_url %}{{ cancel_url }}{% endblock %}"
|
||||
class="btn btn-default cancel">
|
||||
{{ cancel_label }}
|
||||
</a>
|
||||
{% endif %}
|
||||
<input class="btn btn-primary" type="submit" disabled value="{{ submit_label }}">
|
||||
{% endblock %}
|
|
@ -0,0 +1,18 @@
|
|||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block modal-body-right %}
|
||||
<h3>{% trans "Description:" %}</h3>
|
||||
<p>{% trans 'Create/update a floating IP price for each allocation.' %}</p>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block modal-footer %}
|
||||
{% if cancel_url %}
|
||||
<a href="{% block cancel_url %}{{ cancel_url }}{% endblock %}"
|
||||
class="btn btn-default cancel">
|
||||
{{ cancel_label }}
|
||||
</a>
|
||||
{% endif %}
|
||||
<input class="btn btn-primary" type="submit" disabled value="{{ submit_label }}">
|
||||
{% endblock %}
|
|
@ -0,0 +1,17 @@
|
|||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block modal-body-right %}
|
||||
<h3>{% trans "Description:" %}</h3>
|
||||
<p>{% trans 'Create/update a image price for each 1GB of space.' %}</p>
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-footer %}
|
||||
{% if cancel_url %}
|
||||
<a href="{% block cancel_url %}{{ cancel_url }}{% endblock %}"
|
||||
class="btn btn-default cancel">
|
||||
{{ cancel_label }}
|
||||
</a>
|
||||
{% endif %}
|
||||
<input class="btn btn-primary" type="submit" disabled value="{{ submit_label }}">
|
||||
{% endblock %}
|
|
@ -0,0 +1,17 @@
|
|||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block modal-body-right %}
|
||||
<h3>{% trans "Description:" %}</h3>
|
||||
<p>{% trans 'Create/update a router price for each allocation that have external network.' %}</p>
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-footer %}
|
||||
{% if cancel_url %}
|
||||
<a href="{% block cancel_url %}{{ cancel_url }}{% endblock %}"
|
||||
class="btn btn-default cancel">
|
||||
{{ cancel_label }}
|
||||
</a>
|
||||
{% endif %}
|
||||
<input class="btn btn-primary" type="submit" disabled value="{{ submit_label }}">
|
||||
{% endblock %}
|
|
@ -0,0 +1,17 @@
|
|||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block modal-body-right %}
|
||||
<h3>{% trans "Description:" %}</h3>
|
||||
<p>{% trans 'Create/update a snapshot price for each 1GB of space.' %}</p>
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-footer %}
|
||||
{% if cancel_url %}
|
||||
<a href="{% block cancel_url %}{{ cancel_url }}{% endblock %}"
|
||||
class="btn btn-default cancel">
|
||||
{{ cancel_label }}
|
||||
</a>
|
||||
{% endif %}
|
||||
<input class="btn btn-primary" type="submit" disabled value="{{ submit_label }}">
|
||||
{% endblock %}
|
|
@ -0,0 +1,17 @@
|
|||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block modal-body-right %}
|
||||
<h3>{% trans "Description:" %}</h3>
|
||||
<p>{% trans 'Create/update a volume price for each 1GB of space.' %}</p>
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-footer %}
|
||||
{% if cancel_url %}
|
||||
<a href="{% block cancel_url %}{{ cancel_url }}{% endblock %}"
|
||||
class="btn btn-default cancel">
|
||||
{{ cancel_label }}
|
||||
</a>
|
||||
{% endif %}
|
||||
<input class="btn btn-primary" type="submit" disabled value="{{ submit_label }}">
|
||||
{% endblock %}
|
|
@ -0,0 +1,8 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans "Create/Update Flavor Price" %}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% include "yuyu_demo/price_configuration_demo/_create_flavor.html" %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,8 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans "Create/Update Floating IP Price" %}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% include "yuyu_demo/price_configuration_demo/_create_floating_ip.html" %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,8 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans "Create/Update Image Price" %}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% include "yuyu_demo/price_configuration_demo/_create_image.html" %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,8 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans "Create/Update Router Price" %}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% include "yuyu_demo/price_configuration_demo/_create_router.html" %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,8 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans "Create/Update Snapshot Price" %}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% include "yuyu_demo/price_configuration_demo/_create_snapshot.html" %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,8 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans "Create/Update Volume Price" %}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% include "yuyu_demo/price_configuration_demo/_create_volume.html" %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,13 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Price Configuration" %}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% include "admin/price_configuration/missing_prices.html" %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
{{ tab_group.render }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -0,0 +1,70 @@
|
|||
{% if missing_price.flavor %}
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="alert alert-danger alert-dismissible" role="alert">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
Flavor price not complete
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% if missing_price.volume %}
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="alert alert-danger alert-dismissible" role="alert">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
Volume price not complete
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if missing_price.fip %}
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="alert alert-danger alert-dismissible" role="alert">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
Floating IP price not complete
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if missing_price.router %}
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="alert alert-danger alert-dismissible" role="alert">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
Router IP price not complete
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if missing_price.snapshot %}
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="alert alert-danger alert-dismissible" role="alert">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
Snapshot price not complete
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if missing_price.image %}
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="alert alert-danger alert-dismissible" role="alert">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
Image price not complete
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
||||
|
||||
|
35
yuyu_demo/admin/price_configuration_demo/urls.py
Normal file
35
yuyu_demo/admin/price_configuration_demo/urls.py
Normal file
|
@ -0,0 +1,35 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from django.conf.urls import url
|
||||
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', views.IndexView.as_view(), name='index'),
|
||||
url(r'^flavor_price/create/$', views.FlavorPriceAddFormView.as_view(), name='flavor_price_create'),
|
||||
url(r'^flavor_price/update/(?P<id>[^/]+)/$', views.FlavorPriceUpdateFormView.as_view(), name='flavor_price_update'),
|
||||
url(r'^volume_price/create/$', views.VolumePriceAddFormView.as_view(), name='volume_price_create'),
|
||||
url(r'^volume_price/update/(?P<id>[^/]+)/$', views.VolumePriceUpdateFormView.as_view(), name='volume_price_update'),
|
||||
url(r'^floating_ip_price/create/$', views.FloatingIpPriceAddFormView.as_view(), name='floating_ip_price_create'),
|
||||
url(r'^floating_ip_price/update/(?P<id>[^/]+)/$', views.FloatingIpPriceUpdateFormView.as_view(),
|
||||
name='floating_ip_price_update'),
|
||||
url(r'^router_price/create/$', views.RouterPriceAddFormView.as_view(), name='router_price_create'),
|
||||
url(r'^router_price/update/(?P<id>[^/]+)/$', views.RouterPriceUpdateFormView.as_view(),
|
||||
name='router_price_update'),
|
||||
url(r'^snapshot_price/create/$', views.SnapshotPriceAddFormView.as_view(), name='snapshot_price_create'),
|
||||
url(r'^snapshot_price/update/(?P<id>[^/]+)/$', views.SnapshotPriceUpdateFormView.as_view(),
|
||||
name='snapshot_price_update'),
|
||||
url(r'^image_price/create/$', views.ImagePriceAddFormView.as_view(), name='image_price_create'),
|
||||
url(r'^image_price/update/(?P<id>[^/]+)/$', views.ImagePriceUpdateFormView.as_view(),
|
||||
name='image_price_update'),
|
||||
]
|
117
yuyu_demo/admin/price_configuration_demo/views.py
Normal file
117
yuyu_demo/admin/price_configuration_demo/views.py
Normal file
|
@ -0,0 +1,117 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from django.urls import reverse_lazy, reverse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from neutronclient.common import exceptions as neutron_exc
|
||||
|
||||
|
||||
from .forms import FlavorPriceForm, VolumePriceForm, FloatingIpPriceForm, RouterPriceForm, SnapshotPriceForm, ImagePriceForm
|
||||
from .tabs import PriceConfigurationTabs
|
||||
|
||||
from ....yuyu.admin.price_configuration import views
|
||||
|
||||
class IndexView(views.IndexView):
|
||||
tab_group_class = PriceConfigurationTabs
|
||||
|
||||
|
||||
class FlavorPriceAddFormView(views.FlavorPriceAddFormView):
|
||||
form_class = FlavorPriceForm
|
||||
submit_url = reverse_lazy("horizon:yuyu_demo:price_configuration_demo:flavor_price_create")
|
||||
success_url = reverse_lazy("horizon:yuyu_demo:price_configuration_demo:index")
|
||||
template_name = 'yuyu_demo/price_configuration_demo/create_flavor.html'
|
||||
|
||||
|
||||
|
||||
class FlavorPriceUpdateFormView(views.FlavorPriceUpdateFormView):
|
||||
form_class = FlavorPriceForm
|
||||
submit_url = "horizon:yuyu_demo:price_configuration_demo:flavor_price_update"
|
||||
success_url = reverse_lazy("horizon:yuyu_demo:price_configuration_demo:index")
|
||||
template_name = 'yuyu_demo/price_configuration_demo/create_flavor.html'
|
||||
|
||||
|
||||
class VolumePriceAddFormView(views.VolumePriceAddFormView):
|
||||
form_class = VolumePriceForm
|
||||
submit_url = reverse_lazy("horizon:yuyu_demo:price_configuration_demo:volume_price_create")
|
||||
success_url = reverse_lazy("horizon:yuyu_demo:price_configuration_demo:index")
|
||||
template_name = 'yuyu_demo/price_configuration_demo/create_volume.html'
|
||||
|
||||
|
||||
class VolumePriceUpdateFormView(views.VolumePriceUpdateFormView):
|
||||
form_class = VolumePriceForm
|
||||
form_id = "volume_price_form_update"
|
||||
page_title = _("Volume Price")
|
||||
submit_label = _("Update Volume Price")
|
||||
submit_url = "horizon:yuyu_demo:price_configuration_demo:volume_price_update"
|
||||
success_url = reverse_lazy("horizon:yuyu_demo:price_configuration_demo:index")
|
||||
template_name = 'yuyu_demo/price_configuration_demo/create_volume.html'
|
||||
|
||||
|
||||
|
||||
class FloatingIpPriceAddFormView(views.FloatingIpPriceAddFormView):
|
||||
form_class = FloatingIpPriceForm
|
||||
|
||||
submit_url = reverse_lazy("horizon:yuyu_demo:price_configuration_demo:floating_ip_price_create")
|
||||
success_url = reverse_lazy("horizon:yuyu_demo:price_configuration_demo:index")
|
||||
template_name = 'yuyu_demo/price_configuration_demo/create_floating_ip.html'
|
||||
|
||||
|
||||
class FloatingIpPriceUpdateFormView(views.FloatingIpPriceUpdateFormView):
|
||||
form_class = FloatingIpPriceForm
|
||||
submit_url = "horizon:yuyu_demo:price_configuration_demo:floating_ip_price_update"
|
||||
success_url = reverse_lazy("horizon:yuyu_demo:price_configuration_demo:index")
|
||||
template_name = 'yuyu_demo/price_configuration_demo/create_floating_ip.html'
|
||||
|
||||
|
||||
|
||||
class RouterPriceAddFormView(views.RouterPriceAddFormView):
|
||||
form_class = RouterPriceForm
|
||||
|
||||
submit_url = reverse_lazy("horizon:yuyu_demo:price_configuration_demo:router_price_create")
|
||||
success_url = reverse_lazy("horizon:yuyu_demo:price_configuration_demo:index")
|
||||
template_name = 'yuyu_demo/price_configuration_demo/create_router.html'
|
||||
|
||||
class RouterPriceUpdateFormView(views.RouterPriceUpdateFormView):
|
||||
form_class = RouterPriceForm
|
||||
submit_url = "horizon:yuyu_demo:price_configuration_demo:router_price_update"
|
||||
success_url = reverse_lazy("horizon:yuyu_demo:price_configuration_demo:index")
|
||||
template_name = 'yuyu_demo/price_configuration_demo/create_router.html'
|
||||
|
||||
|
||||
|
||||
class SnapshotPriceAddFormView(views.SnapshotPriceAddFormView):
|
||||
form_class = SnapshotPriceForm
|
||||
submit_url = reverse_lazy("horizon:yuyu_demo:price_configuration_demo:snapshot_price_create")
|
||||
success_url = reverse_lazy("horizon:yuyu_demo:price_configuration_demo:index")
|
||||
template_name = 'yuyu_demo/price_configuration_demo/create_snapshot.html'
|
||||
|
||||
|
||||
class SnapshotPriceUpdateFormView(views.SnapshotPriceUpdateFormView):
|
||||
form_class = SnapshotPriceForm
|
||||
submit_url = "horizon:yuyu_demo:price_configuration_demo:snapshot_price_update"
|
||||
success_url = reverse_lazy("horizon:yuyu_demo:price_configuration_demo:index")
|
||||
template_name = 'yuyu_demo/price_configuration_demo/create_snapshot.html'
|
||||
|
||||
|
||||
class ImagePriceAddFormView(views.ImagePriceAddFormView):
|
||||
form_class = ImagePriceForm
|
||||
submit_url = reverse_lazy("horizon:yuyu_demo:price_configuration_demo:image_price_create")
|
||||
success_url = reverse_lazy("horizon:yuyu_demo:price_configuration_demo:index")
|
||||
template_name = 'yuyu_demo/price_configuration_demo/create_image.html'
|
||||
|
||||
|
||||
|
||||
class ImagePriceUpdateFormView(views.ImagePriceUpdateFormView):
|
||||
form_class = ImagePriceForm
|
||||
submit_url = "horizon:yuyu_demo:price_configuration_demo:image_price_update"
|
||||
success_url = reverse_lazy("horizon:yuyu_demo:price_configuration_demo:index")
|
||||
template_name = 'yuyu_demo/price_configuration_demo/create_image.html'
|
0
yuyu_demo/admin/projects_balance_demo/__init__.py
Normal file
0
yuyu_demo/admin/projects_balance_demo/__init__.py
Normal file
65
yuyu_demo/admin/projects_balance_demo/forms.py
Normal file
65
yuyu_demo/admin/projects_balance_demo/forms.py
Normal file
|
@ -0,0 +1,65 @@
|
|||
import base64
|
||||
|
||||
from django.core import validators
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from djmoney.forms import MoneyField
|
||||
|
||||
from horizon import forms, messages, exceptions
|
||||
from openstack_dashboard.dashboards.yuyu.cases.balance_use_case import BalanceUseCase
|
||||
from openstack_dashboard.dashboards.yuyu.cases.setting_use_case import SettingUseCase
|
||||
|
||||
|
||||
class TopUpForm(forms.SelfHandlingForm):
|
||||
USE_CASE = BalanceUseCase()
|
||||
|
||||
amount = MoneyField(label=_("Amount"), min_value=0, max_digits=10)
|
||||
description = forms.CharField(label=_("Description"))
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
self.project_id = kwargs['project_id']
|
||||
del kwargs['project_id']
|
||||
super().__init__(request, *args, **kwargs)
|
||||
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
payload = {
|
||||
"amount": data['amount'].amount,
|
||||
"amount_currency": data['amount'].currency.code,
|
||||
'description': data['description'],
|
||||
}
|
||||
|
||||
result = self.USE_CASE.top_up(request, self.project_id, payload)
|
||||
messages.success(request, _(f"Successfully Top Up"))
|
||||
|
||||
return result
|
||||
except Exception as e:
|
||||
exceptions.handle(request,
|
||||
_('Unable to top up.'))
|
||||
|
||||
|
||||
class TopDownForm(forms.SelfHandlingForm):
|
||||
USE_CASE = BalanceUseCase()
|
||||
|
||||
amount = MoneyField(label=_("Amount"), min_value=0, max_digits=10)
|
||||
description = forms.CharField(label=_("Description"))
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
self.project_id = kwargs['project_id']
|
||||
del kwargs['project_id']
|
||||
super().__init__(request, *args, **kwargs)
|
||||
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
payload = {
|
||||
"amount": data['amount'].amount,
|
||||
"amount_currency": data['amount'].currency.code,
|
||||
'description': data['description'],
|
||||
}
|
||||
|
||||
result = self.USE_CASE.top_down(request, self.project_id, payload)
|
||||
messages.success(request, _(f"Successfully Top Down"))
|
||||
|
||||
return result
|
||||
except Exception as e:
|
||||
exceptions.handle(request,
|
||||
_('Unable to top down.'))
|
21
yuyu_demo/admin/projects_balance_demo/panel.py
Normal file
21
yuyu_demo/admin/projects_balance_demo/panel.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
import horizon
|
||||
|
||||
|
||||
class ProjectsBalanceDemo(horizon.Panel):
|
||||
name = _("Projects Balance")
|
||||
slug = "projects_balance_demo"
|
||||
|
74
yuyu_demo/admin/projects_balance_demo/tables.py
Normal file
74
yuyu_demo/admin/projects_balance_demo/tables.py
Normal file
|
@ -0,0 +1,74 @@
|
|||
from django.urls import reverse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import tables
|
||||
|
||||
|
||||
class DetailAction(tables.LinkAction):
|
||||
name = "detail"
|
||||
verbose_name = "Detail"
|
||||
|
||||
def get_link_url(self, datum=None, ):
|
||||
return reverse("horizon:admin:projects_balance:balance_detail", kwargs={
|
||||
"project_id": datum['project_id'],
|
||||
})
|
||||
|
||||
|
||||
class TopUpAction(tables.LinkAction):
|
||||
name = "top_up"
|
||||
verbose_name = "Top Up"
|
||||
icon = "plus"
|
||||
classes = ("ajax-modal",)
|
||||
|
||||
def get_link_url(self, datum=None, ):
|
||||
return reverse("horizon:admin:projects_balance:top_up", kwargs={
|
||||
"project_id": self.table.kwargs['project_id'],
|
||||
})
|
||||
|
||||
|
||||
class TopDownAction(tables.LinkAction):
|
||||
name = "top_down"
|
||||
verbose_name = "Top Down"
|
||||
icon = "minus"
|
||||
classes = ("ajax-modal",)
|
||||
|
||||
def get_link_url(self, datum=None, ):
|
||||
return reverse("horizon:admin:projects_balance:top_down", kwargs={
|
||||
"project_id": self.table.kwargs['project_id'],
|
||||
})
|
||||
|
||||
|
||||
class BalanceProjectTable(tables.DataTable):
|
||||
project = tables.WrappingColumn('project', verbose_name=_('Project'))
|
||||
amount = tables.WrappingColumn('amount', verbose_name=_('Amount'))
|
||||
|
||||
def get_object_id(self, datum):
|
||||
return datum['id']
|
||||
|
||||
def get_object_display(self, datum):
|
||||
return datum['project']
|
||||
|
||||
class Meta(object):
|
||||
name = "balance_project_table"
|
||||
hidden_title = True
|
||||
verbose_name = _("Project Balance")
|
||||
row_actions = (DetailAction,)
|
||||
|
||||
|
||||
class BalanceTransactionTable(tables.DataTable):
|
||||
date = tables.WrappingColumn('date', verbose_name=_('Date'))
|
||||
amount = tables.WrappingColumn('amount', verbose_name=_('Amount'))
|
||||
action = tables.Column('action', verbose_name=_('Action'))
|
||||
description = tables.Column('description', verbose_name=_('Description'))
|
||||
|
||||
def get_object_id(self, datum):
|
||||
return datum['id']
|
||||
|
||||
def get_object_display(self, datum):
|
||||
return datum['date']
|
||||
|
||||
class Meta(object):
|
||||
name = "balance_table"
|
||||
hidden_title = True
|
||||
verbose_name = _("Balance Transaction")
|
||||
table_actions = (TopUpAction, TopDownAction,)
|
|
@ -0,0 +1,9 @@
|
|||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
{% block form_attrs %}enctype="multipart/form-data"{% endblock %}
|
||||
|
||||
{% block modal-body-right %}
|
||||
<h3>{% trans "Description:" %}</h3>
|
||||
<p>{% trans 'Top down balance for current project' %}</p>
|
||||
{% endblock %}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
{% block form_attrs %}enctype="multipart/form-data"{% endblock %}
|
||||
|
||||
{% block modal-body-right %}
|
||||
<h3>{% trans "Description:" %}</h3>
|
||||
<p>{% trans 'Top up balance for current project' %}</p>
|
||||
{% endblock %}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
{% extends 'base.html' %}
|
||||
{% block title %}{{ page_title }}{% endblock %}
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Project Balance") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{{ table.render }}
|
||||
{% endblock %}
|
||||
{% block js %}
|
||||
{{ block.super }}
|
||||
{% endblock %}
|
|
@ -0,0 +1,13 @@
|
|||
{% extends 'base.html' %}
|
||||
{% block title %}{{ page_title }}{% endblock %}
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=page_title %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
<h3>Current Balance: {{ amount }}</h3>
|
||||
{{ table.render }}
|
||||
{% endblock %}
|
||||
{% block js %}
|
||||
{{ block.super }}
|
||||
{% endblock %}
|
|
@ -0,0 +1,8 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans "Top Up" %}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% include "admin/projects_balance/_form_top_down.html" %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,8 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans "Top Up" %}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% include "admin/projects_balance/_form_top_up.html" %}
|
||||
{% endblock %}
|
24
yuyu_demo/admin/projects_balance_demo/urls.py
Normal file
24
yuyu_demo/admin/projects_balance_demo/urls.py
Normal file
|
@ -0,0 +1,24 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from django.conf.urls import url
|
||||
|
||||
from openstack_dashboard.dashboards.yuyu.admin.projects_balance import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', views.IndexView.as_view(), name='index'),
|
||||
url(r'^detail/(?P<project_id>[^/]+)/$', views.DetailBalanceView.as_view(), name='balance_detail'),
|
||||
url(r'^detail/(?P<project_id>[^/]+)/topup/$',
|
||||
views.TopUpView.as_view(), name='top_up'),
|
||||
url(r'^detail/(?P<project_id>[^/]+)/top_down/$',
|
||||
views.TopDownView.as_view(), name='top_down'),
|
||||
]
|
73
yuyu_demo/admin/projects_balance_demo/views.py
Normal file
73
yuyu_demo/admin/projects_balance_demo/views.py
Normal file
|
@ -0,0 +1,73 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
import dateutil.parser
|
||||
from django.urls import reverse_lazy, reverse
|
||||
from django.utils import formats
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from djmoney.money import Money
|
||||
|
||||
from horizon import exceptions, forms
|
||||
from .forms import TopUpForm, TopDownForm
|
||||
from .tables import BalanceProjectTable, BalanceTransactionTable
|
||||
|
||||
from ....yuyu.admin.projects_balance import views
|
||||
|
||||
class IndexView(views.IndexView):
|
||||
table_class = BalanceProjectTable
|
||||
|
||||
class DetailBalanceView(views.DetailBalanceView):
|
||||
table_class = BalanceTransactionTable
|
||||
|
||||
|
||||
class TopUpView(views.TopDownView):
|
||||
form_class = TopUpForm
|
||||
template_name = 'yuyu_demo/projects_balance_demo/form_top_up.html'
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super(TopUpView, self).get_form_kwargs()
|
||||
kwargs['project_id'] = self.kwargs['project_id']
|
||||
return kwargs
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(TopUpView, self).get_context_data(**kwargs)
|
||||
context['submit_url'] = reverse('horizon:yuyu_demo:projects_balance_demo:top_up', kwargs={
|
||||
'project_id': self.kwargs['project_id']
|
||||
})
|
||||
return context
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('horizon:yuyu_demo:projects_balance_demo:balance_detail', kwargs={
|
||||
'project_id': self.kwargs['project_id']
|
||||
})
|
||||
|
||||
|
||||
class TopDownView(views.TopDownView):
|
||||
form_class = TopDownForm
|
||||
template_name = 'yuyu_demo/projects_balance_demo/form_top_down.html'
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super(TopDownView, self).get_form_kwargs()
|
||||
kwargs['project_id'] = self.kwargs['project_id']
|
||||
return kwargs
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(TopDownView, self).get_context_data(**kwargs)
|
||||
context['submit_url'] = reverse('horizon:yuyu_demo:projects_balance_demo:top_down', kwargs={
|
||||
'project_id': self.kwargs['project_id']
|
||||
})
|
||||
return context
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('horizon:yuyu_demo:projects_balance_demo:balance_detail', kwargs={
|
||||
'project_id': self.kwargs['project_id']
|
||||
})
|
||||
|
0
yuyu_demo/admin/projects_invoice_demo/__init__.py
Normal file
0
yuyu_demo/admin/projects_invoice_demo/__init__.py
Normal file
22
yuyu_demo/admin/projects_invoice_demo/panel.py
Normal file
22
yuyu_demo/admin/projects_invoice_demo/panel.py
Normal file
|
@ -0,0 +1,22 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
import horizon
|
||||
|
||||
|
||||
class ProjectsInvoice(horizon.Panel):
|
||||
name = _("Projects Invoice")
|
||||
slug = "projects_invoice"
|
||||
permissions = ('openstack.roles.admin', )
|
||||
|
44
yuyu_demo/admin/projects_invoice_demo/tables.py
Normal file
44
yuyu_demo/admin/projects_invoice_demo/tables.py
Normal file
|
@ -0,0 +1,44 @@
|
|||
from django.urls import reverse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import tables
|
||||
|
||||
|
||||
class InvoiceAction(tables.LinkAction):
|
||||
name = "invoice"
|
||||
verbose_name = "Invoice"
|
||||
|
||||
def get_link_url(self, datum=None, *args, **kwargs):
|
||||
return reverse("horizon:admin:projects_invoice:invoice_detail", kwargs={
|
||||
"id": datum['id'],
|
||||
"project_id": datum['project_id'],
|
||||
})
|
||||
|
||||
|
||||
class UsageCostAction(tables.LinkAction):
|
||||
name = "usage_cost"
|
||||
verbose_name = "Usage Cost"
|
||||
|
||||
def get_link_url(self, datum=None, *args, **kwargs):
|
||||
return reverse("horizon:admin:projects_invoice:usage_cost", kwargs={
|
||||
"id": datum['id'],
|
||||
"project_id": datum['project_id'],
|
||||
})
|
||||
|
||||
|
||||
class InvoiceTable(tables.DataTable):
|
||||
date = tables.WrappingColumn('date', verbose_name=_('Date'))
|
||||
state = tables.WrappingColumn('state', verbose_name=_('State'))
|
||||
total = tables.Column('total', verbose_name=_('Total'))
|
||||
|
||||
def get_object_id(self, datum):
|
||||
return datum['id']
|
||||
|
||||
def get_object_display(self, datum):
|
||||
return datum['date']
|
||||
|
||||
class Meta(object):
|
||||
name = "invoice_table"
|
||||
hidden_title = True
|
||||
verbose_name = _("Invoice")
|
||||
row_actions = (InvoiceAction, UsageCostAction)
|
|
@ -0,0 +1,159 @@
|
|||
<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">
|
||||
{{ setting.company_logo }}
|
||||
</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>Issue Date:</strong><br>
|
||||
{{ invoice.start_date|date:"d 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>
|
|
@ -0,0 +1,77 @@
|
|||
{% extends 'base.html' %}
|
||||
{% block title %}{{ page_title }}{% endblock %}
|
||||
{% block main %}
|
||||
{% if invoice %}
|
||||
{% 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>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
<div id="usage_cost">
|
||||
<div>
|
||||
<dl class="dl-horizontal">
|
||||
<dt>Invoice Month</dt>
|
||||
<dd>{{ invoice.start_date|date:"M Y" }}</dd>
|
||||
<dt>Invoice State</dt>
|
||||
<dd>{{ invoice.state_text }}</dd>
|
||||
<dt>Subtotal</dt>
|
||||
<dd>{{ invoice.subtotal_money }}</dd>
|
||||
{% if invoice.state != 1 %}
|
||||
<dt>Tax</dt>
|
||||
<dd>{{ invoice.tax_money }}</dd>
|
||||
<dt>Total</dt>
|
||||
<dd>{{ invoice.total_money }}</dd>
|
||||
{% endif %}
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div id="instance-cost">
|
||||
{{ instance_cost_table.render }}
|
||||
</div>
|
||||
|
||||
<div id="volume-cost">
|
||||
{{ volume_cost_table.render }}
|
||||
</div>
|
||||
<div id="floating-ip-cost">
|
||||
{{ floating_ip_cost_table.render }}
|
||||
</div>
|
||||
<div id="router-cost">
|
||||
{{ router_cost_table.render }}
|
||||
</div>
|
||||
<div id="snapshot-cost">
|
||||
{{ snapshot_cost_table.render }}
|
||||
</div>
|
||||
<div id="image-cost">
|
||||
{{ image_cost_table.render }}
|
||||
</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 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'},
|
||||
}
|
||||
html2pdf().set(opt).from(document.getElementById('usage_cost')).save();
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
|
@ -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 %}
|
|
@ -0,0 +1,17 @@
|
|||
{% load horizon %}
|
||||
{% jstemplate %}
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% load i18n %}
|
||||
{% block title %}
|
||||
{% trans "Price_Volume_Panel" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Price_Volume_Panel") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{% endblock %}
|
||||
|
||||
{% endjstemplate %}
|
|
@ -0,0 +1,23 @@
|
|||
{% extends 'base.html' %}
|
||||
{% block title %}{{ page_title }}{% endblock %}
|
||||
{% block main %}
|
||||
<a class="btn btn-default" href="?print=true" target="_blank">Download PDF</a>
|
||||
{% include 'admin/projects_invoice/manage_invoice_modal.html' with invoice=invoice project_balance_amount=project_balance_amount %}
|
||||
<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>
|
|
@ -0,0 +1,32 @@
|
|||
{% extends 'base.html' %}
|
||||
{% block title %}{{ page_title }}{% endblock %}
|
||||
{% block main %}
|
||||
<div class="row" style="margin-bottom: 15px;">
|
||||
<div class="col-md-6">
|
||||
|
||||
<b>Invoice Month</b>
|
||||
<select id="project_select" onchange="onProjectSelect(this.value)">
|
||||
{% for i in project_list %}
|
||||
<option
|
||||
value='{"project_id": "{{ i.id }}", "project_name": "{{ i.name }}"}'
|
||||
{% if i.id == current_project_id %}selected {% endif %}
|
||||
>{{ i.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{{ table.render }}
|
||||
{% endblock %}
|
||||
{% block js %}
|
||||
{{ block.super }}
|
||||
<script type="text/javascript">
|
||||
function onProjectSelect(val) {
|
||||
console.log(val)
|
||||
const data = JSON.parse(val);
|
||||
var search = "?project_id=" + data.project_id + "&project_name=" + data.project_name;
|
||||
window.location.href = window.location.protocol + "//" + window.location.host + window.location.pathname + search;
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
|
@ -0,0 +1,51 @@
|
|||
{% if invoice.state == 2 %}
|
||||
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#invoice_finish_modal">
|
||||
Set to Finished
|
||||
</button>
|
||||
{% endif %}
|
||||
|
||||
{% if invoice.state == 100 %}
|
||||
<button type="button" class="btn btn-danger" data-toggle="modal" data-target="#invoice_rollback_unpaid_modal">
|
||||
Rollback to Unpaid
|
||||
</button>
|
||||
{% endif %}
|
||||
|
||||
<div class="modal fade" id="invoice_finish_modal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="myModalLabel">Finish Invoice</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
Project Balance: {{ project_balance_amount }}
|
||||
|
||||
<br><br>
|
||||
<a class="btn btn-warning"
|
||||
href="{% url 'horizon:admin:projects_invoice:finish_invoice' invoice.id %}?next={{ request.path }}&skip_balance=true">Finish without Deduct Balance</a>
|
||||
|
||||
<a class="btn btn-success"
|
||||
href="{% url 'horizon:admin:projects_invoice:finish_invoice' invoice.id %}?next={{ request.path }}">Finish and Deduct Balance</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="invoice_rollback_unpaid_modal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="myModalLabel">Rollback to Unpaid</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
Project Balance: {{ project_balance_amount }}
|
||||
<br><br>
|
||||
|
||||
<a class="btn btn-danger"
|
||||
href="{% url 'horizon:admin:projects_invoice:rollback_to_unpaid' invoice.id %}?next={{ request.path }}&skip_balance=true">Rollback without Refund Balance</a>
|
||||
|
||||
<a class="btn btn-warning"
|
||||
href="{% url 'horizon:admin:projects_invoice:rollback_to_unpaid' invoice.id %}?next={{ request.path }}">Rollback and Refund Balance</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
25
yuyu_demo/admin/projects_invoice_demo/urls.py
Normal file
25
yuyu_demo/admin/projects_invoice_demo/urls.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from django.conf.urls import url
|
||||
|
||||
from openstack_dashboard.dashboards.yuyu.admin.projects_invoice import views
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
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/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/rollback_to_unpaid/(?P<id>[^/]+)/$', views.RollbackToUnpaidInvoice.as_view(),
|
||||
name='rollback_to_unpaid'),
|
||||
]
|
45
yuyu_demo/admin/projects_invoice_demo/views.py
Normal file
45
yuyu_demo/admin/projects_invoice_demo/views.py
Normal file
|
@ -0,0 +1,45 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import views
|
||||
|
||||
from .tables import InvoiceTable
|
||||
|
||||
from ....yuyu.admin.projects_invoice import views
|
||||
|
||||
class IndexView(views.IndexView):
|
||||
table_class = InvoiceTable
|
||||
|
||||
class InvoiceView(views.InvoiceView):
|
||||
pass
|
||||
|
||||
class UsageCostTabView(views.UsageCostTabView):
|
||||
pass
|
||||
|
||||
class UsageCostView(views.UsageCostView):
|
||||
pass
|
||||
|
||||
|
||||
class FinishInvoice(views.FinishInvoice):
|
||||
def get(self, request, *args, **kwargs):
|
||||
next_url = request.GET.get('next', reverse('horizon:yuyu_demo:projects_invoice_demo:index'))
|
||||
return HttpResponseRedirect(next_url)
|
||||
|
||||
|
||||
class RollbackToUnpaidInvoice(views.RollbackToUnpaidInvoice):
|
||||
def get(self, request, *args, **kwargs):
|
||||
next_url = request.GET.get('next', reverse('horizon:yuyu_demo:projects_invoice_demo:index'))
|
||||
return HttpResponseRedirect(next_url)
|
28
yuyu_demo/dashboard.py
Normal file
28
yuyu_demo/dashboard.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
# Copyright 2012 Nebula, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from openstack_auth import utils
|
||||
|
||||
import horizon
|
||||
from horizon.utils import settings as utils_settings
|
||||
|
||||
|
||||
class YuyuDemo(horizon.Dashboard):
|
||||
name = _("Admin - Yuyu Demo")
|
||||
slug = "yuyu_demo"
|
||||
default_panel = 'billing_overview_demo'
|
||||
|
||||
|
||||
horizon.register(YuyuDemo)
|
6
yuyu_demo/local/enabled/_6600_yuyu_demo.py
Normal file
6
yuyu_demo/local/enabled/_6600_yuyu_demo.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
ADD_INSTALLED_APPS = [
|
||||
'djmoney',
|
||||
'openstack_dashboard.dashboards.yuyu_demo',
|
||||
]
|
||||
|
||||
DASHBOARD ='yuyu_demo'
|
|
@ -0,0 +1,8 @@
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
# The slug of the panel group to be added to HORIZON_CONFIG. Required.
|
||||
PANEL_GROUP = 'billing'
|
||||
# The display name of the PANEL_GROUP. Required.
|
||||
PANEL_GROUP_NAME = _('Billing')
|
||||
# The slug of the dashboard the PANEL_GROUP associated with. Required.
|
||||
PANEL_GROUP_DASHBOARD = 'yuyu_demo'
|
9
yuyu_demo/local/enabled/_6602_admin_billing_overview.py
Normal file
9
yuyu_demo/local/enabled/_6602_admin_billing_overview.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
# The slug of the panel to be added to HORIZON_CONFIG. Required.
|
||||
PANEL = 'billing_overview_demo'
|
||||
# The slug of the dashboard the PANEL associated with. Required.
|
||||
PANEL_DASHBOARD = 'yuyu_demo'
|
||||
# The slug of the panel group the PANEL is associated with.
|
||||
PANEL_GROUP = 'billing'
|
||||
|
||||
# Python panel class of the PANEL to be added.
|
||||
ADD_PANEL = 'openstack_dashboard.dashboards.yuyu_demo.admin.billing_overview_demo.panel.BillingOverviewDemo'
|
|
@ -0,0 +1,9 @@
|
|||
# The slug of the panel to be added to HORIZON_CONFIG. Required.
|
||||
PANEL = 'price_configuration_demo'
|
||||
# The slug of the dashboard the PANEL associated with. Required.
|
||||
PANEL_DASHBOARD = 'yuyu_demo'
|
||||
# The slug of the panel group the PANEL is associated with.
|
||||
PANEL_GROUP = 'billing'
|
||||
|
||||
# Python panel class of the PANEL to be added.
|
||||
ADD_PANEL = 'openstack_dashboard.dashboards.yuyu_demo.admin.price_configuration_demo.panel.PriceConfigurationDemo'
|
9
yuyu_demo/local/enabled/_6604_admin_billing_setting.py
Normal file
9
yuyu_demo/local/enabled/_6604_admin_billing_setting.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
# The slug of the panel to be added to HORIZON_CONFIG. Required.
|
||||
PANEL = 'billing_setting_demo'
|
||||
# The slug of the dashboard the PANEL associated with. Required.
|
||||
PANEL_DASHBOARD = 'yuyu_demo'
|
||||
# The slug of the panel group the PANEL is associated with.
|
||||
PANEL_GROUP = 'billing'
|
||||
|
||||
# Python panel class of the PANEL to be added.
|
||||
ADD_PANEL = 'openstack_dashboard.dashboards.yuyu_demo.admin.billing_setting_demo.panel.BillingSettingDemo'
|
|
@ -0,0 +1,9 @@
|
|||
# The slug of the panel to be added to HORIZON_CONFIG. Required.
|
||||
PANEL = 'projects_invoice_demo'
|
||||
# The slug of the dashboard the PANEL associated with. Required.
|
||||
PANEL_DASHBOARD = 'yuyu_demo'
|
||||
# The slug of the panel group the PANEL is associated with.
|
||||
PANEL_GROUP = 'billing'
|
||||
|
||||
# Python panel class of the PANEL to be added.
|
||||
ADD_PANEL = 'openstack_dashboard.dashboards.yuyu_demo.admin.projects_invoice_demo.panel.ProjectsInvoiceDemo'
|
|
@ -0,0 +1,9 @@
|
|||
# The slug of the panel to be added to HORIZON_CONFIG. Required.
|
||||
PANEL = 'notification_center_demo'
|
||||
# The slug of the dashboard the PANEL associated with. Required.
|
||||
PANEL_DASHBOARD = 'yuyu_demo'
|
||||
# The slug of the panel group the PANEL is associated with.
|
||||
PANEL_GROUP = 'billing'
|
||||
|
||||
# Python panel class of the PANEL to be added.
|
||||
ADD_PANEL = 'openstack_dashboard.dashboards.yuyu_demo.admin.notification_center_demo.panel.NotificationCenterDemo'
|
|
@ -0,0 +1,9 @@
|
|||
# The slug of the panel to be added to HORIZON_CONFIG. Required.
|
||||
PANEL = 'projects_balance_demo'
|
||||
# The slug of the dashboard the PANEL associated with. Required.
|
||||
PANEL_DASHBOARD = 'yuyu_demo'
|
||||
# The slug of the panel group the PANEL is associated with.
|
||||
PANEL_GROUP = 'billing'
|
||||
|
||||
# Python panel class of the PANEL to be added.
|
||||
ADD_PANEL = 'openstack_dashboard.dashboards.yuyu_demo.admin.projects_balance_demo.panel.ProjectsBalanceDemo'
|
Loading…
Add table
Reference in a new issue