unit uRptPresupuestosCliente_Server; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, frxClass, frxDBSet, uDAScriptingProvider, uDADataTable, uDACDSDataTable, DB, uDAClasses, uDABINAdapter, uROTypes, uDAInterfaces, uDAMemDataTable, uDADataStreamer, uDABin2DataStreamer, frxGradient, frxChBox, frxCross, frxOLE, frxBarcode, frxRich, uDAEngine, IBSQL, IBDatabase, IBCustomDataSet, IBQuery, frxExportPDF, FactuGES_Intf; type TRptPresupuestosCliente = class(TDataModule) DADSCabecera: TDADataSource; DADSDetalles: TDADataSource; frxBarCodeObject1: TfrxBarCodeObject; frxOLEObject1: TfrxOLEObject; frxCrossObject1: TfrxCrossObject; frxCheckBoxObject1: TfrxCheckBoxObject; frxGradientObject1: TfrxGradientObject; frxDBCabecera: TfrxDBDataset; frxDBDetalles: TfrxDBDataset; tbl_Cabecera: TDAMemDataTable; frxReport: TfrxReport; IBDatabase1: TIBDatabase; IBTransaction1: TIBTransaction; DataSource1: TDataSource; DataSource2: TDataSource; capitulos: TIBQuery; DataSource3: TDataSource; detalles: TIBQuery; DataSource4: TDataSource; resumen: TIBQuery; resumenID: TIntegerField; resumenID_PRESUPUESTO: TIntegerField; resumenPOSICION: TIntegerField; resumenTIPO_DETALLE: TIBStringField; resumenCONCEPTO: TIBStringField; resumenIMPORTE_TOTAL: TIBBCDField; resumenVISIBLE: TSmallintField; frxDBCapitulos: TfrxDBDataset; frxDBResumen: TfrxDBDataset; DABin2DataStreamer1: TDABin2DataStreamer; cabecera: TIBQuery; cabeceraID: TIntegerField; cabeceraID_EMPRESA: TIntegerField; cabeceraFECHA_PRESUPUESTO: TDateField; cabeceraREFERENCIA: TIBStringField; cabeceraPORTADA: TMemoField; cabeceraMEMORIA: TMemoField; cabeceraOBSERVACIONES: TMemoField; cabeceraIMPORTE_NETO: TIBBCDField; cabeceraIMPORTE_PORTE: TIBBCDField; cabeceraDESCUENTO: TFloatField; cabeceraIMPORTE_DESCUENTO: TIBBCDField; cabeceraBASE_IMPONIBLE: TIBBCDField; cabeceraIVA: TFloatField; cabeceraID_CLIENTE: TIntegerField; cabeceraREFERENCIA_CLIENTE: TIBStringField; cabeceraCLIENTE_FINAL: TIBStringField; cabeceraNIF_CIF: TIBStringField; cabeceraNOMBRE: TIBStringField; cabeceraIMPORTE_IVA: TIBBCDField; cabeceraIMPORTE_TOTAL: TIBBCDField; cabeceraPERSONA_CONTACTO: TIBStringField; cabeceraCALLE: TIBStringField; cabeceraPOBLACION: TIBStringField; cabeceraPROVINCIA: TIBStringField; cabeceraCODIGO_POSTAL: TIBStringField; DADSCapitulos: TDADataSource; tbl_Capitulos: TDAMemDataTable; DADSResumen: TDADataSource; tbl_Resumen: TDAMemDataTable; frxPDFExport1: TfrxPDFExport; DADSInformeListadoPresupuestos: TDADataSource; tbl_InformeListadoPresupuestos: TDAMemDataTable; frxDBInformeListadoPresupuestos: TfrxDBDataset; frxDBInformeListadoPresupuestosResumen: TfrxDBDataset; DADSInformeListadoPresupuestosResumen: TDADataSource; tbl_InformeListadoPresupuestosResumen: TDAMemDataTable; tbl_Detalles: TDAMemDataTable; frxDBInformeListadoPresupuestosGrafComp: TfrxDBDataset; DASInformeListadoPresupuestosGrafComp: TDADataSource; tbl_InformeListadoPresupuestosGrafCompMensual: TDAMemDataTable; tbl_InformeListadoPresupuestosGrafCompTrimestral: TDAMemDataTable; tbl_InformeListadoPresupuestosGrafCompSemestral: TDAMemDataTable; frxDBInformeListadoClientesMayorImporteResumen: TfrxDBDataset; DADSInformeListadoClientesMayorImporteResumen: TDADataSource; tbl_InformeListadoClientesMayorImporteResumen: TDAMemDataTable; tbl_InformeListadoClientesMayorNPresupuestosResumen: TDAMemDataTable; DADSInformeListadoClientesMayorNPresupuestosResumen: TDADataSource; frxDBInformeListadoClientesMayorNPresupuestosResumen: TfrxDBDataset; tbl_InformeListadoClientesMayorNAnuladosResumen: TDAMemDataTable; DADSInformeListadoClientesMayorNAnuladosResumen: TDADataSource; frxDBInformeListadoClientesMayorNAnuladosResumen: TfrxDBDataset; schReport: TDASchema; DataDictionary: TDADataDictionary; procedure DataModuleCreate(Sender: TObject); procedure DataModuleDestroy(Sender: TObject); function frxReportUserFunction(const MethodName: string; var Params: Variant): Variant; private FConnection: IDAConnection; FIdEmpresa: Integer; FFechaInicio: Variant; FFechaFin: Variant; FListaIDClientes: TIntegerArray; FListaNombresClientes: TStringList; FImporteMinimo: Currency; FDesglosado : Boolean; FAno1: Variant; FAno2: Variant; FIntervalo: Variant; FTopN: Integer; procedure _GenerarPresupuesto(const AID : Integer; const VerSello: Integer); procedure PrepararTablaInforme(ATabla: TDAMemDataTable); procedure PrepararTablaResumenInforme(ATabla: IDADataset); procedure PrepararTablaInformeGrafComp(ATabla: TDAMemDataTable); procedure PrepararTablaResumenInformeGrafComp(ATabla: TDAMemDataTable); function _GenerarInforme(const TipoInforme: String): Binary; procedure IniciarParametrosInforme; procedure RecuperarNombresClientes; public function GenerarPresupuesto(const ListaID : TIntegerArray; const VerSello: Integer): Binary; function GenerarPresupuestoEnPDF(const ListaID : TIntegerArray; const VerSello: Integer): Binary; function GenerarInformeListadoPresupuestos(const IdEmpresa: Integer; const FechaInicio: Variant; const FechaFin: Variant; const ListaIDClientes: TIntegerArray; const Desglosado: Boolean; const ImporteMinimo: Currency): Binary; function GenerarInformePresupuestosGrafComp(const IdEmpresa: Integer; const Intervalo: Variant; const Ano1: Variant; const Ano2: Variant; const ListaIDClientes: TIntegerArray; const TopN: Integer; const Serie: Variant): Binary; end; implementation {$R *.dfm} uses uSistemaFunc, StrUtils, uDataModuleServer, schPresupuestosClienteClient_Intf, uROServer, DataAbstract4_Intf, srvGestorInformes_Impl; const rptInforme = 'InfPresupuestoCliente.fr3'; rptInformeListadoPresupuestosDesglosado = 'InformeListadoPresupuestosDesglosado.fr3'; rptInformeListadoPresupuestos = 'InformeListadoPresupuestos.fr3'; rptInformeListadoPresupuestosClienteGrafComp = 'InformeListadoPresupuestosClienteGrafComp.fr3'; { Dataset names for schReport } ds_InformeListadoPresupuestosResumen = 'InformeListadoPresupuestosResumen'; procedure TRptPresupuestosCliente.DataModuleCreate(Sender: TObject); begin schReport.ConnectionManager := dmServer.ConnectionManager; FConnection := dmServer.DarNuevaConexion; frxReport.EngineOptions.NewSilentMode := simReThrow; frxDBCabecera.DataSource := DADSCabecera; frxDBCabecera.CloseDataSource := False; frxDBCapitulos.DataSource := DADSCapitulos; frxDBCapitulos.CloseDataSource := False; frxDBDetalles.DataSource := DADSDetalles; frxDBDetalles.CloseDataSource := False; frxDBResumen.DataSource := DADSResumen; frxDBResumen.CloseDataSource := False; FListaNombresClientes := TStringList.Create; with tbl_Detalles do begin MasterSource := DADSCapitulos; MasterFields := 'ID'; DetailFields := 'ID_CAPITULO'; MasterMappingMode := mmWhere; end; end; function TRptPresupuestosCliente.GenerarPresupuestoEnPDF(const ListaID: TIntegerArray; const VerSello: Integer): Binary; var i: Integer; begin Result := Binary.Create; try //Vamos generando todos y cada uno de los presupuestos recibidos for i := 0 to ListaID.Count - 1 do _GenerarPresupuesto(ListaID.Items[i], VerSello); frxPDFExport1.Stream := Result; frxReport.Export(frxPDFExport1) finally end; end; procedure TRptPresupuestosCliente.IniciarParametrosInforme; var ATextos : TStringList; ACadena : String; begin ATextos := TStringList.Create; try if (not VarIsNull(FFechaInicio)) and (not VarIsNull(FFechaFin)) then ACadena := Format('Fechas de presupuesto desde el %s hasta el %s', [VarToStr(FFechaInicio), VarToStr(FFechaFin)]) else ACadena := 'Sin rango de fechas'; ATextos.Add(ACadena); ACadena := ''; if (FImporteMinimo > 0) then begin ACadena := Format('Presupuestos con importe superior a %m', [FImporteMinimo]); ATextos.Add(ACadena); ACadena := ''; end; if Assigned(FListaIDClientes) and (FListaIDClientes.Count > 0) then begin RecuperarNombresClientes; ACadena := FListaNombresClientes.Text; end else begin ACadena := 'Todos los clientes'; if FDesglosado then ACadena := ACadena + ' (desglosados)' end; ATextos.Add(ACadena); ACadena := ''; frxReport.Variables.Variables['TextoParametros'] := ATextos.Text; finally FreeAndNil(ATextos); end; end; procedure TRptPresupuestosCliente.PrepararTablaInforme(ATabla: TDAMemDataTable); var Condicion: TDAWhereExpression; i: Integer; begin // Filtrar el informe por empresa with ATabla.DynamicWhere do begin // (ID_EMPRESA >= ID) Condicion := NewBinaryExpression(NewField('', fld_PresupuestosClienteID_EMPRESA), NewConstant(FIdEmpresa, datInteger), dboEqual); if IsEmpty then Expression := Condicion else Expression := NewBinaryExpression(Expression, Condicion, dboAnd); end; // Filtrar el informe por fechas if not VarIsNull(FFechaInicio) and not VarIsNull(FFechaFin) then begin with ATabla.DynamicWhere do begin // (FECHA_INICIO between FECHA_FIN) Condicion := NewBinaryExpression(NewField('', fld_PresupuestosClienteFECHA_PRESUPUESTO), NewConstant(FFechaInicio, datDateTime), dboGreaterOrEqual); Condicion := NewBinaryExpression(NewBinaryExpression(NewField('', fld_PresupuestosClienteFECHA_PRESUPUESTO), NewConstant(FFechaFin, datDateTime), dboLessOrEqual), Condicion, dboAnd); if IsEmpty then Expression := Condicion else Expression := NewBinaryExpression(Expression, Condicion, dboAnd); end; end; // Filtrar el informe por proveedor if Assigned(FListaIDClientes) then begin with ATabla.DynamicWhere do begin for i := 0 to FListaIDClientes.Count - 1 do begin // (ID_CLIENTE = ID) Condicion := NewBinaryExpression(NewField('', fld_PresupuestosClienteID_CLIENTE), NewConstant(FListaIDClientes.Items[i], datInteger), dboEqual); if IsEmpty then Expression := Condicion else Expression := NewBinaryExpression(Expression, Condicion, dboAnd); end; end; end; // Filtrar el informe por importe minimo if (FImporteMinimo > 0) then begin with ATabla.DynamicWhere do begin // (IMPORTE_TOTAL > ImporteMinimo) Condicion := NewBinaryExpression(NewField('', fld_PresupuestosClienteIMPORTE_TOTAL), NewConstant(FImporteMinimo, datCurrency), dboGreaterOrEqual); if IsEmpty then Expression := Condicion else Expression := NewBinaryExpression(Expression, Condicion, dboAnd); end; end; end; procedure TRptPresupuestosCliente.PrepararTablaInformeGrafComp( ATabla: TDAMemDataTable); begin if ATabla.Active then ATabla.Active := False; DASInformeListadoPresupuestosGrafComp.DataTable := ATabla; ATabla.ParamByName('ID_EMPRESA1').AsInteger := FIdEmpresa; ATabla.ParamByName('ID_EMPRESA2').AsInteger := FIdEmpresa; ATabla.ParamByName('ANO1').AsVariant := FAno1; ATabla.ParamByName('ANO2').AsVariant := FAno2; ATabla.Active := True; end; procedure TRptPresupuestosCliente.PrepararTablaResumenInforme(ATabla: IDADataset); var i: Integer; AWhereStr : String; begin // Filtrar el informe por empresa AWhereStr := ' (' + fld_PresupuestosClienteID_EMPRESA + ' = ' + IntToStr(FIdEmpresa) + ') '; // Filtrar el informe por fechas if not VarIsNull(FFechaInicio) and not VarIsNull(FFechaFin) then begin if Length(AWhereStr) > 0 then AWhereStr := AWhereStr + 'AND'; AWhereStr := AWhereStr + ' (' + fld_PresupuestosClienteFECHA_PRESUPUESTO + ' between ''' + ReplaceStr(VarToStr(FFechaInicio),'/','.') + ''' and ''' + ReplaceStr(VarToStr(FFechaFin),'/','.') + ''') '; end; // Filtrar el informe por cliente if Assigned(FListaIDClientes) then begin for i := 0 to FListaIDClientes.Count - 1 do begin if Length(AWhereStr) > 0 then AWhereStr := AWhereStr + 'AND'; AWhereStr := AWhereStr + ' (' + fld_PresupuestosClienteID_CLIENTE + ' = ' + IntToStr(FListaIDClientes.Items[i]) + ') '; end; end; // Filtrar el informe por importe minimo if (FImporteMinimo > 0) then begin if Length(AWhereStr) > 0 then AWhereStr := AWhereStr + 'AND'; AWhereStr := AWhereStr + ' (' + fld_PresupuestosClienteIMPORTE_TOTAL + ' >= ' + CurrToStr(FImporteMinimo) + ') '; end; ATabla.Where.AddText(AWhereStr); end; procedure TRptPresupuestosCliente.PrepararTablaResumenInformeGrafComp( ATabla: TDAMemDataTable); begin if ATabla.Active then ATabla.Active := False; ATabla.ParamByName('ID_EMPRESA').AsInteger := FIdEmpresa; ATabla.ParamByName('ANO').AsVariant := FAno1; ATabla.ParamByName('NTOP').AsInteger := FTopN; ATabla.Active := True; end; procedure TRptPresupuestosCliente.RecuperarNombresClientes; var AContactosService : IsrvContactos; Intf : IInterface; AClientID : TGUID; ATableNameArray: StringArray; ATableRequestInfoArray: TableRequestInfoArray; ATableRequestInfo: TableRequestInfoV5; AStream: TMemoryStream; ADataTable: TDAMemDataTable; i: Integer; AWhereBuilder : TDAWhereBuilder; ACondicion : TDAWhereExpression; begin CreateGUID(AClientID); GetClassFactory('srvContactos').CreateInstance(AClientID, Intf); if Assigned(Intf) then begin AContactosService := Intf as IsrvContactos; ATableNameArray := StringArray.Create; ATableRequestInfoArray := TableRequestInfoArray.Create; AWhereBuilder := TDAWhereBuilder.Create; try ATableNameArray.Add('Clientes'); ATableRequestInfo := TableRequestInfoV5.Create; with ATableRequestInfo do begin IncludeSchema := True; MaxRecords := -1; UserFilter := ''; AWhereBuilder.Clear; with AWhereBuilder do for i := 0 to FListaIDClientes.Count - 1 do begin ACondicion := NewBinaryExpression( NewBinaryExpression(NewField('', 'ID'), NewConstant(FListaIDClientes[i], datInteger), dboEqual), NewBinaryExpression(NewField('', 'ID_EMPRESA'), NewConstant(FIdEmpresa, datInteger), dboEqual), dboAnd); if not AWhereBuilder.IsEmpty then Expression := NewBinaryExpression(Expression, ACondicion, dboOr) else Expression := ACondicion; end; WhereClause := AWhereBuilder.ExpressionToXmlNode(AWhereBuilder.Expression); end; try ATableRequestInfoArray.Add(ATableRequestInfo); AStream := AContactosService.GetData(ATableNameArray, ATableRequestInfoArray); if Assigned(AStream) then begin ADataTable := TDAMemDataTable.Create(nil); try ADataTable.Name := 'Clientes'; ADataTable.LocalDataStreamer := DABin2DataStreamer1; ADataTable.RemoteFetchEnabled := False; DABin2DataStreamer1.ReadDataset(AStream, ADataTable, True); ADataTable.Open; FListaNombresClientes.Clear; for i := 0 to ADataTable.RecordCount - 1 do begin FListaNombresClientes.Add(ADataTable.FieldByName('NOMBRE').AsString); ADataTable.Next; end; finally FreeANDNil(ADataTable); end; end; except on e: Exception do dmServer.EscribirLog(e.Message); end; finally FreeANDNIL(ATableRequestInfoArray); FreeANDNIL(ATableNameArray); FreeANDNIL(AWhereBuilder); end; end; end; procedure TRptPresupuestosCliente.DataModuleDestroy(Sender: TObject); begin tbl_Cabecera.Active := False; tbl_Capitulos.Active := False; tbl_Detalles.Active := False; tbl_Resumen.Active := False; FreeANDNIL(FListaNombresClientes); end; function TRptPresupuestosCliente.frxReportUserFunction(const MethodName: string; var Params: Variant): Variant; var AText : String; begin Result := ''; if (MethodName = 'PONERJUSTIFICACIONCOMPLETA') then begin AText := VarToStr(Params[0]); Result := ReplaceStr(AText, '\pard', '\qj'); end; end; function TRptPresupuestosCliente.GenerarInformePresupuestosGrafComp( const IdEmpresa: Integer; const Intervalo, Ano1, Ano2: Variant; const ListaIDClientes: TIntegerArray; const TopN: Integer; const Serie: Variant): Binary; var AStream: TMemoryStream; AInforme: Variant; begin FConnection.BeginTransaction; //<--- Creo que no va a hacer falta. "PUES SI ES NECESARIO" AStream := TMemoryStream.Create; try //Inicializamos parametros FIdEmpresa := IdEmpresa; FAno1 := Ano1; FAno2 := Ano2; FIntervalo := Intervalo; FTopN := TopN; if Assigned(FListaIDClientes) then FListaIDClientes.Free; FListaIDClientes := ListaIDClientes; //Preparamos la tabla correspondiente y la abrimos para el informe if (FIntervalo = CTE_MENSUAL) then PrepararTablaInformeGrafComp(tbl_InformeListadoPresupuestosGrafCompMensual) else if (FIntervalo = CTE_TRIMESTRAL) then PrepararTablaInformeGrafComp(tbl_InformeListadoPresupuestosGrafCompTrimestral) else PrepararTablaInformeGrafComp(tbl_InformeListadoPresupuestosGrafCompSemestral); //Se preparan las tablas del listado resumen del informe PrepararTablaResumenInformeGrafComp(tbl_InformeListadoClientesMayorImporteResumen); PrepararTablaResumenInformeGrafComp(tbl_InformeListadoClientesMayorNPresupuestosResumen); PrepararTablaResumenInformeGrafComp(tbl_InformeListadoClientesMayorNAnuladosResumen); Result := Binary.Create; AInforme := DarRutaFichero(DarRutaInformes, rptInformeListadoPresupuestosClienteGrafComp, IntTostr(FIdEmpresa)); if VarIsNull(AInforme) then raise Exception.Create (('Error Servidor: GenerarInformePresupuestosGrafComp, no encuentra informe ' + rptInformeListadoPresupuestosClienteGrafComp)); frxReport.LoadFromFile(AInforme, True); // IniciarParametrosInforme; frxReport.Variables.Variables['Ano1']:= Ano1; frxReport.Variables.Variables['Ano2']:= Ano2; frxReport.Variables.Variables['Serie']:= Serie; frxReport.PrepareReport(False); frxReport.PreviewPages.SaveToStream(Result); finally AStream.Free; FConnection.RollbackTransaction; //<--- Creo que no va a hacer falta. "PUES SI ES NECESARIO" end; end; function TRptPresupuestosCliente.GenerarInformeListadoPresupuestos( const IdEmpresa: Integer; const FechaInicio, FechaFin: Variant; const ListaIDClientes: TIntegerArray; const Desglosado: Boolean; const ImporteMinimo: Currency): Binary; var ATipoInforme: String; AStream: TMemoryStream; dsMaster: IDADataset; begin FConnection.BeginTransaction; //<--- Creo que no va a hacer falta. "PUES SI ES NECESARIO" AStream := TMemoryStream.Create; try //Inicializamos parametros FIdEmpresa := IdEmpresa; FFechaInicio := FechaInicio; FFechaFin := FechaFin; FImporteMinimo := ImporteMinimo; if Assigned(FListaIDClientes) then FListaIDClientes.Free; FListaIDClientes := ListaIDClientes; if tbl_InformeListadoPresupuestos.Active then tbl_InformeListadoPresupuestos.Active := False; PrepararTablaInforme(tbl_InformeListadoPresupuestos); //Se prepara la tabla del listado resumen del informe if tbl_InformeListadoPresupuestosResumen.Active then tbl_InformeListadoPresupuestosResumen.Active := False; dsMaster := schReport.NewDataset(FConnection, ds_InformeListadoPresupuestosResumen, [], [], False); PrepararTablaResumenInforme(dsMaster); //Esto se hace para rellenar la tabla del datamodule que usa el informe. dsMaster.Open; AStream.Clear; DABin2DataStreamer1.WriteDataset(AStream, dsMaster, [woRows, woSchema], -1); DABin2DataStreamer1.ReadDataset(AStream, tbl_InformeListadoPresupuestosResumen, TRUE, '', TRUE, TRUE); //DESGLOSADO POR CLIENTE EN ESTE INFORME NO SE DESGLOSARÁ POR CLIENTE FDesglosado := Desglosado; if FDesglosado then ATipoInforme := rptInformeListadoPresupuestosDesglosado else ATipoInforme := rptInformeListadoPresupuestos; //Finalmente se abren las tablas del informe tbl_InformeListadoPresupuestos.Active := True; tbl_InformeListadoPresupuestosResumen.Active := True; Result := _GenerarInforme(ATipoInforme); finally AStream.Free; dsMaster := Nil; FConnection.RollbackTransaction; //<--- Creo que no va a hacer falta. "PUES SI ES NECESARIO" end; end; function TRptPresupuestosCliente.GenerarPresupuesto(const ListaID: TIntegerArray; const VerSello: Integer): Binary; var i: Integer; begin Result := Binary.Create; try //Vamos generando todos y cada uno de los presupuestos recibidos for i := 0 to ListaID.Count - 1 do _GenerarPresupuesto(ListaID.Items[i], VerSello); frxReport.PreviewPages.SaveToStream(Result); finally end; end; function TRptPresupuestosCliente._GenerarInforme(const TipoInforme: String): Binary; var AInforme: Variant; begin Result := Binary.Create; AInforme := DarRutaFichero(DarRutaInformes, TipoInforme, IntToStr(FIdEmpresa)); if VarIsNull(AInforme) then raise Exception.Create (('Error Servidor: _GenerarInforme, no encuentra informe ' + TipoInforme)); frxReport.LoadFromFile(AInforme, True); IniciarParametrosInforme; frxReport.PrepareReport(False); frxReport.PreviewPages.SaveToStream(Result); end; procedure TRptPresupuestosCliente._GenerarPresupuesto(const AID: Integer; const VerSello: Integer); var AInforme: Variant; begin FConnection.BeginTransaction; //<--- Creo que no va a hacer falta. "PUES SI ES NECESARIO" try tbl_Cabecera.Active := False; tbl_Capitulos.Active := False; tbl_Detalles.Active := False; tbl_Resumen.Active := False; tbl_Cabecera.ParamByName('ID').AsInteger := AID; tbl_Capitulos.ParamByName('ID_PRESUPUESTO').AsInteger := AID; tbl_Detalles.ParamByName('ID_PRESUPUESTO').AsInteger := AID; tbl_Resumen.ParamByName('ID_PRESUPUESTO').AsInteger := AID; // Se asignan los parametros en este orden para que funcionen // dentro de las relaciones maestro-detalle (capítulos y conceptos). tbl_Cabecera.Active := True; tbl_Capitulos.Active := True; tbl_Detalles.Active := True; tbl_Resumen.Active := True; AInforme := DarRutaFichero(DarRutaInformes, rptInforme, tbl_Cabecera.FieldByName('ID_EMPRESA').AsString); if VarIsNull(AInforme) then raise Exception.Create (('Error Servidor: _GenerarPresupuesto, no encuentra informe ' + rptInforme)); frxReport.LoadFromFile(AInforme, True); frxReport.Variables.Variables['VerSello'] := IntToStr(VerSello); frxReport.AddFunction('function PONERJUSTIFICACIONCOMPLETA(ARTFText : String): String', 'User Function',''); frxReport.PrepareReport(False); finally FConnection.RollbackTransaction; //<--- Creo que no va a hacer falta. "PUES SI ES NECESARIO" end; end; end.