見積書作成システムをGeminiに作ってもらいました。
では今回の手順を見てみます。
1.まずはスプレッドシートで見積書を作ります。
下記の画像はLibreCalcで見積書式を作りPDFにエクスポートしたものです。

2.このPDFをGeminiにアップロードして以下のプロンプトを入力します。
「アップロードしたファイルはよくある見積書です。これをPythonのTkinterで必要な情報を入力します。赤色の部分は、品名、単価、数量を複数と宛先、緑色の部分はプログラムに埋め込んであり、オレンジ色の部分はそれぞれの金額と合計金額でPDFで出力するプログラムを作ってください」
3.すると動作するプログラムが出来上がります。
ここではOS等を指定していなのでいろんな問題が起きました。
起きた問題
・日本語フォントが使えなかった
OS指定と問題点を尋ねることで問題は解決します。
・データの追加しかできなかった
削除ボタンを追加
これでおほぼ使えるものができました。
機能拡張してみる
追懐した機能は以下の通り
1.入れたデータを宛先_YYYYMMDD.jsonに吐き出す。
2.吐き出したデータを読み込んで復元する。
こうすることで、一度発行した見積もりを訂正すしたりテンプレートとして使うことができます。
プログラムと動作環境
動作させるために必要なライブラリ
pip install reportlab
日本語を表示するために必要な操作
sudo apt update
sudo apt install fonts-ipafont
プログラム
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import os
import json
from datetime import datetime
from reportlab.pdfgen import canvas
from reportlab.pdfbase import ttfonts
from reportlab.pdfbase import pdfmetrics
COMPANY_NAME = "株式会社△△AAA"
BANK_INFO = "XXXX銀行 xxx支店 普通 NNNNNNN"
TAX_RATE = 0.10
class EstimateApp:
def __init__(self, root):
self.root = root
self.root.title("見積書作成システム")
input_frame = tk.Frame(root)
input_frame.pack(pady=10, padx=10)
tk.Label(input_frame, text="宛先:").grid(row=0, column=0, sticky="e")
self.client_name = tk.Entry(input_frame, width=25)
self.client_name.grid(row=0, column=1, padx=5)
self.client_name.insert(0, "株式会社○○○○○○")
tk.Label(input_frame, text="見積日:").grid(row=0, column=2, sticky="e")
self.est_date = tk.Entry(input_frame, width=15)
self.est_date.grid(row=0, column=3, padx=5)
self.est_date.insert(0, datetime.now().strftime("%Y/%m/%d"))
self.list_frame = tk.Frame(root)
self.list_frame.pack(pady=5)
tk.Label(self.list_frame, text="品名", width=15).grid(row=0, column=0)
tk.Label(self.list_frame, text="単価", width=10).grid(row=0, column=1)
tk.Label(self.list_frame, text="数量", width=5).grid(row=0, column=2)
tk.Label(self.list_frame, text="操作", width=5).grid(row=0, column=3)
self.rows = []
self.add_item_row("", "", "")
# self.add_item_row("商品B", "900", "5")
btn_frame = tk.Frame(root)
btn_frame.pack(pady=10)
tk.Button(btn_frame, text="行を追加", command=self.add_item_row).grid(row=0, column=0, padx=5)
tk.Button(btn_frame, text="保存 (JSON)", command=self.save_data, bg="#4CAF50", fg="white").grid(row=0, column=1, padx=5)
tk.Button(btn_frame, text="読込 (JSON)", command=self.load_data, bg="#2196F3", fg="white").grid(row=0, column=2, padx=5)
tk.Button(btn_frame, text="PDF出力", command=self.generate_pdf, bg="#FF9800", fg="white").grid(row=0, column=3, padx=5)
def add_item_row(self, name="", price="", qty=""):
name_e = tk.Entry(self.list_frame, width=15)
price_e = tk.Entry(self.list_frame, width=10)
qty_e = tk.Entry(self.list_frame, width=5)
name_e.insert(0, name)
price_e.insert(0, price)
qty_e.insert(0, qty)
del_btn = tk.Button(self.list_frame, text="×", command=lambda: self.remove_item_row(name_e, price_e, qty_e, del_btn))
row_idx = len(self.rows) + 1
name_e.grid(row=row_idx, column=0); price_e.grid(row=row_idx, column=1); qty_e.grid(row=row_idx, column=2); del_btn.grid(row=row_idx, column=3)
self.rows.append((name_e, price_e, qty_e, del_btn))
def remove_item_row(self, *widgets):
if len(self.rows) <= 1: return
for w in widgets: w.destroy()
self.rows = [r for r in self.rows if r[0].winfo_exists()]
def save_data(self):
date_str = self.est_date.get().replace("/", "").replace("-", "")
filename = f"{self.client_name.get()}_{date_str}.json"
data = {"client": self.client_name.get(), "date": self.est_date.get(), "items": [(r[0].get(), r[1].get(), r[2].get()) for r in self.rows]}
with open(filename, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=4)
messagebox.showinfo("完了", f"保存: {filename}")
def load_data(self):
path = filedialog.askopenfilename(filetypes=[("JSON files", "*.json")])
if not path: return
with open(path, 'r', encoding='utf-8') as f:
data = json.load(f)
for r in self.rows:
for w in r: w.destroy()
self.rows.clear()
self.client_name.delete(0, tk.END); self.client_name.insert(0, data["client"])
self.est_date.delete(0, tk.END); self.est_date.insert(0, data["date"])
for item in data["items"]: self.add_item_row(item[0], item[1], item[2])
def get_linux_font(self):
paths = ["/usr/share/fonts/truetype/ipafont-gothic/ipag.ttf", "/usr/share/fonts/truetype/fonts-japanese-gothic.ttf"]
return next((p for p in paths if os.path.exists(p)), None)
def generate_pdf(self):
font_path = self.get_linux_font()
if not font_path: return
pdfmetrics.registerFont(ttfonts.TTFont('JP', font_path))
c = canvas.Canvas(f"Estimate_{self.client_name.get()}.pdf")
c.setFont('JP', 14); c.drawString(50, 780, f"{self.client_name.get()} 様")
c.setFont('JP', 10); c.drawString(380, 800, COMPANY_NAME); c.drawString(380, 785, f"見積日: {self.est_date.get()}"); c.drawString(380, 770, f"振込先: {BANK_INFO}")
y = 700
c.line(50, y+15, 520, y+15); c.drawString(60, y+2, "品名"); c.drawString(200, y+2, "単価"); c.drawString(300, y+2, "数量"); c.drawString(420, y+2, "金額"); c.line(50, y-5, 520, y-5)
y -= 25; total_sub = 0
for r in self.rows:
try:
p, q = int(r[1].get() or 0), int(r[2].get() or 0)
sub = p * q
total_sub += sub
c.drawString(60, y, r[0].get()); c.drawString(200, y, f"¥{p:,}"); c.drawString(300, y, f"{q}"); c.drawString(420, y, f"¥{sub:,}")
y -= 20
except: continue
grand_total = int(total_sub * (1 + TAX_RATE))
c.line(50, y, 520, y); c.setFont('JP', 16); c.drawString(300, y-30, f"合計金額: ¥{grand_total:,} (税込)")
c.save()
messagebox.showinfo("成功", "PDF出力完了")
if __name__ == "__main__":
root = tk.Tk(); app = EstimateApp(root); root.mainloop()
以下に使い方ビデオを追加します。
このビデオではシバンを指定しているのでコマンドのように起動しています。
このように、PCを使ったシステムは既にAIで誰でも作れるところまで来ています。
もっと詳しくお知りになりたい方は、m_kawase@embed-ai.comまでご連絡ください。