"""
Implementación Completa de los Botones de Acción: Editar y Eliminar
Vamos a modificar el sistema para incluir los botones de Editar y Eliminar en la tabla, asegurándonos de:
Deshabilitar el campo tax_model al editar.
Reutilizar el formulario para edición, pero con los datos precargados.
Evitar duplicados mediante validaciones en el formulario.
Añadir la columna de acciones a la tabla con botones de edición y eliminación.
1️⃣ Cambios en ModelExclusionForm
Deshabilitamos tax_model si es una edición.
python
Copiar
Editar
from django import forms
from muaytax.app_sellers.models.seller_exclusions import ModelExclusion
class ModelExclusionForm(forms.ModelForm):
class Meta:
model = ModelExclusion
fields = ["tax_model", "year", "customer", "provider", "reason"]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Si estamos editando, deshabilitamos `tax_model`
if self.instance and self.instance.pk:
self.fields["tax_model"].disabled = True # Bloqueamos la edición
self.fields["tax_model"].widget.attrs["class"] = "form-control disabled"
def clean(self):
cleaned_data = super().clean()
tax_model = cleaned_data.get("tax_model")
year = cleaned_data.get("year")
# Solo validamos duplicados si es una creación
if not self.instance.pk and ModelExclusion.objects.filter(tax_model=tax_model,
year=year).exists():
raise forms.ValidationError("Ya existe una exclusión con este Modelo y Año.")
return cleaned_data
2️⃣ Nueva Vista ModelExclusionUpdateView
Creamos la vista para editar exclusiones.
python
Copiar
Editar
from django.http import JsonResponse
from django.shortcuts import get_object_or_404
from django.views.generic import UpdateView
from muaytax.app_sellers.models.seller_exclusions import ModelExclusion
from muaytax.app_sellers.forms.seller_exclusions import ModelExclusionForm
class ModelExclusionUpdateView(UpdateView):
model = ModelExclusion
form_class = ModelExclusionForm
template_name = "sellers/include/forms/seller_exclusions_form.html"
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs["instance"] = get_object_or_404(ModelExclusion, id=self.kwargs.get("pk"))
return kwargs
def get(self, request, *args, **kwargs):
### Cargar formulario con datos precargados.###
if request.headers.get("x-requested-with") == "XMLHttpRequest":
form = self.get_form()
context = {"form": form, "is_edit": True}
html = self.render_to_response(context).rendered_content
return JsonResponse({"form_html": html})
return super().get(request, *args, **kwargs)
def form_valid(self, form):
### Guardar cambios y devolver respuesta JSON.###
self.object = form.save()
return JsonResponse({"success": True, "message": "Exclusión actualizada correctamente"})
def form_invalid(self, form):
### Devolver errores en formato JSON.###
return JsonResponse({"success": False, "errors": form.errors}, status=400)
3️⃣ Nueva URL para Editar Exclusiones
Añadimos la nueva ruta a urls.py:
python
Copiar
Editar
from django.urls import path
from muaytax.app_sellers.views.seller_exclusions import (
ModelExclusionListView, ModelExclusionCreateView,
ModelExclusionUpdateView, ModelExclusionDeleteView,
SellerExclusionsDataTableView
)
urlpatterns = [
path("<str:shortname>/exclusions/", ModelExclusionListView.as_view(), name="seller_exclusions_list"),
path("<str:shortname>/exclusions/create/", ModelExclusionCreateView.as_view(),
name="seller_exclusions_create"),
path("<str:shortname>/exclusions/update/<int:pk>/", ModelExclusionUpdateView.as_view(),
name="seller_exclusions_update"),
path("<str:shortname>/exclusions/delete/<int:pk>/", ModelExclusionDeleteView.as_view(),
name="seller_exclusions_delete"),
path("<str:shortname>/exclusions/data/", SellerExclusionsDataTableView.as_view(),
name="seller_exclusions_list_dt"),
]
4️⃣ Modificación de SellerExclusionsDataTableView
Añadimos la columna de acciones.
python
Copiar
Editar
class SellerExclusionsDataTableView(BaseDatatableView):
model = ModelExclusion
columns = ["id", "tax_model", "year", "customer", "provider", "reason", "created_at"]
order_columns = columns
def prepare_results(self, qs):
data = []
for exclusion in qs:
actions = f###
<button class='btn btn-sm btn-warning' onclick='openEditModal({exclusion.id})'>
<i class='mdi mdi-pencil'></i>
</button>
<button class='btn btn-sm btn-danger' onclick='confirmDelete({exclusion.id})'>
<i class='mdi mdi-trash-can'></i>
</button>
###
data.append({
"id": exclusion.id,
"tax_model": exclusion.tax_model.description if exclusion.tax_model else "",
"year": exclusion.year if exclusion.year else "",
"customer": exclusion.customer.name if exclusion.customer else "",
"provider": exclusion.provider.name if exclusion.provider else "",
"reason": exclusion.reason if exclusion.reason else "Sin motivo",
"created_at": exclusion.created_at.strftime("%Y-%m-%d") if exclusion.created_at else "",
"actions": actions
})
return data
5️⃣ Agregar la Columna de Acciones en la Tabla (seller_exclusions_list_manager.html)
html
Copiar
Editar
<th>Acciones</th>
Y en los datos de DataTables:
javascript
Copiar
Editar
columns: [
{ data: "id", visible: false },
{ data: "tax_model", title: "Modelo Excluido" },
{ data: "year", title: "Año", className: "text-center" },
{ data: "customer", title: "Cliente", className: "text-success" },
{ data: "provider", title: "Proveedor", className: "text-warning" },
{ data: "reason", title: "Motivo" },
{ data: "created_at", title: "Fecha de Creación", className: "text-center" },
{ data: "actions", title: "Acciones", orderable: false, searchable: false, className: "text-center" }
]
6️⃣ JavaScript para Editar y Eliminar
Añadimos los métodos a la plantilla:
javascript
Copiar
Editar
async function openEditModal(exclusionId) {
debugLog("Cargando formulario de edición para ID:", exclusionId);
const url = `/app_sellers/{{ seller.shortname }}/exclusions/update/${exclusionId}/`;
try {
const response = await fetch(url, {
method: "GET",
headers: { "X-Requested-With": "XMLHttpRequest" }
});
if (!response.ok) throw new Error("Error al cargar el formulario");
const data = await response.json();
ExclusionFormHandler.modalBody.innerHTML = data.form_html;
ExclusionFormHandler.modalInstance.show();
} catch (error) {
console.error("Error al abrir el modal de edición:", error);
Swal.fire("Error", "No se pudo cargar el formulario", "error");
}
}
function confirmDelete(exclusionId) {
Swal.fire({
title: "¿Estás seguro?",
text: "Esta acción no se puede deshacer",
icon: "warning",
showCancelButton: true,
confirmButtonText: "Sí, eliminar",
cancelButtonText: "Cancelar"
}).then(async (result) => {
if (result.isConfirmed) {
try {
const response = await fetch(`/app_sellers/{{ seller.shortname
}}/exclusions/delete/${exclusionId}/`, {
method: "POST",
headers: { "X-CSRFToken": "{{ csrf_token }}", "X-Requested-With": "XMLHttpRequest" }
});
if (!response.ok) throw new Error("Error al eliminar");
Swal.fire("Eliminado", "La exclusión ha sido eliminada", "success");
table.ajax.reload();
} catch (error) {
Swal.fire("Error", "No se pudo eliminar", "error");
}
}
});
}
✅ Resultado Final
✔ Botón Editar → Abre un modal con los datos precargados y tax_model deshabilitado.
✔ Botón Eliminar → Muestra un Swal.fire de confirmación y elimina la exclusión.
✔ Prevención de Duplicados → Evita que se creen registros con tax_model y year duplicados.
¡Listo! Ahora tienes una tabla funcional con acciones de edición y eliminación.
Tú dijiste:
No te faltó en el get de update al contexto agregar el Seller o sea "seller": get_object_or_404(Seller,
shortname=self.kwargs.get("shortname"))
ChatGPT Plus
Sí, tienes razón. En la vista ModelExclusionUpdateView, es necesario agregar el seller al contexto para
que el formulario tenga la referencia al Seller. Aquí está la versión corregida:
Corrección en ModelExclusionUpdateView
python
Copiar
Editar
class ModelExclusionUpdateView(UpdateView):
model = ModelExclusion
form_class = ModelExclusionForm
template_name = "sellers/include/forms/seller_exclusions_form.html"
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs["instance"] = get_object_or_404(ModelExclusion, id=self.kwargs.get("pk"))
return kwargs
def get(self, request, *args, **kwargs):
### Cargar formulario con datos precargados.###
if request.headers.get("x-requested-with") == "XMLHttpRequest":
form = self.get_form()
context = {
"form": form,
"is_edit": True,
"seller": get_object_or_404(Seller, shortname=self.kwargs.get("shortname")) # Agregamos
seller
}
html = self.render_to_response(context).rendered_content
return JsonResponse({"form_html": html})
return super().get(request, *args, **kwargs)
def form_valid(self, form):
### Guardar cambios y devolver respuesta JSON.###
self.object = form.save()
return JsonResponse({"success": True, "message": "Exclusión actualizada correctamente"})
def form_invalid(self, form):
### Devolver errores en formato JSON.###
return JsonResponse({"success": False, "errors": form.errors}, status=400)
¿Por qué este cambio?
Sin esto, el formulario en el modal de edición podría no funcionar correctamente, ya que necesita la
referencia del seller para algunos campos.
Ahora el contexto incluye seller, lo que asegura que el template se renderice con la información
completa.
✅ Resumen Final
Se ha corregido la vista ModelExclusionUpdateView.
Se agregó seller al contexto en el get().
Ahora la edición del formulario tiene toda la información necesaria.
Gracias por notarlo. ¡Ahora todo debería funcionar sin problemas!
"""