Browse Source

* Support for Master-Detail relations in data manager

git-svn-id: trunk@38625 -
michael 7 years ago
parent
commit
492dc01483

+ 68 - 6
packages/fcl-report/src/fpreportdata.pp

@@ -20,6 +20,10 @@ Type
     // Override this to return a dataset which is owned by AOwner, and configured by AConfig.
     // The dataset must not be opened.
     Function CreateDataset(AOwner : TComponent; AConfig : TJSONObject) : TDataset; virtual; abstract;
+    // By default, master-detail is not possible. Let this return 'True' indicate that a dataset allows master-detail.
+    Class Function AllowMasterDetail : Boolean; virtual;
+    // This is called to set the master dataset on Detail
+    Class Procedure SetMasterDataset(ADetail,AMaster : TDataset); virtual;
     // Check if the configuration is valid. Return a string that describes the error(s)
     // If the return is an empty string, the data designer will not close.
     Class Function CheckConfig(AConfig : TJSONObject) : String; virtual;
@@ -36,12 +40,14 @@ Type
   private
     FConfig: TJSONObject;
     FDataType: String;
+    FMaster: String;
     FName: String;
     FReportData: TFPReportDatasetData;
     FRunReportDataItem: TFPReportDataItem;
     function GetJSONConfig: TJSONStringType;
     procedure SetConfig(AValue: TJSONObject);
     procedure SetJSONConfig(AValue: TJSONStringType);
+    procedure SetMaster(AValue: String);
     procedure SetName(AValue: String);
   Protected
     // To hold temporary references
@@ -63,6 +69,7 @@ Type
   Published
     property Name : String Read FName Write SetName;
     Property DataType : String Read FDataType Write FDataType;
+    property Master : String Read FMaster Write SetMaster;
     Property JSONConfig : TJSONStringType Read GetJSONConfig Write SetJSONConfig;
   end;
 
@@ -75,6 +82,7 @@ Type
   Public
     Function IndexOfRunData(aData : TFPReportDatasetData) : integer;
     Function IndexOfName(const aName : String): Integer;
+    Procedure CheckCircularReference(aMasterName : String; aItem : TFPReportDataDefinitionItem);
     Function FindDataByName(const aName : String): TFPReportDataDefinitionItem;
     Function AddData(const aName : String) : TFPReportDataDefinitionItem;
     Procedure SaveToJSON(O : TJSONObject);
@@ -148,7 +156,10 @@ Resourcestring
   SErrInvalidDataType = 'Invalid data type: "%s"';
   SErrInvalidJSONConfig = '%s: Invalid JSON Configuration';
   SErrUnknownDataType =   'Unknown report data type: %s';
-
+  SErrNoMasterDetailSupport = 'No master-detail support for class "%s"';
+  SErrOpeningDataset = 'Error opening data "%s" : Exception %s with message %s';
+  SErrInvalidDatasourceName = 'Invalid data source name : "%s"';
+  SErrCircularReference = 'Invalid master data source "%s", circular references not allowed.';
 
 implementation
 
@@ -333,6 +344,7 @@ Var
   I : Integer;
 
 begin
+  aReport.SaveDataToNames;
   For I:=0 to DataDefinitions.Count-1 do
     begin
     DD:=DataDefinitions[i];
@@ -364,16 +376,17 @@ procedure TFPCustomReportDataManager.ApplyToReport(aReport : TFPReport; Errors:
 
 Var
   I : Integer;
-  DesignD : TFPReportDataDefinitionItem;
+  MasterD,DesignD : TFPReportDataDefinitionItem;
   DatasetD : TFPReportDatasetData;
+  H : TFPReportDataHandler;
   L : TFPList;
   P : TComponent;
+  DDS,MDS : TDataset;
 
 begin
-  RemoveFromReport(aReport);
   P:=GetDatasetParent;
-  aReport.SaveDataToNames;
   aReport.ReportData.Clear;
+  // Create all datasets
   For I:=0 to DataDefinitions.Count-1 do
     begin
     DesignD:=DataDefinitions[i];
@@ -386,7 +399,7 @@ begin
     except
       On E : Exception do
         If Assigned(Errors) then
-          Errors.Add(Format('Error opening data "%s" : Exception %s with message %s',[DesignD.Name,E.ClassName,E.Message]))
+          Errors.Add(Format(SErrOpeningDataset, [DesignD.Name, E.ClassName, E.Message]))
         else
           Raise;
     end;
@@ -395,6 +408,19 @@ begin
     DatasetD.StartDesigning;    // set designing flag, or OI will not show reference to it.
     DesignD.RunReportDataItem:=aReport.ReportData.AddReportData(DatasetD);
     end;
+  // Set master-detail relations
+  For I:=0 to DataDefinitions.Count-1 do
+    begin
+    DesignD:=DataDefinitions[i];
+    if (DesignD.Master<>'') then
+      begin
+      H:=TFPCustomReportDataManager.GetTypeHandler(DesignD.DataType);
+      MasterD:=DataDefinitions.FindDataByName(DesignD.Master);
+      DDS:=(DesignD.RunReportDataItem.Data as TFPReportDatasetData).DataSet;
+      MDS:=(MasterD.RunReportDataItem.Data as TFPReportDatasetData).DataSet;
+      H.SetMasterDataset(DDS,MDS);
+      end;
+    end;
 end;
 
 class function TFPCustomReportDataManager.GetRegisteredTypes(AList: Tstrings): Integer;
@@ -486,6 +512,24 @@ begin
     Dec(Result);
 end;
 
+procedure TFPReportDataDefinitions.CheckCircularReference(aMasterName: String; aItem: TFPReportDataDefinitionItem);
+
+Var
+  DD : TFPReportDataDefinitionItem;
+
+begin
+  While (aMasterName<>'') do
+    begin
+    DD:=FindDataByName(aMasterName);
+    if (DD=Nil) then
+      raise EReportDataError.CreateFmt(SErrInvalidDatasourceName, [aMasterName]);
+    If (DD=aItem) then
+      raise EReportDataError.CreateFmt(SErrCircularReference, [aMasterName]);
+    aMasterName:=DD.Master;
+    end;
+
+end;
+
 function TFPReportDataDefinitions.FindDataByName(const aName: String): TFPReportDataDefinitionItem;
 
 var
@@ -503,7 +547,7 @@ function TFPReportDataDefinitions.AddData(const aName: String): TFPReportDataDef
 
 begin
   if (IndexOfName(aName)<>-1) then
-    raise EReportError.CreateFmt(SErrDuplicateData, [aName]);
+    raise EReportDataError.CreateFmt(SErrDuplicateData, [aName]);
   Result:=add as TFPReportDataDefinitionItem;
   Result.Name:=aName;
 end;
@@ -562,6 +606,15 @@ begin
   TFPCustomReportDataManager.RegisterConfigFrameClass(DataType,aClass);
 end;
 
+class function TFPReportDataHandler.AllowMasterDetail: Boolean;
+begin
+  Result:=False;
+end;
+
+Class procedure TFPReportDataHandler.SetMasterDataset(ADetail, AMaster: TDataset);
+begin
+  Raise EReportDataError.CreateFmt(SErrNoMasterDetailSupport,[ADetail.ClassName]);
+end;
 
 
 class function TFPReportDataHandler.CheckConfig(AConfig: TJSONObject): String;
@@ -612,6 +665,12 @@ begin
     end;
 end;
 
+procedure TFPReportDataDefinitionItem.SetMaster(AValue: String);
+begin
+  if FMaster=AValue then Exit;
+  FMaster:=AValue;
+end;
+
 procedure TFPReportDataDefinitionItem.SetName(AValue: String);
 begin
   if FName=AValue then Exit;
@@ -638,6 +697,7 @@ begin
     Config:=D.Config;
     Name:=D.Name;
     DataType:=D.DataType;
+    Master:=D.Master;
     end
   else
     inherited Assign(Source);
@@ -648,6 +708,7 @@ begin
   O.Add('name',Name);
   O.Add('type',DataType);
   O.Add('config',Config.Clone);
+  O.Add('master',Master);
 end;
 
 procedure TFPReportDataDefinitionItem.LoadFromJSON(O: TJSONObject);
@@ -661,6 +722,7 @@ begin
   C:=O.Get('config',TJSONObject(Nil));
   if Assigned(C) then
     Config:=C;
+  Master:=O.Get('master','');
 end;
 
 function TFPReportDataDefinitionItem.Clone(aNewName: String): TFPReportDataDefinitionItem;

+ 26 - 0
packages/fcl-report/src/fpreportdatasqldb.pp

@@ -68,11 +68,15 @@ Type
   end;
 
 
+  { TSQLDBReportDataHandler }
+
   TSQLDBReportDataHandler = Class(TFPReportDataHandler)
     Function CreateDataset(AOwner : TComponent; AConfig : TJSONObject) : TDataset; override;
     Class Function CheckConfig(AConfig: TJSONObject): String; override;
     Class Function DataType : String; override;
     Class Function DataTypeDescription : String; override;
+    Class Function AllowMasterDetail: Boolean; override;
+    Class Procedure SetMasterDataset(ADetail, AMaster: TDataset); override;
   end;
 
 
@@ -262,6 +266,28 @@ begin
   Result:='SQL Database server';
 end;
 
+class function TSQLDBReportDataHandler.AllowMasterDetail: Boolean;
+begin
+  Result:=True;
+end;
+
+class procedure TSQLDBReportDataHandler.SetMasterDataset(ADetail, AMaster: TDataset);
+
+Var
+  Q : TSQLQuery;
+  DS : TDatasource;
+
+begin
+  Q:=(ADetail as TSQLQuery);
+  DS:=Q.DataSource;
+  if DS=Nil then
+    begin
+    DS:=TDatasource.Create(Q);
+    Q.Datasource:=DS;
+    end;
+  DS.Dataset:=AMaster;
+end;
+
 initialization
   TSQLDBReportDataHandler.RegisterHandler;
   TFPReportConnector.Init;