0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

NetSuiteの機能をChatGPTを使って操作したい

0
Last updated at Posted at 2026-03-24

はじめに

日々の業務でこんな課題はありませんか?

  • NetSuiteのデータを毎回ダウンロードして分析している
  • レポート作成・要約・問い合わせ対応が手作業で時間がかかる
  • 部門間でのデータ共有に時間がかかり、意思決定が遅れる
  • 承認フローが終わったあとの確認・通知もすべて人手に頼っている

こうした課題は、NetSuite × ChatGPT の連携で解決できます。

「NetSuiteで承認された経費レポートや発注書の内容を、都度ChatGPTに貼り付けて分析させるのが面倒……」
「承認フローが終わったら、自動でAI分析して担当者に通知してほしい!」

NetSuiteとChatGPTを別々に使っていると、どうしても手作業でのデータ転記が発生し、業務効率が落ちてしまいます。

この記事では、NetSuiteのレコードが承認されたことをトリガーに、ChatGPT(OpenAI API)がその内容を自動分析し、結果をSlackに通知する仕組みを、SuiteScriptを使って実装する方法を解説します。

プログラミングの基礎知識があれば、NetSuiteの管理者・開発者の方はすぐに試していただける内容です。


この記事でできること

NetSuiteの承認イベントをトリガーとし、以下のフローを自動化します。

NetSuite(経費レポート承認)
    ↓
SuiteScript(User Event Script)がレコード情報を取得
    ↓
OpenAI API(ChatGPT)が内容を分析・分類
    ↓
分析結果に応じて処理を分岐
    ↓
Slack(指定チャンネルに通知)

NetSuite × ChatGPT でできること

# ユースケース 概要
1 売上データの自動要約 期間別売上データをChatGPTに渡し、経営層向けコメントを自動生成
2 請求・入金データの異常検知 入金遅延や金額不一致をAIが検知し、担当者へアラート
3 顧客問い合わせの自動回答生成 受信した問い合わせ内容を分析し、回答案をドラフト
4 会計データのレポート文章化 決算データや予実差異をChatGPTが読み解き、文章レポートとして出力
5 承認フローの自動通知 承認済みレコードをAI分析し、リスク度に応じて担当チャンネルへ通知

活用シーン例(承認フロー)

  • 経費レポート承認後:金額・費目・備考をChatGPTが分析し、勘定科目の確認や異常値の検知をSlackに通知
  • 発注書(PO)承認後:取引先・品目・金額を分析し、優先度・リスク度を判定して担当バイヤーへアラート
  • 請求書(Invoice)作成時:顧客セグメントや支払い条件を分析し、ハイリスク案件を経営層チャンネルへエスカレーション

前提条件

項目 内容
NetSuite SuiteScriptを実行できる権限(Administrator または Custom Role with Script 権限)
OpenAI APIキー(有料プラン、こちらから取得)
Slack Incoming Webhook URL(送信先ワークスペースで設定)

⚠️ OpenAI APIはトークン数に応じた従量課金です。本番運用前に料金プランをご確認ください。


アーキテクチャ概要

┌──────────────────────────────────────────────────┐
│                    NetSuite                       │
│                                                  │
│  [Expense Report]  ──afterSubmit──▶  [SuiteScript] │
│    status: "approved"               User Event Script│
│                                        │           │
└────────────────────────────────────────┼───────────┘
                                         │ HTTPS POST
                            ┌────────────▼──────────────┐
                            │   OpenAI API (ChatGPT)    │
                            │   モデル: gpt-4o           │
                            │   分析・分類・要約          │
                            └────────────┬──────────────┘
                                         │ 分析結果(JSON)
                            ┌────────────▼──────────────┐
                            │   条件分岐ロジック          │
                            │   (費目 / 金額 / リスク)  │
                            └──┬──────────────────────┬─┘
                               │                      │
                   ┌───────────▼───┐      ┌───────────▼───┐
                   │ Slack #経費-通常 │      │ Slack #経費-要確認│
                   └───────────────┘      └───────────────┘

実装手順

ステップ1:Slack Incoming Webhook URLの取得

  1. Slack API にアクセスし、「Create New App」をクリック
  2. 「Incoming Webhooks」を有効化
  3. 通知先チャンネルを選択し、Webhook URLをコピー(https://hooks.slack.com/services/XXX/YYY/ZZZ

取得したURLは後ほどSuiteScript内に設定します。


ステップ2:NetSuiteにシークレット情報を登録

APIキーやWebhook URLをスクリプト内にハードコードするのはセキュリティリスクがあります。
NetSuiteの Setup > Company > Secret Store を使用してシークレットを管理しましょう。

  1. NetSuite管理画面から Setup > Company > Secret Store へ移動
  2. OPENAI_API_KEY という名前でOpenAIのAPIキーを登録
  3. SLACK_WEBHOOK_URL という名前でSlack Webhook URLを登録

Secret Storeはネストしたアクセスが可能で、スクリプト内から安全に参照できます。


ステップ3:SuiteScriptの作成

以下のスクリプトを customscript_expense_ai_notify.js として作成し、NetSuiteのFile Cabinetにアップロードします。

/**
 * @NApiVersion 2.1
 * @NScriptType UserEventScript
 * @NModuleScope SameAccount
 *
 * 経費レポート承認後にChatGPTで分析し、Slackへ通知するスクリプト
 * Trigger: afterSubmit (Expense Report)
 */

define(['N/https', 'N/runtime', 'N/record', 'N/log', 'N/credential'], 
  (https, runtime, record, log, credential) => {

  /**
   * afterSubmit: レコード保存後に実行
   */
  const afterSubmit = (context) => {
    // 承認(Approved)時のみ実行
    if (context.type !== context.UserEventType.EDIT) return;

    const newRecord = context.newRecord;
    const status = newRecord.getValue({ fieldId: 'approvalstatus' });

    // approvalstatus: 2 = Approved
    if (status !== '2') return;

    try {
      // ---- レコード情報の取得 ----
      const expenseData = extractExpenseData(newRecord);
      
      // ---- ChatGPT APIで分析 ----
      const analysisResult = analyzeWithChatGPT(expenseData);
      
      // ---- 分岐してSlack通知 ----
      notifySlack(expenseData, analysisResult);

    } catch (e) {
      log.error({ title: 'AI通知エラー', details: e.message });
    }
  };

  /**
   * 経費レポートから必要情報を抽出
   */
  const extractExpenseData = (rec) => {
    const employeeName = rec.getText({ fieldId: 'entity' });
    const department   = rec.getText({ fieldId: 'department' });
    const totalAmount  = rec.getValue({ fieldId: 'total' });
    const currency     = rec.getText({ fieldId: 'currency' });
    const memo         = rec.getValue({ fieldId: 'memo' }) || '(メモなし)';

    // 経費明細行を取得
    const lineCount = rec.getLineCount({ sublistId: 'expense' });
    const lines = [];
    for (let i = 0; i < lineCount; i++) {
      lines.push({
        category : rec.getSublistText({ sublistId: 'expense', fieldId: 'category', line: i }),
        amount   : rec.getSublistValue({ sublistId: 'expense', fieldId: 'amount',   line: i }),
        memo     : rec.getSublistValue({ sublistId: 'expense', fieldId: 'memo',     line: i }) || '',
      });
    }

    return {
      id           : rec.id,
      employeeName,
      department,
      totalAmount,
      currency,
      memo,
      lines,
    };
  };

  /**
   * OpenAI API(ChatGPT)を呼び出して経費内容を分析
   * @returns {{ category: string, riskLevel: string, summary: string }}
   */
  const analyzeWithChatGPT = (data) => {
    const openaiKey = credential.getValueByType({
      credentialType: credential.Type.OAUTH2_CLIENT_CREDENTIALS,
      credentialId  : 'OPENAI_API_KEY',
    });

    const linesText = data.lines.map((l, i) =>
      `${i + 1}. 費目: ${l.category} / 金額: ${l.amount} ${data.currency} / 摘要: ${l.memo}`
    ).join('\n');

    const prompt = `
あなたはNetSuiteの経費管理の専門家AIです。
以下の経費レポートを分析し、必ずJSON形式のみで回答してください。

【経費レポート情報】
- 申請者    : ${data.employeeName}
- 部門      : ${data.department}
- 合計金額  : ${data.totalAmount} ${data.currency}
- 備考      : ${data.memo}

【明細】
${linesText}

以下のJSONスキーマで回答してください:
{
  "category"  : "交通費 | 交際費 | 消耗品費 | 通信費 | その他",
  "riskLevel" : "LOW | MEDIUM | HIGH",
  "summary"   : "50文字以内の日本語要約",
  "reason"    : "リスクレベルの判定理由(100文字以内)"
}

riskLevelの基準:
- HIGH  : 10万円超 または 費目と金額の不整合あり
- MEDIUM: 3万円以上10万円以下
- LOW   : 3万円未満かつ内容に問題なし
`;

    const response = https.post({
      url    : 'https://api.openai.com/v1/chat/completions',
      headers: {
        'Content-Type' : 'application/json',
        'Authorization': `Bearer ${openaiKey}`,
      },
      body: JSON.stringify({
        model      : 'gpt-4o',
        max_tokens : 512,
        temperature: 0.2,
        messages   : [{ role: 'user', content: prompt }],
      }),
    });

    const parsed  = JSON.parse(response.body);
    const content = parsed.choices[0].message.content;

    // JSON部分のみを抽出してパース
    const jsonMatch = content.match(/\{[\s\S]*\}/);
    if (!jsonMatch) throw new Error('ChatGPTのレスポンスにJSONが含まれていません: ' + content);

    return JSON.parse(jsonMatch[0]);
  };

  /**
   * 分析結果に応じてSlackチャンネルへ通知
   */
  const notifySlack = (data, analysis) => {
    const webhookUrl = credential.getValueByType({
      credentialType: credential.Type.OAUTH2_CLIENT_CREDENTIALS,
      credentialId  : 'SLACK_WEBHOOK_URL',
    });

    // リスクレベルで通知チャンネルと絵文字を切り替え
    const config = {
      HIGH  : { emoji: '🚨', label: '要確認', color: '#FF0000' },
      MEDIUM: { emoji: '⚠️', label: '注意',   color: '#FFA500' },
      LOW   : { emoji: '', label: '通常',   color: '#36A64F' },
    }[analysis.riskLevel] || { emoji: '📋', label: '不明', color: '#808080' };

    const nsUrl = `https://${runtime.accountId}.app.netsuite.com/app/accounting/expense/expensereport.nl?id=${data.id}`;

    const payload = {
      attachments: [{
        color : config.color,
        blocks: [
          {
            type: 'header',
            text: {
              type: 'plain_text',
              text: `${config.emoji} 経費レポート承認通知 [${config.label}]`,
            },
          },
          {
            type  : 'section',
            fields: [
              { type: 'mrkdwn', text: `*申請者*\n${data.employeeName}` },
              { type: 'mrkdwn', text: `*部門*\n${data.department}` },
              { type: 'mrkdwn', text: `*合計金額*\n${Number(data.totalAmount).toLocaleString()} ${data.currency}` },
              { type: 'mrkdwn', text: `*費目分類*\n${analysis.category}` },
            ],
          },
          {
            type: 'section',
            text: {
              type: 'mrkdwn',
              text: `*AIサマリー*\n${analysis.summary}\n\n*判定理由*\n${analysis.reason}`,
            },
          },
          {
            type    : 'actions',
            elements: [{
              type     : 'button',
              text     : { type: 'plain_text', text: 'NetSuiteで確認' },
              url      : nsUrl,
              style    : analysis.riskLevel === 'HIGH' ? 'danger' : 'primary',
            }],
          },
        ],
      }],
    };

    https.post({
      url    : webhookUrl,
      headers: { 'Content-Type': 'application/json' },
      body   : JSON.stringify(payload),
    });

    log.audit({
      title  : 'Slack通知完了',
      details: `ExpenseReport ID: ${data.id} / Risk: ${analysis.riskLevel}`,
    });
  };

  return { afterSubmit };
});

ステップ4:Script Deploymentの設定

  1. NetSuite管理画面から Customization > Scripting > Scripts > New へ移動
  2. 先ほどアップロードしたJSファイルを選択し、スクリプトタイプ User Event を確認
  3. Deploy Script をクリック
  4. デプロイ設定を以下のように構成
項目 設定値
Record Type Expense Report
Event Type After Submit
Status Released
Execute as Role Administrator(または適切なロール)
  1. 「Save」をクリックして完了

ステップ5:動作確認

  1. NetSuiteで任意の経費レポートを作成・承認(Approve)
  2. Script Execution Logを確認(Customization > Scripting > Script Execution Log
  3. 指定したSlackチャンネルに通知が届くことを確認

正常時のSlack通知イメージ:

✅ 経費レポート承認通知 [通常]
────────────────────────────
申請者        | 部門
山田 太郎     | 営業部

合計金額      | 費目分類
¥18,500       | 交通費

AIサマリー
東京-大阪間の出張交通費。新幹線代として妥当な金額。

判定理由
3万円未満かつ費目と金額に整合性あり

[NetSuiteで確認]

カスタマイズのポイント

プロンプトの調整

ChatGPTへの指示(プロンプト)を変更することで、分析の観点を自由にカスタムできます。

① 勘定科目の推奨を含める場合

const prompt = `
...
以下のJSONスキーマで回答してください:
{
  "category"          : "費目分類",
  "riskLevel"         : "LOW | MEDIUM | HIGH",
  "suggestedGLAccount": "推奨勘定科目コード",
  "summary"           : "要約",
  "reason"            : "判定理由"
}
`;

② 売上データを経営層向けに要約する場合

const prompt = `
以下はNetSuiteから取得した売上データです。
重要なポイントを3点にまとめ、経営層向けの簡潔なコメントを日本語で作成してください。

【売上データ】
- 対象期間    : ${period}
- 合計売上    : ${totalRevenue} 円
- 前期比      : ${growthRate} %
- 上位顧客    : ${topCustomers}

出力形式:
{
  "highlights": ["ポイント1", "ポイント2", "ポイント3"],
  "executiveSummary": "経営層向けコメント(150文字以内)"
}
`;

③ 入金遅延の検知に特化する場合

const prompt = `
以下の請求書データから入金遅延リスクを分析してください。

- 顧客名      : ${customerName}
- 請求金額    : ${amount} 円
- 支払期日    : ${dueDate}
- 過去の入金履歴: ${paymentHistory}

{
  "delayRisk"  : "LOW | MEDIUM | HIGH",
  "daysOverdue": 予想超過日数(数値),
  "recommendation": "推奨アクション(100文字以内)"
}
`;

対応レコードの拡張

経費レポート以外にも、以下のレコードタイプで同様のフローが実装できます。

NetSuiteレコード ユースケース
Purchase Order 発注リスク・取引先信用度の分析
Vendor Bill 請求内容の妥当性チェック
Sales Order 大口案件の自動アラート
Journal Entry 仕訳の整合性チェック

通知先の動的切り替え

分析結果のriskLevelcategoryに応じて、Slack通知先チャンネルを動的に変えることができます。

const CHANNEL_MAP = {
  HIGH        : 'https://hooks.slack.com/services/.../channel-alert',
  交際費      : 'https://hooks.slack.com/services/.../channel-entertainment',
  DEFAULT     : 'https://hooks.slack.com/services/.../channel-expense-general',
};

const webhookUrl = CHANNEL_MAP[analysis.riskLevel]
                || CHANNEL_MAP[analysis.category]
                || CHANNEL_MAP['DEFAULT'];

注意事項

  • OpenAI APIの利用コスト:gpt-4oはトークン数に応じた従量課金です。大量の明細行がある場合はプロンプトを簡潔にするか、gpt-4o-miniの使用を検討してください。
  • データガバナンス:NetSuiteの財務データ・顧客情報はOpenAI APIを経由して送信されます。社内のデータ取り扱いポリシーを確認し、機密情報のマスキングやOpenAI Enterprise契約(データ非学習オプション)の適用を検討してください。
  • Governance制限:SuiteScriptのUser Event ScriptはGovernance Unitの上限があります。外部API呼び出しはGovernanceを消費するため、明細行数が多いレコードでは注意が必要です。
  • AI出力の精度管理:ChatGPTの分析結果はあくまで補助情報です。高額案件や法的効力を持つ判断は、必ず人間が最終確認を行う運用フローを設計してください。
  • シークレット管理:APIキーやWebhook URLは必ずSecret Storeを利用し、スクリプト内にハードコードしないでください。
  • エラーハンドリング:本番環境ではtry-catchを適切に設置し、Script Execution Logへの記録を徹底してください。
  • 本番デプロイ前のテスト:Sandboxアカウントで十分な動作確認を行ってから本番環境に適用してください。

発展的な活用例

入金遅延の自動検知とアラート

Saved Searchと組み合わせたScheduled Scriptで、支払期日超過の請求書を定期チェックし、ChatGPTがリスク度と推奨アクションを判定してSlackに通知するフローです。

/**
 * @NApiVersion 2.1
 * @NScriptType ScheduledScript
 * 毎日8時に入金遅延リスクの高い請求書をChatGPTで分析しSlack通知
 */
define(['N/search', 'N/https'], (search, https) => {
  const execute = () => {
    // 支払期日が過ぎていて未入金の請求書を検索
    const invoiceSearch = search.create({
      type   : search.Type.INVOICE,
      filters: [
        ['mainline', 'is', 'T'],
        'AND',
        ['amountremaining', 'greaterthan', '0'],
        'AND',
        ['duedate', 'before', 'today'],
      ],
      columns: ['entity', 'total', 'amountremaining', 'duedate', 'trandate'],
    });

    invoiceSearch.run().each((result) => {
      // ChatGPTで遅延リスク判定 → Slack通知
      // ...(analyzeWithChatGPT + notifySlack を呼び出し)
      return true;
    });
  };
  return { execute };
});

顧客問い合わせの自動回答生成

NetSuiteのCase(サポートチケット)が作成されたタイミングで、ChatGPTが問い合わせ内容を分析し、FAQや過去対応履歴をもとに回答案を生成します。担当者は回答案を確認・修正して送付するだけになります。

// Case作成時(afterSubmit)に自動で回答案を生成
const caseSubject = newRecord.getValue({ fieldId: 'title' });
const caseMessage = newRecord.getValue({ fieldId: 'incomingmessage' });

const prompt = `
以下の顧客問い合わせに対し、NetSuiteサポート担当者として丁寧な回答案を作成してください。

【件名】${caseSubject}
【問い合わせ内容】${caseMessage}

{
  "category"    : "問い合わせ分類(操作方法 | エラー | 機能要望 | その他)",
  "urgency"     : "LOW | MEDIUM | HIGH",
  "draftReply"  : "回答案(300文字以内)",
  "nextAction"  : "推奨対応(50文字以内)"
}
`;

会計データのレポート自動文章化

Scheduled Scriptで月次の損益データをSavedSearchから取得し、ChatGPTが経営層向けのナラティブレポートとして文章化します。

// 月次レポートのプロンプト例
const prompt = `
以下はNetSuiteから取得した今月の損益データです。
経営会議で使用する月次レポートの「コメント欄」を作成してください。

【損益サマリー】
- 売上高    : ${revenue} 円(前月比 ${revenueGrowth}%)
- 売上原価  : ${cogs} 円
- 粗利率    : ${grossMargin}%
- 営業利益  : ${operatingIncome} 円

以下の観点を含めてください:
1. 当月のパフォーマンス評価
2. 前月・前年同月との比較
3. 来月に向けた注目ポイント

出力は400文字以内の日本語で、箇条書きではなく文章形式にしてください。
`;

NetSuiteデータをChatGPTで分析し、緊急度を判断して通知する

発注書が作成されたタイミングでも同様のフローが実装できます。件数が多い場合や、迅速な対応が必要なシーンでは、AIによる優先度判定が特に効果的です。

// Purchase Order用のトリガー設定例
// afterSubmit でステータスが "pendingApproval" の時点で通知
if (newRecord.type === record.Type.PURCHASE_ORDER) {
  const poStatus = newRecord.getValue({ fieldId: 'orderstatus' });
  if (poStatus === 'A') { // A = Pending Approval
    // ... 同様のChatGPT分析フロー
  }
}

Scheduled Scriptで定期バッチ処理

リアルタイム通知に加え、Scheduled Scriptを使って「当日承認された経費レポートの日次サマリー」をChatGPTに生成させる活用も可能です。

/**
 * @NApiVersion 2.1
 * @NScriptType ScheduledScript
 * 毎朝9時に前日承認の経費レポートをまとめてChatGPTで要約しSlackに投稿
 */
define(['N/search', 'N/https', 'N/format'], (search, https, format) => {
  const execute = () => {
    const yesterday = new Date();
    yesterday.setDate(yesterday.getDate() - 1);
    
    // SuiteQLで前日承認レポートを一括取得してバッチ分析
    // ...(SuiteQL + OpenAI API呼び出し)
  };
  return { execute };
});

導入レベル別おすすめ構成

NetSuite × ChatGPT 連携は、技術スタックや社内リソースに応じて段階的に導入できます。

レベル 構成 特徴
初級 NetSuite + Google Sheets + ChatGPT SuiteScriptでデータをシートに出力 → Google Apps ScriptでChatGPT API呼び出し。プログラミング経験が少ない方向け
中級 NetSuite + iPaaS(Yoom/Zapier/Make)+ ChatGPT + Slack ノーコード〜ローコードで連携。フローの可視化・管理がしやすく、業務部門主導で導入しやすい
上級 NetSuite + OIC/Celigo + ChatGPT + DWH(BigQuery等) SuiteScriptで直接API連携。大量データの処理や複雑な分岐ロジックに対応。エンジニアが実装を主導

本記事では「上級」構成(SuiteScript + OpenAI API直結)を解説しています。初級・中級からスタートして、慣れてきたら上級構成に移行するアプローチも有効です。


まとめ

NetSuite × ChatGPT 連携がもたらすメリット

メリット 内容
業務効率化 データのダウンロード・転記・手動分析・通知作成といった定型作業をゼロに
ミス削減 AIが一貫したルールで分析・分類するため、人的ミスや見落としを防止
意思決定の高速化 承認直後に分析結果が届くため、リアルタイムな経営判断が可能に

この記事で実装した自動化フローの振り返り

この記事では、NetSuiteのSuiteScriptを活用して、以下の自動化フローを実装する方法を解説しました。

  1. NetSuiteの承認イベントをUser Event ScriptのafterSubmitでキャッチ
  2. 経費レポートの詳細情報を取得してOpenAI APIに送信
  3. ChatGPTが費目・リスクレベルを自動分析し、JSON形式で結果を返却
  4. 分析結果に応じて処理を分岐し、適切なSlackチャンネルへ通知

NetSuiteはREST APIとSuiteScriptによる高い拡張性を持つERP基盤です。今回のパターンを応用することで、承認フローのあらゆる場面にAI分析を組み込み、経理・調達・営業部門の業務を大幅に効率化できます。

NetSuite × ChatGPT は、ERPを単なる記録システムから、AIが情報を解釈して人の意思決定を支援する"インテリジェントERP"へと進化させる取り組みです。

ぜひ、お使いのNetSuite環境で試してみてください!


参考リンク

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?