Browse Source

На стадии реализации..

devel
Dmitry 9 years ago
parent
commit
9daac62549
  1. 30
      abonapp/locale/ru/LC_MESSAGES/django.po
  2. 241
      abonapp/models.py
  3. 2
      abonapp/templates/abonapp/buy_tariff.html
  4. 26
      abonapp/templates/abonapp/peoples.html
  5. 54
      abonapp/templates/abonapp/service.html
  6. 101
      abonapp/templates/abonapp/services.html
  7. 225
      abonapp/tests.py
  8. 2
      abonapp/urls_abon.py
  9. 67
      abonapp/views.py
  10. 3
      bugs.txt
  11. 2
      clientsideapp/views.py
  12. 2
      cron.py
  13. 2
      djing/utils/save_from_nodeny.py

30
abonapp/locale/ru/LC_MESSAGES/django.po

@ -57,10 +57,6 @@ msgstr "Не хватает денег на счету"
msgid "finish service perm" msgid "finish service perm"
msgstr "Снятие со счёта средств" msgstr "Снятие со счёта средств"
#: abonapp/models.py:145
msgid "activate service perm"
msgstr "Активация услуги абонента"
#: abonapp/models.py:162 #: abonapp/models.py:162
msgid "Digital field" msgid "Digital field"
msgstr "Цифровое поле" msgstr "Цифровое поле"
@ -94,10 +90,6 @@ msgstr "Может просматривать паспортные данные"
msgid "Buy service default log" msgid "Buy service default log"
msgstr "Покупка тарифного плана через админку" msgstr "Покупка тарифного плана через админку"
#: abonapp/models.py:303
msgid "service overdue log"
msgstr "Услуга просрочена, отключаем, и подключаем новую"
#: abonapp/models.py:346 #: abonapp/models.py:346
msgid "Ip address already exist" msgid "Ip address already exist"
msgstr "Такой ip уже у кого-то есть" msgstr "Такой ip уже у кого-то есть"
@ -598,10 +590,6 @@ msgstr "Не найдены улицы для группы"
msgid "Services of subscriber" msgid "Services of subscriber"
msgstr "Купленные абонентом услуги (назначенные тарифные планы)" msgstr "Купленные абонентом услуги (назначенные тарифные планы)"
#: abonapp/templates/abonapp/services.html:9
msgid "Priority"
msgstr "Приоритет"
#: abonapp/templates/abonapp/services.html:12 #: abonapp/templates/abonapp/services.html:12
msgid "Input speed" msgid "Input speed"
msgstr "Входящая скорость" msgstr "Входящая скорость"
@ -614,18 +602,6 @@ msgstr "Исходящая скорость"
msgid "Works until" msgid "Works until"
msgstr "Действует до" msgstr "Действует до"
#: abonapp/templates/abonapp/services.html:15
msgid "Do"
msgstr "Действия"
#: abonapp/templates/abonapp/services.html:52
msgid "Priority up"
msgstr "Повысить приоритет"
#: abonapp/templates/abonapp/services.html:58
msgid "Priority down"
msgstr "Понизить приоритет"
#: abonapp/templates/abonapp/services.html:64 #: abonapp/templates/abonapp/services.html:64
msgid "Delete service" msgid "Delete service"
msgstr "Удалить услугу" msgstr "Удалить услугу"
@ -902,3 +878,9 @@ msgstr "Звонки не найдены"
msgid "Dialing" msgid "Dialing"
msgstr "Звонки" msgstr "Звонки"
msgid "That service already activated"
msgstr "Эта услуга уже подключена"
msgid "Service already activated"
msgstr "Услуга уже подключена"

241
abonapp/models.py

@ -41,107 +41,42 @@ class AbonLog(models.Model):
class AbonTariff(models.Model): class AbonTariff(models.Model):
abon = models.ForeignKey('Abon')
def __init__(self, deadline=None, *args, **kwargs):
super(AbonTariff, self).__init__(*args, **kwargs)
calc_obj = self.tariff.get_calc_type()(self)
self.time_start = timezone.now()
if deadline is None:
self.deadline = calc_obj.calc_deadline()
else:
self.deadline = deadline
tariff = models.ForeignKey(Tariff, related_name='linkto_tariff') tariff = models.ForeignKey(Tariff, related_name='linkto_tariff')
tariff_priority = models.PositiveSmallIntegerField(default=0)
# время начала действия, остальные что не начали действие - NULL
# время начала действия услуги
time_start = models.DateTimeField(null=True, blank=True, default=None) time_start = models.DateTimeField(null=True, blank=True, default=None)
# время завершения услуги # время завершения услуги
deadline = 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(
abon=self.abon,
tariff_priority__lt=self.tariff_priority
).order_by('-tariff_priority')[:1]
if target_abtar.count() > 0:
target_abtar = target_abtar[0]
else:
return
# Ищем текущий тариф абонента
active_abtar = AbonTariff.objects.filter(
abon=self.abon
)[:1]
if active_abtar.count() > 0:
active_abtar = active_abtar[0]
else:
return
# Если услуга с которой хотим поменяться приоритетом является текущей то нельзя меняться
if active_abtar == target_abtar:
return
# Swap приоритетов у текущего и найденного с меньшим tariff_priority (большим приоритетом)
tmp_prior = target_abtar.tariff_priority
target_abtar.tariff_priority = self.tariff_priority
target_abtar.save(update_fields=['tariff_priority'])
self.tariff_priority = tmp_prior
self.save(update_fields=['tariff_priority'])
def priority_down(self):
# ищем услугу с меньшим приоритетом
target_abtar = AbonTariff.objects.filter(
abon=self.abon,
tariff_priority__gt=self.tariff_priority
)[:1]
if target_abtar.count() > 0:
target_abtar = target_abtar[0]
else:
# меньше нет, это самая последняя услуга
return
# Swap приоритетов у текущего и найденного с большим tariff_priority (меньшим приоритетом)
tmp_pr = self.tariff_priority
self.tariff_priority = target_abtar.tariff_priority
target_abtar.tariff_priority = tmp_pr
target_abtar.save(update_fields=['tariff_priority'])
self.save(update_fields=['tariff_priority'])
# Считает текущую стоимость услуг согласно выбранной для тарифа логики оплаты (см. в документации)
def calc_amount_service(self): def calc_amount_service(self):
amount = self.tariff.amount amount = self.tariff.amount
return round(amount, 2) return round(amount, 2)
# Активируем тариф
def activate(self, current_user, deadline=None):
calc_obj = self.tariff.get_calc_type()(self)
amnt = self.tariff.amount
# если не хватает денег
if self.abon.ballance < amnt:
raise LogicError(_('not enough money'))
# считаем дату активации услуги
self.time_start = timezone.now()
# считаем дату завершения услуги
if deadline is None:
self.deadline = calc_obj.calc_deadline()
else:
self.deadline = deadline
# снимаем деньги за услугу
self.abon.make_pay(current_user, amnt)
self.save()
# Используется-ли услуга сейчас, если время старта есть то он активирован # Используется-ли услуга сейчас, если время старта есть то он активирован
def is_started(self): def is_started(self):
return True if self.time_start is not None else False
return False if self.time_start is None else True
def __str__(self): def __str__(self):
return "%d: '%s' - '%s'" % (
self.tariff_priority,
return "'%s' - '%s'" % (
self.tariff.title, self.tariff.title,
self.abon.get_short_name() self.abon.get_short_name()
) )
class Meta: class Meta:
ordering = ('tariff_priority',)
db_table = 'abonent_tariff' db_table = 'abonent_tariff'
unique_together = (('abon', 'tariff', 'tariff_priority'),)
permissions = ( permissions = (
('can_complete_service', _('finish service perm')),
('can_activate_service', _('activate service perm'))
('can_complete_service', _('finish service perm'))
) )
@ -211,7 +146,7 @@ class Opt82(models.Model):
class Abon(UserProfile): class Abon(UserProfile):
current_tariffs = models.ManyToManyField(Tariff, through=AbonTariff)
current_tariff = models.ForeignKey(AbonTariff, null=True, blank=True, on_delete=models.SET_NULL)
group = models.ForeignKey(AbonGroup, models.SET_NULL, blank=True, null=True) group = models.ForeignKey(AbonGroup, models.SET_NULL, blank=True, null=True)
ballance = models.FloatField(default=0.0) ballance = models.FloatField(default=0.0)
ip_address = MyGenericIPAddressField(blank=True, null=True) ip_address = MyGenericIPAddressField(blank=True, null=True)
@ -221,20 +156,9 @@ class Abon(UserProfile):
extra_fields = models.ManyToManyField(ExtraFieldsModel, blank=True) extra_fields = models.ManyToManyField(ExtraFieldsModel, blank=True)
opt82 = models.ForeignKey(Opt82, null=True, blank=True, on_delete=models.SET_NULL) opt82 = models.ForeignKey(Opt82, null=True, blank=True, on_delete=models.SET_NULL)
_act_tar_cache = None
# возвращает текущий тариф для абонента
def active_tariff(self, use_cache=True):
if self._act_tar_cache and use_cache:
return self._act_tar_cache
ats = AbonTariff.objects.filter(abon=self).exclude(time_start=None)
if ats.count() > 0:
self._act_tar_cache = ats[0].tariff
return ats[0].tariff
else:
self._act_tar_cache = None
# возвращает связь с текущим тарифом для абонента
def active_tariff(self):
return self.current_tariff
class Meta: class Meta:
db_table = 'abonent' db_table = 'abonent'
@ -262,23 +186,28 @@ class Abon(UserProfile):
def pick_tariff(self, tariff, author, comment=None, deadline=None): def pick_tariff(self, tariff, author, comment=None, deadline=None):
assert isinstance(tariff, Tariff) assert isinstance(tariff, Tariff)
# выбераем связь ТарифАбонент с самым низким приоритетом
abtrf = AbonTariff.objects.filter(abon=self).order_by('-tariff_priority')[:1]
abtrf = abtrf[0] if abtrf.count() > 0 else None
amount = round(tariff.amount, 2)
# создаём новую связь с приоритетом ещё ниже
new_abtar = AbonTariff(
abon=self,
tariff=tariff,
tariff_priority=abtrf.tariff_priority + 1 if abtrf else -1
)
if self.current_tariff is not None:
if self.current_tariff.tariff == tariff:
# Эта услуга уже подключена
raise LogicError(_('That service already activated'))
else:
# Не надо молча заменять услугу если какая-то уже есть
raise LogicError(_('Service already activated'))
# Если это первая услуга в списке (фильтр по приоритету ничего не вернул)
if not abtrf:
# значит пробуем её активировать
new_abtar.activate(author, deadline)
else:
new_abtar.save()
# если не хватает денег
if self.ballance < amount:
raise LogicError(_('not enough money'))
new_abtar = AbonTariff(deadline=deadline, tariff=tariff)
new_abtar.save()
self.current_tariff = new_abtar
# снимаем деньги за услугу
self.ballance -= amount
self.save()
# Запись об этом в лог # Запись об этом в лог
AbonLog.objects.create( AbonLog.objects.create(
@ -287,43 +216,23 @@ class Abon(UserProfile):
comment=comment or _('Buy service default log') comment=comment or _('Buy service default log')
) )
# Пробует подключить новую услугу если пришло время
def activate_next_tariff(self, author):
ats = AbonTariff.objects.filter(abon=self).order_by('tariff_priority')
# Производим расчёт услуги абонента, т.е. завершаем если пришло время
def bill_service(self, author):
abon_tariff = self.active_tariff()
nw = timezone.now() nw = timezone.now()
for at in ats:
# услуга не активна, продолжаем
if at.deadline is None:
continue
# если услуга просрочена
if nw > at.deadline:
print(_('service overdue log'))
# выберем следующую по приоритету
# next_tarifs = AbonTariff.objects.filter(tariff_priority__gt = self.tariff_priority, abon=self.abon)
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].activate(author)
# удаляем запись о текущей услугу.
at.delete()
return
# если услуга просрочена
if nw > abon_tariff.deadline:
print("Service %s for user %s is overdued, end service" % (abon_tariff.tariff, self))
abon_tariff.delete()
# есть-ли доступ у абонента к услуге, смотрим в tariff_app.custom_tariffs.<TariffBase>.manage_access() # есть-ли доступ у абонента к услуге, смотрим в tariff_app.custom_tariffs.<TariffBase>.manage_access()
def is_access(self): def is_access(self):
ats = AbonTariff.objects.filter(abon=self).exclude(time_start=None)
if not ats or ats.count() < 1:
return False
trf = ats[0].tariff
ct = trf.get_calc_type()(ats[0])
if ct.manage_access(self):
return True
else:
abon_tariff = self.active_tariff()
if abon_tariff is None:
return False return False
trf = abon_tariff.tariff
ct = trf.get_calc_type()(abon_tariff)
return ct.manage_access(self)
# создаём абонента из структуры агента # создаём абонента из структуры агента
def build_agent_struct(self): def build_agent_struct(self):
@ -331,16 +240,17 @@ class Abon(UserProfile):
user_ip = ip2int(self.ip_address) user_ip = ip2int(self.ip_address)
else: else:
return return
inst_tariff = self.active_tariff()
if inst_tariff:
agent_trf = TariffStruct(inst_tariff.id, inst_tariff.speedIn, inst_tariff.speedOut)
else:
abon_tariff = self.active_tariff()
if abon_tariff is None:
agent_trf = TariffStruct() agent_trf = TariffStruct()
else:
trf = abon_tariff.tariff
agent_trf = TariffStruct(trf.id, trf.speedIn, trf.speedOut)
return AbonStruct(self.pk, user_ip, agent_trf, bool(self.is_active)) return AbonStruct(self.pk, user_ip, agent_trf, bool(self.is_active))
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
# проверяем не-ли у кого такого-же ip # проверяем не-ли у кого такого-же ip
if Abon.objects.filter(ip_address=self.ip_address).exclude(pk=self.pk).count() > 0:
if self.ip_address is not None and Abon.objects.filter(ip_address=self.ip_address).exclude(pk=self.pk).count() > 0:
self.is_bad_ip = True self.is_bad_ip = True
raise LogicError(_('Ip address already exist')) raise LogicError(_('Ip address already exist'))
super(Abon, self).save(*args, **kwargs) super(Abon, self).save(*args, **kwargs)
@ -467,40 +377,5 @@ def abon_del_signal(sender, instance, **kwargs):
return True return True
def abontariff_post_save(sender, instance, **kwargs):
# Тут или подключение абону услуги, или изменение приоритета
if not kwargs['created']:
# если изменение приоритета то не говорим об этом NAS'у
return
if instance.abon.ip_address is None:
return
try:
agent_abon = instance.abon.build_agent_struct()
if agent_abon is None:
return True
tm = Transmitter()
tm.update_user(agent_abon)
except (NasFailedResult, NasNetworkError):
return True
def abontariff_del_signal(sender, instance, **kwargs):
if not instance.is_started():
# если удаляем не активную услугу то говорить об этом NAS'у не обязательно
return
if instance.abon.ip_address is None:
# если у абонента нет ip то и создавать правило не на кого
return
try:
agent_abon = instance.abon.build_agent_struct()
tm = Transmitter()
tm.pause_user(agent_abon)
except (NasFailedResult, NasNetworkError):
return True
models.signals.post_save.connect(abon_post_save, sender=Abon)
models.signals.post_delete.connect(abon_del_signal, sender=Abon)
models.signals.post_save.connect(abontariff_post_save, sender=AbonTariff)
models.signals.post_delete.connect(abontariff_del_signal, sender=AbonTariff)
#models.signals.post_save.connect(abon_post_save, sender=Abon)
#models.signals.post_delete.connect(abon_del_signal, sender=Abon)

2
abonapp/templates/abonapp/buy_tariff.html

@ -22,7 +22,7 @@
<form role="form" action="{% url 'abonapp:pick_tariff' abon_group.pk abon.pk %}" <form role="form" action="{% url 'abonapp:pick_tariff' abon_group.pk abon.pk %}"
method="post">{% csrf_token %} method="post">{% csrf_token %}
<div class="form-group"> <div class="form-group">
<label for="id_tariff">{% trans 'Pick a service' %}</label>
<label for="id_tariffs">{% trans 'Pick a service' %}</label>
<div class="input-group"> <div class="input-group">
<span class="input-group-addon"><span class="glyphicon glyphicon-bullhorn"></span></span> <span class="input-group-addon"><span class="glyphicon glyphicon-bullhorn"></span></span>

26
abonapp/templates/abonapp/peoples.html

@ -50,7 +50,7 @@
{% if order_by == 'house' %}<span class="glyphicon glyphicon-filter"></span>{% endif %} {% if order_by == 'house' %}<span class="glyphicon glyphicon-filter"></span>{% endif %}
</th> </th>
<th width="150">{% trans 'Telephone' %}</th> <th width="150">{% trans 'Telephone' %}</th>
<!--<th width="150">{% trans 'Service' %}</th>-->
<th width="150">{% trans 'Service' %}</th>
<th width="50"> <th width="50">
<a href="{% url 'abonapp:people_list' abon_group.pk %}?order_by=ballance&dir={{ dir|default:"down" }}"> <a href="{% url 'abonapp:people_list' abon_group.pk %}?order_by=ballance&dir={{ dir|default:"down" }}">
{% trans 'Ballance' %} {% trans 'Ballance' %}
@ -83,16 +83,16 @@
<td>{{ human.street|default:_('Not assigned') }}</td> <td>{{ human.street|default:_('Not assigned') }}</td>
<td>{{ human.house|default:_('Not assigned') }}</td> <td>{{ human.house|default:_('Not assigned') }}</td>
<td><a href="tel:{{ human.telephone }}">{{ human.telephone }}</a></td> <td><a href="tel:{{ human.telephone }}">{{ human.telephone }}</a></td>
<!--<td>
{ % if human.active_tariff %}
{ % if perms.tariff_app.change_tariff %}
<a href="{ % url 'tarifs:edit' human.active_tariff.pk %}">{ { human.active_tariff.title }}</a>
{ % else %}
{ { human.active_tariff.title }}
{ % endif %}
{ % else %}&mdash;&mdash;&mdash;
{ % endif %}
</td>-->
<td>
{% if human.active_tariff %}
{% if perms.tariff_app.change_tariff %}
<a href="{% url 'tarifs:edit' human.active_tariff.tariff.pk %}">{{ human.active_tariff.tariff.title }}</a>
{% else %}
{{ human.active_tariff.tariff.title }}
{% endif %}
{% else %}&mdash;&mdash;&mdash;
{% endif %}
</td>
<td>{{ human.ballance }}</td> <td>{{ human.ballance }}</td>
<td> <td>
{% if perms.abonapp.delete_abon %} {% if perms.abonapp.delete_abon %}
@ -104,7 +104,7 @@
</tr> </tr>
{% empty %} {% empty %}
<tr> <tr>
<td colspan="10">
<td colspan="11">
{% trans 'Subscribers not found' %}. {% trans 'Subscribers not found' %}.
{% if perms.abonapp.add_abon %} {% if perms.abonapp.add_abon %}
<a href="{% url 'abonapp:add_abon' abon_group.pk %}">{% trans 'Add abon' %}</a> <a href="{% url 'abonapp:add_abon' abon_group.pk %}">{% trans 'Add abon' %}</a>
@ -115,7 +115,7 @@
</tbody> </tbody>
<tfoot> <tfoot>
<tr> <tr>
<td colspan="10" class="btn-group">
<td colspan="11" class="btn-group">
{% if perms.abonapp.add_abon %} {% if perms.abonapp.add_abon %}
<a href="{% url 'abonapp:add_abon' abon_group.pk %}" class="btn btn-sm btn-default" title="Добавить"> <a href="{% url 'abonapp:add_abon' abon_group.pk %}" class="btn btn-sm btn-default" title="Добавить">
<span class="glyphicon glyphicon-plus"></span> {% trans 'Add abon' %} <span class="glyphicon glyphicon-plus"></span> {% trans 'Add abon' %}

54
abonapp/templates/abonapp/service.html

@ -0,0 +1,54 @@
{% extends request.is_ajax|yesno:'nullcont.htm,abonapp/ext.htm' %}
{% load i18n %}
{% block content %}
<div class="row">
<div class="col-sm-6">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{% trans 'Services of subscriber' %}</h3>
</div>
<div class="panel-body">
{% if abon_tariff %}
<p>
<b>{% trans 'Service' %}</b>:
{% if perms.tariff_app.change_tariff %}
<a href="{% url 'tarifs:edit' abon_tariff.pk %}" title="{{ abon_tariff.time_start|default:'' }}">
{{ abon_tariff.tariff.title }}
</a>
{% else %}
{{ abon_tariff.tariff.title }}
{% endif %}
</p>
<i><b>{% trans 'Sum' %}</b>: {{ abon_tariff.tarif.amount }} руб.</i>
<p><b>{% trans 'Input speed' %}</b>: {{ abon_tariff.tariff.speedIn }}</p>
<p><b>{% trans 'Output speed' %}</b>: {{ abon_tariff.tariff.speedOut }}</p>
<p><b>{% trans 'Works until' %}</b>: {{ abon_tariff.deadline|date:"d E Y, l" }}</p>
<p>{{ abon_tariff.tarif.descr }}</p>
{% else %}
No service, <a href="{% url 'abonapp:pick_tariff' abon_group.pk abon.pk %}" class="btn btn-sm btn-success">
<span class="glyphicon glyphicon-plus"></span> {% trans 'Buy service' %}
</a>
{% endif %}
</div>
</div>
</div>
<div class="col-sm-6">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Услуги для заказа</h3>
</div>
<div class="panel-body">
</div>
</div>
</div>
</div>
</div>
{% endblock %}

101
abonapp/templates/abonapp/services.html

@ -1,101 +0,0 @@
{% extends request.is_ajax|yesno:'nullcont.htm,abonapp/ext.htm' %}
{% load i18n %}
{% block content %}
<legend>{% trans 'Services of subscriber' %}</legend>
<table class="table table-striped table-bordered">
<thead>
<tr>
<th width="50">{% trans 'Priority' %}</th>
<th>{% trans 'Service' %}</th>
<th>{% trans 'Sum' %}</th>
<th>{% trans 'Input speed' %}</th>
<th>{% trans 'Output speed' %}</th>
<th>{% trans 'Works until' %}</th>
<th>{% trans 'Do' %}</th>
</tr>
</thead>
<tbody>
{% for trf in abon_tarifs %}
<tr{% if trf.id == active_abontariff_id %} class="active"{% endif %}>
<td>{{ trf.tariff_priority }}</td>
<td>
{% if perms.tariff_app.change_tariff %}
<a href="{% url 'tarifs:edit' trf.tariff.id %}" title="{{ trf.time_start|default:'' }}">
{{ trf.tariff.title }}
</a>
{% else %}
{{ trf.tariff.title }}
{% endif %}
</td>
<td>{{ trf.tariff.amount }}</td>
<td>{{ trf.tariff.speedIn }}</td>
<td>{{ trf.tariff.speedOut }}</td>
<td>{{ trf.deadline|date:"d E Y, l" }}</td>
{% if trf.id != active_abontariff_id %}
<td class="btn-group">
{% if perms.abonapp.can_activate_service %}
{% if not active_abontariff_id %}
<a href="{% url 'abonapp:activate_service' abon_group.id abon.id trf.id %}"
class="btn btn-success btn-sm" title="{% trans 'Activate service' %}">
<i class="glyphicon glyphicon-shopping-cart"></i>
</a>
{% endif %}
{% endif %}
<!-- "{ % url 'abonapp:chpriority_tariff' abon_group.id abon.id % }?t={ { trf.id } }&a=up" -->
<a href="#"
class="btn btn-default btn-sm disabled" title="{% trans 'Priority up' %}">
<i class="glyphicon glyphicon-hand-up"></i>
</a>
<!-- "{ % url 'abonapp:chpriority_tariff' abon_group.id abon.id % }?t={ { trf.id } }&a=down" -->
<a href="#"
class="btn btn-default btn-sm disabled" title="{% trans 'Priority down' %}">
<i class="glyphicon glyphicon-hand-down"></i>
</a>
{% if perms.abonapp.delete_abontariff %}
<a href="{% url 'abonapp:unsubscribe_service' abon_group.id abon.id trf.id %}"
class="btn btn-danger btn-sm" title="{% trans 'Delete service' %}">
<i class="glyphicon glyphicon-remove"></i>
</a>
{% endif %}
</td>
{% else %}
<td>
<a href="{% url 'abonapp:compl_srv' abon_group.id abon.id trf.id %}" class="btn btn-danger btn-sm">
<i class="glyphicon glyphicon-remove"></i> {% trans 'Finish service' %}
</a>
</td>
{% endif %}
</tr>
{% empty %}
<tr>
<td colspan="7">{% trans 'Services of subscribers not found' %}.
{% if perms.abonapp.can_buy_tariff %}
<a href="{% url 'abonapp:pick_tariff' abon_group.id abon.id %}" class="lgtbx">{% trans 'Buy' %}</a>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
{% if perms.abonapp.can_buy_tariff %}
<tfoot>
<tr>
<th colspan="7">
<a href="{% url 'abonapp:pick_tariff' abon_group.id abon.id %}" class="btn btn-sm btn-success">
<span class="glyphicon glyphicon-plus"></span> {% trans 'Buy service' %}
</a>
</th>
</tr>
</tfoot>
{% endif %}
</table>
{% endblock %}

225
abonapp/tests.py

@ -1,225 +0,0 @@
from django.shortcuts import resolve_url
from django.test import TestCase
from django.test.client import Client
from agent import NasNetworkError
from .models import AbonTariff, Abon, AbonGroup, AbonRawPassword
from tariff_app.models import Tariff
from mydefs import LogicError
class AbonTestCase(TestCase):
def setUp(self):
try:
Tariff.objects.create(
title='test_tariff',
descr='taroff descr',
speedIn=1.2,
speedOut=3.0,
amount=3
)
abon = Abon()
abon.username = '1234567'
abon.fio = 'mainuser'
abon.telephone = '+79788328884'
abon.set_password('ps')
abon.is_superuser = True
abon.save()
abon_group = AbonGroup.objects.create(title='abon_group')
abon_group.profiles.add(abon)
except NasNetworkError:
pass
# проверка на пополнение счёта
def test_add_ballance(self):
try:
abon = Abon.objects.get(username='1234567')
ballance = abon.ballance
abon.add_ballance(abon, 13, 'test pay')
abon.save(update_fields=['ballance'])
self.assertEqual(abon.ballance, ballance+13)
ballance = abon.ballance
abon.add_ballance(abon, 5.34, 'test float pay')
abon.save(update_fields=['ballance'])
self.assertEqual(abon.ballance, ballance+5.34)
except NasNetworkError:
pass
# пробуем выбрать услугу
def test_pick_tariff(self):
try:
tariff = Tariff.objects.get(title='test_tariff')
abon = Abon.objects.get(username='1234567')
try:
abon.pick_tariff(tariff, abon)
# нет денег, должно всплыть исключение и сюда дойти мы не должны
self.assertFalse(True)
except LogicError:
pass
act_tar = abon.active_tariff()
# если недостаточно денег на счету
assert abon.ballance <= tariff.amount
# У абонента на счету 0, не должна быть куплена услуга
self.assertEqual(act_tar, None)
# Раз услуги нет то и доступа быть не должно
self.assertTrue(not abon.is_access())
# с деньгами
abon.add_ballance(abon, 7.34, 'add pay for test pick tariff')
abon.pick_tariff(tariff, abon)
act_tar = abon.active_tariff()
# должны получить указанную услугу
self.assertEqual(act_tar, tariff)
# и получить доступ
self.assertTrue(abon.is_access())
except NasNetworkError:
pass
# тестим очередь услуг
def test_services_queue(self):
abon = Abon.objects.get(username='1234567')
tariff = Tariff.objects.get(title='test_tariff')
abon.add_ballance(abon, 9, 'add pay for test services queue')
abon.save()
abon.pick_tariff(tariff, abon)
abon.pick_tariff(tariff, abon)
abon.pick_tariff(tariff, abon)
# снять деньги должно было только за первый выбор, остальные стают в очередь услуг
self.assertEqual(abon.ballance, 6)
c = Client()
# login
c.post(resolve_url('acc_app:login'), {'login': '1234567', 'password': 'ps'})
url = resolve_url('abonapp:compl_srv', gid=1, uid=1, srvid=1)
resp = c.get(url)
print('RESP:', resp)
self.assertEqual(resp.status_code, 200)
resp = c.post(url, data={
'finish_confirm': 'yes'
})
print('RESP:', resp)
# при успешной остановке услуги идёт редирект на др страницу
self.assertEqual(resp.status_code, 302)
# текущей услуги быть не должно
act_tar = abon.active_tariff()
self.assertIsNone(act_tar)
# не активных услуг останется 2
noact_count = AbonTariff.objects.filter(abon=abon).filter(time_start=None).count()
self.assertEqual(noact_count, 2)
# проверяем платёжку alltime
def test_allpay(self):
from hashlib import md5
from djing.settings import pay_SECRET, pay_SERV_ID
import xmltodict
def sig(act, pay_account, pay_id):
md = md5()
s = '_'.join((str(act), str(pay_account), pay_SERV_ID, str(pay_id), pay_SECRET))
md.update(bytes(s, 'utf-8'))
return md.hexdigest()
c = Client()
url = resolve_url('abonapp:terminal_pay')
r = c.get(url, {
'ACT': 1, 'PAY_ACCOUNT': '1234567',
'SERVICE_ID': pay_SERV_ID,
'PAY_ID': 3561234,
'TRADE_POINT': 377,
'SIGN': sig(1, 1234567, 3561234)
})
xobj = xmltodict.parse(r.content)
self.assertEqual(int(xobj['pay-response']['status_code']), 21)
r = c.get(url, {
'ACT': 4, 'PAY_ACCOUNT': '1234567',
'SERVICE_ID': pay_SERV_ID,
'PAY_ID': 3561234,
'PAY_AMOUNT': 1.0,
'TRADE_POINT': 377,
'SIGN': sig(4, 1234567, 3561234)
})
xobj = xmltodict.parse(r.content)
self.assertEqual(int(xobj['pay-response']['status_code']), 22)
r = c.get(url, {
'ACT': 4, 'PAY_ACCOUNT': '1234567',
'SERVICE_ID': pay_SERV_ID,
'PAY_ID': 3561234,
'PAY_AMOUNT': 1.0,
'TRADE_POINT': 377,
'SIGN': sig(4, 1234567, 3561234)
})
xobj = xmltodict.parse(r.content)
self.assertEqual(int(xobj['pay-response']['status_code']), -100)
r = c.get(url, {
'ACT': 7, 'PAY_ACCOUNT': '1234567',
'SERVICE_ID': pay_SERV_ID,
'PAY_ID': 3561234,
'PAY_AMOUNT': 1.0,
'TRADE_POINT': 377,
'SIGN': sig(7, 1234567, 3561234)
})
xobj = xmltodict.parse(r.content)
self.assertEqual(int(xobj['pay-response']['status_code']), 11)
abon = Abon.objects.get(username='1234567')
self.assertEqual(abon.ballance, 1)
# пробуем добавить группу абонентов
def test_add_abongroup(self):
abon = Abon.objects.get(username='1234567')
ag = AbonGroup.objects.create(title='%&34%$&*(')
ag.profiles.add(abon)
# пробуем добавить абонента
def test_add_abon(self):
c = Client()
c.login(username='1234567', password='ps')
url = resolve_url('abonapp:add_abon', gid=1)
r = c.get(url)
# поглядим на страницу добавления абонента
self.assertEqual(r.status_code, 200)
r = c.post(url, {
'username': '123',
'password': 'ps',
'fio': 'Abon Fio',
'telephone': '+79783753914',
'is_active': True
})
self.assertEqual(r.status_code, 302)
r = c.get(resolve_url('abonapp:add_abon', gid=324))
self.assertEqual(r.status_code, 404)
try:
abn = Abon.objects.get(username='123')
self.assertIsNotNone(abn)
psw = AbonRawPassword.objects.get(account=abn, passw_text='ps')
self.assertIsNotNone(psw)
except Abon.DoesNotExist:
# абонент должен был создаться
self.assertTrue(False)
except AbonRawPassword.DoesNotExist:
# должен быть пароль абонента простым текстом
self.assertTrue(False)
# пробуем удалить абонента
def test_view_delentity(self):
c = Client()
c.login(username='1234567', password='ps')
url = resolve_url('abonapp:del_abon') + '?t=a&id=1'
r = c.get('/abons/1/addabon')
class AbonTariffTestCase(TestCase):
def setUp(self):
abon = Abon.objects.create(
username='1234567',
telephone='+79788328884'
)
tariff = Tariff.objects.create(
title='test_tariff',
descr='taroff descr',
speedIn=1.2,
speedOut=3.0,
amount=3
)
AbonTariff.objects.create(
abon=abon,
tariff=tariff
)

2
abonapp/urls_abon.py

@ -15,10 +15,8 @@ urlpatterns = [
url(r'^(?P<uid>\d+)/addinvoice$', views.add_invoice, name='add_invoice'), url(r'^(?P<uid>\d+)/addinvoice$', views.add_invoice, name='add_invoice'),
url(r'^(?P<uid>\d+)/pick$', views.pick_tariff, name='pick_tariff'), url(r'^(?P<uid>\d+)/pick$', views.pick_tariff, name='pick_tariff'),
url(r'^(?P<uid>\d+)/chpriority$', views.chpriority, name='chpriority_tariff'),
url(r'^(?P<uid>\d+)/passport_view$', views.passport_view, name='passport_view'), url(r'^(?P<uid>\d+)/passport_view$', views.passport_view, name='passport_view'),
url(r'^(?P<uid>\d+)/complete_service(?P<srvid>\d+)$', views.complete_service, name='compl_srv'), url(r'^(?P<uid>\d+)/complete_service(?P<srvid>\d+)$', views.complete_service, name='compl_srv'),
url(r'^(?P<uid>\d+)/activate_service(?P<srvid>\d+)$', views.activate_service, name='activate_service'),
url(r'^(?P<uid>\d+)/opt82$', views.opt82, name='opt82'), url(r'^(?P<uid>\d+)/opt82$', views.opt82, name='opt82'),
url(r'^(?P<uid>\d+)/chart$', views.charts, name='charts'), url(r'^(?P<uid>\d+)/chart$', views.charts, name='charts'),
url(r'^(?P<uid>\d+)/dials$', views.dials, name='dials'), url(r'^(?P<uid>\d+)/dials$', views.dials, name='dials'),

67
abonapp/views.py

@ -245,14 +245,10 @@ def pay_history(request, gid, uid):
@mydefs.only_admins @mydefs.only_admins
def abon_services(request, gid, uid): def abon_services(request, gid, uid):
abon = get_object_or_404(models.Abon, pk=uid) abon = get_object_or_404(models.Abon, pk=uid)
abon_tarifs = models.AbonTariff.objects.filter(abon=uid)
active_abontariff = abon_tarifs.exclude(time_start=None)
return render(request, 'abonapp/services.html', {
return render(request, 'abonapp/service.html', {
'abon': abon, 'abon': abon,
'abon_tarifs': abon_tarifs,
'active_abontariff_id': active_abontariff[0].id if active_abontariff.count() > 0 else None,
'abon_tariff': abon.current_tariff,
'abon_group': abon.group 'abon_group': abon.group
}) })
@ -434,28 +430,6 @@ def pick_tariff(request, gid, uid):
}) })
@login_required
@mydefs.only_admins
def chpriority(request, gid, uid):
t = request.GET.get('t')
act = request.GET.get('a')
current_abon_tariff = get_object_or_404(models.AbonTariff, pk=t)
try:
if act == 'up':
current_abon_tariff.priority_up()
elif act == 'down':
current_abon_tariff.priority_down()
except (NasFailedResult, NasNetworkError) as e:
messages.error(request, e)
except mydefs.MultipleException as errs:
for err in errs.err_list:
messages.add_message(request, messages.constants.ERROR, err)
return redirect('abonapp:abon_home', gid=gid, uid=uid)
@login_required @login_required
@permission_required('abonapp.can_complete_service') @permission_required('abonapp.can_complete_service')
def complete_service(request, gid, uid, srvid): def complete_service(request, gid, uid, srvid):
@ -515,38 +489,6 @@ def complete_service(request, gid, uid, srvid):
}) })
@login_required
@permission_required('abonapp.can_activate_service')
def activate_service(request, gid, uid, srvid):
abtar = get_object_or_404(models.AbonTariff, pk=srvid)
amount = abtar.calc_amount_service()
try:
if request.method == 'POST':
if request.POST.get('finish_confirm') != 'yes':
return HttpResponse(_('Not confirmed'))
abtar.activate(request.user)
messages.success(request, _('Service has been activated successfully'))
return redirect('abonapp:abon_services', gid, uid)
except (NasFailedResult, mydefs.LogicError) as e:
messages.error(request, e)
except NasNetworkError as e:
messages.warning(request, e)
except mydefs.MultipleException as errs:
for err in errs.err_list:
messages.add_message(request, messages.constants.ERROR, err)
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,
'deadline': calc_obj.calc_deadline()
})
@login_required @login_required
@permission_required('abonapp.delete_abontariff') @permission_required('abonapp.delete_abontariff')
@ -743,7 +685,8 @@ def charts(request, gid, uid):
abontariff = abon.active_tariff() abontariff = abon.active_tariff()
if abontariff is not None: if abontariff is not None:
high = abontariff.speedIn + abontariff.speedOut
trf = abontariff.tariff
high = trf.speedIn + trf.speedOut
if high > 100: if high > 100:
high = 100 high = 100
@ -888,7 +831,7 @@ def dials(request, gid, uid):
def abons(request): def abons(request):
ablist = [{ ablist = [{
'id': abn.pk, 'id': abn.pk,
'tarif_id': abn.active_tariff().pk if abn.active_tariff() else 0,
'tarif_id': abn.active_tariff().tariff.pk if abn.active_tariff() is not None else 0,
'ip': abn.ip_address.int_ip(), 'ip': abn.ip_address.int_ip(),
'is_active': abn.is_active 'is_active': abn.is_active
} for abn in models.Abon.objects.all()] } for abn in models.Abon.objects.all()]

3
bugs.txt

@ -8,3 +8,6 @@
- Не надо коннектиться к микротику когда не собираемся ничего изменять. А то при сохранении залогинились и вышли без действий - Не надо коннектиться к микротику когда не собираемся ничего изменять. А то при сохранении залогинились и вышли без действий
- Не удаляет просроченные услуги если не пингуется NAS - Не удаляет просроченные услуги если не пингуется NAS
- Надо отменить учёт временной зоны - Надо отменить учёт временной зоны
!!! Обязательно проверить как отрабатывает на NAS удаление и изменение AbonTariff
!!! Удалить всё что связано с активацией услуги
!!! Убрать досрочное завершение услуги

2
clientsideapp/views.py

@ -54,7 +54,7 @@ def buy_service(request, srv_id):
else: else:
return render_to_text('clientsideapp/modal_service_buy.html', { return render_to_text('clientsideapp/modal_service_buy.html', {
'service': service, 'service': service,
'current_service': current_service
'current_service': current_service.tariff if current_service is not None else None
}, request=request) }, request=request)
except LogicError as e: except LogicError as e:
messages.error(request, e) messages.error(request, e)

2
cron.py

@ -16,7 +16,7 @@ def main():
for user in users: for user in users:
try: try:
# бдим за услугами абонента: просроченные отключить, заказанные подключить # бдим за услугами абонента: просроченные отключить, заказанные подключить
user.activate_next_tariff(user)
user.bill_service(user)
# если нет ip то и нет смысла лезть в NAS # если нет ip то и нет смысла лезть в NAS
if user.ip_address is None: if user.ip_address is None:

2
djing/utils/save_from_nodeny.py

@ -102,7 +102,7 @@ class DumpAbon(object):
self.passw = raw_passw self.passw = raw_passw
srv = obj.active_tariff() srv = obj.active_tariff()
if srv is not None: if srv is not None:
self.service = DumpService.build_from_db(srv)
self.service = DumpService.build_from_db(srv.tariff)
else: else:
self.service = None self.service = None
if obj.opt82 is not None and obj.opt82.mac is not None and obj.opt82.port is not None: if obj.opt82 is not None and obj.opt82.mac is not None and obj.opt82.port is not None:

Loading…
Cancel
Save