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

テーブル関係

マスター・台帳・記録を区別します。台帳は事実を積み、記録はその事実がいつ・なぜ変わったかを残します。

法人 › 物件 › 号室通貨・税制が法人から継承され、物件がプロファイルを持ち、号室が賃料・募集価格・原価・面積/タイプ・ステータスを持ちます。現在 物件7・号室98(運営中77・運営未開始21)。
契約 › 請求回次 › 入金契約51件(入居中44・退去6・入力中1)。有効契約44件の回次・月次発生を確定ルールエンジンがすべて計算し(ブロック0)、入金は会計台帳1,385行と突合します。
audit_logすべての書き込みを画面・SQLの区別なくトリガーで記録します。「変更履歴」画面がこのテーブルをそのまま見ています。
請求書スナップショット発行すると番号が付き、内容がsnapshotに固まります。DBトリガーが発行後の変更・削除を防ぎます。
号室マスター — 編集画面はまだありません賃料・面積・原価・エアコンはまだアプリでは直せず、シートで修正しています。契約の「修正」画面もまだありません(登録・退去はあります)。
収支項目 › 取引勘定科目をデータとして定義します。新しい項目が必要なら、開発ではなく項目定義に行を追加します。

Pricing

区分は総日数で分かれます

料金は号室の「列」ではなく「行」として立ちます。区分を列にすると、区分がひとつ増えるたびに列を追加することになり、物件ごとに異なる数を持てません。日本の区分は短期・長期の2つで、判定の基準はヶ月数ではなく総日数です。

マスター物件 7マスターリース2物件42室(東新宿32 · 南大井10)· 受託運営5物件56室
→
1 : N号室 98賃料 · 募集価格 · 原価 · 面積/タイプ · ステータス(運営中77 · 運営未開始21)
→
判定契約 51総日数で短期・長期を自動判定(JC-03)· 中途解約の表示
→
算出請求 · 請求書確定ルールで回次・月次発生を計算 → 発行時にスナップショット
区分期間敷金支払
短期総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)は公開面のため実データを持ちません。

assetアセット物件7 · 号室98 · 運営契約(マスターリース/受託運営)。画面=号室状況
leasing契約契約51件 · 長期短期の判定 · 中途解約。画面=契約(登録・退去)。修正画面はまだありません
billing精算請求回次 · 月次発生 · 入金 · 敷金 · 請求書。画面=請求/入金・未収/請求書
security監査アクセス · audit_log。画面=変更履歴
party主体入居者 · 送付先 — 契約者と受け取る方が異なるケースが実際にあります
platform共通共通型 · 通貨コード · 物件別の入金口座(日本橋 · 九条はそれぞれ別の信託口座)
まだ画面がないもの号室・建物マスターの編集 · 契約の修正 · 調整金の処理(JX-03)はまだありません。
このシステムにないもの問い合わせ・内見管理、契約ステップボード、不動産の公開ページ、経営ダッシュボードはこの台帳の範囲ではありません。

Policies

台帳を台帳たらしめる6つ

スキーマだけでは台帳になりません。以下が実装に入れた設計原則です。

台帳はappend-only修正はsuperseded_byで上書きし、物理削除はしません。退去した契約も一覧から消えません(現在 退去6件)。
すべての書き込みが残ります画面から直しても、SQLで直してもaudit_logにトリガーで記録されます。シートには変更履歴がまったくありませんでした。
発行した請求書は不変発行すると番号が付き、内容がsnapshotに固まります。DBトリガーがその後の変更・削除を防ぎます。
日付はUTC 0時基準基準となる時刻をひとつに統一しました。シートの計算がTODAY()に依存し、開くたびに値が変わっていた問題の反対側です。
稼働率の定義を固定「運営未開始」21室を分母から除外し、運営中の77室だけで計算します — 現在57.1%。98室すべてを分母にしません。
シークレットはリポジトリに置きません認証情報はコード・リポジトリに入れず、環境変数で注入します。

通貨は法人から継承されます。 物件と取引がそれぞれ決めると必ず食い違います。金額は通貨コードを伴って持ち回り、通貨が違えば合計を合算しません — 換算してひとつの行にすると、その為替レートがそのまま根拠のない数字になります。

Verification

シート台帳をそのまま再現できたか

Googleスプレッドシート1つ(18タブ · 77,000セル · 49,400数式)で運用していたものを、この台帳へ移しました。シートはまだ正本で、切り替えの時期は協議中です。金額は画面で計算せず、確定ルールを組み込んだエンジンが算出します — シートの値を書き写しません。

ルールJC-01 〜 JC-07暦月 · 100円切り上げ · 短期/長期 · 再判定 · 敷金 · 請求のタイミング · 更新料
→
エンジン請求エンジン有効契約44件をすべて計算 · ブロック0
→
台帳回次 · 月次発生入金は会計台帳1,385行と突合
→
発行請求書スナップショット番号が付き内容が固まる · DBトリガーが変更・削除を遮断

請求エンジンの回帰テスト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)は今回の範囲から除外しました — まだありません。

行を追加します新しい項目が必要なら項目定義に行を追加します。画面・コードには手を入れません。
帰属の軸が境界を決めます契約・号室・物件に紐づくならこのシステム、法人全体(給与・社会保険)は会計システムの管轄です。人が毎回判断しません。
出所を区別します請求は確定ルールエンジンが契約から自動算出し、一時的なものは手入力します。出所がないと「この金額はなぜあるのか」を後から追えません。
コードは固定、表示名だけ多言語清掃費 / クリーニング / Cleaning が3つの項目になると集計が割れます。

区分は請求 · 入金 · 支出であり、請求 − 入金 = 未収金です。 請求回次はすでにテーブルとして立っており、確定ルールが有効契約44件すべてを計算します(ブロック0)。シートでは入金が入居者の氏名の文字列で結合され、¥58,777,660のうち14.2%しか一致していませんでした。そのため出ていた「差額」¥72,758,400は未収金ではなく虚数でした。オンライン決済はまだありません。