追記

(2025/07/07 追記) 世の中には似たようなことを考えた人がいて、以下のgithubで

コンテキストエンジニアリング入門としていい感じにまとめられている

https://github.com/coleam00/context-engineering-intro

趣旨

2023年時点ではChatGPTに読み込ませるドキュメント量が限られていた

ここではChatGPTを使って有益な資料作成のための質問例をまとめる

別でもまとめている ChatGPTの話題

優秀な秘書にするプロンプト

強いワードがあります。それは

「マネジメントして」

です。

複雑になってきたコードで使うプロンプト

1回目、大筋の確認

修正案を3つ出してください。

2回目、具体的なタスクの確認をすることで、余計な修正と必要な修正を区別して指示できる。

段階ごとに進めたいので計画を立ててください。

という感じで、コード生成の前に上記の指示を2回指示をすることで、コードの精度が高くなる。

claude3.5 sonnetでコード生成するときのおまじない

クラス名メソッド名、引数、条件の意図、コメントを追加してください。既存のコメントは残してください。
ログは、トレースをログからするため、条件分岐のブロックごとに必ず設置し、主要な変数の値を出力してください。
以後指示しなくても、サンプルの出力も含め、コメントルールやログのルールを順守してください。

claude3.5 sonnetに伝える方針

堅実で保守性の高いコードを生成し、充実したコメントとログを含めることで、将来の自分や他の開発者が容易に理解できるようにします。以下の点に特に注意を払います:

既存のコメントを保持し、クラス、メソッド、引数、条件文の意図を明確に説明します。
デバッグを容易にするため、適切なログ出力を含めます。
複雑な処理や長いファイルは適切に分割し、可読性を高めます。
修正箇所が3か所以上の場合は、全文を出力して誤りを防ぎます。
入れ子が5階層を超える場合は、コードの構造を見直します。
ファイルが150行を超える場合は、分割を検討します。

これらの方針に従って、保守性が高く、将来の開発者(自分自身を含む)が容易に理解できるコードを生成します。ご指示の通り、コードを生成する際は常にこれらのルールを適用します。
作業を進める際は、以下のようなステップで慎重に進めていきます:

現在のコードの状態を把握し、問題点や改善点を特定します。
改善計画を立て、小さな単位で修正を行います。
各修正後、コードの動作確認とログの適切性を確認します。
必要に応じて、コードの構造やファイルの分割を検討します。
修正が完了したら、全体的な見直しを行い、コメントとログの充実度を確認します。

このアプローチにより、堅実で保守性の高いコード開発を実現します。ご要望に応じて、細心の注意を払いながら作業を進めていきます。


# 現状のソースコードのフォルダ構成は以下です。

まずは、目標を達成するために作業計画を立ててください。連携や修正が必要なファイル名を挙げてください。

コードを生成依頼するときのお願い分

コメントルールに沿って、全文出力してください

次のステップをお願いします。コメントルールに沿って、全文出力してください。引数や、条件分岐の判定が複雑な場合のコメントもお願いします。

次のステップをお願いします。他とバッティングしないならば、続けてステップを続けても大丈夫です。コメントルールに沿って全文出力をお願いします。ヒューマンエラー低減の意味もあります。

ビジネスプラン

ソフトバンクの孫正義さんが使っているそうです。

あなたは(専門家1),(専門家2),(専門家3),(専門家4),(専門家5)

の役割を持っています。

今から(トピック)について発話させ課題と解決方法も混ぜながら、水平思考を使い議論をしてください。

(ゴール)に向かって議論してください。議論は何週もしてください。

必ず(ゴール)について結論を出してください。

各条件は以下の通りです。

#条件


(専門家1)
・AI研究家

(専門家2)
・AIエンジニア

(専門家3)
・経済アナリスト

(専門家4)
・英語学習のプロ講師

(専門家5)
・AI超名門VC

(テーマ)
・konngo
五カ年の生成AI x 語学学習の未来

(参考資料)
リンクがあれば、記載します。


(ゴール)
・今後5カ年の生成AI x 語学学習のビジネスについての動向について深いインサイトが得られた状態

・専門家全員の意見を交えて、ビジネスについて具体的な新規事業のアイデアを1つ生み出してください。

途中で文章の生成がおわるので以下のコメントで続きを促す 続きをお願いいたします。

もっと議論を深めて具体的なビジネスプランまで落とし込んでください。

ありがとうございます。
他にもいくつかのアイデアを出して下さい。議論は何週もしてください。

OpenAPI作成

様々なサイトではOpenAPI形式よりは、PDF形式でAPIを提供している。2023年ではOpenAPIのリテラシーはまだ浸透していないためである。

ヘッダ部分を作成する際の質問

openapiをyamlで作ろうとしています。
以下の内容をopenapiで表現してほしいです。
summaryやdescriptionなどの説明は日本語でお願いします。説明は情報の劣化を防ぐため原文のままが望ましいです。
リクエストやレスポンスは追って連絡します。
内容:
```

```

リクエスト部分を作成する際の質問

openapiのリクエスト部分をyaml形式で作成してください。
summaryやdescriptionなどの説明は日本語でお願いします。説明は情報の劣化を防ぐため原文のままが望ましいです。


説明:
```

```

レスポンス部分を作成する際の質問

openapiのレスポンス部分をyaml形式で作成してください。
summaryやdescriptionなどの説明は日本語でお願いします。説明は情報の劣化を防ぐため原文のままが望ましいです。


説明:
```

```

JDLの生成

jhipsterのjdlのentityやenumを出力してください。
requireや文字数の指定も注意してください。
エンティティの末尾にはカンマは不要なので出力しないでください。
enumの名前はキャメル形式、enumの変数は大文字のスネーク形式で出力してください。
jdlにおいてentityのフィールド定義の間にカンマは不要です。

input:
```

```

天才会議プロンプト

### タスク
テーマについて関連する天才・初心者・ポジティブ・心配性なキャラ4人を作成し、批判的思考で非常に価値がの高い架空の会議を進めてください(最低4000字以上会議を続けてください)。
会議の最後に、天才ブロガーの目線から、会議の内容を元に、構造化して整理された議事録をステップバイステップで考えてください。

### 会議ルール
- 登場人物のキャラ設定を詳細に記述してください
- 議論は深掘りするか横展開するかのみです。同意のみの意見は必要ないです。
- 批判的思考で本質をついたアイデア出しや反論を活発に行ってください
- 議論を見ている人が最高に価値を感じる議論を初心者にもわかりやすい表現で限界まで続けてください。
- 自分の意見を正当化するために根拠と理由を明確化してください。否定する場合は必ず代替案を出してください。

### テーマ:
・ここにテーマをいれてね

グラフィックレコーディング風インフォグラフィック変換プロンプト

目的

 以下の内容を、超一流デザイナーが作成したような、日本語で完璧なグラフィックレコーディング風のHTMLインフォグラフィックに変換してください。情報設計とビジュアルデザインの両面で最高水準を目指します
 手書き風の図形やアイコンを活用して内容を視覚的に表現します。

デザイン仕様

1. カラースキーム

 <palette>
 <color name='ファッション-1' rgb='593C47' r='89' g='59' b='70' />
 <color name='ファッション-2' rgb='F2E63D' r='242' g='230' b='60' />
 <color name='ファッション-3' rgb='F2C53D' r='242' g='196' b='60' />
 <color name='ファッション-4' rgb='F25C05' r='242' g='91' b='4' />
 <color name='ファッション-5' rgb='F24405' r='242' g='68' b='4' />
 </palette>

2. グラフィックレコーディング要素

  • 左上から右へ、上から下へと情報を順次配置

  • 日本語の手書き風フォントの使用(Yomogi, Zen Kurenaido, Kaisei Decol)

  • 手描き風の囲み線、矢印、バナー、吹き出し

  • テキストと視覚要素(アイコン、シンプルな図形)の組み合わせ

  • キーワードの強調(色付き下線、マーカー効果)

  • 関連する概念を線や矢印で接続

  • 絵文字やアイコンを効果的に配置(✏️📌📝🔍📊など)

    3. タイポグラフィ

    • タイトル:32px、グラデーション効果、太字
    • サブタイトル:16px、#475569
    • セクション見出し:18px、#1e40af、アイコン付き
    • 本文:14px、#334155、行間1.4
    • フォント指定:
    ### 4. レイアウト - ヘッダー:左揃えタイトル+右揃え日付/出典 - 3カラム構成:左側33%、中央33%、右側33% - カード型コンポーネント:白背景、角丸12px、微細シャドウ - セクション間の適切な余白と階層構造 - 適切にグラスモーフィズムを活用 - 横幅は100%にして ## グラフィックレコーディング表現技法
  • テキストと視覚要素のバランスを重視

  • キーワードを囲み線や色で強調

  • 簡易的なアイコンや図形で概念を視覚化

  • 数値データは簡潔なグラフや図表で表現

  • 接続線や矢印で情報間の関係性を明示

  • 余白を効果的に活用して視認性を確保

    全体的な指針

  • 読み手が自然に視線を移動できる配置

  • 情報の階層と関連性を視覚的に明確化

  • 手書き風の要素で親しみやすさを演出

  • 視覚的な記憶に残るデザイン

  • フッターに出典情報を明記

    変換する文章/記事

ネットで紹介されていたプロンプト

参考元

https://note.com/limeblog/n/nb9bd271b955b

AIが賢くなるプロンプト➀

私の戦略パートナーとして、現実的かつ論理的な助言をしてください。
率直さは保ちつつ冷淡にならず
異なる意見がある場合はその理由と代替案を示し、常に全体像と次の行動がわかるようにしてください。

AIが賢くなるプロンプト②

「推測で答えない。出典と日付を示す。
理由→結論→数字の順で回答。
不明は「確認できません」と答える。」
コードブロックで出力して。
私は抽象的なアドバイスが嫌いで行動経済学、認知科学、成功者の思考パターンに基づいて、理由と具体的な実践方法とともに教えてください。

回答は、過剰改行・箇条書きを避け、論点単位の自然な段落で構成してください。
感情語は減らし、接続語を明示し、情報密度を維持したまま読解速度を上げてください。追加誘導や後出しは禁止し、必要な補足は最初の回答に統合してください。

会社の数字のプロンプト

参考

https://app.notion.com/p/nextmeee/20260626-AI-3-aff60cc62d6e8308a25c81ce2f0269d4

Step1 収益シミュレーション準備プロンプト

あなたは、起業初心者向けに収益シミュレーションの前提条件を整理する優秀な経営コンサルタントです。

以下の事業アイデアについて、Googleスプレッドシートの入力フォームに貼り付けやすい形で、PL作成の元になる売上前提・原価内訳・販管費内訳を整理してください。

# 事業アイデア

【ここに事業アイデアを入力】

# 想定している内容

・商品/サービス内容:【入力】

・想定顧客:【入力】

・販売価格/客単価:【分かれば入力。分からなければAIが仮置き】

・月間の想定顧客数:【分かれば入力。分からなければAIが仮置き】

・主な費用:【分かれば入力。分からなければAIが仮置き】

・販売方法:【分かれば入力。分からなければAIが仮置き】

・その他の前提:【分かれば入力。分からなければAIが仮置き】

# あなたの役割

この段階では、月次PL表や5年シミュレーション表は作成しないでください。

あなたが作成するのは、Googleスプレッドシートで収益シミュレーションを作るための「入力データ」です。

# 作成してほしいもの

以下の5つを作成してください。

1. 基本情報
2. 売上前提
3. 原価内訳
4. 販管費内訳
5. 確認ポイント

# 売上前提の考え方

事業内容に応じて、最も自然な売上分解を選んでください。

・通常型:売上高 = 客単価 × 客数

・店舗型:売上高 = 平均注文単価 × 来店客数 × 購入率

・講座型:売上高 = 受講単価 × 受講者数

・物販型:売上高 = 購入単価 × 購入点数 × 購入者数

・サブスク型:売上高 = 月額単価 × 有料会員数

・法人型:売上高 = 契約単価 × 契約社数

MVPでは、最終的に「客単価」と「月間顧客数」に置き換えてください。

# 原価の考え方

原価は、商品・サービスを提供するたびに直接発生する費用です。

原価の例:

・仕入原価

・材料費

・製造原価

・配送費

・決済手数料

・販売手数料

・サービス提供に直接必要な外注費

・教材費

・消耗品費

・交通費

・その他、1件販売するごとに増える費用

すべての項目を入れる必要はありません。

事業内容に合うものだけを選んでください。

# 販管費の考え方

販管費は、販売活動や事業運営のために必要な費用です。

販管費の例:

・人件費

・広告宣伝費

・SNS運用費

・外注費

・システム利用料

・通信費

・家賃

・水道光熱費

・交通費

・交際費

・会議費

・備品費

・消耗品費

・専門家費用

・その他販管費

原価と販管費を混同しないでください。

# 数値設定ルール

未入力の数値は、初期事業として現実的な仮説で仮置きしてください。

仮置きした場合は、設定理由に必ず「仮置き」と書いてください。

売上を過大に見積もりすぎず、起業初期として現実的な数値にしてください。

# 出力形式

以下の4つの表を、TSV形式で出力してください。

TSVとは、列と列の間をタブで区切る形式です。

数値には「円」などの単位を入れず、数値のみで出力してください。

単位は別列に記載してください。

## 表1:基本情報

項目  入力内容    設定理由

事業アイデア

商品/サービス内容

想定顧客

販売方法

その他前提

## 表2:売上前提

項目  値   単位  設定理由

売上タイプ

売上分解式

販売価格/客単価

月間顧客数

月次顧客成長率

## 表3:原価内訳

科目  月額  売上比率    設定理由    補足

原価科目は、事業内容に合うものだけを3〜8項目程度で設定してください。

月額は毎月固定的に発生する金額です。

売上比率は売上に連動する割合です。5%の場合は0.05と出力してください。

## 表4:販管費内訳

科目  月額  売上比率    設定理由    補足

販管費科目は、事業内容に合うものだけを4〜10項目程度で設定してください。

月額は毎月固定的に発生する金額です。

売上比率は売上に連動する割合です。5%の場合は0.05と出力してください。

# 最後に文章で出力

最後に、以下を初心者にも分かる言葉で説明してください。

1. この事業で最も重要な変数
2. 黒字化に必要な条件
3. 原価で注意すべき項目
4. 販管費で注意すべき項目
5. 受講生が最初に見直すべき入力項目

STEP2 Apps Scriptのコード

/************************************************************

- 起業初心者向け 収益シミュレーション自動作成テンプレート
- Step2:AI出力TSVを入力フォームへ反映し、PLを自動作成する版
-
- 対応する流れ:
- Step1:AIに「基本情報・売上前提・原価内訳・販管費内訳」をTSVで出力させる
- Step2:AI出力貼付シートに貼り付ける
- Step3:Apps Scriptで入力フォームへ反映
- Step4:PL、月次、3ケース、5年シミュレーションを自動作成
-
- 使い方:
-
    1. Apps Scriptにこのコードを丸ごと貼り付け
-
    1. 保存
-
    1. スプレッドシートを再読み込み
-
    1. メニュー「シミュレーション作成」を使用
    ************************************************************/

/************************************************************

- メニュー追加
************************************************************/
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('シミュレーション作成')
.addItem('AI出力貼付シートを作成', 'createAiPasteSheet')
.addItem('AI出力を入力フォームへ反映', 'importAiOutputToInputForm')
.addSeparator()
.addItem('全シートを作成', 'createSimulationTemplate')
.addItem('AI出力から反映して全シート作成', 'importAiOutputAndCreateSimulation')
.addSeparator()
.addItem('入力フォーム以外を再作成', 'rebuildOutputSheetsOnly')
.addToUi();
}

/************************************************************

- AI出力貼付シート作成
************************************************************/
function createAiPasteSheet() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = resetOrCreateSheet_(ss, 'AI出力貼付');

sheet.getRange('A1:F1').setValues([
['AI出力貼付シート', '', '', '', '', '']
]);

sheet.getRange('A3:F3').setValues([
['使い方', '', '', '', '', '']
]);

sheet.getRange('A4:F8').setValues([
['1', 'Step1プロンプトをAIに実行する', '', '', '', ''],
['2', 'AIが出力したTSV表を、このシートのA12セル以降に貼り付ける', '', '', '', ''],
['3', 'メニュー「シミュレーション作成」→「AI出力を入力フォームへ反映」を押す', '', '', '', ''],
['4', 'メニュー「シミュレーション作成」→「全シートを作成」を押す', '', '', '', ''],
['5', '一括実行する場合は「AI出力から反映して全シート作成」を押す', '', '', '', '']
]);

sheet.getRange('A10:F10').setValues([
['ここから下に、AIが出力したTSVを貼り付けてください', '', '', '', '', '']
]);

styleTitle_(sheet, 'A1:F1');
styleHeader_(sheet, 'A3:F3');
styleHeader_(sheet, 'A10:F10');
styleTable_(sheet, 'A3:F8');
styleTable_(sheet, 'A10:F10');

sheet.setColumnWidth(1, 220);
sheet.setColumnWidth(2, 420);
sheet.setColumnWidth(3, 180);
sheet.setColumnWidth(4, 360);
sheet.setColumnWidth(5, 320);
sheet.setColumnWidth(6, 240);

sheet.getRange('A1:F10').setWrap(true);
sheet.setFrozenRows(10);

ss.setActiveSheet(sheet);

SpreadsheetApp.getUi().alert(
'AI出力貼付シートを作成しました。\n\nA12セル以降に、Step1でAIが出力したTSVを貼り付けてください。'
);
}

/************************************************************

- AI出力を反映してから全シート作成
************************************************************/
function importAiOutputAndCreateSimulation() {
importAiOutputToInputForm();
createSimulationTemplate();
}

/************************************************************

- AI出力を入力フォームへ反映
************************************************************/
function importAiOutputToInputForm() {
const ss = SpreadsheetApp.getActiveSpreadsheet();

if (!ss.getSheetByName('AI出力貼付')) {
createAiPasteSheet();
SpreadsheetApp.getUi().alert(
'AI出力貼付シートを作成しました。\n\nまず、Step1でAIが出力したTSVをAI出力貼付シートのA12セル以降に貼り付けてください。'
);
return;
}

const parsed = parseAiOutputSheet_(ss);

if (!parsed || parsed.isEmpty) {
SpreadsheetApp.getUi().alert(
'AI出力を読み取れませんでした。\n\nAI出力貼付シートのA12セル以降に、Step1で出力したTSV表を貼り付けてください。'
);
return;
}

resetOrCreateSheet_(ss, '入力フォーム');
buildInputFormSheet_(ss, parsed);

ss.setActiveSheet(ss.getSheetByName('入力フォーム'));

SpreadsheetApp.getUi().alert(
'AI出力を入力フォームへ反映しました。\n\n次に「シミュレーション作成」→「全シートを作成」を押すと、PL・月次・3ケース・5年シミュレーションが作成されます。'
);
}

/************************************************************

- 全シート作成
************************************************************/
function createSimulationTemplate() {
const ss = SpreadsheetApp.getActiveSpreadsheet();

const existingState = getExistingInputState_(ss);

if (!ss.getSheetByName('AI出力貼付')) {
createAiPasteSheetOnly_(ss);
}

resetOrCreateSheet_(ss, '入力フォーム');
resetOrCreateSheet_(ss, '前提条件');
resetOrCreateSheet_(ss, '月次シミュレーション');
resetOrCreateSheet_(ss, '3ケース比較');
resetOrCreateSheet_(ss, '5年シミュレーション');
resetOrCreateSheet_(ss, '確認ポイント');

buildInputFormSheet_(ss, existingState);
const assumptionMeta = buildAssumptionSheet_(ss);
const monthlyMeta = buildMonthlySimulationSheet_(ss, assumptionMeta);
buildCaseComparisonSheet_(ss, monthlyMeta);
buildFiveYearSimulationSheet_(ss, monthlyMeta);
buildCheckPointSheet_(ss);

SpreadsheetApp.flush();

ss.setActiveSheet(ss.getSheetByName('入力フォーム'));

SpreadsheetApp.getUi().alert(
'収益シミュレーションテンプレートを作成しました。\n\n入力フォームの内容を変更した場合は、再度「全シートを作成」を押してください。'
);
}

/************************************************************

- 入力フォーム以外を再作成
************************************************************/
function rebuildOutputSheetsOnly() {
const ss = SpreadsheetApp.getActiveSpreadsheet();

if (!ss.getSheetByName('入力フォーム')) {
resetOrCreateSheet_(ss, '入力フォーム');
buildInputFormSheet_(ss, getDefaultInputState_());
}

resetOrCreateSheet_(ss, '前提条件');
resetOrCreateSheet_(ss, '月次シミュレーション');
resetOrCreateSheet_(ss, '3ケース比較');
resetOrCreateSheet_(ss, '5年シミュレーション');
resetOrCreateSheet_(ss, '確認ポイント');

const assumptionMeta = buildAssumptionSheet_(ss);
const monthlyMeta = buildMonthlySimulationSheet_(ss, assumptionMeta);
buildCaseComparisonSheet_(ss, monthlyMeta);
buildFiveYearSimulationSheet_(ss, monthlyMeta);
buildCheckPointSheet_(ss);

SpreadsheetApp.flush();

ss.setActiveSheet(ss.getSheetByName('前提条件'));

SpreadsheetApp.getUi().alert('入力フォーム以外のシートを再作成しました。');
}

/************************************************************

- 入力フォーム
************************************************************/
function buildInputFormSheet_(ss, state) {
const sheet = ss.getSheetByName('入力フォーム');

sheet.getRange('A1:E1').setValues([
['起業初心者向け 収益シミュレーション 入力フォーム', '', '', '', '']
]);

const basicRows = [
['入力項目', '入力内容', '入力例', '補足', ''],
['事業アイデア', valueOrBlank_(state.basic['事業アイデア']), '例:告白代行ビジネス', '事業の一言説明', ''],
['商品/サービス内容', valueOrBlank_(state.basic['商品/サービス内容']), '例:相談・代行・同席支援', '提供価値を具体的に記載', ''],
['想定顧客', valueOrBlank_(state.basic['想定顧客']), '例:高校生、大学生、20代社会人', '誰がお金を払うか', ''],
['販売価格/客単価', valueOrBlank_(state.basic['販売価格/客単価']), '例:10000', '円。数値で入力推奨', ''],
['月間顧客数', valueOrBlank_(state.basic['月間顧客数']), '例:30', '月あたりの利用者数', ''],
['販売方法', valueOrBlank_(state.basic['販売方法']), '例:SNS、LP、紹介、LINE公式', '集客・販売チャネル', ''],
['その他前提', valueOrBlank_(state.basic['その他前提']), '例:初期は個人運営、副業で開始', '制約条件や前提', '']
];

sheet.getRange(3, 1, basicRows.length, basicRows[0].length).setValues(basicRows);

sheet.getRange('A13:E13').setValues([
['原価内訳', '', '', '', '']
]);

sheet.getRange('A14:E14').setValues([
['科目', '月額', '売上比率', '設定理由', '補足']
]);

const cogsRows = normalizeCostRows_(state.cogs, 10);
sheet.getRange(15, 1, cogsRows.length, 5).setValues(cogsRows);

sheet.getRange('A27:E27').setValues([
['販管費内訳', '', '', '', '']
]);

sheet.getRange('A28:E28').setValues([
['科目', '月額', '売上比率', '設定理由', '補足']
]);

const sgnaRows = normalizeCostRows_(state.sgna, 12);
sheet.getRange(29, 1, sgnaRows.length, 5).setValues(sgnaRows);

sheet.getRange('A43:E43').setValues([
['使い方', '', '', '', '']
]);

sheet.getRange('A44:E49').setValues([
['1', 'Step1でAIに事業の売上・原価・販管費を整理させる', '', '', ''],
['2', 'AI出力貼付シートにTSVを貼る', '', '', ''],
['3', 'AI出力を入力フォームへ反映する', '', '', ''],
['4', '黄色セルの内容を必要に応じて修正する', '', '', ''],
['5', 'メニュー「全シートを作成」をクリックする', '', '', ''],
['6', 'PL、月次、3ケース、5年シミュレーションを確認する', '', '', '']
]);

styleTitle_(sheet, 'A1:E1');
styleHeader_(sheet, 'A3:E3');
styleHeader_(sheet, 'A13:E13');
styleHeader_(sheet, 'A14:E14');
styleHeader_(sheet, 'A27:E27');
styleHeader_(sheet, 'A28:E28');
styleHeader_(sheet, 'A43:E43');

styleInput_(sheet, 'B4:B11');
styleInput_(sheet, 'A15:E24');
styleInput_(sheet, 'A29:E40');

styleTable_(sheet, 'A3:E11');
styleTable_(sheet, 'A13:E24');
styleTable_(sheet, 'A27:E40');
styleTable_(sheet, 'A43:E49');

sheet.getRange('B7:B8').setNumberFormat('#,##0');
sheet.getRange('B15:B24').setNumberFormat('#,##0');
sheet.getRange('C15:C24').setNumberFormat('0.0%');
sheet.getRange('B29:B40').setNumberFormat('#,##0');
sheet.getRange('C29:C40').setNumberFormat('0.0%');

sheet.setColumnWidth(1, 180);
sheet.setColumnWidth(2, 140);
sheet.setColumnWidth(3, 120);
sheet.setColumnWidth(4, 420);
sheet.setColumnWidth(5, 340);

sheet.getRange('A1:E49').setVerticalAlignment('middle');
sheet.getRange('A1:E49').setWrap(true);
sheet.setFrozenRows(3);
}

/************************************************************

- 前提条件
************************************************************/
function buildAssumptionSheet_(ss) {
const sheet = ss.getSheetByName('前提条件');

const state = getInputStateFromForm_(ss);

const unitPrice = parseNumberLike_(state.basic['販売価格/客単価'], 10000);
const customerCount = parseNumberLike_(state.basic['月間顧客数'], 30);
const monthlyGrowthRate = parsePercentLike_(state.basic['月次顧客成長率'], 0.05);

const unitPriceReason = isBlank_(state.basic['販売価格/客単価'])
? '仮置き:初心者向けサービスの初期客単価として10,000円を設定'
: '入力フォームより反映';

const customerReason = isBlank_(state.basic['月間顧客数'])
? '仮置き:初期集客数として月30人を設定'
: '入力フォームより反映';

sheet.getRange('A1:E1').setValues([
['前提条件', '', '', '', '']
]);

const basicRows = [
['区分', '項目', '値', '単位', '設定理由'],
['事業概要', '事業アイデア', state.basic['事業アイデア'] || '未入力', '', isBlank_(state.basic['事業アイデア']) ? '仮置き:未入力のため後で入力してください' : '入力フォームより反映'],
['事業概要', '商品/サービス内容', state.basic['商品/サービス内容'] || '未入力', '', isBlank_(state.basic['商品/サービス内容']) ? '仮置き:未入力のため後で入力してください' : '入力フォームより反映'],
['事業概要', '想定顧客', state.basic['想定顧客'] || '未入力', '', isBlank_(state.basic['想定顧客']) ? '仮置き:未入力のため後で入力してください' : '入力フォームより反映'],
['売上前提', '販売価格/客単価', unitPrice, '円', unitPriceReason],
['売上前提', '月間顧客数', customerCount, '人', customerReason],
['売上前提', '月次顧客成長率', monthlyGrowthRate, '%', isBlank_(state.basic['月次顧客成長率']) ? '仮置き:初期事業の成長率として月5%を設定' : 'AI出力または入力フォームより反映'],
['売上前提', '売上タイプ', state.basic['売上タイプ'] || '通常型', '', isBlank_(state.basic['売上タイプ']) ? '仮置き:客単価×顧客数の通常型で設定' : 'AI出力より反映'],
['売上前提', '売上分解式', state.basic['売上分解式'] || '売上高=客単価×顧客数', '', isBlank_(state.basic['売上分解式']) ? '仮置き:MVP版では客単価×顧客数に置換' : 'AI出力より反映'],
['販売前提', '販売方法', state.basic['販売方法'] || '未入力', '', isBlank_(state.basic['販売方法']) ? '仮置き:未入力のため後で入力してください' : '入力フォームより反映'],
['その他', 'その他前提', state.basic['その他前提'] || '未入力', '', isBlank_(state.basic['その他前提']) ? '仮置き:未入力のため後で入力してください' : '入力フォームより反映']
];

sheet.getRange(3, 1, basicRows.length, basicRows[0].length).setValues(basicRows);

const cogsStartRow = 17;

sheet.getRange('A15:E15').setValues([
['原価内訳', '', '', '', '']
]);

sheet.getRange('A16:E16').setValues([
['区分', '科目', '月額', '売上比率', '設定理由']
]);

const cogsRows = state.cogs.map(function(item) {
return
'原価',
[item.name,
item.monthlyAmount,
item.salesRate,
item.reason
];
});

sheet.getRange(cogsStartRow, 1, cogsRows.length, 5).setValues(cogsRows);

const sgnaTitleRow = cogsStartRow + cogsRows.length + 2;
const sgnaHeaderRow = sgnaTitleRow + 1;
const sgnaStartRow = sgnaHeaderRow + 1;

sheet.getRange(sgnaTitleRow, 1, 1, 5).setValues([
['販管費内訳', '', '', '', '']
]);

sheet.getRange(sgnaHeaderRow, 1, 1, 5).setValues([
['区分', '科目', '月額', '売上比率', '設定理由']
]);

const sgnaRows = state.sgna.map(function(item) {
return
'販管費',
[item.name,
item.monthlyAmount,
item.salesRate,
item.reason
];
});

sheet.getRange(sgnaStartRow, 1, sgnaRows.length, 5).setValues(sgnaRows);

styleTitle_(sheet, 'A1:E1');
styleHeader_(sheet, 'A3:E3');
styleHeader_(sheet, 'A15:E15');
styleHeader_(sheet, 'A16:E16');
styleHeader_(sheet, 'A' + sgnaTitleRow + ':E' + sgnaTitleRow);
styleHeader_(sheet, 'A' + sgnaHeaderRow + ':E' + sgnaHeaderRow);

styleTable_(sheet, 'A3:E13');
styleTable_(sheet, 'A15:E' + (cogsStartRow + cogsRows.length - 1));
styleTable_(sheet, 'A' + sgnaTitleRow + ':E' + (sgnaStartRow + sgnaRows.length - 1));

sheet.getRange('C7:C8').setNumberFormat('#,##0');
sheet.getRange('C9').setNumberFormat('0.0%');

sheet.getRange(cogsStartRow, 3, cogsRows.length, 1).setNumberFormat('#,##0');
sheet.getRange(cogsStartRow, 4, cogsRows.length, 1).setNumberFormat('0.0%');

sheet.getRange(sgnaStartRow, 3, sgnaRows.length, 1).setNumberFormat('#,##0');
sheet.getRange(sgnaStartRow, 4, sgnaRows.length, 1).setNumberFormat('0.0%');

sheet.setColumnWidth(1, 130);
sheet.setColumnWidth(2, 220);
sheet.setColumnWidth(3, 130);
sheet.setColumnWidth(4, 110);
sheet.setColumnWidth(5, 500);

sheet.getRange(1, 1, sheet.getMaxRows(), 5).setVerticalAlignment('middle');
sheet.getRange(1, 1, sheet.getMaxRows(), 5).setWrap(true);
sheet.setFrozenRows(3);

return {
unitPriceCell: "'前提条件'!$C$7",
customerCountCell: "'前提条件'!$C$8",
monthlyGrowthRateCell: "'前提条件'!$C$9",
cogsStartRow: cogsStartRow,
cogsEndRow: cogsStartRow + cogsRows.length - 1,
sgnaStartRow: sgnaStartRow,
sgnaEndRow: sgnaStartRow + sgnaRows.length - 1,
cogsItems: state.cogs,
sgnaItems: state.sgna
};
}

/************************************************************

- 月次シミュレーション
************************************************************/
function buildMonthlySimulationSheet_(ss, meta) {
const sheet = ss.getSheetByName('月次シミュレーション');

sheet.getRange('A1:N1').setValues([
['月次収益シミュレーション', '', '', '', '', '', '', '', '', '', '', '', '', '']
]);

const header = ['項目'];
for (let i = 1; i <= 12; i++) {
header.push(i + 'ヶ月目');
}
header.push('年間合計');

sheet.getRange(3, 1, 1, header.length).setValues([header]);

const rowMap = {};
let row = 4;

rowMap.unitPrice = row;
sheet.getRange(row, 1).setValue('客単価');
row++;

rowMap.customerCount = row;
sheet.getRange(row, 1).setValue('顧客数');
row++;

rowMap.sales = row;
sheet.getRange(row, 1).setValue('売上高');
row++;

rowMap.cogsTotal = row;
sheet.getRange(row, 1).setValue('原価');
row++;

rowMap.cogsItemStart = row;
meta.cogsItems.forEach(function(item) {
sheet.getRange(row, 1).setValue(' ' + item.name);
row++;
});
rowMap.cogsItemEnd = row - 1;

rowMap.grossProfit = row;
sheet.getRange(row, 1).setValue('売上総利益');
row++;

rowMap.sgnaTotal = row;
sheet.getRange(row, 1).setValue('販管費');
row++;

rowMap.sgnaItemStart = row;
meta.sgnaItems.forEach(function(item) {
sheet.getRange(row, 1).setValue(' ' + item.name);
row++;
});
rowMap.sgnaItemEnd = row - 1;

rowMap.operatingProfit = row;
sheet.getRange(row, 1).setValue('営業利益');
row++;

rowMap.operatingMargin = row;
sheet.getRange(row, 1).setValue('営業利益率');
row++;

rowMap.cumulativeProfit = row;
sheet.getRange(row, 1).setValue('営業利益累計');
row++;

const lastRow = row - 1;

for (let col = 2; col <= 13; col++) {
const currentCol = columnToLetter_(col);
const previousCol = columnToLetter_(col - 1);
const monthIndex = col - 1;

sheet.getRange(rowMap.unitPrice, col).setFormula('=' + meta.unitPriceCell);

sheet.getRange(rowMap.customerCount, col).setFormula( monthIndex === 1 ? '=' + meta.customerCountCell : '=' + previousCol + rowMap.customerCount + '*(1+' + meta.monthlyGrowthRateCell + ')' );

sheet.getRange(rowMap.sales, col).setFormula( '=' + currentCol + rowMap.unitPrice + '*' + currentCol + rowMap.customerCount );

for (let i = 0; i < meta.cogsItems.length; i++) { const monthlyRow = rowMap.cogsItemStart + i; const assumptionRow = meta.cogsStartRow + i;

sheet.getRange(monthlyRow, col).setFormula( "='前提条件'!$C$" + assumptionRow + '+' + currentCol + rowMap.sales + "*'前提条件'!$D$" + assumptionRow ); }

sheet.getRange(rowMap.cogsTotal, col).setFormula( '=SUM(' + currentCol + rowMap.cogsItemStart + ':' + currentCol + rowMap.cogsItemEnd + ')' );

sheet.getRange(rowMap.grossProfit, col).setFormula( '=' + currentCol + rowMap.sales + '-' + currentCol + rowMap.cogsTotal );

for (let j = 0; j < meta.sgnaItems.length; j++) { const monthlyRow = rowMap.sgnaItemStart + j; const assumptionRow = meta.sgnaStartRow + j;

sheet.getRange(monthlyRow, col).setFormula( "='前提条件'!$C$" + assumptionRow + '+' + currentCol + rowMap.sales + "*'前提条件'!$D$" + assumptionRow ); }

sheet.getRange(rowMap.sgnaTotal, col).setFormula( '=SUM(' + currentCol + rowMap.sgnaItemStart + ':' + currentCol + rowMap.sgnaItemEnd + ')' );

sheet.getRange(rowMap.operatingProfit, col).setFormula( '=' + currentCol + rowMap.grossProfit + '-' + currentCol + rowMap.sgnaTotal );

sheet.getRange(rowMap.operatingMargin, col).setFormula( '=IFERROR(' + currentCol + rowMap.operatingProfit + '/' + currentCol + rowMap.sales + ',0)' );

sheet.getRange(rowMap.cumulativeProfit, col).setFormula( monthIndex === 1 ? '=' + currentCol + rowMap.operatingProfit : '=' + previousCol + rowMap.cumulativeProfit + '+' + currentCol + rowMap.operatingProfit );


}

const annualCol = 14;
const annualColLetter = 'N';

sheet.getRange(rowMap.unitPrice, annualCol).setFormula('=AVERAGE(B' + rowMap.unitPrice + ':M' + rowMap.unitPrice + ')');
sheet.getRange(rowMap.customerCount, annualCol).setFormula('=SUM(B' + rowMap.customerCount + ':M' + rowMap.customerCount + ')');
sheet.getRange(rowMap.sales, annualCol).setFormula('=SUM(B' + rowMap.sales + ':M' + rowMap.sales + ')');
sheet.getRange(rowMap.cogsTotal, annualCol).setFormula('=SUM(B' + rowMap.cogsTotal + ':M' + rowMap.cogsTotal + ')');

for (let i = rowMap.cogsItemStart; i <= rowMap.cogsItemEnd; i++) {
sheet.getRange(i, annualCol).setFormula('=SUM(B' + i + ':M' + i + ')');
}

sheet.getRange(rowMap.grossProfit, annualCol).setFormula('=SUM(B' + rowMap.grossProfit + ':M' + rowMap.grossProfit + ')');
sheet.getRange(rowMap.sgnaTotal, annualCol).setFormula('=SUM(B' + rowMap.sgnaTotal + ':M' + rowMap.sgnaTotal + ')');

for (let j = rowMap.sgnaItemStart; j <= rowMap.sgnaItemEnd; j++) {
sheet.getRange(j, annualCol).setFormula('=SUM(B' + j + ':M' + j + ')');
}

sheet.getRange(rowMap.operatingProfit, annualCol).setFormula('=SUM(B' + rowMap.operatingProfit + ':M' + rowMap.operatingProfit + ')');
sheet.getRange(rowMap.operatingMargin, annualCol).setFormula('=IFERROR(' + annualColLetter + rowMap.operatingProfit + '/' + annualColLetter + rowMap.sales + ',0)');
sheet.getRange(rowMap.cumulativeProfit, annualCol).setFormula('=M' + rowMap.cumulativeProfit);

styleTitle_(sheet, 'A1:N1');
styleHeader_(sheet, 'A3:N3');
styleTable_(sheet, 'A3:N' + lastRow);

sheet.getRange('A' + rowMap.sales + ':N' + rowMap.sales).setFontWeight('bold');
sheet.getRange('A' + rowMap.cogsTotal + ':N' + rowMap.cogsTotal).setFontWeight('bold');
sheet.getRange('A' + rowMap.grossProfit + ':N' + rowMap.grossProfit).setFontWeight('bold');
sheet.getRange('A' + rowMap.sgnaTotal + ':N' + rowMap.sgnaTotal).setFontWeight('bold');
sheet.getRange('A' + rowMap.operatingProfit + ':N' + rowMap.operatingProfit).setFontWeight('bold');

sheet.getRange(4, 2, lastRow - 3, 13).setNumberFormat('#,##0');
sheet.getRange(rowMap.operatingMargin, 2, 1, 13).setNumberFormat('0.0%');

addNegativeRedFormat_(sheet, 'B' + rowMap.operatingProfit + ':N' + rowMap.operatingProfit);
addNegativeRedFormat_(sheet, 'B' + rowMap.cumulativeProfit + ':N' + rowMap.cumulativeProfit);

sheet.setColumnWidth(1, 210);
for (let col = 2; col <= 14; col++) {
sheet.setColumnWidth(col, 100);
}

sheet.getRange('A1:N' + lastRow).setVerticalAlignment('middle');
sheet.getRange('A1:N' + lastRow).setWrap(true);
sheet.setFrozenRows(3);

return {
salesRow: rowMap.sales,
cogsTotalRow: rowMap.cogsTotal,
grossProfitRow: rowMap.grossProfit,
sgnaTotalRow: rowMap.sgnaTotal,
operatingProfitRow: rowMap.operatingProfit,
operatingMarginRow: rowMap.operatingMargin,
cumulativeProfitRow: rowMap.cumulativeProfit
};
}

/************************************************************

- 3ケース比較
************************************************************/
function buildCaseComparisonSheet_(ss, monthlyMeta) {
const sheet = ss.getSheetByName('3ケース比較');

sheet.getRange('A1:G1').setValues([
['Downside / Base / Upside 比較', '', '', '', '', '', '']
]);

const assumptionRows = [
['ケース', '売上倍率', '原価倍率', '販管費倍率', '考え方', '設定理由', ''],
['Downside', 0.70, 1.10, 1.05, '下振れ', '仮置き:売上未達、原価上振れ、販管費やや増加を想定', ''],
['Base', 1.00, 1.00, 1.00, '通常', '月次シミュレーションの年間合計を使用', ''],
['Upside', 1.50, 0.95, 1.15, '上振れ', '仮置き:売上成長、原価効率改善、成長投資による販管費増加を想定', '']
];

sheet.getRange(3, 1, assumptionRows.length, assumptionRows[0].length).setValues(assumptionRows);

sheet.getRange('A8:G8').setValues([
['ケース', '売上高', '原価', '売上総利益', '販管費', '営業利益', '営業利益率']
]);

for (let row = 9; row <= 11; row++) {
const caseRow = row - 5;

sheet.getRange(row, 1).setFormula('=A' + caseRow); sheet.getRange(row, 2).setFormula("='月次シミュレーション'!$N$" + monthlyMeta.salesRow + 'B' + caseRow); sheet.getRange(row, 3).setFormula("='月次シミュレーション'!$N$" + monthlyMeta.cogsTotalRow + 'B' + caseRow + 'C' + caseRow); sheet.getRange(row, 4).setFormula('=B' + row + '-C' + row); sheet.getRange(row, 5).setFormula("='月次シミュレーション'!$N$" + monthlyMeta.sgnaTotalRow + 'D' + caseRow); sheet.getRange(row, 6).setFormula('=D' + row + '-E' + row); sheet.getRange(row, 7).setFormula('=IFERROR(F' + row + '/B' + row + ',0)');


}

styleTitle_(sheet, 'A1:G1');
styleHeader_(sheet, 'A3:G3');
styleHeader_(sheet, 'A8:G8');
styleTable_(sheet, 'A3:G6');
styleTable_(sheet, 'A8:G11');

sheet.getRange('B4:D6').setNumberFormat('0.0');
sheet.getRange('B9:F11').setNumberFormat('#,##0');
sheet.getRange('G9:G11').setNumberFormat('0.0%');

addNegativeRedFormat_(sheet, 'F9:F11');

sheet.setColumnWidth(1, 120);
sheet.setColumnWidth(2, 130);
sheet.setColumnWidth(3, 130);
sheet.setColumnWidth(4, 130);
sheet.setColumnWidth(5, 130);
sheet.setColumnWidth(6, 130);
sheet.setColumnWidth(7, 420);

sheet.getRange('A1:G11').setVerticalAlignment('middle');
sheet.getRange('A1:G11').setWrap(true);
sheet.setFrozenRows(3);
}

/************************************************************

- 5年シミュレーション
************************************************************/
function buildFiveYearSimulationSheet_(ss, monthlyMeta) {
const sheet = ss.getSheetByName('5年シミュレーション');

sheet.getRange('A1:G1').setValues([
['5年シミュレーション', '', '', '', '', '', '']
]);

sheet.getRange('A3:G3').setValues([
['項目', '1年目', '2年目', '3年目', '4年目', '5年目', '5年合計']
]);

const rows = [
['売上高'],
['原価'],
['売上総利益'],
['販管費'],
['営業利益'],
['営業利益率'],
['営業利益累計']
];

sheet.getRange(4, 1, rows.length, 1).setValues(rows);

sheet.getRange('A13:C13').setValues([
['補足項目', '値', '設定理由']
]);

sheet.getRange('A14:C14').setValues([
['年次売上成長率', 0.30, '仮置き:初期事業の年次成長率として30%を設定']
]);

sheet.getRange('A15:C15').setValues([
['販管費増加率', 0.15, '仮置き:売上成長に伴い販管費も毎年15%増加すると想定']
]);

sheet.getRange('A17:C17').setValues([
['成長要因', '内容', '確認ポイント']
]);

sheet.getRange('A18:C19').setValues([
['外部環境要因', '市場成長、トレンド変化、顧客ニーズ拡大、競合状況など', 'AIが仮置きした成長前提が過大でないか確認'],
['内部環境要因', '商品改善、広告強化、SNS発信、紹介制度、営業活動、リピート施策など', '自社努力で実行できる施策に落ちているか確認']
]);

const salesRow = 4;
const cogsRow = 5;
const gpRow = 6;
const sgnaRow = 7;
const opRow = 8;
const marginRow = 9;
const cumulativeRow = 10;

for (let col = 2; col <= 6; col++) {
const currentCol = columnToLetter_(col);
const previousCol = columnToLetter_(col - 1);
const yearIndex = col - 1;

if (yearIndex === 1) { sheet.getRange(salesRow, col).setFormula("='月次シミュレーション'!$N$" + monthlyMeta.salesRow); sheet.getRange(cogsRow, col).setFormula("='月次シミュレーション'!$N$" + monthlyMeta.cogsTotalRow); sheet.getRange(gpRow, col).setFormula('=' + currentCol + salesRow + '-' + currentCol + cogsRow); sheet.getRange(sgnaRow, col).setFormula("='月次シミュレーション'!$N$" + monthlyMeta.sgnaTotalRow); sheet.getRange(opRow, col).setFormula('=' + currentCol + gpRow + '-' + currentCol + sgnaRow); sheet.getRange(marginRow, col).setFormula('=IFERROR(' + currentCol + opRow + '/' + currentCol + salesRow + ',0)'); sheet.getRange(cumulativeRow, col).setFormula('=' + currentCol + opRow); } else { sheet.getRange(salesRow, col).setFormula('=' + previousCol + salesRow + '(1+$B$14)'); sheet.getRange(cogsRow, col).setFormula('=' + currentCol + salesRow + '($B$5/$B$4)'); sheet.getRange(gpRow, col).setFormula('=' + currentCol + salesRow + '-' + currentCol + cogsRow); sheet.getRange(sgnaRow, col).setFormula('=' + previousCol + sgnaRow + '*(1+$B$15)'); sheet.getRange(opRow, col).setFormula('=' + currentCol + gpRow + '-' + currentCol + sgnaRow); sheet.getRange(marginRow, col).setFormula('=IFERROR(' + currentCol + opRow + '/' + currentCol + salesRow + ',0)'); sheet.getRange(cumulativeRow, col).setFormula('=' + previousCol + cumulativeRow + '+' + currentCol + opRow); }


}

sheet.getRange('G4').setFormula('=SUM(B4:F4)');
sheet.getRange('G5').setFormula('=SUM(B5:F5)');
sheet.getRange('G6').setFormula('=SUM(B6:F6)');
sheet.getRange('G7').setFormula('=SUM(B7:F7)');
sheet.getRange('G8').setFormula('=SUM(B8:F8)');
sheet.getRange('G9').setFormula('=IFERROR(G8/G4,0)');
sheet.getRange('G10').setFormula('=F10');

styleTitle_(sheet, 'A1:G1');
styleHeader_(sheet, 'A3:G3');
styleHeader_(sheet, 'A13:C13');
styleHeader_(sheet, 'A17:C17');
styleTable_(sheet, 'A3:G10');
styleTable_(sheet, 'A13:C15');
styleTable_(sheet, 'A17:C19');

sheet.getRange('B4:G8').setNumberFormat('#,##0');
sheet.getRange('B9:G9').setNumberFormat('0.0%');
sheet.getRange('B10:G10').setNumberFormat('#,##0');
sheet.getRange('B14:B15').setNumberFormat('0.0%');

addNegativeRedFormat_(sheet, 'B8:G8');
addNegativeRedFormat_(sheet, 'B10:G10');

sheet.setColumnWidth(1, 150);
for (let col = 2; col <= 7; col++) {
sheet.setColumnWidth(col, 120);
}
sheet.setColumnWidth(2, 160);
sheet.setColumnWidth(3, 420);

sheet.getRange('A1:G19').setVerticalAlignment('middle');
sheet.getRange('A1:G19').setWrap(true);
sheet.setFrozenRows(3);
}

/************************************************************

- 確認ポイント
************************************************************/
function buildCheckPointSheet_(ss) {
const sheet = ss.getSheetByName('確認ポイント');

sheet.getRange('A1:E1').setValues([
['確認ポイント', '', '', '', '']
]);

const rows = [
['カテゴリ', '確認ポイント', '見るべき数値', '判断の目安', 'メモ'],
['売上', '客単価は顧客が納得する水準か', '販売価格/客単価', '競合・代替手段と比較して高すぎないか', ''],
['売上', '月間顧客数は現実的に獲得できるか', '月間顧客数', '販売方法と集客導線から説明できるか', ''],
['原価', '原価科目は提供価値に直結しているか', '原価内訳', '外注費・材料費・決済手数料などが漏れていないか', ''],
['原価', '売上総利益は十分に残るか', '売上総利益', '原価を差し引いた後に販管費をまかなえるか', ''],
['販管費', '人件費・広告宣伝費を過小に見積もっていないか', '販管費内訳', '実際に集客・運営するための費用が入っているか', ''],
['利益', '営業利益が黒字化するタイミングはいつか', '営業利益/営業利益累計', '赤字が続く場合は価格・原価・販管費を見直す', ''],
['利益率', '営業利益率は健全か', '営業利益率', 'まずは10%以上を目安に改善余地を確認', ''],
['ケース比較', 'Downsideでも継続できるか', 'Downside営業利益', '下振れ時の資金ショート可能性を確認', ''],
['成長性', '5年後の売上・利益が魅力的か', '5年シミュレーション', '労力に見合う事業規模になるか', ''],
['検証', '仮置き前提を実データに置き換えたか', '設定理由', '「仮置き」が残っている項目を優先検証', '']
];

sheet.getRange(3, 1, rows.length, rows[0].length).setValues(rows);

styleTitle_(sheet, 'A1:E1');
styleHeader_(sheet, 'A3:E3');
styleTable_(sheet, 'A3:E14');

sheet.getRange('E4:E14').setBackground('#fff2cc');

sheet.setColumnWidth(1, 120);
sheet.setColumnWidth(2, 360);
sheet.setColumnWidth(3, 220);
sheet.setColumnWidth(4, 400);
sheet.setColumnWidth(5, 300);

sheet.getRange('A1:E14').setVerticalAlignment('middle');
sheet.getRange('A1:E14').setWrap(true);
sheet.setFrozenRows(3);
}

/************************************************************

- AI出力パース
************************************************************/
function parseAiOutputSheet_(ss) {
const sheet = ss.getSheetByName('AI出力貼付');
const values = sheet.getDataRange().getDisplayValues();

const lines = buildLinesFromSheetValues_(values);
const sections = splitAiOutputSections_(lines);

const defaultState = getDefaultInputState_();

const basic = {};
const cogs = [];
const sgna = [];

const basicRows = sections['基本情報'] || sections['表1'] || [];
basicRows.forEach(function(row) {
const key = cleanText_(row[0]);
const value = cleanText_(row[1]);
if (key && key !== '項目') {
if (key.indexOf('事業アイデア') !== -1) basic['事業アイデア'] = value;
if (key.indexOf('商品') !== -1 || key.indexOf('サービス内容') !== -1) basic['商品/サービス内容'] = value;
if (key.indexOf('想定顧客') !== -1) basic['想定顧客'] = value;
if (key.indexOf('販売方法') !== -1) basic['販売方法'] = value;
if (key.indexOf('その他前提') !== -1) basic['その他前提'] = value;
}
});

const salesRows = sections['売上前提'] || sections['表2'] || [];
salesRows.forEach(function(row) {
const key = cleanText_(row[0]);
const value = cleanText_(row[1]);

if (!key || key === '項目') return;

if (key.indexOf('売上タイプ') !== -1) basic['売上タイプ'] = value; if (key.indexOf('売上分解式') !== -1) basic['売上分解式'] = value; if (key.indexOf('販売価格') !== -1 || key.indexOf('客単価') !== -1) basic['販売価格/客単価'] = value; if (key.indexOf('月間顧客数') !== -1 || key.indexOf('顧客数') !== -1) basic['月間顧客数'] = value; if (key.indexOf('月次顧客成長率') !== -1 || key.indexOf('月次成長率') !== -1) basic['月次顧客成長率'] = value;


});

const cogsRows = sections['原価内訳'] || sections['表3'] || [];
cogsRows.forEach(function(row) {
const name = cleanText_(row[0]);
if (!name || name === '科目') return;

cogs.push([ name, parseNumberLike(row[1], 0), parsePercentLike(row[2], 0), cleanText(row[3]) || '仮置き:AI出力より反映。必要に応じて確認してください', cleanText(row[4]) ]);


});

const sgnaRows = sections['販管費内訳'] || sections['表4'] || [];
sgnaRows.forEach(function(row) {
const name = cleanText_(row[0]);
if (!name || name === '科目') return;

sgna.push([ name, parseNumberLike(row[1], 0), parsePercentLike(row[2], 0), cleanText(row[3]) || '仮置き:AI出力より反映。必要に応じて確認してください', cleanText(row[4]) ]);


});

const isEmpty =
Object.keys(basic).length === 0 &&
cogs.length === 0 &&
sgna.length === 0;

if (isEmpty) {
return {
isEmpty: true,
basic: defaultState.basic,
cogs: defaultState.cogs,
sgna: defaultState.sgna
};
}

return {
isEmpty: false,
basic: Object.assign({}, defaultState.basic, basic),
cogs: cogs.length > 0 ? cogs : defaultState.cogs,
sgna: sgna.length > 0 ? sgna : defaultState.sgna
};
}

function buildLinesFromSheetValues_(values) {
const lines = [];

values.forEach(function(row) {
const nonEmpty = row.filter(function(cell) {
return !isBlank_(cell);
});

if (nonEmpty.length === 0) { lines.push(''); return; }

if (nonEmpty.length === 1 && String(nonEmpty[0]).indexOf('\n') !== -1) { const splitLines = String(nonEmpty[0]).split(/\r?\n/); splitLines.forEach(function(line) { lines.push(line); }); } else { lines.push(row.join('\t')); }


});

return lines;
}

function splitAiOutputSections_(lines) {
const sections = {};
let currentKey = '';

lines.forEach(function(line) {
const text = cleanText_(line);

if (!text) { return; }

if (isSectionTitle(text)) { currentKey = getSectionKey(text); if (!sections[currentKey]) { sections[currentKey] = []; } return; }

if (!currentKey) { return; }

const row = String(line).split('\t').map(function(cell) { return cleanText_(cell); });

const hasData = row.some(function(cell) { return !isBlank_(cell); });

if (hasData) { sections[currentKey].push(row); }


});

return sections;
}

function isSectionTitle_(text) {
return (
text.indexOf('表1') !== -1 ||
text.indexOf('表2') !== -1 ||
text.indexOf('表3') !== -1 ||
text.indexOf('表4') !== -1 ||
text.indexOf('基本情報') !== -1 ||
text.indexOf('売上前提') !== -1 ||
text.indexOf('原価内訳') !== -1 ||
text.indexOf('販管費内訳') !== -1
);
}

function getSectionKey_(text) {
if (text.indexOf('基本情報') !== -1 || text.indexOf('表1') !== -1) return '基本情報';
if (text.indexOf('売上前提') !== -1 || text.indexOf('表2') !== -1) return '売上前提';
if (text.indexOf('原価内訳') !== -1 || text.indexOf('表3') !== -1) return '原価内訳';
if (text.indexOf('販管費内訳') !== -1 || text.indexOf('表4') !== -1) return '販管費内訳';
return text;
}

/************************************************************

- 入力状態取得
************************************************************/
function getExistingInputState_(ss) {
const sheet = ss.getSheetByName('入力フォーム');

if (!sheet) {
return getDefaultInputState_();
}

const basic = {};
const basicValues = sheet.getRange('A4:B11').getValues();

basicValues.forEach(function(row) {
const key = row[0];
const value = row[1];

if (!isBlank_(key)) { basic[String(key).trim()] = value; }


});

const cogs = readCostRowsFromSheet_(sheet, 15, 10);
const sgna = readCostRowsFromSheet_(sheet, 29, 12);

const defaultState = getDefaultInputState_();

return {
basic: Object.keys(basic).length > 0 ? Object.assign({}, defaultState.basic, basic) : defaultState.basic,
cogs: cogs.length > 0 ? cogs : defaultState.cogs,
sgna: sgna.length > 0 ? sgna : defaultState.sgna
};
}

function getInputStateFromForm_(ss) {
const sheet = ss.getSheetByName('入力フォーム');
const defaultState = getDefaultInputState_();

if (!sheet) {
return defaultState;
}

const basic = {};
const basicValues = sheet.getRange('A4:B11').getValues();

basicValues.forEach(function(row) {
const key = row[0];
const value = row[1];

if (!isBlank_(key)) { basic[String(key).trim()] = value; }


});

const cogs = readCostItemsForCalculation_(sheet, 15, 10, defaultState.cogs);
const sgna = readCostItemsForCalculation_(sheet, 29, 12, defaultState.sgna);

return {
basic: Object.assign({}, defaultState.basic, basic),
cogs: cogs,
sgna: sgna
};
}

function readCostRowsFromSheet_(sheet, startRow, rowCount) {
const values = sheet.getRange(startRow, 1, rowCount, 5).getValues();
const rows = [];

values.forEach(function(row) {
const name = row[0];

if (!isBlank_(name)) { rows.push([ row[0], row[1], row[2], row[3], row[4] ]); }


});

return rows;
}

function readCostItemsForCalculation_(sheet, startRow, rowCount, defaultRows) {
const rawRows = readCostRowsFromSheet_(sheet, startRow, rowCount);
const sourceRows = rawRows.length > 0 ? rawRows : defaultRows;

return sourceRows
.filter(function(row) {
return !isBlank_(row[0]);
})
.map(function(row) {
return {
name: String(row[0]).trim(),
monthlyAmount: parseNumberLike_(row[1], 0),
salesRate: parsePercentLike_(row[2], 0),
reason: isBlank_(row[3]) ? '仮置き:入力フォームで設定してください' : String(row[3]),
memo: isBlank_(row[4]) ? '' : String(row[4])
};
});
}

function normalizeCostRows_(rows, rowCount) {
const result = [];

rows.forEach(function(row) {
result.push([
row[0] || '',
row[1] === undefined ? '' : row[1],
row[2] === undefined ? '' : row[2],
row[3] || '',
row[4] || ''
]);
});

while (result.length < rowCount) {
result.push(['', '', '', '', '']);
}

return result.slice(0, rowCount);
}

function getDefaultInputState_() {
return {
basic: {
'事業アイデア': '',
'商品/サービス内容': '',
'想定顧客': '',
'販売価格/客単価': '',
'月間顧客数': '',
'月次顧客成長率': '',
'売上タイプ': '',
'売上分解式': '',
'販売方法': '',
'その他前提': ''
},
cogs: [
['外注費', 50000, 0, '仮置き:サービス提供に必要な外部協力費として月5万円を設定', '制作・運営・実務支援など'],
['材料費/仕入原価', 0, 0.10, '仮置き:売上に連動する原価として売上高の10%を設定', '物販・制作物がある場合に使用'],
['決済手数料', 0, 0.04, '仮置き:決済手数料として売上高の4%を設定', 'カード決済・プラットフォーム手数料など'],
['旅費交通費', 10000, 0, '仮置き:サービス提供に直接必要な移動費として月1万円を設定', '訪問・同行・現地対応など'],
['その他原価', 0, 0, '仮置き:必要に応じて入力', '']
],
sgna: [
['人件費', 100000, 0, '仮置き:運営者・サポート人員の人件費として月10万円を設定', '自分の稼働を金額換算してもよい'],
['広告宣伝費', 50000, 0.05, '仮置き:初期集客費として月5万円+売上高の5%を設定', 'SNS広告、LP、チラシなど'],
['通信費・ツール費', 20000, 0, '仮置き:ツール利用料として月2万円を設定', 'Notion、ChatGPT、予約管理、LINE公式など'],
['旅費交通費', 10000, 0, '仮置き:営業・打ち合わせ用の交通費として月1万円を設定', '営業活動に関する移動費'],
['地代家賃', 0, 0, '仮置き:自宅・オンライン中心の場合は0円', 'オフィス・店舗がある場合に入力'],
['その他販管費', 20000, 0, '仮置き:雑費・備品・予備費として月2万円を設定', '']
]
};
}

/************************************************************

- シート作成・初期化
************************************************************/
function resetOrCreateSheet_(ss, sheetName) {
let sheet = ss.getSheetByName(sheetName);

if (!sheet) {
sheet = ss.insertSheet(sheetName);
}

const maxRows = sheet.getMaxRows();
const maxColumns = sheet.getMaxColumns();

sheet.getRange(1, 1, maxRows, maxColumns).breakApart();
sheet.clear();
sheet.clearConditionalFormatRules();
sheet.setFrozenRows(0);
sheet.setFrozenColumns(0);

return sheet;
}

function createAiPasteSheetOnly_(ss) {
let sheet = ss.getSheetByName('AI出力貼付');

if (!sheet) {
sheet = ss.insertSheet('AI出力貼付');
sheet.getRange('A1:F1').setValues([
['AI出力貼付シート', '', '', '', '', '']
]);
styleTitle_(sheet, 'A1:F1');
}

return sheet;
}

/************************************************************

- スタイル
************************************************************/
function styleTitle_(sheet, rangeA1) {
sheet.getRange(rangeA1)
.setBackground('#d9eaf7')
.setFontWeight('bold')
.setFontSize(14)
.setHorizontalAlignment('center')
.setVerticalAlignment('middle')
.setBorder(true, true, true, true, true, true);
}

function styleHeader_(sheet, rangeA1) {
sheet.getRange(rangeA1)
.setBackground('#d9eaf7')
.setFontWeight('bold')
.setHorizontalAlignment('center')
.setVerticalAlignment('middle')
.setBorder(true, true, true, true, true, true);
}

function styleInput_(sheet, rangeA1) {
sheet.getRange(rangeA1)
.setBackground('#fff2cc')
.setVerticalAlignment('middle')
.setWrap(true);
}

function styleTable_(sheet, rangeA1) {
sheet.getRange(rangeA1)
.setBorder(true, true, true, true, true, true)
.setVerticalAlignment('middle')
.setWrap(true);
}

function addNegativeRedFormat_(sheet, rangeA1) {
const range = sheet.getRange(rangeA1);

const rule = SpreadsheetApp.newConditionalFormatRule()
.whenNumberLessThan(0)
.setFontColor('#cc0000')
.setRanges([range])
.build();

const rules = sheet.getConditionalFormatRules();
rules.push(rule);
sheet.setConditionalFormatRules(rules);
}

/************************************************************

- ユーティリティ
************************************************************/
function isBlank_(value) {
return value === null || value === undefined || String(value).trim() === '';
}

function valueOrBlank_(value) {
return value === null || value === undefined ? '' : value;
}

function cleanText_(value) {
if (value === null || value === undefined) {
return '';
}

return String(value)
.replace(/\r/g, '')
.replace(/^#+\s*/, '')
.trim();
}

function parseNumberLike_(value, defaultValue) {
if (value === null || value === undefined || String(value).trim() === '') {
return defaultValue;
}

if (typeof value === 'number') {
return isNaN(value) ? defaultValue : value;
}

const cleaned = String(value)
.replace(/[0-9]/g, function(s) {
return String.fromCharCode(s.charCodeAt(0) - 0xFEE0);
})
.replace(/,/g, '')
.replace(/円/g, '')
.replace(/人/g, '')
.replace(/名/g, '')
.replace(/件/g, '')
.replace(/社/g, '')
.replace(/\s/g, '');

const match = cleaned.match(/-?\d+(\.\d+)?/);

if (!match) {
return defaultValue;
}

const number = Number(match[0]);

return isNaN(number) ? defaultValue : number;
}

function parsePercentLike_(value, defaultValue) {
if (value === null || value === undefined || String(value).trim() === '') {
return defaultValue;
}

if (typeof value === 'number') {
if (isNaN(value)) {
return defaultValue;
}

if (value > 1) { return value / 100; }

return value;


}

const text = String(value)
.replace(/[0-9]/g, function(s) {
return String.fromCharCode(s.charCodeAt(0) - 0xFEE0);
})
.replace(/\s/g, '');

const hasPercent = text.indexOf('%') !== -1 || text.indexOf('%') !== -1;

const cleaned = text
.replace(/%/g, '')
.replace(/%/g, '');

const match = cleaned.match(/-?\d+(\.\d+)?/);

if (!match) {
return defaultValue;
}

const number = Number(match[0]);

if (isNaN(number)) {
return defaultValue;
}

if (hasPercent) {
return number / 100;
}

if (number > 1) {
return number / 100;
}

return number;
}

function columnToLetter_(column) {
let temp;
let letter = '';

while (column > 0) {
temp = (column - 1) % 26;
letter = String.fromCharCode(temp + 65) + letter;
column = Math.floor((column - temp - 1) / 26);
}

return letter;
}

トップ   編集 凍結 差分 履歴 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   最終更新のRSS
Last-modified: 2026-06-27 (土) 01:19:58