From 073c98887cb5abaf0b3ac0f1647835037c2938e8 Mon Sep 17 00:00:00 2001 From: Dmitry Novikov Date: Tue, 18 Sep 2018 17:58:23 +0300 Subject: [PATCH] Make new functionality. Add flag Abon.autoconnect_service, if it checked then service may connect automatically when old service is expired. --- abonapp/locale/ru/LC_MESSAGES/django.po | 3 + abonapp/migrations/0004_auto_20180918_1734.py | 44 +++++++ abonapp/models.py | 55 ++++++--- abonapp/templates/abonapp/service.html | 109 +++++++++--------- abonapp/urls.py | 3 +- abonapp/views.py | 15 ++- clientsideapp/locale/ru/LC_MESSAGES/django.po | 45 ++++++-- .../templates/clientsideapp/services.html | 11 ++ clientsideapp/urls.py | 3 +- clientsideapp/views.py | 15 ++- periodic.py | 39 ++++++- static/clientside/my_clientside.js | 6 + static/js/my.js | 6 + 13 files changed, 267 insertions(+), 87 deletions(-) create mode 100644 abonapp/migrations/0004_auto_20180918_1734.py diff --git a/abonapp/locale/ru/LC_MESSAGES/django.po b/abonapp/locale/ru/LC_MESSAGES/django.po index 9e89ca2..cc058bc 100644 --- a/abonapp/locale/ru/LC_MESSAGES/django.po +++ b/abonapp/locale/ru/LC_MESSAGES/django.po @@ -1153,3 +1153,6 @@ msgstr "Отмена" msgid "View" msgstr "Открыть" + +msgid "Auto continue service." +msgstr "Автопродление услуги." diff --git a/abonapp/migrations/0004_auto_20180918_1734.py b/abonapp/migrations/0004_auto_20180918_1734.py new file mode 100644 index 0000000..cf7bba3 --- /dev/null +++ b/abonapp/migrations/0004_auto_20180918_1734.py @@ -0,0 +1,44 @@ +# Generated by Django 2.1 on 2018-09-18 17:34 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('abonapp', '0003_abon_nas'), + ] + + operations = [ + migrations.AlterModelOptions( + name='abon', + options={'ordering': ('fio',), 'permissions': (('can_buy_tariff', 'Buy service perm'), ('can_add_ballance', 'fill account'), ('can_ping', 'Can ping')), 'verbose_name': 'Abon', 'verbose_name_plural': 'Abons'}, + ), + migrations.AlterModelOptions( + name='abonlog', + options={'ordering': ('-date',)}, + ), + migrations.AlterModelOptions( + name='additionaltelephone', + options={'ordering': ('owner_name',), 'verbose_name': 'Additional telephone', 'verbose_name_plural': 'Additional telephones'}, + ), + migrations.AlterModelOptions( + name='invoiceforpayment', + options={'ordering': ('date_create',), 'verbose_name': 'Debt', 'verbose_name_plural': 'Debts'}, + ), + migrations.AddField( + model_name='abon', + name='autoconnect_service', + field=models.BooleanField(default=False, verbose_name='Automatically connect next service'), + ), + migrations.AlterField( + model_name='abon', + name='is_dynamic_ip', + field=models.BooleanField(default=False, verbose_name='Is dynamic ip'), + ), + migrations.AlterField( + model_name='passportinfo', + name='date_of_acceptance', + field=models.DateField(verbose_name='Date of acceptance'), + ), + ] diff --git a/abonapp/models.py b/abonapp/models.py index 00cfb73..71e5c7d 100644 --- a/abonapp/models.py +++ b/abonapp/models.py @@ -6,7 +6,7 @@ from django.conf import settings from django.core import validators from django.core.validators import RegexValidator from django.db import models, connection, transaction -from django.db.models.signals import post_delete, pre_delete, post_init +from django.db.models.signals import post_delete, pre_delete, post_init, pre_save from django.dispatch import receiver from django.shortcuts import resolve_url from django.utils import timezone @@ -87,7 +87,7 @@ class AbonManager(MyUserManager): class Abon(BaseAccount): - current_tariff = models.ForeignKey(AbonTariff, null=True, blank=True, on_delete=models.SET_NULL) + current_tariff = models.OneToOneField(AbonTariff, null=True, blank=True, on_delete=models.SET_NULL) group = models.ForeignKey(Group, on_delete=models.SET_NULL, blank=True, null=True, verbose_name=_('User group')) ballance = models.FloatField(default=0.0) ip_addresses = models.ManyToManyField(IpLeaseModel, verbose_name=_('Ip addresses')) @@ -96,9 +96,10 @@ class Abon(BaseAccount): house = models.CharField(_('House'), max_length=12, null=True, blank=True) device = models.ForeignKey('devapp.Device', null=True, blank=True, on_delete=models.SET_NULL) dev_port = models.ForeignKey('devapp.Port', null=True, blank=True, on_delete=models.SET_NULL) - is_dynamic_ip = models.BooleanField(default=False) + is_dynamic_ip = models.BooleanField(_('Is dynamic ip'), default=False) nas = models.ForeignKey('nas_app.NASModel', null=True, blank=True, on_delete=models.SET_NULL, verbose_name=_('Network access server'), default=None) + autoconnect_service = models.BooleanField(_('Automatically connect next service'), default=False) MARKER_FLAGS = ( ('icon_donkey', _('Donkey')), @@ -148,7 +149,16 @@ class Abon(BaseAccount): ) self.ballance += amount - def pick_tariff(self, tariff, author, comment=None, deadline=None): + def pick_tariff(self, tariff, author, comment=None, deadline=None) -> None: + """ + Trying to buy a service if enough money. + :param tariff: instance of tariff_app.models.Tariff. + :param author: Instance of accounts_app.models.UserProfile. Who connected this + service. May be None if author is a system. + :param comment: Optional text for logging this pay. + :param deadline: Instance of datetime.datetime. Date when service is expired. + :return: Nothing + """ if not isinstance(tariff, Tariff): raise TypeError @@ -170,22 +180,23 @@ class Abon(BaseAccount): if self.ballance < amount: raise LogicError(_('not enough money')) - new_abtar = AbonTariff.objects.create( - deadline=deadline, tariff=tariff - ) - self.current_tariff = new_abtar + with transaction.atomic(): + new_abtar = AbonTariff.objects.create( + deadline=deadline, tariff=tariff + ) + self.current_tariff = new_abtar - # charge for the service - self.ballance -= amount + # charge for the service + self.ballance -= amount - self.save() + self.save(update_fields=('ballance', 'current_tariff')) - # make log about it - AbonLog.objects.create( - abon=self, amount=-tariff.amount, - author=author, - comment=comment or _('Buy service default log') - ) + # make log about it + AbonLog.objects.create( + abon=self, amount=-tariff.amount, + author=author, + comment=comment or _('Buy service default log') + ) # Destroy the service if the time has come # def bill_service(self, author): @@ -468,8 +479,16 @@ def abon_tariff_post_init(sender, **kwargs): abon_tariff = kwargs["instance"] if getattr(abon_tariff, 'time_start') is None: abon_tariff.time_start = timezone.now() - calc_obj = abon_tariff.tariff.get_calc_type()(abon_tariff) if getattr(abon_tariff, 'deadline') is None: + calc_obj = abon_tariff.tariff.get_calc_type()(abon_tariff) + abon_tariff.deadline = calc_obj.calc_deadline() + + +@receiver(pre_save, sender=AbonTariff) +def abon_tariff_pre_save(sender, **kwargs): + abon_tariff = kwargs["instance"] + if getattr(abon_tariff, 'deadline') is None: + calc_obj = abon_tariff.tariff.get_calc_type()(abon_tariff) abon_tariff.deadline = calc_obj.calc_deadline() diff --git a/abonapp/templates/abonapp/service.html b/abonapp/templates/abonapp/service.html index 3774ddf..a00c3bd 100644 --- a/abonapp/templates/abonapp/service.html +++ b/abonapp/templates/abonapp/service.html @@ -3,6 +3,58 @@ {% block content %}
+
+
+
+

{% trans 'Services for buy' %}

+
+
+ + + + + + + + + + + + {% with can_ch_trf=perms.tariff_app.change_tariff %} + {% for service in services %} + + + + + + + + {% empty %} + + {% endfor %} + {% endwith %} + +
{% trans 'Pick a service' %}{% trans 'Service' %}{% trans 'Price' %}{% trans 'Speed In' %}{% trans 'Speed Out' %}
+ + + {% if can_ch_trf %} + {{ service.title }} + {% else %} + {{ service.title }} + {% endif %} + {{ service.amount }} {% trans 'currency' %}{{ service.speedIn }}{{ service.speedOut }}
+ {% trans 'This group has no services' %} + + {% trans 'Tariffs in groups' %} + +
+ + {% trans 'Attach services to group' %} + +
+
+
@@ -44,6 +96,10 @@
+

+ {% trans 'Auto continue service.' %} + +

{{ abon_tariff.tariff.descr }}

@@ -68,58 +124,6 @@
-
-
-
-

{% trans 'Services for buy' %}

-
-
- - - - - - - - - - - - {% with can_ch_trf=perms.tariff_app.change_tariff %} - {% for service in services %} - - - - - - - - {% empty %} - - {% endfor %} - {% endwith %} - -
{% trans 'Pick a service' %}{% trans 'Service' %}{% trans 'Price' %}{% trans 'Speed In' %}{% trans 'Speed Out' %}
- - - {% if can_ch_trf %} - {{ service.title }} - {% else %} - {{ service.title }} - {% endif %} - {{ service.amount }} {% trans 'currency' %}{{ service.speedIn }}{{ service.speedOut }}
- {% trans 'This group has no services' %} - - {% trans 'Tariffs in groups' %} - -
- - {% trans 'Attach services to group' %} - -
-
-
{% if perms.tariff_app.view_periodic_pay %}
@@ -149,6 +153,7 @@
{% endif %} + {% endblock %} diff --git a/abonapp/urls.py b/abonapp/urls.py index ad06746..d13a2fd 100644 --- a/abonapp/urls.py +++ b/abonapp/urls.py @@ -32,7 +32,8 @@ subscriber_patterns = [ path('periodic_pay//', views.add_edit_periodic_pay, name='add_periodic_pay'), path('periodic_pay//del/', views.del_periodic_pay, name='del_periodic_pay'), path('lease/add/', views.lease_add, name='lease_add'), - path('ping/', views.abon_ping, name='ping') + path('ping/', views.abon_ping, name='ping'), + path('set_auto_continue_service/', views.set_auto_continue_service, name='set_auto_continue_service') ] group_patterns = [ diff --git a/abonapp/views.py b/abonapp/views.py index 1534e7f..22c3531 100644 --- a/abonapp/views.py +++ b/abonapp/views.py @@ -408,7 +408,6 @@ def add_invoice(request, gid: int, uname: str): @login_required @only_admins @permission_required('abonapp.can_buy_tariff') -@transaction.atomic def pick_tariff(request, gid: int, uname): grp = get_object_or_404(Group, pk=gid) abon = get_object_or_404(models.Abon, username=uname) @@ -719,6 +718,20 @@ def abon_ping(request, gid: int, uname): } +@login_required +@only_admins +@json_view +def set_auto_continue_service(request, gid: int, uname): + checked = request.GET.get('checked') + checked = True if checked == 'true' else False + abon = get_object_or_404(models.Abon, username=uname) + abon.autoconnect_service = checked + abon.save(update_fields=('autoconnect_service',)) + return { + 'status': 0 + } + + @login_required @only_admins def vcards(r): diff --git a/clientsideapp/locale/ru/LC_MESSAGES/django.po b/clientsideapp/locale/ru/LC_MESSAGES/django.po index c8726c4..744d0ac 100644 --- a/clientsideapp/locale/ru/LC_MESSAGES/django.po +++ b/clientsideapp/locale/ru/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2018-08-09 14:57+0300\n" +"POT-Creation-Date: 2018-09-18 15:18+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Dmitry Novikov nerosketch@gmail.com\n" "Language: ru\n" @@ -59,7 +59,7 @@ msgid "Your debt" msgstr "Ваши долги" #: templates/clientsideapp/debts.html:11 -#: templates/clientsideapp/tasklist.html:14 +#: templates/clientsideapp/tasklist.html:13 msgid "State" msgstr "Состояние" @@ -72,7 +72,7 @@ msgid "Description" msgstr "Описание" #: templates/clientsideapp/debts.html:14 -#: templates/clientsideapp/tasklist.html:15 +#: templates/clientsideapp/tasklist.html:14 msgid "Date of create" msgstr "Дата создания" @@ -87,7 +87,7 @@ msgstr "Оплатить" #: templates/clientsideapp/debts.html:23 templates/clientsideapp/ext.html:77 #: templates/clientsideapp/modal_service_buy.html:19 #: templates/clientsideapp/services.html:26 -#: templates/clientsideapp/services.html:55 +#: templates/clientsideapp/services.html:66 msgid "currency" msgstr "руб." @@ -195,6 +195,8 @@ msgid "" "Inbound speed: %(speedIn)s MBit/s
Outgoing speed: %(speedOut)s MBit/" "s
Cost: %(amount)s rubles." msgstr "" +"Входящая скорость: %(speedIn)s MBit/s
Исходяшая скорость: %(speedOut)s " +"MBit/s
Стоимость: %(amount)s руб." #: templates/clientsideapp/modal_service_buy.html:19 #, python-format @@ -202,7 +204,7 @@ msgid "The cost is %(amount)s" msgstr "Стоимость %(amount)s" #: templates/clientsideapp/modal_service_buy.html:23 -#: templates/clientsideapp/services.html:63 +#: templates/clientsideapp/services.html:74 msgid "Pick" msgstr "Заказать" @@ -246,7 +248,26 @@ msgstr "Дата завершения услуги" msgid "Cost" msgstr "Стоимость" -#: templates/clientsideapp/services.html:35 +#: templates/clientsideapp/services.html:28 +msgid "Auto continue service." +msgstr "Автопродление услуги." + +#: templates/clientsideapp/services.html:33 +msgid "" +"The check box Auto continue service for auto-renewal service is " +"designed so that the following automatically connected to the service " +"that you had last month. Connection will occur if the account has enough " +"funds.

If you do not have time to make a Deposit before the service " +"ended, replenish your account and log back into your personal account to " +"connect it.

" +msgstr "" +"

Флажок Автопрдление услуги предназначен для того чтоб в следующем " +"месяце автоматически подключилась услуга которая была у вас в прошлом " +"месяце. Подключение произойдёт если на счету достаточно средств.

Если " +"вы не успели внести средства до того как закочилась услуга, пополните счёт и " +"снова войдите в личный кабинет чтоб подключить её.

" + +#: templates/clientsideapp/services.html:46 msgid "" "Attantion! You have not yet a service, for use the services " "please purchase service you want." @@ -254,27 +275,27 @@ msgstr "" "Внимание! У вас нет услуги, для использования ресурсов " "приобретите нужную услугу из представленных тут." -#: templates/clientsideapp/services.html:46 +#: templates/clientsideapp/services.html:57 msgid "Services available for ordering" msgstr "Доступные для заказа услуги" -#: templates/clientsideapp/services.html:68 +#: templates/clientsideapp/services.html:79 msgid "No services available for ordering" msgstr "Нет доступных для заказа услуг" -#: templates/clientsideapp/tasklist.html:7 +#: templates/clientsideapp/tasklist.html:6 msgid "Task history" msgstr "История заявок" -#: templates/clientsideapp/tasklist.html:16 +#: templates/clientsideapp/tasklist.html:15 msgid "The nature of the damage" msgstr "Характер заявки" -#: templates/clientsideapp/tasklist.html:17 +#: templates/clientsideapp/tasklist.html:16 msgid "Expected or real completion date" msgstr "Ожидаемый или реальный срок выполнения" -#: templates/clientsideapp/tasklist.html:43 +#: templates/clientsideapp/tasklist.html:42 msgid "You didn't leave any requests for breakdowns." msgstr "Заявки по вашей учётной записи не найдены" diff --git a/clientsideapp/templates/clientsideapp/services.html b/clientsideapp/templates/clientsideapp/services.html index 45abb0d..265999b 100644 --- a/clientsideapp/templates/clientsideapp/services.html +++ b/clientsideapp/templates/clientsideapp/services.html @@ -24,8 +24,19 @@
{% trans 'Cost' %}
{{ current_service.tariff.amount }} {% trans 'currency' %}
+ +
{% trans 'Auto continue service.' %}
+
+

{{ current_service.tariff.descr }}

+ {% blocktrans trimmed %} + The check box Auto continue service for auto-renewal service + is designed so that the following automatically connected to the service + that you had last month. Connection will occur if the account has enough funds.

+

If you do not have time to make a Deposit before the service ended, + replenish your account and log back into your personal account to connect it.

+ {% endblocktrans %} diff --git a/clientsideapp/urls.py b/clientsideapp/urls.py index fa6fa18..10ec57a 100644 --- a/clientsideapp/urls.py +++ b/clientsideapp/urls.py @@ -10,5 +10,6 @@ urlpatterns = [ path('services//buy/', views.buy_service, name='buy_service'), path('debts/', views.debts_list, name='debts'), path('debts//', views.debt_buy, name='debt_buy'), - path('tasks/', views.task_history, name='task_history') + path('tasks/', views.task_history, name='task_history'), + path('set_auto_continue_service/', views.set_auto_continue_service, name='set_auto_continue_service') ] diff --git a/clientsideapp/views.py b/clientsideapp/views.py index ae7a063..367e53b 100644 --- a/clientsideapp/views.py +++ b/clientsideapp/views.py @@ -5,6 +5,7 @@ from django.db import transaction from django.utils.translation import gettext_lazy as _, gettext from abonapp.models import AbonLog, InvoiceForPayment, Abon +from djing.lib.decorators import json_view from tariff_app.models import Tariff from taskapp.models import Task from djing.lib import LogicError @@ -43,7 +44,6 @@ def services(request): @login_required -@transaction.atomic def buy_service(request, srv_id): abon = request.user service = get_object_or_404(Tariff, pk=srv_id) @@ -115,3 +115,16 @@ def task_history(request): return render(request, 'clientsideapp/tasklist.html', { 'tasks': tasks }) + + +@login_required +@json_view +def set_auto_continue_service(request): + checked = request.GET.get('checked') + checked = True if checked == 'true' else False + abon = request.user + abon.autoconnect_service = checked + abon.save(update_fields=('autoconnect_service',)) + return { + 'status': 0 + } diff --git a/periodic.py b/periodic.py index ed40fae..128cb0b 100755 --- a/periodic.py +++ b/periodic.py @@ -41,7 +41,8 @@ def main(): AbonTariff.objects.filter(abon=None).delete() now = timezone.now() fields = ('id', 'tariff__title', 'abon__id') - expired_services = AbonTariff.objects.filter(deadline__lt=now).exclude(abon=None) + expired_services = AbonTariff.objects.exclude(abon=None).filter(deadline__lt=now, + abon__autoconnect_service=False) # finishing expires services with transaction.atomic(): @@ -57,6 +58,42 @@ def main(): ) print(log) expired_services.delete() + + # Automatically connect new service + for ex in AbonTariff.objects.filter(deadline__lt=now, abon__autoconnect_service=True).exclude( + abon=None).iterator(): + abon = ex.abon + trf = ex.tariff + amount = round(trf.amount, 2) + if abon.ballance >= amount: + # can continue service + with transaction.atomic(): + abon.ballance -= amount + ex.time_start = now + ex.deadline = None # Deadline sets automatically in signal pre_save + ex.save(update_fields=('time_start', 'deadline')) + abon.save(update_fields=('ballance',)) + # make log about it + l = AbonLog.objects.create( + abon=abon, amount=-amount, + comment="Автоматическое продление услуги '%s'" % trf.title + ) + print(l.comment) + else: + # finish service + with transaction.atomic(): + ex.delete() + l = AbonLog.objects.create( + abon_id=ex.abon.id, + amount=0, + author=None, + date=now, + comment="Срок действия услуги '%(service_name)s' истёк" % { + 'service_name': ex.tariff.title + } + ) + print(l.comment) + signals.pre_delete.connect(abontariff_pre_delete, sender=AbonTariff) # manage periodic pays diff --git a/static/clientside/my_clientside.js b/static/clientside/my_clientside.js index 3fcba4b..10d40a2 100644 --- a/static/clientside/my_clientside.js +++ b/static/clientside/my_clientside.js @@ -40,6 +40,12 @@ $(document).ready(function () { } }); + // autosave checkbox + $('input[type=checkbox].autosave').on('click', function(){ + var data_url = $(this).attr('data-url'); + $.getJSON(data_url, {checked: this.checked}); + }); + $('.btn-modal').on('click', function(){ $.get(this.href, function(r){ show_ModalMyContent(r); diff --git a/static/js/my.js b/static/js/my.js index 80c8ecf..91649f7 100644 --- a/static/js/my.js +++ b/static/js/my.js @@ -297,6 +297,12 @@ $(document).ready(function () { return false; }); + // autosave checkbox + $('input[type=checkbox].autosave').on('click', function(){ + var data_url = $(this).attr('data-url'); + $.getJSON(data_url, {checked: this.checked}); + }); + $('button.player-btn').aplayer(); $('[data-toggle="tooltip"]').tooltip({container:'body'});