EDIファイルの『仕様書に書かれていない形式ルール』──受発注システム連携で隠れたコスト

仕様書と現物のズレが生まれる理由

EDIや受発注システムの連携案件で、こんな経験をしたことはありませんか。

「仕様書ではCSVと書いてあるのに、実際のファイルを見たら区切り文字がタブだった」「日付フォーマットは『YYYYMMDD』と明記されているのに、特定の取引先だけ『YYYY/MM/DD』で送ってくる」「数値は右詰めで、先頭ゼロ埋めするはずなのに、実データは左詰めになっている」

こうした食い違いは、単なる実装の手落ちではなく、ビジネス側の慣例や歴史的経緯に根ざしていることが多いです。特に基幹システムの世界では、大手企業との取引が始まった当初の仕様がそのまま生き続けることがあります。取引先が「昔からこのやり方で来ているから」と言えば、こちらがそれに合わせるしかない。仕様書はあくまで『理想形』で、現実はその隣に『運用ルール』が存在しているのです。

『暗黙仕様』がコスト化する瞬間

問題は、この暗黙仕様の把握と対応に思わぬ工数がかかることです。

発見のタイミングが遅い

テスト環境では仕様書通りのサンプルファイルが使われます。本番環境で初めて実取引先のファイルを受け取ると、ズレが判明します。その時点では既にシステムが稼働しており、修正は急を要します。

取引先ごとの多様性

一社のルールなら対応は簡単です。しかし複数の取引先と連携する場合、それぞれが独自の慣例を持っていることがあります。ある企業はSJIS、別の企業はUTF-8。ある企業は固定長レコード、別の企業は可変長。こうした違いを吸収するロジックは、思った以上に複雑になります。

仕様変更ではなく『運用対応』として扱われる

本来なら設計段階で詰めるべき内容が、本番運用中に「あ、この企業はこういう形式で送ってくるんですね」と発見されます。すると、それは『バグ修正』ではなく『運用対応』として扱われ、優先度が後回しにされたり、予算外の追加工数として認識されなかったりします。

現場で起こる判断の分岐点

実装方針を決める際、こういう選択肢が出てきます。

1. 厳密な仕様チェック(バリデーション重視)

  • 仕様書通りのフォーマット以外は受け付けない
  • 不正なファイルは明確にエラーで返す
  • メリット:システムの内部整合性が高い、デバッグが容易
  • デメリット:本番で頻繁に運用エラーが発生、ビジネス側との調整が増える

2. 寛容なパース(実用重視)

  • 複数のフォーマットに対応、自動修正を試みる
  • 曖昧な入力でも『おそらくこの意図だろう』と推測する
  • メリット:現場の手作業が減る、ビジネスの流れが止まりにくい
  • デメリット:意図しない変換が起こる、監査ログが複雑になる、後の保守が難しい

どちらが正解かは、案件の性質によって変わります。

実装の現実的な落とし穴

ケース:複数取引先のCSVを統合するシステム

# 取引先A:タブ区切り、日付はYYYYMMDD、金額は右詰めゼロ埋め
取引ID	商品コード	数量	金額
001	ABC123	10	000001000

# 取引先B:カンマ区切り、日付はYYYY/MM/DD、金額は左詰め
取引ID,商品コード,数量,金額
001,ABC123,10,1000

# 取引先C:カンマ区切り、ダブルクォート囲み、金額にカンマ区切り
"取引ID","商品コード","数量","金額"
"001","ABC123","10","1,000"

一見すると「各フォーマットを認識して分岐させればいい」と思えますが、実装上の問題があります。

  • ファイルヘッダの有無が一定でない:取引先によってはヘッダ行があったり、なかったり。あっても列名の表記ゆれがある
  • エンコーディングが混在:仕様書ではUTF-8と書かれていても、実データはSJISで来ることがある
  • 改行コードが異なる:LF、CRLF、CRが混在
  • 末尾の空行や余分なスペース:パースロジックを複雑にする

こうした問題に対応するために、以下のような前処理層を挟むことになります。

def normalize_input_file(file_path, expected_encoding='utf-8'):
    """
    入力ファイルを標準化する前処理
    """
    # エンコーディング自動判定
    detected_encoding = detect_encoding(file_path)
    
    with open(file_path, 'r', encoding=detected_encoding) as f:
        content = f.read()
    
    # 改行コード統一
    content = content.replace('\r\n', '\n').replace('\r', '\n')
    
    # 末尾の空行削除
    lines = [line.rstrip() for line in content.split('\n') if line.strip()]
    
    # 区切り文字の自動判定(タブ or カンマ)
    delimiter = detect_delimiter(lines[0])
    
    return lines, delimiter

こうした前処理を入れると、パーサ本体がシンプルに保たれます。ただし、この前処理層自体が『隠れた複雑性』になり、テストケースが増えます。

設計時に決めておくべきこと

1. 『許容範囲』を明確にする

仕様書に「許容される範囲」を明記する癖をつけましょう。

【日付フォーマット】
- 標準:YYYYMMDD(例:20250115)
- 許容範囲:YYYY/MM/DD、YYYY-MM-DD
- 非許容:MM/DD/YYYY、その他形式
- 不正な場合の動作:エラーログを出力し、ファイル全体を処理中断

2. 取引先ごとの『マッピング設定』を外出しする

ハードコードではなく、設定ファイルで管理することで、本番での対応が容易になります。

trading_partners:
  partner_A:
    encoding: shift_jis
    delimiter: tab
    has_header: true
    date_format: YYYYMMDD
    number_format: zero_padded_right
  
  partner_B:
    encoding: utf-8
    delimiter: comma
    has_header: true
    date_format: YYYY/MM/DD
    number_format: default

3. 検証ログを充実させる

仕様逸脱を検出したときは、単にエラーを返すのではなく、『何がどう違ったのか』を詳細に記録します。これが後の対応判断を早くします。

[WARN] Partner_B のファイル形式が期待値と異なります
  - 期待:日付フォーマット YYYY/MM/DD
  - 実際:2025-01-15(ISO形式)
  - 行番号:5
  - 値:2025-01-15
  - 判定:自動変換を試みました(成功)

小規模チームで始める現実的なステップ

ステップ1:既存ファイルを全て集める

本番運用中なら、過去の取引ファイルを全て取得して、実際のフォーマットを分析します。仕様書と現物の差分を『実績リスト』として記録しておきます。

ステップ2:許容パターンを決定する

『このフォーマットなら受け入れる』という基準を、ビジネス側と合意します。この合意ドキュメントは、後のトラブル時に重要な判断材料になります。

ステップ3:段階的に厳格化する

最初は『寛容モード』で運用を開始し、エラーログを見ながら対応パターンを把握します。その後、許容範囲外の形式が来たときの対応を決めていきます。

ステップ4:自動テストに組み込む

実際に来たファイルをテストケースに含めることで、将来の修正時に回帰を防げます。

最後に

EDIやファイル連携は『地味だが重要』な領域です。仕様書通りのシステムを作ることは比較的簡単ですが、現実のビジネスフローに合わせることは別問題です。

初期設計時に「仕様書に書かれていないルールがあるかもしれない」という前提で、検証層と設定層を分離しておくだけで、本番運用での対応コストは大きく減ります。小さな投資ですが、後の保守性と運用効率に返ってくる判断だと考えています。