Salesforce 帳票出力 API

見積書兼発注書 PDF 生成サービス

稼働中

デモ帳票を表示

サンプルデータでPDFを生成

ヘルスチェック

APIの稼働状況を確認

エンドポイント一覧

GET /api/health ヘルスチェック
GET /api/quote/demo デモPDF(サンプルデータ)
POST /api/quote/pdf PDF生成(Salesforceから呼び出し)

ヘッダー:

Content-Type: application/json
X-API-Key: {your-api-key}

リクエストボディ例:

{
  "quoteNumber": "P2502672",
  "quoteDate": "2026-02-02",
  "title": "RaySheetライセンス費用",
  "issuerCompanyName": "thomas株式会社",
  "issuerPostalCode": "106-0032",
  "issuerAddress": "東京都港区六本木7-21-24 THE MODULE roppongi 2F",
  "issuerTel": "03-6773-7830",
  "recipientCompanyName": "株式会社サンプルコーポレーション",
  "recipientPostalCode": "100-0001",
  "recipientAddress": "東京都千代田区丸の内1-1-1 サンプルビル10F",
  "subtotal": 2091600,
  "taxRate": 10,
  "tax": 209160,
  "totalAmount": 2300760,
  "remarks": "RaySheet:月額700円(税別)/1ライセンス",
  "paymentTerms": "・支払い条件:発注月の翌月末日に一括前払いとなります",
  "lineItems": [
    {
      "productName": "RaySheet",
      "quantity": 249,
      "unitPrice": 8400,
      "amount": 2091600
    }
  ]
}

Salesforce Apex サンプルコード

以下のApexクラスをSalesforceに追加することで、見積オブジェクトからPDFを生成できます。

// ===================================================
// QuotePdfService.cls
// 見積オブジェクトからPDFを生成するApexクラス
// ===================================================
public class QuotePdfService {

    private static final String API_ENDPOINT =
        'https://your-project.pages.dev/api/quote/pdf';
    private static final String API_KEY_SETTING = 'QuotePdfApiKey'; // Custom Setting名

    /**
     * 見積レコードIDからPDFを生成し、添付ファイルとして保存
     * @param quoteId 見積オブジェクト (Quote__c) のレコードID
     */
    @AuraEnabled
    public static String generateAndAttachPdf(Id quoteId) {
        // 見積データ取得
        Quote__c quote = [
            SELECT Id, QuoteNumber__c, QuoteDate__c, Subject__c,
                   IssuerCompanyName__c, IssuerPostalCode__c,
                   IssuerAddress__c, IssuerTel__c,
                   RecipientCompanyName__c, RecipientPostalCode__c,
                   RecipientAddress__c,
                   Subtotal__c, TaxRate__c, Tax__c, TotalAmount__c,
                   Remarks__c, PaymentTerms__c
            FROM Quote__c
            WHERE Id = :quoteId
            LIMIT 1
        ];

        // 見積明細データ取得
        List<QuoteLineItem__c> lineItems = [
            SELECT ProductName__c, Quantity__c, UnitPrice__c, Amount__c
            FROM QuoteLineItem__c
            WHERE Quote__c = :quoteId
            ORDER BY SortOrder__c ASC
        ];

        // JSONペイロード構築
        Map<String, Object> payload = new Map<String, Object>{
            'quoteNumber'           => quote.QuoteNumber__c,
            'quoteDate'             => String.valueOf(quote.QuoteDate__c),
            'title'                 => quote.Subject__c,
            'issuerCompanyName'     => quote.IssuerCompanyName__c,
            'issuerPostalCode'      => quote.IssuerPostalCode__c,
            'issuerAddress'         => quote.IssuerAddress__c,
            'issuerTel'             => quote.IssuerTel__c,
            'recipientCompanyName'  => quote.RecipientCompanyName__c,
            'recipientPostalCode'   => quote.RecipientPostalCode__c,
            'recipientAddress'      => quote.RecipientAddress__c,
            'subtotal'              => quote.Subtotal__c,
            'taxRate'               => quote.TaxRate__c,
            'tax'                   => quote.Tax__c,
            'totalAmount'           => quote.TotalAmount__c,
            'remarks'               => quote.Remarks__c,
            'paymentTerms'          => quote.PaymentTerms__c,
            'lineItems'             => buildLineItems(lineItems)
        };

        // HTTP呼び出し
        String apiKey = [SELECT Value__c FROM ApiSetting__mdt
                         WHERE DeveloperName = :API_KEY_SETTING].Value__c;
        HttpRequest req = new HttpRequest();
        req.setEndpoint(API_ENDPOINT);
        req.setMethod('POST');
        req.setHeader('Content-Type', 'application/json');
        req.setHeader('X-API-Key', apiKey);
        req.setBody(JSON.serialize(payload));
        req.setTimeout(30000);

        Http http = new Http();
        HttpResponse res = http.send(req);

        if (res.getStatusCode() != 200) {
            throw new CalloutException('PDF生成失敗: ' + res.getStatus());
        }

        // PDFをContentVersionとして保存
        ContentVersion cv = new ContentVersion();
        cv.Title = '見積書兼発注書_' + quote.QuoteNumber__c;
        cv.PathOnClient = '見積書兼発注書_' + quote.QuoteNumber__c + '.pdf';
        cv.VersionData = res.getBodyAsBlob();
        cv.FirstPublishLocationId = quoteId;
        insert cv;

        return '見積書兼発注書_' + quote.QuoteNumber__c + '.pdf';
    }

    private static List<Map<String,Object>> buildLineItems(
        List<QuoteLineItem__c> items
    ) {
        List<Map<String,Object>> result = new List<Map<String,Object>>();
        for (QuoteLineItem__c item : items) {
            result.add(new Map<String,Object>{
                'productName' => item.ProductName__c,
                'quantity'    => item.Quantity__c,
                'unitPrice'   => item.UnitPrice__c,
                'amount'      => item.Amount__c
            });
        }
        return result;
    }
}

LWC サンプルコード(プレビュー・ダウンロード・保存)

見積レコードページに配置するボタンコンポーネントです。クリックするとPDFプレビューを表示し、ダウンロードまたはSalesforceファイルに保存を選択できます。

ファイル構成

📁 force-app/main/default/lwc/quotePdfViewer/
├── quotePdfViewer.html
├── quotePdfViewer.js
└── quotePdfViewer.js-meta.xml
📁 force-app/main/default/classes/
├── QuotePdfService.cls
└── QuotePdfService.cls-meta.xml
<!-- quotePdfViewer.html -->
<template>
  <lightning-card title="帳票出力" icon-name="doctype:pdf">
    <div class="slds-card__body slds-card__body_inner">

      <!-- 生成ボタン -->
      <div class="slds-m-bottom_medium">
        <lightning-button
          label="帳票をプレビュー"
          icon-name="utility:preview"
          variant="brand"
          onclick={handlePreview}
          disabled={isLoading}>
        </lightning-button>
      </div>

      <!-- ローディング -->
      <template if:true={isLoading}>
        <div class="slds-align_absolute-center slds-p-around_medium">
          <lightning-spinner alternative-text="PDF生成中..." size="medium"></lightning-spinner>
          <p class="slds-m-top_small slds-text-color_weak">PDF生成中です。しばらくお待ちください...</p>
        </div>
      </template>

      <!-- エラー表示 -->
      <template if:true={errorMessage}>
        <div class="slds-notify slds-notify_alert slds-alert_error" role="alert">
          <span class="slds-assistive-text">エラー</span>
          <lightning-icon icon-name="utility:error" alternative-text="Error" size="x-small"></lightning-icon>
          <h2 class="slds-m-left_small">{errorMessage}</h2>
          <div class="slds-notify__close">
            <lightning-button-icon
              icon-name="utility:close"
              alternative-text="閉じる"
              onclick={clearError}>
            </lightning-button-icon>
          </div>
        </div>
      </template>

      <!-- プレビュー&アクションパネル -->
      <template if:true={pdfDataUrl}>
        <div class="slds-box slds-box_small slds-theme_shade slds-m-bottom_medium">

          <!-- アクションボタン行 -->
          <div class="slds-grid slds-gutters slds-m-bottom_small">
            <div class="slds-col">
              <lightning-button
                label="ダウンロード"
                icon-name="utility:download"
                variant="neutral"
                onclick={handleDownload}>
              </lightning-button>
            </div>
            <div class="slds-col">
              <lightning-button
                label="Salesforceに保存"
                icon-name="utility:save"
                variant="success"
                onclick={handleSaveToSalesforce}
                disabled={isSaving}>
              </lightning-button>
            </div>
            <div class="slds-col slds-no-flex">
              <lightning-button
                label="閉じる"
                icon-name="utility:close"
                variant="destructive-text"
                onclick={handleClose}>
              </lightning-button>
            </div>
          </div>

          <!-- 保存成功メッセージ -->
          <template if:true={savedFileName}>
            <div class="slds-notify slds-notify_toast slds-theme_success" role="status">
              <lightning-icon icon-name="utility:success" size="x-small"></lightning-icon>
              <div class="slds-notify__content slds-m-left_small">
                <p class="slds-text-heading_small">
                  Salesforceへの保存が完了しました: {savedFileName}
                </p>
                <p class="slds-text-body_small">レコードの「ファイル」欄からアクセスできます。</p>
              </div>
            </div>
          </template>

          <!-- PDFプレビュー -->
          <div class="slds-m-top_small">
            <p class="slds-text-title_caps slds-m-bottom_x-small">プレビュー</p>
            <iframe
              src={pdfDataUrl}
              title="帳票プレビュー"
              style="width:100%; height:700px; border:1px solid #dddbda; border-radius:4px;">
            </iframe>
          </div>

        </div>
      </template>

    </div>
  </lightning-card>
</template>

Salesforce カスタムオブジェクト設計

見積オブジェクト(Quote__c)

項目名 API名
見積番号QuoteNumber__cText(20)
見積日付QuoteDate__cDate
件名Subject__cText(200)
発注元会社名IssuerCompanyName__cText(100)
発注元郵便番号IssuerPostalCode__cText(8)
発注元住所IssuerAddress__cText(200)
発注元TELIssuerTel__cPhone
宛先会社名RecipientCompanyName__cText(100)
宛先郵便番号RecipientPostalCode__cText(8)
宛先住所RecipientAddress__cText(200)
小計Subtotal__cCurrency
税率TaxRate__cNumber(5,2)
消費税Tax__cCurrency
合計金額TotalAmount__cCurrency
発注書備考Remarks__cLong Text
支払い条件PaymentTerms__cLong Text

見積明細オブジェクト(QuoteLineItem__c)

項目名 API名
見積(親)Quote__cLookup(Quote__c)
商品名ProductName__cText(200)
数量Quantity__cNumber(18,2)
単価UnitPrice__cCurrency
金額Amount__cCurrency
説明Description__cText(500)
並び順SortOrder__cNumber(3,0)

APIテストフォーム