見積書兼発注書 PDF 生成サービス
/api/health
ヘルスチェック
/api/quote/demo
デモPDF(サンプルデータ)
/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
}
]
}
以下の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;
}
}
見積レコードページに配置するボタンコンポーネントです。クリックするとPDFプレビューを表示し、ダウンロードまたはSalesforceファイルに保存を選択できます。
ファイル構成
<!-- 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>
| 項目名 | API名 | 型 |
|---|---|---|
| 見積番号 | QuoteNumber__c | Text(20) |
| 見積日付 | QuoteDate__c | Date |
| 件名 | Subject__c | Text(200) |
| 発注元会社名 | IssuerCompanyName__c | Text(100) |
| 発注元郵便番号 | IssuerPostalCode__c | Text(8) |
| 発注元住所 | IssuerAddress__c | Text(200) |
| 発注元TEL | IssuerTel__c | Phone |
| 宛先会社名 | RecipientCompanyName__c | Text(100) |
| 宛先郵便番号 | RecipientPostalCode__c | Text(8) |
| 宛先住所 | RecipientAddress__c | Text(200) |
| 小計 | Subtotal__c | Currency |
| 税率 | TaxRate__c | Number(5,2) |
| 消費税 | Tax__c | Currency |
| 合計金額 | TotalAmount__c | Currency |
| 発注書備考 | Remarks__c | Long Text |
| 支払い条件 | PaymentTerms__c | Long Text |
| 項目名 | API名 | 型 |
|---|---|---|
| 見積(親) | Quote__c | Lookup(Quote__c) |
| 商品名 | ProductName__c | Text(200) |
| 数量 | Quantity__c | Number(18,2) |
| 単価 | UnitPrice__c | Currency |
| 金額 | Amount__c | Currency |
| 説明 | Description__c | Text(500) |
| 並び順 | SortOrder__c | Number(3,0) |