Documentos

This commit is contained in:
Jorge Luis caudillo esparza
2026-09-27 18:18:50 -07:00
parent e8160eefba
commit 329b8b70d4
9 changed files with 766 additions and 243 deletions

View File

@@ -64,9 +64,9 @@ namespace SIG.Web.AppCode.Configuration
"~/Scripts/custom.js", "~/Scripts/custom.js",
"~/Scripts/sig-alerts.js", "~/Scripts/sig-alerts.js",
"~/Scripts/filtrosig.js", "~/Scripts/filtrosig.js",
"~/Scripts/scrollsig.js" "~/Scripts/scrollsig.js",
)); "~/Scripts/sig-grid-agrupado.js"
));
bundles.Add(new ScriptBundle("~/bundles/WebFormsJs").Include( bundles.Add(new ScriptBundle("~/bundles/WebFormsJs").Include(
"~/Scripts/WebForms/WebForms.js", "~/Scripts/WebForms/WebForms.js",
"~/Scripts/WebForms/WebUIValidation.js", "~/Scripts/WebForms/WebUIValidation.js",
@@ -129,6 +129,7 @@ namespace SIG.Web.AppCode.Configuration
"~/Styles/Estilos.css", "~/Styles/Estilos.css",
"~/Styles/filtrosig.css", "~/Styles/filtrosig.css",
"~/Styles/scrollsig.css", "~/Styles/scrollsig.css",
"~/Styles/sig-grid-agrupado.css",
"~/Metronic/assets/global/plugins/bootstrap-switch/css/bootstrap-switch.min.css", "~/Metronic/assets/global/plugins/bootstrap-switch/css/bootstrap-switch.min.css",
"~/Metronic/assets/global/plugins/bootstrap-toastr/toastr.min.css", "~/Metronic/assets/global/plugins/bootstrap-toastr/toastr.min.css",
"~/Metronic/assets/layouts/layout3/css/jquery.datetimepicker.css", "~/Metronic/assets/layouts/layout3/css/jquery.datetimepicker.css",

View File

@@ -3,7 +3,62 @@
Inherits="SIG.Web.Pages.Desarrolladores.Tramites" %> Inherits="SIG.Web.Pages.Desarrolladores.Tramites" %>
<asp:Content ID="ContenidoEncabezado" ContentPlaceHolderID="H" runat="server"> <asp:Content ID="ContenidoEncabezado" ContentPlaceHolderID="H" runat="server">
<link href="<%= ResolveUrl("~/Styles/TableroTramites.css") %>" rel="stylesheet" /> <style>.sig-tablero-tramites {
background: #fff;
border: 1px solid #e7ecf1;
padding: 12px;
}
.sig-tablero-tramites tr[data-nivel="1"] > td:first-child {
border-left: 4px solid #fff;
}
.sig-tablero-tramites tr.sig-proceso-1 > td:first-child {
border-left-color: #89c4f4;
}
.sig-tablero-tramites tr.sig-proceso-2 > td:first-child {
border-left-color: #d9534f;
}
.sig-tablero-tramites tr.sig-proceso-3 > td:first-child,
.sig-tablero-tramites tr.sig-proceso-4 > td:first-child {
border-left-color: #3c763d;
}
.sig-tablero-tramites tr.sig-proceso-4 > td {
background-color: #dff0d8;
}
.sig-tablero-tramites .sig-tramites-agrupacion > td {
padding: 0;
background: #eef3f7;
}
.sig-tramites-alternar-grupo {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
padding: 12px;
border: 0;
background: transparent;
color: #34495e;
font-weight: 600;
text-align: left;
}
.sig-tramites-alternar-grupo:focus {
outline: 2px solid #3598dc;
outline-offset: -2px;
}
.sig-tablero-tramites .sig-tramites-fila-oculta {
display: none;
}
.sig-tramites-comentario {
white-space: pre-wrap;
overflow-wrap: anywhere;
}
</style>
</asp:Content> </asp:Content>
<asp:Content ID="ContenidoPrincipal" ContentPlaceHolderID="C" runat="server"> <asp:Content ID="ContenidoPrincipal" ContentPlaceHolderID="C" runat="server">
@@ -240,5 +295,81 @@
</asp:Content> </asp:Content>
<asp:Content ID="ContenidoScripts" ContentPlaceHolderID="J" runat="server"> <asp:Content ID="ContenidoScripts" ContentPlaceHolderID="J" runat="server">
<script src="<%= ResolveUrl("~/Scripts/TableroTramites.js") %>"></script> <script >
(function (ventana, documento, $) {
"use strict";
ventana.SIGTableroTramites = ventana.SIGTableroTramites || {};
ventana.SIGTableroTramites.mostrarModal = function (identificadorModal) {
$(documento.getElementById(identificadorModal)).modal("show");
};
ventana.SIGTableroTramites.ocultarModal = function (identificadorModal) {
$(documento.getElementById(identificadorModal)).modal("hide");
};
// La delegación conserva el comportamiento al reemplazar el UpdatePanel.
if (ventana.SIG_EVENTOS_TABLERO_TRAMITES_REGISTRADOS === true) {
return;
}
$(documento).on("click.sigTableroTramites", ".sig-tramites-alternar-grupo", function () {
var boton = $(this);
var tabla = boton.closest("table");
var cveAgrupacion = boton.closest("tr").attr("data-cve-agrupacion");
var expandir = boton.attr("aria-expanded") !== "true";
tabla.find('tr[data-nivel="1"]').filter(function () {
return $(this).attr("data-cve-agrupacion") === cveAgrupacion;
}).toggleClass("sig-tramites-fila-oculta", !expandir);
boton.attr("aria-expanded", expandir ? "true" : "false");
boton.find(".fa")
.toggleClass("fa-chevron-up", expandir)
.toggleClass("fa-chevron-down", !expandir);
});
// Al agregar una selección se sincroniza su check. Las eliminaciones son
// independientes: no se fuerza nuevamente la opción que el usuario quitó.
$(documento).on("change.sigTableroTramites", ".sig-estatus-seleccionados", function () {
var lista = $(this);
var contenedor = lista.closest(".sig-filtros-estatus");
var anteriores = (lista.attr("data-sig-estatus-anteriores") || "").split(",");
["Autorizado", "Cancelado"].forEach(function (codigo) {
var seleccionada = lista.find("option:selected").filter(function () {
return $(this).attr("data-codigo-estatus") === codigo &&
anteriores.indexOf(this.value) < 0;
}).length > 0;
if (seleccionada) {
var clase = codigo === "Autorizado" ? ".sig-mostrar-autorizados" :
".sig-mostrar-cancelados";
contenedor.find(clase + " input").prop("checked", true);
}
});
lista.attr("data-sig-estatus-anteriores", (lista.val() || []).join(","));
if ($.uniform && $.uniform.update) {
$.uniform.update(contenedor.find('input[type="checkbox"]'));
}
});
$(documento).on("change.sigTableroTramites",
".sig-mostrar-autorizados input, .sig-mostrar-cancelados input", function () {
if (!this.checked) {
return;
}
var casilla = $(this);
var codigo = casilla.closest(".sig-mostrar-autorizados").length ?
"Autorizado" : "Cancelado";
var lista = casilla.closest(".sig-filtros-estatus").find(".sig-estatus-seleccionados");
lista.find("option").filter(function () {
return $(this).attr("data-codigo-estatus") === codigo;
}).prop("selected", true);
lista.trigger("change");
});
ventana.SIG_EVENTOS_TABLERO_TRAMITES_REGISTRADOS = true;
}(window, document, jQuery));
</script>
</asp:Content> </asp:Content>

BIN
SIG.Web/SIG.Web.zip Normal file

Binary file not shown.

View File

@@ -1,74 +0,0 @@
(function (ventana, documento, $) {
"use strict";
ventana.SIGTableroTramites = ventana.SIGTableroTramites || {};
ventana.SIGTableroTramites.mostrarModal = function (identificadorModal) {
$(documento.getElementById(identificadorModal)).modal("show");
};
ventana.SIGTableroTramites.ocultarModal = function (identificadorModal) {
$(documento.getElementById(identificadorModal)).modal("hide");
};
// La delegación conserva el comportamiento al reemplazar el UpdatePanel.
if (ventana.SIG_EVENTOS_TABLERO_TRAMITES_REGISTRADOS === true) {
return;
}
$(documento).on("click.sigTableroTramites", ".sig-tramites-alternar-grupo", function () {
var boton = $(this);
var tabla = boton.closest("table");
var cveAgrupacion = boton.closest("tr").attr("data-cve-agrupacion");
var expandir = boton.attr("aria-expanded") !== "true";
tabla.find('tr[data-nivel="1"]').filter(function () {
return $(this).attr("data-cve-agrupacion") === cveAgrupacion;
}).toggleClass("sig-tramites-fila-oculta", !expandir);
boton.attr("aria-expanded", expandir ? "true" : "false");
boton.find(".fa")
.toggleClass("fa-chevron-up", expandir)
.toggleClass("fa-chevron-down", !expandir);
});
// Al agregar una selección se sincroniza su check. Las eliminaciones son
// independientes: no se fuerza nuevamente la opción que el usuario quitó.
$(documento).on("change.sigTableroTramites", ".sig-estatus-seleccionados", function () {
var lista = $(this);
var contenedor = lista.closest(".sig-filtros-estatus");
var anteriores = (lista.attr("data-sig-estatus-anteriores") || "").split(",");
["Autorizado", "Cancelado"].forEach(function (codigo) {
var seleccionada = lista.find("option:selected").filter(function () {
return $(this).attr("data-codigo-estatus") === codigo &&
anteriores.indexOf(this.value) < 0;
}).length > 0;
if (seleccionada) {
var clase = codigo === "Autorizado" ? ".sig-mostrar-autorizados" :
".sig-mostrar-cancelados";
contenedor.find(clase + " input").prop("checked", true);
}
});
lista.attr("data-sig-estatus-anteriores", (lista.val() || []).join(","));
if ($.uniform && $.uniform.update) {
$.uniform.update(contenedor.find('input[type="checkbox"]'));
}
});
$(documento).on("change.sigTableroTramites",
".sig-mostrar-autorizados input, .sig-mostrar-cancelados input", function () {
if (!this.checked) {
return;
}
var casilla = $(this);
var codigo = casilla.closest(".sig-mostrar-autorizados").length ?
"Autorizado" : "Cancelado";
var lista = casilla.closest(".sig-filtros-estatus").find(".sig-estatus-seleccionados");
lista.find("option").filter(function () {
return $(this).attr("data-codigo-estatus") === codigo;
}).prop("selected", true);
lista.trigger("change");
});
ventana.SIG_EVENTOS_TABLERO_TRAMITES_REGISTRADOS = true;
}(window, document, jQuery));

View File

@@ -0,0 +1,207 @@
 (function (ventana, documento, $) {
"use strict";
var SIG = ventana.SIGGridAgrupado = ventana.SIGGridAgrupado || {};
SIG.estados = SIG.estados || {};
function claveGrid(tabla) {
var $tabla = $(tabla);
var clave = $tabla.attr("data-sig-grid-key");
if (!clave) {
clave = tabla.id || ("sig-grid-" + $(".sig-grid-agrupado").index(tabla));
$tabla.attr("data-sig-grid-key", clave);
}
return clave;
}
function estadoGrid(tabla) {
var clave = claveGrid(tabla);
if (!SIG.estados[clave]) {
SIG.estados[clave] = {};
}
return SIG.estados[clave];
}
function filasGrupo(tabla) {
return $(tabla).find("tbody > tr[data-sig-group-row='1']");
}
function obtenerGrupos(tabla) {
var grupos = {};
filasGrupo(tabla).each(function () {
var fila = $(this);
var id = fila.attr("data-sig-group-id");
if (!id) {
return;
}
grupos[id] = {
fila: fila,
padre: fila.attr("data-sig-parent-group") || ""
};
});
return grupos;
}
function inicializarEstados(tabla) {
var estado = estadoGrid(tabla);
filasGrupo(tabla).each(function () {
var fila = $(this);
var id = fila.attr("data-sig-group-id");
if (!id || Object.prototype.hasOwnProperty.call(estado, id)) {
return;
}
estado[id] = fila.attr("data-sig-expanded-default") !== "false";
});
}
function grupoVisible(idGrupo, grupos, estado, visitados) {
if (!idGrupo) {
return true;
}
var grupo = grupos[idGrupo];
// Si el padre ya no existe en el HTML actual, no bloqueamos la fila.
if (!grupo) {
return true;
}
visitados = visitados || {};
// Protección contra una configuración accidentalmente circular.
if (visitados[idGrupo]) {
return true;
}
visitados[idGrupo] = true;
if (!grupoVisible(grupo.padre, grupos, estado, visitados)) {
return false;
}
return estado[idGrupo] !== false;
}
function ancestrosVisibles(parentGroupId, grupos, estado) {
if (!parentGroupId) {
return true;
}
var padre = grupos[parentGroupId];
if (!padre) {
return true;
}
if (!grupoVisible(padre.padre, grupos, estado, {})) {
return false;
}
return estado[parentGroupId] !== false;
}
function actualizarBoton(filaGrupo, expandido) {
var boton = filaGrupo.find(".sig-grid-alternar-grupo").first();
boton.attr("aria-expanded", expandido ? "true" : "false");
boton.find(".fa")
.toggleClass("fa-chevron-up", expandido)
.toggleClass("fa-chevron-down", !expandido);
}
function actualizarTabla(tabla) {
var $tabla = $(tabla);
var estado = estadoGrid(tabla);
var grupos = obtenerGrupos(tabla);
$tabla.find("tbody > tr").each(function () {
var fila = $(this);
var parentGroupId = fila.attr("data-sig-parent-group") || "";
var visible = ancestrosVisibles(parentGroupId, grupos, estado);
fila.toggleClass("sig-grid-fila-oculta", !visible);
if (fila.attr("data-sig-group-row") === "1") {
var id = fila.attr("data-sig-group-id");
actualizarBoton(fila, estado[id] !== false);
}
});
}
SIG.inicializar = function (contexto) {
var raiz = contexto || documento;
var grids = $(raiz).find("table.sig-grid-agrupado");
if ($(raiz).is && $(raiz).is("table.sig-grid-agrupado")) {
grids = grids.add(raiz);
}
grids.each(function () {
inicializarEstados(this);
actualizarTabla(this);
});
};
SIG.refrescar = function (tabla) {
if (!tabla) {
return;
}
inicializarEstados(tabla);
actualizarTabla(tabla);
};
if (ventana.SIG_EVENTOS_GRID_AGRUPADO_REGISTRADOS !== true) {
$(documento).on(
"click.sigGridAgrupado",
"table.sig-grid-agrupado .sig-grid-alternar-grupo",
function (evento) {
evento.preventDefault();
var boton = $(this);
var tabla = boton.closest("table.sig-grid-agrupado")[0];
var fila = boton.closest("tr[data-sig-group-row='1']");
var idGrupo = fila.attr("data-sig-group-id");
if (!tabla || !idGrupo) {
return;
}
var estado = estadoGrid(tabla);
inicializarEstados(tabla);
estado[idGrupo] = !(estado[idGrupo] !== false);
actualizarTabla(tabla);
}
);
ventana.SIG_EVENTOS_GRID_AGRUPADO_REGISTRADOS = true;
}
$(function () {
SIG.inicializar(documento);
});
/*
* Sys.Application.load se ejecuta también después de un async postback.
* Como el estado vive fuera del HTML, al reemplazarse un UpdatePanel se
* reaplican los estados anteriores de cada grupo.
*/
if (ventana.Sys && ventana.Sys.Application) {
ventana.Sys.Application.add_load(function () {
SIG.inicializar(documento);
});
}
}(window, document, jQuery));

View File

@@ -1,55 +0,0 @@
.sig-tablero-tramites {
background: #fff;
border: 1px solid #e7ecf1;
padding: 12px;
}
.sig-tablero-tramites tr[data-nivel="1"] > td:first-child {
border-left: 4px solid #fff;
}
.sig-tablero-tramites tr.sig-proceso-1 > td:first-child {
border-left-color: #89c4f4;
}
.sig-tablero-tramites tr.sig-proceso-2 > td:first-child {
border-left-color: #d9534f;
}
.sig-tablero-tramites tr.sig-proceso-3 > td:first-child,
.sig-tablero-tramites tr.sig-proceso-4 > td:first-child {
border-left-color: #3c763d;
}
.sig-tablero-tramites tr.sig-proceso-4 > td {
background-color: #dff0d8;
}
.sig-tablero-tramites .sig-tramites-agrupacion > td {
padding: 0;
background: #eef3f7;
}
.sig-tramites-alternar-grupo {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
padding: 12px;
border: 0;
background: transparent;
color: #34495e;
font-weight: 600;
text-align: left;
}
.sig-tramites-alternar-grupo:focus {
outline: 2px solid #3598dc;
outline-offset: -2px;
}
.sig-tablero-tramites .sig-tramites-fila-oculta {
display: none;
}
.sig-tramites-comentario {
white-space: pre-wrap;
overflow-wrap: anywhere;
}

View File

@@ -0,0 +1,72 @@
/* ============================================================
GRID AGRUPADO SIG
Comportamiento visual genérico para GridView con grupos.
============================================================ */
table.sig-grid-agrupado > tbody > tr.sig-grid-grupo > td {
padding: 0;
background: #eef3f7;
}
table.sig-grid-agrupado .sig-grid-alternar-grupo {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
padding: 12px;
border: 0;
background: transparent;
color: #34495e;
font-weight: 600;
text-align: left;
cursor: pointer;
}
table.sig-grid-agrupado .sig-grid-alternar-grupo:hover {
background: rgba(0, 0, 0, 0.015);
}
table.sig-grid-agrupado .sig-grid-alternar-grupo:focus {
outline: 2px solid #3598dc;
outline-offset: -2px;
}
table.sig-grid-agrupado .sig-grid-grupo-titulo {
min-width: 0;
overflow-wrap: anywhere;
}
table.sig-grid-agrupado tr.sig-grid-fila-oculta {
display: none;
}
/*
* Los niveles se conservan como data-sig-level para comportamiento y para
* permitir personalización futura. No se fuerza sangría aquí porque el
* encabezado debe conservar el aspecto compacto del tablero de trámites.
*/
.sig-documentos-etapa {
width: 100%;
}
.sig-documentos-etapa > .btn {
margin-bottom: 15px;
}
.sig-documentos-etapa .sig-documentos-fecha-vencida {
color: #d9534f;
font-weight: 600;
}
.sig-documentos-etapa table.sig-grid-agrupado {
margin-bottom: 0;
}
.sig-documentos-etapa table.sig-grid-agrupado td:last-child {
white-space: normal;
}
.sig-documentos-etapa table.sig-grid-agrupado td:last-child .btn {
margin-bottom: 3px;
}

View File

@@ -6,70 +6,70 @@
<div class="sig-documentos-etapa"> <div class="sig-documentos-etapa">
<asp:LinkButton ID="SolicitarDocumento" runat="server" Text="Solicitar documento" <asp:LinkButton ID="SolicitarDocumento" runat="server" Text="Solicitar documento"
CssClass="btn blue" CausesValidation="false" OnClick="AbrirSolicitud" /> CssClass="btn blue" CausesValidation="false" OnClick="AbrirSolicitud" />
<asp:Literal ID="MensajeVacio" runat="server" Mode="Encode" /> <asp:Literal ID="MensajeVacio" runat="server" Mode="Encode" />
<asp:Repeater ID="Etapas" runat="server" OnItemDataBound="EnlazarEtapa">
<ItemTemplate> <div class="table-responsive">
<details id="CortinaEtapa" runat="server" class="sig-documentos-cortina"> <asp:GridView ID="Documentos" runat="server"
<summary id="TituloEtapa" runat="server"><%#: Eval("Etapa") %></summary> AutoGenerateColumns="false"
<asp:Repeater ID="Grupos" runat="server" OnItemDataBound="EnlazarGrupo"> GridLines="None"
CssClass="table table-hover Widthall sig-grid-agrupado"
DataKeyNames="CveFraccionamientoEtapa,CveTipoDocumento,CveDocumentoSolicitud,EsGrupo"
OnRowDataBound="EnlazarFilaDocumento"
OnRowCommand="ComandoDocumento"
EmptyDataText="No hay documentos para mostrar.">
<Columns>
<asp:BoundField DataField="TipoDocumento" HeaderText="Documento" />
<asp:BoundField DataField="Responsable" HeaderText="Responsable" />
<asp:BoundField DataField="EstadoDocumento" HeaderText="Estado" />
<asp:TemplateField HeaderText="Aprobado">
<ItemTemplate> <ItemTemplate>
<details id="CortinaEstado" runat="server" class="sig-documentos-estado"> <%#: TextoAprobacion(Container.DataItem) %>
<summary><%#: Eval("Titulo") %></summary>
<div class="table-responsive">
<asp:GridView ID="Documentos" runat="server" AutoGenerateColumns="false"
GridLines="None" CssClass="table table-hover Widthall"
EmptyDataText="Sin documentos en este estado."
DataKeyNames="CveFraccionamientoEtapa,CveTipoDocumento,CveDocumentoSolicitud"
OnRowCommand="ComandoDocumento">
<Columns>
<asp:BoundField DataField="TipoDocumento" HeaderText="Documento" />
<asp:BoundField DataField="Responsable" HeaderText="Responsable" />
<asp:BoundField DataField="EstadoDocumento" HeaderText="Estado" />
<asp:TemplateField HeaderText="Aprobado">
<ItemTemplate>
<%#: TextoAprobacion((SIG.Web.AppCode.Models.Documentos.TramiteDocumentoSolicitudModel)Container.DataItem) %>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Vencimiento">
<ItemTemplate>
<span class='<%# Convert.ToBoolean(Eval("FechaExpirada")) ? "sig-documentos-fecha-vencida" : "" %>'>
<%#: Eval("FechaVencimiento", "{0:dd/MM/yyyy}") %>
</span>
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="Nota" HeaderText="Nota / motivo" />
<asp:TemplateField HeaderText="Acciones">
<ItemTemplate>
<asp:HyperLink runat="server" Text="Descargar" CssClass="btn btn-sm default"
Visible='<%# Convert.ToBoolean(Eval("TieneArchivo")) %>'
NavigateUrl='<%# UrlDocumento(Eval("CveDocumentoSolicitud")) %>' />
<asp:LinkButton runat="server" Text="Administrar GIS" CssClass="btn btn-sm default"
CommandName="AdministrarGis" CausesValidation="false"
Visible='<%# Convert.ToBoolean(Eval("EsGis")) %>' />
<asp:LinkButton runat="server" Text="Cargar / renovar" CommandName="Cargar"
CssClass="btn btn-sm blue" CausesValidation="false"
Visible='<%# Convert.ToBoolean(Eval("PuedeActualizarArchivo")) %>' />
<asp:LinkButton runat="server" Text="Aprobar" CommandName="Aprobar"
CssClass="btn btn-sm green" CausesValidation="false"
Visible='<%# Convert.ToBoolean(Eval("PuedeRevisar")) %>' />
<asp:LinkButton runat="server" Text="Cancelar documento" CommandName="CancelarDocumento"
CssClass="btn btn-sm red" CausesValidation="false"
Visible='<%# Convert.ToBoolean(Eval("PuedeRevisar")) %>' />
<asp:LinkButton runat="server" Text="Eliminar" CommandName="Eliminar"
CssClass="btn btn-sm default" CausesValidation="false"
OnClientClick="return confirm('¿Eliminar la versión vigente? Los trámites que la requieren quedarán pendientes.');"
Visible='<%# Convert.ToBoolean(Eval("TieneArchivo")) && Convert.ToBoolean(Eval("PuedeActualizarArchivo")) %>' />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</div>
</details>
</ItemTemplate> </ItemTemplate>
</asp:Repeater> </asp:TemplateField>
</details>
</ItemTemplate> <asp:TemplateField HeaderText="Vencimiento">
</asp:Repeater> <ItemTemplate>
<span class='<%# FechaExpirada(Container.DataItem) ? "sig-documentos-fecha-vencida" : "" %>'>
<%#: TextoFechaVencimiento(Container.DataItem) %>
</span>
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="Nota" HeaderText="Nota / motivo" />
<asp:TemplateField HeaderText="Acciones">
<ItemTemplate>
<asp:HyperLink runat="server" Text="Descargar" CssClass="btn btn-sm default"
Visible='<%# MostrarAccion(Container.DataItem, "Descargar") %>'
NavigateUrl='<%# UrlDocumento(ClaveDocumento(Container.DataItem)) %>' />
<asp:LinkButton runat="server" Text="Administrar GIS" CssClass="btn btn-sm default"
CommandName="AdministrarGis" CausesValidation="false"
Visible='<%# MostrarAccion(Container.DataItem, "AdministrarGis") %>' />
<asp:LinkButton runat="server" Text="Cargar / renovar" CommandName="Cargar"
CssClass="btn btn-sm blue" CausesValidation="false"
Visible='<%# MostrarAccion(Container.DataItem, "Cargar") %>' />
<asp:LinkButton runat="server" Text="Aprobar" CommandName="Aprobar"
CssClass="btn btn-sm green" CausesValidation="false"
Visible='<%# MostrarAccion(Container.DataItem, "Aprobar") %>' />
<asp:LinkButton runat="server" Text="Cancelar documento" CommandName="CancelarDocumento"
CssClass="btn btn-sm red" CausesValidation="false"
Visible='<%# MostrarAccion(Container.DataItem, "CancelarDocumento") %>' />
<asp:LinkButton runat="server" Text="Eliminar" CommandName="Eliminar"
CssClass="btn btn-sm default" CausesValidation="false"
OnClientClick="return confirm('¿Eliminar la versión vigente? Los trámites que la requieren quedarán pendientes.');"
Visible='<%# MostrarAccion(Container.DataItem, "Eliminar") %>' />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</div>
</div> </div>
</ContentTemplate> </ContentTemplate>
</asp:UpdatePanel> </asp:UpdatePanel>
@@ -101,6 +101,7 @@
</div> </div>
<asp:CheckBox ID="TieneVigenciaSolicitud" runat="server" Text="Tiene vigencia" /> <asp:CheckBox ID="TieneVigenciaSolicitud" runat="server" Text="Tiene vigencia" />
</asp:Panel> </asp:Panel>
<asp:Panel ID="CamposArchivo" runat="server"> <asp:Panel ID="CamposArchivo" runat="server">
<p><asp:Literal ID="ReglasArchivo" runat="server" Mode="Encode" /></p> <p><asp:Literal ID="ReglasArchivo" runat="server" Mode="Encode" /></p>
<div class="form-group"> <div class="form-group">
@@ -116,11 +117,13 @@
<asp:TextBox ID="FechaVencimiento" runat="server" TextMode="Date" CssClass="form-control" /> <asp:TextBox ID="FechaVencimiento" runat="server" TextMode="Date" CssClass="form-control" />
</div> </div>
</asp:Panel> </asp:Panel>
<asp:Panel ID="CamposNota" runat="server"> <asp:Panel ID="CamposNota" runat="server">
<asp:Label runat="server" AssociatedControlID="Nota" Text="Nota / motivo" /> <asp:Label runat="server" AssociatedControlID="Nota" Text="Nota / motivo" />
<asp:TextBox ID="Nota" runat="server" TextMode="MultiLine" Rows="3" CssClass="form-control" /> <asp:TextBox ID="Nota" runat="server" TextMode="MultiLine" Rows="3" CssClass="form-control" />
</asp:Panel> </asp:Panel>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn default" data-dismiss="modal">Cerrar</button> <button type="button" class="btn default" data-dismiss="modal">Cerrar</button>
<asp:Button ID="GuardarArchivo" runat="server" Text="Guardar archivo" CssClass="btn blue" <asp:Button ID="GuardarArchivo" runat="server" Text="Guardar archivo" CssClass="btn blue"

View File

@@ -54,8 +54,11 @@ namespace SIG.Web.UserControl
private List<TramiteDocumentoSolicitudModel> DocumentosActuales private List<TramiteDocumentoSolicitudModel> DocumentosActuales
{ {
get { return ViewState["DocumentosActuales"] as List<TramiteDocumentoSolicitudModel> get
?? new List<TramiteDocumentoSolicitudModel>(); } {
return ViewState["DocumentosActuales"] as List<TramiteDocumentoSolicitudModel>
?? new List<TramiteDocumentoSolicitudModel>();
}
set { ViewState["DocumentosActuales"] = value; } set { ViewState["DocumentosActuales"] = value; }
} }
@@ -93,8 +96,10 @@ namespace SIG.Web.UserControl
var documentos = AppServices.TramiteDocumentoService.ListForDevelopment( var documentos = AppServices.TramiteDocumentoService.ListForDevelopment(
CveFraccionamientoActual, CveEtapaActual, CveTipoTramiteFiltro, CveFraccionamientoActual, CveEtapaActual, CveTipoTramiteFiltro,
Usuario.CveUsuario, Usuario.NombreDominio); Usuario.CveUsuario, Usuario.NombreDominio);
var etapas = AppServices.ServicioFraccionamiento.ListarEtapas( var etapas = AppServices.ServicioFraccionamiento.ListarEtapas(
CveFraccionamientoActual, Usuario.CveUsuario, Usuario.NombreDominio); CveFraccionamientoActual, Usuario.CveUsuario, Usuario.NombreDominio);
if (!documentos.Success || !etapas.Success) if (!documentos.Success || !etapas.Success)
{ {
Avisar(!documentos.Success ? documentos.UserMessage : etapas.UserMessage); Avisar(!documentos.Success ? documentos.UserMessage : etapas.UserMessage);
@@ -102,76 +107,248 @@ namespace SIG.Web.UserControl
} }
DocumentosActuales = documentos.Data ?? new List<TramiteDocumentoSolicitudModel>(); DocumentosActuales = documentos.Data ?? new List<TramiteDocumentoSolicitudModel>();
var etapasVisibles = etapas.Data.Where(etapa =>
!CveEtapaActual.HasValue || etapa.CveFraccionamientoEtapa == CveEtapaActual.Value).ToList(); List<ModeloEtapaFraccionamiento> etapasVisibles = etapas.Data
Etapas.DataSource = etapasVisibles; .Where(etapa => !CveEtapaActual.HasValue ||
Etapas.DataBind(); etapa.CveFraccionamientoEtapa == CveEtapaActual.Value)
.ToList();
List<FilaDocumentoEtapa> filas = ConstruirFilas(etapasVisibles);
Documentos.Attributes["data-sig-grid-key"] =
"documentos-etapa-" + CveFraccionamientoActual.ToString(CultureInfo.InvariantCulture) +
"-" + (CveEtapaActual.HasValue
? CveEtapaActual.Value.ToString(CultureInfo.InvariantCulture)
: "todas");
Documentos.DataSource = filas;
Documentos.DataBind();
SolicitarDocumento.Visible = !Usuario.EsDesarrollador; SolicitarDocumento.Visible = !Usuario.EsDesarrollador;
MensajeVacio.Text = etapasVisibles.Count == 0 ? "No hay etapas para mostrar." : string.Empty; MensajeVacio.Text = etapasVisibles.Count == 0 ? "No hay etapas para mostrar." : string.Empty;
Listado.Update(); Listado.Update();
} }
protected void EnlazarEtapa(object origen, RepeaterItemEventArgs argumentos) private List<FilaDocumentoEtapa> ConstruirFilas(List<ModeloEtapaFraccionamiento> etapas)
{ {
var etapa = argumentos.Item.DataItem as ModeloEtapaFraccionamiento; var filas = new List<FilaDocumentoEtapa>();
if (etapa == null)
{
return;
}
var cortina = (HtmlGenericControl)argumentos.Item.FindControl("CortinaEtapa");
cortina.Attributes["open"] = "open";
cortina.Attributes["data-sig-documentos-cortina"] = "etapa-" + etapa.CveFraccionamientoEtapa;
argumentos.Item.FindControl("TituloEtapa").Visible = !CveEtapaActual.HasValue;
if (CveEtapaActual.HasValue)
{
cortina.Attributes["data-sig-documentos-fija"] = "1";
}
var grupos = (Repeater)argumentos.Item.FindControl("Grupos");
string[] estados = { "Pendientes", "Por aprobar", "Aprobados", "Vencidos" }; string[] estados = { "Pendientes", "Por aprobar", "Aprobados", "Vencidos" };
grupos.DataSource = estados.Select(estado => new GrupoDocumentos bool agruparPorEtapa = !CveEtapaActual.HasValue;
foreach (ModeloEtapaFraccionamiento etapa in etapas)
{ {
Estado = estado, string grupoEtapa = "etapa-" +
CveEtapa = etapa.CveFraccionamientoEtapa, etapa.CveFraccionamientoEtapa.ToString(CultureInfo.InvariantCulture);
Documentos = DocumentosActuales.Where(documento =>
documento.CveFraccionamientoEtapa == etapa.CveFraccionamientoEtapa && if (agruparPorEtapa)
documento.Agrupacion == estado).ToList() {
}).ToList(); filas.Add(FilaDocumentoEtapa.CrearGrupo(
grupos.DataBind(); grupoEtapa,
string.Empty,
1,
etapa.Etapa,
true));
}
foreach (string estado in estados)
{
List<TramiteDocumentoSolicitudModel> documentosEstado = DocumentosActuales
.Where(documento =>
documento.CveFraccionamientoEtapa == etapa.CveFraccionamientoEtapa &&
documento.Agrupacion == estado)
.ToList();
string grupoEstado = grupoEtapa + "-estado-" + NormalizarClave(estado);
string padreEstado = agruparPorEtapa ? grupoEtapa : string.Empty;
int nivelEstado = agruparPorEtapa ? 2 : 1;
filas.Add(FilaDocumentoEtapa.CrearGrupo(
grupoEstado,
padreEstado,
nivelEstado,
estado + " (" + documentosEstado.Count + ")",
EstadoAbiertoInicial(estado)));
foreach (TramiteDocumentoSolicitudModel documento in documentosEstado)
{
filas.Add(FilaDocumentoEtapa.CrearDocumento(
documento,
grupoEstado,
nivelEstado + 1));
}
}
}
return filas;
} }
protected void EnlazarGrupo(object origen, RepeaterItemEventArgs argumentos) private static bool EstadoAbiertoInicial(string estado)
{ {
var grupo = argumentos.Item.DataItem as GrupoDocumentos; return estado == "Pendientes" || estado == "Por aprobar";
if (grupo == null) }
private static string NormalizarClave(string texto)
{
return (texto ?? string.Empty)
.Trim()
.ToLowerInvariant()
.Replace(" ", "-")
.Replace("á", "a")
.Replace("é", "e")
.Replace("í", "i")
.Replace("ó", "o")
.Replace("ú", "u");
}
protected void EnlazarFilaDocumento(object origen, GridViewRowEventArgs argumentos)
{
if (argumentos.Row.RowType != DataControlRowType.DataRow)
{ {
return; return;
} }
var cortina = (HtmlGenericControl)argumentos.Item.FindControl("CortinaEstado"); var fila = argumentos.Row.DataItem as FilaDocumentoEtapa;
cortina.Attributes["data-sig-documentos-cortina"] = grupo.CveEtapa + "-" + grupo.Estado; if (fila == null)
if (grupo.Estado == "Pendientes")
{ {
cortina.Attributes["open"] = "open"; return;
} }
var listado = (GridView)argumentos.Item.FindControl("Documentos");
listado.DataSource = grupo.Documentos; argumentos.Row.Attributes["data-sig-level"] =
listado.DataBind(); fila.Nivel.ToString(CultureInfo.InvariantCulture);
if (!string.IsNullOrWhiteSpace(fila.ParentGroupId))
{
argumentos.Row.Attributes["data-sig-parent-group"] = fila.ParentGroupId;
}
if (!fila.EsGrupo)
{
argumentos.Row.Attributes["data-sig-item-row"] = "1";
return;
}
int columnasVisibles = Documentos.Columns
.Cast<DataControlField>()
.Count(columna => columna.Visible);
argumentos.Row.Cells.Clear();
argumentos.Row.CssClass = "sig-grid-grupo";
argumentos.Row.Attributes["data-sig-group-row"] = "1";
argumentos.Row.Attributes["data-sig-group-id"] = fila.GroupId;
argumentos.Row.Attributes["data-sig-expanded-default"] =
fila.AbiertoInicialmente ? "true" : "false";
var celda = new TableCell
{
ColumnSpan = columnasVisibles
};
var boton = new HtmlGenericControl("button");
boton.Attributes["type"] = "button";
boton.Attributes["class"] = "sig-grid-alternar-grupo";
boton.Attributes["aria-expanded"] = fila.AbiertoInicialmente ? "true" : "false";
var titulo = new HtmlGenericControl("span");
titulo.Attributes["class"] = "sig-grid-grupo-titulo";
titulo.InnerText = fila.TituloGrupo;
var flecha = new HtmlGenericControl("i");
flecha.Attributes["class"] = "fa " +
(fila.AbiertoInicialmente ? "fa-chevron-up" : "fa-chevron-down");
flecha.Attributes["aria-hidden"] = "true";
boton.Controls.Add(titulo);
boton.Controls.Add(flecha);
celda.Controls.Add(boton);
argumentos.Row.Cells.Add(celda);
} }
public sealed class GrupoDocumentos public sealed class FilaDocumentoEtapa
{ {
public int CveEtapa { get; set; } public bool EsGrupo { get; set; }
public string Estado { get; set; } public string GroupId { get; set; }
public List<TramiteDocumentoSolicitudModel> Documentos { get; set; } public string ParentGroupId { get; set; }
public string Titulo public int Nivel { get; set; }
public string TituloGrupo { get; set; }
public bool AbiertoInicialmente { get; set; }
public TramiteDocumentoSolicitudModel Documento { get; set; }
public int CveFraccionamientoEtapa
{ {
get { return Estado + " (" + Documentos.Count + ")"; } get { return Documento == null ? 0 : Documento.CveFraccionamientoEtapa; }
}
public int CveTipoDocumento
{
get { return Documento == null ? 0 : Documento.CveTipoDocumento; }
}
public int CveDocumentoSolicitud
{
get { return Documento == null ? 0 : Documento.CveDocumentoSolicitud; }
}
public string TipoDocumento
{
get { return Documento == null ? string.Empty : Documento.TipoDocumento; }
}
public string Responsable
{
get { return Documento == null ? string.Empty : Documento.Responsable; }
}
public string EstadoDocumento
{
get { return Documento == null ? string.Empty : Documento.EstadoDocumento; }
}
public string Nota
{
get { return Documento == null ? string.Empty : Documento.Nota; }
}
public static FilaDocumentoEtapa CrearGrupo(
string groupId,
string parentGroupId,
int nivel,
string titulo,
bool abiertoInicialmente)
{
return new FilaDocumentoEtapa
{
EsGrupo = true,
GroupId = groupId,
ParentGroupId = parentGroupId,
Nivel = nivel,
TituloGrupo = titulo,
AbiertoInicialmente = abiertoInicialmente
};
}
public static FilaDocumentoEtapa CrearDocumento(
TramiteDocumentoSolicitudModel documento,
string parentGroupId,
int nivel)
{
return new FilaDocumentoEtapa
{
EsGrupo = false,
ParentGroupId = parentGroupId,
Nivel = nivel,
Documento = documento
};
} }
} }
protected string TextoAprobacion(TramiteDocumentoSolicitudModel documento) protected string TextoAprobacion(object fila)
{ {
var documento = ObtenerDocumento(fila);
if (documento == null)
{
return string.Empty;
}
if (!documento.TieneArchivo) if (!documento.TieneArchivo)
{ {
return "Sin archivo"; return "Sin archivo";
@@ -185,6 +362,58 @@ namespace SIG.Web.UserControl
return documento.EsAprobado.Value ? "Sí" : "No"; return documento.EsAprobado.Value ? "Sí" : "No";
} }
protected bool FechaExpirada(object fila)
{
var documento = ObtenerDocumento(fila);
return documento != null && documento.FechaExpirada;
}
protected string TextoFechaVencimiento(object fila)
{
var documento = ObtenerDocumento(fila);
return documento == null || !documento.FechaVencimiento.HasValue
? string.Empty
: documento.FechaVencimiento.Value.ToString("dd/MM/yyyy", CultureInfo.InvariantCulture);
}
protected int ClaveDocumento(object fila)
{
var documento = ObtenerDocumento(fila);
return documento == null ? 0 : documento.CveDocumentoSolicitud;
}
protected bool MostrarAccion(object fila, string accion)
{
var documento = ObtenerDocumento(fila);
if (documento == null)
{
return false;
}
switch (accion)
{
case "Descargar":
return documento.TieneArchivo;
case "AdministrarGis":
return documento.EsGis;
case "Cargar":
return documento.PuedeActualizarArchivo;
case "Aprobar":
case "CancelarDocumento":
return documento.PuedeRevisar;
case "Eliminar":
return documento.TieneArchivo && documento.PuedeActualizarArchivo;
default:
return false;
}
}
private static TramiteDocumentoSolicitudModel ObtenerDocumento(object fila)
{
var filaDocumento = fila as FilaDocumentoEtapa;
return filaDocumento == null || filaDocumento.EsGrupo ? null : filaDocumento.Documento;
}
protected string UrlDocumento(object clave) protected string UrlDocumento(object clave)
{ {
return ResolveUrl("~/Pages/Fraccionamientos/TramiteDocumento.ashx") + return ResolveUrl("~/Pages/Fraccionamientos/TramiteDocumento.ashx") +
@@ -207,10 +436,19 @@ namespace SIG.Web.UserControl
return; return;
} }
DataKey claves = listado.DataKeys[fila.RowIndex]; DataKey claves = listado.DataKeys[fila.RowIndex];
if (Convert.ToBoolean(claves.Values["EsGrupo"]))
{
return;
}
int etapa = Convert.ToInt32(claves.Values["CveFraccionamientoEtapa"]); int etapa = Convert.ToInt32(claves.Values["CveFraccionamientoEtapa"]);
int tipo = Convert.ToInt32(claves.Values["CveTipoDocumento"]); int tipo = Convert.ToInt32(claves.Values["CveTipoDocumento"]);
int solicitud = Convert.ToInt32(claves.Values["CveDocumentoSolicitud"]);
DocumentoSeleccionado = DocumentosActuales.FirstOrDefault(documento => DocumentoSeleccionado = DocumentosActuales.FirstOrDefault(documento =>
documento.CveFraccionamientoEtapa == etapa && documento.CveTipoDocumento == tipo); documento.CveFraccionamientoEtapa == etapa &&
documento.CveTipoDocumento == tipo &&
documento.CveDocumentoSolicitud == solicitud);
if (DocumentoSeleccionado == null) if (DocumentoSeleccionado == null)
{ {
Avisar("El documento ya no está disponible. Actualice el listado."); Avisar("El documento ya no está disponible. Actualice el listado.");