* Support for Master-Detail relations in data manager

git-svn-id: trunk@38625 -
This commit is contained in:
michael 2018-03-25 17:22:31 +00:00
parent 1497b64804
commit 492dc01483
2 changed files with 94 additions and 6 deletions

View File

@ -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;

View File

@ -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;