Exemple #1
0
 /**
  * Gets budgeted cost for all quarters of specified year
  *
  * @param    int      $year       The year
  * @param    string   $ccId       optional The identifier of the cost centre or parent cost centre
  * @param    string   $projectId  optional The identifier of the project
  * @throws   InvalidArgumentException
  */
 protected function getBudgetInfo($year, $ccId = null, $projectId = null)
 {
     if (!empty($projectId)) {
         $subjectId = $projectId;
         $subjectType = QuarterlyBudgetEntity::SUBJECT_TYPE_PROJECT;
         $subjectEntity = ProjectEntity::findPk($projectId);
     } else {
         if (!empty($ccId)) {
             $subjectId = $ccId;
             $subjectType = QuarterlyBudgetEntity::SUBJECT_TYPE_CC;
             $subjectEntity = CostCentreEntity::findPk($ccId);
         }
     }
     if ($subjectEntity->accountId !== $this->user->getAccountId()) {
         //throw new Scalr_Exception_InsufficientPermissions();
     }
     if (empty($subjectId) || !preg_match('/^[[:xdigit:]-]{36}$/', $subjectId)) {
         throw new InvalidArgumentException(sprintf("Invalid identifier of the project or cost center."));
     }
     if (!preg_match('/^\\d{4}$/', $year)) {
         throw new InvalidArgumentException(sprintf("Invalid year."));
     }
     if ($subjectType == QuarterlyBudgetEntity::SUBJECT_TYPE_CC) {
         $collection = QuarterlyBudgetEntity::getCcBudget($year, $subjectId);
         $prevCollection = QuarterlyBudgetEntity::getCcBudget($year - 1, $subjectId);
     } else {
         $collection = QuarterlyBudgetEntity::getProjectBudget($year, $subjectId);
         $prevCollection = QuarterlyBudgetEntity::getProjectBudget($year - 1, $subjectId);
     }
     $quarters = new Quarters(SettingEntity::getQuarters(true));
     $today = new DateTime('now', new DateTimeZone('UTC'));
     //Start dates for an each quarter
     $startDates = $quarters->getDays();
     $budgets = [];
     for ($quarter = 1; $quarter <= 4; ++$quarter) {
         $period = $quarters->getPeriodForQuarter($quarter, $year);
         $prevPeriod = $quarters->getPeriodForQuarter($quarter, $year - 1);
         //Finds budget for specified quarter in the collection
         $entity = current($collection->filterByQuarter($quarter));
         //Previous Year entity
         $prevEntity = current($prevCollection->filterByQuarter($quarter));
         if ($entity instanceof QuarterlyBudgetEntity) {
             $budget = ['budget' => round($entity->budget), 'budgetFinalSpent' => round($entity->cumulativespend), 'spentondate' => $entity->spentondate instanceof DateTime ? $entity->spentondate->format('Y-m-d') : null, 'budgetSpent' => round($entity->cumulativespend)];
             if ($budget['budget']) {
                 $budget['budgetOverspend'] = max(round($entity->cumulativespend - $entity->budget), 0);
                 $budget['budgetOverspendPct'] = round($budget['budgetOverspend'] / $budget['budget'] * 100);
             }
         } else {
             //Budget has not been set yet.
             $budget = ['budget' => 0, 'budgetFinalSpent' => 0, 'spentondate' => null, 'budgetSpent' => 0, 'budgetOverspend' => 0, 'budgetOverspendPct' => 0];
         }
         $budget['year'] = $year;
         $budget['quarter'] = $quarter;
         $budget['startDate'] = $startDates[$quarter - 1];
         //Whether this quarter has been closed or not
         $budget['closed'] = $period->end->format('Y-m-d') < gmdate('Y-m-d');
         //The number of the days in the current quarter
         $daysInQuarter = $period->start->diff($period->end, true)->days + 1;
         //In case quarter is closed projection should be calculated
         if (!$budget['closed']) {
             $daysPassed = $period->start->diff($today, true)->days + 1;
             $budget['dailyAverage'] = $daysPassed == 0 ? 0 : round($budget['budgetSpent'] / $daysPassed, 2);
             $budget['projection'] = round($daysInQuarter * $budget['dailyAverage']);
         } else {
             $budget['dailyAverage'] = $daysInQuarter == 0 ? 0 : round($budget['budgetFinalSpent'] / $daysInQuarter, 2);
         }
         $budget['costVariance'] = round((isset($budget['projection']) ? $budget['projection'] : $budget['budgetSpent']) - $budget['budget'], 2);
         $budget['costVariancePct'] = $budget['budget'] == 0 ? null : round(abs($budget['costVariance']) / $budget['budget'] * 100);
         $budget['monthlyAverage'] = round($budget['dailyAverage'] * 30);
         if ($prevEntity instanceof QuarterlyBudgetEntity) {
             $budget['prev'] = ['budget' => $prevEntity->budget, 'budgetFinalSpent' => $prevEntity->cumulativespend, 'spentondate' => $prevEntity->spentondate instanceof DateTime ? $prevEntity->spentondate->format('Y-m-d') : null, 'closed' => $prevPeriod->end->format('Y-m-d') < gmdate('Y-m-d'), 'costVariance' => $prevEntity->cumulativespend - $prevEntity->budget, 'costVariancePct' => $prevEntity->budget == 0 ? null : round(abs($prevEntity->cumulativespend - $prevEntity->budget) / $prevEntity->budget * 100)];
         } else {
             $budget['prev'] = ['budget' => null, 'budgetFinalSpent' => 0, 'spentondate' => null, 'closed' => $prevPeriod->end->format('Y-m-d') < gmdate('Y-m-d'), 'budgetSpent' => 0, 'costVariance' => 0, 'costVariancePct' => 0];
         }
         $budgets[] = $budget;
     }
     $result = ['budgets' => $budgets, 'ccId' => $ccId, 'projectId' => $projectId, 'quarter' => $quarters->getQuarterForDate(), 'year' => $year];
     if (!empty($projectId)) {
         $result['shared'] = $subjectEntity->shared;
     }
     return $result;
 }
Exemple #2
0
 public function xSaveAction()
 {
     $this->request->defineParams(array('ccId' => ['type' => 'string'], 'projectId' => ['type' => 'string'], 'year' => ['type' => 'int'], 'quarters' => ['type' => 'json'], 'selectedQuarter' => ['type' => 'string']));
     $year = $this->getParam('year');
     $selectedQuarter = $this->getParam('selectedQuarter');
     if ($selectedQuarter !== 'year' && ($selectedQuarter < 1 || $selectedQuarter > 4)) {
         throw new OutOfBoundsException(sprintf("Invalid selectedQuarter number."));
     }
     $quarterReq = [];
     foreach ($this->getParam('quarters') as $q) {
         if (!isset($q['quarter'])) {
             throw new InvalidArgumentException(sprintf("Missing quarter property for quarters data set in the request."));
         }
         if ($q['quarter'] < 1 || $q['quarter'] > 4) {
             throw new OutOfRangeException(sprintf("Quarter value should be between 1 and 4."));
         }
         if (!isset($q['budget'])) {
             throw new InvalidArgumentException(sprintf("Missing budget property for quarters data set in the request."));
         }
         $quarterReq[$q['quarter']] = $q;
     }
     if ($this->getParam('projectId')) {
         $subjectType = QuarterlyBudgetEntity::SUBJECT_TYPE_PROJECT;
         $subjectId = $this->getParam('projectId');
     } else {
         if ($this->getParam('ccId')) {
             $subjectType = QuarterlyBudgetEntity::SUBJECT_TYPE_CC;
             $subjectId = $this->getParam('ccId');
         } else {
             throw new InvalidArgumentException(sprintf('Either ccId or projectId must be provided with the request.'));
         }
     }
     if (!preg_match("/^[[:xdigit:]-]{36}\$/", $subjectId)) {
         throw new InvalidArgumentException(sprintf("Invalid UUID has been passed."));
     }
     if (!preg_match('/^\\d{4}$/', $year)) {
         throw new InvalidArgumentException(sprintf("Invalid year has been passed."));
     }
     //Fetches the previous state of the entities from database
     if ($subjectType == QuarterlyBudgetEntity::SUBJECT_TYPE_CC) {
         $collection = QuarterlyBudgetEntity::getCcBudget($year, $subjectId);
     } else {
         $collection = QuarterlyBudgetEntity::getProjectBudget($year, $subjectId);
     }
     $quarters = new Quarters(SettingEntity::getQuarters(true));
     //Updates|creates entities
     for ($quarter = 1; $quarter <= 4; ++$quarter) {
         if (!isset($quarterReq[$quarter])) {
             continue;
         }
         $period = $quarters->getPeriodForQuarter($quarter, $year);
         //Checks if period has already been closed and forbids update
         if ($period->end->format('Y-m-d') < gmdate('Y-m-d')) {
             continue;
         }
         $entity = current($collection->filterByQuarter($quarter));
         if ($entity instanceof QuarterlyBudgetEntity) {
             //We should update an entity
             $entity->budget = abs((double) $quarterReq[$quarter]['budget']);
         } else {
             //We should create a new one.
             $entity = new QuarterlyBudgetEntity($year, $quarter);
             $entity->subjectType = $subjectType;
             $entity->subjectId = $subjectId;
             $entity->budget = abs((double) $quarterReq[$quarter]['budget']);
         }
         $entity->save();
     }
     if ($selectedQuarter == 'year') {
         $selectedPeriod = $quarters->getPeriodForYear($year);
     } else {
         $selectedPeriod = $quarters->getPeriodForQuarter($selectedQuarter, $year);
     }
     if ($subjectType == QuarterlyBudgetEntity::SUBJECT_TYPE_PROJECT) {
         $data = $this->getProjectData(ProjectEntity::findPk($subjectId), $selectedPeriod, true);
         $budgetInfo = $this->getBudgetInfo($year, $data['ccId'], $data['projectId']);
     } else {
         $data = $this->getCostCenterData(CostCentreEntity::findPk($subjectId), $selectedPeriod);
         $budgetInfo = $this->getBudgetInfo($year, $subjectId);
     }
     $this->response->data(['data' => $data, 'budgetInfo' => $budgetInfo]);
     $this->response->success('Budget changes have been saved');
 }
Exemple #3
0
 /**
  * Gets quarterly budget of specified year for the CC
  *
  * @param   int    $year   The year
  * @throws  \RuntimeException
  * @return  \Scalr\Model\Collections\ArrayCollection Returns collection of the QuarterlyBudgetEntity objects
  */
 public function getQuarterlyBudget($year)
 {
     if ($this->projectId === null) {
         throw new \RuntimeException(sprintf("Identifier of the cost center has not been initialized yet for %s", get_class($this)));
     }
     return QuarterlyBudgetEntity::getCcBudget($year, $this->ccId);
 }
Exemple #4
0
 /**
  * Gets cost center moving average to date
  *
  * @param    string    $ccId       The identifier of the Cost center
  * @param    string    $mode       The mode
  * @param    string    $date       The date within specified period 'Y-m-d H:00'
  * @param    string    $startDate  The start date of the period 'Y-m-d'
  * @param    string    $endDate    The end date of the period 'Y-m-d'
  * @return   array     Returns cost center moving average to date
  */
 public function getCostCenterMovingAverageToDate($ccId, $mode, $date, $startDate, $endDate)
 {
     $iterator = ChartPeriodIterator::create($mode, $startDate, $endDate ?: null, 'UTC');
     if (!preg_match('/^[\\d]{4}-[\\d]{2}-[\\d]{2} [\\d]{2}:00$/', $date)) {
         throw new InvalidArgumentException(sprintf("Invalid date:%s. 'YYYY-MM-DD HH:00' is expected.", strip_tags($date)));
     }
     if (!preg_match('/^[[:xdigit:]-]{36}$/', $ccId)) {
         throw new InvalidArgumentException(sprintf("Invalid identifier of the Cost center:%s. UUID is expected.", strip_tags($ccId)));
     }
     //Interval which is used in the database query for grouping
     $queryInterval = preg_replace('/^1 /', '', $iterator->getInterval());
     $data = $this->getRollingAvg(['ccId' => $ccId], $queryInterval, $date);
     $data['budgetUseToDate'] = null;
     $data['budgetUseToDatePct'] = null;
     //Does not calculate budget use to date for those cases
     if ($mode != 'custom' && $mode != 'year') {
         $pointPosition = $iterator->searchPoint($date);
         if ($pointPosition !== false) {
             $chartPoint = $iterator->current();
             //Gets the end date of the selected interval
             $end = clone $chartPoint->dt;
             if ($chartPoint->interval != '1 day') {
                 $end->modify("+" . $chartPoint->interval . " -1 day");
             }
             //Gets quarters config
             $quarters = new Quarters(SettingEntity::getQuarters());
             //Gets start and end of the quarter for the end date of the current interval
             $period = $quarters->getPeriodForDate($end);
             $data['year'] = $period->year;
             $data['quarter'] = $period->quarter;
             $data['quarterStartDate'] = $period->start->format('Y-m-d');
             $data['quarterEndDate'] = $period->end->format('Y-m-d');
             //Gets budgeted cost
             $budget = current(QuarterlyBudgetEntity::getCcBudget($period->year, $ccId)->filterByQuarter($period->quarter));
             //If budget has not been set we should not calculate anything
             if ($budget instanceof QuarterlyBudgetEntity && round($budget->budget) > 0) {
                 $data['budget'] = round($budget->budget);
                 //Calculates usage from the start date of the quarter to date
                 $usage = $this->get(['ccId' => $ccId], $period->start, $end);
                 $data['budgetUseToDate'] = $usage['cost'];
                 $data['budgetUseToDatePct'] = $data['budget'] == 0 ? null : min(100, round($usage['cost'] / $data['budget'] * 100));
             }
         }
     }
     return ['data' => $data];
 }