From 331de4f08d825794ee90813327c36bd5693fe9ea Mon Sep 17 00:00:00 2001 From: FranciscoBorja12 Date: Thu, 6 Feb 2025 21:03:46 -0600 Subject: [PATCH] first --- .gitignore | 1 + manage.py | 22 ++ registro/__init__.py | 0 registro/__pycache__/__init__.cpython-313.pyc | Bin 0 -> 159 bytes registro/__pycache__/settings.cpython-313.pyc | Bin 0 -> 3516 bytes registro/__pycache__/urls.cpython-313.pyc | Bin 0 -> 1927 bytes registro/__pycache__/wsgi.cpython-313.pyc | Bin 0 -> 649 bytes registro/asgi.py | 16 ++ registro/settings.py | 154 +++++++++++++ registro/urls.py | 33 +++ registro/wsgi.py | 16 ++ tasks/__init__.py | 0 tasks/__pycache__/__init__.cpython-313.pyc | Bin 0 -> 156 bytes tasks/__pycache__/admin.cpython-313.pyc | Bin 0 -> 200 bytes tasks/__pycache__/apps.cpython-313.pyc | Bin 0 -> 518 bytes tasks/__pycache__/forms.cpython-313.pyc | Bin 0 -> 2628 bytes tasks/__pycache__/models.cpython-313.pyc | Bin 0 -> 2861 bytes tasks/__pycache__/views.cpython-313.pyc | Bin 0 -> 10264 bytes tasks/admin.py | 3 + tasks/apps.py | 6 + tasks/forms.py | 36 +++ tasks/migrations/0001_initial.py | 46 ++++ ...as_alter_cuenta_fecha_creacion_and_more.py | 31 +++ .../0003_alter_movimientos_cuentas.py | 19 ++ ..._factura_movimientos_n_factura_and_more.py | 34 +++ tasks/migrations/__init__.py | 0 .../__pycache__/0001_initial.cpython-313.pyc | Bin 0 -> 2605 bytes ...ta_fecha_creacion_and_more.cpython-313.pyc | Bin 0 -> 1560 bytes ..._alter_movimientos_cuentas.cpython-313.pyc | Bin 0 -> 1035 bytes ...mientos_n_factura_and_more.cpython-313.pyc | Bin 0 -> 1303 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 167 bytes tasks/models.py | 33 +++ tasks/templates/base.html | 77 +++++++ tasks/templates/cuentas.html | 21 ++ tasks/templates/index.html | 80 +++++++ tasks/templates/lista_cuentas.html | 54 +++++ tasks/templates/lista_movimientos.html | 49 ++++ tasks/templates/movimiento.html | 49 ++++ tasks/templates/singup.html | 108 +++++++++ tasks/tests.py | 3 + tasks/views.py | 215 ++++++++++++++++++ 41 files changed, 1106 insertions(+) create mode 100644 .gitignore create mode 100644 manage.py create mode 100644 registro/__init__.py create mode 100644 registro/__pycache__/__init__.cpython-313.pyc create mode 100644 registro/__pycache__/settings.cpython-313.pyc create mode 100644 registro/__pycache__/urls.cpython-313.pyc create mode 100644 registro/__pycache__/wsgi.cpython-313.pyc create mode 100644 registro/asgi.py create mode 100644 registro/settings.py create mode 100644 registro/urls.py create mode 100644 registro/wsgi.py create mode 100644 tasks/__init__.py create mode 100644 tasks/__pycache__/__init__.cpython-313.pyc create mode 100644 tasks/__pycache__/admin.cpython-313.pyc create mode 100644 tasks/__pycache__/apps.cpython-313.pyc create mode 100644 tasks/__pycache__/forms.cpython-313.pyc create mode 100644 tasks/__pycache__/models.cpython-313.pyc create mode 100644 tasks/__pycache__/views.cpython-313.pyc create mode 100644 tasks/admin.py create mode 100644 tasks/apps.py create mode 100644 tasks/forms.py create mode 100644 tasks/migrations/0001_initial.py create mode 100644 tasks/migrations/0002_movimientos_cuentas_alter_cuenta_fecha_creacion_and_more.py create mode 100644 tasks/migrations/0003_alter_movimientos_cuentas.py create mode 100644 tasks/migrations/0004_movimientos_fecha_factura_movimientos_n_factura_and_more.py create mode 100644 tasks/migrations/__init__.py create mode 100644 tasks/migrations/__pycache__/0001_initial.cpython-313.pyc create mode 100644 tasks/migrations/__pycache__/0002_movimientos_cuentas_alter_cuenta_fecha_creacion_and_more.cpython-313.pyc create mode 100644 tasks/migrations/__pycache__/0003_alter_movimientos_cuentas.cpython-313.pyc create mode 100644 tasks/migrations/__pycache__/0004_movimientos_fecha_factura_movimientos_n_factura_and_more.cpython-313.pyc create mode 100644 tasks/migrations/__pycache__/__init__.cpython-313.pyc create mode 100644 tasks/models.py create mode 100644 tasks/templates/base.html create mode 100644 tasks/templates/cuentas.html create mode 100644 tasks/templates/index.html create mode 100644 tasks/templates/lista_cuentas.html create mode 100644 tasks/templates/lista_movimientos.html create mode 100644 tasks/templates/movimiento.html create mode 100644 tasks/templates/singup.html create mode 100644 tasks/tests.py create mode 100644 tasks/views.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f7275bb --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +venv/ diff --git a/manage.py b/manage.py new file mode 100644 index 0000000..5dbde4c --- /dev/null +++ b/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'registro.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/registro/__init__.py b/registro/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/registro/__pycache__/__init__.cpython-313.pyc b/registro/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bef31537c03730497261424eb45868c78e2442de GIT binary patch literal 159 zcmey&%ge<81V{UN(?RrO5CH>>P{wB#AY&>+I)f&o-%5reCLr%KNa~iIvsFxJacWU< zOj=Q5UUG~}YH@Z+enCumeo=ODL1J=hOi^ljW^qYTK7tV+pP83g5+AQuP<0CU8BV!RWkOctEe<=R| literal 0 HcmV?d00001 diff --git a/registro/__pycache__/settings.cpython-313.pyc b/registro/__pycache__/settings.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..76bbfa9a0172b48ad88d19127ad71ef463da649f GIT binary patch literal 3516 zcmb7G&2QVt6{lob)`ul4vh3JSoY2mv?U=OUc(YlwX=sUZw2?$bBgIKzfS{$Z%$OpT zA!WzBREJ&`J?vhJc29dM&;mX5PjQe#ggF`LsW&G#K7~@{-5i=y}&U4a0dM+(Cd8qn}=ckz#xV&$b-C3Jl}hXmu0rR^UT)J8pxcd z6zskS+qK@H5Tt?T(EWEFv>tNTlx$4^8AS}8`=Fly` zx=rRGx&?^%+Sc{b_y4(SXH?36KKEZVr7vju`x!@zYu>J$+(t`e0U}w1|5E+V!m_dq z+Y(B@QuMmPn%J^by>7BQ24M-VtENQ^wn>avxN32s&<55qQ7l|zw-4D{HP>iasWnuc zH7$i$J7u@#q3T?(^w5cXN{e+6)AcR9ATBP`-;}W z=Ktf1;GF?S|8!Eh;Gw>vV(r@Ny)3A@iK{Jwmv`=3*OL$A<>X53!Ht!q@&I3JChy~2 z)lRPBh2;Iat9NTVcauwdwQm<+KUgwv-Y74O*oiI-S2c8tsN0;IRD1mVYzsVIIydQ< zVBN9~o7l83o)enbG$GBO3mPC$>R>T*-ok>7sXE(X(qT(6_e=*N1wZwRg^g@Mq`rcXhqi*m5)Bjugf^uTMD|!hWhetu zsZ8KTWbDW1mg*k0rec~02C2zVchs62A6GFk5iE!3Eej*Hp=yf5pl4kTgV+zh7Addl zYNOT2VZCncp4tEAYwf$tD)wSL19GJD0|terp{Ux~?wJj%$%)R~dY2J))SarkYEUoK zCvex_SIvDpy-@}5pp?cQ%uXwQAISGEubeE;E|k7ep{e^r&oRnk(U8E z<#?yToCKV6yYaM`5h}T|3{E6DlNEDm8WHnCHYfM1L#R7+nb_+)eC(^a_&G$!&eUX= z#p44L{pjhbzf4KW%dSQ@*j#d@K*O*wQO-!Eyilf-F7jikf`md^Mq*JYIm@PzOg1OV zdEvQ?vM)uNK$W5s40phoSPO{I#r`qVk=^uMyW<{=II3K-kc@I#UT zIOm(*{3Em{LmrRkgE!>y{cDQxOn>k*qp_a?zkm2XJoh#{_bz#y&myVe6&cr=Xg0(wv9S5n~ zIB=clL_6G>zH-D&c1GvGQEa*$p%?d#m_#Qs_T%Ocn?G*-u=S_;UvB+;>lglK{x7%R h&8+_Y`MbzN@H254a67YeN6buT{^nceDoMiz{Tm;Wa3TNz literal 0 HcmV?d00001 diff --git a/registro/__pycache__/urls.cpython-313.pyc b/registro/__pycache__/urls.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..585b9d12b85c9f0164e4e49767d99b156d99157a GIT binary patch literal 1927 zcmb7FJ#X7a7^Y-OwkXSX94qk`iikh2|(7iN{U!$SH`xbQ-=!q!TS=Fgq^I=sPHH>N(n#Quosn&s$sv|^GTT{}MN&;( zlJrb1y+08fO8Wh&1jlGC9iebtwwB$Wj%SiyAJxY`90?UUP|PGVG18TRA`gliF`xAM z_&`wr#cVPYBYk3^m;}Xq%qP7*HBejw#bPoOBYk?HxDJZq z+yuo^%qP7*D-@c-rdxBBdHftos2uF?B7@pZv*j`Dn$$*3$`B(hle>%}htVg*a8*V5 zyiL$!k98dEx`f&MG3tN>G3vR5BQV2}OVP1Oo^lmE02>x%1erFNvm(PzhhR<+C#0$% z_}Z@P@T#UYsKM376KuC=?9QN;Mr>_OUC~_XmoZmi##2Q0Z|1ApYwh0q~sA0r$I9p)r|3oA7D&}=zq+Iz&L=SDF z<26jX1w{z`iPXdv$Mty_76w5musuRc$`#b?m$Dj_E7}fK6g&>HuU!s{@}HwU?9i45 z1&lqF1QFUTRVvEw;&4_{K?XN0(*_`miVGsFPd6L#S|9~kZno@5BI@5A6=ToQq6yFG zju{@YUCgzhAPy$3_s89dc*6F`G1h{q{^7=~FX@g6TX2rGAhZgoyJ*p4(=x##<=VPw zyVb}_-E7>|f@_h7Ax-zz(BPc~!ZQtK632`V)h6-f%nNf4tKC#4<`EC4yVOs<{=w_-3jtTXMxFr z$v~NpID?m83S@4&B#^~HVWU8>oM^=Ff#pRrE}DX9#KX?S-+_teJjf5-v>&A_proLB zzgj!sgz;LFVcV#ELiiEf|JqZ^j<|yjQi~s^1PITn^DG!!w`jxbkh^RFOu@fL;=e%i zW+atLy-iQ1pGzSw%?*o&&}D+4}cwU)IEU{^dchSoVwMi|4+)0nBA??$ugvVZ~oqx%^<;mvG&#&CO{Pas-{wm~_FGx?_^3|=&J>tvFkX!v}vv+6Dzq5Dwh2_h3 b$lbp1dfJ|^0mpoqzbTGSroYa!IY{jljA6uz@VQ#mvOL_41L(66$nqILsj z#|FGdK9b1CzO{!XHrd{@c5P`B6ySs9in7QDjRmfdU-R#@t$Ag5_Bv|cwB%A^wU#cv zJFom+pG1O>SVBfZ5(x=YS_+a&aRQNc9jC7eyv~IJMf4cRoI%r+9;ZO4B9vqzK1<e3YJFFM){G~rRWIscXgq{rVF^{-n>%qMvw&O9 zm9|kT>cA&V3SKyd7{iF3rMhU`lKhBfrKO^?-3g8qNEM7EHh&=NjdSUIL#veZTi VwXyvbb$>S3zce3RHsoDH`VTNEzlHz+ literal 0 HcmV?d00001 diff --git a/registro/asgi.py b/registro/asgi.py new file mode 100644 index 0000000..6e1358b --- /dev/null +++ b/registro/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for registro project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/5.1/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'registro.settings') + +application = get_asgi_application() diff --git a/registro/settings.py b/registro/settings.py new file mode 100644 index 0000000..90b57cb --- /dev/null +++ b/registro/settings.py @@ -0,0 +1,154 @@ +""" +Django settings for registro project. + +Generated by 'django-admin startproject' using Django 5.1.5. + +For more information on this file, see +https://docs.djangoproject.com/en/5.1/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/5.1/ref/settings/ +""" +import os +from pathlib import Path + +# 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/5.1/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'django-insecure-f2t$19_-10d9%01a9e#p17ehiz13e)17232df21+kd8*q9+s&%' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'tasks', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'registro.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'registro.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/5.1/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.mysql', # Motor de MySQL + 'NAME': 'registro', # Nombre de la base de datos + 'USER': 'root', # Usuario de MySQL + 'PASSWORD': '', # Contraseña de MySQL + 'HOST': 'localhost', # Dirección del servidor (por defecto localhost) + 'PORT': '3306', # Puerto de MySQL (por defecto 3306) + } +} + + +# Password validation +# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/5.1/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/5.1/howto/static-files/ + +STATIC_URL = 'static/' + +LOGIN_URL = 'signin' + +# Default primary key field type +# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + +EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' +EMAIL_HOST = 'smtp.gmail.com' +EMAIL_PORT = 587 +EMAIL_USE_TLS = True +EMAIL_HOST_USER = 'registrogestorpagos@gmail.com' +EMAIL_HOST_PASSWORD = 'mlsr wxwu hfey qgiv' +DEFAULT_FROM_EMAIL = 'registrogestorpagos@gmail.com' + + +LANGUAGE_CODE = 'es-es' +USE_I18N = True +USE_L10N = True +USE_TZ = True +DATE_FORMAT = "d/m/Y" +USE_THOUSAND_SEPARATOR = False +TIME_ZONE = 'America/El_Salvador' + +FILE_MAX_SIZE = 910242880 +SUPPORTED_MIME_TYPES = ['image/png', 'image/jpeg', 'application/pdf','video/mp4', 'text/csv', 'text/plain'] +PATH_FILES = os.path.join(os.path.dirname(BASE_DIR), "files/") +SESSION_COOKIE_AGE = 60 * 15 +SESSION_SAVE_EVERY_REQUEST = True +SESSION_EXPIRE_AT_BROWSER_CLOSE = True \ No newline at end of file diff --git a/registro/urls.py b/registro/urls.py new file mode 100644 index 0000000..306129a --- /dev/null +++ b/registro/urls.py @@ -0,0 +1,33 @@ +""" +URL configuration for registro project. + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/5.1/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +from django.urls import path +from tasks import views + +urlpatterns = [ + path('admin/', admin.site.urls), + path('',views.signin, name='signin'), + path('signup/',views.signup, name='signup'), + path('cuentas/', views.lista_cuentas, name='lista_cuentas'), + path('cuentas/nueva/', views.registrar_cuenta, name='registrar_cuenta'), + path('signout/',views.signout, name='signout'), + path('movimientos//', views.lista_movimientos, name='lista_movimientos'), + path('movimientos//nuevo', views.registrar_movimiento, name='registrar_movimiento'), + path('movimientos/editar//', views.actualizar_movimiento, name='actualizar_movimiento'), + path('movimientos/excel//', views.exportar_movimientos_excel, name='exportar_movimientos_excel'), + +] diff --git a/registro/wsgi.py b/registro/wsgi.py new file mode 100644 index 0000000..c97078f --- /dev/null +++ b/registro/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for registro project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/5.1/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'registro.settings') + +application = get_wsgi_application() diff --git a/tasks/__init__.py b/tasks/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tasks/__pycache__/__init__.cpython-313.pyc b/tasks/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..72afd37ca1f556f4766be8b2ededeaa780daeff6 GIT binary patch literal 156 zcmey&%ge<81U3D==^*+sh=2h`DC08=kTI1Zok5e)ZzV$!6Oi{ABz4Q$*(xTqIJKxa zCaowjFFD2~wK%&ZzaXYOzbL!7ATc>LrYJQ%v$&)vKc*zHIJ-C|K0Y%qvm`!Vub}c4 fhfQvNN@-52T@fqLERbErAjU^#Mn=XWW*`dy4!$RA literal 0 HcmV?d00001 diff --git a/tasks/__pycache__/admin.cpython-313.pyc b/tasks/__pycache__/admin.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f1e4e4473c500989b8f78785c37d8050d1541fda GIT binary patch literal 200 zcmey&%ge<81U3D==}tiUF^B^LOi;#W0U%>4Loh=yqc?*WV-ceQLpqZt^GlGlCgUyE z#FX63JU>mQTYM>5iFxVyddc~DB}JJ@Ma)12D;Yk6)ZMahwu%WYPAw{qNh?asOOA0# zEzT~Y5Z%3x7>&ViQBhC};hJ=!hzL=!P_S9;7M3-)InG?}&e=Ugu@me>3-crV zH;aESvRalt1Z zR3bm9=-L^V9a#?f@{Im=CA{Qmlr_SOMmGyZA{+a0)GSnX6pJL(zOsf_bQ+B4Dq|_{ z3dZypV@6yg7LPM_Rq$lUOfc4tL8>H9MVc9UIuvbQB#Ln}({OgEXT^GsF{!wUTZ}6O z@kOD8WDKfitG+!+ZyoH=b16X5Ht@7X4~6WgET`8QbY#w3f`W)*sUV|+Eh*1)*~o9e zetD4Wg|cqa1A1TFe)HzvYt#3KF!|4)2Cl9RA7IU3|6bbd8l`Z`AYI)p58i_r)7qEI XS(Cn9jPVCr`>8m1<~jI9rVLzv;j)Pw literal 0 HcmV?d00001 diff --git a/tasks/__pycache__/forms.cpython-313.pyc b/tasks/__pycache__/forms.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f31edbd5d4fb9db65bd92aed76246c09cff26b80 GIT binary patch literal 2628 zcma)8OK%fb6rS-s{C5y=Trn_dLFv zjc7E)!MHp7WBG;5aev~ZJ*1YfaUF#FoWd#mC2pLje1=neig3v{E>K}yq~f?lr3}}@ z4RVUu$0<^Q&qvxuwtr6uo=p9&rVN_?|IiGS1KHpjewO8#G-J}LwZ`L2VDh;-F>H;6 zC(QXuwF0tfvDoEG&D>rndIx$awk8|zfpDKIaFka#>Qi_sxc>OSAK`@0)1smxQmHIu zW!9A^<%(rf(>w42II)Jdb{Y4#jn`f?UI0TFt`Q%V3;ukfrMcq=!=^}zobxIE(>&;O z?bvLIu1-J+DxsW!T1q(Qqah_i!{ul;#`eGNWNw1k+8XAN$;DQz%xR0zd97G6jlUxp z&f%hIz}57neUoN&nvzV3WOOa#>96>hRMa)gV&R&u70FdoFA=%|nb2&TT5Fh37MRzt^>7>$?X*~E6QY!ij$b~Q$9DWedb6tP1{rfy_xAQWth>K4nm1x~SzX*Yh* zg`BfnB`dPPwK}1T7O`2FRBQI4s%z6&x?W*2$(xEL%_dnN6Ya$saSbmeE|B6?4eiZn zMY~QlXrN&{EC;YFbC^mT8@~eV%^a0RfTTa=-@rK#c}0S_$#38s;2MZ;5ID!v>damO z#C_WmMrF?c_7arv|ImrFb(CmMqG8Bc#Ld}(Hoxu3nMZQi2U$OhW3qrz+V262&R{In zRv?*VX?n?IL8HCmk`fEns5wta$)v1{602q!mNpHXI9p;?KZM5D--bQIwf!f6DL6QUhC z0iJG-4j^O@TxJYU)~nNmy4*+yarqF!+X$WL!{wC^U>6UvYyjl5{yP#&E&`RM`c_ki zpQjE#{igBRm(K84PD<@aX3gG%tG!2__a1pR*7&0Cyg%>sF2LIIz~g~u2OFOo&WP#s z)|!JuKaKn_@^h}CEjp)`oWZZx!~TvWnCN(6_lAiHd=JiDH7iAO0pA3Q?=bV5)3c-q z>@R3Kw4B?}HvJH{g6E;(Tvbo>f`c^m@3fl5T?FI@%K(75r2ALX$DXH;HIoCY$ss2> zw3Y{t94yK(o%_oGiH8-r(@_}OM`a6KsPmv2qIjBu&G2UGW^Q0L~| zo6Y`1PbQtNGwXsc5qc?e@k5}Pl5b7CiiME40X*VH2PU&YCYNS4qilL9mfZ)@a$iU4 z)_@y;pc@zDH1ADtZ?OBZV1cvnEj+zr^b@d*|5RDXFfRq3=U;F=zj31uH@YeM`Ow4E Z^1!2kO%BA(L=Vi#viZpTiv!Vh?H`yqX3+ou literal 0 HcmV?d00001 diff --git a/tasks/__pycache__/models.cpython-313.pyc b/tasks/__pycache__/models.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9b3ee9a88f38d8267a7e720073e1847853e02df7 GIT binary patch literal 2861 zcmbVOO>7fK6rQ!$|9`}Gen?12;!tFTMrnbzfvPHUNSg#ql?_!}t+d_P6K9*vZaTXT zP%b`I`3)G#m{WE;-5s65`G?@~Z(2urf+B4;4UdQ%Mi&H-4b8Kg0G`1&c!nL%5O{{);29}Jve7M^AS-bH zl3Ap>1(tZ#q71%4TU(;vnFa+rFis2FlByT>E>3J#w$FiakH~}x5@8-mU|w0Aan5Y7 zJHfqrB=4BO{F2yBSU~czV9B2i@c68@Y+g23v?Yxiwz(CAaa%=zb%BSM)a!~)jgq}k z5cb4F8&7Au8{i(fN@n(S?wUgoArI?M*f4>z^l}D>D_T^aw=haSAIqJSao3hS&s3uz zk5g;WHkajVCR?D+=#;bq^!Wc02|v4p1SD zM5><1R1=wJ!OS+UaGm6{As$o|LtUZ}BCNm?i)9_{sG@vZR`q62LQ&>5X4$%CP{ULd zhCAn>+=9v`G^!U_0_rS)d}!DX{5XB z>7iPBXv14gpQ&7V;Sqd8?iloXynEgu}9T^>67hOUZwUTCA zJYIBlr7Y{fGK-5wS=ZTs8zf{J3TTqr6e9ZObk$h=KMG*DI?#Z>;4GG5nsIsWQo&(O z+}+78Yt!fx1#nn<`{u2i&w|}974RPN*$#%BVOXDvWq-WKtMusaB zjnvV4DpN~k);@i3Y5h_)HCD+t!bj@i-debKE&2Gulkw`zm(RZX`uU7n4bN3BHj-WS zWTuwPtd4I?Jjy@JSChHQbfc}a-ZoHc8(0&oZEshmQBpnbIpPBLm((vn0qJxK)xWf1J|BDWbN(H zcdF$mpgKs-HXB(dc)BjBB7Jq*>ya5oNp^V!~m6^Gk%6vEy^>wN?#!8@mt*y{W!=@$bzB@c*DD!H; zE;E(K+(^x;lLmNbnVBn;7EQ*Jdo-&wQ_+6vJdaZ&;DK4c0f0L97Z<))Cu->vtHu-m zukokxYWhm$%Hb;Aed5->cp$IK)$}U-E%jeT7Cq!CZo zmnm7+0oxSzWleNy18>b%^tDU*$ zVu!1H+*&>x<)Pv?s!=jWigWH`-rcMhOaq_o5w&bDjJW?9ZuW~d9m=+*Th0yQGvoZ0 zIoHd5m+=?Qy?H0cIY){4+MO9+S04P({l)l*O+po)Oe+WQhgT4UO>%6L3~!QwP13Uy v^a?|FKEE&Dm3Ii3JJEp9^TYgY{g%E%z}!iOg>m7|TlY`hJ@ta1?M(7FFkn=L literal 0 HcmV?d00001 diff --git a/tasks/__pycache__/views.cpython-313.pyc b/tasks/__pycache__/views.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1a3e6b2460a9d989fd09ec2d454a56a561d7ba16 GIT binary patch literal 10264 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%0AZ^n4l)m1}JKI$wZ0&8d**A z=qJ~?q6-D6ow{^|qLDxH#Twn2p&xw7tI*iT6SR>Da)7Rca97JjTC?XxmC8052MUc( zf|CZ04u;!?T*6YTBE=$!5`nks0)H7h#qo5}uPU}vaCi`Z0|4rZ08k_%ZvfFR;a~Z0 zh?bZq)$M=3@W7{5A6gpT4hUcf-@kqDc7{K+9T1w@K5F`~=|RtGQ@UyYav;|U$Kv)} zPyhX?dsEAS4X%qSw=C~dd0&?ASNZ;RHp9P|b2ogz{WZ5%l@<1@@aNwDw_MKClJ)FW zJ$thrzv}U?cV;}G(8}rF=e{z4CdW108DAOS;MyMXyV8w&*B3s$^~o)jKbdx%e8Phw z+mnZ!bW8twQuU8zT;plW__L7XfRCq;^RGV)Ii^b1F93*XqM%fuw|t0Opd5QI3vnv| zQU%o{Qs_F%N-&$E%LZs`$~u5-5S5%MZVTEb3Kn3I$`FA9bp1QDuuaq!DU6h85v>4S z_8_P%D)7LlK{J3%h3^Sk_UXcnt3|_j3$(M-cJ@>UGg&!p3{@P(!BC8 zSLl6wV2A$onIfd^^o5oCFZ*gx0u@%$SaX4a(VtsC32UVX(E)u`0(>Vz+PJfX3v{E# zHrZsf1xTy990opc`OQTKppP3PB9Kt5@qza_8V658JS>;s8wn!8FEDxyqbm@B4IyBf zqRH!6P_084au@kCjHV#cyk!6dNVp@w>41d@rd!nx-KSSwL?CR9gVABN8VYkEDV~(! zNI|s5^0AB>I#tabPst0VV5y+2QP3;kUcD4fg6}1|2$oNASTX~Ix8Pst2dfT%&(rk} z$JU2b&&j1gu6kFtx*}p^$Kf>BideYk!HXHb-@vsR)$K>*^F6qj@%T$i-{+88$npnN z{=mAF;g95N)589&a9D*u_hD?>ne`k}J%=8$8P8w^l5Kd|n&moGu5*Li{iL=f-8%5_ z@V^ZHc2KQ7ljhEB_Mok{hjI6B5Qs_*-KCAk#W?a#pdiGuM4 zi=Gb!Yy$sN(V<;QzMp5LgU6vFmR2$<(5+>hoWXoF88e80Ou~o$>ZA+jET_AGtns zeZ+srKd`UYXBv+zSO0-;%{8=r5PvVezU$N0Pg?KB(+$U$&wl^JXx*oJ`_|8Xdf}4` zX>VWJdve)M4u zrIP6tx55^gCQvHMLJf1!6ttF{@T?OBRbM0((Jb7d^+4eo2+W9ztp^z6+$t~wa5B8e zf)QAeGH5GO2Fz)*f!u@j)!LSb=9hMQX}h}H`bM^YW<$kTjJz1+;wPs6I3A783<~+0%mKb8`JQw zXf8P(mZs%JDFV+v%%BkP!c>1~qF^7OhV`I32b<-a^9cfXY$X=55|VGijTBh{@^$!E zE>c4o9M?4+1s?(AYTGO0K2%kCv(Dx(oXr_$>(bA2PS>5Ll_p4dgtdnIZTH&N_%y$N z>E~~s14Gwpf9L#L=kGSI_I|YQ!+onAa0!>Ld11q{H)pY7&GPiyqu;_{mrY9}M$z>R zOY;`k$Sup}oTKWFbH$nC_$=3cIPJMR#~nE5l2R^|tgTl;D9&Yp&E=zA<`32`Ws2x`;mF(XXNKdRxE78O78^jL>&Q z&vCjjNl&xLt>u^Gj#u=H_~Ybnpcc58QNDm^)6BB$m&^&3Iq{eoe9Uw{W?CLI-Cr`t zROZ;1OyEoAh{_y!%ryQ7!#`&Be#spF*1@tbd~2&=_kAmv*!HImj;%|#?R(0=@3*Zj z?D5T`jLH3t$;BSqGDB(;Q=84Stbu)%%@<;Fb3ZL@XRjHhnA|+U(qbo@GKw*|*+5I} YtUccYq&B^@te35$W#5<}MXjm-2S~H-*8l(j literal 0 HcmV?d00001 diff --git a/tasks/admin.py b/tasks/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/tasks/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/tasks/apps.py b/tasks/apps.py new file mode 100644 index 0000000..3ff3ab3 --- /dev/null +++ b/tasks/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class TasksConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'tasks' diff --git a/tasks/forms.py b/tasks/forms.py new file mode 100644 index 0000000..f3529de --- /dev/null +++ b/tasks/forms.py @@ -0,0 +1,36 @@ +from django import forms +from .models import Cuenta, Movimientos, TipoMovimiento + +class RegistroCuentaForm(forms.ModelForm): + class Meta: + model = Cuenta + fields = ['cuenta', 'observacion'] + widgets = { + 'cuenta': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Nombre de la cuenta'}), + 'observacion': forms.Textarea(attrs={'class': 'form-control', 'rows': 3, 'placeholder': 'Observaciones'}), + } +class RegistroMovimientosForm(forms.ModelForm): + tipo_movimiento = forms.ModelChoiceField( + queryset=TipoMovimiento.objects.all(), + empty_label="Seleccione un tipo de movimiento", + widget=forms.Select(attrs={'class': 'form-control'}), + label="Tipo de movimiento" + ) + fecha_factura = forms.DateField( + widget=forms.DateInput( + attrs={'type': 'date', 'class': 'form-control'} + ), + label="Fecha de factura", + ) + + class Meta: + model = Movimientos + fields = ['tipo_movimiento','fecha_factura', 'saldo','n_factura', 'observacion','proveedor','responsable_cuenta'] + widgets = { + 'saldo': forms.NumberInput(attrs={'class': 'form-control', 'placeholder': 'Monto'}), + 'observacion': forms.Textarea(attrs={'class': 'form-control', 'rows': 3, 'placeholder': 'Observaciones'}), + 'n_factura': forms.NumberInput(attrs={'class': 'form-control', 'placeholder': 'Ingrese numero de factura'}), + 'proveedor': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Nombre del proveedor'}), + 'responsable_cuenta': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Nombre del responsable'}), + + } \ No newline at end of file diff --git a/tasks/migrations/0001_initial.py b/tasks/migrations/0001_initial.py new file mode 100644 index 0000000..3ce1fd7 --- /dev/null +++ b/tasks/migrations/0001_initial.py @@ -0,0 +1,46 @@ +# Generated by Django 5.1.5 on 2025-02-06 13:47 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='TipoMovimiento', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('tipo_movimiento', models.CharField(max_length=100)), + ], + ), + migrations.CreateModel( + name='Cuenta', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('cuenta', models.CharField(max_length=100)), + ('fecha_creacion', models.DateField(auto_now_add=True)), + ('saldo', models.DecimalField(decimal_places=2, default=0.0, max_digits=15, null=True)), + ('observacion', models.TextField(blank=True)), + ('responsable', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='Movimientos', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('saldo', models.DecimalField(decimal_places=2, default=0.0, max_digits=15, null=True)), + ('fecha_insersion', models.DateField(auto_now_add=True)), + ('observacion', models.TextField(blank=True)), + ('responsable', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ('tipo_movimiento', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='tasks.tipomovimiento')), + ], + ), + ] diff --git a/tasks/migrations/0002_movimientos_cuentas_alter_cuenta_fecha_creacion_and_more.py b/tasks/migrations/0002_movimientos_cuentas_alter_cuenta_fecha_creacion_and_more.py new file mode 100644 index 0000000..438a569 --- /dev/null +++ b/tasks/migrations/0002_movimientos_cuentas_alter_cuenta_fecha_creacion_and_more.py @@ -0,0 +1,31 @@ +# Generated by Django 5.1.5 on 2025-02-06 15:50 + +import django.db.models.deletion +import django.utils.timezone +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tasks', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='movimientos', + name='cuentas', + field=models.ForeignKey(blank=True, default=1, on_delete=django.db.models.deletion.CASCADE, to='tasks.cuenta'), + preserve_default=False, + ), + migrations.AlterField( + model_name='cuenta', + name='fecha_creacion', + field=models.DateField(default=django.utils.timezone.now), + ), + migrations.AlterField( + model_name='movimientos', + name='fecha_insersion', + field=models.DateField(default=django.utils.timezone.now), + ), + ] diff --git a/tasks/migrations/0003_alter_movimientos_cuentas.py b/tasks/migrations/0003_alter_movimientos_cuentas.py new file mode 100644 index 0000000..a8cf530 --- /dev/null +++ b/tasks/migrations/0003_alter_movimientos_cuentas.py @@ -0,0 +1,19 @@ +# Generated by Django 5.1.5 on 2025-02-06 18:13 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tasks', '0002_movimientos_cuentas_alter_cuenta_fecha_creacion_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='movimientos', + name='cuentas', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='tasks.cuenta'), + ), + ] diff --git a/tasks/migrations/0004_movimientos_fecha_factura_movimientos_n_factura_and_more.py b/tasks/migrations/0004_movimientos_fecha_factura_movimientos_n_factura_and_more.py new file mode 100644 index 0000000..d0c91d8 --- /dev/null +++ b/tasks/migrations/0004_movimientos_fecha_factura_movimientos_n_factura_and_more.py @@ -0,0 +1,34 @@ +# Generated by Django 5.1.5 on 2025-02-07 01:41 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tasks', '0003_alter_movimientos_cuentas'), + ] + + operations = [ + migrations.AddField( + model_name='movimientos', + name='fecha_factura', + field=models.DateField(blank=True, null=True), + ), + migrations.AddField( + model_name='movimientos', + name='n_factura', + field=models.PositiveIntegerField(default=1), + preserve_default=False, + ), + migrations.AddField( + model_name='movimientos', + name='proveedor', + field=models.CharField(blank=True, max_length=100), + ), + migrations.AddField( + model_name='movimientos', + name='responsable_cuenta', + field=models.CharField(blank=True, max_length=100), + ), + ] diff --git a/tasks/migrations/__init__.py b/tasks/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tasks/migrations/__pycache__/0001_initial.cpython-313.pyc b/tasks/migrations/__pycache__/0001_initial.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2820e9f8b29d800eed9c98e8eb13e9ae48d451da GIT binary patch literal 2605 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 literal 0 HcmV?d00001 diff --git a/tasks/migrations/__pycache__/0004_movimientos_fecha_factura_movimientos_n_factura_and_more.cpython-313.pyc b/tasks/migrations/__pycache__/0004_movimientos_fecha_factura_movimientos_n_factura_and_more.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..15fb7989299d3893bc41c5adbdcbec3792673342 GIT binary patch literal 1303 zcma)5&1(}u6rcUbHk&4GTlwgg7z~a;x#{^w zm3udT&27y9zHdP?u`1N~%*@PX$`Z+ewz9Rj74uYPg4R2@Goc3eI8~*=>nn^lSY392 zJM^e2CWc6; zn|wF&aem#aeH*ad(%)HQ-K>in-o=k>-MhMVX7Za`z_jxIV!gCpu}^2$y}1L6o!irL zPuAJvp7U}M&gB=l=iADS9%y3?pHmg(X)u|r@jV}5Zw|q<2 literal 0 HcmV?d00001 diff --git a/tasks/migrations/__pycache__/__init__.cpython-313.pyc b/tasks/migrations/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c12a9df43c324db2ad70b1a85a3908256f57fd7b GIT binary patch literal 167 zcmey&%ge<81jT!oq=V?kAOZ#$p^VQgK*m&tbOudEzm*I{OhDdekkl+|2Z%#FEVXyyBSn q_{_Y_lK6PNg34PQHo5sJr8%i~MXW$$LDm+77$2D#85xV1fh+)Ex-3fo literal 0 HcmV?d00001 diff --git a/tasks/models.py b/tasks/models.py new file mode 100644 index 0000000..71d9868 --- /dev/null +++ b/tasks/models.py @@ -0,0 +1,33 @@ +from django.db import models +from django.contrib.auth.models import User +from django.utils import timezone +from decimal import Decimal + + +# Create your models here. +class TipoMovimiento(models.Model): + tipo_movimiento=models.CharField(max_length=100) + def __str__(self): + return self.tipo_movimiento # Retorna el nombre legible + + +class Cuenta (models.Model): + cuenta=models.CharField(max_length=100) + fecha_creacion=models.DateField(default=timezone.now) + saldo=models.DecimalField(max_digits=15, decimal_places=2, null=True, default=Decimal('0.00')) + responsable = models.ForeignKey(User, on_delete=models.CASCADE) + observacion= models.TextField(blank=True) + def __str__(self): + return self.cuenta + +class Movimientos(models.Model): + responsable = models.ForeignKey(User, on_delete=models.CASCADE) + tipo_movimiento=models.ForeignKey(TipoMovimiento, on_delete=models.CASCADE) + saldo=models.DecimalField(max_digits=15, decimal_places=2, null=True, default=Decimal('0.00')) + fecha_insersion=models.DateField(default=timezone.now) + observacion= models.TextField(blank=True) + cuentas= models.ForeignKey(Cuenta, on_delete=models.CASCADE) + n_factura=models.PositiveIntegerField() + fecha_factura = models.DateField(blank=True) + proveedor= models.CharField(max_length=100, blank=True) + responsable_cuenta=models.CharField(max_length=100, blank=True) \ No newline at end of file diff --git a/tasks/templates/base.html b/tasks/templates/base.html new file mode 100644 index 0000000..266e116 --- /dev/null +++ b/tasks/templates/base.html @@ -0,0 +1,77 @@ + + + + + + {% block title %}Registro{% endblock %} + + + + + + +
+ {% block content %} + {% endblock %} +
+
+

© 2025 registro

+
+ + + \ No newline at end of file diff --git a/tasks/templates/cuentas.html b/tasks/templates/cuentas.html new file mode 100644 index 0000000..216f0ed --- /dev/null +++ b/tasks/templates/cuentas.html @@ -0,0 +1,21 @@ +{% extends 'base.html' %} + +{% block content %} +
+
+
+

Registrar Nueva Cuenta

+
+
+
+ {% csrf_token %} + {{ form.as_p }} + + + Cancelar +
+
+
+
+ +{% endblock %} \ No newline at end of file diff --git a/tasks/templates/index.html b/tasks/templates/index.html new file mode 100644 index 0000000..099b452 --- /dev/null +++ b/tasks/templates/index.html @@ -0,0 +1,80 @@ +{% extends "Base.html" %} + +{% block content %} +{% if user.is_authenticated %} + +{% else %} + +
+
+
+
+

Iniciar Sesión

+ +
+ {% csrf_token %} +
+ + +
+ +
+ + +
+ + +
+
+ Registrar +
+
+
+
+
+{% endif %} + +{% endblock %} +{% block footer %} +
+
+ © 2024 Mi Aplicación. Todos los derechos reservados. +
+
+{% endblock %} + \ No newline at end of file diff --git a/tasks/templates/lista_cuentas.html b/tasks/templates/lista_cuentas.html new file mode 100644 index 0000000..60d4d44 --- /dev/null +++ b/tasks/templates/lista_cuentas.html @@ -0,0 +1,54 @@ +{% extends 'base.html' %} + +{% block content %} +
+

Lista de Cuentas

+
+ {% if es_superusuario %} + + Ver Todas las Cuentas + + + Ver Mis Cuentas + + {% endif %} +
+
+ + + + + + + + + + + + + {% for cuenta in cuentas %} + + + + + + + + + {% endfor %} + +
NombreSaldoResponsableFecha CreaciónObservacionesAcciones
{{ cuenta.cuenta }}{{ cuenta.saldo }}{{ cuenta.responsable.username }}{{ cuenta.fecha_creacion }}{{ cuenta.observacion }} + + Ver Movimientos + + + Exportar a Excel + +
+
+
+ + + + +{% endblock %} diff --git a/tasks/templates/lista_movimientos.html b/tasks/templates/lista_movimientos.html new file mode 100644 index 0000000..eb0511e --- /dev/null +++ b/tasks/templates/lista_movimientos.html @@ -0,0 +1,49 @@ +{% extends 'base.html' %} + +{% block content %} +
+

Movimientos de la cuenta: {{ cuenta.cuenta }}

+
+ + + + + + + + + + + + + + + {% for movimiento in movimientos %} + + + + + + + + + + + {% empty %} + + + + {% endfor %} + +
TipoMontoFechaObservación# de facturafecha de facturaresponsableAcciones
{{ movimiento.tipo_movimiento }}{{ movimiento.saldo }}{{ movimiento.fecha_insersion }}{{ movimiento.observacion }}{{ movimiento.n_factura }}{{ movimiento.fecha_factura }}{{ movimiento.responsable_cuenta }} + + Editar + +
No hay movimientos registrados.
+
+
+ + + + +{% endblock %} \ No newline at end of file diff --git a/tasks/templates/movimiento.html b/tasks/templates/movimiento.html new file mode 100644 index 0000000..4dcc068 --- /dev/null +++ b/tasks/templates/movimiento.html @@ -0,0 +1,49 @@ +{% extends 'base.html' %} + +{% block content %} +
+
+
+

Registrar Nuevo Movimiento

+
+
+
+ {% csrf_token %} + + +
+ + +
+
+ + {{ form.tipo_movimiento }} +
+
+ + {{ form.saldo }} +
+
+ + {{ form.observacion }} +
+
+ + {{ form.n_factura }} +
+ + {{ form.fecha_factura }} +
+ + {{ form.proveedor }} +
+ + {{ form.responsable_cuenta }} +
+ + Cancelar +
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/tasks/templates/singup.html b/tasks/templates/singup.html new file mode 100644 index 0000000..cb5661e --- /dev/null +++ b/tasks/templates/singup.html @@ -0,0 +1,108 @@ +{% extends "Base.html" %} + +{% block content %} + + + +
+
+
+
+
+

Registrar Nueva Cuenta

+ +
+ {% csrf_token %} + +
+ + + {% if form.username.errors %} +
+ {% for error in form.username.errors %} +

{{ error }}

+ {% endfor %} +
+ {% endif %} +
+ +
+ + + {% if form.email.errors %} +
+ {% for error in form.email.errors %} +

{{ error }}

+ {% endfor %} +
+ {% endif %} +
+ +
+ + + {% if form.password1.errors %} +
+ {% for error in form.password1.errors %} +

{{ error }}

+ {% endfor %} +
+ {% endif %} +
+ +
+ + + {% if form.password2.errors %} +
+ {% for error in form.password2.errors %} +

{{ error }}

+ {% endfor %} +
+ {% endif %} +
+ +
+ +
+
+ +
+
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/tasks/tests.py b/tasks/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/tasks/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/tasks/views.py b/tasks/views.py new file mode 100644 index 0000000..d74a241 --- /dev/null +++ b/tasks/views.py @@ -0,0 +1,215 @@ +from django.shortcuts import render, redirect, get_object_or_404 +from django.contrib.auth.decorators import login_required +from django.utils import timezone +from decimal import Decimal +from django.http import HttpResponse +from django.contrib.auth import login, authenticate, logout +from django.contrib.auth.forms import AuthenticationForm, UserCreationForm +from .models import Cuenta, Movimientos, TipoMovimiento +from django import forms +import openpyxl +from django.contrib.auth.models import User +from .forms import RegistroCuentaForm, RegistroMovimientosForm + + + +def base(request): + return render(request, 'index.html') + +def signin(request): + if request.method == 'GET': + return render ( request, 'index.html',{ + 'form': AuthenticationForm + }) + else: + user=authenticate(request, username=request.POST['username'], password=request.POST['password']) + if user is None: + return render ( request, 'index.html',{ + 'form': AuthenticationForm , + 'error': 'Username or password is incorrect' + }) + else: + login(request, user) + return redirect('lista_cuentas') + +def signout(request): + logout(request) + return redirect('signin') + +class CustomUserCreationForm(UserCreationForm): + email = forms.EmailField(required=True, help_text="Introduce un correo electrónico válido.") + + class Meta: + model = User + fields = ('username', 'email', 'password1', 'password2') + + def save(self, commit=True): + user = super().save(commit=False) + user.email = self.cleaned_data["email"] + if commit: + user.save() + return user + +def signup(request): + if request.method == 'POST': + form = CustomUserCreationForm(request.POST) + + # Verificamos si el formulario es válido + if form.is_valid(): + # Guardamos el usuario si todo está bien + form.save() + + # Mensaje de éxito + return redirect('signin') # Redirige a la página de inicio de sesión + else: + # Si el formulario no es válido, mostramos los errores en los campos específicos + if 'username' not in form.cleaned_data: + form.add_error('username', 'El nombre de usuario es obligatorio.') + if 'email' not in form.cleaned_data: + form.add_error('email', 'El correo electrónico es obligatorio.') + if 'password1' not in form.cleaned_data: + form.add_error('password1', 'La contraseña es obligatoria.') + if 'password2' not in form.cleaned_data: + form.add_error('password2', 'Debes confirmar la contraseña.') + + return render(request, 'singup.html', {'form': form}) + + else: + # Si es un GET, mostramos un formulario vacío + form = CustomUserCreationForm() + return render(request, 'singup.html', {'form': form}) + +@login_required +def registrar_cuenta(request): + if request.method == "POST": + form = RegistroCuentaForm(request.POST) + if form.is_valid(): + cuenta = form.save(commit=False) + cuenta.responsable = request.user + cuenta.save() + return redirect('lista_cuentas') # Cambia el nombre de la vista destino + else: + form = RegistroCuentaForm() + + return render(request, 'cuentas.html', {'form': form}) + +@login_required +def registrar_movimiento(request, cuenta_id): + cuenta = get_object_or_404(Cuenta, id=cuenta_id) # Obtiene la cuenta específica + + if request.method == "POST": + form = RegistroMovimientosForm(request.POST) + if form.is_valid(): + movimiento = form.save(commit=False) + movimiento.cuentas = cuenta # Asigna la cuenta automáticamente + movimiento.responsable = request.user # Asigna el usuario actual + + # Ajusta el saldo según el tipo de movimiento + if movimiento.tipo_movimiento.id == 1: # Supongamos que ID 1 es ingreso + cuenta.saldo += movimiento.saldo + elif movimiento.tipo_movimiento.id == 2: # ID 2 es egreso + cuenta.saldo -= movimiento.saldo + + cuenta.save() + movimiento.save() + return redirect('lista_movimientos', cuenta_id=cuenta.id) + else: + form = RegistroMovimientosForm() + + return render(request, 'movimiento.html', {'form': form, 'cuenta': cuenta}) + +@login_required +def actualizar_movimiento(request, movimiento_id): + movimiento = get_object_or_404(Movimientos, id=movimiento_id) + cuenta = movimiento.cuentas # Obtener la cuenta asociada + saldo_anterior = movimiento.saldo # Guardar saldo anterior + tipo_movimiento_anterior = movimiento.tipo_movimiento_id # Guardar tipo anterior + + if request.method == "POST": + form = RegistroMovimientosForm(request.POST, instance=movimiento) + if form.is_valid(): + nuevo_movimiento = form.save(commit=False) + + # Restaurar saldo antes de actualizar (solo si cambió el tipo o saldo) + if tipo_movimiento_anterior == 1: # Ingreso anterior + cuenta.saldo -= saldo_anterior + elif tipo_movimiento_anterior == 2: # Egreso anterior + cuenta.saldo += saldo_anterior + + # Aplicar nuevo saldo + if nuevo_movimiento.tipo_movimiento_id == 1: # Nuevo ingreso + cuenta.saldo += nuevo_movimiento.saldo + elif nuevo_movimiento.tipo_movimiento_id == 2: # Nuevo egreso + cuenta.saldo -= nuevo_movimiento.saldo + + cuenta.save() # Guardar la cuenta con el saldo actualizado + nuevo_movimiento.save() # Guardar el movimiento + + return redirect('lista_movimientos', cuenta_id=cuenta.id) + else: + form = RegistroMovimientosForm(instance=movimiento) + + return render(request, 'movimiento.html', {'form': form, 'cuenta': cuenta, 'movimiento': movimiento}) + +@login_required +def lista_cuentas(request): + if request.user.is_superuser: + cuentas = Cuenta.objects.all() # Si es superusuario, puede ver todas las cuentas + mostrar_todas = request.GET.get('todas', False) # Verifica si quiere ver todas las cuentas + if not mostrar_todas: + cuentas = cuentas.filter(responsable=request.user) # Muestra solo las propias si no activa la opción + else: + cuentas = Cuenta.objects.filter(responsable=request.user) # Usuario normal solo ve sus cuentas + + return render(request, 'lista_cuentas.html', {'cuentas': cuentas, 'es_superusuario': request.user.is_superuser}) + +@login_required +def lista_movimientos(request, cuenta_id): + cuenta = get_object_or_404(Cuenta, id=cuenta_id) # Obtiene la cuenta o muestra 404 + movimientos = Movimientos.objects.filter(cuentas=cuenta) # Filtra por cuenta + + return render(request, 'lista_movimientos.html', { + 'movimientos': movimientos, + 'cuenta': cuenta # Para mostrar el nombre en el template + }) + +@login_required +def exportar_movimientos_excel(request, cuenta_id): + # Obtener la cuenta + cuenta = get_object_or_404(Cuenta, id=cuenta_id) + + # Obtener los movimientos de la cuenta + movimientos = Movimientos.objects.filter(cuentas=cuenta) + + # Crear el archivo de Excel + wb = openpyxl.Workbook() + ws = wb.active + ws.title = f"Movimientos de {cuenta.cuenta}" + + # Establecer el nombre de la cuenta en la primera fila + ws.append([f"Cuenta: {cuenta.cuenta}"]) + ws.append([]) # Espacio vacío antes de la tabla + + # Agregar encabezados + encabezados = ["Fecha Inserción", "Tipo de Movimiento", "N° Factura", "Fecha Factura", "Proveedor", "Responsable", "Responsable Cuenta", "Saldo"] + ws.append(encabezados) + + # Agregar datos de los movimientos + for mov in movimientos: + ws.append([ + mov.fecha_insersion.strftime('%Y-%m-%d'), + mov.tipo_movimiento.tipo_movimiento, # Asegúrate de que el modelo tenga 'nombre' + mov.n_factura, + mov.fecha_factura.strftime('%Y-%m-%d') if mov.fecha_factura else '', + mov.proveedor, + mov.responsable.username, + mov.responsable_cuenta, + mov.saldo + ]) + + # Crear la respuesta HTTP con el archivo Excel + response = HttpResponse(content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') + response['Content-Disposition'] = f'attachment; filename="Movimientos_{cuenta.cuenta}.xlsx"' + wb.save(response) + + return response \ No newline at end of file