• #日記
  • #tax-assistant
  • #miller-columns
  • #vue
  • #sqlite
  • #csv-import
  • #gmo
daily-log

2026年1月25日の開発日記

今日は計画ページの大幅リデザイン、GMO CSV仕訳インポートの設計、日付形式統一マイグレーションの完了など、UI改善とデータ整備の両面で進捗があった。

今日やったこと

1. 月次推移表リンク機能とinject問題の解決

月次推移表のファイル列から読取一覧ページへのリンク機能を実装した。

実装内容:

  • ファイル列のセルをクリックすると読取一覧ページへ遷移
  • 該当月・該当勘定科目でフィルタされた状態で表示
  • URLクエリパラメータで状態を引き継ぎ

発生した問題: Vue inject警告

[Vue warn]: injection "Symbol(router)" not found.

ルーターのinjectがコンポーネント内で見つからないエラーが発生。

原因と解決:

// NG: setup外でuseRouterを呼び出し
const router = useRouter()  // これがトップレベルだと問題

// OK: setup内またはonMounted内で呼び出し
const navigateToReceipts = () => {
  const router = useRouter()
  router.push({
    path: '/receipts',
    query: { year, month, accountCode }
  })
}

Composables APIのuseRouterはsetupコンテキスト内で呼び出す必要がある。グローバルスコープで呼び出すとinjectが解決できずエラーになる。


2. 計画ページの大幅リデザイン - Miller Columns風レイアウト

従来のサイドパネル形式から、Miller Columns風の3カラムレイアウトに刷新した。

Before: サイドパネル形式

+------------------+--------+
|   計画リスト     | 詳細   |
|   (全ステータス) | パネル |
+------------------+--------+

After: Miller Columns風

+------------+------------+------------+
| ステータス | 計画リスト | 仕様書     |
| 選択       | (フィルタ) | プレビュー |
+------------+------------+------------+

ステータスの4分離:

ステータス説明表示位置
pending未着手・検討中左カラム上部
in_progress実装中左カラム中央
completed完了左カラム下部
archivedアーカイブ済み左カラム最下部(折りたたみ)

実装のポイント:

<template>
  <div class="plans-page miller-columns">
    <!-- 左カラム: ステータス選択 -->
    <div class="status-column">
      <StatusCard
        v-for="status in statuses"
        :key="status"
        :status="status"
        :count="getCountByStatus(status)"
        :selected="selectedStatus === status"
        @click="selectStatus(status)"
      />
    </div>

    <!-- 中央カラム: 計画リスト -->
    <div class="plans-column">
      <PlanCard
        v-for="plan in filteredPlans"
        :key="plan.path"
        :plan="plan"
        :selected="selectedPlan?.path === plan.path"
        @click="selectPlan(plan)"
      />
    </div>

    <!-- 右カラム: 仕様書プレビュー -->
    <div class="spec-column">
      <SpecPreview
        v-if="selectedPlan"
        :plan="selectedPlan"
        :spec="specContent"
      />
    </div>
  </div>
</template>

<script setup lang="ts">
const selectedStatus = ref<PlanStatus>('in_progress')
const selectedPlan = ref<Plan | null>(null)

const filteredPlans = computed(() =>
  plans.value.filter(p => p.status === selectedStatus.value)
)
</script>

スタイリング:

.miller-columns {
  display: grid;
  grid-template-columns: 200px 1fr 1fr;
  gap: 16px;
  height: calc(100vh - 64px);
}

.status-column {
  border-right: 1px solid var(--border-color);
  overflow-y: auto;
}

.plans-column {
  overflow-y: auto;
}

.spec-column {
  overflow-y: auto;
  background: var(--bg-secondary);
}

3. 仕様書表示機能(requirements.mdの表示)

計画ページの右カラムに仕様書(requirements.md)を表示する機能を追加した。

目次展開機能:

Markdownの見出しから自動で目次を生成し、クリックで該当箇所にスクロール。

const extractHeadings = (markdown: string): Heading[] => {
  const lines = markdown.split('\n')
  return lines
    .filter(line => line.startsWith('#'))
    .map(line => {
      const match = line.match(/^(#{1,6})\s+(.+)$/)
      if (!match) return null
      return {
        level: match[1].length,
        text: match[2],
        id: slugify(match[2])
      }
    })
    .filter(Boolean) as Heading[]
}

画像パス変換:

相対パスで記述された画像を正しく表示するため、パス変換を実装。

const convertImagePaths = (markdown: string, basePath: string): string => {
  // ![alt](./image.png) → ![alt](/plans/project-name/image.png)
  return markdown.replace(
    /!\[([^\]]*)\]\(\.\/([^)]+)\)/g,
    (_, alt, path) => `![${alt}](${basePath}/${path})`
  )
}

4. GMO CSV仕訳インポート計画

GMO決済の取引履歴CSVを仕訳に変換するスラッシュコマンドの設計を行った。

新規スラッシュコマンド: /import-gmo-csv

# コマンド仕様
名前: /import-gmo-csv
入力: GMO取引履歴CSVファイル
出力: 仕訳データ(SQLiteに保存)

CSVフォーマット(GMO取引履歴):

カラム内容
取引日決済日2026/01/15
取引IDGMOの取引識別子GMO-123456
取引種別売上/返金/チャージバック売上
決済金額顧客への請求額10,000
手数料GMO手数料350
入金額実際の入金額9,650
摘要取引の説明ECサイト購入

仕訳パターン:

【売上時】
借方: 売掛金(GMO) 10,000円
貸方: 売上高 10,000円

【入金時(手数料相殺)】
借方: 普通預金 9,650円
借方: 支払手数料 350円
貸方: 売掛金(GMO) 10,000円

Codexレビュー結果:

設計書をCodex CLI(GPT-5.2)でレビューした結果、以下の指摘を受けて修正。

指摘対応
返金時の仕訳パターンが未定義返金パターンを追加
月跨ぎ決済の扱いが曖昧決済日ベースで計上と明記
重複インポート防止策がない取引IDでのユニーク制約を追加

5. 日付形式統一マイグレーション完了

receiptsテーブルの日付形式をYYYY/MM/DDからYYYY-MM-DDに統一するマイグレーションを実行した。

マイグレーション内容:

-- 対象件数の確認
SELECT COUNT(*) FROM receipts WHERE date LIKE '%/%';
-- 結果: 70件

-- 日付形式の変換
UPDATE receipts
SET date = REPLACE(date, '/', '-')
WHERE date LIKE '%/%';

-- 変換結果の確認
SELECT date FROM receipts LIMIT 5;
-- 2026-01-15
-- 2026-01-14
-- 2026-01-13
-- ...

変換件数: 70件

全てのレコードがYYYY-MM-DD形式に統一された。

Pythonコード側の対応:

# 日付パースの統一関数
def parse_date(date_str: str) -> date:
    """複数フォーマットに対応した日付パース"""
    for fmt in ['%Y-%m-%d', '%Y/%m/%d']:
        try:
            return datetime.strptime(date_str, fmt).date()
        except ValueError:
            continue
    raise ValueError(f"Invalid date format: {date_str}")

# 日付フォーマットの統一関数
def format_date(d: date) -> str:
    """日付をYYYY-MM-DD形式に変換"""
    return d.strftime('%Y-%m-%d')

6. 仕訳設計文書作成 - 総額パターン/純額パターン

GMO CSV仕訳インポートに関連して、仕訳の総額パターンと純額パターンを整理する設計文書を作成した。

総額パターンと純額パターンの違い:

パターン借方貸方特徴
総額普通預金 + 手数料売掛金取引の全体像が見える
純額普通預金売上(純額)シンプルだが手数料が埋もれる

ベン図による関係整理:

┌─────────────────────────────────┐
│          決済金額               │
│  ┌────────────┬──────────────┐  │
│  │   入金額   │   手数料     │  │
│  │  (9,650)   │   (350)      │  │
│  └────────────┴──────────────┘  │
│         (10,000)                │
└─────────────────────────────────┘

採用方針:

  • 総額パターンを採用: 手数料の可視化が重要
  • 入金時に手数料を計上: 決済時ではなく入金時に相殺

7. 重複チェック機能強化計画

重複チェック画面に3択ラジオボタンを追加する計画を作成した。

現状の問題:

  • 重複と判定されたレシートをどう処理するかの選択肢がない
  • 「無効化」「帳票扱い」「保留」のいずれかを選びたい

3択ラジオボタン設計:

選択肢動作ステータス変更
無効化このレシートを無効として扱うstatus = 'disabled'
帳票扱い重複ではなく別の帳票として扱うstatus = 'active', duplicate_check = 'document'
保留判断を保留するstatus = 'pending_review'

フロー設計:

重複グループ表示
    ↓
グループ内のレシートを1つずつ確認
    ↓
各レシートに対して3択を選択
    ↓
「確定」ボタンで一括保存
    ↓
次の重複グループへ

UIモック:

<template>
  <div class="duplicate-action">
    <RadioGroup v-model="selectedAction">
      <RadioButton value="disable">
        <XCircle class="icon" />
        無効化
      </RadioButton>
      <RadioButton value="document">
        <FileText class="icon" />
        帳票扱い
      </RadioButton>
      <RadioButton value="pending">
        <Clock class="icon" />
        保留
      </RadioButton>
    </RadioGroup>
  </div>
</template>

8. デザインシステム更新 - 選択UIパターン

重複チェック機能の設計に合わせて、デザインシステムに選択UIパターンを追加した。

追加したパターン:

  1. ラジオボタングループ: 排他選択用
  2. チェックボックスグループ: 複数選択用
  3. トグルボタン: オン/オフ切替用

ラジオボタングループのスタイル:

.radio-group {
  display: flex;
  gap: 8px;
}

.radio-button {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 8px 16px;
  border: 1px solid var(--border-color);
  border-radius: 8px;
  cursor: pointer;
  transition: all 0.2s;
}

.radio-button:hover {
  border-color: var(--primary-color);
}

.radio-button.selected {
  background: var(--primary-bg);
  border-color: var(--primary-color);
  color: var(--primary-color);
}

.radio-button .icon {
  width: 16px;
  height: 16px;
}

今日の学び

  • Miller Columnsの有効性: macOSのFinderで使われているMiller Columns形式は、階層構造のナビゲーションに適している。ステータス→計画→仕様書という3階層を自然に表現できる
  • inject問題の根本原因: VueのComposables APIはsetupコンテキスト内でのみ動作する。グローバルスコープでuseRouter等を呼び出すと「injection not found」エラーになる
  • 総額vs純額の判断基準: 手数料を明示的に追跡したい場合は総額パターン、シンプルさを優先する場合は純額パターン。会計上はどちらも正しいが、管理目的で選択する

明日やること

  • GMO CSV仕訳インポートの実装開始
  • 重複チェック3択ラジオボタンの実装
  • 計画ページの仕様書プレビューにMarkdown描画を追加

関連計画ファイル

ファイルステータス内容
gmo-csv-import-plan.mdplannedGMO CSV仕訳インポート
duplicate-action-plan.mdplanned重複チェック3択ラジオボタン
plans-page-redesign.mdcompleted計画ページMiller Columns化
date-format-unification.mdcompleted日付形式統一