35 changed files with 639 additions and 1131 deletions
-
17README.md
-
89abonapp/locale/ru/LC_MESSAGES/django.po
-
41abonapp/migrations/0022_auto_20170816_1109.py
-
232abonapp/models.py
-
40abonapp/templates/abonapp/activate_service.html
-
72abonapp/templates/abonapp/buy_tariff.html
-
12abonapp/templates/abonapp/peoples.html
-
119abonapp/templates/abonapp/service.html
-
101abonapp/templates/abonapp/services.html
-
225abonapp/tests.py
-
4abonapp/urls_abon.py
-
83abonapp/views.py
-
21accounts_app/migrations/0007_auto_20170816_1109.py
-
5bugs.txt
-
81clientsideapp/locale/ru/LC_MESSAGES/django.po
-
3clientsideapp/templates/clientsideapp/index.html
-
28clientsideapp/templates/clientsideapp/modal_activate_service.html
-
32clientsideapp/templates/clientsideapp/modal_complete_service.html
-
10clientsideapp/templates/clientsideapp/modal_service_buy.html
-
26clientsideapp/templates/clientsideapp/modal_unsubscribe_service.html
-
134clientsideapp/templates/clientsideapp/services.html
-
3clientsideapp/urls.py
-
109clientsideapp/views.py
-
45cron.py
-
20devapp/migrations/0007_auto_20170816_1109.py
-
1devapp/models.py
-
0djing/utils/save_from_nodeny.py
-
80docs/install.md
-
7docs/intro.md
-
4tariff_app/templates/tariff_app/tarifs.html
-
73taskapp/handle.py
-
2taskapp/handle.sh
-
25taskapp/migrations/0015_auto_20170816_1109.py
-
18taskapp/models.py
-
8taskapp/views.py
@ -0,0 +1,41 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# Generated by Django 1.9 on 2017-08-16 11:09 |
||||
|
from __future__ import unicode_literals |
||||
|
|
||||
|
from django.db import migrations, models |
||||
|
import django.db.models.deletion |
||||
|
|
||||
|
|
||||
|
class Migration(migrations.Migration): |
||||
|
|
||||
|
dependencies = [ |
||||
|
('abonapp', '0021_auto_20170705_1403'), |
||||
|
] |
||||
|
|
||||
|
operations = [ |
||||
|
migrations.AlterModelOptions( |
||||
|
name='abontariff', |
||||
|
options={'permissions': (('can_complete_service', 'Снятие со счёта средств'),)}, |
||||
|
), |
||||
|
migrations.RemoveField( |
||||
|
model_name='abon', |
||||
|
name='current_tariffs', |
||||
|
), |
||||
|
migrations.AddField( |
||||
|
model_name='abon', |
||||
|
name='current_tariff', |
||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='abonapp.AbonTariff'), |
||||
|
), |
||||
|
migrations.AlterUniqueTogether( |
||||
|
name='abontariff', |
||||
|
unique_together=set([]), |
||||
|
), |
||||
|
migrations.RemoveField( |
||||
|
model_name='abontariff', |
||||
|
name='abon', |
||||
|
), |
||||
|
migrations.RemoveField( |
||||
|
model_name='abontariff', |
||||
|
name='tariff_priority', |
||||
|
), |
||||
|
] |
||||
@ -1,40 +0,0 @@ |
|||||
{% extends request.is_ajax|yesno:'bajax.html,base.html' %} |
|
||||
{% load i18n %} |
|
||||
{% block main %} |
|
||||
|
|
||||
|
|
||||
<ol class="breadcrumb"> |
|
||||
<li><span class="glyphicon glyphicon-home"></span></li> |
|
||||
<li><a href="{% url 'abonapp:group_list' %}">{% trans 'User groups' %}</a></li> |
|
||||
<li><a href="{% url 'abonapp:people_list' abon_group.id %}">{{ abon_group.title }}</a></li> |
|
||||
<li><a href="{% url 'abonapp:abon_home' abon_group.id abon.id %}">{{ abon.fio }}</a></li> |
|
||||
<li class="active">{% trans 'Activate service' %}</li> |
|
||||
</ol> |
|
||||
|
|
||||
{% include 'message_block.html' %} |
|
||||
|
|
||||
<div class="panel panel-default"> |
|
||||
<div class="panel-heading"> |
|
||||
<h3 class="panel-title">{% trans 'Activate service' %}</h3> |
|
||||
</div> |
|
||||
<div class="panel-body"> |
|
||||
<form role="form" action="{% url 'abonapp:activate_service' abon_group.id abon.id abtar.id %}" |
|
||||
method="post">{% csrf_token %} |
|
||||
<input name="finish_confirm" value="yes" type="hidden"> |
|
||||
|
|
||||
<p> |
|
||||
{% blocktrans with ballance=abon.ballance deadline=deadline|date:'d F Y, H:i:s' %} |
|
||||
Are you sure that you want activate service for the user?<br> |
|
||||
Note that the account will be removed from his money and open access to the resources of the paid services.<br> |
|
||||
Maintenance cost is {{ amount }}. On account of {{ ballance }}, will be {{ diff }}<br> |
|
||||
It will work until {{ deadline }}{% endblocktrans %} |
|
||||
</p> |
|
||||
|
|
||||
<button type="submit" class="btn btn-sm btn-primary"> |
|
||||
<span class="glyphicon glyphicon-save"></span> {% trans 'Save' %} |
|
||||
</button> |
|
||||
</form> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
{% endblock %} |
|
||||
@ -0,0 +1,119 @@ |
|||||
|
{% 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 "Subscriber's service" %}</h3> |
||||
|
</div> |
||||
|
<div class="panel-body"> |
||||
|
{% if abon_tariff %} |
||||
|
|
||||
|
<dl class="dl-horizontal"> |
||||
|
<dt>{% trans 'Service' %}</dt> |
||||
|
<dd> |
||||
|
{% if abon_tariff.tariff %} |
||||
|
{% if perms.tariff_app.change_tariff %} |
||||
|
<a href="{% url 'tarifs:edit' abon_tariff.tariff.pk %}" title="{{ abon_tariff.time_start|default:'' }}"> |
||||
|
{{ abon_tariff.tariff.title }} |
||||
|
</a> |
||||
|
{% else %} |
||||
|
{{ abon_tariff.tariff.title }} |
||||
|
{% endif %} |
||||
|
{% else %} |
||||
|
<b class="text-danger">{% trans 'We have a problem in DB: AbonTariff instance has no related to service' %}</b> |
||||
|
{% endif %} |
||||
|
</dd> |
||||
|
<dt>{% trans 'Sum' %}</dt> |
||||
|
<dd>{{ abon_tariff.tariff.amount }} {% trans 'currency' %}.</dd> |
||||
|
|
||||
|
<dt>{% trans 'Input speed' %}</dt> |
||||
|
<dd>{{ abon_tariff.tariff.speedIn }}</dd> |
||||
|
|
||||
|
<dt>{% trans 'Output speed' %}</dt> |
||||
|
<dd>{{ abon_tariff.tariff.speedOut }}</dd> |
||||
|
|
||||
|
<dt>{% trans 'Date of start' %}</dt> |
||||
|
<dd>{{ abon_tariff.time_start|date:"d E Y, l H:i" }}</dd> |
||||
|
|
||||
|
<dt>{% trans 'Works until' %}</dt> |
||||
|
<dd>{{ abon_tariff.deadline|date:"d E Y, l H:i" }}</dd> |
||||
|
</dl> |
||||
|
|
||||
|
<blockquote> |
||||
|
<p>{{ abon_tariff.tariff.descr }}</p> |
||||
|
</blockquote> |
||||
|
|
||||
|
{% else %} |
||||
|
{% trans 'Subscriber has no service' %}. |
||||
|
<a href="{% url 'abonapp:pick_tariff' abon_group.pk abon.pk %}"> |
||||
|
{% trans 'Buy service' %} |
||||
|
</a> |
||||
|
{% endif %} |
||||
|
|
||||
|
{% if abon_tariff %} |
||||
|
<a href="{% url 'abonapp:unsubscribe_service' abon_group.pk abon.pk abon_tariff.pk %}" class="btn btn-danger"> |
||||
|
<span class="glyphicon glyphicon-remove-circle"></span> {% trans 'Finish 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"> |
||||
|
<table class="table table-condensed"> |
||||
|
<thead> |
||||
|
<tr> |
||||
|
<th>{% trans 'Pick a service' %}</th> |
||||
|
<th>{% trans 'Service' %}</th> |
||||
|
<th>{% trans 'Price' %}</th> |
||||
|
<th>{% trans 'Speed In' %}</th> |
||||
|
<th>{% trans 'Speed Out' %}</th> |
||||
|
</tr> |
||||
|
</thead> |
||||
|
<tbody> |
||||
|
{% with can_ch_trf=perms.tariff_app.change_tariff %} |
||||
|
{% for service in services %} |
||||
|
<tr> |
||||
|
<td><a href="{% url 'abonapp:pick_tariff' abon_group.pk abon.pk %}?selected_tariff={{ service.pk }}" |
||||
|
class="btn btn-sm btn-default" title="{{ service.get_calc_type_display }}" data-toggle="tooltip"{% if abon_tariff %} disabled{% endif %}> |
||||
|
<span class="glyphicon glyphicon-shopping-cart"></span> |
||||
|
</a></td> |
||||
|
<td> |
||||
|
{% if can_ch_trf %} |
||||
|
<a href="{% url 'tarifs:edit' service.pk %}" title="{{ service.descr }}" data-toggle="tooltip"><b>{{ service.title }}</b></a> |
||||
|
{% else %} |
||||
|
{{ service.title }} |
||||
|
{% endif %} |
||||
|
</td> |
||||
|
<td>{{ service.amount }} {% trans 'currency' %}</td> |
||||
|
<td>{{ service.speedIn }}</td> |
||||
|
<td>{{ service.speedOut }}</td> |
||||
|
</tr> |
||||
|
{% empty %} |
||||
|
<tr><td colspan="5"> |
||||
|
{% trans 'This group has no services' %} |
||||
|
<a href="{% url 'abonapp:ch_group_tariff' abon_group.pk %}" class="btn btn-sm btn-default" title="{% trans 'User groups' %}"> |
||||
|
<span class="glyphicon glyphicon-cog"></span> {% trans 'Tariffs in groups' %} |
||||
|
</a> |
||||
|
</td></tr> |
||||
|
{% endfor %} |
||||
|
{% endwith %} |
||||
|
</tbody> |
||||
|
</table> |
||||
|
<a href="{% url 'abonapp:ch_group_tariff' abon_group.pk %}" class="btn btn-sm btn-default" title="{% trans 'User groups' %}"> |
||||
|
<span class="glyphicon glyphicon-cog"></span> {% trans 'Attach services to group' %} |
||||
|
</a> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
{% endblock %} |
||||
@ -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 %} |
|
||||
@ -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 |
|
||||
) |
|
||||
@ -0,0 +1,21 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# Generated by Django 1.9 on 2017-08-16 11:09 |
||||
|
from __future__ import unicode_literals |
||||
|
|
||||
|
import django.core.validators |
||||
|
from django.db import migrations, models |
||||
|
|
||||
|
|
||||
|
class Migration(migrations.Migration): |
||||
|
|
||||
|
dependencies = [ |
||||
|
('accounts_app', '0006_auto_20170416_1029'), |
||||
|
] |
||||
|
|
||||
|
operations = [ |
||||
|
migrations.AlterField( |
||||
|
model_name='userprofile', |
||||
|
name='telephone', |
||||
|
field=models.CharField(max_length=16, validators=[django.core.validators.RegexValidator('^\\+[7,8,9,3]\\d{10,11}$')], verbose_name='Телефон'), |
||||
|
), |
||||
|
] |
||||
@ -1,28 +0,0 @@ |
|||||
<form action="{% url 'client_side:activate_service' abtar.pk %}" method="post">{% csrf_token %} |
|
||||
<div class="modal-header primary"> |
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> |
|
||||
<h4 class="modal-title"><span class="glyphicon glyphicon-question-sign"></span>Активировать услугу</h4> |
|
||||
</div> |
|
||||
<div class="modal-body"> |
|
||||
<h3>Вы уверены что хотите активировать услугу?</h3> |
|
||||
<input type="hidden" name="finish_confirm" value="yes"> |
|
||||
<h3>{{ service.title }}</h3> |
|
||||
|
|
||||
<p>{{ service.descr }}</p> |
|
||||
|
|
||||
<!--<p>Входящая скорость: {{ service.speedIn }} MBit/s<br> |
|
||||
Исходящая скорость: {{ service.speedOut }} MBit/s</p>--> |
|
||||
|
|
||||
<p>Вы уверены что хотите активировать эту услугу?<br> |
|
||||
Обратите внимание что с вашего счёта <b>снимутся деньги</b> и откроется доступ к ресурсам оплаченной |
|
||||
услуги.<br> |
|
||||
Стоимость услуги: {{ amount }}руб. На счету {{ abon.ballance }} руб, останется {{ diff }} руб.</p> |
|
||||
|
|
||||
</div> |
|
||||
<div class="modal-footer"> |
|
||||
<button type="submit" class="btn btn-sm btn-success"> |
|
||||
<span class="glyphicon glyphicon-shopping-cart"></span> Активировать |
|
||||
</button> |
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Закрыть</button> |
|
||||
</div> |
|
||||
</form> |
|
||||
@ -1,32 +0,0 @@ |
|||||
<form action="{% url 'client_side:complete_service' abtar.pk %}" method="post">{% csrf_token %} |
|
||||
<div class="modal-header primary"> |
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> |
|
||||
<h4 class="modal-title"><span class="glyphicon glyphicon-question-sign"></span>Завершить услугу</h4> |
|
||||
</div> |
|
||||
<div class="modal-body"> |
|
||||
<h3>Вы уверены что хотите завершить услугу?</h3> |
|
||||
<input type="hidden" name="finish_confirm" value="yes"> |
|
||||
<h3>{{ service.title }}</h3> |
|
||||
|
|
||||
<p>{{ service.descr }}</p> |
|
||||
|
|
||||
<p>Входящая скорость: {{ service.speedIn }} MBit/s<br> |
|
||||
Исходящая скорость: {{ service.speedOut }} MBit/s</p> |
|
||||
|
|
||||
<p>Завершение услуги приведёт к прекращению действия услуги. Т.е. интернет отключится.<br> |
|
||||
Деньги за неиспользованные ресурсы возвращены не будут.<br> |
|
||||
Для возобновления обслуживания вы должны купить новую услугу.</p> |
|
||||
|
|
||||
Услуга была подключена: {{ abtar.time_start|date:'d F Y, H:i:s' }}<br/> |
|
||||
Сегодня: {% now "d F Y, H:i:s" %}<br/> |
|
||||
Время использования: {{ time_use }}<br/> |
|
||||
Полная стоимость услуги: {{ service.amount }}<br/> |
|
||||
<b>Итоговая стоимость: {{ abtar.calc_amount_service }}</b> |
|
||||
</div> |
|
||||
<div class="modal-footer"> |
|
||||
<button type="submit" class="btn btn-sm btn-danger"> |
|
||||
<span class="glyphicon glyphicon-alert"></span> Завершить |
|
||||
</button> |
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Закрыть</button> |
|
||||
</div> |
|
||||
</form> |
|
||||
@ -1,26 +0,0 @@ |
|||||
{% load i18n %} |
|
||||
<form action="{% url 'client_side:unsubscribe_service' abtar.pk %}" method="post">{% csrf_token %} |
|
||||
<div class="modal-header primary"> |
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> |
|
||||
<h4 class="modal-title"><span class="glyphicon glyphicon-question-sign"></span>{% trans 'Unsubscribe from service' %}</h4> |
|
||||
</div> |
|
||||
<div class="modal-body"> |
|
||||
<h3>{% trans 'Are you sure you want to unsubscribe from the service?' %}</h3> |
|
||||
<input type="hidden" name="finish_confirm" value="yes"> |
|
||||
<h3>{{ service.title }}</h3> |
|
||||
|
|
||||
<p>{{ service.descr }}</p> |
|
||||
|
|
||||
<p>{% blocktrans %}Inbound speed: {{ service.speedIn }} MBit/s<br>Outgoing speed: {{ service.speedOut }} MBit/s{% endblocktrans %}</p> |
|
||||
|
|
||||
<p>{% blocktrans %}When you unsubscribe from the service, it just will remove it from the queue inclusions your services.<br> |
|
||||
Your funds will not be affected, the money will not go away.{% endblocktrans %}</p> |
|
||||
|
|
||||
</div> |
|
||||
<div class="modal-footer"> |
|
||||
<button type="submit" class="btn btn-sm btn-primary"> |
|
||||
<span class="glyphicon glyphicon-alert"></span> {% trans 'Unsubscribe' %} |
|
||||
</button> |
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{% trans 'Close' %}</button> |
|
||||
</div> |
|
||||
</form> |
|
||||
@ -0,0 +1,20 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# Generated by Django 1.9 on 2017-08-16 11:09 |
||||
|
from __future__ import unicode_literals |
||||
|
|
||||
|
from django.db import migrations, models |
||||
|
|
||||
|
|
||||
|
class Migration(migrations.Migration): |
||||
|
|
||||
|
dependencies = [ |
||||
|
('devapp', '0006_auto_20170705_1403'), |
||||
|
] |
||||
|
|
||||
|
operations = [ |
||||
|
migrations.AlterField( |
||||
|
model_name='device', |
||||
|
name='devtype', |
||||
|
field=models.CharField(choices=[('Dl', 'DLink switch'), ('Pn', 'PON OLT'), ('On', 'PON ONU'), ('Ex', 'Eltex switch')], default='Dl', max_length=2), |
||||
|
), |
||||
|
] |
||||
@ -0,0 +1,80 @@ |
|||||
|
## Установка(не завершил описание): |
||||
|
Работа предполагается на python3. |
||||
|
Я предпочитаю запускать wsgi сервер на связке uWSGI + Nginx, так что ставить будем соответствующие пакеты. |
||||
|
|
||||
|
На ArchLinux нужые пакеты можно установить так: |
||||
|
``` |
||||
|
# pacman -Sy mariadb-clients python3 python-pip nginx uwsgi |
||||
|
``` |
||||
|
Дальше ставим всё для python через pip: |
||||
|
``` |
||||
|
# pip install git+https://github.com/nerosketch/djing.git |
||||
|
``` |
||||
|
|
||||
|
### Настройка WEB Сервера |
||||
|
Условимся что путь к папке с проектом находится по адресу: </var/www/djing> |
||||
|
|
||||
|
Конфиг Nginx на моём рабочем сервере выглядит так: |
||||
|
|
||||
|
user http; |
||||
|
worker_processes auto; |
||||
|
pid /run/nginx.pid; |
||||
|
events { |
||||
|
worker_connections 1024; |
||||
|
} |
||||
|
http { |
||||
|
sendfile on; |
||||
|
upstream djing { server unix:///run/uwsgi/djing.sock; } |
||||
|
|
||||
|
server { |
||||
|
listen 80; |
||||
|
server_name <ваш-домен>.com; |
||||
|
root /var/www/djing; |
||||
|
charset utf-8; |
||||
|
|
||||
|
# укажите где лежит ваш раздел с медиа для сайта |
||||
|
location /media { |
||||
|
alias /var/www/djing/media; |
||||
|
} |
||||
|
|
||||
|
# местоположение статики |
||||
|
location /static { |
||||
|
alias /var/www/djing/static; |
||||
|
} |
||||
|
|
||||
|
# тут надо указать путь куда у вас установился Django + путь к статике админки |
||||
|
# путь к Django тут: /usr/lib/python3.5/site-packages/django |
||||
|
# путь к статике соответственно: contrib/admin/static/admin |
||||
|
location /static/admin { |
||||
|
alias /usr/lib/python3.5/site-packages/django/contrib/admin/static/admin; |
||||
|
} |
||||
|
|
||||
|
# на корневом url / реагируем с помощью сокета проекта |
||||
|
# у нас он называется "djing": upstream djing { server ... |
||||
|
location / { |
||||
|
uwsgi_pass djing; |
||||
|
include uwsgi_params; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
Это минимальный конфиг Nginx для работы. Проверте файл /run/uwsgi/djing.sock на доступность пользователю http для чтения. |
||||
|
|
||||
|
Далее настраиваем uWSGI. Мой конфиг для uWSGI в режиме emperor: |
||||
|
|
||||
|
[uwsgi] |
||||
|
uid = http |
||||
|
gid = http |
||||
|
pidfile = /run/uwsgi/uwsgi.pid |
||||
|
emperor = /etc/uwsgi.d |
||||
|
stats = /run/uwsgi/stats.sock |
||||
|
chmod-socket = 660 |
||||
|
emperor-tyrant = true |
||||
|
cap = setgid,setuid |
||||
|
|
||||
|
У меня конфиг лежит по адресу /etc/uwsgi.ini |
||||
|
|
||||
|
|
||||
|
### Настраиваем системные утилиты |
||||
|
Если ваша система работает с поддержкой *systemd* то в каталоге *systemd_units* проекта вы найдёте юниты для systemd. |
||||
|
Скопируйте их в каталог юнитов systemd |
||||
@ -0,0 +1,7 @@ |
|||||
|
Текущие возможности: |
||||
|
1. Может наблюдать за устройствами по snmp |
||||
|
2. Отправлять изменения мгновенно на mikrotik |
||||
|
3. Привязывать на карте к точкам топологии устройства |
||||
|
4. Есть привязка монтажника к группам абонентов |
||||
|
5. Есть менеджер задач на абонентов. Это оператор может выбрать абонента и описать проблему. Система отправит оповещение через telegram ответственному за групу указанного абонента монтажнику с текстом проблемы, адресом и телефоном абонента. |
||||
|
6. Долгие или сложные задачи можно отправлять на очередь исполнения |
||||
@ -0,0 +1,25 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# Generated by Django 1.9 on 2017-08-16 11:09 |
||||
|
from __future__ import unicode_literals |
||||
|
|
||||
|
from django.db import migrations, models |
||||
|
|
||||
|
|
||||
|
class Migration(migrations.Migration): |
||||
|
|
||||
|
dependencies = [ |
||||
|
('taskapp', '0014_auto_20170416_1029'), |
||||
|
] |
||||
|
|
||||
|
operations = [ |
||||
|
migrations.AlterField( |
||||
|
model_name='changelog', |
||||
|
name='act_type', |
||||
|
field=models.CharField(choices=[('e', 'Изменение задачи'), ('c', 'Создание задачи'), ('d', 'Удаление задачи'), ('f', 'Завершение задачи'), ('b', 'Задача провалена')], max_length=1), |
||||
|
), |
||||
|
migrations.AlterField( |
||||
|
model_name='task', |
||||
|
name='state', |
||||
|
field=models.CharField(choices=[('S', 'Новая'), ('C', 'Провалена'), ('F', 'Выполнена')], default='S', max_length=1), |
||||
|
), |
||||
|
] |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue