Index: branches/version-2_5-dev/data/class/SC_Product.php
===================================================================
--- branches/version-2_5-dev/data/class/SC_Product.php	(revision 18852)
+++ branches/version-2_5-dev/data/class/SC_Product.php	(revision 18860)
@@ -442,4 +442,33 @@
         }
         return $limit;
+    }
+
+    /**
+     * 在庫を減少させる.
+     *
+     * 指定の在庫数まで, 在庫を減少させる.
+     * 減少させた結果, 在庫数が 0 未満になった場合, 引数 $quantity が 0 の場合は,
+     * 在庫の減少を中止し, false を返す.
+     * 在庫の減少に成功した場合は true を返す.
+     *
+     * @param integer $productClassId 商品規格ID
+     * @param integer $quantity 減少させる在庫数
+     * @return boolean 在庫の減少に成功した場合 true; 失敗した場合 false
+     */
+    function reduceStock($productClassId, $quantity) {
+
+        $productsClass = $this->getDetailAndProductsClass($productClassId);
+        if (($productsClass['stock_unlimited'] != '1'
+             && $productsClass['stock'] < $quantity)
+            || $quantity == 0) {
+            return false;
+        }
+
+        $objQuery =& SC_Query::getSingletonInstance();
+        $objQuery->update('dtb_products_class', array(),
+                          "product_class_id = ?", array($productClassId),
+                          array('stock' => 'stock - ?'), array($quantity));
+        // TODO エラーハンドリング
+        return true;
     }
 
Index: branches/version-2_5-dev/data/class/pages/shopping/LC_Page_Shopping_Confirm.php
===================================================================
--- branches/version-2_5-dev/data/class/pages/shopping/LC_Page_Shopping_Confirm.php	(revision 18859)
+++ branches/version-2_5-dev/data/class/pages/shopping/LC_Page_Shopping_Confirm.php	(revision 18860)
@@ -83,25 +83,21 @@
         $this->cartdown = $objDb->chkCartDown($objCartSess);
 
-        // カート集計処理
+
         $this->cartKey = $_SESSION['cartKey'];
-        $cartItems = $objCartSess->getCartList($this->cartKey);
-        $i = 0;
-        // TODO リファクタリング
-        foreach (array_keys($cartItems) as $itemKey) {
-            $cartItem =& $cartItems[$itemKey];
-            if (!SC_Utils_Ex::isBlank($cartItem)) {
-                $this->cartItems[$i] =& $cartItem;
-                $i++;
-            }
-        }
+
+        // カート内商品のチェック
         $this->tpl_message = $objCartSess->checkProducts($this->cartKey);
+        if (strlen($this->tpl_message) >= 1) {
+            SC_Utils_Ex::sfDispSiteError(SOLD_OUT, '', true);
+        }
+
+        // カートの商品を取得
+        $this->cartItems = $objCartSess->getCartList($this->cartKey);
+        // 合計金額
         $this->tpl_total_pretax[$this->cartKey] = $objCartSess->getAllProductsTotal($this->cartKey);
+        // 税額
         $this->tpl_total_tax[$this->cartKey] = $objCartSess->getAllProductsTax($this->cartKey);
         // ポイント合計
         $this->tpl_total_point[$this->cartKey] = $objCartSess->getAllProductsPoint($this->cartKey);
-
-        if (strlen($this->tpl_message) >= 1) {
-            SC_Utils_Ex::sfDispSiteError(SOLD_OUT, '', true);
-        }
 
         // TODO リファクタリング
@@ -159,4 +155,7 @@
                 $this->sendRedirect($this->getLocation(URL_SHOP_MODULE));
             }else{
+                // 受注を完了し, 購入完了ページへ
+                $objPurchase = new SC_Helper_Purchase_Ex();
+                $objPurchase->completeOrder();
                 $this->sendRedirect($this->getLocation(URL_SHOP_COMPLETE));
             }
Index: branches/version-2_5-dev/data/class/pages/shopping/LC_Page_Shopping_Payment.php
===================================================================
--- branches/version-2_5-dev/data/class/pages/shopping/LC_Page_Shopping_Payment.php	(revision 18859)
+++ branches/version-2_5-dev/data/class/pages/shopping/LC_Page_Shopping_Payment.php	(revision 18860)
@@ -114,14 +114,5 @@
         // カート内商品の集計処理を行う
         $this->cartKey = $_SESSION['cartKey'];
-        $cartItems = $objCartSess->getCartList($this->cartKey);
-        $i = 0;
-        // TODO リファクタリング
-        foreach (array_keys($cartItems) as $itemKey) {
-            $cartItem =& $cartItems[$itemKey];
-            if (!SC_Utils_Ex::isBlank($cartItem)) {
-                $this->cartItems[$i] =& $cartItem;
-                $i++;
-            }
-        }
+        $this->cartItems = $objCartSess->getCartList($this->cartKey);
         $this->tpl_message = $objCartSess->checkProducts($this->cartKey);
 
@@ -174,6 +165,6 @@
         }
 
-        // 購入金額の取得得
-        $total_pretax = $objCartSess->getAllProductsTotal();
+        // 購入金額の取得
+        $total_pretax = $objCartSess->getAllProductsTotal($this->cartKey);
         // 支払い方法の取得
         $this->arrPayment = $this->lfGetPayment($total_pretax);
Index: branches/version-2_5-dev/data/class/pages/shopping/LC_Page_Shopping_Complete.php
===================================================================
--- branches/version-2_5-dev/data/class/pages/shopping/LC_Page_Shopping_Complete.php	(revision 18858)
+++ branches/version-2_5-dev/data/class/pages/shopping/LC_Page_Shopping_Complete.php	(revision 18860)
@@ -62,80 +62,9 @@
 
         $objView = new SC_SiteView();
-        $this->objSiteSess = new SC_SiteSession();
-        $this->objCartSess = new SC_CartSession();
-        $this->objCampaignSess = new SC_CampaignSession();
         $objSiteInfo = $objView->objSiteInfo;
         $this->arrInfo = $objSiteInfo->data;
-        $this->objCustomer = new SC_Customer();
-        $mailHelper = new SC_Helper_Mail_Ex();
-
-        // 前のページで正しく登録手続きが行われたか判定
-        SC_Utils_Ex::sfIsPrePage($this->objSiteSess);
-        // ユーザユニークIDの取得と購入状態の正当性をチェック
-        $uniqid = SC_Utils_Ex::sfCheckNormalAccess($this->objSiteSess, $this->objCartSess);
-        if ($uniqid != "") {
-
-            // 完了処理
-            $objQuery = new SC_Query();
-            $objQuery->begin();
-            $order_id = $this->lfDoComplete($objQuery, $uniqid);
-            $objQuery->commit();
-
-            // セッションに保管されている情報を更新する
-            $this->objCustomer->updateSession();
-
-            // 完了メール送信
-            if($order_id != "") {
-                $mailHelper->sfSendOrderMail($order_id, '1');
-            }
-
-            // その他情報の取得
-            $arrResults = $objQuery->getAll("SELECT memo02, memo05 FROM dtb_order WHERE order_id = ? ", array($order_id));
-
-            if (count($arrResults) > 0) {
-                if (isset($arrResults[0]["memo02"]) || isset($arrResults[0]["memo05"])) {
-                    // 完了画面で表示する決済内容
-                    $arrOther = unserialize($arrResults[0]["memo02"]);
-                    // 完了画面から送信する決済内容
-                    $arrModuleParam = unserialize($arrResults[0]["memo05"]);
-
-                    // データを編集
-                    foreach($arrOther as $key => $val){
-                        // URLの場合にはリンクつきで表示させる
-                        if (preg_match('/^(https?|ftp)(:\/\/[-_.!~*\'()a-zA-Z0-9;\/?:\@&=+\$,%#]+)$/', $val["value"])) {
-                            $arrOther[$key]["value"] = "<a href='". $val["value"] . "' target=\"_blank\">" . $val["value"] ."</a>";
-                        }
-                    }
-
-                    $this->arrOther = $arrOther;
-                    $this->arrModuleParam = $arrModuleParam;
-                }
-            }
-
-            // アフィリエイト用コンバージョンタグの設定
-            $this->tpl_conv_page = AFF_SHOPPING_COMPLETE;
-            $this->tpl_aff_option = "order_id=$order_id";
-            //合計価格の取得
-            $total = $objQuery->get("dtb_order", "total", "order_id = ? ", array($order_id));
-            if($total != "") {
-                $this->tpl_aff_option.= "|total=$total";
-            }
-
-            // TradeSafe連携用
-            if (function_exists('sfTSRequest')) {
-                sfTSRequest($order_id);
-            }
-        }
-
-        // キャンペーンからの遷移かチェック
-        $this->is_campaign = $this->objCampaignSess->getIsCampaign();
-        $this->campaign_dir = $this->objCampaignSess->getCampaignDir();
 
         $objView->assignobj($this);
-        // フレームを選択(キャンペーンページから遷移なら変更)
-        $this->objCampaignSess->pageView($objView);
-
-        // セッション開放
-        $this->objCampaignSess->delCampaign();
+        $objView->display(SITE_FRAME);
     }
 
Index: branches/version-2_5-dev/data/class/helper/SC_Helper_DB.php
===================================================================
--- branches/version-2_5-dev/data/class/helper/SC_Helper_DB.php	(revision 18859)
+++ branches/version-2_5-dev/data/class/helper/SC_Helper_DB.php	(revision 18860)
@@ -1092,4 +1092,5 @@
      * @param integer $order_temp_id 受注一時ID
      * @return array 受注一時情報の配列
+     * @deprecated SC_Helper_Purchase::getOrderTemp() を使用して下さい
      */
     function sfGetOrderTemp($order_temp_id) {
Index: branches/version-2_5-dev/data/class/helper/SC_Helper_Purchase.php
===================================================================
--- branches/version-2_5-dev/data/class/helper/SC_Helper_Purchase.php	(revision 18860)
+++ branches/version-2_5-dev/data/class/helper/SC_Helper_Purchase.php	(revision 18860)
@@ -0,0 +1,230 @@
+<?php
+/*
+ * This file is part of EC-CUBE
+ *
+ * Copyright(c) 2000-2010 LOCKON CO.,LTD. All Rights Reserved.
+ *
+ * http://www.lockon.co.jp/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+/**
+ * 商品購入関連のヘルパークラス.
+ *
+ * TODO 購入時強制会員登録機能(#521)の実装を検討
+ * TODO dtb_customer.buy_times, dtb_customer.buy_total の更新
+ *
+ * @package Helper
+ * @author Kentaro Ohkouchi
+ * @version $Id$
+ */
+class SC_Helper_Purchase {
+
+    /**
+     * 受注を完了する.
+     */
+    function completeOrder() {
+        $objQuery =& SC_Query::getSingletonInstance();
+        $objSiteSession = new SC_SiteSession();
+        $objCartSession = new SC_CartSession();
+        $objCustomer = new SC_Customer();
+        $customerId = $objCustomer->getValue('customer_id');
+
+        $objQuery->begin();
+        SC_Utils_Ex::sfIsPrePage($objSiteSession);
+        $uniqId = SC_Utils_Ex::sfCheckNormalAccess($objSiteSession,
+                                                   $objCartSession);
+        $orderTemp = $this->getOrderTemp($uniqId);
+
+        if ($objCustomer->isLoginSuccess(true)) {
+            $this->registerOtherDeliv($uniqId, $customerId);
+        }
+
+        $orderId = $this->registerOrder($orderTemp, $objCartSession,
+                                        $_SESSION['cartKey']);
+        $objQuery->commit();
+        $objCustomer->updateSession();
+        $this->sendOrderMail($orderId);
+    }
+
+    /**
+     * 受注一時情報を取得する.
+     *
+     * @param integer $uniqId 受注一時情報ID
+     * @return array 受注一時情報の配列
+     */
+    function getOrderTemp($uniqId) {
+        $objQuery =& SC_Query::getSingletonInstance();
+        return $objQuery->getRow("dtb_order_temp", "*", "order_temp_id = ?",
+                                 array($uniqId));
+    }
+
+    /**
+     * 受注情報を登録する.
+     *
+     * TODO ダウンロード商品の場合の扱いを検討
+     */
+    function registerOrder($orderParams, &$objCartSession, $cartKey) {
+        $objQuery =& SC_Query::getSingletonInstance();
+
+        // 別のお届け先を指定が無ければ, お届け先に登録住所をコピー
+        if ($orderParams['deliv_check'] == "-1") {
+            $keys = array('name01', 'name02', 'kana01', 'kana02', 'pref', 'zip01',
+                          'zip02', 'addr01', 'addr02', 'tel01', 'tel02', 'tel03');
+            foreach ($keys as $key) {
+                $orderParams['deliv_' . $key] = $orderParams['order_' . $key];
+            }
+        }
+
+        // 不要な変数を unset
+        $unsets = array('mailmaga_flg', 'deliv_check', 'point_check', 'password',
+                        'reminder', 'reminder_answer', 'mail_flag', 'session');
+        foreach ($unsets as $unset) {
+            unset($orderParams[$unset]);
+        }
+
+        // ポイントは別登録
+        $addPoint = $orderParams['add_point'];
+        $usePoint = $orderParams['use_point'];
+        $orderParams['add_point'] = 0;
+        $orderParams['use_point'] = 0;
+
+        // 注文ステータスの指定が無い場合は新規受付
+        if(SC_Utils_Ex::isBlank($orderParams['status'])) {
+            $orderParams['status'] = ORDER_NEW;
+        }
+
+        $orderParams['create_date'] = 'Now()';
+        $orderParams['update_date'] = 'Now()';
+
+        $objQuery->insert("dtb_order", $orderParams);
+
+        // 受注.対応状況の更新
+        SC_Helper_DB_Ex::sfUpdateOrderStatus($orderParams['order_id'],
+                                             null, $addPoint, $usePoint);
+
+        // 詳細情報を取得
+        $cartItems = $objCartSession->getCartList($cartKey);
+
+        // 既に存在する詳細レコードを消しておく。
+        $objQuery->delete("dtb_order_detail", "order_id = ?",
+                          array($orderParams['order_id']));
+
+        $objProduct = new SC_Product();
+        foreach ($cartItems as $item) {
+            $p =& $item['productsClass'];
+            $detail['order_id'] = $orderParams['order_id'];
+            $detail['product_id'] = $p['product_id'];
+            $detail['product_class_id'] = $p['product_class_id'];
+            $detail['product_name'] = $p['name'];
+            $detail['product_code'] = $p['product_code'];
+            $detail['classcategory_name1'] = $p['classcategory_name1'];
+            $detail['classcategory_name2'] = $p['classcategory_name2'];
+            $detail['point_rate'] = $item['point_rate'];
+            $detail['price'] = $item['price'];
+            $detail['quantity'] = $item['quantity'];
+
+            // 在庫の減少処理
+            if (!$objProduct->reduceStock($p['product_class_id'], $item['quantity'])) {
+                $objQuery->rollback();
+                SC_Utils_Ex::sfDispSiteError(SOLD_OUT, "", true);
+            }
+            $objQuery->insert("dtb_order_detail", $detail);
+        }
+
+        $objQuery->update("dtb_order_temp", array('del_flg' => 1),
+                          "order_temp_id = ?",
+                          array(SC_SiteSession::getUniqId()));
+
+        $objCartSession->delAllProducts($cartKey);
+        SC_SiteSession::unsetUniqId();
+        return $orderParams['order_id'];
+    }
+
+    /**
+     * 会員登録住所と配送先住所を比較し, 差異があった場合は新規登録を行う.
+     *
+     * 別のお届け先に同一の配送先住所が存在する場合は登録しない.
+     *
+     * @param string $uniqId 配送先住所を特定するための一時テーブルのユニークID
+     * @param integer $customerId 顧客ID
+     * @return boolean 差異があり新規登録を行った場合 true; それ以外は false
+     */
+    function registerOtherDeliv($uniqId, $customerId) {
+        $keys = array('name01', 'name02', 'kana01', 'kana02', 'tel01', 'tel02',
+                      'tel03', 'zip01', 'zip02', 'pref', 'addr01', 'addr02');
+        $delivCols = "";
+        $cols = "";
+        $i = 0;
+        foreach ($keys as $key) {
+            $delivCols .= "deliv_" . $key;
+            $cols .= $key;
+            if ($i < count($keys) - 1) {
+                $delivCols .= ", ";
+                $cols .= ", ";
+            }
+            $i++;
+        }
+
+        $objQuery =& SC_Query::getSingletonInstance();
+        $orderTemp = $objQuery->select($delivCols, "dtb_order_temp",
+                                       "order_temp_id = ?", array($uniqId),
+                                       MDB2_FETCHMODE_ORDERED);
+
+        $customerAddrs = $objQuery->select($cols, "dtb_customer",
+                                           "customer_id = ?", array($customerId),
+                                           MDB2_FETCHMODE_ORDERED);
+
+        $hasAddr = false;
+        if ($orderTemp[0] != $customerAddrs[0]) {
+            $otherAddrs = $objQuery->select($cols, "dtb_other_deliv",
+                                           "customer_id = ?", array($customerId),
+                                            MDB2_FETCHMODE_ORDERED);
+            foreach ($otherAddrs as $otherAddr) {
+                if ($orderTemp[0] == $otherAddr) {
+                    $hasAddr = true;
+                }
+            }
+        }
+        if ($hasAddr) {
+            $i = 0;
+            foreach ($keys as $key) {
+                $addrs[$key] = $orderTemp[0][$i];
+                $i++;
+            }
+            $addrs['customer_id'] = $customerId;
+            $addrs['order_deliv_id'] = $objQuery->nextVal('dtb_other_deliv_other_deliv_id');
+            $objQuery->insert("dtb_other_deliv", $addrs);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * 受注完了メールを送信する.
+     *
+     * HTTP_USER_AGENT の種別により, 携帯電話の場合は携帯用の文面,
+     * PC の場合は PC 用の文面でメールを送信する.
+     *
+     * @param integer $orderId 受注ID
+     * @return void
+     */
+    function sendOrderMail($orderId) {
+        $mailHelper = new SC_Helper_Mail_Ex();
+        $mailHelper->sfSendOrderMail($orderId,
+                                     SC_MobileUserAgent::isMobile() ? 2 : 1);
+    }
+}
