料金計算エンジン 設計壁打ち

2026-09-16 ・ 第6版(HP接続・改訂日方式まで決定済み)

チャーター料金の計算を 自前の料金エンジンとして作り直す ための設計資料。作るものは ①パラメータ設定ページ ②料金シミュレーションページ の2つで、置き場所は当面 mcj-companyapps の /pricing。将来は刷新後のHP・予約管理・代理店ポータル・オーナーポータルから叩かれる 社内外まとめて1本の料金API にする。

第1版で挙げた論点10件はすべて方針が決まった。いちばん大きいのは「HPとの金額一致を条件にしない」こと。料金改定を前提にするので、エンジンは HPの再現装置ではなく、これからの料金の正 になる。飛行時間も 空中ETEを5分切り上げ という自前の定義で持つ。

細部6件も決着し、未決はゼロ。端数はHPの実挙動を実測して確認したうえで 1,000円未満切り捨て・丸めてから貸切料金を充当 に決めた(4章)。この版をそのまま実装仕様として使える状態。次は12章 Phase1。

第6版(2026-09-16)で 14章を追加。刷新後HP mcj-web との接続、版の改訂日方式(改定日を先に仕込んで自動で切り替える)、旧LINE見積Bot mcj-quote の廃止を決めた。版が切り替わる基準日は見積を作る日時。

0. 決定サマリ
  • 課金する飛行時間=空中ETE(風補正・空港ファクター込み)を LEGごとに5分切り上げ。ブロックタイムは使わない。往復で時間が違うのはそのまま出す。
  • テクランは料金計算に持ち込まない。燃料を度外視して直行したときの空中ETEで計算する。運航上テクランが要るかどうかは別の判断(配置エンジンの仕事)。
  • 連続5日以上も計算する。HPの4日上限が外れる。むしろ長期の受注を伸ばしたいので、そこで見積が出ないのは機会損失。
  • エンジンは定価のみを返す。代理店割引・キックバックは代理店ポータル側で管理する。
  • 通貨は当面JPYのみ。USDは将来やるので、データモデルには通貨の軸だけ入れておく。
  • 遠域ゾーンは空港ごとに選ぶ。海外運航が始まり空港もゾーンも増えていく前提で、地理条件のハードコードにはしない。
  • 版(プライスブック)+有効化フラグを持つ。見積時点の額がそのまま請求額なので、見積を出した瞬間の版と明細を焼き付ける。
  • 編集権限は最初は不要。ただし後から挟むので、誰がいつ何を変えたかの記録は最初から取る。
  • 置き場所=いまは companyapps、本格運用時に切り離す。パラメータのD1は mcj-master に相乗り。(9章)
  • HPとの金額一致は条件にしない。companyapps 内で十分検証してから公開へ進む。現行料金からの変更を予定している。
  • 飛行料金は1,000円未満を切り捨て、その丸め後の値で貸切料金を充当する(HPの実挙動と同じ順序)。
  • 型式ごとに「社内で使える/公開してよい」の2フラグを持つ。事業機化が未定の機体を公開しないための栓。
  • 最初の投入は現行のままのパラメータ。改定は次の版で。
1. 作るもの

① パラメータ設定ページ /pricing/params

  • 型式ごとに 基本料金・貸切料金/日・飛行単価①②・海外運航料金・早朝/深夜料金 を設定
  • 遠域ゾーンを自由に追加。ゾーンごと × 型式ごとに 遠域料金・施設料
  • 空港ごとに 空港内送迎料金・所属遠域ゾーン を割当(マスタの空港一覧から検索)
  • 税率・丸め規則・版の作成と有効化
  • 変更は 保存の前に差分プレビュー、保存後は版として残る

② 料金シミュレーションページ /pricing/sim

  • 型式・日程・区間(複数日・複数LEG)・早朝深夜を入れると内訳つきで金額が出る
  • 飛行時間は flight-calc.js が算出(直行・空中ETE・5分切り上げ)
  • 内訳の各行が どのパラメータから来たかを表示(値付けを説明できる状態にする)
  • 版どうしの比較モード — 現行料金と改定案で代表ケースがいくら変わるかを一覧(13章)
この2ページは本番影響ゼロ。既存の予約管理・代理店ポータルの見積経路には一切つながない。つなぐのは第4フェーズ。
2. いまの料金の出所 — 全部HPをスクレイプしている
消費者(5系統) 代理店ポータル 見積 mcj-sales → /api/agency/charter-quote 社内見積(予約管理) /api/internal/charter-quote オーナーポータル 見積 /api/owner/charter-quote LINE秘書 見積 mcj-shift 内で直接 空港マスタ同期 mcj-master/tools/hp-airports/ Service Binding mcj-shift hp-calculator.ts フォームPOST → HTML パース 毎回 scrape mcjet.jp /rc/jpy/ さくらVPS・nginx・CGI Git管理外/FTPで直更新 ここが止まると社内業務が同時に止まる 既知の制約 ・ご利用日数は最大4日(5日以上は計算不能) ・パース失敗時に例外を出さず合計0を返す ・タイムアウト/リトライなし
図1:いまの料金の出所。5系統すべてが1本のスクレイプに乗っている。HP刷新で /rc/ を触ると全部止まる。

自前化の一番の理由は、HP刷新でこの依存が切れること。HP側は同僚が担当する予定で、/rc/ を残すか引っ越すかも未決。料金エンジンを自前で持てば、HPは「料金APIを叩くだけの画面」になり、/rc/ のCGIごと廃止できる。

副次的に、4日上限が外れる(連続5日以上を営業部へ回している運用が不要になる)・失敗が静かに0円にならない・料金改定をこちらの都合でできるようになる。

なお旧LINE見積Bot mcj-quote/workers/src/pricing.ts にも自前実装があるが、C510定数のみ・遠域を全LEGの発着地で加算しており料金表とズレている。流用しない。新エンジン完成時に撤去対象。
3. 料金体系(現行HPの写経 / 2026-08-12 時点)

これは 移行の出発点であって、目標ではない。現行から料金を変更する予定なので、この値は「最初に投入する版(2026-04)」として入れておき、改定は新しい版で行う。

項目Mustang(C510)CJ4(C25C)課金単位適用条件
基本料金700,0001,050,000円 / 件1件につき1回
チャーター(貸切)料金600,000750,000円 / 日飛行料金に充当される(4章)
飛行単価①400,000600,000円 / 時間国内離発着
飛行単価②500,000700,000円 / 時間出発地または到着地が海外
海外運航料金250,000250,000円 / 件国際運航時(HPの表は両型式で結合セル=同額。2026-08-13 確認)
早朝出発料金280,000280,000円 / 件初日の出発が 8:59 まで
深夜到着料金280,000280,000円 / 件最終日の到着が 21:00 以降
遠域発着料金(対象地域)C510CJ4
北海道120,000200,000
奄美以南200,000320,000
那覇以南350,000520,000
台湾500,000750,000
韓国250,000350,000
グアム・サイパン—750,000
香港・マカオ—1,300,000
遠域発着料金は実質フェリー料金。適用は一連の 発地と最終着陸地 のみで、途中で降りた遠域では発生しない。同じ空港・同じゾーンでも重複排除しないので、遠域が起終点なら往復ぶん2回乗る。海外運航の開始でここが増えていく。
空港内送迎料金金額
千歳・成田・羽田・関西・福岡・鹿児島・那覇120,000
中部150,000
海外空港施設利用料金・送迎料金金額
台湾650,000
韓国500,000
グアム・サイパン500,000
香港1,500,000
マカオ550,000
消費税 10%。表示はすべて税抜、最後に一括で課税。
HP表記の「Mustang / CJ4」は、社内マスタの型式コードでは C510 / C25C。エンジンのキーは マスタ側の type_code に統一し、表示名だけHP表記に寄せる。G550(N679MS)・HA420 は料金表にないので当面 料金未設定=見積不可(→ 11章の確認5)。
4. 計算式
// 1件(=連続して飛ぶ日の塊 ひとかたまり)あたり・税抜
合計税抜 =
    基本料金                                       // 件に1回
  + Σ日 [ 飛行料金(その日) + max(0, 貸切料金/日 − 飛行料金(その日)) ]   // ← 明細の「貸切料金」
  + Σ使用空港 空港内送迎料金 × 立ち寄り回数
  + 遠域発着料金(発地) + 遠域発着料金(最終着陸地)        // 実質フェリー料金。同じ空港でも重複排除しない
  + 海外運航料金(国際運航なら件に1回)
  + Σ海外空港 海外空港施設利用料金            // HPは空港内送迎に含めて表示する(v0も同じ扱い)
  + 早朝出発料金 + 深夜到着料金

// 立ち寄り回数(2026-09-08 実測)
立ち寄り回数(空港) = その空港への到着回数 + (発地 かつ 最終着陸地ではない なら 1)
    // 羽田→大島→羽田      = 羽田1回(行って戻るだけ。発地と最終着陸地が同じなら1回に数える)
    // 羽田→大島→羽田→大島 = 羽田2回
    // 降りずに出るだけの空港は0回(到着していないので送迎が発生しない)
    // 回送(機体移動)レグも到着として数える

// 回送(機体移動)レグ … 日をまたいで空港がつながっていないときにHPが自動で挟む
    // 飛行時間には入るが飛行料金には入らない。ただし到着として送迎の回数には数える
    前日の最終地によっては挟まれない(2026-09-08 実測)。挟まれないと翌日の出発空港には送迎料金がかからない。
    //   [大島→八丈島][成田→八丈島] → 回送 八丈島→成田 が入り 成田に送迎 120,000 がかかる
    //   [大島→香港][台湾松山→大島] → 回送が入らず 台湾松山の 650,000 はかからない
    // 挟まれない空港(全98空港を実測)= 海外17空港 + 北大東 + 山口宇部 の19。規則ではなく表で持つ
    // (hp-ferry-origins.json)。つまり同じレグ構成でも「日の切り方」で送迎料金が変わる。

// その日の飛行料金(★LEGごとに丸めてから足し、その値で貸切に充当する。丸める単位と順序が金額に効く)
飛行料金(日) = Σその日のLEG floorN( 課金時間(LEG)[分] ÷ 60 × 飛行単価 )
             飛行単価 = 出発地か到着地が海外なら hourly_intl(単価2) / それ以外は hourly_domestic(単価1)
                        // 判定はLEG単位(羽田→那覇は単価1・那覇→台北は単価2)
             floorN   = 1,000円未満切り捨て(HPの実挙動に合わせる。単位はパラメータで変更可)
                        // 日合計を丸めるのではない。C510 5レグの日で1,000円ずれる(決定ログJ)

// 課金時間の定義(第2版で確定)
課金時間(LEG) = ceil5( 空中ETE(直行・風補正込み) )
    空中ETE = 大圏距離 ÷ 巡航速度 × 60 + 固定分 + 風補正
              固定分   = 20分(羽田・成田の発着は30分)
              風補正   = α(季節,型式) × 東西成分 ÷ 100     // 東向きは短縮・西向きは延長
    ceil5(x) = 5分単位で切り上げ                            // 1:02 → 1:05 / 0:58 → 1:00
    // taxi は含めない(ブロックタイムは使わない)
    // テクランは考慮しない(燃料度外視で直行したときの値)

// 早朝・深夜は時刻が入力されているときだけ判定する(未入力なら加算しない)
// 風補正の季節αは各LEGの運航日の月。運航日未定の問い合わせでは当月を使う

合計税込 = 合計税抜 × (1 + 税率)

いちばん間違えやすいのは貸切料金の充当。「総額に対して」ではなく「日ごとに」充当する。飛ぶ日は飛行料金で吸収され、吸収しきれない差額だけ貸切料金が乗る。飛ばない中日は満額が乗る。

そしてもう一段、順序の罠がある。HPは飛行料金を丸めてからその値で貸切料金を充当している(下表)。先に充当してから丸めると金額が変わるので、この順序を守る。

HPの端数処理(2026-08-12 実測)
区間(C510・単価400,000/時)HPの飛行時間理論値(丸め前)HPの飛行料金HPの貸切料金検算
羽田 → 大島0:25166,666.67166,000434,000600,000 − 166,000
羽田 → 松本0:35233,333.33233,000367,000600,000 − 233,000
羽田 → 八丈島0:45300,000.00300,000300,000端数なし
羽田 → 南紀白浜1:05433,333.33433,000167,000600,000 − 433,000
名古屋 → 仙台1:10466,666.67466,000134,000600,000 − 466,000
羽田 → 稚内2:25966,666.67966,0000飛行料金が60万超
羽田 → 石垣4:101,666,666.671,666,0000同上
読み取れること2つ。①HPは1,000円未満を切り捨て、その丸め後の値で貸切を充当している。②HP側の飛行時間もすでに5分刻み(0:25 / 0:35 / 1:05 / 2:25 / 4:10)で、5分切り上げという決定はHPの見せ方とも整合する。
なお CJ4 は単価600,000/時=1分あたり10,000円ちょうどなので、そもそも端数が出ない。端数が問題になるのは C510(単価①400,000=6,666.67/分)と海外発着の単価②(500,000=8,333.33/分)。
小計は各項目が1,000円単位に揃うので、消費税は必ず100円単位になる(例 1,906,000 × 0.1 = 190,600)。
貸切料金/日 = 600,000(C510) 1日目 羽田→大阪 1:05 飛行料金 433,000 貸切 167,000 ← 差額だけ乗る 2日目 飛行なし 貸切 600,000(満額) ← 中日は丸ごと乗る 別の日 羽田→福岡 2:00 飛行料金 800,000 ← 60万を超えるので 貸切 0円
図2:貸切料金は「その日の飛行料金で埋まらなかったぶん」だけ乗る。
  • 「件」の定義=連続して飛ぶ日の塊。連続する日は機材を通しで確保する=1件なので割らない。日ごとに割ると基本料金が件数ぶん乗る。
  • エンジンの責務は「1件を正しく計算すること」まで。塊が複数あるときの「まとめるか分けるか」の最適化は、エンジンを何度か呼ぶ上位レイヤの仕事として分離する。
  • 遠域は 発地と最終着陸地のみ=実質フェリー料金。羽田→下地島→羽田は起終点が羽田なので該当なし、下地島→羽田→下地島は起終点が下地島なので下地島の遠域料金×2。羽田→新千歳の片道は着地ぶんの1回で、往復を1件にまとめると0回・片道2件に割ると各1回。この非線形があるので、まとめ/分けの損得は静的ルールで決め打ちせず必ず両方計算して比べる。
  • 5日以上の制約はなくす。式はそのまま日数ぶん延びる(貸切料金/日が積み上がる)。長期案件で見積が出せるようになる。
5. 飛行時間の引き方

使うもの = FlightCalc.ete() 一本

  • 入力は 発地・着地・型式・運航月 だけ。返る eteMin(空中ETE)を5分切り上げて使う
  • block(taxi込)は使わない
  • 諸元は cfgFromMaster()、空港は airportsFromMaster()=mcj-master が単一情報源
  • 座標さえマスタにあれば、新規空港も海外空港もそのまま計算できる

使わないもの = assessItinerary()

  • テクラン計画・燃料・滑走路長・代替空港の判定は料金には持ち込まない
  • おかげでエンジンの入力が激減し、燃料モデルの精度が料金に影響しなくなる
  • 「運航できるか」は配置エンジン、「いくらか」は料金エンジン、と責務が割れる
  • 結果として、テクランが要る区間でも料金は直行相当=差分は会社が飲む

コピーを増やさないこと。flight-calc.js はいまリポジトリ内に中身が完全に同一のコピーが3つある(mcj-reservation/frontend/=原本・Workerが直接 import、mcj-sales/frontend/、mcj-companyapps/frontend/flight-calc/)。

1
仮フェーズ(companyapps)=同一Pages内で読む。<script src="/flight-calc/flight-calc.js"> で既存ファイルをそのまま参照する。コピーは増えないし、今日から動く。
2
本体化フェーズ=共有コアにする。原本を shared/flight-calc/ に1つ置き、各プロジェクトはそこを import。そのPRで既存3コピーもまとめて解消する。
×
予約管理の GET /api/feasibility を実行時に叩く案は採らない。将来は逆に予約管理が料金エンジンを呼ぶので相互依存になる。
往復で飛行時間が変わる(東向きは追い風で短く、西向きは向かい風で長い)点はそのまま出す。羽田→那覇 2:50 / 那覇→羽田 2:35 のように往路と復路で飛行料金が違う。実態に忠実な値なので、見積書の見せ方(LEGごとに時間を出すか、合計だけ出すか)で吸収する。
6. パラメータのデータモデル

設計の芯は 「遠域ゾーン」を実体として持ち、空港をそこに割り当てること。海外運航が始まって空港もゾーンも増えていく前提なので、地理条件のハードコードにはしない。ゾーンを足す → 空港を割り当てる の2操作だけで対応できる状態にする。

もうひとつは 版(プライスブック)+有効化フラグ。見積時点の額がそのまま請求額になるので、見積を作った瞬間に「どの版で出したか」と明細を焼き付ける。改定は「新しい版を作る → 内容を詰める → 有効化する」の3手。

テーブル列役割
price_booksid / name / currency / effective_from / is_active / status(draft·active·archived) / note / created_by / created_at版。currency は当面 JPY 固定だが列は持つ(将来USD)
price_type_ratesbook_id / type_code / base_fee / charter_per_day / hourly_domestic / hourly_intl / overseas_ops_fee / early_fee / late_fee / is_internal / is_public型式ごとの単価。型式を足す=1行足す。2つの公開フラグで「社内では見積れるが外には出さない」を作れる
price_zonesbook_id / zone_code / name / sort遠域ゾーンの定義。ここを増やすのが「適宜追加」の実体
price_zone_ratesbook_id / zone_code / type_code / remote_fee / facility_feeゾーン × 型式の金額。CJ4のみ設定(C510は「—」)も表現できる
price_airport_feesbook_id / icao / transfer_fee / zone_code空港ごとの送迎料金と所属ゾーン。未登録の空港は 0円・ゾーンなしで落ちる(=警告対象)
price_rulesbook_id / tax_rate / time_basis / rounding_unit_min / rounding_scope / rounding_mode / amount_round_unit / amount_round_mode / early_before / late_after税率・課金時間・金額の丸め。確定値は air / 5 / leg / ceil + 金額は floor / 単位 1000
price_auditid / book_id / actor / action / diff_json / at誰がいつ何を変えたか。権限は後回しでも記録は最初から

仮フェーズは D1 を作らず KV の JSON 1本で始める

  • companyapps には既に KV バインド(COMPARISON_CACHE)があり、航空路マップの手動経路が同じパターンで既に乗っている(/api/routes-local)。同じ形で /api/pricing-params を足せば、新しいリソースを一切作らずにページから編集・保存ができる。
  • JSON の構造は上の7テーブルと1対1にしておく。本体化のとき流し込むだけになる。
// KV: key = pricing-params  (本体化後は上のテーブルに1:1で写る)
{
  "book": { "id": "2026-04", "name": "2026年度 標準", "currency": "JPY",
            "effective_from": "2026-04-01", "is_active": true, "status": "active" },
  "rules": { "tax_rate": 0.10,
             "time_basis": "air",          // 空中ETE(ブロックではない)
             "rounding_unit_min": 5,        // 5分
             "rounding_scope": "leg",       // LEGごと
             "rounding_mode": "ceil",       // 切り上げ
             "techran": "ignore",           // 燃料度外視の直行で計算
             "amount_round_unit": 1000,     // 飛行料金の丸め単位=1,000円(HP準拠)
             "amount_round_mode": "floor",  // 切り捨て。丸めてから貸切に充当する
             "early_before": "08:59", "late_after": "21:00" },
  "types": {
    "C510": { "label": "Mustang", "base_fee": 700000, "charter_per_day": 600000,
              "hourly_domestic": 400000, "hourly_intl": 500000,
              "overseas_ops_fee": 250000, "early_fee": 280000, "late_fee": 280000,
              "is_internal": true, "is_public": true },
    "C25C": { "label": "CJ4", "base_fee": 1050000, "charter_per_day": 750000,
              "hourly_domestic": 600000, "hourly_intl": 700000,
              "overseas_ops_fee": null, "early_fee": 280000, "late_fee": 280000,
              "is_internal": true, "is_public": true }
    // 事業機化が未定の型式を足すときは is_internal:true / is_public:false で入れる
  },
  "zones": [
    { "code": "HKD",  "name": "北海道",         "rates": { "C510": {"remote":120000}, "C25C": {"remote":200000} } },
    { "code": "AMAMI","name": "奄美以南",       "rates": { "C510": {"remote":200000}, "C25C": {"remote":320000} } },
    { "code": "OKA_S","name": "那覇以南",       "rates": { "C510": {"remote":350000}, "C25C": {"remote":520000} } },
    { "code": "TW",   "name": "台湾",           "rates": { "C510": {"remote":500000,"facility":650000},
                                                          "C25C": {"remote":750000,"facility":650000} } },
    { "code": "KR",   "name": "韓国",           "rates": { "C510": {"remote":250000,"facility":500000},
                                                          "C25C": {"remote":350000,"facility":500000} } },
    { "code": "GUM",  "name": "グアム・サイパン","rates": { "C25C": {"remote":750000,"facility":500000} } },
    { "code": "HKG",  "name": "香港",           "rates": { "C25C": {"remote":1300000,"facility":1500000} } },
    { "code": "MFM",  "name": "マカオ",         "rates": { "C25C": {"remote":1300000,"facility":550000} } }
  ],
  "airports": {
    "RJCC": { "transfer": 120000, "zone": "HKD" },
    "RJTT": { "transfer": 120000 },
    "RJGG": { "transfer": 150000 },
    "ROAH": { "transfer": 120000, "zone": "OKA_S" }
  }
}
香港とマカオは 遠域は同額(1,300,000)だが施設料が違う(1,500,000 / 550,000)ため、HP表記では1行でもゾーンは分ける。この手の「表記は1行だが実体は2つ」がこれから増えるので、ゾーンは細かく持って表示側でまとめる方針にする。

ゾーン割当の漏れ対策。空港が増えるほど「ゾーンを割り当て忘れて静かに安くなる」事故が起きやすい。対策を2つ入れる。
① 設定ページに 「マスタにあるがゾーン未割当の空港」一覧 を常設する。
② 見積時、国内でない空港(country_code <> 'JP')にゾーンが無い場合は警告を返す。海外は必ずどこかのゾーンに属するはずなので、これで取りこぼしを拾える。

型式ごとの公開フラグ(2つ持つ)

  • is_internal … 社内見積・予約管理・各ポータルで計算してよいか
  • is_public … HPの公開APIに出してよいか
  • 新しい型式は internal のみ ON で投入し、値付けを検証してから public を立てる。事業機化が未定の機体を外に出さない栓になる
  • 版とは独立。改定の版を切っても公開状態はその版の設定に従う

master の「事実」とは分ける

  • 就航開始日 aircraft.service_start、機体の有効 aircraft.is_active、国際運航の認可 type_intl_rules は master が持つ事実。料金側では持たない(二重管理になる)
  • 料金側のフラグは 「料金を出してよいか」という意思だけを表す
  • 見積が通る条件は master の事実 AND 料金側のフラグ。どちらで弾かれたかを理由として返す
7. 画面
① パラメータ設定 /pricing/params
上部バー(固定)
版:2026年度 標準有効中新しい版を作る差分を見る保存
タブ
型式別単価遠域ゾーン空港共通ルール版の管理
型式別単価(行=型式・列=項目の編集グリッド)
C510 Mustang基本 700,000貸切/日 600,000①400,000②500,000社内◯ 公開◯
C25C CJ4基本 1,050,000貸切/日 750,000①600,000②700,000社内◯ 公開◯
G550(例)未設定社内◯ 公開✕
遠域ゾーン(+ゾーンを追加)
北海道奄美以南那覇以南台湾韓国グアム・サイパン香港マカオ+ 追加
選んだゾーン → 型式ごとの 遠域料金 / 施設料 を編集 + 所属空港を割当
空港(マスタの空港一覧を検索して割当)
検索:那覇ROAH 那覇送迎 120,000ゾーン:那覇以南
⚠ ゾーン未割当の海外空港 3件
版の管理
2026-04 標準有効2026-10 改定案下書き2025-04アーカイブ
② 料金シミュレーション /pricing/sim
入力
型式 C510版 2026-04日程 8/20〜8/22
8/20 羽田 → 大阪 09:00発
8/21 (飛行なし)
8/22 大阪 → 羽田 17:00着+ LEGを追加
飛行時間(flight-calc / 直行・空中ETE・5分切り上げ)
羽田→大阪 1:02 → 1:05大阪→羽田 0:58 → 1:00
内訳(各行に出所バッジ)
基本料金 700,000飛行料金 833,000貸切料金 967,000
空港内送迎 120,000遠域 0早朝深夜 0
小計 2,620,000消費税 262,000合計 2,882,000
検証
版どうしを比較代表ケース一括実行JSONで出力
内訳の各行には「どのパラメータのどの値から来たか」を出す。値付けを説明できる状態=営業が使える状態、という判断。金額が合わないときの調査も速い。
8. API 契約
// 見積(本体。将来HP・予約管理・各ポータルが叩く)
POST /api/pricing/quote
{
  "type_code": "C510",
  "legs": [
    { "date": "2026-08-20", "from": "RJTT", "to": "RJOO", "dep_time": "09:00" },
    { "date": "2026-08-22", "from": "RJOO", "to": "RJTT", "arr_time": "17:00" }
  ],
  "days": ["2026-08-20","2026-08-21","2026-08-22"],   // 省略時はLEGの初日〜最終日
  "options": { "early_departure": null, "late_arrival": null },  // null=時刻から自動判定
  "price_book": null,        // null=いま有効な版
  "flight_minutes": null     // 指定すればコアを呼ばずその値で計算(版比較・実績精算用)
}
→
{
  "price_book": "2026-04", "currency": "JPY",
  "lines": [
    { "code":"base",     "label":"基本料金",   "qty":1,     "unit":700000, "amount":700000, "src":"types.C510.base_fee" },
    { "code":"flight",   "label":"飛行料金",   "qty":2.083, "unit":400000, "amount":833000, "src":"types.C510.hourly_domestic" },
    { "code":"charter",  "label":"貸切料金",   "qty":3,     "unit":600000, "amount":967000, "src":"types.C510.charter_per_day" },
    { "code":"transfer", "label":"空港内送迎", "qty":1,     "unit":120000, "amount":120000, "src":"airports.RJTT.transfer" }
  ],
  "subtotal": 2620000, "tax": 262000, "total": 2882000,
  "meta": { "legs":[{"from":"RJTT","to":"RJOO","air_min":62,"billed_min":65}],
            "warnings": [] }
}

// パラメータ(設定ページが使う。書き込みは将来 社内認証必須)
GET  /api/pricing/params            // いま有効な版
GET  /api/pricing/params?book=2025-04
PUT  /api/pricing/params            // 版ごと差し替え(版を切らずに上書きはさせない)
POST /api/pricing/books/:id/activate // 有効化(他の版の is_active を落とす)
GET  /api/pricing/books

設計上の約束

  • エンジンは定価だけを返す。代理店割引・キックバックは代理店ポータル側の管理。割引率がエンジンに入ると社外公開できなくなる
  • 1件=1リクエスト。まとめ/分けの比較は呼び出し側がN回叩いて比べる
  • 飛行時間は差し込める。flight_minutes を渡せる形にしておくと、版どうしの比較も実績時間での再計算もできる
  • 合計0は必ずエラー。いまのHPスクレイプの「静かに0円」を繰り返さない
  • 見積を保存する側が price_book と lines を丸ごと持つ。あとで料金が改定されても請求額が動かない

公開(HP)向けの追加

  • APIキー + レート制限(未認証の公開口になるため)
  • CORS を刷新後HPのオリジンに限定
  • 公開版では src(パラメータの出所)を落とす
  • 問い合わせ導線に使うので、計算不能でも理由を返す(運航不可空港・型式未設定・国際運航の認可制約など)
9. 本体の置き場所 — API は切り離す・D1 は master に相乗り

ここは 「Worker(API)をどこに置くか」と「D1(パラメータ)をどこに置くか」を別々に決められるのが要点。D1 は Service Binding を挟まなくても複数の Worker から直接バインドできるので、「APIは独立・データはmasterのDBに同居」という組み合わせが取れる。

D1 は mcj-master-prod に相乗り(price_* テーブル群)

  • パラメータの半分(空港ごとの送迎料金・遠域ゾーン所属)は事実上「空港の属性」。airports と同じDBにある方が素直で、ゾーン割当漏れの検出が1クエリで書ける
  • master には既に aircraft_types(型式諸元)という商売寄りの値も入っている。料金だけ別DBにする強い理由がない
  • 新規D1がゼロ。Free プランはアカウント全体で10個上限で、既に到達した経緯がある
  • 守るルール:テーブル接頭辞 price_ で所有を明示し、書き込みは料金Workerだけ。master worker は price_* を触らない

Worker は独立(mcj-pricing-api)

  • 公開トラフィックを master に流したくない。master は社内の単一情報源で、落ちると全社が止まる。HPからの未認証アクセスは別Workerで受けてレート制限をかける
  • 社外公開(HP)と社内利用で必要な保護が違うのを、1か所で設計しきれる
  • /rc/ のCGI廃止の受け皿になる
  • 予約管理に置かない理由:あそこは 「/api/* 既定拒否+公開許可リスト」 の認証設計で、未認証の公開口と相性が悪い
消費者 刷新後HP(公開) 予約管理(社内見積) 代理店ポータル + 割引はここで乗せる オーナーポータル LINE秘書 APIキー / Binding mcj-pricing-api POST /api/pricing/quote GET/PUT /api/pricing/params 1件を計算する純関数+公開口 飛行時間コア flight-calc shared/ に1本(コピーを解消) D1 直接バインド mcj-master-prod(D1) airports / aircraft / aircraft_types … master が所有 price_books / price_type_rates / price_zones price_zone_rates / price_airport_fees / price_rules ↑ 書き込むのは料金Workerだけ
図3:将来像。料金APIは独立させて公開口を持たせ、パラメータは空港・機材と同じDBに置く。HPは「APIを叩く画面」になる。
仮フェーズ(companyapps + KV)から本体へ移るときは、KVのJSONを price_* テーブルへ流し込むだけになるよう、構造を1対1に保っておく。
10. 決定ログ
#論点決定効いてくるところ
1課金する飛行時間の定義と丸め空中ETE(風・空港ファクター込み)を LEGごとに5分切り上げ。ブロックは使わない。往復非対称はそのまま出す4章の式。rules.time_basis=air / unit=5 / scope=leg / mode=ceil
2テクランの扱い考慮しない。燃料度外視で直行したときの空中ETEで計算コアは ete() だけ呼ぶ。assessItinerary() は使わない(5章)
3連続5日以上の値付け問題ない。むしろ伸ばしたいHPの4日上限に由来する hp_days ブロックを撤廃(12章 Phase4)
4割引をエンジンに入れるか入れない。代理店ポータル等で別管理。エンジンは基本の計算のみ8章「定価だけを返す」。公開APIにそのまま出せる
5多通貨いまはJPYのみ。将来USDを実装するprice_books.currency 列だけ先に持つ(6章)
6遠域ゾーンの帰属空港ごとに選ぶ。海外運航開始で空港もゾーンも増える前提ゾーン追加+割当のUI、割当漏れの検出2種(6章)
7版と再現性見積時点の額がそのまま請求額。版管理+有効化フラグを持つprice_books.is_active、見積側に price_book と明細を焼き付け(8章)
8編集権限・監査最初は権限不要。いずれ挟む(管理方法は別途)price_audit は最初から記録。認証は後付けできる形にしておく
9本体の置き場所いまは companyapps。本格運用時に切り離す。パラメータD1は master に相乗り9章。mcj-pricing-api Worker + mcj-master-prod の price_*
10切替の合格基準HPとの合致は問わない(料金変更を予定)。companyapps 内で十分検証してから公開へ13章。検証は「HP再現」から「版どうしの差分確認」へ
A金額の端数1,000円未満切り捨て。丸めてから貸切に充当(HP実挙動と同じ順序)4章の floorN。rules.amount_round_unit=1000 / mode=floor
B時刻未定のときの早朝・深夜時刻が入力されていなければ加算しない内訳に「時刻未定のため未計上」と出す
C風補正に使う月各LEGの運航日の月。運航日未定なら当月コア呼び出し時の month
D飛行単価②の判定範囲LEG単位。旅程全体のどこかが海外なら②、という判定にするなら再検討4章の式に明記
E料金表にない型式型式ごとに is_internal / is_public の2フラグを持つ。事業機化が未定なら公開しない6章。master の事実(就航開始・認可)とは分けて管理
F改定の中身まずは現行のままのパラメータで作る。改定は次の版で12章 Phase2。エンジンは値を持たないので改定を待たずに実装できる
G遠域の数え方実質フェリー料金なので一連の発地と最終着陸地のみ。同じ空港でも重複排除しない。海外も同じ扱いで、関西⇄台北の往復1件は遠域0(海外運航料金と施設利用料は乗る)(2026-08-13 確定)4章の式。切替パラメータは持たない(誤設定で請求が狂うため)
M空港内送迎の数え方空港ごと1回ではなく「立ち寄り回数」= その空港への到着回数 +(発地かつ最終着陸地でないなら1)(2026-09-08 実測)。行って戻るだけなら1回、経由して何度も降りればその回数、降りずに出るだけなら0回4章の式。rules.transfer_scope='visit'。海外空港の施設利用料も同じ数え方(HPは空港内送迎に合算して表示する)
N回送(機体移動)レグ日をまたいで空港がつながっていないとHPが自動で挟む。飛行料金には乗らないが送迎の回数には数える。しかも前日の最終地によっては挟まれない(2026-09-08 実測)。挟まれないのは海外17空港+北大東+山口宇部の19空港で、規則で説明できないので全98空港を実測した表で持つ同じレグ構成でも日の切り方で送迎料金が変わる。hp-ferry-origins.json と PricingV0.withFerryLegs()
IHP刷新前のバックアップ現行HPの計算を総当たりで採取し、凍結版 v0 として GitHub に持つ(2026-09-08)。飛行時間 9,506区間×2機種・空港98・料金定数はすべて実測から逆算tools/hp-backup/ と frontend/pricing/v0/。HP刷新・料金改定のあとも過去の見積を1円まで辿れる
J飛行料金を丸める単位LEGごとに1,000円未満切り捨て。日合計を丸めるのではない(2026-09-08 実測で確定)C510 5レグの日で1,000円ずれる。rules.flight_round_scope=leg。1日1レグの検証しかしていなかったため長く見えていなかった
KHPの飛行時間の向き向きで値が違う区間がある(松本→成田 1:40 / 成田→松本 0:40)。無向にまとめず、向きつきで持つ(2026-09-08 実測)v0のマトリクスは <発ID>-<着ID>。往復の金額が非対称になるのはHPの実挙動
LHP掲載の料金表と計算ページのズレMustangの海外料金が2つの資料で食い違う(2026-09-08 実測)。
・韓国の遠域発着:料金表 250,000 / 計算ページ 350,000
・グアム・サイパン、香港・マカオ:料金表は Mustang 「-」(設定なし)だが、計算ページは 700,000 で計算してしまう
・海外空港施設利用料:金浦とグアム圏だけ Mustang 650,000(料金表は 500,000)。CJ4 は 500,000 で表どおり
v0は計算ページ側に合わせてある(実際に出した見積がそちらだから)。2026-04版をどちらに寄せるか、そもそも Mustang で海外に飛ぶのかは要判断。空港ごと・機種ごとの額は airports.*.transfer_by_type で持てるようにした
OUSD版の凍結(v0-usd)USDページ mcjet.jp/rc/usd/ も同じやり方で総当たり採取して凍結した(2026-09-09)。為替換算では一致しない別の料金体系で、挙動も一部違う。
・USDページは回送(機体移動)レグをまったく挟まない(全98空港で実測)→ 同じ旅程でも空港送迎の回数が変わる
・CJ4のグアム・サイパン発着に遠域発着料金がかからない(JPYページは 750,000)
・海外運航料金 Mustang 3,900 / CJ4 1,800(JPYはどちらも 250,000)
・空港送迎が機種で違う空港の顔ぶれが逆(JPYは金浦とグアム圏、USDは台湾4空港と香港・マカオ)
・金額の丸めは 100 USD未満切り捨て・LEGごと
・飛行時間表はJPYと1分も違わない(4,753ペア×2機種×両方向を採取して照合)ので表は1つを共有する
v0-price-book-usd.json ほか。画面 /pricing/v0/ の右上で JPY / USD を切り替える。掲載の英語料金表とのズレは known_discrepancies に書き出してある
HCJ4の海外運航料金Mustangと同額 250,000円/件。HPの表が結合セルだった(2026-08-13 確定)3章の表。types.C25C.overseas_ops_fee
P版が切り替わる基準日見積を作る日時(2026-09-16 決定)。運航日基準にすると改定の公表前に新料金が漏れる14-1章。見積側が price_book と明細を焼き付けるので、いったん出した見積は改定後も動かない
Q版の切替方法is_active を捨て、版が改訂日 effective_from を持つ。有効版=改訂日が過ぎた中で最新。null は下書き14-1章。改定日を先に仕込めるので当日の作業が要らない。as_of で過去時点の再現もできる
RHPが叩く口の置き場所独立Worker mcj-pricing-api(9章の決定を実行)。companyapps には公開口を置かない14章。mcj-web の Pages Function → Service Binding で中継。companyapps は社内の編集・検証画面として残す
S就航空港一覧の公開可否公開してよい(2026-09-16 回答)。GET /v1/airports で配る14-2章。mcj-web の静的一覧(89空港)と v0(98空港)のズレも同時に解消する
T内訳の見せ方APIは常にフル内訳を返し、表示の粒度はHP側で決める。当面は現行HPと同じ表示、今後は丸める14-2章。見せ方が変わってもAPIを触らずに済む
U旧LINE見積Bot mcj-quote廃止(2026-09-16 決定)。D1が既に存在せず全ルート500で、実質2026-03から死んでいた14-3章。コード・資料6か所の撤去と、本番Worker/Pages削除(要 高橋確認)・LINE webhook の解除
11. 細部の決定
A金額の丸め
決定:1,000円未満切り捨て

HP準拠の1,000円未満切り捨てとする。丸めてから貸切料金に充当する順序も同じ(4章の実測表)。いまの見積書と桁の見え方が変わらない。羽田→大島なら 166,666.67 → 166,000。

単位は rules.amount_round_unit のパラメータなので、あとから100円に変えることもできる(版を切れば過去の見積は動かない)。

  • B 時刻未定の早朝・深夜 … 加算しない。内訳に「時刻未定のため未計上」と出す。
  • C 風補正の月 … 各LEGの運航日の月。運航日未定なら当月。
  • D 飛行単価②の判定 … LEG単位。旅程全体で判定する運用になったら再検討。
  • E 型式の公開 … is_internal / is_public の2フラグ(6章)。事業機化が未定なら公開しない。
  • F 改定の中身 … まずは現行のままのパラメータで作る。改定は次の版で。
12. 進め方
Phase 1

companyapps で作る

パラメータ設定(KVのJSON)+シミュレーション。飛行時間は同一Pages内の /flight-calc/flight-calc.js を読む(コピーを増やさない)。

本番影響ゼロ・既存経路に触らない

Phase 2

現行料金を版として投入

現行のままのパラメータを「2026-04 標準」として入れ、代表ケースを回して内訳の妥当性を確認。HPと突き合わせるのは参考まで。

ここで料金改定の議論の土台ができる

Phase 3

mcj-pricing へ切り出す

Worker化+パラメータを mcj-master-prod の price_* へ。同PRで flight-calc を shared/ に集約し3コピーを解消。

要 高橋相談(新Worker・masterへのmigration)

Phase 4

社内の見積を載せ替える

予約管理の charterQuoteHandler を mcj-shift のHPスクレイプから mcj-pricing の Service Binding へ差し替え。4日上限のブロックを外す。

本番影響あり・段階切替+差分監視

Phase 5

HPに公開APIを出す

刷新後HPが叩く公開口(APIキー・レート制限・CORS)。ここまで来ると /rc/ のCGIが不要になる。

HP側の進行と歩調を合わせる

Phase 6

後片付け

mcj-shift/hp-calculator.ts・mcj-quote/pricing.ts・空き確認画面の概算モデルを撤去。料金の実装を1本にする。

残していると必ず食い違う

Phase 1〜2 はこのリポジトリ内で完結し、誰の業務も止めない。Phase 3 以降は本番影響が出るので着手前に高橋さんへ相談する。
13. 検証

HPとの一致を条件にしないので、検証の軸は2つになる。①内訳が式どおりに積み上がっているか(回帰)と、②版を変えたときに金額がどう動くか(差分)。②はそのまま 料金改定の検討ツール になる。

① 回帰 — 代表ケースを固定

  • 飛行時間を固定値で差し込み(flight_minutes)、パラメータを変えていないのに金額が動いたら落ちる、という形にする
  • ケースは「単発 / 連続2日 / 中日1日 / 中日2日 × 近距離・遠距離・遠域あり」+連続7日(HPでは作れなかった形)+海外発着
  • 非線形の確認:羽田発着の往復1件なら遠域0回、片道2件に割ると各1回。遠域が起終点なら往復ぶん2回

② 差分 — 版どうしの比較

  • 同じケース集合を「現行 2026-04」と「改定案」で流し、1件ずついくら変わるかの一覧を出す
  • 影響の大きい順に並べれば、改定の議論がそのまま数字でできる
  • 飛行時間の定義変更(空中ETE・5分切り上げ)による増減も、この形で1回だけ測っておく
参考として、現行HPの実測値(2026-08-03・税込・往復)を残しておく。新エンジンはこれと一致しないが、桁が違わないことの目安にはなる。
C510 羽田⇄伊丹 単発×2件 3,124,000 / 連続2日 2,222,000 / 中日1日 2,882,000 ・・・ CJ4 羽田⇄新千歳 単発×2件 5,148,000 / 連続2日 3,289,000 / 中日1日 4,114,000。中日1日ぶんの増分は C510 660,000・CJ4 825,000(=貸切料金/日 ×1.1)で一定。
14. HP接続と版の改訂日(第6版・2026-09-16)

刷新後HP mcj-web の見積UIを料金エンジンに繋ぐ段になった。HP側は 料金を一切計算しない(mcj-web の CLAUDE.md「独自の料金計算ロジックを実装しない」)ので、金額の正はこのエンジンだけになる。接続点は public/design/c*/quote.js の requestQuote() 関数ひとつで、そこを差し替えれば繋がる状態まで HP 側は出来ている。

ここで9章の「本格運用時に切り離す」を実行に移す。公開トラフィックを迎えるのは companyapps ではなく独立Worker mcj-pricing-api。companyapps /pricing/ はパラメータ編集・シミュレータ・v0閲覧の社内画面として残す。

あわせて、近々料金改定があることが決まったため、版の切替を is_active の1フラグから 改訂日(effective_from)方式に変える。改定版を事前に作って日時を仕込んでおけば、当日の作業ゼロで切り替わる。

同一Pages内 Service Binding mcj-web 見積UI /quote/ ・ requestQuote() Pages Function /api/quote(中継) mcj-pricing-api 公開Worker ・ 定価のみ 社内4系統 予約管理 / 代理店 / オーナー / 秘書 v0(凍結・不変) JSONをWorkerにバンドル price_* テーブル(改定版) mcj-master-prod に相乗り 有効版で分岐 mcj-companyapps /pricing/(社内のみ) パラメータ編集 ・ シミュレータ ・ v0閲覧 版の編集・改訂日の設定
公開の口は mcj-pricing-api だけ。社内も同じWorkerを叩くので、HPと社内の見積が食い違う余地がない。計算コード(pricing-core.js / pricing-v0.js)は shared/ に1本置き、Worker と companyapps の両方が読む。

公開の口は独立Worker mcj-pricing-api(9章の決定を実行)

  • HPからは 直接CORSで叩かない。mcj-web の Pages Function /api/quote からサーバ間で呼ぶ。Origin制限・レート制限・キー秘匿がそこで片づく
  • 同一アカウントのWorkerへ URL fetch すると 1042 になるので、mcj-web Pages → pricing は Service Binding
  • 旧 mcj-quote-api は discount_rate を呼び出し側が指定できた。この作りは引き継がない(4章・定価のみ)

companyapps に公開口を置かない理由(実測)

  • functions/api/pricing-params.js の PUT が今も無認証で、公開Pages URL 上にある。料金パラメータを誰でも書き換えられる状態
  • KV が FedEx照合のOAuthトークンと同じ namespace(COMPARISON_CACHE)の相乗り
  • MASTER_API / SCHEDULE_API の Service Binding と同じデプロイ単位。PDF分割の修正で公開HPの料金が落ちうるし、公開側のロールバックに社内ツールが巻き込まれる
14-1. 版の切替 — 改訂日方式

7章では「版+有効化フラグ」としていたが、フラグ1つだと改定日を予約できない(当日に人が押しに行くことになる)。版が改訂日を持ち、APIがその時点で有効な版を自分で選ぶ形に変える。

// price_books(6章の定義からの差分)
  id              // 'v0' | '2026-04' | '2026-11' ...
  label           // 表示名
  currency
- is_active       // ← 捨てる
+ effective_from   // '2026-11-01'(その日の 00:00 JST から)。null = 下書き
  note

// 有効版の決め方(as_of 省略時は「いま」)
有効版 = effective_from IS NOT NULL AND effective_from <= :as_of の中で最新のもの

これで何ができるか

  • 改定版を事前に作り込んで日時を入れて放置 → 当日に勝手に切り替わる。デプロイも当日作業も不要
  • effective_from が null の間は下書き。編集途中の版が公開に出ない
  • 改定日の前倒し・後ろ倒しは日時の書き換えだけ
  • effective_from = 今 にすれば即時切替にもなる(「任意のタイミングで切り替える」も同じ仕組みで足りる)
  • 副産物として as_of を渡せば過去の時点の料金を再現できる。v0を凍結した目的とそのまま噛み合う

v0 と改定版でデータの出所が分かれる

  • v0 は凍結・不変なので JSON を Worker にバンドルする(最大 hp-flight-times.json 224KB)。KV も D1 も要らない
  • 改定版は編集されるので price_*(または仮フェーズのKV)から読む
  • 版の一覧(メタ)はストアに持ち、データの実体だけ分岐する。v0 も effective_from を過去日として同じ表に並べる
  • すぐ改定が来るので、この分岐は最初から入れておく。あとから足すと有効版の判定を二重に書くことになる
P「その時点」の基準日をどちらに取るか
決定:見積を作る日時

11/1 改定として、10/20 に「12/5 に飛ぶ便」の見積を出したらどちらの料金か、という話。

A見積を作る日時を基準にする(採用) … 10/20 に出した見積は旧料金。見積時点の額がそのまま請求額(決定#7)なので、提示した額がそのまま生きるのが素直。お客様への説明も「〇月〇日までにお見積りしたものは旧料金」で通る。
B運航日を基準にする … 12/5 に飛ぶので新料金。「11月以降の運航分から改定」という告知にはこちらが合うが、改定を公表する前に新料金が漏れる。10/20 の時点で新料金を出してしまうため。

見積側が price_book と明細を焼き付ける(8章)ので、いったん出した見積は改定後も動かない。基準日が効くのは「新しく見積を作るとき」だけになる。

切替日は一発勝負なので、次の2つは実装時に必ず潰す。

① Workers の時刻はUTC。D1 の datetime('now') も localtime もUTCで、過去に踏んでいる。「2026-11-01 00:00 に切り替える」つもりで素直に書くと 9時間ずれて 10/31 15:00 に切り替わる。実装では日付だけを持ち、'YYYY-MM-DD' を「その日の 00:00 JST」と読む(比較の直前に +09:00 を付ける)。画面の入力が date なので、書き方を1つに固定できるほうが事故が少ない。切り替わる瞬間が 00:00 JST であることは回帰テストで固定した。

② 公開APIのレスポンスをキャッシュしない。mcj-web の Pages Function で素直にキャッシュを効かせると、改定日をまたいで古い額が出続ける。cache-control: no-store か、ごく短いTTLにする。

14-2. 公開APIの口
// 見積(8章の /api/pricing/quote の公開版。割引は受け付けない・src は落とす)
POST /v1/quote
{ "type_code":"C510", "currency":"JPY",
  "legs":[{ "date":"2026-11-05", "from":"RJTT", "to":"RJOO", "dep_time":"09:00" }],
  "options":{ "early_departure":null, "late_arrival":null } }
// price_book は受け付けない(公開は常に「いま有効な版」)
→ { "price_book":"2026-11", "currency":"JPY", "lines":[...], "subtotal":..., "tax":..., "total":... }

GET /v1/airports          // 有効版で見積できる空港。HP側の静的一覧を捨てる
GET /v1/books             // 版の一覧と改訂日(社内画面用)
POST /v1/quote?as_of=...  // 過去時点の再現。社内のみ・公開口には出さない

内訳はフルで返し、見せ方はHP側で決める

  • 当面は現行HPと同じ表示。今後まとめて丸める方針だが、APIは常にフル内訳を返す
  • こうしておけば、表示の粒度が変わってもAPIを触らずに済む
  • 公開版では src(パラメータの出所)は落とす(8章)

就航空港の一覧は公開してよい(2026-09-16 回答)

  • mcj-web は空港名を quote.js に静的に持っている。これを GET /v1/airports に置き換える
  • いま既にズレている:mcj-web 89空港(国内85+台湾4)/ v0 98空港(国内81+台湾4+韓国7+香港マカオ2+グアム圏4)
  • 版によって空港も変わるので、一覧もAPIから配るのが正しい
14-3. 旧LINE見積Bot mcj-quote は廃止

mcj-web の docs/quote-api-contract.md が接続先候補として mcj-quote-api を挙げていたので現況を確認したところ、すでに死んでいた。代理店向けにLINEへ「羽田→福岡 往復 3名」と投げると見積を返すBotで、agencies.discount_rate に ARILA 12% 等を持っていたもの。割引の役目は代理店ポータル(mcj-sales)へ移行済み。

死んでいる根拠(2026-09-16 実測)

  • D1 mcj-quote-prod が もう存在しない。wrangler d1 list に無く、指定すると 7404(7403/10000 と違い transient ではない)
  • Worker は残っていて GET /api/agencies が 500。しかも素の text Internal Server Error = onError をJSON化した #676(2026-07-24)以降デプロイされていない
  • 実質の最終更新は 2026-03-29
  • mcj-shift/workers/wrangler.toml の QUOTE_API_URL は src/ から一度も参照されていない(Env の型宣言だけ)の死に設定
  • mcj-quote-logic.pages.dev は静的ページなので 200 で生きている

撤去する残骸

  • コード … mcj-quote/、mcj-shift の QUOTE_API_URL(toml+型宣言)
  • 資料 … README.md、system-map.html、system-overview.html、docs/tech-stack.html、dev.html の Quote Bot カード
  • 別リポジトリ … mcj-web docs/quote-api-contract.md を新APIで書き直す
  • 本番の Worker / Pages 削除は本番操作なので高橋さんに確認してから
  • LINEチャネルのwebhookがまだ向いているなら外す。放置すると「代理店がLINEに投げても無反応」が残る
14-4. 段取り
H-1

mcj-pricing-api を立てる

v0 を返すだけの最小Worker。凍結JSONをバンドルするので KVもD1も要らない。POST /v1/quote と GET /v1/airports。

要 高橋相談(新Worker)

H-2

mcj-web と繋ぐ

Pages Function /api/quote + Service Binding、requestQuote() の差し替え、空港一覧をAPIへ。ここでHPに金額が出る。

mcj-web 側のPR(別リポジトリ)

H-3

改訂日方式に載せ替える

is_active を effective_from へ。版の一覧をストアに持ち、v0 とストアで実体を分岐。設定画面に改訂日の入力を足す。

改定に間に合わせる必要あり

H-4

改定版を投入して日時を仕込む

改定後のパラメータを新しい版として作り、下書きのまま検証。13章②の差分比較で1件ずついくら動くか一覧を出してから改訂日を入れる。

料金の中身は営業判断

H-5

社内4系統を載せ替える

12章 Phase4 と同じ。予約管理の charterQuoteHandler を HPスクレイプから mcj-pricing-api へ。HPと社内が同じ版を見る状態にする。

本番影響あり・差分監視

H-6

後片付け

mcj-quote 撤去(14-3)、hp-calculator.ts 撤去、/rc/ のCGI停止。料金の実装を1本にする。

残していると必ず食い違う

H-1・H-2 は v0 をそのまま出すだけなので、改定の中身が決まっていなくても進められる。改定に間に合わせる必要があるのは H-3 まで。
mcj-companyapps / pricing — 設計壁打ち 第6版(2026-09-16)