IBM i (旧AS400）の自己学習メモ　RPG Ⅳプログラミング編０７　検索一覧画面の画面開発（サブファイル）

# 自己学習メモインデックス

[IBM i (旧AS400）の自己学習メモ　インデックス](https://qiita.com/You_name_is_YU/items/3964849e7803c8eaf767)

# 前提

使用する技術としてはRPG Ⅳを利用しますが、ここに掲載するプログラムソースは、**フリーフォーマット**ではなく**定位置記入方式**を利用しています。

# 概要

得意先マスタ（MTOKISKP）の情報を検索して一覧表示する画面の開発を行いました。

# 得意先検索一覧画面の主な機能

- 得意先マスタの情報を一覧表示する
- １ページあたり７件の得意先情報を表示し、７件を超える場合は次のページへ遷移可能。
- 検索キーワードとして得意先ｶﾅのみ指定可能
    - 検索キーワードを含む得意先だけが一覧に表示されるわけではない。

![as400-1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246556/a431e7bd-88d2-f36e-becd-d28253da72cc.gif)

Webアプリで検索一覧画面を作る場合、簡単な画面であっても、最低でも以下のような機能はあるものです。

- 複数の検索対象項目に対して検索キーワードをあいまい検索
- 検索キーワードを含むレコードのみ検索結果の一覧画面に表示

でも、RPGプログラミングで上記のような機能の実現方法が分からず、`SETLL`によって検索キーワードに近いレコードにカーソルを合わせ、それ以降のデータをまとめて出力するようなしょぼい検索一覧画面しか作れませんでした。
（自己学習でRPGプログラミングに触れることが目的なので、機能の完成度はそこまで求めずに、とりあえず画面実装を進めることを優先しました。）

# ソースコード

## DDSソース

得意先名ｶﾅの項目を検索キーワードとするため、論理ファイルのキー項目として得意先名ｶﾅを指定した論理ファイルを作成。

```shell:MTOKISKP.dds
..... *. 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 ...+... 7 ...+... 8 A* 得意先マスタ
     A*
     A          R MTOKISKR
     A*
     A            TOKCD          5A         COLHDG('得意先コード')
     A            TOKNM         40O         COLHDG('得意先名')
     A            TOKKANA       80A         COLHDG('得意先名カナ')
     A            TOKTEL        15A         COLHDG('電話番号')
     A            TOKRMT         9P 0       COLHDG('信用限度額')
```

```shell:MTOKISKL01.dds
..... *. 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 ...+... 7 ...+... 8
     A*  論理：得意先マスタ
     A* キー：得意先名ｶﾅ
     A          R MTOKISKR                  PFILE(MTOKISKP)
     A          K TOKKANA
```

## DSPFのソース

```shell:DSP03.dspf
..... *. 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 ...+... 7 ...+... 8
     A*%%TS  SD  20200509  092445  YAMAGUCHI   REL-V7R4M0  5770-WDS
     A*%%EC
     A                                      DSPSIZ(24 80 *DS3)
     A                                      PRINT(*LIBL/QPRINT)
     A                                      CF03(03 '終了')
     A                                      CF12(12 '戻る')
     A          R PANEL01
     A*%%TS  SD  20200509  014350  YAMAGUCHI   REL-V7R4M0  5770-WDS
     A                                      OVERLAY
     A                                  1  3'DSP03'
     A                                  1 35'得意先検索一覧'
     A                                      DSPATR(RI)
     A                                      COLOR(BLU)
     A                                  1 70DATE(*JOB *YY)
     A                                      EDTCDE(Y)
     A                                  5  2'得意先ｶﾅ・・・・・　'
     A                                      COLOR(WHT)
     A            P1@KEYWORD    40A  B  5 27COLOR(WHT)
     A          R PANEL99
     A*%%TS  SD  20200509  092445  YAMAGUCHI   REL-V7R4M0  5770-WDS
     A                                 23  3'F3=終了'
     A                                      COLOR(BLU)
     A                                 23 14'F12=戻る'
     A                                      COLOR(BLU)
     A          R M1
     A                                      OVERLAY
     A            M1MSGID        7A  O 24  2
     A                                      DSPATR(BL HI)
     A            M1MSG         67A  O 24 10
     A                                      DSPATR(BL HI)
     A          R SFL01                     SFL
     A*%%TS  SD  20200509  015416  YAMAGUCHI   REL-V7R4M0  5770-WDS
     A            SF@NO          2  0  10  2EDTCDE(Z)
     A            SF@TOKCD       5     10  5
     A            SF@TOKNM      20     10 11
     A            SF@TOKKANA    20     10 32
     A            SF@TOKTEL     15     10 53
     A            SF@TOKRMT      9  0  10 69EDTCDE(J)
     A
     A          R SFLCTL01                  SFLCTL(SFL01)
     A*%%TS  SD  20200509  015416  YAMAGUCHI   REL-V7R4M0  5770-WDS
     A                                      SFLSIZ(49)
     A                                      SFLPAG(7)
     A                                      OVERLAY PROTECT
     A  32                                  SFLCLR
     A  33                                  SFLDSP
     A  34                                  SFLDSPCTL
     A  35                                  SFLEND(*MORE)
     A                                  9  2'NO'
     A                                  8  4'得意先'
     A                                  9  5'CODE'
     A                                  9 11'得意先名'
     A                                  9 32'得意先ｶﾅ'
     A                                  9 53'電話番号'
     A                                  9 69'信用限度額'
```

## DSPFのポイント解説

### サブファイル

- サブファイルを定義する時のルールとして２つのレコード様式をセットで定義
    - キーワード
        - `SFL`
            - サブファイルレコードであることを表すキーワード
        - `SFLCTL(SFL01)`
            - サブファイル制御レコードであることを表すキーワード
            - パラメータは制御対象のサブファイルレコードを表す
        - 上記２つのキーワードを定義した２つのレコード様式は、以下のルールがあります。
            - ２つのレコード様式は２つセットで必ず定義する。
            - 定義する順番はSFL→SFLCTLの順番。
            - SFLCTLはSFLの直後のレコード様式として定義する。
- サブファイルレコード（SFL）
    - 明細に表示する１レコードの情報を定義
    - １ページあたり数レコードを表示する場合でも、１レコード分の定義のみでOK
    - 各項目の表示位置の設定は、１レコード目の表示位置を表している。
- サブファイル制御レコード（SFLCTL）
    - サブファイルの制御を行う定義として
        - `SFLSIZ` : サブファイル全体で何レコード管理するかを定義
            - 最大9999レコード管理可能
        - `SFLPAG` : １ページあたりに表示するレコード件数を設定
        - `SFLCLR` : サブファイルのクリア（標識によって有効にする）
        - `SFLDSP` : サブファイルレコードの表示（標識によって有効にする）
            - 基本的には以下の`SFLDSPCTL`とセットで標識によって有効／無効を切り替えて制御する。
        - `SFLDSPCTL` : サブファイル制御レコードの表示（標識によって有効にする）
        - `SFLEND` : サブファイルレコードの継続文字の設定（標識によって有効にする）
            - *MORE : レコードに続きがある場合、「More」と表示され、ページの終わりで「Bottom」と表示される。
            - *PLUS : レコードに続きがある場合、「+」と表示される。（検証したが表示される位置がおかしい？）
- サブファイルレコードの項目ヘッダーの定義
    - サブファイルレコード（SFL）では明細に表示するレコードの定義をしたが、項目ヘッダーはサブファイル制御レコードで定義する。

```shell
..... *. 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 ...+... 7 ...+... 8
     A          R SFL01                     SFL
     A*%%TS  SD  20200509  015416  YAMAGUCHI   REL-V7R4M0  5770-WDS
     A            SF@NO          2  0  10  2EDTCDE(Z)
     A            SF@TOKCD       5     10  5
     A            SF@TOKNM      20     10 11
     A            SF@TOKKANA    20     10 32
     A            SF@TOKTEL     15     10 53
     A            SF@TOKRMT      9  0  10 69EDTCDE(J)
     A
     A          R SFLCTL01                  SFLCTL(SFL01)
     A*%%TS  SD  20200509  015416  YAMAGUCHI   REL-V7R4M0  5770-WDS
     A                                      SFLSIZ(49)
     A                                      SFLPAG(7)
     A                                      OVERLAY PROTECT
     A  32                                  SFLCLR
     A  33                                  SFLDSP
     A  34                                  SFLDSPCTL
     A  35                                  SFLEND(*MORE)
     A                                  9  2'NO'
     A                                  8  4'得意先'
     A                                  9  5'CODE'
     A                                  9 11'得意先名'
     A                                  9 32'得意先ｶﾅ'
     A                                  9 53'電話番号'
     A                                  9 69'信用限度額'
```

## RPGのソース

```shell:RPG03.rpgle
..... *. 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 ...+... 7 ...+... 8
      **********************************************************************
      *使用標識    :
      **********************************************************************
      * 03 : F3 キー終了
      * 12 : F12キー前画面
      * 32 : SFLCLR（サブファイルレコードのクリア）の標識
      * 33 : SFLDSP（サブファイルレコード表示）の標識
      * 34 : SFLDSPCTL（サブファイル制御レコード表示）の標識
      * 35 : SFLEND（サブファイル継続文字表示）の標識
      * 50 :得意先マスタからのREADがEOFに到達
      * 51 :サブファイルへの追加がEOFに到達（これ以上書き込めない状態？）
      * 91 :得意先マスタから未取得
      **********************************************************************
     H DATEDIT(*YMD)
      **********************************************************************
      *ファイル定義
      **********************************************************************
      *画面ファイル(@@RRN01は相対レコード番号で、サブファイルから
      * レコードを読み取る場合やサブファイルにレコードを書き込む場合に
      * 使用するフィールド）
     FDSP03     CF   E             WORKSTN SFILE(SFL01:@@RRN01)
      *得意先マスタ
     FMTOKISKL01IF   E           K DISK
      **********************************************************************
      *ワークフィールド
      **********************************************************************
      *メインルーチン切替制御用
     D @@CTL           S              3
      *サブファイルの相対レコード番号用
     D @@RRN01         S              4  0
      *得意先ｶﾅ
     D @@TOKKANA       S                   LIKE(TOKKANA)
      **********************************************************************
      *パラメータリスト
      **********************************************************************
      *メッセージ用パラメータリスト
     C     PL@MSG        PLIST
     C                   PARM                    PL@MID            5
     C                   PARM                    PL@MMSG          57
      **********************************************************************
      *メイン処理
      **********************************************************************
     C     @@CTL         CASEQ     'HED'         @HED
     C     @@CTL         CASEQ     'DTL'         @DTL
     C     @@CTL         CASEQ     'END'         @END
     C                   ENDCS
      **********************************************************************
      *サブルーチン処理
      **********************************************************************

      **********************************************************************
      *キーワード指定画面の処理
      **********************************************************************
     C     @HED          BEGSR
     C                   WRITE     PANEL99
     C                   WRITE     M1
     C                   EXFMT     PANEL01
      *キー入力に応じた処理
     C                   SELECT
      * F3キー終了
     C                   WHEN      *IN03 = *ON
     C                   EVAL      @@CTL = 'END'
      * F12キー何もしない
     C                   WHEN      *IN12 = *ON
      * Enterキー
     C                   OTHER
      *チェック処理
     C                   EXSR      @CHK
      *エラーがなければサブファイルを表示
     C                   IF        *IN91 = *OFF
     C                   EXSR      @SETDATA
     C                   EVAL      @@CTL = 'DTL'
     C                   ENDIF
     C                   ENDSL
     C                   ENDSR
      **********************************************************************
      *得意先一覧サブファイル画面の処理
      **********************************************************************
     C     @DTL          BEGSR
     C                   WRITE     PANEL99
     C                   WRITE     PANEL01
      * サブファイルを表示する際にはSFLDSP/SFLDSPCTL/SFLENDを有効に
      * つまり、標識をONにしてからEXTFMTで表示しないといけない。
      * らしい。
     C                   MOVEA     '111'         *IN(33)
     C                   EXFMT     SFLCTL01
     C                   MOVEA     '000'         *IN(33)
      *キーに応じた処理
     C                   SELECT
      * F3キー終了
     C                   WHEN      *IN03 = *ON
     C                   EVAL      @@CTL = 'END'
      * F12キー　PANEL02を閉じてPANEL01だけの表示に切り替える
     C                   WHEN      *IN12 = *ON
     C                   EXSR      @RESET
     C                   EVAL      @@CTL = 'HED'
     C                   OTHER
     C                   ENDSL
     C                   ENDSR
      **********************************************************************
      *チェック処理
      **********************************************************************
     C     @CHK          BEGSR
     C                   EVAL      @@TOKKANA = P1@KEYWORD
      * 指定された得意先ｶﾅでカーソル位置をセットできない場合エラー
     C     @@TOKKANA     SETLL     MTOKISKL01                         91
     C                   IF        *IN91 = *ON
     C                   EVAL      PL@MID = 'ERR01'
     C                   EVAL      PL@MMSG = '見つかりませんでした。'
     C                   EXSR      @MSG
     C                   LEAVESR
     C                   ELSE
     C                   EXSR      @RESET
     C                   ENDIF
     C                   ENDSR
      **********************************************************************
      *サブファイルのデータセット処理
      **********************************************************************
     C     @SETDATA      BEGSR
     C                   EVAL      @@RRN01 = *ZERO
      * サブファイルのデータをセットするためSFLCLR用の標識をONにして
      * サブファイルの情報をいったんクリアする。
     C                   EVAL      *IN32 = *ON
     C                   WRITE     SFLCTL01
     C                   EVAL      *IN32 = *OFF
      * 得意先マスタのポインタセット位置からEOFに到達するまで又は
      * サブファイルへの書き込みが上限に到達するまで繰り返し処理
     C                   DOU       *IN50 OR *IN51
     C                   READ      MTOKISKR                               50
     C                   IF        NOT *IN50
      * サブファイルの明細の各項目に値をセット
     C                   EVAL      @@RRN01 = @@RRN01 + 1
     C                   EVAL      SF@NO = @@RRN01
     C                   EVAL      SF@TOKCD = TOKCD
     C                   EVAL      SF@TOKNM = TOKNM
     C                   EVAL      SF@TOKKANA = TOKKANA
     C                   EVAL      SF@TOKTEL = TOKTEL
     C                   EVAL      SF@TOKRMT = TOKRMT
      * サブファイルへの書き込み
     C                   WRITE     SFL01                                  51
     C                   ENDIF
     C                   ENDDO
     C                   ENDSR
      **********************************************************************
      *終了処理
      **********************************************************************
     C     @END          BEGSR
     C                   EVAL      *INLR = *ON
     C                   RETURN
     C                   ENDSR
      **********************************************************************
      *リセット処理
      **********************************************************************
     C     @RESET        BEGSR
      *エラー情報をクリア
     C                   CLEAR                   M1
     C                   SETOFF                                       91
     C                   ENDSR
      **********************************************************************
      *メッセージ表示処理
      **********************************************************************
     C     @MSG          BEGSR
     C                   EVAL      M1MSGID = PL@MID
     C                   EVAL      M1MSG = PL@MMSG
     C                   ENDSR
      **********************************************************************
      *初期設定*INZSR初期化サブルーチン
      **********************************************************************
     C     *INZSR        BEGSR
     C                   EVAL      @@CTL = 'HED'
     C                   ENDSR
```

## RPGのポイント解説

### サブファイル利用の定義

DSP03ファイルの定義に`SFILE(SFL01:@@RRN01)`という定義をしています。
この定義はサブファイルレコードを明示し、そのサブファイルレコードにアクセスするために必要な相対レコード番号を定義しています。
`@@RRN01`という相対レコード番号の変数は、D仕様書で別途定義する必要があります。

```shell
..... *. 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 ...+... 7 ...+... 8
      **********************************************************************
      *ファイル定義
      **********************************************************************
      *画面ファイル(@@RRN01は相対レコード番号で、サブファイルから
      * レコードを読み取る場合やサブファイルにレコードを書き込む場合に
      * 使用するフィールド）
     FDSP03     CF   E             WORKSTN SFILE(SFL01:@@RRN01)
      *得意先マスタ
     FMTOKISKL01IF   E           K DISK
.....
     *サブファイルの相対レコード番号用
     D @@RRN01         S              4  0
```

### サブファイルへの明細データ表示

```shell
..... *. 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 ...+... 7 ...+... 8
      *チェック処理
     C                   EXSR      @CHK
      *エラーがなければサブファイルを表示
     C                   IF        *IN91 = *OFF
     C                   EXSR      @SETDATA
     C                   EVAL      @@CTL = 'DTL'
     C                   ENDIF
     C                   ENDSL
     C                   ENDSR
```

`SETLL`を使って、得意先名ｶﾅをキー項目に得意先マスタを検索し、指定された得意先名ｶﾅと一致またはそれより大きいと判断される最初のレコードにカーソルをセットしています。
※SETLL自体はデータを読み取るわけではなく、カーソル位置をセットするために使うもののようです。

なので、得意先名ｶﾅを含んだデータのみが検索結果の一覧画面に表示されるわけではなく、SETLLによってセットされたカーソル位置から後続のデータが全部一覧に表示されることになります。

```shell
..... *. 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 ...+... 7 ...+... 8
      **********************************************************************
      *チェック処理
      **********************************************************************
     C     @CHK          BEGSR
     C                   EVAL      @@TOKKANA = P1@KEYWORD
      * 指定された得意先ｶﾅでカーソル位置をセットできない場合エラー
     C     @@TOKKANA     SETLL     MTOKISKL01                         91
     C                   IF        *IN91 = *ON
     C                   EVAL      PL@MID = 'ERR01'
     C                   EVAL      PL@MMSG = '見つかりませんでした。'
     C                   EXSR      @MSG
     C                   LEAVESR
     C                   ELSE
     C                   EXSR      @RESET
     C                   ENDIF
     C                   ENDSR
```

`@SETDATA`サブルーチンでサブファイルのレコードへ表示用のデータを追加しています。

- *IN32の標識はSFLCLRをON/OFFするための標識で、サブファイルのデータをセットする前に一度サブファイルのデータをクリアするためにONに切り替えています。
- *IN50の標識は、SETLLしてカーソル位置がセットされた得意先マスタのレコードを読み取り、そのレコードが最終レコード（EOF）に到達した場合ONになる標識です。
- *IN51の標識は、サブファイルへのデータを書き出し（WRITE）していき、書き出し可能な最終レコードに到達した場合ONになる標識です。今回はDSPFの定義として`SFLSIZ(49)`と定義しているので４９レコード書き込んだらONになります。
- *IN50/*IN51の標識がONになるまで得意先マスタのデータを１レコードずつ読み取り、サブファイルレコードの各項目に値をセットする。

```shell
..... *. 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 ...+... 7 ...+... 8
      **********************************************************************
      *サブファイルのデータセット処理
      **********************************************************************
     C     @SETDATA      BEGSR
     C                   EVAL      @@RRN01 = *ZERO
      * サブファイルのデータをセットするためSFLCLR用の標識をONにして
      * サブファイルの情報をいったんクリアする。
     C                   EVAL      *IN32 = *ON
     C                   WRITE     SFLCTL01
     C                   EVAL      *IN32 = *OFF
      * 得意先マスタのポインタセット位置からEOFに到達するまで又は
      * サブファイルへの書き込みが上限に到達するまで繰り返し処理
     C                   DOU       *IN50 OR *IN51
     C                   READ      MTOKISKR                               50
     C                   IF        NOT *IN50
      * サブファイルの明細の各項目に値をセット
     C                   EVAL      @@RRN01 = @@RRN01 + 1
     C                   EVAL      SF@NO = @@RRN01
     C                   EVAL      SF@TOKCD = TOKCD
     C                   EVAL      SF@TOKNM = TOKNM
     C                   EVAL      SF@TOKKANA = TOKKANA
     C                   EVAL      SF@TOKTEL = TOKTEL
     C                   EVAL      SF@TOKRMT = TOKRMT
      * サブファイルへの書き込み
     C                   WRITE     SFL01                                  51
     C                   ENDIF
     C                   ENDDO
     C                   ENDSR
```

- MOVEAで'111' *IN(33)と指定して値をセットしているのは、*IN33/*IN34/*IN35に対して標識をONにする指示を１行にまとめて配列指定する場合に使う記述方法のようです。
- SFLDSP/SFLDSPCTL/SFLENDの３つの定義を有効にする標識*IN33/*IN34/*IN35をONにしてからサブファイル制御レコードをEXFMTで表示処理を行いユーザーの入力を待機する。
- EXFMTでサブファイル制御レコードを表示する処理をする前に、@SETDATAサブルーチンで表示用のデータをセットしているため、レコード表示処理だけで得意先一覧情報が画面に表示される。

```shell
..... *. 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 ...+... 7 ...+... 8
      **********************************************************************
      *得意先一覧サブファイル画面の処理
      **********************************************************************
     C     @DTL          BEGSR
     C                   WRITE     PANEL99
     C                   WRITE     PANEL01
      * サブファイルを表示する際にはSFLDSP/SFLDSPCTL/SFLENDを有効に
      * つまり、標識をONにしてからEXTFMTで表示しないといけない。
      * らしい。
     C                   MOVEA     '111'         *IN(33)
     C                   EXFMT     SFLCTL01
     C                   MOVEA     '000'         *IN(33)
      *キーに応じた処理
     C                   SELECT
      * F3キー終了
     C                   WHEN      *IN03 = *ON
     C                   EVAL      @@CTL = 'END'
      * F12キー　PANEL02を閉じてPANEL01だけの表示に切り替える
     C                   WHEN      *IN12 = *ON
     C                   EXSR      @RESET
     C                   EVAL      @@CTL = 'HED'
     C                   OTHER
     C                   ENDSL
     C                   ENDSR
```

以上がサブファイルを利用した一覧画面の実装です。