Compare commits
10 Commits
5503bbe8f7
...
latest
| Author | SHA1 | Date | |
|---|---|---|---|
| 1d319d8efa | |||
| 874f4e29db | |||
| 131fe8fecc | |||
| b154da09a5 | |||
| f47fd21deb | |||
| ac27137b77 | |||
| 7c445d4b66 | |||
| e4f0611ac5 | |||
| 33dee87cb2 | |||
| 3de6d37e03 |
+70
-100
@@ -11,83 +11,47 @@ https://docs.djangoproject.com/en/6.0/ref/settings/
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os, sys
|
import os
|
||||||
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
import environ
|
||||||
|
|
||||||
|
|
||||||
DEV_ENV = len(sys.argv) > 1 and sys.argv[1] == 'runserver'
|
DEV_ENV = len(sys.argv) > 1 and sys.argv[1] == 'runserver'
|
||||||
|
|
||||||
RUNNING_TESTS = any(arg in {'test', 'pytest'} for arg in sys.argv) or 'PYTEST_CURRENT_TEST' in os.environ
|
RUNNING_TESTS = any(arg in {'test', 'pytest'} for arg in sys.argv) or 'PYTEST_CURRENT_TEST' in os.environ
|
||||||
|
|
||||||
|
|
||||||
def load_dotenv(dotenv_path: Path) -> None:
|
|
||||||
if not dotenv_path.exists():
|
|
||||||
return
|
|
||||||
|
|
||||||
for raw_line in dotenv_path.read_text(encoding='utf-8').splitlines():
|
|
||||||
line = raw_line.strip()
|
|
||||||
if not line or line.startswith('#') or '=' not in line:
|
|
||||||
continue
|
|
||||||
|
|
||||||
key, value = line.split('=', 1)
|
|
||||||
key = key.strip()
|
|
||||||
value = value.strip().strip('"').strip("'")
|
|
||||||
os.environ.setdefault(key, value)
|
|
||||||
|
|
||||||
|
|
||||||
def env_bool(name: str, default: bool = False) -> bool:
|
|
||||||
value = os.getenv(name)
|
|
||||||
if value is None:
|
|
||||||
return default
|
|
||||||
return value.strip().lower() in {'1', 'true', 'yes', 'on'}
|
|
||||||
|
|
||||||
|
|
||||||
def env_list(name: str, default: list[str] | None = None) -> list[str]:
|
|
||||||
value = os.getenv(name)
|
|
||||||
if value is None:
|
|
||||||
return default or []
|
|
||||||
return [item.strip() for item in value.split(',') if item.strip()]
|
|
||||||
|
|
||||||
|
|
||||||
def env_int(name: str, default: int) -> int:
|
|
||||||
value = os.getenv(name)
|
|
||||||
if value is None:
|
|
||||||
return default
|
|
||||||
return int(value)
|
|
||||||
|
|
||||||
|
|
||||||
def env_str(name: str, default: str = '') -> str:
|
|
||||||
value = os.getenv(name)
|
|
||||||
if value is None:
|
|
||||||
return default
|
|
||||||
return value.strip()
|
|
||||||
|
|
||||||
|
|
||||||
def env_optional_str(name: str) -> str | None:
|
|
||||||
value = os.getenv(name)
|
|
||||||
if value is None:
|
|
||||||
return None
|
|
||||||
value = value.strip()
|
|
||||||
return value or None
|
|
||||||
|
|
||||||
# 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
|
||||||
load_dotenv(BASE_DIR / '.env')
|
env = environ.Env(
|
||||||
|
DEBUG=(bool, True),
|
||||||
|
S3_ENABLE=(bool, False),
|
||||||
|
S3_USE_LOCAL_URLS=(bool, False),
|
||||||
|
POSTGRES_ENABLED=(bool, True),
|
||||||
|
POSTGRES_PORT=(int, 5432),
|
||||||
|
SMTP_PORT=(int, 587),
|
||||||
|
AWS_S3_USE_SSL=(bool, True),
|
||||||
|
AWS_QUERYSTRING_AUTH=(bool, False),
|
||||||
|
)
|
||||||
|
env.read_env(BASE_DIR / '.env')
|
||||||
|
|
||||||
|
|
||||||
# Quick-start development settings - unsuitable for production
|
# Quick-start development settings - unsuitable for production
|
||||||
# See https://docs.djangoproject.com/en/6.0/howto/deployment/checklist/
|
# See https://docs.djangoproject.com/en/6.0/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 = os.getenv('SECRET_KEY', '')
|
SECRET_KEY = env('SECRET_KEY', default='')
|
||||||
|
|
||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
DEBUG = env_bool('DEBUG', True)
|
DEBUG = env.bool('DEBUG')
|
||||||
S3_ENABLE = env_bool('S3_ENABLE', False)
|
S3_ENABLE = env.bool('S3_ENABLE')
|
||||||
S3_USE_LOCAL_URLS = env_bool('S3_USE_LOCAL_URLS', False)
|
S3_USE_LOCAL_URLS = env.bool('S3_USE_LOCAL_URLS')
|
||||||
|
|
||||||
ALLOWED_HOSTS = env_list('ALLOWED_HOSTS', [
|
ALLOWED_HOSTS = env.list('ALLOWED_HOSTS', default=[
|
||||||
'localhost',
|
'localhost',
|
||||||
'127.0.0.1',
|
'127.0.0.1',
|
||||||
|
'zkqpv8r3-8000.uks1.devtunnels.ms'
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
@@ -147,7 +111,7 @@ WSGI_APPLICATION = 'proyecto.wsgi.application'
|
|||||||
# https://docs.djangoproject.com/en/6.0/ref/settings/#databases
|
# https://docs.djangoproject.com/en/6.0/ref/settings/#databases
|
||||||
# Usa PostgreSQL por defecto (POSTGRES_ENABLED=True); si no, SQLite.
|
# Usa PostgreSQL por defecto (POSTGRES_ENABLED=True); si no, SQLite.
|
||||||
|
|
||||||
if RUNNING_TESTS or not env_bool('POSTGRES_ENABLED', True):
|
if RUNNING_TESTS or not env.bool('POSTGRES_ENABLED'):
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
'default': {
|
'default': {
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
'ENGINE': 'django.db.backends.sqlite3',
|
||||||
@@ -158,11 +122,11 @@ else:
|
|||||||
DATABASES = {
|
DATABASES = {
|
||||||
'default': {
|
'default': {
|
||||||
'ENGINE': 'django.db.backends.postgresql',
|
'ENGINE': 'django.db.backends.postgresql',
|
||||||
'NAME': os.getenv('POSTGRES_DB', 'tienda'),
|
'NAME': env('POSTGRES_DB', default='tienda'),
|
||||||
'USER': os.getenv('POSTGRES_USER', 'postgres'),
|
'USER': env('POSTGRES_USER', default='postgres'),
|
||||||
'PASSWORD': os.getenv('POSTGRES_PASSWORD', ''),
|
'PASSWORD': env('POSTGRES_PASSWORD', default=''),
|
||||||
'HOST': os.getenv('POSTGRES_HOST', '127.0.0.1'),
|
'HOST': env('POSTGRES_HOST', default='127.0.0.1'),
|
||||||
'PORT': env_int('POSTGRES_PORT', 5432),
|
'PORT': env.int('POSTGRES_PORT'),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,10 +164,10 @@ USE_TZ = True
|
|||||||
# Static files (CSS, JavaScript, Images)
|
# Static files (CSS, JavaScript, Images)
|
||||||
# https://docs.djangoproject.com/en/6.0/howto/static-files/
|
# https://docs.djangoproject.com/en/6.0/howto/static-files/
|
||||||
|
|
||||||
STATIC_URL = 'static/'
|
|
||||||
STATIC_ROOT = BASE_DIR / 'staticfiles'
|
STATIC_ROOT = BASE_DIR / 'staticfiles'
|
||||||
COMPRESS_ROOT = STATIC_ROOT
|
COMPRESS_ROOT = STATIC_ROOT
|
||||||
COMPRESS_URL = STATIC_URL
|
|
||||||
STATICFILES_DIRS = [
|
STATICFILES_DIRS = [
|
||||||
BASE_DIR / 'tienda' / 'static',
|
BASE_DIR / 'tienda' / 'static',
|
||||||
]
|
]
|
||||||
@@ -222,15 +186,15 @@ STORAGES = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if S3_ENABLE:
|
if S3_ENABLE:
|
||||||
AWS_STORAGE_BUCKET_NAME = env_str('AWS_STORAGE_BUCKET_NAME') or None
|
AWS_STORAGE_BUCKET_NAME = env('AWS_STORAGE_BUCKET_NAME', default='') or None
|
||||||
AWS_ACCESS_KEY_ID = env_optional_str('AWS_ACCESS_KEY_ID')
|
AWS_ACCESS_KEY_ID = env('AWS_ACCESS_KEY_ID', default=None)
|
||||||
AWS_SECRET_ACCESS_KEY = env_optional_str('AWS_SECRET_ACCESS_KEY')
|
AWS_SECRET_ACCESS_KEY = env('AWS_SECRET_ACCESS_KEY', default=None)
|
||||||
AWS_S3_REGION_NAME = env_optional_str('AWS_S3_REGION_NAME')
|
AWS_S3_REGION_NAME = env('AWS_S3_REGION_NAME', default=None)
|
||||||
AWS_S3_ENDPOINT_URL = env_optional_str('AWS_S3_ENDPOINT_URL')
|
AWS_S3_ENDPOINT_URL = env('AWS_S3_ENDPOINT_URL', default=None)
|
||||||
AWS_S3_CUSTOM_DOMAIN = env_optional_str('AWS_S3_CUSTOM_DOMAIN')
|
AWS_S3_CUSTOM_DOMAIN = env('AWS_S3_CUSTOM_DOMAIN', default=None)
|
||||||
AWS_S3_USE_SSL = env_bool('AWS_S3_USE_SSL', True)
|
AWS_S3_USE_SSL = env.bool('AWS_S3_USE_SSL')
|
||||||
AWS_QUERYSTRING_AUTH = env_bool('AWS_QUERYSTRING_AUTH', False)
|
AWS_QUERYSTRING_AUTH = env.bool('AWS_QUERYSTRING_AUTH')
|
||||||
AWS_DEFAULT_ACL = env_str('AWS_DEFAULT_ACL', 'public-read') or None
|
AWS_DEFAULT_ACL = env('AWS_DEFAULT_ACL', default='public-read') or None
|
||||||
AWS_S3_OBJECT_PARAMETERS = {}
|
AWS_S3_OBJECT_PARAMETERS = {}
|
||||||
|
|
||||||
STORAGES = {
|
STORAGES = {
|
||||||
@@ -242,6 +206,14 @@ if S3_ENABLE:
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if S3_ENABLE and AWS_S3_CUSTOM_DOMAIN:
|
||||||
|
STATIC_URL = f"https://{AWS_S3_CUSTOM_DOMAIN}/static/"
|
||||||
|
MEDIA_URL = f"https://{AWS_S3_CUSTOM_DOMAIN}/media/"
|
||||||
|
else:
|
||||||
|
STATIC_URL = env("STATIC_URL", default="static/")
|
||||||
|
MEDIA_URL = env("MEDIA_URL", default="media/")
|
||||||
|
|
||||||
|
COMPRESS_URL = STATIC_URL
|
||||||
STATICFILES_FINDERS = [
|
STATICFILES_FINDERS = [
|
||||||
'django.contrib.staticfiles.finders.FileSystemFinder',
|
'django.contrib.staticfiles.finders.FileSystemFinder',
|
||||||
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
|
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
|
||||||
@@ -250,15 +222,13 @@ STATICFILES_FINDERS = [
|
|||||||
|
|
||||||
COMPRESS_PRECOMPILERS = ()
|
COMPRESS_PRECOMPILERS = ()
|
||||||
|
|
||||||
# Media files (User uploads)
|
MEDIA_ROOT = Path(env('MEDIA_ROOT', default='/app/media'))
|
||||||
MEDIA_URL = 'media/'
|
|
||||||
MEDIA_ROOT = Path(os.getenv('MEDIA_ROOT', '/app/media'))
|
|
||||||
|
|
||||||
# Redis Configuration
|
# Redis Configuration
|
||||||
CACHES = {
|
CACHES = {
|
||||||
'default': {
|
'default': {
|
||||||
'BACKEND': 'django_redis.cache.RedisCache',
|
'BACKEND': 'django_redis.cache.RedisCache',
|
||||||
'LOCATION': os.getenv('REDIS_URL', 'redis://127.0.0.1:6379/1'),
|
'LOCATION': env('REDIS_URL', default='redis://127.0.0.1:6379/1'),
|
||||||
'OPTIONS': {
|
'OPTIONS': {
|
||||||
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
|
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
|
||||||
}
|
}
|
||||||
@@ -283,30 +253,30 @@ MESSAGE_TAGS = {
|
|||||||
# Login URL
|
# Login URL
|
||||||
LOGIN_URL = '/tienda/login/'
|
LOGIN_URL = '/tienda/login/'
|
||||||
|
|
||||||
STRIPE_PUBLISHABLE_KEY = os.getenv('STRIPE_PUBLISHABLE_KEY', '')
|
STRIPE_PUBLISHABLE_KEY = env('STRIPE_PUBLISHABLE_KEY', default='')
|
||||||
STRIPE_SECRET_KEY = os.getenv('STRIPE_SECRET_KEY', '')
|
STRIPE_SECRET_KEY = env('STRIPE_SECRET_KEY', default='')
|
||||||
|
|
||||||
# PayPal Configuration (Sandbox)
|
# PayPal Configuration (Sandbox)
|
||||||
# Para obtener credenciales: https://sandbox.paypal.com/
|
# Para obtener credenciales: https://sandbox.paypal.com/
|
||||||
PAYPAL_CLIENT_ID = os.getenv('PAYPAL_CLIENT_ID', '') # Reemplazar con tu Client ID de PayPal Sandbox
|
PAYPAL_CLIENT_ID = env('PAYPAL_CLIENT_ID', default='') # Reemplazar con tu Client ID de PayPal Sandbox
|
||||||
PAYPAL_CLIENT_SECRET = os.getenv('PAYPAL_CLIENT_SECRET', '') # Reemplazar con tu Client Secret de PayPal Sandbox
|
PAYPAL_CLIENT_SECRET = env('PAYPAL_CLIENT_SECRET', default='') # Reemplazar con tu Client Secret de PayPal Sandbox
|
||||||
PAYPAL_MODE = os.getenv('PAYPAL_MODE', 'sandbox') # Cambiar a 'live' en producción
|
PAYPAL_MODE = env('PAYPAL_MODE', default='sandbox') # Cambiar a 'live' en producción
|
||||||
|
|
||||||
|
|
||||||
SMTP_ENDPOINT = os.getenv('SMTP_ENDPOINT', 'smtp.email.eu-paris-1.oci.oraclecloud.com')
|
SMTP_ENDPOINT = env('SMTP_ENDPOINT', default='smtp.email.eu-paris-1.oci.oraclecloud.com')
|
||||||
SMTP_PORT = env_int('SMTP_PORT', 587)
|
SMTP_PORT = env.int('SMTP_PORT')
|
||||||
SECURITY = os.getenv('SECURITY', 'tls')
|
SECURITY = env('SECURITY', default='tls')
|
||||||
SMTP_USERNAME = os.getenv('SMTP_USERNAME', None)
|
SMTP_USERNAME = env('SMTP_USERNAME', default=None)
|
||||||
SMTP_PASSWORD = os.getenv('SMTP_PASSWORD', None)
|
SMTP_PASSWORD = env('SMTP_PASSWORD', default=None)
|
||||||
SMTP_EMAIL = os.getenv("SMTP_EMAIL", None)
|
SMTP_EMAIL = env('SMTP_EMAIL', default=None)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
AUTH_USER_MODEL = 'tienda.User'
|
AUTH_USER_MODEL = 'tienda.User'
|
||||||
|
|
||||||
|
|
||||||
DOMAIN = os.getenv("DOMAIN", "localhost")
|
DOMAIN = env('DOMAIN', default='localhost')
|
||||||
PROTOCOL = os.getenv("PROTOCOL", "http")
|
PROTOCOL = env('PROTOCOL', default='http')
|
||||||
|
|
||||||
default_csrf_trusted_origins = []
|
default_csrf_trusted_origins = []
|
||||||
if DOMAIN:
|
if DOMAIN:
|
||||||
@@ -316,16 +286,16 @@ for host in ALLOWED_HOSTS:
|
|||||||
if host and host != '*':
|
if host and host != '*':
|
||||||
default_csrf_trusted_origins.append(f"{PROTOCOL}://{host}")
|
default_csrf_trusted_origins.append(f"{PROTOCOL}://{host}")
|
||||||
|
|
||||||
CSRF_TRUSTED_ORIGINS = env_list(
|
CSRF_TRUSTED_ORIGINS = env.list(
|
||||||
'CSRF_TRUSTED_ORIGINS',
|
'CSRF_TRUSTED_ORIGINS',
|
||||||
list(dict.fromkeys(default_csrf_trusted_origins)),
|
default=list(dict.fromkeys(default_csrf_trusted_origins)),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
LOG_LEVEL = os.getenv('LOG_LEVEL', 'INFO').upper()
|
LOG_LEVEL = env('LOG_LEVEL', default='INFO').upper()
|
||||||
LOG_DIR = Path(os.getenv('LOG_DIR', BASE_DIR / 'logs'))
|
LOG_DIR = Path(env('LOG_DIR', default=str(BASE_DIR / 'logs')))
|
||||||
LOG_DIR.mkdir(parents=True, exist_ok=True)
|
LOG_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
LOG_FILE = LOG_DIR / os.getenv('LOG_FILE', 'app.log')
|
LOG_FILE = LOG_DIR / env('LOG_FILE', default='app.log')
|
||||||
|
|
||||||
|
|
||||||
LOGGING = {
|
LOGGING = {
|
||||||
@@ -399,13 +369,13 @@ EMAIL_HOST_USER = SMTP_USERNAME
|
|||||||
EMAIL_HOST_PASSWORD = SMTP_PASSWORD
|
EMAIL_HOST_PASSWORD = SMTP_PASSWORD
|
||||||
|
|
||||||
# El correo que se usará como remitente por defecto
|
# El correo que se usará como remitente por defecto
|
||||||
DEFAULT_FROM_EMAIL = os.getenv("DEFAULT_FROM_EMAIL") or SMTP_EMAIL or "no-reply@localhost"
|
DEFAULT_FROM_EMAIL = env('DEFAULT_FROM_EMAIL', default='') or SMTP_EMAIL or 'no-reply@localhost'
|
||||||
|
|
||||||
# URL de Redis (asumiendo que corre en el puerto default 6379)
|
# URL de Redis (asumiendo que corre en el puerto default 6379)
|
||||||
CELERY_BROKER_URL = os.getenv("REDIS_URL", "redis://localhost:6379/0")
|
CELERY_BROKER_URL = env('REDIS_URL', default='redis://localhost:6379/0')
|
||||||
|
|
||||||
# Opcional: para guardar el resultado de las tareas
|
# Opcional: para guardar el resultado de las tareas
|
||||||
CELERY_RESULT_BACKEND = os.getenv("REDIS_URL", "redis://localhost:6379/0")
|
CELERY_RESULT_BACKEND = env('REDIS_URL', default='redis://localhost:6379/0')
|
||||||
|
|
||||||
# Configuraciones adicionales recomendadas
|
# Configuraciones adicionales recomendadas
|
||||||
CELERY_ACCEPT_CONTENT = ['json']
|
CELERY_ACCEPT_CONTENT = ['json']
|
||||||
|
|||||||
+2
-1
@@ -7,6 +7,7 @@ dependencies = [
|
|||||||
"celery==5.6.3",
|
"celery==5.6.3",
|
||||||
"Django==6.0.5",
|
"Django==6.0.5",
|
||||||
"django-compressor==4.6.0",
|
"django-compressor==4.6.0",
|
||||||
|
"django-environ>=0.13.0",
|
||||||
"django-ninja>=1.6.2",
|
"django-ninja>=1.6.2",
|
||||||
"django-redis==6.0.0",
|
"django-redis==6.0.0",
|
||||||
# S3 backend requerido por tienda/storage_backends.py cuando S3_ENABLE=True.
|
# S3 backend requerido por tienda/storage_backends.py cuando S3_ENABLE=True.
|
||||||
@@ -17,7 +18,7 @@ dependencies = [
|
|||||||
"pillow==12.2.0",
|
"pillow==12.2.0",
|
||||||
"psycopg2-binary==2.9.12",
|
"psycopg2-binary==2.9.12",
|
||||||
"requests==2.34.2",
|
"requests==2.34.2",
|
||||||
"stripe==15.1.0",
|
"stripe==15.2.0",
|
||||||
"whitenoise==6.12.0",
|
"whitenoise==6.12.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -86,12 +86,12 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
|
|
||||||
stars.forEach(star => {
|
stars.forEach(star => {
|
||||||
star.addEventListener('click', function() {
|
star.addEventListener('click', function() {
|
||||||
const value = parseInt(this.dataset.value);
|
const value = Number.parseInt(this.dataset.value);
|
||||||
updateStars(value);
|
updateStars(value);
|
||||||
});
|
});
|
||||||
|
|
||||||
star.addEventListener('mouseenter', function() {
|
star.addEventListener('mouseenter', function() {
|
||||||
const value = parseInt(this.dataset.value);
|
const value = Number.parseInt(this.dataset.value);
|
||||||
stars.forEach((s, index) => {
|
stars.forEach((s, index) => {
|
||||||
if (index < value) {
|
if (index < value) {
|
||||||
s.classList.remove('text-secondary');
|
s.classList.remove('text-secondary');
|
||||||
@@ -101,7 +101,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
star.addEventListener('mouseleave', function() {
|
star.addEventListener('mouseleave', function() {
|
||||||
updateStars(parseInt(ratingInput.value) || 1);
|
updateStars(Number.parseInt(ratingInput.value) || 1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label id="label-card-data" class="form-label">Datos de la tarjeta</label>
|
<label id="label-card-data" class="form-label">Datos de la tarjeta <input type="hidden"></label>
|
||||||
<div id="card-element" aria-labelledby="label-card-data"></div>
|
<div id="card-element" aria-labelledby="label-card-data"></div>
|
||||||
<div id="card-errors" role="alert"></div>
|
<div id="card-errors" role="alert"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -106,8 +106,8 @@
|
|||||||
<!-- Barra de búsqueda con sugerencias -->
|
<!-- Barra de búsqueda con sugerencias -->
|
||||||
<form class="search-suggestions-container" method="GET" action="{% url 'search' %}" role="search" id="searchForm">
|
<form class="search-suggestions-container" method="GET" action="{% url 'search' %}" role="search" id="searchForm">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input class="form-control" type="search" name="q" id="searchInput" placeholder="Buscar productos..." aria-label="Buscar" autocomplete="off" role="combobox" aria-expanded="false" aria-autocomplete="list" aria-controls="searchSuggestions" aria-activedescendant="" aria-haspopup="listbox">
|
<input class="form-control" type="search" name="q" id="searchInput" placeholder="Buscar productos..." aria-label="Buscar" autocomplete="off" role="combobox" aria-expanded="false" aria-autocomplete="list" aria-controls="searchSuggestions" aria-activedescendant="searchbutton" aria-haspopup="listbox">
|
||||||
<button class="btn btn-outline-primary" type="submit" aria-label="Buscar productos">🔍 Buscar</button>
|
<button class="btn btn-outline-primary" type="submit" id="searchbutton" aria-label="Buscar productos">🔍 Buscar</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="search-suggestions" id="searchSuggestions" role="listbox" aria-label="Sugerencias de búsqueda"></div>
|
<div class="search-suggestions" id="searchSuggestions" role="listbox" aria-label="Sugerencias de búsqueda"></div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -84,11 +84,11 @@
|
|||||||
<table class="table table-striped align-middle">
|
<table class="table table-striped align-middle">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Producto</th>
|
<th scope="col">Producto</th>
|
||||||
<th class="text-end">Precio (sin IVA)</th>
|
<th scope="col" class="text-end">Precio (sin IVA)</th>
|
||||||
<th class="text-end">Cantidad</th>
|
<th scope="col" class="text-end">Cantidad</th>
|
||||||
<th class="text-end">Stock actual</th>
|
<th scope="col" class="text-end">Stock actual</th>
|
||||||
<th class="text-end">Subtotal (con IVA)</th>
|
<th scope="col" class="text-end">Subtotal (con IVA)</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -104,16 +104,16 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
<tfoot>
|
<tfoot>
|
||||||
<tr>
|
<tr>
|
||||||
<th colspan="4" class="text-end">Subtotal:</th>
|
<th colspan="4" scope="row" class="text-end">Subtotal:</th>
|
||||||
<th class="text-end">{{ cart.get_total|format_price }}€</th>
|
<td class="text-end">{{ cart.get_total|format_price }}€</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th colspan="4" class="text-end">IVA (21%):</th>
|
<th scope="row" colspan="4" class="text-end">IVA (21%):</th>
|
||||||
<th class="text-end text-success">+{{ cart.get_vat_amount|format_price }}€</th>
|
<td class="text-end text-success">+{{ cart.get_vat_amount|format_price }}€</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr style="background-color: #f8f9fa;">
|
<tr style="background-color: #f8f9fa;">
|
||||||
<th colspan="4" class="text-end" style="font-size: 1.1rem;">Total:</th>
|
<th scope="row" colspan="4" class="text-end" style="font-size: 1.1rem;">Total:</th>
|
||||||
<th class="text-end" style="font-size: 1.1rem;">{{ cart.get_total_with_vat|format_price }}€</th>
|
<td class="text-end" style="font-size: 1.1rem;">{{ cart.get_total_with_vat|format_price }}€</th>
|
||||||
</tr>
|
</tr>
|
||||||
</tfoot>
|
</tfoot>
|
||||||
</table>
|
</table>
|
||||||
@@ -125,7 +125,7 @@
|
|||||||
<h5 class="card-title mb-3">2) Selecciona tu método de pago</h5>
|
<h5 class="card-title mb-3">2) Selecciona tu método de pago</h5>
|
||||||
|
|
||||||
<!-- Tabs -->
|
<!-- Tabs -->
|
||||||
<ul class="nav nav-tabs mb-3" id="paymentTabs" role="tablist">
|
<ul class="nav nav-tabs mb-3" id="paymentTabs">
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<button class="nav-link active" id="tab-card" data-tab="pane-card" type="button"
|
<button class="nav-link active" id="tab-card" data-tab="pane-card" type="button"
|
||||||
role="tab" aria-selected="true" aria-controls="pane-card" tabindex="0">
|
role="tab" aria-selected="true" aria-controls="pane-card" tabindex="0">
|
||||||
@@ -142,7 +142,7 @@
|
|||||||
|
|
||||||
<!-- Tarjeta tab -->
|
<!-- Tarjeta tab -->
|
||||||
<div id="pane-card" class="payment-tab-content active"
|
<div id="pane-card" class="payment-tab-content active"
|
||||||
role="tabpanel" aria-labelledby="tab-card" tabindex="0">
|
role="tabpanel" aria-labelledby="tab-card">
|
||||||
{% if saved_cards %}
|
{% if saved_cards %}
|
||||||
<fieldset class="mb-3">
|
<fieldset class="mb-3">
|
||||||
<legend class="fw-semibold fs-6 mb-2">Selección de tarjeta</legend>
|
<legend class="fw-semibold fs-6 mb-2">Selección de tarjeta</legend>
|
||||||
@@ -164,7 +164,7 @@
|
|||||||
|
|
||||||
<div id="new-card-section" {% if saved_cards %}style="display:none;"{% endif %}>
|
<div id="new-card-section" {% if saved_cards %}style="display:none;"{% endif %}>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label id="label-card-number" class="form-label">Número de tarjeta</label>
|
<label id="label-card-number" class="form-label">Número de tarjeta <input type="hidden"></label>
|
||||||
<div id="card-element" aria-labelledby="label-card-number"></div>
|
<div id="card-element" aria-labelledby="label-card-number"></div>
|
||||||
<div id="card-errors" role="alert"></div>
|
<div id="card-errors" role="alert"></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -187,7 +187,7 @@
|
|||||||
|
|
||||||
<!-- PayPal tab -->
|
<!-- PayPal tab -->
|
||||||
<div id="pane-paypal" class="payment-tab-content"
|
<div id="pane-paypal" class="payment-tab-content"
|
||||||
role="tabpanel" aria-labelledby="tab-paypal" tabindex="0">
|
role="tabpanel" aria-labelledby="tab-paypal">
|
||||||
{% if saved_paypal %}
|
{% if saved_paypal %}
|
||||||
<div class="alert alert-light border mb-3">
|
<div class="alert alert-light border mb-3">
|
||||||
<small class="text-muted">Cuenta PayPal guardada:</small>
|
<small class="text-muted">Cuenta PayPal guardada:</small>
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<table width="100%" border="0" cellspacing="0" cellpadding="0">
|
<table width="100%" border="0" cellspacing="0" cellpadding="0">
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center" style="padding: 20px;">
|
<th align="center" style="padding: 20px;">
|
||||||
<table width="600" border="0" cellspacing="0" cellpadding="0" style="border: 1px solid #eeeeee; background-color: #ffffff;">
|
<table width="600" border="0" cellspacing="0" cellpadding="0" style="border: 1px solid #eeeeee; background-color: #ffffff;">
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center" style="background-color: #007bff; padding: 40px;">
|
<th scope="col" align="center" style="background-color: #007bff; padding: 40px;">
|
||||||
<h1 style="color: #ffffff; font-family: sans-serif; margin: 0;">Su cuenta ha sido bloqueada</h1>
|
<h1 style="color: #ffffff; font-family: sans-serif; margin: 0;">Su cuenta ha sido bloqueada</h1>
|
||||||
</td>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center" style="padding: 40px">
|
<td align="center" style="padding: 40px">
|
||||||
@@ -22,6 +22,6 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</td>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<table width="100%" border="0" cellspacing="0" cellpadding="0">
|
<table width="100%" border="0" cellspacing="0" cellpadding="0">
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center" style="padding: 20px;">
|
<th scope="col" align="center" style="padding: 20px;">
|
||||||
<table width="600" border="0" cellspacing="0" cellpadding="0" style="border: 1px solid #eeeeee; background-color: #ffffff;">
|
<table width="600" border="0" cellspacing="0" cellpadding="0" style="border: 1px solid #eeeeee; background-color: #ffffff;">
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center" style="background-color: #007bff; padding: 40px;">
|
<th scope="col" align="center" style="background-color: #007bff; padding: 40px;">
|
||||||
<h1 style="color: #ffffff; font-family: sans-serif; margin: 0;">¡Hola {{ name }}!</h1>
|
<h1 style="color: #ffffff; font-family: sans-serif; margin: 0;">¡Hola {{ name }}!</h1>
|
||||||
</td>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="padding: 30px; font-family: sans-serif; line-height: 1.5; color: #444444;">
|
<td style="padding: 30px; font-family: sans-serif; line-height: 1.5; color: #444444;">
|
||||||
@@ -16,6 +16,6 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</td>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<table width="100%" border="0" cellspacing="0" cellpadding="0">
|
<table width="100%" border="0" cellspacing="0" cellpadding="0">
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center" style="padding: 20px;">
|
<th scope="col" align="center" style="padding: 20px;">
|
||||||
<table width="600" border="0" cellspacing="0" cellpadding="0" style="border: 1px solid #eeeeee; background-color: #ffffff;">
|
<table width="600" border="0" cellspacing="0" cellpadding="0" style="border: 1px solid #eeeeee; background-color: #ffffff;">
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center" style="background-color: #007bff; padding: 40px;">
|
<th scope="col" align="center" style="background-color: #007bff; padding: 40px;">
|
||||||
<h1 style="color: #ffffff; font-family: sans-serif; margin: 0;">¡Hola {{ name }}!</h1>
|
<h1 style="color: #ffffff; font-family: sans-serif; margin: 0;">¡Hola {{ name }}!</h1>
|
||||||
</td>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center" style="padding: 40px">
|
<td align="center" style="padding: 40px">
|
||||||
@@ -22,6 +22,6 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</td>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<table border="0" cellpadding="0" style="width: 100%;">
|
<table border="0" cellpadding="0" style="width: 100%;">
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center" style="padding: 20px;">
|
<th scope="col" align="center" style="padding: 20px;">
|
||||||
<table width="600" border="0" cellspacing="0" cellpadding="0" style="border: 1px solid #eeeeee; background-color: #ffffff;">
|
<table width="600" border="0" cellspacing="0" cellpadding="0" style="border: 1px solid #eeeeee; background-color: #ffffff;">
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center" style="background-color: #007bff; padding: 40px;">
|
<th scope="col" align="center" style="background-color: #007bff; padding: 40px;">
|
||||||
<h1 style="color: #ffffff; font-family: sans-serif; margin: 0;">Su cuenta ha sido desbloqueada</h1>
|
<h1 style="color: #ffffff; font-family: sans-serif; margin: 0;">Su cuenta ha sido desbloqueada</h1>
|
||||||
</td>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center" style="padding: 40px">
|
<td align="center" style="padding: 40px">
|
||||||
@@ -22,6 +22,6 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</td>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<table width="100%" border="0" cellspacing="0" cellpadding="0">
|
<table width="100%" border="0" cellspacing="0" cellpadding="0">
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center" style="padding: 20px;">
|
<th scope="col" align="center" style="padding: 20px;">
|
||||||
<table width="600" border="0" cellspacing="0" cellpadding="0" style="border: 1px solid #eeeeee; background-color: #ffffff;">
|
<table width="600" border="0" cellspacing="0" cellpadding="0" style="border: 1px solid #eeeeee; background-color: #ffffff;">
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center" style="background-color: #007bff; padding: 40px;">
|
<th scope="col" align="center" style="background-color: #007bff; padding: 40px;">
|
||||||
<h1 style="color: #ffffff; font-family: sans-serif; margin: 0;">¡Hola {{ name }}!</h1>
|
<h1 style="color: #ffffff; font-family: sans-serif; margin: 0;">¡Hola {{ name }}!</h1>
|
||||||
</td>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center" style="padding: 40px">
|
<td align="center" style="padding: 40px">
|
||||||
@@ -21,6 +21,6 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</td>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
+2
-26
@@ -4,30 +4,6 @@ from django.conf import settings
|
|||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger("email.system")
|
logger = logging.getLogger("email.system")
|
||||||
#
|
|
||||||
#def send_email(dest: str, title: str, body: str):
|
|
||||||
# context = ssl.create_default_context()
|
|
||||||
# try:
|
|
||||||
# with smtplib.SMTP(settings.SMTP_ENDPOINT, settings.SMTP_PORT) as server:
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# server.ehlo()
|
|
||||||
# server.starttls(context=context)
|
|
||||||
# server.ehlo()
|
|
||||||
# server.login(settings.SMTP_USERNAME, settings.SMTP_PASSWORD)
|
|
||||||
#
|
|
||||||
# message = """\
|
|
||||||
#Subject: {}
|
|
||||||
#{}
|
|
||||||
# """.format(title, body)
|
|
||||||
# server.sendmail(settings.SMTP_EMAIL, dest, message)
|
|
||||||
# logger.info("EMAIL_SENT to=%s subject=%s", dest, title)
|
|
||||||
#
|
|
||||||
# except Exception as e:
|
|
||||||
# logger.exception("EMAIL_SEND_FAILED to=%s subject=%s error=%s", dest, title, str(e))
|
|
||||||
# return (False, e)
|
|
||||||
#
|
|
||||||
# return (True,)
|
|
||||||
|
|
||||||
def send_email(dest: str, title: str, body: str):
|
def send_email(dest: str, title: str, body: str):
|
||||||
try:
|
try:
|
||||||
@@ -40,7 +16,7 @@ def send_email(dest: str, title: str, body: str):
|
|||||||
)
|
)
|
||||||
|
|
||||||
logger.info("EMAIL_SENT to=%s subject=%s", dest, title)
|
logger.info("EMAIL_SENT to=%s subject=%s", dest, title)
|
||||||
return (True,)
|
return (True, None)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception("EMAIL_SEND_FAILED to=%s subject=%s error=%s", dest, title, str(e))
|
logger.exception("EMAIL_SEND_FAILED to=%s subject=%s error=%s", dest, title, str(e))
|
||||||
return (False, e)
|
return (False, e)
|
||||||
@@ -57,7 +33,7 @@ def send_hemail(dest: str, title: str, body: str, nbody: str):
|
|||||||
)
|
)
|
||||||
|
|
||||||
logger.info("EMAIL_SENT to=%s subject=%s", dest, title)
|
logger.info("EMAIL_SENT to=%s subject=%s", dest, title)
|
||||||
return (True,)
|
return (True, None)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception("EMAIL_SEND_FAILED to=%s subject=%s error=%s", dest, title, str(e))
|
logger.exception("EMAIL_SEND_FAILED to=%s subject=%s error=%s", dest, title, str(e))
|
||||||
return (False, e)
|
return (False, e)
|
||||||
@@ -333,6 +333,15 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/d5/9d/9a0ba39f33574994e5b33aea55a68e8fad72b8dd923a82300e4e91774f59/django_compressor-4.6.0-py3-none-any.whl", hash = "sha256:6e7b21020a0d86272c5e37000c33accc4ebeb77394a3dd86d775a09aae7aade4", size = 96828, upload-time = "2025-11-10T13:12:10.001Z" },
|
{ url = "https://files.pythonhosted.org/packages/d5/9d/9a0ba39f33574994e5b33aea55a68e8fad72b8dd923a82300e4e91774f59/django_compressor-4.6.0-py3-none-any.whl", hash = "sha256:6e7b21020a0d86272c5e37000c33accc4ebeb77394a3dd86d775a09aae7aade4", size = 96828, upload-time = "2025-11-10T13:12:10.001Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "django-environ"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/18/3c/60983e6ec9b24a8d8588eecebfd21123cba980bce0a905807a27692f0860/django_environ-0.13.0.tar.gz", hash = "sha256:6c401e4c219442c2c4588c2116d5292b5484a6f69163ed09cd41f3943bfb645f", size = 63529, upload-time = "2026-02-18T01:08:08.791Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c4/00/3767393ece946084e1c6830a33ffb8e39d68642e27ad5ac7d4c8bd5de866/django_environ-0.13.0-py3-none-any.whl", hash = "sha256:37799d14cd78222c6fd8298e48bfe17965ff8e586091ad66a463e52e0e7b799e", size = 20682, upload-time = "2026-02-18T01:08:07.359Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "django-ninja"
|
name = "django-ninja"
|
||||||
version = "1.6.2"
|
version = "1.6.2"
|
||||||
@@ -536,6 +545,7 @@ dependencies = [
|
|||||||
{ name = "celery" },
|
{ name = "celery" },
|
||||||
{ name = "django" },
|
{ name = "django" },
|
||||||
{ name = "django-compressor" },
|
{ name = "django-compressor" },
|
||||||
|
{ name = "django-environ" },
|
||||||
{ name = "django-ninja" },
|
{ name = "django-ninja" },
|
||||||
{ name = "django-redis" },
|
{ name = "django-redis" },
|
||||||
{ name = "django-storages", extra = ["s3"] },
|
{ name = "django-storages", extra = ["s3"] },
|
||||||
@@ -554,6 +564,7 @@ requires-dist = [
|
|||||||
{ name = "celery", specifier = "==5.6.3" },
|
{ name = "celery", specifier = "==5.6.3" },
|
||||||
{ name = "django", specifier = "==6.0.5" },
|
{ name = "django", specifier = "==6.0.5" },
|
||||||
{ name = "django-compressor", specifier = "==4.6.0" },
|
{ name = "django-compressor", specifier = "==4.6.0" },
|
||||||
|
{ name = "django-environ", specifier = ">=0.13.0" },
|
||||||
{ name = "django-ninja", specifier = ">=1.6.2" },
|
{ name = "django-ninja", specifier = ">=1.6.2" },
|
||||||
{ name = "django-redis", specifier = "==6.0.0" },
|
{ name = "django-redis", specifier = "==6.0.0" },
|
||||||
{ name = "django-storages", extras = ["s3"], specifier = "==1.14.6" },
|
{ name = "django-storages", extras = ["s3"], specifier = "==1.14.6" },
|
||||||
|
|||||||
Reference in New Issue
Block a user