【業務自動化】Chapter3-2:テンプレートを使った請求書自動作成

ながみえ

一つ前のページでは Excelの結合ツール を作成しました。

今回は 請求書の自動生成ツール を作成しましょう。

Chapter1:PythonによるExcel自動化の基本を身につけよう
Chapter2:自動化ツールを.exe化して配布しよう
Chapter3:現場で使える便利な自動化ツールを4つ作ろう
 ・Chapter3-1:Excelファイルをフォルダ丸ごと結合しよう
 ・Chapter3-2:テンプレートを使った請求書自動作成 ◁今回はここ
 ・Chapter3-3:グラフ付き売上レポート自動生成ツールを作ろう
 ・Chapter3-4:勤怠の丸め・残業集計自動化ツールを作ろう
Chapter4:Webスクレイピングでデータを自動で集めよう
Chapter5:自動化ツールを作ってお金を稼ごう

日々の業務で「請求書を毎回Excelで作るのが大変…」と感じたことはありませんか?

本記事ではopenpyxl を活用して、あらかじめ用意したテンプレートと顧客リストから 請求書を自動生成するコード を紹介します。

  • 顧客リストと明細データをもとに請求書を自動で大量作成
  • テンプレートを利用してフォーマットを統一
  • 複数の顧客分をまとめて一括出力

初心者の方でも理解できるように、サンプルデータとサンプルスクリプトを用意しました。

記事の最後まで読み進めれば、あなたも 「ボタンひとつで請求書が完成する世界」 を体験できるはずです。

<<前のページ

業務自動化の記事一覧

次のページ>>

Excel請求書を自動作成する手順とサンプルコード

ここからは、実際に作成する「請求書自動化ツール」の仕組みとコードについて解説していきます。

あわせて読みたい
openpyxl使える関数・メソッド・クラス一覧【Excel自動化の基本リファレンス】
openpyxl使える関数・メソッド・クラス一覧【Excel自動化の基本リファレンス】
【Python】勉強猫がノートパソコンを前にして学習を始める様子。記事内の学習スタート用イラスト

請求書自動化ツールでできること|自動化の前提条件とイメージ

このツールは、事前に用意された 顧客リスト と 請求書明細リスト をもとに、テンプレートをコピーして自動的に請求書を作成します。

手作業では時間がかかる以下のような処理を、一度の実行で一気に行えるのが特徴です。

  • 顧客ごとに請求書を複製して保存
  • 請求書番号・請求日・支払期日などの基本情報を自動入力
  • 品目や数量、単価から小計・消費税・合計金額を自動計算
  • 備考欄があれば自動で反映
  • すべての請求書を指定フォルダに一括保存

これにより、毎月の請求業務を大幅に効率化できます。特に、顧客数が増えても処理時間がほとんど変わらない点が大きなメリットです。

たとえ数百~数千の請求書を作る場合でも、ワンクリックで実行できます。

学習用サンプルデータを作成するスクリプト

請求書自動化ツールを動かすには、入力となる 顧客リスト と 請求書明細リスト が必要です。

しかし、最初から自分でExcelファイルを作成すると、項目名の入力やフォーマット調整に手間がかかってしまい、学習に集中できません。

そこで本記事では、学習をスムーズに進めるために Make_Sample_Excel.py というスクリプトを用意しました。

このスクリプトを実行することで、次のような教材用のExcelファイルが自動的に生成されます。

  • 顧客リスト.xlsx
    • 顧客ID、会社名、担当者名、住所、支払期日などの情報をまとめたファイル
  • 請求書明細リスト.xlsx
    • 顧客ごとの請求書番号、請求日、品目名、単価、数量などをまとめたファイル

このデータを使うことで、初心者でもすぐに請求書自動作成ツールを試せるようになっています。

また、請求書のテンプレートも用意してあります。

こちらは少々複雑なので、↑↑のダウンロードリンクからファイルをダウンロードし、適切な場所に保存するようにしてください。

import openpyxl, pathlib
from datetime import datetime, timedelta

# === 出力先フォルダをテンプレートと同じ場所に設定 ===
file_path = pathlib.Path(__file__).parent / "workbooks"
customer_file = file_path / "顧客リスト.xlsx"
items_file = file_path / "請求書明細リスト.xlsx"

# === 顧客リスト(教材用サンプルデータ) ===
customers_data = [
    {
        "顧客ID": "CUST001",
        "会社名": "サンプル株式会社",
        "部署": "経理部",
        "担当者名": "山田 太郎",
        "郵便番号": "100-0001",
        "住所1": "東京都千代田区サンプル町1-1-1",
        "住所2": "",
        "メール": "taro.yamada@sample.co.jp",
        "電話": "03-0000-0001",
        "標準税率": 0.1,
        "支払期日(日)": 30,
        "通貨": "JPY",
    },
    {
        "顧客ID": "CUST002",
        "会社名": "テスト商事株式会社",
        "部署": "管理部",
        "担当者名": "佐藤 花子",
        "郵便番号": "530-0001",
        "住所1": "大阪府大阪市北区テスト町2-2-2",
        "住所2": "",
        "メール": "hanako.sato@test-shoji.co.jp",
        "電話": "06-0000-0002",
        "標準税率": 0.1,
        "支払期日(日)": 45,
        "通貨": "JPY",
    },
]

# === 請求書明細リスト(教材用サンプルデータ) ===
items_data = [
    {
        "請求書番号": "INV-2025-001",
        "請求日": datetime(2025, 9, 18),
        "顧客ID": "CUST001",
        "注文番号": "PO-1001",
        "明細日": datetime(2025, 9, 18),
        "品目名": "サンプルトレーニング教材",
        "単位": "式",
        "数量": 1,
        "単価(税抜)": 50000,
        "行別税率(任意)": None,
        "備考": None,
    },
    {
        "請求書番号": "INV-2025-001",
        "請求日": datetime(2025, 9, 18),
        "顧客ID": "CUST001",
        "注文番号": "PO-1001",
        "明細日": datetime(2025, 9, 18),
        "品目名": "オンサイトサポート(4時間)",
        "単位": "時間",
        "数量": 4,
        "単価(税抜)": 8000,
        "行別税率(任意)": None,
        "備考": None,
    },
    {
        "請求書番号": "INV-2025-002",
        "請求日": datetime(2025, 9, 15),
        "顧客ID": "CUST002",
        "注文番号": "PO-2002",
        "明細日": datetime(2025, 9, 15),
        "品目名": "月次レポート作成",
        "単位": "式",
        "数量": 1,
        "単価(税抜)": 30000,
        "行別税率(任意)": None,
        "備考": "契約: 月次スポット",
    },
    {
        "請求書番号": "INV-2025-002",
        "請求日": datetime(2025, 9, 15),
        "顧客ID": "CUST002",
        "注文番号": "PO-2002",
        "明細日": datetime(2025, 9, 15),
        "品目名": "データ加工スクリプト修正",
        "単位": "時間",
        "数量": 3,
        "単価(税抜)": 9000,
        "行別税率(任意)": 0.08,
        "備考": None,
    },
]

# === 支払期日(日)を具体的日付に変換 ===
date_map = {}
for item in items_data:
    cid = item["顧客ID"]
    if cid not in date_map:
        date_map[cid] = item["請求日"]

for cust in customers_data:
    cid = cust["顧客ID"]
    days = cust["支払期日(日)"]
    cust["支払期日"] = (date_map[cid] + timedelta(days=days)).strftime("%Y-%m-%d")
    del cust["支払期日(日)"]

# === openpyxlで顧客リストを保存 ===
book = openpyxl.Workbook()
sheet = book.active
sheet.title = "顧客リスト"

# ヘッダ
sheet.append(list(customers_data[0].keys()))
# データ
for row in customers_data:
    sheet.append(list(row.values()))

book.save(customer_file)

# === openpyxlで請求書明細リストを保存 ===
book = openpyxl.Workbook()
sheet = book.active
sheet.title = "請求書明細リスト"

# ヘッダ
sheet.append(list(items_data[0].keys()))
# データ
for row in items_data:
    sheet.append(list(row.values()))

book.save(items_file)

print(f"Excelファイルを出力しました:\n  {customer_file}\n  {items_file}")

請求書を自動生成するサンプルスクリプト

ここまでで準備が整ったので、いよいよ実際に請求書を自動で作成するスクリプトについて解説します。

今回のサンプルスクリプトでは、先ほど生成した 顧客リスト.xlsx と 請求書明細リスト.xlsx を読み込み、テンプレートをコピーして請求書を作成していきます。

このスクリプトを実行すると、例えば「サンプル株式会社 山田太郎 様」や「テスト商事株式会社 佐藤花子 様」といった形式で、それぞれの顧客専用の請求書が自動的にExcelファイルとして出力されます。

一度の実行で複数の顧客分を処理できるため、実務での「手作業による請求書作成」の手間を大幅に削減できるのが最大のポイントです。

import openpyxl, pathlib, sys, pandas
from tkinter import messagebox
from datetime import datetime

# 実行環境に応じた基準ディレクトリ
if getattr(sys, 'frozen', False):
    BASE_DIR = pathlib.Path(sys.executable).parent
else:
    BASE_DIR = pathlib.Path(__file__).parent

file_path = BASE_DIR / "workbooks"
file_path.mkdir(exist_ok=True)

# 顧客リストと明細ファイル、テンプレートのパス
customer_file = file_path / "顧客リスト.xlsx"
items_file = file_path / "請求書明細リスト.xlsx"
template_file = file_path / "請求書_テンプレート.xlsx"

# 顧客リストを読み込む
try:
    customers_df = pandas.read_excel(customer_file)
except FileNotFoundError:
    messagebox.showerror("エラー", f"顧客リスト {customer_file} が見つかりません")
    sys.exit(1)

# 明細リストを読み込む
try:
    items_df = pandas.read_excel(items_file)
except FileNotFoundError:
    messagebox.showerror("エラー", f"明細リスト {items_file} が見つかりません")
    sys.exit(1)

# テンプレート確認
if not template_file.exists():
    messagebox.showerror("エラー", f"テンプレート {template_file} が見つかりません")
    sys.exit(1)

invoice_count = 0

# 顧客ごとに処理
for _, row in customers_df.iterrows():
    customer_id = row["顧客ID"]
    company = row["会社名"]
    contact = row["担当者名"]
    name = company + "(" + contact + ")"

    # この顧客の明細を抽出
    customer_items = items_df[items_df["顧客ID"] == customer_id]
    if customer_items.empty:
        continue

    # 請求書番号・請求日など
    invoice_no = customer_items.iloc[0]["請求書番号"]
    invoice_date = customer_items.iloc[0]["請求日"]
    if isinstance(invoice_date, (datetime, pandas.Timestamp)):
        invoice_date_str = invoice_date.strftime("%Y-%m-%d")
    else:
        invoice_date_str = str(invoice_date)

    payment_due = row["支払期日"]

    # 備考(1つでもあれば最初を採用)
    remarks = customer_items["備考"].dropna().astype(str).tolist()
    remarks_text = remarks[0] if remarks else ""

    # テンプレートを開く
    book = openpyxl.load_workbook(template_file)
    sheet = book.active

    # --- ヘッダ情報 ---
    sheet["A1"].value = f"請求書 {name} 様"
    sheet["C3"].value = invoice_no
    sheet["C4"].value = invoice_date_str
    sheet["C5"].value = payment_due

    # --- 明細 ---
    start_row = 10
    subtotal = 0
    for i, item_row in enumerate(customer_items.itertuples(index=False), start=1):
        row_idx = start_row + i - 1
        item, unit, qty, price = item_row.品目名, item_row.単位, item_row.数量, item_row._8
        amount = qty * price
        subtotal += amount

        sheet[f"A{row_idx}"].value = i
        sheet[f"B{row_idx}"].value = item
        sheet[f"C{row_idx}"].value = unit
        sheet[f"D{row_idx}"].value = qty
        sheet[f"E{row_idx}"].value = price
        sheet[f"F{row_idx}"].value = amount

    # --- 集計 ---
    tax_rate = row["標準税率"]
    tax = int(subtotal * tax_rate)
    total = subtotal + tax

    sheet["F30"].value = subtotal
    sheet["F31"].value = tax
    sheet["F32"].value = total

    # --- 備考 ---
    if remarks_text:
        sheet["A35"].value = f"備考: {remarks_text}"
    else:
        sheet["A35"].value = None  # 空欄にする

    # 保存
    save_path = file_path / f"{name}_請求書.xlsx"
    book.save(save_path)
    invoice_count += 1

# 完了メッセージ
messagebox.showinfo("完了", f"{invoice_count} 件の請求書を作成しました\n保存先: {file_path}")

Chapter2-1 で解説した、PyInstallerを用いたツール化(exe化)も忘れずに実行しましょう。

まとめ|Excel請求書を自動生成する価値

今回の記事では、Excelテンプレートを使って請求書を自動生成するコードを紹介しました。

ポイントは、単に「プログラムでセルに値を書き込む」という操作にとどまらず、業務の一連の流れそのものを自動化できる という点にあります。

こうした作業は一件あたり数分で済むように思えても、顧客数が数十件・数百件に増えると膨大な時間を奪っていきます。

さらに重要なのは「作業の質」です。人間が手作業で入力すると、どうしてもミスが起こりがちですが、自動化すれば決まった処理を正確に繰り返すことができるため、業務の信頼性が大幅に向上 します。

請求書のような定型業務は、その入口にすぎません。

ここで得た知識は、売上集計、ダッシュボード作成、勤怠管理など、幅広い業務に応用することができます。

あなたのPCで動く「小さな業務自動化ツール」が、やがて大きな生産性向上につながるはずです。

もっと分かりやすい学習サイトにするために

この記事を読んで「ここが分かりにくかった」「ここが難しかった」等の意見を募集しています。

世界一わかりやすいExcel自動化学習サイトにするため、ぜひ 問い合わせフォーム からご意見ください。

<<前のページ

業務自動化の記事一覧

次のページ>>

記事URLをコピーしました