diff --git a/abonapp/migrations/0009_abontariff_death_line.py b/abonapp/migrations/0009_abontariff_death_line.py
new file mode 100644
index 0000000..1152d33
--- /dev/null
+++ b/abonapp/migrations/0009_abontariff_death_line.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9 on 2017-02-16 12:22
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('abonapp', '0008_auto_20170209_0002'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='abontariff',
+ name='deadline',
+ field=models.DateTimeField(blank=True, default=None, null=True),
+ ),
+ ]
diff --git a/abonapp/models.py b/abonapp/models.py
index f5c7d54..eeb94a7 100644
--- a/abonapp/models.py
+++ b/abonapp/models.py
@@ -1,6 +1,5 @@
# -*- coding: utf-8 -*-
from django.utils import timezone
-from django.utils.datetime_safe import datetime
from django.db import models
from django.core.validators import DecimalValidator
from agent import Transmitter, AbonStruct, TariffStruct, NasFailedResult
@@ -10,14 +9,7 @@ from accounts_app.models import UserProfile
class LogicError(Exception):
- def __init__(self, value):
- self.message = value
-
- def __str__(self):
- return repr(self.message)
-
- def __str__(self):
- return repr(self.message)
+ pass
class AbonGroup(models.Model):
@@ -56,6 +48,9 @@ class AbonTariff(models.Model):
# время начала действия, остальные что не начали действие - NULL
time_start = models.DateTimeField(null=True, blank=True, default=None)
+ # время завершения услуги
+ deadline = models.DateTimeField(null=True, blank=True, default=None)
+
def priority_up(self):
# ищем услугу с большим приоритетом(число приоритета меньше)
target_abtar = AbonTariff.objects.filter(
@@ -108,21 +103,24 @@ class AbonTariff(models.Model):
# Считает текущую стоимость услуг согласно выбранной для тарифа логики оплаты (см. в документации)
def calc_amount_service(self):
- calc_obj = self.tariff.get_calc_type()
+ calc_obj = self.tariff.get_calc_type()(self)
# calc_obj - instance of tariff_app.custom_tariffs.TariffBase
- amount = calc_obj.calc_amount(self)
+ amount = calc_obj.calc_amount()
return round(amount, 2)
# Активируем тариф
def activate(self, current_user):
+ calc_obj = self.tariff.get_calc_type()(self)
amnt = self.calc_amount_service()
# если не хватает денег
if self.abon.ballance < amnt:
raise LogicError('Не хватает денег на счету')
- # дата активации услуги
+ # считаем дату активации услуги
self.time_start = timezone.now()
+ # считаем дату завершения услуги
+ self.deadline = calc_obj.calc_deadline()
# снимаем деньги за услугу
- self.abon.make_pay(current_user, amnt)
+ self.abon.make_pay(current_user, amnt, u_comment='Завершение и оплата услуги по истечению срока действия')
self.save()
# Используется-ли услуга сейчас, если время старта есть то он активирован
@@ -209,7 +207,7 @@ class Abon(UserProfile):
self.ballance += amount
# покупаем тариф
- def buy_tariff(self, tariff, author, comment=None):
+ def pick_tariff(self, tariff, author, comment=None):
assert isinstance(tariff, Tariff)
# выбераем связь ТарифАбонент с самым низким приоритетом
@@ -241,47 +239,29 @@ class Abon(UserProfile):
def activate_next_tariff(self, author):
ats = AbonTariff.objects.filter(abon=self).order_by('tariff_priority')
- nw = datetime.now(tz=timezone.get_current_timezone())
+ nw = timezone.datetime.now()
for at in ats:
- # Если активированный тариф
- if not at.is_started():
- return
-
- # время к началу месяца
- to_start_month = datetime(nw.year, nw.month, 1, tzinfo=timezone.get_current_timezone())
-
- # проверяем расстояние от Сегодня до начала этого месяца.
- # И от заказа тарифа до начала этого месяца
- if (nw - at.time_start) > (nw - to_start_month):
- # Заказ из прошлого месяца, срок действия закончен
- print('Заказ из прошлого месяца, срок действия закончен')
-
+ # если услуга просрочена
+ if nw > at.deadline:
+ print("Услуга просрочена, отключаем, и подключаем новую")
# выберем следующую по приоритету
# next_tarifs = AbonTariff.objects.filter(tariff_priority__gt = self.tariff_priority, abon=self.abon)
- next_tarifs = filter(lambda tr: tr.tariff_priority > at.tariff_priority, ats)[:2]
+ next_tarifs = [tr for tr in ats if tr.tariff_priority > at.tariff_priority][:2]
+ #next_tarifs = filter(lambda tr: tr.tariff_priority > at.tariff_priority, ats)[:2]
- # и если что-нибудь вернулось то активируем, давая время начала действия
+ # и если что-нибудь из списка следующих услуг вернулось - то активируем
if len(next_tarifs) > 0:
- next_tarifs[0].time_start = nw
- next_tarifs[0].save(update_fields=['time_start'])
+ next_tarifs[0].activate(author)
- # завершаем текущую услугу.
+ # удаляем запись о текущей услугу.
at.delete()
- # Создаём лог о завершении услуги
- AbonLog.objects.create(
- abon=self,
- amount=0,
- author=author,
- comment='Завершение услуги по истечению срока действия'
- )
-
# есть-ли доступ у абонента к услуге, смотрим в tariff_app.custom_tariffs..manage_access()
def is_access(self):
trf = self.active_tariff()
if not trf: return False
- ct = trf.get_calc_type()
+ ct = trf.get_calc_type()()
if ct.manage_access(self):
return True
else:
@@ -311,7 +291,7 @@ class InvoiceForPayment(models.Model):
author = models.ForeignKey(UserProfile, related_name='+')
def __str__(self):
- return "%s -> %d $" % (self.abon.username, self.amount)
+ return "%s -> %.2f" % (self.abon.username, self.amount)
def set_ok(self):
self.status = True
diff --git a/abonapp/urls_abon.py b/abonapp/urls_abon.py
index 45fcf77..730e3b8 100644
--- a/abonapp/urls_abon.py
+++ b/abonapp/urls_abon.py
@@ -1,5 +1,4 @@
from django.conf.urls import url
-
from . import views
@@ -14,7 +13,7 @@ urlpatterns = [
url(r'^(?P\d+)/pay_history', views.pay_history, name='abon_phistory'),
url(r'^(?P\d+)/addinvoice$', views.add_invoice, name='add_invoice'),
- url(r'^(?P\d+)/buy$', views.buy_tariff, name='buy_tariff'),
+ url(r'^(?P\d+)/pick$', views.pick_tariff, name='pick_tariff'),
url(r'^(?P\d+)/chpriority$', views.chpriority, name='chpriority_tariff'),
url(r'^(?P\d+)/complete_service(?P\d+)$', views.complete_service, name='compl_srv'),
url(r'^(?P\d+)/activate_service(?P\d+)$', views.activate_service, name='activate_service'),
diff --git a/abonapp/views.py b/abonapp/views.py
index 26d0f2f..2b04f05 100644
--- a/abonapp/views.py
+++ b/abonapp/views.py
@@ -285,6 +285,7 @@ def abonhome(request, gid, uid):
})
+@mydefs.require_ssl
def terminal_pay(request):
from .pay_systems import allpay
ret_text = allpay(request)
@@ -329,7 +330,7 @@ def add_invoice(request, gid, uid):
@login_required
@permission_required('abonapp.can_buy_tariff')
-def buy_tariff(request, gid, uid):
+def pick_tariff(request, gid, uid):
frm = None
grp = get_object_or_404(models.AbonGroup, id=gid)
abon = get_object_or_404(models.Abon, id=uid)
@@ -338,7 +339,7 @@ def buy_tariff(request, gid, uid):
frm = forms.BuyTariff(request.POST)
if frm.is_valid():
cd = frm.cleaned_data
- abon.buy_tariff(cd['tariff'], request.user)
+ abon.pick_tariff(cd['tariff'], request.user)
#abon.save()
messages.success(request, 'Тариф успешно выбран')
return redirect('abonapp:abon_services', gid=gid, uid=abon.id)
@@ -433,7 +434,7 @@ def activate_service(request, gid, uid, srvid):
abtar.activate(request.user)
messages.success(request, 'Услуга активирована')
- return redirect('abonapp:abon_home', gid, uid)
+ return redirect('abonapp:abon_services', gid, uid)
except NasFailedResult as e:
messages.error(request, e)
@@ -441,12 +442,14 @@ def activate_service(request, gid, uid, srvid):
messages.warning(request, e)
except models.LogicError as e:
messages.error(request, e)
+ calc_obj = abtar.tariff.get_calc_type()(abtar)
return render(request, 'abonapp/activate_service.html', {
'abon': abtar.abon,
'abon_group': abtar.abon.group,
'abtar': abtar,
'amount': amount,
- 'diff': abtar.abon.ballance - amount
+ 'diff': abtar.abon.ballance - amount,
+ 'deadline': calc_obj.calc_deadline()
})
diff --git a/clientsideapp/views.py b/clientsideapp/views.py
index 63e48bf..bd04e14 100644
--- a/clientsideapp/views.py
+++ b/clientsideapp/views.py
@@ -45,7 +45,7 @@ def buy_service(request, srv_id):
service = get_object_or_404(Tariff, id=srv_id)
current_service = abon.active_tariff()
if request.method == 'POST':
- abon.buy_tariff(service, request.user, 'Покупка тарифного плана через личный кабинет, тариф "%s"'
+ abon.pick_tariff(service, request.user, 'Покупка тарифного плана через личный кабинет, тариф "%s"'
% service)
messages.success(request, 'Вы подписались на новую услугу. Она встала на очередь подключений. '
'Когда закончится ваша текущая услуга, то включится эта')
diff --git a/cron.py b/cron.py
index ff04abb..b1c19a0 100755
--- a/cron.py
+++ b/cron.py
@@ -2,14 +2,13 @@
# -*- coding: utf-8 -*-
import os
import django
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djing.settings")
+django.setup()
+from abonapp.models import Abon, LogicError
+from agent import Transmitter, NasNetworkError, NasFailedResult
-if __name__ == "__main__":
- os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djing.settings")
- django.setup()
- from abonapp.models import Abon
- from agent import Transmitter
-
+def main():
tm = Transmitter()
# получим инфу о записях в NAS
@@ -17,45 +16,63 @@ if __name__ == "__main__":
users = Abon.objects.all()
for user in users:
+ try:
+ # бдим за услугами абонента: просроченные отключить, заказанные подключить
+ user.activate_next_tariff(user)
+
+ # если нет ip то и нет смысла лезть в NAS
+ if user.ip_address is None:
+ continue
+
+ # а есть-ли у абонента доступ к услуге
+ if not user.is_access():
+ continue
- # если нет ip то и нет смысла лезть в NAS
- if user.ip_address is None:
- continue
-
- # а есть-ли у абонента доступ к услуге
- if not user.is_access():
- continue
-
- # строим структуру агента
- ab = user.build_agent_struct()
- if ab is None:
- # если не построилась структура агента, значит нет ip
- # а если нет ip то и синхронизировать абонента без ip нельзя
- continue
-
- # ищем абонента в списке инфы из nas
- abons = [queue for queue in queues if queue is not None]
- abons = [{'abon': queue.abon, 'mikro_id': queue.sid} for queue in queues if queue.abon.uid == user.pk]
- abons_len = len(abons)
- if abons_len < 1:
- # абонент не найден в nas, добавим
- tm.add_user(ab)
- continue
- elif abons_len > 1:
- # удаляем срез из nas, всё кроме 1й записи
- tm.remove_user_range(
- [mkid['mikro_id'] for mkid in abons[1:]]
- )
- # один абонент
- # сравним совпадает-ли инфа об абоненте в базе и в nas
- if ab == abons[0]['abon']:
- # если всё совпадает, то менять нечего
- continue
- else:
- # иначе обновляем абонента
- tm.update_user(ab, abons[0]['mikro_id'])
- # если не активен то приостановим услугу
- if user.is_active:
- tm.start_user(abons[0]['mikro_id'])
+ # строим структуру агента
+ ab = user.build_agent_struct()
+ if ab is None:
+ # если не построилась структура агента, значит нет ip
+ # а если нет ip то и синхронизировать абонента без ip нельзя
+ continue
+
+ # ищем абонента в списке инфы из nas
+ abons = [queue for queue in queues if queue is not None]
+ abons = [{'abon': queue.abon, 'mikro_id': queue.sid} for queue in abons if queue.abon.uid == user.pk]
+ abons_len = len(abons)
+ if abons_len < 1:
+ # абонент не найден в nas, добавим
+ tm.add_user(ab)
+ continue
+ elif abons_len > 1:
+ # удаляем срез из nas, всё кроме 1й записи
+ tm.remove_user_range(
+ [mkid['mikro_id'] for mkid in abons[1:]]
+ )
+ # один абонент
+ # сравним совпадает-ли инфа об абоненте в базе и в nas
+ if ab == abons[0]['abon']:
+ # если всё совпадает, то менять нечего
+ continue
else:
- tm.pause_user(abons[0]['mikro_id'])
+ # иначе обновляем абонента
+ tm.update_user(ab, abons[0]['mikro_id'])
+ # если не активен то приостановим услугу
+ if user.is_active:
+ tm.start_user(abons[0]['mikro_id'])
+ else:
+ tm.pause_user(abons[0]['mikro_id'])
+ except NasNetworkError as er:
+ print("Error:", er)
+ except NasFailedResult as er:
+ print("Error:", er)
+ except LogicError as er:
+ print("Notice:", er)
+
+
+if __name__ == "__main__":
+ try:
+ main()
+ except NasNetworkError as e:
+ print(e)
+ except NasFailedResult as e:
+ print(e)
diff --git a/mydefs.py b/mydefs.py
index e122e32..51e7256 100644
--- a/mydefs.py
+++ b/mydefs.py
@@ -5,11 +5,12 @@ import socket
import struct
from collections import Iterator
import os
-from django.http import HttpResponse, Http404
+from functools import wraps
+from django.http import HttpResponse, Http404, HttpResponseRedirect
from django.shortcuts import redirect
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
from django.db import models
-from djing.settings import PAGINATION_ITEMS_PER_PAGE
+from djing.settings import PAGINATION_ITEMS_PER_PAGE, DEBUG
ip_addr_regex = r'^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$'
@@ -138,6 +139,7 @@ def order_helper(request):
# Декоратор проверяет аккаунт, чтоб не пускать клиентов в страницы администрации
def only_admins(fn):
+ @wraps(fn)
def wrapped(request, *args, **kwargs):
if request.user.is_admin:
return fn(request, *args, **kwargs)
@@ -180,3 +182,18 @@ class RuTimedelta(timedelta):
ru_days = 'день'
text_date = '%d %s %s' % (self.days, ru_days, text_date)
return text_date
+
+
+def require_ssl(view):
+ """
+ Decorator that requires an SSL connection. If the current connection is not SSL, we redirect to the SSL version of
+ the page.
+ from: https://gist.github.com/ckinsey/9709984
+ """
+ @wraps(view)
+ def wrapper(request, *args, **kwargs):
+ if not DEBUG and not request.is_secure():
+ target_url = "https://" + request.META['HTTP_HOST'] + request.path_info
+ return HttpResponseRedirect(target_url)
+ return view(request, *args, **kwargs)
+ return wrapper
diff --git a/tariff_app/base_intr.py b/tariff_app/base_intr.py
index 2014a5e..caa6723 100644
--- a/tariff_app/base_intr.py
+++ b/tariff_app/base_intr.py
@@ -5,12 +5,12 @@ from abc import ABCMeta, abstractmethod
class TariffBase(metaclass=ABCMeta):
@abstractmethod
- def calc_amount(self, abon_tariff):
+ def calc_amount(self):
"""Считает итоговую сумму платежа"""
@abstractmethod
- def get_avail_time(self):
- """Возвращает оставшееся время услуги в секундах"""
+ def calc_deadline(self):
+ """Считаем дату, до которой действкет тариф"""
@staticmethod
def description():
diff --git a/tariff_app/custom_tariffs.py b/tariff_app/custom_tariffs.py
index b4f0abe..7cef333 100644
--- a/tariff_app/custom_tariffs.py
+++ b/tariff_app/custom_tariffs.py
@@ -1,42 +1,46 @@
# -*- coding: utf-8 -*-
-from datetime import datetime, timedelta
+from datetime import timedelta
from django.utils import timezone
from .base_intr import TariffBase
+from calendar import monthrange
# from abonapp import AbonTariff
class TariffDefault(TariffBase):
- # Базовый функционал считает стоимость пропорционально использованному времени
- def calc_amount(self, abon_tariff):
+
+ def __init__(self, abon_tariff):
#assert isinstance(abon_tariff, AbonTariff)
+ self.abon_tariff = abon_tariff
+
+ # Базовый функционал считает стоимость пропорционально использованному времени
+ def calc_amount(self):
# сейчас
nw = timezone.now()
# сколько прошло с начала действия услуги
# если времени начала нет то это начало действия, использованное время 0
- time_diff = nw - abon_tariff.time_start if abon_tariff.time_start else timedelta(0)
+ time_diff = nw - self.abon_tariff.time_start if self.abon_tariff.time_start else timedelta(0)
# времени в этом месяце
- curr_month_time = datetime(nw.year, nw.month if nw.month == 12 else nw.month + 1, 1) - timedelta(days=1)
+ curr_month_time = timezone.datetime(nw.year, nw.month if nw.month == 12 else nw.month + 1, 1) - timedelta(days=1)
curr_month_time = timedelta(days=curr_month_time.day)
# Сколько это в процентах от всего месяца (k - коеффициент)
k = time_diff.total_seconds() / curr_month_time.total_seconds()
# результат - это полная стоимость тарифа умноженная на k
- res = k * abon_tariff.tariff.amount
+ res = k * self.abon_tariff.tariff.amount
return float(res)
- # возвращаем сколько времени осталось до завершения услуги (конца месяца)
- def get_avail_time(self):
- from calendar import monthrange
+ # Тут мы расчитываем конец действия услуги, завершение будет в конце месяца
+ def calc_deadline(self):
nw = timezone.now()
last_day = monthrange(nw.year, nw.month)[1]
- last_month_date = datetime(year=nw.year, month=nw.month, day=last_day,
- hour=23,minute=59, second=59,tzinfo=nw.tzinfo)
- return last_month_date - nw
+ last_month_date = timezone.datetime(year=nw.year, month=nw.month, day=last_day,
+ hour=23, minute=59, second=59)
+ return timezone.make_aware(last_month_date)
@staticmethod
def description():
@@ -47,8 +51,8 @@ class TariffDp(TariffDefault):
# в IS снимается вся стоимость тарифа вне зависимости от времени использования
# просто возвращаем всю стоимость тарифа
- def calc_amount(self, abon_tariff):
- return float(abon_tariff.tariff.amount)
+ def calc_amount(self):
+ return float(self.abon_tariff.tariff.amount)
@staticmethod
def description():
@@ -56,7 +60,7 @@ class TariffDp(TariffDefault):
class TariffCp(TariffDefault):
- def calc_amount(self, abon_tariff):
+ def calc_amount(self):
return 12.6
@staticmethod
diff --git a/tariff_app/models.py b/tariff_app/models.py
index 411f5c0..508276a 100644
--- a/tariff_app/models.py
+++ b/tariff_app/models.py
@@ -28,7 +28,7 @@ class Tariff(models.Model):
if len(ob) > 0:
res_type = ob[0][1]
assert issubclass(res_type, TariffBase)
- return res_type()
+ return res_type
def __str__(self):
return "%s (%.2f)" % (self.title, self.amount)
diff --git a/templates/abonapp/activate_service.html b/templates/abonapp/activate_service.html
index 14c2827..bfa6dfd 100644
--- a/templates/abonapp/activate_service.html
+++ b/templates/abonapp/activate_service.html
@@ -24,7 +24,9 @@
Вы уверены что хотите активировать абоненту эту услугу?
Обратите внимание что с его счёта снимутся деньги и откроется доступ к ресурсам оплаченной
услуги.
- Стоимость услуги: {{ amount }}руб. На счету {{ abon.ballance }} руб, останется {{ diff }} руб.
+ Стоимость услуги: {{ amount }}руб. На счету {{ abon.ballance }} руб, останется {{ diff }} руб.
+ Услуга будет действовать до {{ deadline|date:'d F Y, H:i:s' }}
+