Merge branch '21-Features/setting-billing-company' into 'main'

Resolve "Setting Billing Yuyu"

Closes #21

See merge request dev/yuyu_dashboard!8
This commit is contained in:
Setyo Nugroho 2022-07-08 06:25:25 +00:00
commit 725e647446
22 changed files with 417 additions and 5 deletions

View file

@ -18,6 +18,7 @@ ln -sf $root/yuyu/local/enabled/_6111_project_billing_panel_group.py $horizon_pa
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/_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
echo "Symlink Creation Done"
echo "Now you can configure and yuyu dashboard"

View file

@ -0,0 +1,37 @@
from django.utils.translation import ugettext_lazy as _
from horizon import forms, messages, exceptions
from openstack_dashboard.dashboards.yuyu.cases.setting_use_case import SettingUseCase
class SettingForm(forms.SelfHandlingForm):
NAME = "Settings"
USE_CASE = SettingUseCase()
company_name = forms.CharField(label=_("COMPANY NAME"),
required=False)
company_logo = forms.URLField(label=_("COMPANY LOGO URL"),
required=False)
company_address = forms.CharField(label=_("COMPANY ADDRESS"),
required=False)
email_admin = forms.EmailField(label=_("EMAIL ADMIN"),
required=True)
invoice_tax = forms.IntegerField(label=_("INVOICE TAX (%)"),
required=True)
def handle(self, request, data):
try:
result = ""
for k, v in data.items():
result = self.USE_CASE.set_setting(
request=request,
key=k,
value=v
)
messages.success(request, _(f"Successfully update {self.NAME}"))
return result
except Exception as e:
exceptions.handle(request,
_('Unable to update.'))

View file

@ -0,0 +1,11 @@
from openstack_dashboard.dashboards.yuyu.core.billing_setting.tables import BaseUpdateSettingAction, BaseSettingTable
class UpdateSettingAction(BaseUpdateSettingAction):
url = "horizon:admin:billing_setting:update_setting"
class SettingTable(BaseSettingTable):
class Meta(object):
table_actions = (UpdateSettingAction,)

View file

@ -0,0 +1,8 @@
{% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% block modal-body-right %}
<h3>{% trans "Description:" %}</h3>
<p>{% trans 'Update a billing setting.' %}</p>
{% endblock %}

View file

@ -0,0 +1,8 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Update Billing Setting" %}{% endblock %}
{% block main %}
{% include "admin/billing_setting/_form_setting.html" %}
{% endblock %}

View file

@ -4,6 +4,14 @@
{% 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">
@ -13,8 +21,7 @@
{% else %}
<h1>Billing Disabled</h1> <br/>
<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 %} disabled {% endif %}">Enable</a>
<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 Billing Data</a>
{% endif %}
</div>

View file

@ -0,0 +1,38 @@
{% 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">&times;</span></button>
Tax not complete
</div>
</div>
</div>
{% endif %}
{% if missing_setting.email_notification %}
<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">&times;</span></button>
Project Email Notification 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">&times;</span></button>
Email Admin not complete
</div>
</div>
</div>
{% endif %}

View file

@ -19,5 +19,7 @@ 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'^update_setting/$',
views.UpdateSettingView.as_view(), name='update_setting'),
]

View file

@ -10,27 +10,70 @@
# License for the specific language governing permissions and limitations
# under the License.
from django import shortcuts
from django.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _
from horizon import views, exceptions, messages
from horizon import views, exceptions, messages, tables, forms
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.utils.price_checker import has_missing_price
from .forms import SettingForm
from .tables import SettingTable
class IndexView(views.APIView):
class IndexView(tables.DataTableView):
page_title = _("Setting")
template_name = "admin/billing_setting/index.html"
table_class = SettingTable
setting_uc = SettingUseCase()
def get_data(self):
try:
setting_uc = self.setting_uc.get_setting_admin(self.request)
except Exception:
setting_uc = []
exceptions.handle(self.request,
_("Unable to retrieve data."))
return setting_uc
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if hasattr(self, "table"):
context[self.context_object_name] = self.table
context['setting'] = self.setting_uc.get_settings(self.request)
context['missing_price'] = has_missing_price(self.request)
context['missing_setting'] = self.setting_uc.has_missing_setting(self.request)
return context
class UpdateSettingView(forms.ModalFormView):
form_class = SettingForm
form_id = "setting_form_update"
modal_id = "update_setting_modal"
modal_header = _("Update Setting")
page_title = _("Setting")
submit_label = _("Update Setting")
submit_url = reverse_lazy("horizon:admin:billing_setting:update_setting")
success_url = reverse_lazy("horizon:admin:billing_setting:index")
template_name = 'admin/billing_setting/form_setting.html'
setting_uc = SettingUseCase()
def get_initial(self):
try:
setting_uc = dict(self.setting_uc.get_setting_admin(self.request))
except Exception:
setting_uc = None
exceptions.handle(self.request,
_("Unable to retrieve setting."))
return setting_uc
class EnableBillingView(views.APIView):
invoice_uc = InvoiceUseCase()

View file

@ -2,6 +2,15 @@ from openstack_dashboard.dashboards.yuyu.core import yuyu_client
class ProjectOverviewUseCase:
def get_tenant(self, request):
response = yuyu_client.get(request, f"project_overview/{request.user.tenant_id}/get_tenant/")
keys_to_include = ['email_notification', ]
return [x for x in response.json().items() if x[0] in keys_to_include]
def update_email(self, request, payload):
return yuyu_client.post(request, f"project_overview/{request.user.tenant_id}/update_email/", payload).json()
def total_resource(self, request):
response = yuyu_client.get(request, f"project_overview/total_resource/?tenant_id={request.user.tenant_id}")
return response.json()

View file

@ -2,10 +2,32 @@ from openstack_dashboard.dashboards.yuyu.core import yuyu_client
class SettingUseCase:
def get_settings(self, request):
return yuyu_client.get(request, "settings/").json()
response = yuyu_client.get(request, "settings/").json()
return response
def set_setting(self, request, key, value):
return yuyu_client.patch(request, f"settings/{key}/", {
"value": value
}).json()
def get_setting_admin(self, request):
keys_to_exclude = ['billing_enabled',
'email_notification']
response = self.get_settings(request)
return [x for x in response.items() if x[0] not in keys_to_exclude]
def has_missing_setting(self, request):
missing = [None, '']
response = self.get_settings(request)
context = {x[0]: True for x in response.items() if x[1] in missing}
if context:
context['has_missing'] = True
return context
context['has_missing'] = True
return context

View file

@ -0,0 +1,48 @@
from django.urls import reverse
from django.utils.translation import ugettext_lazy as _
from horizon import tables
class SettingName:
SETTING_NAMES = {
"billing_enabled": _("Billing Enabled"),
"invoice_tax": _("Invoice Tax"),
"company_name": _("Company Name"),
"company_logo": _("Company Logo"),
"company_address": _("Company Address"),
"email_admin": _("Email Admin"),
"email_notification": _("Email Notification")
}
def get_setting_name(self, setting):
return self.SETTING_NAMES.get(setting[0], setting[0].replace("_", " ").title())
def get_setting_value(self, setting):
return setting[1]
class BaseUpdateSettingAction(tables.LinkAction):
name = "update_setting"
verbose_name = _("Update Setting")
url = None
classes = ("ajax-modal",)
icon = "pencil"
step = None
def get_link_url(self, datum=None):
return reverse(self.url)
class BaseSettingTable(tables.DataTable):
setting_name = SettingName()
name = tables.Column(setting_name.get_setting_name, verbose_name=_('Setting Name'))
value = tables.Column(setting_name.get_setting_value, verbose_name=_('Value'))
def get_object_id(self, obj):
return obj
class Meta(object):
name = "settings"
verbose_name = _("Settings")
multi_select = False

View file

@ -0,0 +1,10 @@
# The slug of the panel to be added to HORIZON_CONFIG. Required.
PANEL = 'billing_setting'
# 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.billing_setting.panel.BillingSetting'
# ADD_PANEL = 'openstack_dashboard.dashboards.yuyu.project.invoice.panel.Invoice'

View file

View file

@ -0,0 +1,26 @@
from django.utils.translation import ugettext_lazy as _
from horizon import forms, messages, exceptions
from openstack_dashboard.dashboards.yuyu.cases.project_overview_use_case import ProjectOverviewUseCase
class SettingForm(forms.SelfHandlingForm):
NAME = "Settings"
USE_CASE = ProjectOverviewUseCase()
email_notification = forms.EmailField(label=_("EMAIL NOTIFICATION"),
required=True)
def handle(self, request, data):
try:
print(data)
result = self.USE_CASE.update_email(
request=request,
payload=data
)
messages.success(request, _(f"Successfully update {self.NAME}"))
return result
except Exception as e:
exceptions.handle(request,
_('Unable to update.'))

View 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 BillingSetting(horizon.Panel):
name = _("Billing Setting")
slug = "billing_setting"

View file

@ -0,0 +1,10 @@
from openstack_dashboard.dashboards.yuyu.core.billing_setting.tables import BaseSettingTable, BaseUpdateSettingAction
class UpdateSettingAction(BaseUpdateSettingAction):
url = "horizon:project:billing_setting:update_setting"
class SettingTable(BaseSettingTable):
class Meta(object):
table_actions = (UpdateSettingAction,)

View file

@ -0,0 +1,8 @@
{% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% block modal-body-right %}
<h3>{% trans "Description:" %}</h3>
<p>{% trans 'Update a billing setting.' %}</p>
{% endblock %}

View file

@ -0,0 +1,8 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Create/Update Volume Price" %}{% endblock %}
{% block main %}
{% include "project/billing_setting/_form_setting.html" %}
{% endblock %}

View file

@ -0,0 +1,12 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Billing Setting" %}{% endblock %}
{% block main %}
{# Form #}
<div class="row">
<div class="col-sm-12">
{{ table.render }}
</div>
</div>
{% endblock %}

View 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.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^update_setting/$',
views.UpdateSettingView.as_view(), name='update_setting'),
]

View file

@ -0,0 +1,62 @@
# 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 django.urls import reverse_lazy, reverse
from django.utils.translation import ugettext_lazy as _
from horizon import views, exceptions, messages, tables, forms
from openstack_dashboard.dashboards.yuyu.cases.setting_use_case import SettingUseCase
from .forms import SettingForm
from .tables import SettingTable
from ...cases.project_overview_use_case import ProjectOverviewUseCase
class IndexView(tables.DataTableView):
page_title = _("Setting")
template_name = "project/billing_setting/index.html"
table_class = SettingTable
setting_uc = ProjectOverviewUseCase()
def get_data(self):
try:
setting_uc = self.setting_uc.get_tenant(self.request)
except Exception:
setting_uc = []
exceptions.handle(self.request,
_("Unable to retrieve data."))
return setting_uc
class UpdateSettingView(forms.ModalFormView):
form_class = SettingForm
form_id = "setting_form_update"
modal_id = "update_setting_modal"
modal_header = _("Update Setting")
page_title = _("Setting")
submit_label = _("Update Setting")
submit_url = reverse_lazy("horizon:project:billing_setting:update_setting")
success_url = reverse_lazy("horizon:project:billing_setting:index")
template_name = 'project/billing_setting/form_setting.html'
setting_uc = ProjectOverviewUseCase()
def get_initial(self):
try:
setting_uc = dict(self.setting_uc.get_tenant(self.request))
except Exception:
setting_uc = None
exceptions.handle(self.request,
_("Unable to retrieve setting."))
return setting_uc