<?php
/**
 * The model file of customer module of ZDOO.
 *
 * @copyright   Copyright 2009-2018 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
 * @license     ZPL (http://zpl.pub/page/zplv12.html)
 * @author      Tingting Dai <daitingting@xirangit.com>
 * @package     customer
 * @version     $Id$
 * @link        http://www.zdoo.com
 */
class customerModel extends model
{
    /**
     * The customer's contracts.
     *
     * @var array 
     * @access public
     */
    public $contracts = array();

    /**
     * The customer's stages.
     *
     * @var array 
     * @access public
     */
    public $stages = array();

    /**
     * Material list.
     *
     * @var bool | array
     * @access public
     */
    public $materialList = false;

    /**
     * Get customer by id.
     *
     * @param  int    $id
     * @access public
     * @return object | bool
     */
    public function getByID($id)
    {
        $customerIdList = $this->getCustomersSawByMe();
        if(empty($customerIdList)) return false;
        if(!in_array($id, $customerIdList)) return false;

        $customer = $this->dao->select('*')->from(TABLE_CUSTOMER)->where('id')->eq($id)->limit(1)->fetch();
        $customer = $this->loadModel('file')->replaceImgURL($customer, 'desc');

        return $customer;
    }

    /**
     * Get my customer id list or allowed view customer.
     *
     * @param  string $type           view|edit
     * @param  array  $customerIdList
     * @param  object $user
     * @access public
     * @return array
     */
    public function getCustomersSawByMe($type = 'view', $customerIdList = array(), $user = null)
    {
        if(!$user) $user = $this->app->user;

        $accountsSawByMe = $this->loadModel('sales', 'crm')->getAccountsSawByMe($user->account, $type);

        $customerList = $this->dao->select('id')->from(TABLE_CUSTOMER)
            ->where(1)
            ->beginIF(!isset($user->rights['crm']['manageall']) and ($user->admin != 'super'))
            ->andWhere('assignedTo', true)->in($accountsSawByMe)
            ->orWhere('public')->eq('1')
            ->markRight(1)
            ->fi()
            ->fetchPairs();

        if($customerIdList)
        {
            foreach($customerList as $id => $customer)
            {
                if(!in_array($id, $customerIdList)) unset($customerList[$id]);
            }
        }

        /* Get customers not assigned to these accounts but theirs orders assigned to. */
        $accountsSawByMe = trim($accountsSawByMe, ',');
        $customers = $this->dao->select('t1.customer')->from(TABLE_ORDER)->alias('t1')
            ->leftJoin(TABLE_CUSTOMER)->alias('t2')->on('t1.customer = t2.id')
            ->where('t1.assignedTo')->in($accountsSawByMe)
            ->andWhere('t2.deleted')->eq('0')
            ->fetchPairs();
        foreach($customers as $customer) $customerList[$customer] = $customer;

        return $customerList;
    }

    /** 
     * Get customer list.
     *
     * @param  string  $mode
     * @param  string  $param
     * @param  string  $relation  client|provider
     * @param  string  $orderBy
     * @param  object  $pager
     * @access public
     * @return array
     */
    public function getList($mode = 'all', $param = '', $relation = 'client', $orderBy = 'id_desc', $pager = null)
    {
        $user      = $this->app->user;
        $canSeeAll = $user->admin == 'super' || !empty($user->rights['crm']['manageall']);

        $customerIdList = array();
        if(!$canSeeAll)
        {
            $customerIdList = $this->getCustomersSawByMe();
            if(empty($customerIdList)) return array();
        }
        $this->session->set('customerIdList', $customerIdList);

        $this->app->loadClass('date', $static = true);
        $thisMonth = date::getThisMonth();
        $thisWeek  = date::getThisWeek();

        if($this->session->customerQuery == false) $this->session->set('customerQuery', ' 1 = 1');
        $customerQuery = $this->loadModel('search')->replaceDynamic($this->session->customerQuery);

        if(strpos($orderBy, 'id') === false) $orderBy .= ', id_desc';

        $category = '';
        if($relation == 'provider' && $param) $category = $this->loadModel('tree')->getFamily($param);

        $customers = $this->dao->select('t1.*, MIN(t2.date) AS nextDate')->from(TABLE_CUSTOMER)->alias('t1')
            ->leftJoin(TABLE_DATING)->alias('t2')->on("t1.id=t2.objectID AND t2.status='wait' AND t2.objectType='customer' AND t2.date!='0000-00-00'")
            ->where('t1.deleted')->eq('0')
            ->beginIF($relation == 'client')->andWhere('t1.relation')->ne('provider')->fi()
            ->beginIF($relation == 'provider')->andWhere('t1.relation')->ne('client')->fi()
            ->beginIF($relation == 'provider' && $param)->andWhere('t1.category')->in($category)->fi()
            ->beginIF($mode == 'assignedTo')->andWhere('t1.assignedTo')->eq($user->account)->fi()
            ->beginIF($mode == 'followedBy')->andWhere('t1.CSM')->eq($user->account)->fi()
            ->beginIF($mode == 'contactedby')->andWhere('t2.account')->eq($user->account)->fi()
            ->beginIF($mode == 'past')->andWhere('t2.date')->lt(helper::today())->fi()
            ->beginIF($mode == 'today')->andWhere('t2.date')->eq(helper::today())->fi()
            ->beginIF($mode == 'tomorrow')->andWhere('t2.date')->eq(formattime(date::tomorrow(), DT_DATE1))->fi()
            ->beginIF($mode == 'thisweek')->andWhere('t2.date')->between($thisWeek['begin'], $thisWeek['end'])->fi()
            ->beginIF($mode == 'thismonth')->andWhere('t2.date')->between($thisMonth['begin'], $thisMonth['end'])->fi()
            ->beginIF($mode == 'public')->andWhere('t1.public')->eq('1')->fi()
            ->beginIF($mode == 'area')->andWhere('t1.area')->eq($param)->fi()
            ->beginIF($mode == 'industry')->andWhere('t1.industry')->eq($param)->fi()
            ->beginIF($mode == 'bysearch')->andWhere($customerQuery)->fi()
            ->beginIF($customerIdList)->andWhere('t1.id')->in($customerIdList)->fi()
            ->groupBy('t1.id')
            ->orderBy($orderBy)
            ->page($pager, 't1.id')
            ->fetchAll('id');

        $this->session->set('customerOnlyCondition', true);
        $this->session->set('customerQueryCondition', $this->dao->get());

        return $customers;
    }

    /** 
     * Get customer pairs.
     *
     * @param  string  $relation
     * @param  bool    $emptyOption
     * @param  string  $orderBy
     * @param  int     $limit
     * @param  string  $customerID
     * @access public
     * @return array
     */
    public function getPairs($relation = '', $emptyOption = true, $orderBy = 'id_desc', $limit = 0, $customerID = '', $keywords = '')
    {
        $customerList   = array();
        $customerIdList = $this->getCustomersSawByMe();

        if($customerIdList)
        {
            if($this->app->clientDevice == 'mobile' && $this->app->viewType == 'mhtml') $limit = 0;
            
            $customers = $this->dao->select('id, name')->from(TABLE_CUSTOMER)
                ->where('deleted')->eq('0')
                ->beginIF($keywords)->andWhere('name')->like('%' . $keywords . '%')->fi()
                ->beginIF($relation == 'client')->andWhere('relation')->ne('provider')->fi()
                ->beginIF($relation == 'provider')->andWhere('relation')->ne('client')->fi()
                ->orderBy($orderBy)
                ->fetchPairs();
            if(!$limit)
            {
                if($emptyOption) return array('') + $customers;
                return $customers;
            }

            if($customerID)
            {
                $idList = explode(',', trim($customerID, ','));
                foreach($idList as $id) if(isset($customers[$id])) $customerList[$id] = $customers[$id];
            }
                
            $i = 1;
            foreach($customers as $id => $name)
            {
                if(!isset($customerIdList[$id])) continue;

                $customerList[$id] = $name;
                if($limit > 0 && ++$i > $limit)
                {
                    $customerList['ajax_search_more'] = $this->lang->more . $this->lang->ellipsis;
                    break;
                }
            }
            krsort($customerList);
        }

        if($emptyOption) $customerList = array('') + $customerList;
        return $customerList;
    }

    /**
     * Get all customer pairs.
     *
     * @Param  array  $customers
     * @access public
     * @return array
     */
    public function getPairsByIDList($customers)
    {
        if(!$customers) return array();

        return $this->dao->select('id, name')->from(TABLE_CUSTOMER)->where('id')->in($customers)->fetchPairs();
    }

    /**
     * Get added amount. 
     * 
     * @param  string $visible   toMe  | canSee
     * @param  string $dateType  month | year 
     * @param  string $dateMode  this  | last
     * @access public
     * @return void
     */
    public function getAddedAmount($visible, $dateType, $dateMode = 'this')
    {
        $customerIdList = $this->getCustomersSawByMe();
        if(empty($customerIdList)) return 0;

        return $this->dao->select('1')
            ->from(TABLE_CUSTOMER)
            ->where('deleted')->eq('0')
            ->andWhere('relation')->ne('provider')->fi()
            ->beginIF($visible == 'toMe')->andWhere('assignedTo')->eq($this->app->user->account)->fi()
            ->beginIF($dateType == 'month' and $dateMode == 'this')->andWhere('createdDate')->like(date('Y-m') . '%')->fi()
            ->beginIF($dateType == 'month' and $dateMode == 'last')->andWhere('createdDate')->like(date('Y-m', strtotime("-1 months")) . '%')->fi()
            ->beginIF($dateType == 'year' and $dateMode == 'this')->andWhere('createdDate')->like(date('Y') . '%')->fi()
            ->beginIF($dateType == 'year' and $dateMode == 'last')->andWhere('createdDate')->like(date('Y', strtotime("-1 year")) . '%')->fi()
            ->andWhere('id')->in($customerIdList)
            ->count();
    }

    /**
     * Get todo amount. 
     * 
     * @param  string $visible   toMe  | canSee
     * @param  string $dateType  month | year 
     * @access public
     * @return void
     */
    public function getTodoAmount($visible, $dateType)
    {
        $customerIdList = $this->getCustomersSawByMe();
        if(empty($customerIdList)) return 0;

        return $this->dao->select('COUNT(DISTINCT c.id) as count')
            ->from(TABLE_CUSTOMER)->alias('c')
            ->leftjoin(TABLE_DATING)->alias('d')
            ->on('c.id=d.objectID')
            ->where('c.deleted')->eq('0')
            ->andWhere('c.relation')->ne('provider')->fi()
            ->andWhere('d.status')->eq('wait')
            ->andWhere('d.objectType')->eq('customer')
            ->beginIF($visible == 'toMe')
            ->andWhere('d.account')->eq($this->app->user->account)
            ->fi()
            ->beginIF($dateType == 'month')->andWhere('d.date')->like(date('Y-m') . '%')->fi()
            ->beginIF($dateType == 'year')->andWhere('d.date')->like(date('Y') . '%')->fi()
            ->andWhere('c.id')->in($customerIdList)
            ->fetch('count');
    }

    /**
     * Get stage info.
     *
     * @param  string $stage
     * @param  int    $customerID
     * @param  int    $contractID
     * @access public
     * @return void
     */
    public function getStageInfo($stage, $customerID, $contractID = '')
    {
        if(empty($this->config->customer->stageTable[$stage])) return array();

        return $this->dao->select('*')->from($this->config->customer->stageTable[$stage])
                   ->where('customer')->eq($customerID)
                   ->beginIF($contractID)->andWhere('contract')->eq($contractID)->fi()
                   ->fetch();
    }

    /**
     * Process next contact date of customers.
     *
     * @param  string $mode
     * @param  array  $customers
     * @param  array  $thisWeek
     * @param  array  $thisMonth
     * @access public
     * @return array
     */
    public function processNextDate($mode, $customers, $thisWeek, $thisMonth)
    {
        $datingList = $this->dao->select('objectID, MIN(date) AS date')->from(TABLE_DATING)
             ->where('status')->eq('wait')
             ->andWhere('objectType')->eq('customer')
             ->andWhere('objectID')->in(array_keys($customers))
             ->beginIF($mode == 'contactedby')->andWhere('account')->eq($this->app->user->account)->fi()
             ->beginIF($mode == 'followedBy')->andWhere('account')->eq($this->app->user->account)->fi()
             ->beginIF($mode == 'past')->andWhere('date')->lt(helper::today())->fi()
             ->beginIF($mode == 'today')->andWhere('date')->eq(helper::today())->fi()
             ->beginIF($mode == 'tomorrow')->andWhere('date')->eq(formattime(date::tomorrow(), DT_DATE1))->fi()
             ->beginIF($mode == 'thisweek')->andWhere('date')->between($thisWeek['begin'], $thisWeek['end'])->fi()
             ->beginIF($mode == 'thismonth')->andWhere('date')->between($thisMonth['begin'], $thisMonth['end'])->fi()
             ->andWhere('date')->ne('0000-00-00')
             ->groupBy('objectID')
             ->fetchPairs();

        foreach($customers as $id => $customer) $customer->nextDate = zget($datingList, $id, $customer->nextDate);

        return $customers;
    }

    /**
     * Create a customer.
     * 
     * @param  object    $customer 
     * @access public
     * @return int|bool
     */
    public function create($customer = null, $relation = 'client')
    {
        $now = helper::now();
        if(empty($customer))
        {
            $customer = fixer::input('post')
                ->add('relation', $relation)
                ->add('createdBy', $this->app->user->account)
                ->add('assignedTo', $this->app->user->account)
                ->add('createdDate', $now)
                ->setDefault('stage', 'communicate')
                ->setDefault('contact', '')
                ->setIF($this->post->name == '' and !$this->post->selectContact, 'name', $this->post->contact)
                ->setIF($relation == 'provider', 'public', 1)
                ->stripTags('desc', $this->config->allowedTags)
                ->remove('address')
                ->get();

            /* check field before insert. */
            $this->dao->insert(TABLE_CUSTOMER)
                ->data($customer, 'position')
                ->check('contact', 'length', 30, 0)
                ->checkFlow();
        }

        if(!empty($customer->name))    $customer->name    = trim($customer->name);
        if(!empty($customer->contact)) $customer->contact = trim($customer->contact);
        if(!$this->post->continue)
        {
            $return = $this->checkUnique($customer);
            if($return['result'] == 'fail') return $return;
        }

        $this->loadModel('contact', 'crm');

        if(!empty($customer->contact))
        {
            $contact = new stdclass();
            $contact->realname    = $customer->contact;
            $contact->customer    = '';
            $contact->gender      = zget($customer, 'gender', 'u');
            $contact->mobile      = zget($customer, 'mobile', '');
            $contact->phone       = zget($customer, 'phone', '');
            $contact->email       = str_replace(array(' ', '，'), ',', zget($customer, 'email', ''));
            $contact->qq          = zget($customer, 'qq', '');
            $contact->weixin      = zget($customer, 'weixin', '');
            $contact->title       = zget($customer, 'title', '');
            $contact->position    = is_array(zget($customer, 'position')) ? zget($customer, 'position') : array() ;
            $contact->createdBy   = $this->app->user->account;
            $contact->createdDate = $now;

            $this->dao->insert(TABLE_CONTACT)->data($contact, 'position')
                ->autoCheck()
                ->checkIF($contact->phone, 'phone', 'length', 20, 7);

            if(dao::isError()) return array('result' => 'fail', 'message' => dao::getError());

            if(!$this->post->continue)
            {
                $return = $this->contact->checkContact($contact);
                if($return['result'] == 'fail') return $return;
            }
        }

        if(isset($customer->customerWeixin)) $customer->weixin = $customer->customerWeixin;
        if(!isset($customer->contact) or $this->post->selectContact) $this->config->customer->require->create = 'name';
        $this->dao->insert(TABLE_CUSTOMER)
            ->data($customer, $skip = 'uid,contact,email,customerWeixin,gender,qq,phone,mobile,continue,selectContact,contactID,title,position')
            ->autoCheck()
            ->batchCheck($this->config->customer->require->create, 'notempty')
            ->exec();

        if(dao::isError()) return array('result' => 'fail', 'message' => dao::getError());
        $customerID = $this->dao->lastInsertID();
        $objectType = $relation == 'provider' ? 'provider' : 'customer';
        $this->loadModel('action')->create($objectType, $customerID, 'Created');

        if(isset($contact))
        {
            $contact->customer = $customerID;
            $return = $this->contact->create($contact);
            if($return['result'] == 'fail') return $return;
        }

        if($this->post->selectContact)
        {
            $contactID = $this->post->contactID;
            $contact   = $this->contact->getByID($contactID);

            $resume = new stdclass();
            $resume->customer = $customerID;
            $resume->contact  = $contactID;
            $resumeID = $this->loadModel('resume', 'crm')->create($contactID, $resume);

            if($resumeID)
            {
                $changes[] = array('field' => 'customer', 'old' => $contact->customer, 'new' => $customerID, 'diff' => '');
                $actionID  = $this->action->create('contact', $contactID, 'Edited');
                $this->action->logHistory($actionID, $changes);
            }
        }

        if(!empty($customerID) && $this->post->address['location'])
        {
            $address = new stdclass();
            $address->objectType = 'customer';
            $address->objectID   = $customerID;
            $address->title      = $this->post->address['title'];
            $address->area       = $this->post->address['area'];
            $address->location   = $this->post->address['location'];

            $this->dao->insert(TABLE_ADDRESS)->data($address)->autoCheck()->exec();
        }   

        $locate = $relation == 'provider' ? helper::createLink('provider', 'browse') : helper::createLink('customer', 'browse');
        return array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $locate, 'customerID' => $customerID);
    }

    /**
     * Update a customer.
     * 
     * @param  int    $customerID 
     * @access public
     * @return array
     */
    public function update($customerID)
    {
        $oldCustomer = $this->getByID($customerID);
        $customer    = fixer::input('post')
            ->add('id', $customerID)
            ->add('editedBy', $this->app->user->account)
            ->add('editedDate', helper::now())
            ->setIF(!$this->post->public and $oldCustomer->relation == 'client', 'public', 0)
            ->stripTags('desc', $this->config->allowedTags)
            ->setIF(!isset($_POST['name']), 'name', $oldCustomer->name)
            ->setDefault('site', $oldCustomer->site)
            ->setDefault('weibo', $oldCustomer->weibo)
            ->remove('onlyRecord')
            ->get();

        $customer->name = trim($customer->name);
        /* Add http:// in head when that has not http:// or https://. */
        if(strpos($customer->site, '://') === false )  $customer->site  = 'http://' . $customer->site;
        if(strpos($customer->weibo, 'http://weibo.com/') === false ) $customer->weibo = 'http://weibo.com/' . $customer->weibo;
        if($customer->site == 'http://') $customer->site = '';
        if($customer->weibo == 'http://weibo.com/') $customer->weibo = '';

        $customer = $this->loadModel('file')->processImgURL($customer, $this->config->customer->editor->edit['id']);
        $this->dao->update(TABLE_CUSTOMER)
            ->data($customer, $skip = 'uid')
            ->autoCheck()
            ->batchCheck($this->config->customer->require->edit, 'notempty')
            ->checkFlow()
            ->where('id')->eq($customerID)
            ->exec();

        return commonModel::createChanges($oldCustomer, $customer);
    }

    /**
     * Update editeddate. 
     * 
     * @param  int    $customerID 
     * @access public
     * @return void
     */
    public function updateEditedDate($customerID)
    {
        $this->dao->update(TABLE_CUSTOMER)
            ->set('editedDate')->eq(helper::now())
            ->where('id')->eq($customerID)
            ->exec();
    }

    /**
     * Assign an customer to a member again.
     * 
     * @param  int    $customerID 
     * @access public
     * @return void
     */
    public function assign($customerID)
    {
        $customer = fixer::input('post')
            ->setDefault('assignedBy', $this->app->user->account)
            ->setDefault('assignedDate', helper::now())
            ->setIF(!$this->post->public, 'public', 0)
            ->add('editedBy', $this->app->user->account)
            ->add('editedDate', helper::now())
            ->add('id', $customerID)
            ->get();

        $this->dao->update(TABLE_CUSTOMER)
            ->data($customer, $skip = 'uid, note')
            ->autoCheck()
            ->check('assignedTo', 'notempty')
            ->checkFlow()
            ->where('id')->eq($customerID)
            ->exec();

        return !dao::isError();
    }

    /**
     * Link contact.
     * 
     * @param  int    $customerID 
     * @access public
     * @return bool
     */
    public function linkContact($customerID)
    {
        $this->loadModel('action');
        $this->loadModel('contact', 'crm');
        if(!$this->post->selectContact)
        {
            $contact = fixer::input('post')
                ->add('customer', $customerID)
                ->add('createdBy', $this->app->user->account)
                ->add('createdDate', helper::now())
                ->remove('contact')
                ->get();

            return $this->contact->create($contact);
        }

        if($this->post->contact)
        {
            $contactID = $this->post->contact;
            $contact   = $this->contact->getByID($contactID);
            $title     = $this->post->title;
            $position  = $this->post->position;

            if($contact->customer == $customerID)
            {
                return array('result' => 'fail', 'message' => $this->lang->customer->contactRepeat); 
            }
            if($contact->customer != $customerID or ($contact->left != '' and strtotime($contact->left) <= strtotime(helper::today())))
            {
                $resume = new stdclass();
                $resume->customer = $customerID;
                $resume->contact  = $contactID;
                $resume->title    = $title;
                $resume->position = ','. join(',', $position) .',';
                $resumeID = $this->loadModel('resume', 'crm')->create($contactID, $resume);

                if($resumeID)
                {
                    $changes[] = array('field' => 'customer', 'old' => $contact->customer, 'new' => $customerID, 'diff' => '');
                    $actionID  = $this->action->create('contact', $contactID, 'Edited');
                    $this->action->logHistory($actionID, $changes);
                }

                $this->action->create('customer', $customerID, 'linkContact', '', $this->post->newcontact ? $this->post->realname : $contact->realname);

                return array('result' => 'success', 'message' => $this->lang->saveSuccess, 'customerID' => $customerID);
            }
        }

        return array('result' => 'fail', 'message' => dao::getError());
    }

    /**
     * Combine sizeList for customer.
     * 
     * @access public
     * @return array
     */
    public function combineSizeList()
    {
        $sizeList = array();
        foreach($this->lang->customer->sizeNameList as $key => $sizeName)
        {
            $sizeList[$key] = $sizeName . '(' . $this->lang->customer->sizeNoteList[$key] . ')';
            if(empty($sizeName)) $sizeList[$key] = '';
        }
        return $sizeList;
    }

    /**
     * Combine levelList for customer.
     * 
     * @access public
     * @return array
     */
    public function combineLevelList()
    {
        $levelList = array();
        foreach($this->lang->customer->levelNameList as $key => $levelName)
        {
            $levelList[$key] = $levelName . '(' . $this->lang->customer->levelNoteList[$key] . ')';
            if(empty($levelName)) $levelList[$key] = '';
        }
        return $levelList;
    }


    /**
     * Check customer is unique.
     * 
     * @param  object  $customer
     * @access public
     * @return array
     */
    public function checkUnique($customer)
    {
        if($customer->name) $data = $this->dao->select('id, relation')->from(TABLE_CUSTOMER)->where('name')->eq($customer->name)->andWhere('deleted')->eq('0')->limit(1)->fetch();
        if(!empty($data))
        {
            $module = $data->relation == 'provider' ? 'provider' : 'customer';
            $params = $data->relation == 'provider' ? "providerID={$data->id}" : "customerID={$data->id}";
            $label  = commonModel::hasPriv($module, 'view') ? html::a(helper::createLink($module, 'view', $params), $customer->name, "target='_blank'") : $customer->name;
            $error  = sprintf($this->lang->error->unique, $this->lang->customer->name, $label);

            return array('result' => 'fail', 'error' => $error);
        }

        return array('result' => 'success');
    }

    /**
     * Move customer to public if customer longtime no edited.
     * 
     * @access public
     * @return void
     */
    public function move2CustomerPool()
    {
        $reserveDays = isset($this->config->customer->reserveDays) ? $this->config->customer->reserveDays : 0;
        if($reserveDays == 0) return true;
        
        $reserveTime = date(DT_DATETIME1, strtotime("-{$reserveDays} day"));
        $customers = $this->dao->select('id')->from(TABLE_CUSTOMER)
            ->where('editedDate')
            ->andWhere('editedDate')->lt($reserveTime)
            ->andWhere('status')->in('potential,intension,failed')
            ->andWhere('public')->eq('0')
            ->andWhere('deleted')->eq('0')
            ->fetchAll('id');
        if(empty($customers)) return true;

        $this->dao->update(TABLE_CUSTOMER)
            ->set('public')->eq('1')
            ->set('editedDate')->eq(helper::now())
            ->where('id')->in(array_keys($customers))
            ->exec();

        $changes[] = array('field' => 'public', 'old' => '0', 'new' => '1', 'diff' => '');
        foreach($customers as $key => $customer)
        {
            $actionID = $this->loadModel('action')->create('customer', $key, 'Moved2CustomerPool', '', '', 'system');
            $this->action->logHistory($actionID, $changes);
        }
    }

    /**
     * Merge two customers.
     *
     * @param  int    $origin
     * @param  string $type         customer | provider
     * @access public
     * @return bool
     */
    public function merge($origin, $type = 'customer')
    {
        $target = $this->post->$type;
        if(!$target)
        {
            dao::$errors[$type][] = sprintf($this->lang->error->notempty, $this->lang->$type->common);
            return false;
        }

        $originCustomer = $this->getByID($origin);
        if(!$originCustomer)
        {
            dao::$errors = sprintf($this->lang->error->notexist, $this->lang->$type->common . $origin);
            return false;
        }
        $targetCustomer = $this->getByID($target);
        if(!$targetCustomer)
        {
            dao::$errors[$type][] = sprintf($this->lang->error->notexist, $this->lang->$type->common . $target);
            return false;
        }

        $this->dao->update(TABLE_CONTRACT)->set('customer')->eq($target)->where('customer')->eq($origin)->exec();
        $this->dao->update(TABLE_ORDER)->set('customer')->eq($target)->where('customer')->eq($origin)->exec();
        $this->dao->update(TABLE_DATING)->set('customer')->eq($target)->where('customer')->eq($origin)->exec();
        $this->dao->update(TABLE_DATING)->set('objectID')->eq($target)->where('objectType')->in('customer,provider')->andWhere('objectID')->eq($origin)->exec();
        $this->dao->update(TABLE_RESUME)->set('customer')->eq($target)->where('customer')->eq($origin)->exec();
        $this->dao->update(TABLE_REFUND)->set('customer')->eq($target)->where('customer')->eq($origin)->exec();
        $this->dao->update(TABLE_ADDRESS)->set('objectID')->eq($target)->where('objectType')->in('customer,provider')->andWhere('objectID')->eq($origin)->exec();
        $this->dao->update(TABLE_ACTION)->set('customer')->eq($target)->where('customer')->eq($origin)->exec();
        $this->dao->update(TABLE_ACTION)->set('objectID')->eq($target)->where('objectType')->in('customer,provider')->andWhere('objectID')->eq($origin)->andWhere('action')->notin('created,edited')->exec();
        $this->dao->update(TABLE_TASK)->set('customer')->eq($target)->where('customer')->eq($origin)->exec();
        $this->dao->update(TABLE_TRADE)->set('trader')->eq($target)->where('trader')->eq($origin)->exec();

        $tripPairs = $this->dao->select('id,customers')->from(TABLE_TRIP)->where('customers')->ne('')->fetchPairs();
        foreach($tripPairs as $tripID => $customers)
        {
            if(strpos(",{$customers},", ",{$origin},") === false) continue;

            $customers = explode(',', $customers);
            $customers = array_flip($customers);
            unset($customers[$origin]);
            $customers[$target] = $target;
            $customers = array_keys($customers);

            $this->dao->update(TABLE_TRIP)->set('customers')->eq(implode(',', $customers))->where('id')->eq($tripID)->exec();
        }

        if(dao::isError()) return false;

        $newCustomer = new stdclass();
        $newCustomer->type      = $targetCustomer->type ? $targetCustomer->type : $originCustomer->type;
        $newCustomer->size      = $targetCustomer->size ? $targetCustomer->size : $originCustomer->size;
        $newCustomer->industry  = $targetCustomer->industry ? $targetCustomer->industry : $originCustomer->industry;
        $newCustomer->area      = $targetCustomer->area ? $targetCustomer->area : $originCustomer->area;
        $newCustomer->intension = $targetCustomer->intension ? $targetCustomer->intension : $originCustomer->intension;
        $newCustomer->site      = $targetCustomer->site ? $targetCustomer->site : $originCustomer->site;
        $newCustomer->weibo     = $targetCustomer->weibo ? $targetCustomer->weibo : $originCustomer->weibo;
        $newCustomer->weixin    = $targetCustomer->weixin ? $targetCustomer->weixin : $originCustomer->weixin;
        $newCustomer->depositor = $targetCustomer->depositor ? $targetCustomer->depositor : $originCustomer->depositor;
        $newCustomer->desc      = $targetCustomer->desc ? $targetCustomer->desc : $originCustomer->desc;

        $levelList  = array_flip(array_keys($this->lang->customer->levelNameList));
        $statusList = array_flip(array_keys($this->lang->customer->statusList));

        $newCustomer->level  = zget($levelList, $targetCustomer->level) > zget($levelList, $originCustomer->level) ? $targetCustomer->level : $originCustomer->level;
        $newCustomer->status = zget($statusList, $targetCustomer->status) > zget($statusList, $originCustomer->status) ? $targetCustomer->status : $originCustomer->status;

        $this->dao->update(TABLE_CUSTOMER)->data($newCustomer)->where('id')->eq($target)->exec();
        $this->dao->update(TABLE_CUSTOMER)->set('deleted')->eq('1')->where('id')->eq($origin)->exec();

        return !dao::isError();
    }

    /**
     * Process categories.
     * Remove the category which is not in $categoryIDList. 
     * 
     * @param  array  $categories 
     * @param  array  $categoryIDList 
     * @access public
     * @return array
     */
    public function processCategories($categories, $categoryIDList)
    {
        foreach($categories as $key => $category)
        {
            if(in_array($category->id, $categoryIDList))
            {
                if($category->parent) 
                {
                    $parent = $categories[$category->parent];
                    $parent->children[] = $category;

                    $categories[$category->parent] = $parent;

                    unset($categories[$key]);
                }
            }
            else
            {
                if(!isset($category->children)) unset($categories[$key]);
            }
        }
        return $categories;
    }

    /**
     * Get menu by categories. 
     * 
     * @param  array  $categories 
     * @param  string $mode 
     * @param  string $orderBy 
     * @param  int    $recTotal 
     * @param  int    $recPerPage 
     * @param  int    $pageID 
     * @access public
     * @return string
     */
    public function getMenuByCategories($categories, $mode, $orderBy, $recTotal, $recPerPage, $pageID)
    {
        $menu = '';
        foreach($categories as $category)
        {
            if(empty($category->children))
            {
                $menu .= '<li>' . html::a(helper::createLink('crm.customer', 'browse', "mode=$mode&param=$category->id&orderBy=$orderBy&recTotal=$recTotal&recPerPage=$recPerPage&pageID=$pageID"), $category->name) . '</li>';
            }
            else
            {
                $menu .= "<li class='dropdown-submenu'>";
                $menu .= html::a(helper::createLink('crm.customer', 'browse', "mode=$mode&param=$category->id&orderBy=$orderBy&recTotal=$recTotal&recPerPage=$recPerPage&pageID=$pageID"), $category->name);
                $menu .= "<ul class='dropdown-menu'>";
                $menu .= $this->getMenuByCategories($category->children, $mode, $orderBy, $recTotal, $recPerPage, $pageID);
                $menu .= '</ul></li>';
            }
        }
        return $menu;
    }

    /**
     * Create module menu. 
     * 
     * @param  string $mode 
     * @param  string $param 
     * @param  string $orderBy 
     * @param  int    $recTotal 
     * @param  int    $recPerPage 
     * @param  int    $pageID 
     * @access public
     * @return string 
     */
    public function createModuleMenu($mode, $param, $orderBy, $recTotal, $recPerPage, $pageID)
    {
        $menu = commonModel::createModuleMenu('customer');

        if($this->app->viewType == 'mhtml') return $menu;

        $customerIdList = $this->getCustomersSawByMe();
        if(empty($customerIdList)) return $menu;

        $customers = $this->dao->select('area, industry')->from(TABLE_CUSTOMER)
            ->where('deleted')->eq('0')
            ->andWhere('id')->in($customerIdList)
            ->fetchAll();

        $css  = '';
        $menu = str_replace('</ul></div></nav>', '', $menu);
        foreach(array('area', 'industry') as $field)
        {
            $categoryIDList = array();
            foreach($customers as $customer)
            {
                if($customer->$field) $categoryIDList[$customer->$field] = $customer->$field;
            }

            $categoryList = $this->loadModel('tree')->getListByType($field, 'grade_desc, id');
            $categories   = $this->processCategories($categoryList, $categoryIDList);

            $label = $this->lang->customer->$field;
            if($mode == $field)
            {
                if(isset($categoryList[$param]))
                {
                    $category = $categoryList[$param];
                    $label    = $category->name;
                }
            }

            if($categories)
            {
                $menu .= "<li class='dropdown dropdown-$field'>";
                $menu .= html::a(inlink('browse', "mode=$field"), $label . "<span class='caret'></span>", "data-toggle='dropdown'");
                $menu .= "<ul class='dropdown-menu'>";
                $menu .= $this->getMenuByCategories($categories, $field, $orderBy, $recTotal, $recPerPage, $pageID);
                $menu .= '</ul></li>';
            }
            if(count($categories) > 20) $css .= "li.dropdown-$field > ul{width:320px} li.dropdown-$field > ul > li {width: 50%; display:inline-block;}\n";
        }

        $menu .= '</ul></div>';
        if($css) $menu .= "<style>$css</style>";
        $menu .= '</nav>';

        return $menu;
    }

    /**
     * Build operate menu
     *
     * @param  object $customer
     * @param  string $type      browse | view
     * @access public
     * @return string
     */
    public function buildOperateMenu($customer, $type = 'browse')
    {
        $menu    = '';
        $class   = $type == 'view' ? 'btn' : '';
        $history = $type == 'view' ? false : true;

        if($type == 'view') $menu .= commonModel::printLink('order', 'create', "customer={$customer->id}", $this->lang->order->common, "class='{$class} universal'", false);
        if($type == 'view') $menu .= commonModel::printLink('contract', 'create', "type=sale&customer={$customer->id}", $this->lang->contract->common, "class='{$class} universal'", false);
        if($type == 'view') $menu .= "<div class='divider'></div>";

        $menu .= commonModel::printLink('action', 'createRecord', "objectType=customer&objectID={$customer->id}&customer={$customer->id}&history={$history}&stage={$customer->stage}", $this->lang->customer->record, "class='{$class} universal' data-toggle='modal' data-width='800'", false);
        $menu .= commonModel::printLink('address', 'browse', "objectType=customer&objectID={$customer->id}", $this->lang->customer->address, "class='{$class} universal' data-toggle='modal'", false);
        
        $menu .= $this->buildMenu('customer', 'assign',  "customerID={$customer->id}", $this->lang->customer->assign,  $customer, $type, "class='{$class} universal' data-toggle='modal'");
        $menu .= $this->buildMenu('customer', 'contact', "customerID={$customer->id}", $this->lang->customer->contact, $customer, $type, "class='{$class} universal' data-toggle='modal' data-width='950'");
        $menu .= $this->buildMenu('customer', 'edit',    "customerID={$customer->id}", $this->lang->edit,              $customer, $type, "class='{$class}'");

        if($type == 'view') $menu .= "<div class='divider'></div>";

        $menu .= $this->buildFlowMenu('customer', $customer, $type, 'direct');

        if($type == 'browse') $menu .= "<div class='dropdown'><a data-toggle='dropdown' href='javascript:;'>" . $this->lang->more . "<span class='caret'></span> </a><ul class='dropdown-menu pull-right'>";

        $menu .= $this->buildFlowMenu('customer', $customer, $type, 'dropdownlist');

        if($type == 'browse') $menu .= $this->buildMenu('customer', 'order',    "customerID={$customer->id}", $this->lang->customer->order,    $customer, $type, "class='{$class}' data-toggle='modal'", true);
        if($type == 'browse') $menu .= $this->buildMenu('customer', 'contract', "customerID={$customer->id}", $this->lang->customer->contract, $customer, $type, "class='{$class}' data-toggle='modal'", true);
        if($type == 'browse') $menu .= $this->buildMenu('customer', 'merge',    "customerID={$customer->id}", $this->lang->customer->merge,    $customer, $type, "class='{$class}' data-toggle='modal'", true);
        if($type == 'view')   $menu .= "<div class='divider'></div>";

        $menu .= $this->buildMenu('customer', 'delete', "customerID={$customer->id}", $this->lang->delete, $customer, $type, "class='{$class} deleter'", true);

        if($type == 'browse') $menu .= '</ul></div>';

        return $menu;
    }

    /**
     * Build app operate menu of an customer.
     * 
     * @param  object    $customer 
     * @access public
     * @return array
     */
    public function buildAppOperateMenu($customer)
    {
        $menus   = array(); 
        //$menus[] = array('title' => $this->lang->delete, 'class' => 'strong text', 'clickTap' => 'delete', 'app' => 'sys', 'module' => 'customer', 'method' => 'delete', 'params' => "customerID={$customer->id}"); 
        //$menus[] = array('title' => $this->lang->comment, 'class' => 'strong text', 'clickTap' => 'comment', 'app' => 'sys', 'module' => 'customer', 'method' => 'edit', 'params' => "customerID={$customer->id}&comment=1"); 
        //$menus[] = array('title' => $this->lang->assign, 'class' => 'strong text text-primary', 'clickTap' => 'assign', 'app' => 'sys', 'module' => 'customer', 'method' => 'assign', 'params' => "customerID={$customer->id}"); 
        $menus[] = array('title' => $this->lang->order->common, 'class' => 'strong text text-primary', 'clickTap' => 'contract', 'app' => 'crm', 'module' => 'order', 'method' => 'create', 'params' => "customerID={$customer->id}"); 
        $menus[] = array('title' => $this->lang->customer->record, 'class' => 'strong text text-primary', 'clickTap' => 'record', 'app' => 'sys', 'module' => 'action', 'method' => 'createRecord', 'params' => "objectType=customer&objectID={$customer->id}&customer={$customer->id}&history=0"); 

        foreach($menus as $key => $menu) if(!commonModel::hasPriv($menu['app'] . '.' . $menu['module'], $menu['method'])) unset($menus[$key]);
        return $menus;
    }

    /**
     * Get list by id array.
     * @param  array   $ids
     * @param  string  $fields
     * @access public
     * @return array
     */
    public function getListByIds($ids, $fields = '*')
    {
        if(empty($ids)) return array();
        return $this->dao->select($fields)->from(TABLE_CUSTOMER)->where('id')->in($ids)->fetchAll('id');
    }

    /**
     * Update stage.
     *
     * @param  int    $customerID
     * @access public
     * @return void
     */
    public function updateStage($customerID)
    {
        $currentStage = $this->getCurrentStage($customerID);
        $this->dao->update(TABLE_CUSTOMER)->set('stage')->eq($currentStage)->where('id')->eq($customerID)->exec();

        return !dao::isError();
    }

    /**
     * Turn the stage.
     *
     * @param  int    $customerID
     * @param  string $stage
     * @access public
     * @return void
     */
    public function turnStage($customerID, $stage)
    {
        $currentStage = $this->dao->select('stage')->from(TABLE_CUSTOMER)->where('id')->eq($customerID)->fetch('stage');
        $stageList    = array_keys($this->lang->customer->stages);

        if(!in_array($stage, $stageList)) return false;
        if(array_search($currentStage, $stageList) >= array_search($stage, $stageList)) return false;

        $this->dao->update(TABLE_CUSTOMER)->set('stage')->eq($stage)->where('id')->eq($customerID)->exec();

        return !dao::isError();
    }

    /**
     * Get current stage.
     *
     * @param  int    $customer 
     * @access public
     * @return void
     */
    public function getCurrentStage($customer)
    {
        $stages  = array_reverse($this->getStages($customer));

        $current = 'communicate';
        if($stages['renew']['status'] == 'done')
        {
            $current = 'sign';
            unset($stages['communicate']);
            unset($stages['guide']);
            unset($stages['try']);
            unset($stages['renew']);
        }

        foreach($stages as $code => $stage)
        {
            if($stage['status'] == 'doing')
            {
                return $code;
            }
            else
            {
                if($stage['status'] == 'done') return $current;
                $current = $code;
            }
        }

        return $current;
    }

    /**
     * Get stages for customer.
     *
     * @param  int    $customer
     * @param  bool   $withMaterial
     * @access public
     * @return array
     */
    public function getStages($customer, $withMaterial = false)
    {
        $this->loadModel('file');
        $this->contracts = $this->initStagesContract($customer);

        $stages = array();
        foreach($this->lang->customer->stages as $stage => $stageName)
        {
            $function  = 'getStage' . ucfirst($stage);
            $stageData = $this->$function($customer);
            if($withMaterial && $stage !== 'detail')
            {
                $stageData['materials']      = $this->getMaterialList($stage);
                $stageData['ydiskMaterials'] = $this->getYdiskMaterialList($stage);
            }
            $this->stages[$stage] = $stageData;
        }

        return $this->stages;
    }

    /**
     * Get contract data for customer.
     *
     * @param  int    $customer
     * @access public
     * @return void
     */
    public function initStagesContract($customer)
    {
        $this->loadModel('contract');
        $contracts = $this->dao->select('*')->from(TABLE_CONTRACT)
            ->where('customer')->eq($customer)
            ->andWhere('type')->eq('sale')
            ->orderBy('id_desc')
            ->fetchAll('id');

        foreach($contracts as $contract)
        {
            $contract->returnList = $this->contract->getReturnList($contract->id);
            $returnAmount = 0;
            foreach($contract->returnList as $return) $returnAmount += $return->amount;
            $contract->unReturn = $contract->amount - $returnAmount > 0 ? $contract->amount - $returnAmount : 0;
        }

        return $contracts;
    }

    /**
     * Get detail stage for customer.
     *
     * @param  int    $customer
     * @access public
     * @return string
     */
    public function getStageDetail($customer)
    {
        return array('status' => 'done');
    }

    /**
     * Get communicate stages for customer.
     *
     * @param  int    $customerID
     * @access public
     * @return string
     */
    public function getStageCommunicate($customerID)
    {
        $communicate = $this->getStageInfo('communicate', $customerID);

        $status = 'wait';
        if($communicate)
        {
            $status     = 'doing';
            $expect     = json_decode(zget($communicate, 'expect', '{}'), true);
            $expectDone = false;
            $expectList = $this->lang->customer->expectList;
            foreach($expectList as $role => $expectItem)
            {
                $roleExpect = zget($expect, $role, array());
                if(count($roleExpect) > 1 || zget(zget($roleExpect, 'other', ''), 'other', false))
                {
                    $expectDone = true;
                    break;
                }
            }
            if($communicate->scene && $communicate->currentWay && $expectDone) $status = 'done';
        }
        else
        {
            $record = $this->dao->select('id')->from(TABLE_ACTION)->where('customer')->eq($customerID)->andWhere('stage')->eq('communicate')->fetch();
            if(!empty($record)) $status = 'doing';
        }
        return array('status' => $status, 'data' => $communicate);
    }

    /**
     * Get guide stages for customer.
     *
     * @param  int    $customer
     * @access public
     * @return string
     */
    public function getStageGuide($customer)
    {
        $guideData = $this->dao->select('*')->from(TABLE_STAGEGUIDANCE)->where('customer')->eq($customer)->fetch();

        if(!empty($guideData))
        {
            foreach($guideData as $field => $value)
            {
                if(empty($value)) return array('status' => 'doing', 'data' => $guideData);
            }

            return array('status' => 'done', 'data' => $guideData);
        }
        else
        {
            $record = $this->dao->select('id')->from(TABLE_ACTION)->where('customer')->eq($customer)->andWhere('stage')->eq('guide')->fetch();
            if(empty($record)) return array('status' => 'wait', 'data' => array());

            return array('status' => 'doing', 'data' => array());
        }
    }

    /**
     * Get try stages for customer.
     *
     * @param  int    $customer
     * @access public
     * @return string
     */
    public function getStageTry($customer)
    {
        $tryData = $this->dao->select('*')->from(TABLE_STAGETRIAL)->where('customer')->eq($customer)->fetch();
        $record  = $this->dao->select('id')->from(TABLE_ACTION)->where('customer')->eq($customer)->andWhere('stage')->eq('try')->fetch();

        if(empty($tryData) && empty($record))
        {
            $status = 'wait';
        }
        else
        {
            $status = 'doing';

            if(!empty($tryData))
            {
                $files = $this->file->getByObject('stagetrial', $tryData->id);

                foreach($files as $file)
                {
                    if($file->extra === 'plan')       $tryData->plan[]       = $file;
                    if($file->extra === 'screenshot') $tryData->screenshot[] = $file;
                }

                if(!empty($tryData->reply) && !empty($tryData->plan) && !empty($tryData->screenshot)) $status = 'done';
            }
        }

        return array('status' => $status, 'data' => $tryData, 'licenses' => array());
    }

    /**
     * Get sign stages for customer.
     *
     * @param  int    $customer
     * @access public
     * @return string
     */
    public function getStageSign($customer)
    {
        $contracts = $this->contracts;
        $record    = $this->dao->select('id')->from(TABLE_ACTION)->where('customer')->eq($customer)->andWhere('stage')->eq('sign')->fetch();

        $status = 'wait';
        if(!empty($record))    $status = 'doing';
        if(!empty($contracts)) $status = 'done';

        $contracts = array_reverse($contracts, true);

        return array('status' => $status, 'contracts' => $contracts);
    }

    /**
     * Get return stages for customer.
     *
     * @access public
     * @return string
     */
    public function getStageReturn($customer)
    {
        $contracts = $this->contracts;
        $status    = 'wait';

        if(empty($contracts))
        {
            $record = $this->dao->select('id')->from(TABLE_ACTION)->where('customer')->eq($customer)->andWhere('stage')->eq('return')->fetch();
            if(!empty($record)) $status = 'doing';
        }
        else
        {
            $contract = current($contracts);
            $record   = $this->dao->select('id,date')->from(TABLE_ACTION)->where('customer')->eq($customer)->andWhere('stage')->eq('return')->andWhere('date')->ge($contract->createdDate)->fetch();
            if(!empty($record) || $contract->return ===  'doing') $status = 'doing';
            if($contract->return == 'done')  $status = 'done';
        }

        return array('status' => $status, 'contracts' => $contracts);
    }

    /**
     * Get deliver stages for customer.
     *
     * @access public
     * @return string
     */
    public function getStageDeliver($customer)
    {
        $contracts = $this->contracts;
        $status    = 'wait';
        if(empty($contracts))
        {
            $record = $this->dao->select('id')->from(TABLE_ACTION)->where('customer')->eq($customer)->andWhere('stage')->eq('deliver')->fetch();
            if(!empty($record)) $status = 'doing';
        }
        else
        {
            $contract         = current($contracts);
            $record           = $this->dao->select('id,date')->from(TABLE_ACTION)->where('customer')->eq($customer)->andWhere('stage')->eq('deliver')->andWhere('date')->ge($contract->createdDate)->fetch();
            $deliveryList     = $this->dao->select('*')->from(TABLE_DELIVERY)->where('contract')->in(array_keys($this->contracts))->fetchGroup('contract');
            $deliveryDataList = $this->dao->select('*')->from(TABLE_STAGEDELIVERY)->where('contract')->in(array_keys($this->contracts))->fetchAll('contract');
            $files            = $this->loadModel('file')->getByObject('stagedelivery', array_column(object2Array($deliveryDataList), 'id'));

            if(!empty($deliveryList) || !empty($record) || isset($deliveryDataList[array_keys($contracts)[0]])) $status = 'doing';

            foreach($deliveryDataList as $deliveryData) $deliveryData->files = zget($files, $deliveryData->id, array());

            foreach($contracts as $contract)
            {
                $contract->deliveryList = zget($deliveryList, $contract->id, array());
                $contract->deliverData  = zget($deliveryDataList, $contract->id, array());
            }

            $fieldDone = true;
            if(!empty($contracts[array_keys($contracts)[0]]->deliverData))
            {
                foreach($contract->deliverData as $value) if(empty($value)) $fieldDone = false;
                if($contracts[array_keys($contracts)[0]]->delivery == 'done' && $fieldDone && $files)  $status = 'done';
            }
        }

        return array('status' => $status, 'contracts' => $contracts);
    }

    /**
     * Get serve stages for customer.
     *
     * @access public
     * @return string
     */
    public function getStageServe($customer)
    {
        $contracts = $this->contracts;
        $status    = 'wait';

        if(empty($contracts))
        {
            $record = $this->dao->select('id')->from(TABLE_ACTION)->where('customer')->eq($customer)->andWhere('stage')->eq('serve')->fetch();
            if(!empty($record)) $status = 'doing';
        }
        else
        {
            $contract      = current($contracts);
            $record        = $this->dao->select('id,date')->from(TABLE_ACTION)->where('customer')->eq($customer)->andWhere('stage')->eq('serve')->andWhere('date')->ge($contract->createdDate)->fetch();
            $serveDataList = $this->dao->select('*')->from(TABLE_STAGESERVICE)->where('contract')->in(array_keys($this->contracts))->fetchAll('contract');
            $files         = $this->loadModel('file')->getByObject('stageservice', array_column(object2Array($serveDataList), 'id'));

            if(!empty($record) || isset($serveDataList[array_keys($contracts)[0]])) $status = 'doing';

            foreach($serveDataList as $serveData)
            {
                $serveFiles = zget($files, $serveData->id, array());
                $serveData->fileDone = false;
                foreach($serveFiles as $file)
                {
                    if($file->extra === 'returnVisit3') $serveData->returnVisit3[] = $file;
                    if($file->extra === 'returnVisit6') $serveData->returnVisit6[] = $file;
                    if($file->extra === 'returnVisit9') $serveData->returnVisit9[] = $file;
                }
                if(!empty($serveData->returnVisit3) && !empty($serveData->returnVisit6) && !empty($serveData->returnVisit9)) $serveData->fileDone = true;
            }

            foreach($this->contracts as $contract) $contract->serveData = zget($serveDataList, $contract->id, array());

            $fieldDone = true;
            $contract = current($this->contracts);
            if(!empty($contract->serveData))
            {
                foreach($contract->serveData as $value) if(empty($value)) $fieldDone = false;
                if($fieldDone) $status = 'done';
            }
        }

        return array('status' => $status, 'contracts' => $this->contracts);
    }

    /**
     * Get renew stages for customer.
     *
     * @access public
     * @return string
     */
    public function getStageRenew($customer)
    {
        $contracts = $this->contracts;
        $record    = $this->dao->select('id')->from(TABLE_ACTION)->where('customer')->eq($customer)->andWhere('stage')->eq('renew')->fetch();

        $status = 'wait';
        if(!empty($contracts) || !empty($record))
        {
            if(!empty($record))
            {
                $status = 'doing';
            }
            if(!empty($contracts))
            {
                $contract = current($contracts);
                if($contract->signType != 'new' && $this->stages['serve']['status'] !== 'done') $status ='done';
            }
        }
        $contracts = array_filter($contracts, function($item){return $item->signType !== 'new';});
        
        return array('status' => $status, 'contracts' => $contracts);
    }


    /**
     * Set communicate stage.
     *
     * @param  int    $customerID 
     * @access public
     * @return bool
     */
    public function setStageCommunicate($customerID)
    {
        $now  = helper::now();
        $data = fixer::input('post')
            ->setDefault('scene', '')
            ->join('scene', ',')
            ->get();

        $data->expect = json_encode($this->post->expect);

        $oldData = $this->dao->select('*')->from(TABLE_STAGECOMMUNICATION)->where('customer')->eq($customerID)->fetch();

        if(empty($oldData))
        {
            $data->customer    = $customerID;
            $data->createdBy   = $this->app->user->account;
            $data->createdDate = $now;
            $this->dao->insert(TABLE_STAGECOMMUNICATION)->data($data)->exec();
        }
        else
        {
            $data->editedBy   = $this->app->user->account;
            $data->editedDate = $now;
            $this->dao->update(TABLE_STAGECOMMUNICATION)->data($data)->where('customer')->eq($customerID)->exec();
        }

        return !dao::isError();
    }

    /**
     * Set guide stage.
     *
     * @param  int    $customerID 
     * @access public
     * @return bool
     */
    public function setStageGuide($customerID)
    {
        $now = helper::now();
        $data = fixer::input('post')
            ->add('editedBy', $this->app->user->account)
            ->add('editedDate', $now)
            ->add('customer', $customerID)
            ->setIF(isset($_POST['materialFinished']), 'materialFinished', 1)
            ->setIF(!isset($_POST['materialFinished']), 'materialFinished', 0)
            ->join('ourStaff', ',')
            ->get();

        $oldData = $this->dao->select('*')->from(TABLE_STAGEGUIDANCE)->where('customer')->eq($customerID)->fetch();

        if(empty($oldData))
        {
            $data->createdBy   = $this->app->user->account;
            $data->createdDate = $now;
            $this->dao->insert(TABLE_STAGEGUIDANCE)->data($data)->exec();
        }
        else
        {
            $this->dao->update(TABLE_STAGEGUIDANCE)->data($data)->where('customer')->eq($customerID)->exec();
        }

        $this->dao->update(TABLE_CUSTOMER)->set('paymentIntention')->eq($data->paymentIntention)->where('id')->eq($customerID)->exec();

        return !dao::isError();
    }

    /**
     * Set try stage.
     *
     * @param  int    $customerID 
     * @access public
     * @return bool
     */
    public function setStageTry($customerID)
    {
        $now = helper::now();
        $data = fixer::input('post')
            ->add('editedBy', $this->app->user->account)
            ->add('editedDate', $now)
            ->add('customer', $customerID)
            ->remove('plan, planLabel, screenshot, screenshotLabel')
            ->get();

        $oldData = $this->dao->select('*')->from(TABLE_STAGETRIAL)->where('customer')->eq($customerID)->fetch();

        if(empty($oldData))
        {
            $data->createdBy   = $this->app->user->account;
            $data->createdDate = $now;
            $this->dao->insert(TABLE_STAGETRIAL)->data($data)->exec();

            $dataID = $this->dao->lastInsertID();
        }
        else
        {
            $this->dao->update(TABLE_STAGETRIAL)->data($data)->where('customer')->eq($customerID)->exec();
            $dataID = $oldData->id;
        }

        $this->loadModel('file')->saveUpload('stagetrial', $dataID, 'plan', 'plan', 'planLabel');
        $this->file->saveUpload('stagetrial', $dataID, 'screenshot', 'screenshot', 'screenshotLabel');

        return !dao::isError();
    }

    /**
     * Set try stage.
     *
     * @param  int    $customerID
     * @param  int    $contractID
     * @access public
     * @return bool
     */
    public function setStageServe($customerID, $contractID)
    {
        $now = helper::now();
        $data = fixer::input('post')
            ->add('editedBy', $this->app->user->account)
            ->add('editedDate', $now)
            ->add('customer', $customerID)
            ->add('contract', $contractID)
            ->remove('returnVisit3, returnVisit3Label, returnVisit6, returnVisit6Label, returnVisit9, returnVisit9Label,')
            ->get();

        $oldData = $this->dao->select('*')->from(TABLE_STAGESERVICE)->where('contract')->eq($contractID)->fetch();

        if(empty($oldData))
        {
            $data->createdBy   = $this->app->user->account;
            $data->createdDate = $now;
            $this->dao->insert(TABLE_STAGESERVICE)->data($data)->exec();

            $dataID = $this->dao->lastInsertID();
        }
        else
        {
            $this->dao->update(TABLE_STAGESERVICE)->data($data)->where('contract')->eq($contractID)->exec();
            $dataID = $oldData->id;
        }

        $this->loadModel('file');
        $this->file->saveUpload('stageservice', $dataID, 'returnVisit3', 'returnVisit3', 'returnVisit3Label');
        $this->file->saveUpload('stageservice', $dataID, 'returnVisit6', 'returnVisit6', 'returnVisit6Label');
        $this->file->saveUpload('stageservice', $dataID, 'returnVisit9', 'returnVisit9', 'returnVisit9Label');

        $this->dao->update(TABLE_CUSTOMER)->set('renewalIntention')->eq($data->renewalIntention)->where('id')->eq($customerID)->exec();

        return !dao::isError();
    }

    /**
     * Set deliver stage.
     *
     * @param  int    $customerID 
     * @param  int    $contractID 
     * @access public
     * @return bool
     */
    public function setStageDeliver($customerID, $contractID)
    {
        $now = helper::now();
        $data = fixer::input('post')
            ->add('editedBy', $this->app->user->account)
            ->add('editedDate', $now)
            ->add('customer', $customerID)
            ->add('contract', $contractID)
            ->remove('files, labels')
            ->get();

        $oldData = $this->dao->select('*')->from(TABLE_STAGEDELIVERY)->where('contract')->eq($contractID)->fetch();

        if(empty($oldData))
        {
            $data->createdBy   = $this->app->user->account;
            $data->createdDate = $now;
            $this->dao->insert(TABLE_STAGEDELIVERY)->data($data)->exec();

            $dataID = $this->dao->lastInsertID();
        }
        else
        {
            $this->dao->update(TABLE_STAGEDELIVERY)->data($data)->where('contract')->eq($contractID)->exec();
            $dataID = $oldData->id;
        }

        $this->loadModel('file')->saveUpload('stagedelivery', $dataID);

        $this->dao->update(TABLE_CUSTOMER)->set('health')->eq($data->health)->where('id')->eq($customerID)->exec();

        return !dao::isError();
    }

    /**
     * Get material list for stage.
     *
     * @param  string $stage
     * @access public
     * @return array
     */
    public function getMaterialList($stage)
    {
        if($this->materialList === false)
        {
            $this->materialList = $this->dao->select('*')->from(TABLE_MATERIAL)->fetchGroup('stage');
        }
        return isset($this->materialList[$stage]) ? $this->materialList[$stage] : array();
    }

    /**
     * Get ydisk material list for stage.
     *
     * @param  string $stage
     * @access public
     * @return array
     */
    public function getYdiskMaterialList($stage)
    {
        if(!isset($this->config->customer->stageStoreID) || !isset($this->config->customer->stageFilesID[$stage])) return array();
        return $this->loadModel('ydisk')->getFiles($this->config->customer->stageStoreID, $this->config->customer->stageFilesID[$stage]);
    }
}
