なんとか、ごりごりいじっくって、おためし一番 をオープンさせました!。(知名度が0ですが・・・Orz)

出来てみれば、結構簡単でしたので、公開しといたほうが役に立つかと思うのと、

自分の作業の復習をかねてます。


環境設定

私が使用している環境です。

  EC-CUBEバージョン 2.11.1

  PHPバージョン PHP 5.2.6

   DBバージョン MySQL 5.0.67-community-nt


クール便を考える

ヤマト運輸さん等にあるクール便の設定ですね。

このクール便。よく考えると結構ややこしいです。

クール便には冷蔵と冷凍があります。(クール便ではないのを通常便で説明します)

いわゆる同梱を考えると、


 1.通常便とクール便(冷蔵)は同梱できる。

 2.クール便(冷蔵)の商品は同梱できない。(別口で発送する)

 3.一定金額以上の場合、送料無料にできる。


が考えられます。パターンとしては

 1.通常便のみ

 2.クール便(冷蔵)のみ

 3.クール便(冷凍)のみ

 4.通常便とクール便(冷蔵)

 5.通常便とクール便(冷凍)

 6.通常便とクール便(冷蔵)とクール便(冷凍)

 7.クール便(冷蔵)とクール便(冷凍)

 8.一定料金以上の送料無料

ですね。


これなら、合計金額の算出するロジックの近くに送料を計算するのもあるだろうからなんとかなるかなと!!

カートの商品のループのところにフラグを設けて、計算は後からにすればできるかな。


以上を踏まえて解決編です。


1.商品明細にクール便判定のフラグを設置

   判断できるフィールドがないとさすがになにも出来ないので、商品明細テーブルにフィールドを追加します。

   (はじめ通常商品とクール便(冷蔵)とクール便(冷凍)を別フィールドで設計さ¥したけど、複雑になるので1つのフィールドでやります。)

   追加するフィールドの設定値を下記のように考えました。

    0:通常便

    1:クール便(冷蔵)

    2:クール便(冷凍)

実際には[dtb_products ]テーブルにフィールドを追加します。私は[ hds_default ] で設定しました。

(最初、別フィールドで考えていたのでデフォルト(通常便)の意味ですが1つでいいのでそのまま流用してますw)

各環境(GUI)が違うと思いますので、詳細は省略します。


2.SQLにフィールドを追加

 追加したフィールドをカートに入れらた商品データを検索するロジックのSQL分に追加します。

[ルートフォルダ\data\class\SC_Product.php]内の[function alldtlSQL]のSQLに追加し[function prdclsSQL]にも追加します。


   /**
* 商品詳細の SQL を取得する.
*
* @param string $where 商品詳細の WHERE 句
* @return string 商品詳細の SQL
*/
function alldtlSQL($where = "") {
$whereCause = "";
if (!SC_Utils_Ex::isBlank($where)) {
$whereCause = " WHERE " . $where;
}
/*
* point_rate, deliv_fee は商品規格(dtb_products_class)ごとに保持しているが,
* 商品(dtb_products)ごとの設定なので MAX のみを取得する.
*/
$sql = <<< __EOS__
(
SELECT dtb_products.product_id,
dtb_products.name,
dtb_products.maker_id,
dtb_products.status,
dtb_products.comment1,
dtb_products.comment2,
dtb_products.comment3,
dtb_products.comment4,
dtb_products.comment5,
dtb_products.comment6,
dtb_products.note,
dtb_products.main_list_comment,
dtb_products.main_list_image,
dtb_products.main_comment,
dtb_products.main_image,
dtb_products.main_large_image,
dtb_products.sub_title1,
dtb_products.sub_comment1,
dtb_products.sub_image1,
dtb_products.sub_large_image1,
dtb_products.sub_title2,
dtb_products.sub_comment2,
dtb_products.sub_image2,
dtb_products.sub_large_image2,
dtb_products.sub_title3,
dtb_products.sub_comment3,
dtb_products.sub_image3,
dtb_products.sub_large_image3,
dtb_products.sub_title4,
dtb_products.sub_comment4,
dtb_products.sub_image4,
dtb_products.sub_large_image4,
dtb_products.sub_title5,
dtb_products.sub_comment5,
dtb_products.sub_image5,
dtb_products.sub_large_image5,
dtb_products.sub_title6,
dtb_products.sub_comment6,
dtb_products.sub_image6,
dtb_products.sub_large_image6,
dtb_products.del_flg,
dtb_products.creator_id,
dtb_products.create_date,
dtb_products.update_date,
dtb_products.deliv_date_id,
dtb_products.headline,
dtb_products.sake_flg,
dtb_products.hds_default,
T4.product_code_min,
T4.product_code_max,
T4.price01_min,
T4.price01_max,
T4.price02_min,
T4.price02_max,
T4.stock_min,
T4.stock_max,
T4.stock_unlimited_min,
T4.stock_unlimited_max,
T4.point_rate,
T4.deliv_fee,
T4.class_count
FROM dtb_products
JOIN (
SELECT product_id,
MIN(product_code) AS product_code_min,
MAX(product_code) AS product_code_max,
MIN(price01) AS price01_min,
MAX(price01) AS price01_max,
MIN(price02) AS price02_min,
MAX(price02) AS price02_max,
MIN(stock) AS stock_min,
MAX(stock) AS stock_max,
MIN(stock_unlimited) AS stock_unlimited_min,
MAX(stock_unlimited) AS stock_unlimited_max,
MAX(point_rate) AS point_rate,
MAX(deliv_fee) AS deliv_fee,
COUNT(*) as class_count
FROM dtb_products_class
$whereCause
GROUP BY product_id
) AS T4
ON dtb_products.product_id = T4.product_id
) AS alldtl
__EOS__;
return $sql;
}


  /**
* 商品規格詳細の SQL を取得する.
*
* MEMO: 2.4系 vw_product_classに相当(?)するイメージ
*
* @param string $where 商品詳細の WHERE 句
* @return string 商品規格詳細の SQL
*/
function prdclsSQL($where = "") {
$whereCause = "";
if (!SC_Utils_Ex::isBlank($where)) {
$whereCause = " WHERE " . $where;
}
$sql = <<< __EOS__
(
SELECT dtb_products.*,
dtb_products_class.product_class_id,
dtb_products_class.class_combination_id,
dtb_products_class.product_type_id,
dtb_products_class.product_code,
dtb_products_class.stock,
dtb_products_class.stock_unlimited,
dtb_products_class.sale_limit,
dtb_products_class.price01,
dtb_products_class.price02,
dtb_products_class.deliv_fee,
dtb_products_class.point_rate,
dtb_products_class.down_filename,
dtb_products_class.down_realfilename,
dtb_class_combination.parent_class_combination_id,
dtb_class_combination.classcategory_id,
dtb_class_combination.level as classlevel,
dtb_products.sake_flg,
dtb_products.hds_default,
Tpcm.classcategory_id as parent_classcategory_id,
Tpcm.level as parent_classlevel,
Tcc1.class_id as class_id,
Tcc1.name as classcategory_name,
Tcc2.class_id as parent_class_id,
Tcc2.name as parent_classcategory_name
FROM dtb_products
LEFT JOIN dtb_products_class
ON dtb_products.product_id = dtb_products_class.product_id
LEFT JOIN dtb_class_combination
ON dtb_products_class.class_combination_id = dtb_class_combination.class_combination_id
LEFT JOIN dtb_class_combination as Tpcm
ON dtb_class_combination.parent_class_combination_id = Tpcm.class_combination_id
LEFT JOIN dtb_classcategory as Tcc1
ON dtb_class_combination.classcategory_id = Tcc1.classcategory_id
LEFT JOIN dtb_classcategory as Tcc2
ON Tpcm.classcategory_id = Tcc2.classcategory_id
$whereCause
) as prdcls
__EOS__;
return $sql;
}

  赤字の箇所に追加してます。

  あれ?自分の見ているのとフィールドがないのがある!!(sake_flg とか)って方も絶対いると思いますが、気にしないで下さい。他の機能も実はすでに追加していまして(酒類販売対応)のちのち紹介していこうと思ってます。


これでデータベースからの抽出ができます。ですがこれだけではまだ参照できません。値を格納しなければ、参照できないので次に商品一覧の配列に格納します。


2.商品配列に格納

  SQLで読みだしたデータを配列に格納しないとEC-CUBEでは楽に画面に表示できません。(実際は他のやり方もあるでしょうが・・・)

 ですのでちゃちゃっと配列にいれちゃいましょう!

 [ルートフォルダ\data\class\SC_Product.php]内の[function lists]に追加したフィールドを追加します。


  /**
* SC_Queryインスタンスに設定された検索条件をもとに商品一覧の配列を取得する.
*
* 主に SC_Product::findProductIds() で取得した商品IDを検索条件にし,
* SC_Query::setOrder() や SC_Query::setLimitOffset() を設定して, 商品一覧
* の配列を取得する.
*
* @param SC_Query $objQuery SC_Query インスタンス
* @param array $arrVal 検索パラメータ(ソート条件)の配列
* @return array 商品一覧の配列
*/
function lists(&$objQuery, $arrVal = array()) {
$col = <<< __EOS__
product_id
,product_code_min
,product_code_max
,name
,comment1
,comment2
,comment3
,main_list_comment
,main_image
,main_list_image
,price01_min
,price01_max
,price02_min
,price02_max
,stock_min
,stock_max
,stock_unlimited_min
,stock_unlimited_max
,deliv_date_id
,status
,del_flg
,update_date
,sake_flg
,hds_default
__EOS__;
$res = $objQuery->select($col, $this->alldtlSQL($objQuery->where),
"", $arrVal);
return $res;
}


  これで、配列に入ってロジックないで使えるようになりました!ここから送料計算です!


3.送料計算

  やっと計算できる準備が整いました。[ルートフォルダ\data\class\SC_CartSession.php]内の[function calculate]内で処理します。


  /**
* カートの内容を計算する.
*
* カートの内容を計算し, 下記のキーを保持する連想配列を返す.
*
* - tax: 税額
* - subtotal: カート内商品の小計
* - deliv_fee: カート内商品の合計送料
* - total: 合計金額
* - payment_total: お支払い合計
* - add_point: 加算ポイント
*
* @param integer $productTypeId 商品種別ID
* @param SC_Customer $objCustomer ログイン中の SC_Customer インスタンス
* @param integer $use_point 今回使用ポイント
* @param integer|array $deliv_pref 配送先都道府県ID.
複数に配送する場合は都道府県IDの配列
* @param integer $charge 手数料
* @param integer $discount 値引
* @param integer $deliv_id 配送業者ID
* @return array カートの計算結果の配列
*/
function calculate($productTypeId, &$objCustomer, $use_point = 0,
$deliv_pref = "", $charge = 0, $discount = 0, $deliv_id = 0) {
$objDb = new SC_Helper_DB_Ex();

$total_point = $this->getAllProductsPoint($productTypeId);
$results['tax'] = $this->getAllProductsTax($productTypeId);
$results['subtotal'] = $this->getAllProductsTotal($productTypeId);
$results['deliv_fee'] = 0;

// 商品ごとの送料を加算
if (OPTION_PRODUCT_DELIV_FEE == 1) {
$cartItems = $this->getCartList($productTypeId);
foreach ($cartItems as $item) {
$results['deliv_fee'] += $item['productsClass']['deliv_fee'] * $item['quantity'];
if ($item['productsClass']['hds_default'] == 0) {
         $cool0 += 1;
       }
       if ($item['productsClass']['hds_default'] == 1) {
        $cool1 += 1;
       }
       if ($item['productsClass']['hds_default'] == 2) {
        $cool2 += 1;
       }
}
}
if ($cool1 > 0) {
$results['deliv_fee'] += 315;
}
if ($cool2 > 0) {
$results['deliv_fee'] += 315;
}

// 配送業者の送料を加算
if (OPTION_DELIV_FEE == 1
&& !SC_Utils_Ex::isBlank($deliv_pref)
&& !SC_Utils_Ex::isBlank($deliv_id)) {
$def = $objDb->sfGetDelivFee($deliv_pref, $deliv_id);
$results['deliv_fee'] += $def;
}
if ($cool0 > 0) {
if ($cool2 > 0) {
$results['deliv_fee'] +=$def;
}
}
if ($cool1 > 0) {
if ($cool2 > 0) {
$results['deliv_fee'] +=$def;
}
}
// 送料無料チェック
if ($this->isDelivFree($productTypeId)) {
$results['deliv_fee'] = 0;
}

// 合計を計算
$results['total'] = $results['subtotal'];
$results['total'] += $results['deliv_fee'];
$results['total'] += $charge;
$results['total'] -= $discount;

// お支払い合計
$results['payment_total'] = $results['total'] - $use_point * POINT_VALUE;

// 加算ポイントの計算
if (USE_POINT !== false) {
$results['add_point'] = SC_Helper_DB_Ex::sfGetAddPoint($total_point,
$use_point);
if($objCustomer != "") {
// 誕生日月であった場合
if($objCustomer->isBirthMonth()) {
$results['birth_point'] = BIRTH_MONTH_POINT;
$results['add_point'] += $results['birth_point'];
}
}
if($results['add_point'] < 0) {
$results['add_point'] = 0;
}
}
return $results;
}

 

  オレンジ色の箇所が追加したロジックです。案外少ないかなと。

  少し解説しますと


 商品ループのところで、追加したフィールドの値を参照し、条件が一致すればColl0からColl2の値をプラスして

各商品の配達条件をカウントしています。(通常便が2個で冷蔵便が1個 みたいな感じ)


次にクール便の商品が1つ以上ある場合、315円足してます。


送料無料の処理の前に、同梱時、冷蔵便がある場合、地域配送料を倍にして2個口配送に対応させています。

通常便と冷凍便がある場合と、冷蔵便と冷凍便がある場合は、地域配送料を加算する


これで、8つの配送条件をクリアできたと思います。

送料表示ですべて加算されて表示されるので、他のロジックの変更は要らないはずです。(PDFとか)



気を付けなれればならないことは、クラスにちょくせつロジック変更しているので、バージョンアップしたらきえちゃったってことになるかもしれません。(サブクラス化だれか教えて下さいあせる


カスタマイズするとすれば、送料の表記をより親切にして、クール便でいくら増えたとかを表示できるようにしてあげてもよいかも。


実行結果は、おためし一番 で可動させていますので確認して見て下さい。そのまま買ってもらってもうれしいのですが!!


アメーバ始めたばっかりでだらだらと長くなってなってしまい見づらい個所も多々ありますがご容赦を。


ん!? 実際のデータはどこで登録するの?EC-CUBEの商品登録で出来ないと意味ないじゃん!!ってつっこまれそうなので、次回、


追加したフィールドを商品登録に反映させるにはってことでまた書きますね。


ね!簡単でしょ!ドキドキ