AbetoDesign_FactuGES2/Source/Base/Utiles/uDataTableUtils.pas
2022-12-14 16:26:47 +00:00

545 lines
17 KiB
ObjectPascal
Raw Permalink Blame History

unit uDataTableUtils;
interface
uses
uDACDSDataTable, uDADataTable, uDAInterfaces, uDADelta, uDAMemDataTable;
const
ID_NULO = -9000;
ID_TODOS = -9001;
ID_PRIMERO = -9002;
type
TModoDuplicarRegistros = (mdrTodos, mdrActual, mdrSeleccionados);
function SetFieldNull(ATarget: TDADataTable; const FieldName: String): Boolean;
procedure ConectarTabla (ATarget: TDADataTable);
procedure DesconectarTabla (ATarget: TDADataTable);
procedure CopyDataTableDA5(ASource : TDADataTable; ATarget: TDADataTable;
const OnlySelectedRows : Boolean = False);
procedure CloneDataTable(const ASource : TDACDSDataTable;
var ATarget : TDACDSDataTable;
RemoteUpdate: Boolean = True); overload;
function CloneDataTable(const ASource : TDAMemDataTable;
RemoteUpdate: Boolean = True): TDAMemDataTable; overload;
procedure DuplicarRegistro(ASource : TDADataTable; ATarget : TDADataTable;
const WithPKKey: Boolean = False; const WithFKKey: Boolean = False;
const Insertar: Boolean = True);
procedure DuplicarRegistros(ASource : TDADataTable; ATarget : TDADataTable;
AModo : TModoDuplicarRegistros; APermitirRepetidos: Boolean = True;
const WithDeltas: Boolean = True; const WithPKKey: Boolean = False; const WithFKKey: Boolean = False);
procedure DeleteAllTable(const ADataTable : TDADataTable);
function DeltaValuesAreDifferent(const aDelta : IDADelta): boolean;
procedure EnlazarMaestroDetalle(AMasterDataSource : TDADataSource;
ADetail : IDAStronglyTypedDataTable);
function DataTableModified (const ADataTable : TDADataTable): Boolean;
procedure SetDataTableReadOnly(ADataTable: TDADataTable; const value: Boolean);
implementation
uses
Classes, DB, uDAClasses, SysUtils, uDABINAdapter, uROTypes, cxControls,
Dialogs, Variants, uDBSelectionListUtils, uROClasses, uDADataStreamer, uDABin2DataStreamer;
procedure CopyDataTableDA5(ASource : TDADataTable; ATarget: TDADataTable;
const OnlySelectedRows : Boolean = False);
var
DABin: Binary;
DADataStreamer : TDABin2DataStreamer;
AFiltered : Boolean;
AFilter : String;
AObj : ISeleccionable;
i : Integer;
AID: Integer;
begin
AFilter := '';
AFiltered := False;
ShowHourglassCursor;
ASource.DisableControls;
ATarget.DisableControls;
AID := ASource.GetRowRecIDValue; //Se guarda la posicion de la tabla fuente
try
if OnlySelectedRows then
begin
if not Supports(ASource, ISeleccionable, aObj) then
raise Exception.Create('El origen de datos no soporta la interfaz ISeleccionable (CopyDataTable)');
{ Si la tabla est<73> abierta, la cerramos antes de aplicar los filtros
porque por cada cambio en el filtro se hacen llamadas internas de TDADataTable. }
//Se comenta porque el cierre y apertura de la tabla porque ocasiona el recuperar todos los registros del servidor nuevamente.
// if ASource.Active then
// ASource.Close;
// Si la tabla origen viene con un filtro, lo guardamos para luego restablecerlo.
if ASource.Filtered then
begin
AFiltered := True;
AFilter := ASource.Filter;
ASource.Filtered := False;
end;
ASource.Filter := '';
//Si no hay elemento seleccionados filtramos para que ATarget se quede vacia
if (AObj.SelectedRecords.Count = 0) then
ASource.Filter := ASource.Filter + '(' + ASource.RecIDField.FieldName + ' = ' + IntToStr(ID_NULO) + ')'
//En caso contrario filtramos por los elementos seleccionados
else
for i := 0 to AObj.SelectedRecords.Count - 1 do
begin
if (i > 0) then
ASource.Filter := ASource.Filter + ' or ';
ASource.Filter := ASource.Filter + '(' + ASource.RecIDField.FieldName + ' = ' + IntToStr(AObj.SelectedRecords.Items[i]) + ')';
end;
ASource.Filtered := True;
end;
DABin := Binary.Create;
DADataStreamer := TDABin2DataStreamer.Create(nil);
ATarget.LogicalName := ASource.LogicalName; // We need to specify new dataset LogicalName
ATarget.RemoteFetchEnabled := False; // "Desconectamos" la tabla destino del servidor
//Se comenta porque el cierre y apertura de la tabla porque ocasiona el recuperar todos los registros del servidor nuevamente.
// if not ASource.Active then
// ASource.Open;
ASource.First;
DADataStreamer.WriteDataset(DABin, ASource, [woRows, woSchema], -1);
DADataStreamer.ReadDataset(DABin, ATarget, True);
DADataStreamer.Finalize;
ATarget.RemoteFetchEnabled := True; // "Conectamos" la tabla del servidor otra vez
// Dejar el filtro de la tabla origen como estaba
if OnlySelectedRows then
begin
ASource.Filtered := False;
ASource.Filter := AFilter;
if AFiltered then
ASource.Filtered := True;
end;
ASource.Locate(ASource.RecIDField.FieldName, AID, []); //Se intenta restablecer la posicion inicial de la tabla fuente
finally
FreeAndNil(DABin);
FreeAndNil(DADataStreamer);
ASource.EnableControls;
ATarget.EnableControls;
HideHourglassCursor;
end;
end;
function SetFieldNull(ATarget: TDADataTable; const FieldName: String): Boolean;
begin
Result := False;
if Assigned(ATarget) then
begin
try
ATarget.FieldByName(FieldName).AsVariant := Null;
Result := True;
except
on E: Exception do
Result := False;
end;
end;
end;
function DataTableModified (const ADataTable : TDADataTable): Boolean;
var
bCambiado : Boolean;
dtDetails : TList;
i : integer;
begin
bCambiado := False;
if Assigned(ADataTable) and (ADataTable.Active) then
begin
bCambiado :=(ADataTable.State = dsEdit) or
(ADataTable.HasDelta and DeltaValuesAreDifferent(ADataTable.Delta));
if (not bCambiado) then
begin
dtDetails := ADataTable.GetDetailDataTables;
for i := 0 to dtDetails.Count - 1 do
begin
bCambiado := bCambiado or
((TDADataTable(dtDetails.Items[i])).State in dsEditModes) or
((TDADataTable(dtDetails.Items[i])).HasDelta and
DeltaValuesAreDifferent((TDADataTable(dtDetails.Items[i])).Delta));
if bCambiado then
Break;
end;
end;
end;
Result := bCambiado;
end;
procedure DeleteAllTable(const ADataTable : TDADataTable);
begin
ADataTable.ClearRows;
end;
function CloneDataTable(const ASource : TDAMemDataTable;
RemoteUpdate: Boolean = True): TDAMemDataTable;
var
i : Integer;
ATarget : TDAMemDataTable;
begin
if not Assigned(ASource) then
raise Exception.Create ('No se ha asignado la tabla de origen (CloneDataTable)');
ATarget := TDAMemDataTable.Create(NIL);
with ATarget do
begin
{ ATENCI<43>N !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
--------------------------------------------------------------------
DA5 tiene un fallo en el cual las tablas definidas como detalle no
tienen asociados los eventos internos before... y after... por lo
que los campos AutoInc no funcionan (no pone el -1, -2...).
La forma de arreglarlo es llamar a DisableEventHandlers/EnableEventHandlers
que realiza la asociaci<63>n de los eventos.
}
ATarget.DisableEventHandlers; // <--- INDISPENSABLE!!!!!!!!!!!!!!!!!!!!
try
if not Assigned(ASource.RemoteDataAdapter) then
raise Exception.Create('No est<73> asignado RemoteDataAdapter (' + ASource.Name + ')');
LogicalName := ASource.LogicalName;
CustomAttributes.Assign(ASource.CustomAttributes);
Fields.AssignFieldCollection(ASource.Fields); // o tambi<62>n ATarget.LoadSchema;
Params.AssignParamCollection(ASource.Params);
RemoteDataAdapter := ASource.RemoteDataAdapter;
Randomize;
Name := ASource.Name + '_' + IntToStr(Random(MAXINT));
BusinessRulesID := ASource.BusinessRulesID;
with MasterParamsMappings do
for i := 0 to ASource.MasterParamsMappings.Count - 1 do
Add(ASource.MasterParamsMappings.Strings[i]);
with MasterRequestMappings do
for i := 0 to ASource.MasterRequestMappings.Count - 1 do
Add(ASource.MasterRequestMappings.Strings[i]);
MasterMappingMode := ASource.MasterMappingMode;
MasterFields := ASource.MasterFields;
MasterOptions := ASource.MasterOptions;
DetailFields := ASource.DetailFields;
DetailOptions := ASource.DetailOptions;
RemoteUpdatesOptions := ASource.RemoteUpdatesOptions;
StreamingOptions := ASource.StreamingOptions;
RemoteFetchEnabled := ASource.RemoteFetchEnabled;
finally
ATarget.EnableEventHandlers; // <--- INDISPENSABLE!!!!!!!!!!!!!!!!!!!!!!!!!
end;
end;
if not RemoteUpdate then
begin
ATarget.LogChanges := False;
ATarget.RemoteFetchEnabled := False;
end;
Result := ATarget;
end;
procedure CloneDataTable(const ASource : TDACDSDataTable;
var ATarget : TDACDSDataTable; RemoteUpdate: Boolean);
var
i : Integer;
begin
with ATarget do
begin
if not Assigned(ASource.RemoteDataAdapter) then
raise Exception.Create('No est<73> asignado RemoteDataAdapter (' + ASource.Name + ')');
RemoteDataAdapter.Assign(ASource.RemoteDataAdapter);
BusinessRulesID := ASource.BusinessRulesID;
Randomize;
Name := ASource.Name + '_' + IntToStr(Random(MAXINT));
LogicalName := ASource.LogicalName;
Params.AssignParamCollection(ASource.Params);
if Assigned(ASource.LocalSchema) then
LocalSchema := ASource.LocalSchema
else begin
ATarget.Fields.AssignFieldCollection(ASource.Fields); // o tambi<62>n ATarget.LoadSchema;
end;
with MasterParamsMappings do
for i := 0 to ASource.MasterParamsMappings.Count - 1 do
Add(ASource.MasterParamsMappings.Strings[i]);
with MasterRequestMappings do
for i := 0 to ASource.MasterRequestMappings.Count - 1 do
Add(ASource.MasterRequestMappings.Strings[i]);
MasterMappingMode := ASource.MasterMappingMode;
MasterFields := ASource.MasterFields;
MasterOptions := ASource.MasterOptions;
DetailFields := ASource.DetailFields;
DetailOptions := ASource.DetailOptions;
RemoteUpdatesOptions := ASource.RemoteUpdatesOptions;
StreamingOptions := ASource.StreamingOptions;
RemoteFetchEnabled := ASource.RemoteFetchEnabled;
end;
if not RemoteUpdate then
begin
ATarget.LogChanges := False;
ATarget.RemoteFetchEnabled := False;
end;
end;
function DeltaValuesAreDifferent(const aDelta : IDADelta): boolean;
var
i, x : integer;
OldNewAreDifferent: boolean;
begin
OldNewAreDifferent := FALSE;
for i := 0 to (aDelta.Count-1) do
begin
for x := 0 to (aDelta.LoggedFieldCount-1) do
begin
OldNewAreDifferent := (aDelta.Changes[i].OldValues[x] <> aDelta.Changes[i].NewValues[x]);
if OldNewAreDifferent then
Break; // Abandon iteration at the first difference between old and new.
end;
if OldNewAreDifferent then
Break; // Abandon iteration at the first difference between old and new.
end;
result := OldNewAreDifferent;
end;
procedure DuplicarRegistro(ASource : TDADataTable; ATarget : TDADataTable; const WithPKKey: Boolean = False; const WithFKKey: Boolean = False; const Insertar: Boolean = True);
var
i, j: Integer;
ATargetField: TDAField;
ASourceField: TDAField;
ADetailFields : TStringList;
begin
if not ASource.Active then
ASource.Open;
if not ATarget.Active then
ATarget.Open;
ADetailFields := Nil;
// <20>ATarget es una tabla detalle?
if Assigned(ATarget.MasterSource) then
begin
case ATarget.MasterMappingMode of
//mmDataRequest: //Sin hacer;
mmParams, mmWhere: begin
//Creamos la lista de campos foreing key
ADetailFields := TStringList.Create;
ADetailFields.Sorted := True;
ADetailFields.Delimiter := ',';
ADetailFields.Duplicates := dupIgnore;
ADetailFields.Add(ATarget.DetailFields);
end;
end;
end
else
ADetailFields := Nil;
if Insertar then
ATarget.Insert
else
ATarget.Edit;
{ Hay que desactivar los eventos para que dejan de funcionar
las reglas de negocio y no nos interfieran en la copia
de valores de los campos. }
ATarget.DisableEventHandlers;
try
for i := 0 to ASource.Fields.Count - 1 do
begin
ASourceField := ASource.Fields[i];
ATargetField := ATarget.FindField(ASourceField.Name);
if Assigned(ATargetField) then
begin
// Los campos AutoInc no se rellenan y tampoco los campos que
// formen parte de la relaci<63>n maestro-detalle en el
// caso de que la tabla destino sea una tabla detalle.
if not WithPKKey then
begin
//Si no es campo clave
//Si no es autoinc (podria desaparecer pero no lo quitamos por si acaso
if not (ATargetField.InPrimaryKey) and
(ATargetField.DataType <> datAutoInc) then
begin
//En el caso de no copiar las claves foraneas
//Si no hay campos foreing key o los hay pero el campo a copiar no es uno de ellos
if not WithFKKey then
begin
if (not Assigned(ADetailFields) or not ADetailFields.Find(ATargetField.Name, j)) then
//Copiamos el campo
ATargetField.Value := ASourceField.Value;
end
else
//Copiamos el campo
ATargetField.Value := ASourceField.Value;
end;
end
else
//En el caso de no copiar las claves foraneas
//Si no hay campos foreing key o los hay pero el campo a copiar no es uno de ellos
if not WithFKKey then
begin
//Si no hay campos foreing key o los hay pero el campo a copiar no es uno de ellos
if (not Assigned(ADetailFields) or not ADetailFields.Find(ATargetField.Name, j)) then
//Copiamos el campo
ATargetField.Value := ASourceField.Value;
end
else
//Copiamos el campo
ATargetField.Value := ASourceField.Value;
end;
end;
finally
ATarget.EnableEventHandlers;
ATarget.Post;
end;
end;
procedure DuplicarRegistros(ASource : TDADataTable; ATarget : TDADataTable;
AModo : TModoDuplicarRegistros; APermitirRepetidos: Boolean = True;
Const WithDeltas: Boolean = True; Const WithPKKey: Boolean = False; Const WithFKKey: Boolean = False);
begin
if not ASource.Active then
ASource.Open;
if not ATarget.Active then
ATarget.Open;
try
//Para que no se generen deltas de inserci<63>n, y luego actue como un update
if not WithDeltas then
DesconectarTabla(ATarget);
if AModo = mdrActual then
DuplicarRegistro(ASource, ATarget, WithPKKey, WithFKKey) //ATarget.CloneSelectedRecord(ASource, True)
else begin
ASource.First;
while not ASource.EOF do
begin
if APermitirRepetidos then
DuplicarRegistro(ASource, ATarget, WithPKKey, WithFKKey)
else
begin
ATarget.First;
if not ATarget.Locate(ATarget.RecIDField.FieldName, ASource.GetRowRecIDValue, []) then
DuplicarRegistro(ASource, ATarget, WithPKKey, WithFKKey);
end;
ASource.Next;
end;
end;
finally
//Para que no se generen deltas de inserci<63>n, y luego actue como un update
if not WithDeltas then
ConectarTabla(ATarget);
end;
end;
procedure EnlazarMaestroDetalle(AMasterDataSource : TDADataSource;
ADetail : IDAStronglyTypedDataTable);
begin
if Assigned(ADetail) then
begin
// <20><><EFBFBD><EFBFBD><EFBFBD> MUY IMPORTANTE !!!!!!!!!!!!!!!!!!
//ADetail.DataTable.DisableEventHandlers;
try
ADetail.DataTable.MasterSource := AMasterDataSource;
finally
{ Hay que activar los eventos porque dejan de funcionar
las reglas de negocio al establecer la relaci<63>n
maestro-detalle. (Fallo de Data Abstract 3) }
//ADetail.DataTable.EnableEventHandlers;
end;
end;
end;
procedure DesconectarTabla (ATarget: TDADataTable);
begin
ATarget.RemoteFetchEnabled := False;
ATarget.LogChanges := False;
end;
procedure ConectarTabla (ATarget: TDADataTable);
begin
ATarget.RemoteFetchEnabled := True;
ATarget.LogChanges := True;
end;
procedure SetDataTableReadOnly(ADataTable: TDADataTable; const value: Boolean);
var
dtDetails : TList;
i : integer;
begin
if Assigned(ADataTable)
and (ADataTable.ReadOnly <> Value) then
begin
ADataTable.ReadOnly := Value;
dtDetails := ADataTable.GetDetailDataTables;
for i := 0 to dtDetails.Count - 1 do
TDADataTable(dtDetails.Items[i]).ReadOnly := ADataTable.ReadOnly;
end;
end;
end.