refactor: reorganizar estructura del proyecto al estándar app/api_config
All checks were successful
DEPLOY_MULTI_BRACH/pipeline/head This commit looks good
All checks were successful
DEPLOY_MULTI_BRACH/pipeline/head This commit looks good
- core/ → app/api_config/ - apps/backend_admin/ → app/backend_admin/ - apps/common/ → app/common/ - apps/promociones/ → app/promociones/ - manage.py → app/manage.py - Añadir app/requirements.txt - Actualizar todos los imports y referencias (DJANGO_SETTINGS_MODULE, ROOT_URLCONF, WSGI_APPLICATION, INSTALLED_APPS) - Actualizar Dockerfile con nuevo WORKDIR Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
64
app/promociones/acciones.py
Normal file
64
app/promociones/acciones.py
Normal file
@@ -0,0 +1,64 @@
|
||||
from django.db import connection
|
||||
from common.utils import clean_sql_string, clean_sql_int
|
||||
|
||||
|
||||
def get_status_action():
|
||||
"""
|
||||
Acción simple para comprobar que la lógica de la API responde.
|
||||
"""
|
||||
return {"status": "ok", "message": "API is running"}
|
||||
|
||||
def getData(params):
|
||||
"""
|
||||
Función estándar para obtener datos de promociones.
|
||||
"""
|
||||
# 1. Definimos la query con placeholders (%s)
|
||||
query = """
|
||||
SELECT id, nombre, fecha_inicio, descripcion
|
||||
FROM promociones
|
||||
WHERE id = %s AND activo = %s
|
||||
"""
|
||||
|
||||
|
||||
# 2. Preparamos el diccionario de parámetros (tu estándar get_parameterized)
|
||||
# Limpiamos los datos antes de enviarlos a la base de datos
|
||||
id_promocion = clean_sql_int(params.get('id'))
|
||||
is_active = True if params.get('activo') else False
|
||||
|
||||
parameter_dict = [id_promocion, is_active]
|
||||
|
||||
# 3. Ejecución parametrizada
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute(query, parameter_dict)
|
||||
columns = [col[0] for col in cursor.description]
|
||||
result = [dict(zip(columns, row)) for row in cursor.fetchall()]
|
||||
|
||||
return result
|
||||
|
||||
def setData(params):
|
||||
"""
|
||||
Función estándar para insertar o actualizar (set_parameterized).
|
||||
"""
|
||||
# Ejemplo de UPDATE con JOIN (como solicitaste en tu estándar)
|
||||
query = """
|
||||
UPDATE promociones p
|
||||
SET p.nombre = %s, p.fecha_modificacion = %s
|
||||
FROM categorias c
|
||||
WHERE p.categoria_id = c.id AND p.id = %s
|
||||
"""
|
||||
|
||||
# Fecha en formato YYYY-MM-DD para SQL
|
||||
import datetime
|
||||
fecha_hoy = datetime.datetime.now().strftime('%Y-%m-%d')
|
||||
|
||||
parameter_dict = [
|
||||
clean_sql_string(params.get('nombre')),
|
||||
fecha_hoy,
|
||||
clean_sql_int(params.get('id'))
|
||||
]
|
||||
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute(query, parameter_dict)
|
||||
affected_rows = cursor.rowcount
|
||||
|
||||
return affected_rows
|
||||
5
app/promociones/apps.py
Normal file
5
app/promociones/apps.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
class PromocionesConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'promociones'
|
||||
35
app/promociones/fixtures/semillas.json
Normal file
35
app/promociones/fixtures/semillas.json
Normal file
@@ -0,0 +1,35 @@
|
||||
[
|
||||
{
|
||||
"model": "promociones.promocion",
|
||||
"pk": 1,
|
||||
"fields": {
|
||||
"nombre": "Oferta de Bienvenida",
|
||||
"fecha_inicio": "2026-04-01",
|
||||
"descripcion": "Descuento para nuevos usuarios",
|
||||
"activo": true,
|
||||
"categoria_id": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "promociones.promocion",
|
||||
"pk": 2,
|
||||
"fields": {
|
||||
"nombre": "Promo Primavera",
|
||||
"fecha_inicio": "2026-05-01",
|
||||
"descripcion": "Todo al 20% de descuento",
|
||||
"activo": true,
|
||||
"categoria_id": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "promociones.promocion",
|
||||
"pk": 3,
|
||||
"fields": {
|
||||
"nombre": "Liquidación Stock",
|
||||
"fecha_inicio": "2026-04-14",
|
||||
"descripcion": "Últimas unidades",
|
||||
"activo": false,
|
||||
"categoria_id": 1
|
||||
}
|
||||
}
|
||||
]
|
||||
1
app/promociones/migrations/__init__.py
Normal file
1
app/promociones/migrations/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# Archivo para marcar esta carpeta como paquete de migraciones
|
||||
20
app/promociones/models.py
Normal file
20
app/promociones/models.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from django.db import models
|
||||
|
||||
class Promocion(models.Model):
|
||||
# Campos detectados en tu getData y setData
|
||||
nombre = models.CharField(max_length=255)
|
||||
fecha_inicio = models.DateField(null=True, blank=True)
|
||||
fecha_modificacion = models.DateField(null=True, blank=True)
|
||||
descripcion = models.TextField(null=True, blank=True)
|
||||
activo = models.BooleanField(default=True)
|
||||
|
||||
# Campo usado en tu JOIN de setData
|
||||
categoria_id = models.IntegerField(null=True, blank=True)
|
||||
|
||||
class Meta:
|
||||
# ¡CRÍTICO! Esto le dice a Django que la tabla se llame exactamente
|
||||
# como la has escrito en tu SQL manual
|
||||
db_table = 'promociones'
|
||||
|
||||
def __str__(self):
|
||||
return self.nombre
|
||||
6
app/promociones/urls.py
Normal file
6
app/promociones/urls.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.urls import path
|
||||
from .views import PromocionObtener
|
||||
|
||||
urlpatterns = [
|
||||
path('obtener/', PromocionObtener.as_view(), name='obtener_promocion'),
|
||||
]
|
||||
46
app/promociones/views.py
Normal file
46
app/promociones/views.py
Normal file
@@ -0,0 +1,46 @@
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework_simplejwt.authentication import JWTAuthentication
|
||||
from django.http import JsonResponse
|
||||
from backend_admin.models import Log
|
||||
from .acciones import getData
|
||||
from django.utils import timezone # Esta es la forma correcta
|
||||
class PromocionObtener(APIView):
|
||||
authentication_classes = [JWTAuthentication]
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request):
|
||||
# --- BLOQUE 1: LOG INITIATION ---
|
||||
log_entry = Log.objects.create(
|
||||
user=request.user.username,
|
||||
path='promociones/obtener/',
|
||||
method='POST',
|
||||
status_code='0'
|
||||
)
|
||||
|
||||
print('llega a despues de log entry')
|
||||
|
||||
try:
|
||||
# --- BLOQUE 2: DATA CLEANING ---
|
||||
data = request.data
|
||||
log_entry.request = data
|
||||
log_entry.save()
|
||||
|
||||
params = {'id': data.get('id'), 'activo': data.get('activo')}
|
||||
|
||||
# --- BLOQUE 3: ACTION CALL ---
|
||||
# Aquí ya recibimos las fechas como strings gracias al paso 1
|
||||
resultado = getData(params)
|
||||
|
||||
# --- BLOQUE 4: LOG CLOSURE & RESPONSE ---
|
||||
log_entry.response = {"count": len(resultado)} # No guardes todo el JSON si es muy grande
|
||||
log_entry.status_code = '200'
|
||||
log_entry.save()
|
||||
|
||||
return JsonResponse(resultado, safe=False, status=200)
|
||||
|
||||
except Exception as e:
|
||||
log_entry.status_code = '500'
|
||||
log_entry.response = {'error': str(e)}
|
||||
log_entry.save()
|
||||
return JsonResponse({'error': str(e)}, status=500)
|
||||
Reference in New Issue
Block a user