This commit is contained in:
Jorge Luis caudillo esparza
2026-08-18 17:40:19 -07:00
parent 1c84a87f11
commit fba8e12376
12 changed files with 879 additions and 60 deletions

View File

@@ -100,7 +100,8 @@ namespace SIG.Web.AppCode.Configuration
new FraccionamientoGisFileStore(); new FraccionamientoGisFileStore();
FraccionamientoGisService = new FraccionamientoGisService( FraccionamientoGisService = new FraccionamientoGisService(
gisRepository, gisRepository,
gisFileStore); gisFileStore,
BitacoraService);
TramiteCatalogService = new TramiteCatalogService( TramiteCatalogService = new TramiteCatalogService(
new TramiteCatalogRepository(DbExecutor), new TramiteCatalogRepository(DbExecutor),
BitacoraService); BitacoraService);

View File

@@ -21,5 +21,10 @@ namespace SIG.Web.AppCode.Contracts.Fraccionamientos
IList<FraccionamientoGisLotLinkModel> links, IList<FraccionamientoGisLotLinkModel> links,
int executingUser, int executingUser,
string domainName); string domainName);
DbResult<bool> ClearLinks(
int stageId,
int executingUser,
string domainName);
} }
} }

View File

@@ -21,6 +21,11 @@ namespace SIG.Web.AppCode.Contracts.Fraccionamientos
int executingUser, int executingUser,
string domainName); string domainName);
DbResult<FraccionamientoGisSaveResultModel> Delete(
int stageId,
int executingUser,
string domainName);
DbResult<FraccionamientoGisFileModel> OpenFile( DbResult<FraccionamientoGisFileModel> OpenFile(
int stageId, int stageId,
int executingUser, int executingUser,

View File

@@ -53,21 +53,27 @@ namespace SIG.Web.Pages.Fraccionamientos
"POST", "POST",
StringComparison.OrdinalIgnoreCase)) StringComparison.OrdinalIgnoreCase))
{ {
if (!string.Equals(method, "Save", StringComparison.OrdinalIgnoreCase)) if (string.Equals(method, "Save", StringComparison.OrdinalIgnoreCase))
{ {
WriteJson( Save(context, currentUser);
context,
405,
new
{
success = false,
message = "La operación GIS solicitada no es válida.",
codigo = string.Empty
});
return; return;
} }
Save(context, currentUser); if (string.Equals(method, "Delete", StringComparison.OrdinalIgnoreCase))
{
Delete(context, currentUser);
return;
}
WriteJson(
context,
405,
new
{
success = false,
message = "La operación GIS solicitada no es válida.",
codigo = string.Empty
});
return; return;
} }
@@ -235,6 +241,48 @@ namespace SIG.Web.Pages.Fraccionamientos
}); });
} }
private static void Delete(
HttpContext context,
UserSessionModel currentUser)
{
int stageId;
if (!TryGetStageId(context, out stageId))
{
WriteJson(
context,
400,
new
{
success = false,
message = "Seleccione una etapa válida.",
codigo = string.Empty
});
return;
}
DbResult<FraccionamientoGisSaveResultModel> result =
AppServices.FraccionamientoGisService.Delete(
stageId,
currentUser.CveUsuario,
currentUser.NombreDominio);
if (!result.Success)
{
WriteResultFailure(context, result, 400);
return;
}
WriteJson(
context,
200,
new
{
success = true,
message = "GIS eliminado y lotes desvinculados correctamente.",
data = result.Data
});
}
private static void OpenFile( private static void OpenFile(
HttpContext context, HttpContext context,
UserSessionModel currentUser, UserSessionModel currentUser,

View File

@@ -89,5 +89,21 @@ namespace SIG.Web.AppCode.Repositories.Fraccionamientos
DbType.Xml); DbType.Xml);
}); });
} }
public DbResult<bool> ClearLinks(
int stageId,
int executingUser,
string domainName)
{
return Execute(
"dbo.PA_Fraccionamientos_EtapaGISVinculosClear",
executingUser,
domainName,
command => StoredProcedureCommand.AddInput(
command,
"@CveFraccionamientoEtapa",
stageId,
DbType.Int32));
}
} }
} }

View File

@@ -161,6 +161,85 @@ namespace SIG.Web.AppCode.Services.Fraccionamientos
return auditEvent; return auditEvent;
} }
public static BitacoraEventoModel CreateStageGisFileChange(
FraccionamientoGisContextModel context,
bool hadFile,
bool fileUploaded,
int previousLinkedLots,
int currentLinkedLots)
{
string description;
if (!hadFile && fileUploaded)
{
description =
"Se agregó el GIS de la etapa " + context.Etapa +
". Se vincularon " + currentLinkedLots + " lotes.";
}
else if (hadFile && fileUploaded)
{
description =
"Se reemplazó el GIS de la etapa " + context.Etapa +
". Se desvincularon " + previousLinkedLots +
" lotes y se vincularon " + currentLinkedLots +
" lotes al nuevo GIS.";
}
else
{
description =
"Se modificaron los vínculos GIS de la etapa " +
context.Etapa + ". Vínculos anteriores: " +
previousLinkedLots + "; vínculos actuales: " +
currentLinkedLots + ".";
}
return CreateStageGisFileEvent(
context,
BitacoraTipoMovimiento.Editado,
description);
}
public static BitacoraEventoModel CreateStageGisFileDelete(
FraccionamientoGisContextModel context,
int unlinkedLots)
{
return CreateStageGisFileEvent(
context,
BitacoraTipoMovimiento.Eliminado,
"Se eliminó el GIS de la etapa " + context.Etapa +
". Se desvincularon " + unlinkedLots + " lotes.");
}
private static BitacoraEventoModel CreateStageGisFileEvent(
FraccionamientoGisContextModel context,
BitacoraTipoMovimiento movementType,
string description)
{
var auditEvent = CreateEvent(
"FraccionamientosEtapas",
movementType,
context.CveFraccionamientoEtapa,
description,
context.CveCliente);
auditEvent.Relaciones.Add(
CreateRelation(
BitacoraTipoRelacion.Directa,
"FraccionamientosEtapas",
"CveFraccionamientoEtapa",
context.CveFraccionamientoEtapa,
"Etapa del GIS."));
auditEvent.Relaciones.Add(
CreateRelation(
BitacoraTipoRelacion.Relacionada,
"Fraccionamientos",
"CveFraccionamiento",
context.CveFraccionamiento,
"Fraccionamiento de la etapa."));
return auditEvent;
}
public static BitacoraEventoModel CreateLot( public static BitacoraEventoModel CreateLot(
FraccionamientoLoteModel previous, FraccionamientoLoteModel previous,
FraccionamientoLoteModel current, FraccionamientoLoteModel current,

View File

@@ -2,8 +2,10 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using SIG.Web.AppCode.Contracts.Bitacora;
using SIG.Web.AppCode.Contracts.Fraccionamientos; using SIG.Web.AppCode.Contracts.Fraccionamientos;
using SIG.Web.AppCode.Infrastructure.Transactions; using SIG.Web.AppCode.Infrastructure.Transactions;
using SIG.Web.AppCode.Models.Bitacora;
using SIG.Web.AppCode.Models.Common; using SIG.Web.AppCode.Models.Common;
using SIG.Web.AppCode.Models.Fraccionamientos; using SIG.Web.AppCode.Models.Fraccionamientos;
using SIG.Web.AppCode.Services.Logging; using SIG.Web.AppCode.Services.Logging;
@@ -18,10 +20,12 @@ namespace SIG.Web.AppCode.Services.Fraccionamientos
private readonly IFraccionamientoGisRepository _repository; private readonly IFraccionamientoGisRepository _repository;
private readonly IFraccionamientoGisFileStore _fileStore; private readonly IFraccionamientoGisFileStore _fileStore;
private readonly IBitacoraService _bitacoraService;
public FraccionamientoGisService( public FraccionamientoGisService(
IFraccionamientoGisRepository repository, IFraccionamientoGisRepository repository,
IFraccionamientoGisFileStore fileStore) IFraccionamientoGisFileStore fileStore,
IBitacoraService bitacoraService)
{ {
if (repository == null) if (repository == null)
{ {
@@ -33,8 +37,14 @@ namespace SIG.Web.AppCode.Services.Fraccionamientos
throw new ArgumentNullException("fileStore"); throw new ArgumentNullException("fileStore");
} }
if (bitacoraService == null)
{
throw new ArgumentNullException("bitacoraService");
}
_repository = repository; _repository = repository;
_fileStore = fileStore; _fileStore = fileStore;
_bitacoraService = bitacoraService;
} }
public DbResult<FraccionamientoGisViewModel> Get( public DbResult<FraccionamientoGisViewModel> Get(
@@ -167,6 +177,24 @@ namespace SIG.Web.AppCode.Services.Fraccionamientos
} }
FraccionamientoGisContextModel gisContext = contextResult.Data; FraccionamientoGisContextModel gisContext = contextResult.Data;
DbResult<List<FraccionamientoGisLotLinkModel>> previousLinksResult =
_repository.ListLinks(
stageId,
executingUser,
domainName);
if (!previousLinksResult.Success)
{
return CopyFailure<List<FraccionamientoGisLotLinkModel>, FraccionamientoGisSaveResultModel>(
previousLinksResult,
"No fue posible consultar los vínculos GIS actuales.");
}
IList<FraccionamientoGisLotLinkModel> previousLinks =
previousLinksResult.Data ??
new List<FraccionamientoGisLotLinkModel>();
bool hadFile; bool hadFile;
try try
@@ -234,6 +262,40 @@ namespace SIG.Web.AppCode.Services.Fraccionamientos
fileWasWritten = true; fileWasWritten = true;
} }
bool linksChanged = !LinksEqual(previousLinks, links);
bool hasChange = hasUploadedFile || linksChanged;
if (hasChange)
{
BitacoraEventoModel auditEvent =
FraccionamientoBitacoraFactory.CreateStageGisFileChange(
gisContext,
hadFile,
hasUploadedFile,
CountLinked(previousLinks),
links.Count);
DbResult<long> auditResult =
_bitacoraService.Registrar(auditEvent);
if (!auditResult.Success)
{
if (fileWasWritten)
{
RestorePreviousFile(
gisContext,
hadFile,
previousFile);
fileWasWritten = false;
}
return DbResult<FraccionamientoGisSaveResultModel>.Fail(
auditResult.UserMessage,
auditResult.TechnicalMessage,
auditResult.Exception);
}
}
transaction.Commit(); transaction.Commit();
} }
@@ -268,6 +330,167 @@ namespace SIG.Web.AppCode.Services.Fraccionamientos
} }
} }
public DbResult<FraccionamientoGisSaveResultModel> Delete(
int stageId,
int executingUser,
string domainName)
{
if (stageId <= 0)
{
return Fail<FraccionamientoGisSaveResultModel>(
"Seleccione una etapa válida.");
}
DbResult<FraccionamientoGisContextModel> contextResult =
_repository.GetContext(
stageId,
executingUser,
domainName);
if (!contextResult.Success || contextResult.Data == null)
{
return CopyFailure<FraccionamientoGisContextModel, FraccionamientoGisSaveResultModel>(
contextResult,
"No fue posible obtener el contexto GIS de la etapa.");
}
DbResult<List<FraccionamientoGisLotLinkModel>> linksResult =
_repository.ListLinks(
stageId,
executingUser,
domainName);
if (!linksResult.Success)
{
return CopyFailure<List<FraccionamientoGisLotLinkModel>, FraccionamientoGisSaveResultModel>(
linksResult,
"No fue posible consultar los vínculos GIS actuales.");
}
FraccionamientoGisContextModel gisContext = contextResult.Data;
IList<FraccionamientoGisLotLinkModel> previousLinks =
linksResult.Data ?? new List<FraccionamientoGisLotLinkModel>();
bool hadFile;
byte[] previousFile = null;
try
{
hadFile = _fileStore.Exists(gisContext);
if (hadFile)
{
using (Stream previous = _fileStore.OpenRead(gisContext))
using (var backup = new MemoryStream())
{
previous.CopyTo(backup);
previousFile = backup.ToArray();
}
}
}
catch (Exception ex)
{
return FileFailure<FraccionamientoGisSaveResultModel>(
ex,
"FraccionamientoGisService.Delete.Backup");
}
int linkedCount = CountLinked(previousLinks);
if (!hadFile && linkedCount == 0)
{
return Fail<FraccionamientoGisSaveResultModel>(
"La etapa no tiene un GIS para eliminar.");
}
bool fileWasDeleted = false;
try
{
using (var transaction = new TransactionLogic())
{
DbResult<bool> clearResult =
_repository.ClearLinks(
stageId,
executingUser,
domainName);
if (!clearResult.Success)
{
return DbResult<FraccionamientoGisSaveResultModel>.Fail(
clearResult.UserMessage,
clearResult.TechnicalMessage,
clearResult.Exception);
}
if (hadFile)
{
_fileStore.Delete(gisContext);
fileWasDeleted = true;
}
DbResult<long> auditResult =
_bitacoraService.Registrar(
FraccionamientoBitacoraFactory.CreateStageGisFileDelete(
gisContext,
linkedCount));
if (!auditResult.Success)
{
if (fileWasDeleted && previousFile != null)
{
using (var backup = new MemoryStream(previousFile, false))
{
_fileStore.Save(gisContext, backup);
}
fileWasDeleted = false;
}
return DbResult<FraccionamientoGisSaveResultModel>.Fail(
auditResult.UserMessage,
auditResult.TechnicalMessage,
auditResult.Exception);
}
transaction.Commit();
}
return DbResult<FraccionamientoGisSaveResultModel>.Ok(
new FraccionamientoGisSaveResultModel
{
CveFraccionamientoEtapa = stageId,
VinculosGuardados = 0,
ArchivoGuardado = false,
TieneArchivo = false,
NombreArchivo = string.Empty,
FechaArchivo = null
});
}
catch (Exception ex)
{
if (fileWasDeleted && previousFile != null)
{
try
{
using (var backup = new MemoryStream(previousFile, false))
{
_fileStore.Save(gisContext, backup);
}
}
catch (Exception restoreException)
{
ExceptionLogger.Log(
restoreException,
"FraccionamientoGisService.Delete.Restore",
"No fue posible restaurar el GIS después de un error al eliminarlo.");
}
}
return FileFailure<FraccionamientoGisSaveResultModel>(
ex,
"FraccionamientoGisService.Delete");
}
}
public DbResult<FraccionamientoGisFileModel> OpenFile( public DbResult<FraccionamientoGisFileModel> OpenFile(
int stageId, int stageId,
int executingUser, int executingUser,
@@ -345,6 +568,50 @@ namespace SIG.Web.AppCode.Services.Fraccionamientos
} }
} }
private static int CountLinked(
IEnumerable<FraccionamientoGisLotLinkModel> links)
{
return links == null
? 0
: links.Count(link =>
link != null &&
link.CveFraccionamientoLote > 0 &&
!string.IsNullOrWhiteSpace(link.GISPoligonoClave));
}
private static bool LinksEqual(
IEnumerable<FraccionamientoGisLotLinkModel> previous,
IEnumerable<FraccionamientoGisLotLinkModel> current)
{
var previousMap = (previous ?? Enumerable.Empty<FraccionamientoGisLotLinkModel>())
.Where(link =>
link != null &&
link.CveFraccionamientoLote > 0 &&
!string.IsNullOrWhiteSpace(link.GISPoligonoClave))
.ToDictionary(
link => link.GISPoligonoClave.Trim(),
link => link.CveFraccionamientoLote,
StringComparer.Ordinal);
var currentMap = (current ?? Enumerable.Empty<FraccionamientoGisLotLinkModel>())
.Where(link =>
link != null &&
link.CveFraccionamientoLote > 0 &&
!string.IsNullOrWhiteSpace(link.GISPoligonoClave))
.ToDictionary(
link => link.GISPoligonoClave.Trim(),
link => link.CveFraccionamientoLote,
StringComparer.Ordinal);
return previousMap.Count == currentMap.Count &&
previousMap.All(item =>
{
int lotId;
return currentMap.TryGetValue(item.Key, out lotId) &&
lotId == item.Value;
});
}
private static string ValidateLinks( private static string ValidateLinks(
IList<FraccionamientoGisLotLinkModel> links) IList<FraccionamientoGisLotLinkModel> links)
{ {

View File

@@ -12,7 +12,7 @@
<link href='<%= ResolveUrl("~/Styles/CatalogosTramites.css") %>' <link href='<%= ResolveUrl("~/Styles/CatalogosTramites.css") %>'
rel="stylesheet" rel="stylesheet"
type="text/css" /> type="text/css" />
<link href='<%= ResolveUrl("~/Styles/fraccionamiento-gis.css?v=20260818-1") %>' <link href='<%= ResolveUrl("~/Styles/fraccionamiento-gis.css?v=20260818-2") %>'
rel="stylesheet" rel="stylesheet"
type="text/css" /> type="text/css" />
</asp:Content> </asp:Content>
@@ -1004,6 +1004,14 @@
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button"
id="BtnDeleteStageGis"
class="btn red"
style="display:none;">
<i class="fa fa-trash"></i>
Eliminar GIS
</button>
<button type="button" <button type="button"
class="btn default" class="btn default"
data-dismiss="modal"> data-dismiss="modal">
@@ -1012,7 +1020,8 @@
<button type="button" <button type="button"
id="BtnSaveStageGis" id="BtnSaveStageGis"
class="btn blue"> class="btn blue"
disabled="disabled">
<i class="fa fa-save"></i> <i class="fa fa-save"></i>
Guardar GIS Guardar GIS
</button> </button>
@@ -1314,7 +1323,7 @@
<asp:Content ID="ContentScripts" ContentPlaceHolderID="J" runat="server"> <asp:Content ID="ContentScripts" ContentPlaceHolderID="J" runat="server">
<script src='<%= ResolveUrl("~/Scripts/CatalogosTramites.js") %>' <script src='<%= ResolveUrl("~/Scripts/CatalogosTramites.js") %>'
type="text/javascript"></script> type="text/javascript"></script>
<script src='<%= ResolveUrl("~/Scripts/fraccionamiento-gis.js?v=20260818-1") %>' <script src='<%= ResolveUrl("~/Scripts/fraccionamiento-gis.js?v=20260818-2") %>'
type="text/javascript"></script> type="text/javascript"></script>
<script> <script>
window.FraccionamientoGISOptions = { window.FraccionamientoGISOptions = {
@@ -1325,6 +1334,7 @@
linksTableId: 'GisVinculos', linksTableId: 'GisVinculos',
statusId: 'GisVinculosStatus', statusId: 'GisVinculosStatus',
saveButtonId: 'BtnSaveStageGis', saveButtonId: 'BtnSaveStageGis',
deleteButtonId: 'BtnDeleteStageGis',
refreshButtonId: 'BtnRefreshStageGis', refreshButtonId: 'BtnRefreshStageGis',
handlerUrl: '<%= ResolveUrl("~/Pages/Fraccionamientos/FraccionamientoGis.ashx") %>' handlerUrl: '<%= ResolveUrl("~/Pages/Fraccionamientos/FraccionamientoGis.ashx") %>'
}; };

View File

@@ -456,11 +456,12 @@ namespace SIG.Web.Pages.Fraccionamientos
{ {
HideStageGisMessage(); HideStageGisMessage();
BindSelectedStageLotsContext(); BindSelectedStageLotsContext();
RefreshBitacoraDetalle();
UpDetailTabs.Update(); UpDetailTabs.Update();
UpStageGisModal.Update(); UpStageGisModal.Update();
HideStageGisModal(); HideStageGisModal();
Alert( Alert(
"GIS y vínculos guardados correctamente.", "GIS actualizado correctamente.",
TipoMensaje.ToastExito); TipoMensaje.ToastExito);
} }

View File

@@ -1614,11 +1614,6 @@ function ADDropzoneLocal(txtid) {
return null; return null;
} }
if (typeof Dropzone === 'undefined') {
console.error('Dropzone no está disponible para #' + txtid);
return null;
}
if (txt.attr('data-sig-dropzone-local-initialized') === '1') { if (txt.attr('data-sig-dropzone-local-initialized') === '1') {
return window.ADSLocalDropzone return window.ADSLocalDropzone
? window.ADSLocalDropzone.instance(txtid) ? window.ADSLocalDropzone.instance(txtid)
@@ -1641,7 +1636,7 @@ function ADDropzoneLocal(txtid) {
} }
var mydiv = $( var mydiv = $(
'<div id="' + dropId + '" class="addropzone ADSdropzoneLocalContainer">' + '<div id="' + dropId + '" class="addropzone ADSdropzoneLocalContainer" tabindex="0">' +
' <div class="dz-message">' + $('<div/>').text(message).html() + '</div>' + ' <div class="dz-message">' + $('<div/>').text(message).html() + '</div>' +
'</div>'); '</div>');
@@ -1651,6 +1646,237 @@ function ADDropzoneLocal(txtid) {
txt.after(mydiv); txt.after(mydiv);
} }
function registerInstance(instance) {
txt.attr('data-sig-dropzone-local-initialized', '1');
window.ADSLocalDropzone = window.ADSLocalDropzone || {};
window.ADSLocalDropzone.instances =
window.ADSLocalDropzone.instances || {};
window.ADSLocalDropzone.instances[txtid] = instance;
return instance;
}
function isAccepted(file) {
if (!acceptedFiles) {
return true;
}
var name = String(file && file.name || '').toLowerCase();
var type = String(file && file.type || '').toLowerCase();
var accepted = acceptedFiles.split(',');
for (var i = 0; i < accepted.length; i++) {
var rule = $.trim(accepted[i]).toLowerCase();
if (!rule) {
continue;
}
if (rule.charAt(0) === '.' && name.slice(-rule.length) === rule) {
return true;
}
if (rule.slice(-2) === '/*' &&
type.indexOf(rule.substring(0, rule.length - 1)) === 0) {
return true;
}
if (type && type === rule) {
return true;
}
}
return false;
}
function readFile(file, instance) {
if (!file) {
return;
}
if (!isAccepted(file)) {
txt.trigger('adsdropzone:fileerror', [
file,
'El tipo de archivo no está permitido.',
instance
]);
return;
}
if (file.size > maxFilesize * 1024 * 1024) {
txt.trigger('adsdropzone:fileerror', [
file,
'El archivo excede el tamaño permitido.',
instance
]);
return;
}
var reader = new FileReader();
reader.onload = function (event) {
txt.trigger('adsdropzone:fileadded', [
file,
event.target.result,
instance
]);
};
reader.onerror = function () {
txt.trigger('adsdropzone:fileerror', [
file,
'No fue posible leer el archivo seleccionado.',
instance
]);
};
reader.readAsText(file);
}
/*
* El proyecto recibido conserva el helper histórico ADDropzone(), pero no
* contiene la librería Dropzone dentro de sus bundles. Para que el GIS no
* dependa de una referencia externa, este componente tiene un fallback
* nativo que mantiene la misma experiencia: clic, drag/drop, vista del
* archivo y los mismos eventos adsdropzone:* consumidos por la pantalla.
* Si Dropzone está cargado en otra instalación, se usa normalmente.
*/
if (typeof Dropzone === 'undefined') {
var input = $('<input type="file" style="display:none;" />');
if (acceptedFiles) {
input.attr('accept', acceptedFiles);
}
if (isNaN(maxFile) || maxFile !== 1) {
input.attr('multiple', 'multiple');
}
mydiv.append(input);
var fallback = {
files: [],
removeFile: function (file) {
var index = this.files.indexOf(file);
if (index >= 0) {
this.files.splice(index, 1);
}
renderFallback();
txt.trigger('adsdropzone:fileremoved', [file, fallback]);
},
removeAllFiles: function () {
var files = this.files.slice(0);
this.files = [];
renderFallback();
$.each(files, function (_, file) {
txt.trigger('adsdropzone:fileremoved', [file, fallback]);
});
}
};
function renderFallback() {
mydiv.find('.ADSdropzoneLocalFallbackPreview').remove();
mydiv.find('.dz-message').toggle(fallback.files.length === 0);
$.each(fallback.files, function (_, file) {
var preview = $('<div class="ADSdropzoneLocalFallbackPreview"></div>');
$('<div class="ADSdropzoneLocalFileName"></div>')
.text(file.name)
.appendTo(preview);
$('<button type="button" class="btn btn-sm btn-block default">Quitar archivo</button>')
.on('click', function (event) {
event.preventDefault();
event.stopPropagation();
fallback.removeFile(file);
})
.appendTo(preview);
mydiv.append(preview);
});
}
function addFallbackFile(file) {
if (!file) {
return;
}
if (!isAccepted(file)) {
txt.trigger('adsdropzone:fileerror', [
file,
'El tipo de archivo no está permitido.',
fallback
]);
return;
}
if (file.size > maxFilesize * 1024 * 1024) {
txt.trigger('adsdropzone:fileerror', [
file,
'El archivo excede el tamaño permitido.',
fallback
]);
return;
}
if (!isNaN(maxFile) && maxFile > 0 && fallback.files.length >= maxFile) {
fallback.removeAllFiles();
}
fallback.files.push(file);
renderFallback();
readFile(file, fallback);
}
input.on('change', function () {
var files = this.files || [];
for (var i = 0; i < files.length; i++) {
addFallbackFile(files[i]);
}
this.value = '';
});
mydiv.on('click', function (event) {
if ($(event.target).is('button')) {
return;
}
input.trigger('click');
});
mydiv.on('keydown', function (event) {
if (event.which === 13 || event.which === 32) {
event.preventDefault();
input.trigger('click');
}
});
mydiv.on('dragenter dragover', function (event) {
event.preventDefault();
event.stopPropagation();
mydiv.addClass('is-dragging');
});
mydiv.on('dragleave', function (event) {
event.preventDefault();
event.stopPropagation();
mydiv.removeClass('is-dragging');
});
mydiv.on('drop', function (event) {
event.preventDefault();
event.stopPropagation();
mydiv.removeClass('is-dragging');
var original = event.originalEvent;
var files = original && original.dataTransfer
? original.dataTransfer.files
: null;
if (!files) {
return;
}
for (var i = 0; i < files.length; i++) {
addFallbackFile(files[i]);
}
});
return registerInstance(fallback);
}
var instance; var instance;
try { try {
@@ -1663,6 +1889,7 @@ function ADDropzoneLocal(txtid) {
maxFilesize: maxFilesize, maxFilesize: maxFilesize,
acceptedFiles: acceptedFiles, acceptedFiles: acceptedFiles,
addRemoveLinks: true, addRemoveLinks: true,
dictDefaultMessage: message,
dictRemoveFile: 'Quitar archivo', dictRemoveFile: 'Quitar archivo',
dictInvalidFileType: 'El tipo de archivo no está permitido.', dictInvalidFileType: 'El tipo de archivo no está permitido.',
dictFileTooBig: 'El archivo excede el tamaño permitido.', dictFileTooBig: 'El archivo excede el tamaño permitido.',
@@ -1676,26 +1903,7 @@ function ADDropzoneLocal(txtid) {
return; return;
} }
var reader = new FileReader(); readFile(file, dz);
reader.onload = function (event) {
txt.trigger('adsdropzone:fileadded', [
file,
event.target.result,
dz
]);
};
reader.onerror = function () {
dz.removeFile(file);
txt.trigger('adsdropzone:fileerror', [
file,
'No fue posible leer el archivo seleccionado.',
dz
]);
};
reader.readAsText(file);
}); });
dz.on('removedfile', function (file) { dz.on('removedfile', function (file) {
@@ -1720,14 +1928,7 @@ function ADDropzoneLocal(txtid) {
return null; return null;
} }
txt.attr('data-sig-dropzone-local-initialized', '1'); return registerInstance(instance);
window.ADSLocalDropzone = window.ADSLocalDropzone || {};
window.ADSLocalDropzone.instances =
window.ADSLocalDropzone.instances || {};
window.ADSLocalDropzone.instances[txtid] = instance;
return instance;
} }
window.ADSLocalDropzone = window.ADSLocalDropzone || {}; window.ADSLocalDropzone = window.ADSLocalDropzone || {};
@@ -1737,7 +1938,7 @@ window.ADSLocalDropzone.instance = function (txtid) {
}; };
window.ADSLocalDropzone.getFiles = function (txtid) { window.ADSLocalDropzone.getFiles = function (txtid) {
var instance = window.ADSLocalDropzone.instance(txtid); var instance = window.ADSLocalDropzone.instance(txtid);
return instance ? instance.files.slice(0) : []; return instance && instance.files ? instance.files.slice(0) : [];
}; };
window.ADSLocalDropzone.getFile = function (txtid) { window.ADSLocalDropzone.getFile = function (txtid) {
var files = window.ADSLocalDropzone.getFiles(txtid); var files = window.ADSLocalDropzone.getFiles(txtid);
@@ -1745,7 +1946,7 @@ window.ADSLocalDropzone.getFile = function (txtid) {
}; };
window.ADSLocalDropzone.clear = function (txtid) { window.ADSLocalDropzone.clear = function (txtid) {
var instance = window.ADSLocalDropzone.instance(txtid); var instance = window.ADSLocalDropzone.instance(txtid);
if (instance) { if (instance && typeof instance.removeAllFiles === 'function') {
instance.removeAllFiles(true); instance.removeAllFiles(true);
} }
}; };

View File

@@ -13,6 +13,7 @@
linksTableId: 'GisVinculos', linksTableId: 'GisVinculos',
statusId: 'GisVinculosStatus', statusId: 'GisVinculosStatus',
saveButtonId: 'BtnSaveStageGis', saveButtonId: 'BtnSaveStageGis',
deleteButtonId: 'BtnDeleteStageGis',
refreshButtonId: 'BtnRefreshStageGis', refreshButtonId: 'BtnRefreshStageGis',
handlerUrl: '/Pages/Fraccionamientos/FraccionamientoGis.ashx', handlerUrl: '/Pages/Fraccionamientos/FraccionamientoGis.ashx',
initialZoom: 16, initialZoom: 16,
@@ -34,6 +35,8 @@
geojson: null, geojson: null,
file: null, file: null,
hasServerFile: false, hasServerFile: false,
isDirty: false,
busy: false,
mapMode: null, mapMode: null,
map: null, map: null,
dataLayer: null dataLayer: null
@@ -193,10 +196,13 @@
state.polygonByLotId[id] = key; state.polygonByLotId[id] = key;
} }
}); });
setDirty(state, false);
updateActionButtons(state);
} }
function loadServerFile(state) { function loadServerFile(state) {
$.ajax({ return $.ajax({
url: state.options.handlerUrl, url: state.options.handlerUrl,
type: 'GET', type: 'GET',
dataType: 'text', dataType: 'text',
@@ -209,6 +215,7 @@
try { try {
state.file = null; state.file = null;
loadGeoJson(state, parseJsonText(text)); loadGeoJson(state, parseJsonText(text));
setDirty(state, false);
} catch (ex) { } catch (ex) {
clearViewer(state); clearViewer(state);
renderLinks(state); renderLinks(state);
@@ -603,28 +610,37 @@
function assign(state, polygonKeyValue, lotId) { function assign(state, polygonKeyValue, lotId) {
var priorLot = state.linksByPolygonKey[polygonKeyValue]; var priorLot = state.linksByPolygonKey[polygonKeyValue];
var priorPolygon = state.polygonByLotId[lotId];
if (priorLot === lotId &&
(!priorPolygon || priorPolygon === polygonKeyValue)) {
return;
}
if (priorLot) { if (priorLot) {
delete state.polygonByLotId[priorLot]; delete state.polygonByLotId[priorLot];
} }
var priorPolygon = state.polygonByLotId[lotId];
if (priorPolygon && priorPolygon !== polygonKeyValue) { if (priorPolygon && priorPolygon !== polygonKeyValue) {
delete state.linksByPolygonKey[priorPolygon]; delete state.linksByPolygonKey[priorPolygon];
} }
state.linksByPolygonKey[polygonKeyValue] = lotId; state.linksByPolygonKey[polygonKeyValue] = lotId;
state.polygonByLotId[lotId] = polygonKeyValue; state.polygonByLotId[lotId] = polygonKeyValue;
setDirty(state, true);
refreshMapStyles(state); refreshMapStyles(state);
renderLinks(state); renderLinks(state);
} }
function unlink(state, polygonKeyValue) { function unlink(state, polygonKeyValue) {
var lotId = state.linksByPolygonKey[polygonKeyValue]; var lotId = state.linksByPolygonKey[polygonKeyValue];
if (lotId) { if (!lotId) {
delete state.polygonByLotId[lotId]; return;
} }
delete state.polygonByLotId[lotId];
delete state.linksByPolygonKey[polygonKeyValue]; delete state.linksByPolygonKey[polygonKeyValue];
setDirty(state, true);
refreshMapStyles(state); refreshMapStyles(state);
renderLinks(state); renderLinks(state);
} }
@@ -765,6 +781,8 @@
state.hasServerFile = true; state.hasServerFile = true;
state.file = null; state.file = null;
setDirty(state, false);
updateActionButtons(state);
showMessage( showMessage(
response.message || 'GIS y vínculos guardados correctamente.', response.message || 'GIS y vínculos guardados correctamente.',
false); false);
@@ -793,17 +811,35 @@
$input.on('adsdropzone:fileadded.sigGis', function (_, file, text) { $input.on('adsdropzone:fileadded.sigGis', function (_, file, text) {
try { try {
state.file = file; state.file = file;
// Un GIS nuevo/reemplazado debe volver a vincularse completo.
// Las relaciones guardadas no se tocan en SQL hasta Guardar GIS.
state.linksByPolygonKey = {};
state.polygonByLotId = {};
loadGeoJson(state, parseJsonText(text)); loadGeoJson(state, parseJsonText(text));
setDirty(state, true);
clearMessage(); clearMessage();
} catch (ex) { } catch (ex) {
state.file = null; state.file = null;
clearViewer(state);
renderLinks(state);
if (window.ADSLocalDropzone) { if (window.ADSLocalDropzone) {
window.ADSLocalDropzone.clear(state.options.fileInputId); window.ADSLocalDropzone.clear(state.options.fileInputId);
} }
if (state.hasServerFile) {
applyContext(state, {
Contexto: state.context,
Lotes: state.lots,
TieneArchivo: true
});
loadServerFile(state);
} else {
clearViewer(state);
renderLinks(state);
setDirty(state, false);
}
showMessage( showMessage(
ex.message || 'El archivo GIS no es válido.', ex.message || 'El archivo GIS no es válido.',
true); true);
@@ -818,10 +854,16 @@
state.file = null; state.file = null;
if (state.hasServerFile) { if (state.hasServerFile) {
applyContext(state, {
Contexto: state.context,
Lotes: state.lots,
TieneArchivo: true
});
loadServerFile(state); loadServerFile(state);
} else { } else {
clearViewer(state); clearViewer(state);
renderLinks(state); renderLinks(state);
setDirty(state, false);
} }
}); });
@@ -835,13 +877,135 @@
.off('click.sigGis') .off('click.sigGis')
.on('click.sigGis', function (event) { .on('click.sigGis', function (event) {
event.preventDefault(); event.preventDefault();
if (!state.isDirty || state.busy) {
return;
}
save(state); save(state);
}); });
} }
function bindDelete(state) {
$('#' + state.options.deleteButtonId)
.off('click.sigGis')
.on('click.sigGis', function (event) {
event.preventDefault();
if (!state.hasServerFile || state.busy) {
return;
}
var executeDelete = function () {
deleteGis(state);
};
if (window.bootbox && typeof bootbox.confirm === 'function') {
bootbox.confirm(
'Se eliminará el archivo GIS y se desvincularán todos los lotes de esta etapa. ¿Desea continuar?',
function (confirmed) {
if (confirmed) {
executeDelete();
}
});
return;
}
if (window.confirm(
'Se eliminará el archivo GIS y se desvincularán todos los lotes de esta etapa. ¿Desea continuar?')) {
executeDelete();
}
});
}
function deleteGis(state) {
setBusy(true);
clearMessage();
$.ajax({
url: state.options.handlerUrl,
type: 'POST',
dataType: 'json',
data: {
metodo: 'Delete',
CveFraccionamientoEtapa: state.stageId
}
}).done(function (response) {
if (!response || response.success !== true) {
showServerError(response);
return;
}
state.hasServerFile = false;
state.file = null;
state.linksByPolygonKey = {};
state.polygonByLotId = {};
$.each(state.lots, function (_, lot) {
if (lot) {
lot.GISPoligonoClave = null;
lot.gisPoligonoClave = null;
}
});
if (window.ADSLocalDropzone) {
window.ADSLocalDropzone.clear(state.options.fileInputId);
}
clearViewer(state);
renderLinks(state);
setDirty(state, false);
updateActionButtons(state);
showMessage(
response.message || 'GIS eliminado y lotes desvinculados correctamente.',
false);
$(document).trigger('siggis:deleted', [response]);
var refreshButton = $('#' + state.options.refreshButtonId);
if (refreshButton.length) {
refreshButton.click();
}
}).fail(function (xhr) {
showXhrError(xhr);
}).always(function () {
setBusy(false);
});
}
function setDirty(state, value) {
if (!state) {
return;
}
state.isDirty = !!value;
updateActionButtons(state);
}
function updateActionButtons(state) {
if (!state) {
return;
}
$('#' + state.options.saveButtonId)
.prop('disabled', state.busy || !state.isDirty);
$('#' + state.options.deleteButtonId)
.toggle(!!state.hasServerFile)
.prop('disabled', !!state.busy);
}
function setBusy(value) { function setBusy(value) {
var config = options(); var config = options();
$('#' + config.saveButtonId).prop('disabled', !!value);
if (current) {
current.busy = !!value;
updateActionButtons(current);
} else {
$('#' + config.saveButtonId).prop('disabled', true);
$('#' + config.deleteButtonId).prop('disabled', !!value);
}
if (window.Metronic && typeof Metronic.blockUI === 'function' && value) { if (window.Metronic && typeof Metronic.blockUI === 'function' && value) {
Metronic.blockUI({ target: '#' + config.modalId + ' .modal-content', animate: true }); Metronic.blockUI({ target: '#' + config.modalId + ' .modal-content', animate: true });
@@ -920,7 +1084,9 @@
current = createState(stageId); current = createState(stageId);
bindFile(current); bindFile(current);
bindSave(current); bindSave(current);
bindDelete(current);
renderLinks(current); renderLinks(current);
updateActionButtons(current);
loadContext(current); loadContext(current);
return current; return current;
}; };
@@ -961,6 +1127,8 @@
window.ADSLocalDropzone.clear(config.fileInputId); window.ADSLocalDropzone.clear(config.fileInputId);
} }
$('#' + config.saveButtonId).prop('disabled', true);
$('#' + config.deleteButtonId).hide().prop('disabled', false);
current = null; current = null;
}); });
} }

View File

@@ -146,3 +146,21 @@
height: 360px; height: 360px;
} }
} }
.ADSdropzoneLocalContainer.is-dragging {
border-style: solid;
background: #eef6fb;
}
.ADSdropzoneLocalFallbackPreview {
padding: 10px;
background: #fff;
border: 1px solid #d9dee4;
}
.ADSdropzoneLocalFileName {
margin-bottom: 8px;
font-weight: 600;
word-break: break-all;
}