diff --git a/abonapp/forms.py b/abonapp/forms.py index 36605a3..eefc816 100644 --- a/abonapp/forms.py +++ b/abonapp/forms.py @@ -226,13 +226,12 @@ class AddIpForm(forms.ModelForm): groups=instance.group ).first() if net is not None: - ips = ( - ip.ip_address for ip in models.Abon.objects.filter( - group__in=net.groups.all() - ).order_by('ip_address').only( - 'ip_address' - ).iterator() - ) + ips = (ip.ip_address for ip in + models.Abon.objects.filter( + group__in=net.groups.all(), + nas=instance.nas + ).order_by('ip_address').only( + 'ip_address').iterator()) free_ip = net.get_free_ip(ips) self.initial['ip_address'] = free_ip else: diff --git a/abonapp/locale/ru/LC_MESSAGES/django.po b/abonapp/locale/ru/LC_MESSAGES/django.po index 2d77f35..758df1c 100644 --- a/abonapp/locale/ru/LC_MESSAGES/django.po +++ b/abonapp/locale/ru/LC_MESSAGES/django.po @@ -1168,3 +1168,6 @@ msgstr "Балланс" msgid "Date joined" msgstr "Дата создания" + +msgid "Update ip address" +msgstr "Обновить ip адрес" diff --git a/abonapp/migrations/0001_squashed_0008_auto_20181115_1206.py b/abonapp/migrations/0001_squashed_0008_auto_20181115_1206.py new file mode 100644 index 0000000..28accc3 --- /dev/null +++ b/abonapp/migrations/0001_squashed_0008_auto_20181115_1206.py @@ -0,0 +1,356 @@ +# Generated by Django 2.1.1 on 2019-03-05 20:00 + +import bitfield.models +import django.core.validators +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + replaces = [('abonapp', '0001_initial'), ('abonapp', '0002_auto_20180808_1448'), ('abonapp', '0003_abon_nas'), + ('abonapp', '0004_auto_20180918_1734'), ('abonapp', '0005_current_tariff'), + ('abonapp', '0006_change_ip'), ('abonapp', '0007_auto_20181101_1545'), + ('abonapp', '0008_auto_20181115_1206')] + + initial = True + + dependencies = [ + ('tariff_app', '0003_auto_20181115_1206'), + ('gw_app', '0001_initial'), + ('tariff_app', '0001_initial'), + ('ip_pool', '0001_initial'), + ('gw_app', '0002_auto_20181101_1545'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('group_app', '0001_initial'), + ('accounts_app', '0001_initial'), + ('devapp', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='AbonTariff', + fields=[ + ('id', models.AutoField( + auto_created=True, primary_key=True, + serialize=False, verbose_name='ID' + )), + ('tariff', models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name='linkto_tariff', + to='tariff_app.Tariff' + )), + ('time_start', models.DateTimeField( + blank=True, default=None, null=True + )), + ('deadline', models.DateTimeField( + blank=True, default=None, null=True + )), + ], + options={ + 'ordering': ('time_start',), + 'permissions': (('can_complete_service', 'finish service perm'),), + 'verbose_name': 'Abon service', + 'verbose_name_plural': 'Abon services', + 'db_table': 'abonent_tariff' + }, + ), + migrations.CreateModel( + name='AbonStreet', + fields=[ + ('id', models.AutoField( + auto_created=True, primary_key=True, + serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=64)), + ('group', models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to='group_app.Group' + )), + ], + options={ + 'verbose_name': 'Street', + 'verbose_name_plural': 'Streets', + 'db_table': 'abon_street', + 'ordering': ('name',), + }, + ), + migrations.CreateModel( + name='Abon', + fields=[ + ('baseaccount_ptr', models.OneToOneField( + auto_created=True, on_delete=django.db.models.deletion.CASCADE, + parent_link=True, primary_key=True, serialize=False, + to='accounts_app.BaseAccount' + )), + ('current_tariff', models.OneToOneField( + blank=True, default=None, null=True, + on_delete=django.db.models.deletion.SET_NULL, + to='abonapp.AbonTariff' + )), + ('group', models.ForeignKey( + blank=True, null=True, + on_delete=django.db.models.deletion.SET_NULL, + to='group_app.Group', verbose_name='User group' + )), + ('ballance', models.FloatField(default=0.0)), + ('ip_address', models.GenericIPAddressField( + blank=True, null=True, + verbose_name='Ip address' + )), + ('description', models.TextField( + blank=True, null=True, verbose_name='Comment' + )), + ('street', models.ForeignKey( + blank=True, null=True, + on_delete=django.db.models.deletion.SET_NULL, + to='abonapp.AbonStreet', verbose_name='Street' + )), + ('house', models.CharField( + blank=True, max_length=12, + null=True, verbose_name='House' + )), + ('device', models.ForeignKey( + blank=True, null=True, + on_delete=django.db.models.deletion.SET_NULL, + to='devapp.Device' + )), + ('dev_port', models.ForeignKey( + blank=True, null=True, + on_delete=django.db.models.deletion.SET_NULL, + to='devapp.Port' + )), + ('is_dynamic_ip', models.BooleanField( + default=False, verbose_name='Is dynamic ip' + )), + ('nas', models.ForeignKey( + blank=True, default=None, null=True, + on_delete=django.db.models.deletion.SET_NULL, + to='gw_app.NASModel', verbose_name='Network access server' + )), + ('autoconnect_service', models.BooleanField( + default=False, verbose_name='Automatically connect next service' + )), + ('last_connected_tariff', models.ForeignKey( + blank=True, default=None, null=True, + on_delete=django.db.models.deletion.SET_NULL, + to='tariff_app.Tariff', + verbose_name='Last connected service' + )), + ('markers', bitfield.models.BitField( + (('icon_donkey', 'Donkey'), ('icon_fire', 'Fire'), + ('icon_ok', 'Ok'), ('icon_king', 'King'), + ('icon_tv', 'TV'), ('icon_smile', 'Smile'), + ('icon_dollar', 'Dollar'), ('icon_service', 'Service'), + ('icon_mrk', 'Marker')), default=0 + )), + ], + options={ + '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', + 'db_table': 'abonent', + 'unique_together': {('ip_address', 'nas')} + }, + bases=('accounts_app.baseaccount',), + ), + migrations.CreateModel( + name='AbonLog', + fields=[ + ('id', models.AutoField( + auto_created=True, primary_key=True, + serialize=False, verbose_name='ID' + )), + ('abon', models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to='abonapp.Abon')), + ('amount', models.FloatField(default=0.0)), + ('author', models.ForeignKey( + blank=True, null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='+', to=settings.AUTH_USER_MODEL + )), + ('comment', models.CharField(max_length=128)), + ('date', models.DateTimeField(auto_now_add=True)), + ], + options={ + 'db_table': 'abonent_log', + 'ordering': ('-date',) + }, + ), + migrations.CreateModel( + name='AdditionalTelephone', + fields=[ + ('abon', models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name='additional_telephones', + to='abonapp.Abon' + )), + ('id', models.AutoField( + auto_created=True, primary_key=True, + serialize=False, verbose_name='ID' + )), + ('telephone', models.CharField( + max_length=16, validators=[ + django.core.validators.RegexValidator('^(\\+[7,8,9,3]\\d{10,11})?$') + ], + verbose_name='Telephone') + ), + ('owner_name', models.CharField(max_length=127)), + ], + options={ + 'verbose_name': 'Additional telephone', + 'verbose_name_plural': 'Additional telephones', + 'db_table': 'additional_telephones', + 'ordering': ('owner_name',), + }, + ), + migrations.CreateModel( + name='AllPayLog', + fields=[ + ('pay_id', models.CharField(max_length=64, primary_key=True, serialize=False)), + ('date_action', models.DateTimeField(auto_now_add=True)), + ('summ', models.FloatField(default=0.0)), + ('pay_system_name', models.CharField(max_length=16)), + ], + options={ + 'db_table': 'all_pay_log', + 'ordering': ('-date_action',), + }, + ), + migrations.CreateModel( + name='AllTimePayLog', + fields=[ + ('abon', + models.ForeignKey( + blank=True, default=None, null=True, + on_delete=django.db.models.deletion.SET_DEFAULT, + to='abonapp.Abon' + )), + ('pay_id', models.CharField( + max_length=36, primary_key=True, + serialize=False, unique=True + )), + ('date_add', models.DateTimeField(auto_now_add=True)), + ('summ', models.FloatField(default=0.0)), + ('trade_point', models.CharField( + blank=True, default=None, max_length=20, + null=True, verbose_name='Trade point' + )), + ('receipt_num', models.BigIntegerField(default=0, verbose_name='Receipt number')), + ], + options={ + 'db_table': 'all_time_pay_log', + 'ordering': ('-date_add',), + }, + ), + migrations.CreateModel( + name='InvoiceForPayment', + fields=[ + ('id', models.AutoField( + auto_created=True, primary_key=True, + serialize=False, verbose_name='ID' + )), + ('abon', models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to='abonapp.Abon' + )), + ('status', models.BooleanField(default=False)), + ('amount', models.FloatField(default=0.0)), + ('comment', models.CharField(max_length=128)), + ('date_create', models.DateTimeField(auto_now_add=True)), + ('date_pay', models.DateTimeField(blank=True, null=True)), + ('author', models.ForeignKey( + blank=True, null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='+', to=settings.AUTH_USER_MODEL + )) + ], + options={ + 'verbose_name': 'Debt', + 'verbose_name_plural': 'Debts', + 'db_table': 'abonent_inv_pay', + 'ordering': ('date_create',) + }, + ), + migrations.CreateModel( + name='PassportInfo', + fields=[ + ('id', models.AutoField( + auto_created=True, primary_key=True, + serialize=False, verbose_name='ID' + )), + ('series', models.CharField( + max_length=4, validators=[django.core.validators.integer_validator], + verbose_name='Pasport serial' + )), + ('number', models.CharField( + max_length=6, validators=[django.core.validators.integer_validator], + verbose_name='Pasport number' + )), + ('distributor', models.CharField( + max_length=64, verbose_name='Distributor' + )), + ('date_of_acceptance', models.DateField( + verbose_name='Date of acceptance' + )), + ('abon', models.OneToOneField( + blank=True, null=True, + on_delete=django.db.models.deletion.CASCADE, + to='abonapp.Abon' + )) + ], + options={ + 'ordering': ('series',), + 'verbose_name': 'Passport Info', + 'verbose_name_plural': 'Passport Info', + 'db_table': 'passport_info' + }, + ), + migrations.CreateModel( + name='PeriodicPayForId', + fields=[ + ('id', models.AutoField( + auto_created=True, primary_key=True, + serialize=False, verbose_name='ID' + )), + ('periodic_pay', models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to='tariff_app.PeriodicPay', + verbose_name='Periodic pay' + )), + ('last_pay', models.DateTimeField( + blank=True, null=True, verbose_name='Last pay time' + )), + ('next_pay', models.DateTimeField( + verbose_name='Next time to pay' + )), + ('account', models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to='abonapp.Abon', + verbose_name='Account' + )) + ], + options={ + 'db_table': 'periodic_pay_for_id', + 'ordering': ('last_pay',) + }, + ), + migrations.CreateModel( + name='AbonRawPassword', + fields=[ + ('account', models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + primary_key=True, serialize=False, + to='abonapp.Abon' + )), + ('passw_text', models.CharField(max_length=64)), + ], + options={ + 'db_table': 'abon_raw_password', + }, + ), + ] diff --git a/abonapp/migrations/0002_auto_20180808_1448.py b/abonapp/migrations/0002_auto_20180808_1448.py index 93694e6..71f992f 100644 --- a/abonapp/migrations/0002_auto_20180808_1448.py +++ b/abonapp/migrations/0002_auto_20180808_1448.py @@ -19,12 +19,15 @@ TMP_FILE = '/tmp/djing_ip_field_abonapp_migrate.json' def backup_info(apps, _): Abon = apps.get_model('abonapp', 'Abon') obs = Abon.objects.exclude(ip_address=None).only('ip_address', 'is_dynamic_ip') - with open(TMP_FILE, 'w') as f: - serializers.serialize('json', obs, stream=f, fields=('ip_address', 'is_dynamic_ip')) + if obs.exists(): + with open(TMP_FILE, 'w') as f: + serializers.serialize('json', obs, stream=f, fields=('ip_address', 'is_dynamic_ip')) def restore_info_to_new_scheme(apps, _): Abon = apps.get_model('abonapp', 'Abon') + if not os.path.isfile(TMP_FILE): + return with open(TMP_FILE, 'r') as f: for abon in load(f): ip_addr = abon['fields'].get('ip_address') diff --git a/abonapp/models.py b/abonapp/models.py index 3f58cd8..3e0dd2b 100644 --- a/abonapp/models.py +++ b/abonapp/models.py @@ -7,8 +7,7 @@ from django.conf import settings from django.core import validators from django.core.validators import RegexValidator from django.db import models, transaction -from django.db.models.signals import post_delete, pre_delete, post_init, \ - pre_save +from django.db.models.signals import post_init, pre_save from django.dispatch import receiver from django.shortcuts import resolve_url from django.utils import timezone @@ -349,24 +348,6 @@ class Abon(BaseAccount): except LogicError: pass - def nas_remove_self(self): - """ - Will remove this user to network access server - :return: - """ - if self.nas is None: - raise LogicError(_('gateway required')) - try: - agent_abon = self.build_agent_struct() - if agent_abon is not None: - mngr = self.nas.get_nas_manager() - mngr.remove_user(agent_abon) - except (NasFailedResult, NasNetworkError, ConnectionResetError) as e: - print('ERROR:', e) - return e - except LogicError: - pass - def get_absolute_url(self): return resolve_url('abonapp:abon_home', self.group.id, self.username) @@ -539,17 +520,6 @@ class PeriodicPayForId(models.Model): ordering = ('last_pay',) -@receiver(post_delete, sender=Abon) -def abon_del_signal(sender, **kwargs): - abon = kwargs.get("instance") - if abon is None: - raise ValueError('Instance does not passed to a signal') - try: - abon.nas_remove_self() - except (NasFailedResult, NasNetworkError, LogicError): - return True - - @receiver(post_init, sender=AbonTariff) def abon_tariff_post_init(sender, **kwargs): abon_tariff = kwargs["instance"] @@ -566,17 +536,3 @@ def abon_tariff_pre_save(sender, **kwargs): 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_delete, sender=AbonTariff) -def abontariff_pre_delete(sender, **kwargs): - abon_tariff = kwargs.get("instance") - if abon_tariff is None: - raise ValueError('Instance does not passed to a signal') - try: - abon = Abon.objects.get(current_tariff=abon_tariff) - abon.nas_remove_self() - except (NasFailedResult, NasNetworkError, LogicError): - return True - except Abon.DoesNotExist: - print('Error: abontariff_pre_delete - user not found') diff --git a/abonapp/tasks.py b/abonapp/tasks.py new file mode 100644 index 0000000..22e8773 --- /dev/null +++ b/abonapp/tasks.py @@ -0,0 +1,47 @@ +from celery import shared_task + +from abonapp.models import Abon +from djing.lib import LogicError +from gw_app.models import NASModel +from gw_app.nas_managers import NasFailedResult, NasNetworkError, SubnetQueue + + +@shared_task +def customer_nas_command(customer_uid: int, command: str): + if command not in ('add', 'sync'): + return 'Command required' + try: + cust = Abon.objects.get(pk=customer_uid) + print(cust, command) + if command == 'sync': + r = cust.nas_sync_self() + if isinstance(r, Exception): + return 'ABONAPP SYNC ERROR: %s' % r + elif command == 'add': + cust.nas_add_self() + else: + return 'ABONAPP SYNC ERROR: Unknown command "%s"' % command + except Abon.DoesNotExist: + pass + except (LogicError, NasFailedResult, NasNetworkError, ConnectionResetError) as e: + return 'ABONAPP ERROR: %s' % e + + +@shared_task +def customer_nas_remove(customer_uid: int, ip_addr: str, speed: tuple, is_access: bool, nas_pk: int): + try: + if not isinstance(ip_addr, (str, int)): + ip_addr = str(ip_addr) + sq = SubnetQueue( + name="uid%d" % customer_uid, + network=ip_addr, + max_limit=speed, + is_access=is_access + ) + nas = NASModel.objects.get(pk=nas_pk) + mngr = nas.get_nas_manager() + mngr.remove_user(sq) + except (ValueError, NasFailedResult, NasNetworkError, LogicError) as e: + return 'ABONAPP ERROR: %s' % e + except NASModel.DoesNotExist: + return 'NASModel.DoesNotExist id=%d' % nas_pk diff --git a/abonapp/templates/abonapp/debtors.html b/abonapp/templates/abonapp/debtors.html index 01d1a2b..dda83d5 100644 --- a/abonapp/templates/abonapp/debtors.html +++ b/abonapp/templates/abonapp/debtors.html @@ -31,13 +31,19 @@ {% for invoice in invoices %}