概要
EC-CUBEで商品に対して送料無料(または送料込み)を設定できるようにカスタマイズしたいと思います。
キャンペーンという機能を使うと送料無料を設定できるようなんですが、キャンペーン用にページを用意しなきゃならないみたいで、これはちょっとニュアンスが違うなと。やっぱり「送料別」「送料無料(または送料込み)」を商品ごとに設定できるようにしたいなと思うわけです。
考えている仕様は…
- dtb_productsにpostage_flag*1 を追加。商品登録・編集時に送料区分を選べるようにする。
- 送料無料の商品が1つでも買物かごにあればその買物の送料は無料。
書いてみるとたったこれだけ?って感じですね。
事前調査ではなんかいじらなきゃならないファイルがたくさんあって大変だなぁ…とか思ってましたが意外に簡単にいっちゃうかな?
というわけで、カスタマイズ作業をしながらこの記事を書いていきたいと思います。
商品テーブルに送料区分を追加
項目の追加は「[EC-CUBE]商品情報の項目を追加する」で以前にもやりましたので、流れの説明にとどめますね。詳しくは当該記事を読んでください。
手順は…
- 商品テーブルに送料区分を追加します。
対象テーブル:dtb_products
フィールド名:postage_flag
データ型:tinyint
変換式:”n”*2
バリデーション:”EXIST_CHECK”*3,”NUM_CHECK”*4,”SPTAB_CHECK”*5 - LC_Page_Admin_Products_Product_Exを編集します。
親クラスLC_Page_Admin_Products_Productより下記メソッドをコピー。- lfProductPage()
- lfRegistProduct()
- lfConvertParam()
- lfErrorCheck()
それぞれの項目にpostage_flagに関する記述を追加してください。
- 商品追加・編集画面に送料区分を追加します。
/data/Smarty/templates/custom/admin/products/product.tplに送料区分のフォームを追加。送料区分<span class="red"> *</span> <input name="postage_flag" type="radio" value="1" />checked<!--{/if}-->/>送料込 <input name="postage_flag" type="radio" value="0" />checked<!--{/if}--> />送料別
を任意の場所に追加してください。*6
- 送料区分のデフォルト値をセットします。
2でコピーしたメソッドlfProductPageに以下を追加。if ($this->arrForm['postage_flag'] == "") { $this->arrForm['postage_flag'] = 1; }
- 商品の編集確認画面に送料区分を追加します。
/data/Smarty/templates/custome/admin/products/confirm.tplに送料区分を追加。送料区分 <!--{if $arrForm.postage_flag == 1}--> 送料込 <!--{else}--> 送料別 <!--{/if}-->
- 商品情報を取得するクエリに送料区分を追加します。*7
SC_DB_DBFactory_MYSQL_ExにSC_DB_DBFactory_MYSQLのメソッドviewToSubQuery()をコピーして、returnする配列の要素にそれぞれpostage_flagを追加します。
こんなところでしょう。今テストしてみたら登録はOKでした。
表示はチェックしてないけどDBにデータが入っているのでいかようにもなりますよね。ということで次に進みます。
カスタマイズ箇所の洗い出し
送料を計算する部分に「カート内の商品の送料区分を判定して、送料無料商品を含む場合は送料を0にする」処理を追加したいと思います。今この瞬間作業をしながら書いておりますのでソースを読むところから、臨場感たっぷりにお伝えしていきたいと思います*8 。
じゃあ、ぼつぼつはじめますか。
まずはフロントページで実際の購入手順をたどって、どこで送料を計算しているのか洗ってみたいと思います。
送料計算をしているところはどこ?
商品を買物かごに入れて、配送先を設定して、と買物をすすめていくと、出ました。「ご入力内容のご確認」ページで初めて送料が出てきました。
じゃあ、ブラウザのアドレス欄を確認します。「/html/shopping/confirm.php」とありますね?
この画面を表示するときに通る処理に送料計算ロジックが書かれているはずですので、/data/class/pages/shopping/LC_Page_Shopping_Confirm.phpを開いてみてみましょう*9 。
// カート集計処理 $objDb->sfTotalCart($this, $objCartSess, $arrInfo); // 一時受注テーブルの読込 $arrData = $objDb->sfGetOrderTemp($uniqid); // カート集計を元に最終計算 $arrData = $objDb->sfTotalConfirm($arrData, $this, $objCartSess, $arrInfo, $objCustomer, $objCampaignSess); // キャンペーンからの遷移で送料が無料だった場合の処理 if($objCampaignSess->getIsCampaign()) { $deliv_free_flg = $objQuery->get("dtb_campaign", "deliv_free_flg", "campaign_id = ?", array($objCampaignSess->getCampaignId())); // 送料無料が設定されていた場合 if($deliv_free_flg) { $arrData['payment_total'] -= $arrData['deliv_fee']; $arrData['deliv_fee'] = 0; } }
ちらっと見た感じこの辺が怪しいですよね。
送料無料のキャンペーンを通ってきた場合、その前の「カート集計を元に最終計算」で算出した$arrData[‘payment_total’] から$arrData[‘deliv_fee’]を引くことで総額を修正してから $arrData[‘deliv_fee’] = 0で送料を0円にしているようですね。
最悪、このキャンペーンと同じようにこのメソッドに書いちゃえば機能は実装できそうですが、どこに入れるのがベストなのかを知るためにも、とりあえず一番関係ありそうな「カート集計処理」と「カート集計を元に最終計算」を見てみましょうか。
カート集計処理を見てみる
まず、LC_Page_Shopping_ConfirmクラスのsfTotalCartメソッドを見てみます。
このメソッドは引数で渡された$thisオブジェクト(LC_Page_Shopping_Confirmクラス(もっと言えば_exクラス)のインスタンス)に、「費用合計(税込み)」だとか「消費税合計」だとか「ポイント合計」などのプロパティをセットするために使ってるみたいです。「送料の合計」なんてのもありますね。
「送料の合計」は恐らく「商品に個別の送料を設定する」場合以外には使わないでしょうからこの送料は無視して良いですね。多分この後で判定処理があって個別送料の場合にこのプロパティを使うんでしょう。
ということでここには送料判定処理は入れられませんので、次に進みましょう。
※でももしかしたら送料無料の商品があるかどうかの判定はここでやるかも。その判定結果をLC_Page_Shopping_Confirmのプロパティにセットしてその結果deliv_feeを0みたいな。とりあえずは後回しと。
カート集計を元に最終計算を見てみる
最終計算の前に「一時受注テーブルの読込」があるんだけど、これは最終計算の第1引数で渡されているデータなんで、恐らくsfTotalConfirmを読めばなんとなくわかりそうだということでパス。わからなければ見れば良いだけだしね。
で、読んでみると…ほら、あったあった。
// 商品ごとの送料が有効の場合 if (OPTION_PRODUCT_DELIV_FEE == 1) { $arrData['deliv_fee']+= $objCartSess->getAllProductsDelivFee(); } // 配送業者の送料が有効の場合 if (OPTION_DELIV_FEE == 1) { // 送料の合計を計算する $arrData['deliv_fee'] += $this->sfGetDelivFee($arrData['deliv_pref'], $arrData['payment_id']); } // 送料無料の購入数が設定されている場合 if(DELIV_FREE_AMOUNT > 0) { if($total_quantity >= DELIV_FREE_AMOUNT) { $arrData['deliv_fee'] = 0; } } // 送料無料条件が設定されている場合 if($arrInfo['free_rule'] > 0) { // 小計が無料条件を超えている場合 if($arrData['subtotal'] >= $arrInfo['free_rule']) { $arrData['deliv_fee'] = 0; } }
送料に関する条件ごとに送料を算出する処理がありました。
それぞれの条件がそれぞれのif文になっていて、上から順に判定。上書きされていくみたい。ちょっと処理的に無駄な気はしますが*10 まあこのさい関係ないか。
この二つのメソッドを見る限り「sfTotalCart」でカート内の商品をなめているので、そこで送料無料商品があるかどうか判定し、プロパティとしてフラグをセット。それを「sfTotalConfirm」で送料無料商品があれば送料を0円にするという処理が妥当かと思いますが、いかがでしょう?*11 とりあえずこの形ですすめることにしましょうか。
送料計算ロジックの修正
これでどこをいじればいいかハッキリしましたね。編集対象は…
SC_Helper_DBクラスの子、SC_Helper_DB_Exクラスですね。
対象となるメソッドの「sfTotalCart」と「sfTotalConfirm」を親クラスから子クラスへコピーしましょう。
多分SC_Helper_DB_Exクラス以外にもチェックしなきゃならないクラスはたくさん出てくると思いますので、その都度そのクラスのことも書いていきますね。
それじゃカスタマイズ、Let’s Start!
sfTotalCartメソッドの修正
ちょっとfor文を見て欲しいんですが、規格情報があればDBに存在する商品としてみなし処理を行い、そうでなければ不正な商品としてカートから削除しているみたいなんですが、商品に対して規格って絶対あるんでしたっけ?ってところからわかってないので、ちょっと確認。
ちなみにデバッグには次の関数が用意されているんで活用してください*12 。
SC_Utils::sfPrintR($obj);
ちなみに管理画面->基本情報管理->パラメータ管理のDEBUG_MODEをtrueにしないと表示できないみたいなことが書いてあるんだけど、僕falseで出力されちゃってるんですよね。僕だけ?
他にもSC_Utilsクラスにはたくさん色々な関数が詰まってますので、もっと本格的にカスタマイズする方はぜひ見たほうがいいですね。また、いろいろなところで使えるような汎用関数はSC_Utilsクラスの子クラスSC_Utils_exに定義するとよさそうですね。余談でした。
話を戻すと、規格情報が必ずあるのかどうか、ですがSC_Utils::sfPrintR( $arrData );で一発ですよ。これ商品管理の「規格」とは違うのね。てっきりそうだとばっかり思ってたけど、dtb_products_classのことね。
って、ことはですよ…。
がっかりだよ。もしかしてpostage_flagはdtb_products_classに追加しなきゃだった?
と思ったら大丈夫でした。あはははは。
えーと、for文のしょっぱな、
// 商品規格情報の取得 $arrData = $this->sfGetProductsClass($arrCart[$i]['id']);
で取得してくるデータにpostage_flagを持ってきたいので、sfGetProductsClassメソッドを修正しましょう。
ここで番組の途中ですが臨時レポートをお伝えします。
sfGetProductsClassメソッドを修正する
SC_Helper_DBクラスの子クラスSC_Helper_DB_exにsfGetProductsClassメソッドをコピーしましょう。
取得するフィールドは「$col」で定義しているので、ここに「postage_flag」を追加しましょう。念のためSC_DB_DBFactory_MYSQL_Exのvw_product_classをチェックしてみたけど複雑すぎてワカンネ。でもこのクエリでdeliv_fee拾ってるってことは多分大丈夫だろうと思って再度SC_Utils::sfPrintR( $arrData );
バッチリOKですよ!取れました。
それでは取得してきたpostage_flagを使ってsfTotalCartメソッドに判定処理書いちゃいましょう。
sfTotalCartメソッドの修正を再開!
まず冒頭の以下の部分に送料無料フラグの追加。
$objPage->tpl_total_pretax = 0; // 費用合計(税込み) $objPage->tpl_total_tax = 0; // 消費税合計 $objPage->tpl_total_point = 0; // ポイント合計 // 以下を追加 $objPage->tpl_postage_flag = 0; // 送料無料フラグ
そして、for文の中の最後の方「送料の合計を計算する」の下に、
// 送料無料フラグを判定する if ($arrData['postage_flag'] > 0) { $objPage->tpl_postage_flag = 1; }
を追加しましょう。
そしたら今度はsfTotalConfirmですよ?。
sfTotalConfirmメソッドの修正
LC_Page_Shopping_Confirmクラスのprocessメソッドでは、sfTotalConfirmメソッドを呼ぶときに
// カート集計を元に最終計算 $arrData = $objDb->sfTotalConfirm($arrData, $this, $objCartSess, $arrInfo, $objCustomer, $objCampaignSess);
第二引数で$thisを渡していましたね。
ということは$objPageに参照渡ししているので、先ほどの送料判定フラグの値は$objPage->tpl_postage_flagに入っているはずです。
ちょっとチェックしてみましょう。
SC_Utils::sfPrintR( $objPage->tpl_postage_flag );
はい、取れましたね?
もう、大詰めですね…思えば長かった…。書き始めは10:22ですよ。今15:56。まあご飯食べたり記事を書いたり、記事の余計なところが目に付いて直したりしながらの?今ですけれど。ど?でもいいですね、そんなこと。
フラグがちゃんと取れているのを確認できたので、フラグを利用して$objPage->tpl_postage_flagが1ならば送料を無料にしてやりましょう!「送料無料条件が設定されている場合」の処理の下に以下を追加します。
// 送料無料商品が含まれている場合 if ($objPage->tpl_postage_flag > 0) { $arrData['deliv_fee'] = 0; }
はい。終了ー。
これで恐らく目的のカスタマイズは完了したと思うのですが、やってみなきゃわかりませんね。
ちょっとテストしてみます。しばしお待ちを。。。
現在16:16。テスト完了。送料無料の商品を含む購入の場合は送料が0円になることを確認いたしました。
もし、私の記事に誤りや直した方がよい点などございましたらぜひご指摘いただければと思います。お付き合いのほど誠にありがとうございました 🙂
- 某ショッピングモールの送料区分フィールド名からインスパイア [↩]
- ラジオボタンにするから変換しなくていいかなとは思いますが [↩]
- 必須チェック [↩]
- 数値チェック [↩]
- たしかスペースとかタブが含まれていないかチェック…だったと思う [↩]
- 僕は販売価格の下に追加しました [↩]
- この例はMySQLの場合ね [↩]
- ほんとはた記事をまとめるのが面倒だからという… [↩]
- なんでこのクラスを見るのかわからないという方は僕のブログを最初から最後まで全部の記事を3回読んでください。それでもどうしてもわからないという方がいらっしゃいましたら、僕のブログを3回読んで感じたこと、わかったこと、直して欲しいこと、今後取り扱って欲しい話題などについて書いたレポートを4,000文字以上書いて僕にください。そしたら手取り足取り教えて差し上げたいと思います。なんてね。 [↩]
- まあ前の段階で「個別送料」かどうか判定せずに「送料の合計」とか出してる時点で無駄なんでしょうけれども。効率重視ということでしょうか。往々にしてありますよね。 [↩]
- もっといい方法があれば教えていただけると大変ありがたいです。 [↩]
- なんか偉そうかな。僕。 [↩]
コメント
はじめまして。
いやぁ?本当に素晴らしいですね!!
突然ですいません。
操作に関しての質問ですが、
手順6の「商品情報を取得するクエリに送料区分を追加します」の
PostgreSQLの場合は、どのように操作すればよいのでしょうか?
何卒ご教授ください。
よろしくお願い致します。
現状稼働しているサイトで上記の[EC-CUBE]商品に送料無料フラグをつけたいのですがお願いする事は可能でしょうか?
もし可能であれば連絡して頂けると幸いです。
メールさせていただきました^^