Merge pull request #1 from fjrodriguez28/segunda

Segunda
This commit is contained in:
fjrodriguez28
2023-02-01 10:29:00 -06:00
committed by GitHub
41 changed files with 1660 additions and 327 deletions

View File

@@ -9,21 +9,16 @@ import pytz
# Build paths inside the project like this: BASE_DIR / 'subdir'. # Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production # Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.1/howto/deployment/checklist/ # See https://docs.djangoproject.com/en/4.1/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret! # SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '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! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False DEBUG = False
ALLOWED_HOSTS = ['*'] ALLOWED_HOSTS = ['*']
# Application definition # Application definition
INSTALLED_APPS = [ INSTALLED_APPS = [
'django.contrib.admin', 'django.contrib.admin',
'django.contrib.auth', 'django.contrib.auth',
@@ -39,8 +34,9 @@ INSTALLED_APPS = [
'allauth.account', 'allauth.account',
'allauth.socialaccount', 'allauth.socialaccount',
'widget_tweaks', 'widget_tweaks',
'Admin',
'Clientes', 'Clientes',
'Sistemas',
] ]
REST_FRAMEWORK = { REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [ 'DEFAULT_AUTHENTICATION_CLASSES': [
@@ -57,9 +53,7 @@ MIDDLEWARE = [
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
] ]
ROOT_URLCONF = 'Admin.urls' ROOT_URLCONF = 'Admin.urls'
TEMPLATES = [ TEMPLATES = [
{ {
'BACKEND': 'django.template.backends.django.DjangoTemplates', 'BACKEND': 'django.template.backends.django.DjangoTemplates',
@@ -68,11 +62,12 @@ TEMPLATES = [
'OPTIONS': { 'OPTIONS': {
'context_processors': [ 'context_processors': [
'django.template.context_processors.debug', 'django.template.context_processors.debug',
# `allauth` needs this from django
'django.template.context_processors.request', 'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth', 'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages', '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', 'Clientes.saldo_context_proc.get_saldo',
], ],
}, },
@@ -104,15 +99,24 @@ ACCOUNT_LOGOUT_REDIRECT_URL = '/accounts/login/'
ACCOUNT_SIGNUP_REDIRECT_URL =LOGIN_REDIRECT_URL ACCOUNT_SIGNUP_REDIRECT_URL =LOGIN_REDIRECT_URL
ACCOUNT_LOGOUT_ON_PASSWORD_CHANGE = True ACCOUNT_LOGOUT_ON_PASSWORD_CHANGE = True
EMAIL_TIMEOUT = 10 EMAIL_TIMEOUT = 10
#EMAIL_USE_TLS = True
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' 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_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 # Database
@@ -126,7 +130,7 @@ if DEBUG:
'ENGINE': 'django.db.backends.mysql', 'ENGINE': 'django.db.backends.mysql',
'NAME': 'cfdi_as', 'NAME': 'cfdi_as',
'USER': 'root', 'USER': 'root',
'PASSWORD': 'Soluciones28@', 'PASSWORD':os.getenv("BD_PASS"),
'HOST': '127.0.0.1', 'HOST': '127.0.0.1',
'PORT': '', 'PORT': '',
'OPTIONS': {'init_command': "SET sql_mode='STRICT_TRANS_TABLES'"}, 'OPTIONS': {'init_command': "SET sql_mode='STRICT_TRANS_TABLES'"},
@@ -138,7 +142,7 @@ else:
'ENGINE': 'django.db.backends.mysql', 'ENGINE': 'django.db.backends.mysql',
'NAME': 'fjrodriguez$cfdi_as', 'NAME': 'fjrodriguez$cfdi_as',
'USER': 'fjrodriguez', 'USER': 'fjrodriguez',
'PASSWORD': 'Soluciones28@', 'PASSWORD':'Soluciones28@',
'HOST': 'fjrodriguez.mysql.pythonanywhere-services.com', 'HOST': 'fjrodriguez.mysql.pythonanywhere-services.com',
'PORT': '3306', 'PORT': '3306',
'OPTIONS': {'init_command': "SET sql_mode='STRICT_TRANS_TABLES'"}, 'OPTIONS': {'init_command': "SET sql_mode='STRICT_TRANS_TABLES'"},
@@ -170,7 +174,7 @@ AUTH_PASSWORD_VALIDATORS = [
LANGUAGE_CODE = 'es-MX' LANGUAGE_CODE = 'es-MX'
TIME_ZONE = 'CST6CDT' TIME_ZONE = 'America/Chihuahua'
USE_I18N = True USE_I18N = True

View File

@@ -9,12 +9,19 @@ from django.http import HttpResponse
from django.test import SimpleTestCase, override_settings from django.test import SimpleTestCase, override_settings
from rest_framework.authtoken.views import obtain_auth_token from rest_framework.authtoken.views import obtain_auth_token
def response_error_handler(request, exception=None): def response_error_handler(request, exception=None):
context={} context={}
return render(request, '403.html',context,status=403) 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): def permission_denied_view(request):
raise PermissionDenied raise PermissionDenied
@@ -24,23 +31,26 @@ urlpatterns = [
path('accounts/', include('allauth.urls')), path('accounts/', include('allauth.urls')),
path('DRF_Token/', obtain_auth_token, name='DRF_Token'), path('DRF_Token/', obtain_auth_token, name='DRF_Token'),
path('', include('Clientes.urls')), path('', include('Clientes.urls')),
path('sistemas/',include('Sistemas.urls')),
path('403/', permission_denied_view), path('403/', permission_denied_view),
] ]
handler403 = response_error_handler handler403 = response_error_handler
handler404 = response_error_handler_404
if settings.DEBUG: #DEV only if settings.DEBUG: #DEV only
urlpatterns += static(settings.STATIC_URL, document_root= settings.STATIC_ROOT) urlpatterns += static(settings.STATIC_URL, document_root= settings.STATIC_ROOT)
urlpatterns += static(settings.MEDIA_URL, document_root= settings.MEDIA_ROOT) urlpatterns += static(settings.MEDIA_URL, document_root= settings.MEDIA_ROOT)
# ROOT_URLCONF must specify the module that contains handler403 = ... # ROOT_URLCONF must specify the module that contains handler403 = ...
@override_settings(ROOT_URLCONF=__name__) @override_settings(ROOT_URLCONF=__name__)
class CustomErrorHandlerTests(SimpleTestCase): class CustomErrorHandlerTests(SimpleTestCase):
def test_handler_renders_template_response(self): def test_handler_renders_template_response(self):
response = self.client.get('/403/') response = self.client.get('/403/')
# Make assertions on the response here. For example: # Make assertions on the response here. For example:
self.assertContains(response, 'Error handler content', status_code=403) self.assertContains(response, 'Error handler content', status_code=403)

View File

@@ -4,10 +4,12 @@ from .models import Timbres, saldoModel, Clientes,ErroresTimbres
class TimbresAdmin(admin.ModelAdmin): class TimbresAdmin(admin.ModelAdmin):
list_display=['uuid','rfcc','rfcp','fecha','folio','serie','tipo','modo','created_at'] list_display=['uuid','rfcc','rfcp','fecha','folio','serie','tipo','modo','created_at']
list_filter=['modo','tipo']
class ClientesAdmin(admin.ModelAdmin): class ClientesAdmin(admin.ModelAdmin):
list_display= ['RFC','Nombre','Activo','fecha_baja'] list_display= ['RFC','Nombre','Activo','fecha_baja']
list_filter =['RFC','Activo']
admin.site.register(Timbres,TimbresAdmin) admin.site.register(Timbres,TimbresAdmin)
admin.site.register(saldoModel) admin.site.register(saldoModel)

View File

@@ -1,15 +1,16 @@
from functools import wraps from functools import wraps
from django.contrib import messages from django.contrib import messages
from django.shortcuts import redirect 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): def Custom_is_staff_function(user):
if user.is_staff: if user.is_staff:
return True return True
return False return False
def is_staff_access(view_to_return="index"): def is_staff_access(view_to_return="index"):
def decorator(view): def decorator(view):
@wraps(view) @wraps(view)
@@ -20,3 +21,37 @@ def is_staff_access(view_to_return="index"):
return view(request, *args, **kwargs) return view(request, *args, **kwargs)
return _wrapped_view return _wrapped_view
return decorator 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

View File

@@ -40,33 +40,32 @@ class Clientes(models.Model):
fecha_baja = models.DateField(blank=True,null=True) fecha_baja = models.DateField(blank=True,null=True)
email = models.EmailField(max_length=254, blank=True) email = models.EmailField(max_length=254, blank=True)
conteo_mes = models.IntegerField(blank=True,null=True,default=0) conteo_mes = models.IntegerField(blank=True,null=True,default=0)
def timbres_X_MES(self, mes): def timbres_X_MES(self, mes=None, year=None, PAC=None):
today = datetime.date.today() today = datetime.date.today()
if year is None:
year = today.year year = today.year
if mes==None: if mes==None:
mes = today.month mes = today.month
dat = datetime.datetime(int(year),int(mes),1) dat = datetime.datetime(int(year),int(mes),1)
if dat.month in (1,3,5,7,8,10,12):#31 if dat.month in (1,3,5,7,8,10,12):#31
findate = dat + datetime.timedelta(days=30) findate = dat + datetime.timedelta(days=30)
#findate += datetime.timedelta(days=0)
elif dat.month in (4,6,9,11):#30
elif dat.month in (4,6,9,11):#30
findate = dat + datetime.timedelta(days=29) findate = dat + datetime.timedelta(days=29)
#findate += datetime.timedelta(days=0)
else:#28 or 29 else:#28 or 29
findate = dat + datetime.timedelta(days=28) findate = dat + datetime.timedelta(days=28)
findate += datetime.timedelta(days=1) findate += datetime.timedelta(days=1)
#print(f'dat {(dat)} fdate={findate}') 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() cou = Timbres.objects.filter(rfcc=self.RFC, created_at__range=[dat,findate]).count()
self.conteo_mes =cou self.conteo_mes =cou
self.save() self.save()
return cou
@property @property
def timbres_mes_count(self): def timbres_mes_count(self):
today = datetime.date.today() today = datetime.date.today()
month = today.month month = today.month
year = today.year year = today.year
@@ -74,15 +73,6 @@ class Clientes(models.Model):
class Meta: class Meta:
ordering = ('-Activo','-conteo_mes','RFC') ordering = ('-Activo','-conteo_mes','RFC')
def __str__(self):
class Maquinas_Conectadas(models.Model): return self.Nombre
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

View File

@@ -1,5 +1,4 @@
import requests import requests
from asgiref.sync import sync_to_async
from .models import saldoModel from .models import saldoModel
import datetime import datetime

View File

@@ -1,3 +1,56 @@
from django.test import TestCase 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

View File

@@ -4,8 +4,12 @@ from .views import (
timbres_cliente, timbres_cliente,
ClientesUpdateView, ClientesUpdateView,
ClientesCreateView, ClientesCreateView,
ErroresTimbresListView,
export_Excel, export_Excel,
send_timbres_Email, send_timbres_Email,
Retrive_Cliente_Email,
PACS_Retrive_RFCS,
#API DRF #API DRF
saldo_funct2, saldo_funct2,
@@ -20,10 +24,12 @@ urlpatterns = [
path('timbres_cliente/<str:RFC>/', timbres_cliente, name='timbres_cliente'), path('timbres_cliente/<str:RFC>/', timbres_cliente, name='timbres_cliente'),
path('cliente/update/<int:pk>/',ClientesUpdateView.as_view(),name='update_cliente'), path('cliente/update/<int:pk>/',ClientesUpdateView.as_view(),name='update_cliente'),
path('cliente/add/', ClientesCreateView.as_view(), name='add_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('get_timbres_xls/', export_Excel, name='export_Excel'),
path('send_timbres_cliente/',send_timbres_Email, name='send_timbres_Email'), path('send_timbres_cliente/',send_timbres_Email, name='send_timbres_Email'),
path('getActivoRFC/', check_RFC.as_view(), name='check_active_RFC'), path('getActivoRFC/', check_RFC.as_view(), name='check_active_RFC'),
path('get_saldo2/', saldo_funct2.as_view(), name='saldo_funct2'), path('get_saldo2/', saldo_funct2.as_view(), name='saldo_funct2'),
path('check_host/',check_host.as_view(),name='check_host'), 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'),
] ]

View File

@@ -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.shortcuts import render,redirect
from django.contrib import messages from django.contrib import messages
from django.http import HttpResponse from django.http import HttpResponse,JsonResponse
from django.http import JsonResponse
from django.contrib.auth.decorators import login_required 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 .models import Clientes,Timbres,saldoModel,ErroresTimbres
from .serailizers import ClienteSerializer
from .forms import ClienteForm,EmailForm
from rest_framework.views import APIView from rest_framework.views import APIView
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated 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 #EXCEL
from openpyxl import Workbook from openpyxl import Workbook
from openpyxl.styles import Alignment, Border, Font, PatternFill, Side 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.core.mail import EmailMessage
from django.conf import settings from django.conf import settings
from io import BytesIO 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(): def read_env_file():
try: try:
#env_file = os.listdir(settings.BASE_DIR) #env_file = os.listdir(settings.BASE_DIR)
storage = FileSystemStorage(location=settings.BASE_DIR) storage = FileSystemStorage(location=settings.BASE_DIR)
env_file= os.path.join(settings.BASE_DIR,'.env') env_file= os.path.join(settings.BASE_DIR,'.env')
print('EEEEE: ', env_file)
lista =['asds', 'asdasd','sss','com' ,'pol.com'] lista =['asds', 'asdasd','sss','com' ,'pol.com']
if storage.exists(env_file): if storage.exists(env_file):
with open(env_file, 'r') as file: with open(env_file, 'r') as file:
data = file.read() data = file.read()
data =re.findall('"([^"]*)"',data) data =re.findall('"([^"]*)"',data)
print('data',data)
for x in lista: for x in lista:
data.append(x) data.append(x)
res= ' '.join(data) res= ' '.join(data)
final = '"'+res+'"' final = '"'+res+'"'
print('final',final)
with open(env_file, 'w') as newFile: with open(env_file, 'w') as newFile:
newFile.write("export hosts="+final) newFile.write("export hosts="+final)
except: except:
@@ -62,6 +63,7 @@ def read_env_file():
@sync_to_async(thread_sensitive=False) @sync_to_async(thread_sensitive=False)
@login_required @login_required
@is_staff_access()
def send_timbres_Email(request): def send_timbres_Email(request):
req = request.method req = request.method
@@ -71,6 +73,7 @@ def send_timbres_Email(request):
messages.add_message(request, messages.ERROR, f'{form.errors}') messages.add_message(request, messages.ERROR, f'{form.errors}')
return redirect('index') return redirect('index')
today = datetime.date.today() today = datetime.date.today()
year = today.year year = today.year
RFC = request.GET.get('RFC', None) if req=='GET' else form.cleaned_data["RFC"] RFC = request.GET.get('RFC', None) if req=='GET' else form.cleaned_data["RFC"]
@@ -98,7 +101,7 @@ def send_timbres_Email(request):
else: else:
findate = dat+datetime.timedelta(days=28) findate = dat+datetime.timedelta(days=28)
findate +=datetime.timedelta(days=1) findate +=datetime.timedelta(days=1)
print(f'dat{dat} findate:{findate}')
if mes is not None and RFC is not None: 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: else:
@@ -162,28 +165,47 @@ def send_timbres_Email(request):
def index(request): def index(request):
#read_env_file() #read_env_file()
clientes_list = Clientes.objects.all() clientes_list = Clientes.objects.all()
PAC = request.GET.get('PAC',None)
mes = request.GET.get('mes', 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) page = request.GET.get('page', 1)
search = request.GET.get('search',None) search = request.GET.get('search',None)
rfcc = request.GET.get('rfcc', 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('page', '')
filters.pop('datepicker','') filters.pop('datepicker','')
filters.pop('datepickerFin','') filters.pop('datepickerFin','')
print('filters------',filters) filters.pop('mes','')
if rfcc: filters.pop('year','')
if PAC=='00':
filters.pop('PAC')
PAC=None
if rfcc and search:
clientes_list = Clientes.objects.filter(Q(RFC__icontains=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) clientes_list =pageFunc(page,clientes_list,20)
filters.pop('mes','')
context = { context = {
'lista':clientes_list, 'lista':clientes_list,
'mes':mes, 'mes':mes,
'year':year,
'filters':filters, 'filters':filters,
'emailForm':EmailForm(), 'emailForm':EmailForm(),
} }
return render(request,'Clientes/index.html',context) return render(request,'Clientes/index.html',context)
@@ -207,40 +229,68 @@ def timbres_cliente(request, RFC):
else: else:
lista = Timbres.objects.filter(rfcc=RFC,modo='Normal') lista = Timbres.objects.filter(rfcc=RFC,modo='Normal')
PAC= request.GET.get('PAC',None) mes = request.GET.get('mes', None)
if PAC=='01': year = request.GET.get('year',None)
lista = lista.filter(rfcp='EME000602QR9')
if PAC=='02': if year is None or year=='':
lista = lista.exclude(rfcp='EME000602QR9') today= datetime.date.today()
year =today.year
search = request.GET.get('search',None) search = request.GET.get('search',None)
page = request.GET.get('page', 1) page = request.GET.get('page', 1)
datepicker = request.GET.get('datepicker', None) datepicker = request.GET.get('datepicker', None)
datepickerFin = request.GET.get('datepickerFin', 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 = {key:value[0] for (key,value) in dict(request.GET).items() if value !=[""]}
filters.pop('page', '') filters.pop('page', '')
filters.pop('PAC', '') if 'PAC' not in filters:
filters['PAC']='00'
if 'mes' not in filters:
filters['mes']='00'
if tipo and search is not None:
if tipo:
lista = lista.filter(Q(tipo__icontains=search)) lista = lista.filter(Q(tipo__icontains=search))
if datepicker and datepickerFin: if datepicker and datepickerFin:
inicio = [int(i) for i in datepicker.split("/")] # inicio = [int(i) for i in datepicker.split("/")]
fin = [int(i) for i in datepickerFin.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("-")]
#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) start += datetime.timedelta(days=0)
end = datetime.datetime(fin[2],fin[0],fin[1]) # end = datetime.datetime(fin[2],fin[0],fin[1])
end = datetime.datetime(fin[0],fin[1],fin[2])
end += datetime.timedelta(days=1) end += datetime.timedelta(days=1)
#datetime.date.today()
#print('FECHA',datetime.datetime.today(), 'HORA')
print('start',start, 'end',end)
lista = lista.filter(created_at__range=[start, end]) 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() conteo = lista.count()
lista =pageFunc(page,lista,50) lista =pageFunc(page,lista,50)
@@ -256,6 +306,7 @@ def timbres_cliente(request, RFC):
@sync_to_async(thread_sensitive=False) @sync_to_async(thread_sensitive=False)
@login_required @login_required
@is_staff_access()
def export_Excel(request): def export_Excel(request):
RFC = request.GET.get('RFC', None) RFC = request.GET.get('RFC', None)
@@ -265,7 +316,7 @@ def export_Excel(request):
month = today.month month = today.month
year = today.year year = today.year
if mes is None or mes =='None': if mes is None or mes =='None' or mes=='':
mes = month mes = month
dat =datetime.datetime(int(year), int(mes),1) dat =datetime.datetime(int(year), int(mes),1)
@@ -277,7 +328,7 @@ def export_Excel(request):
else: else:
findate = dat+datetime.timedelta(days=28) findate = dat+datetime.timedelta(days=28)
findate +=datetime.timedelta(days=1) findate +=datetime.timedelta(days=1)
print(f'dat{dat} findate:{findate}')
if mes is not None and RFC is not None: 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: else:
@@ -285,8 +336,6 @@ def export_Excel(request):
for i,ii in enumerate(objeto_a_trabajar): for i,ii in enumerate(objeto_a_trabajar):
ii.timbres_X_MES(mes=mes ) ii.timbres_X_MES(mes=mes )
wb = Workbook() wb = Workbook()
ws = wb.active ws = wb.active
@@ -312,28 +361,58 @@ def export_Excel(request):
contenido = "attachment; filename = {0}".format(nombre_archivo) contenido = "attachment; filename = {0}".format(nombre_archivo)
response["Content-Disposition"] = contenido response["Content-Disposition"] = contenido
wb.save( response) wb.save( response)
return 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 #-----------------------------------API VIEWS
#-------------------------------------------- #--------------------------------------------
class check_RFC(APIView): class check_RFC(APIView):
permission_classes = (IsAuthenticated,) permission_classes = (ItsAdminToken,IsAuthenticated,)
def get(self,request): def get(self,request):
rfc = request.GET.get('RFC', None) 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) cliente, created = Clientes.objects.get_or_create(RFC=rfc)
serializer = ClienteSerializer(cliente)
if created: if created:
cliente.Activo=True cliente.Activo=True
cliente.save() cliente.save()
#cliente = get_object_or_404(Clientes, RFC=rfc)
serializer = ClienteSerializer(cliente) if not serializer.is_valid:
return Response({'Error':f'{serializer.errors}','isError':True},status=200)
return Response(serializer.data) return Response(serializer.data)
except Exception as E:
return Response({'Error':f'check_RFC:{E} RFC:{rfc}','isError':True})
class add_timbre2(APIView): class add_timbre2(APIView):
permission_classes = (IsAuthenticated,) permission_classes = (ItsAdminToken,IsAuthenticated,)
def get(self,request): def get(self,request):
uuid= request.GET.get('uuid', None) uuid= request.GET.get('uuid', None)
rfcc= request.GET.get('rfcc', None) rfcc= request.GET.get('rfcc', None)
fecha=request.GET.get('fecha', None) fecha=request.GET.get('fecha', None)
@@ -342,16 +421,8 @@ class add_timbre2(APIView):
tipo=request.GET.get('tipo', None) tipo=request.GET.get('tipo', None)
rfcp=request.GET.get('rfcp', None) rfcp=request.GET.get('rfcp', None)
modo=request.GET.get('modo', None) modo=request.GET.get('modo', None)
obj={'uuid':uuid,'rfcc':rfcc,'fecha':fecha,'folio':folio,
obj={ 'serie':serie,'tipo':tipo,'rfcp':rfcp,'modo':modo
'uuid':uuid,
'rfcc':rfcc,
'fecha':fecha,
'folio':folio,
'serie':serie,
'tipo':tipo,
'rfcp':rfcp,
'modo':modo
} }
try: try:
obj = Timbres.objects.create(**obj) obj = Timbres.objects.create(**obj)
@@ -364,10 +435,10 @@ class add_timbre2(APIView):
folio=folio, folio=folio,
modo=modo modo=modo
) )
return Response({'fok':f'{e}'}) return Response({'Error':f'{e}'})
class saldo_funct2(APIView): class saldo_funct2(APIView):
permission_classes =(IsAuthenticated,) permission_classes =(ItsAdminToken,IsAuthenticated,)
def get(self, request): def get(self, request):
timbres = request.GET.get('num',None) timbres = request.GET.get('num',None)
try: try:
@@ -381,15 +452,19 @@ class saldo_funct2(APIView):
return Response(content) return Response(content)
class check_host(APIView): class check_host(APIView):
permission_classes = (IsAuthenticated,) permission_classes = (ItsAdminToken,IsAuthenticated,)
def post(self,request, format=None): def post(self,request, format=None):
data = request.data data = request.data
print(data)
return Response(data) return Response(data)
#---------------------------CLASS BASED VIEWS #---------------------------CLASS BASED VIEWS
#-------------------------------------------- #--------------------------------------------
class ErroresTimbresListView(LoginRequiredMixin,ListView):
model = ErroresTimbres
paginate_by = 100
template_name = 'Clientes/Errores_Timbres_list.html'
class ClientesUpdateView(UserPassesTestMixin,LoginRequiredMixin,UpdateView): class ClientesUpdateView(UserPassesTestMixin,LoginRequiredMixin,UpdateView):
model= Clientes model= Clientes
form_class=ClienteForm form_class=ClienteForm
@@ -397,13 +472,11 @@ class ClientesUpdateView(UserPassesTestMixin,LoginRequiredMixin,UpdateView):
template_name='Clientes/edit_cliente.html' template_name='Clientes/edit_cliente.html'
def test_func(self): def test_func(self):
res = self.request.user.groups.filter(name= 'admin_soft') res = self.request.user.groups.filter(name= 'admin_soft')
if not res: if not res:
messages.error(self.request, f'Lo sentimos. La página que buscas no está disponible, no cuentas con los permisos.') messages.error(self.request, f'Lo sentimos. La página que buscas no está disponible, no cuentas con los permisos.')
return res return res
class ClientesCreateView(UserPassesTestMixin,LoginRequiredMixin,CreateView): class ClientesCreateView(UserPassesTestMixin,LoginRequiredMixin,CreateView):
model = Clientes model = Clientes
form_class = ClienteForm form_class = ClienteForm

0
Sistemas/__init__.py Normal file
View File

28
Sistemas/admin.py Normal file
View File

@@ -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)

6
Sistemas/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class SistemasConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'Sistemas'

View File

@@ -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')),
],
),
]

View File

@@ -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')),
],
),
]

View File

@@ -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,
),
]

View File

@@ -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),
),
]

View File

@@ -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',
),
]

View File

@@ -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'),
),
]

View File

@@ -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'),
),
]

View File

@@ -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),
),
]

View File

111
Sistemas/models.py Normal file
View File

@@ -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

14
Sistemas/permissions.py Normal file
View File

@@ -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')

49
Sistemas/serializers.py Normal file
View File

@@ -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

3
Sistemas/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

22
Sistemas/urls.py Normal file
View File

@@ -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/<str:pk>/',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'),
]

130
Sistemas/views.py Normal file
View File

@@ -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)

35
Templates/404.html Normal file
View File

@@ -0,0 +1,35 @@
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.2.1/dist/css/bootstrap.min.css" integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS" crossorigin="anonymous">
<title>AS Admin Pagina No Encontrada</title>
</head>
<body >
<div>
{% include 'partials/messages.html' %}
<img class="rounded mx-auto d-block" src="https://static.doofinder.com/main-files/uploads/2019/08/110655-404-not-found-Doofinder.jpg" alt="Forbidden">
<a href="/" class="btn btn-info btn-lg rounded mx-auto d-block mt-3">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-return-left" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M14.5 1.5a.5.5 0 0 1 .5.5v4.8a2.5 2.5 0 0 1-2.5 2.5H2.707l3.347 3.346a.5.5 0 0 1-.708.708l-4.2-4.2a.5.5 0 0 1 0-.708l4-4a.5.5 0 1 1 .708.708L2.707 8.3H12.5A1.5 1.5 0 0 0 14 6.8V2a.5.5 0 0 1 .5-.5z"/>
</svg>
Regresar al Inicio
</a>
</div>
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.14.6/dist/umd/popper.min.js" integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.2.1/dist/js/bootstrap.min.js" integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k" crossorigin="anonymous"></script>
</body>
</html>

View File

@@ -0,0 +1,69 @@
{% extends "base.html" %}
{% block content %}
<div class="table-responsive-sm">
<table class="table">
<thead class="thead-dark">
<tr>
<th scope="col">UUID</th>
<th scope="col">Error</th>
<th scope="col">RCF</th>
<th scope="col">Folio</th>
</tr>
</thead>
<tbody>
{% for errTimbre in object_list %}
<tr class="{% if errTimbre.modo|lower == 'prueba' %}table-danger{% endif %}">
<th scope="row">{{errTimbre.uuid}}</th>
<td>{{errTimbre.description}}</td>
<td>{{errTimbre.rfcc}}</td>
<td>{{errTimbre.folio}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% if page_obj.has_other_pages %}
<nav>
<ul class="pagination justify-content-center">
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}{% for i,v in filters.items %}&{{i}}={{v}}{% endfor%}" aria-label="Previous">
<span aria-hidden="true">&laquo;</span>
</a>
</li>
{% else %}
<li class="page-item disabled">
<span class="page-link" aria-hidden="true">&laquo;</span>
</li>
{% endif %}
{% for p in page_obj.paginator.page_range %}
{% if page_obj.number == p %}
<li class="page-item"><a class="page-link" style="color: red;" href="#">{{ p }}</a></li>
{% else %}
<li class="page-item"><a class="page-link" href="?page={{ p }}{% for i,v in filters.items %}&{{i}}={{v}}{% endfor%}">{{ p }}</a></li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}{% for i,v in filters.items %}&{{i}}={{v}}{% endfor%}" aria-label="Next">
<span aria-hidden="true">&raquo;</span>
</a>
</li>
{% else %}
<li class="page-item disabled">
<span class="page-link" aria-hidden="true">&raquo;</span>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
{% endblock content %}

View File

@@ -19,10 +19,32 @@ Timbres disponibles Comercio Digital: {{saldo}}
<th>Nombre</th> <th>Nombre</th>
<th> <th>
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
Totales Mes <div class="">
<div style="display:none" id="spinner_id" class="spinner-border ml-auto spinner-border-sm" role="status" aria-hidden="true"></div> <a data-toggle="collapse" href="#collapseExample" role="button" aria-expanded="false" aria-controls="collapseExample">
</div> Total x mes
</a>
<small>
<strong>
Filtrado: Mes:{{mes}}, Año:{{year}}, PAC:<label id="lbl_pac"></label>
</strong>
</small>
</div>
<div style="display:none;" id="spinner_id" class="spinner-border ml-auto spinner-border-sm" role="status" aria-hidden="true"></div>
</div>
<div class="collapse" id="collapseExample">
<div class="card card-body">
<!--PACS-->
<select id="table_select_PAC" class="form-control form-control-sm my_event_cls">
<option value="00">Todos</option>
<!--option value="01">EDICOM</option-->
</select>
<!--ANIOS-->
<select style="display:inline" id="table_select_anio" class="form-control form-control-sm">
<!--option value="0"></option-->
</select>
<!--MESES-->
<select id="table_select" class="form-control form-control-sm"> <select id="table_select" class="form-control form-control-sm">
<option value="01">Enero</option> <option value="01">Enero</option>
<option value="02">Febrero</option> <option value="02">Febrero</option>
@@ -47,7 +69,8 @@ Timbres disponibles Comercio Digital: {{saldo}}
<option value="12">Diciembre <option value="12">Diciembre
</option> </option>
</select> </select>
</div>
</div>
</th> </th>
<th>Estado</th> <th>Estado</th>
<th scope="col"> <th scope="col">
@@ -119,7 +142,7 @@ Timbres disponibles Comercio Digital: {{saldo}}
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<!-- Button trigger modal --> <!-- Button trigger modal -->
<button class="btn btn-primary w-100" onclick="addValues('{{obj.RFC}}')" type="button" data-toggle="modal" data-target="#exampleModalCenter"> <button class="btn btn-primary w-100" onclick="addValues('{{obj.RFC}}'); retriveEmails('{{obj.RFC}}');" type="button" data-toggle="modal" data-target="#exampleModalCenter">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-envelope-plus" viewBox="0 0 16 16"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-envelope-plus" viewBox="0 0 16 16">
<path d="M2 2a2 2 0 0 0-2 2v8.01A2 2 0 0 0 2 14h5.5a.5.5 0 0 0 0-1H2a1 1 0 0 1-.966-.741l5.64-3.471L8 9.583l7-4.2V8.5a.5.5 0 0 0 1 0V4a2 2 0 0 0-2-2H2Zm3.708 6.208L1 11.105V5.383l4.708 2.825ZM1 4.217V4a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v.217l-7 4.2-7-4.2Z"/> <path d="M2 2a2 2 0 0 0-2 2v8.01A2 2 0 0 0 2 14h5.5a.5.5 0 0 0 0-1H2a1 1 0 0 1-.966-.741l5.64-3.471L8 9.583l7-4.2V8.5a.5.5 0 0 0 1 0V4a2 2 0 0 0-2-2H2Zm3.708 6.208L1 11.105V5.383l4.708 2.825ZM1 4.217V4a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v.217l-7 4.2-7-4.2Z"/>
<path d="M16 12.5a3.5 3.5 0 1 1-7 0 3.5 3.5 0 0 1 7 0Zm-3.5-2a.5.5 0 0 0-.5.5v1h-1a.5.5 0 0 0 0 1h1v1a.5.5 0 0 0 1 0v-1h1a.5.5 0 0 0 0-1h-1v-1a.5.5 0 0 0-.5-.5Z"/> <path d="M16 12.5a3.5 3.5 0 1 1-7 0 3.5 3.5 0 0 1 7 0Zm-3.5-2a.5.5 0 0 0-.5.5v1h-1a.5.5 0 0 0 0 1h1v1a.5.5 0 0 0 1 0v-1h1a.5.5 0 0 0 0-1h-1v-1a.5.5 0 0 0-.5-.5Z"/>
@@ -136,11 +159,11 @@ Timbres disponibles Comercio Digital: {{saldo}}
</tbody> </tbody>
</table> </table>
</div> </div>
<div id='id_filters' style="display: none;">{% for i,v in filters.items %}&{{i}}={{v}}{% endfor%}</div>
<!-- Modal --> <div id='id_filters' style="display: none;">{% for i,v in filters.items %}&{{i}}={{v}}{% endfor%}</div>
<div class="modal fade" data-backdrop="static" id="exampleModalCenter" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true"> <!-- Modal EMAIL-->
<form enctype="multipart/form-data" id="id_form" action="{% url 'send_timbres_Email' %}" method="post"> <div class="modal fade" data-backdrop="static" id="exampleModalCenter" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
<form enctype="multipart/form-data" id="id_form" action="{% url 'send_timbres_Email' %}?mes={{mes}}{% for i,v in filters.items %}&{{i}}={{v}}{% endfor%}" method="post">
{% csrf_token %} {% csrf_token %}
<div class="modal-dialog modal-dialog-centered" role="document"> <div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content"> <div class="modal-content">
@@ -152,22 +175,32 @@ Timbres disponibles Comercio Digital: {{saldo}}
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div class="form-group"> <div class="form-group">
<label for="email"><strong> Email </strong></label> <label for="email"><strong>Email</strong></label>
<input type="email" name="email" id="email_add" autocomplete="off" class="form-control" placeholder="Email"> <!--input type="email" name="email" id="email_add" autocomplete="off" class="form-control" placeholder="Email"-->
<div class="input-group mb-3">
<input type="email" name="email" id="email_add" class="form-control" placeholder="Email" aria-label="Email" aria-describedby="button-addon2">
<div class="input-group-append">
<button class="btn btn-outline-secondary" type="button" data-toggle="modal" data-target="#exampleModalCenter2" id="button-addon2" >
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-search" viewBox="0 0 16 16">
<path d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001c.03.04.062.078.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1.007 1.007 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z"/>
</svg>
</button>
</div>
</div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="subject"><strong> Subject </strong></label> <label for="subject"><strong> Subject </strong></label>
<input type="text" name="subject" maxlength="100" id="subject_add" autocomplete="off" class="form-control" placeholder="Subject"> <input type="text" name="subject" maxlength="100" id="subject_add" autocomplete="off" class="form-control" placeholder="Subject">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="adjunto"><strong> Adjunto</strong></label> <label for="adjunto"><strong>Adjunto</strong></label>
<!--input type="file" name="adjunto" multiple="" id="adjunto_add" autocomplete="off" class="form-control" placeholder="Adjunto"--> <!--input type="file" name="adjunto" multiple="" id="adjunto_add" autocomplete="off" class="form-control" placeholder="Adjunto"-->
{{emailForm.adjunto}} {{emailForm.adjunto}}
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="message"><strong> Message </strong></label> <label for="message"><strong>Message</strong></label>
<textarea name="message" cols="40" rows="10" id="message_add" autocomplete="off" class="form-control" placeholder="Message" ></textarea> <textarea name="message" cols="40" rows="10" id="message_add" autocomplete="off" class="form-control" placeholder="Message" required></textarea>
</div> </div>
<div hidden class="form-group"> <div hidden class="form-group">
<label for="RFC"><strong> Rfc </strong></label> <label for="RFC"><strong> Rfc </strong></label>
@@ -189,13 +222,134 @@ Timbres disponibles Comercio Digital: {{saldo}}
</div> </div>
</div> </div>
</form> </form>
</div>
<!-- Modal LISTA EMAILS-->
<div class="modal fade bd-example-modal-lg" id="exampleModalCenter2" tabindex="-1" role="contentinfo" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalCenterTitle">Lista email</h5>
<button id="close_emails_list" type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div> </div>
<div class="modal-body">
<div class="table-responsive">
<table id="email_list" class="table table-hover">
<thead>
<tr>
<th scope="col">EMAIL</th>
</tr>
</thead>
<tbody id="table_body" style="cursor: pointer;">
<!--This is a exaple append js rows structure---->
<!--tr class="table_row"><td id="1">@Mark</td></tr-->
</tbody>
</table>
</div>
</div>
<div class="modal-footer">
<h1>Footer</h1>
</div>
</div>
</div>
</div>
{% endblock content %} {% endblock content %}
{% block scripts %} {% block scripts %}
<script> <script>
function retriveEmails(RFC){
fetch(`{% url 'Retrive_Cliente_Email' %}?RFC=${RFC}`,{
method: 'GET',
headers: {
'Content-Type': 'application/json',
'X-Requested-With':'XMLHttpRequest',
},
credentials:"same-origin"
})
.then(res=>{
return res.json()
})
.then(data=>{
if(table_body.querySelectorAll('tr').length >0){
const table_rows = document.querySelectorAll('.table_row')
table_rows.forEach((rem)=>{
rem.remove()
})
}
const re = Object.values(data['data'])
re.forEach((val,index)=>{
const row = document.createElement("tr")
row.setAttribute("class","table_row")
const cell = document.createElement("td")
cell.setAttribute("id",index)
const cellText = document.createTextNode(`${val.email}`)
cell.appendChild(cellText)
row.appendChild(cell)
table_body.appendChild(row)
})
email_list.appendChild(table_body)
const table_rows = document.querySelectorAll('.table_row')
table_rows.forEach((box)=>{
box.addEventListener('click',(event)=>{
email_add.value=event.target.textContent
close_emails_list.click()
})
})
})//.then(data)
}
let filters = document.getElementById('id_filters').textContent let filters = document.getElementById('id_filters').textContent
console.log(filters) let mes ='{{mes}}'
let year = parseInt('{{year}}')
let anc = document.getElementById('home_id')
let PAC = Object.values(filters.split('&'))
PAC.filter(val=>{
lbl_pac.innerHTML="Todos"
if(val.length >0){
lbl_pac.innerHTML= val.split('=')[1]=="" ? "Todos" :val.split('=')[1]
}
})
/*-------------------------Carga los pacs*/
fetch("{% url 'PACS_Retrive_RFCS' %}",{
method:'GET',
headers:{
'Content-Type':'application/json',
'X-Requested-With':'XMLHttpRequest',
},
credentials:"same-origin"
})
.then(res=>{
return res.json()
})
.then(data=>{
let pacs = [...new Set(data['PACS'].map((arr)=>arr.rfcp) )]
pacs.forEach((val,index)=>{
let option = document.createElement('option')
option.value=val
option.text=val
table_select_PAC.add(option)
PAC.filter(val=>val.includes('PAC'))
.forEach((val,index)=>{
table_select_PAC.value = val.split('=')[1]
})
})
})
.catch(error=>{
console.log(error)
})
/*----------------------fin carga pacs*/
function aclick(event,RFC ,cuantos){ function aclick(event,RFC ,cuantos){
if( parseInt(cuantos)===0){ if( parseInt(cuantos)===0){
@@ -211,24 +365,72 @@ Timbres disponibles Comercio Digital: {{saldo}}
}) })
window.addEventListener("load", (event)=>{ window.addEventListener("load", (event)=>{
let mes ='{{mes}}' // let mes ='{{mes}}'
mes.length == 1 ? mes="0"+mes: mes
if(mes !="None"){ if(mes !="None"){
document.getElementById('table_select').value='{{mes}}' document.getElementById('table_select').value=mes
mes_id.value='{{mes}}' mes_id.value=mes
}else{ }else{
document.getElementById('table_select').value='{{fecha|date:"m"}}' document.getElementById('table_select').value='{{fecha|date:"m"}}'
mes_id.value='{{fecha|date:"m"}}' mes_id.value='{{fecha|date:"m"}}'
} }
/*Add years to select anio tag*/
let anio = parseInt('{{fecha|date:"Y"}}')
let fin = anio-5
for(var i = anio; i >= fin ;i--){
let option = document.createElement('option')
option.value=i
option.text=i
table_select_anio.add(option)
}
/*despues de agregar los anios al control agrega el value del contexto*/
table_select_anio.value=year
}) })
document.getElementById('table_select').addEventListener('change',(event)=>{ document.getElementById('table_select').addEventListener('change',(event)=>{
spinner_id.setAttribute('style','display:inline;') spinner_id.setAttribute('style','display:inline;')
let anc = document.getElementById('home_id')
anc.href='' anc.href=''
let url = `?mes=${event.target.value}${filters}`
let url = `?mes=${event.target.value}&year=${year}${filters}`
setTimeout(()=>{
anc.href=url anc.href=url
anc.click() anc.click()
},1000)
})
table_select_anio.addEventListener('change',(event)=>{
spinner_id.setAttribute('style','display:inline;')
anc.href=''
let url = `?mes=${mes}&year=${event.target.value}${filters}`
setTimeout(()=>{
anc.href=url
anc.click()
},1000)
})
table_select_PAC.addEventListener('change',(event)=>{
spinner_id.setAttribute('style','display:inline;');
anc.href='';
let url =`?mes=${mes}&year=${year}&PAC=${event.target.value}`
setTimeout(()=>{
anc.href=url
anc.click()
},1000)
}) })
enviar_btn.addEventListener('click',(e)=>{ enviar_btn.addEventListener('click',(e)=>{
@@ -236,12 +438,7 @@ Timbres disponibles Comercio Digital: {{saldo}}
if(id_form.checkValidity()){ if(id_form.checkValidity()){
// //
spinner_enviar.removeAttribute('style'); spinner_enviar.removeAttribute('style');
/*
id_form.getElementsByTagName('*');
for (var node of childNodes) {
node.disabled = true;
}
*/
} }
}) })
@@ -253,7 +450,7 @@ Timbres disponibles Comercio Digital: {{saldo}}
fsize = id_adjunto.files.item(i).size fsize = id_adjunto.files.item(i).size
fsize +=fsize; fsize +=fsize;
console.log(((fsize/1024)/1024))
if( 25 <= ((fsize/1024)/1024) ){ if( 25 <= ((fsize/1024)/1024) ){
alert('Limite excedido de 25 MegaBytes adjuntos'); alert('Limite excedido de 25 MegaBytes adjuntos');
id_adjunto.value='' id_adjunto.value=''
@@ -270,10 +467,10 @@ Timbres disponibles Comercio Digital: {{saldo}}
}) })
function addValues(RFC){ function addValues(RFC){
RFC_add.value = RFC RFC_add.value = RFC
console.log('RFC', RFC)
} }
</script> </script>
{% endblock scripts %} {% endblock scripts %}

View File

@@ -1,20 +1,18 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block content %} {% block content %}
<h1>Timbres <strong>{{RFC}}</strong> </h1> <h1>Timbres <strong>{{RFC}}</strong> </h1>
<h2>Timbres totales: <strong></strong>{{conteo}}</strong></h2> <h2>Timbres totales: <strong></strong>{{conteo}}</strong></h2>
<span class="navbar-text mr-2">
{% if filters.datepicker%}
De fecha: <strong>"{{filters.datepicker}}"</strong>
{% endif %}
{% if filters.datepickerFin %} <span class="justify-content-sm-end navbar-text mr-2">
{% if filters.datepicker%}
De fecha: <strong>"{{filters.datepicker}}"</strong>
{% endif %}
{% if filters.datepickerFin %}
A fecha: <strong>"{{filters.datepickerFin}}"</strong> A fecha: <strong>"{{filters.datepickerFin}}"</strong>
{% endif %} {% endif %}
</span> </span>
<br><br> <br><br>
<table class="table"> <table class="table">
@@ -22,23 +20,40 @@
<tr> <tr>
<th></th> <th></th>
<th scope="col"> UUID</th> <th scope="col"> UUID</th>
<th scope="col"> <th scope="col">
PAC PAC
<select id="table_select_PAC" class="form-control form-control-sm"> <select id="table_select_PAC" class="form-control form-control-sm my_event_cls">
<option value="00">Todos</option> <option value="00">Todos</option>
<option value="01">EDICOM</option> <!--option value="01">EDICOM</option-->
<option value="02">Comercio Dig.</option>
</select> </select>
</th> </th>
<th scope="col"> <th scope="col">
<input id="table_tipo" name="tipo" value="True" type="checkbox" class="form-check-input" > <input id="table_tipo" name="tipo" value="True" type="checkbox" class="form-check-input my_event_cls" >
Tipo CFDI Tipo CFDI
</th> </th>
<th scope="col">Serie/Folio</th> <th scope="col">Serie/Folio</th>
<th scope="col"> <th scope="col">
<input id="table_fecha" name="fecha" value="True" type="checkbox" class="form-check-input" > <input id="table_fecha" name="fecha" value="True" type="checkbox" class="form-check-input my_event_cls" >
Fecha Fecha
<select id="table_select_anio" class="form-control form-control-sm my_event_cls">
<!--option value="2023"></option-->
</select>
<select id="table_select_Meses" class="form-control form-control-sm my_event_cls">
<option value="00">Todos</option>
<option value="01">Enero</option>
<option value="02">Febrero</option>
<option value="03">Marzo</option>
<option value="04">Abril</option>
<option value="05">Mayo</option>
<option value="06">Junio</option>
<option value="07">Julio</option>
<option value="08">Agosto</option>
<option value="09">Septiembre</option>
<option value="10">Octubre</option>
<option value="11">Noviembre</option>
<option value="12">Diciembre</option>
</select>
</th> </th>
</tr> </tr>
</thead> </thead>
@@ -56,26 +71,151 @@
</tbody> </tbody>
</table> </table>
<div id='id_filters' style="display: None;">{% for i,v in filters.items %}&{{i}}={{v}}{% endfor%}</div> <div id='id_filters' style="display: none;">{% for i,v in filters.items %}&{{i}}={{v}}{% endfor%}</div>
{% endblock content %} {% endblock content %}
{% block scripts %} {% block scripts %}
<script> <script>
window.addEventListener("load", (event)=>{ function get_pacs(){
let PAC = '{{PAC}}'
if(PAC !="None"){ let rfc = '{{request.path}}'
table_select_PAC.value=PAC rfc= Object.values(rfc.split('/'))
url = `{% url 'PACS_Retrive_RFCS' %}?RFC=${rfc[2]}`
fetch(url, {
method: 'GET',
headers:{
'Content-Type':'application/json',
'X-Requested-With':'XMLHttpRequest',
},
credentials:"same-origin"
})
.then(res=>{
return res.json()
})
.then(data=>{
let pacs = [...new Set( data['PACS'].map((arr)=> arr.rfcp) )]
pacs.forEach((val,index)=>{
let option = document.createElement('option')
option.value=val
option.text=val
table_select_PAC.add(option)
PAC.filter(val=>val.includes('PAC'))
.forEach((val,index)=>{
table_select_PAC.value = val.split('=')[1]
})
})
})
}
</script>
<!--end functions-->
<script>
let filters = document.getElementById('id_filters').textContent
let mes = Object.values(filters.split('&'))
let year = Object.values(filters.split('&'))
let PAC = Object.values(filters.split('&'))
let tipo = Object.values(filters.split('&'))
let lsearch = Object.values(filters.split('&'))
let ldatepicker = Object.values(filters.split('&'))
let ldatepickerFin = Object.values(filters.split('&'))
let anc = document.getElementById('home_id')
let ref=''
/*Add years to select anio tag*/
let anio = parseInt('{{fecha|date:"Y"}}')
let fin = anio-5
console.log(anio)
for(var i = anio; i >= fin ;i--){
let option = document.createElement('option')
option.value=i
option.text=i
table_select_anio.add(option)
}
year.filter(val=> val.length>0)
.filter(val=>val.includes('year'))
.forEach((val,index)=>{
table_select_anio.value=val.split('=')[1]
})
mes.filter(val=> val.length >0)
.filter(val=>val.includes('mes'))
.forEach((val,index)=>{
table_select_Meses.value= val.split('=')[1]
})
tipo.filter(val=>val.includes('tipo'))
.forEach((val,index)=>{
table_tipo.checked= val.split('=')[1] ==='on' ?true:false
})
lsearch.filter(val=>val.includes('search'))
.forEach((val,index)=>{
search.value = val.split('=')[1]
})
ldatepicker.filter(val=>val.includes('datepicker'))
.forEach((val,index)=>{
datepicker.value=val.split('=')[1]
})
ldatepickerFin.filter(val=>val.includes('datepicker'))
.forEach((val,index)=>{
datepickerFin.value=val.split('=')[1]
})
window.addEventListener('load',event=>{
get_pacs()
})
document.querySelectorAll('.my_event_cls').forEach(item=>{
if(item.id==='datepicker' || item.id==='datepickerFin'){
item.addEventListener('focusout',event=>{
ref=`?PAC=${table_select_PAC.value}`
ref+=`&mes=${table_select_Meses.value}`
ref+=`&year=${table_select_anio.value}`
ref+=`&tipo=${table_tipo.checked}`
ref+=`&search=${search.value}`
ref+=`&datepicker=${datepicker.value}`
ref+=`&datepickerFin=${datepickerFin.value}`
})
}else{
item.addEventListener('change', event=>{
ref=`?PAC=${table_select_PAC.value}`
ref+=`&mes=${table_select_Meses.value}`
ref+=`&year=${table_select_anio.value}`
ref+=`&tipo=${table_tipo.checked}`
ref+=`&search=${search.value}`
ref+=`&datepicker=${datepicker.value}`
ref+=`&datepickerFin=${datepickerFin.value}`
})
} }
}) })
let filters = document.getElementById('id_filters').textContent
table_select_PAC.addEventListener('change',(event)=>{ table_select_PAC.addEventListener('change',(event)=>{
let anc = document.getElementById('home_id')
anc.href='' anc.href=''
let url = `?PAC=${event.target.value}${filters}` anc.href=ref
anc.href=url anc.click()
})
table_select_Meses.addEventListener('change',(event)=>{
anc.href=''
anc.href=ref
anc.click()
})
table_select_anio.addEventListener('change',(event)=>{
anc.href=''
anc.href=ref
anc.click() anc.click()
}) })
@@ -87,8 +227,6 @@
}) })
table_fecha.addEventListener('click', (event)=>{ table_fecha.addEventListener('click', (event)=>{
if(dates.hasAttribute("style")){ if(dates.hasAttribute("style")){
dates.removeAttribute('style') dates.removeAttribute('style')
datepicker.value='' datepicker.value=''

View File

@@ -0,0 +1,33 @@
{% extends 'base.html' %}
{% block title %}
| Sistemas
{% endblock title %}
{% block content %}
<table class="table">
<thead class="thead-dark">
<tr>
<th scope="col">Usuario</th>
<th scope="col">Expira</th>
<th scope="col"></th>
<th scope="col"></th>
<th></th>
</tr>
</thead>
<tbody>
{% for user in object_list %}
<tr>
<th scope="row">{{user.username}}</th>
<td>Session expire at: {{ user.session_expire }}</td>
<td>-</td>
<td><a href="#" class="btn btn-info">Detalles</a></td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock content %}

View File

@@ -0,0 +1,89 @@
{% extends 'base.html' %}
{% block title %}
{{object}} | Sistemas
{% endblock title %}
{% block content %}
<style>
@import url('https://fonts.googleapis.com/css?family=Assistant');
body {
background: #eee;
font-family: Assistant, sans-serif
}
.cell-1 {
border-collapse: separate;
border-spacing: 0 4em;
background: #ffffff;
border-bottom: 5px solid transparent;
/*background-color: gold;*/
background-clip: padding-box;
cursor: pointer;
}
thead {
background: #dddcdc;
}
.table-elipse {
cursor: pointer;
}
#demo {
-webkit-transition: all 0.3s ease-in-out;
-moz-transition: all 0.3s ease-in-out;
-o-transition: all 0.3s 0.1s ease-in-out;
transition: all 0.3s ease-in-out;
}
.row-child {
background-color: rgb(113, 178, 238);
color: rgb(0, 0, 0);
}
</style>
<h1>{{object.cliente}}</h1>
<div class="table-responsive table-borderless">
<table class="table">
<thead>
<tr>
<th class="text-center">Num. Licencias contratadas</th>
<th>Sistema</th>
<th>-</th>
<th>Estatus</th>
<th>-</th>
<th>-</th>
<th></th>
</tr>
</thead>
<tbody class="table-body">
<tr class="cell-1" data-toggle="collapse" data-target="#demo">
<td class="text-center">{{object.num_licencias}}</td>
<td>{{object.id_sistema}}</td>
<td>-</td>
<td><span class="badge badge-{% if object.cliente.Activo %}success{%else%}danger{% endif %}">{% if object.cliente.Activo %}Activo{%else%}Inactivo{% endif %}</span></td>
<td>-</td>
<td>click me</td>
<td class="table-elipse" data-toggle="collapse" data-target="#demo"><i class="fa fa-ellipsis-h text-black-50"></i></td>
</tr>
{% for sistema in object.id_sistema.device_set.all %}
<tr id="demo" class="collapse cell-1 row-child">
<td class="text-center" colspan="1"><i class="fa fa-angle-up"></i></td>
<td colspan="1">Dispositivo: <strong>{{sistema}}</strong> </td>
<td colspan="3"></td>
<td colspan="1">-</td>
<td colspan="2">-</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock content %}

View File

@@ -0,0 +1,36 @@
{% extends "base.html" %}
{% block title %}
Sistemas Lista
{% endblock title %}
{% block content %}
<table class="table">
<thead class="thead-dark">
<tr>
<th scope="col">#</th>
<th scope="col">Sistema</th>
<th scope="col">Cliente</th>
<th scope="col">No. Licencias</th>
<th></th>
</tr>
</thead>
<tbody>
{% for row in object_list %}
<tr>
<th scope="row">{{row.id}}</th>
<td>{{row.id_sistema}}</td>
<td>{{row.cliente}}</td>
<td>{{row.num_licencias}}</td>
<td><a href="{% url 'detail_sistemas' row.id %}" class="btn btn-info">Detalles</a></td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock content %}

View File

@@ -43,8 +43,14 @@
</div> </div>
</form> </form>
</div> </div>
{% comment %} {% comment %}
<div class="row">
<p>No recuerdas tu contrasena? <a href="/accounts/password/reset/">Reset password</a></p>
</div>
<div class="row">
<p>No recuerdas tu contrasena? <a href="/accounts/password/reset/">Reset password</a></p>
</div>
<div class="row"> <div class="row">
<p>Don't have an account? <a href="{{ signup_url }}">Register Here</a></p> <p>Don't have an account? <a href="{{ signup_url }}">Register Here</a></p>
</div> </div>

View File

@@ -40,6 +40,7 @@
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script> <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.14.6/dist/umd/popper.min.js" integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/popper.js@1.14.6/dist/umd/popper.min.js" integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.2.1/dist/js/bootstrap.min.js" integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.2.1/dist/js/bootstrap.min.js" integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"></script>
<script> <script>

View File

@@ -37,4 +37,3 @@
</ul> </ul>
</nav> </nav>
{% endif %} {% endif %}
</div>

View File

@@ -2,29 +2,22 @@
<span class="navbar-text mr-2"> <span class="navbar-text mr-2">
Fecha: <strong>{{fecha|date:"d F Y"}}</strong> Fecha: <strong>{{fecha|date:"d F Y"}}</strong>
</span> </span>
<form action="{{request.path}}" method="get" class="form-inline my-2 my-lg-0"> <form id="searchForm" action="{{request.path}}" method="get" class="form-inline my-2 my-lg-0 my_event_cls">
<input style="display:none" id="mes_id" class="form-control mr-sm-2" name="mes" type="input" aria-label="mes">
<input id="search" class="form-control mr-sm-2" name="search" type="search" placeholder="Search" aria-label="Search"> <input style="display:none" id="mes_id" class="form-control mr-sm-2 my_event_cls" name="mes" type="input" aria-label="mes">
<input id="search" class="form-control mr-sm-2 my_event_cls" name="search" type="search" placeholder="Search" aria-label="Search">
<div class="form-group form-check"> <div class="form-group form-check">
<input style="display:none" name="rfcc" type="checkbox" class="form-check-input" id="rfcc"> <input style="display:none" name="rfcc" type="checkbox" class="form-check-input my_event_cls" id="rfcc">
</div> </div>
<div class="form-group form-check"> <div class="form-group form-check">
<input style="display:none" name="tipo" type="checkbox" class="form-check-input" id="tipo"> <input style="display:none" name="tipo" type="checkbox" class="form-check-input my_event_cls" id="tipo">
</div> </div>
<div id="dates" style="display:none;" class="group-form mr-2" > <div id="dates" style="display:none;" class="group-form mr-2" >
<input class="form-control sm-2" name ="datepicker" id="datepicker" placeholder="Initial Date" /> <input type="date" class="form-control sm-2 my_event_cls" name ="datepicker" id="datepicker" placeholder="Initial Date" />
<input class="form-control sm-2" name ="datepickerFin" id="datepickerFin" placeholder="End Date" /> <input type="date" class="form-control sm-2 my_event_cls" name ="datepickerFin" id="datepickerFin" placeholder="End Date" />
<script>
$('#datepicker').datepicker({
uiLibrary: 'bootstrap4'
});
$('#datepickerFin').datepicker({
uiLibrary: 'bootstrap4'
});
</script>
</div> </div>
<button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button> <button id="id_searchbtn" class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
</form> </form>

View File

@@ -39,8 +39,11 @@
<li class="nav-item"> <li class="nav-item">
<a class="nav-link disabled" href="#">Disabled</a> <a class="nav-link disabled" href="#">Disabled</a>
</li> </li>
{% endcomment %}
{% endcomment %}
<li class="nav-item">
<a class="nav-link" href="/sistemas" target="_blank">Sistemas</a>
</li>
{% if request.user.is_superuser %} {% if request.user.is_superuser %}
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/admin" target="_blank">Admin site</a> <a class="nav-link" href="/admin" target="_blank">Admin site</a>
@@ -61,14 +64,13 @@
</form> </form>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<a class="dropdown-item" href="#">Another action</a> {% if request.user.is_superuser %}
<a class="dropdown-item" href="{% url 'ErroresTimbresList' %}">Lista Errores</a>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
{% endif %}
<a class="dropdown-item" href="#">Something else here</a> <a class="dropdown-item" href="#">Something else here</a>
</div> </div>
</li> </li>
</div> </div>
</nav> </nav>