EC-CUBE 2系:MYページに「御請求書」PDF出力機能を追加する
はじめに
EC-CUBE 2系には標準で管理者向けの帳票出力機能(納品書PDF)が搭載されています。
しかし、顧客が自分で請求書を取得できる機能はありません。
今回は MYページの「購入履歴詳細」画面に「請求書」ボタンを追加し、
ログイン中のユーザーが自分の注文に対して御請求書PDFをブラウザで直接開けるようにします。
完成イメージ
購入履歴詳細ページの「再注文」ボタンの横に「請求書」ボタンが表示され、
クリックすると新しいタブで御請求書PDFが開きます。
[ 再注文 ] [ 請求書 ]
既存機能の確認
EC-CUBE 2系には管理者向けに以下の帳票出力機能が実装されています。
/shopman/order/pdf.php?order_id=XXXX
内部では SC_Fpdf クラス(FPDFベース)を使ってPDFを生成します。
今回はこの仕組みをそのまま流用し、ユーザー向けのエントリーポイントを新設します。
実装方針
| 項目 | 方針 |
|---|---|
| 入力フォーム | 不要(管理者と違い、全項目固定値) |
| 発行日 | 注文日(create_date)を自動セット |
| タイトル | 御請求書(固定) |
| PDF出力先 | ブラウザに直接表示(ダウンロードなし) |
| セキュリティ | order_id がログイン中ユーザーのものか必ず検証 |
作成・修正ファイル一覧
| # | ファイル | 種別 | 内容 |
|---|---|---|---|
| 1 | mypage/invoice.php | 新規 | エントリーポイント |
| 2 | data/class/pages/mypage/LC_Page_Mypage_Invoice.php | 新規 | メインロジック(ページクラス) |
| 3 | data/class_extends/page_extends/mypage/LC_Page_Mypage_Invoice_Ex.php | 新規 | 拡張クラス(EC-CUBE慣例) |
| 4 | data/Smarty/templates/simple/mypage/history.tpl | 修正 | 「請求書」ボタン追加 |
実装手順
1. エントリーポイントの作成
mypage/invoice.php を新規作成します。
history.php と同じ構造で、対応する拡張ページクラスを呼び出すだけのシンプルなファイルです。
<?php
/*
* ユーザー向け御請求書 PDF出力
*/
require_once '../require.php';
require_once CLASS_EX_REALDIR . 'page_extends/mypage/LC_Page_Mypage_Invoice_Ex.php';
$objPage = new LC_Page_Mypage_Invoice_Ex();
$objPage->init();
$objPage->process();
2. メインロジック(ページクラス)の作成
data/class/pages/mypage/LC_Page_Mypage_Invoice.php を新規作成します。
ポイントは3つです:
- 親クラス
LC_Page_AbstractMypage_Exを継承することで、未ログイン時のリダイレクトが自動で行われる $objPurchase->getOrder($order_id, $customer_id)に customer_id を渡すことで、他ユーザーの注文IDを指定した場合はエラーになる(セキュリティ)SC_Response_Ex::actionExit()を呼ぶことで、PDFを出力した後にSmartyレンダリングが走らないようにする
<?php
require_once CLASS_EX_REALDIR . 'page_extends/mypage/LC_Page_AbstractMypage_Ex.php';
class LC_Page_Mypage_Invoice extends LC_Page_AbstractMypage_Ex
{
public function init()
{
parent::init();
$this->tpl_mypageno = 'index';
$this->tpl_subtitle = '御請求書';
$this->httpCacheControl('nocache');
}
public function process()
{
parent::process();
}
public function action()
{
$objCustomer = new SC_Customer_Ex();
$objPurchase = new SC_Helper_Purchase_Ex();
// order_id の整数チェック
if (!SC_Utils_Ex::sfIsInt($_GET['order_id'])) {
SC_Utils_Ex::sfDispSiteError(CUSTOMER_ERROR);
}
$order_id = intval($_GET['order_id']);
// ★ セキュリティ:customer_id を渡すことで他ユーザーの注文を弾く
$arrOrder = $objPurchase->getOrder($order_id, $objCustomer->getValue('customer_id'));
if (empty($arrOrder)) {
SC_Utils_Ex::sfDispSiteError(CUSTOMER_ERROR);
}
// 発行日 = 注文日から自動取得
$timestamp = strtotime($arrOrder['create_date']);
$year = date('Y', $timestamp);
$month = date('m', $timestamp);
$day = date('d', $timestamp);
// PDF生成パラメーター(全て固定値)
$arrData = array(
'order_id' => $order_id,
'title' => '御請求書',
'download' => 0, // 0: ブラウザに開く
'type' => 0, // 0: 納品書
'year' => $year,
'month' => $month,
'day' => $day,
'msg1' => 'このたびはお買上げいただきありがとうございます。',
'msg2' => '下記の内容にて納品させていただきます。',
'msg3' => 'ご確認くださいますよう、お願いいたします。',
'disp_point' => 1, // ポイント表記あり
'etc1' => '※※※※※※※※※※ 重要 ※※※※※※※※※※',
'etc2' => '銀行振込の場合、商品の発送は入金確認後となります。下記口座へお振り込みください。',
'etc3' => '振込先口座:PAYPAY銀行(0033) すずめ支店(002) 普通2281992 ユ)エグゼ',
);
// SC_Fpdf_Ex(管理者機能と同じエンジン)でPDF生成・出力
require_once CLASS_REALDIR . 'SC_Fpdf.php';
$objFpdf = new SC_Fpdf_Ex($arrData['download'], $arrData['title']);
$objFpdf->setData($arrData);
$objFpdf->createPdf();
// ★ PDF出力後はここで処理を終了(Smarty描画をスキップ)
SC_Response_Ex::actionExit();
}
}
3. 拡張クラスの作成
EC-CUBE 2系の慣例として、ページクラスの _Ex バージョンを class_extends ディレクトリ以下に置きます。
mypage/invoice.php はこちらを直接 require_once します。
data/class_extends/page_extends/mypage/LC_Page_Mypage_Invoice_Ex.php を新規作成します。
<?php
require_once CLASS_REALDIR . 'pages/mypage/LC_Page_Mypage_Invoice.php';
class LC_Page_Mypage_Invoice_Ex extends LC_Page_Mypage_Invoice
{
public function init()
{
parent::init();
}
public function process()
{
parent::process();
}
}
補足:_Ex クラスはカスタマイズのフックポイントです。
コアを変更せず _Ex クラスだけ編集することでアップデート時の差分を最小化できます。
4. テンプレートに「請求書」ボタンを追加
data/Smarty/templates/simple/mypage/history.tpl の「再注文」フォームを修正し、
同じ row 内にBootstrapスタイルの「請求書」ボタンを追加します。
変更前:
<form action="order.php" method="post" class="margin-bottom-xl">
...
<div class="row">
<div class="col-md-4">
<button class="btn btn-primary btn-block" name="submit">再注文</button>
</div>
</div>
</form>
変更後:
<form action="order.php" method="post" class="margin-bottom-xl">
...
<div class="row">
<div class="col-md-4">
<button class="btn btn-primary btn-block" name="submit">再注文</button>
</div>
<div class="col-md-4">
<a href="<!--{$smarty.const.ROOT_URLPATH}-->mypage/invoice.php?order_id=<!--{$tpl_arrOrderData.order_id|u}-->"
target="_blank"
class="btn btn-default btn-block">請求書</a>
</div>
</div>
</form>
target="_blank"でPDFを新しいタブで開きますbtn btn-default btn-blockで「再注文」と同サイズ・横並びになりますorder_idは Smarty の|uフィルタでURLエンコードしています
セキュリティについて
このような機能で必ず考慮すべき問題が「他ユーザーの請求書を閲覧できてしまわないか」です。
EC-CUBE の SC_Helper_Purchase::getOrder() は第2引数に customer_id を渡すと
そのユーザーの注文のみをSQLで絞り込んで返します。
他人の order_id を指定した場合は空配列が返り、CUSTOMER_ERROR として処理されます。
// 他ユーザーの注文 → $arrOrder が空 → エラー画面へ
$arrOrder = $objPurchase->getOrder($order_id, $objCustomer->getValue('customer_id'));
if (empty($arrOrder)) {
SC_Utils_Ex::sfDispSiteError(CUSTOMER_ERROR);
}
また、LC_Page_AbstractMypage_Ex を継承しているため、
未ログイン状態では自動的にログインページへリダイレクトされます。
テンプレートキャッシュのクリア
テンプレートを修正した後は、Smartyのコンパイル済みキャッシュを削除してください。
# Dockerを使っている場合
docker exec <コンテナ名> bash -c "find /var/www/html/data/cache -type f -delete"
docker exec <コンテナ名> bash -c "find /var/www/html/data/Smarty/cache -type f -delete"
通常環境の場合は data/cache/ と data/Smarty/templates_c/ 以下のファイルを削除してください。
注意点:テンプレートの種類
EC-CUBE 2系は複数のテンプレートを同時に保有できます。
現在使用しているテンプレートを確認し、対応するファイルを修正してください。
| テンプレートディレクトリ | 用途 |
|---|---|
templates/default/ |
標準テンプレート |
templates/simple/ |
Bootstrap3ベースのレスポンシブテンプレート |
templates/sphone/ |
スマートフォン専用テンプレート |
templates/mobile/ |
ガラケー用テンプレート(現在ほぼ不要) |
現在使用しているテンプレートは管理者画面の
システム設定 → パラメーター設定 → CONST_TEMPLATE_NAME で確認できます。
まとめ
| 項目 | 内容 |
|---|---|
| 追加ファイル | 3ファイル(invoice.php, ページクラス2つ) |
| 修正ファイル | 1ファイル(history.tpl) |
| 流用した既存機能 | SC_Fpdf_Ex(管理者向け帳票出力エンジン) |
| セキュリティ対策 | ログインチェック(AbstractMypage)+所有者確認(getOrder with customer_id) |
管理者向けに実装済みの PDF エンジンをそのまま活用できるため、
追加コードは最小限に抑えられています。