Browse Source

1) Maked periodic pays.

2) Settings reformat.
3) more logs
devel
bashmak 8 years ago
parent
commit
62c3aa8118
  1. 2
      .gitignore
  2. 3
      README.md
  3. 6
      abonapp/forms.py
  4. 483
      abonapp/locale/ru/LC_MESSAGES/django.po
  5. 130
      abonapp/migrations/0004_auto_20180122_1732.py
  6. 65
      abonapp/models.py
  7. 11
      abonapp/pay_systems.py
  8. 36
      abonapp/templates/abonapp/modal_periodic_pay.html
  9. 20
      abonapp/templates/abonapp/payHistory.html
  10. 35
      abonapp/templates/abonapp/service.html
  11. 6
      abonapp/urls_abon.py
  12. 59
      abonapp/views.py
  13. 8
      cron.py
  14. 52
      djing/local_settings.py.template
  15. 59
      djing/settings.py
  16. 1
      requirements.txt
  17. 44
      tariff_app/base_intr.py
  18. 42
      tariff_app/custom_tariffs.py
  19. 14
      tariff_app/forms.py
  20. 69
      tariff_app/migrations/0006_auto_20180122_1732.py
  21. 94
      tariff_app/models.py
  22. 67
      tariff_app/templates/tariff_app/editTarif.html
  23. 36
      tariff_app/templates/tariff_app/ext.html
  24. 51
      tariff_app/templates/tariff_app/periodic_pays/add_edit.html
  25. 62
      tariff_app/templates/tariff_app/periodic_pays/list.html
  26. 12
      tariff_app/templates/tariff_app/tarifs.html
  27. 6
      tariff_app/urls.py
  28. 45
      tariff_app/views.py
  29. 6
      taskapp/templates/taskapp/tasklist.html
  30. 4
      taskapp/templates/taskapp/tasklist_all.html
  31. 4
      taskapp/templates/taskapp/tasklist_failed.html
  32. 4
      taskapp/templates/taskapp/tasklist_finish.html
  33. 4
      taskapp/templates/taskapp/tasklist_own.html

2
.gitignore

@ -5,7 +5,7 @@ media/*
media/min/* media/min/*
~*/migrations/00*.py ~*/migrations/00*.py
.idea/ .idea/
djing/settings.py
djing/local_settings.py
agent/settings.py agent/settings.py
gmap/fixtures gmap/fixtures
*.sqlite3 *.sqlite3

3
README.md

@ -3,6 +3,9 @@
Сейчас идёт тестирвоание работы на Mikrotik, функционал пока минимальный, т.к. пишу в свободное время. Работа планируется в реальных условиях и на реальных абонентах. Сейчас идёт тестирвоание работы на Mikrotik, функционал пока минимальный, т.к. пишу в свободное время. Работа планируется в реальных условиях и на реальных абонентах.
Использовано python 3, django 1.11, bootstrap 3, и другое в файле requirements.txt Использовано python 3, django 1.11, bootstrap 3, и другое в файле requirements.txt
P.S. Возможно понадобится **Python 3.5** и выше из-за указания статических типов. [typing — Support for type hints](https://docs.python.org/3/library/typing.html).
Вы можете использовать билиотеку [mypy](http://www.mypy-lang.org/) для старших версий python.
## Содержание ## Содержание
* [Установка](./docs/install.md) * [Установка](./docs/install.md)
* [Разработка расширений](./docs/dev.md) * [Разработка расширений](./docs/dev.md)

6
abonapp/forms.py

@ -150,3 +150,9 @@ class AdditionalTelephoneForm(forms.ModelForm):
}), }),
'owner_name': forms.TextInput(attrs={'class': 'form-control', 'required':''}) 'owner_name': forms.TextInput(attrs={'class': 'form-control', 'required':''})
} }
class PeriodicPayForIdForm(forms.ModelForm):
class Meta:
model = models.PeriodicPayForId
exclude = ['account']

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

@ -6,10 +6,8 @@
#, fuzzy #, fuzzy
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-12-27 14:08+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Report-Msgid-Bugs-To: nerosketch@gmail.com\n"
"POT-Creation-Date: 2018-01-22 17:58+0300\n"
"Last-Translator: Dmitry Novikov nerosketch@gmail.com\n" "Last-Translator: Dmitry Novikov nerosketch@gmail.com\n"
"Language: ru\n" "Language: ru\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@ -19,7 +17,13 @@ msgstr ""
"%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (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" "%100>=11 && n%100<=14)? 2 : 3);\n"
#: templates/abonapp/peoples.html:38 templates/abonapp/viewAbon.html.py:32
#: forms.py:45 templates/abonapp/addAbon.html:21
#: templates/abonapp/viewAbon.html:28
msgid "login"
msgstr "Логин"
#: forms.py:58 templates/abonapp/peoples.html:38
#: templates/abonapp/viewAbon.html:32
msgid "fio" msgid "fio"
msgstr "ФИО" msgstr "ФИО"
@ -27,15 +31,15 @@ msgstr "ФИО"
msgid "telephone placeholder" msgid "telephone placeholder"
msgstr "+[7,8,9,3] и 10,11 цифр" msgstr "+[7,8,9,3] и 10,11 цифр"
#: models.py:30
#: models.py:29
msgid "Can view subscriber group" msgid "Can view subscriber group"
msgstr "Может просматривать группу абонентов" msgstr "Может просматривать группу абонентов"
#: models.py:32
#: models.py:31
msgid "Abon group" msgid "Abon group"
msgstr "Группа абонентов" msgstr "Группа абонентов"
#: models.py:33
#: models.py:32
msgid "Abon groups" msgid "Abon groups"
msgstr "Группы абонентов" msgstr "Группы абонентов"
@ -43,150 +47,194 @@ msgstr "Группы абонентов"
msgid "Can view subscriber logs" msgid "Can view subscriber logs"
msgstr "Может видеть логи абонента" msgstr "Может видеть логи абонента"
#: models.py:82
#: models.py:83
msgid "finish service perm" msgid "finish service perm"
msgstr "Снятие со счёта средств" msgstr "Снятие со счёта средств"
#: models.py:84
#: models.py:85
msgid "Abon service" msgid "Abon service"
msgstr "Услуга абонента" msgstr "Услуга абонента"
#: models.py:85
#: models.py:86
msgid "Abon services" msgid "Abon services"
msgstr "Услуги абонентов" msgstr "Услуги абонентов"
#: models.py:97 templates/abonapp/addAbon.html.py:59
#: templates/abonapp/editAbon.html:64 templates/abonapp/peoples.html.py:44
#: templates/abonapp/viewAbon.html:40
#: models.py:99 models.py:159 templates/abonapp/addAbon.html:59
#: templates/abonapp/peoples.html:44 templates/abonapp/viewAbon.html:40
msgid "Street" msgid "Street"
msgstr "Улица" msgstr "Улица"
#: models.py:98 templates/abonapp/peoples.html.py:148
#: models.py:100 templates/abonapp/peoples.html:148
msgid "Streets" msgid "Streets"
msgstr "Улицы" msgstr "Улицы"
#: models.py:103
#: models.py:106
msgid "Digital field" msgid "Digital field"
msgstr "Цифровое поле" msgstr "Цифровое поле"
#: models.py:104
#: models.py:107
msgid "Text field" msgid "Text field"
msgstr "Текстовое поле" msgstr "Текстовое поле"
#: models.py:105
#: models.py:108
msgid "Floating field" msgid "Floating field"
msgstr "Дробное с плавающей точкой" msgstr "Дробное с плавающей точкой"
#: models.py:106 templates/abonapp/editAbon.html.py:50
#: models.py:109 templates/abonapp/editAbon.html:39
#: templates/abonapp/viewAbon.html:50 #: templates/abonapp/viewAbon.html:50
msgid "Ip Address" msgid "Ip Address"
msgstr "IP Адрес" msgstr "IP Адрес"
#: models.py:131
#: models.py:134
msgid "Double invalid value" msgid "Double invalid value"
msgstr "Введите число с плавающей запятой" msgstr "Введите число с плавающей запятой"
#: models.py:172
#: models.py:155 templates/abonapp/addAbon.html:45
#: templates/abonapp/modal_addstreet.html:21 templates/abonapp/viewAbon.html:16
msgid "User group"
msgstr "Группа"
#: models.py:158 templates/abonapp/addAbon.html:53
#: templates/abonapp/addInvoice.html:40 templates/abonapp/debtors.html:22
#: templates/abonapp/invoiceForPayment.html:23 templates/abonapp/log.html:20
#: templates/abonapp/payHistory.html:13 templates/abonapp/task_log.html:10
msgid "Comment"
msgstr "Комментарий"
#: models.py:160 templates/abonapp/viewAbon.html:46
msgid "House"
msgstr "Дом"
#: models.py:175
msgid "Buy service perm" msgid "Buy service perm"
msgstr "Покупка тарифа абоненту" msgstr "Покупка тарифа абоненту"
#: models.py:173
#: models.py:176
msgid "Can view passport" msgid "Can view passport"
msgstr "Может просматривать паспортные данные" msgstr "Может просматривать паспортные данные"
#: models.py:174
#: models.py:177
msgid "fill account" msgid "fill account"
msgstr "Пополнение счёта" msgstr "Пополнение счёта"
#: models.py:175
#: models.py:178
msgid "Can ping" msgid "Can ping"
msgstr "Может пинговать" msgstr "Может пинговать"
#: models.py:178
#: models.py:182
msgid "Abon" msgid "Abon"
msgstr "Абонент" msgstr "Абонент"
#: models.py:179
#: models.py:183
msgid "Abons" msgid "Abons"
msgstr "Абоненты" msgstr "Абоненты"
#: models.py:204
#: models.py:211
msgid "User that is no staff can not buy admin services" msgid "User that is no staff can not buy admin services"
msgstr "" msgstr ""
"Пользователь, который не является персоналом не может покупать услуги для " "Пользователь, который не является персоналом не может покупать услуги для "
"внутренних нужд" "внутренних нужд"
#: models.py:209
#: models.py:216
msgid "That service already activated" msgid "That service already activated"
msgstr "Эта услуга уже подключена" msgstr "Эта услуга уже подключена"
#: models.py:212
#: models.py:219
msgid "Service already activated" msgid "Service already activated"
msgstr "Услуга уже подключена" msgstr "Услуга уже подключена"
#: models.py:216
#: models.py:223
msgid "not enough money" msgid "not enough money"
msgstr "Не хватает денег на счету" msgstr "Не хватает денег на счету"
#: models.py:231
#: models.py:238
msgid "Buy service default log" msgid "Buy service default log"
msgstr "Покупка тарифного плана через админку" msgstr "Покупка тарифного плана через админку"
#: models.py:272
#: models.py:279
msgid "Ip address already exist" msgid "Ip address already exist"
msgstr "Такой ip уже у кого-то есть" msgstr "Такой ip уже у кого-то есть"
#: models.py:285 models.py:286
#: models.py:292 models.py:293
msgid "Passport Info" msgid "Passport Info"
msgstr "Паспортные данные" msgstr "Паспортные данные"
#: models.py:315
#: models.py:322
msgid "Can view invoice for payment" msgid "Can view invoice for payment"
msgstr "Может видеть назначенные платежи" msgstr "Может видеть назначенные платежи"
#: models.py:317
#: models.py:324
msgid "Debt" msgid "Debt"
msgstr "Квитанция (долг)" msgstr "Квитанция (долг)"
#: models.py:318 templates/abonapp/invoiceForPayment.html.py:10
#: templates/abonapp/payHistory.html:44
#: models.py:325 templates/abonapp/invoiceForPayment.html:10
#: templates/abonapp/payHistory.html:50
msgid "Debts" msgid "Debts"
msgstr "Квитанции (долги)" msgstr "Квитанции (долги)"
#: models.py:380 templates/abonapp/addAbon.html.py:37
#: templates/abonapp/editAbon.html:30 templates/abonapp/modal_add_phone.html:10
#: models.py:348
msgid "Trade point"
msgstr "Терминал"
#: models.py:349
msgid "Receipt number"
msgstr "Номер пас."
#: models.py:391 templates/abonapp/addAbon.html:37
#: templates/abonapp/editAbon.html:19 templates/abonapp/modal_add_phone.html:10
#: templates/abonapp/modal_additional_telephones.html:14 #: templates/abonapp/modal_additional_telephones.html:14
#: templates/abonapp/modal_phonebook.html:10 templates/abonapp/peoples.html:54 #: templates/abonapp/modal_phonebook.html:10 templates/abonapp/peoples.html:54
#: templates/abonapp/viewAbon.html.py:36
#: templates/abonapp/viewAbon.html:36
msgid "Telephone" msgid "Telephone"
msgstr "Телефон" msgstr "Телефон"
#: models.py:393
#: models.py:404
msgid "Can view additional telephones" msgid "Can view additional telephones"
msgstr "Может видеть дополнительные телефоны" msgstr "Может видеть дополнительные телефоны"
#: models.py:395
#: models.py:406
msgid "Additional telephone" msgid "Additional telephone"
msgstr "Дополнительный телефон" msgstr "Дополнительный телефон"
#: models.py:396 templates/abonapp/editAbon.html.py:38
#: models.py:407 templates/abonapp/editAbon.html:27
#: templates/abonapp/modal_additional_telephones.html:5 #: templates/abonapp/modal_additional_telephones.html:5
msgid "Additional telephones" msgid "Additional telephones"
msgstr "Дополнительные телефоны" msgstr "Дополнительные телефоны"
#: templates/abonapp/addAbon.html:7 templates/abonapp/addGroup.html.py:7
#: models.py:411 templates/abonapp/service.html:122
msgid "Periodic pay"
msgstr "Периодический платёж"
#: models.py:412
msgid "Last pay time"
msgstr "Последний платёж"
#: models.py:413
msgid "Next time to pay"
msgstr "Следующий платёж"
#: models.py:414
msgid "Account"
msgstr "Учётная запись"
#: models.py:432
#, python-format
msgid "Charge for \"%(service)s\""
msgstr "Плата за \"%(service)s\""
#: templates/abonapp/addAbon.html:7 templates/abonapp/addGroup.html:7
#: templates/abonapp/addInvoice.html:7 templates/abonapp/buy_tariff.html:7 #: templates/abonapp/addInvoice.html:7 templates/abonapp/buy_tariff.html:7
#: templates/abonapp/debtors.html.py:8 templates/abonapp/group_list.html:8
#: templates/abonapp/debtors.html:8 templates/abonapp/group_list.html:8
#: templates/abonapp/group_list.html:11 templates/abonapp/group_list.html:47 #: templates/abonapp/group_list.html:11 templates/abonapp/group_list.html:47
#: templates/abonapp/group_tariffs.html:7 #: templates/abonapp/group_tariffs.html:7
#: templates/abonapp/invoiceForPayment.html:7 templates/abonapp/log.html:7 #: templates/abonapp/invoiceForPayment.html:7 templates/abonapp/log.html:7
#: templates/abonapp/peoples.html.py:9 templates/abonapp/peoples.html:134
#: templates/abonapp/service.html.py:103 templates/abonapp/service.html:111
#: templates/abonapp/peoples.html:9 templates/abonapp/peoples.html:134
#: templates/abonapp/service.html:103 templates/abonapp/service.html:111
msgid "User groups" msgid "User groups"
msgstr "Группы абонентов" msgstr "Группы абонентов"
#: templates/abonapp/addAbon.html:9 templates/abonapp/addAbon.html.py:16
#: templates/abonapp/peoples.html:119 templates/abonapp/peoples.html.py:131
#: templates/abonapp/addAbon.html:9 templates/abonapp/addAbon.html:16
#: templates/abonapp/peoples.html:119 templates/abonapp/peoples.html:131
msgid "Add abon" msgid "Add abon"
msgstr "Добавить абонента" msgstr "Добавить абонента"
@ -194,38 +242,26 @@ msgstr "Добавить абонента"
msgid "Long name" msgid "Long name"
msgstr "Фамилия и Имя" msgstr "Фамилия и Имя"
#: templates/abonapp/addAbon.html:45 templates/abonapp/editAbon.html.py:87
#: templates/abonapp/modal_addstreet.html:21 templates/abonapp/viewAbon.html:16
msgid "User group"
msgstr "Группа"
#: templates/abonapp/addAbon.html:53 templates/abonapp/addInvoice.html.py:40
#: templates/abonapp/debtors.html:22 templates/abonapp/editAbon.html.py:108
#: templates/abonapp/invoiceForPayment.html:23 templates/abonapp/log.html:20
#: templates/abonapp/payHistory.html.py:13 templates/abonapp/task_log.html:10
msgid "Comment"
msgstr "Комментарий"
#: templates/abonapp/addAbon.html:67 templates/abonapp/peoples.html.py:50
#: templates/abonapp/addAbon.html:67 templates/abonapp/peoples.html:50
msgid "Apartment" msgid "Apartment"
msgstr "Квартира" msgstr "Квартира"
#: templates/abonapp/addAbon.html:76 templates/abonapp/editAbon.html.py:94
#: templates/abonapp/addAbon.html:76 templates/abonapp/editAbon.html:58
#: templates/abonapp/viewAbon.html:54 #: templates/abonapp/viewAbon.html:54
msgid "Password" msgid "Password"
msgstr "Пароль" msgstr "Пароль"
#: templates/abonapp/addAbon.html:91 templates/abonapp/addGroup.html.py:29
#: templates/abonapp/addAbon.html:91 templates/abonapp/addGroup.html:29
#: templates/abonapp/addInvoice.html:46 templates/abonapp/buy_tariff.html:71 #: templates/abonapp/addInvoice.html:46 templates/abonapp/buy_tariff.html:71
#: templates/abonapp/editAbon.html:117 templates/abonapp/editAbon.html:197
#: templates/abonapp/editAbon.html:242 templates/abonapp/group_tariffs.html:29
#: templates/abonapp/editAbon.html:76 templates/abonapp/editAbon.html:165
#: templates/abonapp/editAbon.html:210 templates/abonapp/group_tariffs.html:29
#: templates/abonapp/modal_dev.html:31 #: templates/abonapp/modal_dev.html:31
#: templates/abonapp/modal_editstreet.html:30 #: templates/abonapp/modal_editstreet.html:30
#: templates/abonapp/passport_view.html:49 #: templates/abonapp/passport_view.html:49
msgid "Save" msgid "Save"
msgstr "Сохранить" msgstr "Сохранить"
#: templates/abonapp/addAbon.html:94 templates/abonapp/addGroup.html.py:32
#: templates/abonapp/addAbon.html:94 templates/abonapp/addGroup.html:32
#: templates/abonapp/addInvoice.html:49 templates/abonapp/buy_tariff.html:74 #: templates/abonapp/addInvoice.html:49 templates/abonapp/buy_tariff.html:74
#: templates/abonapp/group_tariffs.html:29 #: templates/abonapp/group_tariffs.html:29
#: templates/abonapp/modal_abonamount.html:25 #: templates/abonapp/modal_abonamount.html:25
@ -234,10 +270,11 @@ msgstr "Сохранить"
#: templates/abonapp/modal_dev.html:34 #: templates/abonapp/modal_dev.html:34
#: templates/abonapp/modal_editstreet.html:33 #: templates/abonapp/modal_editstreet.html:33
#: templates/abonapp/modal_extra_field.html:40 #: templates/abonapp/modal_extra_field.html:40
#: templates/abonapp/modal_periodic_pay.html:30
msgid "Reset" msgid "Reset"
msgstr "Сбросить" msgstr "Сбросить"
#: templates/abonapp/addGroup.html:8 templates/abonapp/addGroup.html.py:15
#: templates/abonapp/addGroup.html:8 templates/abonapp/addGroup.html:15
#: templates/abonapp/group_list.html:63 #: templates/abonapp/group_list.html:63
msgid "Add group" msgid "Add group"
msgstr "Добавьте группу абонентов" msgstr "Добавьте группу абонентов"
@ -255,7 +292,7 @@ msgstr "Добавить квитанцию"
msgid "Add receipt for" msgid "Add receipt for"
msgstr "Добавьте платёж на оплату для" msgstr "Добавьте платёж на оплату для"
#: templates/abonapp/addInvoice.html:25 templates/abonapp/debtors.html.py:21
#: templates/abonapp/addInvoice.html:25 templates/abonapp/debtors.html:21
msgid "Sum of pay" msgid "Sum of pay"
msgstr "Сумма для платежа" msgstr "Сумма для платежа"
@ -265,7 +302,7 @@ msgid "Pay status"
msgstr "Статус оплаты" msgstr "Статус оплаты"
#: templates/abonapp/buy_tariff.html:10 templates/abonapp/buy_tariff.html:26 #: templates/abonapp/buy_tariff.html:10 templates/abonapp/buy_tariff.html:26
#: templates/abonapp/service.html.py:74
#: templates/abonapp/service.html:74
msgid "Pick a service" msgid "Pick a service"
msgstr "Заказать услугу" msgstr "Заказать услугу"
@ -273,14 +310,14 @@ msgstr "Заказать услугу"
msgid "Pick a service for" msgid "Pick a service for"
msgstr "Купить новую услугу (заказать тариф) для" msgstr "Купить новую услугу (заказать тариф) для"
#: templates/abonapp/buy_tariff.html:18 templates/abonapp/debtors.html.py:20
#: templates/abonapp/log.html:19 templates/abonapp/payHistory.html.py:9
#: templates/abonapp/buy_tariff.html:18 templates/abonapp/debtors.html:20
#: templates/abonapp/log.html:19 templates/abonapp/payHistory.html:9
#: templates/abonapp/peoples.html:25 #: templates/abonapp/peoples.html:25
msgid "Sub" msgid "Sub"
msgstr "Абонент" msgstr "Абонент"
#: templates/abonapp/buy_tariff.html:36 templates/abonapp/group_tariffs.html:24 #: templates/abonapp/buy_tariff.html:36 templates/abonapp/group_tariffs.html:24
#: templates/abonapp/service.html:31 templates/abonapp/service.html.py:96
#: templates/abonapp/service.html:31 templates/abonapp/service.html:96
msgid "currency" msgid "currency"
msgstr "руб" msgstr "руб"
@ -293,10 +330,19 @@ msgid "Attach serices to groups"
msgstr "Привязать услуги к группам" msgstr "Привязать услуги к группам"
#: templates/abonapp/charts.html:9 #: templates/abonapp/charts.html:9
msgid "Graph of use"
msgstr "График использования"
#, python-format
msgid "Graph of use by %(wantdate_d)s"
msgstr "График использования за %(wantdate_d)s"
#: templates/abonapp/charts.html:17
msgid "Show graph by date"
msgstr "Показать график по дате"
#: templates/abonapp/charts.html:20
msgid "Choose a date"
msgstr "Выберите дату"
#: templates/abonapp/charts.html:44 templates/abonapp/charts.html.py:56
#: templates/abonapp/charts.html:55
msgid "Static info was Not found" msgid "Static info was Not found"
msgstr "Статистика не найдена" msgstr "Статистика не найдена"
@ -314,7 +360,7 @@ msgstr "Народ, у которого есть неоплаченные усл
msgid "Date of make" msgid "Date of make"
msgstr "Дата создания" msgstr "Дата создания"
#: templates/abonapp/debtors.html:24 templates/abonapp/log.html.py:22
#: templates/abonapp/debtors.html:24 templates/abonapp/log.html:22
#: templates/abonapp/task_log.html:8 #: templates/abonapp/task_log.html:8
msgid "Author" msgid "Author"
msgstr "Автор" msgstr "Автор"
@ -367,89 +413,92 @@ msgstr "Звонки не найдены"
msgid "Change subscriber" msgid "Change subscriber"
msgstr "Изменение абонента" msgstr "Изменение абонента"
#: templates/abonapp/editAbon.html:35
#: templates/abonapp/editAbon.html:24
msgid "Call to" msgid "Call to"
msgstr "Позвонить" msgstr "Позвонить"
#: templates/abonapp/editAbon.html:41 templates/abonapp/modal_add_phone.html:5
#: templates/abonapp/editAbon.html:30 templates/abonapp/modal_add_phone.html:5
msgid "Add telephone" msgid "Add telephone"
msgstr "Добавить номер телефона" msgstr "Добавить номер телефона"
#: templates/abonapp/editAbon.html:53 templates/abonapp/editAbon.html.py:155
#: templates/abonapp/editAbon.html:174 templates/abonapp/editAbon.html:220
#: templates/abonapp/peoples.html.py:90 templates/abonapp/peoples.html:92
#: templates/abonapp/viewAbon.html.py:18 templates/abonapp/viewAbon.html:29
#: templates/abonapp/viewAbon.html.py:33 templates/abonapp/viewAbon.html:37
#: templates/abonapp/viewAbon.html.py:42 templates/abonapp/viewAbon.html:47
#: templates/abonapp/viewAbon.html.py:51
#: templates/abonapp/editAbon.html:42 templates/abonapp/editAbon.html:125
#: templates/abonapp/editAbon.html:144 templates/abonapp/editAbon.html:188
#: templates/abonapp/peoples.html:90 templates/abonapp/peoples.html:92
#: templates/abonapp/viewAbon.html:18 templates/abonapp/viewAbon.html:29
#: templates/abonapp/viewAbon.html:33 templates/abonapp/viewAbon.html:37
#: templates/abonapp/viewAbon.html:42 templates/abonapp/viewAbon.html:47
#: templates/abonapp/viewAbon.html:51
msgid "Not assigned" msgid "Not assigned"
msgstr "&lt;Не назначен&gt;" msgstr "&lt;Не назначен&gt;"
#: templates/abonapp/editAbon.html:55
#: templates/abonapp/editAbon.html:44
msgid "Reset ip" msgid "Reset ip"
msgstr "Сбросить ip" msgstr "Сбросить ip"
#: templates/abonapp/editAbon.html:72 templates/abonapp/viewAbon.html.py:46
msgid "House"
msgstr "Дом"
#: templates/abonapp/editAbon.html:120 templates/abonapp/editAbon.html:121
#, fuzzy
#| msgid "Add debt"
#: templates/abonapp/editAbon.html:80 templates/abonapp/editAbon.html:81
msgid "Add new task" msgid "Add new task"
msgstr "Добавить квитанцию"
msgstr "Добавить задачу"
#: templates/abonapp/editAbon.html:145 templates/abonapp/modal_dev.html:13
#: templates/abonapp/editAbon.html:95
msgid "No have ip"
msgstr "Нет ip адреса"
#: templates/abonapp/editAbon.html:101
msgid "Send sms"
msgstr "Отправить смс"
#: templates/abonapp/editAbon.html:115 templates/abonapp/modal_dev.html:13
msgid "Select the device" msgid "Select the device"
msgstr "Выберите устройство" msgstr "Выберите устройство"
#: templates/abonapp/editAbon.html:152
#: templates/abonapp/editAbon.html:122
msgid "Device" msgid "Device"
msgstr "Устройство" msgstr "Устройство"
#: templates/abonapp/editAbon.html:155
#: templates/abonapp/editAbon.html:125
msgid "Mac Address" msgid "Mac Address"
msgstr "Мак" msgstr "Мак"
#: templates/abonapp/editAbon.html:159
#: templates/abonapp/editAbon.html:129
msgid "Remove clutch" msgid "Remove clutch"
msgstr "Удалить муфту" msgstr "Удалить муфту"
#: templates/abonapp/editAbon.html:163 templates/abonapp/modal_dev.html.py:6
#: templates/abonapp/editAbon.html:133 templates/abonapp/modal_dev.html:6
msgid "Add clutch" msgid "Add clutch"
msgstr "Добавить муфту" msgstr "Добавить муфту"
#: templates/abonapp/editAbon.html:171
#: templates/abonapp/editAbon.html:141
msgid "Device port" msgid "Device port"
msgstr "Порт&nbsp;устройства" msgstr "Порт&nbsp;устройства"
#: templates/abonapp/editAbon.html:190
#: templates/abonapp/editAbon.html:158
msgid "Is dynamic network settings" msgid "Is dynamic network settings"
msgstr "Динамические настройки по dhcp" msgstr "Динамические настройки по dhcp"
#: templates/abonapp/editAbon.html:209
#: templates/abonapp/editAbon.html:177
msgid "Extra fields" msgid "Extra fields"
msgstr "Динамические записи" msgstr "Динамические записи"
#: templates/abonapp/editAbon.html:223
#: templates/abonapp/editAbon.html:191
#: templates/abonapp/modal_additional_telephones.html:24 #: templates/abonapp/modal_additional_telephones.html:24
msgid "Delete" msgid "Delete"
msgstr "Удалить" msgstr "Удалить"
#: templates/abonapp/editAbon.html:232 views.py:671
#: templates/abonapp/editAbon.html:200 views.py:681
msgid "Extra field does not exist" msgid "Extra field does not exist"
msgstr "Поле не найдено" msgstr "Поле не найдено"
#: templates/abonapp/editAbon.html:237
#: templates/abonapp/editAbon.html:205
#: templates/abonapp/modal_extra_field.html:6 #: templates/abonapp/modal_extra_field.html:6
msgid "Add extra field" msgid "Add extra field"
msgstr "Добавить новое динамическое поле" msgstr "Добавить новое динамическое поле"
#: templates/abonapp/editAbon.html:238
#: templates/abonapp/editAbon.html:206
#: templates/abonapp/modal_add_phone.html:27 #: templates/abonapp/modal_add_phone.html:27
#: templates/abonapp/modal_addstreet.html:30 #: templates/abonapp/modal_addstreet.html:30
#: templates/abonapp/modal_extra_field.html:37 #: templates/abonapp/modal_extra_field.html:37
#: templates/abonapp/peoples.html:130 templates/abonapp/peoples.html.py:157
#: templates/abonapp/modal_periodic_pay.html:27
#: templates/abonapp/peoples.html:130 templates/abonapp/peoples.html:157
msgid "Add" msgid "Add"
msgstr "Добавить" msgstr "Добавить"
@ -463,19 +512,22 @@ msgstr "Сумма денег за сутки"
#: templates/abonapp/fin_report.html:17 #: templates/abonapp/fin_report.html:17
#: templates/abonapp/invoiceForPayment.html:22 templates/abonapp/log.html:18 #: templates/abonapp/invoiceForPayment.html:22 templates/abonapp/log.html:18
#: templates/abonapp/payHistory.html.py:10 templates/abonapp/service.html:30
#: templates/abonapp/payHistory.html:10 templates/abonapp/service.html:30
msgid "Sum" msgid "Sum"
msgstr "Сумма" msgstr "Сумма"
#: templates/abonapp/fin_report.html:18 templates/abonapp/log.html.py:21
#: templates/abonapp/fin_report.html:18 templates/abonapp/log.html:21
msgid "Date" msgid "Date"
msgstr "Время" msgstr "Время"
#: templates/abonapp/fin_report.html:29 #: templates/abonapp/fin_report.html:29
#, fuzzy
#| msgid "Tasks not found"
msgid "Pays not found" msgid "Pays not found"
msgstr "Нет задач"
msgstr "Нет платежей"
#: templates/abonapp/fin_report.html:38
#: templates/abonapp/modal_phonebook.html:30
msgid "Export to csv"
msgstr "Сохранить в csv"
#: templates/abonapp/group_list.html:27 #: templates/abonapp/group_list.html:27
msgid "Number of subscribers" msgid "Number of subscribers"
@ -485,7 +537,7 @@ msgstr "Количество абонентов"
msgid "Groups was not found" msgid "Groups was not found"
msgstr "Ещё нет групп" msgstr "Ещё нет групп"
#: templates/abonapp/group_list.html:68 templates/abonapp/log.html.py:8
#: templates/abonapp/group_list.html:68 templates/abonapp/log.html:8
msgid "Subscribers actions" msgid "Subscribers actions"
msgstr "История действий абонентов" msgstr "История действий абонентов"
@ -582,19 +634,18 @@ msgstr "Тип динамического поля"
msgid "Field content" msgid "Field content"
msgstr "Содержимое динамического поля" msgstr "Содержимое динамического поля"
#: templates/abonapp/modal_periodic_pay.html:5
#: templates/abonapp/service.html:139
msgid "Add periodic pay"
msgstr "Добавить периодический платёж"
#: templates/abonapp/modal_phonebook.html:4 templates/abonapp/peoples.html:138 #: templates/abonapp/modal_phonebook.html:4 templates/abonapp/peoples.html:138
msgid "Phonebook" msgid "Phonebook"
msgstr "Телефонная книга" msgstr "Телефонная книга"
#: templates/abonapp/modal_phonebook.html:22 #: templates/abonapp/modal_phonebook.html:22
#, fuzzy
#| msgid "Telephone not found"
msgid "Telephone numbers not found" msgid "Telephone numbers not found"
msgstr "Телефон не найден"
#: templates/abonapp/modal_phonebook.html:30
msgid "Export to csv"
msgstr "Сохранить в csv"
msgstr "Номера телефонов не найдены"
#: templates/abonapp/passport_view.html:9 templates/abonapp/viewAbon.html:68 #: templates/abonapp/passport_view.html:9 templates/abonapp/viewAbon.html:68
msgid "Passport information" msgid "Passport information"
@ -616,15 +667,19 @@ msgstr "Кем выдан"
msgid "Date of acceptance" msgid "Date of acceptance"
msgstr "Дата выдачи" msgstr "Дата выдачи"
#: templates/abonapp/payHistory.html:27
#: templates/abonapp/payHistory.html:26
msgid "System"
msgstr "Система"
#: templates/abonapp/payHistory.html:33
msgid "Payment history is empty" msgid "Payment history is empty"
msgstr "История платежей пуста" msgstr "История платежей пуста"
#: templates/abonapp/payHistory.html:36 templates/abonapp/payHistory.html:40
#: templates/abonapp/payHistory.html:42 templates/abonapp/payHistory.html:46
msgid "Fill account" msgid "Fill account"
msgstr "Пополнить счёт" msgstr "Пополнить счёт"
#: templates/abonapp/payHistory.html:39
#: templates/abonapp/payHistory.html:45
msgid "Permission denied" msgid "Permission denied"
msgstr "Доступ запрещён" msgstr "Доступ запрещён"
@ -637,12 +692,10 @@ msgid "Last traffic"
msgstr "Траф." msgstr "Траф."
#: templates/abonapp/peoples.html:32 #: templates/abonapp/peoples.html:32
#, fuzzy
#| msgid "Ip Address"
msgid "Ip address" msgid "Ip address"
msgstr "IP Адрес" msgstr "IP Адрес"
#: templates/abonapp/peoples.html:55 templates/abonapp/service.html.py:16
#: templates/abonapp/peoples.html:55 templates/abonapp/service.html:16
#: templates/abonapp/service.html:75 #: templates/abonapp/service.html:75
msgid "Service" msgid "Service"
msgstr "Услуга" msgstr "Услуга"
@ -655,7 +708,7 @@ msgstr "Балланс"
msgid "Subscribers not found" msgid "Subscribers not found"
msgstr "Абоненты не найдены" msgstr "Абоненты не найдены"
#: templates/abonapp/peoples.html:135 templates/abonapp/service.html.py:104
#: templates/abonapp/peoples.html:135 templates/abonapp/service.html:104
msgid "Tariffs in groups" msgid "Tariffs in groups"
msgstr "Тарифы в группах" msgstr "Тарифы в группах"
@ -703,6 +756,10 @@ msgstr "Купить услугу"
msgid "Finish service" msgid "Finish service"
msgstr "Завершить услугу" msgstr "Завершить услугу"
#: templates/abonapp/service.html:68
msgid "Services for buy"
msgstr "Услуги для заказа"
#: templates/abonapp/service.html:76 #: templates/abonapp/service.html:76
msgid "Price" msgid "Price"
msgstr "Сумма" msgstr "Сумма"
@ -719,6 +776,18 @@ msgstr "Исходящая скорость"
msgid "Attach services to group" msgid "Attach services to group"
msgstr "Привязать услуги к этой группе" msgstr "Привязать услуги к этой группе"
#: templates/abonapp/service.html:127
msgid "Pay logic"
msgstr "Алгоритм платежа"
#: templates/abonapp/service.html:129
msgid "Last pay"
msgstr "Последний платёж"
#: templates/abonapp/service.html:135
msgid "Remove periodic pay"
msgstr "Удалить периодический платёж"
#: templates/abonapp/task_log.html:9 #: templates/abonapp/task_log.html:9
msgid "Recipients" msgid "Recipients"
msgstr "Исполнители" msgstr "Исполнители"
@ -743,6 +812,10 @@ msgstr "Нет задач"
msgid "View the subscriber" msgid "View the subscriber"
msgstr "Просмотр абонента" msgstr "Просмотр абонента"
#: templates/abonapp/viewAbon.html:22
msgid "Is active"
msgstr "Активен"
#: templates/abonapp/viewAbon.html:24 #: templates/abonapp/viewAbon.html:24
msgid "yes,no" msgid "yes,no"
msgstr "Да,Нет" msgstr "Да,Нет"
@ -751,8 +824,8 @@ msgstr "Да,Нет"
msgid "create group success msg" msgid "create group success msg"
msgstr "Группа успешно создана" msgstr "Группа успешно создана"
#: views.py:86 views.py:158 views.py:295 views.py:407 views.py:485 views.py:627
#: views.py:778 views.py:850
#: views.py:86 views.py:158 views.py:305 views.py:417 views.py:495 views.py:637
#: views.py:800 views.py:872
msgid "fix form errors" msgid "fix form errors"
msgstr "Некоторые поля заполнены не правильно, проверте ещё раз" msgstr "Некоторые поля заполнены не правильно, проверте ещё раз"
@ -790,181 +863,204 @@ msgstr "Счёт пополнен на %.2f"
msgid "I not know the account id" msgid "I not know the account id"
msgstr "Счёт успешно пополнен на %.2f" msgstr "Счёт успешно пополнен на %.2f"
#: views.py:293
#: views.py:267
msgid "User group id is not matches with group in url"
msgstr "Группа абонента не совпадает с группой указанной в url"
#: views.py:303
msgid "edit abon success msg" msgid "edit abon success msg"
msgstr "Абонент успешно изменён" msgstr "Абонент успешно изменён"
#: views.py:300
#: views.py:310
msgid "User device was not found" msgid "User device was not found"
msgstr "Пользовательское устройство не найдено" msgstr "Пользовательское устройство не найдено"
#: views.py:309
#: views.py:319
msgid "User has not have password, and cannot login" msgid "User has not have password, and cannot login"
msgstr "Для абонента не задан пароль, он не сможет войти в учётку" msgstr "Для абонента не задан пароль, он не сможет войти в учётку"
#: views.py:362
#: views.py:372
msgid "Receipt has been created" msgid "Receipt has been created"
msgstr "Квитанция на оплату была создана" msgstr "Квитанция на оплату была создана"
#: views.py:394
#: views.py:404
msgid "Tariff has been picked" msgid "Tariff has been picked"
msgstr "Тариф успешно выбран" msgstr "Тариф успешно выбран"
#: views.py:402
#: views.py:412
msgid "Tariff your picked does not exist" msgid "Tariff your picked does not exist"
msgstr "Тариф, который вы выбрали, не существует" msgstr "Тариф, который вы выбрали, не существует"
#: views.py:424
#: views.py:434
msgid "User has been detached from service" msgid "User has been detached from service"
msgstr "Абонент отвязан от услуги" msgstr "Абонент отвязан от услуги"
#: views.py:482
#: views.py:492
msgid "Passport information has been saved" msgid "Passport information has been saved"
msgstr "Информация о паспорте сохранена" msgstr "Информация о паспорте сохранена"
#: views.py:490 views.py:537 views.py:556 views.py:597
#: views.py:500 views.py:547 views.py:566 views.py:607
msgid "Abon does not exist" msgid "Abon does not exist"
msgstr "Абонент не найден" msgstr "Абонент не найден"
#: views.py:493
#: views.py:503
msgid "Passport info for the user does not exist" msgid "Passport info for the user does not exist"
msgstr "Для абонента не найдены паспортные данные" msgstr "Для абонента не найдены паспортные данные"
#: views.py:530
#: views.py:540
msgid "Device has successfully attached" msgid "Device has successfully attached"
msgstr "Устройство успешно прикреплено" msgstr "Устройство успешно прикреплено"
#: views.py:535
#: views.py:545
msgid "Device your selected already does not exist" msgid "Device your selected already does not exist"
msgstr "Устройство, выбранное вами, уже не существует" msgstr "Устройство, выбранное вами, уже не существует"
#: views.py:554
#: views.py:564
msgid "Device has successfully unattached" msgid "Device has successfully unattached"
msgstr "Устройство успешно откреплено" msgstr "Устройство успешно откреплено"
#: views.py:600
#: views.py:610
msgid "Group what you want doesn't exist" msgid "Group what you want doesn't exist"
msgstr "Указанная вами группа не найдена" msgstr "Указанная вами группа не найдена"
#: views.py:625
#: views.py:635
msgid "Extra field successfully created" msgid "Extra field successfully created"
msgstr "Динамичесое поле добавлено успешно" msgstr "Динамичесое поле добавлено успешно"
#: views.py:655
#: views.py:665
msgid "Extra fields has been saved" msgid "Extra fields has been saved"
msgstr "Динамические поля сохранены" msgstr "Динамические поля сохранены"
#: views.py:657
#: views.py:667
msgid "One or more extra fields has not been saved" msgid "One or more extra fields has not been saved"
msgstr "Поле или одно из полей не найдено" msgstr "Поле или одно из полей не найдено"
#: views.py:669
#: views.py:679
msgid "Extra field successfully deleted" msgid "Extra field successfully deleted"
msgstr "Динамическое поле успешно удалено" msgstr "Динамическое поле успешно удалено"
#: views.py:680
#: views.py:690
msgid "no ping" msgid "no ping"
msgstr "не пингуется" msgstr "не пингуется"
#: views.py:683
#: views.py:693
msgid "Ip not passed" msgid "Ip not passed"
msgstr "Ip адрес не передан" msgstr "Ip адрес не передан"
#: views.py:689 views.py:702
#: views.py:699 views.py:712
msgid "ping ok" msgid "ping ok"
msgstr "пингуется" msgstr "пингуется"
#: views.py:695
#: views.py:705
#, python-format #, python-format
msgid "IP Conflict! %(all)d/%(return)d results" msgid "IP Conflict! %(all)d/%(return)d results"
msgstr "IP Конфликт! ping %(all)d из %(return)d" msgstr "IP Конфликт! ping %(all)d из %(return)d"
#: views.py:697
#: views.py:707
#, python-format #, python-format
msgid "ok ping, %(all)d/%(return)d loses" msgid "ok ping, %(all)d/%(return)d loses"
msgstr "пингуется, %(all)d/%(return)d" msgstr "пингуется, %(all)d/%(return)d"
#: views.py:700
#: views.py:710
#, python-format #, python-format
msgid "no ping, %(all)d/%(return)d loses" msgid "no ping, %(all)d/%(return)d loses"
msgstr "не пингуется, %(all)d/%(return)d" msgstr "не пингуется, %(all)d/%(return)d"
#: views.py:743
#: views.py:753
msgid "Method is not POST" msgid "Method is not POST"
msgstr "Метод не POST" msgstr "Метод не POST"
#: views.py:759
#: views.py:767
#, python-format
msgid ""
"<a href='%(user_url)s'>%(user_name)s</a> already pinned to this port on this "
"device"
msgstr ""
"<a href='%(user_url)s'>%(user_name)s</a> уже привязан к этому порту на этом "
"устройстве"
#: views.py:781
msgid "User port has been saved" msgid "User port has been saved"
msgstr "Порт абонента успешно выбран" msgstr "Порт абонента успешно выбран"
#: views.py:761
#: views.py:783
msgid "Selected port does not exist" msgid "Selected port does not exist"
msgstr "Выбранный порт не существует" msgstr "Выбранный порт не существует"
#: views.py:763
#, fuzzy
#| msgid "Abon does not exist"
#: views.py:785
msgid "User does not exist" msgid "User does not exist"
msgstr "Абонент не найден" msgstr "Абонент не найден"
#: views.py:775
#: views.py:797
msgid "Street successfully saved" msgid "Street successfully saved"
msgstr "Улица успешно сохранена" msgstr "Улица успешно сохранена"
#: views.py:799
#: views.py:821
msgid "Streets has been saved" msgid "Streets has been saved"
msgstr "Улицы сохранены" msgstr "Улицы сохранены"
#: views.py:807
#: views.py:829
msgid "One of these streets has not been found" msgid "One of these streets has not been found"
msgstr "Одна из этих улиц не была найдена" msgstr "Одна из этих улиц не была найдена"
#: views.py:818
#: views.py:840
msgid "The street successfully deleted" msgid "The street successfully deleted"
msgstr "Улица успешно удалена" msgstr "Улица успешно удалена"
#: views.py:820
#: views.py:842
msgid "The street has not been found" msgid "The street has not been found"
msgstr "Улица не найдена" msgstr "Улица не найдена"
#: views.py:847
#: views.py:869
msgid "New telephone has been saved" msgid "New telephone has been saved"
msgstr "Новый телефон сохранен" msgstr "Новый телефон сохранен"
#: views.py:867
#: views.py:889
msgid "Additional telephone successfully deleted" msgid "Additional telephone successfully deleted"
msgstr "Номер телефона успешно удалён" msgstr "Номер телефона успешно удалён"
#: views.py:869
#: views.py:891
msgid "Telephone not found" msgid "Telephone not found"
msgstr "Телефон не найден" msgstr "Телефон не найден"
msgid "Enter a valid integer."
msgstr "Введите верное число"
#: views.py:967
msgid "Periodic pays has been designated"
msgstr "Периодический платёж назначен"
#: views.py:969
msgid "Something wrong in form"
msgstr "Что-то не так в форме"
#: views.py:988
msgid "Periodic pay successfully deleted"
msgstr "Периодический платёж успешно удалён"
#~ msgid "Graph of use"
#~ msgstr "График использования"
msgid "Enter a valid MAC Address."
msgstr "Введите валидный mac адрес"
#~ msgid "Enter a valid integer."
#~ msgstr "Введите верное число"
msgid "Port"
msgstr "Порт"
#~ msgid "Enter a valid MAC Address."
#~ msgstr "Введите валидный mac адрес"
msgid "Priority"
msgstr "Приоритет"
#~ msgid "Port"
#~ msgstr "Порт"
msgid "Delete service"
msgstr "Удалить услугу"
#~ msgid "Priority"
#~ msgstr "Приоритет"
msgid "Services of subscribers not found"
msgstr "Нет подключённых абоненту услуг"
#~ msgid "Services of subscribers not found"
#~ msgstr "Нет подключённых абоненту услуг"
msgid "Buy"
msgstr "Купить"
#~ msgid "Buy"
#~ msgstr "Купить"
msgid "Not confirmed"
msgstr "Действие не подтверждено"
#~ msgid "Not confirmed"
#~ msgstr "Действие не подтверждено"
msgid "Services"
msgstr "Услуги"
#~ msgid "Services"
#~ msgstr "Услуги"
msgid "Payments" msgid "Payments"
msgstr "Финансы" msgstr "Финансы"
@ -980,18 +1076,3 @@ msgstr "Инфо."
msgid "Dialing" msgid "Dialing"
msgstr "Звонки" msgstr "Звонки"
msgid "No have ip"
msgstr "Нет ip адреса"
#, python-format
msgid "Graph of use by %(wantdate_d)s"
msgstr "График использования за %(wantdate_d)s"
msgid "Show graph by date"
msgstr "Показать график по дате"
#: views.py:757
#, python-format
msgid "<a href='%(user_url)s'>%(user_name)s</a> already pinned to this port on this device"
msgstr "<a href='%(user_url)s'>%(user_name)s</a> уже привязан к этому порту на этом устройстве"

130
abonapp/migrations/0004_auto_20180122_1732.py

@ -0,0 +1,130 @@
# -*- 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'),
),
]

65
abonapp/models.py

@ -5,11 +5,11 @@ from django.core.validators import RegexValidator
from django.db.models.signals import post_save, post_delete, pre_delete, post_init from django.db.models.signals import post_save, post_delete, pre_delete, post_init
from django.dispatch import receiver from django.dispatch import receiver
from django.utils import timezone from django.utils import timezone
from django.db import models, connection
from django.db import models, connection, transaction
from django.core import validators from django.core import validators
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from agent import Transmitter, AbonStruct, TariffStruct, NasFailedResult, NasNetworkError from agent import Transmitter, AbonStruct, TariffStruct, NasFailedResult, NasNetworkError
from tariff_app.models import Tariff
from tariff_app.models import Tariff, PeriodicPay
from accounts_app.models import UserProfile, MyUserManager from accounts_app.models import UserProfile, MyUserManager
from mydefs import MyGenericIPAddressField, ip2int, LogicError, ip_addr_regex from mydefs import MyGenericIPAddressField, ip2int, LogicError, ip_addr_regex
from django.conf import settings from django.conf import settings
@ -30,6 +30,7 @@ class AbonGroup(models.Model):
) )
verbose_name = _('Abon group') verbose_name = _('Abon group')
verbose_name_plural = _('Abon groups') verbose_name_plural = _('Abon groups')
ordering = ['title']
def __str__(self): def __str__(self):
return self.title return self.title
@ -38,7 +39,7 @@ class AbonGroup(models.Model):
class AbonLog(models.Model): class AbonLog(models.Model):
abon = models.ForeignKey('Abon', models.CASCADE) abon = models.ForeignKey('Abon', models.CASCADE)
amount = models.FloatField(default=0.0) amount = models.FloatField(default=0.0)
author = models.ForeignKey(UserProfile, models.CASCADE, related_name='+')
author = models.ForeignKey(UserProfile, models.CASCADE, related_name='+', blank=True, null=True)
comment = models.CharField(max_length=128) comment = models.CharField(max_length=128)
date = models.DateTimeField(auto_now_add=True) date = models.DateTimeField(auto_now_add=True)
@ -47,6 +48,7 @@ class AbonLog(models.Model):
permissions = ( permissions = (
('can_view_abonlog', _('Can view subscriber logs')), ('can_view_abonlog', _('Can view subscriber logs')),
) )
ordering = ['-date']
def __str__(self): def __str__(self):
return self.comment return self.comment
@ -82,6 +84,7 @@ class AbonTariff(models.Model):
) )
verbose_name = _('Abon service') verbose_name = _('Abon service')
verbose_name_plural = _('Abon services') verbose_name_plural = _('Abon services')
ordering = ['time_start']
class AbonStreet(models.Model): class AbonStreet(models.Model):
@ -95,6 +98,7 @@ class AbonStreet(models.Model):
db_table = 'abon_street' db_table = 'abon_street'
verbose_name = _('Street') verbose_name = _('Street')
verbose_name_plural = _('Streets') verbose_name_plural = _('Streets')
ordering = ['name']
class ExtraFieldsModel(models.Model): class ExtraFieldsModel(models.Model):
@ -173,14 +177,18 @@ class Abon(UserProfile):
('can_add_ballance', _('fill account')), ('can_add_ballance', _('fill account')),
('can_ping', _('Can ping')) ('can_ping', _('Can ping'))
) )
unique_together = ('device', 'dev_port')
# TODO: Fix when duplicates already in database
#unique_together = ('device', 'dev_port')
verbose_name = _('Abon') verbose_name = _('Abon')
verbose_name_plural = _('Abons') verbose_name_plural = _('Abons')
ordering = ['fio']
# Платим за что-то
# pay something
def make_pay(self, curuser, how_match_to_pay=0.0): 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.ballance -= how_match_to_pay
self.save(update_fields=['ballance']) self.save(update_fields=['ballance'])
post_save.connect(abon_post_save, sender=Abon)
# Пополняем счёт # Пополняем счёт
def add_ballance(self, current_user, amount, comment): def add_ballance(self, current_user, amount, comment):
@ -318,8 +326,8 @@ class InvoiceForPayment(models.Model):
class AllTimePayLogManager(models.Manager): class AllTimePayLogManager(models.Manager):
def by_days(self):
@staticmethod
def by_days():
cur = connection.cursor() cur = connection.cursor()
cur.execute(r'SELECT SUM(summ) as alsum, DATE_FORMAT(date_add, "%Y-%m-%d") AS pay_date FROM all_time_pay_log ' cur.execute(r'SELECT SUM(summ) as alsum, DATE_FORMAT(date_add, "%Y-%m-%d") AS pay_date FROM all_time_pay_log '
r'GROUP BY DATE_FORMAT(date_add, "%Y-%m-%d")') r'GROUP BY DATE_FORMAT(date_add, "%Y-%m-%d")')
@ -333,9 +341,12 @@ class AllTimePayLogManager(models.Manager):
# Log for pay system "AllTime" # Log for pay system "AllTime"
class AllTimePayLog(models.Model): class AllTimePayLog(models.Model):
abon = models.ForeignKey(Abon, models.SET_DEFAULT, blank=True, null=True, default=None)
pay_id = models.CharField(max_length=36, unique=True, primary_key=True) pay_id = models.CharField(max_length=36, unique=True, primary_key=True)
date_add = models.DateTimeField(auto_now_add=True) date_add = models.DateTimeField(auto_now_add=True)
summ = models.FloatField(default=0.0) summ = models.FloatField(default=0.0)
trade_point = models.CharField(_('Trade point'), max_length=20, default=None, null=True, blank=True)
receipt_num = models.IntegerField(_('Receipt number'), default=0)
objects = AllTimePayLogManager() objects = AllTimePayLogManager()
@ -344,7 +355,7 @@ class AllTimePayLog(models.Model):
class Meta: class Meta:
db_table = 'all_time_pay_log' db_table = 'all_time_pay_log'
ordering = ('date_add',)
ordering = ['-date_add']
# log for all terminals # log for all terminals
@ -359,7 +370,7 @@ class AllPayLog(models.Model):
class Meta: class Meta:
db_table = 'all_pay_log' db_table = 'all_pay_log'
ordering = ('date_action',)
ordering = ['-date_action']
class AbonRawPassword(models.Model): class AbonRawPassword(models.Model):
@ -396,6 +407,42 @@ class AdditionalTelephone(models.Model):
verbose_name_plural = _('Additional telephones') verbose_name_plural = _('Additional telephones')
class PeriodicPayForId(models.Model):
periodic_pay = models.ForeignKey(PeriodicPay, models.CASCADE, verbose_name=_('Periodic pay'))
last_pay = models.DateTimeField(_('Last pay time'), blank=True, null=True)
next_pay = models.DateTimeField(_('Next time to pay'))
account = models.ForeignKey(Abon, models.CASCADE, verbose_name=_('Account'))
def payment_for_service(self, author=None, comment=None):
"""
Charge for the service and leave a log about it
"""
now = timezone.now()
if self.next_pay < now:
pp = self.periodic_pay
amount = pp.calc_amount()
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
}
)
self.last_pay = now
self.next_pay = next_pay_date
self.save(update_fields=['last_pay', 'next_pay'])
def __str__(self):
return "%s %s" % (self.periodic_pay, self.next_pay)
class Meta:
db_table = 'periodic_pay_for_id'
@receiver(post_save, sender=Abon) @receiver(post_save, sender=Abon)
def abon_post_save(sender, **kwargs): def abon_post_save(sender, **kwargs):
instance, created = kwargs["instance"], kwargs["created"] instance, created = kwargs["instance"], kwargs["created"]

11
abonapp/pay_systems.py

@ -10,10 +10,6 @@ SECRET = getattr(settings, 'PAY_SECRET')
SERV_ID = getattr(settings, 'PAY_SERV_ID') SERV_ID = getattr(settings, 'PAY_SERV_ID')
#?ACT=1&PAY_ACCOUNT=960849&SERVICE_ID=y832r92y8f9e&PAY_ID=3561234&TRADE_POINT=377&SIGN=32e533a72389fe4e93746509f9d672f8
#?ACT=4&PAY_ACCOUNT=960849&PAY_AMOUNT=1.00&RECEIPT_NUM=29096&SERVICE_ID=y832r92y8f9e&PAY_ID=3561234&TRADE_POINT=496&SIGN=c42161214099dba01e6ab008552bbd3d
def allpay(request): def allpay(request):
def bad_ret(err_id): def bad_ret(err_id):
@ -60,6 +56,8 @@ def allpay(request):
" <time_stamp>%s</time_stamp>\n" % current_date +\ " <time_stamp>%s</time_stamp>\n" % current_date +\
"</pay-response>" "</pay-response>"
elif act == 4: elif act == 4:
trade_point = safe_int(request.GET.get('TRADE_POINT'))
receipt_num = safe_int(request.GET.get('RECEIPT_NUM'))
abon = Abon.objects.get(username=pay_account) abon = Abon.objects.get(username=pay_account)
pays = AllTimePayLog.objects.filter(pay_id=pay_id) pays = AllTimePayLog.objects.filter(pay_id=pay_id)
if pays.count() > 0: if pays.count() > 0:
@ -71,7 +69,10 @@ def allpay(request):
AllTimePayLog.objects.create( AllTimePayLog.objects.create(
pay_id=pay_id, pay_id=pay_id,
summ=pay_amount
summ=pay_amount,
abon=abon,
trade_point=trade_point,
receipt_num=receipt_num
) )
current_date = timezone.now().strftime("%d.%m.%Y %H:%M:%S") current_date = timezone.now().strftime("%d.%m.%Y %H:%M:%S")
return "<?xml version='1.0' encoding='UTF-8'?>" \ return "<?xml version='1.0' encoding='UTF-8'?>" \

36
abonapp/templates/abonapp/modal_periodic_pay.html

@ -0,0 +1,36 @@
{% load i18n %}
{% load bootstrap3 %}
<form action="{% url 'abonapp:add_periodic_pay' gid uid %}" 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-plus"></span>{% trans 'Add periodic pay' %}</h4>
</div>
{% include 'message_block.html' %}
<div class="modal-body">
{% bootstrap_icon 'calendar' as ic %}
{% bootstrap_field form.periodic_pay addon_before=ic %}
{% bootstrap_icon 'time' as ic %}
{% bootstrap_field form.next_pay addon_before=ic %}
<script type="text/javascript">
$(function () {
$('#{{ form.next_pay.id_for_label }}').datetimepicker({
format: 'YYYY-MM-DD'
});
});
</script>
<div class="btn-group">
<button type="submit" class="btn btn-sm btn-success">
<span class="glyphicon glyphicon-plus"></span> {% trans 'Add' %}
</button>
<button type="reset" class="btn btn-sm btn-default">
<span class="glyphicon glyphicon-remove-circle"></span> {% trans 'Reset' %}
</button>
</div>
</div>
</form>

20
abonapp/templates/abonapp/payHistory.html

@ -6,7 +6,6 @@
<table class="table table-striped table-bordered"> <table class="table table-striped table-bordered">
<thead> <thead>
<tr> <tr>
<th>{% trans 'Sub' %}</th>
<th>{% trans 'Sum' %}</th> <th>{% trans 'Sum' %}</th>
<th>{% trans 'Date of payment' %}</th> <th>{% trans 'Date of payment' %}</th>
<th>{% trans 'Author of payment' %}</th> <th>{% trans 'Author of payment' %}</th>
@ -16,21 +15,26 @@
<tbody> <tbody>
{% for ph in pay_history %} {% for ph in pay_history %}
<tr> <tr>
<td><a href="{% url 'abonapp:abon_home' abon_group.pk abon.pk %}">{{ ph.abon.username }}</a></td>
<td>{{ ph.amount }}</td>
<td>{{ ph.date|date:'d F Y, H:i:s' }}</td>
<td><a target="_blank" href="{% url 'acc_app:other_profile' ph.author.pk %}">{{ ph.author.username }}</a></td>
<td>{{ ph.comment }}</td>
<td>{{ ph.amount }}</td>
<td>{{ ph.date|date:'d F Y, H:i:s' }}</td>
<td>
{% if ph.author %}
<a target="_blank" href="{% url 'acc_app:other_profile' ph.author.pk %}">{{ ph.author.username }}</a>
{% else %}
{% trans 'System' %}
{% endif %}
</td>
<td>{{ ph.comment }}</td>
</tr> </tr>
{% empty %} {% empty %}
<tr> <tr>
<td colspan="5">{% trans 'Payment history is empty' %}</td>
<td colspan="4">{% trans 'Payment history is empty' %}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
<tfoot> <tfoot>
<tr> <tr>
<td colspan="5" class="btn-group btn-group-sm">
<td colspan="4" class="btn-group btn-group-sm">
{% if perms.abonapp.can_add_ballance %} {% 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' abon_group.pk abon.pk %}" class="btn btn-default btn-modal">
<span class="glyphicon glyphicon-credit-card"></span> {% trans 'Fill account' %} <span class="glyphicon glyphicon-credit-card"></span> {% trans 'Fill account' %}

35
abonapp/templates/abonapp/service.html

@ -55,7 +55,7 @@
{% endif %} {% endif %}
{% if abon_tariff %} {% if abon_tariff %}
<a href="{% url 'abonapp:unsubscribe_service' abon_group.pk abon.pk abon_tariff.pk %}" class="btn btn-danger">
<a href="{% url 'abonapp:unsubscribe_service' abon_group.pk abon.pk abon_tariff.pk %}" class="btn btn-sm btn-danger">
<span class="glyphicon glyphicon-remove-circle"></span> {% trans 'Finish service' %} <span class="glyphicon glyphicon-remove-circle"></span> {% trans 'Finish service' %}
</a> </a>
{% endif %} {% endif %}
@ -65,7 +65,7 @@
<div class="col-sm-6"> <div class="col-sm-6">
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<h3 class="panel-title">Услуги для заказа</h3>
<h3 class="panel-title">{% trans 'Services for buy' %}</h3>
</div> </div>
<div class="panel-body"> <div class="panel-body">
<table class="table table-condensed"> <table class="table table-condensed">
@ -108,12 +108,41 @@
{% endwith %} {% endwith %}
</tbody> </tbody>
</table> </table>
<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' abon_group.pk %}" class="btn btn-sm btn-primary" title="{% trans 'User groups' %}">
<span class="glyphicon glyphicon-cog"></span> {% trans 'Attach services to group' %} <span class="glyphicon glyphicon-cog"></span> {% trans 'Attach services to group' %}
</a> </a>
</div> </div>
</div> </div>
</div> </div>
{% if perms.tariff_app.can_view_periodic_pay %}
<div class="col-sm-6">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{% trans 'Periodic pay' %}</h3>
</div>
<div class="panel-body">
{% if periodic_pay %}
<dl class="dl-horizontal">
<dt>{% trans 'Pay logic' %}</dt>
<dd>{{ periodic_pay.periodic_pay }}</dd>
<dt>{% trans 'Last pay' %}</dt>
<dd>{{ periodic_pay.last_pay|default:'Not yet paid' }}</dd>
<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">
<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">
<span class="glyphicon glyphicon-plus"></span> {% trans 'Add periodic pay' %}
</a>
{% endif %}
</div>
</div>
</div>
{% endif %}
</div> </div>
{% endblock %} {% endblock %}

6
abonapp/urls_abon.py

@ -38,5 +38,9 @@ urlpatterns = [
url(r'^(?P<uid>\d+)/tel$', views.tels, name='telephones'), url(r'^(?P<uid>\d+)/tel$', views.tels, name='telephones'),
url(r'^(?P<uid>\d+)/tel/add$', views.tel_add, name='telephone_new'), url(r'^(?P<uid>\d+)/tel/add$', views.tel_add, name='telephone_new'),
url(r'^(?P<uid>\d+)/tel/del$', views.tel_del, name='telephone_del')
url(r'^(?P<uid>\d+)/tel/del$', views.tel_del, name='telephone_del'),
url(r'^(?P<uid>\d+)/periodic_pay$', views.add_edit_periodic_pay, name='add_periodic_pay'),
url(r'^(?P<uid>\d+)/periodic_pay(?P<periodic_pay_id>\d+)$', views.add_edit_periodic_pay, name='add_periodic_pay'),
url(r'^(?P<uid>\d+)/periodic_pay(?P<periodic_pay_id>\d+)/del$', views.del_periodic_pay, name='del_periodic_pay')
] ]

59
abonapp/views.py

@ -21,7 +21,7 @@ from devapp.models import Device, Port as DevPort
from datetime import datetime, date, timedelta from datetime import datetime, date, timedelta
from taskapp.models import Task from taskapp.models import Task
from dialing_app.models import AsteriskCDR from dialing_app.models import AsteriskCDR
from statistics.models import getModel, get_dates
from statistics.models import getModel
from guardian.shortcuts import get_objects_for_user, assign_perm from guardian.shortcuts import get_objects_for_user, assign_perm
from guardian.decorators import permission_required_or_403 as permission_required from guardian.decorators import permission_required_or_403 as permission_required
@ -263,11 +263,21 @@ def abon_services(request, gid, uid):
raise PermissionDenied raise PermissionDenied
abon = get_object_or_404(models.Abon, pk=uid) abon = get_object_or_404(models.Abon, pk=uid)
if abon.group != grp:
messages.warning(request, _("User group id is not matches with group in url"))
return redirect('abonapp:abon_services', abon.group.id, abon.id)
try:
periodic_pay = models.PeriodicPayForId.objects.get(account=abon)
except models.PeriodicPayForId.DoesNotExist:
periodic_pay = None
return render(request, 'abonapp/service.html', { return render(request, 'abonapp/service.html', {
'abon': abon, 'abon': abon,
'abon_tariff': abon.current_tariff, 'abon_tariff': abon.current_tariff,
'abon_group': abon.group, 'abon_group': abon.group,
'services': grp.tariffs.all()
'services': grp.tariffs.all(),
'periodic_pay': periodic_pay
}) })
@ -918,7 +928,6 @@ def reset_ip(request, gid, uid):
})) }))
@login_required @login_required
@mydefs.only_admins @mydefs.only_admins
def fin_report(request): def fin_report(request):
@ -937,6 +946,50 @@ def fin_report(request):
}) })
@login_required
@permission_required('abonapp.can_view_abongroup', (models.AbonGroup, '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'):
raise PermissionDenied
periodic_pay_instance = models.PeriodicPayForId()
else:
if not request.user.has_perm('abonapp.change_periodicpayforid'):
raise PermissionDenied
periodic_pay_instance = get_object_or_404(models.PeriodicPayForId, pk=periodic_pay_id)
if request.method == 'POST':
frm = forms.PeriodicPayForIdForm(request.POST, instance=periodic_pay_instance)
if frm.is_valid():
abon = get_object_or_404(models.Abon, pk=uid)
inst = frm.save(commit=False)
inst.account = abon
inst.save()
messages.success(request, _('Periodic pays has been designated'))
else:
messages.error(request, _('Something wrong in form'))
return redirect('abonapp:abon_services', gid, uid)
else:
frm = forms.PeriodicPayForIdForm(instance=periodic_pay_instance)
return render_to_text('abonapp/modal_periodic_pay.html', {
'form': frm,
'gid': gid,
'uid': uid
}, request=request)
@login_required
@permission_required('abonapp.can_view_abongroup', (models.AbonGroup, '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)
if periodic_pay_instance.account.id != uid:
uid = periodic_pay_instance.account.id
periodic_pay_instance.delete()
messages.success(request, _('Periodic pay successfully deleted'))
return redirect('abonapp:abon_services', gid, uid)
# API's # API's
def abons(request): def abons(request):

8
cron.py

@ -1,12 +1,11 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os import os
import django import django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djing.settings") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djing.settings")
django.setup() django.setup()
from django.utils import timezone from django.utils import timezone
from django.db.models import signals from django.db.models import signals
from abonapp.models import Abon, AbonTariff, abontariff_pre_delete
from abonapp.models import Abon, AbonTariff, abontariff_pre_delete, PeriodicPayForId
from agent import Transmitter, NasNetworkError, NasFailedResult from agent import Transmitter, NasNetworkError, NasFailedResult
from mydefs import LogicError from mydefs import LogicError
@ -19,6 +18,11 @@ def main():
tm.sync_nas(users) tm.sync_nas(users)
signals.pre_delete.connect(abontariff_pre_delete, sender=AbonTariff) signals.pre_delete.connect(abontariff_pre_delete, sender=AbonTariff)
# manage periodic pays
ppays = PeriodicPayForId.objects.filter(next_pay__lt=timezone.now()).prefetch_related('account', 'periodic_pay')
for pay in ppays:
pay.payment_for_service()
if __name__ == "__main__": if __name__ == "__main__":
try: try:

52
djing/local_settings.py.template

@ -0,0 +1,52 @@
"""
Custom settings for each system
"""
DEBUG = True
ALLOWED_HOSTS = ['*']
DEFAULT_FROM_EMAIL = 'admin@yoursite.com'
PAGINATION_ITEMS_PER_PAGE = 10
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '!!!!!!!!!!!!!!!!!!!!!!!!YOUR SECRET KEY!!!!!!!!!!!!!!!!!!!!!!!!'
DATABASES = {
'default': {
'OPTIONS': {
'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
'isolation_level': 'read uncommitted'
},
#'ENGINE': 'django.db.backends.sqlite3',
#'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
'ENGINE': 'django.db.backends.mysql',
'NAME': 'djing_db',
'USER': 'DJING_MYSQL_USERNAME', # You can change the user name
'PASSWORD': 'DJING_MYSQL_PASSWORD', # You can change the password
'HOST': 'localhost'
}
}
# service id for AllPay payment system
PAY_SERV_ID = '<service id>'
PAY_SECRET = '<secret>'
# path to asterisk dial records
DIALING_MEDIA = 'path/to/asterisk_records'
# DHCP lease time
DHCP_TIMEOUT = 14400
DEFAULT_SNMP_PASSWORD = 'public'
TELEGRAM_BOT_TOKEN = 'bot token'
TELEPHONE_REGEXP = r'^\+[7,8,9,3]\d{10,11}$'
ASTERISK_MANAGER_AUTH = {
'username': 'admin',
'password': 'password',
'host': '127.0.0.1'
}

59
djing/settings_example.py → djing/settings.py

@ -1,6 +1,13 @@
# -*- coding: utf-8 -* # -*- coding: utf-8 -*
import os import os
try:
from . import local_settings
except ImportError:
raise ImportError("You must create config file local_settings.py from template")
# Build paths inside the project like this: os.path.join(BASE_DIR, ...) # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
from django.urls import reverse_lazy from django.urls import reverse_lazy
@ -9,12 +16,12 @@ from django.urls import reverse_lazy
# See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/ # See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret! # SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '!!!!!!!!!!!!!!!!!!!!!!!!YOUR SECRET KEY!!!!!!!!!!!!!!!!!!!!!!!!'
SECRET_KEY = local_settings.SECRET_KEY
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
DEBUG = local_settings.DEBUG or False
ALLOWED_HOSTS = ['*']
ALLOWED_HOSTS = local_settings.ALLOWED_HOSTS
# required for django-guardian # required for django-guardian
AUTHENTICATION_BACKENDS = ( AUTHENTICATION_BACKENDS = (
@ -87,23 +94,13 @@ WSGI_APPLICATION = 'djing.wsgi.application'
# Database # Database
# https://docs.djangoproject.com/en/1.9/ref/settings/#databases
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
#'ENGINE': 'django.db.backends.mysql',
#'NAME': 'djingdb',
#'USER': 'USER', # You can change the user name
#'PASSWORD': 'PASSWORD', # You can change the password
#'HOST': 'localhost'
}
}
DATABASES = local_settings.DATABASES
# Password validation # Password validation
# https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators
# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [ AUTH_PASSWORD_VALIDATORS = [
{ {
@ -125,7 +122,7 @@ SESSION_ENGINE = 'django.contrib.sessions.backends.file'
SESSION_COOKIE_HTTPONLY = True SESSION_COOKIE_HTTPONLY = True
# Internationalization # Internationalization
# https://docs.djangoproject.com/en/1.9/topics/i18n/
# https://docs.djangoproject.com/en/1.11/topics/i18n/
LANGUAGE_CODE = 'ru-RU' LANGUAGE_CODE = 'ru-RU'
@ -137,12 +134,12 @@ USE_L10N = False
USE_TZ = False USE_TZ = False
DEFAULT_FROM_EMAIL = 'nerosketch@gmail.com'
DEFAULT_FROM_EMAIL = local_settings.DEFAULT_FROM_EMAIL
# Максимальный загружаемый файл 3.90625M (кратно размеру блока диска 4kb, 4000 блоков)
# Maximum file size is 3.90625M
FILE_UPLOAD_MAX_MEMORY_SIZE = 4096000 FILE_UPLOAD_MAX_MEMORY_SIZE = 4096000
# Время жизни сессии, 1 сутки
# time to session live, 1 day
SESSION_COOKIE_AGE = 60 * 60 * 24 SESSION_COOKIE_AGE = 60 * 60 * 24
@ -154,7 +151,7 @@ if DEBUG:
STATICFILES_DIRS = (os.path.join(BASE_DIR, "static"),) STATICFILES_DIRS = (os.path.join(BASE_DIR, "static"),)
# Пример вывода: 16 сентября 2012
# Example output: 16 september 2018
DATE_FORMAT = 'd E Y' DATE_FORMAT = 'd E Y'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media') MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
@ -167,23 +164,19 @@ LOGIN_URL = reverse_lazy('acc_app:login')
LOGIN_REDIRECT_URL = reverse_lazy('acc_app:profile') LOGIN_REDIRECT_URL = reverse_lazy('acc_app:profile')
LOGOUT_URL = reverse_lazy('acc_app:logout_link') LOGOUT_URL = reverse_lazy('acc_app:logout_link')
PAGINATION_ITEMS_PER_PAGE=10
PAGINATION_ITEMS_PER_PAGE = local_settings.PAGINATION_ITEMS_PER_PAGE
PAY_SERV_ID = '<service id>'
PAY_SECRET = '<secret>'
PAY_SERV_ID = local_settings.PAY_SERV_ID
PAY_SECRET = local_settings.PAY_SECRET
DIALING_MEDIA = 'path/to/asterisk_records'
DIALING_MEDIA = local_settings.DIALING_MEDIA
DHCP_TIMEOUT = 14400
DHCP_TIMEOUT = local_settings.DHCP_TIMEOUT
DEFAULT_SNMP_PASSWORD = 'public'
DEFAULT_SNMP_PASSWORD = local_settings.DEFAULT_SNMP_PASSWORD
TELEGRAM_BOT_TOKEN = 'bot token'
TELEGRAM_BOT_TOKEN = local_settings.TELEGRAM_BOT_TOKEN
TELEPHONE_REGEXP = r'^\+[7,8,9,3]\d{10,11}$' TELEPHONE_REGEXP = r'^\+[7,8,9,3]\d{10,11}$'
ASTERISK_MANAGER_AUTH = {
'username': 'admin',
'password': 'password',
'host': '127.0.0.1'
}
ASTERISK_MANAGER_AUTH = local_settings.ASTERISK_MANAGER_AUTH

1
requirements.txt

@ -16,6 +16,7 @@ pid
django-guardian django-guardian
pinax-theme-bootstrap pinax-theme-bootstrap
django-bootstrap3 django-bootstrap3
django-jsonfield
requests requests
webdavclient webdavclient
pyst2 pyst2

44
tariff_app/base_intr.py

@ -1,29 +1,57 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from abc import ABCMeta, abstractmethod from abc import ABCMeta, abstractmethod
# from abonapp import Abon
class TariffBase(metaclass=ABCMeta): class TariffBase(metaclass=ABCMeta):
@abstractmethod @abstractmethod
def calc_amount(self): def calc_amount(self):
"""Считает итоговую сумму платежа"""
"""Calculates total amount of payment"""
raise NotImplementedError
@abstractmethod @abstractmethod
def calc_deadline(self): def calc_deadline(self):
"""Считаем дату, до которой действкет тариф"""
"""Calculate deadline date"""
raise NotImplementedError
@staticmethod @staticmethod
def description(): def description():
"""Возвращает текстовое описание"""
"""
Usage in mydefs.MyChoicesAdapter for choices fields.
:return: human readable description
"""
raise NotImplementedError
@staticmethod @staticmethod
def manage_access(abon): def manage_access(abon):
"""Управляет доступом абонента к услуге"""
"""Manage subscribers access to service"""
#assert isinstance(abon, Abon) #assert isinstance(abon, Abon)
# если абонент не активен то выходим
if not abon.is_active: return False if not abon.is_active: return False
# смотрим на текущую услугу
act_tar = abon.active_tariff() act_tar = abon.active_tariff()
# если есть услуга
if act_tar: if act_tar:
return True return True
class PeriodicPayCalcBase(metaclass=ABCMeta):
@abstractmethod
def calc_amount(self, model_object):
"""
:param model_object: it is a instance of models.PeriodicPay model
:return: float: amount for the service
"""
raise NotImplementedError
@abstractmethod
def get_next_time_to_pay(self, model_object, last_time_payment):
"""
:param model_object: it is a instance of models.PeriodicPay model
:param last_time_payment: May be None if first pay
:return: datetime.datetime: time for next pay
"""
raise NotImplementedError
@staticmethod
def description():
"""Return text description.
Uses in mydefs.MyChoicesAdapter for CHOICES fields"""
raise NotImplementedError

42
tariff_app/custom_tariffs.py

@ -2,16 +2,15 @@
from datetime import timedelta, datetime from datetime import timedelta, datetime
from django.utils import timezone from django.utils import timezone
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from .base_intr import TariffBase
from .base_intr import TariffBase, PeriodicPayCalcBase
from calendar import monthrange from calendar import monthrange
# from abonapp import AbonTariff
from random import uniform
class TariffDefault(TariffBase): class TariffDefault(TariffBase):
def __init__(self, abon_tariff): def __init__(self, abon_tariff):
#assert isinstance(abon_tariff, AbonTariff)
# assert isinstance(abon_tariff, AbonTariff)
self.abon_tariff = abon_tariff self.abon_tariff = abon_tariff
# Базовый функционал считает стоимость пропорционально использованному времени # Базовый функционал считает стоимость пропорционально использованному времени
@ -62,11 +61,10 @@ class TariffDp(TariffDefault):
# Как в IS только не на время, а на 10 лет # Как в IS только не на время, а на 10 лет
class TariffCp(TariffDp): class TariffCp(TariffDp):
def calc_deadline(self): def calc_deadline(self):
# делаем время окончания услуги на 10 лет вперёд # делаем время окончания услуги на 10 лет вперёд
nw = timezone.now() nw = timezone.now()
long_long_time = datetime(year=nw.year+10, month=nw.month, day=nw.day,
long_long_time = datetime(year=nw.year + 10, month=nw.month, day=nw.day,
hour=23, minute=59, second=59) hour=23, minute=59, second=59)
return long_long_time return long_long_time
@ -81,3 +79,35 @@ TARIFF_CHOICES = (
('Dp', TariffDp), ('Dp', TariffDp),
('Cp', TariffCp) ('Cp', TariffCp)
) )
class PeriodicPayCalcDefault(PeriodicPayCalcBase):
def calc_amount(self, model_object):
return model_object.amount
def get_next_time_to_pay(self, model_object, last_time_payment):
# TODO: решить какой будет расёт периодических платежей
return datetime.now() + timedelta(days=30)
@staticmethod
def description():
return _('Default periodic pay')
class PeriodicPayCalcCustom(PeriodicPayCalcDefault):
def calc_amount(self, model_object):
"""
:param model_object: it is a instance of models.PeriodicPay model
:return: float: amount for the service
"""
return uniform(1, 10)
@staticmethod
def description():
return _('Custom periodic pay')
PERIODIC_PAY_CHOICES = (
('df', PeriodicPayCalcDefault),
('cs', PeriodicPayCalcCustom)
)

14
tariff_app/forms.py

@ -7,11 +7,9 @@ class TariffForm(forms.ModelForm):
class Meta: class Meta:
model = models.Tariff model = models.Tariff
fields = '__all__' fields = '__all__'
widgets = {
'title': forms.TextInput(attrs={'class': 'form-control'}),
'descr': forms.TextInput(attrs={'class': 'form-control'}),
'speedIn': forms.NumberInput(attrs={'class': 'form-control'}),
'speedOut': forms.NumberInput(attrs={'class': 'form-control'}),
'amount': forms.NumberInput(attrs={'class': 'form-control'}),
'calc_type': forms.Select(attrs={'class': 'form-control'})
}
class PeriodicPayForm(forms.ModelForm):
class Meta:
model = models.PeriodicPay
exclude = ['when_add', 'extra_info']

69
tariff_app/migrations/0006_auto_20180122_1732.py

@ -0,0 +1,69 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-01-22 17:32
from __future__ import unicode_literals
from django.db import migrations, models
import jsonfield.fields
class Migration(migrations.Migration):
dependencies = [
('tariff_app', '0005_auto_20170502_2229'),
]
operations = [
migrations.CreateModel(
name='PeriodicPay',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=64, verbose_name='Periodic pay name')),
('when_add', models.DateTimeField(auto_now_add=True, verbose_name='When pay created')),
('calc_type', models.CharField(choices=[('df', 'Default periodic pay'), ('cs', 'Custom periodic pay')], default='df', max_length=2, verbose_name='Script type for calculations')),
('amount', models.FloatField(verbose_name='Total amount')),
('extra_info', jsonfield.fields.JSONField(default=dict)),
],
options={
'verbose_name': 'Periodic pay',
'verbose_name_plural': 'Periodic pays',
'db_table': 'periodic_pay',
'ordering': ['-id'],
'permissions': (('can_view_periodic_pay', 'Can view periodic pay'),),
},
),
migrations.AlterField(
model_name='tariff',
name='amount',
field=models.FloatField(default=0.0, verbose_name='Price'),
),
migrations.AlterField(
model_name='tariff',
name='calc_type',
field=models.CharField(choices=[('Df', 'Base calculate functionality'), ('Dp', 'IS'), ('Cp', 'Private service')], default='Df', max_length=2, verbose_name='Script'),
),
migrations.AlterField(
model_name='tariff',
name='descr',
field=models.CharField(max_length=256, verbose_name='Service description'),
),
migrations.AlterField(
model_name='tariff',
name='is_admin',
field=models.BooleanField(default=False, verbose_name='Tech service'),
),
migrations.AlterField(
model_name='tariff',
name='speedIn',
field=models.FloatField(default=0.0, verbose_name='Speed In'),
),
migrations.AlterField(
model_name='tariff',
name='speedOut',
field=models.FloatField(default=0.0, verbose_name='Speed Out'),
),
migrations.AlterField(
model_name='tariff',
name='title',
field=models.CharField(max_length=32, verbose_name='Service title'),
),
]

94
tariff_app/models.py

@ -1,27 +1,33 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from django.db import models
from .custom_tariffs import TariffBase, TARIFF_CHOICES
from datetime import datetime
from django.db import models, IntegrityError
from django.utils.translation import gettext_lazy as _
from django.dispatch import receiver
from .base_intr import TariffBase, PeriodicPayCalcBase
from .custom_tariffs import TARIFF_CHOICES, PERIODIC_PAY_CHOICES
from mydefs import MyChoicesAdapter from mydefs import MyChoicesAdapter
from jsonfield import JSONField
class Tariff(models.Model): class Tariff(models.Model):
title = models.CharField(max_length=32)
descr = models.CharField(max_length=256)
speedIn = models.FloatField(default=0.0)
speedOut = models.FloatField(default=0.0)
amount = models.FloatField(default=0.0)
calc_type = models.CharField(max_length=2, default=TARIFF_CHOICES[0][0], choices=MyChoicesAdapter(TARIFF_CHOICES))
is_admin = models.BooleanField(default=False)
title = models.CharField(_('Service title'), max_length=32)
descr = models.CharField(_('Service description'), max_length=256)
speedIn = models.FloatField(_('Speed In'), default=0.0)
speedOut = models.FloatField(_('Speed Out'), default=0.0)
amount = models.FloatField(_('Price'), default=0.0)
calc_type = models.CharField(_('Script'), max_length=2, default=TARIFF_CHOICES[0][0],
choices=MyChoicesAdapter(TARIFF_CHOICES))
is_admin = models.BooleanField(_('Tech service'), default=False)
# Возвращает потомок класса TariffBase, методы которого дают нужную логику оплаты по тарифу # Возвращает потомок класса TariffBase, методы которого дают нужную логику оплаты по тарифу
def get_calc_type(self): def get_calc_type(self):
ob = [TC for TC in TARIFF_CHOICES if TC[0] == self.calc_type]
if len(ob) > 0:
res_type = ob[0][1]
if not issubclass(res_type, TariffBase):
raise TypeError
return res_type
calc_code = self.calc_type
for choice_pair in TARIFF_CHOICES:
choice_code, logic_class = choice_pair
if choice_code == calc_code:
if not issubclass(logic_class, TariffBase):
raise TypeError
return logic_class
def calc_deadline(self): def calc_deadline(self):
calc_type = self.get_calc_type() calc_type = self.get_calc_type()
@ -30,3 +36,59 @@ class Tariff(models.Model):
def __str__(self): def __str__(self):
return "%s (%.2f)" % (self.title, self.amount) return "%s (%.2f)" % (self.title, self.amount)
class PeriodicPay(models.Model):
name = models.CharField(_('Periodic pay name'), max_length=64)
when_add = models.DateTimeField(_('When pay created'), auto_now_add=True)
calc_type = models.CharField(_('Script type for calculations'), max_length=2, default='df',
choices=MyChoicesAdapter(PERIODIC_PAY_CHOICES))
amount = models.FloatField(_('Total amount'))
extra_info = JSONField()
def _get_calc_object(self):
"""
:return: subclass of custom_tariffs.PeriodicPayCalcBase with required
logic depending on the selected in database.
"""
calc_code = self.calc_type
for choice_pair in PERIODIC_PAY_CHOICES:
choice_code, logic_class = choice_pair
if choice_code == calc_code:
if not issubclass(logic_class, PeriodicPayCalcBase):
raise TypeError
return logic_class()
def get_next_time_to_pay(self, last_time_payment):
#
# last_time_payment may be None if it is a first payment
#
calc_obj = self._get_calc_object()
res = calc_obj.get_next_time_to_pay(self, last_time_payment)
if type(res) is not datetime:
raise TypeError
return res
def calc_amount(self):
calc_obj = self._get_calc_object()
res = calc_obj.calc_amount(self)
if type(res) is not float:
raise TypeError
return res
def __str__(self):
return self.name
class Meta:
db_table = 'periodic_pay'
permissions = (
('can_view_periodic_pay', _('Can view periodic pay')),
)
verbose_name = _('Periodic pay')
verbose_name_plural = _('Periodic pays')
ordering = ['-id']
@receiver(models.signals.pre_delete, sender=PeriodicPay)
def periodic_pay_pre_delete(sender, **kwargs):
raise IntegrityError('All linked abonapp.PeriodicPayForId will be removed, be careful')

67
tariff_app/templates/tariff_app/editTarif.html

@ -1,5 +1,6 @@
{% extends request.is_ajax|yesno:'bajax.html,base.html' %} {% extends request.is_ajax|yesno:'bajax.html,base.html' %}
{% load i18n %} {% load i18n %}
{% load bootstrap3 %}
{% block main %} {% block main %}
<ol class="breadcrumb"> <ol class="breadcrumb">
@ -21,65 +22,27 @@
<h3 class="panel-title">{% if tarif_id == 0 %}{% trans 'Create' %}{% else %}{% trans 'Edit' %}{% endif %} {% trans 'tariff' %}</h3> <h3 class="panel-title">{% if tarif_id == 0 %}{% trans 'Create' %}{% else %}{% trans 'Edit' %}{% endif %} {% trans 'tariff' %}</h3>
</div> </div>
<div class="panel-body"> <div class="panel-body">
<form role="form" action="{% url 'tarifs:edit' tarif_id %}" method="post">{% csrf_token %}
<form action="{% url 'tarifs:edit' tarif_id %}" method="post">{% csrf_token %}
<div class="form-group">
<label for="id_title">{% trans 'Service title' %}</label>
{% bootstrap_icon 'tag' as ic %}
{% bootstrap_field form.title addon_before=ic %}
<div class="input-group">
<span class="input-group-addon"><span class="glyphicon glyphicon-tag"></span></span>
{{ form.title }}{{ form.title.errors }}
</div>
</div>
<div class="form-group">
<label for="id_descr">{% trans 'Service description' %}</label>
{% bootstrap_icon 'comment' as ic %}
{% bootstrap_field form.descr addon_before=ic %}
<div class="input-group">
<span class="input-group-addon"><span class="glyphicon glyphicon-comment"></span></span>
{{ form.descr }}{{ form.descr.errors }}
</div>
</div>
{% bootstrap_icon 'chevron-down' as ic %}
{% bootstrap_field form.speedIn addon_before=ic %}
<div class="form-group">
<label for="id_speedIn">{% trans 'Speed In' %}</label>
{% bootstrap_icon 'chevron-up' as ic %}
{% bootstrap_field form.speedOut addon_before=ic %}
<div class="input-group">
<span class="input-group-addon"><span class="glyphicon glyphicon-chevron-down"></span></span>
{{ form.speedIn }}{{ form.speedIn.errors }}
</div>
</div>
<div class="form-group">
<label for="id_speedOut">{% trans 'Speed Out' %}</label>
<div class="input-group">
<span class="input-group-addon"><span class="glyphicon glyphicon-chevron-up"></span></span>
{{ form.speedOut }}{{ form.speedOut.errors }}
</div>
</div>
{% bootstrap_icon 'usd' as ic %}
{% bootstrap_field form.amount addon_before=ic %}
<div class="form-group">
<label for="id_amount">{% trans 'Price' %}</label>
{% bootstrap_icon 'link' as ic %}
{% bootstrap_field form.calc_type addon_before=ic %}
<div class="input-group">
<span class="input-group-addon"><span class="glyphicon glyphicon-usd"></span></span>
{{ form.amount }}{{ form.amount.errors }}
</div>
</div>
<div class="form-group">
<label for="id_calc_type">{% trans 'Script' %}</label>
<div class="input-group">
<span class="input-group-addon"><span class="glyphicon glyphicon-link"></span></span>
{{ form.calc_type }}{{ form.calc_type.errors }}
</div>
</div>
<div class="checkbox">
<label>{{ form.is_admin }} {% trans 'Tech service' %}</label>
</div>
{% bootstrap_field form.is_admin %}
<div class="btn-group"> <div class="btn-group">
<button type="submit" class="btn btn-sm btn-primary"> <button type="submit" class="btn btn-sm btn-primary">

36
tariff_app/templates/tariff_app/ext.html

@ -0,0 +1,36 @@
{% 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 class="active">{% trans 'Services' %}</li>
</ol>
{% include 'message_block.html' %}
<ul class="nav nav-tabs">
{% url 'tarifs:home' as tarhome %}
<li{% if tarhome == request.path %} class="active"{% endif %}>
<a href="{{ tarhome }}">
{% trans 'Services' %}
</a>
</li>
{% url 'tarifs:periodic_pays' as perlist %}
<li{% if perlist == request.path %} class="active"{% endif %}>
<a href="{{ perlist }}">
{% trans 'Periodic pays' %}
</a>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane active">
{% block content %}{% endblock %}
</div>
</div>
{% endblock %}

51
tariff_app/templates/tariff_app/periodic_pays/add_edit.html

@ -0,0 +1,51 @@
{% extends request.is_ajax|yesno:'bajax.html,base.html' %}
{% load i18n %}
{% load bootstrap3 %}
{% block main %}
<ol class="breadcrumb">
<li><span class="glyphicon glyphicon-home"></span></li>
<li><a href="{% url 'tarifs:periodic_pays' %}">{% trans 'Services' %}</a></li>
{% if pay_instance %}
<li class="active">{% trans 'Change periodic pay' %}</li>
{% else %}
<li class="active">{% trans 'Add new periodic pay' %}</li>
{% endif %}
</ol>
{% include 'message_block.html' %}
{% if pay_instance %}
<div class="page-header">
<h2>{{ pay_instance.name }}</h2>
</div>
{% url 'tarifs:periodic_pay_edit' pay_instance.id as form_url %}
{% else %}
{% url 'tarifs:periodic_pay_add' as form_url %}
{% endif %}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{% trans 'Pay details' %}</h3>
</div>
<div class="panel-body">
<form action="{{ form_url }}" method="post" autocomplete="off">{% csrf_token %}
{% bootstrap_icon 'header' as ic %}
{% bootstrap_field form.name addon_before=ic %}
{% bootstrap_icon 'transfer' as ic %}
{% bootstrap_field form.calc_type addon_before=ic %}
{% bootstrap_icon 'rub' as ic %}
{% bootstrap_field form.amount addon_before=ic %}
{% trans 'Save' as ic %}
{% bootstrap_button ic button_class="btn-primary" icon="save" %}
</form>
</div>
</div>
{% endblock %}

62
tariff_app/templates/tariff_app/periodic_pays/list.html

@ -0,0 +1,62 @@
{% extends request.is_ajax|yesno:'nullcont.htm,tariff_app/ext.html' %}
{% load i18n %}
{% block content %}
<div class="table-responsive">
<table class="table table-striped table-bordered">
<thead>
<tr>
<th class="col-sm-4">{% trans 'Periodic pay name' %}</th>
<th class="col-sm-2">{% trans 'When pay created' %}</th>
<th class="col-sm-3">{% trans 'Script type for calculations' %}</th>
<th class="col-sm-2">{% trans 'Total amount' %}</th>
<th class="col-sm-1">#</th>
</tr>
</thead>
<tbody>
{% trans 'Edit' as edtext %}
{% with can_view=perms.tariff_app.can_view_periodic_pay %}
{% for pay in pays %}
<tr>
<td>{{ pay.name }}</td>
<td>{{ pay.when_add|date:'j M Y' }}</td>
<td>{{ pay.get_calc_type_display }}</td>
<td>{{ pay.amount }}</td>
<td>
{% if can_view %}
<a href="{% url 'tarifs:periodic_pay_edit' pay.id %}" class="btn btn-sm btn-default" title="{{ edtext }}">
<span class="glyphicon glyphicon-edit"></span>
</a>
{% else %}
<a href="#" class="btn btn-sm btn-default" disabled>
<span class="glyphicon glyphicon-edit"></span>
</a>
{% endif %}
</td>
</tr>
{% empty %}
<tr>
<td colspan="5">{% trans 'The list is empty' %}</td>
</tr>
{% endfor %}
{% endwith %}
</tbody>
{% if perms.tariff_app.add_periodicpay %}
<tfoot>
<tr>
<td colspan="5">
<a href="{% url 'tarifs:periodic_pay_add' %}" class="btn btn-sm btn-success">
<span class="glyphicon glyphicon-plus"></span> {% trans 'Add' %}
</a>
</td>
</tr>
</tfoot>
{% endif %}
</table>
</div>
{% include 'toolbar_page.html' with pag=pays %}
{% endblock %}

12
tariff_app/templates/tariff_app/tarifs.html

@ -1,15 +1,7 @@
{% extends 'base.html' %}
{% extends request.is_ajax|yesno:'nullcont.htm,tariff_app/ext.html' %}
{% load i18n %} {% load i18n %}
{% block main %}
{% block content %}
<ol class="breadcrumb">
<li><span class="glyphicon glyphicon-home"></span></li>
<li class="active">{% trans 'Tarifs' %}</li>
</ol>
{% include 'message_block.html' %}
<h3>{% trans 'Service list' %}</h3>
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-striped table-bordered"> <table class="table table-striped table-bordered">
<thead> <thead>

6
tariff_app/urls.py

@ -8,5 +8,9 @@ urlpatterns = [
url(r'^$', views.tarifs, name='home'), url(r'^$', views.tarifs, name='home'),
url(r'^(?P<tarif_id>\d+)$', views.edit_tarif, name='edit'), url(r'^(?P<tarif_id>\d+)$', views.edit_tarif, name='edit'),
url(r'^add$', views.edit_tarif, name='add'), url(r'^add$', views.edit_tarif, name='add'),
url(r'^del(?P<tid>\d+)$', views.del_tarif, name='del')
url(r'^del(?P<tid>\d+)$', views.del_tarif, name='del'),
url(r'^periodic_pays$', views.periodic_pays, name='periodic_pays'),
url(r'^periodic_pays/add$', views.periodic_pay, name='periodic_pay_add'),
url(r'^periodic_pays/(?P<pay_id>\d+)$', views.periodic_pay, name='periodic_pay_edit')
] ]

45
tariff_app/views.py

@ -7,7 +7,7 @@ from django.contrib import messages
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from guardian.decorators import permission_required_or_403 as permission_required from guardian.decorators import permission_required_or_403 as permission_required
from .models import Tariff
from .models import Tariff, PeriodicPay
import mydefs import mydefs
from . import forms from . import forms
@ -15,7 +15,7 @@ from . import forms
@login_required @login_required
@mydefs.only_admins @mydefs.only_admins
def tarifs(request): def tarifs(request):
tars = Tariff.objects.all()
tars = Tariff.objects.order_by('title')
# фильтр # фильтр
direct, field = mydefs.order_helper(request) direct, field = mydefs.order_helper(request)
@ -72,3 +72,44 @@ def del_tarif(request, tid):
messages.error(request, _('Not have a confirmations of delete')) messages.error(request, _('Not have a confirmations of delete'))
return mydefs.res_success(request, 'tarifs:home') return mydefs.res_success(request, 'tarifs:home')
return render_to_text('tariff_app/modal_del_warning.html', {'tid': tid}, request=request) return render_to_text('tariff_app/modal_del_warning.html', {'tid': tid}, request=request)
@login_required
@permission_required('tariff_app.can_view_periodic_pay')
def periodic_pays(request):
pays = PeriodicPay.objects.all()
pays = mydefs.pag_mn(request, pays)
return render(request, 'tariff_app/periodic_pays/list.html', {
'pays': pays
})
@login_required
def periodic_pay(request, pay_id=0):
if pay_id != 0:
pay_inst = get_object_or_404(PeriodicPay, pk=pay_id)
if not request.user.has_perm('tariff_app.change_periodicpay'):
raise PermissionDenied
else:
pay_inst = None
if not request.user.has_perm('tariff_app.add_periodicpay'):
raise PermissionDenied
if request.method == 'POST':
frm = forms.PeriodicPayForm(request.POST, instance=pay_inst)
if frm.is_valid():
new_periodic_pay = frm.save()
if pay_inst is None:
comment = _('New periodic pay successfully created')
else:
comment = _('Periodic pay has been changed')
messages.success(request, comment)
return redirect('tarifs:periodic_pay_edit', new_periodic_pay.pk)
else:
messages.error(request, _('Some fields were filled incorrect, please try again'))
else:
frm = forms.PeriodicPayForm(instance=pay_inst)
return render(request, 'tariff_app/periodic_pays/add_edit.html', {
'pay_instance': pay_inst,
'form': frm
})

6
taskapp/templates/taskapp/tasklist.html

@ -33,10 +33,10 @@
{% endif %} {% endif %}
{% endif %} {% endif %}
<td class="hidden-xs"><a href="{% url 'taskapp:view' task.pk %}" target="_blank">{{ task.pk }}</a></td>
<td class="hidden-xs"><a href="{% url 'taskapp:view' task.pk %}">{{ task.pk }}</a></td>
{% if task.abon and task.abon.group %} {% if task.abon and task.abon.group %}
<td><a href="{% url 'abonapp:abon_home' task.abon.group.pk task.abon.pk %}" target="_blank" title="{{ task.abon.description|default:'' }}" data-toggle="tooltip">{{ task.abon.get_full_name }}</a></td>
<td><a href="{% url 'abonapp:abon_home' task.abon.group.pk task.abon.pk %}" title="{{ task.abon.description|default:'' }}" data-toggle="tooltip">{{ task.abon.get_full_name }}</a></td>
<td>{{ task.abon.group.title }}, {{ task.abon.street|default:'' }} {{ task.abon.house|default:'' }}</td> <td>{{ task.abon.group.title }}, {{ task.abon.street|default:'' }} {{ task.abon.house|default:'' }}</td>
{% else %} {% else %}
<td>{% trans 'User does not exist' %}</td> <td>{% trans 'User does not exist' %}</td>
@ -51,7 +51,7 @@
{% else %} {% else %}
{% trans 'Author does not exist' %} {% trans 'Author does not exist' %}
{% endif %} {% endif %}
</td>
</td>
<td class="hidden-xs">{{ task.time_of_create|date:'d E H:i' }}</td> <td class="hidden-xs">{{ task.time_of_create|date:'d E H:i' }}</td>
<td class="btn-group btn-group-xs btn-group-justified"> <td class="btn-group btn-group-xs btn-group-justified">

4
taskapp/templates/taskapp/tasklist_all.html

@ -46,10 +46,10 @@
{% endif %} {% endif %}
{% endif %} {% endif %}
<td class="hidden-xs"><a href="{% url 'taskapp:view' task.pk %}" target="_blank">{{ task.pk }}</a></td>
<td class="hidden-xs"><a href="{% url 'taskapp:view' task.pk %}">{{ task.pk }}</a></td>
{% if task.abon and task.abon.group %} {% if task.abon and task.abon.group %}
<td><a href="{% url 'abonapp:abon_home' task.abon.group.pk task.abon.pk %}" target="_blank">{{ task.abon.get_full_name }}</a></td>
<td><a href="{% url 'abonapp:abon_home' task.abon.group.pk task.abon.pk %}">{{ task.abon.get_full_name }}</a></td>
<td>{{ task.abon.group.title }}, {{ task.abon.street|default:_('Not assigned') }} {{ task.abon.house|default:_('Not assigned') }}</td> <td>{{ task.abon.group.title }}, {{ task.abon.street|default:_('Not assigned') }} {{ task.abon.house|default:_('Not assigned') }}</td>
{% else %} {% else %}
<td>{% trans 'User does not exist' %}</td> <td>{% trans 'User does not exist' %}</td>

4
taskapp/templates/taskapp/tasklist_failed.html

@ -33,10 +33,10 @@
{% endif %} {% endif %}
{% endif %} {% endif %}
<td class="hidden-xs"><a href="{% url 'taskapp:view' task.pk %}" target="_blank">{{ task.pk }}</a></td>
<td class="hidden-xs"><a href="{% url 'taskapp:view' task.pk %}">{{ task.pk }}</a></td>
{% if task.abon and task.abon.group %} {% if task.abon and task.abon.group %}
<td><a href="{% url 'abonapp:abon_home' task.abon.group.pk task.abon.pk %}" target="_blank">{{ task.abon.get_full_name }}</a></td>
<td><a href="{% url 'abonapp:abon_home' task.abon.group.pk task.abon.pk %}">{{ task.abon.get_full_name }}</a></td>
<td>{{ task.abon.group.title }}, {{ task.abon.street|default:_('Not assigned') }} {{ task.abon.house|default:_('Not assigned') }}</td> <td>{{ task.abon.group.title }}, {{ task.abon.street|default:_('Not assigned') }} {{ task.abon.house|default:_('Not assigned') }}</td>
{% else %} {% else %}
<td>{% trans 'User does not exist' %}</td> <td>{% trans 'User does not exist' %}</td>

4
taskapp/templates/taskapp/tasklist_finish.html

@ -33,10 +33,10 @@
{% endif %} {% endif %}
{% endif %} {% endif %}
<td class="hidden-xs"><a href="{% url 'taskapp:view' task.pk %}" target="_blank">{{ task.pk }}</a></td>
<td class="hidden-xs"><a href="{% url 'taskapp:view' task.pk %}">{{ task.pk }}</a></td>
{% if task.abon and task.abon.group %} {% if task.abon and task.abon.group %}
<td><a href="{% url 'abonapp:abon_home' task.abon.group.pk task.abon.pk %}" target="_blank">{{ task.abon.get_full_name }}</a></td>
<td><a href="{% url 'abonapp:abon_home' task.abon.group.pk task.abon.pk %}">{{ task.abon.get_full_name }}</a></td>
<td>{{ task.abon.group.title }}, {{ task.abon.street|default:_('Not assigned') }} {{ task.abon.house|default:_('Not assigned') }}</td> <td>{{ task.abon.group.title }}, {{ task.abon.street|default:_('Not assigned') }} {{ task.abon.house|default:_('Not assigned') }}</td>
{% else %} {% else %}
<td>{% trans 'User does not exist' %}</td> <td>{% trans 'User does not exist' %}</td>

4
taskapp/templates/taskapp/tasklist_own.html

@ -33,10 +33,10 @@
{% endif %} {% endif %}
{% endif %} {% endif %}
<td class="hidden-xs"><a href="{% url 'taskapp:view' task.id %}" target="_blank">{{ task.id }}</a></td>
<td class="hidden-xs"><a href="{% url 'taskapp:view' task.id %}">{{ task.id }}</a></td>
{% if task.abon and task.abon.group %} {% if task.abon and task.abon.group %}
<td><a href="{% url 'abonapp:abon_home' task.abon.group.pk task.abon.pk %}" target="_blank">{{ task.abon.get_full_name }}</a></td>
<td><a href="{% url 'abonapp:abon_home' task.abon.group.pk task.abon.pk %}">{{ task.abon.get_full_name }}</a></td>
<td>{{ task.abon.group.title }}, {{ task.abon.street|default:_('Not assigned') }} {{ task.abon.house|default:_('Not assigned') }}</td> <td>{{ task.abon.group.title }}, {{ task.abon.street|default:_('Not assigned') }} {{ task.abon.house|default:_('Not assigned') }}</td>
{% else %} {% else %}
<td>{% trans 'User does not exist' %}</td> <td>{% trans 'User does not exist' %}</td>

Loading…
Cancel
Save