【Bubble×PMS】ローカルとクラウドの境界線に橋を架ける~中小ホテルマンのDX奮闘記vol.6-2~
前回記事の続きでBubbleアプリから指定フォルダのCSVファイル監視方式のPMSへのPOSシステムの実現にどのようなことを意識して挑戦したか書いています。
技術面の話がかなり多いので、ホテル関係者は目次の最後にどのくらい費用がかかったかを書いていますので、そこだけでも参考になればと思います。
Scene1:繋ぐといってもどういうイメージ?
ホテリエちゃん
「コーギーマネージャー……“どうやって”のところ、イメージがわかないです!」
コーギーマネージャー
「OK。今回の肝は “PMSにAPIがない” ことを前提に、CSV監視フォルダに確実に落とす 仕組みを作ること。全体像はこう。」
[Bubble(クラウド)]
↓ HTTPS POST(注文JSON)
[ngrok(トンネル)]
↓
[レストランPC(Windows)]
↓ Node/Express が受信 → 16列CSV生成(Shift_JIS等)
↓ アトミック書き込み(tmp→rename)
[満室御礼:フォルダ監視取り込み]
ホテリエちゃん
「橋を架けるって、こういうことか……!」
Scene2:設計方針は“CSVを正しく出す”に全振り
コーギーマネージャー
「開発の優先順位はシンプル。」
PMSが読めるCSV仕様に100%合わせる(列数・並び・フラグ・文字コード)
二重送信でも二重計上しない(重複防止)
現場PCが落ちても復旧しやすい(ログ・ストレージ・再送)
ホテリエちゃん
「Webアプリの開発というより、“現場で壊れない仕組み作り” ですね……」
Scene3:受け口は “注文JSON” で統一
Bubble側から送るのは「1注文=複数アイテム」のJSON。
例(概念):
{
"order_id": "R20251101-0001",
"room_no": "A-101",
"sold_at": "2025-11-01T12:34:56+09:00",
"receipt_no": "10001",
"type": "売上",
"items": [
{"item_code":"DRK001","item_name":"水","qty":2,"unit_price":150},
{"item_code":"SN001","item_name":"スナック","qty":1,"unit_price":500}
]
}
この「order_id」「room_no」「items」を最重要キーにして、CSVに落とすのが中核。
Scene4:ローカルPC側(Node/Express)の準備はしてるんですか?
コーギーマネージャー
「吐き出したJSONをCSVファイルにして指定したフォルダに送る必要がある。そこでローカルPCでサーバーを建てる必要があり、コーディングしたんだ。」
① JSONを受ける(POST /ingest or /order)
Expressで受信してバリデーション。(server.js)
※ここで唯一コーディングしています。コードは見せることができませんが、どういう仕様を意識したか書いておきます。
itemsが空なら弾く
qtyや単価が不正なら弾く
APIキーserver 税・サービス料の計算ロジックを分離
現場で一番揉めるのは「税のかけ方」「サ別/サ込」「税込/税別」。
計算は calc.js に隔離して、ルールをコードとして固定します。
ここを分離しておくと、「税率が変わった」「サ込み扱いが変わった」時に、修正点がcalc16列を“順番固定”で組み立てる
PMS側が列番号で見ているケースが多いので、ヘッダ名より順番の厳守が大事。
② 文字コード(Shift_JIS)を切り替え可能に
ここ、チャットでも出READMEMSがShift_JIS前提だと、UTF-8で吐いた時点で“見た目は読めても照合がズレる”ことが起きます。
なのでサーバは CSV_ENCODING=Shift_JIS を用意して、確実に合わせにいく。
③ アトミック書き込み(tmp→rename)
監視フォルダに“書きかけのCSV”が見えると、PMSが途中で取り込んで事故ります。だから必ず tmp で書いてから rename。
Scene5:重複防止は「現場を救う保険」
ホテリエちゃん
「現場って、通信失敗すると“連打”します……」
コーギーマネージャー
「それで二重計上が起きたら、精算事故になる。だから重複防止を入れる。」
order_id(またはdup_key)を 処理済みとして保存
同じキーなら “duplicate ignored” で終了
それでも必要な時は force=true で再生成可能
この状態は storage/state.json に残して、再起動しても維持。
Scene6:ngrokで“外からローカル”を成立させる
ホテリエちゃん
「Bubbleから直接ローカルPCのサーバーにポストできないですよね?」
コーギーマネージャー
「そうなんだ。だからngrokを使用する。ただ下記の点は注意だよ。」
ngrokはアカウント認証(authtoken)必須になりがち
→ “急に動かない”の典型。現場PC更新・別PC移行でハマる。Windowsの実行権限・実行場所問題
→ 管理者権限が必要になったり、PATHが複数あって別ngrokを叩いてたりする。IP制限と trust proxy
→ X-Forwarded-For を信頼する設定(TRUST_PROXY)と、許可IP(ALLOW_IPS)の整合。
ホテリエちゃん
「“現場PC運用まで設計が必要”って、まさにここですね……」
Scene7:返品や伝票取り消し
チャットでも出た「返品・取消をマイナスで送りたい」件。
ここは “PMSが許す表現” に寄せる必要があります。
PMSが qtyマイナスを許さないなら、
区分(売上/取消)を分ける
もしくは 別の伝票種別 で処理
あるいは 単価側で符号を持つ(これも弾かれることあり)
つまり「サーバの構造」だけじゃなく、満室御礼側の取込仕様に依存します。
(開発編の時点では“マイナスを通す”より、弾かれた事実をログに残して復旧可能にするのが先。)
Scene8:テスト手順は“現場目線”で決め打ちする
ホテリエちゃん
「最後に、どうテストしたんですか?」
コーギーマネージャー
「“作った”より“事故らない”が価値。だからテストはこれをやる。」
1商品 / 複数商品 / 数量2以上
税別/税込・サ別/サ込の組み合わせ
日本語商品名(全角)
通信失敗→再送(重複防止が効くか)
PMSで実際に取り込まれ、商品コード照合が通るか(ここが最重要)
Scene9:14万円の開発は高い?安い?(開発編の結論)
ホテリエちゃん
「ちなみに……この開発に14万円かかった話、どうしてもPMS側に参照するフォルダを設定してもらう作業は発生するので削れなかったんですよね。会社的にはどう評価すべきですか?」
コーギーマネージャー
「“安い/高い”は置いといて、評価軸はこの3つ。」
精算漏れ(売上取りこぼし)を構造で減らせるか
現場の20分/日を削れるか(年間にすると効く)
“攻めの注文”(アップセル/多言語モバイルオーダー)を増やしても崩れないか
この3つのうち、どれか1つでも数字で効いてくれば、投資として十分成立します。
次回予告:一番大切な運用はどうなのか?
次回は運用してみてどうなのかを書いていきます。
現在、運用しているので、どのような効果もあったのかも報告できればと思います。

