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

Revision 20865, 22.2 KB checked in by saiteisan, 13 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        return;
263    }
264
265    /**
266     * デストラクタ.
267     *
268     * @return void
269     */
270    function destroy() {
271        parent::destroy();
272    }
273
274    /**
275     * ファイル情報の初期化を行う.
276     *
277     * @return void
278     */
279    function lfInitFile(&$objUpFile) {
280        $objUpFile->addFile("CSVファイル", 'csv_file', array('csv'), CSV_SIZE, true, 0, 0, false);
281    }
282
283    /**
284     * 入力情報の初期化を行う.
285     *
286     * @param array CSV構造設定配列
287     * @return void
288     */
289    function lfInitParam(&$objFormParam, &$arrCSVFrame) {
290        // 固有の初期値調整
291        $arrCSVFrame = $this->lfSetParamDefaultValue($arrCSVFrame);
292        // CSV項目毎の処理
293        foreach($arrCSVFrame as $item) {
294            if($item['status'] == CSV_COLUMN_STATUS_FLG_DISABLE) continue;
295            //サブクエリ構造の場合は AS名 を使用
296            if(preg_match_all('/\(.+\)[ ]*as[ ]*(.+)$/i', $item['col'], $match, PREG_SET_ORDER)) {
297                $col = $match[0][1];
298            }else{
299                $col = $item['col'];
300            }
301            // HTML_TAG_CHECKは別途実行なので除去し、別保存しておく
302            if(strpos(strtoupper($item['error_check_types']), 'HTML_TAG_CHECK') !== FALSE) {
303                $this->arrTagCheckItem[] = $item;
304                $error_check_types = str_replace('HTML_TAG_CHECK', '', $item['error_check_types']);
305            }else{
306                $error_check_types = $item['error_check_types'];
307            }
308            $arrErrorCheckTypes = explode(',', $error_check_types);
309            foreach($arrErrorCheckTypes as $key => $val) {
310                if(trim($val) == "") {
311                    unset($arrErrorCheckTypes[$key]);
312                }else{
313                    $arrErrorCheckTypes[$key] = trim($val);
314                }
315            }
316            // パラメーター登録
317            $objFormParam->addParam(
318                    $item['disp_name']
319                    , $col
320                    , constant($item['size_const_type'])
321                    , $item['mb_convert_kana_option']
322                    , $arrErrorCheckTypes
323                    , $item['default']
324                    , ($item['rw_flg'] != CSV_COLUMN_RW_FLG_READ_ONLY) ? true : false
325                    );
326        }
327    }
328
329    /**
330     * 入力チェックを行う.
331     *
332     * @return void
333     */
334    function lfCheckError(&$objFormParam) {
335        // 入力データを渡す。
336        $arrRet =  $objFormParam->getHashArray();
337        $objErr = new SC_CheckError_Ex($arrRet);
338        $objErr->arrErr = $objFormParam->checkError(false);
339        // HTMLタグチェックの実行
340        foreach($this->arrTagCheckItem as $item) {
341            $objErr->doFunc(array( $item['disp_name'], $item['col'], $this->arrAllowedTag), array("HTML_TAG_CHECK"));
342        }
343        // このフォーム特有の複雑系のエラーチェックを行う
344        if(count($objErr->arrErr) == 0) {
345            $objErr->arrErr = $this->lfCheckErrorDetail($arrRet, $objErr->arrErr);
346        }
347        return $objErr->arrErr;
348    }
349
350    /**
351     * 保存先テーブル情報の初期化を行う.
352     *
353     * @return void
354     */
355    function lfInitTableInfo() {
356        $objQuery =& SC_Query_Ex::getSingletonInstance();
357        $this->arrOrderColumn = $objQuery->listTableFields('dtb_order');
358        $this->arrShippingColumn = $objQuery->listTableFields('dtb_shipping');
359    }
360
361    /**
362     * 新規受注登録を行う.
363     *
364     *
365     * @param SC_Query $objQuery SC_Queryインスタンス
366     * @param string|integer $line 処理中の行数
367     * @return void
368     */
369    function lfRegistOrder($objQuery, $line = "", &$objFormParam) {
370        // 登録データ対象取得
371        $arrList = $objFormParam->getHashArray();
372        // 登録時間を生成(DBのnow()だとcommitした際、すべて同一の時間になってしまう)
373        $arrList['update_date'] = $this->lfGetDbFormatTimeWithLine($line);
374
375        // 商品テーブルのカラムに存在しているもののうち、Form投入設定されていないデータは上書きしない。
376        $sqlval = SC_Utils_Ex::sfArrayIntersectKeys($arrList, $this->arrOrderColumn);
377
378        // 必須入力では無い項目だが、空文字では問題のある特殊なカラム値の初期値設定
379        $sqlval = $this->lfSetOrderDefaultData($sqlval);
380
381        if($sqlval['order_id'] != "") {
382            $sqlval['create_date'] = $arrList['update_date'];
383            // INSERTの実行
384            $objQuery->insert("dtb_order", $sqlval);
385            // シーケンスの調整
386            $seq_count = $objQuery->currVal('dtb_order_order_id');
387            if($seq_count < $sqlval['order_id']){
388                $objQuery->setVal('dtb_order_order_id', $sqlval['order_id'] + 1);
389            }
390            $order_id = $sqlval['order_id'];
391        } else {
392            // 新規登録
393            $sqlval['order_id'] = $objQuery->nextVal('dtb_order_order_id');
394            $order_id = $sqlval['order_id'];
395            $sqlval['create_date'] = $arrList['update_date'];
396            // INSERTの実行
397            $objQuery->insert("dtb_order", $sqlval);
398        }
399        // 配送先情報を登録する
400        $this->lfRegistShipping($objQuery, $arrList, $order_id);
401        return $order_id;
402    }
403
404    /**
405     * 配送先登録を行う.
406     *
407     * @param SC_Query $objQuery SC_Queryインスタンス
408     * @param array $arrList 受注登録情報配列
409     * @param integer $order_id 受注ID
410     * @return void
411     */
412     function lfRegistShipping($objQuery, $arrList, $order_id) {
413        // 配送先登録情報を生成する。
414        // 配送先テーブルのカラムに存在しているもののうち、Form投入設定されていないデータは上書きしない。
415        $sqlval = SC_Utils_Ex::sfArrayIntersectKeys($arrList, $this->arrShippingColumn);
416        // 必須入力では無い項目だが、空文字では問題のある特殊なカラム値の初期値設定
417        $sqlval = $this->lfSetShippingDefaultData($sqlval);
418       
419        $objQuery->delete("dtb_shipping", "order_id = ?", array($order_id));
420       
421        // 配送日付を timestamp に変換
422        if (!SC_Utils_Ex::isBlank($sqlval['shipping_date'])) {
423            $d = mb_strcut($sqlval["shipping_date"], 0, 10);
424            $arrDate = explode("/", $d);
425            $ts = mktime(0, 0, 0, $arrDate[1], $arrDate[2], $arrDate[0]);
426            $sqlval['shipping_date'] = date("Y-m-d", $ts);
427        }
428        // 非会員購入の場合は shipping_id が存在しない
429        if (!is_numeric($sqlval['shipping_id'])) {
430            $sqlval['shipping_id'] = '0';
431        }
432        $sqlval['order_id'] = $order_id;
433        //$sqlval['product_class_id'] = $objQuery->nextVal('dtb_products_class_product_class_id');
434        $sqlval['create_date'] = $arrList['update_date'];
435        // INSERTの実行
436        $objQuery->insert("dtb_shipping", $sqlval);
437    }
438
439    /**
440     * 初期値の設定
441     *
442     * @param array $arrCSVFrame CSV構造配列
443     * @return array $arrCSVFrame CSV構造配列
444     */
445    function lfSetParamDefaultValue(&$arrCSVFrame) {
446        foreach($arrCSVFrame as $key => $val) {
447            switch($val['col']) {
448                case 'status':
449                    $arrCSVFrame[$key]['default'] = '1';
450                    break;
451                case 'del_flg':
452                    $arrCSVFrame[$key]['default'] = '0';
453                    break;
454                default:
455                    break;
456            }
457        }
458        return $arrCSVFrame;
459    }
460
461    /**
462     * 受注データ登録前に特殊な値の持ち方をする部分のデータ部分の初期値補正を行う
463     *
464     * @param array $sqlval 受注登録情報配列
465     * @return $sqlval 登録情報配列
466     */
467    function lfSetOrderDefaultData(&$sqlval) {
468        if($sqlval['del_flg'] == ""){
469            $sqlval['del_flg'] = '0'; //有効
470        }
471        return $sqlval;
472    }
473
474    /**
475     * 配送先データ登録前に特殊な値の持ち方をする部分のデータ部分の初期値補正を行う
476     *
477     * @param array $sqlval 配送先登録情報配列
478     * @return $sqlval 登録情報配列
479     */
480    function lfSetShippingDefaultData(&$sqlval) {
481        return $sqlval;
482    }
483
484    /**
485     * このフォーム特有の複雑な入力チェックを行う.
486     *
487     * @param array 確認対象データ
488     * @param array エラー配列
489     * @return array エラー配列
490     */
491    function lfCheckErrorDetail($item, $arrErr) {
492        // 顧客IDの存在チェック
493        if(!$this->lfIsDbRecord('dtb_customer', 'customer_id', $item)) {
494            $arrErr['product_class_id'] = "※ 指定の顧客IDは、登録されていません。";
495        }
496        // 対応状況の存在チェック
497        if(!$this->lfIsArrayRecord($this->arrOrderSTATUS, 'status', $item)) {
498            $arrErr['status'] = "※ 指定の対応状況は、登録されていません。";
499        }
500        // 支払い方法IDの存在チェック
501        if(!$this->lfIsArrayRecordMulti($this->arrPayments, 'product_payment_ids', $item, ',')) {
502            $arrErr['product_payment_ids'] = "※ 指定の支払い方法IDは、登録されていません。";
503        }
504        // 削除フラグのチェック
505        if(array_search('del_flg', $this->arrFormKeyList) !== FALSE
506                and $item['del_flg'] != "") {
507            if(!($item['del_flg'] == "0" or $item['del_flg'] == "1")) {
508                $arrErr['del_flg'] = "※ 削除フラグは「0」(有効)、「1」(削除)のみが有効な値です。";
509            }
510        }
511
512        return $arrErr;
513    }
514
515    // TODO: ここから下のルーチンは汎用ルーチンとして移動が望ましい
516
517    /**
518     * 指定された行番号をmicrotimeに付与してDB保存用の時間を生成する。
519     * トランザクション内のnow()は全てcommit()時の時間に統一されてしまう為。
520     *
521     * @param string $line_no 行番号
522     * @return string $time DB保存用の時間文字列
523     */
524    function lfGetDbFormatTimeWithLine($line_no = '') {
525        $time = date("Y-m-d H:i:s");
526        // 秒以下を生成
527        if($line_no != '') {
528            $microtime = sprintf("%06d", $line_no);
529            $time .= ".$microtime";
530        }
531        return $time;
532    }
533
534    /**
535     * 指定されたキーと複数値の有効性の配列内確認
536     *
537     * @param string $arr チェック対象配列
538     * @param string $keyname フォームキー名
539     * @param array  $item 入力データ配列
540     * @param string $delimiter 分割文字
541     * @return boolean true:有効なデータがある false:有効ではない
542     */
543    function lfIsArrayRecordMulti($arr, $keyname, $item, $delimiter = ',') {
544        if(array_search($keyname, $this->arrFormKeyList) === FALSE) {
545            return true;
546        }
547        if($item[$keyname] == "") {
548            return true;
549        }
550        $arrItems = explode($delimiter, $item[$keyname]);
551        //空項目のチェック 1つでも空指定があったら不正とする。
552        if(array_search("", $arrItems) !== FALSE) {
553            return false;
554        }
555        //キー項目への存在チェック
556        foreach($arrItems as $item) {
557            if(!array_key_exists($item, $arr)) {
558                return false;
559            }
560        }
561        return true;
562    }
563
564    /**
565     * 指定されたキーと複数値の有効性のDB確認
566     *
567     * @param string $table テーブル名
568     * @param string $tblkey テーブルキー名
569     * @param string $keyname フォームキー名
570     * @param array  $item 入力データ配列
571     * @param string $delimiter 分割文字
572     * @return boolean true:有効なデータがある false:有効ではない
573     */
574    function lfIsDbRecordMulti($table, $tblkey, $keyname, $item, $delimiter = ',') {
575        if(array_search($keyname, $this->arrFormKeyList) === FALSE) {
576            return true;
577        }
578        if($item[$keyname] == "") {
579            return true;
580        }
581        $arrItems = explode($delimiter, $item[$keyname]);
582        //空項目のチェック 1つでも空指定があったら不正とする。
583        if(array_search("", $arrItems) !== FALSE) {
584            return false;
585        }
586        $count = count($arrItems);
587        $where = $tblkey ." IN (" . implode(",", array_fill(0, $count, "?")) . ")";
588
589        $objQuery =& SC_Query_Ex::getSingletonInstance();
590        $db_count = $objQuery->count($table, $where, $arrItems);
591        if($count != $db_count) {
592            return false;
593        }
594        return true;
595    }
596
597    /**
598     * 指定されたキーと値の有効性のDB確認
599     *
600     * @param string $table テーブル名
601     * @param string $keyname キー名
602     * @param array  $item 入力データ配列
603     * @return boolean true:有効なデータがある false:有効ではない
604     */
605    function lfIsDbRecord($table, $keyname, $item) {
606        if(array_search($keyname, $this->arrFormKeyList) !== FALSE  //入力対象である
607                and $item[$keyname] != ""   // 空ではない
608                and !$this->objDb->sfIsRecord($table, $keyname, (array)$item[$keyname]) //DBに存在するか
609                ) {
610            return false;
611        }
612        return true;
613    }
614
615    /**
616     * 指定されたキーと値の有効性の配列内確認
617     *
618     * @param string $arr チェック対象配列
619     * @param string $keyname キー名
620     * @param array  $item 入力データ配列
621     * @return boolean true:有効なデータがある false:有効ではない
622     */
623    function lfIsArrayRecord($arr, $keyname, $item) {
624        if(array_search($keyname, $this->arrFormKeyList) !== FALSE //入力対象である
625                and $item[$keyname] != "" // 空ではない
626                and !array_key_exists($item[$keyname], $arr) //配列に存在するか
627                ) {
628            return false;
629        }
630        return true;
631    }
632}
Note: See TracBrowser for help on using the repository browser.