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 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 WithKey: Boolean = False); procedure DuplicarRegistros(ASource : TDADataTable; ATarget : TDADataTable; AModo : TModoDuplicarRegistros; APermitirRepetidos: Boolean = True; Const WithDeltas: Boolean = True; Const WithKey: Boolean = False); procedure CopyDataTable(ASource : TDADataTable; ATarget : TDADataTable; const OnlySelectedRows : 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; 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; {procedure EliminarNoSeleccionados(const ASource : IDADataset; var ATarget : TDADataTable); var AObj : ISelectedRowList; i : Integer; begin if not Supports(ASource, ISelectedRowList, aObj) then Exit; // ATarget.DisableControls; // ATarget.DisableEventHandlers; ShowHourglassCursor; try ATarget.Open; ATarget.Last; while ATarget.RecordCount > AObj.SelectedRows.Count do begin if AObj.SelectedRows.IndexOf(ATarget.RecIDValue) < 0 then ATarget.Delete else ATarget.Prior; end; finally // ATarget.EnableControls; // ATarget.EnableControls; HideHourglassCursor; 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 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 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 Result := NIL; 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 if not Assigned(ASource.RemoteDataAdapter) then raise Exception.Create('No está asignado RemoteDataAdapter (' + ASource.Name + ')'); RemoteDataAdapter := ASource.RemoteDataAdapter; // RemoteDataAdapter.Assign(ASource.RemoteDataAdapter); BusinessRulesID := ASource.BusinessRulesID; Randomize; Name := ASource.Name + '_' + IntToStr(Random(MAXINT)); LogicalName := ASource.LogicalName; Params.AssignParamCollection(ASource.Params); Fields.AssignFieldCollection(ASource.Fields); // o también ATarget.LoadSchema; 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; Result := ATarget; end; procedure CloneDataTable(const ASource : TDACDSDataTable; var ATarget : TDACDSDataTable; RemoteUpdate: Boolean); var i : Integer; begin with ATarget do begin // ****** Inicio migración DA 5 {RemoteService := ASource.GetRemoteService; Adapter := ASource.GetAdapter;} if not Assigned(ASource.RemoteDataAdapter) then raise Exception.Create('No está asignado RemoteDataAdapter (' + ASource.Name + ')'); RemoteDataAdapter.Assign(ASource.RemoteDataAdapter); // ****** Fin migración DA 5 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 // ****** Inicio migración DA 5 { with SchemaCall do begin MethodName := ASource.SchemaCall.MethodName; for i := 0 to ASource.SchemaCall.Params.Count - 1 do begin with Params.Add do begin Name := ASource.SchemaCall.Params[i].Name; ParamType := ASource.SchemaCall.Params[i].ParamType; DataType := ASource.SchemaCall.Params[i].DataType; Value := ASource.SchemaCall.Params[i].Value; UserClassName := ASource.SchemaCall.Params[i].UserClassName; end; end; end; } // ****** Fin migración DA 5 ATarget.Fields.AssignFieldCollection(ASource.Fields); // o también ATarget.LoadSchema; end; // ****** Inicio migración DA 5 { with DataUpdateCall do begin MethodName := ASource.DataUpdateCall.MethodName; for i := 0 to ASource.DataUpdateCall.Params.Count - 1 do begin with Params.Add do begin Name := ASource.DataUpdateCall.Params[i].Name; ParamType := ASource.DataUpdateCall.Params[i].ParamType; DataType := ASource.DataUpdateCall.Params[i].DataType; Value := ASource.DataUpdateCall.Params[i].Value; UserClassName := ASource.DataUpdateCall.Params[i].UserClassName; end; end; end; with ScriptCall do begin MethodName := ASource.ScriptCall.MethodName; for i := 0 to ASource.ScriptCall.Params.Count - 1 do begin with Params.Add do begin Name := ASource.ScriptCall.Params[i].Name; ParamType := ASource.ScriptCall.Params[i].ParamType; DataType := ASource.ScriptCall.Params[i].DataType; Value := ASource.ScriptCall.Params[i].Value; UserClassName := ASource.ScriptCall.Params[i].UserClassName; end; end; end; with DataRequestCall do begin MethodName := ASource.DataRequestCall.MethodName; for i := 0 to ASource.DataRequestCall.Params.Count - 1 do begin with Params.Add do begin begin Name := ASource.DataRequestCall.Params[i].Name; ParamType := ASource.DataRequestCall.Params[i].ParamType; DataType := ASource.DataRequestCall.Params[i].DataType; Value := ASource.DataRequestCall.Params[i].Value; UserClassName := ASource.DataRequestCall.Params[i].UserClassName; end; end; end; end; } // ****** Fin migración DA 5 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 CopyDataTable(ASource : TDADataTable; ATarget : TDADataTable; const OnlySelectedRows : Boolean = False); var DABin: Binary; DAAdapter : TDABINAdapter; AFilter : String; AFiltered : Boolean; AObj : ISeleccionable; i : Integer; begin AFilter := ''; AFiltered := False; if OnlySelectedRows then begin if not Supports(ASource, ISeleccionable, aObj) then raise Exception.Create('El origen de datos no soporta la interfaz ISeleccionable (CopyDataTable)'); if ASource.Filtered then begin AFiltered := True; AFilter := ASource.Filter; ASource.Filtered := False; end; ASource.Filter := ''; if ASource.Active then ASource.Close; for i := 0 to AObj.SelectedRecords.Count - 1 do begin if (i > 0) then ASource.Filter := ASource.Filter + ' or '; ASource.Filter := ASource.Filter + '(ID = ' + IntToStr(AObj.SelectedRecords.Items[i]) + ')'; end; ASource.Filtered := True; end; DABin := Binary.Create; DAAdapter := TDABINAdapter.Create(nil); try ATarget.LogicalName := ASource.LogicalName; // We need to specify new dataset LogicalName ATarget.RemoteFetchEnabled := False; // "Desconectamos" la tabla destino del servidor if not ASource.Active then ASource.Open; ASource.First; DAAdapter.Initialize(DABin, aiWrite); DAAdapter.WriteDataset(DABin, ASource, [woRows]); DAAdapter.Initialize(DABin, aiReadFromBeginning); DAAdapter.ReadDataset(DABin, ATarget); DAAdapter.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; finally FreeAndNil(DABin); FreeAndNil(DAAdapter); end; end; procedure DuplicarRegistro(ASource : TDADataTable; ATarget : TDADataTable; Const WithKey: Boolean = False); 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; // ¿ATarget es una tabla detalle? if Assigned(ATarget.MasterSource) then begin case ATarget.MasterMappingMode of //mmDataRequest: //Sin hacer; mmParams: 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; ATarget.Insert; { 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ón maestro-detalle en el // caso de que la tabla destino sea una tabla detalle. if not WithKey then begin //Si no es campo clave if not (ATargetField.InPrimaryKey) and //Si no es autoinc (podria desaparecer pero no lo quitamos por si acaso (ATargetField.DataType <> datAutoInc) and //Si no hay campos foreing key o los hay pero el campo a copiar no es uno de ellos (not Assigned(ADetailFields) or not ADetailFields.Find(ATargetField.Name, j)) then //Copiamos el campo ATargetField.Value := ASourceField.Value; end else 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 WithKey: 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ón, y luego actue como un update if not WithDeltas then DesconectarTabla(ATarget); if AModo = mdrActual then DuplicarRegistro(ASource, ATarget, WithKey) //ATarget.CloneSelectedRecord(ASource, True) else begin ASource.First; while not ASource.EOF do begin if APermitirRepetidos then DuplicarRegistro(ASource, ATarget, WithKey) else begin ATarget.First; if not ATarget.Locate('ID', ASource.FieldByName('ID').AsVariant, []) then DuplicarRegistro(ASource, ATarget, WithKey); end; ASource.Next; end; end; finally //Para que no se generen deltas de inserció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 // ¡¡¡¡¡ 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ó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.