/**
  * Checks for broken counters.
  * Expirity counter is broken if it is < 0, or if it is more than tasks count in list of expired tasks
  * Other counters is broken if it is < 0, or if it is != tasks count in list of respective tasks
  *
  * Method is called inside CTask::GetList() to perform recounting of broken counters.
  * 
  * @param $arFilter Filter was used in GetList() call
  * @param $tasksCountInList Number of records returned by GetList() call
  */
 public static function onTaskGetList($arFilter, $tasksCountInList)
 {
     if (!CTaskCountersProcessorInstaller::isInstallComplete()) {
         return;
     }
     // Is there our marker?
     if (!(array_key_exists('::MARKERS', $arFilter) && array_key_exists(self::MARKER_ID, $arFilter['::MARKERS']) && $tasksCountInList !== null)) {
         return;
     }
     $tasksCountInList = (int) $tasksCountInList;
     $counterOwnerUserId = $arFilter['::MARKERS'][self::MARKER_ID]['userId'];
     $counterId = $arFilter['::MARKERS'][self::MARKER_ID]['counterId'];
     $counterValue = (int) CUserCounter::GetValue($counterOwnerUserId, $counterId, $site_id = '**');
     if (in_array($counterId, array(CTaskCountersProcessor::COUNTER_TASKS_MY_EXPIRED, CTaskCountersProcessor::COUNTER_TASKS_ACCOMPLICE_EXPIRED, CTaskCountersProcessor::COUNTER_TASKS_AUDITOR_EXPIRED, CTaskCountersProcessor::COUNTER_TASKS_ORIGINATOR_EXPIRED, CTaskCountersProcessor::COUNTER_TASKS_MY_EXPIRED_CANDIDATES, CTaskCountersProcessor::COUNTER_TASKS_ACCOMPLICE_EXPIRED_CANDIDATES), true)) {
         $isExpirityCounter = true;
     } else {
         $isExpirityCounter = false;
     }
     $isCounterBrokeDetected = false;
     $realTasksCount = null;
     // Is checksum correct?
     $filterCheksum = $arFilter['::MARKERS'][self::MARKER_ID]['filterCheksum'];
     $realCheksum = self::calcFilterChecksum($arFilter);
     // break detection part
     if ($filterCheksum === $realCheksum) {
         $realTasksCount = $tasksCountInList;
         if ($counterValue < 0 || $tasksCountInList != $counterValue) {
             $isCounterBrokeDetected = true;
         }
     } else {
         if (isset($arFilter['SAME_GROUP_PARENT'], $arFilter['ONLY_ROOT_TASKS']) && $arFilter['SAME_GROUP_PARENT'] === 'Y' && $arFilter['ONLY_ROOT_TASKS'] === 'Y') {
             // unset the corresponding fields and try to compare checksums again
             unset($arFilter['SAME_GROUP_PARENT']);
             unset($arFilter['ONLY_ROOT_TASKS']);
             $realCheksum = self::calcFilterChecksum($arFilter);
             if ($filterCheksum === $realCheksum) {
                 // tasks count in list shouldn't be more than registered in counter
                 // and counter shouldn't be less than zero
                 if ($counterValue < 0 || $tasksCountInList > $counterValue) {
                     $isCounterBrokeDetected = true;
                 } else {
                     if (static::getCountersRecheckForSubTasksNeed()) {
                         $rsTasksCount = CTasks::getCount($arFilter, array('bIgnoreDbErrors' => true, 'bSkipUserFields' => true, 'bSkipExtraTables' => true));
                         if ($rsTasksCount && ($arTasksCount = $rsTasksCount->fetch()) && isset($arTasksCount['CNT'])) {
                             $realTasksCount = (int) $arTasksCount['CNT'];
                             if ($realTasksCount != $counterValue) {
                                 // and finally check
                                 $isCounterBrokeDetected = true;
                             }
                         }
                     }
                 }
             }
         }
     }
     /*
     if ( ! $isCounterBrokeDetected )
     {
     	if ($counterValue < 0)
     	{
     		$isCounterBrokeDetected = true;
     	}
     	else if ($realTasksCount !== null)
     	{
     		if ($isExpirityCounter)
     		{
     			if ($realTasksCount < $counterValue)
     				$isCounterBrokeDetected = true;
     		}
     		else
     		{
     			if ($realTasksCount !== $counterValue)
     				$isCounterBrokeDetected = true;
     		}
     	}
     }
     */
     if ($isCounterBrokeDetected) {
         ob_start();
         // a special way for correction of 'deadline expired' counters
         if ($isExpirityCounter) {
             // pend counters reinstalling (agent is used)
             self::pendCountersRecalculation();
         } else {
             if ($realTasksCount !== null) {
                 $delta = $realTasksCount - $counterValue;
                 CTaskCountersQueue::push($counterId, CTaskCountersQueue::OP_INCREMENT, array($counterOwnerUserId), $delta);
                 CTaskCountersQueue::execute();
             } else {
                 CTaskAssert::logError('[0x97e63b37] counter "' . $counterId . '" was mistimed for user ' . $counterOwnerUserId . '. But no correct data available for recount.');
             }
         }
         ob_end_clean();
     }
 }
 public static function onAfterTaskUpdate($taskId, $arPrevFields, $arNewFields)
 {
     // Deadline was changed?
     if (isset($arNewFields['DEADLINE']) && $arNewFields['DEADLINE'] != $arPrevFields['DEADLINE']) {
         $deadlineAfter = $arNewFields['DEADLINE'];
     } else {
         $deadlineAfter = $arPrevFields['DEADLINE'];
     }
     // Status was changed?
     if (isset($arNewFields['STATUS']) && $arNewFields['STATUS'] != $arPrevFields['REAL_STATUS']) {
         self::onAfterStatusChanged($taskId, $arPrevFields['REAL_STATUS'], $arNewFields['STATUS'], $arPrevFields['DEADLINE'], $arPrevFields['RESPONSIBLE_ID'], $arPrevFields['ACCOMPLICES'], $arPrevFields['CREATED_BY']);
         $statusAfter = $arNewFields['STATUS'];
     } else {
         $statusAfter = $arPrevFields['REAL_STATUS'];
     }
     if (isset($arNewFields['CREATED_BY'])) {
         $originatorAfter = $arNewFields['CREATED_BY'];
     } else {
         $originatorAfter = $arPrevFields['CREATED_BY'];
     }
     if (isset($arNewFields['RESPONSIBLE_ID'])) {
         $responsibleAfter = $arNewFields['RESPONSIBLE_ID'];
     } else {
         $responsibleAfter = $arPrevFields['RESPONSIBLE_ID'];
     }
     if (isset($arNewFields['ACCOMPLICES'])) {
         $arAccomplicesAfter = $arNewFields['ACCOMPLICES'];
     } else {
         $arAccomplicesAfter = $arPrevFields['ACCOMPLICES'];
     }
     // Count absents deadline (only for tasks that not from self to self)
     if ($arPrevFields['CREATED_BY'] != $arPrevFields['RESPONSIBLE_ID'] && !self::isCompletedStatus($statusAfter)) {
         $deadlineWasAbsent = self::isDeadlineAbsentsInFields($arPrevFields);
         if (!array_key_exists('DEADLINE', $arNewFields)) {
             $deadlineIsAbsents = $deadlineWasAbsent;
         } else {
             $deadlineIsAbsents = self::isDeadlineAbsentsInFields($arNewFields);
         }
         // Deadline was added?
         if ($deadlineWasAbsent && !$deadlineIsAbsents) {
             CTaskCountersQueue::push(CTaskCountersProcessor::COUNTER_TASKS_ORIGINATOR_WO_DEADLINE, CTaskCountersQueue::OP_DECREMENT, array((int) $arPrevFields['CREATED_BY']));
             CTaskCountersQueue::push(CTaskCountersProcessor::COUNTER_TASKS_MY_WO_DEADLINE, CTaskCountersQueue::OP_DECREMENT, array((int) $arPrevFields['RESPONSIBLE_ID']));
         } elseif (!$deadlineWasAbsent && $deadlineIsAbsents) {
             CTaskCountersQueue::push(CTaskCountersProcessor::COUNTER_TASKS_ORIGINATOR_WO_DEADLINE, CTaskCountersQueue::OP_INCREMENT, array((int) $arPrevFields['CREATED_BY']));
             CTaskCountersQueue::push(CTaskCountersProcessor::COUNTER_TASKS_MY_WO_DEADLINE, CTaskCountersQueue::OP_INCREMENT, array((int) $arPrevFields['RESPONSIBLE_ID']));
         }
     }
     self::onAfterMembersChanged($taskId, $statusAfter, $deadlineAfter, $arPrevFields['CREATED_BY'], $originatorAfter, $arPrevFields['RESPONSIBLE_ID'], $responsibleAfter, $arPrevFields['ACCOMPLICES'], $arAccomplicesAfter);
     // Execute counters queue in any case because it's expected by onBeforeTaskUpdate
     CTaskCountersQueue::execute();
 }