diff --git a/abonapp/admin.py b/abonapp/admin.py index f98011b..afd453a 100644 --- a/abonapp/admin.py +++ b/abonapp/admin.py @@ -1,13 +1,12 @@ from django.contrib import admin -from abonapp.models import generic +from abonapp import models -admin.site.register(generic.Abon) -admin.site.register(generic.InvoiceForPayment) -admin.site.register(generic.AbonLog) -admin.site.register(generic.AbonTariff) -admin.site.register(generic.AbonStreet) -admin.site.register(generic.AllTimePayLog) -admin.site.register(generic.AbonRawPassword) -admin.site.register(generic.PassportInfo) -admin.site.register(generic.AdditionalTelephone) +admin.site.register(models.Abon) +admin.site.register(models.InvoiceForPayment) +admin.site.register(models.AbonLog) +admin.site.register(models.AbonTariff) +admin.site.register(models.AbonStreet) +admin.site.register(models.AbonRawPassword) +admin.site.register(models.PassportInfo) +admin.site.register(models.AdditionalTelephone) diff --git a/abonapp/forms.py b/abonapp/forms.py index 3a32dda..36605a3 100644 --- a/abonapp/forms.py +++ b/abonapp/forms.py @@ -7,7 +7,7 @@ from string import digits, ascii_lowercase from djing.lib import LogicError from ip_pool.models import NetworkModel from gw_app.models import NASModel -from abonapp.models import generic +from abonapp import models from django.conf import settings @@ -21,12 +21,12 @@ def _generate_random_chars(length=6, chars=digits, split=2, delimiter=''): ) try: - generic.Abon.objects.get(username=username) + models.Abon.objects.get(username=username) return _generate_random_chars( length=length, chars=chars, split=split, delimiter=delimiter ) - except generic.Abon.DoesNotExist: + except models.Abon.DoesNotExist: return username @@ -44,11 +44,11 @@ class AbonForm(forms.ModelForm): super(AbonForm, self).__init__(*args, **kwargs) instance = getattr(self, 'instance') if instance is not None and instance.group is not None: - abon_group_queryset = generic.AbonStreet.objects.filter( + abon_group_queryset = models.AbonStreet.objects.filter( group=instance.group ) elif 'group' in self.initial.keys() and self.initial['group'] is not None: - abon_group_queryset = generic.AbonStreet.objects.filter( + abon_group_queryset = models.AbonStreet.objects.filter( group=self.initial['group'] ) else: @@ -75,7 +75,7 @@ class AbonForm(forms.ModelForm): ) class Meta: - model = generic.Abon + model = models.Abon fields = ('username', 'telephone', 'fio', 'group', 'description', 'street', 'house', 'is_active', 'nas') widgets = { @@ -100,11 +100,11 @@ class AbonForm(forms.ModelForm): if commit: acc.save() try: - abon_raw_passw = generic.AbonRawPassword.objects.get(account=acc) + abon_raw_passw = models.AbonRawPassword.objects.get(account=acc) abon_raw_passw.passw_text = raw_password abon_raw_passw.save(update_fields=('passw_text',)) - except generic.AbonRawPassword.DoesNotExist: - generic.AbonRawPassword.objects.create( + except models.AbonRawPassword.DoesNotExist: + models.AbonRawPassword.objects.create( account=acc, passw_text=raw_password ) @@ -113,7 +113,7 @@ class AbonForm(forms.ModelForm): class PassportForm(forms.ModelForm): class Meta: - model = generic.PassportInfo + model = models.PassportInfo exclude = ('abon',) widgets = { 'series': forms.TextInput(attrs={ @@ -132,7 +132,7 @@ class PassportForm(forms.ModelForm): class AbonStreetForm(forms.ModelForm): class Meta: - model = generic.AbonStreet + model = models.AbonStreet fields = '__all__' widgets = { 'name': forms.TextInput(attrs={ @@ -145,7 +145,7 @@ class AbonStreetForm(forms.ModelForm): class AdditionalTelephoneForm(forms.ModelForm): class Meta: - model = generic.AdditionalTelephone + model = models.AdditionalTelephone exclude = ('abon',) widgets = { 'telephone': forms.TextInput(attrs={ @@ -165,7 +165,7 @@ class AdditionalTelephoneForm(forms.ModelForm): class PeriodicPayForIdForm(forms.ModelForm): class Meta: - model = generic.PeriodicPayForId + model = models.PeriodicPayForId exclude = ('account',) @@ -194,7 +194,7 @@ class ExportUsersForm(forms.Form): class MarkersForm(forms.ModelForm): class Meta: - model = generic.Abon + model = models.Abon fields = 'markers', def save(self, commit=True): @@ -227,7 +227,7 @@ class AddIpForm(forms.ModelForm): ).first() if net is not None: ips = ( - ip.ip_address for ip in generic.Abon.objects.filter( + ip.ip_address for ip in models.Abon.objects.filter( group__in=net.groups.all() ).order_by('ip_address').only( 'ip_address' @@ -245,5 +245,5 @@ class AddIpForm(forms.ModelForm): ) class Meta: - model = generic.Abon + model = models.Abon fields = 'ip_address', diff --git a/abonapp/migrations/0009_auto_20181123_1556.py b/abonapp/migrations/0009_auto_20181123_1556.py new file mode 100644 index 0000000..c204f0a --- /dev/null +++ b/abonapp/migrations/0009_auto_20181123_1556.py @@ -0,0 +1,16 @@ +# Generated by Django 2.1 on 2018-11-23 15:56 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('abonapp', '0008_auto_20181115_1206'), + ] + + operations = [ + migrations.DeleteModel( + name='AllPayLog', + ), + ] diff --git a/abonapp/models/generic.py b/abonapp/models.py similarity index 89% rename from abonapp/models/generic.py rename to abonapp/models.py index 2598abd..3923dc6 100644 --- a/abonapp/models/generic.py +++ b/abonapp/models.py @@ -6,7 +6,7 @@ from bitfield import BitField 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 import models, transaction from django.db.models.signals import post_delete, pre_delete, post_init, \ pre_save from django.dispatch import receiver @@ -22,8 +22,10 @@ from tariff_app.models import Tariff, PeriodicPay class AbonLog(models.Model): abon = models.ForeignKey('Abon', on_delete=models.CASCADE) amount = models.FloatField(default=0.0) - author = models.ForeignKey(UserProfile, on_delete=models.SET_NULL, - related_name='+', blank=True, null=True) + author = models.ForeignKey( + UserProfile, on_delete=models.SET_NULL, + related_name='+', blank=True, null=True + ) comment = models.CharField(max_length=128) date = models.DateTimeField(auto_now_add=True) @@ -452,79 +454,9 @@ class InvoiceForPayment(models.Model): verbose_name_plural = _('Debts') -class AllTimePayLogManager(models.Manager): - @staticmethod - def by_days(): - cur = connection.cursor() - cur.execute( - 'SELECT SUM(summ) AS alsum, ' - 'DATE_FORMAT(date_add, "%Y-%m-%d") AS pay_date ' - 'FROM all_time_pay_log ' - 'GROUP BY DATE_FORMAT(date_add, "%Y-%m-%d")' - ) - while True: - r = cur.fetchone() - if r is None: - break - summ, dat = r - yield { - 'summ': summ, - 'pay_date': datetime.strptime(dat, '%Y-%m-%d') - } - - -# Log for pay system "AllTime" -class AllTimePayLog(models.Model): - abon = models.ForeignKey( - Abon, - on_delete=models.SET_DEFAULT, - blank=True, - null=True, - default=None - ) - pay_id = models.CharField( - max_length=36, - unique=True, - primary_key=True - ) - date_add = models.DateTimeField(auto_now_add=True) - summ = models.FloatField(default=0.0) - trade_point = models.CharField( - _('Trade point'), - max_length=20, - default=None, - null=True, - blank=True - ) - receipt_num = models.BigIntegerField(_('Receipt number'), default=0) - - objects = AllTimePayLogManager() - - def __str__(self): - return self.pay_id - - class Meta: - db_table = 'all_time_pay_log' - ordering = ('-date_add',) - - -# log for all terminals -class AllPayLog(models.Model): - pay_id = models.CharField(max_length=64, primary_key=True) - date_action = models.DateTimeField(auto_now_add=True) - summ = models.FloatField(default=0.0) - pay_system_name = models.CharField(max_length=16) - - def __str__(self): - return self.pay_system_name - - class Meta: - db_table = 'all_pay_log' - ordering = ('-date_action',) - - class AbonRawPassword(models.Model): account = models.OneToOneField(Abon, models.CASCADE, primary_key=True) + # TODO: make password to EncryptedCharField passw_text = models.CharField(max_length=64) def __str__(self): diff --git a/abonapp/pay_systems.py b/abonapp/pay_systems.py deleted file mode 100644 index 88667d4..0000000 --- a/abonapp/pay_systems.py +++ /dev/null @@ -1,104 +0,0 @@ -from hashlib import md5 -from django.utils import timezone -from djing.lib import safe_int, safe_float -from .models import Abon, AllTimePayLog -from django.db import DatabaseError -from django.conf import settings -from xmlview.decorators import xml_view - - -@xml_view(root_node='pay-response') -def allpay(request): - def bad_ret(err_id, err_description=None): - now = timezone.now() - r = { - 'status_code': safe_int(err_id), - 'time_stamp': now.strftime("%d.%m.%Y %H:%M") - } - if err_description: - r.update({'description': err_description}) - return r - - try: - serv_id = request.GET.get('SERVICE_ID') - act = safe_int(request.GET.get('ACT')) - pay_account = request.GET.get('PAY_ACCOUNT') - pay_id = request.GET.get('PAY_ID') - pay_amount = safe_float(request.GET.get('PAY_AMOUNT')) - sign = request.GET.get('SIGN').lower() - current_date = timezone.now().strftime("%d.%m.%Y %H:%M") - - if act <= 0: - return bad_ret(-101, 'ACT less than zero') - - # check sign - md = md5() - s = '_'.join((str(act), pay_account or '', serv_id or '', pay_id, getattr(settings, 'PAY_SECRET'))) - md.update(bytes(s, 'utf-8')) - our_sign = md.hexdigest() - if our_sign != sign: - return bad_ret(-101) - - if act == 1: - abon = Abon.objects.get(username=pay_account) - fio = abon.fio - ballance = float(abon.ballance) - return { - 'balance': ballance, - 'name': fio, - 'account': pay_account, - 'service_id': getattr(settings, 'PAY_SERV_ID'), - 'min_amount': 10.0, - 'max_amount': 5000, - 'status_code': 21, - 'time_stamp': current_date - } - 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) - pays = AllTimePayLog.objects.filter(pay_id=pay_id) - if pays.count() > 0: - return bad_ret(-100) - - abon.add_ballance(None, pay_amount, comment='AllPay %.2f' % pay_amount) - abon.save(update_fields=('ballance',)) - - AllTimePayLog.objects.create( - pay_id=pay_id, - summ=pay_amount, - abon=abon, - trade_point=trade_point, - receipt_num=receipt_num - ) - return { - 'pay_id': pay_id, - 'service_id': serv_id, - 'amount': pay_amount, - 'status_code': 22, - 'time_stamp': current_date - } - elif act == 7: - pay = AllTimePayLog.objects.get(pay_id=pay_id) - return { - 'status_code': 11, - 'time_stamp': current_date, - 'transaction': { - 'pay_id': pay_id, - 'service_id': serv_id, - 'amount': pay.summ, - 'status': 111, - 'time_stamp': pay.date_add.strftime("%d.%m.%Y %H:%M") - } - } - else: - return bad_ret(-101, 'ACT is not passed') - - except Abon.DoesNotExist: - return bad_ret(-40) - except DatabaseError: - return bad_ret(-90) - except AllTimePayLog.DoesNotExist: - return bad_ret(-10) - except AttributeError: - return bad_ret(-101) diff --git a/abonapp/templates/abonapp/group_list.html b/abonapp/templates/abonapp/group_list.html index 0347216..a243df0 100644 --- a/abonapp/templates/abonapp/group_list.html +++ b/abonapp/templates/abonapp/group_list.html @@ -65,11 +65,6 @@ {% endif %} - {% if request.user.is_superuser %} - - - - {% endif %} diff --git a/abonapp/templates/abonapp/invoiceForPayment.html b/abonapp/templates/abonapp/invoiceForPayment.html index 594339e..34c67a0 100644 --- a/abonapp/templates/abonapp/invoiceForPayment.html +++ b/abonapp/templates/abonapp/invoiceForPayment.html @@ -6,7 +6,6 @@
  • {% trans 'User groups' %}
  • {{ group.title }}
  • -
  • {{ abon.fio }}
  • {% trans 'Debts' %}
  • {% endblock %} diff --git a/abonapp/tests.py b/abonapp/tests.py index 5a7d896..bfe2ddf 100644 --- a/abonapp/tests.py +++ b/abonapp/tests.py @@ -6,13 +6,9 @@ from accounts_app.models import UserProfile from django.shortcuts import resolve_url from django.test import TestCase, RequestFactory from django.conf import settings -from django.utils import timezone -from django.utils.html import escape from django.utils.translation import gettext_lazy as _ -from xmltodict import parse -from abonapp.models.generic import Abon, AbonStreet, PassportInfo -from abonapp.pay_systems import allpay +from abonapp.models import Abon, AbonStreet, PassportInfo from group_app.models import Group from tariff_app.models import Tariff from ip_pool.models import NetworkModel @@ -57,192 +53,6 @@ class MyBaseTestCase(metaclass=ABCMeta): self.group = grp -class AllPayTestCase(MyBaseTestCase, TestCase): - pay_url = '/' - time_format = '%d.%m.%Y %H:%M' - - def setUp(self): - a1 = Abon.objects.create_user( - telephone='+79785276481', - username='pay_account1', - password='passw1' - ) - a1.ballance = -13.12 - a1.fio = 'Test Name' - a1.save(update_fields=('ballance', 'fio')) - # Abon.objects.create_user( - # telephone='+79788163841', - # username='pay_account2', - # password='passw2' - # ) - - def user_pay_view(self): - print('test_user_pay_view') - current_date = timezone.now().strftime(self.time_format) - service_id = getattr(settings, 'PAY_SERV_ID') - r = allpay(rf.get(self.pay_url, { - 'ACT': 1, - 'PAY_ACCOUNT': 'pay_account1', - 'SERVICE_ID': service_id, - 'PAY_ID': '840ab457-e7d1-4494-8197-9570da035170', - 'TRADE_POINT': 'term1', - 'SIGN': _make_sign(1, 'pay_account1', service_id, '840ab457-e7d1-4494-8197-9570da035170') - } - )) - r = r.content.decode('utf-8') - o = ''.join(( - "", - "-13.12", - "Test Name", - "pay_account1", - "%s" % escape(service_id), - "10.0", - "5000", - "21", - "%s" % escape(current_date), - "" - )) - self.assertXMLEqual(r, o) - - def user_pay_pay(self): - print('test_user_pay_pay') - current_date = timezone.now().strftime(self.time_format) - service_id = getattr(settings, 'PAY_SERV_ID') - r = allpay(rf.get(self.pay_url, { - 'ACT': 4, - 'PAY_ACCOUNT': 'pay_account1', - 'PAY_AMOUNT': 18.21, - 'RECEIPT_NUM': 2126235, - 'SERVICE_ID': service_id, - 'PAY_ID': '840ab457-e7d1-4494-8197-9570da035170', - 'TRADE_POINT': 'term1', - 'SIGN': _make_sign(4, 'pay_account1', service_id, '840ab457-e7d1-4494-8197-9570da035170') - })) - r = r.content.decode('utf-8') - xml = ''.join(( - "", - "840ab457-e7d1-4494-8197-9570da035170", - "%s" % escape(service_id), - "18.21", - "22", - "%s" % escape(current_date), - "" - )) - self.test_pay_time = current_date - self.assertXMLEqual(r, xml) - - def user_pay_check(self): - print('test_user_pay_check') - current_date = timezone.now().strftime(self.time_format) - service_id = getattr(settings, 'PAY_SERV_ID') - r = allpay(rf.get(self.pay_url, - { - 'ACT': 7, - 'SERVICE_ID': service_id, - 'PAY_ID': '840ab457-e7d1-4494-8197-9570da035170', - 'SIGN': _make_sign(7, '', service_id, '840ab457-e7d1-4494-8197-9570da035170') - } - )) - r = r.content.decode('utf-8') - xml = ''.join(( - "", - "11", - "%s" % escape(current_date), - "", - "840ab457-e7d1-4494-8197-9570da035170", - "%s" % escape(service_id), - "18.21", - "111", - "%s" % escape(self.test_pay_time), - "" - "" - )) - self.assertXMLEqual(r, xml) - - def check_ballance(self): - print('check_ballance') - service_id = getattr(settings, 'PAY_SERV_ID') - r = allpay(rf.get(self.pay_url, - { - 'ACT': 1, - 'PAY_ACCOUNT': 'pay_account1', - 'SERVICE_ID': service_id, - 'PAY_ID': '840ab457-e7d1-4494-8197-9570da035170', - 'TRADE_POINT': 'term1', - 'SIGN': _make_sign(1, 'pay_account1', service_id, '840ab457-e7d1-4494-8197-9570da035170') - } - )) - r = r.content.decode('utf-8') - r = parse(r) - bl = float(r['pay-response']['balance']) - self.assertEqual(bl, 5.09) - - def test_client_does_not_exist(self): - print('test_client_does_not_exist') - current_date = timezone.now().strftime(self.time_format) - service_id = getattr(settings, 'PAY_SERV_ID') - r = allpay(rf.get(self.pay_url, { - 'ACT': 1, - 'PAY_ACCOUNT': 'not_existing_acc', - 'SERVICE_ID': service_id, - 'PAY_ID': '840ab457-e7d1-4494-8197-9570da035170', - 'TRADE_POINT': 'term1', - 'SIGN': _make_sign(1, 'not_existing_acc', service_id, '840ab457-e7d1-4494-8197-9570da035170') - } - )) - r = r.content.decode('utf-8') - self.assertXMLEqual(r, ''.join(( - "", - "-40", - "%s" % escape(current_date), - "" - ))) - - def try_pay_double(self): - print('try_pay_double') - service_id = getattr(settings, 'PAY_SERV_ID') - r = allpay(rf.get(self.pay_url, { - 'ACT': 4, - 'PAY_ACCOUNT': 'pay_account1', - 'SERVICE_ID': service_id, - 'PAY_ID': '840ab457-e7d1-4494-8197-9570da035170', - 'TRADE_POINT': 'term1', - 'SIGN': _make_sign(4, 'pay_account1', service_id, '840ab457-e7d1-4494-8197-9570da035170') - })) - r = r.content.decode('utf-8') - r = parse(r) - status_code = int(r['pay-response']['status_code']) - self.assertEqual(status_code, -100) - - def non_existing_pay(self): - print('non_existing_pay') - current_date = timezone.now().strftime(self.time_format) - uuid = '9f154e93-d800-419a-92f7-da33529138be' - service_id = getattr(settings, 'PAY_SERV_ID') - r = allpay(rf.get(self.pay_url, { - 'ACT': 7, - 'SERVICE_ID': service_id, - 'PAY_ID': uuid, - 'SIGN': _make_sign(7, '', service_id, uuid) - })) - r = r.content.decode('utf-8') - xml = ''.join(( - "", - "-10", - "%s" % escape(current_date), - "" - )) - self.assertXMLEqual(r, xml) - - def test_pays(self): - self.user_pay_view() - self.user_pay_pay() - self.user_pay_check() - self.check_ballance() - self.try_pay_double() - self.non_existing_pay() - - class StreetTestCase(MyBaseTestCase, TestCase): group = None street = None diff --git a/abonapp/urls.py b/abonapp/urls.py index 16092a6..e5585cd 100644 --- a/abonapp/urls.py +++ b/abonapp/urls.py @@ -50,16 +50,13 @@ group_patterns = [ urlpatterns = [ path('', views.GroupListView.as_view(), name='group_list'), - path('fin_report/', views.fin_report, name='fin_report'), path('/', include(group_patterns)), path('log/', views.LogListView.as_view(), name='log'), - path('pay/', views.terminal_pay, name='terminal_pay'), path('debtors/', views.DebtorsListView.as_view(), name='debtors'), path('contacts/vcards/', views.vcards, name='vcards'), # Api's path('api/abons/', views.abons), path('api/abon_filter/', views.search_abon), - path('api/dhcp_lever/', views.DhcpLever.as_view()), - path('api/duplicate_pay/', views.DublicatePay.as_view()) + path('api/dhcp_lever/', views.DhcpLever.as_view()) ] diff --git a/abonapp/views.py b/abonapp/views.py index 4c444a9..379336d 100644 --- a/abonapp/views.py +++ b/abonapp/views.py @@ -11,14 +11,13 @@ from django.contrib.auth.mixins import LoginRequiredMixin, \ PermissionRequiredMixin as PermissionRequiredMixin_django, \ PermissionRequiredMixin from django.core.exceptions import PermissionDenied, ValidationError -from django.db import IntegrityError, ProgrammingError, transaction, \ - DatabaseError +from django.db import IntegrityError, ProgrammingError, transaction from django.db.models import Count, Q from django.http import HttpResponse, HttpResponseBadRequest, \ HttpResponseRedirect from django.shortcuts import render, redirect, get_object_or_404, resolve_url from django.urls import reverse_lazy -from django.utils import timezone + from django.utils.decorators import method_decorator from django.utils.translation import gettext_lazy as _ from django.views.generic import ListView, UpdateView, CreateView, DeleteView @@ -37,9 +36,8 @@ from gw_app.nas_managers import NasFailedResult, NasNetworkError from ip_pool.models import NetworkModel from tariff_app.models import Tariff from taskapp.models import Task -from xmlview.decorators import xml_view from abonapp import forms -from abonapp.models import generic +from abonapp import models class PeoplesListView(LoginRequiredMixin, OnlyAdminsMixin, @@ -49,7 +47,7 @@ class PeoplesListView(LoginRequiredMixin, OnlyAdminsMixin, def get_queryset(self): street_id = lib.safe_int(self.request.GET.get('street')) gid = lib.safe_int(self.kwargs.get('gid')) - peoples_list = generic.Abon.objects.filter(group__pk=gid) + peoples_list = models.Abon.objects.filter(group__pk=gid) if street_id > 0: peoples_list = peoples_list.filter(street=street_id) peoples_list = peoples_list.select_related( @@ -75,7 +73,7 @@ class PeoplesListView(LoginRequiredMixin, OnlyAdminsMixin, context = super(PeoplesListView, self).get_context_data(**kwargs) - context['streets'] = generic.AbonStreet.objects.filter( + context['streets'] = models.AbonStreet.objects.filter( group=gid ).only('name') context['street_id'] = lib.safe_int(self.request.GET.get('street')) @@ -104,7 +102,7 @@ class AbonCreateView(LoginRequiredMixin, OnlyAdminsMixin, group = None abon = None form_class = forms.AbonForm - model = generic.Abon + model = models.Abon template_name = 'abonapp/addAbon.html' context_object_name = 'group' @@ -161,7 +159,7 @@ class AbonCreateView(LoginRequiredMixin, OnlyAdminsMixin, class DelAbonDeleteView(LoginAdminMixin, PermissionRequiredMixin, DeleteView): permission_required = 'abonapp.delete_abon' - model = generic.Abon + model = models.Abon slug_url_kwarg = 'uname' slug_field = 'username' success_url = reverse_lazy('abonapp:group_list') @@ -203,7 +201,7 @@ class DelAbonDeleteView(LoginAdminMixin, PermissionRequiredMixin, DeleteView): @permission_required('abonapp.can_add_ballance') @transaction.atomic def abonamount(request, gid: int, uname): - abon = get_object_or_404(generic.Abon, username=uname) + abon = get_object_or_404(models.Abon, username=uname) frm = None try: if request.method == 'POST': @@ -218,7 +216,7 @@ def abonamount(request, gid: int, uname): messages.success( request, _('Account filled successfully on %.2f') % amnt ) - return redirect('abonapp:abon_phistory', gid=gid, uname=uname) + return redirect('abonapp:abon_home', gid=gid, uname=uname) else: messages.error(request, _('I not know the account id')) else: @@ -244,10 +242,10 @@ class DebtsListView(LoginAdminPermissionMixin, OrderedFilteredList): return self.abon.group def get_queryset(self): - abon = get_object_or_404(generic.Abon, + abon = get_object_or_404(models.Abon, username=self.kwargs.get('uname')) self.abon = abon - return generic.InvoiceForPayment.objects.filter(abon=abon) + return models.InvoiceForPayment.objects.filter(abon=abon) def get_context_data(self, **kwargs): context = super(DebtsListView, self).get_context_data(**kwargs) @@ -256,39 +254,13 @@ class DebtsListView(LoginAdminPermissionMixin, OrderedFilteredList): return context -class PayHistoryListView(LoginAdminPermissionMixin, OrderedFilteredList): - permission_required = 'group_app.view_group' - context_object_name = 'pay_history' - template_name = 'abonapp/payHistory.html' - - def get_permission_object(self): - if hasattr(self, 'abon'): - return self.abon.group - return generic.Group.objects.filter(pk=self.kwargs.get('gid')).first() - - def get_queryset(self): - abon = get_object_or_404(generic.Abon, - username=self.kwargs.get('uname')) - self.abon = abon - pay_history = generic.AbonLog.objects.filter( - abon=abon - ).order_by('-date') - return pay_history - - def get_context_data(self, **kwargs): - context = super(PayHistoryListView, self).get_context_data(**kwargs) - context['group'] = self.abon.group - context['abon'] = self.abon - return context - - @login_required @only_admins def abon_services(request, gid: int, uname): grp = get_object_or_404(Group, pk=gid) if not request.user.has_perm('group_app.view_group', grp): raise PermissionDenied - abon = get_object_or_404(generic.Abon, username=uname) + abon = get_object_or_404(models.Abon, username=uname) if abon.group != grp: messages.warning( @@ -298,10 +270,10 @@ def abon_services(request, gid: int, uname): return redirect('abonapp:abon_services', abon.group.id, abon.username) try: - periodic_pay = generic.PeriodicPayForId.objects.filter( + periodic_pay = models.PeriodicPayForId.objects.filter( account=abon ).first() - except generic.PeriodicPayForId.DoesNotExist: + except models.PeriodicPayForId.DoesNotExist: periodic_pay = None return render(request, 'abonapp/service.html', { @@ -315,7 +287,7 @@ def abon_services(request, gid: int, uname): class AbonHomeUpdateView(LoginAdminMixin, PermissionRequiredMixin, UpdateView): permission_required = 'abonapp.view_abon' - model = generic.Abon + model = models.Abon form_class = forms.AbonForm slug_field = 'username' slug_url_kwarg = 'uname' @@ -370,13 +342,13 @@ class AbonHomeUpdateView(LoginAdminMixin, PermissionRequiredMixin, UpdateView): if self.initial: return self.initial try: - passw = generic.AbonRawPassword.objects.get( + passw = models.AbonRawPassword.objects.get( account=abon ).passw_text return { 'password': passw } - except generic.AbonRawPassword.DoesNotExist: + except models.AbonRawPassword.DoesNotExist: messages.warning( self.request, _('User has not have password, and cannot login') @@ -396,21 +368,11 @@ class AbonHomeUpdateView(LoginAdminMixin, PermissionRequiredMixin, UpdateView): return super(AbonHomeUpdateView, self).get_context_data(**context) -@transaction.atomic -def terminal_pay(request): - from .pay_systems import allpay - ret_text = allpay(request) - if isinstance(ret_text, HttpResponse): - return ret_text - else: - return HttpResponse(ret_text) - - @login_required @only_admins @permission_required('abonapp.add_invoiceforpayment') def add_invoice(request, gid: int, uname: str): - abon = get_object_or_404(generic.Abon, username=uname) + abon = get_object_or_404(models.Abon, username=uname) grp = get_object_or_404(Group, pk=gid) try: @@ -418,7 +380,7 @@ def add_invoice(request, gid: int, uname: str): curr_amount = lib.safe_int(request.POST.get('curr_amount')) comment = request.POST.get('comment') - newinv = generic.InvoiceForPayment() + newinv = models.InvoiceForPayment() newinv.abon = abon newinv.amount = curr_amount newinv.comment = comment @@ -438,7 +400,7 @@ def add_invoice(request, gid: int, uname: str): messages.error(request, err) return render(request, 'abonapp/addInvoice.html', { 'abon': abon, - 'invcount': generic.InvoiceForPayment.objects.filter(abon=abon).count(), + 'invcount': models.InvoiceForPayment.objects.filter(abon=abon).count(), 'group': grp }) @@ -448,7 +410,7 @@ def add_invoice(request, gid: int, uname: str): @permission_required('abonapp.can_buy_tariff') def pick_tariff(request, gid: int, uname): grp = get_object_or_404(Group, pk=gid) - abon = get_object_or_404(generic.Abon, username=uname) + abon = get_object_or_404(models.Abon, username=uname) tariffs = Tariff.objects.get_tariffs_by_group(grp.pk) try: if request.method == 'POST': @@ -503,7 +465,7 @@ def pick_tariff(request, gid: int, uname): @permission_required('abonapp.can_complete_service') def unsubscribe_service(request, gid: int, uname, abon_tariff_id: int): try: - abon_tariff = get_object_or_404(generic.AbonTariff, + abon_tariff = get_object_or_404(models.AbonTariff, pk=int(abon_tariff_id)) abon_tariff.delete() messages.success(request, _('User has been detached from service')) @@ -523,7 +485,7 @@ class LogListView(LoginAdminPermissionMixin, ListView): http_method_names = ('get',) context_object_name = 'logs' template_name = 'abonapp/log.html' - model = generic.AbonLog + model = models.AbonLog class DebtorsListView(LoginAdminPermissionMixin, ListView): @@ -532,7 +494,7 @@ class DebtorsListView(LoginAdminPermissionMixin, ListView): http_method_names = ('get',) context_object_name = 'invoices' template_name = 'abonapp/debtors.html' - queryset = generic.InvoiceForPayment.objects.filter(status=True) + queryset = models.InvoiceForPayment.objects.filter(status=True) class TaskLogListView(LoginAdminPermissionMixin, ListView): @@ -546,10 +508,10 @@ class TaskLogListView(LoginAdminPermissionMixin, ListView): if hasattr(self, 'abon'): return self.abon.group else: - return get_object_or_404(generic.Group, pk=self.kwargs.get('gid')) + return get_object_or_404(models.Group, pk=self.kwargs.get('gid')) def get_queryset(self): - abon = get_object_or_404(generic.Abon, + abon = get_object_or_404(models.Abon, username=self.kwargs.get('uname')) self.abon = abon return Task.objects.filter(abon=abon) @@ -564,17 +526,17 @@ class TaskLogListView(LoginAdminPermissionMixin, ListView): class PassportUpdateView(LoginAdminPermissionMixin, UpdateView): permission_required = 'abonapp.view_passportinfo' form_class = forms.PassportForm - model = generic.PassportInfo + model = models.PassportInfo template_name = 'abonapp/modal_passport_view.html' def get_object(self, queryset=None): - self.abon = get_object_or_404(generic.Abon, + self.abon = get_object_or_404(models.Abon, username=self.kwargs.get('uname')) try: - passport_instance = generic.PassportInfo.objects.get( + passport_instance = models.PassportInfo.objects.get( abon=self.abon ) - except generic.PassportInfo.DoesNotExist: + except models.PassportInfo.DoesNotExist: passport_instance = None return passport_instance @@ -611,7 +573,7 @@ class PassportUpdateView(LoginAdminPermissionMixin, UpdateView): class IpUpdateView(LoginAdminPermissionMixin, UpdateView): permission_required = 'abonapp.change_abon' form_class = forms.AddIpForm - model = generic.Abon + model = models.Abon slug_url_kwarg = 'uname' slug_field = 'username' template_name = 'abonapp/modal_ip_form.html' @@ -674,7 +636,7 @@ def chgroup_tariff(request, gid): def dev(request, gid: int, uname): abon_dev = None try: - abon = generic.Abon.objects.get(username=uname) + abon = models.Abon.objects.get(username=uname) if request.method == 'POST': abon.device = Device.objects.get(pk=request.POST.get('dev')) abon.save(update_fields=('device',)) @@ -687,7 +649,7 @@ def dev(request, gid: int, uname): request, _('Device your selected already does not exist') ) - except generic.Abon.DoesNotExist: + except models.Abon.DoesNotExist: messages.error(request, _('Abon does not exist')) return redirect('abonapp:people_list', gid=gid) return render(request, 'abonapp/modal_dev.html', { @@ -703,13 +665,13 @@ def dev(request, gid: int, uname): @permission_required('group_app.view_group', (Group, 'pk', 'gid')) def clear_dev(request, gid: int, uname): try: - abon = generic.Abon.objects.get(username=uname) + abon = models.Abon.objects.get(username=uname) abon.device = None abon.dev_port = None abon.is_dynamic_ip = False abon.save(update_fields=('device', 'dev_port', 'is_dynamic_ip')) messages.success(request, _('Device has successfully unattached')) - except generic.Abon.DoesNotExist: + except models.Abon.DoesNotExist: messages.error(request, _('Abon does not exist')) return redirect('abonapp:people_list', gid=gid) return redirect('abonapp:abon_home', gid=gid, uname=uname) @@ -724,7 +686,7 @@ def abon_ping(request, gid: int, uname): status = False text = ' %s' % _( 'no ping') - abon = get_object_or_404(generic.Abon, username=uname) + abon = get_object_or_404(models.Abon, username=uname) try: if ip is None: raise lib.LogicError(_('Ip not passed')) @@ -780,7 +742,7 @@ def abon_ping(request, gid: int, uname): 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(generic.Abon, username=uname) + abon = get_object_or_404(models.Abon, username=uname) abon.autoconnect_service = checked abon.save(update_fields=('autoconnect_service',)) return { @@ -791,14 +753,14 @@ def set_auto_continue_service(request, gid: int, uname): @login_required @only_admins def vcards(r): - users = generic.Abon.objects.exclude(group=None).select_related( + users = models.Abon.objects.exclude(group=None).select_related( 'group', 'street' ).only( 'username', 'fio', 'group__title', 'telephone', 'street__name', 'house' ) - additional_tels = generic.AdditionalTelephone.objects.select_related( + additional_tels = models.AdditionalTelephone.objects.select_related( 'abon', 'abon__group', 'abon__street' @@ -844,7 +806,7 @@ class DialsListView(LoginRequiredMixin, OnlyAdminsMixin, OrderedFilteredList): template_name = 'abonapp/dial_log.html' def get_queryset(self): - abon = get_object_or_404(generic.Abon, + abon = get_object_or_404(models.Abon, username=self.kwargs.get('uname')) if not self.request.user.has_perm('group_app.view_group', abon.group): raise PermissionDenied @@ -853,7 +815,7 @@ class DialsListView(LoginRequiredMixin, OnlyAdminsMixin, OrderedFilteredList): tel = abon.telephone.replace('+', '') additional_tels = tuple( t.telephone for t in - generic.AdditionalTelephone.objects.filter( + models.AdditionalTelephone.objects.filter( abon=abon ).iterator() ) @@ -904,14 +866,14 @@ def save_user_dev_port(request, gid: int, uname): is_dynamic_ip = request.POST.get('is_dynamic_ip') is_dynamic_ip = True if is_dynamic_ip == 'on' else False try: - abon = generic.Abon.objects.get(username=uname) + abon = models.Abon.objects.get(username=uname) if user_port == 0: port = None else: port = DevPort.objects.get(pk=user_port) if abon.device is not None: try: - other_abon = generic.Abon.objects.get( + other_abon = models.Abon.objects.get( device=abon.device, dev_port=port ) @@ -928,9 +890,9 @@ def save_user_dev_port(request, gid: int, uname): } ) return redirect('abonapp:abon_home', gid, uname) - except generic.Abon.DoesNotExist: + except models.Abon.DoesNotExist: pass - except generic.Abon.MultipleObjectsReturned: + except models.Abon.MultipleObjectsReturned: messages.error(request, _('Multiple users on the same device port')) return redirect('devapp:view', abon.device.group.pk, @@ -945,7 +907,7 @@ def save_user_dev_port(request, gid: int, uname): messages.success(request, _('User port has been saved')) except DevPort.DoesNotExist: messages.error(request, _('Selected port does not exist')) - except generic.Abon.DoesNotExist: + except models.Abon.DoesNotExist: messages.error(request, _('User does not exist')) return redirect('abonapp:abon_home', gid, uname) @@ -980,17 +942,17 @@ def street_edit(request, gid): if request.method == 'POST': for sid, sname in zip(request.POST.getlist('sid'), request.POST.getlist('sname')): - street = generic.AbonStreet.objects.get(pk=sid) + street = models.AbonStreet.objects.get(pk=sid) street.name = sname street.save() messages.success(request, _('Streets has been saved')) else: return render(request, 'abonapp/modal_editstreet.html', { 'gid': gid, - 'streets': generic.AbonStreet.objects.filter(group=gid) + 'streets': models.AbonStreet.objects.filter(group=gid) }) - except generic.AbonStreet.DoesNotExist: + except models.AbonStreet.DoesNotExist: messages.error(request, _('One of these streets has not been found')) return redirect('abonapp:people_list', gid) @@ -1002,9 +964,9 @@ def street_edit(request, gid): @permission_required('group_app.view_group', (Group, 'pk', 'gid')) def street_del(request, gid: int, sid: int): try: - generic.AbonStreet.objects.get(pk=sid, group=gid).delete() + models.AbonStreet.objects.get(pk=sid, group=gid).delete() messages.success(request, _('The street successfully deleted')) - except generic.AbonStreet.DoesNotExist: + except models.AbonStreet.DoesNotExist: messages.error(request, _('The street has not been found')) return redirect('abonapp:people_list', gid) @@ -1024,7 +986,7 @@ def active_nets(request, gid): @permission_required('abonapp.view_additionaltelephones') @permission_required('group_app.view_group', (Group, 'pk', 'gid')) def tels(request, gid: int, uname): - abon = get_object_or_404(generic.Abon, username=uname) + abon = get_object_or_404(models.Abon, username=uname) telephones = abon.additional_telephones.all() return render(request, 'abonapp/modal_additional_telephones.html', { 'telephones': telephones, @@ -1041,7 +1003,7 @@ def tel_add(request, gid: int, uname): frm = forms.AdditionalTelephoneForm(request.POST) if frm.is_valid(): new_tel = frm.save(commit=False) - abon = get_object_or_404(generic.Abon, username=uname) + abon = get_object_or_404(models.Abon, username=uname) new_tel.abon = abon new_tel.save() messages.success(request, _('New telephone has been saved')) @@ -1063,11 +1025,11 @@ def tel_add(request, gid: int, uname): def tel_del(request, gid: int, uname): try: tid = lib.safe_int(request.GET.get('tid')) - tel = generic.AdditionalTelephone.objects.get(pk=tid) + tel = models.AdditionalTelephone.objects.get(pk=tid) tel.delete() messages.success(request, _('Additional telephone successfully deleted')) - except generic.AdditionalTelephone.DoesNotExist: + except models.AdditionalTelephone.DoesNotExist: messages.error(request, _('Telephone not found')) return redirect('abonapp:abon_home', gid, uname) @@ -1077,12 +1039,12 @@ def tel_del(request, gid: int, uname): @permission_required('group_app.view_group', (Group, 'pk', 'gid')) def phonebook(request, gid): res_format = request.GET.get('f') - t1 = generic.Abon.objects.filter( + t1 = models.Abon.objects.filter( group__id=int(gid) ).only('telephone', 'fio').values_list( 'telephone', 'fio' ) - t2 = generic.AdditionalTelephone.objects.filter( + t2 = models.AdditionalTelephone.objects.filter( abon__group__id=gid ).only( 'telephone', 'owner_name' @@ -1115,7 +1077,7 @@ def abon_export(request, gid): if frm.is_valid(): cleaned_data = frm.clean() fields = cleaned_data.get('fields') - subscribers = generic.Abon.objects.filter(group__id=gid).only( + subscribers = models.Abon.objects.filter(group__id=gid).only( *fields).values_list(*fields) if res_format == 'csv': import csv @@ -1151,26 +1113,6 @@ def abon_export(request, gid): }) -@login_required -@only_admins -def fin_report(request): - q = generic.AllTimePayLog.objects.by_days() - res_format = request.GET.get('f') - if res_format == 'csv': - import csv - response = HttpResponse(content_type='text/csv') - response['Content-Disposition'] = 'attachment; filename="report.csv"' - writer = csv.writer(response, quoting=csv.QUOTE_NONNUMERIC) - for row in q: - writer.writerow( - (row['summ'], row['pay_date'].strftime('%Y-%m-%d')) - ) - return response - return render(request, 'abonapp/fin_report.html', { - 'logs': q - }) - - @login_required @only_admins @permission_required('group_app.view_group', (Group, 'pk', 'gid')) @@ -1178,12 +1120,12 @@ def add_edit_periodic_pay(request, gid: int, uname, periodic_pay_id=0): if periodic_pay_id == 0: if not request.user.has_perm('abonapp.add_periodicpayforid'): raise PermissionDenied - periodic_pay_instance = generic.PeriodicPayForId() + periodic_pay_instance = models.PeriodicPayForId() else: if not request.user.has_perm('abonapp.change_periodicpayforid'): raise PermissionDenied periodic_pay_instance = get_object_or_404( - generic.PeriodicPayForId, + models.PeriodicPayForId, pk=periodic_pay_id ) if request.method == 'POST': @@ -1192,7 +1134,7 @@ def add_edit_periodic_pay(request, gid: int, uname, periodic_pay_id=0): instance=periodic_pay_instance ) if frm.is_valid(): - abon = get_object_or_404(generic.Abon, username=uname) + abon = get_object_or_404(models.Abon, username=uname) inst = frm.save(commit=False) inst.account = abon inst.save() @@ -1215,7 +1157,7 @@ def add_edit_periodic_pay(request, gid: int, uname, periodic_pay_id=0): @permission_required('abonapp.delete_periodicpayforid') def del_periodic_pay(request, gid: int, uname, periodic_pay_id): periodic_pay_instance = get_object_or_404( - generic.PeriodicPayForId, + models.PeriodicPayForId, pk=periodic_pay_id ) if periodic_pay_instance.account.username != uname: @@ -1230,7 +1172,7 @@ class EditSibscriberMarkers(LoginAdminPermissionMixin, UpdateView): http_method_names = ('get', 'post') template_name = 'abonapp/modal_user_markers.html' form_class = forms.MarkersForm - model = generic.Abon + model = models.Abon slug_url_kwarg = 'uname' slug_field = 'username' @@ -1266,7 +1208,7 @@ class EditSibscriberMarkers(LoginAdminPermissionMixin, UpdateView): @only_admins @permission_required('abonapp.change_abon') def user_session_free(request, gid: int, uname): - abon = get_object_or_404(generic.Abon, username=uname) + abon = get_object_or_404(models.Abon, username=uname) if abon.nas is None: messages.error(request, _('gateway required')) return redirect('abonapp:abon_home', gid, uname) @@ -1286,7 +1228,7 @@ def attach_nas(request, gid): gateway_id = lib.safe_int(request.POST.get('gateway')) if gateway_id: nas = get_object_or_404(NASModel, pk=gateway_id) - abons = generic.Abon.objects.filter(group__id=gid) + abons = models.Abon.objects.filter(group__id=gid) if abons.exists(): abons.update(nas=nas) messages.success( @@ -1315,7 +1257,7 @@ def abons(request): 'tarif_id': abn.active_tariff().tariff.pk if abn.active_tariff() is not None else 0, 'ip': abn.ip_address - } for abn in generic.Abon.objects.iterator()) + } for abn in models.Abon.objects.iterator()) tarlist = ({ 'id': trf.pk, @@ -1338,7 +1280,7 @@ def search_abon(request): word = request.GET.get('s') if not word: return None - results = generic.Abon.objects.filter(fio__icontains=word)[:8] + results = models.Abon.objects.filter(fio__icontains=word)[:8] return list( {'id': usr.pk, 'text': "%s: %s" % (usr.username, usr.fio)} for usr in results @@ -1398,103 +1340,27 @@ class DhcpLever(SecureApiView): return str(e) -class DublicatePay(SecureApiView): - http_method_names = 'get', - - @staticmethod - def _bad_ret(err_id, err_description=None): - now = timezone.now() - r = { - 'status_code': lib.safe_int(err_id), - 'time_stamp': now.strftime("%d.%m.%Y %H:%M") - } - if err_description: - r.update({'description': err_description}) - return r - - @method_decorator(xml_view(root_node='pay-response')) - def get(self, request, *args, **kwargs): - act = lib.safe_int(request.GET.get('ACT')) - self.current_date = timezone.now().strftime("%d.%m.%Y %H:%M") - - if act <= 0: - return self._bad_ret(-101, 'ACT less than zero') +class PayHistoryListView(LoginAdminPermissionMixin, OrderedFilteredList): + permission_required = 'group_app.view_group' + context_object_name = 'pay_history' + template_name = 'abonapp/payHistory.html' - try: - if act == 1: - return self._fetch_user_info(request.GET) - elif act == 4: - return self._make_pay(request.GET) - elif act == 7: - return self._check_pay(request.GET) - else: - return self._bad_ret(-101, 'ACT is not passed') - except generic.Abon.DoesNotExist: - return self._bad_ret(-40) - except DatabaseError: - return self._bad_ret(-90) - except generic.AllTimePayLog.DoesNotExist: - return self._bad_ret(-10) - except AttributeError: - return self._bad_ret(-101) - - def _fetch_user_info(self, data: dict): - pay_account = data.get('PAY_ACCOUNT') - abon = generic.Abon.objects.get(pk=pay_account) - fio = abon.fio - ballance = float(abon.ballance) - return { - 'balance': ballance, - 'name': fio, - 'account': pay_account, - 'service_id': getattr(settings, 'PAY_SERV_ID'), - 'min_amount': 10.0, - 'max_amount': 5000, - 'status_code': 21, - 'time_stamp': self.current_date - } + def get_permission_object(self): + if hasattr(self, 'abon'): + return self.abon.group + return Group.objects.filter(pk=self.kwargs.get('gid')).first() - def _make_pay(self, data: dict): - trade_point = lib.safe_int(data.get('TRADE_POINT')) - receipt_num = lib.safe_int(data.get('RECEIPT_NUM')) - pay_account = data.get('PAY_ACCOUNT') - pay_id = data.get('PAY_ID') - pay_amount = lib.safe_float(data.get('PAY_AMOUNT')) - abon = generic.Abon.objects.get(pk=pay_account) - pays = generic.AllTimePayLog.objects.filter(pay_id=pay_id) - if pays.count() > 0: - return self._bad_ret(-100) - - abon.add_ballance(None, pay_amount, - comment='KonikaForward %.2f' % pay_amount) - abon.save(update_fields=('ballance',)) - - generic.AllTimePayLog.objects.create( - pay_id=pay_id, - summ=pay_amount, - abon=abon, - trade_point=trade_point, - receipt_num=receipt_num - ) - return { - 'pay_id': pay_id, - 'service_id': data.get('SERVICE_ID'), - 'amount': pay_amount, - 'status_code': 22, - 'time_stamp': self.current_date - } + def get_queryset(self): + abon = get_object_or_404(models.Abon, + username=self.kwargs.get('uname')) + self.abon = abon + pay_history = models.AbonLog.objects.filter(abon=abon).order_by('-date') + return pay_history - def _check_pay(self, data: dict): - pay_id = data.get('PAY_ID') - pay = generic.AllTimePayLog.objects.get(pay_id=pay_id) - return { - 'status_code': 11, - 'time_stamp': self.current_date, - 'transaction': { - 'pay_id': pay_id, - 'service_id': data.get('SERVICE_ID'), - 'amount': pay.summ, - 'status': 111, - 'time_stamp': pay.date_add.strftime("%d.%m.%Y %H:%M") - } + def get_context_data(self, **kwargs): + context = { + 'group': self.abon.group, + 'abon': self.abon } + context.update(kwargs) + return super(PayHistoryListView, self).get_context_data(**context) diff --git a/accounts_app/templatetags/acc_tags.py b/accounts_app/templatetags/acc_tags.py index e4891fa..2aa9a1d 100644 --- a/accounts_app/templatetags/acc_tags.py +++ b/accounts_app/templatetags/acc_tags.py @@ -3,7 +3,7 @@ from ipaddress import ip_address, AddressValueError from django import template from django.db.models import Model from django.apps import apps -from abonapp.models.generic import Abon +from abonapp.models import Abon from six import string_types, class_types register = template.Library() diff --git a/agent/commands/dhcp.py b/agent/commands/dhcp.py index 6e1d0b6..12ebf16 100644 --- a/agent/commands/dhcp.py +++ b/agent/commands/dhcp.py @@ -1,6 +1,6 @@ from typing import Optional from django.core.exceptions import MultipleObjectsReturned -from abonapp.models.generic import Abon +from abonapp.models import Abon from devapp.models import Device, Port diff --git a/clientsideapp/views.py b/clientsideapp/views.py index add68c9..e6b92dd 100644 --- a/clientsideapp/views.py +++ b/clientsideapp/views.py @@ -4,7 +4,7 @@ from django.contrib import messages from django.db import transaction from django.utils.translation import gettext_lazy as _, gettext -from abonapp.models.generic import AbonLog, InvoiceForPayment, Abon +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 diff --git a/devapp/views.py b/devapp/views.py index d01a19c..3613f52 100644 --- a/devapp/views.py +++ b/devapp/views.py @@ -1,7 +1,7 @@ import re from ipaddress import ip_address -from abonapp.models.generic import Abon +from abonapp.models import Abon from accounts_app.models import UserProfile from chatbot.models import ChatException from devapp.base_intr import DeviceImplementationError diff --git a/dialing_app/views.py b/dialing_app/views.py index 0cacffd..3c06c5f 100644 --- a/dialing_app/views.py +++ b/dialing_app/views.py @@ -12,7 +12,7 @@ from guardian.decorators import permission_required_or_403 as permission_require from django.db.models import Q from django.conf import settings -from abonapp.models.generic import Abon +from abonapp.models import Abon from djing.global_base_views import SecureApiView from djing import JSONType from djing.lib import safe_int diff --git a/djing/lib/auth_backends.py b/djing/lib/auth_backends.py index 7ed5c8d..65b36c5 100644 --- a/djing/lib/auth_backends.py +++ b/djing/lib/auth_backends.py @@ -2,7 +2,7 @@ from ipaddress import ip_address, AddressValueError from django.contrib.auth.backends import ModelBackend from accounts_app.models import BaseAccount, UserProfile -from abonapp.models.generic import Abon +from abonapp.models import Abon class CustomAuthBackend(ModelBackend): diff --git a/djing/local_settings.py.template b/djing/local_settings.py.example similarity index 90% rename from djing/local_settings.py.template rename to djing/local_settings.py.example index a564159..26330b4 100644 --- a/djing/local_settings.py.template +++ b/djing/local_settings.py.example @@ -33,10 +33,6 @@ DATABASES = { } } -# service id for AllPay payment system -PAY_SERV_ID = '' -PAY_SECRET = '' - # path to asterisk dial records DIALING_MEDIA = 'path/to/asterisk_records' @@ -69,3 +65,8 @@ EMAIL_HOST = 'smtp.mailserver.com' EMAIL_PORT = 587 EMAIL_HOST_PASSWORD = 'password' EMAIL_USE_TLS = True + +# Encrypted fields +# https://pypi.org/project/django-encrypted-model-fields/ +# You must change this value +#FIELD_ENCRYPTION_KEY = 'vZpDlDPQyU6Ha7NyUGj9uYMuPigejtEPMOZfkYXIQRw=' diff --git a/djing/settings.py b/djing/settings.py index 12d6a85..16580aa 100644 --- a/djing/settings.py +++ b/djing/settings.py @@ -41,6 +41,7 @@ INSTALLED_APPS = [ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'encrypted_model_fields', 'ip_pool', 'accounts_app', 'gw_app', @@ -49,7 +50,7 @@ INSTALLED_APPS = [ 'searchapp', 'devapp', 'mapapp', - 'statistics', + 'finapp', 'taskapp', 'clientsideapp', 'chatbot', @@ -186,9 +187,6 @@ LOGOUT_URL = reverse_lazy('acc_app:logout') PAGINATION_ITEMS_PER_PAGE = local_settings.PAGINATION_ITEMS_PER_PAGE -PAY_SERV_ID = local_settings.PAY_SERV_ID -PAY_SECRET = local_settings.PAY_SECRET - DIALING_MEDIA = local_settings.DIALING_MEDIA DEFAULT_SNMP_PASSWORD = local_settings.DEFAULT_SNMP_PASSWORD @@ -234,3 +232,12 @@ REDIS_PORT = '6379' BROKER_URL = 'redis://' + REDIS_HOST + ':' + REDIS_PORT + '/0' BROKER_TRANSPORT_OPTIONS = {'visibility_timeout': 3600} CELERY_RESULT_BACKEND = 'redis://' + REDIS_HOST + ':' + REDIS_PORT + '/0' + + +# Encrypted fields +# https://pypi.org/project/django-encrypted-model-fields/ +FIELD_ENCRYPTION_KEY = getattr( + local_settings, + 'FIELD_ENCRYPTION_KEY', + 'vZpDlDPQyU6Ha7NyUGj9uYMuPigejtEPMOZfkYXIQRw=' +) diff --git a/djing/urls.py b/djing/urls.py index ad7e9da..54bf7b8 100644 --- a/djing/urls.py +++ b/djing/urls.py @@ -18,7 +18,8 @@ urlpatterns = [ path('dialing/', include('dialing_app.urls', namespace='dialapp')), path('groups/', include('group_app.urls', namespace='group_app')), path('ip_pool/', include('ip_pool.urls', namespace='ip_pool')), - path('gw/', include('gw_app.urls', namespace='gw_app')) + path('gw/', include('gw_app.urls', namespace='gw_app')), + path('fin/', include('finapp.urls', namespace='finapp')) # Switch language #path(r'i18n/', include('django.conf.urls.i18n')), diff --git a/abonapp/models/__init__.py b/finapp/__init__.py similarity index 100% rename from abonapp/models/__init__.py rename to finapp/__init__.py diff --git a/finapp/admin.py b/finapp/admin.py new file mode 100644 index 0000000..55264f3 --- /dev/null +++ b/finapp/admin.py @@ -0,0 +1,4 @@ +from django.contrib import admin +from finapp import models + +admin.site.register(models.AllTimePayLog) diff --git a/finapp/apps.py b/finapp/apps.py new file mode 100644 index 0000000..b11d31d --- /dev/null +++ b/finapp/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class FinappConfig(AppConfig): + name = 'finapp' diff --git a/finapp/forms.py b/finapp/forms.py new file mode 100644 index 0000000..f8115c0 --- /dev/null +++ b/finapp/forms.py @@ -0,0 +1,19 @@ +from django import forms +from finapp.models import PayAllTimeGateway + + +class PayAllTimeGatewayForm(forms.ModelForm): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + instance = getattr(self, 'instance') + if instance and instance.pk: + self.fields['slug'].disabled = True + + def clean_slug(self): + if self.instance and self.instance.pk: + return self.instance.slug + return self.data['slug'] + + class Meta: + model = PayAllTimeGateway + fields = '__all__' diff --git a/finapp/locale/ru/LC_MESSAGES/django.po b/finapp/locale/ru/LC_MESSAGES/django.po new file mode 100644 index 0000000..5447fe3 --- /dev/null +++ b/finapp/locale/ru/LC_MESSAGES/django.po @@ -0,0 +1,159 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Dmitry Novikov nerosketch@gmail.com, 2018. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-11-27 11:42+0300\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Dmitry Novikov nerosketch@gmail.com\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n" +"%100>=11 && n%100<=14)? 2 : 3);\n" + +#: models.py:32 templates/finapp/payalltimegateway_list.html:22 +msgid "Title" +msgstr "Название" + +#: models.py:33 +msgid "Secret" +msgstr "Секрет" + +#: models.py:34 templates/finapp/payalltimegateway_list.html:23 +msgid "Service id" +msgstr "" + +#: models.py:35 +msgid "Slug" +msgstr "" + +#: models.py:46 +msgid "All time gateway" +msgstr "Платёжный шлюз AllTime" + +#: models.py:65 templates/finapp/payHistory.html:24 +msgid "Cost" +msgstr "Цена" + +#: models.py:67 templates/finapp/payHistory.html:25 +msgid "Trade point" +msgstr "Торговая точка" + +#: models.py:73 templates/finapp/payHistory.html:26 +msgid "Receipt num" +msgstr "Номер квитанции" + +#: models.py:75 +msgid "Pay gateway" +msgstr "Платёжный шлюз" + +#: templates/finapp/fin_report.html:7 +msgid "Fin report" +msgstr "Фин. отчёт" + +#: templates/finapp/fin_report.html:12 +msgid "Money by days" +msgstr "Денег за дни" + +#: templates/finapp/fin_report.html:21 templates/finapp/payHistory.html:23 +msgid "Date" +msgstr "Дата" + +#: templates/finapp/fin_report.html:32 +msgid "Pays not found" +msgstr "Платежи не найдены" + +#: templates/finapp/fin_report.html:41 +msgid "Export to csv" +msgstr "Экспортировать в csv" + +#: templates/finapp/payHistory.html:8 +#: templates/finapp/payalltimegateway_form.html:7 +#: templates/finapp/payalltimegateway_list.html:8 +msgid "Payment system" +msgstr "Платёжная система" + +#: templates/finapp/payHistory.html:9 +msgid "Payment history" +msgstr "История платежей" + +#: templates/finapp/payHistory.html:21 +msgid "User" +msgstr "Абонент" + +#: templates/finapp/payHistory.html:22 +msgid "Pay id" +msgstr "ID платежа" + +#: templates/finapp/payHistory.html:41 +msgid "Payment history is empty" +msgstr "История платежей пуста" + +#: templates/finapp/payalltimegateway_form.html:10 +msgid "Change payment gateway" +msgstr "Редактировать платёжный шлюз" + +#: templates/finapp/payalltimegateway_form.html:12 +msgid "Add payment gateway" +msgstr "Добавить платёжный шлюз" + +#: templates/finapp/payalltimegateway_form.html:20 +msgid "Make new payment gateway" +msgstr "Создать новый платёжный шлюз" + +#: templates/finapp/payalltimegateway_form.html:33 +msgid "Type info about payment gateway" +msgstr "Укажите данные для платёжной системы" + +#: templates/finapp/payalltimegateway_form.html:43 +msgid "Save" +msgstr "Сохранить" + +#: templates/finapp/payalltimegateway_form.html:47 +#: templates/finapp/payalltimegateway_list.html:62 +#: templates/finapp/payalltimegateway_list.html:66 +msgid "Add" +msgstr "Добвить" + +#: templates/finapp/payalltimegateway_form.html:51 +msgid "Reset" +msgstr "Сбросить" + +#: templates/finapp/payalltimegateway_list.html:13 +msgid "List of payment gateways" +msgstr "Список платёжных систем" + +#: templates/finapp/payalltimegateway_list.html:24 +msgid "Pays count" +msgstr "Количество платежей всего" + +#: templates/finapp/payalltimegateway_list.html:36 +msgid "Edit" +msgstr "Изменить" + +#: templates/finapp/payalltimegateway_list.html:40 +msgid "Permission required" +msgstr "Не хватает прав" + +#: templates/finapp/payalltimegateway_list.html:53 +msgid "Payment gateways not found" +msgstr "Платёжные шлюзы не найдены" + +#: views.py:198 +msgid "New pay gateway created successfully" +msgstr "Новый платёжный шлюз успешно создан" + +#: views.py:221 +msgid "Payment gateway successfully updated" +msgstr "Платёжный шлюз успешно обновлён" + +msgid "Deleted" +msgstr "Удалён" diff --git a/finapp/migrations/0001_initial.py b/finapp/migrations/0001_initial.py new file mode 100644 index 0000000..6f3891d --- /dev/null +++ b/finapp/migrations/0001_initial.py @@ -0,0 +1,71 @@ +# Generated by Django 2.1 on 2018-11-23 15:56 + +from django.db import migrations, models +import django.db.models.deletion +import encrypted_model_fields.fields + +PAY_GW_ID = 0 + + +def make_default_pay_gw(apps, _): + global PAY_GW_ID + PayAllTimeGateway = apps.get_model('finapp.PayAllTimeGateway') + pay_gw = PayAllTimeGateway.objects.first() + if pay_gw is None: + pay_gw = PayAllTimeGateway.objects.create( + title='default', + secret='secret', + service_id='service_id', + slug='default' + ) + PAY_GW_ID = pay_gw.pk + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('abonapp', '0009_auto_20181123_1556'), + ] + + operations = [ + migrations.RunSQL(migrations.RunSQL.noop, state_operations=[ + migrations.CreateModel( + name='AllTimePayLog', + fields=[ + ('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(verbose_name='Cost', 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')), + ('abon', models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_DEFAULT, to='abonapp.Abon')), + ], + options={ + 'db_table': 'all_time_pay_log', + 'ordering': ('-date_add',), + }, + ) + ]), + migrations.CreateModel( + name='PayAllTimeGateway', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=64, verbose_name='Title')), + ('secret', encrypted_model_fields.fields.EncryptedCharField(verbose_name='Secret')), + ('service_id', models.CharField(max_length=64, verbose_name='Service id')), + ('slug', models.SlugField(max_length=32, unique=True, verbose_name='Slug')), + ], + options={ + 'verbose_name': 'All time gateway', + 'db_table': 'pay_all_time_gateways', + 'ordering': ('title',), + }, + ), + migrations.RunPython(make_default_pay_gw), + migrations.AddField( + model_name='alltimepaylog', + name='pay_gw', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='finapp.PayAllTimeGateway', verbose_name='Pay gateway', default=lambda: PAY_GW_ID), + ), + ] diff --git a/abonapp/models/payment.py b/finapp/migrations/__init__.py similarity index 100% rename from abonapp/models/payment.py rename to finapp/migrations/__init__.py diff --git a/finapp/models.py b/finapp/models.py new file mode 100644 index 0000000..72b2503 --- /dev/null +++ b/finapp/models.py @@ -0,0 +1,85 @@ +from datetime import datetime +from django.db import models, connection +from django.urls import reverse +from django.utils.translation import ugettext_lazy as _ +from encrypted_model_fields.fields import EncryptedCharField + +from abonapp.models import Abon + + +class AllTimePayLogManager(models.Manager): + @staticmethod + def by_days(): + cur = connection.cursor() + cur.execute( + 'SELECT SUM(summ) AS alsum, ' + 'DATE_FORMAT(date_add, "%Y-%m-%d") AS pay_date ' + 'FROM all_time_pay_log ' + 'GROUP BY DATE_FORMAT(date_add, "%Y-%m-%d")' + ) + while True: + r = cur.fetchone() + if r is None: + break + summ, dat = r + yield { + 'summ': summ, + 'pay_date': datetime.strptime(dat, '%Y-%m-%d') + } + + +class PayAllTimeGateway(models.Model): + title = models.CharField(_('Title'), max_length=64) + secret = EncryptedCharField(verbose_name=_('Secret'), max_length=64) + service_id = models.CharField(_('Service id'), max_length=64) + slug = models.SlugField(_('Slug'), max_length=32, + unique=True, allow_unicode=False) + + def __str__(self): + return self.title + + def get_absolute_url(self): + return reverse('finapp:edit_pay_gw', self.slug) + + class Meta: + db_table = 'pay_all_time_gateways' + verbose_name = _('All time gateway') + ordering = 'title', + + +# Log for pay system "AllTime" +class AllTimePayLog(models.Model): + abon = models.ForeignKey( + Abon, + on_delete=models.SET_DEFAULT, + blank=True, + null=True, + default=None + ) + pay_id = models.CharField( + max_length=36, + unique=True, + primary_key=True + ) + date_add = models.DateTimeField(auto_now_add=True) + summ = models.FloatField(_('Cost'), default=0.0) + trade_point = models.CharField( + _('Trade point'), + max_length=20, + default=None, + null=True, + blank=True + ) + receipt_num = models.BigIntegerField(_('Receipt number'), default=0) + pay_gw = models.ForeignKey(PayAllTimeGateway, + verbose_name=_('Pay gateway'), + on_delete=models.CASCADE) + + objects = AllTimePayLogManager() + + def __str__(self): + return self.pay_id + + class Meta: + db_table = 'all_time_pay_log' + ordering = ('-date_add',) diff --git a/finapp/templates/finapp/ext.htm b/finapp/templates/finapp/ext.htm new file mode 100644 index 0000000..8f1347a --- /dev/null +++ b/finapp/templates/finapp/ext.htm @@ -0,0 +1,24 @@ +{% extends request.is_ajax|yesno:'bajax.html,base.html' %} +{% load i18n %} + +{% block breadcrumb %}{% endblock %} + +{% block page-header %} + {% trans 'Payment gateways' %} +{% endblock %} + +{% block main %} + + +
    +
    + {% block content %}{% endblock %} +
    +
    +{% endblock %} diff --git a/abonapp/templates/abonapp/fin_report.html b/finapp/templates/finapp/fin_report.html similarity index 87% rename from abonapp/templates/abonapp/fin_report.html rename to finapp/templates/finapp/fin_report.html index 09ec971..e02b64a 100644 --- a/abonapp/templates/abonapp/fin_report.html +++ b/finapp/templates/finapp/fin_report.html @@ -17,7 +17,7 @@ - + @@ -37,7 +37,7 @@ diff --git a/finapp/templates/finapp/payHistory.html b/finapp/templates/finapp/payHistory.html new file mode 100644 index 0000000..c35e720 --- /dev/null +++ b/finapp/templates/finapp/payHistory.html @@ -0,0 +1,53 @@ +{% extends 'base.html' %} +{% load i18n %} + + +{% block breadcrumb %} + +{% endblock %} + +{% block page-header %}{{ pay_gw.title }}{% endblock %} + + +{% block main %} + +
    {% trans 'Sum' %}{% trans 'Cost' %} {% trans 'Date' %}
    - +
    + + + + + + + + + + + + {% for pay in object_list %} + + + + + + + + + {% empty %} + + + + {% endfor %} + +
    {% trans 'User' %}{% trans 'Pay id' %}{% trans 'Date' %}{% trans 'Cost' %}{% trans 'Trade point' %}{% trans 'Receipt num' %}
    + {% if pay.abon %} + {{ pay.abon }} + {% else %} + {% trans 'Deleted' %} + {% endif %} + {{ pay.pay_id }}{{ pay.date_add|date:'D d E Y H:i:s' }}{{ pay.summ }}{{ pay.trade_point|default_if_none:'—' }}{{ pay.receipt_num }}
    {% trans 'Payment history is empty' %}
    + +{% endblock %} diff --git a/finapp/templates/finapp/payalltimegateway_form.html b/finapp/templates/finapp/payalltimegateway_form.html new file mode 100644 index 0000000..4b404b6 --- /dev/null +++ b/finapp/templates/finapp/payalltimegateway_form.html @@ -0,0 +1,62 @@ +{% extends request.is_ajax|yesno:'bajax.html,base.html' %} +{% load i18n bootstrap3 %} + +{% block breadcrumb %} + +{% endblock %} + + +{% block page-header %} + {% trans 'Make new payment gateway' %} +{% endblock %} + + +{% block main %} + {% if object %} + {% url 'finapp:edit_pay_gw' object.slug as form_url %} + {% else %} + {% url 'finapp:add_alltime_gateway' as form_url %} + {% endif %} + +
    +
    +

    {% trans 'Type info about payment gateway' %}

    +
    +
    +
    {% csrf_token %} +
    + {% bootstrap_form form %} +
    +
    + {% if object %} + + {% else %} + + {% endif %} + + {% if object %} + + {% trans 'Payment history' %} + + {% endif %} +
    +
    +
    +
    +{% endblock %} diff --git a/finapp/templates/finapp/payalltimegateway_list.html b/finapp/templates/finapp/payalltimegateway_list.html new file mode 100644 index 0000000..b5fdcd4 --- /dev/null +++ b/finapp/templates/finapp/payalltimegateway_list.html @@ -0,0 +1,75 @@ +{% extends 'base.html' %} +{% load i18n %} + + +{% block breadcrumb %} + +{% endblock %} + +{% block page-header %} + {% trans 'List of payment gateways' %} +{% endblock %} + + +{% block main %} + + + + + + + + + + + + {% for gw in object_list %} + + + + + + + {% empty %} + + + + {% endfor %} + + + + + + + +
    {% trans 'Title' %}{% trans 'Service id' %}{% trans 'Pays count' %}
    {{ gw.title }}{{ gw.service_id }}{{ gw.pays_count }} + {% if perms.finapp.change_payalltimegateway %} + + + + {% else %} + + + + {% endif %} + {% if perms.finapp.view_alltimepaylog %} + + + + {% endif %} +
    {% trans 'Payment gateways not found' %}
    + {% if perms.finapp.add_payalltimegateway %} + + {% trans 'Add' %} + + {% else %} + + {% trans 'Add' %} + + {% endif %} +
    + +{% endblock %} diff --git a/finapp/tests.py b/finapp/tests.py new file mode 100644 index 0000000..c7e4d55 --- /dev/null +++ b/finapp/tests.py @@ -0,0 +1,250 @@ +from abc import ABCMeta +from hashlib import md5 + +from django.shortcuts import resolve_url +from django.test import TestCase +from django.utils import timezone +from django.utils.html import escape +from xmltodict import parse + +from abonapp.models import Abon +from accounts_app.models import UserProfile +from djing import settings +from finapp.models import PayAllTimeGateway +from group_app.models import Group + + +def _make_sign(act: int, pay_account: str, serv_id: str, pay_id, secret: str): + md = md5() + s = "%d_%s_%s_%s_%s" % (act, pay_account, serv_id, pay_id, secret) + md.update(bytes(s, 'utf-8')) + return md.hexdigest() + + +class MyBaseTestCase(metaclass=ABCMeta): + def _client_get_check_login(self, url): + """ + Checks if url is protected from unauthorized access + :param url: + :return: authorized response + """ + r = self.client.get(url) + self.assertRedirects(r, "%s?next=%s" % (getattr(settings, 'LOGIN_URL'), url)) + self.client.force_login(self.adminuser) + r = self.client.get(url) + self.assertEqual(r.status_code, 200) + return r + + def setUp(self): + grp = Group.objects.create(title='Grp1') + a1 = Abon.objects.create_user( + telephone='+79781234567', + username='abon', + password='passw1' + ) + a1.group = grp + a1.save(update_fields=('group',)) + my_admin = UserProfile.objects.create_superuser('+79781234567', 'local_superuser', 'ps') + self.adminuser = my_admin + self.abon = a1 + self.group = grp + + +class AllPayTestCase(MyBaseTestCase, TestCase): + time_format = '%d.%m.%Y %H:%M' + + def setUp(self): + a1 = Abon.objects.create_user( + telephone='+79785276481', + username='pay_account1', + password='passw1' + ) + a1.ballance = -13.12 + a1.fio = 'Test Name' + a1.save(update_fields=('ballance', 'fio')) + pay_system = PayAllTimeGateway.objects.create( + title='Test pay system', + secret='secret', + service_id='service_id', + slug='pay_gw_slug' + ) + self.pay_system = pay_system + + def user_pay_view(self): + print('test_user_pay_view') + current_date = timezone.now().strftime(self.time_format) + url = resolve_url('finapp:all_time_pay', self.pay_system.slug) + service_id = self.pay_system.service_id + r = self.client.get(url, { + 'ACT': 1, + 'PAY_ACCOUNT': 'pay_account1', + 'SERVICE_ID': service_id, + 'PAY_ID': '840ab457-e7d1-4494-8197-9570da035170', + 'TRADE_POINT': 'term1', + 'SIGN': _make_sign(1, 'pay_account1', service_id, + '840ab457-e7d1-4494-8197-9570da035170', self.pay_system.secret) + } + ) + r = r.content.decode('utf-8') + o = ''.join(( + "", + "-13.12", + "Test Name", + "pay_account1", + "%s" % escape(service_id), + "10.0", + "5000", + "21", + "%s" % escape(current_date), + "" + )) + self.assertXMLEqual(r, o) + + def user_pay_pay(self): + print('test_user_pay_pay') + current_date = timezone.now().strftime(self.time_format) + url = resolve_url('finapp:all_time_pay', self.pay_system.slug) + service_id = self.pay_system.service_id + r = self.client.get(url, { + 'ACT': 4, + 'PAY_ACCOUNT': 'pay_account1', + 'PAY_AMOUNT': 18.21, + 'RECEIPT_NUM': 2126235, + 'SERVICE_ID': service_id, + 'PAY_ID': '840ab457-e7d1-4494-8197-9570da035170', + 'TRADE_POINT': 'term1', + 'SIGN': _make_sign(4, 'pay_account1', service_id, + '840ab457-e7d1-4494-8197-9570da035170', self.pay_system.secret) + }) + r = r.content.decode('utf-8') + xml = ''.join(( + "", + "840ab457-e7d1-4494-8197-9570da035170", + "%s" % escape(service_id), + "18.21", + "22", + "%s" % escape(current_date), + "" + )) + self.test_pay_time = current_date + self.assertXMLEqual(r, xml) + + def user_pay_check(self): + print('test_user_pay_check') + current_date = timezone.now().strftime(self.time_format) + url = resolve_url('finapp:all_time_pay', self.pay_system.slug) + service_id = self.pay_system.service_id + r = self.client.get(url, + { + 'ACT': 7, + 'SERVICE_ID': service_id, + 'PAY_ID': '840ab457-e7d1-4494-8197-9570da035170', + 'SIGN': _make_sign(7, '', service_id, + '840ab457-e7d1-4494-8197-9570da035170', self.pay_system.secret) + } + ) + r = r.content.decode('utf-8') + xml = ''.join(( + "", + "11", + "%s" % escape(current_date), + "", + "840ab457-e7d1-4494-8197-9570da035170", + "%s" % escape(service_id), + "18.21", + "111", + "%s" % escape(self.test_pay_time), + "" + "" + )) + self.assertXMLEqual(r, xml) + + def check_ballance(self): + print('check_ballance') + url = resolve_url('finapp:all_time_pay', self.pay_system.slug) + service_id = self.pay_system.service_id + r = self.client.get(url, + { + 'ACT': 1, + 'PAY_ACCOUNT': 'pay_account1', + 'SERVICE_ID': service_id, + 'PAY_ID': '840ab457-e7d1-4494-8197-9570da035170', + 'TRADE_POINT': 'term1', + 'SIGN': _make_sign(1, 'pay_account1', service_id, + '840ab457-e7d1-4494-8197-9570da035170', self.pay_system.secret) + } + ) + r = r.content.decode('utf-8') + r = parse(r) + bl = float(r['pay-response']['balance']) + self.assertEqual(bl, 5.09) + + def test_client_does_not_exist(self): + print('test_client_does_not_exist') + current_date = timezone.now().strftime(self.time_format) + url = resolve_url('finapp:all_time_pay', self.pay_system.slug) + service_id = self.pay_system.service_id + r = self.client.get(url, { + 'ACT': 1, + 'PAY_ACCOUNT': 'not_existing_acc', + 'SERVICE_ID': service_id, + 'PAY_ID': '840ab457-e7d1-4494-8197-9570da035170', + 'TRADE_POINT': 'term1', + 'SIGN': _make_sign(1, 'not_existing_acc', service_id, + '840ab457-e7d1-4494-8197-9570da035170', self.pay_system.secret) + }) + r = r.content.decode('utf-8') + self.assertXMLEqual(r, ''.join(( + "", + "-40", + "%s" % escape(current_date), + "Account does not exist", + "" + ))) + + def try_pay_double(self): + print('try_pay_double') + url = resolve_url('finapp:all_time_pay', self.pay_system.slug) + service_id = self.pay_system.service_id + r = self.client.get(url, { + 'ACT': 4, + 'PAY_ACCOUNT': 'pay_account1', + 'SERVICE_ID': service_id, + 'PAY_ID': '840ab457-e7d1-4494-8197-9570da035170', + 'TRADE_POINT': 'term1', + 'SIGN': _make_sign(4, 'pay_account1', service_id, + '840ab457-e7d1-4494-8197-9570da035170', self.pay_system.secret) + }) + r = r.content.decode('utf-8') + r = parse(r) + status_code = int(r['pay-response']['status_code']) + self.assertEqual(status_code, -100) + + def non_existing_pay(self): + print('non_existing_pay') + current_date = timezone.now().strftime(self.time_format) + uuid = '9f154e93-d800-419a-92f7-da33529138be' + url = resolve_url('finapp:all_time_pay', self.pay_system.slug) + service_id = self.pay_system.service_id + r = self.client.get(url, { + 'ACT': 7, + 'SERVICE_ID': service_id, + 'PAY_ID': uuid, + 'SIGN': _make_sign(7, '', service_id, uuid, self.pay_system.secret) + }) + r = r.content.decode('utf-8') + xml = ''.join(( + "", + "-10", + "%s" % escape(current_date), + "" + )) + self.assertXMLEqual(r, xml) + + def test_pays(self): + self.user_pay_view() + self.user_pay_pay() + self.user_pay_check() + self.check_ballance() + self.try_pay_double() + self.non_existing_pay() diff --git a/finapp/urls.py b/finapp/urls.py new file mode 100644 index 0000000..deaf102 --- /dev/null +++ b/finapp/urls.py @@ -0,0 +1,25 @@ +from django.urls import path +from finapp import views + + +app_name = 'finapp' + +urlpatterns = [ + path('', views.AllTimeGatewaysListView.as_view(), + name='alltime_gateways_list'), + + # path('fin_report/', views.BasicFinReport.as_view(), name='fin_report'), + # path('pay/', views.terminal_pay, name='terminal_pay'), + + path('add/', views.AddAllTimeGateway.as_view(), + name='add_alltime_gateway'), + + path('/pay_history/', views.PayHistoryListView.as_view(), + name='pay_history'), + + path('/make_pay/', views.AllTimePay.as_view(), + name='all_time_pay'), + + path('/edit/', views.EditPayUpdateView.as_view(), + name='edit_pay_gw'), +] diff --git a/finapp/views.py b/finapp/views.py new file mode 100644 index 0000000..2f2930a --- /dev/null +++ b/finapp/views.py @@ -0,0 +1,222 @@ +import csv +from hashlib import md5 + +from django.contrib import messages +from django.contrib.auth.mixins import PermissionRequiredMixin +from django.db import DatabaseError +from django.db.models import Count +from django.http import HttpResponse +from django.shortcuts import get_object_or_404, resolve_url +from django.urls import reverse_lazy +from django.utils import timezone +from django.utils.decorators import method_decorator +from django.views.generic import ListView, DetailView, CreateView, UpdateView +from django.utils.translation import ugettext_lazy as _ +from xmlview.decorators import xml_view +from djing import lib +from djing.global_base_views import OrderedFilteredList +from djing.lib import safe_int +from djing.lib.mixins import LoginAdminMixin, LoginAdminPermissionMixin +from finapp.forms import PayAllTimeGatewayForm +from finapp.models import AllTimePayLog, PayAllTimeGateway +from abonapp.models import Abon + + +class AllTimePay(DetailView): + http_method_names = 'get', + model = PayAllTimeGateway + pk_url_kwarg = 'slug' + slug_url_kwarg = 'pay_slug' + + @staticmethod + def _bad_ret(err_id, err_description=None): + now = timezone.now() + r = { + 'status_code': lib.safe_int(err_id), + 'time_stamp': now.strftime("%d.%m.%Y %H:%M") + } + if err_description: + r.update({'description': err_description}) + return r + + def check_sign(self, data: dict, sign: str) -> bool: + act = safe_int(data.get('ACT')) + pay_account = data.get('PAY_ACCOUNT') + serv_id = data.get('SERVICE_ID') + pay_id = data.get('PAY_ID') + md = md5() + s = '_'.join( + (str(act), pay_account or '', serv_id or '', + pay_id, self.object.secret) + ) + md.update(bytes(s, 'utf-8')) + our_sign = md.hexdigest() + return our_sign == sign + + @method_decorator(xml_view(root_node='pay-response')) + def get(self, request, *args, **kwargs): + self.object = self.get_object() + act = lib.safe_int(request.GET.get('ACT')) + self.current_date = timezone.now().strftime("%d.%m.%Y %H:%M") + + if act <= 0: + return self._bad_ret(-101, 'ACT must be more than 0') + if not self.check_sign(request.GET, request.GET.get('SIGN').lower()): + self._bad_ret(-101, 'Bad sign') + + try: + if act == 1: + return self._fetch_user_info(request.GET) + elif act == 4: + return self._make_pay(request.GET) + elif act == 7: + return self._check_pay(request.GET) + else: + return self._bad_ret(-101, 'ACT is not passed') + except Abon.DoesNotExist: + return self._bad_ret(-40, 'Account does not exist') + except DatabaseError: + return self._bad_ret(-90) + except AllTimePayLog.DoesNotExist: + return self._bad_ret(-10) + except AttributeError: + return self._bad_ret(-101) + + def _fetch_user_info(self, data: dict): + pay_account = data.get('PAY_ACCOUNT') + abon = Abon.objects.get(username=pay_account) + fio = abon.fio + ballance = float(abon.ballance) + return { + 'balance': ballance, + 'name': fio, + 'account': pay_account, + 'service_id': self.object.service_id, + 'min_amount': 10.0, + 'max_amount': 5000, + 'status_code': 21, + 'time_stamp': self.current_date + } + + def _make_pay(self, data: dict): + trade_point = lib.safe_int(data.get('TRADE_POINT')) + receipt_num = lib.safe_int(data.get('RECEIPT_NUM')) + pay_account = data.get('PAY_ACCOUNT') + pay_id = data.get('PAY_ID') + pay_amount = lib.safe_float(data.get('PAY_AMOUNT')) + abon = Abon.objects.get(username=pay_account) + pays = AllTimePayLog.objects.filter(pay_id=pay_id) + if pays.exists(): + return self._bad_ret(-100, 'Pay already exists') + + abon.add_ballance( + None, pay_amount, + comment='%s %.2f' % (self.object.title, pay_amount) + ) + abon.save(update_fields=('ballance',)) + + AllTimePayLog.objects.create( + pay_id=pay_id, + summ=pay_amount, + abon=abon, + trade_point=trade_point, + receipt_num=receipt_num, + pay_gw=self.object + ) + return { + 'pay_id': pay_id, + 'service_id': data.get('SERVICE_ID'), + 'amount': pay_amount, + 'status_code': 22, + 'time_stamp': self.current_date + } + + def _check_pay(self, data: dict): + pay_id = data.get('PAY_ID') + pay = AllTimePayLog.objects.get(pay_id=pay_id) + return { + 'status_code': 11, + 'time_stamp': self.current_date, + 'transaction': { + 'pay_id': pay_id, + 'service_id': data.get('SERVICE_ID'), + 'amount': pay.summ, + 'status': 111, + 'time_stamp': pay.date_add.strftime("%d.%m.%Y %H:%M") + } + } + + +class BasicFinReport(LoginAdminMixin, ListView): + model = AllTimePayLog + queryset = AllTimePayLog.objects.by_days() + template_name = 'finapp/fin_report.html' + context_object_name = 'logs' + + def get(self, request, *args, **kwargs): + res_format = request.GET.get('f') + if res_format == 'csv': + response = HttpResponse(content_type='text/csv') + response['Content-Disposition'] = 'attachment; filename="report.csv"' + writer = csv.writer(response, quoting=csv.QUOTE_NONNUMERIC) + for row in self.object_list: + writer.writerow( + (row['summ'], row['pay_date'].strftime('%Y-%m-%d')) + ) + return response + return super().get(request, *args, **kwargs) + + +class PayHistoryListView(LoginAdminMixin, PermissionRequiredMixin, + OrderedFilteredList): + permission_required = 'group_app.view_group' + context_object_name = 'pay_history' + template_name = 'finapp/payHistory.html' + model = AllTimePayLog + + def get_queryset(self): + pay_history = AllTimePayLog.objects.filter( + pay_gw__slug=self.kwargs.get('pay_slug') + ).order_by('-date_add') + return pay_history + + def get_context_data(self, **kwargs): + context = { + 'pay_gw': get_object_or_404(PayAllTimeGateway, slug=self.kwargs.get('pay_slug')) + } + context.update(kwargs) + return super(PayHistoryListView, self).get_context_data(**context) + + +class AddAllTimeGateway(LoginAdminMixin, PermissionRequiredMixin, CreateView): + permission_required = 'finapp.add_payalltimegateway' + model = PayAllTimeGateway + form_class = PayAllTimeGatewayForm + success_url = reverse_lazy('finapp:alltime_gateways_list') + + def form_valid(self, form): + messages.success(self.request, _('New pay gateway created successfully')) + return super(AddAllTimeGateway, self).form_valid(form) + + +class AllTimeGatewaysListView(LoginAdminPermissionMixin, ListView): + permission_required = 'finapp.view_payalltimegateway' + model = PayAllTimeGateway + queryset = PayAllTimeGateway.objects.annotate( + pays_count=Count('alltimepaylog') + ) + + +class EditPayUpdateView(LoginAdminPermissionMixin, UpdateView): + permission_required = 'finapp.change_payalltimegateway' + model = PayAllTimeGateway + form_class = PayAllTimeGatewayForm + pk_url_kwarg = 'slug' + slug_url_kwarg = 'pay_slug' + + def get_success_url(self): + return resolve_url('finapp:edit_pay_gw', self.object.slug) + + def form_valid(self, form): + messages.success(self.request, _('Payment gateway successfully updated')) + return super(EditPayUpdateView, self).form_valid(form) diff --git a/gw_app/tests.py b/gw_app/tests.py index 344e820..0b57879 100644 --- a/gw_app/tests.py +++ b/gw_app/tests.py @@ -1,6 +1,6 @@ from abc import ABCMeta -from abonapp.models.generic import Abon +from abonapp.models import Abon from accounts_app.models import UserProfile from django.conf import settings from django.shortcuts import resolve_url diff --git a/locale/ru/LC_MESSAGES/django.po b/locale/ru/LC_MESSAGES/django.po index 43fb3cd..54a1c4d 100644 --- a/locale/ru/LC_MESSAGES/django.po +++ b/locale/ru/LC_MESSAGES/django.po @@ -125,3 +125,6 @@ msgstr "На сервере произошла ошибка. Пожалуйст msgid "Are you sure about them?" msgstr "Вы уверены в этом?" + +msgid "Finance" +msgstr "Финансы" diff --git a/periodic.py b/periodic.py index 5063140..cc00c3f 100755 --- a/periodic.py +++ b/periodic.py @@ -8,7 +8,7 @@ django.setup() from django.utils import timezone from django.db import transaction from django.db.models import signals, Count -from abonapp.models.generic import Abon, AbonTariff, abontariff_pre_delete, \ +from abonapp.models import Abon, AbonTariff, abontariff_pre_delete, \ PeriodicPayForId, AbonLog from gw_app.nas_managers import NasNetworkError, NasFailedResult from gw_app.models import NASModel diff --git a/requirements.txt b/requirements.txt index c0fa958..1de9427 100644 --- a/requirements.txt +++ b/requirements.txt @@ -25,6 +25,7 @@ pyst2 django-bitfield transliterate asterisk +django-encrypted-model-fields # django-xmlview for pay system allpay -e git://github.com/nerosketch/django-xmlview.git#egg=django-xmlview diff --git a/searchapp/views.py b/searchapp/views.py index 5f7ccdc..78c111f 100644 --- a/searchapp/views.py +++ b/searchapp/views.py @@ -2,7 +2,7 @@ import re from django.db.models import Q from django.shortcuts import render from django.utils.html import escape -from abonapp.models.generic import Abon +from abonapp.models import Abon from devapp.models import Device from djing import MAC_ADDR_REGEX from django.contrib.auth.decorators import login_required diff --git a/taskapp/models.py b/taskapp/models.py index a413fcf..3b25a3d 100644 --- a/taskapp/models.py +++ b/taskapp/models.py @@ -6,7 +6,7 @@ from django.conf import settings from django.shortcuts import resolve_url from django.utils import timezone from django.utils.translation import ugettext as _ -from abonapp.models.generic import Abon +from abonapp.models import Abon from .handle import handle as task_handle TASK_PRIORITIES = ( diff --git a/taskapp/views.py b/taskapp/views.py index 26f9d16..03e9d45 100644 --- a/taskapp/views.py +++ b/taskapp/views.py @@ -14,7 +14,7 @@ from django.views.generic.edit import FormMixin, DeleteView, UpdateView from guardian.decorators import permission_required_or_403 as permission_required from chatbot.models import MessageQueue -from abonapp.models.generic import Abon +from abonapp.models import Abon from djing import httpresponse_to_referrer from djing.lib import safe_int, MultipleException, RuTimedelta from djing.lib.decorators import only_admins, json_view diff --git a/templates/base.html b/templates/base.html index 7d261fb..937955f 100644 --- a/templates/base.html +++ b/templates/base.html @@ -62,6 +62,15 @@ + {% if perms.fonapp.view_payalltimegateway %} + {% url 'finapp:alltime_gateways_list' as finhome %} + + + {% trans 'Finance' %} + + + {% endif %} + {% url 'ip_pool:networks' as ippool_home %}