<?php
/**
 * The control file for contract 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      Yidong Wang <yidong@cnezsoft.com>
 * @package     contract
 * @version     $Id$
 * @link        http://www.zdoo.com
 */
class contract extends control
{
    /**
     * Construct method.
     *
     * @param  string $moduleName
     * @param  string $methodName
     * @param  string $appName
     * @access public
     * @return void
     */
    public function __construct($moduleName = '', $methodName = '', $appName = '')
    {
        parent::__construct($moduleName, $methodName, $appName);
        $this->app->loadLang('order', 'crm');
if(function_exists('ioncube_license_properties')) $properties = ioncube_license_properties();
$contactEmail  = !empty($properties['email']['value'])  ? $properties['email']['value']  : 'jinjuan@easycorp.ltd';
$contactMobile = !empty($properties['mobile']['value']) ? $properties['mobile']['value'] : '185 6285 6230';
$user = $this->dao->select("COUNT('*') as count")->from(TABLE_USER)->where('deleted')->eq(0)->andWhere('locked')->lt(helper::now())->fetch();
if(!empty($properties['user']) and $properties['user']['value'] < $user->count) die("<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dli'>
<html xmlns='http://www.w3.org/1999/xhtml'>
<head>
  <meta http-equiv='Content-Type' content='text/html; charset=utf-8' />
  <title>Error</title>
</head>
<body>
<h2 style='color:red;text-align:center'>人数超出限制</h2>
您版本的用户数是{$properties['user']['value']}，您目前系统中已有{$user->count}人，已经超过了限制，请联系我们增加人数授权。<br>
Email：<a href='mailto:$contactEmail'>$contactEmail</a><br>
电话：$contactMobile<br />
网址：<a href='https://www.zdoo.com/'>www.zdoo.com</a><br />
<br /><br /><br />
<h2 style='color:red;text-align:center'>Accounts has exceed the limit.</h2>
The accounts has exceed the limit of {$properties['user']['value']} peoples, please contact us to buy more licenses.<br />
Email : <a href='mailto:philip@easycorp.ltd'>philip@easycorp.ltd</a><br />
Site : <a href='http://www.zdoo.org/'>http://www.zdoo.org/</a><br />
</body>
</html>");

if(strpos($this->config->version, 'flow') !== false)
{
    $flow = $this->dao->select("COUNT('*') as count")->from(TABLE_WORKFLOW)->where('type')->eq('flow')->fetch();
    if(!empty($properties['flowLimit']) and $properties['flowLimit']['value'] < $flow->count) die("<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dli'>
<html xmlns='http://www.w3.org/1999/xhtml'>
<head>
  <meta http-equiv='Content-Type' content='text/html; charset=utf-8' />
  <title>Error</title>
</head>
<body>
<h2 style='color:red;text-align:center'>流程超出限制</h2>
您版本的流程数是{$properties['flowLimit']['value']}，您目前系统中已有{$flow->count}个流程，已经超过了限制，请联系我们升级到企业版。<br>
Email：<a href='mailto:$contactEmail'>$contactEmail</a><br>
电话：$contactMobile<br />
网址：<a href='https://www.zdoo.com/'>www.zdoo.com</a><br />
<br /><br /><br />
<h2 style='color:red;text-align:center'>Accounts has exceed the limit.</h2>
The accounts has exceed the limit of {$properties['flowLimit']['value']} flows, please contact us to upgrade to biz version.<br />
Email : <a href='mailto:philip@easycorp.ltd'>philip@easycorp.ltd</a><br />
Site : <a href='http://www.zdoo.org/'>http://www.zdoo.org/</a><br />
</body>
</html>");
}

if(!empty($properties['enableApps']['value']))
{
    if(isset($_GET['entryID']))
    {
        $entryID = $_GET['entryID'];
        if(strpos(',dashboard,allapps,superadmin,', ",{$entryID},") === false)
        {
            $entry = $this->dao->select('code, buildin')->from(TABLE_ENTRY)->where('id')->eq($entryID)->fetch();
            if($entry->buildin && strpos(",{$properties['enableApps']['value']},", ",{$entry->code},") === false) die("<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dli'>
<html xmlns='http://www.w3.org/1999/xhtml'>
<head>
  <meta http-equiv='Content-Type' content='text/html; charset=utf-8' />
  <title>Error</title>
</head>
<body>
<h2 style='color:red;text-align:center'>应用超出限制</h2>
您的版本只能使用{$this->lang->apps->{$properties['enableApps']['value']}}应用，请联系我们升级到完整版。<br>
Email：<a href='mailto:$contactEmail'>$contactEmail</a><br>
电话：$contactMobile<br />
网址：<a href='https://www.zdoo.com/'>www.zdoo.com</a><br />
<br /><br /><br />
<h2 style='color:red;text-align:center'>Accounts has exceed the limit.</h2>
The accounts can only use the {$properties['enableApps']['value']} app, please contact us to upgrade to full version.<br />
Email : <a href='mailto:philip@easycorp.ltd'>philip@easycorp.ltd</a><br />
Site : <a href='http://www.zdoo.org/'>http://www.zdoo.org/</a><br />
</body>
</html>");
        } 
    }
    else
    {
        $appName = !empty($appName) ? $appName : $this->appName;
        if($appName != 'sys' && strpos(",{$properties['enableApps']['value']},", ",{$appName},") === false) die("<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dli'>
<html xmlns='http://www.w3.org/1999/xhtml'>
<head>
  <meta http-equiv='Content-Type' content='text/html; charset=utf-8' />
  <title>Error</title>
</head>
<body>
<h2 style='color:red;text-align:center'>应用超出限制</h2>
您的版本只能使用{$this->lang->apps->{$properties['enableApps']['value']}}应用，请联系我们升级到完整版。<br>
Email：<a href='mailto:$contactEmail'>$contactEmail</a><br>
电话：$contactMobile<br />
网址：<a href='https://www.zdoo.com/'>www.zdoo.com</a><br />
<br /><br /><br />
<h2 style='color:red;text-align:center'>Accounts has exceed the limit.</h2>
The accounts can only use the {$properties['enableApps']['value']} app, please contact us to upgrade to full version.<br />
Email : <a href='mailto:philip@easycorp.ltd'>philip@easycorp.ltd</a><br />
Site : <a href='http://www.zdoo.org/'>http://www.zdoo.org/</a><br />
</body>
</html>");
    }
}

if(!empty($properties['domain']))
{
    $host    = $_SERVER['HTTP_HOST'];
    $portPos = strrpos($host, ':');
    if($portPos !== false)
    {
        $port = substr($host, $portPos + 1);
        if(is_numeric($port)) $host = substr($host, 0, $portPos);
    }
    $host .= $_SERVER['REQUEST_URI'];

    $checkHost  = false;
    $allowHosts = explode(',', $properties['domain']['value']);
    foreach($allowHosts as $allowHost)
    {
        if(strpos($host, $allowHost) !== false)
        {
            $checkHost = true;
            break;
        }
    }
    if(!$checkHost) die("<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dli'>
<html xmlns='http://www.w3.org/1999/xhtml'>
<head>
  <meta http-equiv='Content-Type' content='text/html; charset=utf-8' />
  <title>Error</title>
</head>
<body>
<h2 style='color:red;text-align:center'>绑定域名访问错误</h2>
您版本绑定的域名是{$properties['domain']['value']}，您目前访问的域名是{$_SERVER['HTTP_HOST']}，如果有问题，请联系我们修改绑定域名。<br>
Email：<a href='mailto:$contactEmail'>$contactEmail</a><br>
电话：$contactMobile<br />
网址：<a href='https://www.zdoo.com/'>www.zdoo.com</a><br />
<br /><br /><br />
<h2 style='color:red;text-align:center'>Accounts has exceed the limit.</h2>
The binding domain is {$properties['domain']['value']}, please contact us to change binding domain.<br />
Email : <a href='mailto:philip@easycorp.ltd'>philip@easycorp.ltd</a><br />
Site : <a href='http://www.zdoo.org/'>http://www.zdoo.org/</a><br />
</body>
</html>");
}
    }

    /**
     * Contract index page.
     *
     * @access public
     * @return void
     */
    public function index()
    {
        $this->locate(inlink('browse'));
    }

    /**
     * Browse all sale contracts;
     *
     * @param  string $mode
     * @param  string $orderBy
     * @param  int    $recTotal
     * @param  int    $recPerPage
     * @param  int    $pageID
     * @access public
     * @return void
     */
    public function browse($mode = 'all', $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
    {
        $this->app->loadClass('pager', $static = true);
        $pager = new pager($recTotal, $recPerPage, $pageID);

        $contracts = $this->contract->getList('sale', 0, $mode, '', $orderBy, $pager);

        /* Save session for return link. */
        $this->session->set('contractList', $this->app->getURI(true));

        /* Build search form. */
        $this->loadModel('search');
        $this->config->contract->search['actionURL'] = inlink('browse', "mode=bysearch");
        $this->config->contract->search['params']['product']['values']  = array('' => '') + $this->loadModel('product')->getPairs();
        $this->search->setSearchParams($this->config->contract->search);

        /* Set allowed edit contract ID list. */
        $this->app->user->canEditContractIdList = ',' . implode(',', $this->contract->getContractsSawByMe('edit', array_keys($contracts))) . ',';

        $this->view->moduleMenu   = $this->contract->createModuleMenu('contract', 'sale');
        $this->view->title        = $this->lang->contract->browse;
        $this->view->contracts    = $contracts;
        $this->view->currencySign = $this->loadModel('common')->getCurrencySign();
        $this->view->currencyList = $this->common->getCurrencyList();
        $this->view->type         = 'sale';
        $this->view->moduleType   = 'contract';
        $this->view->mode         = $mode;
        $this->view->orderBy      = $orderBy;
        $this->view->pager        = $pager;
        if($contracts) $this->view->totalAmount = $this->contract->countAmount($contracts);

        $this->display();
    }

    /**
     * Create contract.
     *
     * @param  string $type         sale | purchase
     * @param  int    $customerID
     * @param  int    $orderID
     * @access public
     * @return void
     */
    public function create($type = 'sale', $customerID = 0, $orderID = 0)
    {
        $moduleType   = $this->config->contract->moduleType[$type];
        $customerType = $this->config->contract->customerType[$type];
        $relationType = $this->config->contract->relationType[$type];

        if($_POST)
        {
            if($moduleType == 'purchasecontract') $this->lang->contract->customer = $this->lang->purchasecontract->customer;

            $contractID = $this->contract->create($type);
            if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));

            $extra = array("crm.{$moduleType}", 'view', "contractID=$contractID", $this->post->name);
            $this->loadModel('action')->create($moduleType, $contractID, 'Created');
            $this->action->create($customerType, $this->post->customer, 'createContract', '', $extra);

            $message = $this->executeHooks($contractID, false);
            if(!$message) $message = $this->lang->saveSuccess;
            return $this->send(array('result' => 'success', 'message' => $message, 'locate' => inlink('browse'), 'contractID' => $contractID)); // ContractID is used for mobile.
        }

        if($customerID) $this->view->customer = $customerID;
        if($orderID)
        {
            $this->view->currentOrder = $this->loadModel('order', 'crm')->getByID($orderID);
            $this->view->orders       = $this->order->getList($mode = 'query', "o.customer={$customerID} and o.status = 'normal'");
        }

        $this->view->title        = $this->lang->$moduleType->create;
        $this->view->position[]   = $this->lang->$moduleType->create;
        $this->view->customers    = $this->loadModel('customer')->getPairs($relationType, $emptyOption = true, $orderBy = 'id_desc', $limit = $this->config->customerLimit, $customerID);
        $this->view->users        = $this->loadModel('user')->getPairs('nodeleted,noforbidden,noclosed');
        $this->view->currencyList = $this->loadModel('common')->getCurrencyList();
        $this->view->currencySign = $this->common->getCurrencySign();
        $this->view->mainCurrency = $this->config->setting->mainCurrency; // It is used for mobile.
        $this->view->type         = $type;
        $this->view->moduleType   = $moduleType;
        $this->view->orderID      = $orderID;
        $this->view->moduleMenu   = false;
        $this->display();
    }

    /**
     * Edit contract.
     *
     * @param  int    $contractID
     * @access public
     * @return void
     */
    public function edit($contractID)
    {
        $contract     = $this->contract->getByID($contractID);
        $moduleType   = $this->config->contract->moduleType[$contract->type];
        $customerType = $this->config->contract->customerType[$contract->type];

        if($_POST)
        {
            if($moduleType == 'purchasecontract') $this->lang->contract->customer = $this->lang->purchasecontract->customer;

            $changes = $this->contract->update($contractID);
            if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));

            $files        = $this->loadModel('file')->saveUpload($moduleType, $contractID);
            $scanFiles    = array();
            $inspectFiles = array();

            if($moduleType == 'contract')
            {
                $scanFiles    = $this->file->saveUpload($moduleType, $contractID, 'scanFile',    'scanFile',    'scanFileLabel');
                $inspectFiles = $this->file->saveUpload($moduleType, $contractID, 'inspectFile', 'inspectFile', 'inspectFileLabel');
                if(!empty($scanFiles) || !empty($inspectFiles))
                {
                    $data = array();
                    if(!empty($scanFiles))    $data['scanFile']    = 1;
                    if(!empty($inspectFiles)) $data['inspectFile'] = 1;
                    $this->dao->update(TABLE_CONTRACT)->data($data)->where('id')->eq($contractID)->exec();
                }
            }

            if($this->post->remark || $changes || $files || $scanFiles || $inspectFiles)
            {
                $fileAction = '';
                if($this->post->remark) $fileAction = $this->post->remark;
                if($files)        $fileAction  = $this->lang->addFiles . join(',', $files) . '<br>';
                if($scanFiles)    $fileAction .= $this->lang->contract->addScanFiles . join(',', $scanFiles) . '<br>';
                if($inspectFiles) $fileAction .= $this->lang->contract->addInspectFiles . join(',', $inspectFiles);
                $fileAction = trim($fileAction, '<br>');

                $action           = $this->post->remark == '' ? 'Edited' : 'Commented';
                $contractActionID = $this->loadModel('action')->create($moduleType, $contractID, $action, $fileAction);
                if($changes) $this->action->logHistory($contractActionID, $changes);

                $extra            = array("crm.{$moduleType}", 'view', "contractID=$contractID", $contract->name);
                $customerActionID = $this->action->create($customerType, $contract->customer, 'editContract', $fileAction, $extra);
                if($changes) $this->action->logHistory($customerActionID, $changes);

                if($contract->order)
                {
                    foreach($contract->order as $orderID)
                    {
                        $orderActionID = $this->action->create('order', $orderID, 'editContract', $fileAction, $extra);
                        if($changes) $this->action->logHistory($orderActionID, $changes);
                    }
                }
            }

            $message = $this->executeHooks($contractID);
            if(!$message) $message = $this->lang->saveSuccess;

            return $this->send(array('result' => 'success', 'message' => $message, 'locate' => inlink('view', "contractID=$contractID")));
        }

        $this->view->title        = $this->lang->$moduleType->edit;
        $this->view->position[]   = $this->lang->$moduleType->edit;
        $this->view->contract     = $contract;
        $this->view->customer     = $this->loadModel('customer')->getByID($contract->customer);
        $this->view->contacts     = $this->loadModel('contact', 'crm')->getPairs($contract->customer);
        $this->view->users        = $this->loadModel('user')->getPairs('nodeleted,noforbidden,noclosed');
        $this->view->currencyList = $this->loadModel('common')->getCurrencyList();
        $this->view->currencySign = $this->common->getCurrencySign();
        $this->view->moduleType   = $moduleType;
        $this->view->moduleMenu   = false;

        if($moduleType == 'contract')
        {
            $this->view->contractOrders = $this->loadModel('order', 'crm')->getByIdList($contract->order);
            $this->view->orders         = array('' => '') + $this->order->getList($mode = 'query', "o.customer={$contract->customer}");
            $this->view->addresses      = array('') +$this->loadModel('address', 'crm')->getPairsByObject('customer', $contract->customer);
        }

        $this->display();
    }

    /**
     * The delivery of the contract.
     *
     * @param  int    $contractID
     * @access public
     * @return void
     */
    public function delivery($contractID)
    {
        $contract = $this->contract->getByID($contractID);
        if(!empty($_POST))
        {
            $this->contract->delivery($contractID);
            if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));

            $this->loadModel('action');

            $extra = array('crm.contract', 'view', "contractID=$contractID", $contract->name);
            if($this->post->finish)
            {
                $this->action->create('contract', $contractID, 'finishDelivered', $this->post->comment, '', $this->post->deliveredBy);
                $this->action->create('customer', $contract->customer, 'finishDeliverContract', $this->post->comment, $extra, $this->post->deliveredBy);
            }
            else
            {
                $this->action->create('contract', $contractID, 'Delivered', $this->post->comment, '', $this->post->deliveredBy);
                $this->action->create('customer', $contract->customer, 'deliverContract', $this->post->comment, $extra, $this->post->deliveredBy);
            }

            $customerID         = $this->dao->select('customer')->from(TABLE_CONTRACT)->where('id')->eq($contractID)->fetch('customer');
            $deliverStageStatus = $this->loadModel('customer')->getStageDeliver($customerID)['status'];
            if($deliverStageStatus == 'wait') $this->customer->turnStage($customerID, 'deliver');
            if($deliverStageStatus == 'done') $this->customer->turnStage($customerID, 'serve');

            $message = $this->executeHooks($contractID);
            if(!$message) $message = $this->lang->saveSuccess;

            return $this->send(array('result' => 'success', 'message' => $message, 'locate' => 'reload'));
        }

        $this->view->title    = $this->lang->contract->delivery;
        $this->view->contract = $contract;
        $this->view->users    = $this->loadModel('user')->getPairs('nodeleted,noforbidden,noclosed');
        $this->display();
    }

    /**
     * Edit delivery.
     *
     * @param  int    $deliveryID
     * @access public
     * @return void
     */
    public function editDelivery($deliveryID)
    {
        $delivery = $this->contract->getDeliveryByID($deliveryID);
        $contract = $this->contract->getByID($delivery->contract);
        if(!empty($_POST))
        {
            $this->contract->editDelivery($delivery, $contract);
            if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));

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

        $this->view->title    = $this->lang->contract->editDelivery;
        $this->view->delivery = $delivery;
        $this->view->contract = $contract;
        $this->view->users    = $this->loadModel('user')->getPairs('nodeleted,noforbidden,noclosed');
        $this->display();
    }

    /**
     * Delete delivery.
     *
     * @param  int    $deliveryID
     * @access public
     * @return void
     */
    public function deleteDelivery($deliveryID)
    {
        $delivery = $this->contract->getDeliveryByID($deliveryID);
        $contract = $this->contract->getByID($delivery->contract);

        $this->contract->deleteDelivery($deliveryID);
        if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => $this->lang->fail));

        $deleteInfo = sprintf($this->lang->contract->deleteDeliveryInfo, $delivery->deliveredDate);
        $extra      = array('crm.contract', 'view', "contractID=$contract->id", $contract->name, '', $deleteInfo);
        $this->loadModel('action')->create('contract', $contract->id, 'deletedelivered', '', $deleteInfo);
        $this->action->create('customer', $contract->customer, 'deletedelivered', $this->post->comment, $extra, $this->post->returnedBy);

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

    /**
     * Receive payments of the contract.
     *
     * @param  int    $contractID
     * @access public
     * @return void
     */
    public function receive($contractID)
    {
        $this->loadModel('trade', 'cash');  // Must call loadModel function because the config of trade and the result message used the lang of trade.

        $contract     = $this->contract->getByID($contractID);
        $currencySign = $this->loadModel('common')->getCurrencySign();
        $moduleType   = $this->config->contract->moduleType[$contract->type];
        $customerType = $this->config->contract->customerType[$contract->type];
        $tradeType    = $this->config->contract->tradeType[$contract->type];

        if(!empty($_POST))
        {
            if($moduleType == 'purchasecontract')
            {
                $this->lang->plan->amount       = $this->lang->purchasecontract->thisAmount;
                $this->lang->plan->returnedDate = $this->lang->purchasecontract->returnedDate;
            }

            $result = $this->contract->receive($contractID);
            if(!empty($result['result']) && $result['result'] == 'fail') return $this->send($result);

            $this->loadModel('action');
            $extra = array("crm.{$moduleType}", 'view', "contractID=$contractID", $contract->name, '', $this->lang->$moduleType->return . zget($currencySign, $this->post->currency, '') . $this->post->amount);

            $receiveType = $this->config->contract->receiveType[$contract->type];
            $returnType  = $this->config->contract->returnType[$contract->type];
            if($this->post->finish)
            {
                $this->action->create($moduleType, $contractID, "finish{$returnType}", $this->post->comment, zget($currencySign, $this->post->currency, '') . $this->post->amount, $this->post->returnedBy);
                $this->action->create($customerType, $contract->customer, "finish{$receiveType}Contract", $this->post->comment, $extra, $this->post->returnedBy);
            }
            else
            {
                $this->action->create($moduleType, $contractID, $returnType, $this->post->comment, zget($currencySign, $this->post->currency, '') . $this->post->amount, $this->post->returnedBy);
                $this->action->create($customerType, $contract->customer, "{$receiveType}Contract", $this->post->comment, $extra, $this->post->returnedBy);
            }

            if(!empty($result['result']) && $result['result'] == 'success')
            {
                $message = $this->executeHooks($contractID);
                if($message) $result['message'] = $message;
            }

            return $this->send($result);
        }

        if($moduleType == 'contract')
        {
            $orders = $this->dao->select('`order`')->from(TABLE_CONTRACTORDER)->where('contract')->eq($contractID)->andWhere('`order`')->ne('0')->fetchPairs();
            $productList = $this->loadModel('product')->getPairs();
            if($orders)
            {
                $productList = array();
                $this->loadModel('order', 'crm');
                foreach($orders as $orderID)
                {
                    $order = $this->order->getByID($orderID);
                    if($order->product) $productList += $this->product->getPairsByIDList($order->product);
                }
            }

            $this->view->productList = array(0 => '') + $productList;
            $this->view->product     = isset($order->product) && strpos(trim($order->product, ','), ',') === false ? $order->product : '';
        }

        $this->view->title         = $contract->name;
        $this->view->users         = $this->loadModel('user')->getPairs('nodeleted,noforbidden,noclosed');
        $this->view->creator       = $this->user->getByAccount($contract->createdBy);
        $this->view->depositorList = $this->loadModel('depositor', 'cash')->getDepositorsWithCurrency($status = 'normal', $contract->currency);
        $this->view->deptList      = $this->loadModel('tree')->getOptionMenu('dept');
        $this->view->moduleType    = $moduleType;
        $this->view->contract      = $contract;
        $this->view->currencySign  = $currencySign;
        $this->view->categories    = $this->tree->getOptionMenu($tradeType, 0, false, 0, true);

        $this->display();
    }

    /**
     * Edit return.
     *
     * @param  int    $returnID
     * @access public
     * @return void
     */
    public function editReturn($returnID)
    {
        $return       = $this->contract->getReturnByID($returnID);
        $contract     = $this->contract->getByID($return->contract);
        $currencySign = $this->loadModel('common')->getCurrencySign();
        $moduleType   = $this->config->contract->moduleType[$contract->type];

        if(!empty($_POST))
        {
            if($moduleType == 'purchasecontract')
            {
                $this->lang->plan->amount       = $this->lang->purchasecontract->thisAmount;
                $this->lang->plan->returnedDate = $this->lang->purchasecontract->returnedDate;
            }

            $this->contract->editReturn($return, $contract);
            if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));

            $message = $this->executeHooks($returnID);
            if(!$message) $message = $this->lang->saveSuccess;

            return $this->send(array('result' => 'success', 'message' => $message, 'locate' => inlink('view', "contractID=$contract->id")));
        }

        $this->view->title        = $this->lang->$moduleType->editReturn;
        $this->view->users        = $this->loadModel('user')->getPairs('nodeleted,noforbidden,noclosed');
        $this->view->moduleType   = $moduleType;
        $this->view->return       = $return;
        $this->view->contract     = $contract;
        $this->view->currencySign = $currencySign;
        $this->display();
    }

    /**
     * Delete return.
     *
     * @param  int    $returnID
     * @access public
     * @return void
     */
    public function deleteReturn($returnID)
    {
        $return   = $this->contract->getReturnByID($returnID);
        $contract = $this->contract->getByID($return->contract);
        $currencySign = $this->loadModel('common')->getCurrencySign();

        $this->contract->deleteReturn($returnID);
        if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => $this->lang->fail));

        $moduleType   = $this->config->contract->moduleType[$contract->type];
        $customerType = $this->config->contract->customerType[$contract->type];
        $returnType   = $this->config->contract->returnType[$contract->type];
        $deleteInfo   = sprintf($this->lang->$moduleType->deleteReturnInfo, $return->returnedDate, zget($currencySign, $contract->currency, '') . $return->amount);
        $extra        = array("crm.{$moduleType}", 'view', "contractID=$contract->id", $contract->name, '', $deleteInfo);
        $this->loadModel('action')->create($moduleType, $contract->id, "delete{$returnType}", '', $deleteInfo);
        $this->action->create($customerType, $contract->customer, "delete{$returnType}", $this->post->comment, $extra, $this->post->returnedBy);

        $message = $this->executeHooks($returnID);
        if(!$message) $message = $this->lang->saveSuccess;

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

    /**
     * Cancel contract.
     *
     * @param  int    $contractID
     * @access public
     * @return void
     */
    public function cancel($contractID)
    {
        $contract     = $this->contract->getByID($contractID);
        $moduleType   = $this->config->contract->moduleType[$contract->type];
        $customerType = $this->config->contract->customerType[$contract->type];
        if(!empty($_POST))
        {
            $this->contract->cancel($contractID);
            if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));

            $extra = array("crm.{$moduleType}", 'view', "contractID=$contractID", $contract->name);
            $this->loadModel('action')->create($moduleType, $contractID, 'Canceled', $this->post->comment);
            $this->action->create($customerType, $contract->customer, 'cancelContract', $this->post->comment, $extra);

            if($contract->order)
            {
                foreach($contract->order as $orderID) $this->action->create('order', $orderID, 'cancelContract', $this->post->comment, $extra);
            }

            $message = $this->executeHooks($contractID);
            if(!$message) $message = $this->lang->saveSuccess;

            return $this->send(array('result' => 'success', 'message' => $message, 'locate' => 'reload'));
        }

        $this->view->title      = $this->lang->cancel;
        $this->view->moduleType = $moduleType;
        $this->view->contract   = $contract;
        $this->display();
    }

    /**
     * Finish contract.
     *
     * @param  int    $contractID
     * @access public
     * @return void
     */
    public function finish($contractID)
    {
        $contract     = $this->contract->getByID($contractID);
        $moduleType   = $this->config->contract->moduleType[$contract->type];
        $customerType = $this->config->contract->customerType[$contract->type];
        if(!empty($_POST))
        {
            $this->contract->finish($contractID);
            if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));

            $extra = array("crm.{$moduleType}", 'view', "contractID=$contractID", $contract->name);
            $this->loadModel('action')->create($moduleType, $contractID, 'Finished', $this->post->comment);
            $this->action->create($customerType, $contract->customer, 'finishContract', $this->post->comment, $extra);

            $message = $this->executeHooks($contractID);
            if(!$message) $message = $this->lang->saveSuccess;

            return $this->send(array('result' => 'success', 'message' => $message, 'locate' => 'reload'));
        }

        $this->view->title      = $this->lang->finish;
        $this->view->moduleType = $moduleType;
        $this->view->contract   = $contract;
        $this->display();
    }

    /**
     * View contract.
     *
     * @param  int    $contractID
     * @access public
     * @return void
     */
    public function view($contractID)
    {
        $this->loadModel('trade', 'cash');
        $contract = $this->contract->getByID($contractID);
        $this->loadModel('common')->checkPrivByCustomer(empty($contract) ? '0' : $contract->customer);

        /* Set allowed edit contract ID list. */
        $this->app->user->canEditContractIdList = ',' . implode(',', $this->contract->getContractsSawByMe('edit', (array)$contractID)) . ',';

        /* Save session for return link. */
        $uri = $this->app->getURI(true);
        $this->session->set('contactList', $uri);
        if(strpos($this->session->contractList, 'customer') === false and strpos($this->session->contractList, 'view') === false) $this->session->set('customerList', $this->app->getURI(true));
        if(strpos($this->session->contractList, 'order') === false and strpos($this->session->contractList, 'view') === false)    $this->session->set('orderList', $this->app->getURI(true));

        $expenseTypes = $this->loadModel('tree')->getOptionMenu('out', 0, $removeRoot = true);
        $incomeTypes  = $this->tree->getOptionMenu('in', 0, $removeRoot = true);

        foreach($expenseTypes as $key => $expenseType)
        {
            $path = explode('/', trim($expenseType, '/'));
            if(count($path) > 1) array_shift($path);

            $expenseTypes[$key] = implode('/', $path);
        }

        foreach($incomeTypes as $key => $incomeType)
        {
            $path = explode('/', trim($incomeType, '/'));
            if(count($path) > 1) array_shift($path);

            $incomeTypes[$key] = implode('/', $path);
        }

        $customers = array($contract->customer);
        foreach($contract->tradeList as $trade) if(!empty($trade->trader)) $customers[] = $trade->trader;

        $moduleType   = $this->config->contract->moduleType[$contract->type];
        $contractList = $moduleType . 'List';
        $browseLink   = $this->session->$contractList ? $this->session->$contractList : inlink('browse');

        $this->view->title         = $this->lang->$moduleType->view;
        $this->view->allCustomers  = $this->loadModel('customer')->getPairsByIDList($customers);
        $this->view->contacts      = $this->loadModel('contact', 'crm')->getPairs($contract->customer);
        $this->view->products      = $this->loadModel('product')->getPairs();
        $this->view->users         = $this->loadModel('user')->getPairs();
        $this->view->currencySign  = $this->loadModel('common')->getCurrencySign();
        $this->view->depositorList = $this->loadModel('depositor', 'cash')->getPairs();
        $this->view->currencyList  = $this->loadModel('common')->getCurrencySign();
        $this->view->deptList      = $this->tree->getPairs('dept');
        $this->view->categories    = $expenseTypes + $incomeTypes;
        $this->view->preAndNext    = $this->common->getPreAndNextObject('contract', $contractID);
        $this->view->contract      = $contract;
        $this->view->moduleType    = $moduleType;
        $this->view->customerType  = $this->config->contract->customerType[$contract->type];
        $this->view->browseLink    = $browseLink;
        $this->view->moduleMenu    = false;
        $this->view->appOperate    = $this->contract->buildAppOperateMenu($contract);

        if($moduleType == 'contract')
        {
            $this->view->orders    = $this->loadModel('order', 'crm')->getByIdList($contract->order);
            $this->view->addresses = $this->loadModel('address', 'crm')->getPairsByObject('customer', $contract->customer);
            $this->view->members   = $this->contract->getMembers($contractID);
        }

        $this->executeHooks($contractID);
        $this->display();
    }

    /**
     * Delete contract.
     *
     * @param  int    $contractID
     * @access public
     * @return void
     */
    public function delete($contractID)
    {
        $this->contract->delete(TABLE_CONTRACT, $contractID);
        if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));

        $this->executeHooks($contractID);
        return $this->send(array('result' => 'success', 'locate' => inlink('browse')));
    }

    /**
     * Manage team.
     *
     * @param  int    $contractID
     * @access public
     * @return void
     */
    public function manageTeam($contractID)
    {
        if($_POST)
        {
            $result = $this->contract->checkTeam();
            if(!empty($result['result']) && $result['result'] == 'fail') return $this->send($result);

            $oldMembers = $this->contract->getMembers($contractID);
            $this->contract->manageTeam($contractID);
            if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));

            $members = $this->contract->getMembers($contractID);
            if($members && $members != $oldMembers)
            {
                $actionID = $this->loadModel('action')->create('contract', $contractID, 'manageTeam');
                $this->sendmail($contractID, $actionID);
            }

            $message = $this->executeHooks($contractID);
            if(!$message) $message = $this->lang->saveSuccess;

            return $this->send(array('result' => 'success', 'message' => $message, 'locate' => 'reload'));
        }

        $this->view->title      = $this->lang->contract->manageTeam;
        $this->view->contract   = $this->contract->getByID($contractID);
        $this->view->users      = $this->loadModel('user')->getPairs('noclosed,nodeleted,noforbidden');
        $this->view->members    = $this->contract->getMembers($contractID);
        $this->view->modalWidth = 1000;
        $this->display();
    }

    /**
     * Confirm contribution of a team.
     *
     * @param  int    $contractID
     * @param  string $status
     * @access public
     * @return void
     */
    public function confirmTeam($contractID, $status = '')
    {
        if($status == 'accept' or $status == 'reject')
        {
            $this->contract->confirmTeam($contractID, $status);
            if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));

            $actionID = $this->loadModel('action')->create('contract', $contractID, 'confirmTeam', '', $this->lang->contract->team->statusList[$status]);
            if($status == 'reject') $this->sendmail($contractID, $actionID);

            return $this->send(array('result' => 'success', 'locate' => $this->server->http_referer));
        }

        $this->view->title    = $this->lang->contract->confirmTeam;
        $this->view->contract = $this->contract->getByID($contractID);
        $this->view->users    = $this->loadModel('user')->getPairs('noclosed,nodelete,noforbidden');
        $this->view->members  = $this->contract->getMembers($contractID);
        $this->display();
    }

    /**
     * Send notice or email.
     *
     * @param  int    $contractID
     * @param  int    $actionID
     * @access public
     * @return void
     */
    public function sendmail($contractID, $actionID)
    {
        /* Reset $this->output. */
        $this->clear();

        /* Get action info. */
        $action          = $this->loadModel('action')->getById($actionID);
        $history         = $this->action->getHistory($actionID);
        $action->history = isset($history[$actionID]) ? $history[$actionID] : array();

        /* Set toList and ccList. */
        $toList  = '';
        $users   = $this->loadModel('user')->getPairs();
        $subject = "{$this->lang->contract->common}#{$contractID} " . str_replace('(%)', '', $this->lang->contract->team->contribution) . ' - ' . zget($users, $this->app->user->account);
        if($action->action == 'manageteam')
        {
            $toList = $this->dao->select('account')->from(TABLE_TEAM)
                ->where('type')->eq('contract')
                ->andWhere('id')->eq($contractID)
                ->andWhere('status')->ne('accept')
                ->fetchPairs();
            $toList = implode(',', $toList);
        }
        elseif($action->action == 'confirmteam')
        {
            $toList = $this->dao->select('actor')->from(TABLE_ACTION)
                ->where('objectType')->eq('contract')
                ->andWhere('objectID')->eq($contractID)
                ->andWhere('action')->eq('manageteam')
                ->orderBy('id_desc')
                ->limit(1)
                ->fetch('actor');

            $subject .= '#' . $this->lang->contract->team->statusList['reject'];
        }

        /* send notice if user is online and return failed accounts. */
        $toList = $this->action->sendNotice($actionID, $toList);

        /* Create the email content. */
        $this->view->contract = $this->contract->getById($contractID);
        $this->view->members  = $this->contract->getMembers($contractID);
        $this->view->action   = $action;
        $this->view->users    = $users;

        $mailContent = $this->parse($this->moduleName, 'sendmail');

        /* Send emails. */
        $this->loadModel('mail')->send($toList, $subject, $mailContent);
        if($this->mail->isError()) trigger_error(join("\n", $this->mail->getError()));
    }

    /**
     * Export contracts.
     *
     * @param  string $type     sale | purchase
     * @param  string $mode     all | thisPage
     * @access public
     * @return void
     */
    public function export($type = 'sale', $mode = 'all')
    {
        $moduleType = $this->config->contract->moduleType[$type];

        if($_POST)
        {
            $contractLang   = $this->lang->$moduleType;
            $contractConfig = $this->config->$moduleType;

            /* Create field lists. */
            $fields = explode(',', $contractConfig->list->exportFields);
            foreach($fields as $key => $fieldName)
            {
                $fieldName = trim($fieldName);
                $fields[$fieldName] = isset($contractLang->$fieldName) ? $contractLang->$fieldName : $fieldName;
                unset($fields[$key]);
            }

            $contracts      = array();
            $contractQuery  = $moduleType . 'QueryCondition';
            $queryCondition = $this->session->$contractQuery;
            if($queryCondition)
            {
                if($mode == 'all' && strpos($queryCondition, 'LIMIT') !== false) $queryCondition = substr($queryCondition, 0, strpos($queryCondition, 'LIMIT'));

                $stmt = $this->dbh->query($queryCondition);
                while($row = $stmt->fetch()) $contracts[$row->id] = $row;
            }

            $users        = $this->loadModel('user')->getPairs();
            $customers    = $this->loadModel('customer')->getPairs();
            $contacts     = $this->loadModel('contact', 'crm')->getPairs();
            $relatedFiles = $this->dao->select('id, objectID, pathname, title')->from(TABLE_FILE)->where('objectType')->eq('contract')->andWhere('objectID')->in(@array_keys($contracts))->fetchGroup('objectID');

            if($moduleType == 'contract')
            {
                $addresses = $this->loadModel('address', 'crm')->getPairs(); 
                $contractOrderList = $this->dao->select('*')->from(TABLE_CONTRACTORDER)->fetchGroup('contract');
                foreach($contracts as $id => $contract)
                {
                    if(isset($contractOrderList[$id]))
                    {
                        $contract->order = array();
                        foreach($contractOrderList[$id] as $contractOrder)
                        {
                            $contract->order[] = $contractOrder->order;
                        }
                    }
                    else
                    {
                        $contract->order = '';
                    }
                }

                /* Get related products names. */
                $orderPairs = array();
                $products   = array();
                $orders     = $this->dao->select('*')->from(TABLE_ORDER)->fetchAll('id');
                $this->loadModel('order', 'crm')->setProductsForOrders($orders);
                foreach($orders as $key => $order)
                {
                    $productName = count($order->products) > 1 ? current($order->products) . $this->lang->etc : current($order->products);
                    $orderPairs[$key] = sprintf($this->lang->order->titleLBL, zget($customers, $order->customer), $productName, date('Y-m-d', strtotime($order->createdDate)));
                    $products[$key]   = $order->products;
                }
            }


            foreach($contracts as $contract)
            {
                $contract->items = htmlspecialchars_decode($contract->items);
                $contract->items = str_replace("<br />", "\n", $contract->items);
                $contract->items = str_replace('"', '""', $contract->items);

                /* fill some field with useful value. */
                if(isset($customers[$contract->customer])) $contract->customer = $customers[$contract->customer] . "(#$contract->customer)";
                if(isset($contacts[$contract->contact]))   $contract->contact  = $contacts[$contract->contact] . "(#$contract->contact)";

                if(isset($contractLang->signTypeList[$contract->signType])) $contract->signType = $contractLang->signTypeList[$contract->signType];
                if(isset($contractLang->statusList[$contract->status]))     $contract->status   = $contractLang->statusList[$contract->status];
                if(isset($contractLang->returnList[$contract->return]))     $contract->return   = $contractLang->returnList[$contract->return];
                if(isset($this->lang->currencyList[$contract->currency]))   $contract->currency = $this->lang->currencyList[$contract->currency];

                if(isset($users[$contract->createdBy]))   $contract->createdBy   = $users[$contract->createdBy];
                if(isset($users[$contract->editedBy]))    $contract->editedBy    = $users[$contract->editedBy];
                if(isset($users[$contract->signedBy]))    $contract->signedBy    = $users[$contract->signedBy];
                if(isset($users[$contract->returnedBy]))  $contract->returnedBy  = $users[$contract->returnedBy];
                if(isset($users[$contract->finishedBy]))  $contract->finishedBy  = $users[$contract->finishedBy];
                if(isset($users[$contract->canceledBy]))  $contract->canceledBy  = $users[$contract->canceledBy];
                if(isset($users[$contract->contactedBy])) $contract->contactedBy = $users[$contract->contactedBy];

                $contract->begin         = formatTime($contract->begin, DT_DATE1);
                $contract->end           = formatTime($contract->end, DT_DATE1);
                $contract->createdDate   = formatTime($contract->createdDate, DT_DATE1);
                $contract->editedDate    = formatTime($contract->editedDate, DT_DATE1);
                $contract->signedDate    = formatTime($contract->signedDate, DT_DATE1);
                $contract->returnedDate  = formatTime($contract->returnedDate, DT_DATE1);
                $contract->finishedDate  = formatTime($contract->finishedDate, DT_DATE1);
                $contract->canceledDate  = formatTime($contract->canceledDate, DT_DATE1);
                $contract->contactedDate = formatTime($contract->contactedDate, DT_DATE1);
                $contract->nextDate      = formatTime($contract->nextDate, DT_DATE1);

                if($moduleType == 'contract')
                {
                    if(isset($addresses[$contract->address])) $contract->address = $addresses[$contract->address];
                    if(isset($contractLang->deliveryList[$contract->delivery])) $contract->delivery = $contractLang->deliveryList[$contract->delivery];
                    if(isset($users[$contract->deliveredBy])) $contract->deliveredBy = $users[$contract->deliveredBy];

                    $contract->deliveredDate = formatTime($contract->deliveredDate, DT_DATE1);

                    if($contract->handlers)
                    {
                        $tmpHandlers = array();
                        $handlers = explode(',', $contract->handlers);
                        foreach($handlers as $handler)
                        {
                            if(!$handler) continue;
                            $handler = trim($handler);
                            $tmpHandlers[] = isset($users[$handler]) ? $users[$handler] : $handler;
                        }

                        $contract->handlers = join("; \n", $tmpHandlers);
                    }

                    $contract->product = '';
                    if(!empty($contract->order))
                    {
                        $tmpOrders   = array();
                        $tmpProducts = array();
                        foreach($contract->order as $orderID)
                        {
                            if(!$orderID) continue;
                            $orderID = trim($orderID);
                            $tmpOrders[] = isset($orderPairs[$orderID]) ? $orderPairs[$orderID] : $orderID;
                            if(isset($products[$orderID]))
                            {
                                foreach($products[$orderID] as $product) $tmpProducts[] = $product;
                            }
                        }

                        $contract->order   = join("; \n", $tmpOrders);
                        $contract->product = join("; \n", $tmpProducts);
                    }
                }

                /* Set related files. */
                $contract->files = '';
                if(isset($relatedFiles[$contract->id]))
                {
                    foreach($relatedFiles[$contract->id] as $file)
                    {
                        $fileURL = 'http://' . $this->server->http_host . $this->config->webRoot . "data/upload/" . $file->pathname;
                        $contract->files .= html::a($fileURL, $file->title, '_blank') . '<br />';
                    }
                }
            }

            $this->post->set('fields', $fields);
            $this->post->set('rows', $contracts);
            $this->post->set('kind', 'contract');
            $this->fetch('file', 'export2CSV' , $_POST);
        }

        $this->view->fileName = $this->lang->$moduleType->list;
        $this->display();
    }

    /**
     * Get order by ajax.
     *
     * @param  int       $customerID
     * @param  string    $status
     * @access public
     * @return string
     */
    public function ajaxGetOrder($customerID, $status = '')
    {
        $html  = "<div class='form-group'><span class='col-sm-6'><select name='order[]' id='order' class='select-order form-control'>";
        $html .= "<option value='' data-real='' data-currency=''></option>";

        $orders = $this->loadModel('order', 'crm')->getListByCustomer($customerID, $status);
        foreach($orders as $order)
        {
            if(!$order) continue;

            $html .= "<option value='{$order->id}' data-real='{$order->plan}' data-currency='{$order->currency}'>{$order->title}</option>";
        }

        $html .= '</select></span>';
        $html .= "<span class='col-sm-4'><div class='input-group'><span class='input-group-addon order-currency'>￥</span>";
        $html .= html::input('real[]', '', "class='order-real form-control' placeholder='{$this->lang->contract->placeholder->real}'");
        $html .= "<span class='input-group-btn'>" . html::a('javascript:;', "<i class='icon-plus'></i>", "class='plus btn'") . "</span>";
        $html .= "<span class='input-group-btn'>" . html::a('javascript:;', "<i class='icon-remove'></i>", "class='minus btn'") . "</span></div></span></div>";

        echo $html;
    }

    /**
     * Get option menu by ajax.
     *
     * @param  int    $customer
     * @access public
     * @return void
     */
    public function ajaxGetOptionMenu($customer, $contractID = 0, $type = 'sale')
    {
        $contractList = $this->contract->getList($type, $customer);
        if($this->app->getViewType() == 'json') return $this->send(array('result' => 'success', 'contracts' => $contractList)); // It is used for mobile

        echo "<option value=''></option>";
        foreach($contractList as $id => $contract)
        {
            $date     = date('Y-m-d', strtotime($contract->createdDate));
            $selected = $id == $contractID ? "selected='selected'" : '';
            echo "<option value='{$id}' data-amount='{$contract->amount}' {$selected}>{$contract->name}({$contract->amount}  {$date})</option>";
        }
        exit;
    }

    /**
     * Get addresses by ajax.
     *
     * @param  int    $customer
     * @access public
     * @return void
     */
    public function ajaxGetAddresses($customer = 0)
    {
        $addresses = $this->loadModel('address', 'crm')->getPairsByObject('customer', $customer);
        if($this->app->getViewType() == 'json') return $this->send(array('result' => 'success', 'addresses' => $addresses)); // It is used for mobile

        $html = "<option value='0'></option>";
        foreach($addresses as $id => $location) $html .= "<option value='$id'>$location</option>";
        echo $html;
    }

    /**
     * Search contract by ajax.
     *
     * @param  string $key
     * @param  string $contracts
     * @access public
     * @return void
     */
    public function ajaxSearchContract($key = '', $contracts = '')
    {
        $this->view->title     = $this->lang->contract->search;
        $this->view->key       = $key;
        $this->view->contracts = $contracts;
        $this->display();
    }

    /**
     * Ajax get contract pairs.
     *
     * @param  string $key
     * @param  int    $limit
     * @access public
     * @return void
     */
    public function ajaxGetPairs($key = '', $limit = 0)
    {
        $i      = 0;
        $result = array();
        if(!$limit) $limit = $this->config->searchLimit;
        $contracts = $this->contract->getPairs('sale');
        foreach($contracts as $id => $contract)
        {
            if($limit > 0 && $i > $limit) break;
            if(stripos($contract,  $key) !== false)
            {
                $result[$id] = $contract;
                $i++;
            }
        }
        if($i < 1)
        {
            $result['info'] = $this->lang->noResultsMatch;
        }

        die(json_encode($result));
    }
}
