source: branches/version-2_11-dev/data/class/pages/admin/order/LC_Page_Admin_Order_UploadCSV.php @ 20854

Revision 20854, 22.3 KB checked in by saiteisan, 12 years ago (diff)

refs #1197 (新規受注CSV登録)

Line 
1<?php
2/*
3 * This file is part of EC-CUBE
4 *
5 * Copyright(c) 2000-2011 LOCKON CO.,LTD. All Rights Reserved.
6 *
7 * http://www.lockon.co.jp/
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22 */
23
24// {{{ requires
25require_once CLASS_EX_REALDIR . 'page_extends/admin/LC_Page_Admin_Ex.php';
26require_once CLASS_EX_REALDIR . 'helper_extends/SC_Helper_CSV_Ex.php';
27
28/**
29 * 受注登録CSVのページクラス.
30 *
31 * @package Page
32 * @author LOCKON CO.,LTD.
33 *
34 */
35class LC_Page_Admin_Order_UploadCSV extends LC_Page_Admin_Ex {
36
37    // }}}
38    // {{{ functions
39
40    /** TAGエラーチェックフィールド情報 */
41    var $arrTagCheckItem;
42
43    /** 受注テーブルカラム情報 (登録処理用) **/
44    var $arrOrderColumn;
45
46    /** 配送先テーブルカラム情報 (登録処理用) **/
47    var $arrShippingColumn;
48
49    /** 登録フォームカラム情報 **/
50    var $arrFormKeyList;
51
52    var $arrRowErr;
53
54    var $arrRowResult;
55
56    /**
57     * Page を初期化する.
58     *
59     * @return void
60     */
61    function init() {
62        parent::init();
63        $this->tpl_mainpage = 'order/upload_csv.tpl';
64        $this->tpl_subnavi = 'order/subnavi.tpl';
65        $this->tpl_mainno = 'order';
66        $this->tpl_subno = 'upload_csv';
67        $this->tpl_subtitle = '新規受注登録CSV';
68        $this->csv_id = '9';
69
70        $masterData = new SC_DB_MasterData_Ex();
71        $this->arrDISP = $masterData->getMasterData("mtb_disp");
72        $this->arrSTATUS = $masterData->getMasterData("mtb_status");
73        $this->arrOrderSTATUS = $masterData->getMasterData("mtb_order_status");
74        $this->arrDELIVERYDATE = $masterData->getMasterData("mtb_delivery_date");
75        $this->arrPayments = SC_Helper_DB_Ex::sfGetIDValueList("dtb_payment", "payment_id", "payment_method");
76        $this->arrInfo = SC_Helper_DB_Ex::sfGetBasisData();
77        $this->arrAllowedTag = $masterData->getMasterData("mtb_allowed_tag");
78        $this->arrTagCheckItem = array();
79    }
80
81    /**
82     * Page のプロセス.
83     *
84     * @return void
85     */
86    function process() {
87        $this->action();
88        $this->sendResponse();
89    }
90
91    /**
92     * Page のアクション.
93     *
94     * @return void
95     */
96    function action() {
97        $this->objDb = new SC_Helper_DB_Ex();
98
99        // CSV管理ヘルパー
100        $objCSV = new SC_Helper_CSV_Ex();
101        // CSV構造読み込み
102        $arrCSVFrame = $objCSV->sfGetCsvOutput($this->csv_id);
103
104        // CSV構造がインポート可能かのチェック
105        if(!$objCSV->sfIsImportCSVFrame($arrCSVFrame) ) {
106            // 無効なフォーマットなので初期状態に強制変更
107            $arrCSVFrame = $objCSV->sfGetCsvOutput($this->csv_id, '', array(), 'no');
108            $this->tpl_is_format_default = true;
109        }
110        // CSV構造は更新可能なフォーマットかのフラグ取得
111        $this->tpl_is_update = $objCSV->sfIsUpdateCSVFrame($arrCSVFrame);
112
113        // CSVファイルアップロード情報の初期化
114        $objUpFile = new SC_UploadFile_Ex(IMAGE_TEMP_REALDIR, IMAGE_SAVE_REALDIR);
115        $this->lfInitFile($objUpFile);
116
117        // パラメータ情報の初期化
118        $objFormParam = new SC_FormParam_Ex();
119        $this->lfInitParam($objFormParam, $arrCSVFrame);
120
121        $objFormParam->setHtmlDispNameArray();
122        $this->arrTitle = $objFormParam->getHtmlDispNameArray();
123
124        switch($this->getMode()) {
125        case 'csv_upload':
126            $this->doUploadCsv($objFormParam, $objUpFile);
127            break;
128        default:
129            break;
130        }
131    }
132
133    /**
134     * 登録/編集結果のメッセージをプロパティへ追加する
135     *
136     * @param integer $line_count 行数
137     * @param stirng $message メッセージ
138     * @return void
139     */
140    function addRowResult($line_count, $message) {
141        $this->arrRowResult[] = $line_count . "行目:" . $message;
142    }
143
144    /**
145     * 登録/編集結果のエラーメッセージをプロパティへ追加する
146     *
147     * @param integer $line_count 行数
148     * @param stirng $message メッセージ
149     * @return void
150     */
151    function addRowErr($line_count, $message) {
152        $this->arrRowErr[] = $line_count . "行目:" . $message;
153    }
154
155    /**
156     * CSVアップロードを実行します.
157     *
158     * @return void
159     */
160    function doUploadCsv(&$objFormParam, &$objUpFile) {
161        // ファイルアップロードのチェック
162        $objUpFile->makeTempFile('csv_file');
163        $arrErr = $objUpFile->checkExists();
164        if (count($arrErr) > 0) {
165            $this->arrErr = $arrErr;
166            return;
167        }
168        // 一時ファイル名の取得
169        $filepath = $objUpFile->getTempFilePath('csv_file');
170        // CSVファイルの文字コード変換
171        $enc_filepath = SC_Utils_Ex::sfEncodeFile($filepath, CHAR_CODE, CSV_TEMP_REALDIR);
172        // CSVファイルのオープン
173        $fp = fopen($enc_filepath, 'r');
174        // 失敗した場合はエラー表示
175        if (!$fp) {
176             SC_Utils_Ex::sfDispError("");
177        }
178
179        // 登録先テーブル カラム情報の初期化
180        $this->lfInitTableInfo();
181
182        // 登録フォーム カラム情報
183        $this->arrFormKeyList = $objFormParam->getKeyList();
184
185        // 登録対象の列数
186        $col_max_count = $objFormParam->getCount();
187        // 行数
188        $line_count = 0;
189
190        $objQuery =& SC_Query_Ex::getSingletonInstance();
191        $objQuery->begin();
192
193        $errFlag = false;
194        $all_line_checked = false;
195
196        while (!feof($fp)) {
197            $arrCSV = fgetcsv($fp, CSV_LINE_MAX);
198
199            // 全行入力チェック後に、ファイルポインターを先頭に戻す
200            if (feof($fp) && !$all_line_checked) {
201                rewind($fp);
202                $line_count = 0;
203                $all_line_checked = true;
204                continue;
205            }
206
207            // 行カウント
208            $line_count++;
209            // ヘッダ行はスキップ
210            if ($line_count == 1) {
211                continue;
212            }
213            // 空行はスキップ
214            if (empty($arrCSV)) {
215                continue;
216            }
217            // 列数が異なる場合はエラー
218            $col_count = count($arrCSV);
219            if ($col_max_count != $col_count) {
220                $this->addRowErr($line_count, "※ 項目数が" . $col_count . "個検出されました。項目数は" . $col_max_count . "個になります。");
221                $errFlag = true;
222                break;
223            }
224            // シーケンス配列を格納する。
225            $objFormParam->setParam($arrCSV, true);
226            $arrRet = $objFormParam->getHashArray();
227            $objFormParam->setParam($arrRet);
228            // 入力値の変換
229            $objFormParam->convParam();
230            // <br>なしでエラー取得する。
231            $arrCSVErr = $this->lfCheckError($objFormParam);
232
233            // 入力エラーチェック
234            if (count($arrCSVErr) > 0) {
235                foreach ($arrCSVErr as $err) {
236                    $this->addRowErr($line_count, $err);
237                }
238                $errFlag = true;
239                break;
240            }
241
242            if ($all_line_checked) {
243                $order_id = $this->lfRegistOrder($objQuery, $line_count, $objFormParam);
244                $arrParam = $objFormParam->getHashArray();
245
246                $this->addRowResult($line_count, "受注ID:".$order_id );
247            }
248        }
249
250        // 実行結果画面を表示
251        $this->tpl_mainpage = 'order/upload_csv_complete.tpl';
252
253        fclose($fp);
254
255        if ($errFlag) {
256            $objQuery->rollback();
257            return;
258        }
259
260        $objQuery->commit();
261
262        // 商品件数カウント関数の実行
263        $this->objDb->sfCountCategory($objQuery);
264        $this->objDb->sfCountMaker($objQuery);
265        return;
266    }
267
268    /**
269     * デストラクタ.
270     *
271     * @return void
272     */
273    function destroy() {
274        parent::destroy();
275    }
276
277    /**
278     * ファイル情報の初期化を行う.
279     *
280     * @return void
281     */
282    function lfInitFile(&$objUpFile) {
283        $objUpFile->addFile("CSVファイル", 'csv_file', array('csv'), CSV_SIZE, true, 0, 0, false);
284    }
285
286    /**
287     * 入力情報の初期化を行う.
288     *
289     * @param array CSV構造設定配列
290     * @return void
291     */
292    function lfInitParam(&$objFormParam, &$arrCSVFrame) {
293        // 固有の初期値調整
294        $arrCSVFrame = $this->lfSetParamDefaultValue($arrCSVFrame);
295        // CSV項目毎の処理
296        foreach($arrCSVFrame as $item) {
297            if($item['status'] == CSV_COLUMN_STATUS_FLG_DISABLE) continue;
298            //サブクエリ構造の場合は AS名 を使用
299            if(preg_match_all('/\(.+\)[ ]*as[ ]*(.+)$/i', $item['col'], $match, PREG_SET_ORDER)) {
300                $col = $match[0][1];
301            }else{
302                $col = $item['col'];
303            }
304            // HTML_TAG_CHECKは別途実行なので除去し、別保存しておく
305            if(strpos(strtoupper($item['error_check_types']), 'HTML_TAG_CHECK') !== FALSE) {
306                $this->arrTagCheckItem[] = $item;
307                $error_check_types = str_replace('HTML_TAG_CHECK', '', $item['error_check_types']);
308            }else{
309                $error_check_types = $item['error_check_types'];
310            }
311            $arrErrorCheckTypes = explode(',', $error_check_types);
312            foreach($arrErrorCheckTypes as $key => $val) {
313                if(trim($val) == "") {
314                    unset($arrErrorCheckTypes[$key]);
315                }else{
316                    $arrErrorCheckTypes[$key] = trim($val);
317                }
318            }
319            // パラメーター登録
320            $objFormParam->addParam(
321                    $item['disp_name']
322                    , $col
323                    , constant($item['size_const_type'])
324                    , $item['mb_convert_kana_option']
325                    , $arrErrorCheckTypes
326                    , $item['default']
327                    , ($item['rw_flg'] != CSV_COLUMN_RW_FLG_READ_ONLY) ? true : false
328                    );
329        }
330    }
331
332    /**
333     * 入力チェックを行う.
334     *
335     * @return void
336     */
337    function lfCheckError(&$objFormParam) {
338        // 入力データを渡す。
339        $arrRet =  $objFormParam->getHashArray();
340        $objErr = new SC_CheckError_Ex($arrRet);
341        $objErr->arrErr = $objFormParam->checkError(false);
342        // HTMLタグチェックの実行
343        foreach($this->arrTagCheckItem as $item) {
344            $objErr->doFunc(array( $item['disp_name'], $item['col'], $this->arrAllowedTag), array("HTML_TAG_CHECK"));
345        }
346        // このフォーム特有の複雑系のエラーチェックを行う
347        if(count($objErr->arrErr) == 0) {
348            $objErr->arrErr = $this->lfCheckErrorDetail($arrRet, $objErr->arrErr);
349        }
350        return $objErr->arrErr;
351    }
352
353    /**
354     * 保存先テーブル情報の初期化を行う.
355     *
356     * @return void
357     */
358    function lfInitTableInfo() {
359        $objQuery =& SC_Query_Ex::getSingletonInstance();
360        $this->arrOrderColumn = $objQuery->listTableFields('dtb_order');
361        $this->arrShippingColumn = $objQuery->listTableFields('dtb_shipping');
362    }
363
364    /**
365     * 新規受注登録を行う.
366     *
367     *
368     * @param SC_Query $objQuery SC_Queryインスタンス
369     * @param string|integer $line 処理中の行数
370     * @return void
371     */
372    function lfRegistOrder($objQuery, $line = "", &$objFormParam) {
373        // 登録データ対象取得
374        $arrList = $objFormParam->getHashArray();
375        // 登録時間を生成(DBのnow()だとcommitした際、すべて同一の時間になってしまう)
376        $arrList['update_date'] = $this->lfGetDbFormatTimeWithLine($line);
377
378        // 商品テーブルのカラムに存在しているもののうち、Form投入設定されていないデータは上書きしない。
379        $sqlval = SC_Utils_Ex::sfArrayIntersectKeys($arrList, $this->arrOrderColumn);
380
381        // 必須入力では無い項目だが、空文字では問題のある特殊なカラム値の初期値設定
382        $sqlval = $this->lfSetOrderDefaultData($sqlval);
383
384        if($sqlval['order_id'] != "") {
385            $sqlval['create_date'] = $arrList['update_date'];
386            // INSERTの実行
387            $objQuery->insert("dtb_order", $sqlval);
388            // シーケンスの調整
389            $seq_count = $objQuery->currVal('dtb_order_order_id');
390            if($seq_count < $sqlval['order_id']){
391                $objQuery->setVal('dtb_order_order_id', $sqlval['order_id'] + 1);
392            }
393            $order_id = $sqlval['order_id'];
394        } else {
395            // 新規登録
396            $sqlval['order_id'] = $objQuery->nextVal('dtb_order_order_id');
397            $order_id = $sqlval['order_id'];
398            $sqlval['create_date'] = $arrList['update_date'];
399            // INSERTの実行
400            $objQuery->insert("dtb_order", $sqlval);
401        }
402        // 配送先情報を登録する
403        $this->lfRegistShipping($objQuery, $arrList, $order_id);
404        return $order_id;
405    }
406
407    /**
408     * 配送先登録を行う.
409     *
410     * @param SC_Query $objQuery SC_Queryインスタンス
411     * @param array $arrList 受注登録情報配列
412     * @param integer $order_id 受注ID
413     * @return void
414     */
415     function lfRegistShipping($objQuery, $arrList, $order_id) {
416        // 配送先登録情報を生成する。
417        // 配送先テーブルのカラムに存在しているもののうち、Form投入設定されていないデータは上書きしない。
418        $sqlval = SC_Utils_Ex::sfArrayIntersectKeys($arrList, $this->arrShippingColumn);
419        // 必須入力では無い項目だが、空文字では問題のある特殊なカラム値の初期値設定
420        $sqlval = $this->lfSetShippingDefaultData($sqlval);
421       
422        $objQuery->delete("dtb_shipping", "order_id = ?", array($order_id));
423       
424        // 配送日付を timestamp に変換
425        if (!SC_Utils_Ex::isBlank($sqlval['shipping_date'])) {
426            $d = mb_strcut($sqlval["shipping_date"], 0, 10);
427            $arrDate = explode("/", $d);
428            $ts = mktime(0, 0, 0, $arrDate[1], $arrDate[2], $arrDate[0]);
429            $sqlval['shipping_date'] = date("Y-m-d", $ts);
430        }
431        // 非会員購入の場合は shipping_id が存在しない
432        if (!is_numeric($sqlval['shipping_id'])) {
433            $sqlval['shipping_id'] = '0';
434        }
435        $sqlval['order_id'] = $order_id;
436        //$sqlval['product_class_id'] = $objQuery->nextVal('dtb_products_class_product_class_id');
437        $sqlval['create_date'] = $arrList['update_date'];
438        // INSERTの実行
439        $objQuery->insert("dtb_shipping", $sqlval);
440    }
441
442    /**
443     * 初期値の設定
444     *
445     * @param array $arrCSVFrame CSV構造配列
446     * @return array $arrCSVFrame CSV構造配列
447     */
448    function lfSetParamDefaultValue(&$arrCSVFrame) {
449        foreach($arrCSVFrame as $key => $val) {
450            switch($val['col']) {
451                case 'status':
452                    $arrCSVFrame[$key]['default'] = '1';
453                    break;
454                case 'del_flg':
455                    $arrCSVFrame[$key]['default'] = '0';
456                    break;
457                default:
458                    break;
459            }
460        }
461        return $arrCSVFrame;
462    }
463
464    /**
465     * 受注データ登録前に特殊な値の持ち方をする部分のデータ部分の初期値補正を行う
466     *
467     * @param array $sqlval 受注登録情報配列
468     * @return $sqlval 登録情報配列
469     */
470    function lfSetOrderDefaultData(&$sqlval) {
471        if($sqlval['del_flg'] == ""){
472            $sqlval['del_flg'] = '0'; //有効
473        }
474        return $sqlval;
475    }
476
477    /**
478     * 配送先データ登録前に特殊な値の持ち方をする部分のデータ部分の初期値補正を行う
479     *
480     * @param array $sqlval 配送先登録情報配列
481     * @return $sqlval 登録情報配列
482     */
483    function lfSetShippingDefaultData(&$sqlval) {
484        return $sqlval;
485    }
486
487    /**
488     * このフォーム特有の複雑な入力チェックを行う.
489     *
490     * @param array 確認対象データ
491     * @param array エラー配列
492     * @return array エラー配列
493     */
494    function lfCheckErrorDetail($item, $arrErr) {
495        // 顧客IDの存在チェック
496        if(!$this->lfIsDbRecord('dtb_customer', 'customer_id', $item)) {
497            $arrErr['product_class_id'] = "※ 指定の顧客IDは、登録されていません。";
498        }
499        // 対応状況の存在チェック
500        if(!$this->lfIsArrayRecord($this->arrOrderSTATUS, 'status', $item)) {
501            $arrErr['status'] = "※ 指定の対応状況は、登録されていません。";
502        }
503        // 支払い方法IDの存在チェック
504        if(!$this->lfIsArrayRecordMulti($this->arrPayments, 'product_payment_ids', $item, ',')) {
505            $arrErr['product_payment_ids'] = "※ 指定の支払い方法IDは、登録されていません。";
506        }
507        // 削除フラグのチェック
508        if(array_search('del_flg', $this->arrFormKeyList) !== FALSE
509                and $item['del_flg'] != "") {
510            if(!($item['del_flg'] == "0" or $item['del_flg'] == "1")) {
511                $arrErr['del_flg'] = "※ 削除フラグは「0」(有効)、「1」(削除)のみが有効な値です。";
512            }
513        }
514
515        return $arrErr;
516    }
517
518    // TODO: ここから下のルーチンは汎用ルーチンとして移動が望ましい
519
520    /**
521     * 指定された行番号をmicrotimeに付与してDB保存用の時間を生成する。
522     * トランザクション内のnow()は全てcommit()時の時間に統一されてしまう為。
523     *
524     * @param string $line_no 行番号
525     * @return string $time DB保存用の時間文字列
526     */
527    function lfGetDbFormatTimeWithLine($line_no = '') {
528        $time = date("Y-m-d H:i:s");
529        // 秒以下を生成
530        if($line_no != '') {
531            $microtime = sprintf("%06d", $line_no);
532            $time .= ".$microtime";
533        }
534        return $time;
535    }
536
537    /**
538     * 指定されたキーと複数値の有効性の配列内確認
539     *
540     * @param string $arr チェック対象配列
541     * @param string $keyname フォームキー名
542     * @param array  $item 入力データ配列
543     * @param string $delimiter 分割文字
544     * @return boolean true:有効なデータがある false:有効ではない
545     */
546    function lfIsArrayRecordMulti($arr, $keyname, $item, $delimiter = ',') {
547        if(array_search($keyname, $this->arrFormKeyList) === FALSE) {
548            return true;
549        }
550        if($item[$keyname] == "") {
551            return true;
552        }
553        $arrItems = explode($delimiter, $item[$keyname]);
554        //空項目のチェック 1つでも空指定があったら不正とする。
555        if(array_search("", $arrItems) !== FALSE) {
556            return false;
557        }
558        //キー項目への存在チェック
559        foreach($arrItems as $item) {
560            if(!array_key_exists($item, $arr)) {
561                return false;
562            }
563        }
564        return true;
565    }
566
567    /**
568     * 指定されたキーと複数値の有効性のDB確認
569     *
570     * @param string $table テーブル名
571     * @param string $tblkey テーブルキー名
572     * @param string $keyname フォームキー名
573     * @param array  $item 入力データ配列
574     * @param string $delimiter 分割文字
575     * @return boolean true:有効なデータがある false:有効ではない
576     */
577    function lfIsDbRecordMulti($table, $tblkey, $keyname, $item, $delimiter = ',') {
578        if(array_search($keyname, $this->arrFormKeyList) === FALSE) {
579            return true;
580        }
581        if($item[$keyname] == "") {
582            return true;
583        }
584        $arrItems = explode($delimiter, $item[$keyname]);
585        //空項目のチェック 1つでも空指定があったら不正とする。
586        if(array_search("", $arrItems) !== FALSE) {
587            return false;
588        }
589        $count = count($arrItems);
590        $where = $tblkey ." IN (" . implode(",", array_fill(0, $count, "?")) . ")";
591
592        $objQuery =& SC_Query_Ex::getSingletonInstance();
593        $db_count = $objQuery->count($table, $where, $arrItems);
594        if($count != $db_count) {
595            return false;
596        }
597        return true;
598    }
599
600    /**
601     * 指定されたキーと値の有効性のDB確認
602     *
603     * @param string $table テーブル名
604     * @param string $keyname キー名
605     * @param array  $item 入力データ配列
606     * @return boolean true:有効なデータがある false:有効ではない
607     */
608    function lfIsDbRecord($table, $keyname, $item) {
609        if(array_search($keyname, $this->arrFormKeyList) !== FALSE  //入力対象である
610                and $item[$keyname] != ""   // 空ではない
611                and !$this->objDb->sfIsRecord($table, $keyname, (array)$item[$keyname]) //DBに存在するか
612                ) {
613            return false;
614        }
615        return true;
616    }
617
618    /**
619     * 指定されたキーと値の有効性の配列内確認
620     *
621     * @param string $arr チェック対象配列
622     * @param string $keyname キー名
623     * @param array  $item 入力データ配列
624     * @return boolean true:有効なデータがある false:有効ではない
625     */
626    function lfIsArrayRecord($arr, $keyname, $item) {
627        if(array_search($keyname, $this->arrFormKeyList) !== FALSE //入力対象である
628                and $item[$keyname] != "" // 空ではない
629                and !array_key_exists($item[$keyname], $arr) //配列に存在するか
630                ) {
631            return false;
632        }
633        return true;
634    }
635}
Note: See TracBrowser for help on using the repository browser.