「JKS 受注管理システム」解読ノート
レガシーシステム移行アセスメント ── 仕様書なし・ソースコードのみからのリバースエンジニアリング報告書
解読対象: 37ファイル(Java 20 / JSP 7 / XML 2 / SQL 2 / COBOL 2 / JCL 2 / sh 1 / README 1)
解読・執筆: Claude Code
作成日: 2026-07-04
※ 仕様書なし・コードのみから解読。README記載の設計書共有サーバ(\\hoshino-fs01)は失われている前提。
※ 題材は架空企業「株式会社ホシノ物産」のダミーシステムであり、実在の企業・システムとは関係ない。
1エグゼクティブサマリ
- 正体: 2006年構築の社内向け受注管理システム。オンライン(Java/Struts 1.2 + JSP + Oracle)で受注の登録・更新・一覧・顧客検索を行い、月次バッチ(COBOL 2本)で売上集計と顧客別請求計算を行う、典型的な「Web+汎用機バッチ」ハイブリッド構成である。
- 規模: 画面5+共通2、Actionクラス5、DAO 4、テーブル6(トランザクション3・マスタ3)、バッチ2本。小規模だが受注〜請求という金銭に直結する基幹業務を担う。
- 健康状態: 重症。オンラインは消費税8%のまま、バッチは2014年改定で10%に改修済みで、しかもバッチは税込金額にさらに10%を課税している(H-02)。現行の請求額自体が正しくない可能性が高い。
- セキュリティ: SQLインジェクション、平文パスワード、admin/admin残存、認証チェックの欠落、XSSと、2006年当時の水準でも危険な状態。社内システムであることだけが防波堤になっている。
- 移行の要点: 「現行踏襲」で移行してはならない。金額計算・売上計上・請求の各所にバグと暗黙仕様が混在しており、まず本ノート第4章・第6章をもとに「どれが仕様でどれがバグか」を業務部門と確定させることが最優先である。
2システム構成図
■ オンライン系(受注入力・照会) ── Struts 1.2 / Servlet 2.3 / 文字コード Windows-31J
ブラウザ社内LAN
→
JSP(5画面)login / orderList /
orderRegist / orderUpdate /
customerSearch
→
ActionServletweb.xml / *.do
struts-config.xml
→
Action(5本)入力チェック・
金額計算 税8%
→
DAO(4本)Order / Customer /
Product / User
→
Oracle JKSPRDT_JUCHU / T_URIAGE / T_SEIKYU
M_KOKYAKU / M_SHOHIN / M_USER
■ バッチ系(月次・毎月1日 02:00) ── COBOL。JCL(メインフレーム)と run_nightly.sh(Unix系 cobrun)の2系統の起動経路が存在
T_JUCHU→ファイル
アンロードジョブ不明推定
→
JUCHU.DAT受注 順編成
42バイト固定
→
URIAGE02売上集計
受注日の年月で抽出
STATUS≧20 かつ ≠99
→
URIAGE.DAT売上 40バイト
→
SORT顧客コード順
JCLのみに存在
shには無い(H-03)
→
SEIKYU01請求計算
顧客別ブレーク集計
税10%を再課税
→
SEIKYU.DAT
→
T_SEIKYU へ
ロードジョブ不明推定
・DB⇔ファイル間のアンロード/ロードのジョブはリポジトリに存在しない。テーブル定義コメント(01_create_transaction.sql 34行目「請求(月次バッチ SEIKYU01 が作成)」)とJCLのDSN名から連携があると推定した。
・オンラインの受注更新(OrderDao.update)はDBの T_URIAGE を直接書き換えるが、バッチはファイル(URIAGE.DAT)を読む。両者は同期していない(M-03)。
3機能一覧
3.1 オンライン(画面・Action)
| 画面 | 構成ファイル | 機能の推定 | 備考 |
| ログイン | login.jsp LoginAction UserDao | ユーザID・パスワードで認証し、セッションに userId / userName / authKbn を格納。M_USER と平文突き合わせ。 | welcome-file(web.xml 28行目) |
| 受注一覧 | orderList.jsp OrderListAction OrderDao.findList | T_JUCHU をステータス絞り込み付きで一覧表示(受注日降順)。各行から更新画面へ遷移。 | 唯一ログインチェックあり(Action 26-29行目) |
| 受注登録 | orderRegist.jsp OrderRegistAction OrderDao.insert | 顧客CD・商品CD・数量・受注日を入力し、商品マスタ単価×数量で金額・消費税(8%)・合計を計算して T_JUCHU に1件INSERT。受注番号は「JK+受注登録日+4桁連番」を自前採番。ステータスは「10:受付」固定。 | 2013/04改修の形跡(コメント) |
| 受注更新 | orderUpdate.jsp OrderUpdateAction OrderDao.update | 受注番号をキーに数量・金額・税・合計・ステータスを再計算してUPDATE。続けて T_URIAGE の合計金額も合わせて更新(2段commit)。 | 既存値は画面に表示されない(M-10) |
| 顧客検索 | customerSearch.jsp CustomerSearchAction CustomerDao.search | 顧客名・カナ(部分一致)、電話番号(完全一致)で M_KOKYAKU を検索。削除フラグ'0'のみ。 | SQL文字列連結(H-01) |
| 共通 | header.jsp / error.jsp | ヘッダメニュー(受注一覧・受注登録・顧客検索・ログアウト)とエラー表示。 | ログアウトは login.jsp への単なるリンク(M-05) |
3.2 バッチ(COBOL・JCL・シェル)
| ジョブ | 構成ファイル | 機能の推定 | 起動契機 |
| 売上集計 | URIAGE02.cbl URIAGE02.jcl | 受注ファイル(JUCHU.DAT)から「受注日の年月=処理対象年月」かつ「ステータス20以上・99以外」のレコードを抽出し、売上ファイル(URIAGE.DAT)を作成。対象年月はSYSINから受領。 | 月次 第1営業日 夜間(JCLコメント) |
| 請求計算 | SEIKYU01.cbl SEIKYU01.jcl | 売上ファイルを顧客コード順に読み、顧客が変わるごとに合計金額へ消費税10%を加えた請求レコードを出力(コントロールブレーク)。請求番号は「SK+年月+連番」。JCLでは前段にSORT(顧客コード順)あり。 | URIAGE02完了後(JCLコメント前提) |
| 夜間起動 | run_nightly.sh | cron(毎月1日 02:00)で URIAGE02 → SEIKYU01 を cobrun で順次実行。異常時はログ出力して停止(メール通知はTODOのまま未実装)。 | cron 0 2 1 * * |
4業務ルールの解読 ── コードから逆算した仕様
4.1 金額計算(受注時)
OrderRegistAction.java 67-76行目より:
| 項目 | 計算式 | 根拠 |
| 単価 UNIT_PRICE | 受注時点の商品マスタ標準単価(M_SHOHIN.PRICE)をコピーして保持 | OrderRegistAction 71行目、ProductDao.findByCode |
| 金額 AMOUNT(税抜) | 単価 × 数量 | 同 72行目 |
| 消費税 TAX | 金額 × 0.08 を long キャストで切り捨て(浮動小数点演算) | 同 75行目 (long)(kingaku * 0.08) |
| 合計 TOTAL_AMOUNT(税込) | 金額 + 消費税 | 同 76行目 |
暗黙仕様①: 税額の端数は「切り捨て」。ドキュメントはなく (long) キャストにのみ表現されている。しかも浮動小数点経由のため、大きな金額では二進数誤差により1円ずれる可能性がある(例: 理論上 x*0.08 が 799.9999… と評価されるケース)。移行先で BigDecimal 等に置き換えると現行と1円差異が出る取引が発生し得る。
暗黙仕様②: 割引・掛率のロジックは存在しない。単価は常に標準単価。顧客区分(法人/個人)は検索画面の表示にしか使われておらず、価格には影響しない(CustomerSearchAction / customerSearch.jsp 39行目のみで使用)。
4.2 金額再計算(受注更新時)
OrderUpdateAction.java 71-93行目より。更新時は入力された商品コードでその時点のマスタ単価を引き直して全額を再計算する。
暗黙仕様③(重要): 受注登録後に商品マスタの単価が改定されると、その受注を(数量変更などの目的で)更新した瞬間に金額が新単価で洗い替えられる。「受注時単価の保持」はINSERT時のみで、更新には引き継がれない。現行データには「受注日と単価が対応しない」レコードが混在していると推定される。移行データ検証時の金額不一致の主要因になり得る。
4.3 受注番号・請求番号の採番
| 番号 | 形式 | 根拠と問題 |
受注番号 ORDER_NO | JK+登録日yyyyMMdd+4桁連番 = 14桁 | OrderRegistAction 79行目。ただし「連番」の実体は System.currentTimeMillis() % 10000 ベース(101-105行目)で連番性・一意性の保証なし(H-04)。なお受注番号の日付部は画面入力の受注日ではなく登録処理日である点も暗黙仕様。 |
請求番号 SEIKYU_NO | SK+請求年月yyyyMM+連番 = 設計上12桁 | SEIKYU01.cbl 78-81行目。ただし連番が PIC 9(06) のため合計14桁になり12桁領域からあふれる(H-10)。 |
4.4 受注ステータスの遷移
コード値は第5章参照。遷移を強制するロジックはどこにも存在しない。orderUpdate.jsp のプルダウン(36-42行目)で任意のステータスへ直接変更でき、「完了→受付」への巻き戻しや「取消の解除」も可能である。
10 受付登録時の初期値
→
20 出荷済売上計上の起点
→
30 請求済
→
40 完了
⤳
99 取消集計・請求対象外
暗黙仕様④: 上図の順方向遷移は「運用ルール」として存在した推定もので、システムは強制していない。また「30 請求済」への更新は人手であり、バッチ(SEIKYU01)が書き戻す処理は見当たらない。請求済ステータスと T_SEIKYU の整合は運用頼み。
4.5 売上計上ルール(URIAGE02)
- 計上対象: 「受注日の年月 = 処理対象年月」かつ「ステータス ≧ 20 かつ ≠ 99」(URIAGE02.cbl 41-47行目)。
- 計上金額: T_JUCHU の TOTAL_AMOUNT(税込)をそのまま転記(60-65行目)。
- 売上の帰属月は出荷日ではなく受注日で決まる。出荷日という項目自体が存在しない。
暗黙仕様⑤(重大な業務バグの疑い): 月次バッチ実行時点でまだ「10 受付」のままの受注(=月内に受注したが月初バッチまでに出荷されなかったもの)は、その月の集計から漏れる。そして翌月のバッチは翌月の受注日しか見ないため、この受注は永久に売上・請求に計上されない(H-11)。再実行や差分取り込みの仕組みはコード上見当たらない。現行の売上・請求データに欠落がある可能性を移行前に棚卸しすべきである。
4.6 請求ルール(SEIKYU01)
- 顧客コード順にソートされた売上ファイルをコントロールブレークで集計し、顧客ごとに月1枚の請求レコードを作る(明細なし・顧客単位合算)。SEIKYU01.cbl 50-56行目。
- 消費税率は 10%(2014年改定対応。36行目
WK-TAX-RATE PIC V999 VALUE 0.100)。オンライン側の8%とは不一致(H-02)。
- 集計元の UR-AMOUNT は URIAGE02 が書いた税込合計であるにもかかわらず、これを「請求額(税抜) KINGAKU」として扱い、さらに10%を課税する(57行目・75-76行目、T_SEIKYU定義 01_create_transaction.sql 39行目)。8%課税済みの金額への10%二重課税である。
- 合計0円の顧客には請求レコードを出力しない(63-65行目
IF WK-KINGAKU > ZERO)。仕様かバグかは断定できない推定。
- 取消(99)は請求対象外として読み飛ばす(46-49行目)。ただしURIAGE02側で既に除外済みのため通常は到達しない防御コード推定。
金額差異の試算例: 税抜10,000円の受注 → オンラインで税800円、税込10,800円 → URIAGE02が10,800を転記 → SEIKYU01が10,800を「税抜」とみなし税1,080円を加算 → 請求 11,880円。10%時代の正解11,000円に対し880円(8%)の過大請求。移行で正しく再実装すると現行実績と必ず差異が出るため、「現行値との突合」を合格条件にしてはならない。
4.7 日付の取り扱い(2桁年の窓化)
DateUtil.java 25-50行目。旧システムからの移行データが2桁年(YYMMDD)で残っており、「YY > 50 なら1900年代、それ以外は2000年代」という窓化ルールで4桁化する。境界値50は2050年扱い。この変換ルール自体が「コードにしか書かれていない暗黙仕様」であり、移行時のデータクレンジングで同一ルールを再現する必要がある。
5コード値・変数辞書
5.1 区分コード
| 区分 | 値 | 意味 | 根拠 |
受注ステータス T_JUCHU.STATUS | 10 | 受付 | CodeConst.java 13行目 / orderList.jsp 38行目 |
| 20 | 出荷済(売上計上の起点) | CodeConst.java 15行目 / URIAGE02.cbl 44行目 |
| 30 | 請求済 | CodeConst.java 17行目 |
| 40 | 完了 | CodeConst.java 19行目 |
99 (90) | 取消。正は99。orderList.jsp のみ「90」と直書きしており不整合(M-01) | CodeConst.java 21行目 / orderUpdate.jsp 41行目 / SEIKYU01.cbl 47行目 ⇔ orderList.jsp 23・42行目 |
顧客区分 M_KOKYAKU.CUST_TYPE | 1 | 法人 | CodeConst.java 25行目 / 02_create_master.sql 11行目 |
| 2 | 個人(JSPでは「1以外=個人」扱い) | CodeConst.java 27行目 / customerSearch.jsp 39行目 |
権限区分 M_USER.AUTH_KBN | 1 | 一般 | User.java 12行目 / 02_create_master.sql 35行目 |
| 9 | 管理者。ただし参照箇所ゼロで機能差なし(H-09関連) | セッション格納のみ(LoginAction 34行目) |
削除フラグ 各テーブル DEL_FLG | 0 | 有効 | Customer.java 16行目 / 各DAOのWHERE句 |
| 1 | 削除(論理削除。物理削除機能は見当たらない) | 同上 |
出荷区分 (対応カラム不明) | 0 | 通常 | CodeConst.java 33-35行目。定義のみで使用箇所なし。過去機能の残骸か未完成機能と推定推定 |
| 1 | 緊急 |
5.2 テーブル・ファイル
| 物理名 | 意味 | 根拠 |
| T_JUCHU | 受注ヘッダ(明細テーブルはなく1受注=1商品) | 01_create_transaction.sql 7-22行目 / Order.java |
| T_URIAGE | 売上実績(月次集計・請求のもと。PKはORDER_NOで1受注1行) | 同 24-32行目(2012/11追加) |
| T_SEIKYU | 請求(顧客×月単位。SEIKYU01が作成) | 同 34-44行目 |
| M_KOKYAKU / M_SHOHIN / M_USER | 顧客・商品・利用者マスタ | 02_create_master.sql |
| JKS.PROD.JUCHU.DAT | 受注アンロードファイル(42桁: 受注番号14+受注日8+顧客6+状態2+税込金額12) | URIAGE02.jcl 10行目 / URIAGE02.cbl 19-24行目 |
| JKS.PROD.URIAGE.DAT / .SORT | 売上ファイルとその顧客コード順ソート版(40桁) | SEIKYU01.jcl 10-14行目(SORT位置21,6=顧客CD) |
| JKS.PROD.SEIKYU.DAT | 請求ファイル(80桁固定) | SEIKYU01.jcl 19-22行目 |
5.3 紛らわしいワーク変数・略語
| 変数/略語 | 登場箇所 | 意味の推定 |
| str1 / str2 / str3 | OrderRegistAction 34-60行目ほか | 順に 顧客コード / 商品コード / 数量(文字列)。連番変数のため取り違えに注意。 |
| flg | OrderRegistAction 48行目 | 数量が全桁数字かの判定フラグ(true=数値)。 |
| tanka / kingaku / zei / goukei | OrderRegistAction 71-76行目 | 単価 / 税抜金額 / 消費税 / 税込合計。ローマ字命名。 |
| WK-KINGAKU / WK-TAX / WK-TOTAL | SEIKYU01.cbl 32-34行目 | 顧客別の集計金額 / 税 / 税込合計。ただしWK-KINGAKUの中身は実際には税込金額の合計(H-02)。 |
| WK-PREV-CUST | SEIKYU01.cbl 31行目 | コントロールブレーク用の直前顧客コード。 |
| JU- / UR- 接頭辞 | 両COBOL FD句 | JUCHU(受注)レコード / URIAGE(売上)レコードの項目。 |
| authKbn | User.java 13行目 | 権限区分(Kbn=区分)。 |
| windowYear | DateUtil.java 30行目 | 2桁年の「窓化」(ピボット50で世紀を補完)。 |
6問題点・移行リスク一覧
発見した問題34件をすべて掲載する。重大度は「移行時・現行運用への金銭影響/セキュリティ影響」の観点で付与した。行番号は本リポジトリのファイルに基づく。
重大度: 高(11件)
高H-01顧客検索にSQLインジェクション(文字列連結+Statement)
- 箇所
- CustomerDao.java 30-45行目(
"... LIKE '%" + keyName + "%'" を Statement で実行)。呼び出し元 CustomerSearchAction.java 30行目は画面入力を無加工で渡す。
- 移行時に何が起きるか
- 現行はDB全体(平文パスワードのM_USER含む)が奪取・改ざんされ得る状態。移行の際にこの検索仕様(部分一致の挙動、
%や'を含む顧客名の扱い)をそのまま写経すると脆弱性ごと移植される。バインド変数化とLIKEエスケープ仕様の再定義が必須。
高H-02消費税の不整合と二重課税 ── オンライン8%・バッチ10%、しかも税込額に再課税
- 箇所
- OrderRegistAction.java 74-76行目・OrderUpdateAction.java 77-78行目(8%直書き)⇔ SEIKYU01.cbl 35-36行目(10%・2014年改定コメント)。二重課税の経路は URIAGE02.cbl 60-65行目(税込TOTAL_AMOUNTを転記)→ SEIKYU01.cbl 57行目(それをWK-KINGAKUに加算)→ 75-76行目(10%を課税)。T_SEIKYU.KINGAKUは「請求額(税抜)」と定義(01_create_transaction.sql 39行目)。
- 移行時に何が起きるか
- 現行の請求額は「税抜×1.08×1.10」で計算されており正解値ではない。移行後システムを正しく作るほど現行実績と全件不一致になる。①現行バグとして業務部門と合意、②過去請求の過大分の扱い(返金・遡及)の経営判断、③並行稼働時の突合基準の再定義、の3点を移行前に済ませる必要がある。
高H-03Unix実行経路でソート工程が欠落 ── 顧客別請求の集計が崩れる
- 箇所
- SEIKYU01.jcl 6-15行目はSORT前段(顧客コード順、FIELDS=(21,6))を必須と明記。一方 run_nightly.sh 17-31行目は URIAGE02 → SEIKYU01 を直結しソートしていない。SEIKYU01.cbl 50-56行目のコントロールブレークはソート済み入力が前提。
- 移行時に何が起きるか
- URIAGE02の出力は受注ファイル順(≒受注日順)のため、shell経路で実行すると同一顧客が飛び飛びに現れるたびにブレークが発火し、1顧客に複数の請求が分割発行される。どちらの経路が本番かはコードから断定できない(推定: メインフレームからUnixへ移行中/移行済みで、shが現行)。移行時は「顧客×月で1請求」の業務要件を明文化し、実データのT_SEIKYUに分割請求が混在していないか検証が必要。
高H-04受注番号採番が時刻ベースで衝突し、PK違反が握りつぶされ受注が消える
- 箇所
- OrderRegistAction.java 101-105行目(
currentTimeMillis() % 10000 を加工した4桁。コメント自身が「本当はシーケンスを使うべき」と自白)。OrderDao.java 82-86行目は SQLException をcatchして printStackTrace のみ、Actionは常に成功画面へ遷移(97-98行目)。
- 移行時に何が起きるか
- 同日中に同じ下4桁ミリ秒が出れば ORDER_NO 重複でINSERT失敗、しかし画面には受注番号付きの成功が表示され受注がサイレントに消失する。現行データに「登録したはずの受注がない」欠落が既にある可能性。移行時はDBシーケンス採番へ置換し、過去の欠落有無を業務側と照合すべき。
高H-05受注更新で顧客コード・商品コードがDBに反映されない(金額だけ書き換わる)
- 箇所
- OrderUpdateAction.java 71-93行目は入力された商品コードで単価を引き直し金額を再計算するが、OrderDao.java 103-105行目のUPDATE文のSET句には
CUST_CODE と PRODUCT_CODE が含まれない(QUANTITY, UNIT_PRICE, AMOUNT, TAX, TOTAL_AMOUNT, STATUS, UPD_USERのみ)。
- 移行時に何が起きるか
- 更新画面で商品を変えると「商品コードは旧商品のまま、単価・金額だけ新商品」のレコードが生まれる。現行T_JUCHUには商品コードと単価が矛盾するデータが混在していると推定され、移行時の「単価×数量=金額」検証で大量の不一致として顕在化する。データクレンジング方針の事前決定が必要。
高H-06パスワードが平文で格納・照合されている
- 箇所
- 02_create_master.sql 33行目(
PASSWORD VARCHAR2(20) -- パスワード(平文))、LoginAction.java 30-31行目(「平文で突き合わせる」と明記)、User.java 10行目。
- 移行時に何が起きるか
- 現行はDB閲覧権限=全員のパスワード閲覧。H-01と組み合わさると外部漏えいに直結。移行時は平文のままコピーせず、初回ログイン時再設定またはハッシュ化移行(bcrypt等)を設計に織り込む。20桁上限もハッシュ格納には不足。
高H-07初期アカウント admin / admin が本番に残存
- 箇所
- 02_create_master.sql 40-42行目。コメントに「開発時に投入。本番でもそのまま残っている」と明記。権限区分9(管理者)。
- 移行時に何が起きるか
- 誰でも管理者としてログイン可能。移行時のユーザマスタ移送で無条件コピーすると新システムにも持ち込まれる。移行対象ユーザの棚卸しと初期アカウント削除を移行手順書に明記すること。
高H-08本番DBの接続先・ユーザ・パスワードがソースコードに直書き
- 箇所
- DBConnection.java 20-22行目(
jdbc:oracle:thin:@hoshino-db01:1521:JKSPRD / jks_app / jksapp2006)。19行目に「TODO 2009-03 外だしする あとで直す」が17年放置。
- 移行時に何が起きるか
- ソース閲覧者全員が本番DB資格情報を知っている状態。パスワードは2006年から未変更と推定される。移行時は資格情報のローテーションと構成外部化(環境変数・シークレット管理)が必須。リポジトリ履歴にも残るため、リポジトリごと配布する場合は要注意。
高H-09認証・認可の欠如 ── ログインチェックは受注一覧のみ、権限区分はどこでも未使用
- 箇所
- ログイン確認があるのは OrderListAction.java 26-29行目だけ。OrderRegistAction / OrderUpdateAction / CustomerSearchAction は未ログインでも実行可能(
*.do へ直接POSTで受注登録・更新・顧客検索ができる)。JSPも直接アクセス可(header.jsp 24-25行目が orderRegist.jsp / customerSearch.jsp へ直リンクしている構造)。AUTH_KBN はセッション格納(LoginAction 34行目)のみで参照箇所ゼロ。
- 移行時に何が起きるか
- 未ログイン実行時は UPD_USER にnullが入る(OrderRegistAction 92行目)ため、現行データの更新者欄にNULL/不明が混在していると推定。移行では「誰が何をできるか」の権限要件がコードから復元できないため、業務部門へのヒアリングで新規定義が必要。
高H-10請求番号が12桁領域に14桁を詰めて桁あふれ ── 請求番号の一意性崩壊推定
- 箇所
- SEIKYU01.cbl 38行目
WK-SEIKYU-NO PIC X(12) に対し、78-81行目のSTRINGは「SK(2)+年月(6)+連番PIC 9(06)」=14桁を転記。COBOLのSTRINGは領域を超えた分を切り捨てるため、連番は先頭4桁しか残らない。連番1〜9999はすべて「0000」「0001」…でなく、ゼロ埋め6桁の先頭4桁(000001→0000、000002→0000…)となり連番の下2桁が失われる。
- 移行時に何が起きるか
- 1か月の請求件数が100件以上あると請求番号が重複する(000100と000101はどちらも「0001」)。T_SEIKYU.SEIKYU_NOはPRIMARY KEY(01_create_transaction.sql 43行目)のため、ファイル→テーブルのロード時に一意制約違反で欠落するか、ロード方式によっては上書きされていると推定。現行のT_SEIKYU件数と請求ファイル行数の突合を移行前に実施すべき。
高H-11月をまたいで出荷された受注が売上・請求から永久に漏れる推定
- 箇所
- URIAGE02.cbl 41-47行目。抽出条件は「受注日の年月=対象年月 かつ ステータス≧20」。バッチは毎月1日深夜に前月分を1回だけ処理する(run_nightly.sh 5行目)。
- 移行時に何が起きるか
- 6月受注・7月出荷の案件は、6月分バッチ時点ではステータス10で対象外、7月分バッチでは受注日が6月なので対象外。どの月にも計上されない。救済ジョブはリポジトリに存在しない。現行の売上実績・請求に計上漏れが蓄積している可能性が高く、移行時の「新旧突合」の前に漏れ分の実態調査が必要。
重大度: 中(12件)
中M-01取消コードが画面によって「90」と「99」で不整合 ── 一覧の絞り込み・表示が壊れている
- 箇所
- 正: CodeConst.java 21行目・orderUpdate.jsp 41行目・SEIKYU01.cbl 47行目は「99」。誤: orderList.jsp 23行目(絞り込みプルダウン)と42行目(表示名変換)は「90」。
- 移行時に何が起きるか
- 一覧で「取消」を選んでも0件(DBに90は存在しない)、取消済み受注は状態名が空欄で表示される。移行時にJSPを仕様の根拠にすると誤ったコード値(90)を仕様化してしまう。コード値のマスタ化(DB管理)を移行方針に含めるべき。
中M-02受注更新が T_JUCHU と T_URIAGE を別コミットで更新 ── 途中失敗で金額不整合
- 箇所
- OrderDao.java 95-135行目。UPDATE T_JUCHU → commit(116行目)→ UPDATE T_URIAGE → commit(125行目)。128行目のコメント自身が「(1)がcommit済みで(2)で落ちると不整合になる」と認めている。
- 移行時に何が起きるか
- 現行データに T_JUCHU.TOTAL_AMOUNT ≠ T_URIAGE.TOTAL_AMOUNT のレコードが存在し得る。移行時にどちらを正とするかの規則(推定: T_JUCHUが正)を決めてクレンジングする必要がある。新システムでは単一トランザクション化が必須。
中M-03オンラインの T_URIAGE 更新がファイルベースの請求処理に届かない ── DB⇔ファイル連携が分断
- 箇所
- OrderDao.java 118-125行目はDBの T_URIAGE を更新。一方 SEIKYU01 はファイル URIAGE.DAT(URIAGE02の出力)を読む(SEIKYU01.jcl 18行目)。T_URIAGEテーブルとURIAGE.DATファイルを同期するジョブはリポジトリに存在しない。
- 移行時に何が起きるか
- 売上集計後〜請求計算前(または請求後)にオンラインで金額を直しても請求には反映されない(あるいは逆に翌月の再集計で二重反映される可能性)。データフロー全体がコードから完全には復元できないため、失われたアンロード/ロードジョブの所在確認が移行アセスメントの残課題である。
中M-04出力エスケープが全JSPで皆無 ── 反射型・格納型XSS
- 箇所
- orderUpdate.jsp 15・19行目(
request.getParameter("orderNo") をそのままhidden値とHTMLに出力=反射型XSS)、customerSearch.jsp 43-46行目・orderList.jsp 45-52行目(DB由来の顧客名等を無エスケープ出力=格納型)、error.jsp 17行目。
- 移行時に何が起きるか
- URLにスクリプトを仕込んだリンクでセッション乗っ取りが可能。移行時にJSPをテンプレートへ機械変換する場合もエスケープ方針(自動エスケープの採用)を必ず設計に入れる。
中M-05ログアウトがセッションを破棄しない
- 箇所
- header.jsp 14行目。「ログアウト」は login.jsp への単なるリンクで、
session.invalidate() がどこにも存在しない。ログイン成功時のセッションID再発行もない(LoginAction.java 32-35行目)。
- 移行時に何が起きるか
- 共用端末でログアウト後もブラウザバック等で操作継続可能。セッション固定攻撃にも弱い。新システムでは認証基盤側で解決するため、現行仕様として引き継がないこと。
中M-06存在しない商品コードで NullPointerException(500エラー)
- 箇所
- OrderRegistAction.java 68-71行目。
ProductDao.findByCode は該当なし(削除済み含む。ProductDao.java 28行目で DEL_FLG='0' 限定)のとき null を返すが、nullチェックなしで p.getPrice() を呼ぶ。OrderUpdateAction.java 72-75行目も同様。
- 移行時に何が起きるか
- 商品コードの打ち間違い・商品の論理削除後の更新で例外画面。特に「受注済み商品の削除→その受注の更新でNPE」という運用ハマりがある。移行時は「削除済み商品を持つ受注の扱い」を仕様化する必要がある。
中M-07SQLException の握りつぶしが全DAOに蔓延 ── 失敗しても成功画面
- 箇所
- OrderDao.java 48・82・127・159行目、CustomerDao.java 57行目、ProductDao.java 39・67行目、UserDao.java 36行目。すべて
printStackTrace のみで呼び出し元へ通知せず、一覧系は空リスト、更新系は成功扱いになる。
- 移行時に何が起きるか
- DB障害時もユーザには正常に見えるため、現行データには「入ったはずが入っていない」類の欠落が潜在し得る(H-04と複合)。移行データの件数突合だけでなく、業務帳票(紙の受注控え等)との照合も検討すべき。
中M-08処理対象年月がJCLに直書き(202606)── 毎月の手修正運用、Unix経路ではSYSIN未供給
- 箇所
- URIAGE02.jcl 15-17行目にSYSINで「202606」を直書き。URIAGE02.cbl 34-35行目は
ACCEPT WK-TARGET-YM FROM SYSIN。一方 run_nightly.sh 18行目の cobrun 呼び出しは対象年月を渡していない。
- 移行時に何が起きるか
- JCL経路は毎月人手でJCLを書き換える運用(修正漏れ=前月分を再集計して二重計上のリスク)。sh経路では標準入力が空でWK-TARGET-YMが空白となり、対象0件で「正常終了」する可能性が高い推定。移行後は実行日から対象年月を自動導出する設計へ変更すべき。
中M-09数量の桁数チェックが登録5桁・更新4桁で不一致(DBは7桁)
- 箇所
- OrderRegistAction.java 57-59行目(5桁以内)⇔ OrderUpdateAction.java 61-63行目(4桁以内)。DB定義は NUMBER(7)(01_create_transaction.sql 13行目)。
- 移行時に何が起きるか
- 数量10000〜99999で登録した受注は更新画面で保存できない(数量を変えない更新でも再入力必須のため引っかかる)。どちらが正しい業務上限かはコードから断定できないため、移行仕様として上限値の確認が必要。
中M-10更新画面が既存値を一切表示せず、全項目の再入力を強いる(状態の初期値は「受付」)
- 箇所
- orderUpdate.jsp 22-43行目は顧客CD・商品CD・数量が空のテキストボックス、状態プルダウンは先頭「10 受付」が既定。既存値を取得できる
OrderDao.findByNo(OrderDao.java 137-167行目)は実装済みだがどこからも呼ばれていない。
- 移行時に何が起きるか
- 「数量だけ直したい」場合も全項目を正しく再入力しないと、うっかり状態が「受付」に巻き戻る等のデータ破壊が日常的に起き得る(H-05と複合)。現行データの品質劣化要因として移行データ検証の観点に含める。
中M-11TAX_RATE 定数が定義されているのに未使用 ── 税率は各Actionに直書き
- 箇所
- CodeConst.java 29-31行目(
TAX_RATE = 0.08、「マスタ化したい あとで直す」の2009年TODO付き)。実際の計算は OrderRegistAction.java 75行目・OrderUpdateAction.java 77行目がリテラル 0.08 を直書きしており、定数を変更しても挙動が変わらない。
- 移行時に何が起きるか
- 「定数を10%に直せば済む」と誤解しやすい罠。税率が3回変わった歴史(5%→8%→10%)に追随できなかった構造的原因でもある。新システムでは適用日付き税率マスタが必須(軽減税率の要否も業務確認)。
中M-12受注日の妥当性検証がゼロ ── 自由入力文字列が月次集計のキーに直結
- 箇所
- OrderRegistAction.java は orderDate を一切チェックせずそのままINSERT(83行目)。画面はmaxlength=8のテキストボックスのみ(orderRegist.jsp 18行目)。この値の先頭6桁が URIAGE02.cbl 41行目で売上帰属月の判定キーになる。
- 移行時に何が起きるか
- 「20260631」「2026/6/1」「空欄」等が正常に登録でき、該当受注は売上集計から漏れる(H-11と同様に永久欠落)。移行データに不正日付が混在している前提でクレンジングルールを用意すること。
重大度: 低(11件)
低L-012桁年窓化のピボットが50固定 ── 2051年問題と境界値の曖昧さ
- 箇所
- DateUtil.java 30-38行目。
yy > 50 で1900年代。YY=50は2050年、YY=51は1951年。
- 移行時に何が起きるか
- 旧システム由来の2桁年データを新システムで再変換する際、同じピボットを使わないと生年月日等が100年ずれる。変換済みデータなら新規実装は不要だが、変換ルールとして本仕様を記録しておくこと。
低L-02文字コードが Windows-31J(Shift_JIS系)固定
- 箇所
- 全JSP 1行目、web.xml・struts-config.xml 1行目のencoding指定。
- 移行時に何が起きるか
- UTF-8移行時に機種依存文字(①、㈱、ハシゴ高等)の顧客名・住所で文字化けや変換不能が出る。DB文字コード(推定JA16SJIS)からの移行も含めた文字コード変換計画が必要。
低L-03Struts 1.2 / Servlet 2.3 ── 20年前にサポート終了系のフレームワーク
- 箇所
- struts-config.xml 2-4行目、web.xml 2-4行目。Struts 1系は2013年EOLで既知の重大脆弱性が複数ある。
- 移行時に何が起きるか
- セキュリティパッチが存在しないため延命不可。移行方式は「リフト(そのまま)」を選べず、少なくともフレームワーク刷新(リライト)が必須である根拠となる。
低L-04コネクションプールなし・リクエストごとに都度DB接続
- 箇所
- DBConnection.java 11行目(コメントで自認)・36-40行目。DriverManagerで毎回接続。
- 移行時に何が起きるか
- 性能面の制約。現行の応答性能を移行後のSLA基準にする際、この非効率を含んだ値である点に留意。
低L-05CSRF対策なし(全フォームにトークンなし)
- 箇所
- login.jsp / orderRegist.jsp / orderUpdate.jsp / customerSearch.jsp の全form。Strutsのトークン機構(saveToken)も未使用。二重送信防止も兼ねて欠落。
- 移行時に何が起きるか
- 外部サイト経由で受注登録・更新を実行させられる。また登録ボタン連打で二重受注が起き得る(H-04の採番方式では同一番号衝突→片方消失の可能性も)。
低L-06実行スケジュールの記述矛盾 ── cron「毎月1日」vs JCL「第1営業日」
- 箇所
- run_nightly.sh 5行目(cron: 0 2 1 * *)⇔ URIAGE02.jcl 6行目(月次 第1営業日 夜間)。
- 移行時に何が起きるか
- 正しい運用日がどちらかコードから断定できない推定。締め日運用(月末締め・翌1日集計か、翌営業日か)の確認が必要。移行後のジョブスケジューラ定義の要件になる。
低L-07SEIKYU01 の構造的な危うさ(インラインPERFORM内のGO TO・EOF後のレコード参照ほか)
- 箇所
- SEIKYU01.cbl 44-61行目: インラインPERFORM内に
GO TO SKIP-RTN と段落ラベル風の SKIP-RTN. が混在する非標準的構造(コンパイラ依存で挙動が変わる恐れ)。79行目: 最終顧客の請求出力時、EOF後の UR-URIAGE-YM(READ後の未定義領域)を参照。47-58行目: 取消レコードをスキップする際 WK-PREV-CUST を更新しないため、顧客の先頭レコードが取消だった場合にブレークが遅延する潜在バグ(現状はURIAGE02側で取消除外済みのため未発現推定)。
- 移行時に何が起きるか
- COBOL資産を別コンパイラ/別基盤へ移す「リホスト」を選んだ場合に挙動差が出やすい箇所。ロジック再実装(リライト)を推奨する根拠のひとつ。
低L-08CustomerDao.findByCode が削除フラグを見ない(search とは非対称)
- 箇所
- CustomerDao.java 77行目(WHERE CUST_CODE のみ)⇔ search は30行目で DEL_FLG='0' 限定。
- 移行時に何が起きるか
- 削除済み顧客もコード直指定なら取得できる。受注登録は顧客コードの存在チェック自体をしていないため実害は限定的だが、「削除済み顧客への受注可否」という業務仕様の確認ポイント。
低L-09合計0円の顧客には請求レコードが出力されない ── 仕様かバグか不明
- 箇所
- SEIKYU01.cbl 62-65行目
IF WK-KINGAKU > ZERO。厳密には「最終顧客」のみこの判定で、途中の顧客は0円でも出力される非対称もある。
- 移行時に何が起きるか
- 0円請求の要否(相殺・値引で0円になるケース)を業務部門に確認しないと、新旧比較で件数差異が出る。
低L-10「出荷区分」定数が定義のみで未使用 ── 失われた機能の痕跡の可能性
- 箇所
- CodeConst.java 33-35行目(SHUKKA_NORMAL / SHUKKA_EMERGENCY)。2011/08の「区分追加」改修コメント(5行目)と時期が符合する推定。対応するカラム・画面は存在しない。
- 移行時に何が起きるか
- 緊急出荷という業務要件が過去に存在した(または頓挫した)可能性。移行要件ヒアリング時の確認項目。
低L-11デッドコード・未使用資産の混在
- 箇所
- OrderDao.findByNo(137-167行目、呼び出しゼロ)、CodeConst.TAX_RATE(M-11)、SHUKKA定数(L-10)、Customer.delFlg / User.delFlg のsetter群(DAOが詰めていない)、DateUtil.expandYmd6 / windowYear / lastDayOfMonth(リポジトリ内に呼び出しなし。バッチ以外の失われたコードから呼ばれていた可能性推定)。
- 移行時に何が起きるか
- 「コードがある=使われている仕様」ではない。移行スコープ見積り時にデッドコードを機能数に数えない一方、DateUtil系は他資産の存在(リポジトリの欠落)を示唆するため所在確認を推奨。
7移行への提言
| 優先 | アクション | 内容・理由 |
P1 即時 |
現行請求金額の緊急検証(移行以前の問題) |
H-02(8%/10%不整合・税込額への二重課税)とH-10(請求番号重複)は現在進行形で顧客への過大請求を生んでいる疑いがある。直近数か月のT_SEIKYUを手計算と突合し、事実なら移行と切り離して即時是正・顧客対応を行う。あわせてH-07(admin/admin削除)とH-08(DBパスワード変更)は今日にでも実施可能。 |
| P2 |
「仕様」と「バグ」の仕分け会議(業務部門との合意形成) |
第4章の暗黙仕様①〜⑤と第6章の各項目を、業務部門と1件ずつ「移行先で踏襲する/直す」に仕分ける。特に (a) 税端数の切り捨て、(b) 売上帰属月=受注日、(c) 更新時の単価洗い替え、(d) 顧客単位合算請求・0円請求なし、の4点は金額に直結するため文書で合意を取る。 |
| P3 |
失われた資産の所在調査 |
DB⇔ファイルのアンロード/ロードジョブ(M-03)、T_SEIKYUロード方式(H-10の実害判定に必要)、README記載の設計書サーバ、DateUtilを呼んでいた他プログラム(L-11)。これらが見つからない場合、本ノートの推定箇所は実データ検証で裏取りする。 |
| P4 |
現行データのプロファイリングとクレンジング設計 |
移行前に実データで検証すべき項目: ①商品コードと単価の矛盾レコード(H-05)、②T_JUCHUとT_URIAGEの金額不一致(M-02)、③不正な受注日(M-12)、④売上・請求への計上漏れ受注(H-11)、⑤UPD_USERのNULL(H-09)、⑥ステータス値の分布(90の混入有無、M-01)。件数を把握してから移行方式・工数を確定する。 |
| P5 |
移行方式は「リライト」を推奨(リホスト・リフト不可) |
Struts 1系はEOLで脆弱性が残置され(L-03、H-01、M-04)、COBOLはコンパイラ依存の危うい構造(L-07)を含むため、そのまま載せ替える価値がない。規模が小さく(画面5・バッチ2・テーブル6)、本ノートの業務ルールを仕様書代わりにすれば再実装は現実的。オンラインとバッチで重複している金額計算を単一のドメインロジックに統合し、税率は適用日付きマスタで管理する。 |
| P6 |
新システムの必須設計要件 |
①DBシーケンスによる採番(H-04)、②単一トランザクション(M-02)、③認証認可の全画面適用と権限設計(H-09)、④パスワードハッシュ化と資格情報の外部化(H-06/H-08)、⑤出荷日ベースの売上計上または再集計可能なバッチ設計(H-11)、⑥コード値のマスタ管理(M-01)、⑦UTF-8化と機種依存文字の変換計画(L-02)。 |
| P7 |
並行稼働・受け入れ基準の再定義 |
現行がバグを含むため「新旧一致」を合格基準にできない。期待値は本ノート第4章の業務ルール(是正後)から独立に算出し、差異は「現行バグ由来/移行ミス由来」に分類してクローズする運用とする。 |