EC-CUBEのカスタマイズ ギフト包装・のし対応

EC-CUBEの2.1.2の注文画面で、ギフト包装や、リボン、のしを選択できるようにカスタマイズしてみた。
その他、概要はこんな感じ。

  • ギフト包装の種類は、管理画面>基本情報管理>マスタデータ管理で変更可能にする。
  • ギフト包装が有料の場合にも対応する。ただし、有料ギフト包装の金額はポイント対象外。
  • ギフト包装の金額は、管理画面>基本情報管理>マスタデータ管理で変可能にする。
  • MYページの購入履歴画面や注文完了メール内に、選択したギフト包装の種類と金額を載せる。

ロジックの変更はするけど、それは全部継承で対応する方針で。親のクラスには手を入れない。

ちょっと長いので、順番にやってきます。
まずは、データの準備から。

1.ギフト包装の種類のマスタテーブルを作る

以下SQLでギフト用のマスタテーブルを作る。

CREATE TABLE `mtb_gift` (
  `id` smallint(6) NOT NULL default '0',
  `name` text,
  `rank` smallint(6) NOT NULL default '0',
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

DBやEC-CUBEのWikiにあるテーブル仕様書をみてみると、mtb_*がマスタ系になっている。だいたいみんな同じ構造している。
mtb_*の名称が管理画面>基本情報管理>マスタデータ管理で変更できるが、mtb_pref、mtb_zip、mtb_constants はマスタ管理からはずされている。
それについてはLC_Page_Admin_Basis_Masterdata.php::getMasterDataNames() に書いてあった。

2.ギフト包装の種類を入れる。


管理画面>基本情報管理>マスタデータ管理で、さっき作ったmtb_giftを選択して、ギフト包装の種類を登録していく。
ID=0はギフトなしとして使うので、登録しない。1から登録する。

3.受注データ内に、ギフト情報をどのように格納するか

受注テーブル(dtb_order)や、一時的な受注テーブル(dtb_order_temp)を見てみると、text型のmemo1〜memo10という汎用項目として使える列があって、memo1〜4あたりは既に使ってるっぽい。
なので、memo09とmemo10を使ってギフト情報を格納することにした。

  • memo09・・・ギフト包装の種類。0はギフト包装なし。
  • memo10・・・受注したときのギフト包装の価格。

4.ギフト包装の税込金額を定数で設定しておく

以下SQLで新しく定数を登録する。

INSERT INTO `mtb_constants` values('GIFT_PRICE', '577', '523', 'ギフト包装の税込価格');
  • 1つ目のidが定数名。
  • 2つ目のnameに入れるのが定数の値。今回の場合はギフト包装の税込金額。
  • 3番目のrankに入れるのが管理画面での表示順。今入っているレコードの最大+1だと一番下に表示される。
  • 4番目のremarkに入れるのが管理画面でパラメータ入力欄の上に灰色で小さく出るパラメータの説明文。

次に、管理画面>基本情報管理>マスタデータ管理へアクセスし、一回保存する。
そうすると、「マスタテーブル」⇒「定数設定ばっかりするPHPファイル」に変換することでDBをキャッシュするようにしてるファイルが更新される。
data/cache/mtb_*.phpが、それら。


これで、ギフト包装の価格が変わっても管理画面で変更できるー。

5.お支払方法・お届け時間等の指定画面


こんな感じでリストが出るようにする。

PCサイトのテンプレートの修正

data/Smarty/templates/テンプレート名/shopping/payment.tpl
内に以下を加える。

<!--{assign var=key value="memo09"}-->
<span class="attention"><!--{$arrErr[$key]}--></span>
<select name="<!--{$key}-->" style="<!--{$arrErr[$key]|sfGetErrorColor}-->">
<option value="0" selected="selected">ギフトなし</option>
<!--{html_options options=$arrGift selected=$arrForm[$key].value}-->
</select>
モバイルサイトのテンプレートの修正

data/Smarty/templates/テンプレート名/mobile/shopping/deliv_date.tpl
内に以下を加える。

<!--{assign var=key value="memo09"}-->
<!--{if $arrErr[$key] != ""}-->
<font color="red"><!--{$arrErr[$key]}--></font>
<!--{/if}-->
<select name="<!--{$key}-->">
<option value="0">ギフトなし</option>
<!--{html_options options=$arrGift selected=$arrForm[$key].value}-->
</select>
ページクラスの拡張

data/class_extends/page_extends/shopping/LC_Page_Shopping_Payment_Ex.php
init()、lfInitParam()、lfSetOrderTempData()をオーバーライドする。

<?

    function init() {
        parent::init();
        $this->tpl_title = "お支払方法・お届け時間・ギフト包装の指定";
        $masterData = new SC_DB_MasterData_Ex();
        $this->arrGift = $masterData->getMasterData( "mtb_gift",
                                array( "id", "name", "rank" ) );
    }

    function lfInitParam() {
        parent::lfInitParam();
        $this->objFormParam->addParam( "ギフト・のし", "memo09", INT_LEN, "n", array( "MAX_LENGTH_CHECK", "NUM_CHECK") );
        
    }

    //一時受注テーブルからの情報を格納する
    function lfSetOrderTempData($uniqid) {

        $objQuery = new SC_Query();
        $col = "payment_id, use_point, deliv_time_id, message, point_check, deliv_date, memo09";
        $from = "dtb_order_temp";
        $where = "order_temp_id = ?";
        $arrRet = $objQuery->select($col, $from, $where, array($uniqid));
        // DB値の取得
        $this->objFormParam->setParam($arrRet[0]);
        return $this->objFormParam;
    }
?>

6.ご入力内容の確認画面


こんな感じで、注文内容の欄に、前の画面で選択したギフト包装の種類と価格を含めるようにする。
ギフトなしの場合はこの行自体ださないようにする。

PCサイトのテンプレートの修正

data/Smarty/templates/テンプレート名/shopping/confirm.tpl

<!--{if $arrData.memo09 gt 0}-->
<tr>
  <th colspan="4" class="resulttd">ギフト包装 : <!--{$arrGift[$arrData.memo09]}--></th>
  <td class="pricetd"><!--{$smarty.const.GIFT_PRICE|number_format}-->円</td>
</tr>
<!--{/if}-->
モバイルサイトのテンプレートの修正

data/Smarty/templates/テンプレート名/mobile/shopping/confirm.tpl

<!--{if $arrData.memo09 gt 0}-->ギフト包装(<!--{$arrGift[$arrData.memo09]}-->):<!--{$smarty.const.GIFT_PRICE|number_format}--><br><!--{/if}-->

あと、その下のほうにこれも。

<input type="hidden" name="memo09" value="<!--{$arrData.memo09}-->">
ページクラスの拡張

data/class_extends/page_extends/admin/order/LC_Page_Shopping_Confirm_Ex.php
init()をオーバーライドする。

<?
    function init() {
        parent::init();
        $masterData = new SC_DB_MasterData_Ex();
        $this->arrGift = $masterData->getMasterData( "mtb_gift",
                                array( "id", "name", "rank" ) );
    }
?>
お会計ロジックを拡張

data/class_extends/helper_extends/SC_Helper_DB_Ex.php
sfTotalConfirm()をオーバーライド。

<?
    /**
     * 集計情報を元に最終計算を行う.
     *
     * @param array $arrData 各種情報
     * @param LC_Page $objPage LC_Page インスタンス
     * @param SC_CartSession $objCartSess SC_CartSession インスタンス
     * @param array $arrInfo 店舗情報の配列
     * @param SC_Customer $objCustomer SC_Customer インスタンス
     * @return array 最終計算後の配列
     */
    function sfTotalConfirm($arrData, &$objPage, &$objCartSess, $arrInfo, $objCustomer = "") {
        $arrData = parent::sfTotalConfirm($arrData, &$objPage, &$objCartSess, $arrInfo, $objCustomer = "");
        
        // ギフト包装の計算。0はギフト包装なしのこと。
        if ( intval( $arrData["memo09"] ) > 0 ) {
            $arrData['payment_total'] += GIFT_PRICE;
            $arrData['memo10'] = GIFT_PRICE;
        } else {
            $arrData['memo10'] = 0;
        }
        
        return $arrData;
    }
?>

7.購入履歴画面

PCサイトのテンプレートの修正

data/Smarty/templates/テンプレート名/mypage/history.tpl

<!--{if $arrDisp.memo09 != "" && $arrDisp.memo09 gt 0}-->
<tr>
<th colspan="4" class="resulttd">ギフト包装 : <!--{$arrGift[$arrDisp.memo09]}--></th>
<td class="pricetd"><!--{$arrDisp.memo10|escape|number_format}-->円</td>
</tr>
<!--{/if}-->
モバイルサイトのテンプレートの修正

data/Smarty/templates/テンプレート名/mobile/mypage/history_detail.tpl

<!--{if $arrDisp.memo09 != "" && $arrDisp.memo09 gt 0}-->
ギフト包装(<!--{$arrGift[$arrDisp.memo09]}-->):<!--{$arrDisp.memo10|escape|number_format}-->円<br>
<!--{/if}-->
ページクラスの拡張

data/class_extends/page_extends/mypage/LC_Page_Mypage_History_Ex.php
init()、lfGetOrderData()をオーバーライド。

<?
    function init() {
        parent::init();
        $masterData = new SC_DB_MasterData_Ex();
        $this->arrGift = $masterData->getMasterData( "mtb_gift",
                                array( "id", "name", "rank" ) );
    }

    //受注詳細データの取得
    function lfGetOrderData($order_id) {
        //受注番号が数字であれば
        if(SC_Utils_Ex::sfIsInt($order_id)) {
            // DBから受注情報を読み込む
            $objQuery = new SC_Query();
            $col = "order_id, create_date, payment_id, subtotal, tax, use_point, add_point, discount, ";
            $col .= "deliv_fee, charge, payment_total, deliv_name01, deliv_name02, deliv_kana01, deliv_kana02, ";
            $col .= "deliv_zip01, deliv_zip02, deliv_pref, deliv_addr01, deliv_addr02, deliv_tel01, deliv_tel02, deliv_tel03, deliv_time_id, deliv_date, memo09, memo10 ";
            $from = "dtb_order";
            $where = "order_id = ?";
            $arrRet = $objQuery->select($col, $from, $where, array($order_id));
            $arrOrder = $arrRet[0];
            // 受注詳細データの取得
            $arrRet = $this->lfGetOrderDetail($order_id);
            $arrOrderDetail = SC_Utils_Ex::sfSwapArray($arrRet);
            $arrData = array_merge($arrOrder, $arrOrderDetail);
        }
        return $arrData;
    }
?>

モバイルサイト用に、こっちもおなじように拡張。
data/class_extends/page_extends/mypage/LC_Page_Mypage_HistoryDetail_Ex.php

<?
    function mobileInit() {
        parent::mobileInit();
        $masterData = new SC_DB_MasterData_Ex();
        $this->arrGift = $masterData->getMasterData( "mtb_gift",
                                array( "id", "name", "rank" ) );
    }

    //受注詳細データの取得
    function lfGetOrderData($order_id) {
        //受注番号が数字であれば
        if(SC_Utils_Ex::sfIsInt($order_id)) {
            // DBから受注情報を読み込む
            $objQuery = new SC_Query();
            $col = "order_id, create_date, payment_id, subtotal, tax, use_point, add_point, discount, ";
            $col .= "deliv_fee, charge, payment_total, deliv_name01, deliv_name02, deliv_kana01, deliv_kana02, ";
            $col .= "deliv_zip01, deliv_zip02, deliv_pref, deliv_addr01, deliv_addr02, deliv_tel01, deliv_tel02, deliv_tel03, deliv_time_id, deliv_date, memo09, memo10 ";
            $from = "dtb_order";
            $where = "order_id = ?";
            $arrRet = $objQuery->select($col, $from, $where, array($order_id));
            $arrOrder = $arrRet[0];
            // 受注詳細データの取得
            $arrRet = $this->lfGetOrderDetail($order_id);
            $arrOrderDetail = SC_Utils_Ex::sfSwapArray($arrRet);
            $arrData = array_merge($arrOrder, $arrOrderDetail);
        }
        return $arrData;
    }

?>

8.受注完了メール

                                                                                                                    • -

小 計 ¥ 3,021 (うち消費税 ¥144)
値引き ¥ 1,195
送 料 ¥ 1,000
手数料 ¥ 300
ギフト包装:包装+リボン ¥ 577
===============================================================
合 計 ¥ 3,703
===============================================================

こんな感じで、受注完了メールに選択したギフト包装の種類と、価格を載せるようにする。

PCサイト用受注完了メールテンプレートの修正

data/Smarty/templates/テンプレート名/mail_templates/order_mail.tpl

ギフト包装:<!--{$arrOrder.gift_type}--> ¥ <!--{$arrOrder.gift_price|number_format|default:0}-->
モバイルサイト用受注完了メールテンプレートの修正

data/Smarty/templates/テンプレート名/mobile/mail_templates/order_mail.tpl
こっちもおなじ。

ギフト包装:<!--{$arrOrder.gift_type}--> ¥ <!--{$arrOrder.gift_price|number_format|default:0}-->
受注完了メール送信ロジックの拡張

data/class_extends/helper_extends/SC_Helper_Mail_EX.php
コンストラクタと、sfSendOrderMail()をオーバーライド。

<?
    function SC_Helper_Mail_Ex() {
        parent::SC_Helper_Mail();
        $masterData = new SC_DB_MasterData_Ex();
        $this->arrGift = $masterData->getMasterData( "mtb_gift",
                                array( "id", "name", "rank" ) );
    }

    /* 受注完了メール送信 */
    function sfSendOrderMail($order_id, $template_id, $subject = "", $header = "", $footer = "", $send = true) {
        // 省略・・・

        // 都道府県変換
        $objPage->arrOrder['deliv_pref'] = $this->arrPref[$objPage->arrOrder['deliv_pref']];
        
        // ↓このあたりに追加。
        // ギフト包装変換
        $objPage->arrOrder['gift_type'] = $this->arrGift[$arrOrder['memo09']];
        $objPage->arrOrder['gift_price'] = $arrOrder['memo10'];
        // ↑このあたりに追加。

        $objPage->arrOrderDetail = $arrOrderDetail;

        // 省略・・・
    }
?>

9.管理側受注管理


こんなかんじで、管理画面>受注管理>受注管理>受注履歴編集 で、ギフト包装の情報が見れたり、変更できたりできるようにする。
有料に設定したid=1以上のギフト包装から、ギフトなしに変更(その逆も)して「計算結果の確認」や「この内容で変更する」を押したときに金額が変わることにも対応する。


管理画面>受注管理>ステータス管理 で受注番号を選択して出るポップアップのほうも対応する。

テンプレートの修正

data/Smarty/templates/テンプレート名/admin/order/edi.tpl
手数料の行の下あたり。

<tr bgcolor="#ffffff" class="fs12n">
<td colspan="5" align="right">ギフト包装 : 
<!--{assign var=key value="memo09"}-->
<span class="red12"><!--{$arrErr[$key]}--></span>
<select name="<!--{$key}-->" style="<!--{$arrErr[$key]|sfGetErrorColor}-->">	
<option value="0">ギフトなし</option>
<!--{html_options options=$arrGift selected=$arrForm[$key].value}-->
</td>
<td align="right"><!--{$arrForm.memo10.value|number_format}--> 円</td>
</tr>

data/Smarty/templates/テンプレート名/admin/order/disp.tpl
手数料の行の下あたり。

<tr bgcolor="#ffffff" class="fs12n">
<td colspan="4" align="right">ギフト包装 :
<!--{if $arrDisp.memo09 != "" && $arrDisp.memo09 gt 0}-->
    <!--{$arrGift[$arrDisp.memo09]}-->
<!--{else}-->
    ギフトなし
<!--{/if}-->
</td>
<td align="right"><!--{$arrDisp.memo10|number_format}--> 円</td>
</tr>
ページクラスの拡張

data/class_extends/page_extends/admin/order/LC_Page_Admin_Order_Edit_Ex.php
init()、lfInitParam()、lfCheek()をオーバーライド。

<?
    function init() {
        parent::init();
        $masterData = new SC_DB_MasterData_Ex();
        $this->arrGift = $masterData->getMasterData( "mtb_gift",
                                array( "id", "name", "rank" ) );
    }

    function lfInitParam() {
        parent::lfInitParam();
        $this->objFormParam->addParam("ギフト・のし", "memo09", INT_LEN, "n", array("EXIST_CHECK", "MAX_LENGTH_CHECK", "NUM_CHECK"), '0' );
        $this->objFormParam->addParam("ギフト包装料金", "memo10" );
    }

    /* 計算処理 */
    function lfCheek($arrInfo) {
        $arrErr = parent::lfCheek( $arrInfo );
        $arrVal = $this->objFormParam->getHashArray();
        
        // ギフト包装料金の計算。合計とお支払い合計を計算しなおしす。0はギフト包装なしのこと。
        if ( intval( $_POST["memo09"] ) > 0 ) {
            // 合計
            $arrVal['total'] += GIFT_PRICE;
            // お支払い合計
            $arrVal['payment_total'] = $arrVal['total'] - ($arrVal['use_point'] * POINT_VALUE);
            $arrVal['memo10'] = GIFT_PRICE;
        } else {
            $arrVal['memo10'] = 0;
        }
        
        
        $this->objFormParam->setParam( $arrVal );
        
        return $arrErr;
        
    }
?>


これでおしまい。
長かった・・・。
あとは、ポイントのON/OFF機能とか、次期バージョンで載ってるといいなー。