YetiForceCompany/YetiForceCRM

View on GitHub
modules/Calendar/models/RecuringEvents.php

Summary

Maintainability
D
2 days
Test Coverage
F
1%
<?php

/**
 * Recurring Events Class.
 *
 * @copyright YetiForce S.A.
 * @license   YetiForce Public License 6.5 (licenses/LicenseEN.txt or yetiforce.com)
 * @author    Tomasz Kur <t.kur@yetiforce.com>
 */
class Calendar_RecuringEvents_Model extends \App\Base
{
    public $recordModel;
    public $typeSaving;
    public $isNew;
    public $changes;
    public $templateRecordId;

    const UPDATE_ALL_EVENTS = 1;
    const UPDATE_THIS_EVENT = 2;
    const UPDATE_FUTURE_EVENTS = 3;

    /**
     * Function to get empty instance.
     *
     * @return Calendar_RecuringEvents_Model
     */
    public static function getInstance()
    {
        return new self();
    }

    /**
     * Function to create new records in never ending events. Function uses only by cron.
     *
     * @param int $recordId
     */
    public function updateNeverEndingEvents($recordId)
    {
        $record = Vtiger_Record_Model::getInstanceById($recordId);
        $cleanInstance = Vtiger_Record_Model::getCleanInstance($record->getModuleName());
        $cleanInstance->setData($record->getData());
        $this->recordModel = $cleanInstance;
        $records = $this->getLastRecord($recordId);
        $dates = $this->getDates($records['date_start'] . ' ' . $records['time_start'], $records['due_date'] . ' ' . $records['time_end']);
        unset($dates[0]);
        $endingDate = date('Y-m-d', strtotime(date('Y-m-d') . ' +1 year'));
        foreach ($dates as $date) {
            if ($endingDate > $date['startDate']) {
                $this->createRecords([$date]);
            }
        }
    }

    /**
     * Function to get instance of class.
     *
     * @param \App\Request $request
     *
     * @return Calendar_RecuringEvents_Model
     */
    public static function getInstanceFromRequest(App\Request $request)
    {
        $instance = new self();
        $moduleName = $request->getModule();
        $instance->recordModel = Vtiger_Record_Model::getCleanInstance($moduleName);
        $instance->isNew = $request->isEmpty('record');
        if (!$instance->isNew) {
            $instance->templateRecordId = $request->getInteger('record');
            $instance->typeSaving = $request->getInteger('typeSaving');
            if (empty($instance->typeSaving)) {
                $instance->typeSaving = self::UPDATE_THIS_EVENT;
            }
        }
        return $instance;
    }

    /**
     * Function to set data to record model.
     *
     * @param array $values
     */
    public function setData($values)
    {
        $this->recordModel->setData($values);
    }

    /**
     * Function to create new records.
     *
     * @param array $dates
     */
    public function createRecords($dates)
    {
        foreach ($dates as $date) {
            $record = clone $this->recordModel;
            $record->set('date_start', $date['startDate']);
            $record->set('time_start', $date['startTime']);
            $record->set('due_date', $date['endDate']);
            $record->set('time_end', $date['endTime']);
            $record->save();
        }
    }

    /**
     * Function to set changes which user modified.
     *
     * @param type $values
     */
    public function setChanges($values)
    {
        $this->changes = $values;
    }

    /**
     * Update ommited records, change recuring rule for each records.
     *
     * @param type $records
     * @param type $dateStart
     */
    public function updateOmmitedRecords($records, $dateStart)
    {
        foreach ($records as $recordId) {
            $record = Vtiger_Record_Model::getInstanceById($recordId);
            $rule = new \Recurr\Rule($record->get('recurrence'));
            $rule->setUntil(new \DateTime($dateStart));
            $record->set('recurrence', $rule->getString());
            $record->save();
        }
    }

    /**
     * Function to edit record.
     *
     * @param int   $recordId
     * @param array $dates
     */
    public function updateRecord($recordId, $dates)
    {
        $record = Vtiger_Record_Model::getInstanceById($recordId);
        foreach ($this->changes as $fieldName => $value) {
            $record->set($fieldName, $this->recordModel->get($fieldName));
        }
        $record->set('date_start', $dates['startDate']);
        $record->set('time_start', $dates['startTime']);
        $record->set('due_date', $dates['endDate']);
        $record->set('time_end', $dates['endTime']);
        $record->save();
    }

    /**
     * Save records.
     */
    public function save()
    {
        if (!$this->isNew) {
            switch ($this->typeSaving) {
                case self::UPDATE_ALL_EVENTS:
                    $dates = [];
                    $recordsIds = $this->getRecords($this->recordModel->get('followup'));
                    $itemNumber = 0;
                    foreach ($recordsIds as $recordId => $data) {
                        if (0 === $itemNumber) {
                            $dates = $this->getDates($data['date_start'] . ' ' . $data['time_start'], $data['due_date'] . ' ' . $data['time_end']);
                        }
                        if ($recordId === $this->templateRecordId) {
                            unset($dates[$itemNumber]);
                            ++$itemNumber;
                            continue;
                        }
                        if (isset($dates[$itemNumber])) {
                            $this->updateRecord($recordId, $dates[$itemNumber]);
                            unset($dates[$itemNumber]);
                        } else {
                            Vtiger_Record_Model::getInstanceById($recordId)->delete();
                        }
                        ++$itemNumber;
                    }
                    if ($dates) {
                        $this->createRecords($dates);
                    }
                    break;
                case self::UPDATE_FUTURE_EVENTS:
                    $recordsIds = $this->getRecords($this->recordModel->get('followup'));
                    $itemNumber = 0;
                    $skip = true;
                    $omittedRecords = [];
                    $dates = reset($recordsIds);
                    $dates = $this->getDates($dates['date_start'] . ' ' . $dates['time_start'], $dates['due_date'] . ' ' . $dates['time_end'], $dates['recurrence']);
                    foreach ($recordsIds as $recordId => $data) {
                        if ($skip && $data['date_start'] >= $this->recordModel->get('date_start')) {
                            $skip = false;
                            if (!($this->recordModel->getPreviousValue('recurrence') === $this->recordModel->get('recurrence'))) {
                                $this->updateOmmitedRecords($omittedRecords, $data['date_start']);
                                $this->changes['followup'] = $recordId;
                                $this->recordModel->set('followup', $recordId);
                                $dates = $this->getDates($data['date_start'] . ' ' . $data['time_start'], $data['due_date'] . ' ' . $data['time_end']);
                                $itemNumber = 0;
                            }
                        }
                        if ($skip) {
                            $omittedRecords[] = $recordId;
                            unset($dates[$itemNumber]);
                            ++$itemNumber;
                            continue;
                        }
                        if (isset($dates[$itemNumber])) {
                            $this->updateRecord($recordId, $dates[$itemNumber]);
                            unset($dates[$itemNumber]);
                        } else {
                            Vtiger_Record_Model::getInstanceById($recordId)->delete();
                        }
                        ++$itemNumber;
                    }
                    if ($dates) {
                        $this->createRecords($dates);
                    }
                    break;
                default:
                    break;
            }
        } else {
            $dates = $this->getDates($this->recordModel->get('date_start') . ' ' . $this->recordModel->get('time_start'), $this->recordModel->get('due_date') . ' ' . $this->recordModel->get('time_end'));
            unset($dates[0]);
            if ($dates) {
                $this->createRecords($dates);
            }
        }
    }

    /**
     * Function to remove records.
     */
    public function delete()
    {
        switch ($this->typeSaving) {
            case self::UPDATE_ALL_EVENTS:
                $records = $this->getRecords($this->recordModel->get('followup'));
                foreach ($records as $recordId => $data) {
                    if ($recordId !== $this->templateRecordId) {
                        Vtiger_Record_Model::getInstanceById($recordId)->changeState('Trash');
                    }
                }
                break;
            case self::UPDATE_FUTURE_EVENTS:
                $recordsIds = $this->getRecords($this->recordModel->get('followup'));
                $skip = true;
                $omittedRecords = [];
                foreach ($recordsIds as $recordId => $data) {
                    if ($skip && $data['date_start'] >= $this->recordModel->get('date_start')) {
                        $this->updateOmmitedRecords($omittedRecords, $data['date_start']);
                        $skip = false;
                    }
                    if ($skip) {
                        $omittedRecords[] = $recordId;
                        continue;
                    }
                    Vtiger_Record_Model::getInstanceById($recordId)->changeState('Trash');
                }
                break;
            case self::UPDATE_THIS_EVENT:
                if ($this->templateRecordId === $this->recordModel->get('followup')) {
                    $recordsIds = $this->getRecords($this->recordModel->get('followup'));
                    $skip = true;
                    foreach ($recordsIds as $recordId => $data) {
                        if ($data['date_start'] >= $this->recordModel->get('date_start')) {
                            App\Db::getInstance()->createCommand()->update('vtiger_activity', ['followup' => $recordId, 'reapeat' => 1], ['followup' => $this->templateRecordId])->execute();
                            break;
                        }
                    }
                }
                break;
            default:
                break;
        }
    }

    /**
     * Check if recurrence rule is never ending.
     *
     * @param type $recurrenceRule
     *
     * @return type
     */
    public function isNeverEndingRule($recurrenceRule)
    {
        return false === strpos($recurrenceRule, 'COUNT') && false === strpos($recurrenceRule, 'UNTIL');
    }

    /**
     * Function to get dates.
     *
     * @param string $startDateTime
     * @param string $endDateTime
     * @param string $recurrenceRule
     *
     * @return array
     */
    public function getDates($startDateTime, $endDateTime, $recurrenceRule = false)
    {
        if (!$recurrenceRule) {
            $recurrenceRule = $this->recordModel->get('recurrence');
        }
        $isNeverEnding = $this->isNeverEndingRule($recurrenceRule);
        if ($isNeverEnding) {
            $endingDate = date('Y-m-d', strtotime(date('Y-m-d', strtotime($startDateTime)) . ' +1 year'));
        }
        $rule = new \Recurr\Rule($recurrenceRule, new \DateTime($startDateTime), new \DateTime($endDateTime));
        $data = (new \Recurr\Transformer\ArrayTransformer())->transform($rule);
        $dates = [];
        foreach ($data as $date) {
            if ($isNeverEnding && $date->getStart()->format('Y-m-d') > $endingDate) {
                break;
            }
            $dates[] = [
                'startDate' => $date->getStart()->format('Y-m-d'),
                'startTime' => $date->getStart()->format('H:i:s'),
                'endDate' => $date->getEnd()->format('Y-m-d'),
                'endTime' => $date->getEnd()->format('H:i:s'),
            ];
        }
        return $dates;
    }

    /**
     * Function to get related records.
     *
     * @param int $id
     *
     * @return array
     */
    public function getRecords($id)
    {
        return (new App\Db\Query())->from('vtiger_activity')
            ->where(['followup' => $id, 'deleted' => 0, 'reapeat' => 1])
            ->orderBy(['date_start' => SORT_ASC])
            ->indexBy('activityid')
            ->all();
    }

    /**
     * Function to get the last record in series.
     *
     * @param int $id
     *
     * @return array
     */
    public function getLastRecord($id)
    {
        return (new App\Db\Query())->from('vtiger_activity')
            ->where(['followup' => $id, 'deleted' => 0, 'reapeat' => 1])
            ->orderBy(['date_start' => SORT_DESC])
            ->limit(1)
            ->indexBy('activityid')
            ->one();
    }
}