概要

項目内容
案件IDMAS-021
カテゴリUX / レポーティング
PhaseP2
優先度★★
所要時間6-8時間
対象ファイル000_infra/002_constants.jsMENU_DEFINITION 追記) / 100_config/101_sys_config.js(サイドバー開閉関数追加) / 新規 HTML(templates/financial_statement_export.html設計変更後: 新規 GAS ロジック(600_report/609_financial_statement_export.js)+ 新規 Cockpit コンポーネント(webapp_client/src/cockpit/ExportPanel.tsx)+ 新規テンプレートシート(スプレッドシート上)
前提案件MAS-020(YoY実装・取締役会用フォーマット参照), MAS-001(予実差異分析・取締役会用フォーマット参照), MAS-094(HtmlService サイドバー UI パターン参考)MAS-358(Cockpit 左ナビ・画面固有サイドバー)

⚠️ 設計変更 2026-05-11: GAS 操作パネル廃止方針により、当初の実装アプローチ(templates/financial_statement_export.html による HtmlService サイドバー)は破棄。GAS エクスポートロジック(609_financial_statement_export.js)は変更なし。UI 起動部分のみ変更:

  • : GAS サイドバー(openFinancialStatementExport()showSidebar()
  • : 月次財務諸表 Cockpit 画面の固有サイドバーにエクスポートボタンを追加 → google.script.run.exportFinancialStatement(params) で GAS を直呼び
  • ExportPanel.tsx を新設し、フォーム入力(対象帳票・期間・フォーマット・ファイル種別)と結果リンク表示を担当
  • templates/financial_statement_export.html作成しない
  • MENU_DEFINITION のサイドバー分類追加は不要(GAS メニュー廃止のため)

目的

経営者・銀行・取締役会等への社外共有に対応するため、P/LB/S・キャッシュフロー計算書を PDF または Excel でワンクリック出力できる機能を新規実装する。「銀行提出用(シンプル)」と「取締役会用(分析付き)」の 2 種のフォーマットを切り替え可能とし、マートシート(61_pl_monthly / 71_bs / 81_cf_indirect 等)の数値をテンプレートに流し込んで誤差なく再現する。

現在のコード

該当なし。本案件で新規追加する。

既存の財務諸表マートシートとアクセスパターン(Phase 1 調査結果)

帳票種別月次/単発シート年度累計シートアクセスパターン
P/L 損益計算書61_pl_monthly62_pl_ytdUtils.getSheetByKey('PL_M_ACT', '61_pl_monthly') / Utils.getSheetByKey('PL_Y_ACT', '62_pl_ytd')600_report/602_datamart_main.js L185-186)
B/S 貸借対照表71_bs(スナップショット 72_bs_snapUtils.getSheetByKey('BS_ACT', '71_bs')(同 L191)
C/F 間接法81_cf_indirect81b_cf_indirect_ytdUtils.getSheetByKey('CF_IND_ACT', '81_cf_indirect') / Utils.getSheetByKey('CF_IND_ACT_YTD', '81b_cf_indirect_ytd')(同 L195-196)
決算書フォーマット(年間合計・報告式)91_fs_bs / 92_fs_plss.getSheetByName('91_fs_bs') / ss.getSheetByName('92_fs_pl')600_report/602_datamart_main.js L354-359。01_sys_config にシステムキー登録なし)

本案件では月次/年度累計の数値を別途「エクスポート用テンプレートシート」に流し込んで出力する方式をとる(既存 91/92 は年度合計の 2 列固定レイアウトで、月次・YoY・予実の比較出力には不向きなため)。

既存メニュー構造の把握(Phase 1 調査結果)

100_config/101_sys_config.jsonOpen()Constants.MENU_DEFINITION000_infra/002_constants.js L206-324)を動的にループしてメニューを生成する。直接 ui.createMenu('xxx').addItem(...) でハードコードされているメニューは存在しない(MAS-217 仕様でサイドバー templates/operations_sidebar.html に一元化済み)。したがってエクスポート機能の導線は Constants.MENU_DEFINITION へのカテゴリ追加、またはサイドバーからの起動関数追加として実装する。

修正方針

アーキテクチャ: テンプレートシート複製方式

  1. エクスポート用テンプレートシートをスプレッドシート上にあらかじめ用意する(シート名例: _TMPL_pl_bank / _TMPL_pl_board / _TMPL_bs_bank / _TMPL_bs_board / _TMPL_cf_bank / _TMPL_cf_board の 6 枚。アンダースコア接頭辞で DDL 対象外であることを明示)。
  2. ユーザー起動時、バックエンドの exportFinancialStatement(params) が対応するテンプレートシートを templateSheet.copyTo(ss) で一時複製する。
  3. try { データ取得 → setValues() でテンプレートにデータ配置 → PDF/Excel Blob 生成 → DriveApp.createFile() で保存 → getDownloadUrl() をサイドバーに返却 } finally { ss.deleteSheet(tempSheet) } の構造で一時シートを確実に削除する。
  4. PDF 化は SpreadsheetApp.openById(ss.getId()).getAs(MimeType.PDF) ではなく、Sheets export URL(https://docs.google.com/spreadsheets/d/{ID}/export?format=pdf&gid={GID}&...)に UrlFetchApp.fetch() + OAuth token を付与して取得する。これにより対象シートの限定・余白・用紙サイズ・フィット設定をクエリパラメータで制御できる。
  5. Excel 化は同 export URL の format=xlsx&gid={GID} を利用する。

メニュー導線

  • 000_infra/002_constants.jsMENU_DEFINITION のサイドバー分類配列に、新規カテゴリ 📋 サイドバー: 📤 エクスポート を追加し、{ label: '📤 財務諸表のPDF/Excelエクスポート', funcName: 'openFinancialStatementExport', description: 'P/L・B/S・CF を PDF または Excel で出力' } を登録する。
  • メニュー名・カテゴリ名の文字列は Phase 1 で実際に Read した MENU_DEFINITION の表記ゆれ(絵文字前置・コロン有無)に揃える。既存の「📋 サイドバー: 📊 マート更新」「📋 サイドバー: 📝 費用登録」「📋 サイドバー: ⚙️ メンテナンス」のスタイルに合わせる。
  • サイドバー起動関数 openFinancialStatementExport()100_config/101_sys_config.js に追加し、HtmlService.createHtmlOutputFromFile('templates/financial_statement_export').setTitle('📤 財務諸表エクスポート').setWidth(360)SpreadsheetApp.getUi().showSidebar(...) で表示する(既存 openOperationsSidebar() 〜L356-364 のパターンに倣う)。

データ取得方針

  • 財務諸表マートシート(61_pl_monthly / 62_pl_ytd / 71_bs / 81_cf_indirect / 81b_cf_indirect_ytd)は Utils.getSheetByKey(sysKey, fallbackName) 経由で直接読み取る。01_sys_config にシステムキーが登録されている前提で、フォールバック引数にシート名を渡す形でfallback も Phase 1 で確認した実在のシート名のみ使用する。
  • 91_fs_bs / 92_fs_plss.getSheetByName('91_fs_bs') / ss.getSheetByName('92_fs_pl') で直接参照(年度合計決算書フォーマットを銀行提出用で流用する場合)。
  • OrderRepository / InvoiceRepository / BankTxRepository / JournalRepository は使用しない。これらは 200_data/202_repository.js で定義された ERP 元帳(31/32/33/42 タブ)専用クラスであり、財務諸表マートシートに対応する Repository は存在しない(CLAUDE.md 名前空間表参照)。
  • 失敗パターン#21 対策: 月次列は C〜N の 12 列固定で取得。sheet.getLastColumn() による動的取得を禁止(MAS-020 で追加された YoY 差異列 O-Z を誤って取り込むリスク)。
  • 失敗パターン#22 対策: マートシートのラベル列(A 列)のマッチングは .replace(/[\s ]+/g, '') で全角スペース U+3000 を含む空白を正規化してから比較する。
  • 失敗パターン#23 対策: テンプレート内に YYYY-MM 形式文字列(例: 基準年月「2026-03」)を書き込む場合は range.setValue("'2026-03") の apostrophe 前置、または range.setNumberFormat('@') 後に setValue() する。
  • 失敗パターン#24 対策: テンプレートへのデータ配置は GAS 側で行番号を事前特定し range.setValues([[...]]) で書き込む。テンプレート上の MATCH / ARRAYFORMULA / SUBSTITUTE 数式に依存するラベル解決は禁止。必要ならテンプレート側は静的セル位置(例: B5:B18 の固定範囲に科目名、C5:N18 に月次金額)に設計する。

UI 設計(サイドバー)

サイドバー HTML(templates/financial_statement_export.html)に以下のフォーム要素を配置する。

要素選択肢備考
対象諸表(プルダウン)損益計算書 (P/L) / 貸借対照表 (B/S) / キャッシュフロー計算書 (C/F)value は pl / bs / cf
対象期間(プルダウン)月次 / 年度累計 (YTD)value は monthly / ytd
基準年月(テキスト入力 or プルダウン)YYYY-MM 形式MAS-094(dev_mas-094_boundary_month_selector.md)の今期 12 ヶ月選択肢生成ロジックを流用。未入力時は当期最新月を使用
フォーマット(プルダウン)銀行提出用(シンプル) / 取締役会用(分析付き)value は bank / board
ファイル形式(ラジオボタン)PDF / Excelvalue は pdf / xlsx
エクスポートボタンクリック時 google.script.run.withSuccessHandler(onSuccess).withFailureHandler(onFailure).exportFinancialStatement(params) を呼ぶ。呼び出し直後 button.disabled = true で二重送信防止
ダウンロードリンク表示領域onSuccess(result)result.url をアンカータグで表示、onFailure(err) でエラーメッセージ表示。いずれの場合も button.disabled = false に戻す

フォーマット別仕様

フォーマット内容データ参照先
銀行提出用(シンプル)勘定科目名・金額のみ。グラフ・色分け・分析指標なし。金額セルに Constants.NUMBER_FORMATS.CURRENCY'#,##0;[Red]△ #,##0;"-"')を適用61_pl_monthly / 71_bs / 81_cf_indirect(または 91_fs_bs / 92_fs_pl の年度合計)
取締役会用(分析付き)主要 KPI(売上総利益率・営業利益率・BEP 充足率等)+ YoY 比較+予実差異を追加掲載同上+ MAS-020 で 61_pl_monthly / 62_pl_ytd に追加された YoY 差異列(O 列以降)、MAS-001 で 65_pl_variance に出力される予実差異列

影響範囲

変更対象変更内容変更量
000_infra/002_constants.jsMENU_DEFINITION にサイドバー分類 📋 サイドバー: 📤 エクスポート を 1 件追加(配列末尾に push)~8 行
100_config/101_sys_config.jsopenFinancialStatementExport() 関数を新設(openOperationsSidebar L356-364 と同じパターン)~10 行
新規 600_report/609_financial_statement_export.jsexportFinancialStatement(params) 本体・テンプレート複製/データ配置/PDF or Excel 化/Drive 保存/一時シート削除~300 行
新規 templates/financial_statement_export.htmlサイドバー UI(フォーム+google.script.run 呼び出し+ダウンロードリンク表示)~150 行
新規作成が必要: エクスポート用テンプレートシート_TMPL_pl_bank / _TMPL_pl_board / _TMPL_bs_bank / _TMPL_bs_board / _TMPL_cf_bank / _TMPL_cf_board の 6 枚(初期スコープにより一部先行)人間が用意 or 雛形生成関数
Drive フォルダエクスポートファイル保存先のフォルダ ID人間が検討すべき事項に記載
  • 01_sys_config にテンプレートシート名・保存先 Drive フォルダ ID のシステムキーを登録する場合は別途 DDL 更新が必要(人間が検討すべき事項)。

注意事項

  1. テンプレートシートの作成・管理方針は実装着手前に確定すること(人間が検討すべき事項を参照)。6 枚のテンプレートを人間が手動で用意するか、雛形生成関数で初期化するか方針が決まらないと実装が始められない。
  2. 一時シートの削除は try-finally で必ず実行する。エクスポート処理中のエラーでも後処理が走るよう保証する。
  3. GAS 実行時間制限(6 分): 複数諸表の一括エクスポートや大量データは制限に抵触する可能性あり。初期実装は 1 シート 1 エクスポートに限定し、複数諸表のバッチ化は将来的な課題として記載する。
  4. PDF 出力精度(余白・用紙サイズ・改ページ)は export URL のクエリパラメータに依存する。fzr(固定行)・portraitsize=A4fitw=truegridlines=falseprinttitle=false 等の実機確認が必須。
  5. HtmlService サイドバーから google.script.run を呼ぶ際は .withSuccessHandler().withFailureHandler() を必ず設定する。未設定時のエラーが UI に伝わらず二重送信ロック解除不能になる。
  6. メニュー文字列は Phase 1 で Read した Constants.MENU_DEFINITION の実在表記を使う。記憶・推測で書かない(失敗パターン#20)。
  7. 有効フラグ=FALSE の行は全処理でスキップ(CLAUDE.md コーディング規約)。マートシートからの読み取り時にも A 列のチェックボックスを確認する。
  8. 監査ログ記録: Utils.auditLog('RUN', '', '', '', 'exportFinancialStatement', '', JSON.stringify(params), 'file=' + url) でエクスポート操作を監査ログ(98_audit_log)に必ず記録する。
  9. Drive フォルダ ID の取得方式: Env.receiptFolderId() のような Env モジュール経由取得を踏襲する場合は Env モジュールに exportFolderId() を追加、03_sys_params 経由なら Utils.getParam('EXPORT_FOLDER_ID', null) で取得する。どちらを選ぶかは人間が検討すべき事項。

エッジケース

条件挙動理由
指定期間のデータがマートシートに存在しない処理中断。Utils.toastResult('exportFinancialStatement', '対象期間のデータが存在しません。マートを更新してください。', 7) を呼び出し、withFailureHandler 経由でサイドバーにもエラーを返すデータなしでテンプレート書き込みを実行するとゼロ埋め出力になり誤った財務諸表が生成される
テンプレートシート(例: _TMPL_pl_bank)が見つからない(ss.getSheetByName(templateName) が null)処理中断。Utils.logError('exportFinancialStatement', new Error('テンプレートシートが見つかりません: ' + templateName)) + Utils.toastResult() でユーザー通知。サイドバーにもエラーメッセージ返却設定漏れ・シート名の変更が原因。エラーメッセージにシート名を明示する
一時シート複製後にエクスポート処理でエラー発生try-finally ブロックで ss.deleteSheet(tempSheet) を確実に実行して一時シートを除去一時シートがスプレッドシートに残留すると次回以降の処理に干渉する。残留時は手動削除が必要になる
Drive の保存先フォルダが存在しない(DriveApp.getFolderById() が例外をスロー)処理中断。Utils.logError() + Utils.toastResult() でフォルダ ID の設定を促すメッセージを表示フォルダ ID が未設定、または削除された場合のガード
取締役会用フォーマットで KPI 算出時に分母がゼロ(売上ゼロ月の売上総利益率など)"-" を表示。ゼロ除算を起こさないガードを実装(denominator === 0 ? '-' : numerator / denominatorゼロ除算すると Infinity または NaN が出力され、Sheets 上の書式が △ Infinity のように崩れる
年度累計(YTD)で未更新月が存在する(マート未更新)存在する月のみ集計し、欠損月はゼロとして扱う。出力末尾に「※ YYYY-MM 以降のデータは未反映」の注記行を追加マートが最新月まで更新されていない状態での実行を考慮
ユーザーがエクスポートボタンを連打する初回クリックで button.disabled = true に設定し二重送信を防止。onSuccess / onFailure ハンドラで button.disabled = false に戻すGAS 呼び出し中の二重実行を防ぐ。二重実行が走ると同じファイル名で Drive に 2 つ保存される
基準年月に不正な文字列(例: abc / 2026-13)が入力されたサイドバー側で正規表現 `/^\d{4}-(0[1-9]1[0-2])$/チェック → 非適合時はエラーメッセージを表示しgoogle.script.runを呼ばない。サーバ側でもUtils.parseDateToYm(input)` が空文字を返したら処理中断
Drive に同名ファイルが既存(連続エクスポート時)ファイル名にタイムスタンプサフィックスを付加(例: 損益計算書_2026-03_銀行提出用_20260420-153022.pdf同名ファイルを上書きせず履歴を残す。Drive の重複ファイル機能に依存しない
エクスポート処理が GAS 実行時間制限(6 分)に接近初期実装では 1 リクエスト 1 諸表 1 ファイル形式に制限。複数諸表一括エクスポートは将来案件複数ファイル生成は export URL への UrlFetchApp 呼び出しが諸表数分必要で時間制限に抵触しやすい

実データ検証

以下を MCP(Google Sheets API)または手動で確認し、実装前にシート構成・列範囲・キー名を確定させる。

確認項目確認方法理由
92_fs_pl / 91_fs_bs の実際の列数・レイアウトss.getSheetByName('92_fs_pl').getDataRange() で 2 列(科目・金額)の年間合計構成であることを確認銀行提出用で年度合計を流用する場合、テンプレートのセル範囲を正しく設計するため
61_pl_monthly のヘッダー行と月次列範囲1 行目にヘッダー、C〜N 列が今期 12 ヶ月、O〜Z 列が YoY 差異・予実差異等(MAS-020/MAS-001)であることを確認失敗パターン#21 に基づき、月次列を C〜N の 12 列固定で取得するため
71_bs / 81_cf_indirect / 81b_cf_indirect_ytd の同上同じ列構造(1 行目ヘッダー、C〜N が月次)であることを確認B/S・C/F 用テンプレートの設計根拠を固めるため
01_sys_config に登録されている財務諸表系システムキーPL_M_ACT / PL_Y_ACT / BS_ACT / CF_IND_ACT / CF_IND_ACT_YTD が実在することを 01_sys_config 上で確認。FS_PL / FS_BS キーは未登録のため ss.getSheetByName() 直接参照であることを確認Utils.getSheetByKey() 呼び出し時のキー名と fallback 名を正しく指定するため
YoY 比較データの格納列(取締役会用フォーマットの前提)MAS-020 仕様書(docs/dev/dev_mas-020_yoy_comparison.md)と 61_pl_monthly の実シートで O 列以降の列名を照合取締役会用フォーマットで YoY を表示するため、列範囲を正しく特定する
予実差異データの格納シート(取締役会用フォーマットの前提)MAS-001 仕様書(docs/dev/dev_mas-001_variance_analysis.md)と 65_pl_variance の実シートでレイアウトを確認取締役会用フォーマットで予実差異を引用するため
エクスポートファイル保存先 Drive フォルダ ID の管理場所03_sys_paramsEXPORT_FOLDER_ID キーがあるか、または Env モジュールに対応メソッドがあるか確認実装方針(Utils.getParam() vs Env.xxxFolderId())を決定するため
テンプレートシート _TMPL_* の既存有無SpreadsheetApp.getActive().getSheets()_TMPL_ 接頭辞のシートを列挙未作成なら人間が用意する前提となる(人間が検討すべき事項)

関連ドキュメント

仕様書関連箇所
CLAUDE.mdコーディング規約(ヘッダー名ベースの列参照、有効フラグ FALSE スキップ)・GAS ファイル番号体系(609 の割当根拠)
dev_mas-094_boundary_month_selector.md基準年月プルダウン UI のパターン(今期 12 ヶ月選択肢生成・Utils.parseDateToYm / Utils.addMonths
dev_mas-020_yoy_comparison.md取締役会用フォーマットの YoY データ参照先(61_pl_monthly O 列以降)
dev_mas-001_variance_analysis.md取締役会用フォーマットの予実差異データ参照先(65_pl_variance
dev_mas-003_kpi_dashboard.md取締役会用フォーマットで引用する KPI(売上総利益率・営業利益率等)の算出ロジック参考
failure_patterns.md#21〜#24: 数式設計の教訓(getLastColumn 禁止・全角スペース除去・apostrophe 前置・GAS 側リテラル埋め込み)。本案件の実装制約の根拠
dev_mas-217_sidebar_catalog_and_readability.mdConstants.MENU_DEFINITION にサイドバー項目を登録する既存パターン
600_report/602_datamart_main.js L185-201, L354-359マートシートの Utils.getSheetByKey() / ss.getSheetByName() アクセスパターン
600_report/607_datamart_fs.js既存の 91_fs_bs / 92_fs_pl 決算書生成ロジック(参考、直接は使わない)

人間が検討すべき事項

  1. TODO_future.md 記載の検討事項: 「出力フォーマット(銀行提出用 vs 取締役会用の違い)」— 本仕様では 2 種フォーマットを実装する前提で設計したが、実際にどの項目を「銀行提出用=シンプル」「取締役会用=分析付き」に含めるかはステークホルダー(経営者・銀行担当者)との合意が必要。
  2. テンプレートシートの管理方法: 人間がスプレッドシート上に手動で作成・管理するか、GAS で雛形を自動生成する関数を実装するか。フォーマット数(諸表 3 × フォーマット 2 = 6 枚)が多いため、方針確定後に実装着手すること。手動管理の場合はシート保護(編集制限)を設定して誤編集を防止する。
  3. エクスポートファイルの保存先 Drive フォルダ: 以下 2 案のどちらを採用するか決定が必要。
    • A 案: 03_sys_paramsEXPORT_FOLDER_ID キーを登録し、Utils.getParam('EXPORT_FOLDER_ID', null) で取得(既存 getParam パターン踏襲・DDL 側の追加なし)
    • B 案: Env モジュール(000_infra/001_env.js)に exportFolderId() / setExportFolderId() を追加(既存 receiptFolderId() パターン踏襲・環境分離に統合)
  4. 取締役会用フォーマットの Sparkline(ミニグラフ): GAS の Sheets API では Sparkline の動的生成が複雑なため、テンプレートシートにあらかじめ埋め込む方式が現実的。実装スコープに含めるか確認。
  5. ファイル命名規則の確定: [諸表名]_[基準年月]_[フォーマット種別]_[YYYYMMDD-HHMMSS].(pdf|xlsx) 形式(例: 損益計算書_2026-03_銀行提出用_20260420-153022.pdf)でよいか。タイムスタンプ有無・命名順序。
  6. 初期実装スコープ: 3 諸表 × 2 フォーマット × 2 ファイル形式 = 12 パターン全てを初期実装するか、P/L × 銀行提出用 × PDF のみ先行実装し段階拡張するか。
  7. 社外共有時のセキュリティ配慮: 生成ファイルを Drive に保存する場合、共有リンクを「リンクを知っている全員」にするか「特定ユーザーのみ」にするか。file.setSharing() の設定方針。
  8. 監査証跡の粒度: Utils.auditLog()note 列にエクスポート内容(諸表種別・基準年月・フォーマット)をどこまで詳細に記録するか。個人情報・機密数値を含むため保管期間ポリシーと整合をとる。
  9. 年度累計(YTD)時のデータ範囲: 期首(11 月)から基準年月までを累計するか、会計年度(8 月始まり)で累計するか。現在の 62_pl_ytd 実装がどちらに準拠しているかを確認し、ユーザーが混乱しない表示ラベルを決める。

実装プロンプト(Claude Code用)

あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者です。
案件 MAS-021「財務諸表のPDF/Excelエクスポート」を実装してください。

## 実行前タスク
- `000_infra/002_constants.js` の `MENU_DEFINITION`(L206-324)をReadし、既存サイドバー分類(`📋 サイドバー: 📊 マート更新` 等)の記法を確認。新規カテゴリ `📋 サイドバー: 📤 エクスポート` を同形式で追加する位置を決定
- `100_config/101_sys_config.js` の `openOperationsSidebar()`(L356-364)をReadし、`HtmlService.createTemplateFromFile()` + `showSidebar()` のパターンを確認
- `docs/dev/dev_mas-094_boundary_month_selector.md` をReadし、今期12ヶ月選択肢生成ロジック(`startYear` 判定 + `for` ループ)を流用
- `000_infra/004_utils.js` をReadし、以下のシグネチャを確認:
  - `Utils.getSheetByKey(key, fallbackName)` — 第1引数がシステムキー、第2引数がfallbackシート名
  - `Utils.toastResult(funcName, message, duration)` — funcNameが第1引数
  - `Utils.logError(funcName, error, context)` — funcNameが第1引数
  - `Utils.auditLog(operation, targetSheet, targetId, targetCol, funcName, beforeValue, afterValue, note)` — 8引数
  - `Utils.parseDateToYm()` / `Utils.addMonths()`(基準年月処理用)
- `600_report/602_datamart_main.js` L185-201, L354-359 をReadし、マートシートのアクセスパターンを確認
- MCPで `92_fs_pl` / `91_fs_bs` / `61_pl_monthly` / `71_bs` / `81_cf_indirect` のヘッダー行と月次列構成を確認
- `01_sys_config` にシステムキー `PL_M_ACT` / `BS_ACT` / `CF_IND_ACT` 等が登録済みであることを確認

## 修正対象ファイル
- `000_infra/002_constants.js` — `MENU_DEFINITION` にサイドバー分類 `📋 サイドバー: 📤 エクスポート` を1件追加
- `100_config/101_sys_config.js` — `openFinancialStatementExport()` 関数を新設(ファイル末尾付近の `openOperationsSidebar()` 周辺)
- 新規: `600_report/609_financial_statement_export.js` — バックエンドロジック本体
- 新規: `templates/financial_statement_export.html` — サイドバー UI

## 実装内容
仕様書「修正方針」セクションのアーキテクチャ方針(テンプレートシート複製方式)に完全準拠して実装すること。

### A: `Constants.MENU_DEFINITION` への追記
サイドバー分類配列の末尾(`📋 サイドバー: 🧪 テスト` の後)に以下を追加:
```js
{
  category: '📋 サイドバー: 📤 エクスポート',
  source: 'sidebar',
  items: [
    { label: '📤 財務諸表のPDF/Excelエクスポート', funcName: 'openFinancialStatementExport', description: 'P/L・B/S・CF を PDF または Excel で出力' },
  ]
},
```

### B: `openFinancialStatementExport()` の新設(101_sys_config.js)
`openOperationsSidebar()` 直下に追加:
```js
function openFinancialStatementExport() {
  var html = HtmlService.createHtmlOutputFromFile('templates/financial_statement_export')
    .setTitle('📤 財務諸表エクスポート').setWidth(360);
  SpreadsheetApp.getUi().showSidebar(html);
}
```

### C: `exportFinancialStatement(params)` の新設(609_financial_statement_export.js)
主要な処理フロー:
1. サイドバーからのリクエスト: `params = { sheet: 'pl'|'bs'|'cf', period: 'monthly'|'ytd', baseYm: 'YYYY-MM', format: 'bank'|'board', fileType: 'pdf'|'xlsx' }`
2. テンプレートシート名を解決(例: `sheet='pl'` + `format='bank'` → `_TMPL_pl_bank`)
3. `var ss = SpreadsheetApp.getActive(); var templateSheet = ss.getSheetByName(templateName);` — null ならエラー
4. `var tempSheet = templateSheet.copyTo(ss).setName(templateName + '_tmp_' + Date.now());`
5. `try { データ取得 (Utils.getSheetByKey) → ラベル正規化 (.replace(/[\s ]+/g, '')) で行特定 → tempSheet.getRange(...).setValues([[...]]) でデータ配置 → Sheets export URL を UrlFetchApp で叩いてBlob取得 → DriveApp.getFolderById(folderId).createFile(blob) → file.getDownloadUrl() を返却 } finally { ss.deleteSheet(tempSheet) }`
6. 月次列は `C〜N` の 12 列固定で取得(`getLastColumn()` 禁止)
7. `Utils.auditLog('RUN', '', '', '', 'exportFinancialStatement', '', JSON.stringify(params), 'file=' + file.getUrl())` で監査記録
8. 成功時は `{ url: file.getDownloadUrl(), name: file.getName() }` をサイドバーに返却
9. 失敗時は `throw new Error('...')` で `withFailureHandler` に伝達

### D: `templates/financial_statement_export.html` の新設
- フォーム要素: 対象諸表プルダウン(P/L・B/S・C/F)、対象期間プルダウン(月次・YTD)、基準年月(YYYY-MM)、フォーマット(銀行提出用・取締役会用)、ファイル形式(PDF・Excel)
- MAS-094 と同じ今期 12 ヶ月選択肢生成ロジックを JavaScript 側で実装し、基準年月をプルダウンに展開
- エクスポートボタンのクリックハンドラで `button.disabled = true` → `google.script.run.withSuccessHandler(onSuccess).withFailureHandler(onFailure).exportFinancialStatement(params)` を呼ぶ
- `onSuccess(result)`: `result.url` をアンカータグで表示、`button.disabled = false`
- `onFailure(err)`: `err.message` をエラー領域に表示、`button.disabled = false`
- 基準年月の入力バリデーション: 正規表現 `/^\d{4}-(0[1-9]|1[0-2])$/`

## 制約
- `OrderRepository` / `InvoiceRepository` / `BankTxRepository` / `JournalRepository` は使用しない(ERP 元帳専用。マートシートに対応する Repository は存在しない)
- `sheet.getLastColumn()` による動的列数取得を禁止。月次列は `C〜N` の 12 列固定で取得(失敗パターン#21)
- ラベル正規化は `.replace(/[\s ]+/g, '')` を使用(失敗パターン#22)
- YYYY-MM 形式の文字列書き込みは `range.setValue("'2026-03")` の apostrophe 前置、または `setNumberFormat('@')` 後に `setValue()`(失敗パターン#23)
- テンプレートへのデータ配置は GAS 側で行番号を事前特定し `range.setValues([[...]])` で書き込む。`MATCH` / `ARRAYFORMULA` / `SUBSTITUTE` 数式への依存禁止(失敗パターン#24)
- 一時シートの削除は `try-finally` で必ず実行すること
- `101_sys_config.js` は `openFinancialStatementExport()` の追加のみ。他の既存関数を変更しない
- `002_constants.js` は `MENU_DEFINITION` への1カテゴリ追加のみ。既存カテゴリの順序・内容を変更しない
- メニュー・カテゴリ名は Phase 1 で Read した `MENU_DEFINITION` の実在表記に揃える(失敗パターン#20)

## エッジケース
仕様書「エッジケース」セクションのテーブルに従って実装すること。特に以下を必ず実装:
- データ不在時の処理中断と `Utils.toastResult()` 通知+サイドバーへのエラー伝達
- テンプレートシート不在時のエラーハンドリング(シート名をエラーメッセージに含める)
- KPI 算出時の分母ゼロガード(売上ゼロ月で `"-"` 表示)
- `try-finally` による一時シートの確実な削除
- 連打防止(`button.disabled` トグル)
- 基準年月の正規表現バリデーション(クライアント・サーバ両側)
- Drive 保存ファイル名へのタイムスタンプ付与(同名重複回避)

## 動作確認
1. `npm run push:dev` でデプロイ
2. スプレッドシートを再読込し、`operations_sidebar.html` の「📤 エクスポート」カテゴリに「📤 財務諸表のPDF/Excelエクスポート」項目が追加されていることを確認
3. テンプレートシート(`_TMPL_pl_bank` 等)が存在することを手動確認(未作成なら先に作成するか雛形生成関数を実行)
4. サイドバーを開き、P/L・月次・基準年月=2026-03・銀行提出用・PDF でエクスポート実行 → 一時シートが削除され、Drive にファイルが保存され、ダウンロード URL がサイドバーに表示されることを確認
5. 同様に Excel(xlsx)で実行 → Excel ファイルがダウンロード可能であること
6. 取締役会用フォーマットで実行 → YoY・予実差異・KPI が含まれていることを確認
7. 存在しない期間(例: 未更新の未来月)を指定 → 「対象期間のデータが存在しません」の Toast 通知+サイドバーエラー表示を確認
8. テンプレートシート名を一時的に変更(例: `_TMPL_pl_bank` → `_TMPL_pl_bank_x`) → テンプレート不在時のエラーメッセージにシート名が含まれることを確認
9. エクスポートボタン連打 → 2回目以降がブロックされること
10. `98_audit_log` に `exportFinancialStatement` の RUN エントリが記録されていることを確認

### 拡張思考の使用状況

| フェーズ | 拡張思考 | 備考 |
|----------|:--------:|------|
| Phase 1(調査・設計) | あり | 固有名詞・シート構成・列範囲・メニュー文字列を完全確定 |
| Phase 2(実装) | なし | Phase 1 の確定内容を書き下す。テンプレート設計・データ配置順は仕様書記載の通り |

推奨実行モデル

工程推奨モデル理由
仕様書作成Claude Sonnet 4.6複数ファイル横断調査・新規アーキテクチャ設計が必要。既存仕様書(MAS-094, MAS-020, MAS-001)との整合性チェック・失敗パターン#21-24の適用等、中程度の判断要素あり
実装 A(MENU_DEFINITION 追記)Claude Haiku 4.5仕様書でカテゴリ・項目が完全定義済み。既存パターンに倣った配列追加のみ
実装 B(openFinancialStatementExport() 追加)Claude Haiku 4.5既存 openOperationsSidebar() を完全に模倣した10行程度の関数
実装 C(609_financial_statement_export.js 本体)Claude Sonnet 4.6テンプレートシート複製・UrlFetchApp による Sheets export URL 呼び出し・try-finally での一時シート削除・監査ログ記録等、既存パターンを超える新規実装あり。テンプレート上のセル配置判断も必要
実装 D(HTML サイドバー UI)Claude Haiku 4.5MAS-094 のパターン踏襲。仕様書でフォーム項目・バリデーション正規表現・google.script.run 呼び出しが完全定義済み
動作確認ユーザー手動テンプレートシート作成・Drive フォルダ設定・実機エクスポートテストが必要

変更履歴

日付変更内容
2026-04-20初版作成
2026-05-11設計変更: GAS HtmlService サイドバー → Cockpit 画面固有サイドバー + ExportPanel.tsx。GAS エクスポートロジックは変更なし

仕様書作成プロンプト(再現性・監査性のため必ず記録)

展開して表示
【タイムアウト回避・実行原則(v1.7・必ず遵守すること)】
1. **拡張思考の使い分け**: Phase 1(設計フェーズ)では拡張思考をフル活用し、ファイル名形式・エッジケース一覧・Step分割粒度・固有名詞(関数名/シート名/列名/行番号)をここで完全に確定させる。Phase 2(清書フェーズ)では各Step内の拡張思考を最小限に抑え、Phase 1で確定した内容の書き下しに徹する。出力途中で再考しない。
2. **テキスト報告の禁止**: 「〜を作成します」等のtextのみで tool_use なしにturnを終了しない。説明は1文以内。直ちにtoolを呼ぶ。
3. **4-5分割のWrite/Edit実行**: Step 2-1(骨格 ~20行)/ 2-2(概要〜注意事項 ~300行)/ 2-3a(エッジケース〜人間検討事項 ~200行)/ 2-3b(実装プロンプト〜変更履歴 ~250行)/ 2-4(`<details>`にプロンプト全文記録)に分割。1回のWrite/Editは300行以内を厳守。
4. **各Stepで何を書くかを具体指示**: 設計判断はPhase 1で完全に確定させ、Phase 2では書き下しに徹する。

======================================================================
あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者兼仕様書ライターです。
案件 F-21「財務諸表のPDF/Excelエクスポート」の開発仕様書を作成してください。
作成後は `docs/_config.json` の `nav` 配列の適切なセクションにも必ず追記してください。

---

## Phase 1: 実行前タスク(テキスト報告禁止。直ちにツール実行)

以下をすべてRead/Grepで確認し、Phase 2で設計判断を再考しないよう固有名詞・構造・行番号を確定させること。
**Grepは「どこにあるか」の発見まで。「どう書くか」の判断は必ずReadで裏取り。名前や記憶から推測した瞬間に手を止めてReadする(失敗パターン#18-#20対策)。**

### 1-A: 案件定義の読み込み
- `docs/_internal/TODO_future.md` — F-21の案件名・概要・人間が検討すべき事項を取得

### 1-B: プロジェクト規約の読み込み
- `CLAUDE.md` — コーディング規約・ファイル番号体系・GAS環境分離を確認

### 1-C: 参考実装の読み込み(既存UIパターンの把握)
- `docs/dev/dev_mas-094_boundary_month_selector.md` — HtmlServiceサイドバーの実装パターンを確認
  - HTMLファイルの命名規則とプロジェクト内配置場所
  - `google.script.run.withSuccessHandler().withFailureHandler()` の呼び出しパターン
  - 基準年月入力UIのフォーム設計

### 1-D: 関連コードの調査(Grepで場所を特定してからReadで構造確認)

1. **`100_config/101_sys_config.js`(最優先・必ずRead)**
   - `onOpen()` の既存メニュー構造を**コードに記載されたそのままの文字列で**確認
   - 「エクスポート」メニューを追加する際の挿入位置(既存メニューの末尾か、新規グループか)を特定
   - ※メニュー名を記憶や推測で書くことを禁止する(失敗パターン#20)

2. **`000_infra/004_utils.js`**
   - `Utils.getSheetByKey(key, fallbackName)` — シグネチャと戻り値の型
   - `Utils.toastResult(funcName, message, duration)` — 引数の順序(funcNameが第1引数)
   - `Utils.logError(funcName, error, context)` — エラーハンドリング用
   - `Utils.auditLog(operation, targetSheet, targetId, targetCol, funcName, ...)` — エクスポート操作の監査記録用

3. **`000_infra/002_constants.js`**
   - `Constants.NUMBER_FORMATS.CURRENCY` の実際のフォーマット文字列を確認
   - `Constants.CONFIG_SHEET` の値(`'01_sys_config'`)を確認

4. **`600_report/` ディレクトリ配下(Grepで該当ファイルを絞ってからRead)**
   - `91_fs_bs`・`92_fs_pl`・キャッシュフロー計算書シートへのアクセスパターン
   - `Utils.getSheetByKey()` に渡すシステムキー名(例: `TRN_PL`等)が実在するか確認。存在しない場合はシート名を直接指定するフォールバックパターンを確認
   - データマートの列構造(ラベル列・月次列の範囲。C〜N列固定か否か)

5. **`docs/_internal/failure_patterns.md`(#21〜#24のセクション)**
   - GAS側で行番号を事前特定してリテラル埋め込みする原則
   - `getLastColumn()` 禁止の理由
   - 全角スペース `U+3000` 除去パターン `.replace(/[\s ]+/g, '')`
   - YYYY-MM形式文字列のapostrophe前置 `setValue("'2026-03")`

### 1-E: 実データ確認(MCPツールで確認)
- `92_fs_pl`(P/Lマートシート)のヘッダー行・ラベル列・月次列の実際の構成
- `91_fs_bs`(B/Sマートシート)の同上
- キャッシュフロー計算書の実際のシート名を確認(`84_cf_daily_plan` か別シートか)
- `01_sys_config` に登録されている財務諸表系シートのシステムキー一覧
- YoY比較データが格納されているシートの実名(取締役会用フォーマットの前提)

---

## Phase 2: 仕様書の分割作成

出力先: `docs/dev/dev_mas-021_financial_statement_export.md`
**1回のツール呼び出しで全内容を出力することを禁止。必ず以下のStepに分割すること。**

### Step 2-1: 骨格の作成(Write, ~20行)

見出しのみ。本文は空で可。以下の全セクション見出しを含めること(F-21 仕様書のセクション見出し一覧は冒頭を参照)。

### Step 2-2: 前半セクションの追記(Edit or Bash, ~300行)
(元プロンプトの詳細な内容を参照。概要・目的・現在のコード・修正方針・影響範囲・注意事項を埋める)

### Step 2-3a: エッジケース〜人間検討事項の追記(Edit or Bash, ~200行)
(エッジケース・実データ検証・関連ドキュメント・人間が検討すべき事項をテーブル形式で記述)

### Step 2-3b: 実装プロンプト〜変更履歴の追記(Edit or Bash, ~250行)
(実装プロンプトは行頭4スペースインデント。推奨実行モデル・変更履歴を含む)

### Step 2-4: 仕様書作成プロンプト全文の記録(Edit or Bash)
`## 仕様書作成プロンプト` セクションに `<details>` タグでこの `<instruction>` タグ全文を記録する。

---

## Phase 3: 保存・登録・コミット

### 3-A: ファイル保存確認
`docs/dev/dev_mas-021_financial_statement_export.md` が正しく保存されていることを確認

### 3-B: `docs/_config.json` へのナビゲーション登録(必須)
`docs/_config.json` の `nav` 配列の §E.5(FP&A・レポーティング)セクションに追記:
`{ "file": "dev/dev_mas-021_financial_statement_export.md", "title": "E.5.X F-21 財務諸表のPDF/Excelエクスポート" }`
(X は既存エントリの末尾番号に +1 した値を付与)

### 3-C: changelog追記
`docs/_internal/changelog.md` の先頭行(ヘッダーの直後)に追記:
`| 2026-04-20 | [dev_mas-021_financial_statement_export.md](dev_mas-021_financial_statement_export.md) | 初版作成。財務諸表PDF/Excelエクスポート機能の仕様書。テンプレートシート複製方式・failure_patterns#21-24対策を含む |`

### 3-D: コミット&プッシュ
git add / commit / push を実行し、PR 用メッセージを用意する。