Data Architecture
韓国と日本をひとつのシステムで運営できるように設計しました。いま実際に動いているのは日本の7物件98室で、韓国トラックは運用ポリシー23項目のうち多くが未確定のため、まだ開いていません。物件プロファイルがルールを持ち、請求額は確定ルールを組み込んだエンジンが算出します。
Principle
国ごとの違いは条件分岐ではなく物件プロファイルに持たせます。物件ごとに下記を指定すれば、ルールがついてきます。いま値が入って動いているのは日本側で、韓国トラックは運用ポリシー23項目のうち多くが未確定のため、まだ値を入れていません。
| プロファイル | 韓国 | 日本 |
|---|---|---|
| 請求サイクル | 未確定 — ポリシー協議中 | 暦月 — 毎月1日〜末日が1ヶ月。最初と最後の月だけ、その月の実日数を分母に日割り(JC-01) |
| 日割りの分母 | 未確定 — ポリシー協議中 | 最初の月と最後の月だけその月の実日数。中間の月は丸ごと1ヶ月(JC-01) |
| 金額の丸め | 未確定 — ポリシー協議中 | 100円単位で切り上げ(JC-02) |
| 長期/短期の判定 | 未確定 — ポリシー協議中 | 総日数31〜180日=短期/181日以上=長期。30日以下は取り扱いません(JC-03) |
| 延長の処理 | 未確定 — ポリシー協議中 | すべての延長は新規契約として判定し直します(JC-04) |
| 敷金 | 未確定 — ポリシー協議中 | 長期=賃料1ヶ月分/短期=なし(JC-05) |
| 請求のタイミング | 未確定 — ポリシー協議中 | 入居月=当月日割+翌月分を同時に請求。以降は毎月最初の営業日に翌月分(JC-06) |
| 更新料 | 未確定 — ポリシー協議中 | 直前の契約と新規契約がどちらも長期のときだけ、新規賃料の10%(JC-07) |
| 管理手数料 | 未確定 — ポリシー協議中 | 受託運営=月額賃料 × 5% × 1.1/マスターリース=月間収入 − 原価(固定費) |
| 原状回復 | 未確定 — ポリシー協議中 | 清掃 2,000円/㎡ + エアコン 25,000円/台 |
| 中途解約 | 未確定 — ポリシー協議中 | 契約画面で中途解約として表示します。違約金の計算ルールはまだ確定していません |
ルールは文書で確定してからエンジンに入れました。 上の日本のルールはJC-01〜JC-07と番号を付けて固定し、請求エンジンの回帰テスト47件がこのルールをロックしています。韓国の値はまだ空です — 埋める前に開けば、二国のうち必ずどちらかが間違います。
Tables
マスター・台帳・記録を区別します。台帳は事実を積み、記録はその事実がいつ・なぜ変わったかを残します。
Pricing
料金は号室の「列」ではなく「行」として立ちます。区分を列にすると、区分がひとつ増えるたびに列を追加することになり、物件ごとに異なる数を持てません。日本の区分は短期・長期の2つで、判定の基準はヶ月数ではなく総日数です。
| 区分 | 期間 | 敷金 | 支払 |
|---|---|---|---|
| 短期 | 総31 〜 180日 | なし(JC-05) | 入居月=当月日割+翌月分を同時。以降は毎月最初の営業日に翌月分(JC-06) |
| 長期 | 総181日以上 | 賃料1ヶ月分(JC-05) | 入居月=当月日割+翌月分を同時。以降は毎月最初の営業日に翌月分(JC-06) |
日本基準です。30日以下は取り扱いません(JC-03)。すべての延長は新規契約として判定し直すため(JC-04)、区分もそのときに決め直されます。金額は100円単位で切り上げます(JC-02)。更新料は直前の契約と新規契約がどちらも長期のときだけ、新規賃料の10%です(JC-07)。
号室テーブルに料金の列はつくりません。 つくると物件が増えるたびにマイグレーションが必要になります。ただし賃料・面積・原価・エアコンのマスターは、まだアプリで編集できません — 現在はシートで修正しており、マスター編集画面はまだありません。
Ledger
PostgreSQLのマイグレーション20件で立っています。すべての書き込みはaudit_logトリガーを通り、同じ台帳に残ります。この台帳は運営コンソール(freymadmin.llnpco.dev、サイト全体が認証)にだけ接続されており、いまご覧いただいている紹介サイト(freym.llnpco.dev)は公開面のため実データを持ちません。
Policies
スキーマだけでは台帳になりません。以下が実装に入れた設計原則です。
superseded_byで上書きし、物理削除はしません。退去した契約も一覧から消えません(現在 退去6件)。audit_logにトリガーで記録されます。シートには変更履歴がまったくありませんでした。TODAY()に依存し、開くたびに値が変わっていた問題の反対側です。通貨は法人から継承されます。 物件と取引がそれぞれ決めると必ず食い違います。金額は通貨コードを伴って持ち回り、通貨が違えば合計を合算しません — 換算してひとつの行にすると、その為替レートがそのまま根拠のない数字になります。
Verification
Googleスプレッドシート1つ(18タブ · 77,000セル · 49,400数式)で運用していたものを、この台帳へ移しました。シートはまだ正本で、切り替えの時期は協議中です。金額は画面で計算せず、確定ルールを組み込んだエンジンが算出します — シートの値を書き写しません。
請求エンジンの回帰テスト47件。シート突合 — 月次発生533ヶ月分のうち完全一致494件(93%)· 端数差36件 · 実際の差異3件。請求イベント337件のうち完全一致303件(90%)。突合に使ったシート複製の台帳行31,920。
だから台帳をこう立てました。 シートで実測により確認した問題は4つでした — ①請求した事実が記録されず、計算がTODAY()に依存して開くたびに値が変わる ②入金を入居者の「氏名の文字列」で結合していたため、実際の¥58,777,660のうち14.2%しか一致しない ③「号室情報」を参照する数式がワークブック全体で0件(料金マスター未接続)④変更履歴がない。append-only台帳 · audit_logトリガー · 請求書スナップショット · 確定ルールエンジンが、それぞれこの4つに対応します。
Accounting
原状回復費(清掃 2,000円/㎡ + エアコン 25,000円/台)· 更新料 · 管理手数料 · 公共料金… 項目は増え続けます。そのたびに開発しなくて済むよう、項目をデータとして定義します。調整金の処理(JX-03)は今回の範囲から除外しました — まだありません。
区分は請求 · 入金 · 支出であり、請求 − 入金 = 未収金です。 請求回次はすでにテーブルとして立っており、確定ルールが有効契約44件すべてを計算します(ブロック0)。シートでは入金が入居者の氏名の文字列で結合され、¥58,777,660のうち14.2%しか一致していませんでした。そのため出ていた「差額」¥72,758,400は未収金ではなく虚数でした。オンライン決済はまだありません。