No.006 pythonで見積書(PDF)を作成

1. 今回の目的

その際必要な情報はExcel formから読み込むようにしました。
最後にBackupをExcelで保存できるようにもしています。

2. Reference

以下のページを参考にさせていただきました。
Reportlabの使い方について

https://evaluelog.com/post-173/

OSモジュールについて

日付の表示Formatについて

Excel同一Fileの複数シートへの書き出し

3. Pythonでのコード

Step1
まず、ExcelでFormを作ります。今回は下記のようなFormatにしました。
黄色い部分が入力エリアです。

文書管理番号:  英文字と数字の組み合わせでランダムで生成される式をC1セルに入力しました。
=BASE(RANDBETWEEN(0,36^5-1),36,5)&RANDBETWEEN(1000,9999)
見積日:  見積もりの日付を入力
社名: 相手先社名の入力
顧客ID:  相手先社名の顧客IDを入力
件名: 見積もり案件名を入力
納期: 納期を入力
見積番号  "Q-" と文章番号の組み合わせ
備考1:  備考欄1
備考2:  備考欄2

Step2
Step1で作成したFileを保存してPythonで読み込みますが、事前準備として今回使うライブラリを読み込みます。

from reportlab.pdfgen import canvas
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.cidfonts import UnicodeCIDFont
from reportlab.lib.pagesizes import A4, portrait
from reportlab.lib.units import mm
from reportlab.platypus import Table, TableStyle
from reportlab.lib import colors
import os
import pandas as pd
import numpy as np
import datetime

Step3-1
Step1のfile ("quotation_form.xlsxとした)をDataframeに読み込みます。
"gaiyo"という変数に pd.read_excelで読み込みます。その際B1:C9セルを読み込むため、skipfooter=15 (全24行中終わりの15行を無視), usecols=[1,2](コラムB, C列のみを読み込み)としています。

また、gaiyo.fillna("-")で空白の値を "-"に置き換えるようにしました。これをしないと後のPDFの書き込みでエラーになってしまうので。

gaiyo = pd.read_excel("quotation_form.xlsx", header=None, usecols= [1,2],skipfooter=15)
gaiyo = gaiyo.fillna("-")  #Nan値のままだと、canvasでdrawStringするときにエラーになるので "-"に置き換える
gaiyo

以下のように読み込めました。

Step3-2
Step2で読み込んだDataframe ("gaiyo")から値を変数に格納します。
例えば文書管理番号はoriginal_idという変数にgaiyo.loc[0,2)で0行2列目の値を代入しています。

original_id = gaiyo.loc[0,2]
quotation_date = gaiyo.loc[1,2]
company = gaiyo.loc[2,2]
company_id = gaiyo.loc[3,2]
project_name = gaiyo.loc[4,2]
deadline = gaiyo.loc[5,2]
quotation_number = gaiyo.loc[6,2]
biko1 = gaiyo.loc[7,2]
biko2 = gaiyo.loc[8,2]
quotation_date=quotation_date.strftime("%Y %b %d")
deadline = deadline.strftime("%Y %b %d")
print('文書管理番号は',original_id)
print('見積日は',quotation_date)
print('社名は',company)
print('顧客IDは',company_id)
print('プロジェクト名は',project_name)
print('納期は',deadline)
print('見積番号は',quotation_number)
print('備考欄1',biko1)
print('備考欄2',biko2)

Step4
次にExcel Formから明細欄を読み込んで "meisai"という変数に代入しています。skiprows=10で10行目からを取り込む指定です。
meisai = meisai.fillna('')で na値を空白(" ")に変更しています。
meisai13行6列目に請求総額があるので invoice_totalという変数に代入します。

meisai = pd.read_excel("quotation_form.xlsx", header=None, skiprows = 10)
meisai = meisai.fillna('')
meisai = meisai.replace(0,'')
print('明細書',meisai)
invoice_total = meisai.loc[13,6] #13行6列目の総額を取り出し
invoice_total = "{:,d}".format(invoice_total)
print('請求金額(税込)は',invoice_total)

Step5

5-1  osモジュールを使ってfile path をdesktopに設定しました(desktopに保存するようにした)
5-2 canvasでA4縦型の設定をした
5-3 canvasに使用するfontの登録
5-4  cv.drawStringで位置を指定して文字を記載している。
5-5 Tableにして表(件名、納期、支払い条件、有効期限)の記載
5-6 TableにしてInvoice_totalの記載
5-7 Tableにして明細の記載
5-8 見積書下部にに備考および連絡先、ロゴ等を記載
cv.line(1*mm,47*mm,210*mm,47*mm)# 線を描画(始点x、始点y、終点x、終点y)で線を引いている
cv.drawImage("hanko.jpg",130*mm,25*mm,width=50,height=50)で同じJupyterNotebookと同じFolerにある。hanko.jpgを読み込んでいる。
5-9 cv.save()で#5-1で設定したfile path file名でPDFが保存される。

#5-1 ユーザのデスクトップのディレクトリを取得
file = company+original_id+"様.pdf"
file_path = os.path.expanduser("~") + "/Desktop/" + file

#5-2 縦型A4のCanvasを準備
cv = canvas.Canvas(file_path, pagesize=portrait(A4))  #A4縦型設定

#5-3 フォント登録
pdfmetrics.registerFont(UnicodeCIDFont('HeiseiKakuGo-W5'))  #フォント設定
font_size = 20
cv.setFont('HeiseiKakuGo-W5', font_size)

#5-4 文字記載
cv.drawString(90*mm, 260*mm, '見 積 書')#左から9cm, 下から26cmに見積書と記載"
cv.drawString(10*mm, 245*mm, f'{company} 御中')#左から1cm, 下から24.5cmに{company}(f文字列で御中)

font_size = 10
cv.setFont('HeiseiKakuGo-W5', font_size)
cv.drawString(160*mm, 285*mm, f'文書管理番号: {original_id}' ) #右上ヘッダー(左から16cm, 下から28.5cmに{original_id}()
cv.drawString(10*mm, 230*mm, '下記のとおり、御見積もり申し上げます。')

font_size = 12
cv.setFont('HeiseiKakuGo-W5', font_size)#フォントサイズを変更
cv.drawString(140*mm, 245*mm, f'見積番号 {quotation_number}')
cv.drawString(140*mm, 240*mm, f'見積日   {quotation_date}')

cv.drawString(140*mm, 230*mm, "サンプル株式会社")
cv.drawString(140*mm, 225*mm, "〒161-0033")
cv.drawString(140*mm, 220*mm, "東京都新宿区下落合1-1-1")
cv.drawString(140*mm, 215*mm, "サンプルビル1F")
cv.drawString(140*mm, 210*mm, "TEL: 03-0000-0000")
cv.drawString(140*mm, 205*mm, "担当: サンプルタロウ")

#5-5 テーブルフォームにしてtable.drawOnでtable記載
#件名、納期,支払い条件、有効期限はTableにして左上部に記載
data = [
        ['件名', project_name],
        ['納期', deadline],
        ['支払条件', '月末締翌月末払'],
        ['有効期限', '御見積後2週間']
    ]
table = Table(data)

# tableの装飾
table.setStyle(TableStyle([                              
        ('FONT', (0, 0), (-1, -1), 'HeiseiKakuGo-W5', 12), # フォント
        ('GRID', (0, 0), (-1, -1), 1, colors.black),       # 罫線
    ]))
table.wrapOn(cv, 15*mm, 195*mm) # table位置
table.drawOn(cv, 15*mm, 195*mm)

#5-6 Tableで請求金額の表記
data = [
        ['ご請求金額  ', f'  {invoice_total}  円(税込)']
    ]
table = Table(data)

table.setStyle(TableStyle([                              
        ('FONT', (0, 0), (-1, -1), 'HeiseiKakuGo-W5', 14), # フォント
        ('GRID', (0, 0), (-1, -1), 1, colors.black),       # 罫線
    ]))
table.wrapOn(cv, 15*mm, 180*mm) # table位置
table.drawOn(cv, 15*mm, 180*mm)

#5-7明細の表記
cv.drawString(10*mm, 170*mm, '見積内訳')
meisai = meisai.to_numpy().tolist()

table = Table(meisai, colWidths=(10*mm,80*mm, 10*mm, 20*mm,20*mm,20*mm,20*mm))
table.setStyle(TableStyle([                              
        ('FONT', (0, 0), (-1, -1), 'HeiseiKakuGo-W5', 10), # フォント
        ('GRID', (0, 0), (-1, -1), 1, colors.black),       # 罫線
    ]))
table.wrapOn(cv, 10*mm, 80*mm) # table位置
table.drawOn(cv, 10*mm, 80*mm)

#5-8下部に備考および連絡先
cv.drawString(10*mm, 50*mm, '-[備考]-')
cv.drawString(10*mm, 40*mm, biko1)
cv.drawString(10*mm, 30*mm, biko2)
cv.line(1*mm,47*mm,210*mm,47*mm)# 線を描画(始点x、始点y、終点x、終点y)
cv.drawImage("hanko.jpg",130*mm,25*mm,width=50,height=50)
cv.drawString(150*mm, 35*mm, "担当    XXX")
cv.drawString(150*mm, 30*mm, "メール  xyxy@gmail.com")

# 5-9保存
cv.showPage()
cv.save()

DesktopにPDFが造られました。

Step6
BackupをExcelで保存できるようにしておきます。
#6-1サマリーを作りたいので必要な項目を一旦リストにしてdataに代入
そのとdataframeに変換して"backup"に代入

#Backup_excelfile

#6-1 サマリー項目をリストしてdataに格納
data = {'文書管理番号':[original_id], 
        '見積日':[quotation_date], 
        '社名':[company],
        '顧客ID':[company_id],
        '件名':[project_name],
        '納期':[deadline],
        '見積番号':[quotation_number],
        '備考1':[biko1],
        '備考2':[biko2],
        '請求金額(税込)':[invoice_total]}
backup = pd.DataFrame(data=data)
backup

#6-2明細もbackupとして残したいですが、明細には文書番号、見積番号、社名が入ってないので、追加します。

#6-2
meisai=pd.DataFrame(meisai)
meisai[7]=original_id
meisai.loc[0,7]="文書番号"

meisai[8]=quotation_number
meisai.loc[0,8]="見積番号"

meisai[9]=company
meisai.loc[0,9]="社名"
meisai

会社名+original_idの名前でexcel fileをDesktopに書き出します。

# ユーザのデスクトップのディレクトリを取得
file = company+original_id+"様.xlsx"
file_path = os.path.expanduser("~") + "/Desktop/" + file

# savenameは例えば"../output/sample.xlsx"のような、エクセルの出力に対応した名称
with pd.ExcelWriter(file_path, engine="openpyxl") as writer:
    backup.to_excel(writer, sheet_name="list", index=False)
    meisai.to_excel(writer, sheet_name="detail", index=False)

無事に保存されました。

以上です。cv.drawStringで位置を指定して書き込むのが結構大変でした…


いいなと思ったら応援しよう!