PHPを使った請求書PDF生成システムの実装
- LOGZAWEB
- ホームページ制作の技術的な取り組み
- PHPを使った請求書PDF生成システムの実装
ここでは、PHPを使用して請求書PDFを生成するシステムの実装方法について解説します。
本システムではPHP・JavaScript・データベースを連携させることで、請求データを管理画面からPDF出力する構成としています。
請求書の登録・管理機能を含むシステム全体につきましては、請求書管理システムをご確認下さい。
ファイル構成
ここでは、請求書PDF生成システムの基本的なファイル構成について説明します。
PDF出力処理は管理画面から呼び出され、データベースの請求情報を取得してPDFを生成する構成としています。
admin/kanri/shorui/seisan/
├─ index.php ← 一覧・管理画面
└─ pdf.php ← PDF生成・出力処理
tcpdf/
└─ tcpdf.php ← PDFライブラリ
db/
└─ db.php ← データベース接続
PDF生成処理
実際にPHPで作成した請求書のPDFは下記のようなイメージとなります。
請求書PDFは、TCPDFライブラリを使用してHTMLレイアウトから生成する構成としています。
① PDF生成処理(pdf.php)
TCPDFはHTMLをそのままPDF化できる便利なライブラリですが、通常のWebブラウザと異なり、CSSが完全には反映されない場合があります。
実際の開発ではclass指定よりもtableレイアウト+inline styleを中心に構築する方が安定します。
- TCPDFライブラリの読み込みとPDF初期設定
- URLパラメータ(hid_id)から対象データIDを取得
- 請求データ・会社情報をデータベースから取得
- 明細情報を取得しループでHTML行を生成
- 税区分(税別・税込)に応じた金額計算処理
- 明細行数が少ない場合の空行補完によるレイアウト調整
- HTMLテンプレートを組み立てて帳票レイアウトを構築
- TCPDFのwriteHTMLでPDF描画
- ファイル名を自動生成(文字コード変換対応)
- ダウンロード形式でPDFを出力
<?php
require_once '../../../../tcpdf/tcpdf.php';
$pdf = new TCPDF("P", "mm", "A4", true, "UTF-8" );
$pdf->setPrintHeader(false);
$pdf->setPrintFooter(false);
$pdf->AddPage();
$pdf->SetFont('kozminproregular', '', 12);
$pdf->SetMargins(20, 10, true);
if(isset($_GET['hid_id'])){
$st_id = $_GET['hid_id'];
}elseif(isset($_POST['hid_id'])){
$st_id = $_POST['hid_id'];
}
//css
$css = '<style>
table {
text-align: left;
width: 100%;
font-size:10px;
}
th {
vertical-align: middle;
background-color: rgb(153, 153, 153);
text-align: center;"
padding: 3px 3px;
}
th.num {
vertical-align: middle;
background-color: rgb(153, 153, 153);
text-align: right;"
}
td {
vertical-align: middle;
text-align: center;"
padding: 3px 3px;
}
td.num {
vertical-align: middle;
text-align: right;
}
td.migi {
vertical-align: middle;
text-align: right;
}
table.noline {
border:none;
}
table.noline th {
border:none;
width:60%;
background-color:#fff;
text-align:left;
}
table.noline td {
border:none;
width:40%;
background-color:#fff;
text-align:left;
font-size:10px;
}
table.noline2 {
border:none;
}
table.noline2 th {
border:none;
width:60%;
background-color:#fff;
text-align:left;
}
table.noline2 td {
border:none;
background-color:#fff;
text-align:left;
font-size:10px;
}
.strleft {
float:left;
text-align:left;
}
.strright {
float:right;
text-align:right;
}
.relative {
position: relative;
}
.absolute_test {
position: relative;
text-align:right;
}
.absolute_test img{
width:20px;
height:20px;
float:right;
text-align:right;
}
</style>';
require "../../../db/db.php";
$sum = 0;
$sql = "SELECT
seisan.goukei, seisan.zeigoukei, seisan.s_kenmei, seisan.s_no,
seisan.s_hakko, seisan.s_company, seisan.s_yukou, seisan.s_siharai,
seisan.s_nohin, seisan.zeiritu, seisan.zeikubun
FROM seisan
WHERE seisan.id = :id";
$params = [':id' => $st_id];
$result = execPrep($sql, $params);
foreach( $result as $row ) {
$s_no=$row["s_no"];
$s_hakko=$row["s_hakko"];
$s_company=$row["s_company"];
$s_yukou=$row["s_yukou"];
$s_siharai=$row["s_siharai"];
$s_nohin=$row["s_nohin"];
$s_kenmei=$row["s_kenmei"];
$zeiritu=$row["zeiritu"];
$zeikubun=$row["zeikubun"];
$total=$row["goukei"];
$totalzei=$row["zeigoukei"];
}
$sql = "SELECT * FROM companyinfo;";
$result = execPrep($sql);
foreach( $result as $row ) {
$h_company=$row["h_company"];
$h_simei=$row["h_simei"];
$h_busho=$row["h_busho"];
$h_yubin=$row["h_yubin"];
$h_jyusho=$row["h_jyusho"];
$h_tel=$row["h_tel"];
$h_fax=$row["h_fax"];
$h_mail=$row["h_mail"];
$h_hurikomi=$row["h_hurikomi"];
$h_biko=$row["h_biko"];
}
$hinmokucnt=0;
$goukei=0;
$html2='';
$sql = "SELECT name, cnt, tanka, kingaku FROM hinmoku WHERE s_no = :s_no";
$params = [':s_no' => $s_no];
$result = execPrep($sql, $params);
foreach( $result as $row ) {
$hinmokucnt=$hinmokucnt+1;
$goukei=$goukei+$row["kingaku"];
$html2.='<tr>';
$html2.='<td class="num">'.$hinmokucnt.'</td>';
$html2.='<td class="strleft">'.$row["name"].'</td>';
$html2.='<td>'.$row["cnt"].'</td>';
$html2.='<td class="num">'.number_format($row["tanka"]).'</td>';
$html2.='<td class="num">'.number_format($row["kingaku"]).'</td>';
$html2.='</tr>';
}
if($zeikubun == '税込み'){
$zeigaku = 0;
$kei=$total;
$kei=number_format($kei);
}else{
$zeigaku = $totalzei-$total;
$kei=$totalzei;
$kei=number_format($kei);
}
$html = '';
$html.='<div style="text-align: center;font-size: 22pt;font-weight: bold;"> 御請求書 </div>';
$html.= '<br><br>';
$html.= '<table class="noline">';
$html.= '<tr>';
$html.= '<th style="font-size:13px;font-weight:bold;text-decoration: underline;">'.$s_company.' 御中</th>';
$html.= '<td>請求書番号. '.$s_no.'</td>';
$html.= '</tr>';
$html.= '<tr>';
$html.= '<th></th>';
$html.= '<td>登録番号. Txxxxxxxxxxxxx</td>';
$html.= '</tr>';
$html.= '<tr>';
$html.= '<th></th>';
$html.= '<td>発行日. '.date('Y年m月d日', strtotime($s_hakko)).'</td>';
$html.= '</tr>';
$html.= '<tr>';
$html.= '<th></th>';
$html.= '<td><img src="/images/samplelogo.png" width="300" height="90"></td>';
$html.= '</tr>';
$html.= '</table>';
$html.= '<table class="noline">';
$html.= '<tr>';
$html.= '<th style="font-size:13px;font-weight:bold;text-decoration: underline;">件名:'.$s_kenmei.'</th>';
$html.= '<td>'.$h_company.'</td>';
$html.= '</tr>';
$html.= '<tr>';
$html.= '<th>下記の通り御請求申し上げます。</th>';
$html.= '<td>〒'.$h_yubin.'</td>';
$html.= '</tr>';
$html.= '<tr>';
$html.= '<th></th>';
$html.= '<td>'.$h_jyusho.'</td>';
$html.= '</tr>';
$html.= '<tr>';
$html.= '<th></th>';
$html.= '<td></td>';
$html.= '</tr>';
$html.= '<tr>';
$html.= '<th></th>';
$html.= '<td>TEL.'.$h_tel.'</td>';
$html.= '</tr>';
$html.= '<tr>';
$html.= '<th></th>';
$html.= '<td class="strright">FAX.'.$h_fax.'</td>';
$html.= '</tr>';
$html.= '<tr>';
$html.= '<th></th>';
$html.= '<td>MAIL.'.$h_mail.'</td>';
$html.= '</tr>';
$html.= '<tr>';
$html.= '<th></th>';
$html.= '<td>担当者.'.$h_simei.'</td>';
$html.= '</tr>';
if($h_busho <> ''){
$html.= '<tr>';
$html.= '<th></th>';
$html.= '<td>部署.'.$h_busho.'</td>';
$html.= '</tr>';
}
$html.= '<tr>';
$html.= '<th></th>';
$html.= '<td></td>';
$html.= '</tr>';
$html.= '<tr>';
$html.= '<th style="font-size:13px;font-weight:bold;text-decoration: underline;">合計金額 '.$kei.' (税込)</th>';
$html.= '<td style="font-size:11px;font-weight:bold;text-decoration: underline;">支払期限.'.date('Y年m月d日', strtotime($s_yukou)).'</td>';
$html.= '</tr>';
$html.= '</table>';
$html.= '<br>';
$html.='<div style="text-align: center;">';
$html.='<table border="1" cellspacing="0">';
$html.='<tbody>';
$html.='<tr>';
$html.='<th style="width:30px;">No</th>';
$html.='<th style="width:200px;">摘要</th>';
$html.='<th style="width:60px;">数量</th>';
$html.='<th>単価</th>';
$html.='<th>金額</th>';
$html.='</tr>';
$html.=$html2;
if($hinmokucnt < 6){
for($i = 0; $i < 5; $i++){
$html.='<tr>';
$html.='<td class="num"> </td>';
$html.='<td> </td>';
$html.='<td> </td>';
$html.='<td class="num"> </td>';
$html.='<td class="num"> </td>';
$html.='</tr>';
}
}else{
for($i = 0; $i < 1; $i++){
$html.='<tr>';
$html.='<td class="num"> </td>';
$html.='<td> </td>';
$html.='<td> </td>';
$html.='<td class="num"> </td>';
$html.='<td class="num"> </td>';
$html.='</tr>';
}
}
$html.='<tr>';
$html.='<td class="migi" colspan="4">消費税</td>';
$html.='<td class="num">'.number_format($zeigaku).'</td>';
$html.='</tr>';
$html.='<tr>';
$html.='<td class="migi" colspan="4">合 計</td>';
$html.='<td class="num">'.$kei.'</td>';
$html.='</tr>';
$html.='</tbody></table>';
$html.='</div>';
$html.='<table border="1">';
$html.='<tbody>';
$html.= '<tr>';
$html.= '<th style="text-align: left;">振込先</th>';
$html.= '</tr>';
$html.= '<tr>';
$html.= '<td style="text-align: left;">'.nl2br($h_hurikomi).'</td>';
$html.= '</tr>';
$html.='</tbody>';
$html.= '</table>';
$html.= '<br><br>';
$html.='<table border="1">';
$html.='<tbody>';
$html.= '<tr>';
$html.= '<th style="text-align: left;">備考</th>';
$html.= '</tr>';
$html.= '<tr>';
$html.= '<td style="text-align: left;">'.nl2br($h_biko).'</td>';
$html.= '</tr>';
$html.='</tbody>';
$html.= '</table>';
//output
$pdf->writeHTML($css . $html, true, 0, true, 0);
// PDFファイル名を生成
$filename = $s_no."請求書.pdf";
$filename = mb_convert_encoding( $filename, 'SJIS-WIN', 'UTF-8' );
// PDF出力
$pdf_output = $pdf->Output( $filename, "S" );
header( "Pragma: public" );
header( "Expires: 0 ");
header( "Cache-Control: must-revalidate, post-check=0, pre-check=0" );
header( "Content-Transfer-Encoding: binary" );
header( "Content-Type: application/octet-streams" );
header( "Content-Disposition: attachment; filename=\"{$filename}\"" );
print $pdf_output;
exit;
?>
まとめ
請求書や帳票の作成業務では、Excelや手作業でPDFを作成しているケースも多く、作業時間や人的ミスが発生しやすい課題があります。
今回解説したように、PHPとTCPDFを組み合わせることで、データベースの情報をもとに帳票を自動生成し、管理画面からワンクリックでPDF出力できる仕組みを構築することが可能になります。
HTMLベースでレイアウトを設計できるため、請求書・見積書・納品書など様々な帳票フォーマットに応用できる点も大きなメリットです。
- TCPDFライブラリを利用したPDF生成処理
- 管理画面から対象IDを取得して帳票出力
- データベース連携による帳票自動生成
- HTMLレイアウトによる柔軟なデザイン制御
- 明細行数に応じたレイアウト調整処理
- ブラウザから直接ダウンロード可能なPDF出力
LOGZAWEBでは、業務内容に合わせた帳票出力機能やPDF生成システムの開発にも対応しております。
既存システムでは対応できない帳票フォーマットや業務フローをご希望の場合も、お気軽にご相談ください。