FastReport 空白対応の固定行複数明細

## 【環境】
Delphi 10.2.2
FastReport 5

## やりたいこと
- ヘッダーグループ毎に改ページを行う
- 明細行は3行固定、2行以下の場合空白行を挿入したい
- 明細行が4行以上の場合も改ページ必要
- 1ページに4項目、同一のヘッダー情報、明細情報を繰り返す（最小限のコードで保守を楽にしたい）

## やりたい表示処理
```
ヘッダー(1項目)	
明細1	
明細2　or　空白行	
明細3　or　空白行	
フッター	
ヘッダー(2項目)	
明細1	
明細2　or　空白行	
明細3　or　空白行	
フッター	
ヘッダー(3項目)	
明細1	
明細2　or　空白行	
明細3　or　空白行	
フッター	
ヘッダー(4項目)	
明細1	
明細2　or　空白行	
明細3　or　空白行	
フッター	
```
## 成果物サンプル

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/618527/f399eed2-8c87-3a3f-d4c6-431d52eb9673.png" width=60%>

## フロー
- DB準備、加工
    - TFDQuery クエリエディタ作成
    - TCliendDataSet フィールドセット
    - TfrxDBDataset 更新
    
- fastReport作成
    - fastReport データ連携 
    - fastReport Page作成
    - fastReport Code作成
    - fastReoprt イベント連携

## DB
### 加工前
ヘッダー＆明細
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/618527/b93aafb3-f9f4-9eb4-a291-902ae498a7c6.png" width=40%><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/618527/9d034e15-dc65-bb21-d538-0bbd0a7975b2.png" width=30%>

### 加工後
ヘッダー＆明細にキーNoとMAX3行Noをセット
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/618527/93bd2600-59a8-3f51-92ee-d5a1e4502c5a.png" width=40%><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/618527/efb5a477-c627-b490-4ab3-68e068f522e7.png" width=40%>

順序的に明細加工から作成します。
```SQL:明細加工SQL文
select  DENSE_RANK() OVER(ORDER BY T.OldMTNO) AS キーNo,
T.NewGNO AS MAX3行,T.MTGNO AS 明細No, T.MHNO AS 見積No,MTSHNM AS 品名
FROM
(select --3行明細のため%3している
(case when (MTGNO > 3) and (MTGNO % 3 = 0) then (RANK() OVER(ORDER BY MHIRDT,MHTKCD))+(MTGNO/3-1)
 else  
  (case when (MTGNO > 3) and (MTGNO % 3 = 2) then (RANK() OVER(ORDER BY MHIRDT,MHTKCD))+(MTGNO/3) 
 else 
  (case when (MTGNO > 3) and (MTGNO % 3 = 1) then (RANK() OVER(ORDER BY MHIRDT,MHTKCD))+(MTGNO/3) 
 else 
  (RANK() OVER(ORDER BY MHIRDT,MHTKCD)) end)end)end) AS OldMTNO,

(case when (MTGNO % 3 = 1) then 1 
 else
  (case when (MTGNO % 3 = 2) then 2 
   else 
    (case when (MTGNO % 3 = 0) then 3 
	 else 
	  0 end) end) end) as NewGNO,
 
 MH.*,
 MM.*

from MTMFLP AS MM
LEFT JOIN MTHFLP AS MH
ON MM.MTNO = MH.MHNO) AS T


WHERE 1=1
--and T.MHTKCD in ('002','003','004') //最初は数件で試した方がよい
ORDER BY T.MHNO,T.MTGNO --ユニークになるように
```
```SQL:ヘッダー加工SQL文
SELECT  DENSE_RANK() OVER(ORDER BY T.OldMTNO) AS キーNo,
T.MHIRDT AS 見積日,T.MHNO AS 見積No,T.MHTKNM AS 得意先名
FROM
(SELECT　--3行明細のため%3している
(case when (MTGNO > 3) and (MTGNO % 3 = 0) then (RANK() OVER(ORDER BY MHIRDT,MHTKCD))+(MTGNO/3-1)
 else  
  (case when (MTGNO > 3) and (MTGNO % 3 = 2) then (RANK() OVER(ORDER BY MHIRDT,MHTKCD))+(MTGNO/3) 
 else 
  (case when (MTGNO > 3) and (MTGNO % 3 = 1) then (RANK() OVER(ORDER BY MHIRDT,MHTKCD))+(MTGNO/3) 
 else 
  (RANK() OVER(ORDER BY MHIRDT,MHTKCD)) end)end)end) AS OldMTNO,

(case when (MTGNO % 3 = 1) then 1 
 else
  (case when (MTGNO % 3 = 2) then 2 
   else 
    (case when (MTGNO % 3 = 0) then 3 
	 else 
	  0 end) end) end) as NewGNO,
 
 MH.*,
 MM.*

from MTMFLP AS MM
LEFT JOIN MTHFLP AS MH
ON MM.MTNO = MH.MHNO) AS T
WHERE 1=1
--and T.MHTKCD in ('002','003','004') //最初は数件で試した方がよい
GROUP BY T.OldMTNO,T.MHIRDT,T.MHNO,T.MHTKNM
ORDER BY T.MHNO --ユニークになるように
```


### TFDQuery（ヘッダー、明細）
各クエリエディタ作成で加工後のSQL文をセットする

### TCliendDataSet（ヘッダー、明細）
フィールドをセットする

### TfrxDBDataset（ヘッダー、明細）
フィールド更新する

## fastReport
### データ連携
コンポーネント展開＞レポート＞データ＞（ヘッダー、明細）DBDataSetを選択

### Page作成
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/618527/bac1c37d-08e7-87f7-b6e0-825f34f0cbff.png" width=80%>



#### グループヘッダー
- ヘッダー.キーNoをセット
- ヘッダー情報（見積№、納入年月日、得意先名など）が変われば改ページ
- 明細4行以降も改ページ

#### マスターデータ
- 明細をセット

#### ヘッダー
- 明細までの余白部分を設定

#### 詳細データ
- 明細データセット割当
- フィルターにヘッダー.キーNo=明細.キーNoで連携させる
- 詳細データは1行分のため今回はheight*3が明細部分の高さになる
- イベントセットMasterData1OnBeforePrint

#### フッター
- 子バンドを挿入用の装置
- Height0
- イベントセット
    - フッター1：Footer1OnBeforePrint
    - フッター2：Footer2OnBeforePrint


#### 子バンド
- 明細に空白行を挿入するための部分
- 詳細データと同じHeightに設定
- Child1は詳細データとフッターに設定している

#### オーバレイ
- ヘッダーフィールドはオーバレイ部分にセット
- ※オーバレイ作成時※ミシン目位置を最初に決める
再調整はかなり面倒です※

### Code作成
```PASCAL:fastReport Code
var
PageLine: integer;//現在の行数を出力します
PageMaxRow: integer =3;//ページあたりの最大行数を設定します
  
procedure MasterData1OnBeforePrint(Sender: TfrxComponent);
begin    
  PageLine := <line> mod PageMaxRow; //このコードは固定数の行に必要です                  
  child1.visible := False; //初期化  
  child2.visible := False; //初期化            
end;
  
//1,3,4項目用 フッターイベント                        
procedure Footer1OnBeforePrint(Sender: TfrxComponent);
var
  i: integer;
begin
  i := IIF(PageLine = 0, PageMaxRow, PageLine);                
  child1.visible := True;
        
  while i < PageMaxRow do
  begin
    i := i + 1;
    Engine.ShowBand(Child1);//空白のフォームを印刷する              
  end;        
  child1.visible := False;//初期化                               
end;
  
//2項目専用 フッターイベント    
procedure Footer2OnBeforePrint(Sender: TfrxComponent);
var
  i: integer;
begin
  i := IIF(PageLine = 0, PageMaxRow, PageLine);                
  child2.visible := True;     
   
  while i < PageMaxRow do
  begin
    i := i + 1;
    Engine.ShowBand(Child2);//空白のフォームを印刷する              
  end;        
  child2.visible := False;//初期化                               
end;

begin

end.
```

### イベント連携
各詳細データ： MasterData1OnBeforePrint
フッター1：Footer1OnBeforePrint
フッター2：Footer2OnBeforePrint
Pageから各バンドプロパティに設定する

## 苦労した点
明細行数を1,2,3－1,2,3と繰り返す部分
改ページ対応のためNewMTNOをセットする部分
一気にやろうとせずにステップごとに試行して組み合わせていった

## 参考サイト
[FastReport 自动换行与行高自适应及自动增加空行（翻訳してね）](https://www-programminghunter-com.translate.goog/article/5640326276/?_x_tr_sl=zh-CN&_x_tr_tl=ja&_x_tr_hl=ja&_x_tr_pto=sc)