Add Balance Feature
This commit is contained in:
parent
d700e2a3d4
commit
6c6f70202e
32 changed files with 675 additions and 32 deletions
|
@ -13,11 +13,13 @@ rm $horizon_path/openstack_dashboard/local/enabled/_6103_admin_billing_price_con
|
||||||
rm $horizon_path/openstack_dashboard/local/enabled/_6104_admin_billing_setting.py
|
rm $horizon_path/openstack_dashboard/local/enabled/_6104_admin_billing_setting.py
|
||||||
rm $horizon_path/openstack_dashboard/local/enabled/_6104_admin_billing_setting.py
|
rm $horizon_path/openstack_dashboard/local/enabled/_6104_admin_billing_setting.py
|
||||||
rm $horizon_path/openstack_dashboard/local/enabled/_6105_admin_billing_projects_invoice.py
|
rm $horizon_path/openstack_dashboard/local/enabled/_6105_admin_billing_projects_invoice.py
|
||||||
rm $horizon_path/openstack_dashboard/local/enabled/_6105_admin_notification_center.py
|
rm $horizon_path/openstack_dashboard/local/enabled/_6106_admin_notification_center.py
|
||||||
|
rm $horizon_path/openstack_dashboard/local/enabled/_6107_admin_billing_projects_balance.py
|
||||||
|
|
||||||
rm $horizon_path/openstack_dashboard/local/enabled/_6111_project_billing_panel_group.py
|
rm $horizon_path/openstack_dashboard/local/enabled/_6111_project_billing_panel_group.py
|
||||||
rm $horizon_path/openstack_dashboard/local/enabled/_6112_project_billing_overview.py
|
rm $horizon_path/openstack_dashboard/local/enabled/_6112_project_billing_overview.py
|
||||||
rm $horizon_path/openstack_dashboard/local/enabled/_6113_project_billing_usage_cost.py
|
rm $horizon_path/openstack_dashboard/local/enabled/_6113_project_billing_usage_cost.py
|
||||||
rm $horizon_path/openstack_dashboard/local/enabled/_6114_project_billing_invoice.py
|
rm $horizon_path/openstack_dashboard/local/enabled/_6114_project_billing_invoice.py
|
||||||
rm $horizon_path/openstack_dashboard/local/enabled/_6115_project_billing_setting.py
|
rm $horizon_path/openstack_dashboard/local/enabled/_6115_project_billing_setting.py
|
||||||
|
rm $horizon_path/openstack_dashboard/local/enabled/_6116_project_balance.py
|
||||||
echo "Yuyu Removal Done"
|
echo "Yuyu Removal Done"
|
|
@ -14,12 +14,14 @@ ln -sf $root/yuyu/local/enabled/_6104_admin_billing_setting.py $horizon_path/ope
|
||||||
ln -sf $root/yuyu/local/enabled/_6104_admin_billing_setting.py $horizon_path/openstack_dashboard/local/enabled/_6104_admin_billing_setting.py
|
ln -sf $root/yuyu/local/enabled/_6104_admin_billing_setting.py $horizon_path/openstack_dashboard/local/enabled/_6104_admin_billing_setting.py
|
||||||
ln -sf $root/yuyu/local/enabled/_6105_admin_billing_projects_invoice.py $horizon_path/openstack_dashboard/local/enabled/_6105_admin_billing_projects_invoice.py
|
ln -sf $root/yuyu/local/enabled/_6105_admin_billing_projects_invoice.py $horizon_path/openstack_dashboard/local/enabled/_6105_admin_billing_projects_invoice.py
|
||||||
ln -sf $root/yuyu/local/enabled/_6106_admin_notification_center.py $horizon_path/openstack_dashboard/local/enabled/_6105_admin_notification_center.py
|
ln -sf $root/yuyu/local/enabled/_6106_admin_notification_center.py $horizon_path/openstack_dashboard/local/enabled/_6105_admin_notification_center.py
|
||||||
|
ln -sf $root/yuyu/local/enabled/_6107_admin_billing_projects_balance.py $horizon_path/openstack_dashboard/local/enabled/_6107_admin_billing_projects_balance.py
|
||||||
|
|
||||||
ln -sf $root/yuyu/local/enabled/_6111_project_billing_panel_group.py $horizon_path/openstack_dashboard/local/enabled/_6111_project_billing_panel_group.py
|
ln -sf $root/yuyu/local/enabled/_6111_project_billing_panel_group.py $horizon_path/openstack_dashboard/local/enabled/_6111_project_billing_panel_group.py
|
||||||
ln -sf $root/yuyu/local/enabled/_6112_project_billing_overview.py $horizon_path/openstack_dashboard/local/enabled/_6112_project_billing_overview.py
|
ln -sf $root/yuyu/local/enabled/_6112_project_billing_overview.py $horizon_path/openstack_dashboard/local/enabled/_6112_project_billing_overview.py
|
||||||
ln -sf $root/yuyu/local/enabled/_6113_project_billing_usage_cost.py $horizon_path/openstack_dashboard/local/enabled/_6113_project_billing_usage_cost.py
|
ln -sf $root/yuyu/local/enabled/_6113_project_billing_usage_cost.py $horizon_path/openstack_dashboard/local/enabled/_6113_project_billing_usage_cost.py
|
||||||
ln -sf $root/yuyu/local/enabled/_6114_project_billing_invoice.py $horizon_path/openstack_dashboard/local/enabled/_6114_project_billing_invoice.py
|
ln -sf $root/yuyu/local/enabled/_6114_project_billing_invoice.py $horizon_path/openstack_dashboard/local/enabled/_6114_project_billing_invoice.py
|
||||||
ln -sf $root/yuyu/local/enabled/_6115_project_billing_setting.py $horizon_path/openstack_dashboard/local/enabled/_6115_project_billing_setting.py
|
ln -sf $root/yuyu/local/enabled/_6115_project_billing_setting.py $horizon_path/openstack_dashboard/local/enabled/_6115_project_billing_setting.py
|
||||||
|
ln -sf $root/yuyu/local/enabled/_6116_project_balance.py $horizon_path/openstack_dashboard/local/enabled/_6116_project_balance.py
|
||||||
|
|
||||||
echo "Symlink Creation Done"
|
echo "Symlink Creation Done"
|
||||||
echo "Now you can configure and use yuyu dashboard"
|
echo "Now you can configure and use yuyu dashboard"
|
|
@ -23,6 +23,10 @@ class SettingForm(forms.SelfHandlingForm):
|
||||||
|
|
||||||
invoice_tax = forms.IntegerField(label=_("INVOICE TAX (%)"),
|
invoice_tax = forms.IntegerField(label=_("INVOICE TAX (%)"),
|
||||||
required=True)
|
required=True)
|
||||||
|
invoice_auto_deduct_balance = forms.BooleanField(label=_("Invoice Auto Deduct Balance"),
|
||||||
|
required=False, )
|
||||||
|
how_to_top_up = forms.CharField(label=_("HOW TO TOP UP"),
|
||||||
|
required=True, widget=forms.Textarea())
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
data = super(SettingForm, self).clean()
|
data = super(SettingForm, self).clean()
|
||||||
|
|
|
@ -8,5 +8,7 @@
|
||||||
<p>{% trans '<b>Company Logo</b> : A logo that will be used 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>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 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 %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
0
yuyu/admin/projects_balance/__init__.py
Normal file
0
yuyu/admin/projects_balance/__init__.py
Normal file
65
yuyu/admin/projects_balance/forms.py
Normal file
65
yuyu/admin/projects_balance/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.'))
|
20
yuyu/admin/projects_balance/panel.py
Normal file
20
yuyu/admin/projects_balance/panel.py
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# 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 ProjectsBalance(horizon.Panel):
|
||||||
|
name = _("Projects Balance")
|
||||||
|
slug = "projects_balance"
|
74
yuyu/admin/projects_balance/tables.py
Normal file
74
yuyu/admin/projects_balance/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/admin/projects_balance/urls.py
Normal file
24
yuyu/admin/projects_balance/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'),
|
||||||
|
]
|
144
yuyu/admin/projects_balance/views.py
Normal file
144
yuyu/admin/projects_balance/views.py
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
# 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 horizon import tables
|
||||||
|
from openstack_dashboard import api
|
||||||
|
from .forms import TopUpForm, TopDownForm
|
||||||
|
from .tables import BalanceProjectTable, BalanceTransactionTable
|
||||||
|
from ...cases.balance_use_case import BalanceUseCase
|
||||||
|
from keystoneclient import exceptions as keystone_exceptions
|
||||||
|
|
||||||
|
|
||||||
|
class IndexView(tables.DataTableView):
|
||||||
|
table_class = BalanceProjectTable
|
||||||
|
page_title = _("Balance")
|
||||||
|
template_name = "admin/projects_balance/balance_projects.html"
|
||||||
|
|
||||||
|
balance_uc = BalanceUseCase()
|
||||||
|
|
||||||
|
def get_data(self):
|
||||||
|
try:
|
||||||
|
data = []
|
||||||
|
project_list, has_more = api.keystone.tenant_list(self.request)
|
||||||
|
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'
|
||||||
|
project_id = 'invalid'
|
||||||
|
if len(project) > 0:
|
||||||
|
project_name = project[0].name
|
||||||
|
project_id = project[0].id
|
||||||
|
|
||||||
|
data.append({
|
||||||
|
'id': d['id'],
|
||||||
|
'project_id': project_id,
|
||||||
|
'project': project_name,
|
||||||
|
'amount': Money(amount=d['amount'], currency=d['amount_currency']),
|
||||||
|
})
|
||||||
|
return list(data)
|
||||||
|
except Exception as e:
|
||||||
|
error_message = _('Unable to get balance')
|
||||||
|
exceptions.handle(self.request, error_message)
|
||||||
|
|
||||||
|
|
||||||
|
class DetailBalanceView(tables.DataTableView):
|
||||||
|
table_class = BalanceTransactionTable
|
||||||
|
template_name = "admin/projects_balance/balance_table.html"
|
||||||
|
|
||||||
|
balance_uc = BalanceUseCase()
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
project_id = self.kwargs['project_id']
|
||||||
|
balance = self.balance_uc.retrieve_by_project(self.request, project_id)
|
||||||
|
try:
|
||||||
|
project = api.keystone.tenant_get(self.request, project_id)
|
||||||
|
except keystone_exceptions.NotFound:
|
||||||
|
project = 'Unknown'
|
||||||
|
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context['page_title'] = f'Project {project.name} Balance'
|
||||||
|
context['amount'] = Money(amount=balance['amount'], currency=balance['amount_currency']) if balance else 0
|
||||||
|
return context
|
||||||
|
|
||||||
|
def get_data(self):
|
||||||
|
project_id = self.kwargs['project_id']
|
||||||
|
data = []
|
||||||
|
|
||||||
|
for d in self.balance_uc.transaction_by_project(self.request, project_id):
|
||||||
|
data.append({
|
||||||
|
'id': d['id'],
|
||||||
|
'date': formats.date_format(dateutil.parser.isoparse(d['created_at']), 'd M Y H:m'),
|
||||||
|
'amount': Money(amount=d['amount'], currency=d['amount_currency']),
|
||||||
|
'action': d['action'],
|
||||||
|
'description': d['description'],
|
||||||
|
})
|
||||||
|
return list(data)
|
||||||
|
|
||||||
|
|
||||||
|
class TopUpView(forms.ModalFormView):
|
||||||
|
form_class = TopUpForm
|
||||||
|
form_id = "top_up_form"
|
||||||
|
modal_id = "top_up_modal"
|
||||||
|
modal_header = _("Top Up")
|
||||||
|
page_title = _("Top Up")
|
||||||
|
submit_label = _("Top Up")
|
||||||
|
template_name = 'admin/projects_balance/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:admin:projects_balance:top_up', kwargs={
|
||||||
|
'project_id': self.kwargs['project_id']
|
||||||
|
})
|
||||||
|
return context
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse('horizon:admin:projects_balance:balance_detail', kwargs={
|
||||||
|
'project_id': self.kwargs['project_id']
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
class TopDownView(forms.ModalFormView):
|
||||||
|
form_class = TopDownForm
|
||||||
|
form_id = "top_down_form"
|
||||||
|
modal_id = "top_down_modal"
|
||||||
|
modal_header = _("Top Down")
|
||||||
|
page_title = _("Top Down")
|
||||||
|
submit_label = _("Top Down")
|
||||||
|
template_name = 'admin/projects_balance/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:admin:projects_balance:top_down', kwargs={
|
||||||
|
'project_id': self.kwargs['project_id']
|
||||||
|
})
|
||||||
|
return context
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse('horizon:admin:projects_balance:balance_detail', kwargs={
|
||||||
|
'project_id': self.kwargs['project_id']
|
||||||
|
})
|
||||||
|
|
|
@ -2,17 +2,7 @@
|
||||||
{% block title %}{{ page_title }}{% endblock %}
|
{% block title %}{{ page_title }}{% endblock %}
|
||||||
{% block main %}
|
{% block main %}
|
||||||
{% if invoice %}
|
{% if invoice %}
|
||||||
{% if invoice.state == 2 %}
|
{% include 'admin/projects_invoice/manage_invoice_modal.html' with invoice=invoice project_balance_amount=project_balance_amount %}
|
||||||
<a class="btn btn-primary"
|
|
||||||
href="{% url 'horizon:admin:projects_invoice:finish_invoice' invoice.id %}?next={{ request.path }}">Set
|
|
||||||
to Finished</a>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if invoice.state == 100 %}
|
|
||||||
<a class="btn btn-danger"
|
|
||||||
href="{% url 'horizon:admin:projects_invoice:rollback_to_unpaid' invoice.id %}?next={{ request.path }}">Rollback
|
|
||||||
to Unpaid</a>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<button onclick="javascript:downloadPdf();" class="btn btn-default">Download PDF</button>
|
<button onclick="javascript:downloadPdf();" class="btn btn-default">Download PDF</button>
|
||||||
|
|
||||||
|
|
|
@ -2,19 +2,10 @@
|
||||||
{% block title %}{{ page_title }}{% endblock %}
|
{% block title %}{{ page_title }}{% endblock %}
|
||||||
{% block main %}
|
{% block main %}
|
||||||
<a class="btn btn-default" href="?print=true" target="_blank">Download PDF</a>
|
<a class="btn btn-default" href="?print=true" target="_blank">Download PDF</a>
|
||||||
{% if invoice.state == 2 %}
|
{% include 'admin/projects_invoice/manage_invoice_modal.html' with invoice=invoice project_balance_amount=project_balance_amount %}
|
||||||
<a class="btn btn-primary"
|
<br/>
|
||||||
href="{% url 'horizon:admin:projects_invoice:finish_invoice' invoice.id %}?next={{ request.path }}">Set to
|
<br/>
|
||||||
Finished</a>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if invoice.state == 100 %}
|
|
||||||
<a class="btn btn-danger"
|
|
||||||
href="{% url 'horizon:admin:projects_invoice:rollback_to_unpaid' invoice.id %}?next={{ request.path }}">Rollback
|
|
||||||
to Unpaid</a>
|
|
||||||
{% endif %}
|
|
||||||
<br/>
|
|
||||||
<br/>
|
|
||||||
{% include 'admin/projects_invoice/base_invoice.html' %}
|
{% include 'admin/projects_invoice/base_invoice.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block js %}
|
{% block js %}
|
||||||
|
|
|
@ -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>
|
|
@ -27,6 +27,7 @@ from horizon import views
|
||||||
from openstack_dashboard import api
|
from openstack_dashboard import api
|
||||||
from openstack_dashboard.dashboards.yuyu.cases.invoice_use_case import InvoiceUseCase
|
from openstack_dashboard.dashboards.yuyu.cases.invoice_use_case import InvoiceUseCase
|
||||||
from .tables import InvoiceTable
|
from .tables import InvoiceTable
|
||||||
|
from ...cases.balance_use_case import BalanceUseCase
|
||||||
from ...cases.setting_use_case import SettingUseCase
|
from ...cases.setting_use_case import SettingUseCase
|
||||||
from ...core.usage_cost.tables import InstanceCostTable, VolumeCostTable, FloatingIpCostTable, RouterCostTable, \
|
from ...core.usage_cost.tables import InstanceCostTable, VolumeCostTable, FloatingIpCostTable, RouterCostTable, \
|
||||||
SnapshotCostTable, ImageCostTable
|
SnapshotCostTable, ImageCostTable
|
||||||
|
@ -42,7 +43,7 @@ class IndexView(tables.DataTableView):
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['project_list'], _ = api.keystone.tenant_list(self.request, user=self.request.user.id)
|
context['project_list'], _ = api.keystone.tenant_list(self.request)
|
||||||
context['current_project_id'] = self.request.GET.get('project_id', self.request.user.project_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)
|
context['current_project_name'] = self.request.GET.get('project_name', self.request.user.project_id)
|
||||||
return context
|
return context
|
||||||
|
@ -71,6 +72,7 @@ class InvoiceView(views.APIView):
|
||||||
|
|
||||||
invoice_uc = InvoiceUseCase()
|
invoice_uc = InvoiceUseCase()
|
||||||
setting_uc = SettingUseCase()
|
setting_uc = SettingUseCase()
|
||||||
|
balance_uc = BalanceUseCase()
|
||||||
|
|
||||||
def get_template_names(self):
|
def get_template_names(self):
|
||||||
if self.request.GET.get('print', None):
|
if self.request.GET.get('print', None):
|
||||||
|
@ -81,8 +83,11 @@ class InvoiceView(views.APIView):
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
invoice = self.invoice_uc.get_invoice(self.request, self.kwargs['id'], tenant_id=self.kwargs['project_id'])
|
invoice = self.invoice_uc.get_invoice(self.request, self.kwargs['id'], tenant_id=self.kwargs['project_id'])
|
||||||
|
balance = self.balance_uc.retrieve_by_project(self.request, self.kwargs['project_id'])
|
||||||
context['invoice'] = invoice
|
context['invoice'] = invoice
|
||||||
context['setting'] = self.setting_uc.get_settings(self.request)
|
context['setting'] = self.setting_uc.get_settings(self.request)
|
||||||
|
context['project_balance_amount'] = Money(amount=balance['amount'],
|
||||||
|
currency=balance['amount_currency']) if balance else 0
|
||||||
context['instance_cost'] = self.get_sum_price(invoice, 'instances')
|
context['instance_cost'] = self.get_sum_price(invoice, 'instances')
|
||||||
context['volume_cost'] = self.get_sum_price(invoice, 'volumes')
|
context['volume_cost'] = self.get_sum_price(invoice, 'volumes')
|
||||||
context['fip_cost'] = self.get_sum_price(invoice, 'floating_ips')
|
context['fip_cost'] = self.get_sum_price(invoice, 'floating_ips')
|
||||||
|
@ -108,6 +113,7 @@ class UsageCostView(tables.MultiTableView):
|
||||||
template_name = "admin/projects_invoice/cost_tables.html"
|
template_name = "admin/projects_invoice/cost_tables.html"
|
||||||
|
|
||||||
invoice_uc = InvoiceUseCase()
|
invoice_uc = InvoiceUseCase()
|
||||||
|
balance_uc = BalanceUseCase()
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
@ -119,7 +125,10 @@ class UsageCostView(tables.MultiTableView):
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
|
balance = self.balance_uc.retrieve_by_project(self.request, self.kwargs['project_id'])
|
||||||
context['invoice'] = self.request.invoice
|
context['invoice'] = self.request.invoice
|
||||||
|
context['project_balance_amount'] = Money(amount=balance['amount'],
|
||||||
|
currency=balance['amount_currency']) if balance else 0
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def _get_flavor_name(self, flavor_id):
|
def _get_flavor_name(self, flavor_id):
|
||||||
|
@ -257,7 +266,9 @@ class FinishInvoice(views.APIView):
|
||||||
invoice_uc = InvoiceUseCase()
|
invoice_uc = InvoiceUseCase()
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
self.invoice_uc.finish_invoice(request, kwargs['id'])
|
skip_balance = request.GET.get('skip_balance', '')
|
||||||
|
self.invoice_uc.\
|
||||||
|
finish_invoice(request, kwargs['id'], skip_balance)
|
||||||
next_url = request.GET.get('next', reverse('horizon:admin:projects_invoice:index'))
|
next_url = request.GET.get('next', reverse('horizon:admin:projects_invoice:index'))
|
||||||
return HttpResponseRedirect(next_url)
|
return HttpResponseRedirect(next_url)
|
||||||
|
|
||||||
|
@ -266,6 +277,7 @@ class RollbackToUnpaidInvoice(views.APIView):
|
||||||
invoice_uc = InvoiceUseCase()
|
invoice_uc = InvoiceUseCase()
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
self.invoice_uc.rollback_to_unpaid_invoice(request, kwargs['id'])
|
skip_balance = request.GET.get('skip_balance', '')
|
||||||
|
self.invoice_uc.rollback_to_unpaid_invoice(request, kwargs['id'], skip_balance)
|
||||||
next_url = request.GET.get('next', reverse('horizon:admin:projects_invoice:index'))
|
next_url = request.GET.get('next', reverse('horizon:admin:projects_invoice:index'))
|
||||||
return HttpResponseRedirect(next_url)
|
return HttpResponseRedirect(next_url)
|
||||||
|
|
34
yuyu/cases/balance_use_case.py
Normal file
34
yuyu/cases/balance_use_case.py
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
from openstack_dashboard.dashboards.yuyu.core import yuyu_client
|
||||||
|
|
||||||
|
|
||||||
|
class BalanceUseCase():
|
||||||
|
def list(self, request):
|
||||||
|
response = yuyu_client.get(request, f"balance/")
|
||||||
|
|
||||||
|
return response.json()
|
||||||
|
|
||||||
|
def retrieve_by_project(self, request, tenant_id=None):
|
||||||
|
if not tenant_id:
|
||||||
|
tenant_id = request.user.project_id
|
||||||
|
response = yuyu_client.get(request, f"balance/{tenant_id}/retrieve_by_project/")
|
||||||
|
|
||||||
|
if response.status_code == 404:
|
||||||
|
return None
|
||||||
|
return response.json()
|
||||||
|
|
||||||
|
def transaction_by_project(self, request, tenant_id=None):
|
||||||
|
if not tenant_id:
|
||||||
|
tenant_id = request.user.project_id
|
||||||
|
response = yuyu_client.get(request, f"balance/{tenant_id}/transaction_by_project/")
|
||||||
|
|
||||||
|
if response.status_code == 404:
|
||||||
|
return []
|
||||||
|
return response.json()
|
||||||
|
|
||||||
|
def top_up(self, request, tenant_id, payload):
|
||||||
|
response = yuyu_client.post(request, f"balance/{tenant_id}/top_up_by_project/", payload)
|
||||||
|
return response.json()
|
||||||
|
|
||||||
|
def top_down(self, request, tenant_id, payload):
|
||||||
|
response = yuyu_client.post(request, f"balance/{tenant_id}/top_down_by_project/", payload)
|
||||||
|
return response.json()
|
|
@ -110,12 +110,12 @@ class InvoiceUseCase:
|
||||||
def reset_billing(self, request):
|
def reset_billing(self, request):
|
||||||
yuyu_client.post(request, f"invoice/reset_billing/", {})
|
yuyu_client.post(request, f"invoice/reset_billing/", {})
|
||||||
|
|
||||||
def finish_invoice(self, request, id):
|
def finish_invoice(self, request, id, skip_balance):
|
||||||
response = yuyu_client.get(request, f"invoice/{id}/finish/")
|
response = yuyu_client.get(request, f"invoice/{id}/finish/?skip_balance={skip_balance}")
|
||||||
data = response.json()
|
data = response.json()
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def rollback_to_unpaid_invoice(self, request, id):
|
def rollback_to_unpaid_invoice(self, request, id, skip_balance):
|
||||||
response = yuyu_client.get(request, f"invoice/{id}/rollback_to_unpaid/")
|
response = yuyu_client.get(request, f"invoice/{id}/rollback_to_unpaid/?skip_balance={skip_balance}")
|
||||||
data = response.json()
|
data = response.json()
|
||||||
return data
|
return data
|
|
@ -17,6 +17,11 @@ class SettingUseCase:
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
def get_setting(self, request, key):
|
||||||
|
response = yuyu_client.get(request, f"settings/{key}").json()
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
def set_setting(self, request, key, value):
|
def set_setting(self, request, key, value):
|
||||||
return yuyu_client.patch(request, f"settings/{key}/", {
|
return yuyu_client.patch(request, f"settings/{key}/", {
|
||||||
"value": value
|
"value": value
|
||||||
|
|
|
@ -12,7 +12,8 @@ class SettingName:
|
||||||
"company_logo": _("Company Logo"),
|
"company_logo": _("Company Logo"),
|
||||||
"company_address": _("Company Address"),
|
"company_address": _("Company Address"),
|
||||||
"email_admin": _("Email Admin"),
|
"email_admin": _("Email Admin"),
|
||||||
"email_notification": _("Email Notification")
|
"email_notification": _("Email Notification"),
|
||||||
|
'how_to_top_up': _("How To Top Up"),
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_setting_name(self, setting):
|
def get_setting_name(self, setting):
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
# The slug of the panel to be added to HORIZON_CONFIG. Required.
|
||||||
|
PANEL = 'projects_balance'
|
||||||
|
# The slug of the dashboard the PANEL associated with. Required.
|
||||||
|
PANEL_DASHBOARD = 'admin'
|
||||||
|
# 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.admin.projects_balance.panel.ProjectsBalance'
|
10
yuyu/local/enabled/_6116_project_balance.py
Normal file
10
yuyu/local/enabled/_6116_project_balance.py
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
# The slug of the panel to be added to HORIZON_CONFIG. Required.
|
||||||
|
PANEL = 'balance'
|
||||||
|
# The slug of the dashboard the PANEL associated with. Required.
|
||||||
|
PANEL_DASHBOARD = 'project'
|
||||||
|
# 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.project.balance.panel.Balance'
|
||||||
|
# ADD_PANEL = 'openstack_dashboard.dashboards.yuyu.project.invoice.panel.Invoice'
|
0
yuyu/project/balance/__init__.py
Normal file
0
yuyu/project/balance/__init__.py
Normal file
20
yuyu/project/balance/panel.py
Normal file
20
yuyu/project/balance/panel.py
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# 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 Balance(horizon.Panel):
|
||||||
|
name = _("Balance")
|
||||||
|
slug = "balance"
|
21
yuyu/project/balance/tables.py
Normal file
21
yuyu/project/balance/tables.py
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from horizon import tables
|
||||||
|
|
||||||
|
|
||||||
|
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")
|
33
yuyu/project/balance/templates/balance/balance_table.html
Normal file
33
yuyu/project/balance/templates/balance/balance_table.html
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
{% 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 %}
|
||||||
|
<h3>Current Balance: {{ amount }}</h3>
|
||||||
|
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#how_to_topup_modal">
|
||||||
|
How To Top Up?
|
||||||
|
</button>
|
||||||
|
<div class="modal fade" id="how_to_topup_modal" tabindex="-1" role="dialog" aria-labelledby="howToTopUpLabel">
|
||||||
|
<div class="modal-dialog" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h4 class="modal-title" id="myModalLabel">How To Top Up?</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
{{ how_to_top_up.how_to_top_up }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<hr>
|
||||||
|
<br>
|
||||||
|
<h3>Balance History</h3>
|
||||||
|
{{ table.render }}
|
||||||
|
{% endblock %}
|
||||||
|
{% block js %}
|
||||||
|
{{ block.super }}
|
||||||
|
{% endblock %}
|
19
yuyu/project/balance/urls.py
Normal file
19
yuyu/project/balance/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 openstack_dashboard.dashboards.yuyu.project.balance import views
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
url(r'^$', views.IndexView.as_view(), name='index'),
|
||||||
|
]
|
51
yuyu/project/balance/views.py
Normal file
51
yuyu/project/balance/views.py
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
# 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.utils import formats
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from djmoney.money import Money
|
||||||
|
|
||||||
|
from horizon import exceptions
|
||||||
|
from horizon import tables
|
||||||
|
from .tables import BalanceTransactionTable
|
||||||
|
from ...cases.balance_use_case import BalanceUseCase
|
||||||
|
from ...cases.setting_use_case import SettingUseCase
|
||||||
|
|
||||||
|
|
||||||
|
class IndexView(tables.DataTableView):
|
||||||
|
table_class = BalanceTransactionTable
|
||||||
|
page_title = _("Balance")
|
||||||
|
template_name = "project/balance/balance_table.html"
|
||||||
|
|
||||||
|
balance_uc = BalanceUseCase()
|
||||||
|
setting_uc = SettingUseCase()
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
balance = self.balance_uc.retrieve_by_project(self.request)
|
||||||
|
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context['amount'] = Money(amount=balance['amount'], currency=balance['amount_currency']) if balance else 0
|
||||||
|
context['how_to_top_up'] = self.setting_uc.get_setting(self.request, 'how_to_top_up')
|
||||||
|
return context
|
||||||
|
|
||||||
|
def get_data(self):
|
||||||
|
data = []
|
||||||
|
|
||||||
|
for d in self.balance_uc.transaction_by_project(self.request):
|
||||||
|
data.append({
|
||||||
|
'id': d['id'],
|
||||||
|
'date': formats.date_format(dateutil.parser.isoparse(d['created_at']), 'd M Y H:m'),
|
||||||
|
'amount': Money(amount=d['amount'], currency=d['amount_currency']),
|
||||||
|
'action': d['action'],
|
||||||
|
'description': d['description'],
|
||||||
|
})
|
||||||
|
return list(data)
|
Loading…
Add table
Reference in a new issue