SCALE — Build Lab
機能パターン · REACT COMPONENT

請求書PDF生成

CATEGORY機能パターン TYPEReact Component EFFORT120〜240分 DIFFICULTY
PRIMARY CODE
ts
import jsPDF from 'jspdf';
import 'jspdf-autotable';

export async function generateInvoicePDF(invoice: {
  no: string; date: string; clientName: string;
  items: { name: string; qty: number; unitPrice: number }[];
  invoiceNumber: string; // インボイス番号
}) {
  const doc = new jsPDF();
  // 日本語フォント登録(事前にbase64で埋込み)
  // doc.addFont(...);

  doc.setFontSize(20);
  doc.text('請求書', 105, 20, { align: 'center' });

  doc.setFontSize(10);
  doc.text(`請求書番号: ${invoice.no}`, 14, 40);
  doc.text(`日付: ${invoice.date}`, 14, 46);
  doc.text(invoice.clientName + ' 御中', 14, 60);

  const subtotal = invoice.items.reduce((s, i) => s + i.qty * i.unitPrice, 0);
  const tax = Math.floor(subtotal * 0.1);

  (doc as any).autoTable({
    startY: 80,
    head: [['品目', '数量', '単価', '小計']],
    body: invoice.items.map(i => [i.name, i.qty, '¥' + i.unitPrice.toLocaleString(), '¥' + (i.qty * i.unitPrice).toLocaleString()]),
  });

  doc.text(`小計: ¥${subtotal.toLocaleString()}`, 150, (doc as any).lastAutoTable.finalY + 10);
  doc.text(`消費税: ¥${tax.toLocaleString()}`, 150, (doc as any).lastAutoTable.finalY + 16);
  doc.text(`合計: ¥${(subtotal + tax).toLocaleString()}`, 150, (doc as any).lastAutoTable.finalY + 22);
  doc.text(`登録番号: ${invoice.invoiceNumber}`, 14, (doc as any).lastAutoTable.finalY + 30);

  doc.save(`invoice-${invoice.no}.pdf`);
}
DEPENDENCIES
jspdfjspdf-autotable
前提条件
Tailwind CSS v4TypeScript 5
USE CASES
  • 任意のダッシュボードに組み込み

請求書PDF生成

:LiTarget: 用途

取引データから請求書PDFを生成。jsPDF + 日本語フォント対応。

:LiSparkle: 特徴

  • 日本語フォント
  • ロゴ挿入
  • 消費税自動計算
  • 電子保存タイムスタンプ
  • インボイス番号

:LiCode: コード(コピペ用)

import jsPDF from 'jspdf';
import 'jspdf-autotable';

export async function generateInvoicePDF(invoice: {
  no: string; date: string; clientName: string;
  items: { name: string; qty: number; unitPrice: number }[];
  invoiceNumber: string; // インボイス番号
}) {
  const doc = new jsPDF();
  // 日本語フォント登録(事前にbase64で埋込み)
  // doc.addFont(...);

  doc.setFontSize(20);
  doc.text('請求書', 105, 20, { align: 'center' });

  doc.setFontSize(10);
  doc.text(`請求書番号: ${invoice.no}`, 14, 40);
  doc.text(`日付: ${invoice.date}`, 14, 46);
  doc.text(invoice.clientName + ' 御中', 14, 60);

  const subtotal = invoice.items.reduce((s, i) => s + i.qty * i.unitPrice, 0);
  const tax = Math.floor(subtotal * 0.1);

  (doc as any).autoTable({
    startY: 80,
    head: [['品目', '数量', '単価', '小計']],
    body: invoice.items.map(i => [i.name, i.qty, '¥' + i.unitPrice.toLocaleString(), '¥' + (i.qty * i.unitPrice).toLocaleString()]),
  });

  doc.text(`小計: ¥${subtotal.toLocaleString()}`, 150, (doc as any).lastAutoTable.finalY + 10);
  doc.text(`消費税: ¥${tax.toLocaleString()}`, 150, (doc as any).lastAutoTable.finalY + 16);
  doc.text(`合計: ¥${(subtotal + tax).toLocaleString()}`, 150, (doc as any).lastAutoTable.finalY + 22);
  doc.text(`登録番号: ${invoice.invoiceNumber}`, 14, (doc as any).lastAutoTable.finalY + 30);

  doc.save(`invoice-${invoice.no}.pdf`);
}

:LiHandPointer: 使い方

対象プロジェクトに該当ファイルをコピーして、props を流し込むだけ。

:LiAlertCircle: 注意事項

  • 依存パッケージを忘れず追加