diff --git a/cloud/endagaweb/forms/dashboard_forms.py b/cloud/endagaweb/forms/dashboard_forms.py index 2f9a6ddc..963616cc 100644 --- a/cloud/endagaweb/forms/dashboard_forms.py +++ b/cloud/endagaweb/forms/dashboard_forms.py @@ -367,4 +367,4 @@ def __init__(self, *args, **kwargs): self.helper.form_method = 'post' self.helper.form_action = '/dashboard/staff/tower-monitoring' self.helper.add_input(Submit('submit', 'Select')) - self.helper.layout = Layout('tower') + self.helper.layout = Layout('tower') \ No newline at end of file diff --git a/cloud/endagaweb/models.py b/cloud/endagaweb/models.py index 65b0780f..cadfb72f 100644 --- a/cloud/endagaweb/models.py +++ b/cloud/endagaweb/models.py @@ -525,6 +525,7 @@ class Subscriber(models.Model): # When toggled, this will protect a subsriber from getting "vacuumed." You # can still delete subs with the usual "deactivate" button. prevent_automatic_deactivation = models.BooleanField(default=False) + valid_through = models.DateTimeField(null=True, auto_now_add=True) @classmethod def update_balance(cls, imsi, other_bal): @@ -978,6 +979,9 @@ class Network(models.Model): # Network environments let you specify things like "prod", "test", "dev", # etc so they can be filtered out of alerts. For internal use. environment = models.TextField(default="default") + # Added for Network Balance Limit + max_account_limit = models.BigIntegerField(default=10000) + max_failure_transaction = models.IntegerField(default=3) class Meta: permissions = ( diff --git a/cloud/endagaweb/tasks.py b/cloud/endagaweb/tasks.py index 405ca698..95774494 100644 --- a/cloud/endagaweb/tasks.py +++ b/cloud/endagaweb/tasks.py @@ -17,6 +17,7 @@ import os import paramiko import zipfile +import pytz try: # we only import zlib here to check that it is available # (why would it not be?), so we have to disable the 'unused' warning @@ -30,6 +31,7 @@ from django.core.mail import send_mail from django.template.loader import render_to_string from django.db.models import Avg, Count +from django.db import transaction import django.utils.timezone import requests @@ -42,6 +44,7 @@ from endagaweb.models import UsageEvent from endagaweb.models import SystemEvent from endagaweb.models import TimeseriesStat +from endagaweb.models import NetworkDenomination from endagaweb.ic_providers.nexmo import NexmoProvider @@ -211,11 +214,32 @@ def update_credit(self, imsi, update_id): url, params={'jwt': jwt}, timeout=settings.ENDAGA['BTS_REQUEST_TIMEOUT_SECS']) if request.status_code >= 200 and request.status_code < 300: - print "update_credit SUCCESS. id=%s, imsi=%s, amount=%s. (%d)" % ( - update_id, imsi, update.amount, request.status_code) - update.delete() - bts.mark_active() - bts.save() + with transaction.atomic(): + # Check for existing denomination range exist. + denom = NetworkDenomination.objects.filter( + start_amount__lte=update.amount, + end_amount__gte=update.amount, + network=update.subscriber.network).order_by('-end_amount') + if len(denom): + denom = denom[0] + expiry_date = datetime.datetime.now(pytz.UTC) + \ + datetime.timedelta(days=denom.validity_days) + if update.subscriber.valid_through: + # Check if existing validity is greater than new validity + # then dont update new validity + if expiry_date >= update.subscriber.valid_through: + update.subscriber.valid_through = expiry_date + else: + # Check if subscriber has no validity set + update.subscriber.valid_through = expiry_date + + update.subscriber.state = 'active' + update.subscriber.save() + print "update_credit SUCCESS. id=%s, imsi=%s, amount=%s. (%d)"\ + % (update_id, imsi, update.amount, request.status_code) + update.delete() + bts.mark_active() + bts.save() else: message = ("update_credit FAIL. id=%s, imsi=%s, (bts=%s), " "amount=%s. (%d)") diff --git a/cloud/endagaweb/templates/dashboard/subscriber_detail/adjust_credit.html b/cloud/endagaweb/templates/dashboard/subscriber_detail/adjust_credit.html index b2078ef7..ad5e2b57 100644 --- a/cloud/endagaweb/templates/dashboard/subscriber_detail/adjust_credit.html +++ b/cloud/endagaweb/templates/dashboard/subscriber_detail/adjust_credit.html @@ -29,7 +29,9 @@
{% for message in messages %} -
{{ message }}
+
+ ×{{ message }} +
{% endfor %}
diff --git a/cloud/endagaweb/tests/test_denomination.py b/cloud/endagaweb/tests/test_denomination.py index 71cd6341..d797824f 100644 --- a/cloud/endagaweb/tests/test_denomination.py +++ b/cloud/endagaweb/tests/test_denomination.py @@ -8,9 +8,22 @@ of patent rights can be found in the PATENTS file in the same directory. """ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +from datetime import datetime +from random import randrange +import uuid from django import test +import json + +import pytz + from django.test import TestCase +from ccm.common import crdt from endagaweb import models diff --git a/cloud/endagaweb/views/dashboard.py b/cloud/endagaweb/views/dashboard.py index fad4b84e..f05b4185 100644 --- a/cloud/endagaweb/views/dashboard.py +++ b/cloud/endagaweb/views/dashboard.py @@ -28,6 +28,7 @@ from django.views.generic import View from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from django.db.models import Q +from endagaweb.models import NetworkDenomination import django_tables2 as tables import csv @@ -510,28 +511,30 @@ def get(self, request, imsi=None): try: subscriber = Subscriber.objects.get(imsi=imsi, network=network) + # Set the response context. + pending_updates = subscriber.pendingcreditupdate_set.all().\ + order_by('date') + initial_form_data = { + 'imsi': subscriber.imsi, + } + context = { + 'networks': get_objects_for_user(request.user, + 'view_network', + klass=Network), + 'currency': CURRENCIES[network.subscriber_currency], + 'user_profile': user_profile, + 'subscriber': subscriber, + 'pending_updates': pending_updates, + 'credit_update_form': dform.SubscriberCreditUpdateForm( + initial=initial_form_data), + } + # Render template. + template = get_template( + 'dashboard/subscriber_detail/adjust_credit.html') + html = template.render(context, request) + return HttpResponse(html) except Subscriber.DoesNotExist: - return HttpResponseBadRequest() - # Set the response context. - pending_updates = subscriber.pendingcreditupdate_set.all().order_by( - 'date') - initial_form_data = { - 'imsi': subscriber.imsi, - } - context = { - 'networks': get_objects_for_user(request.user, 'view_network', klass=Network), - 'currency': CURRENCIES[network.subscriber_currency], - 'user_profile': user_profile, - 'subscriber': subscriber, - 'pending_updates': pending_updates, - 'credit_update_form': dform.SubscriberCreditUpdateForm( - initial=initial_form_data), - } - # Render template. - template = get_template( - 'dashboard/subscriber_detail/adjust_credit.html') - html = template.render(context, request) - return HttpResponse(html) + return dashboard_view(request) def post(self, request, imsi=None): """Operators can use this API to add credit to a subscriber. @@ -553,23 +556,49 @@ def post(self, request, imsi=None): # Validate the input. if 'amount' not in request.POST: return HttpResponseBadRequest() - error_text = 'Error: credit value must be between -10M and 10M.' + error_text = 'Credit value must be between -10M and 10M.' + try: currency = network.subscriber_currency amount = parse_credits(request.POST['amount'], - CURRENCIES[currency]).amount_raw + CURRENCIES[currency]).amount_raw if abs(amount) > 2147483647: + error_text = 'Credit value must be between -10M and 10M.' + raise ValueError(error_text) + if sub.balance + amount > network.max_account_limit: + error_text = 'Error : Crossed Credit Limit.' + raise ValueError(error_text) + try: + # Check for existing denomination range exist. + denom_exists = NetworkDenomination.objects.filter( + start_amount__lte=amount, + end_amount__gte=amount, + network=network).exists() + # Update user validity for recharge denomination amount + if denom_exists: + try: + # Validation suceeded, create a PCU and start the + # update credit task. + msgid = str(uuid.uuid4()) + credit_update = PendingCreditUpdate(subscriber=sub, + uuid=msgid, + amount=amount) + credit_update.save() + tasks.update_credit.delay(sub.imsi, msgid) + return adjust_credit_redirect + except Number.DoesNotExist: + error_text = 'Subscriber has no number assigned.' + raise ValueError(error_text) + else: + error_text = 'Credit value must be in denomination range.' + raise ValueError(error_text) + except NetworkDenomination.DoesNotExist: + error_text = 'Credit value must be in denomination range.' raise ValueError(error_text) except ValueError: - messages.error(request, error_text) + messages.error(request, error_text, + extra_tags="alert alert-danger") return adjust_credit_redirect - # Validation suceeded, create a PCU and start the update credit task. - msgid = str(uuid.uuid4()) - credit_update = PendingCreditUpdate(subscriber=sub, uuid=msgid, - amount=amount) - credit_update.save() - tasks.update_credit.delay(sub.imsi, msgid) - return adjust_credit_redirect def delete(self, request, imsi=None): """Handle the deletion of Pending Credit Updates.""" diff --git a/cloud/endagaweb/views/django_tables.py b/cloud/endagaweb/views/django_tables.py index fe8daf18..83484d97 100644 --- a/cloud/endagaweb/views/django_tables.py +++ b/cloud/endagaweb/views/django_tables.py @@ -364,4 +364,4 @@ def render_action(self, record): "class='btn btn-xs btn-info'>Edit   " % (record.id, record.id) element += "Delete" % (record.id) - return safestring.mark_safe(element) + return safestring.mark_safe(element) \ No newline at end of file diff --git a/cloud/endagaweb/views/network.py b/cloud/endagaweb/views/network.py index 318335c4..9cb54c29 100644 --- a/cloud/endagaweb/views/network.py +++ b/cloud/endagaweb/views/network.py @@ -569,12 +569,12 @@ def post(self, request): denom = models.NetworkDenomination.objects.get( id=dnm_id) # Check for existing denomination range exist. - denom_exists = \ - models.NetworkDenomination.objects.filter( - end_amount__gte=start_amount, - start_amount__lte=end_amount, - network=user_profile.network).exclude( - id=dnm_id).count() + denom_exists = models.NetworkDenomination.objects.\ + filter( + end_amount__gte=start_amount, + start_amount__lte=end_amount, + network=user_profile.network).\ + exclude(id=dnm_id).count() if denom_exists: messages.error( request, 'Denomination range already exists.',