CTS-KB
RPG 3 / 5

RPG 業務ロジック実装

#RPG #IBM i #業務ロジック

対象: メーカー・問屋の受注処理、在庫管理、出荷処理など業務システムの設計パターン
基本文法は RPG 文法基礎、モジュール化は RPG モジュール設計 を参照
COBOL版は COBOL 業務ロジック実装 を参照


目次

  1. メーカー・問屋の業務フロー全体像
  2. 受注処理(オンライン)
  3. 在庫引当処理(バッチ)
  4. 出荷指図処理(バッチ)
  5. 月次締め処理(バッチ)
  6. マスタメンテナンス
  7. バッチ連携パターン集

1. メーカー・問屋の業務フロー全体像

メーカー・問屋の基幹業務:

受注          在庫          出荷          請求
───────       ───────       ───────       ───────
得意先から    在庫引当      ピッキング    月次締め
受注入力      └→不足なら    出荷指図      請求書発行
  │            発注起票      送り状印刷    入金消込
  │              │           │
  ↓              ↓           ↓
┌─────────────────────────────────────────┐
│         DB2 for i(物理ファイル群)       │
│  ORDHDR / ORDDTL / CUSTPF / ITMPF      │
│  INVPF / SHPPF / BILPF                  │
└─────────────────────────────────────────┘

典型的なプログラム構成

プログラム種別内容実行タイミング
ORD001R対話型受注入力画面日中随時
ORD002Rバッチ受注データチェック日次夜間
INV001Rバッチ在庫引当処理日次夜間
INV002Rバッチ発注点チェック→自動発注日次夜間
SHP001Rバッチ出荷指図作成日次朝
SHP002Rバッチ送り状印刷日次朝
BIL001Rバッチ月次締め→請求書作成月次
MST001R対話型得意先マスタメンテ随時

2. 受注処理(オンライン)

5250画面から受注データを入力する対話型プログラム。

ファイル構成

**free
ctl-opt dftactgrp(*no) actgrp(*caller);

// 画面ファイル
dcl-f ORDDSP  workstn(*ext) usage(*input:*output);

// マスタファイル(参照)
dcl-f CUSTPF  disk(*ext) usage(*input) keyed;
dcl-f ITMPF   disk(*ext) usage(*input) keyed;

// トランザクションファイル(書込)
dcl-f ORDHDR  disk(*ext) usage(*output) keyed;
dcl-f ORDDTL  disk(*ext) usage(*output);

メインロジック

// 画面表示ループ
dou *inkc;  // F3キーで終了
  exfmt ORDSCR;  // 画面表示&入力待ち

  if *inkc;  // F3 = 終了
    leave;
  endif;

  // 得意先チェック
  chain custId CUSTPF;
  if not %found(CUSTPF);
    errMsg = '得意先が存在しません';
    iter;
  endif;

  // 与信チェック
  if custRec.balance + orderAmt > custRec.creditLimit;
    errMsg = '与信限度額を超えています';
    iter;
  endif;

  // 受注ヘッダ書込
  exsr writeOrderHeader;

  // 受注明細書込(配列ループ)
  for i = 1 to lineCount;
    exsr writeOrderDetail;
  endfor;
enddo;

*inlr = *on;

// ──────────────────────────────────
begsr writeOrderHeader;
  ordHdrRec.ordNo    = nextOrderNo();
  ordHdrRec.custId   = custId;
  ordHdrRec.ordDate  = %date();
  ordHdrRec.status   = 'N';  // New
  ordHdrRec.totalAmt = orderAmt;
  write ORDHDRFMT ordHdrRec;
endsr;

begsr writeOrderDetail;
  ordDtlRec.ordNo   = ordHdrRec.ordNo;
  ordDtlRec.lineNo  = i;
  ordDtlRec.itemCd  = detail(i).itemCd;
  ordDtlRec.qty     = detail(i).qty;
  ordDtlRec.price   = detail(i).price;
  write ORDDTLFMT ordDtlRec;
endsr;

AI解析での注目点

  • workstn ファイル = 5250画面。モダナイゼーション時にAngular UIに変換する対象
  • chain + %found() = マスタ検証パターン。API化時にバリデーションロジックとして抽出
  • exfmt = 画面の表示&入力待ち。このループ構造がWebアプリのリクエスト/レスポンスに対応

3. 在庫引当処理(バッチ)

日次夜間バッチで、受注データに対して在庫を引き当てる。メーカー・問屋の基幹業務の中核。

**free
ctl-opt dftactgrp(*no) actgrp(*caller);

dcl-f ORDDTL disk(*ext) usage(*input) keyed;
dcl-f INVPF  disk(*ext) usage(*update) keyed;
dcl-f ALCLOG disk(*ext) usage(*output);

dcl-s allocQty   packed(7:0);
dcl-s shortQty   packed(7:0);
dcl-s allocCount int(10) inz(0);
dcl-s shortCount int(10) inz(0);

// 未引当の受注明細を順次処理
setll *loval ORDDTL;
read ORDDTL;
dow not %eof(ORDDTL);

  // 該当商品の在庫を検索
  chain ordDtlRec.itemCd INVPF;
  if %found(INVPF);

    // 有効在庫 = 在庫数量 - 引当済数量
    dcl-s available packed(7:0);
    available = invRec.onHand - invRec.allocated;

    if available >= ordDtlRec.qty;
      // 全数引当可能
      allocQty = ordDtlRec.qty;
      invRec.allocated += allocQty;
      update INVFMT invRec;
      allocCount += 1;
    else;
      // 一部引当(在庫不足)
      allocQty = available;
      shortQty = ordDtlRec.qty - available;
      invRec.allocated += allocQty;
      update INVFMT invRec;
      shortCount += 1;

      // 不足分を発注起票(別プログラムで処理)
      exsr writeShortageLog;
    endif;

    // 引当ログ書込
    exsr writeAllocLog;
  endif;

  read ORDDTL;
enddo;

// 集計出力
dsply ('引当完了: ' + %char(allocCount) + '件');
dsply ('在庫不足: ' + %char(shortCount) + '件');

*inlr = *on;

AI解析での注目点

  • onHand - allocated = available → 在庫引当の基本公式。AI在庫最適化の入力データ
  • shortQty → 欠品データ。AI需要予測で削減可能な部分
  • このバッチの実行順序(受注取込→引当→出荷指図)はCLプログラムで制御されている

4. 出荷指図処理(バッチ)

引当済の受注明細から出荷指図データを作成し、ピッキングリストを印刷する。

**free
ctl-opt dftactgrp(*no) actgrp(*caller);

dcl-f ORDHDR  disk(*ext) usage(*update) keyed;
dcl-f ORDDTL  disk(*ext) usage(*input)  keyed;
dcl-f SHPPF   disk(*ext) usage(*output);
dcl-f PICKPRT printer(*ext) oflind(overflow);

dcl-s overflow ind;
dcl-s shipNo   char(10);

// 引当済受注を検索
setll *loval ORDHDR;
read ORDHDR;
dow not %eof(ORDHDR);

  if ordHdrRec.status = 'A';  // Allocated(引当済)

    // 出荷番号採番
    shipNo = nextShipNo();

    // 出荷ヘッダ作成
    exsr createShipment;

    // 明細をピッキングリストに印刷
    setll ordHdrRec.ordNo ORDDTL;
    read ORDDTL;
    dow not %eof(ORDDTL) and ordDtlRec.ordNo = ordHdrRec.ordNo;

      exsr printPickLine;

      if overflow;
        write PAGEBREAK;
        overflow = *off;
      endif;

      read ORDDTL;
    enddo;

    // 受注ステータスを「出荷済」に更新
    ordHdrRec.status = 'S';  // Shipped
    update ORDHDRF ordHdrRec;

  endif;

  read ORDHDR;
enddo;

*inlr = *on;

AI解析での注目点

  • overflow インジケータ = 帳票のページ送り。PDF生成に変換する際のポイント
  • nextShipNo() = 採番処理。データ区域(DTAARA)で管理されていることが多い
  • status = 'A' → 'S' = ステータス遷移。業務フローの状態マシンとして抽出可能

5. 月次締め処理(バッチ)

月末に実行する請求データ作成・帳票出力バッチ。

**free
ctl-opt dftactgrp(*no) actgrp(*caller);

dcl-f CUSTPF  disk(*ext) usage(*input) keyed;
dcl-f SHPPF   disk(*ext) usage(*input) keyed;
dcl-f BILPF   disk(*ext) usage(*output);
dcl-f BILPRT  printer(*ext) oflind(overflow);

dcl-s prevCust  char(8) inz('');
dcl-s custTotal packed(11:2) inz(0);

// 出荷データを得意先順に読む
setll *loval SHPPF;
read SHPPF;
dow not %eof(SHPPF);

  // ブレイク処理(得意先が変わったら小計出力)
  if shpRec.custId <> prevCust and prevCust <> '';
    exsr outputCustBill;
    custTotal = 0;
  endif;

  // 売上加算
  custTotal += shpRec.amount;
  prevCust = shpRec.custId;

  read SHPPF;
enddo;

// 最後の得意先の処理
if prevCust <> '';
  exsr outputCustBill;
endif;

*inlr = *on;

// ──────────────────────────────────
begsr outputCustBill;
  // 請求ファイルに書込
  bilRec.custId   = prevCust;
  bilRec.billDate = %date();
  bilRec.amount   = custTotal;
  write BILFMT bilRec;

  // 請求書印刷
  chain prevCust CUSTPF;
  exsr printBillHeader;
  exsr printBillTotal;
endsr;

ブレイク処理パターン

ブレイク処理 = キー値が変わったタイミングで小計・合計を出力するパターン

RPGのブレイク処理:                     SQLでの対応:
1. キー順にREAD                       SELECT custId, SUM(amount)
2. 前回のキーと比較                    FROM shipments
3. 変わったら小計出力                  GROUP BY custId
4. 最後のレコード後にも小計出力

→ モダナイゼーション時は GROUP BY + Window関数 で置き換え可能

6. マスタメンテナンス

得意先マスタの照会・追加・更新・削除を行う対話型プログラム。

**free
ctl-opt dftactgrp(*no) actgrp(*caller);

dcl-f MSTDSP  workstn(*ext) usage(*input:*output);
dcl-f CUSTPF  disk(*ext) usage(*update:*delete:*output) keyed;

// 画面ループ
dou *inkc;  // F3 で終了
  exfmt SELSCR;  // 選択画面

  if *inkc; leave; endif;

  select;
    when action = '1';  // 照会
      chain custId CUSTPF;
      if %found(CUSTPF);
        exfmt DTLSCR;  // 詳細表示
      else;
        errMsg = '該当なし';
      endif;

    when action = '2';  // 追加
      chain custId CUSTPF;
      if %found(CUSTPF);
        errMsg = '既に存在します';
      else;
        exfmt ADDSCR;  // 入力画面
        if not *inkc;
          exsr writeCust;
        endif;
      endif;

    when action = '3';  // 更新
      chain custId CUSTPF;
      if %found(CUSTPF);
        exfmt UPDSCR;  // 更新画面
        if not *inkc;
          exsr updateCust;
        endif;
      endif;

    when action = '4';  // 削除
      chain custId CUSTPF;
      if %found(CUSTPF);
        exfmt CFMSCR;  // 確認画面
        if confirm = 'Y';
          delete CUSTFMT;
        endif;
      endif;
  endsl;
enddo;

*inlr = *on;

AI解析での注目点

  • CRUD操作が select/when で分岐 → REST APIの GET/POST/PUT/DELETE に自然に対応
  • exfmt による画面遷移 → Angular のルーティング + コンポーネントに変換
  • chain + %found() によるバリデーション → バリデーションサービスに抽出

7. バッチ連携パターン集

パターン1: マッチング処理(2ファイル突合)

// 受注ファイルと出荷ファイルをキー順に突合
read ORDFILE;
read SHPFILE;
dow not %eof(ORDFILE) or not %eof(SHPFILE);
  select;
    when ordRec.key < shpRec.key or %eof(SHPFILE);
      // 受注あり・出荷なし(未出荷)
      exsr processUnshipped;
      read ORDFILE;
    when ordRec.key > shpRec.key or %eof(ORDFILE);
      // 出荷あり・受注なし(異常)
      exsr processOrphanShip;
      read SHPFILE;
    when ordRec.key = shpRec.key;
      // 一致(正常突合)
      exsr processMatch;
      read ORDFILE;
      read SHPFILE;
  endsl;
enddo;

→ モダナイゼーション: SQL の FULL OUTER JOIN + CASE WHEN で置換

パターン2: 集計バッチ(合計・件数・平均)

dcl-s totalAmt  packed(13:2) inz(0);
dcl-s totalCnt  int(10) inz(0);

read ORDFILE;
dow not %eof(ORDFILE);
  totalAmt += ordRec.amount;
  totalCnt += 1;
  read ORDFILE;
enddo;

avgAmt = totalAmt / totalCnt;

→ モダナイゼーション: SELECT SUM(amount), COUNT(*), AVG(amount) FROM orders

パターン3: 一括更新バッチ

read INVFILE;
dow not %eof(INVFILE);
  if invRec.onHand < invRec.safetyStock;
    invRec.reorderFlag = 'Y';
    update INVFMT invRec;
  endif;
  read INVFILE;
enddo;

→ モダナイゼーション: UPDATE inventory SET reorder_flag = 'Y' WHERE on_hand < safety_stock


関連記事