Browse Source

Merge remote-tracking branch 'origin/global_group_update' into devel

devel
bashmak 8 years ago
parent
commit
2b13fa136e
  1. 1
      abonapp/admin.py
  2. 19
      abonapp/forms.py
  3. 23
      abonapp/locale/ru/LC_MESSAGES/django.po
  4. 308
      abonapp/migrations/0001_initial.py
  5. 52
      abonapp/migrations/0002_auto_20170905_1248.py
  6. 43
      abonapp/migrations/0003_auto_20170927_1838.py
  7. 130
      abonapp/migrations/0004_auto_20180122_1732.py
  8. 20
      abonapp/migrations/0005_auto_20180123_1353.py
  9. 23
      abonapp/migrations/0006_abon_markers.py
  10. 96
      abonapp/models.py
  11. 4
      abonapp/templates/abonapp/addAbon.html
  12. 39
      abonapp/templates/abonapp/addGroup.html
  13. 8
      abonapp/templates/abonapp/addInvoice.html
  14. 10
      abonapp/templates/abonapp/buy_tariff.html
  15. 2
      abonapp/templates/abonapp/charts.html
  16. 24
      abonapp/templates/abonapp/editAbon.html
  17. 16
      abonapp/templates/abonapp/ext.htm
  18. 12
      abonapp/templates/abonapp/group_list.html
  19. 6
      abonapp/templates/abonapp/group_tariffs.html
  20. 6
      abonapp/templates/abonapp/invoiceForPayment.html
  21. 7
      abonapp/templates/abonapp/log.html
  22. 3
      abonapp/templates/abonapp/modal_abonamount.html
  23. 2
      abonapp/templates/abonapp/passport_view.html
  24. 4
      abonapp/templates/abonapp/payHistory.html
  25. 40
      abonapp/templates/abonapp/peoples.html
  26. 14
      abonapp/templates/abonapp/service.html
  27. 2
      abonapp/urls.py
  28. 245
      abonapp/views.py
  29. 63
      accounts_app/migrations/0001_initial.py
  30. 20
      accounts_app/migrations/0002_userprofile_email.py
  31. 25
      accounts_app/migrations/0003_auto_20161206_2135.py
  32. 18
      accounts_app/migrations/0004_auto_20170128_1316.py
  33. 21
      accounts_app/migrations/0005_auto_20170222_2224.py
  34. 21
      accounts_app/migrations/0006_auto_20170416_1029.py
  35. 21
      accounts_app/migrations/0007_auto_20170816_1109.py
  36. 19
      accounts_app/migrations/0008_auto_20170927_1838.py
  37. 28
      accounts_app/models.py
  38. 6
      accounts_app/templates/accounts/ext.htm
  39. 24
      accounts_app/templates/accounts/profile_chgroup.html
  40. 10
      accounts_app/templates/accounts/set_abon_groups_permission.html
  41. 1
      accounts_app/urls.py
  42. 45
      accounts_app/views.py
  43. 17
      agent/commands/dhcp.py
  44. 32
      chatbot/migrations/0001_initial.py
  45. 59
      chatbot/migrations/0002_auto_20171214_1517.py
  46. 49
      clientsideapp/locale/ru/LC_MESSAGES/django.po
  47. 8
      clientsideapp/templates/clientsideapp/debt_buy.html
  48. 2
      clientsideapp/templates/clientsideapp/ext.html
  49. 3
      clientsideapp/templates/clientsideapp/modal_service_buy.html
  50. 23
      clientsideapp/views.py
  51. 5
      devapp/forms.py
  52. 4
      devapp/locale/ru/LC_MESSAGES/django.po
  53. 62
      devapp/migrations/0001_initial.py
  54. 90
      devapp/migrations/0001_squashed_0007_auto_20170816_1109.py
  55. 22
      devapp/migrations/0002_device_user_group.py
  56. 23
      devapp/migrations/0003_auto_20170927_1838.py
  57. 72
      devapp/migrations/0004_auto_20171103_0006.py
  58. 20
      devapp/migrations/0005_device_snmp_item_num.py
  59. 34
      devapp/migrations/0006_auto_20180129_1625.py
  60. 7
      devapp/models.py
  61. 2
      devapp/templates/devapp/add_dev.html
  62. 2
      devapp/templates/devapp/custom_dev_page/olt.html
  63. 2
      devapp/templates/devapp/custom_dev_page/onu.html
  64. 4
      devapp/templates/devapp/custom_dev_page/ports.html
  65. 2
      devapp/templates/devapp/dev.html
  66. 8
      devapp/templates/devapp/devices.html
  67. 6
      devapp/templates/devapp/devices_null_group.html
  68. 10
      devapp/templates/devapp/ext.htm
  69. 6
      devapp/templates/devapp/fix_dev_group.html
  70. 19
      devapp/templates/devapp/group_list.html
  71. 8
      devapp/templates/devapp/manage_ports/add_ports.html
  72. 4
      devapp/templates/devapp/manage_ports/fix_abon_device.html
  73. 8
      devapp/templates/devapp/manage_ports/list.html
  74. 80
      devapp/views.py
  75. 37
      dialing_app/migrations/0001_initial.py
  76. 35
      dialing_app/migrations/0002_auto_20171229_1353.py
  77. 31
      dialing_app/migrations/0003_smsout.py
  78. 10
      dialing_app/views.py
  79. 33
      djing/auth_backends.py
  80. 10
      djing/settings.py
  81. 1
      djing/urls.py
  82. 12
      global_context_processors.py
  83. 0
      group_app/__init__.py
  84. 4
      group_app/admin.py
  85. 5
      group_app/apps.py
  86. 8
      group_app/forms.py
  87. 69
      group_app/locale/ru/LC_MESSAGES/django.po
  88. 30
      group_app/migrations/0001_initial.py
  89. 0
      group_app/migrations/__init__.py
  90. 23
      group_app/models.py
  91. 20
      group_app/templates/group_app/add_group.html
  92. 20
      group_app/templates/group_app/edit_group.html
  93. 23
      group_app/templates/group_app/ext.html
  94. 59
      group_app/templates/group_app/group_list.html
  95. 3
      group_app/tests.py
  96. 10
      group_app/urls.py
  97. 52
      group_app/views.py
  98. 66
      locale/ru/LC_MESSAGES/django.po
  99. 16
      mapapp/migrations/0001_initial.py
  100. 45
      mapapp/migrations/0002_auto_20171103_0006.py

1
abonapp/admin.py

@ -3,7 +3,6 @@ from django.contrib import admin
from . import models
admin.site.register(models.AbonGroup)
admin.site.register(models.Abon)
admin.site.register(models.InvoiceForPayment)
admin.site.register(models.AbonLog)

19
abonapp/forms.py

@ -12,7 +12,7 @@ from django.conf import settings
TELEPHONE_REGEXP = getattr(settings, 'TELEPHONE_REGEXP', r'^\+[7,8,9,3]\d{10,11}$')
def generate_random_username(length=6, chars=digits, split=2, delimiter=''):
def generate_random_chars(length=6, chars=digits, split=2, delimiter=''):
username = ''.join([choice(chars) for i in range(length)])
if split:
@ -25,8 +25,13 @@ def generate_random_username(length=6, chars=digits, split=2, delimiter=''):
return username
def generate_random_username():
username = generate_random_chars(length=6, chars=digits)
return str(int(username))
def generate_random_password():
return generate_random_username(length=8, chars=digits + ascii_lowercase)
return generate_random_chars(length=8, chars=digits + ascii_lowercase)
class AbonForm(forms.ModelForm):
@ -92,16 +97,6 @@ class AbonForm(forms.ModelForm):
return acc
class AbonGroupForm(forms.ModelForm):
class Meta:
model = models.AbonGroup
fields = '__all__'
widgets = {
'title': forms.TextInput(attrs={'class': 'form-control'}),
'profiles': forms.TextInput(attrs={'class': 'form-control'})
}
class PassportForm(forms.ModelForm):
class Meta:
model = models.PassportInfo

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

@ -96,10 +96,6 @@ msgstr "Динамический ip"
msgid "Fields"
msgstr "Поля"
#: models.py:31
msgid "Can view subscriber group"
msgstr "Может просматривать группу абонентов"
#: models.py:33
msgid "Abon group"
msgstr "Группа абонентов"
@ -894,19 +890,11 @@ msgstr "Просмотр абонента"
msgid "yes,no"
msgstr "Да,Нет"
#: views.py:94
msgid "create group success msg"
msgstr "Группа успешно создана"
#: views.py:97 views.py:161 views.py:308 views.py:423 views.py:500 views.py:642
#: views.py:812 views.py:884 views.py:953 views.py:1059
msgid "fix form errors"
msgstr "Некоторые поля заполнены не правильно, проверте ещё раз"
#: views.py:128
msgid "delete group success msg"
msgstr "Группа успешно удалена"
#: views.py:158
msgid "create abon success msg"
msgstr "Абонент успешно создан"
@ -1128,7 +1116,7 @@ msgid "User flags has changed successfully"
msgstr "Флаги абонента изменены успешно"
msgid "Services"
msgstr "Услуги"
msgstr "Тарифы"
msgid "Payments"
msgstr "Финансы"
@ -1144,3 +1132,12 @@ msgstr "Инфо."
msgid "Dialing"
msgstr "Звонки"
msgid "Subscribers"
msgstr "Абоненты"
msgid "Successfully saved"
msgstr "Успешно сохранено"
msgid "This user can not buy admin services"
msgstr "Этот пользователь не может назначать административные услуги"

308
abonapp/migrations/0001_squashed_0022_auto_20170816_1109.py → abonapp/migrations/0001_initial.py

@ -1,7 +1,8 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2017-09-04 16:15
# Generated by Django 1.11 on 2018-02-26 00:20
from __future__ import unicode_literals
import bitfield.models
from django.conf import settings
import django.core.validators
from django.db import migrations, models
@ -15,8 +16,8 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
('devapp', '0001_squashed_0007_auto_20170816_1109'),
('tariff_app', '0002_tariff_descr'),
('devapp', '0001_initial'),
('group_app', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('accounts_app', '0001_initial'),
('tariff_app', '0001_initial'),
@ -26,128 +27,82 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='Abon',
fields=[
('userprofile_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to=settings.AUTH_USER_MODEL)),
('ballance', models.FloatField(default=0.0, validators=[django.core.validators.DecimalValidator])),
('address', models.CharField(max_length=256)),
('baseaccount_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='accounts_app.BaseAccount')),
('ballance', models.FloatField(default=0.0)),
('ip_address', mydefs.MyGenericIPAddressField(blank=True, max_length=8, null=True, protocol='ipv4')),
('description', models.TextField(blank=True, null=True, verbose_name='Comment')),
('house', models.CharField(blank=True, max_length=12, null=True, verbose_name='House')),
('is_dynamic_ip', models.BooleanField(default=False)),
('markers', bitfield.models.BitField((('icon_donkey', 'Donkey'), ('icon_fire', 'Fire'), ('icon_ok', 'Ok'), ('icon_king', 'King'), ('icon_tv', 'TV'), ('icon_smile', 'Smile'), ('icon_dollar', 'Dollar'), ('icon_service', 'Service'), ('icon_mrk', 'Marker')), default=0)),
],
options={
'verbose_name': 'Abon',
'verbose_name_plural': 'Abons',
'db_table': 'abonent',
'ordering': ['fio'],
'permissions': (('can_buy_tariff', 'Buy service perm'), ('can_view_passport', 'Can view passport'), ('can_add_ballance', 'fill account'), ('can_ping', 'Can ping')),
},
bases=('accounts_app.userprofile',),
bases=('accounts_app.baseaccount',),
),
migrations.CreateModel(
name='AbonGroup',
name='AbonLog',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=127, unique=True)),
('amount', models.FloatField(default=0.0)),
('comment', models.CharField(max_length=128)),
('date', models.DateTimeField(auto_now_add=True)),
],
options={
'db_table': 'abonent_groups',
'db_table': 'abonent_log',
'ordering': ['-date'],
'permissions': (('can_view_abonlog', 'Can view subscriber logs'),),
},
),
migrations.CreateModel(
name='AbonLog',
name='AbonStreet',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('amount', models.FloatField(default=0.0)),
('comment', models.CharField(max_length=128)),
('date', models.DateTimeField(auto_now_add=True)),
('abon', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='abonapp.Abon')),
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL)),
('name', models.CharField(max_length=64)),
('group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='group_app.Group')),
],
options={
'db_table': 'abonent_log',
'verbose_name': 'Street',
'verbose_name_plural': 'Streets',
'db_table': 'abon_street',
'ordering': ['name'],
},
),
migrations.CreateModel(
name='AbonTariff',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('tariff_priority', models.PositiveSmallIntegerField(default=0)),
('time_start', models.DateTimeField(blank=True, default=None, null=True)),
('abon', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='abonapp.Abon')),
('deadline', models.DateTimeField(blank=True, default=None, null=True)),
('tariff', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='linkto_tariff', to='tariff_app.Tariff')),
],
options={
'ordering': ('tariff_priority',),
'verbose_name': 'Abon service',
'verbose_name_plural': 'Abon services',
'db_table': 'abonent_tariff',
'ordering': ['time_start'],
'permissions': (('can_complete_service', 'finish service perm'),),
},
),
migrations.CreateModel(
name='InvoiceForPayment',
name='AdditionalTelephone',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('status', models.BooleanField(default=False)),
('amount', models.FloatField(default=0.0)),
('comment', models.CharField(max_length=128)),
('date_create', models.DateTimeField(auto_now_add=True)),
('date_pay', models.DateTimeField(blank=True, null=True)),
('abon', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='abonapp.Abon')),
('author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
('telephone', models.CharField(max_length=16, validators=[django.core.validators.RegexValidator('^\\+[7,8,9,3]\\d{10,11}$')], verbose_name='Telephone')),
('owner_name', models.CharField(max_length=127)),
],
options={
'ordering': ('date_create',),
'db_table': 'abonent_inv_pay',
'verbose_name': 'Additional telephone',
'verbose_name_plural': 'Additional telephones',
'db_table': 'additional_telephones',
'ordering': ('owner_name',),
'permissions': (('can_view_additionaltelephones', 'Can view additional telephones'),),
},
),
migrations.AddField(
model_name='abon',
name='current_tariffs',
field=models.ManyToManyField(through='abonapp.AbonTariff', to='tariff_app.Tariff'),
),
migrations.AddField(
model_name='abon',
name='group',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='abonapp.AbonGroup'),
),
migrations.AlterUniqueTogether(
name='abontariff',
unique_together=set([('abon', 'tariff', 'tariff_priority')]),
),
migrations.AddField(
model_name='abongroup',
name='profiles',
field=models.ManyToManyField(blank=True, related_name='abon_groups', to=settings.AUTH_USER_MODEL),
),
migrations.AlterModelOptions(
name='abon',
options={'permissions': (('can_buy_tariff', 'Покупка тарифа абоненту'),)},
),
migrations.AlterModelOptions(
name='abongroup',
options={'permissions': (('can_add_ballance', 'Пополнение счёта'),)},
),
migrations.AlterModelOptions(
name='abontariff',
options={'ordering': ('tariff_priority',), 'permissions': (('can_complete_service', 'Досрочное завершение услуги абонента'), ('can_activate_service', 'Активация услуги абонента'))},
),
migrations.CreateModel(
name='AbonStreet',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=64)),
('group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='abonapp.AbonGroup')),
],
),
migrations.RemoveField(
model_name='abon',
name='address',
),
migrations.AddField(
model_name='abon',
name='description',
field=models.TextField(blank=True, null=True),
),
migrations.AddField(
model_name='abon',
name='house',
field=models.CharField(blank=True, max_length=12, null=True),
),
migrations.AddField(
model_name='abon',
name='street',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='abonapp.AbonStreet'),
),
migrations.CreateModel(
name='AllPayLog',
fields=[
@ -158,7 +113,7 @@ class Migration(migrations.Migration):
],
options={
'db_table': 'all_pay_log',
'ordering': ('date_action',),
'ordering': ['-date_action'],
},
),
migrations.CreateModel(
@ -167,70 +122,44 @@ class Migration(migrations.Migration):
('pay_id', models.CharField(max_length=36, primary_key=True, serialize=False, unique=True)),
('date_add', models.DateTimeField(auto_now_add=True)),
('summ', models.FloatField(default=0.0)),
('trade_point', models.CharField(blank=True, default=None, max_length=20, null=True, verbose_name='Trade point')),
('receipt_num', models.BigIntegerField(default=0, verbose_name='Receipt number')),
],
options={
'db_table': 'all_time_pay_log',
'ordering': ('date_add',),
'ordering': ['-date_add'],
},
),
migrations.CreateModel(
name='AbonRawPassword',
name='ExtraFieldsModel',
fields=[
('account', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to='abonapp.Abon')),
('passw_text', models.CharField(max_length=64)),
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(default='no title', max_length=16)),
('field_type', models.CharField(choices=[('int', 'Digital field'), ('str', 'Text field'), ('dbl', 'Floating field'), ('ipa', 'Ip Address')], default='str', max_length=3)),
('data', models.CharField(blank=True, max_length=64, null=True)),
],
options={
'db_table': 'abon_raw_password',
'db_table': 'abon_extra_fields',
},
),
migrations.AlterModelTable(
name='abonstreet',
table='abon_street',
),
migrations.AddField(
model_name='abontariff',
name='deadline',
field=models.DateTimeField(blank=True, default=None, null=True),
),
migrations.CreateModel(
name='ExtraFieldsModel',
name='InvoiceForPayment',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('field_type', models.CharField(choices=[('int', 'Цифровое поле'), ('str', 'Текстовое поле'), ('dbl', 'Дробное с плавающей точкой'), ('ipa', 'IP Адрес')], default='str', max_length=3)),
('data', models.CharField(blank=True, max_length=64, null=True)),
('title', models.CharField(default='no title', max_length=16)),
('status', models.BooleanField(default=False)),
('amount', models.FloatField(default=0.0)),
('comment', models.CharField(max_length=128)),
('date_create', models.DateTimeField(auto_now_add=True)),
('date_pay', models.DateTimeField(blank=True, null=True)),
],
options={
'db_table': 'abon_extra_fields',
'verbose_name': 'Debt',
'verbose_name_plural': 'Debts',
'db_table': 'abonent_inv_pay',
'ordering': ('date_create',),
'permissions': (('can_view_invoiceforpayment', 'Can view invoice for payment'),),
},
),
migrations.AlterModelOptions(
name='abon',
options={'permissions': (('can_buy_tariff', 'Покупка тарифа абоненту'), ('can_view_passport', 'Can view passport'))},
),
migrations.AlterModelOptions(
name='abontariff',
options={'ordering': ('tariff_priority',), 'permissions': (('can_complete_service', 'Снятие со счёта средств'), ('can_activate_service', 'Активация услуги абонента'))},
),
migrations.AlterField(
model_name='abon',
name='ballance',
field=models.FloatField(default=0.0),
),
migrations.AlterModelOptions(
name='abon',
options={'permissions': (('can_buy_tariff', 'Покупка тарифа абоненту'), ('can_view_passport', 'Может просматривать паспортные данные'))},
),
migrations.AddField(
model_name='abon',
name='extra_fields',
field=models.ManyToManyField(blank=True, to='abonapp.ExtraFieldsModel'),
),
migrations.AddField(
model_name='abongroup',
name='tariffs',
field=models.ManyToManyField(blank=True, related_name='tariff_groups', to='tariff_app.Tariff'),
),
migrations.CreateModel(
name='PassportInfo',
fields=[
@ -239,38 +168,83 @@ class Migration(migrations.Migration):
('number', models.CharField(max_length=6, validators=[django.core.validators.RegexValidator(re.compile('^-?\\d+\\Z', 32), code='invalid', message='Enter a valid integer.')])),
('distributor', models.CharField(max_length=64)),
('date_of_acceptance', models.DateField()),
('abon', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='abonapp.Abon')),
],
options={
'verbose_name': 'Passport Info',
'verbose_name_plural': 'Passport Info',
'db_table': 'passport_info',
},
),
migrations.CreateModel(
name='AbonDevice',
name='PeriodicPayForId',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('abon', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='abonapp.Abon')),
('device', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='devapp.Device')),
('last_pay', models.DateTimeField(blank=True, null=True, verbose_name='Last pay time')),
('next_pay', models.DateTimeField(verbose_name='Next time to pay')),
],
options={
'db_table': 'abon_device',
'db_table': 'periodic_pay_for_id',
},
),
migrations.AlterUniqueTogether(
name='abondevice',
unique_together=set([('abon', 'device')]),
migrations.CreateModel(
name='AbonRawPassword',
fields=[
('account', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to='abonapp.Abon')),
('passw_text', models.CharField(max_length=64)),
],
options={
'db_table': 'abon_raw_password',
},
),
migrations.AddField(
model_name='periodicpayforid',
name='account',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='abonapp.Abon', verbose_name='Account'),
),
migrations.AlterField(
model_name='abondevice',
migrations.AddField(
model_name='periodicpayforid',
name='periodic_pay',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='tariff_app.PeriodicPay', verbose_name='Periodic pay'),
),
migrations.AddField(
model_name='passportinfo',
name='abon',
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='abonapp.Abon'),
),
migrations.AddField(
model_name='invoiceforpayment',
name='abon',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='abonapp.Abon'),
),
migrations.AlterField(
model_name='abondevice',
name='device',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='devapp.Device'),
migrations.AddField(
model_name='invoiceforpayment',
name='author',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='alltimepaylog',
name='abon',
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_DEFAULT, to='abonapp.Abon'),
),
migrations.AddField(
model_name='additionaltelephone',
name='abon',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='additional_telephones', to='abonapp.Abon'),
),
migrations.AddField(
model_name='abonlog',
name='abon',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='abonapp.Abon'),
),
migrations.AddField(
model_name='abonlog',
name='author',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='abon',
name='ip_address',
field=mydefs.MyGenericIPAddressField(blank=True, max_length=8, null=True, protocol='ipv4'),
name='current_tariff',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='abonapp.AbonTariff'),
),
migrations.AddField(
model_name='abon',
@ -284,35 +258,17 @@ class Migration(migrations.Migration):
),
migrations.AddField(
model_name='abon',
name='is_dynamic_ip',
field=models.BooleanField(default=False),
),
migrations.DeleteModel(
name='AbonDevice',
),
migrations.AlterModelOptions(
name='abontariff',
options={'permissions': (('can_complete_service', 'Снятие со счёта средств'),)},
name='extra_fields',
field=models.ManyToManyField(blank=True, to='abonapp.ExtraFieldsModel'),
),
migrations.RemoveField(
migrations.AddField(
model_name='abon',
name='current_tariffs',
name='group',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='group_app.Group', verbose_name='User group'),
),
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',
name='street',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='abonapp.AbonStreet', verbose_name='Street'),
),
]

52
abonapp/migrations/0002_auto_20170905_1248.py

@ -1,52 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2017-09-05 12:48
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('abonapp', '0001_squashed_0022_auto_20170816_1109'),
]
operations = [
migrations.CreateModel(
name='AdditionalTelephone',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('telephone', models.CharField(max_length=16, validators=[django.core.validators.RegexValidator('^\\+[7,8,9,3]\\d{10,11}$')], verbose_name='Телефон')),
('owner_name', models.CharField(max_length=127)),
],
options={
'verbose_name': 'Дополнительный телефон',
'verbose_name_plural': 'Дополнительные телефоны',
'db_table': 'additional_telephones',
'ordering': ('owner_name',),
},
),
migrations.AlterModelOptions(
name='abon',
options={'permissions': (('can_buy_tariff', 'Покупка тарифа абоненту'), ('can_view_passport', 'Может просматривать паспортные данные')), 'verbose_name': 'Абонент', 'verbose_name_plural': 'Абоненты'},
),
migrations.AlterModelOptions(
name='abongroup',
options={'permissions': (('can_add_ballance', 'Пополнение счёта'),), 'verbose_name': 'Группа абонентов', 'verbose_name_plural': 'Группы абонентов'},
),
migrations.AlterModelOptions(
name='abonstreet',
options={'verbose_name': 'Улица', 'verbose_name_plural': 'Улицы'},
),
migrations.AlterModelOptions(
name='abontariff',
options={'permissions': (('can_complete_service', 'Снятие со счёта средств'),), 'verbose_name': 'Услуга абонента', 'verbose_name_plural': 'Услуги абонентов'},
),
migrations.AddField(
model_name='additionaltelephone',
name='abon',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='additional_telephones', to='abonapp.Abon'),
),
]

43
abonapp/migrations/0003_auto_20170927_1838.py

@ -1,43 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2017-09-27 18:38
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('abonapp', '0002_auto_20170905_1248'),
]
operations = [
migrations.AlterModelOptions(
name='abon',
options={'permissions': (('can_buy_tariff', 'Покупка тарифа абоненту'), ('can_view_passport', 'Может просматривать паспортные данные'), ('can_add_ballance', 'Пополнение счёта'), ('can_ping', 'Может пинговать')), 'verbose_name': 'Абонент', 'verbose_name_plural': 'Абоненты'},
),
migrations.AlterModelOptions(
name='abongroup',
options={'permissions': (('can_view_abongroup', 'Может просматривать группу абонентов'),), 'verbose_name': 'Группа абонентов', 'verbose_name_plural': 'Группы абонентов'},
),
migrations.AlterModelOptions(
name='abonlog',
options={'permissions': (('can_view_abonlog', 'Может видеть логи абонента'),)},
),
migrations.AlterModelOptions(
name='additionaltelephone',
options={'ordering': ('owner_name',), 'permissions': (('can_view_additionaltelephones', 'Может видеть дополнительные телефоны'),), 'verbose_name': 'Дополнительный телефон', 'verbose_name_plural': 'Дополнительные телефоны'},
),
migrations.AlterModelOptions(
name='invoiceforpayment',
options={'ordering': ('date_create',), 'permissions': (('can_view_invoiceforpayment', 'Может видеть назначенные платежи'),), 'verbose_name': 'Квитанция (долг)', 'verbose_name_plural': 'Квитанции (долги)'},
),
migrations.AlterModelOptions(
name='passportinfo',
options={'verbose_name': 'Паспортные данные', 'verbose_name_plural': 'Паспортные данные'},
),
migrations.AlterModelTable(
name='passportinfo',
table='passport_info',
),
]

130
abonapp/migrations/0004_auto_20180122_1732.py

@ -1,130 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-01-22 17:32
from __future__ import unicode_literals
from django.conf import settings
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('tariff_app', '0006_auto_20180122_1732'),
('abonapp', '0003_auto_20170927_1838'),
]
operations = [
migrations.CreateModel(
name='PeriodicPayForId',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('last_pay', models.DateTimeField(blank=True, null=True, verbose_name='Last pay time')),
('next_pay', models.DateTimeField(verbose_name='Next time to pay')),
],
options={
'db_table': 'periodic_pay_for_id',
},
),
migrations.AlterModelOptions(
name='abon',
options={'ordering': ['fio'], 'permissions': (('can_buy_tariff', 'Buy service perm'), ('can_view_passport', 'Can view passport'), ('can_add_ballance', 'fill account'), ('can_ping', 'Can ping')), 'verbose_name': 'Abon', 'verbose_name_plural': 'Abons'},
),
migrations.AlterModelOptions(
name='abongroup',
options={'ordering': ['title'], 'permissions': (('can_view_abongroup', 'Can view subscriber group'),), 'verbose_name': 'Abon group', 'verbose_name_plural': 'Abon groups'},
),
migrations.AlterModelOptions(
name='abonlog',
options={'ordering': ['-date'], 'permissions': (('can_view_abonlog', 'Can view subscriber logs'),)},
),
migrations.AlterModelOptions(
name='abonstreet',
options={'ordering': ['name'], 'verbose_name': 'Street', 'verbose_name_plural': 'Streets'},
),
migrations.AlterModelOptions(
name='abontariff',
options={'ordering': ['time_start'], 'permissions': (('can_complete_service', 'finish service perm'),), 'verbose_name': 'Abon service', 'verbose_name_plural': 'Abon services'},
),
migrations.AlterModelOptions(
name='additionaltelephone',
options={'ordering': ('owner_name',), 'permissions': (('can_view_additionaltelephones', 'Can view additional telephones'),), 'verbose_name': 'Additional telephone', 'verbose_name_plural': 'Additional telephones'},
),
migrations.AlterModelOptions(
name='allpaylog',
options={'ordering': ['-date_action']},
),
migrations.AlterModelOptions(
name='alltimepaylog',
options={'ordering': ['-date_add']},
),
migrations.AlterModelOptions(
name='invoiceforpayment',
options={'ordering': ('date_create',), 'permissions': (('can_view_invoiceforpayment', 'Can view invoice for payment'),), 'verbose_name': 'Debt', 'verbose_name_plural': 'Debts'},
),
migrations.AlterModelOptions(
name='passportinfo',
options={'verbose_name': 'Passport Info', 'verbose_name_plural': 'Passport Info'},
),
migrations.AddField(
model_name='alltimepaylog',
name='abon',
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_DEFAULT, to='abonapp.Abon'),
),
migrations.AddField(
model_name='alltimepaylog',
name='receipt_num',
field=models.IntegerField(default=0, verbose_name='Receipt number'),
),
migrations.AddField(
model_name='alltimepaylog',
name='trade_point',
field=models.CharField(blank=True, default=None, max_length=20, null=True, verbose_name='Trade point'),
),
migrations.AlterField(
model_name='abon',
name='description',
field=models.TextField(blank=True, null=True, verbose_name='Comment'),
),
migrations.AlterField(
model_name='abon',
name='group',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='abonapp.AbonGroup', verbose_name='User group'),
),
migrations.AlterField(
model_name='abon',
name='house',
field=models.CharField(blank=True, max_length=12, null=True, verbose_name='House'),
),
migrations.AlterField(
model_name='abon',
name='street',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='abonapp.AbonStreet', verbose_name='Street'),
),
migrations.AlterField(
model_name='abonlog',
name='author',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL),
),
migrations.AlterField(
model_name='additionaltelephone',
name='telephone',
field=models.CharField(max_length=16, validators=[django.core.validators.RegexValidator('^\\+[7,8,9,3]\\d{10,11}$')], verbose_name='Telephone'),
),
migrations.AlterField(
model_name='extrafieldsmodel',
name='field_type',
field=models.CharField(choices=[('int', 'Digital field'), ('str', 'Text field'), ('dbl', 'Floating field'), ('ipa', 'Ip Address')], default='str', max_length=3),
),
migrations.AddField(
model_name='periodicpayforid',
name='account',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='abonapp.Abon', verbose_name='Account'),
),
migrations.AddField(
model_name='periodicpayforid',
name='periodic_pay',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='tariff_app.PeriodicPay', verbose_name='Periodic pay'),
),
]

20
abonapp/migrations/0005_auto_20180123_1353.py

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-01-23 13:53
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('abonapp', '0004_auto_20180122_1732'),
]
operations = [
migrations.AlterField(
model_name='alltimepaylog',
name='receipt_num',
field=models.BigIntegerField(default=0, verbose_name='Receipt number'),
),
]

23
abonapp/migrations/0006_abon_markers.py

@ -1,23 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-02-13 14:06
from __future__ import unicode_literals
import bitfield.models
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('abonapp', '0005_auto_20180123_1353'),
]
operations = [
migrations.AddField(
model_name='abon',
name='markers',
field=bitfield.models.BitField((('icon_donkey', 'Donkey'), ('icon_fire', 'Fire'), ('icon_ok', 'Ok'),
('icon_king', 'King'), ('icon_tv', 'TV'), ('icon_smile', 'Smile'),
('icon_dollar', 'Dollar'), ('icon_service', 'Service'),
('icon_mrk', 'Marker')), default=None),
),
]

96
abonapp/models.py

@ -5,13 +5,14 @@ from django.core import validators
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
from django.db import models, connection, transaction
from django.db.models.signals import post_save, post_delete, pre_delete, post_init
from django.db.models.signals import post_delete, pre_delete, post_init
from django.dispatch import receiver
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ugettext_lazy as _, gettext
from accounts_app.models import UserProfile, MyUserManager
from accounts_app.models import UserProfile, MyUserManager, BaseAccount
from agent import Transmitter, AbonStruct, TariffStruct, NasFailedResult, NasNetworkError
from group_app.models import Group
from mydefs import MyGenericIPAddressField, ip2int, LogicError, ip_addr_regex
from tariff_app.models import Tariff, PeriodicPay
from bitfield import BitField
@ -20,24 +21,6 @@ from bitfield import BitField
TELEPHONE_REGEXP = getattr(settings, 'TELEPHONE_REGEXP', r'^\+[7,8,9,3]\d{10,11}$')
class AbonGroup(models.Model):
title = models.CharField(max_length=127, unique=True)
profiles = models.ManyToManyField(UserProfile, blank=True, related_name='abon_groups')
tariffs = models.ManyToManyField(Tariff, blank=True, related_name='tariff_groups')
class Meta:
db_table = 'abonent_groups'
permissions = (
('can_view_abongroup', _('Can view subscriber group')),
)
verbose_name = _('Abon group')
verbose_name_plural = _('Abon groups')
ordering = ['title']
def __str__(self):
return self.title
class AbonLog(models.Model):
abon = models.ForeignKey('Abon', models.CASCADE)
amount = models.FloatField(default=0.0)
@ -89,7 +72,7 @@ class AbonTariff(models.Model):
class AbonStreet(models.Model):
name = models.CharField(max_length=64)
group = models.ForeignKey(AbonGroup, models.CASCADE)
group = models.ForeignKey(Group, models.CASCADE)
def __str__(self):
return self.name
@ -149,9 +132,9 @@ class AbonManager(MyUserManager):
return super(MyUserManager, self).get_queryset().filter(is_admin=False)
class Abon(UserProfile):
class Abon(BaseAccount):
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, verbose_name=_('User group'))
group = models.ForeignKey(Group, models.SET_NULL, blank=True, null=True, verbose_name=_('User group'))
ballance = models.FloatField(default=0.0)
ip_address = MyGenericIPAddressField(blank=True, null=True)
description = models.TextField(_('Comment'), null=True, blank=True)
@ -204,13 +187,6 @@ class Abon(UserProfile):
verbose_name_plural = _('Abons')
ordering = ['fio']
# pay something
def make_pay(self, curuser, how_match_to_pay=0.0):
post_save.disconnect(abon_post_save, sender=Abon)
self.ballance -= how_match_to_pay
self.save(update_fields=['ballance'])
post_save.connect(abon_post_save, sender=Abon)
def add_ballance(self, current_user, amount, comment):
AbonLog.objects.create(
abon=self,
@ -226,8 +202,11 @@ class Abon(UserProfile):
amount = round(tariff.amount, 2)
if not author.is_staff and tariff.is_admin:
raise LogicError(_('User that is no staff can not buy admin services'))
if tariff.is_admin:
if author is not None and not author.is_staff:
raise LogicError(_('User that is no staff can not buy admin services'))
else:
raise LogicError(_('This user can not buy admin services'))
if self.current_tariff is not None:
if self.current_tariff.tariff == tariff:
@ -298,6 +277,23 @@ class Abon(UserProfile):
raise LogicError(_('Ip address already exist'))
super(Abon, self).save(*args, **kwargs)
def sync_with_nas(self, created: bool):
timeout = None
if hasattr(self, 'is_dhcp') and self.is_dhcp:
timeout = getattr(settings, 'DHCP_TIMEOUT', 14400)
agent_abon = self.build_agent_struct()
if agent_abon is None:
return True
try:
tm = Transmitter()
if created:
tm.add_user(agent_abon, ip_timeout=timeout)
else:
tm.update_user(agent_abon, ip_timeout=timeout)
except (NasFailedResult, NasNetworkError, ConnectionResetError) as e:
print('ERROR:', e)
return True
class PassportInfo(models.Model):
series = models.CharField(max_length=4, validators=[validators.integer_validator])
@ -354,7 +350,6 @@ class AllTimePayLogManager(models.Manager):
r = cur.fetchone()
if r is None: break
summ, dat = r
print(summ, dat)
yield {'summ': summ, 'pay_date': datetime.strptime(dat, '%Y-%m-%d')}
@ -443,14 +438,10 @@ class PeriodicPayForId(models.Model):
next_pay_date = pp.get_next_time_to_pay(self.last_pay)
abon = self.account
with transaction.atomic():
abon.make_pay(author, amount)
AbonLog.objects.create(
abon=abon, amount=-amount,
author=author,
comment=comment or _('Charge for "%(service)s"') % {
'service': self.periodic_pay
}
)
abon.add_ballance(author, -amount, comment=gettext('Charge for "%(service)s"') % {
'service': self.periodic_pay
})
abon.save(update_fields=['ballance'])
self.last_pay = now
self.next_pay = next_pay_date
self.save(update_fields=['last_pay', 'next_pay'])
@ -462,27 +453,6 @@ class PeriodicPayForId(models.Model):
db_table = 'periodic_pay_for_id'
@receiver(post_save, sender=Abon)
def abon_post_save(sender, **kwargs):
instance, created = kwargs["instance"], kwargs["created"]
timeout = None
if hasattr(instance, 'is_dhcp') and instance.is_dhcp:
timeout = getattr(settings, 'DHCP_TIMEOUT', 14400)
agent_abon = instance.build_agent_struct()
if agent_abon is None:
return True
try:
tm = Transmitter()
if created:
tm.add_user(agent_abon, ip_timeout=timeout)
else:
tm.update_user(agent_abon, ip_timeout=timeout)
except (NasFailedResult, NasNetworkError, ConnectionResetError) as e:
print('ERROR:', e)
return True
@receiver(post_delete, sender=Abon)
def abon_del_signal(sender, **kwargs):
abon = kwargs["instance"]

4
abonapp/templates/abonapp/addAbon.html

@ -5,7 +5,7 @@
<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:people_list' group.id %}">{{ group.title }}</a></li>
<li class="active">{% trans 'Add abon' %}</li>
</ol>
@ -16,7 +16,7 @@
<h3 class="panel-title">{% trans 'Add abon' %}</h3>
</div>
<div class="panel-body">
<form role="form" action="{% url 'abonapp:add_abon' abon_group.id %}" method="post">{% csrf_token %}
<form role="form" action="{% url 'abonapp:add_abon' group.id %}" method="post">{% csrf_token %}
<div class="form-group">
<label for="login">{% trans 'login' %}</label>

39
abonapp/templates/abonapp/addGroup.html

@ -1,39 +0,0 @@
{% extends '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 class="active">{% trans 'Add group' %}</li>
</ol>
{% include 'message_block.html' %}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{% trans 'Add group' %}</h3>
</div>
<div class="panel-body">
<form role="form" action="{% url 'abonapp:add_group' %}" method="post">{% csrf_token %}
<div class="form-group">
<label for="gtitle">{% trans 'Group title' %}</label>
<div class="input-group">
<span class="input-group-addon"><span class="glyphicon glyphicon-bookmark"></span></span>
<input id="gtitle" type="text" name="title" required class="form-control">{{ form.title.errors }}
</div>
</div>
<div class="btn-group">
<button type="submit" class="btn btn-sm btn-primary">
<span class="glyphicon glyphicon-save"></span> {% trans 'Save' %}
</button>
<button type="reset" class="btn btn-sm btn-default">
<span class="glyphicon glyphicon-remove-circle"></span> {% trans 'Reset' %}
</button>
</div>
</form>
</div>
</div>
{% endblock %}

8
abonapp/templates/abonapp/addInvoice.html

@ -5,8 +5,8 @@
<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><a href="{% url 'abonapp:people_list' group.id %}">{{ group.title }}</a></li>
<li><a href="{% url 'abonapp:abon_home' group.id abon.id %}">{{ abon.fio }}</a></li>
<li class="active">{% trans 'Add debt' %}</li>
</ol>
@ -15,11 +15,11 @@
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{% trans 'Add receipt for' %}
<u><a href="{% url 'abonapp:abon_home' abon_group.id abon.id %}">{{ abon.fio }}</a></u>
<u><a href="{% url 'abonapp:abon_home' group.id abon.id %}">{{ abon.fio }}</a></u>
</h3>
</div>
<div class="panel-body">
<form role="form" action="{% url 'abonapp:add_invoice' abon_group.id abon.id %}"
<form role="form" action="{% url 'abonapp:add_invoice' group.id abon.id %}"
method="post">{% csrf_token %}
<div class="form-group">
<label for="pamount">{% trans 'Sum of pay' %}</label>

10
abonapp/templates/abonapp/buy_tariff.html

@ -5,8 +5,8 @@
<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.pk %}">{{ abon_group.title }}</a></li>
<li><a href="{% url 'abonapp:abon_home' abon_group.pk abon.pk %}">{{ abon.fio }}</a></li>
<li><a href="{% url 'abonapp:people_list' group.pk %}">{{ group.title }}</a></li>
<li><a href="{% url 'abonapp:abon_home' group.pk abon.pk %}">{{ abon.fio }}</a></li>
<li class="active">{% trans 'Pick a service' %}</li>
</ol>
@ -15,11 +15,11 @@
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{% trans 'Pick a service for' %}
<a href="{% url 'abonapp:abon_home' abon_group.pk abon.pk %}"><u>{% trans 'Sub' %}</u></a>
<a href="{% url 'abonapp:abon_home' group.pk abon.pk %}"><u>{% trans 'Sub' %}</u></a>
</h3>
</div>
<div class="panel-body">
<form role="form" action="{% url 'abonapp:pick_tariff' abon_group.pk abon.pk %}"
<form role="form" action="{% url 'abonapp:pick_tariff' group.pk abon.pk %}"
method="post">{% csrf_token %}
<div class="form-group">
{% if tariffs %}
@ -59,7 +59,7 @@
<div class="alert alert-danger" role="alert">
<span class="glyphicon glyphicon-exclamation-sign"></span>
{% trans 'This group has no services' %},
<a href="{% url 'abonapp:ch_group_tariff' abon_group.pk %}">
<a href="{% url 'abonapp:ch_group_tariff' group.pk %}">
{% trans 'Attach serices to groups' %}
</a>
</div>

2
abonapp/templates/abonapp/charts.html

@ -11,7 +11,7 @@
<div class="panel-body">
{% if charts_data %}
<div id="chrt"></div>
<form action="{% url 'abonapp:charts' abon_group.pk abon.pk %}" method="get" class="input-group">
<form action="{% url 'abonapp:charts' group.pk abon.pk %}" method="get" class="input-group">
<span class="input-group-btn">
<button class="btn btn-default" type="submit">
<span class="glyphicon glyphicon-calendar"></span> {% trans 'Show graph by date' %}

24
abonapp/templates/abonapp/editAbon.html

@ -10,7 +10,7 @@
<h3 class="panel-title">{% trans 'Change subscriber' %}</h3>
</div>
<div class="panel-body">
<form autocomplete="off" class="form-horizontal" action="{% url 'abonapp:abon_home' abon_group.pk abon.pk %}" method="post">{% csrf_token %}
<form autocomplete="off" class="form-horizontal" action="{% url 'abonapp:abon_home' group.pk abon.pk %}" method="post">{% csrf_token %}
{% bootstrap_field form.username label_class='col-sm-4' field_class='col-sm-8' form_group_class='form-group-sm' %}
{% bootstrap_field form.fio label_class='col-sm-4' field_class='col-sm-8' form_group_class='form-group-sm' %}
@ -24,10 +24,10 @@
<a href="sip:{{ form.telephone.value }}" class="btn btn-default" data-toggle="tooltip" title="{% trans 'Call to' %}">
<span class="glyphicon glyphicon-earphone"></span>
</a>
<a href="{% url 'abonapp:telephones' abon_group.pk abon.pk %}" class="btn btn-default btn-modal" data-toggle="tooltip" title="{% trans 'Additional telephones' %}">
<a href="{% url 'abonapp:telephones' group.pk abon.pk %}" class="btn btn-default btn-modal" data-toggle="tooltip" title="{% trans 'Additional telephones' %}">
<span class="glyphicon glyphicon-list"></span>
</a>
<a href="{% url 'abonapp:telephone_new' abon_group.pk abon.pk %}" class="btn btn-default btn-modal" data-toggle="tooltip" title="{% trans 'Add telephone' %}">
<a href="{% url 'abonapp:telephone_new' group.pk abon.pk %}" class="btn btn-default btn-modal" data-toggle="tooltip" title="{% trans 'Add telephone' %}">
<span class="glyphicon glyphicon-plus"></span>
</a>
</span>
@ -41,7 +41,7 @@
<div class="input-group input-group-sm">
<input type="text" value="{{ ip|default:'' }}" class="form-control" name="ip" placeholder="{% trans 'Not assigned' %}" id="ipfield" pattern="^(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]?)$"{% if abon.is_dynamic_ip %} disabled{% endif %}/>
<span class="input-group-btn">
<a href="{% url 'abonapp:reset_ip' abon_group.pk abon.pk %}" class="btn btn-default btn-cmd" data-toggle="tooltip" title="{% trans 'Reset ip' %}" onclick="$('#ipfield').val('');">
<a href="{% url 'abonapp:reset_ip' group.pk abon.pk %}" class="btn btn-default btn-cmd" data-toggle="tooltip" title="{% trans 'Reset ip' %}" onclick="$('#ipfield').val('');">
<span class="glyphicon glyphicon-refresh"></span>
</a>
</span>
@ -116,20 +116,20 @@
</div>
<div class="panel-body">
<form class="form-horizontal" action="{% url 'abonapp:save_user_dev_port' abon_group.pk abon.pk %}" method="post">{% csrf_token %}
<form class="form-horizontal" action="{% url 'abonapp:save_user_dev_port' group.pk abon.pk %}" method="post">{% csrf_token %}
<div class="form-group-sm">
<label for="id_method" class="col-sm-4 control-label">{% trans 'Device' %}</label>
<div class="col-sm-8 btn-group btn-group-sm">
{% if device %}
<a href="{% url 'devapp:view' abon_group.pk device.pk %}" target="_blank" class="btn btn-sm btn-default" title="{% trans 'Mac Address' %}: {{ device.mac_addr|default:_('Not assigned') }}">
<a href="{% url 'devapp:view' group.pk device.pk %}" target="_blank" class="btn btn-sm btn-default" title="{% trans 'Mac Address' %}: {{ device.mac_addr|default:_('Not assigned') }}">
<span class="glyphicon glyphicon-hdd"></span> <span class="hidden-md">{{ device.comment|truncatechars:11 }} {{ device.ip_address }}</span>
</a>
<a href="{% url 'abonapp:clear_dev' abon_group.pk abon.pk %}" class="btn btn-sm btn-danger">
<a href="{% url 'abonapp:clear_dev' group.pk abon.pk %}" class="btn btn-sm btn-danger">
<span class="glyphicon glyphicon-remove-circle"></span> <span class="hidden-xs hidden-lg">{% trans 'Remove clutch' %}</span>
</a>
{% else %}
<a href="{% url 'abonapp:dev' abon_group.pk abon.pk %}" class="btn btn-success btn-sm btn-modal">
<a href="{% url 'abonapp:dev' group.pk abon.pk %}" class="btn btn-success btn-sm btn-modal">
<span class="glyphicon glyphicon-plus"></span> {% trans 'Add clutch' %}
</a>
{% endif %}
@ -177,7 +177,7 @@
<h3 class="panel-title">{% trans 'Extra fields' %}</h3>
</div>
<div class="panel-body">
<form class="form-horizontal" action="{% url 'abonapp:extra_field_edit' abon_group.pk abon.pk %}" method="post">{% csrf_token %}
<form class="form-horizontal" action="{% url 'abonapp:extra_field_edit' group.pk abon.pk %}" method="post">{% csrf_token %}
{% for ef in abon.extra_fields.all %}
<div class="form-group-sm">
@ -188,7 +188,7 @@
<input type="text" value="{{ ef.data|default:_('Not assigned') }}" class="form-control" pattern="{{ ef.get_regexp }}" name="ex">
<input type="hidden" value="{{ ef.pk }}" name="ed">
<span class="input-group-btn">
<a href="{% url 'abonapp:extra_field_delete' abon_group.pk abon.pk ef.pk %}" class="btn btn-danger" title="{% trans 'Delete' %}">
<a href="{% url 'abonapp:extra_field_delete' group.pk abon.pk ef.pk %}" class="btn btn-danger" title="{% trans 'Delete' %}">
<span class="glyphicon glyphicon-remove"></span>
</a>
</span>
@ -202,7 +202,7 @@
<div class="form-group-sm">
<div class="col-sm-offset-4 col-sm-8 btn-group btn-group-sm">
<a href="{% url 'abonapp:extra_field' abon_group.pk abon.pk %}" class="btn btn-success btn-modal" title="{% trans 'Add extra field' %}">
<a href="{% url 'abonapp:extra_field' group.pk abon.pk %}" class="btn btn-success btn-modal" title="{% trans 'Add extra field' %}">
<span class="glyphicon glyphicon-plus"></span> {% trans 'Add' %}
</a>
@ -227,7 +227,7 @@
</div>
<div class="panel-footer">
<div class="btn-group">
<a href="{% url 'abonapp:markers_edit' abon_group.pk abon.pk %}" class="btn btn-default btn-modal">
<a href="{% url 'abonapp:markers_edit' group.pk abon.pk %}" class="btn btn-default btn-modal">
<span class="glyphicon glyphicon-edit"></span> {% trans 'Edit' %}
</a>
</div>

16
abonapp/templates/abonapp/ext.htm

@ -5,7 +5,7 @@
<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.pk %}">{{ abon_group.title }}</a></li>
<li><a href="{% url 'abonapp:people_list' group.pk %}">{{ group.title }}</a></li>
<li class="active">{{ abon.fio }}</li>
</ol>
@ -21,39 +21,39 @@
<ul class="nav nav-tabs nav-justified">
{% url 'abonapp:abon_home' abon_group.pk abon.pk as abon_home %}
{% url 'abonapp:abon_home' group.pk abon.pk as abon_home %}
<li{% if abon_home == request.path %} class="active"{% endif %}>
<a href="{{ abon_home }}">{% trans 'Sub information' %}</a>
</li>
{% url 'abonapp:abon_services' abon_group.pk abon.pk as abserv %}
{% url 'abonapp:abon_services' group.pk abon.pk as abserv %}
<li{% if abserv == request.path %} class="active"{% endif %}>
<a href="{{ abserv }}">{% trans 'Services' %}</a>
</li>
{% if perms.abonapp.can_view_passport %}
{% url 'abonapp:passport_view' abon_group.pk abon.pk as passport_view_url %}
{% url 'abonapp:passport_view' group.pk abon.pk as passport_view_url %}
<li{% if passport_view_url == request.path %} class="active"{% endif %}>
<a href="{{ passport_view_url }}">{% trans 'Passport information' %}</a>
</li>
{% endif %}
{% url 'abonapp:abon_phistory' abon_group.pk abon.pk as abphist %}
{% url 'abonapp:abon_phistory' group.pk abon.pk as abphist %}
<li{% if abphist == request.path %} class="active"{% endif %}>
<a href="{{ abphist }}">{% trans 'Payments' %}</a>
</li>
{% url 'abonapp:task_log' abon_group.pk abon.pk as abtasklog %}
{% url 'abonapp:task_log' group.pk abon.pk as abtasklog %}
<li{% if abtasklog == request.path %} class="active"{% endif %}>
<a href="{{ abtasklog }}">{% trans 'History of tasks' %}</a>
</li>
{% url 'abonapp:charts' abon_group.pk abon.pk as abtasklog %}
{% url 'abonapp:charts' group.pk abon.pk as abtasklog %}
<li{% if abtasklog == request.path %} class="active"{% endif %}>
<a href="{{ abtasklog }}">{% trans 'Charts' %}</a>
</li>
{% url 'abonapp:dials' abon_group.pk abon.pk as abdials %}
{% url 'abonapp:dials' group.pk abon.pk as abdials %}
<li{% if abdials == request.path %} class="active"{% endif %}>
<a href="{{ abdials }}">{% trans 'Dialing' %}</a>
</li>

12
abonapp/templates/abonapp/group_list.html

@ -35,13 +35,6 @@
<td><a href="{% url 'abonapp:people_list' gr.pk %}">{{ gr.title }}</a></td>
<td class="hidden-xs">{{ gr.usercount }}</td>
<td class="btn-group">
{% if perms.abonapp.delete_abongroup %}
{% if gr.usercount == 0 %}
<a href="{% url 'abonapp:del_group' %}?id={{ gr.pk }}" class="btn btn-sm btn-danger">
<span class="glyphicon glyphicon-remove-circle"></span>
</a>
{% endif %}
{% endif %}
<a href="{% url 'abonapp:ch_group_tariff' gr.pk %}" class="btn btn-sm btn-default"
title="{% trans 'User groups' %}">
<span class="glyphicon glyphicon-cog"></span>
@ -57,11 +50,6 @@
<tfoot>
<tr>
<td colspan="4" class="btn-group btn-group-sm">
{% if perms.abonapp.add_abongroup %}
<a href="{% url 'abonapp:add_group' %}" class="btn btn-success">
<span class="glyphicon glyphicon-plus"></span> <span class="hidden-xs">{% trans 'Add group' %}</span>
</a>
{% endif %}
{% if perms.abonapp.can_view_abonlog %}
<a href="{% url 'abonapp:log' %}" class="btn btn-default">
<span class="glyphicon glyphicon-record"></span> <span class="hidden-xs">{% trans 'Subscribers actions' %}</span>

6
abonapp/templates/abonapp/group_tariffs.html

@ -5,18 +5,18 @@
<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:people_list' group.id %}">{{ group.title }}</a></li>
<li class="active">{% trans 'Belonging services for groups' %}</li>
</ol>
{% include 'message_block.html' %}
<legend>{% trans 'Belonging services for groups' %}</legend>
<form action="{% url 'abonapp:ch_group_tariff' abon_group.pk %}" method="post" role="form">{% csrf_token %}
<form action="{% url 'abonapp:ch_group_tariff' group.pk %}" method="post" role="form">{% csrf_token %}
{% for tariff in tariffs %}
<div class="checkbox">
<label>
{% if tariff in abon_group.tariffs.all %}
{% if tariff.pk in seleted_tariffs %}
<input name="tr" type="checkbox" value="{{ tariff.pk }}" checked/>
{% else %}
<input name="tr" type="checkbox" value="{{ tariff.pk }}"/>

6
abonapp/templates/abonapp/invoiceForPayment.html

@ -5,8 +5,8 @@
<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.pk %}">{{ abon_group.title }}</a></li>
<li><a href="{% url 'abonapp:abon_phistory' abon_group.pk abon.pk %}">{{ abon.fio }}</a></li>
<li><a href="{% url 'abonapp:people_list' group.pk %}">{{ group.title }}</a></li>
<li><a href="{% url 'abonapp:abon_phistory' group.pk abon.pk %}">{{ abon.fio }}</a></li>
<li class="active">{% trans 'Debts' %}</li>
</ol>
@ -57,7 +57,7 @@
<tfoot>
<tr>
<th colspan="7">
<a href="{% url 'abonapp:add_invoice' abon_group.id abon.id %}" class="btn btn-success btn-sm">
<a href="{% url 'abonapp:add_invoice' group.id abon.id %}" class="btn btn-success btn-sm">
<span class="glyphicon glyphicon-plus"></span> {% trans 'Add debt' %}
</a>
</th>

7
abonapp/templates/abonapp/log.html

@ -39,7 +39,12 @@
</td>
<td>{{ l.comment }}</td>
<td>{{ l.date|date:"D d E Y H:i:s" }}</td>
<td><a href="{% url 'acc_app:other_profile' l.author.id %}">{{ l.author.username }}</a></td>
<td>
{% if l.author %}
<a href="{% url 'acc_app:other_profile' l.author.id %}">{{ l.author.username }}</a></td>
{% else %}
---
{% endif %}
</tr>
{% empty %}
<tr>

3
abonapp/templates/abonapp/modal_abonamount.html

@ -1,6 +1,5 @@
{% load i18n %}
<form role="form" action="{% url 'abonapp:abon_amount' abon_group.id abon.id %}" method="post"> {% csrf_token %}
<form role="form" action="{% url 'abonapp:abon_amount' group_id abon.id %}" method="post"> {% csrf_token %}
<div class="modal-header primary">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title"><span class="glyphicon glyphicon-shopping-cart"></span>{% trans 'Additional sum' %}</h4>

2
abonapp/templates/abonapp/passport_view.html

@ -9,7 +9,7 @@
<h3 class="panel-title">{% trans 'Passport information' %}</h3>
</div>
<div class="panel-body">
<form role="form" class="form-horizontal" action="{% url 'abonapp:passport_view' abon_group.pk abon.pk %}" method="post">{% csrf_token %}
<form role="form" class="form-horizontal" action="{% url 'abonapp:passport_view' group.pk abon.pk %}" method="post">{% csrf_token %}
<div class="form-group-sm">
<label for="id_series" class="col-sm-3 control-label">{% trans 'Pasport serial' %}</label>

4
abonapp/templates/abonapp/payHistory.html

@ -36,7 +36,7 @@
<tr>
<td colspan="4" class="btn-group btn-group-sm">
{% if perms.abonapp.can_add_ballance %}
<a href="{% url 'abonapp:abon_amount' abon_group.pk abon.pk %}" class="btn btn-default btn-modal">
<a href="{% url 'abonapp:abon_amount' group.pk abon.pk %}" class="btn btn-default btn-modal">
<span class="glyphicon glyphicon-credit-card"></span> {% trans 'Fill account' %}
</a>
{% else %}
@ -44,7 +44,7 @@
<span class="glyphicon glyphicon-credit-card"></span> {% trans 'Fill account' %}
</a>
{% endif %}
<a href="{% url 'abonapp:abon_debts' abon_group.pk abon.pk %}" class="btn btn-default">
<a href="{% url 'abonapp:abon_debts' group.pk abon.pk %}" class="btn btn-default">
<span class="glyphicon glyphicon-gbp"></span> {% trans 'Debts' %}
</a>
</td>

40
abonapp/templates/abonapp/peoples.html

@ -3,13 +3,13 @@
{% load dpagination %}
{% 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 class="active">{{ abon_group.title }}</li>
</ol>
<ol class="breadcrumb">
<li><span class="glyphicon glyphicon-home"></span></li>
<li><a href="{% url 'abonapp:group_list' %}">{% trans 'User groups' %}</a></li>
<li class="active">{{ group.title }}</li>
</ol>
{% include 'message_block.html' %}
{% include 'message_block.html' %}
<h3>{% trans 'The people in the selected group' %}</h3>
<div class="row">
@ -20,32 +20,32 @@
<tr>
<th>#</th>
<th class="col-xs-1">
<a href="{% url 'abonapp:people_list' abon_group.pk %}?{% url_replace request order_by='username' dir=dir|default:'down' %}">
<a href="{% url 'abonapp:people_list' group.pk %}?{% url_replace request order_by='username' dir=dir|default:'down' %}">
{% trans 'Sub' %}
</a>
{% if order_by == 'username' %}<span class="glyphicon glyphicon-filter"></span>{% endif %}
</th>
<th class="hidden-xs">{% trans 'Last traffic' %}</th>
<th class="col-xs-1 hidden-md">
<a href="{% url 'abonapp:people_list' abon_group.pk %}?{% url_replace request order_by='ip_address' dir=dir|default:'down' %}">
<a href="{% url 'abonapp:people_list' group.pk %}?{% url_replace request order_by='ip_address' dir=dir|default:'down' %}">
{% trans 'Ip address' %}
</a>
{% if order_by == 'ip_address' %}<span class="glyphicon glyphicon-filter"></span>{% endif %}
</th>
<th class="col-xs-2">
<a href="{% url 'abonapp:people_list' abon_group.pk %}?{% url_replace request order_by='fio' dir=dir|default:'down' %}">
<a href="{% url 'abonapp:people_list' group.pk %}?{% url_replace request order_by='fio' dir=dir|default:'down' %}">
{% trans 'fio' %}
</a>
{% if order_by == 'fio' %}<span class="glyphicon glyphicon-filter"></span>{% endif %}
</th>
<th class="col-xs-2">
<a href="{% url 'abonapp:people_list' abon_group.pk %}?{% url_replace request order_by='street' dir=dir|default:'down' %}">
<a href="{% url 'abonapp:people_list' group.pk %}?{% url_replace request order_by='street' dir=dir|default:'down' %}">
{% trans 'Street' %}
</a>
{% if order_by == 'street' %}<span class="glyphicon glyphicon-filter"></span>{% endif %}
</th>
<th class="col-xs-1">
<a href="{% url 'abonapp:people_list' abon_group.pk %}?{% url_replace request order_by='house' dir=dir|default:'down' %}">
<a href="{% url 'abonapp:people_list' group.pk %}?{% url_replace request order_by='house' dir=dir|default:'down' %}">
{% trans 'Apartment' %}
</a>
{% if order_by == 'house' %}<span class="glyphicon glyphicon-filter"></span>{% endif %}
@ -53,7 +53,7 @@
<th class="col-xs-2">{% trans 'Telephone' %}</th>
<th class="col-xs-2">{% trans 'Service' %}</th>
<th class="hidden-xs col-sm-1">
<a href="{% url 'abonapp:people_list' abon_group.pk %}?{% url_replace request order_by='ballance' dir=dir|default:'down' %}">
<a href="{% url 'abonapp:people_list' group.pk %}?{% url_replace request order_by='ballance' dir=dir|default:'down' %}">
{% trans 'Ballance' %}
</a>
{% if order_by == 'ballance' %}<span class="glyphicon glyphicon-filter"></span>{% endif %}
@ -121,7 +121,7 @@
<td colspan="12">
{% trans 'Subscribers not found' %}.
{% if perms.abonapp.add_abon %}
<a href="{% url 'abonapp:add_abon' abon_group.pk %}">{% trans 'Add abon' %}</a>
<a href="{% url 'abonapp:add_abon' group.pk %}">{% trans 'Add abon' %}</a>
{% endif %}
</td>
</tr>
@ -132,17 +132,17 @@
<tr>
<td colspan="12" class="btn-group btn-group-sm">
{% if perms.abonapp.add_abon %}
<a href="{% url 'abonapp:add_abon' abon_group.pk %}" class="btn btn-default" title="{% trans 'Add' %}" data-toggle="tooltip">
<a href="{% url 'abonapp:add_abon' group.pk %}" class="btn btn-default" title="{% trans 'Add' %}" data-toggle="tooltip">
<span class="glyphicon glyphicon-plus"></span> {% trans 'Add abon' %}
</a>
{% endif %}
<a href="{% url 'abonapp:ch_group_tariff' abon_group.pk %}" class="btn btn-default" title="{% trans 'User groups' %}" data-toggle="tooltip">
<a href="{% url 'abonapp:ch_group_tariff' group.pk %}" class="btn btn-default" title="{% trans 'User groups' %}" data-toggle="tooltip">
<span class="glyphicon glyphicon-subscript"></span> {% trans 'Tariffs in groups' %}
</a>
<a href="{% url 'abonapp:phonebook' abon_group.pk %}" class="btn btn-default btn-modal">
<a href="{% url 'abonapp:phonebook' group.pk %}" class="btn btn-default btn-modal">
<span class="glyphicon glyphicon-earphone"></span> {% trans 'Phonebook' %}
</a>
<a href="{% url 'abonapp:abon_export' abon_group.pk %}" class="btn btn-default btn-modal">
<a href="{% url 'abonapp:abon_export' group.pk %}" class="btn btn-default btn-modal">
<span class="glyphicon glyphicon-export"></span> {% trans 'Export users' %}
</a>
</td>
@ -156,15 +156,15 @@
<div class="panel-heading">{% trans 'Streets' %}</div>
<div class="list-group">
{% for street in streets %}
<a href="{% url 'abonapp:people_list' abon_group.pk %}?{% url_page_replace request 'street' street.pk %}" class="list-group-item{% if street_id == street.pk %} active{% endif %}">{{ street.name }}</a>
<a href="{% url 'abonapp:people_list' group.pk %}?{% url_page_replace request 'street' street.pk %}" class="list-group-item{% if street_id == street.pk %} active{% endif %}">{{ street.name }}</a>
{% empty %}
<a href="#" class="list-group-item">{% trans 'No streets found for that group' %}</a>
{% endfor %}
<div class="btn-group btn-group-sm btn-group-justified">
<a href="{% url 'abonapp:street_add' abon_group.pk %}" class="btn btn-success btn-modal" data-toggle="tooltip" title="{% trans 'Add street' %}">
<a href="{% url 'abonapp:street_add' group.pk %}" class="btn btn-success btn-modal" data-toggle="tooltip" title="{% trans 'Add street' %}">
<span class="glyphicon glyphicon-plus"></span> {% trans 'Add' %}
</a>
<a href="{% url 'abonapp:street_edit' abon_group.pk %}" class="btn btn-primary btn-modal" data-toggle="tooltip" title="{% trans 'Edit streets' %}">
<a href="{% url 'abonapp:street_edit' group.pk %}" class="btn btn-primary btn-modal" data-toggle="tooltip" title="{% trans 'Edit streets' %}">
<span class="glyphicon glyphicon-edit"></span> {% trans 'Edit' %}
</a>
</div>

14
abonapp/templates/abonapp/service.html

@ -49,13 +49,13 @@
{% else %}
{% trans 'Subscriber has no service' %}.
<a href="{% url 'abonapp:pick_tariff' abon_group.pk abon.pk %}">
<a href="{% url 'abonapp:pick_tariff' 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-sm btn-danger">
<a href="{% url 'abonapp:unsubscribe_service' group.pk abon.pk abon_tariff.pk %}" class="btn btn-sm btn-danger">
<span class="glyphicon glyphicon-remove-circle"></span> {% trans 'Finish service' %}
</a>
{% endif %}
@ -82,7 +82,7 @@
{% 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 }}"
<td><a href="{% url 'abonapp:pick_tariff' 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>
@ -100,7 +100,7 @@
{% 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' %}">
<a href="{% url 'abonapp:ch_group_tariff' 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>
@ -108,7 +108,7 @@
{% endwith %}
</tbody>
</table>
<a href="{% url 'abonapp:ch_group_tariff' abon_group.pk %}" class="btn btn-sm btn-primary" title="{% trans 'User groups' %}">
<a href="{% url 'abonapp:ch_group_tariff' group.pk %}" class="btn btn-sm btn-primary" title="{% trans 'User groups' %}">
<span class="glyphicon glyphicon-cog"></span> {% trans 'Attach services to group' %}
</a>
</div>
@ -131,11 +131,11 @@
<dt>{% trans 'Next time to pay' %}</dt>
<dd>{{ periodic_pay.next_pay|date:'d E Y' }}</dd>
</dl>
<a href="{% url 'abonapp:del_periodic_pay' abon_group.pk abon.pk periodic_pay.pk %}" class="btn btn-sm btn-danger">
<a href="{% url 'abonapp:del_periodic_pay' group.pk abon.pk periodic_pay.pk %}" class="btn btn-sm btn-danger">
<span class="glyphicon glyphicon-remove-circle"></span> {% trans 'Remove periodic pay' %}
</a>
{% else %}
<a href="{% url 'abonapp:add_periodic_pay' abon_group.pk abon.pk %}" class="btn btn-primary btn-sm btn-modal">
<a href="{% url 'abonapp:add_periodic_pay' group.pk abon.pk %}" class="btn btn-primary btn-sm btn-modal">
<span class="glyphicon glyphicon-plus"></span> {% trans 'Add periodic pay' %}
</a>
{% endif %}

2
abonapp/urls.py

@ -8,8 +8,6 @@ app_name = 'abonapp'
urlpatterns = [
url(r'^$', views.GroupListView.as_view(), name='group_list'),
url(r'^addgroup$', views.addgroup, name='add_group'),
url(r'^delgroup', views.delgroup, name='del_group'),
url(r'^fin_report$', views.fin_report, name='fin_report'),

245
abonapp/views.py

@ -3,7 +3,7 @@ from json import dumps
from django.contrib.gis.shortcuts import render_to_text
from django.core.exceptions import PermissionDenied
from django.db import IntegrityError, ProgrammingError, transaction
from django.db.models import Count, Q, signals
from django.db.models import Count, Q
from django.shortcuts import render, redirect, get_object_or_404, resolve_url
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse, Http404
@ -24,6 +24,7 @@ from datetime import datetime, date, timedelta
from taskapp.models import Task
from dialing_app.models import AsteriskCDR
from statistics.models import getModel
from group_app.models import Group
from guardian.shortcuts import get_objects_for_user, assign_perm
from guardian.decorators import permission_required_or_403 as permission_required
from djing.global_base_views import OrderingMixin, BaseListWithFiltering
@ -69,85 +70,39 @@ class PeoplesListView(BaseAbonListView):
def get_context_data(self, **kwargs):
gid = mydefs.safe_int(self.kwargs.get('gid'))
if gid == 0:
return Http404('group id is broken')
abon_group = get_object_or_404(models.AbonGroup, pk=gid)
if not self.request.user.has_perm('abonapp.can_view_abongroup', abon_group):
return HttpResponseBadRequest('group id is broken')
group = get_object_or_404(Group, pk=gid)
if not self.request.user.has_perm('group_app.can_view_group', group):
raise PermissionDenied
context = super(PeoplesListView, self).get_context_data(**kwargs)
context['streets'] = models.AbonStreet.objects.filter(group=gid)
context['street_id'] = mydefs.safe_int(self.request.GET.get('street'))
context['abon_group'] = abon_group
context['group'] = group
return context
@login_required
@permission_required('abonapp.add_abongroup')
def addgroup(request):
frm = forms.AbonGroupForm()
try:
if request.method == 'POST':
frm = forms.AbonGroupForm(request.POST)
if frm.is_valid():
grp = frm.save()
assign_perm('abonapp.can_view_abongroup', request.user, grp)
assign_perm('abonapp.delete_abongroup', request.user, grp)
assign_perm('abonapp.change_abongroup', request.user, grp)
messages.success(request, _('create group success msg'))
return redirect('abonapp:group_list')
else:
messages.error(request, _('fix form errors'))
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 render(request, 'abonapp/addGroup.html', {
'form': frm
})
class GroupListView(BaseAbonListView):
context_object_name = 'groups'
template_name = 'abonapp/group_list.html'
queryset = models.AbonGroup.objects.annotate(usercount=Count('abon')).order_by('title')
queryset = Group.objects.annotate(usercount=Count('abon'))
def get_queryset(self):
queryset = super(GroupListView, self).get_queryset()
queryset = get_objects_for_user(self.request.user, 'abonapp.can_view_abongroup', klass=queryset,
queryset = get_objects_for_user(self.request.user, 'group_app.can_view_group', klass=queryset,
accept_global_perms=False)
return queryset
@login_required
def delgroup(request):
try:
agd = mydefs.safe_int(request.GET.get('id'))
abon_group = models.AbonGroup.objects.get(pk=agd)
if not request.user.has_perm('abonapp.delete_abongroup', abon_group):
raise PermissionDenied
abon_group.delete()
messages.success(request, _('delete group success msg'))
return mydefs.res_success(request, 'abonapp:group_list')
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)
except models.AbonGroup.DoesNotExist:
return mydefs.res_error(request, 'Group with id=%d does not exist' % agd)
return mydefs.res_error(request, 'abonapp:group_list')
@login_required
@permission_required('abonapp.add_abon')
def addabon(request, gid):
frm = None
group = None
try:
group = get_object_or_404(models.AbonGroup, pk=gid)
if not request.user.has_perm('abonapp.can_view_abongroup', group):
group = get_object_or_404(Group, pk=gid)
if not request.user.has_perm('group_app.can_view_group', group):
raise PermissionDenied
if request.method == 'POST':
frm = forms.AbonForm(request.POST, initial={'group': group})
@ -158,6 +113,7 @@ def addabon(request, gid):
assign_perm("abonapp.can_buy_tariff", request.user, abon)
assign_perm("abonapp.can_view_passport", request.user, abon)
assign_perm('abonapp.can_add_ballance', request.user, abon)
abon.sync_with_nas(created=True)
messages.success(request, _('create abon success msg'))
return redirect('abonapp:abon_home', group.id, abon.pk)
else:
@ -167,7 +123,7 @@ def addabon(request, gid):
messages.error(request, e)
except mydefs.MultipleException as errs:
for err in errs.err_list:
messages.add_message(request, messages.constants.ERROR, err)
messages.error(request, err)
if not frm:
frm = forms.AbonForm(initial={
@ -178,7 +134,7 @@ def addabon(request, gid):
return render(request, 'abonapp/addAbon.html', {
'form': frm,
'abon_group': group
'group': group
})
@ -189,10 +145,11 @@ def del_abon(request):
try:
abon = get_object_or_404(models.Abon, pk=uid)
if not request.user.has_perm('abonapp.delete_abon') or not request.user.has_perm(
'abonapp.can_view_abongroup', abon.group):
'group_app.can_view_group', abon.group):
raise PermissionDenied
gid = abon.group.id
abon.delete()
abon.sync_with_nas(created=False)
messages.success(request, _('delete abon success msg'))
return mydefs.res_success(request, resolve_url('abonapp:people_list', gid=gid))
@ -202,7 +159,7 @@ def del_abon(request):
messages.error(request, _("NAS says: '%s'") % e)
except mydefs.MultipleException as errs:
for err in errs.err_list:
messages.add_message(request, messages.constants.ERROR, err)
messages.error(request, err)
return redirect('abonapp:group_list')
@ -226,14 +183,14 @@ def abonamount(request, gid, uid):
messages.error(request, e)
except mydefs.MultipleException as errs:
for err in errs.err_list:
messages.add_message(request, messages.constants.ERROR, err)
messages.error(request, err)
return render_to_text('abonapp/modal_abonamount.html', {
'abon': abon,
'abon_group': get_object_or_404(models.AbonGroup, pk=gid)
'group_id': gid
}, request=request)
@method_decorator(permission_required('abonapp.can_view_abongroup', (models.AbonGroup, 'pk', 'gid')), name='dispatch')
@method_decorator(permission_required('group_app.can_view_group', (Group, 'pk', 'gid')), name='dispatch')
class DebtsListView(BaseAbonListView):
context_object_name = 'invoices'
template_name = 'abonapp/invoiceForPayment.html'
@ -245,12 +202,12 @@ class DebtsListView(BaseAbonListView):
def get_context_data(self, **kwargs):
context = super(DebtsListView, self).get_context_data(**kwargs)
context['abon_group'] = self.abon.group
context['group'] = self.abon.group
context['abon'] = self.abon
return context
@method_decorator(permission_required('abonapp.can_view_abongroup', (models.AbonGroup, 'pk', 'gid')), name='dispatch')
@method_decorator(permission_required('group_app.can_view_group', (Group, 'pk', 'gid')), name='dispatch')
class PayHistoryListView(BaseAbonListView):
context_object_name = 'pay_history'
template_name = 'abonapp/payHistory.html'
@ -263,7 +220,7 @@ class PayHistoryListView(BaseAbonListView):
def get_context_data(self, **kwargs):
context = super(PayHistoryListView, self).get_context_data(**kwargs)
context['abon_group'] = self.abon.group
context['group'] = self.abon.group
context['abon'] = self.abon
return context
@ -271,8 +228,8 @@ class PayHistoryListView(BaseAbonListView):
@login_required
@mydefs.only_admins
def abon_services(request, gid, uid):
grp = get_object_or_404(models.AbonGroup, pk=gid)
if not request.user.has_perm('abonapp.can_view_abongroup', grp):
grp = get_object_or_404(Group, pk=gid)
if not request.user.has_perm('group_app.can_view_group', grp):
raise PermissionDenied
abon = get_object_or_404(models.Abon, pk=uid)
@ -288,8 +245,8 @@ def abon_services(request, gid, uid):
return render(request, 'abonapp/service.html', {
'abon': abon,
'abon_tariff': abon.current_tariff,
'abon_group': abon.group,
'services': grp.tariffs.all(),
'group': abon.group,
'services': Tariff.objects.get_tariffs_by_group(abon.group.pk),
'periodic_pay': periodic_pay
})
@ -298,8 +255,8 @@ def abon_services(request, gid, uid):
@mydefs.only_admins
def abonhome(request, gid, uid):
abon = get_object_or_404(models.Abon, pk=uid)
abon_group = get_object_or_404(models.AbonGroup, pk=gid)
if not request.user.has_perm('abonapp.can_view_abongroup', abon_group):
group = get_object_or_404(Group, pk=gid)
if not request.user.has_perm('group_app.can_view_group', group):
raise PermissionDenied
frm = None
passw = None
@ -312,7 +269,8 @@ def abonhome(request, gid, uid):
newip = request.POST.get('ip')
if newip:
abon.ip_address = newip
frm.save()
abon = frm.save()
abon.sync_with_nas(created=False)
messages.success(request, _('edit abon success msg'))
else:
messages.warning(request, _('fix form errors'))
@ -332,13 +290,13 @@ def abonhome(request, gid, uid):
messages.warning(request, _('User has not have password, and cannot login'))
except mydefs.MultipleException as errs:
for err in errs.err_list:
messages.add_message(request, messages.constants.ERROR, err)
messages.error(request, err)
if request.user.has_perm('abonapp.change_abon'):
return render(request, 'abonapp/editAbon.html', {
'form': frm or forms.AbonForm(instance=abon, initial={'password': passw}),
'abon': abon,
'abon_group': abon_group,
'group': group,
'ip': abon.ip_address,
'is_bad_ip': getattr(abon, 'is_bad_ip', False),
'device': abon.device,
@ -347,7 +305,7 @@ def abonhome(request, gid, uid):
else:
return render(request, 'abonapp/viewAbon.html', {
'abon': abon,
'abon_group': abon_group,
'group': group,
'ip': abon.ip_address,
'passw': passw
})
@ -365,7 +323,7 @@ def terminal_pay(request):
def add_invoice(request, gid, uid):
uid = mydefs.safe_int(uid)
abon = get_object_or_404(models.Abon, pk=uid)
grp = get_object_or_404(models.AbonGroup, pk=gid)
grp = get_object_or_404(Group, pk=gid)
try:
if request.method == 'POST':
@ -389,21 +347,22 @@ def add_invoice(request, gid, uid):
messages.error(request, e)
except mydefs.MultipleException as errs:
for err in errs.err_list:
messages.add_message(request, messages.constants.ERROR, err)
messages.error(request, err)
return render(request, 'abonapp/addInvoice.html', {
'abon': abon,
'invcount': models.InvoiceForPayment.objects.filter(abon=abon).count(),
'abon_group': grp
'group': grp
})
@login_required
@mydefs.only_admins
@permission_required('abonapp.can_buy_tariff')
@transaction.atomic
def pick_tariff(request, gid, uid):
grp = get_object_or_404(models.AbonGroup, pk=gid)
grp = get_object_or_404(Group, pk=gid)
abon = get_object_or_404(models.Abon, pk=uid)
tariffs = grp.tariffs.all()
tariffs = Tariff.objects.get_tariffs_by_group(grp.pk)
try:
if request.method == 'POST':
trf = Tariff.objects.get(pk=request.POST.get('tariff'))
@ -417,6 +376,7 @@ def pick_tariff(request, gid, uid):
deadline = datetime.strptime(deadline, '%Y-%m-%d')
deadline += timedelta(hours=23, minutes=59, seconds=59)
abon.pick_tariff(trf, request.user, deadline=deadline, comment=log_comment)
abon.sync_with_nas(created=False)
messages.success(request, _('Tariff has been picked'))
return redirect('abonapp:abon_services', gid=gid, uid=abon.id)
except (mydefs.LogicError, NasFailedResult) as e:
@ -428,14 +388,14 @@ def pick_tariff(request, gid, uid):
messages.error(request, _('Tariff your picked does not exist'))
except mydefs.MultipleException as errs:
for err in errs.err_list:
messages.add_message(request, messages.constants.ERROR, err)
messages.error(request, err)
except ValueError as e:
messages.error(request, "%s: %s" % (_('fix form errors'), e))
return render(request, 'abonapp/buy_tariff.html', {
'tariffs': tariffs,
'abon': abon,
'abon_group': grp,
'group': grp,
'selected_tariff': mydefs.safe_int(request.GET.get('selected_tariff'))
})
@ -444,7 +404,9 @@ def pick_tariff(request, gid, uid):
@permission_required('abonapp.delete_abontariff')
def unsubscribe_service(request, gid, uid, abon_tariff_id):
try:
abon = get_object_or_404(models.Abon, pk=uid)
abon_tariff = get_object_or_404(models.AbonTariff, pk=int(abon_tariff_id))
abon.sync_with_nas(created=False)
abon_tariff.delete()
messages.success(request, _('User has been detached from service'))
except NasFailedResult as e:
@ -453,7 +415,7 @@ def unsubscribe_service(request, gid, uid, abon_tariff_id):
messages.warning(request, e)
except mydefs.MultipleException as errs:
for err in errs.err_list:
messages.add_message(request, messages.constants.ERROR, err)
messages.error(request, err)
return redirect('abonapp:abon_services', gid=gid, uid=uid)
@ -478,7 +440,7 @@ class DebtorsListView(ListView):
@method_decorator(login_required, name='dispatch')
@method_decorator(permission_required('abonapp.can_view_abongroup', (models.AbonGroup, 'pk', 'gid')), name='dispatch')
@method_decorator(permission_required('group_app.can_view_group', (Group, 'pk', 'gid')), name='dispatch')
class TaskLogListView(ListView):
paginate_by = PAGINATION_ITEMS_PER_PAGE
http_method_names = ['get']
@ -492,7 +454,7 @@ class TaskLogListView(ListView):
def get_context_data(self, **kwargs):
context = super(TaskLogListView, self).get_context_data(**kwargs)
context['abon_group'] = self.abon.group
context['group'] = self.abon.group
context['abon'] = self.abon
return context
@ -500,8 +462,8 @@ class TaskLogListView(ListView):
@login_required
@permission_required('abonapp.can_view_passport')
def passport_view(request, gid, uid):
abon = get_object_or_404(models.Abon, pk=uid)
try:
abon = models.Abon.objects.get(pk=uid)
if request.method == 'POST':
try:
passport_instance = models.PassportInfo.objects.get(abon=abon)
@ -526,7 +488,7 @@ def passport_view(request, gid, uid):
messages.warning(request, _('Passport info for the user does not exist'))
frm = forms.PassportForm()
return render(request, 'abonapp/passport_view.html', {
'abon_group': get_object_or_404(models.AbonGroup, pk=gid),
'group': get_object_or_404(Group, pk=gid),
'abon': abon,
'frm': frm
})
@ -535,17 +497,21 @@ def passport_view(request, gid, uid):
@login_required
@mydefs.only_admins
def chgroup_tariff(request, gid):
grp = get_object_or_404(models.AbonGroup, pk=gid)
if not request.user.has_perm('abonapp.change_abongroup', grp):
grp = get_object_or_404(Group, pk=gid)
if not request.user.has_perm('group_app.change_group', grp):
raise PermissionDenied
if request.method == 'POST':
tr = request.POST.getlist('tr')
grp.tariffs.clear()
grp.tariffs.add(*[int(d) for d in tr])
grp.tariff_set.clear()
grp.tariff_set.add(*[int(d) for d in tr])
grp.save()
messages.success(request, _('Successfully saved'))
return redirect('abonapp:ch_group_tariff', gid)
tariffs = Tariff.objects.all()
seleted_tariffs_id = [pk[0] for pk in grp.tariff_set.only('pk').values_list('pk')]
return render(request, 'abonapp/group_tariffs.html', {
'abon_group': grp,
'group': grp,
'seleted_tariffs': seleted_tariffs_id,
'tariffs': tariffs
})
@ -570,7 +536,7 @@ def dev(request, gid, uid):
messages.error(request, _('Abon does not exist'))
return redirect('abonapp:people_list', gid=gid)
return render(request, 'abonapp/modal_dev.html', {
'devices': Device.objects.filter(user_group=gid),
'devices': Device.objects.filter(group=gid),
'dev': abon_dev,
'gid': gid, 'uid': uid
})
@ -578,7 +544,7 @@ def dev(request, gid, uid):
@login_required
@permission_required('abonapp.change_abon')
@permission_required('abonapp.can_view_abongroup', (models.AbonGroup, 'pk', 'gid'))
@permission_required('group_app.can_view_group', (Group, 'pk', 'gid'))
def clear_dev(request, gid, uid):
try:
abon = models.Abon.objects.get(pk=uid)
@ -593,7 +559,7 @@ def clear_dev(request, gid, uid):
@login_required
@permission_required('abonapp.can_view_abongroup', (models.AbonGroup, 'pk', 'gid'))
@permission_required('group_app.can_view_group', (Group, 'pk', 'gid'))
def charts(request, gid, uid):
high = 100
@ -607,9 +573,8 @@ def charts(request, gid, uid):
StatElem = getModel(wandate)
abon = models.Abon.objects.get(pk=uid)
if abon.group is None:
abon.group = models.AbonGroup.objects.get(pk=gid)
abon.group = Group.objects.get(pk=gid)
abon.save(update_fields=['group'])
abongroup = abon.group
if abon.ip_address is None:
charts_data = None
@ -630,7 +595,7 @@ def charts(request, gid, uid):
except models.Abon.DoesNotExist:
messages.error(request, _('Abon does not exist'))
return redirect('abonapp:people_list', gid)
except models.AbonGroup.DoesNotExist:
except Group.DoesNotExist:
messages.error(request, _("Group what you want doesn't exist"))
return redirect('abonapp:group_list')
except ProgrammingError as e:
@ -638,7 +603,7 @@ def charts(request, gid, uid):
return redirect('abonapp:abon_home', gid=gid, uid=uid)
return render(request, 'abonapp/charts.html', {
'abon_group': abongroup,
'group': abon.group,
'abon': abon,
'charts_data': ',\n'.join(charts_data) if charts_data is not None else None,
'high': high,
@ -668,7 +633,7 @@ def make_extra_field(request, gid, uid):
frm = forms.ExtraFieldForm()
except mydefs.MultipleException as errs:
for err in errs.err_list:
messages.add_message(request, messages.constants.ERROR, err)
messages.error(request, err)
frm = forms.ExtraFieldForm()
return render_to_text('abonapp/modal_extra_field.html', {
'abon': abon,
@ -724,14 +689,17 @@ def abon_ping(request):
else:
if type(ping_result) is tuple:
loses_percent = (ping_result[0] / ping_result[1] if ping_result[1] != 0 else 1)
ping_result = {'all':ping_result[0], 'return': ping_result[1]}
ping_result = {'all': ping_result[0], 'return': ping_result[1]}
if loses_percent > 1.0:
text = '<span class="glyphicon glyphicon-exclamation-sign"></span> %s' % _('IP Conflict! %(all)d/%(return)d results') % ping_result
text = '<span class="glyphicon glyphicon-exclamation-sign"></span> %s' % _(
'IP Conflict! %(all)d/%(return)d results') % ping_result
elif loses_percent > 0.5:
text = '<span class="glyphicon glyphicon-ok"></span> %s' % _('ok ping, %(all)d/%(return)d loses') % ping_result
text = '<span class="glyphicon glyphicon-ok"></span> %s' % _(
'ok ping, %(all)d/%(return)d loses') % ping_result
status = True
else:
text = '<span class="glyphicon glyphicon-exclamation-sign"></span> %s' % _('no ping, %(all)d/%(return)d loses') % ping_result
text = '<span class="glyphicon glyphicon-exclamation-sign"></span> %s' % _(
'no ping, %(all)d/%(return)d loses') % ping_result
else:
text = '<span class="glyphicon glyphicon-ok"></span> %s' % _('ping ok') + ' ' + str(ping_result)
status = True
@ -753,7 +721,7 @@ class DialsListView(BaseAbonListView):
def get_queryset(self):
abon = get_object_or_404(models.Abon, pk=self.kwargs.get('uid'))
if not self.request.user.has_perm('abonapp.can_view_abongroup', abon.group):
if not self.request.user.has_perm('group_app.can_view_group', abon.group):
raise PermissionDenied
self.abon = abon
if abon.telephone is not None and abon.telephone != '':
@ -767,7 +735,7 @@ class DialsListView(BaseAbonListView):
def get_context_data(self, **kwargs):
context = super(DialsListView, self).get_context_data(**kwargs)
context['abon_group'] = get_object_or_404(models.AbonGroup, pk=self.kwargs.get('gid'))
context['group'] = get_object_or_404(Group, pk=self.kwargs.get('gid'))
context['abon'] = self.abon
return context
@ -776,6 +744,15 @@ class DialsListView(BaseAbonListView):
return redirect('abonapp:dials', self.abon.group.pk, self.abon.pk)
return super(DialsListView, self).render_to_response(context, **response_kwargs)
def get(self, request, *args, **kwargs):
try:
return super(DialsListView, self).get(request, *args, **kwargs)
except ProgrammingError as e:
messages.error(request, e)
return redirect('abonapp:abon_home',
self.kwargs.get('gid'),
self.kwargs.get('uid'))
@login_required
@permission_required('abonapp.change_abon')
@ -796,16 +773,17 @@ def save_user_dev_port(request, gid, uid):
other_abon = models.Abon.objects.get(device=abon.device, dev_port=port)
if other_abon != abon:
user_url = resolve_url('abonapp:abon_home', other_abon.group.id, other_abon.id)
messages.error(request, _("<a href='%(user_url)s'>%(user_name)s</a> already pinned to this port on this device") % {
'user_url': user_url,
'user_name': other_abon.get_full_name()
})
messages.error(request, _(
"<a href='%(user_url)s'>%(user_name)s</a> already pinned to this port on this device") % {
'user_url': user_url,
'user_name': other_abon.get_full_name()
})
return redirect('abonapp:abon_home', gid, uid)
except models.Abon.DoesNotExist:
pass
except models.Abon.MultipleObjectsReturned:
messages.error(request, _('Multiple users on the same device port'))
return redirect('devapp:manage_ports', abon.device.user_group.pk, abon.device.pk)
return redirect('devapp:manage_ports', abon.device.group.pk, abon.device.pk)
abon.dev_port = port
if abon.is_dynamic_ip != is_dynamic_ip:
@ -826,7 +804,7 @@ def save_user_dev_port(request, gid, uid):
@login_required
@permission_required('abonapp.add_abonstreet')
@permission_required('abonapp.can_view_abongroup', (models.AbonGroup, 'pk', 'gid'))
@permission_required('group_app.can_view_group', (Group, 'pk', 'gid'))
def street_add(request, gid):
if request.method == 'POST':
frm = forms.AbonStreetForm(request.POST)
@ -846,7 +824,7 @@ def street_add(request, gid):
@login_required
@permission_required('abonapp.change_abonstreet')
@permission_required('abonapp.can_view_abongroup', (models.AbonGroup, 'pk', 'gid'))
@permission_required('group_app.can_view_group', (Group, 'pk', 'gid'))
def street_edit(request, gid):
try:
if request.method == 'POST':
@ -871,7 +849,7 @@ def street_edit(request, gid):
@login_required
@permission_required('abonapp.delete_abonstreet')
@permission_required('abonapp.can_view_abongroup', (models.AbonGroup, 'pk', 'gid'))
@permission_required('group_app.can_view_group', (Group, 'pk', 'gid'))
def street_del(request, gid, sid):
try:
models.AbonStreet.objects.get(pk=sid, group=gid).delete()
@ -883,7 +861,7 @@ def street_del(request, gid, sid):
@login_required
@permission_required('abonapp.can_view_additionaltelephones')
@permission_required('abonapp.can_view_abongroup', (models.AbonGroup, 'pk', 'gid'))
@permission_required('group_app.can_view_group', (Group, 'pk', 'gid'))
def tels(request, gid, uid):
abon = get_object_or_404(models.Abon, pk=uid)
telephones = abon.additional_telephones.all()
@ -931,11 +909,12 @@ def tel_del(request, gid, uid):
@login_required
@permission_required('abonapp.can_view_abongroup', (models.AbonGroup, 'pk', 'gid'))
@permission_required('group_app.can_view_group', (Group, 'pk', 'gid'))
def phonebook(request, gid):
res_format = request.GET.get('f')
t1 = models.Abon.objects.filter(group__id=int(gid)).only('telephone', 'fio').values_list('telephone', 'fio')
t2 = models.AdditionalTelephone.objects.filter(abon__group__id=gid).only('telephone', 'owner_name').values_list('telephone', 'owner_name')
t2 = models.AdditionalTelephone.objects.filter(abon__group__id=gid).only('telephone', 'owner_name').values_list(
'telephone', 'owner_name')
tels = list(t1) + list(t2)
if res_format == 'csv':
import csv
@ -952,7 +931,7 @@ def phonebook(request, gid):
@login_required
@permission_required('abonapp.can_view_abongroup', (models.AbonGroup, 'pk', 'gid'))
@permission_required('group_app.can_view_group', (Group, 'pk', 'gid'))
def abon_export(request, gid):
res_format = request.GET.get('f')
@ -988,13 +967,11 @@ def abon_export(request, gid):
@login_required
@permission_required('abonapp.change_abon')
@permission_required('abonapp.can_view_abongroup', (models.AbonGroup, 'pk', 'gid'))
@permission_required('group_app.can_view_group', (Group, 'pk', 'gid'))
def reset_ip(request, gid, uid):
abon = get_object_or_404(models.Abon, pk=uid)
signals.post_save.disconnect(models.abon_post_save, sender=models.Abon)
abon.ip_address = None
abon.save(update_fields=['ip_address'])
signals.post_save.connect(models.abon_post_save, sender=models.Abon)
return HttpResponse(dumps({
'status': 0,
'dat': "<span class='glyphicon glyphicon-refresh'></span>"
@ -1020,7 +997,7 @@ def fin_report(request):
@login_required
@permission_required('abonapp.can_view_abongroup', (models.AbonGroup, 'pk', 'gid'))
@permission_required('group_app.can_view_group', (Group, 'pk', 'gid'))
def add_edit_periodic_pay(request, gid, uid, periodic_pay_id=0):
if periodic_pay_id == 0:
if not request.user.has_perm('abonapp.add_periodicpayforid'):
@ -1051,7 +1028,7 @@ def add_edit_periodic_pay(request, gid, uid, periodic_pay_id=0):
@login_required
@permission_required('abonapp.can_view_abongroup', (models.AbonGroup, 'pk', 'gid'))
@permission_required('group_app.can_view_group', (Group, 'pk', 'gid'))
@permission_required('abonapp.delete_periodicpayforid')
def del_periodic_pay(request, gid, uid, periodic_pay_id):
periodic_pay_instance = get_object_or_404(models.PeriodicPayForId, pk=periodic_pay_id)
@ -1082,12 +1059,12 @@ class EditSibscriberMarkers(UpdateView):
return context
def form_invalid(self, form):
messages.add_message(self.request, messages.ERROR, _('fix form errors'))
messages.error(self.request, _('fix form errors'))
return super(EditSibscriberMarkers, self).form_invalid(form)
def form_valid(self, form):
v = super(EditSibscriberMarkers, self).form_valid(form)
messages.add_message(self.request, messages.SUCCESS, _('User flags has changed successfully'))
messages.success(self.request, _('User flags has changed successfully'))
return v
@ -1095,17 +1072,17 @@ class EditSibscriberMarkers(UpdateView):
def abons(request):
ablist = [{
'id': abn.pk,
'tarif_id': abn.active_tariff().tariff.pk if abn.active_tariff() is not None else 0,
'ip': abn.ip_address.int_ip(),
'is_active': abn.is_active
} for abn in models.Abon.objects.all()]
'id': abn.pk,
'tarif_id': abn.active_tariff().tariff.pk if abn.active_tariff() is not None else 0,
'ip': abn.ip_address.int_ip(),
'is_active': abn.is_active
} for abn in models.Abon.objects.all()]
tarlist = [{
'id': trf.pk,
'speedIn': trf.speedIn,
'speedOut': trf.speedOut
} for trf in Tariff.objects.all()]
'id': trf.pk,
'speedIn': trf.speedIn,
'speedOut': trf.speedOut
} for trf in Tariff.objects.all()]
data = {
'subscribers': ablist,

63
accounts_app/migrations/0001_initial.py

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2016-06-28 23:51
# Generated by Django 1.11 on 2018-02-26 00:20
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
@ -7,44 +8,58 @@ import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('auth', '0007_alter_validators_add_error_messages'),
('auth', '0008_alter_user_username_max_length'),
('group_app', '0001_initial'),
('photo_app', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='UserProfile',
name='BaseAccount',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('is_superuser', models.BooleanField(default=False,
help_text='Designates that this user has all permissions without explicitly assigning them.',
verbose_name='superuser status')),
('username', models.CharField(max_length=127, unique=True)),
('fio', models.CharField(max_length=256)),
('birth_day', models.DateField(auto_now_add=True)),
('is_active', models.BooleanField(default=True)),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('username', models.CharField(max_length=127, unique=True, verbose_name='profile username')),
('fio', models.CharField(max_length=256, verbose_name='fio')),
('birth_day', models.DateField(auto_now_add=True, verbose_name='birth day')),
('is_active', models.BooleanField(default=True, verbose_name='Is active')),
('is_admin', models.BooleanField(default=False)),
('telephone', models.CharField(max_length=16, unique=True, validators=[
django.core.validators.RegexValidator(r'^\\+[7,8,9,3]\\d{10,11}$')],
verbose_name='Telephone number')),
('skype', models.CharField(blank=True, max_length=20)),
('avatar', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE,
to='photo_app.Photo')),
('groups', models.ManyToManyField(blank=True,
help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.',
related_name='user_set', related_query_name='user', to='auth.Group',
verbose_name='groups')),
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.',
related_name='user_set', related_query_name='user',
to='auth.Permission', verbose_name='user permissions')),
('telephone', models.CharField(max_length=16, validators=[django.core.validators.RegexValidator('^\\+[7,8,9,3]\\d{10,11}$')], verbose_name='Telephone')),
],
options={
'abstract': False,
'db_table': 'base_accounts',
},
),
migrations.CreateModel(
name='UserProfile',
fields=[
('baseaccount_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='accounts_app.BaseAccount')),
('email', models.EmailField(default='admin@example.ru', max_length=254)),
('avatar', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='photo_app.Photo')),
('responsibility_groups', models.ManyToManyField(blank=True, to='group_app.Group', verbose_name='Responsibility groups')),
],
options={
'verbose_name': 'Staff account profile',
'verbose_name_plural': 'Staff account profiles',
'ordering': ['fio'],
'permissions': (('can_view_userprofile', 'Can view staff profile'),),
},
bases=('accounts_app.baseaccount',),
),
migrations.AddField(
model_name='baseaccount',
name='groups',
field=models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups'),
),
migrations.AddField(
model_name='baseaccount',
name='user_permissions',
field=models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions'),
),
]

20
accounts_app/migrations/0002_userprofile_email.py

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2016-11-30 15:15
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts_app', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='userprofile',
name='email',
field=models.EmailField(default='ex@host.loc', max_length=254),
preserve_default=False,
),
]

25
accounts_app/migrations/0003_auto_20161206_2135.py

@ -1,25 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2016-12-06 18:35
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts_app', '0002_userprofile_email'),
]
operations = [
migrations.RemoveField(
model_name='userprofile',
name='skype',
),
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='Telephone number'),
),
]

18
accounts_app/migrations/0004_auto_20170128_1316.py

@ -1,18 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2017-01-28 10:16
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts_app', '0003_auto_20161206_2135'),
]
operations = [
migrations.AlterField(
model_name='userprofile',
name='email',
field=models.EmailField(default='admin@example.ru', max_length=254),
),
]

21
accounts_app/migrations/0005_auto_20170222_2224.py

@ -1,21 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2017-02-22 19:24
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts_app', '0004_auto_20170128_1316'),
]
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='Номер телефона'),
),
]

21
accounts_app/migrations/0006_auto_20170416_1029.py

@ -1,21 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2017-04-16 07:29
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('accounts_app', '0005_auto_20170222_2224'),
]
operations = [
migrations.AlterField(
model_name='userprofile',
name='avatar',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='photo_app.Photo'),
),
]

21
accounts_app/migrations/0007_auto_20170816_1109.py

@ -1,21 +0,0 @@
# -*- 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='Телефон'),
),
]

19
accounts_app/migrations/0008_auto_20170927_1838.py

@ -1,19 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2017-09-27 18:38
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('accounts_app', '0007_auto_20170816_1109'),
]
operations = [
migrations.AlterModelOptions(
name='userprofile',
options={'permissions': (('can_view_userprofile', 'Может просматривать учётку сотрудника'),), 'verbose_name': 'Учётная запись работника', 'verbose_name_plural': 'Учётные записи работников'},
),
]

28
accounts_app/models.py

@ -6,6 +6,7 @@ from django.core.validators import RegexValidator
from django.utils.translation import gettext_lazy as _
from django.conf import settings
from photo_app.models import Photo
from group_app.models import Group
DEFAULT_PICTURE = getattr(settings, 'DEFAULT_PICTURE', '/static/img/user_ava.gif')
@ -45,7 +46,7 @@ class MyUserManager(BaseUserManager):
return user
class UserProfile(AbstractBaseUser, PermissionsMixin):
class BaseAccount(AbstractBaseUser, PermissionsMixin):
username = models.CharField(_('profile username'), max_length=127, unique=True)
fio = models.CharField(_('fio'), max_length=256)
birth_day = models.DateField(_('birth day'), auto_now_add=True)
@ -57,8 +58,6 @@ class UserProfile(AbstractBaseUser, PermissionsMixin):
#unique=True,
validators=[RegexValidator(TELEPHONE_REGEXP)]
)
avatar = models.ForeignKey(Photo, null=True, blank=True, on_delete=models.SET_NULL)
email = models.EmailField(default='admin@example.ru')
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['telephone']
@ -78,6 +77,26 @@ class UserProfile(AbstractBaseUser, PermissionsMixin):
# Simplest possible answer: All admins are staff
return self.is_admin
def __str__(self):
return self.get_full_name()
class Meta:
db_table = 'base_accounts'
class UserProfileManager(MyUserManager):
def get_profiles_by_group(self, group_id):
return self.filter(responsibility_groups__id__in=[group_id], is_admin=True, is_active=True)
class UserProfile(BaseAccount):
avatar = models.ForeignKey(Photo, null=True, blank=True, on_delete=models.SET_NULL)
email = models.EmailField(default='admin@example.ru')
responsibility_groups = models.ManyToManyField(Group, blank=True, verbose_name=_('Responsibility groups'))
objects = UserProfileManager()
def get_big_ava(self):
if self.avatar:
path = self.avatar.big()
@ -99,9 +118,6 @@ class UserProfile(AbstractBaseUser, PermissionsMixin):
else:
return DEFAULT_PICTURE
def __str__(self):
return self.get_full_name()
class Meta:
permissions = (
('can_view_userprofile', _('Can view staff profile')),

6
accounts_app/templates/accounts/ext.htm

@ -53,12 +53,6 @@
{% trans 'Administrator' %}
</a>
</li>
{% url 'acc_app:profile_setup_group' uid as profilegroups_url %}
<li{% if profilegroups_url == request.path %} class="active"{% endif %}>
<a href="{{ profilegroups_url }}">
{% trans 'Groups' %}
</a>
</li>
{% if request.user.is_superuser %}
{% url 'acc_app:set_abon_groups_permission' uid as set_ag_perm %}
<li{% if set_ag_perm == request.path %} class="active"{% endif %}>

24
accounts_app/templates/accounts/profile_chgroup.html

@ -1,24 +0,0 @@
{% extends request.is_ajax|yesno:'nullcont.htm,accounts/ext.htm' %}
{% load i18n %}
{% block content %}
<legend>{% trans 'The responsibility of the staff of the group of subscribers' %}</legend>
<form action="{% url 'acc_app:profile_setup_group' userprofile.id %}" method="post" role="form">{% csrf_token %}
{% for ag in abongroups %}
<div class="checkbox">
<label>
{% if userprofile in ag.profiles.all %}
<input name="ag" type="checkbox" value="{{ ag.pk }}" checked/>
{% else %}
<input name="ag" type="checkbox" value="{{ ag.pk }}"/>
{% endif %}
{{ ag.title }}
</label>
</div>
{% endfor %}
<div class="btn-group">
<input type="submit" class="btn btn-primary" value="{% trans 'Save' %}"> <input type="reset" class="btn btn-default" value="{% trans 'Reset' %}">
</div>
</form>
{% endblock %}

10
accounts_app/templates/accounts/set_abon_groups_permission.html

@ -4,15 +4,15 @@
<legend>{% trans 'The list of user groups to which the account has access' %}</legend>
<form action="{% url 'acc_app:set_abon_groups_permission' userprofile.pk %}" method="post" role="form">{% csrf_token %}
{% for ag in abongroups %}
{% for grp in groups %}
<div class="checkbox">
<label>
{% if ag.pk in picked_groups_ids %}
<input name="ag" type="checkbox" value="{{ ag.pk }}" checked/>
{% if grp.pk in picked_groups_ids %}
<input name="grp" type="checkbox" value="{{ grp.pk }}" checked/>
{% else %}
<input name="ag" type="checkbox" value="{{ ag.pk }}"/>
<input name="grp" type="checkbox" value="{{ grp.pk }}"/>
{% endif %}
{{ ag.title }}
{{ grp.title }}
</label>
</div>
{% endfor %}

1
accounts_app/urls.py

@ -24,7 +24,6 @@ urlpatterns = [
url(r'^(?P<uid>\d+)/perms$', views.perms, name='setup_perms'),
url(r'^(?P<uid>\d+)/perms/(?P<klass_name>[a-z_]+\.[a-zA-Z_]+)$', views.PermissionClassListView.as_view(), name='perms_klasses'),
url(r'^(?P<uid>\d+)/perms/(?P<klass_name>[a-z_]+\.[a-zA-Z_]+)/(?P<obj_id>\d+)$', views.perms_edit, name='perms_edit'),
url(r'^(?P<uid>\d+)/chgroup$', views.chgroup, name='profile_setup_group'),
url(r'^(?P<uid>\d+)/del$', views.delete_profile, name='delete_profile'),
url(r'^(?P<uid>\d+)/user_group_access$', views.set_abon_groups_permission, name='set_abon_groups_permission')

45
accounts_app/views.py

@ -10,7 +10,7 @@ from django.utils.translation import ugettext as _
from django.views.generic import ListView
from django.conf import settings
from abonapp.models import AbonGroup
from group_app.models import Group
from photo_app.models import Photo
from .models import UserProfile
@ -89,29 +89,6 @@ def profile_show(request, uid=0):
})
@login_required
@mydefs.only_admins
def chgroup(request, uid):
uid = mydefs.safe_int(uid)
if uid == 0:
usr = request.user
else:
usr = get_object_or_404(UserProfile, id=uid)
if usr != request.user and not request.user.has_perm('accounts_app.change_userprofile', usr):
raise PermissionDenied
if request.method == 'POST':
ag = request.POST.getlist('ag')
usr.abon_groups.clear()
usr.abon_groups.add(*[int(d) for d in ag])
usr.save()
abongroups = AbonGroup.objects.only('pk', 'title')
return render(request, 'accounts/profile_chgroup.html', {
'uid': uid,
'userprofile': usr,
'abongroups': abongroups
})
@login_required
@mydefs.only_admins
def ch_ava(request):
@ -234,7 +211,7 @@ def perms(request, uid):
raise PermissionDenied
userprofile = get_object_or_404(UserProfile, id=uid)
klasses = (
'abonapp.AbonGroup', 'abonapp.Abon', 'accounts_app.UserProfile',
'abonapp.Abon', 'accounts_app.UserProfile',
'abonapp.AbonTariff', 'abonapp.AbonStreet', 'devapp.Device',
'abonapp.PassportInfo', 'abonapp.AdditionalTelephone', 'tariff_app.PeriodicPay'
)
@ -303,22 +280,22 @@ def set_abon_groups_permission(request, uid):
raise PermissionDenied
userprofile = get_object_or_404(UserProfile, pk=uid)
picked_groups = get_objects_for_user(userprofile, 'abonapp.can_view_abongroup', accept_global_perms=False)
picked_groups = get_objects_for_user(userprofile, 'group_app.can_view_group', accept_global_perms=False)
picked_groups = picked_groups.values_list('pk', flat=True)
if request.method == 'POST':
checked_groups = [int(ag) for ag in request.POST.getlist('ag', default=0)]
for abon_group in AbonGroup.objects.all():
if abon_group.pk in checked_groups and abon_group.pk not in picked_groups:
assign_perm('abonapp.can_view_abongroup', userprofile, obj=abon_group)
elif abon_group.pk not in checked_groups and abon_group.pk in picked_groups:
remove_perm('abonapp.can_view_abongroup', userprofile, obj=abon_group)
checked_groups = [int(ag) for ag in request.POST.getlist('grp', default=0)]
for grp in Group.objects.all():
if grp.pk in checked_groups and grp.pk not in picked_groups:
assign_perm('groupapp.can_view_group', userprofile, obj=grp)
elif grp.pk not in checked_groups and grp.pk in picked_groups:
remove_perm('groupapp.can_view_group', userprofile, obj=grp)
return redirect('acc_app:set_abon_groups_permission', uid)
abongroups = AbonGroup.objects.only('pk', 'title')
groups = Group.objects.only('pk', 'title')
return render(request, 'accounts/set_abon_groups_permission.html', {
'uid': uid,
'userprofile': userprofile,
'abongroups': abongroups,
'groups': groups,
'picked_groups_ids': picked_groups
})

17
agent/commands/dhcp.py

@ -1,6 +1,5 @@
# -*- coding: utf-8 -*-
from django.core.exceptions import MultipleObjectsReturned
from django.utils.translation import ugettext as _
from abonapp.models import Abon
from devapp.models import Device, Port
@ -16,21 +15,23 @@ def dhcp_commit(client_ip, client_mac, switch_mac, switch_port):
else:
abon = Abon.objects.get(device=dev)
if not abon.is_dynamic_ip:
print('D:', _('User settings is not dynamic'))
print('D:', 'User settings is not dynamic')
return
if not abon.is_access():
print('D:', 'User %s is not access to service' % abon.username)
return
abon.ip_address = client_ip
abon.is_dhcp = True
abon.save(update_fields=['ip_address'])
#print('S:', _("Ip address:'%s' update for '%s' successfull, on port: %s") % (client_ip, abon.get_short_name(), port))
abon.sync_with_nas(created=False)
except Abon.DoesNotExist:
print('N:', _("User with device '%s' does not exist") % dev)
print('N:', "User with device '%s' does not exist" % dev)
except Device.DoesNotExist:
print('N:', _('Device with mac %s not found') % switch_mac)
print('N:', 'Device with mac %s not found' % switch_mac)
except Port.DoesNotExist:
print('N:', _('Port %d on device with mac %s does not exist') % (int(switch_port), switch_mac))
print('N:', 'Port %(switch_port)d on device with mac %(switch_mac)s does not exist' % {
'switch_port': int(switch_port),
'switch_mac': switch_mac
})
except MultipleObjectsReturned as e:
print('E:', 'MultipleObjectsReturned:', type(e), e, port, dev)
@ -39,8 +40,8 @@ def dhcp_expiry(client_ip):
try:
abon = Abon.objects.get(ip_address=client_ip)
abon.ip_address = None
abon.is_dhcp = True
abon.save(update_fields=['ip_address'])
abon.sync_with_nas(created=False)
except Abon.DoesNotExist:
pass

32
chatbot/migrations/0001_initial.py

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2017-01-15 20:55
# Generated by Django 1.11 on 2018-02-26 00:20
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
@ -23,13 +24,38 @@ class Migration(migrations.Migration):
('date_sent', models.DateTimeField(auto_now_add=True)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'Message history',
'verbose_name_plural': 'Message histories',
'db_table': 'chat_message_history',
},
),
migrations.CreateModel(
name='MessageQueue',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('message', models.CharField(max_length=255, verbose_name='Message')),
('status', models.CharField(choices=[('n', 'New'), ('r', 'Read')], default='n', max_length=1, verbose_name='Status of message')),
('tag', models.CharField(default='none', max_length=6, verbose_name='App tag')),
('target_employee', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Target employee')),
],
options={
'verbose_name': 'Message queue',
'verbose_name_plural': 'Message queue',
'db_table': 'chat_message_queue',
},
),
migrations.CreateModel(
name='TelegramBot',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('chat_id', models.PositiveIntegerField(default=0)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
('chat_id', models.PositiveIntegerField(default=0, verbose_name='Telegram chat id')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Employee')),
],
options={
'verbose_name': 'Telegram bot',
'verbose_name_plural': 'Telegram bots',
'db_table': 'chat_telegram_bot',
},
),
]

59
chatbot/migrations/0002_auto_20171214_1517.py

@ -1,59 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2017-12-14 15:17
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('chatbot', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='MessageQueue',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('message', models.CharField(max_length=255, verbose_name='Message')),
('status', models.CharField(choices=[('n', 'New'), ('r', 'Read')], default='n', max_length=1, verbose_name='Status of message')),
('tag', models.CharField(default='none', max_length=6, verbose_name='App tag')),
('target_employee', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Target employee')),
],
options={
'verbose_name': 'Message queue',
'verbose_name_plural': 'Message queue',
'db_table': 'chat_message_queue',
},
),
migrations.AlterModelOptions(
name='messagehistory',
options={'verbose_name': 'Message history', 'verbose_name_plural': 'Message histories'},
),
migrations.AlterModelOptions(
name='telegrambot',
options={'verbose_name': 'Telegram bot', 'verbose_name_plural': 'Telegram bots'},
),
migrations.AlterField(
model_name='telegrambot',
name='chat_id',
field=models.PositiveIntegerField(default=0, verbose_name='Telegram chat id'),
),
migrations.AlterField(
model_name='telegrambot',
name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Employee'),
),
migrations.AlterModelTable(
name='messagehistory',
table='chat_message_history',
),
migrations.AlterModelTable(
name='telegrambot',
table='chat_telegram_bot',
),
]

49
clientsideapp/locale/ru/LC_MESSAGES/django.po

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-03-13 11:49+0300\n"
"POT-Creation-Date: 2018-03-20 01:51+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Dmitry Novikov nerosketch@gmail.com\n"
"Language: ru\n"
@ -31,25 +31,26 @@ msgstr "Оплатить задолженность"
msgid "Are you sure you want to spend a payment?"
msgstr "Вы уверены что хотите провести платёж?"
#: templates/clientsideapp/debt_buy.html:22
#: templates/clientsideapp/debt_buy.html:21
#, python-format
msgid ""
"From your account, they withdraw funds in <b>%(amount)s</b> rub.<br/> As a "
"result, you will remain on your account %(ballance_after)s rubles.<br/> The "
"administrator can immediately see that you shut down the debt."
msgstr ""
"С вашего счёта снимутся средства в размере <b>%(amount)s</b> руб.<br/>\n"
"В результате у вас на счету останется %(ballance_after)s руб.<br/>\n"
"Администратор сразу сможет видеть что у вас закрыта задолженность."
"С вашего счёта снимутся средства в размере <b>%(amount)s</b> руб.<br/>В "
"результате у вас на счету останется %(ballance_after)s руб.<br/"
">Администратор сразу сможет видеть что у вас закрыта задолженность."
#: templates/clientsideapp/debt_buy.html:28
#: templates/clientsideapp/debt_buy.html:26
msgid "Description of payment"
msgstr "Описание платежа"
#: templates/clientsideapp/debt_buy.html:36
#: templates/clientsideapp/debt_buy.html:34
msgid "Confirm"
msgstr "Подтвердить"
#: templates/clientsideapp/debt_buy.html:39
#: templates/clientsideapp/debt_buy.html:37
msgid "Cancel"
msgstr "Отменить"
@ -82,7 +83,7 @@ msgid "Pay"
msgstr "Оплатить"
#: templates/clientsideapp/debts.html:23 templates/clientsideapp/ext.html:63
#: templates/clientsideapp/modal_service_buy.html:18
#: templates/clientsideapp/modal_service_buy.html:19
#: templates/clientsideapp/services.html:26
#: templates/clientsideapp/services.html:55
msgid "currency"
@ -151,28 +152,27 @@ msgid ""
"Be careful, after purchasing the service you will <b>withdraw money</b>, and "
"you will be able to use the purchased service."
msgstr ""
"Будте внимательны, после заказа услуги с вашего счёта <b>снимутся средства</b>, "
"и вы сможете пользоваться купленной услугой."
"Будте внимательны, после заказа услуги с вашего счёта <b>снимутся средства</"
"b>, и вы сможете пользоваться купленной услугой."
#: templates/clientsideapp/modal_service_buy.html:15
#, python-format
msgid ""
"Inbound speed: %(speedIn)s MBit/s<br>\n"
"Outgoing speed: %(speedOut)s MBit/s<br>\n"
"Cost: %(amount)s rubles."
"Inbound speed: %(speedIn)s MBit/s<br> Outgoing speed: %(speedOut)s MBit/"
"s<br> Cost: %(amount)s rubles."
msgstr ""
#: templates/clientsideapp/modal_service_buy.html:18
#: templates/clientsideapp/modal_service_buy.html:19
#, python-format
msgid "The cost is %(amount)s"
msgstr "Стоимость %(amount)s"
#: templates/clientsideapp/modal_service_buy.html:22
#: templates/clientsideapp/modal_service_buy.html:23
#: templates/clientsideapp/services.html:63
msgid "Pick"
msgstr "Заказать"
#: templates/clientsideapp/modal_service_buy.html:24
#: templates/clientsideapp/modal_service_buy.html:25
msgid "Close"
msgstr "Закрыть"
@ -217,8 +217,8 @@ msgid ""
"<strong>Attantion!</strong> You have not yet a service, for use the services "
"please purchase service you want."
msgstr ""
"<strong>Внимание!</strong> У вас нет услуги, для использования ресурсов приобретите "
"нужную услугу из представленных тут."
"<strong>Внимание!</strong> У вас нет услуги, для использования ресурсов "
"приобретите нужную услугу из представленных тут."
#: templates/clientsideapp/services.html:46
msgid "Services available for ordering"
@ -233,15 +233,20 @@ msgstr "Нет доступных для заказа услуг"
msgid "Buy the service via user side, service '%s'"
msgstr "Покупка тарифного плана через личный кабинет, тариф '%s'"
#: views.py:53
#: views.py:54
#, python-format
msgid "The service '%s' wan successfully activated"
msgstr "Услуга '%s' успешно подключена"
#: views.py:83
#: views.py:84
msgid "Are you not sure that you want buy the service?"
msgstr "Вы не уверены что хотите оплатить долг?"
#: views.py:85
#: views.py:86
msgid "Your account have not enough money"
msgstr "Недостаточно средств на счету"
#: views.py:89
#, python-format
msgid "%(username)s paid the debt %(amount).2f"
msgstr "%(username)s заплатил долг в размере %(amount).2f"

8
clientsideapp/templates/clientsideapp/debt_buy.html

@ -18,13 +18,11 @@
{% trans 'Are you sure you want to spend a payment?' %}
</label>
</div>
<p>
{% blocktrans trimmed %}
<p>{% blocktrans trimmed %}
From your account, they withdraw funds in <b>{{ amount }}</b> rub.<br/>
As a result, you will remain on your account {{ ballance_after }} rubles.<br/>
The administrator can immediately see that you shut down the debt.
{% endblocktrans %}
</p>
{% endblocktrans %}</p>
<h4>{% trans 'Description of payment' %}</h4>
<div class="alert alert-info">
@ -42,4 +40,4 @@
</form>
</div>
</div>
{% endblock %}
{% endblock %}

2
clientsideapp/templates/clientsideapp/ext.html

@ -60,7 +60,7 @@
</li>
</ul>
<span class="navbar-text">
{% blocktrans with ballance=subscriber.ballance|floatformat:2 %}Your balance is <b>{{ ballance }}</b>{% endblocktrans %} {% trans 'currency' %}
{% blocktrans with ballance=request.user.ballance|floatformat:2 %}Your balance is <b>{{ ballance }}</b>{% endblocktrans %} {% trans 'currency' %}
</span>
</div><!--/.nav-collapse -->
</div>

3
clientsideapp/templates/clientsideapp/modal_service_buy.html

@ -12,7 +12,8 @@
<p>{{ service.descr }}</p>
<!--<p>{% blocktrans with speedIn=service.speedIn speedOut=service.speedOut amount=service.amount %}Inbound speed: {{ speedIn }} MBit/s<br>
<!--<p>{# {% blocktrans trimmed with speedIn=service.speedIn speedOut=service.speedOut amount=service.amount %}
Inbound speed: {{ speedIn }} MBit/s<br>
Outgoing speed: {{ speedOut }} MBit/s<br>
Cost: {{ amount }} rubles.{% endblocktrans %}</p>-->
<p>{% blocktrans with amount=service.amount|floatformat:2 %}The cost is {{ amount }}{% endblocktrans %} {% trans 'currency' %}</p>

23
clientsideapp/views.py

@ -4,7 +4,7 @@ from django.contrib.gis.shortcuts import render_to_text
from django.shortcuts import render, get_object_or_404, redirect
from django.contrib import messages
from django.db import transaction
from django.utils.translation import gettext_lazy as _
from django.utils.translation import gettext_lazy as _, gettext
from abonapp.models import AbonLog, InvoiceForPayment, Abon
from tariff_app.models import Tariff
@ -28,10 +28,10 @@ def pays(request):
@login_required
def services(request):
try:
abon = Abon.objects.get(pk=request.user.pk)
all_tarifs = abon.group.tariffs.filter(is_admin=False)
abon = request.user
all_tarifs = Tariff.objects.get_tariffs_by_group(abon.group.pk)
current_service = abon.active_tariff()
except:
except Abon.DoesNotExist:
all_tarifs = None
current_service = None
return render(request, 'clientsideapp/services.html', {
@ -43,13 +43,14 @@ def services(request):
@login_required
@transaction.atomic
def buy_service(request, srv_id):
abon = get_object_or_404(Abon, pk=request.user.pk)
abon = request.user
service = get_object_or_404(Tariff, pk=srv_id)
try:
current_service = abon.active_tariff()
if request.method == 'POST':
abon.pick_tariff(service, request.user, _("Buy the service via user side, service '%s'")
abon.pick_tariff(service, None, _("Buy the service via user side, service '%s'")
% service)
abon.sync_with_nas(created=False)
messages.success(request, _("The service '%s' wan successfully activated") % service.title)
else:
return render_to_text('clientsideapp/modal_service_buy.html', {
@ -75,7 +76,7 @@ def debts_list(request):
@transaction.atomic
def debt_buy(request, d_id):
debt = get_object_or_404(InvoiceForPayment, id=d_id)
abon = get_object_or_404(Abon, id=request.user.id)
abon = request.user
if request.method == 'POST':
try:
sure = request.POST.get('sure')
@ -84,9 +85,13 @@ def debt_buy(request, d_id):
if abon.ballance < debt.amount:
raise LogicError(_('Your account have not enough money'))
abon.make_pay(request.user, debt.amount)
debt.set_ok()
amount = -debt.amount
abon.add_ballance(None, amount, comment=gettext('%(username)s paid the debt %(amount).2f') % {
'username': abon.get_full_name(),
'amount': amount
})
abon.save(update_fields=['ballance'])
debt.set_ok()
debt.save(update_fields=['status', 'date_pay'])
return redirect('client_side:debts')
except LogicError as e:

5
devapp/forms.py

@ -28,10 +28,7 @@ class DeviceForm(forms.ModelForm):
'comment': forms.TextInput(attrs={
'required': True
}),
'man_passw': forms.PasswordInput(render_value=True),
'user_group': forms.Select(attrs={
'class': 'form-control'
})
'man_passw': forms.PasswordInput(render_value=True)
}

4
devapp/locale/ru/LC_MESSAGES/django.po

@ -411,7 +411,7 @@ msgid "You have redirected to existing device"
msgstr "Вы были переадресованы на существующее устройство"
#: views.py:113 views.py:329 views.py:419
msgid "Please attach user group for device"
msgid "Please attach group for device"
msgstr "Пожалуйста назначте устройству группу в настройках"
#: views.py:117 views.py:275 views.py:304 views.py:421
@ -424,7 +424,7 @@ msgid "Duplicate user and port: %s"
msgstr "Пользователь с таким портом и устройством уже есть: %s"
#: views.py:159 views.py:198
msgid "Device is not have a group, please fix that"
msgid "Device does not have a group, please fix that"
msgstr "У устройства нет группы, пожалуйста, исправьте это"
#: views.py:164 views.py:231 views.py:316

62
devapp/migrations/0001_initial.py

@ -0,0 +1,62 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-02-26 00:20
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
import djing.fields
import mydefs
class Migration(migrations.Migration):
initial = True
dependencies = [
('group_app', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='Device',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('ip_address', mydefs.MyGenericIPAddressField(max_length=8, protocol='ipv4', verbose_name='Ip address')),
('mac_addr', djing.fields.MACAddressField(blank=True, integer=True, null=True, unique=True, verbose_name='Mac address')),
('comment', models.CharField(max_length=256, verbose_name='Comment')),
('devtype', models.CharField(choices=[('Dl', 'DLink switch'), ('Pn', 'PON OLT'), ('On', 'PON ONU'), ('Ex', 'Eltex switch')], default='Dl', max_length=2, verbose_name='Device type')),
('man_passw', models.CharField(blank=True, max_length=16, null=True, verbose_name='SNMP password')),
('snmp_item_num', models.PositiveSmallIntegerField(blank=True, default=0, verbose_name='SNMP Number')),
('status', models.CharField(choices=[('und', 'Undefined'), ('up', 'Up'), ('unr', 'Unreachable'), ('dwn', 'Down')], default='und', max_length=3, verbose_name='Status')),
('is_noticeable', models.BooleanField(default=False, verbose_name='Send notify when monitoring state changed')),
('group', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='group_app.Group', verbose_name='Device group')),
('parent_dev', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='devapp.Device', verbose_name='Parent device')),
],
options={
'verbose_name': 'Device',
'verbose_name_plural': 'Devices',
'db_table': 'dev',
'ordering': ['comment'],
'permissions': (('can_view_device', 'Can view device'),),
},
),
migrations.CreateModel(
name='Port',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('num', models.PositiveSmallIntegerField(default=0, verbose_name='Number')),
('descr', models.CharField(blank=True, max_length=60, null=True, verbose_name='Description')),
('device', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='devapp.Device', verbose_name='Device')),
],
options={
'verbose_name': 'Port',
'verbose_name_plural': 'Ports',
'db_table': 'dev_port',
'permissions': (('can_toggle_ports', 'Can toggle ports'),),
},
),
migrations.AlterUniqueTogether(
name='port',
unique_together=set([('device', 'num')]),
),
]

90
devapp/migrations/0001_squashed_0007_auto_20170816_1109.py

@ -1,90 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2017-09-04 16:14
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
import djing.fields
import mydefs
class Migration(migrations.Migration):
initial = True
dependencies = [
('mapapp', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='Device',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('ip_address', mydefs.MyGenericIPAddressField(max_length=8, protocol='ipv4')),
('comment', models.CharField(max_length=256)),
('devtype', models.CharField(choices=[('Dl', "Свич D'Link")], default='Dl', max_length=2)),
('man_passw', models.CharField(blank=True, max_length=16, null=True)),
],
),
migrations.CreateModel(
name='Port',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('num', models.PositiveSmallIntegerField(default=0)),
('speed', models.CharField(choices=[('h', '100Mbps'), ('k', '1Gbps'), ('d', '10Gbps')], default='h', max_length=1)),
('device', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='devapp.Device')),
],
),
migrations.AlterUniqueTogether(
name='port',
unique_together=set([('device', 'num')]),
),
migrations.AlterModelTable(
name='device',
table='dev',
),
migrations.AlterModelTable(
name='port',
table='dev_port',
),
migrations.AddField(
model_name='device',
name='map_dot',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mapapp.Dot'),
),
migrations.AlterField(
model_name='device',
name='devtype',
field=models.CharField(choices=[('Dl', 'DLink switch'), ('Pn', 'PON OLT')], default='Dl', max_length=2),
),
migrations.RemoveField(
model_name='port',
name='speed',
),
migrations.AddField(
model_name='device',
name='mac_addr',
field=djing.fields.MACAddressField(blank=True, integer=True, null=True, unique=True),
),
migrations.AddField(
model_name='device',
name='parent_dev',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='devapp.Device'),
),
migrations.AddField(
model_name='port',
name='descr',
field=models.CharField(blank=True, max_length=60, null=True),
),
migrations.AlterField(
model_name='device',
name='devtype',
field=models.CharField(choices=[('Dl', 'DLink switch'), ('Pn', 'PON OLT'), ('On', 'PON ONU')], default='Dl', max_length=2),
),
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),
),
]

22
devapp/migrations/0002_device_user_group.py

@ -1,22 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2017-09-04 16:16
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('abonapp', '0001_squashed_0022_auto_20170816_1109'),
('devapp', '0001_squashed_0007_auto_20170816_1109'),
]
operations = [
migrations.AddField(
model_name='device',
name='user_group',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='abonapp.AbonGroup'),
),
]

23
devapp/migrations/0003_auto_20170927_1838.py

@ -1,23 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2017-09-27 18:38
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('devapp', '0002_device_user_group'),
]
operations = [
migrations.AlterModelOptions(
name='device',
options={'permissions': (('can_view_device', 'Can view device'),), 'verbose_name': 'Device', 'verbose_name_plural': 'Devices'},
),
migrations.AlterModelOptions(
name='port',
options={'permissions': (('can_toggle_ports', 'Can toggle ports'),), 'verbose_name': 'Port', 'verbose_name_plural': 'Ports'},
),
]

72
devapp/migrations/0004_auto_20171103_0006.py

@ -1,72 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2017-11-03 00:06
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
import djing.fields
import mydefs
class Migration(migrations.Migration):
dependencies = [
('devapp', '0003_auto_20170927_1838'),
]
operations = [
migrations.RemoveField(
model_name='device',
name='map_dot',
),
migrations.AlterField(
model_name='device',
name='comment',
field=models.CharField(max_length=256, verbose_name='Comment'),
),
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, verbose_name='Device type'),
),
migrations.AlterField(
model_name='device',
name='ip_address',
field=mydefs.MyGenericIPAddressField(max_length=8, protocol='ipv4', verbose_name='Ip address'),
),
migrations.AlterField(
model_name='device',
name='mac_addr',
field=djing.fields.MACAddressField(blank=True, integer=True, null=True, unique=True, verbose_name='Mac address'),
),
migrations.AlterField(
model_name='device',
name='man_passw',
field=models.CharField(blank=True, max_length=16, null=True, verbose_name='SNMP password'),
),
migrations.AlterField(
model_name='device',
name='parent_dev',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='devapp.Device', verbose_name='Parent device'),
),
migrations.AlterField(
model_name='device',
name='user_group',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='abonapp.AbonGroup', verbose_name='User group'),
),
migrations.AlterField(
model_name='port',
name='descr',
field=models.CharField(blank=True, max_length=60, null=True, verbose_name='Description'),
),
migrations.AlterField(
model_name='port',
name='device',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='devapp.Device', verbose_name='Device'),
),
migrations.AlterField(
model_name='port',
name='num',
field=models.PositiveSmallIntegerField(default=0, verbose_name='Number'),
),
]

20
devapp/migrations/0005_device_snmp_item_num.py

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2017-11-07 16:19
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('devapp', '0004_auto_20171103_0006'),
]
operations = [
migrations.AddField(
model_name='device',
name='snmp_item_num',
field=models.PositiveSmallIntegerField(default=0),
),
]

34
devapp/migrations/0006_auto_20180129_1625.py

@ -1,34 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-01-29 16:25
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('devapp', '0005_device_snmp_item_num'),
]
operations = [
migrations.AlterModelOptions(
name='device',
options={'ordering': ['comment'], 'permissions': (('can_view_device', 'Can view device'),), 'verbose_name': 'Device', 'verbose_name_plural': 'Devices'},
),
migrations.AddField(
model_name='device',
name='is_noticeable',
field=models.BooleanField(default=False, verbose_name='Send notify when monitoring state changed'),
),
migrations.AddField(
model_name='device',
name='status',
field=models.CharField(choices=[('und', 'Undefined'), ('up', 'Up'), ('unr', 'Unreachable'), ('dwn', 'Down')], default='und', max_length=3, verbose_name='Status'),
),
migrations.AlterField(
model_name='device',
name='snmp_item_num',
field=models.PositiveSmallIntegerField(blank=True, default=0, verbose_name='SNMP Number'),
),
]

7
devapp/models.py

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
import requests
from django.db import models
from django.db import models, ProgrammingError
from djing.fields import MACAddressField
from .base_intr import DevBase
from mydefs import MyGenericIPAddressField, MyChoicesAdapter, ip2int
@ -9,6 +9,7 @@ from subprocess import run
from django.conf import settings
from django.utils.translation import gettext_lazy as _
from json.decoder import JSONDecodeError
from group_app.models import Group
DEVICE_TYPES = (
@ -51,7 +52,7 @@ class Device(models.Model):
comment = models.CharField(_('Comment'), max_length=256)
devtype = models.CharField(_('Device type'), max_length=2, default=DEVICE_TYPES[0][0], choices=MyChoicesAdapter(DEVICE_TYPES))
man_passw = models.CharField(_('SNMP password'), max_length=16, null=True, blank=True)
user_group = models.ForeignKey('abonapp.AbonGroup', verbose_name=_('User group'), on_delete=models.SET_NULL, null=True, blank=True)
group = models.ForeignKey(Group, on_delete=models.SET_NULL, null=True, blank=True, verbose_name=_('Device group'))
parent_dev = models.ForeignKey('self', verbose_name=_('Parent device'), blank=True, null=True, on_delete=models.SET_NULL)
snmp_item_num = models.PositiveSmallIntegerField(_('SNMP Number'), default=0, blank=True)
@ -106,6 +107,8 @@ class Device(models.Model):
def update_dhcp(self):
if self.devtype not in ('On','Dl'):
return
raise ProgrammingError('переделать это безобразие')
# FIXME: переделать это безобразие
grp = self.user_group.id
code = ''
if grp == 87:

2
devapp/templates/devapp/add_dev.html

@ -40,7 +40,7 @@
{% bootstrap_field form.man_passw addon_before=ic %}
{% bootstrap_icon 'subscript' as ic %}
{% bootstrap_field form.user_group addon_before=ic %}
{% bootstrap_field form.group addon_before=ic %}
<div class="form-group">
<label for="id_parent_dev">{% trans 'Parent device' %}</label>

2
devapp/templates/devapp/custom_dev_page/olt.html

@ -23,7 +23,7 @@
</thead>
<tbody>
{% with dip=dev.ip_address grp=dev.user_group.pk %}
{% with dip=dev.ip_address grp=dev.group.pk %}
{% for port in ports %}
<tr>
<td>{% if port.st %}<span class="glyphicon glyphicon-ok text-success"></span>

2
devapp/templates/devapp/custom_dev_page/onu.html

@ -30,7 +30,7 @@
{% endfor %}
{% if dev.parent_dev %}
<li class="list-group-item">
{% with pdev=dev.parent_dev pdgrp=dev.parent_dev.user_group %}
{% with pdev=dev.parent_dev pdgrp=dev.parent_dev.group %}
{% trans 'Parent device' %}:<a href="{% url 'devapp:view' pdgrp.pk pdev.pk %}"
title="{{ pdev.mac_addr|default:'' }}"
target="_blank">{{ pdev.ip_address }} {{ pdev.comment }}</a>

4
devapp/templates/devapp/custom_dev_page/ports.html

@ -41,11 +41,11 @@
<b>{{ port.num }}</b>
</a>
{% if port.st %}
<a href="{% url 'devapp:port_toggle' dev.user_group.pk|default:0 dev.id port.num 0 %}" class="btn btn-xs btn-danger" title="{% trans 'Disable port' %}">
<a href="{% url 'devapp:port_toggle' dev.group.pk|default:0 dev.id port.num 0 %}" class="btn btn-xs btn-danger" title="{% trans 'Disable port' %}">
<span class="glyphicon glyphicon-off"></span>
</a>
{% else %}
<a href="{% url 'devapp:port_toggle' dev.user_group.pk|default:0 dev.id port.num 1 %}" class="btn btn-xs btn-success" title="{% trans 'Enable port' %}">
<a href="{% url 'devapp:port_toggle' dev.group.pk|default:0 dev.id port.num 1 %}" class="btn btn-xs btn-success" title="{% trans 'Enable port' %}">
<span class="glyphicon glyphicon-ok"></span>
</a>
{% endif %}

2
devapp/templates/devapp/dev.html

@ -27,7 +27,7 @@
{% bootstrap_field form.man_passw addon_before=ic %}
{% bootstrap_icon 'subscript' as ic %}
{% bootstrap_field form.user_group addon_before=ic %}
{% bootstrap_field form.group addon_before=ic %}
<div class="form-group">
<label for="id_parent_dev">{% trans 'Parent device' %}</label>

8
devapp/templates/devapp/devices.html

@ -41,7 +41,7 @@
</thead>
<tbody>
{% with can_del_dev=perms.devapp.delete_device can_ch_dev=perms.devapp.change_device %}
{% with can_del_dev=perms.devapp.delete_device can_ch_dev=perms.devapp.change_device grpid=group.id %}
{% for dev in devices %}
<tr>
@ -57,19 +57,19 @@
{% endif %}
{% endif %}
</td>
<td><a href="{% url 'devapp:view' dev.user_group.pk dev.pk %}">{{ dev.ip_address }}</a></td>
<td><a href="{% url 'devapp:view' grpid dev.pk %}">{{ dev.ip_address }}</a></td>
<td>{{ dev.comment }}</td>
<td>{{ dev.mac_addr|default:_('Not assigned') }}</td>
{# <td class="hidden-xs hidden-sm">{{ dev.mon.plugin_output|default:'&ndash;' }}</td> #}
<td>{{ dev.get_devtype_display }}</td>
<td class="btn-group btn-group-xs btn-group-justified">
{% if can_del_dev %}
<a href="{% url 'devapp:del' dev.user_group.pk dev.pk %}" class="btn btn-danger">
<a href="{% url 'devapp:del' grpid dev.pk %}" class="btn btn-danger">
<span class="glyphicon glyphicon-remove"></span>
</a>
{% endif %}
{% if can_ch_dev %}
<a href="{% url 'devapp:edit' dev.user_group.pk dev.id %}" class="btn btn-default">
<a href="{% url 'devapp:edit' grpid dev.id %}" class="btn btn-default">
<span class="glyphicon glyphicon-edit"></span>
</a>
{% endif %}

6
devapp/templates/devapp/devices_null_group.html

@ -41,17 +41,17 @@
{% with can_del_dev=perms.devapp.delete_device can_change_dev=perms.devapp.change_device %}
{% for dev in devices %}
<tr>
<td><a href="{% url 'devapp:view' dev.user_group.pk|default:0 dev.pk %}">{{ dev.ip_address }}</a></td>
<td><a href="{% url 'devapp:view' 0 dev.pk %}">{{ dev.ip_address }}</a></td>
<td>{{ dev.comment }}</td>
<td>{{ dev.get_devtype_display }}</td>
<td class="btn-group btn-group-sm">
{% if can_del_dev %}
<a href="{% url 'devapp:del' dev.user_group.pk|default:0 dev.pk %}" class="btn btn-danger btn-sm">
<a href="{% url 'devapp:del' 0 dev.pk %}" class="btn btn-danger btn-sm">
<span class="glyphicon glyphicon-remove"></span>
</a>
{% endif %}
{% if can_change_dev %}
<a href="{% url 'devapp:edit' dev.user_group.pk|default:0 dev.pk %}" class="btn btn-default btn-sm">
<a href="{% url 'devapp:edit' 0 dev.pk %}" class="btn btn-default btn-sm">
<span class="glyphicon glyphicon-edit"></span>
</a>
{% endif %}

10
devapp/templates/devapp/ext.htm

@ -5,8 +5,8 @@
<ol class="breadcrumb">
<li><span class="glyphicon glyphicon-home"></span></li>
<li><a href="{% url 'devapp:group_list' %}">{% trans 'Groups' %}</a></li>
{% if dev.user_group %}
<li><a href="{% url 'devapp:devs' dev.user_group.pk %}">{{ dev.user_group.title }}</a></li>
{% if dev.group %}
<li><a href="{% url 'devapp:devs' dev.group.pk %}">{{ dev.group.title }}</a></li>
{% else %}
<li>{% trans 'Not assigned' %}</li>
{% endif %}
@ -21,7 +21,7 @@
<ul class="nav nav-tabs">
{% url 'devapp:view' dev.user_group.pk|default:0 dev.pk as devapp_view %}
{% url 'devapp:view' dev.group.pk|default:0 dev.pk as devapp_view %}
<li{% if devapp_view == request.path %} class="active"{% endif %}>
<a href="{{ devapp_view }}">
{% trans 'Ports' %} {{ dev.ip_address }}
@ -29,12 +29,12 @@
</li>
{% if perms.devapp.change_device %}
{% url 'devapp:edit' dev.user_group.pk|default:0 dev.pk as devapp_edit %}
{% url 'devapp:edit' dev.group.pk|default:0 dev.pk as devapp_edit %}
<li{% if devapp_edit == request.path %} class="active"{% endif %}>
<a href="{{ devapp_edit }}">{% trans 'Edit' %}</a>
</li>
{% url 'devapp:manage_ports' dev.user_group.pk|default:0 dev.pk as devapp_mports %}
{% url 'devapp:manage_ports' dev.group.pk|default:0 dev.pk as devapp_mports %}
<li{% if devapp_mports == request.path %} class="active"{% endif %}>
<a href="{{ devapp_mports }}">{% trans 'Ports' %}</a>
</li>

6
devapp/templates/devapp/fix_dev_group.html

@ -36,12 +36,12 @@
{% bootstrap_field form.man_passw addon_before=ic %}
<div class="form-group">
<label class="control-label" for="{{ form.user_group.id_for_label }}">{{ form.user_group.label }}</label>
<div class="input-group{% if not dev.user_group %} has-error{% endif %}">
<label class="control-label" for="{{ form.group.id_for_label }}">{{ form.group.label }}</label>
<div class="input-group{% if not dev.group %} has-error{% endif %}">
<span class="input-group-addon">
{% bootstrap_icon 'subscript' %}
</span>
{{ form.user_group }}
{{ form.group }}
</div>
</div>

19
devapp/templates/devapp/group_list.html

@ -7,17 +7,13 @@
<li class="active">{% trans 'Groups' %}</li>
</ol>
<h3>{% trans 'Groups' %}</h3>
{% include 'message_block.html' %}
<div class="table-responsive">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{% trans 'Group title' %}</h3>
</div>
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>{% trans 'Group title' %}</th>
</tr>
</thead>
<tbody>
{% for gr in groups %}
<tr>
@ -31,12 +27,7 @@
</tbody>
<tfoot>
<tr>
<td class="btn-group">
{% if perms.abonapp.add_abongroup %}
<a href="{% url 'abonapp:add_group' %}" class="btn btn-success btn-sm">
<span class="glyphicon glyphicon-plus"></span> <span class="hidden-xs">{% trans 'Add group' %}</span>
</a>
{% endif %}
<td>
<a href="{% url 'devapp:devices_null_group' %}" class="btn btn-primary btn-sm">
<span class="glyphicon glyphicon-list-alt"></span> {% trans 'Devices without group' %}
</a>

8
devapp/templates/devapp/manage_ports/add_ports.html

@ -5,8 +5,8 @@
<ol class="breadcrumb">
<li><span class="glyphicon glyphicon-home"></span></li>
<li><a href="{% url 'devapp:group_list' %}">{% trans 'Groups' %}</a></li>
<li><a href="{% url 'devapp:devs' dev.user_group.pk %}">{{ dev.user_group.title }}</a></li>
<li><a href="{% url 'devapp:view' dev.user_group.pk dev.pk %}">{{ dev.comment }}</a></li>
<li><a href="{% url 'devapp:devs' dev.group.pk %}">{{ dev.group.title }}</a></li>
<li><a href="{% url 'devapp:view' dev.group.pk dev.pk %}">{{ dev.comment }}</a></li>
<li class="active">{% trans 'Add ports' %}</li>
</ol>
@ -22,7 +22,7 @@
</div>
<div class="panel-body">
<form class="table-responsive" role="form" action="{% url 'devapp:add_ports' dev.user_group.pk dev.pk %}" method="post">{% csrf_token %}
<form class="table-responsive" role="form" action="{% url 'devapp:add_ports' dev.group.pk dev.pk %}" method="post">{% csrf_token %}
<table class="table table-striped table-bordered">
<thead>
@ -34,7 +34,7 @@
</tr>
</thead>
<tbody>
{% with gid=dev.user_group.pk did=dev.pk can_del_port=perms.devapp.delete_port %}
{% with gid=dev.group.pk did=dev.pk can_del_port=perms.devapp.delete_port %}
{% for port in ports %}
<tr>
<td>{% if port.status %}

4
devapp/templates/devapp/manage_ports/fix_abon_device.html

@ -5,8 +5,8 @@
<ol class="breadcrumb">
<li><span class="glyphicon glyphicon-home"></span></li>
<li><a href="{% url 'devapp:group_list' %}">{% trans 'Groups' %}</a></li>
<li><a href="{% url 'devapp:devs' user_group.pk %}">{{ user_group.title }}</a></li>
<li><a href="{% url 'devapp:view' user_group.pk device.pk %}">{{ device.comment }}</a></li>
<li><a href="{% url 'devapp:devs' group.pk %}">{{ group.title }}</a></li>
<li><a href="{% url 'devapp:view' group.pk device.pk %}">{{ device.comment }}</a></li>
<li class="active">{% trans 'Fix subscriber ports conflict' %}</li>
</ol>

8
devapp/templates/devapp/manage_ports/list.html

@ -16,19 +16,19 @@
</thead>
<tbody>
{% with gid=dev.user_group.pk did=dev.pk can_del_port=perms.devapp.delete_port can_edit_port=perms.devapp.change_port %}
{% with gid=dev.group.pk did=dev.pk can_del_port=perms.devapp.delete_port can_edit_port=perms.devapp.change_port %}
{% for port in ports %}
<tr>
<td>{{ port.num }}</td>
<td>{{ port.descr }}</td>
{% if port.num_abons > 1 %}
{% url 'devapp:fix_port_conflict' dev.user_group.id dev.id port.id as fixurl %}
{% url 'devapp:fix_port_conflict' gid did port.id as fixurl %}
<td class="text-danger"><b>{{ port.num_abons }}</b>. {% blocktrans with furl=fixurl %}Port should not have more than one subscriber, <a href='{{ furl }}'>fix that</a>{% endblocktrans %}</td>
{% else %}
<td>{{ port.num_abons }}</td>
{% endif %}
<td class="btn-group btn-group-sm">
<a href="{% url 'devapp:show_subscriber_on_port' dev.user_group.id dev.id port.id %}" class="btn btn-default btn-modal" title="{% trans 'Show subscriber on port' %}" data-toggle="tooltip">
<a href="{% url 'devapp:show_subscriber_on_port' gid did port.id %}" class="btn btn-default btn-modal" title="{% trans 'Show subscriber on port' %}" data-toggle="tooltip">
<span class="glyphicon glyphicon-eye-open"></span>
</a>
{% if can_del_port %}
@ -54,7 +54,7 @@
<tr>
<td colspan="4" class="btn-group">
{% if perms.devapp.add_port %}
<a href="{% url 'devapp:add_ports' dev.user_group.pk dev.pk %}" class="btn btn-sm btn-default" title="{% trans 'Add' %}">
<a href="{% url 'devapp:add_ports' dev.group.pk dev.pk %}" class="btn btn-sm btn-default" title="{% trans 'Add' %}">
<span class="glyphicon glyphicon-plus"></span> {% trans 'Add ports' %}
</a>
{% endif %}

80
devapp/views.py

@ -15,7 +15,9 @@ from django.views.generic import ListView, DetailView
from devapp.base_intr import DeviceImplementationError
from mydefs import res_success, res_error, only_admins, ping, ip_addr_regex
from abonapp.models import AbonGroup, Abon
from abonapp.models import Abon
from group_app.models import Group
from accounts_app.models import UserProfile
from django.conf import settings
from guardian.decorators import permission_required_or_403 as permission_required
from guardian.shortcuts import get_objects_for_user
@ -27,8 +29,6 @@ from .models import Device, Port, DeviceDBException, DeviceMonitoringException
from .forms import DeviceForm, PortForm
from mydefs import safe_int
PAGINATION_ITEMS_PER_PAGE = getattr(settings, 'PAGINATION_ITEMS_PER_PAGE', 10)
class BaseDeviceListView(ListView):
http_method_names = ['get']
@ -42,15 +42,15 @@ class DevicesListView(BaseDeviceListView, global_base_views.OrderingMixin):
def get_queryset(self):
group_id = safe_int(self.kwargs.get('group_id'))
queryset = Device.objects.filter(user_group__pk=group_id) \
.select_related('user_group') \
.only('comment', 'mac_addr', 'devtype', 'user_group', 'pk', 'ip_address')
queryset = Device.objects.filter(group__pk=group_id) \
.select_related('group') \
.only('comment', 'mac_addr', 'devtype', 'group', 'pk', 'ip_address')
return queryset
def get_context_data(self, **kwargs):
group_id = safe_int(self.kwargs.get('group_id'))
context = super(DevicesListView, self).get_context_data(**kwargs)
context['group'] = get_object_or_404(AbonGroup, pk=group_id)
context['group'] = get_object_or_404(Group, pk=group_id)
return context
def dispatch(self, request, *args, **kwargs):
@ -66,7 +66,7 @@ class DevicesListView(BaseDeviceListView, global_base_views.OrderingMixin):
class DevicesWithoutGroupsListView(BaseDeviceListView, global_base_views.OrderingMixin):
context_object_name = 'devices'
template_name = 'devapp/devices_null_group.html'
queryset = Device.objects.filter(user_group=None).only('comment', 'devtype', 'user_group', 'pk', 'ip_address')
queryset = Device.objects.filter(group=None).only('comment', 'devtype', 'pk', 'ip_address')
@login_required
@ -74,7 +74,7 @@ class DevicesWithoutGroupsListView(BaseDeviceListView, global_base_views.Orderin
def devdel(request, device_id):
try:
dev = Device.objects.get(pk=device_id)
back_url = resolve_url('devapp:devs', group_id=dev.user_group.pk if dev.user_group else 0)
back_url = resolve_url('devapp:devs', group_id=dev.group.pk if dev.group else 0)
dev.delete()
return res_success(request, back_url)
except Device.DoesNotExist:
@ -86,8 +86,8 @@ def devdel(request, device_id):
@login_required
@permission_required('devapp.can_view_device')
def dev(request, group_id, device_id=0):
user_group = get_object_or_404(AbonGroup, pk=group_id)
if not request.user.has_perm('abonapp.can_view_abongroup', user_group):
device_group = get_object_or_404(Group, pk=group_id)
if not request.user.has_perm('group_app.can_view_group', device_group):
raise PermissionDenied
devinst = get_object_or_404(Device, id=device_id) if device_id != 0 else None
already_dev = None
@ -106,11 +106,11 @@ def dev(request, group_id, device_id=0):
# check if that device is exist
try:
already_dev = Device.objects.exclude(pk=device_id).get(mac_addr=request.POST.get('mac_addr'))
if already_dev.user_group:
if already_dev.group:
messages.warning(request, _('You have redirected to existing device'))
return redirect('devapp:view', already_dev.user_group.pk, already_dev.pk)
return redirect('devapp:view', already_dev.group.pk, already_dev.pk)
else:
messages.warning(request, _('Please attach user group for device'))
messages.warning(request, _('Please attach group for device'))
return redirect('devapp:fix_device_group', already_dev.pk)
except Device.DoesNotExist:
pass
@ -120,7 +120,7 @@ def dev(request, group_id, device_id=0):
# change device info in dhcpd.conf
ndev.update_dhcp()
messages.success(request, _('Device info has been saved'))
return redirect('devapp:edit', ndev.user_group.pk, ndev.pk)
return redirect('devapp:edit', ndev.group.pk, ndev.pk)
else:
messages.error(request, _('Form is invalid, check fields and try again'))
except IntegrityError as e:
@ -131,7 +131,7 @@ def dev(request, group_id, device_id=0):
else:
if devinst is None:
frm = DeviceForm(initial={
'user_group': user_group,
'group': device_group,
'devtype': request.GET.get('t'),
'mac_addr': request.GET.get('mac'),
'comment': request.GET.get('c'),
@ -145,7 +145,7 @@ def dev(request, group_id, device_id=0):
if devinst is None:
return render(request, 'devapp/add_dev.html', {
'form': frm,
'group': user_group,
'group': device_group,
'already_dev': already_dev
})
else:
@ -153,7 +153,7 @@ def dev(request, group_id, device_id=0):
'form': frm,
'dev': devinst,
'selected_parent_dev': devinst.parent_dev,
'group': user_group,
'group': device_group,
'already_dev': already_dev
})
@ -163,8 +163,8 @@ def dev(request, group_id, device_id=0):
def manage_ports(request, device_id):
try:
dev = Device.objects.get(pk=device_id)
if dev.user_group is None:
messages.error(request, _('Device is not have a group, please fix that'))
if dev.group is None:
messages.error(request, _('Device does not have a group, please fix that'))
return redirect('devapp:fix_device_group', dev.pk)
ports = Port.objects.filter(device=dev).annotate(num_abons=Count('abon'))
@ -220,13 +220,13 @@ def add_ports(request, device_id):
return self.pid
def __str__(self):
return "p:%d\tM:%s\tT:%s" % (self.pid, self.text)
return "p:%d\tT:%s" % (self.pid, self.text)
try:
res_ports = list()
dev = Device.objects.get(pk=device_id)
if dev.user_group is None:
messages.error(request, _('Device is not have a group, please fix that'))
if dev.group is None:
messages.error(request, _('Device does not have a group, please fix that'))
return redirect('devapp:fix_device_group', dev.pk)
if request.method == 'POST':
ports = zip(
@ -356,8 +356,8 @@ def devview(request, device_id):
ports, manager = None, None
dev = get_object_or_404(Device, id=device_id)
if not dev.user_group:
messages.warning(request, _('Please attach user group for device'))
if not dev.group:
messages.warning(request, _('Please attach group for device'))
return redirect('devapp:fix_device_group', dev.pk)
template_name = 'ports.html'
@ -409,18 +409,18 @@ def toggle_port(request, device_id, portid, status=0):
messages.error(request, _('wait for a reply from the SNMP Timeout'))
except EasySNMPError as e:
messages.error(request, 'EasySNMPError: %s' % e)
return redirect('devapp:view', dev.user_group.pk if dev.user_group is not None else 0, device_id)
return redirect('devapp:view', dev.group.pk if dev.group is not None else 0, device_id)
@method_decorator([login_required, only_admins], name='dispatch')
class GroupsListView(BaseDeviceListView):
context_object_name = 'groups'
template_name = 'devapp/group_list.html'
model = AbonGroup
model = Group
def get_queryset(self):
groups = super(GroupsListView, self).get_queryset()
groups = get_objects_for_user(self.request.user, 'abonapp.can_view_abongroup', klass=groups,
groups = get_objects_for_user(self.request.user, 'group_app.can_view_group', klass=groups,
accept_global_perms=False)
return groups
@ -446,11 +446,11 @@ def fix_device_group(request, device_id):
frm = DeviceForm(request.POST, instance=dev)
if frm.is_valid():
ch_dev = frm.save()
if ch_dev.user_group:
if ch_dev.group:
messages.success(request, _('Device fixed'))
return redirect('devapp:devs', ch_dev.user_group.pk)
return redirect('devapp:devs', ch_dev.group.pk)
else:
messages.error(request, _('Please attach user group for device'))
messages.error(request, _('Please attach group for device'))
else:
messages.error(request, _('Form is invalid, check fields and try again'))
else:
@ -478,6 +478,7 @@ def fix_onu(request):
text = '<span class="glyphicon glyphicon-ok"></span> <span class="hidden-xs">%s</span>' % \
(_('Device with mac address %(mac)s does not exist') % {'mac': mac})
for srcmac, snmpnum in ports:
# convert bytes mac address to str presentation mac address
real_mac = ':'.join(['%x' % ord(i) for i in srcmac])
if mac == real_mac:
onu.snmp_item_num = snmpnum
@ -497,13 +498,13 @@ def fix_onu(request):
@login_required
def fix_port_conflict(request, group_id, device_id, port_id):
user_group = get_object_or_404(AbonGroup, pk=group_id)
dev_group = get_object_or_404(Group, pk=group_id)
device = get_object_or_404(Device, pk=device_id)
port = get_object_or_404(Port, pk=port_id)
abons = Abon.objects.filter(device__id=device_id, dev_port__id=port_id)
return render(request, 'devapp/manage_ports/fix_abon_device.html', {
'abons': abons,
'user_group': user_group,
'group': dev_group,
'device': device,
'port': port
})
@ -527,19 +528,14 @@ class OnDevDown(global_base_views.AllowedSubnetMixin, global_base_views.HashAuth
if not bool(re.match(ip_addr_regex, dev_ip)):
return {'text': 'ip address %s is not valid' % dev_ip}
possible_devices = Device.objects.filter(ip_address=dev_ip)
if possible_devices.count() < 1:
return {
'text': 'Devices with ip %s does not exist' % dev_ip
}
else:
device_down = possible_devices[0]
device_down = Device.objects.filter(ip_address=dev_ip).first()
if device_down is None:
return {'text': 'Devices with ip %s does not exist' % dev_ip}
if not device_down.is_noticeable:
return {'text': 'Notification for %s is unnecessary' % device_down.ip_address}
recipients = device_down.user_group.profiles.all()
recipients = UserProfile.objects.get_profiles_by_group(device_down.group.pk)
names = list()
if dev_status == 'UP':

37
dialing_app/migrations/0001_initial.py

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2017-05-30 13:33
# Generated by Django 1.11 on 2018-02-26 00:20
from __future__ import unicode_literals
from django.db import migrations, models
@ -38,6 +38,41 @@ class Migration(migrations.Migration):
],
options={
'db_table': 'cdr',
'ordering': ['-calldate'],
'managed': False,
},
),
migrations.CreateModel(
name='SMSModel',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('when', models.DateTimeField(auto_now_add=True)),
('who', models.CharField(max_length=32)),
('dev', models.CharField(max_length=20)),
('text', models.CharField(max_length=255)),
],
options={
'verbose_name': 'SMS',
'verbose_name_plural': 'SMS',
'db_table': 'sms',
'ordering': ['-when'],
'permissions': (('can_view_sms', 'Can view sms'),),
},
),
migrations.CreateModel(
name='SMSOut',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('when', models.DateTimeField(auto_now_add=True, verbose_name='When')),
('dst', models.CharField(max_length=16, verbose_name='Telephone')),
('text', models.CharField(max_length=255, verbose_name='Text')),
('status', models.CharField(choices=[('nw', 'New'), ('st', 'Sent'), ('fd', 'Failed')], default='nw', max_length=2, verbose_name='Status')),
],
options={
'verbose_name': 'Out SMS',
'verbose_name_plural': 'Out SMS',
'db_table': 'out_sms',
'permissions': (('can_view_sms', 'Can view sms'), ('can_send_sms', 'Can send sms')),
},
),
]

35
dialing_app/migrations/0002_auto_20171229_1353.py

@ -1,35 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2017-12-29 13:53
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dialing_app', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='SMSModel',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('when', models.DateTimeField(auto_now_add=True)),
('who', models.CharField(max_length=32)),
('dev', models.CharField(max_length=20)),
('text', models.CharField(max_length=255)),
],
options={
'verbose_name': 'SMS',
'verbose_name_plural': 'SMS',
'db_table': 'sms',
'permissions': (('can_view_sms', 'Can view sms'),),
},
),
migrations.AlterModelOptions(
name='asteriskcdr',
options={'managed': False},
),
]

31
dialing_app/migrations/0003_smsout.py

@ -1,31 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2018-01-04 18:16
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dialing_app', '0002_auto_20171229_1353'),
]
operations = [
migrations.CreateModel(
name='SMSOut',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('when', models.DateTimeField(auto_now_add=True, verbose_name='When')),
('dst', models.CharField(max_length=16, verbose_name='Telephone')),
('text', models.CharField(max_length=255, verbose_name='Text')),
('status', models.CharField(choices=[('nw', 'New'), ('st', 'Sent'), ('fd', 'Failed')], default='nw', max_length=2, verbose_name='Status')),
],
options={
'verbose_name': 'Out SMS',
'verbose_name_plural': 'Out SMS',
'db_table': 'out_sms',
'permissions': (('can_view_sms', 'Can view sms'), ('can_send_sms', 'Can send sms')),
},
),
]

10
dialing_app/views.py

@ -3,7 +3,8 @@ from subprocess import run
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from django.contrib.gis.shortcuts import render_to_text
from django.shortcuts import render, redirect
from django.db import ProgrammingError
from django.shortcuts import redirect
from django.utils.translation import gettext_lazy as _
from django.utils.decorators import method_decorator
from django.views.generic import ListView
@ -28,6 +29,13 @@ class LastCallsListView(BaseListView):
context_object_name = 'logs'
queryset = AsteriskCDR.objects.exclude(userfield='request')
def get(self, request, *args, **kwargs):
try:
return super(LastCallsListView, self).get(request, *args, **kwargs)
except ProgrammingError as e:
messages.error(self.request, e)
return redirect('abonapp:group_list')
def get_context_data(self, **kwargs):
context = super(LastCallsListView, self).get_context_data(**kwargs)
context['title'] = _('Last calls')

33
djing/auth_backends.py

@ -0,0 +1,33 @@
from django.contrib.auth.backends import ModelBackend
from accounts_app.models import BaseAccount, UserProfile
from abonapp.models import Abon
class CustomAuthBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
if username is None:
username = kwargs.get(BaseAccount.USERNAME_FIELD)
try:
user = BaseAccount._default_manager.get_by_natural_key(username)
if user.check_password(password):
if user.is_staff:
auser = UserProfile.objects.get_by_natural_key(username)
else:
auser = Abon.objects.get_by_natural_key(username)
if self.user_can_authenticate(auser):
return auser
except BaseAccount.DoesNotExist:
# Run the default password hasher once to reduce the timing
# difference between an existing and a non-existing user (#20760).
BaseAccount().set_password(password)
def get_user(self, user_id):
try:
user = BaseAccount._default_manager.get(pk=user_id)
if user.is_staff:
user = UserProfile._default_manager.get(pk=user_id)
else:
user = Abon._default_manager.get(pk=user_id)
except BaseAccount.DoesNotExist:
return None
return user if self.user_can_authenticate(user) else None

10
djing/settings.py

@ -19,13 +19,14 @@ from django.urls import reverse_lazy
SECRET_KEY = local_settings.SECRET_KEY
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = local_settings.DEBUG or False
DEBUG = local_settings.DEBUG
ALLOWED_HOSTS = local_settings.ALLOWED_HOSTS
# required for django-guardian
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend', # default
'djing.auth_backends.CustomAuthBackend',
#'django.contrib.auth.backends.ModelBackend', # default
'guardian.backends.ObjectPermissionBackend'
)
@ -51,11 +52,11 @@ INSTALLED_APPS = [
'chatbot',
'msg_app',
'dialing_app',
'group_app',
'guardian',
'pinax_theme_bootstrap',
'bootstrapform',
'bootstrap3'
]
MIDDLEWARE = [
@ -78,12 +79,11 @@ TEMPLATES = [
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
#'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'taskapp.context_proc.get_active_tasks_count',
'global_context_processors.context_processor_additional_profile',
'msg_app.context_processors.get_new_messages_count'
],
'libraries': {

1
djing/urls.py

@ -17,6 +17,7 @@ urlpatterns = [
url(r'^client/', include('clientsideapp.urls', namespace='client_side')),
url(r'^msg/', include('msg_app.urls', namespace='msg_app')),
url(r'^dialing/', include('dialing_app.urls', namespace='dialapp')),
url(r'^groups/', include('group_app.urls', namespace='group_app')),
url(r'^admin/', admin.site.urls)
]

12
global_context_processors.py

@ -1,12 +0,0 @@
# -*- coding: utf-8 -*-
from django.shortcuts import get_object_or_404
from abonapp.models import Abon
from django.conf import settings
# От сюда можно получать на клиентской стороне профиль абонента
def context_processor_additional_profile(request):
if request.user.is_staff or request.user.is_anonymous():
return {'subscriber': request.user, 'FILE_UPLOAD_MAX_MEMORY_SIZE': settings.FILE_UPLOAD_MAX_MEMORY_SIZE}
else:
return {'subscriber': get_object_or_404(Abon, id=request.user.pk), 'FILE_UPLOAD_MAX_MEMORY_SIZE': settings.FILE_UPLOAD_MAX_MEMORY_SIZE}

0
group_app/__init__.py

4
group_app/admin.py

@ -0,0 +1,4 @@
from django.contrib import admin
from . import models
admin.site.register(models.Group)

5
group_app/apps.py

@ -0,0 +1,5 @@
from django.apps import AppConfig
class GroupAppConfig(AppConfig):
name = 'group_app'

8
group_app/forms.py

@ -0,0 +1,8 @@
from django import forms
from . import models
class GroupForm(forms.ModelForm):
class Meta:
model = models.Group
fields = ['title']

69
group_app/locale/ru/LC_MESSAGES/django.po

@ -0,0 +1,69 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2018-02-26 01:47+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Dmitry Novikov <nerosketch@gmail.com>\n"
"Language: ru\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n"
"%100>=11 && n%100<=14)? 2 : 3);\n"
#: models.py:7
msgid "Title"
msgstr "Название"
#: models.py:16
msgid "Can view group"
msgstr "Может видеть группы"
#: models.py:18
msgid "Group"
msgstr "Группа"
#: models.py:19 templates/group_app/ext.html:7 templates/group_app/ext.html:8
#: templates/group_app/ext.html:14 templates/group_app/group_list.html:7
#: templates/group_app/group_list.html:10
msgid "Groups"
msgstr "Группы"
#: templates/group_app/add_group.html:9 templates/group_app/group_list.html:49
msgid "Add group"
msgstr "Добавить группу"
#: templates/group_app/add_group.html:16
msgid "Add"
msgstr "Добавить"
#: templates/group_app/edit_group.html:4 templates/group_app/edit_group.html:9
msgid "Edit group"
msgstr "Изменить группу"
#: templates/group_app/edit_group.html:16
msgid "Save"
msgstr "Сохранить"
#: templates/group_app/group_list.html:41
msgid "Groups was not found"
msgstr "Не найдено ни одной группы"
#: views.py:31
msgid "Group changes has been saved"
msgstr "Изменения сохранены"
#: views.py:35 views.py:51
msgid "Please fix form errors"
msgstr "Исправьте ошибки формы"
#: views.py:47
msgid "New group are created"
msgstr "Новая группа успешно создана"

30
group_app/migrations/0001_initial.py

@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-02-26 00:20
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Group',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=127, unique=True, verbose_name='Title')),
],
options={
'verbose_name': 'Group',
'verbose_name_plural': 'Groups',
'db_table': 'groups',
'ordering': ['title'],
'permissions': (('can_view_group', 'Can view group'),),
},
),
]

0
group_app/migrations/__init__.py

23
group_app/models.py

@ -0,0 +1,23 @@
from django.utils.translation import gettext_lazy as _
from django.shortcuts import resolve_url
from django.db import models
class Group(models.Model):
title = models.CharField(_('Title'), max_length=127, unique=True)
def get_absolute_url(self):
url = resolve_url('group_app:edit', self.pk)
return url
class Meta:
db_table = 'groups'
permissions = (
('can_view_group', _('Can view group')),
)
verbose_name = _('Group')
verbose_name_plural = _('Groups')
ordering = ['title']
def __str__(self):
return self.title

20
group_app/templates/group_app/add_group.html

@ -0,0 +1,20 @@
{% extends request.is_ajax|yesno:'nullcont.htm,group_app/ext.html' %}
{% load i18n %}
{% load bootstrap3 %}
{% block active_text %}{% trans 'Add group' %}{% endblock %}
{% block content %}
<form action="{% url 'group_app:add' %}" method="post">{% csrf_token %}
<div class="modal-header success">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title"><span class="glyphicon glyphicon-info-sign"></span>{% trans 'Add group' %}</h4>
</div>
<div class="modal-body">
{% bootstrap_form form %}
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-success">
<span class="glyphicon glyphicon-plus"></span> {% trans 'Add' %}
</button>
</div>
</form>
{% endblock %}

20
group_app/templates/group_app/edit_group.html

@ -0,0 +1,20 @@
{% extends request.is_ajax|yesno:'nullcont.htm,group_app/ext.html' %}
{% load i18n %}
{% load bootstrap3 %}
{% block active_text %}{% trans 'Edit group' %}{% endblock %}
{% block content %}
<form action="{% url 'group_app:edit' object.pk %}" method="post">{% csrf_token %}
<div class="modal-header primary">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title"><span class="glyphicon glyphicon-info-sign"></span>{% trans 'Edit group' %}</h4>
</div>
<div class="modal-body">
{% bootstrap_form form %}
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">
<span class="glyphicon glyphicon-save"></span> {% trans 'Save' %}
</button>
</div>
</form>
{% endblock %}

23
group_app/templates/group_app/ext.html

@ -0,0 +1,23 @@
{% 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 'group_app:group_list' %}">{% trans 'Groups' %}</a></li>
<li class="active">{% block active_text %}{% trans 'Groups' %}{% endblock %}</li>
</ol>
{% include 'message_block.html' %}
<div class="page-header">
<h2>{% trans 'Groups' %}</h2>
</div>
<div class="panel panel-default">
<div class="panel-body">
{% block content %}{% endblock %}
</div>
</div>
{% endblock %}

59
group_app/templates/group_app/group_list.html

@ -0,0 +1,59 @@
{% extends 'base.html' %}
{% load i18n %}
{% block main %}
<ol class="breadcrumb">
<li><span class="glyphicon glyphicon-home"></span></li>
<li class="active">{% trans 'Groups' %}</li>
</ol>
<h3>{% trans 'Groups' %}</h3>
{% include 'message_block.html' %}
<div class="table-responsive">
<table class="table table-striped table-bordered">
<thead>
<tr>
<th width="50">#</th>
<th>
<a href="{% url 'group_app:group_list' %}?order_by=title&dir={{ dir|default:'down' }}">
{% trans 'Title' %}
</a>
{% if order_by == 'title' %}<span class="glyphicon glyphicon-filter"></span>{% endif %}
</th>
<th width="100">#</th>
</tr>
</thead>
<tbody>
{% for gr in groups %}
<tr>
<td>{{ gr.pk }}</td>
<td><a href="{% url 'group_app:edit' gr.pk %}" class="btn-modal">{{ gr.title }}</a></td>
<td class="btn-group">
<a href="{% url 'group_app:edit' gr.pk %}" class="btn btn-sm btn-default btn-modal">
<span class="glyphicon glyphicon-cog"></span>
</a>
</td>
</tr>
{% empty %}
<tr>
<td colspan="3"><a href="#">{% trans 'Groups was not found' %}</a></td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<td colspan="3" class="btn-group btn-group-sm">
<a href="{% url 'group_app:add' %}" class="btn btn-success btn-modal">
<span class="glyphicon glyphicon-plus"></span> <span class="hidden-xs">{% trans 'Add group' %}</span>
</a>
</td>
</tr>
</tfoot>
</table>
</div>
{% include 'pagination.html' %}
{% endblock %}

3
group_app/tests.py

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

10
group_app/urls.py

@ -0,0 +1,10 @@
from django.conf.urls import url
from . import views
app_name = 'group_app'
urlpatterns = [
url(r'^$', views.GroupListView.as_view(), name='group_list'),
url(r'^add/$', views.AddGroupView.as_view(), name='add'),
url(r'^(?P<group_id>\d{1,6})/edit/$', views.EditGroupView.as_view(), name='edit')
]

52
group_app/views.py

@ -0,0 +1,52 @@
from django.views.generic import ListView, UpdateView, CreateView
from django.contrib.auth.decorators import login_required
from django.utils.translation import gettext_lazy as _
from django.utils.decorators import method_decorator
from django.urls import reverse_lazy
from django.contrib import messages
from django.conf import settings
from . import models
from . import forms
@method_decorator(login_required, name='dispatch')
class GroupListView(ListView):
http_method_names = ['get']
paginate_by = getattr(settings, 'PAGINATION_ITEMS_PER_PAGE', 10)
template_name = 'group_app/group_list.html'
model = models.Group
context_object_name = 'groups'
@method_decorator(login_required, name='dispatch')
class EditGroupView(UpdateView):
http_method_names = ['get', 'post']
template_name = 'group_app/edit_group.html'
form_class = forms.GroupForm
model = models.Group
pk_url_kwarg = 'group_id'
success_url = reverse_lazy('group_app:group_list')
def form_valid(self, form):
messages.success(self.request, _('Group changes has been saved'))
return super(EditGroupView, self).form_valid(form)
def form_invalid(self, form):
messages.success(self.request, _('Please fix form errors'))
return super(EditGroupView, self).form_invalid(form)
@method_decorator(login_required, name='dispatch')
class AddGroupView(CreateView):
http_method_names = ['get', 'post']
template_name = 'group_app/add_group.html'
form_class = forms.GroupForm
success_url = reverse_lazy('group_app:group_list')
def form_valid(self, form):
messages.success(self.request, _('New group are created'))
return super(AddGroupView, self).form_valid(form)
def form_invalid(self, form):
messages.success(self.request, _('Please fix form errors'))
return super(AddGroupView, self).form_invalid(form)

66
locale/ru/LC_MESSAGES/django.po

@ -7,10 +7,9 @@
msgid ""
msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-01-26 19:22+0300\n"
"POT-Creation-Date: 2018-02-26 02:06+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Dmitry Novikov nerosketch@gmail.com\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: ru\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@ -19,37 +18,13 @@ msgstr ""
"%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n"
"%100>=11 && n%100<=14)? 2 : 3);\n"
#: agent/commands/dhcp.py:19
msgid "User settings is not dynamic"
msgstr ""
#: agent/commands/dhcp.py:29
#, python-format
msgid "User with device '%s' does not exist"
msgstr ""
#: agent/commands/dhcp.py:31
#, python-format
msgid "Device with mac %s not found"
msgstr ""
#: agent/commands/dhcp.py:33
#, python-format
msgid "Port %d on device with mac %s does not exist"
msgstr ""
#: cron.py:30
#, python-format
msgid "Service '%(service_name)s' has expired"
msgstr "Срок действия услуги '%(service_name)s' истёк"
#: djing/formfields.py:13
msgid "Enter a valid integer."
msgstr ""
msgstr "Введите число"
#: djing/formfields.py:22
msgid "Enter a valid MAC Address."
msgstr ""
msgstr "Введите мак адрес"
#: templates/403.html:5
msgid "Permission denied"
@ -67,3 +42,38 @@ msgstr "У вас нет прав просматривать эту страни
#: templates/403_for_modal.html:11
msgid "Close"
msgstr "Закрыть"
#: templates/base.html:28
msgid "Administrators"
msgstr "Сотрудники"
#: templates/base.html:35
msgid "Subscribers"
msgstr "Абоненты"
#: templates/base.html:41
msgid "Tasks"
msgstr "Задачи"
#: templates/base.html:48
msgid "Groups"
msgstr "Группы"
#: templates/base.html:54
msgid "Services"
msgstr "Тарифы"
#: templates/base.html:61
msgid "Map settings"
msgstr "Настройки карты"
#: templates/base.html:68
msgid "Messages"
msgstr "Сообщения"
#: templates/base.html:85
msgid "Devices"
msgstr "Устрофства"
#~ msgid "Service '%(service_name)s' has expired"
#~ msgstr "Срок действия услуги '%(service_name)s' истёк"

16
mapapp/migrations/0001_initial.py

@ -1,5 +1,7 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2017-01-25 16:02
# Generated by Django 1.11 on 2018-02-26 00:20
from __future__ import unicode_literals
from django.db import migrations, models
@ -8,6 +10,7 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
('devapp', '0001_initial'),
]
operations = [
@ -15,12 +18,17 @@ class Migration(migrations.Migration):
name='Dot',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=127)),
('latitude', models.FloatField()),
('longitude', models.FloatField()),
('title', models.CharField(max_length=127, verbose_name='Map point title')),
('latitude', models.FloatField(verbose_name='Latitude')),
('longitude', models.FloatField(verbose_name='Longitude')),
('attachment', models.FileField(blank=True, null=True, upload_to='map_attachments/%Y_%m_%d', verbose_name='Attachment')),
('devices', models.ManyToManyField(db_table='dot_device', to='devapp.Device', verbose_name='Devices')),
],
options={
'verbose_name': 'Map point',
'verbose_name_plural': 'Map points',
'db_table': 'dots',
'permissions': (('can_view', 'Can view'),),
},
),
]

45
mapapp/migrations/0002_auto_20171103_0006.py

@ -1,45 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2017-11-03 00:06
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('devapp', '0004_auto_20171103_0006'),
('mapapp', '0001_initial'),
]
operations = [
migrations.AlterModelOptions(
name='dot',
options={'permissions': (('can_view', 'Can view'),), 'verbose_name': 'Map dot', 'verbose_name_plural': 'Map dots'},
),
migrations.AddField(
model_name='dot',
name='attachment',
field=models.FileField(blank=True, null=True, upload_to='map_attachments/%Y_%m_%d', verbose_name='Attachment'),
),
migrations.AddField(
model_name='dot',
name='devices',
field=models.ManyToManyField(db_table='dot_device', to='devapp.Device', verbose_name='Devices'),
),
migrations.AlterField(
model_name='dot',
name='latitude',
field=models.FloatField(verbose_name='Latitude'),
),
migrations.AlterField(
model_name='dot',
name='longitude',
field=models.FloatField(verbose_name='Longitude'),
),
migrations.AlterField(
model_name='dot',
name='title',
field=models.CharField(max_length=127, verbose_name='Map point title'),
),
]

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save