diff --git a/Admin/settings.py b/Admin/settings.py index 57601c5..a8b2f22 100644 --- a/Admin/settings.py +++ b/Admin/settings.py @@ -9,21 +9,16 @@ import pytz # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent - - # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/4.1/howto/deployment/checklist/ - # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'django-insecure-5*mm&uf5zq@t6nrs_5z8-_qtyapm^3&yz^wqqkc_a!v(!ulj-^' +SECRET_KEY = os.getenv("adminAS_KEY") # SECURITY WARNING: don't run with debug turned on in production! DEBUG = False ALLOWED_HOSTS = ['*'] - # Application definition - INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', @@ -39,8 +34,9 @@ INSTALLED_APPS = [ 'allauth.account', 'allauth.socialaccount', 'widget_tweaks', - 'Admin', + 'Clientes', + 'Sistemas', ] REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ @@ -57,9 +53,7 @@ MIDDLEWARE = [ 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] - ROOT_URLCONF = 'Admin.urls' - TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', @@ -68,11 +62,12 @@ TEMPLATES = [ 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', + # `allauth` needs this from django 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', - # `allauth` needs this from django - 'django.template.context_processors.request', + + #custom context processor 'Clientes.saldo_context_proc.get_saldo', ], }, @@ -104,15 +99,24 @@ ACCOUNT_LOGOUT_REDIRECT_URL = '/accounts/login/' ACCOUNT_SIGNUP_REDIRECT_URL =LOGIN_REDIRECT_URL ACCOUNT_LOGOUT_ON_PASSWORD_CHANGE = True + EMAIL_TIMEOUT = 10 -#EMAIL_USE_TLS = True EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' -EMAIL_HOST = 'secure.emailsrvr.com' -EMAIL_PORT = 465 -EMAIL_HOST_USER = 'noreply@aduanasoft.com.mx' -EMAIL_HOST_PASSWORD = 'N036p7y!' EMAIL_SUBJECT_PREFIX = 'AS Timbres' -EMAIL_USE_SSL=True +if DEBUG : + EMAIL_USE_TLS = True + EMAIL_HOST = 'smtp.gmail.com' + EMAIL_PORT = 587 + EMAIL_HOST_USER = 'aduanasoftpruebas@gmail.com' + EMAIL_HOST_PASSWORD = os.getenv("test_pwd_email") + #EMAIL_USE_SSL=False +else: + EMAIL_USE_TLS = False + EMAIL_HOST = 'secure.emailsrvr.com' + EMAIL_PORT = 465 + EMAIL_HOST_USER = 'noreply@aduanasoft.com.mx' + EMAIL_HOST_PASSWORD = os.getenv("pwd_email") + EMAIL_USE_SSL=True # Database @@ -126,7 +130,7 @@ if DEBUG: 'ENGINE': 'django.db.backends.mysql', 'NAME': 'cfdi_as', 'USER': 'root', - 'PASSWORD': 'Soluciones28@', + 'PASSWORD':os.getenv("BD_PASS"), 'HOST': '127.0.0.1', 'PORT': '', 'OPTIONS': {'init_command': "SET sql_mode='STRICT_TRANS_TABLES'"}, @@ -138,7 +142,7 @@ else: 'ENGINE': 'django.db.backends.mysql', 'NAME': 'fjrodriguez$cfdi_as', 'USER': 'fjrodriguez', - 'PASSWORD': 'Soluciones28@', + 'PASSWORD':'Soluciones28@', 'HOST': 'fjrodriguez.mysql.pythonanywhere-services.com', 'PORT': '3306', 'OPTIONS': {'init_command': "SET sql_mode='STRICT_TRANS_TABLES'"}, @@ -170,7 +174,7 @@ AUTH_PASSWORD_VALIDATORS = [ LANGUAGE_CODE = 'es-MX' -TIME_ZONE = 'CST6CDT' +TIME_ZONE = 'America/Chihuahua' USE_I18N = True diff --git a/Admin/urls.py b/Admin/urls.py index 184a42e..49e0e97 100644 --- a/Admin/urls.py +++ b/Admin/urls.py @@ -9,38 +9,48 @@ from django.http import HttpResponse from django.test import SimpleTestCase, override_settings from rest_framework.authtoken.views import obtain_auth_token + + def response_error_handler(request, exception=None): context={} + return render(request, '403.html',context,status=403) +def response_error_handler_404(request,exception=None): + + return render(request,'404.html',status=404) def permission_denied_view(request): + raise PermissionDenied urlpatterns = [ path('admin/', admin.site.urls), - path('accounts/', include('allauth.urls')), + path('accounts/', include('allauth.urls')), path('DRF_Token/', obtain_auth_token, name='DRF_Token'), path('', include('Clientes.urls')), + path('sistemas/',include('Sistemas.urls')), path('403/', permission_denied_view), + + + + ] handler403 = response_error_handler - - +handler404 = response_error_handler_404 if settings.DEBUG: #DEV only urlpatterns += static(settings.STATIC_URL, document_root= settings.STATIC_ROOT) urlpatterns += static(settings.MEDIA_URL, document_root= settings.MEDIA_ROOT) - - # ROOT_URLCONF must specify the module that contains handler403 = ... @override_settings(ROOT_URLCONF=__name__) class CustomErrorHandlerTests(SimpleTestCase): - def test_handler_renders_template_response(self): response = self.client.get('/403/') + # Make assertions on the response here. For example: - self.assertContains(response, 'Error handler content', status_code=403) \ No newline at end of file + self.assertContains(response, 'Error handler content', status_code=403) + diff --git a/Clientes/admin.py b/Clientes/admin.py index 561cda6..c180667 100644 --- a/Clientes/admin.py +++ b/Clientes/admin.py @@ -4,10 +4,12 @@ from .models import Timbres, saldoModel, Clientes,ErroresTimbres class TimbresAdmin(admin.ModelAdmin): list_display=['uuid','rfcc','rfcp','fecha','folio','serie','tipo','modo','created_at'] + list_filter=['modo','tipo'] class ClientesAdmin(admin.ModelAdmin): list_display= ['RFC','Nombre','Activo','fecha_baja'] + list_filter =['RFC','Activo'] admin.site.register(Timbres,TimbresAdmin) admin.site.register(saldoModel) diff --git a/Clientes/custom_decorators.py b/Clientes/custom_decorators.py index c635a1a..b37afe6 100644 --- a/Clientes/custom_decorators.py +++ b/Clientes/custom_decorators.py @@ -1,15 +1,16 @@ from functools import wraps from django.contrib import messages from django.shortcuts import redirect -from django.http import HttpResponse - - +from django.http import HttpResponse, JsonResponse +from django.contrib.auth import authenticate, login +from rest_framework.authtoken.models import Token +from django.contrib.auth.models import User +import base64 +from django.shortcuts import get_object_or_404 def Custom_is_staff_function(user): if user.is_staff: return True return False - - def is_staff_access(view_to_return="index"): def decorator(view): @wraps(view) @@ -19,4 +20,38 @@ def is_staff_access(view_to_return="index"): return redirect(view_to_return) return view(request, *args, **kwargs) return _wrapped_view + return decorator + +#--------------------Auth basica +def auth_basic(request,*args, **kwargs): + if request.META['CONTENT_TYPE'] == 'application/json' and 'HTTP_AUTHORIZATION' in request.META.keys(): + authmeth, auth = request.META['HTTP_AUTHORIZATION'].split(' ', 1) + if authmeth.lower() == 'token': + tokenA,user = auth.split(':', 1) + user = base64.b64decode(user) + user = user.decode('utf-8') + token = get_object_or_404(Token, key=tokenA) + if token and str(token.user)==user: + return True + #user= authenticate(username=token.user, password=pwd) + # print('user.is_authenticated',user.is_authenticated) + #if user.is_authenticated: + # return True + else: + return False + return False + elif request.META['CONTENT_TYPE'] == 'application/json' and 'HTTP_AUTHORIZATION' not in request.META.keys(): + return request.user.is_authenticated + else: + return False + + +def http_basic_auth(): + def decorator(view): + @wraps(view) + def _wrapped_view(request,*args, **kwargs): + if not auth_basic(request,*args, **kwargs): + return JsonResponse({'Error':'las credenciales Token:user(base64) son incorrectas.'},status=401) + return view(request, *args, **kwargs) + return _wrapped_view return decorator \ No newline at end of file diff --git a/Clientes/forms.py b/Clientes/forms.py index 22714ab..c95ff05 100644 --- a/Clientes/forms.py +++ b/Clientes/forms.py @@ -10,7 +10,7 @@ class EmailForm(forms.Form): subject = forms.CharField(max_length=100, required=False) adjunto = forms.FileField(widget=forms.ClearableFileInput( attrs={ - 'multiple': True, + 'multiple': True, 'accept':'application/pdf,application/vnd.ms-excel', diff --git a/Clientes/models.py b/Clientes/models.py index a98ba6f..d54d68b 100644 --- a/Clientes/models.py +++ b/Clientes/models.py @@ -40,49 +40,39 @@ class Clientes(models.Model): fecha_baja = models.DateField(blank=True,null=True) email = models.EmailField(max_length=254, blank=True) conteo_mes = models.IntegerField(blank=True,null=True,default=0) - def timbres_X_MES(self, mes): - - today = datetime.date.today() - year = today.year - + def timbres_X_MES(self, mes=None, year=None, PAC=None): + today = datetime.date.today() + if year is None: + year = today.year if mes==None: mes = today.month - dat = datetime.datetime(int(year),int(mes),1) - if dat.month in (1,3,5,7,8,10,12):#31 - findate = dat + datetime.timedelta(days=30) - #findate += datetime.timedelta(days=0) - elif dat.month in (4,6,9,11):#30 - - findate = dat + datetime.timedelta(days=29) - #findate += datetime.timedelta(days=0) - else:#28 or 29 - findate = dat + datetime.timedelta(days=28) - findate += datetime.timedelta(days=1) - #print(f'dat {(dat)} fdate={findate}') - cou = Timbres.objects.filter(rfcc=self.RFC, created_at__range=[dat,findate]).count() + dat = datetime.datetime(int(year),int(mes),1) + if dat.month in (1,3,5,7,8,10,12):#31 + findate = dat + datetime.timedelta(days=30) + + elif dat.month in (4,6,9,11):#30 + findate = dat + datetime.timedelta(days=29) + + else:#28 or 29 + findate = dat + datetime.timedelta(days=28) + findate += datetime.timedelta(days=1) + if PAC: + cou = Timbres.objects.filter(rfcc=self.RFC, created_at__range=[dat,findate], rfcp=PAC).count() + else: + cou = Timbres.objects.filter(rfcc=self.RFC, created_at__range=[dat,findate]).count() self.conteo_mes =cou self.save() - return cou + @property - def timbres_mes_count(self): - + def timbres_mes_count(self): today = datetime.date.today() month = today.month - year = today.year + year = today.year return Timbres.objects.filter(rfcc=self.RFC, created_at__year=str(year),created_at__month=str(month)).count() class Meta: ordering = ('-Activo','-conteo_mes','RFC') - - -class Maquinas_Conectadas(models.Model): - UserName = models.CharField(max_length=255) - PC_Name = models.CharField(max_length=255) - Is64 = models.BooleanField() - OSversion= models.CharField(max_length=255) - local_ip = models.CharField(max_length=55) - public_ip= models.CharField(max_length=55) - RFC = models.CharField(max_length=13) - class Meta: - abstract =True + + def __str__(self): + return self.Nombre diff --git a/Clientes/saldo_context_proc.py b/Clientes/saldo_context_proc.py index 355bf95..36cf467 100644 --- a/Clientes/saldo_context_proc.py +++ b/Clientes/saldo_context_proc.py @@ -1,5 +1,4 @@ import requests -from asgiref.sync import sync_to_async from .models import saldoModel import datetime diff --git a/Clientes/serailizers.py b/Clientes/serailizers.py index 1e62dd8..9e7cab0 100644 --- a/Clientes/serailizers.py +++ b/Clientes/serailizers.py @@ -4,4 +4,4 @@ from .models import Clientes class ClienteSerializer(serializers.ModelSerializer): class Meta: model = Clientes - fields = ('RFC', 'Nombre', 'Activo', 'fecha_baja',) \ No newline at end of file + fields = ('RFC', 'Nombre', 'Activo', 'fecha_baja',) diff --git a/Clientes/tests.py b/Clientes/tests.py index 7ce503c..882f46f 100644 --- a/Clientes/tests.py +++ b/Clientes/tests.py @@ -1,3 +1,56 @@ from django.test import TestCase +from django.urls import reverse -# Create your tests here. +from django.contrib.auth.models import User +from .models import Clientes + +class ClientesTests(TestCase): + @classmethod + def setUpTestData(cls): + test_user = User.objects.create_user(username='testuser1',password='Soluciones28@') + test_user.save() + + cls.cliente = Clientes.objects.create( + RFC="SCT050708EB2", + Nombre="Aduanasoft", + Activo=True, + #fecha_baja="2022-12-20", + email="fjrodriguez28@gmail.com", + conteo_mes=12, + ) + + + def test_redirect_if_not_logged_in(self): + response = self.client.get(reverse('index')) + self.assertRedirects(response, '/accounts/login/?next=/') + + def test_cliente_content(self): + self.assertEqual(self.cliente.RFC,"SCT050708EB2") + self.assertEqual(self.cliente.Nombre,"Aduanasoft") + self.assertEqual(self.cliente.Activo,True) + #self.assertEqual(self.cliente.fecha_baja,"2022-12-20") + self.assertEqual(self.cliente.email,"fjrodriguez28@gmail.com") + self.assertEqual(self.cliente.conteo_mes,12) + + #test for login user view and contains client with RFC SCT... + def test_cliente_login_cliente_listview(self): + login = self.client.login(username='testuser1',password='Soluciones28@') + response = self.client.get(reverse('index')) + + self.assertEqual(str(response.context['user']), 'testuser1') + + self.assertEqual(response.status_code,200) + self.assertContains(response, "SCT050708EB2") + self.assertTemplateUsed(response, "Clientes/index.html") + +#---------------_APIs Views Tests +from rest_framework import status +from rest_framework.test import APITestCase, APIClient + +class APITests(APITestCase): + @classmethod + def setUpTestData(cls): + pass + + def test_api_check_RFC(self): + pass \ No newline at end of file diff --git a/Clientes/urls.py b/Clientes/urls.py index 4f92fe0..3901903 100644 --- a/Clientes/urls.py +++ b/Clientes/urls.py @@ -4,8 +4,12 @@ from .views import ( timbres_cliente, ClientesUpdateView, ClientesCreateView, + ErroresTimbresListView, export_Excel, send_timbres_Email, + Retrive_Cliente_Email, + PACS_Retrive_RFCS, + #API DRF saldo_funct2, @@ -15,15 +19,17 @@ from .views import ( ) urlpatterns = [ - path('', index, name='index'), + path('', index, name='index'), path('add_timbre2/', add_timbre2.as_view(), name='add_timbre2'), path('timbres_cliente//', timbres_cliente, name='timbres_cliente'), path('cliente/update//',ClientesUpdateView.as_view(),name='update_cliente'), path('cliente/add/', ClientesCreateView.as_view(), name='add_cliente'), + path('errores/timbres/list/', ErroresTimbresListView.as_view(), name='ErroresTimbresList'), path('get_timbres_xls/', export_Excel, name='export_Excel'), path('send_timbres_cliente/',send_timbres_Email, name='send_timbres_Email'), path('getActivoRFC/', check_RFC.as_view(), name='check_active_RFC'), path('get_saldo2/', saldo_funct2.as_view(), name='saldo_funct2'), path('check_host/',check_host.as_view(),name='check_host'), - + path('emails_cliente/',Retrive_Cliente_Email, name='Retrive_Cliente_Email'), + path('pacs/list/',PACS_Retrive_RFCS,name='PACS_Retrive_RFCS'), ] \ No newline at end of file diff --git a/Clientes/views.py b/Clientes/views.py index 16746c2..9c90adb 100644 --- a/Clientes/views.py +++ b/Clientes/views.py @@ -1,26 +1,33 @@ +import os +import re +import datetime +import functools + +from asgiref.sync import sync_to_async +from django.conf import settings +from django.core.files.storage import FileSystemStorage from django.shortcuts import render,redirect from django.contrib import messages -from django.http import HttpResponse -from django.http import JsonResponse +from django.http import HttpResponse,JsonResponse from django.contrib.auth.decorators import login_required -from .custom_decorators import is_staff_access +from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger +from django.db.models import Q +from django.views.generic.edit import CreateView,UpdateView +from django.views.generic.list import ListView +from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin +from .custom_decorators import is_staff_access, http_basic_auth from .models import Clientes,Timbres,saldoModel,ErroresTimbres +from .serailizers import ClienteSerializer +from .forms import ClienteForm,EmailForm from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.permissions import IsAuthenticated +from rest_framework import status + +from Sistemas.permissions import ItsAdminToken -from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger -from django.db.models import Q -import datetime -from django.views.generic.edit import CreateView,UpdateView -from .forms import ClienteForm,EmailForm -from django.contrib.auth.mixins import LoginRequiredMixin -from django.contrib.auth.mixins import UserPassesTestMixin -from asgiref.sync import sync_to_async -from django.shortcuts import get_object_or_404 -from .serailizers import ClienteSerializer #EXCEL from openpyxl import Workbook from openpyxl.styles import Alignment, Border, Font, PatternFill, Side @@ -29,32 +36,26 @@ from openpyxl.styles import Alignment, Border, Font, PatternFill, Side from django.core.mail import EmailMessage from django.conf import settings from io import BytesIO -import functools - -from django.core.files.storage import FileSystemStorage -import os -from django.conf import settings -import re def read_env_file(): try: #env_file = os.listdir(settings.BASE_DIR) storage = FileSystemStorage(location=settings.BASE_DIR) env_file= os.path.join(settings.BASE_DIR,'.env') - print('EEEEE: ', env_file) + lista =['asds', 'asdasd','sss','com' ,'pol.com'] if storage.exists(env_file): with open(env_file, 'r') as file: data = file.read() data =re.findall('"([^"]*)"',data) - print('data',data) + for x in lista: data.append(x) res= ' '.join(data) final = '"'+res+'"' - print('final',final) + with open(env_file, 'w') as newFile: newFile.write("export hosts="+final) except: @@ -62,6 +63,7 @@ def read_env_file(): @sync_to_async(thread_sensitive=False) @login_required +@is_staff_access() def send_timbres_Email(request): req = request.method @@ -71,6 +73,7 @@ def send_timbres_Email(request): messages.add_message(request, messages.ERROR, f'{form.errors}') return redirect('index') today = datetime.date.today() + year = today.year RFC = request.GET.get('RFC', None) if req=='GET' else form.cleaned_data["RFC"] @@ -84,7 +87,7 @@ def send_timbres_Email(request): if req=='POST': email = form.cleaned_data["email"] if 'email' in form.cleaned_data.keys() else '' emails = [Cli.email] if req=='GET' else [ Cli.email, email ] - + if len(emails) ==0 : messages.add_message(request, messages.ERROR, f'La lista de correos esta vacia, favor de agregar un correo.') return redirect('index') @@ -98,9 +101,9 @@ def send_timbres_Email(request): else: findate = dat+datetime.timedelta(days=28) findate +=datetime.timedelta(days=1) - print(f'dat{dat} findate:{findate}') + if mes is not None and RFC is not None: - objeto_a_trabajar = Timbres.objects.filter(rfcc=RFC, created_at__range=[dat,findate]) + objeto_a_trabajar = Timbres.objects.filter(rfcc=RFC, created_at__range=[dat,findate]) else: objeto_a_trabajar = Clientes.objects.all() for i,ii in enumerate(objeto_a_trabajar): @@ -132,9 +135,9 @@ def send_timbres_Email(request): contenido = "attachment; filename = {0}".format(nombre_archivo) response["Content-Disposition"] = contenido wb.save(response) - + files = BytesIO(response.content) - + try: mail = EmailMessage(subject,message,settings.EMAIL_HOST_USER,emails) mail.attach(filename='Timbres.xls',content=files.getbuffer(),mimetype='application/vnd.ms-excel') @@ -162,28 +165,47 @@ def send_timbres_Email(request): def index(request): #read_env_file() clientes_list = Clientes.objects.all() + PAC = request.GET.get('PAC',None) mes = request.GET.get('mes', None) + today = datetime.date.today() + + year = request.GET.get('year',None) + if year is None or year =='None': + year= today.year + if mes is None or mes =='None': + mes = today.month page = request.GET.get('page', 1) search = request.GET.get('search',None) rfcc = request.GET.get('rfcc', None) - filters = {key:value[0] for (key,value) in dict(request.GET).items() if value !=[""]} + filters = {key:value[0] for (key,value) in dict(request.GET).items() if value !=[""] or value!=None} filters.pop('page', '') filters.pop('datepicker','') filters.pop('datepickerFin','') - print('filters------',filters) - if rfcc: + filters.pop('mes','') + filters.pop('year','') + if PAC=='00': + filters.pop('PAC') + PAC=None + + if rfcc and search: clientes_list = Clientes.objects.filter(Q(RFC__icontains=search)) - for i,ii in enumerate(clientes_list): - ii.timbres_X_MES(mes=mes) + + met = iter(clientes_list) + while met: + try: + c = next(met) + c.timbres_X_MES(mes=mes,year=year, PAC=PAC) + except StopIteration: + break + clientes_list =pageFunc(page,clientes_list,20) - filters.pop('mes','') context = { 'lista':clientes_list, 'mes':mes, + 'year':year, 'filters':filters, 'emailForm':EmailForm(), - } return render(request,'Clientes/index.html',context) @@ -206,41 +228,69 @@ def timbres_cliente(request, RFC): lista = Timbres.objects.filter(rfcc=RFC) else: lista = Timbres.objects.filter(rfcc=RFC,modo='Normal') - - PAC= request.GET.get('PAC',None) - if PAC=='01': - lista = lista.filter(rfcp='EME000602QR9') - if PAC=='02': - lista = lista.exclude(rfcp='EME000602QR9') + + mes = request.GET.get('mes', None) + year = request.GET.get('year',None) + + if year is None or year=='': + today= datetime.date.today() + year =today.year + search = request.GET.get('search',None) page = request.GET.get('page', 1) datepicker = request.GET.get('datepicker', None) datepickerFin = request.GET.get('datepickerFin', None) - tipo = request.GET.get('tipo',None) - + tipo = request.GET.get('tipo',None) + PAC= request.GET.get('PAC',None) + + if PAC is not None and PAC !='00': + lista = lista.filter(rfcp=PAC) filters = {key:value[0] for (key,value) in dict(request.GET).items() if value !=[""]} + filters.pop('page', '') - filters.pop('PAC', '') - - - if tipo: + if 'PAC' not in filters: + filters['PAC']='00' + if 'mes' not in filters: + filters['mes']='00' + + if tipo and search is not None: lista = lista.filter(Q(tipo__icontains=search)) if datepicker and datepickerFin: - inicio = [int(i) for i in datepicker.split("/")] - fin = [int(i) for i in datepickerFin.split("/")] - #print('inicio',inicio,' fin',fin) + # inicio = [int(i) for i in datepicker.split("/")] + # fin = [int(i) for i in datepickerFin.split("/")] + inicio = [int(i) for i in datepicker.split("-")] + fin = [int(i) for i in datepickerFin.split("-")] + + #start = datetime.datetime(inicio[2],inicio[0],inicio[1]) + start = datetime.datetime(inicio[0],inicio[1],inicio[2]) - start = datetime.datetime(inicio[2],inicio[0],inicio[1]) start += datetime.timedelta(days=0) - end = datetime.datetime(fin[2],fin[0],fin[1]) - end += datetime.timedelta(days=1) - #datetime.date.today() - #print('FECHA',datetime.datetime.today(), 'HORA') - print('start',start, 'end',end) + # end = datetime.datetime(fin[2],fin[0],fin[1]) + end = datetime.datetime(fin[0],fin[1],fin[2]) + + end += datetime.timedelta(days=1) + lista = lista.filter(created_at__range=[start, end]) + if mes is None or mes =='None' or mes=='': + mes='00' #Todos + + if mes != '00': + + dat = datetime.datetime(int(year), int(mes),1) + if dat.month in(1,3,5,7,8,10,12): + findate = dat+datetime.timedelta(days=30) + elif dat.month in (4,6,9,11): + findate = dat+datetime.timedelta(days=29) + else: + findate = dat+datetime.timedelta(days=28) + findate +=datetime.timedelta(days=1) + lista = lista.filter(created_at__range=[dat,findate]) + else: + lista = lista.filter(created_at__year=int(year)) + conteo = lista.count() lista =pageFunc(page,lista,50) @@ -256,6 +306,7 @@ def timbres_cliente(request, RFC): @sync_to_async(thread_sensitive=False) @login_required +@is_staff_access() def export_Excel(request): RFC = request.GET.get('RFC', None) @@ -265,11 +316,11 @@ def export_Excel(request): month = today.month year = today.year - if mes is None or mes =='None': + if mes is None or mes =='None' or mes=='': mes = month dat =datetime.datetime(int(year), int(mes),1) - + if dat.month in(1,3,5,7,8,10,12): findate = dat +datetime.timedelta(days=30) elif dat.month in (4,6,9,11): @@ -277,19 +328,17 @@ def export_Excel(request): else: findate = dat+datetime.timedelta(days=28) findate +=datetime.timedelta(days=1) - print(f'dat{dat} findate:{findate}') + if mes is not None and RFC is not None: objeto_a_trabajar = Timbres.objects.filter(rfcc=RFC, created_at__range=[dat,findate]) else: objeto_a_trabajar = Clientes.objects.all() for i,ii in enumerate(objeto_a_trabajar): ii.timbres_X_MES(mes=mes ) - - wb = Workbook() ws = wb.active - + if RFC is not None: #Encabezado ws['A1']='RFC_EXPEDIDO' @@ -311,29 +360,59 @@ def export_Excel(request): response = HttpResponse(content_type="application/ms-excel") contenido = "attachment; filename = {0}".format(nombre_archivo) response["Content-Disposition"] = contenido - wb.save( response) - + wb.save( response) return response +@http_basic_auth() +def Retrive_Cliente_Email(request): + if request.method == 'GET': + clientes = list(Clientes.objects.values('email').filter(RFC=request.GET.get('RFC',None))) + return JsonResponse({'data':clientes}) + +@login_required +#@is_staff_access() +def PACS_Retrive_RFCS(request): + timbres = {} + status = 200 + if request.method == 'GET': + RFC= request.GET.get('RFC','') + + if RFC !='': + timbres =list(Timbres.objects.values('rfcp').filter(rfcc__in=[RFC])) + else: + timbres =list(Timbres.objects.values('rfcp').filter(rfcp__isnull=False)) + else: + status=403 + return JsonResponse({'PACS':timbres},status=status) + + #-----------------------------------API VIEWS #-------------------------------------------- + class check_RFC(APIView): - permission_classes = (IsAuthenticated,) + permission_classes = (ItsAdminToken,IsAuthenticated,) def get(self,request): - rfc = request.GET.get('RFC', None) - cliente, created = Clientes.objects.get_or_create(RFC=rfc) - if created: - cliente.Activo=True - cliente.save() - #cliente = get_object_or_404(Clientes, RFC=rfc) - serializer = ClienteSerializer(cliente) - return Response(serializer.data) + rfc = request.GET.get('RFC', None) + try: + if rfc is None or len(rfc)==0: + return Response({'Error': 'RFC parameter cannot be empty', 'isError': True}) + + cliente, created = Clientes.objects.get_or_create(RFC=rfc) + serializer = ClienteSerializer(cliente) + if created: + cliente.Activo=True + cliente.save() + + if not serializer.is_valid: + return Response({'Error':f'{serializer.errors}','isError':True},status=200) + return Response(serializer.data) + except Exception as E: + return Response({'Error':f'check_RFC:{E} RFC:{rfc}','isError':True}) class add_timbre2(APIView): - permission_classes = (IsAuthenticated,) - def get(self,request): - + permission_classes = (ItsAdminToken,IsAuthenticated,) + def get(self,request): uuid= request.GET.get('uuid', None) rfcc= request.GET.get('rfcc', None) fecha=request.GET.get('fecha', None) @@ -341,18 +420,10 @@ class add_timbre2(APIView): serie=request.GET.get('serie', None) tipo=request.GET.get('tipo', None) rfcp=request.GET.get('rfcp', None) - modo=request.GET.get('modo', None) - - obj={ - 'uuid':uuid, - 'rfcc':rfcc, - 'fecha':fecha, - 'folio':folio, - 'serie':serie, - 'tipo':tipo, - 'rfcp':rfcp, - 'modo':modo - } + modo=request.GET.get('modo', None) + obj={'uuid':uuid,'rfcc':rfcc,'fecha':fecha,'folio':folio, + 'serie':serie,'tipo':tipo,'rfcp':rfcp,'modo':modo + } try: obj = Timbres.objects.create(**obj) return Response({'data':'ok'}) @@ -364,10 +435,10 @@ class add_timbre2(APIView): folio=folio, modo=modo ) - return Response({'fok':f'{e}'}) + return Response({'Error':f'{e}'}) class saldo_funct2(APIView): - permission_classes =(IsAuthenticated,) + permission_classes =(ItsAdminToken,IsAuthenticated,) def get(self, request): timbres = request.GET.get('num',None) try: @@ -381,28 +452,30 @@ class saldo_funct2(APIView): return Response(content) class check_host(APIView): - permission_classes = (IsAuthenticated,) - + permission_classes = (ItsAdminToken,IsAuthenticated,) def post(self,request, format=None): data = request.data - print(data) + return Response(data) #---------------------------CLASS BASED VIEWS #-------------------------------------------- +class ErroresTimbresListView(LoginRequiredMixin,ListView): + model = ErroresTimbres + paginate_by = 100 + template_name = 'Clientes/Errores_Timbres_list.html' + class ClientesUpdateView(UserPassesTestMixin,LoginRequiredMixin,UpdateView): model= Clientes form_class=ClienteForm success_url='/' template_name='Clientes/edit_cliente.html' - def test_func(self): - + def test_func(self): res = self.request.user.groups.filter(name= 'admin_soft') if not res: messages.error(self.request, f'Lo sentimos. La página que buscas no está disponible, no cuentas con los permisos.') return res - class ClientesCreateView(UserPassesTestMixin,LoginRequiredMixin,CreateView): model = Clientes diff --git a/Sistemas/__init__.py b/Sistemas/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Sistemas/admin.py b/Sistemas/admin.py new file mode 100644 index 0000000..e7eb3f0 --- /dev/null +++ b/Sistemas/admin.py @@ -0,0 +1,28 @@ +from django.contrib import admin +from .models import Sistema, sistemas_por_cliente, Device,DeviceHistory +# Register your models here. + + + +class Sistema_Admin(admin.ModelAdmin): + def NSistema(self,obj): + return obj.nombre_sistema + list_display = ['NSistema'] + +class SPC(admin.ModelAdmin): + '''Sistemas Por Cliente''' + def Cliente(self,obj): + return obj + list_display = ['id_sistema','Cliente','num_licencias'] + +class DeviceHistoryAdmin(admin.ModelAdmin): + list_display = ['device','first_authentication', 'last_authentication'] + +class DeviceAdmin(admin.ModelAdmin): + list_display = ['client', 'device_name', 'ip_address', 'sistema', 'macAddress'] + + +admin.site.register(Sistema,Sistema_Admin) +admin.site.register(sistemas_por_cliente,SPC) +admin.site.register(Device,DeviceAdmin) +admin.site.register(DeviceHistory,DeviceHistoryAdmin) \ No newline at end of file diff --git a/Sistemas/apps.py b/Sistemas/apps.py new file mode 100644 index 0000000..0b7f7bc --- /dev/null +++ b/Sistemas/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class SistemasConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'Sistemas' diff --git a/Sistemas/migrations/0001_initial.py b/Sistemas/migrations/0001_initial.py new file mode 100644 index 0000000..4fcfe94 --- /dev/null +++ b/Sistemas/migrations/0001_initial.py @@ -0,0 +1,32 @@ +# Generated by Django 4.1.3 on 2023-01-19 19:43 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('Clientes', '0014_alter_clientes_options'), + ] + + operations = [ + migrations.CreateModel( + name='Sistema', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('nombre_sistema', models.CharField(max_length=100, unique=True)), + ], + ), + migrations.CreateModel( + name='sistemas_por_cliente', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('num_licencias', models.IntegerField(default=1)), + ('cliente', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='cliente_spc', to='Clientes.clientes')), + ('id_sistema', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sistema_spc', to='Sistemas.sistema')), + ], + ), + ] diff --git a/Sistemas/migrations/0002_device_alter_sistemas_por_cliente_options_and_more.py b/Sistemas/migrations/0002_device_alter_sistemas_por_cliente_options_and_more.py new file mode 100644 index 0000000..58025f2 --- /dev/null +++ b/Sistemas/migrations/0002_device_alter_sistemas_por_cliente_options_and_more.py @@ -0,0 +1,44 @@ +# Generated by Django 4.1.3 on 2023-01-23 15:37 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('Clientes', '0014_alter_clientes_options'), + ('Sistemas', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Device', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('password', models.CharField(max_length=128, verbose_name='password')), + ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), + ('device_name', models.CharField(max_length=255)), + ('device_os', models.CharField(max_length=255)), + ('ip_address', models.GenericIPAddressField()), + ('client', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='Clientes.clientes')), + ], + options={ + 'abstract': False, + }, + ), + migrations.AlterModelOptions( + name='sistemas_por_cliente', + options={'ordering': ('-cliente', 'id_sistema')}, + ), + migrations.CreateModel( + name='DeviceHistory', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('first_authentication', models.DateTimeField(auto_now_add=True)), + ('last_authentication', models.DateTimeField(auto_now=True)), + ('ip_address', models.GenericIPAddressField()), + ('device', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='Sistemas.device')), + ], + ), + ] diff --git a/Sistemas/migrations/0003_device_token_device_username.py b/Sistemas/migrations/0003_device_token_device_username.py new file mode 100644 index 0000000..01563d4 --- /dev/null +++ b/Sistemas/migrations/0003_device_token_device_username.py @@ -0,0 +1,26 @@ +# Generated by Django 4.1.3 on 2023-01-23 17:18 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('authtoken', '0003_tokenproxy'), + ('Sistemas', '0002_device_alter_sistemas_por_cliente_options_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='device', + name='token', + field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='authtoken.token'), + ), + migrations.AddField( + model_name='device', + name='username', + field=models.CharField(default=1, max_length=150, unique=True), + preserve_default=False, + ), + ] diff --git a/Sistemas/migrations/0004_alter_device_username.py b/Sistemas/migrations/0004_alter_device_username.py new file mode 100644 index 0000000..a4fb970 --- /dev/null +++ b/Sistemas/migrations/0004_alter_device_username.py @@ -0,0 +1,21 @@ +# Generated by Django 4.1.3 on 2023-01-23 17:22 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('Sistemas', '0003_device_token_device_username'), + ] + + operations = [ + migrations.AlterField( + model_name='device', + name='username', + field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/Sistemas/migrations/0005_remove_device_last_login_remove_device_password.py b/Sistemas/migrations/0005_remove_device_last_login_remove_device_password.py new file mode 100644 index 0000000..fcd01a1 --- /dev/null +++ b/Sistemas/migrations/0005_remove_device_last_login_remove_device_password.py @@ -0,0 +1,21 @@ +# Generated by Django 4.1.3 on 2023-01-23 17:39 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('Sistemas', '0004_alter_device_username'), + ] + + operations = [ + migrations.RemoveField( + model_name='device', + name='last_login', + ), + migrations.RemoveField( + model_name='device', + name='password', + ), + ] diff --git a/Sistemas/migrations/0006_device_sistema.py b/Sistemas/migrations/0006_device_sistema.py new file mode 100644 index 0000000..f34d0bb --- /dev/null +++ b/Sistemas/migrations/0006_device_sistema.py @@ -0,0 +1,19 @@ +# Generated by Django 4.1.3 on 2023-01-25 13:59 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('Sistemas', '0005_remove_device_last_login_remove_device_password'), + ] + + operations = [ + migrations.AddField( + model_name='device', + name='sistema', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='Sistemas.sistemas_por_cliente'), + ), + ] diff --git a/Sistemas/migrations/0007_alter_device_sistema.py b/Sistemas/migrations/0007_alter_device_sistema.py new file mode 100644 index 0000000..76fd7b5 --- /dev/null +++ b/Sistemas/migrations/0007_alter_device_sistema.py @@ -0,0 +1,19 @@ +# Generated by Django 4.1.3 on 2023-01-25 14:33 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('Sistemas', '0006_device_sistema'), + ] + + operations = [ + migrations.AlterField( + model_name='device', + name='sistema', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='Sistemas.sistema'), + ), + ] diff --git a/Sistemas/migrations/0008_device_macaddress.py b/Sistemas/migrations/0008_device_macaddress.py new file mode 100644 index 0000000..c73d42d --- /dev/null +++ b/Sistemas/migrations/0008_device_macaddress.py @@ -0,0 +1,18 @@ +# Generated by Django 4.1.3 on 2023-01-25 18:00 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('Sistemas', '0007_alter_device_sistema'), + ] + + operations = [ + migrations.AddField( + model_name='device', + name='macAddress', + field=models.CharField(blank=True, max_length=30, null=True, unique=True), + ), + ] diff --git a/Sistemas/migrations/__init__.py b/Sistemas/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Sistemas/models.py b/Sistemas/models.py new file mode 100644 index 0000000..aeba84d --- /dev/null +++ b/Sistemas/models.py @@ -0,0 +1,111 @@ +from django.db import models +from django.core.exceptions import ValidationError +from Clientes.models import Clientes +from django.contrib.auth.models import User +from rest_framework.authtoken.models import Token +from django.contrib.auth.models import BaseUserManager + +import re + +class Sistema(models.Model): + nombre_sistema= models.CharField(max_length=100, blank=False,null=False,unique=True) + # class Meta: + # abstract=True + def __str__(self): + return self.nombre_sistema + +class sistemas_por_cliente(models.Model): + id_sistema= models.ForeignKey(Sistema, related_name='sistema_spc', on_delete=models.CASCADE) + cliente = models.ForeignKey(Clientes, related_name='cliente_spc', on_delete=models.CASCADE) + num_licencias= models.IntegerField(default=1) + + def __str__(self): + + return f'{self.cliente.Nombre}' + class Meta: + ordering= ('-cliente','id_sistema') + + +class DeviceManager(BaseUserManager): + def generate_unique_username(self,client, device_name, ip_address): + username = f"Device_{client.RFC}_{device_name}_{ip_address}" + username_ = re.sub(r'\W+', '', username) + if User.objects.filter(username=username_).exists(): + raise ValidationError(f"El Usuario ya existe {username_}") + return username_ + + def create_user(self,client, device_name, device_os, ip_address ): + user_= User.objects.create_user( + username=self.generate_unique_username(client,device_name,ip_address) + ) + device =self.model( + client=client, + device_name= device_name, + device_os=device_os, + ip_address=ip_address, + username=user_ + ) + device.save() + + Token.objects.create(user=user_) + DeviceHistory.objects.create(device=device) + return device + +class Device(models.Model): + client = models.ForeignKey(Clientes,on_delete=models.CASCADE) + device_name = models.CharField(max_length=255) + device_os = models.CharField(max_length=255) + ip_address= models.GenericIPAddressField() + token = models.OneToOneField(Token, on_delete=models.CASCADE, blank=True,null=True) + username = models.OneToOneField(User, on_delete=models.CASCADE) + sistema = models.ForeignKey(Sistema,on_delete=models.CASCADE, blank=True, null=True) + macAddress = models.CharField(max_length=30, unique=True, blank=True,null=True) + #objects = DeviceManager() + def generate_unique_username(self,client, device_name,ip_address, macAddress): + username = f"Device_{client.RFC}_{device_name}_{ip_address}_{macAddress}" + username_ = re.sub(r'\W+', '', username) + + if User.objects.filter(username=username_).exists(): + raise ValidationError(f"El Usuario ya existe {username_}") + obj= User.objects.create_user( + username=username_ + ) + return obj + + def save(self, *args, **kwargs): + if not self.pk: + obj = self.generate_unique_username(self.client,self.device_name, self.ip_address, self.macAddress) + self.username= obj + token= Token.objects.create(user=obj) + self.token=token + super().save(*args, **kwargs) + DeviceHistory.objects.create(device=self,ip_address=self.ip_address) + + def __str__(self): + return f'{self.username}' + +class DeviceHistory(models.Model): + device = models.ForeignKey(Device, on_delete=models.CASCADE) + first_authentication = models.DateTimeField(auto_now_add=True) + last_authentication = models.DateTimeField(auto_now=True) + ip_address = models.GenericIPAddressField() + + + + + + + +class Maquinas_Conectadas(models.Model): + + UserName = models.CharField(max_length=255) + PC_Name = models.CharField(max_length=255) + Is64 = models.BooleanField() + OSversion= models.CharField(max_length=255) + local_ip = models.CharField(max_length=55) + public_ip= models.CharField(max_length=55) + + Token = models.CharField(max_length=50) + Cliente = models.CharField(max_length=13) + class Meta: + abstract =True \ No newline at end of file diff --git a/Sistemas/permissions.py b/Sistemas/permissions.py new file mode 100644 index 0000000..303d579 --- /dev/null +++ b/Sistemas/permissions.py @@ -0,0 +1,14 @@ +from rest_framework.permissions import BasePermission + + +class HasAuthorizationHeader(BasePermission): + def has_permission(self, request, view): + return 'Authorization' in request.headers + +class ItsAdminToken(BasePermission): + def has_permission(self,request,view): + return request.user.is_superuser + +class ItsAdminGroup(BasePermission): + def has_permission(self, request, view): + return request.user.groups.filter(name= 'admin_soft') \ No newline at end of file diff --git a/Sistemas/serializers.py b/Sistemas/serializers.py new file mode 100644 index 0000000..9558c4d --- /dev/null +++ b/Sistemas/serializers.py @@ -0,0 +1,49 @@ + +from rest_framework import serializers +from .models import Device, Sistema, sistemas_por_cliente +from Clientes.models import Clientes + +class SistemaPrimaryKeyRelatedField(serializers.PrimaryKeyRelatedField): + def to_internal_value(self,data): + try: + return Sistema.objects.get(nombre_sistema=data) + except Sistema.DoesNotExist: + raise serializers.ValidationError("Sistema no existe") + +class ClientPrimaryKeyRelatedField(serializers.PrimaryKeyRelatedField): + def to_internal_value(self,data): + try: + return Clientes.objects.get(RFC=data) + except Clientes.DoesNotExist: + raise serializers.ValidationError("No existe Cliente") + +class DeviceSerializer(serializers.ModelSerializer): + client = ClientPrimaryKeyRelatedField(queryset=Clientes.objects.all()) + sistema = SistemaPrimaryKeyRelatedField(queryset=Sistema.objects.all()) + + #this read_only fields not are required to be updated in the serializer. + #however in the model are required in other instantiation like queries, forms, are required to add it + token = serializers.CharField(read_only=True) + macAddress = serializers.CharField(read_only=True) + class Meta: + model =Device + fields = ('client','sistema','device_name','device_os', 'ip_address','token', 'macAddress') + + #this assign the masAddress value from + #the request context passed throught argument in the view to the serializer + #given that the macAddres field are read_Only in the beggining of this serialazer + #we need passing the post data to the creation instance before commited to the DB + def create(self, validated_data): + validated_data['macAddress']= self.context['request'].data.get('macAddress') + return super().create(validated_data) + + def validate(self, data): + sistema = data.get('sistema', None) + client = data.get('client', None) + try: + sistemaxCli = sistemas_por_cliente.objects.get(id_sistema=sistema,cliente=client) + except sistemas_por_cliente.DoesNotExist: + raise serializers.ValidationError('No existe licencia para este sistmea y/o cliente') + if sistemaxCli.num_licencias <= Device.objects.filter(sistema=sistemaxCli.id_sistema).count(): + raise serializers.ValidationError(f"No hay licencias disponibles para este sistema:{sistema} y cliente:{client}") + return data \ No newline at end of file diff --git a/Sistemas/tests.py b/Sistemas/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/Sistemas/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/Sistemas/urls.py b/Sistemas/urls.py new file mode 100644 index 0000000..5bb14d5 --- /dev/null +++ b/Sistemas/urls.py @@ -0,0 +1,22 @@ +from django.urls import path + +from .views import ( + + #CVB + SistemasXCliente_ListView, + SistemasXCliente_DetailView, + UsersConnectedList, + + #DRF APIViews + RegisterDeviceView, + AuthenticateDeviceView, + LogoutView, +) +urlpatterns = [ + path('',SistemasXCliente_ListView.as_view(),name='lista_sistmas'), + path('detail//',SistemasXCliente_DetailView.as_view(),name='detail_sistemas'), + path('registerPC/',RegisterDeviceView.as_view(),name='register_PC'), + path('authenticatePC/',AuthenticateDeviceView.as_view(), name="authenticateDevice"), + path('logout/', LogoutView.as_view(), name='logout_sistemas'), + path('usuariosConectados/', UsersConnectedList.as_view(), name='lista_usuarios'), +] \ No newline at end of file diff --git a/Sistemas/views.py b/Sistemas/views.py new file mode 100644 index 0000000..1b2fa6d --- /dev/null +++ b/Sistemas/views.py @@ -0,0 +1,130 @@ +from django.shortcuts import render +from django.contrib.auth import logout +from django.views.generic.list import ListView +from django.views.generic.detail import DetailView +from rest_framework.authentication import TokenAuthentication +from rest_framework.views import APIView +from rest_framework.response import Response +from rest_framework.permissions import IsAuthenticated +from rest_framework import status, permissions +from django.contrib.auth.models import User +from django.contrib import messages +from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin +from .models import sistemas_por_cliente, DeviceHistory,Device +from .serializers import DeviceSerializer +from .permissions import HasAuthorizationHeader +from django.utils import timezone +import re + +from rest_framework.authtoken.models import Token +from django.utils import timezone +from datetime import timedelta + +from django.contrib.sessions.models import Session + +def get_logged_in_users(): + sessions = Session.objects.filter(expire_date__gte=timezone.now()) + + # Get all non-expired tokens + #tokens = Token.objects.filter(created__gte=timezone.now() - timedelta(hours=1)) + tokens = Token.objects.all() + + # Get a list of logged-in user ids + uid_list = [token.user_id for token in tokens] + + # Get the logged-in users + users = User.objects.filter(id__in=uid_list) + for user,session in zip(users,sessions): + user.session_data = session.get_decoded() + user.session_expire = session.expire_date + return users + +class UsersConnectedList(UserPassesTestMixin,LoginRequiredMixin,ListView): + model = User + template_name= 'Sistemas/Usuarios/lista.html' + + def get_queryset(self): + return get_logged_in_users() + + def test_func(self): + res = self.request.user.groups.filter(name= 'admin_soft') + if not res: + messages.error(self.request, f'Lo sentimos. La página que buscas no está disponible, no cuentas con los permisos.') + return res + +class SistemasXCliente_ListView(UserPassesTestMixin,LoginRequiredMixin, ListView): + model = sistemas_por_cliente + paginate_by = 5 + template_name = 'Sistemas/Xclientes/lista.html' + def test_func(self): + res = self.request.user.groups.filter(name= 'admin_soft') + if not res: + messages.error(self.request, f'Lo sentimos. La página que buscas no está disponible, no cuentas con los permisos.') + return res + +class SistemasXCliente_DetailView(UserPassesTestMixin,LoginRequiredMixin, DetailView): + model = sistemas_por_cliente + template_name= 'Sistemas/Xclientes/detail.html' + def test_func(self): + res = self.request.user.groups.filter(name= 'admin_soft') + if not res: + messages.error(self.request, f'Lo sentimos. La página que buscas no está disponible, no cuentas con los permisos.') + return res + + + + +''' +========================= API Views +''' + +class RegisterDeviceView(APIView): + permissions_classes = (permissions.AllowAny,) + + def post(self,request): + try: + serializer = DeviceSerializer(data=request.data,context={'request':request}) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data, status=status.HTTP_200_OK) + else: + return Response({'Error':f'{serializer.errors}','isError':True}, status=status.HTTP_200_OK) + except Exception as e: + return Response( + {'Error':f'{e}','isError':True} + , status=status.HTTP_200_OK) + + + +class AuthenticateDeviceView(APIView): + authentication_classes= [TokenAuthentication] + permissions_classes=[IsAuthenticated, HasAuthorizationHeader] + + def get(self, request): + try: + obj, created = DeviceHistory.objects.get_or_create( + device=request.user.device, + ip_address=request.META.get('REMOTE_ADDR'), + ) + obj.last_authentication=timezone.now() + obj.save() + device_data=DeviceSerializer(request.user.device).data + if device_data.serializer.is_valid: + return Response(device_data.serializer.data, status=status.HTTP_200_OK) + else: + return Response( + {'Error':f'{device_data.serializer.errors}','isError':True} + , status=status.HTTP_200_OK) + except Exception as e: + return Response( + {'Error':f'{e}','isError':True} + , status=status.HTTP_200_OK) + + +class LogoutView(APIView): + authentication_classes = (TokenAuthentication,) + permission_classes = (IsAuthenticated,HasAuthorizationHeader,) + + def post(self, request): + logout(request) + return Response({'OK':'Dispositivo desautenticado'},status=200) \ No newline at end of file diff --git a/Templates/404.html b/Templates/404.html new file mode 100644 index 0000000..c333307 --- /dev/null +++ b/Templates/404.html @@ -0,0 +1,35 @@ + + + + + + + + + + + AS Admin Pagina No Encontrada + + +
+ {% include 'partials/messages.html' %} + Forbidden + + + + + Regresar al Inicio + + + + +
+ + + + + + + + + diff --git a/Templates/Clientes/Errores_Timbres_list.html b/Templates/Clientes/Errores_Timbres_list.html new file mode 100644 index 0000000..bb37dc4 --- /dev/null +++ b/Templates/Clientes/Errores_Timbres_list.html @@ -0,0 +1,69 @@ +{% extends "base.html" %} + + + +{% block content %} +
+ + + + + + + + + + + {% for errTimbre in object_list %} + + + + + + + {% endfor %} + +
UUIDErrorRCFFolio
{{errTimbre.uuid}}{{errTimbre.description}}{{errTimbre.rfcc}}{{errTimbre.folio}}
+
+ +{% if page_obj.has_other_pages %} + + +{% endif %} + +{% endblock content %} + \ No newline at end of file diff --git a/Templates/Clientes/index.html b/Templates/Clientes/index.html index 195919b..c6d4e40 100644 --- a/Templates/Clientes/index.html +++ b/Templates/Clientes/index.html @@ -18,36 +18,59 @@ Timbres disponibles Comercio Digital: {{saldo}} Nombre -
- Totales Mes - +
+
+ + + + + Filtrado: Mes:{{mes}}, Año:{{year}}, PAC: + + + +
+
- - - +
+
+ + + + + + +
+
Estado @@ -119,7 +142,7 @@ Timbres disponibles Comercio Digital: {{saldo}} -
- - -