/**
  * Formats the bean so it is ready to be handed back to the API's client. Certain fields will get extra processing
  * to make them easier to work with from the client end.
  *
  * @param $bean SugarBean|ForecastManagerWorksheet The bean you want formatted
  * @param $fieldList array Which fields do you want formatted and returned (leave blank for all fields)
  * @param $options array Currently no options are supported
  * @return array The bean in array format, ready for passing out the API to clients.
  */
 public function formatForApi(SugarBean $bean, array $fieldList = array(), array $options = array())
 {
     $data = parent::formatForApi($bean, $fieldList, $options);
     $sq = new SugarQuery();
     $sq->select('date_modified');
     $sq->from($bean)->where()->equals('assigned_user_id', $bean->assigned_user_id)->equals('user_id', $bean->user_id)->equals('draft', 0)->equals('timeperiod_id', $bean->timeperiod_id);
     $beans = $sq->execute();
     $data['show_history_log'] = 0;
     if (empty($beans) && !empty($bean->fetched_row['date_modified'])) {
         /* @var $tp TimePeriod */
         $tp = BeanFactory::getBean('TimePeriods', $bean->timeperiod_id);
         // When reportee has committed but manager has not
         // make sure that the reportee actually has a commit for the timeperiod,
         // this is to handle the case where the manager saves draft before the reportee can commit
         $sq = new SugarQuery();
         $sq->select('id');
         $sq->from(BeanFactory::getBean('ForecastWorksheets'))->where()->equals('assigned_user_id', $bean->user_id)->equals('draft', 0)->queryAnd()->gte('date_closed_timestamp', $tp->start_date_timestamp)->lte('date_closed_timestamp', $tp->end_date_timestamp);
         $worksheets = $sq->execute();
         if (!empty($worksheets)) {
             $data['show_history_log'] = 1;
         }
     } else {
         if (!empty($beans)) {
             $fBean = $beans[0];
             $committed_date = $bean->db->fromConvert($fBean["date_modified"], "datetime");
             if (strtotime($committed_date) < strtotime($bean->fetched_row['date_modified'])) {
                 $db = DBManagerFactory::getInstance();
                 // find the differences via the audit table
                 // we use a direct query since SugarQuery can't do the audit tables...
                 $sql = sprintf("SELECT field_name, before_value_string, after_value_string FROM %s\n                        WHERE parent_id = %s AND date_created >= " . $db->convert('%s', 'datetime'), $bean->get_audit_table_name(), $db->quoted($bean->id), $db->quoted($committed_date));
                 $results = $db->query($sql);
                 // get the setting for which fields to compare on
                 /* @var $admin Administration */
                 $admin = BeanFactory::getBean('Administration');
                 $settings = $admin->getConfigForModule('Forecasts', 'base');
                 while ($row = $db->fetchByAssoc($results)) {
                     $field = substr($row['field_name'], 0, strpos($row['field_name'], '_'));
                     if ($settings['show_worksheet_' . $field] == "1") {
                         // calculate the difference to make sure it actually changed at 2 digits vs changed at 6
                         $diff = SugarMath::init($row['after_value_string'], 6)->sub($row['before_value_string'])->result();
                         // due to decimal rounding on the front end, we only want to know about differences greater
                         // of two decimal places.
                         // todo-sfa: This hardcoded 0.01 value needs to be changed to a value determined by userprefs
                         if (abs($diff) >= 0.01) {
                             $data['show_history_log'] = 1;
                             break;
                         }
                     }
                 }
             }
         }
     }
     if (!empty($bean->user_id)) {
         $data['is_manager'] = User::isManager($bean->user_id);
     }
     return $data;
 }
Esempio n. 2
0
 /**
  * Rounds the first param down into a specific precision
  */
 public function evaluate()
 {
     $params = $this->getParameters();
     $base = $params[0]->evaluate();
     $precision = $params[1]->evaluate();
     return SugarMath::init($base, $precision)->result();
 }
 /**
  * This checks to see if the only thing that has changed is the quota, if it is, then don't update the date
  * modified
  *
  * @param ForecastManagerWorksheet $worksheet       The Bean
  * @param string $event                             Which event is being fired
  * @param array $params                             Extra Params
  */
 public static function draftRecordQuotaOnlyCheck(ForecastManagerWorksheet $worksheet, $event, $params = array())
 {
     // this should only run on before_save and when the worksheet is a draft record
     // and the draft_save_type is assign_quota
     if ($event == 'before_save' && $worksheet->draft == 1 && $worksheet->draft_save_type == 'assign_quota') {
         $mm = MetadataManager::getManager();
         $views = $mm->getModuleViews($worksheet->module_name);
         $fields = $views['list']['meta']['panels'][0]['fields'];
         $onlyQuotaChanged = true;
         foreach ($fields as $field) {
             if ($field['type'] == 'currency' && preg_match('#\\.[\\d]{6}$#', $worksheet->{$field}['name']) === 0) {
                 $worksheet->{$field}['name'] = SugarMath::init($worksheet->{$field}['name'], 6)->result();
             }
             if ($worksheet->fetched_row[$field['name']] !== $worksheet->{$field}['name']) {
                 if ($field['name'] !== 'quota') {
                     $onlyQuotaChanged = false;
                     break;
                 }
             }
         }
         if ($onlyQuotaChanged === true) {
             $worksheet->update_date_modified = false;
         }
     }
 }
Esempio n. 4
0
 /**
  * Returns the negative of the expression that it contains.
  */
 public function evaluate()
 {
     $params = $this->getParameters();
     $base = $params[0]->evaluate();
     $power = $params[1]->evaluate();
     return SugarMath::init($base)->pow($power)->result();
 }
 /**
  * The Logic for running in PHP, this uses SugarMath as to avoid potential floating-point errors
  *
  * @return String
  */
 public function evaluate()
 {
     $product = '1';
     foreach ($this->getParameters() as $expr) {
         $product = SugarMath::init($product, 6)->mul($expr->evaluate())->result();
     }
     return (string) $product;
 }
Esempio n. 6
0
 /**
  * The Logic for running in PHP, this uses SugarMath as to avoid potential floating-point errors
  *
  * @returns String
  */
 public function evaluate()
 {
     $sum = '0';
     foreach ($this->getParameters() as $expr) {
         $sum = SugarMath::init($sum, 6)->add($expr->evaluate())->result();
     }
     return (string) $sum;
 }
 /**
  * The Logic for running in PHP, this uses SugarMath as to avoid potential floating-point errors
  *
  * @returns string
  */
 public function evaluate()
 {
     $params = $this->getParameters();
     $diff = $params[0]->evaluate();
     for ($i = 1; $i < sizeof($params); $i++) {
         $diff = SugarMath::init($diff, 6)->sub($params[$i]->evaluate())->result();
     }
     return (string) $diff;
 }
Esempio n. 8
0
 /**
  * The Logic for running in PHP, this uses SugarMath as to avoid potential floating-point errors
  *
  * @throws Exception
  * @return String
  */
 public function evaluate()
 {
     $params = $this->getParameters();
     $numerator = $params[0]->evaluate();
     $denominator = $params[1]->evaluate();
     if ($denominator == 0) {
         throw new Exception("Division by zero");
     }
     return (string) SugarMath::init($numerator, 6)->div($denominator)->result();
 }
Esempio n. 9
0
 /**
  * Returns itself when evaluating.
  */
 public function evaluate()
 {
     $params = $this->getParameters();
     $base = $params[1]->evaluate();
     $value = $params[0]->evaluate();
     if ($base == 1) {
         throw new Exception("Log base can not be 1");
     }
     return SugarMath::init(log($value))->div(log($base))->result();
 }
Esempio n. 10
0
 /**
  * Returns itself when evaluating.
  */
 public function evaluate()
 {
     $sum = 0;
     $count = 0;
     foreach ($this->getParameters() as $expr) {
         $sum = SugarMath::init($sum)->add($expr->evaluate())->result();
         $count++;
     }
     // since Expression guarantees at least 1 parameter
     // we can safely assume / by 0 will not happen
     return SugarMath::init($sum)->div($count)->result();
 }
 /**
  * Returns the entire enumeration bare.
  */
 public function evaluate()
 {
     $params = $this->getParameters();
     //This should be of relate type, which means an array of SugarBean objects
     $linkField = $params[0]->evaluate();
     $relfield = $params[1]->evaluate();
     $ret = '0';
     if (!is_array($linkField) || empty($linkField)) {
         return $ret;
     }
     foreach ($linkField as $bean) {
         if (!empty($bean->{$relfield})) {
             $ret = SugarMath::init($ret)->add($bean->{$relfield})->result();
         }
     }
     return SugarMath::init($ret)->div(count($linkField))->result();
 }
 /**
  * Ability only rollup specific values from related records when a field on the related record is equal to
  * something.
  *
  * @return string
  */
 public function evaluate()
 {
     $params = $this->getParameters();
     // This should be of relate type, which means an array of SugarBean objects
     $linkField = $params[0]->evaluate();
     $relfield = $params[1]->evaluate();
     $conditionalField = $params[2]->evaluate();
     $conditionalValues = $params[3]->evaluate();
     if (!is_array($conditionalValues)) {
         $conditionalValues = array($conditionalValues);
     }
     $ret = '0';
     if (!is_array($linkField) || empty($linkField)) {
         return $ret;
     }
     if (!isset($this->context)) {
         //If we don't have a context provided, we have to guess. This can be a large performance hit.
         $this->setContext();
     }
     $toRate = isset($this->context->base_rate) ? $this->context->base_rate : null;
     $checkedTypeForCurrency = false;
     $relFieldIsCurrency = false;
     foreach ($linkField as $bean) {
         if (!in_array($bean->{$conditionalField}, $conditionalValues)) {
             continue;
         }
         // only check the target field once to see if it's a currency field.
         if ($checkedTypeForCurrency === false) {
             $checkedTypeForCurrency = true;
             $relFieldIsCurrency = $this->isCurrencyField($bean, $relfield);
         }
         if (!empty($bean->{$relfield})) {
             $value = $bean->{$relfield};
             // if we have a currency field, it needs to convert the value into the rate of the row it's
             // being returned to.
             if ($relFieldIsCurrency) {
                 $value = SugarCurrency::convertWithRate($value, $bean->base_rate, $toRate);
             }
             $ret = SugarMath::init($ret)->add($value)->result();
         }
     }
     return $ret;
 }
 /**
  * Returns itself when evaluating.
  */
 public function evaluate()
 {
     $params = $this->getParameters();
     $values = array();
     // find the mean
     $sum = 0;
     $count = sizeof($params);
     foreach ($params as $param) {
         $value = $param->evaluate();
         $values[] = $value;
         $sum = SugarMath::init($sum)->add($value)->result();
     }
     $mean = SugarMath::init($sum)->div($count)->result();
     // find the summation of deviations
     $deviation_sum = 0;
     foreach ($values as $value) {
         $deviation_sum = SugarMath::init($value)->sub($mean)->pow(2)->add($deviation_sum)->result();
     }
     // find the std dev
     return SugarMath::init()->exp('((1/?)*?)', array($count, $deviation_sum))->sqrt()->result();
 }
Esempio n. 14
0
 /**
  * Get the Numbers for the Manager View
  *
  * @return array
  */
 public function getManagerProgress()
 {
     $user_id = $this->getArg('user_id');
     $timeperiod_id = $this->getArg('timeperiod_id');
     $getTargetQuota = (bool) $this->getArg('target_quota');
     /* @var $mgr_worksheet ForecastManagerWorksheet */
     $mgr_worksheet = BeanFactory::getBean('ForecastManagerWorksheets');
     $totals = $mgr_worksheet->worksheetTotals($user_id, $timeperiod_id);
     // pull the quota from the worksheet data since we need the draft records if they exist
     // to show what could be in draft for the user, if they are the current user.
     $totals['quota_amount'] = $totals['quota'];
     // add the target quota to the return data if passed target_quota=true as a param
     if ($getTargetQuota) {
         /* @var $quotaBean Quota */
         $quotaBean = BeanFactory::getBean('Quotas');
         $quota = $quotaBean->getRollupQuota($timeperiod_id, $user_id, true);
         $totals['target_quota_amount'] = $quota['amount'];
     }
     // we should send back the adjusted totals with out the closed_amount included.
     foreach (array('worst_adjusted', 'likely_adjusted', 'best_adjusted') as $field) {
         $totals[$field] = SugarMath::init($totals[$field])->sub($totals['closed_amount'])->result();
     }
     $totals['user_id'] = $user_id;
     $totals['timeperiod_id'] = $timeperiod_id;
     // unset some vars that come from the worksheet to avoid confusion with correct data
     // coming from this endpoint for progress
     unset($totals['pipeline_opp_count'], $totals['quota'], $totals['included_opp_count'], $totals['pipeline_amount']);
     return $totals;
 }
Esempio n. 15
0
 /**
  * convert a currency with a given rate
  *
  * @access public
  * @param  float  $amount
  * @param  float  $fromRate rate to convert from (default base rate)
  * @param  float  $toRate rate to convert to (default base rate)
  * @param  int    $precision Optional decimal precision
  * @return float   converted amount
  */
 public static function convertWithRate($amount, $fromRate = 1.0, $toRate = 1.0, $precision = 6)
 {
     // if rate is 0 or null, just return the amount
     if (empty($fromRate) || empty($toRate)) {
         return $amount;
     }
     // handle the use case for when a currency field get added to a module, where no default is set
     // and if the $amount is passed in as a string but $amount is an empty string or $amount is null,
     // we should default then we should return as the conversion is not needed
     if (is_string($amount) && $amount === '' || is_null($amount)) {
         return '0';
     }
     return SugarMath::init(0, $precision)->exp('?/?*?', array($amount, $fromRate, $toRate))->result();
 }
 /**
  * This method emulates the Forecast Manager Worksheet calculateTotals method.
  *
  * @param string $userId
  * @param string $timeperiodId
  * @param boolean $useDraftRecords
  * @return array|bool            Return calculated totals or boolean false if timeperiod is not valid
  */
 public function worksheetTotals($userId, $timeperiodId, $useDraftRecords = false)
 {
     /* @var $tp TimePeriod */
     $tp = BeanFactory::getBean('TimePeriods', $timeperiodId);
     if (empty($tp->id)) {
         // timeperiod not found
         return false;
     }
     $return = array("quota" => '0', "best_case" => '0', "best_adjusted" => '0', "likely_case" => '0', "likely_adjusted" => '0', "worst_case" => '0', "worst_adjusted" => '0', "included_opp_count" => 0, "pipeline_opp_count" => 0, "pipeline_amount" => '0', "closed_amount" => '0');
     global $current_user;
     require_once 'include/SugarQuery/SugarQuery.php';
     $sq = new SugarQuery();
     $bean_obj = BeanFactory::getBean($this->module_name);
     $sq->select(array($bean_obj->getTableName() . '.*'));
     $sq->from($bean_obj)->where()->equals('timeperiod_id', $tp->id)->equals('assigned_user_id', $userId)->equals('draft', $current_user->id == $userId || $useDraftRecords === true ? 1 : 0)->equals('deleted', 0);
     $results = $sq->execute();
     foreach ($results as $row) {
         $return['quota'] = SugarMath::init($return['quota'], 6)->add(SugarCurrency::convertWithRate($row['quota'], $row['base_rate']))->result();
         $return['best_case'] = SugarMath::init($return['best_case'], 6)->add(SugarCurrency::convertWithRate($row['best_case'], $row['base_rate']))->result();
         $return['best_adjusted'] = SugarMath::init($return['best_adjusted'], 6)->add(SugarCurrency::convertWithRate($row['best_case_adjusted'], $row['base_rate']))->result();
         $return['likely_case'] = SugarMath::init($return['likely_case'], 6)->add(SugarCurrency::convertWithRate($row['likely_case'], $row['base_rate']))->result();
         $return['likely_adjusted'] = SugarMath::init($return['likely_adjusted'], 6)->add(SugarCurrency::convertWithRate($row['likely_case_adjusted'], $row['base_rate']))->result();
         $return['worst_case'] = SugarMath::init($return['worst_case'], 6)->add(SugarCurrency::convertWithRate($row['worst_case'], $row['base_rate']))->result();
         $return['worst_adjusted'] = SugarMath::init($return['worst_adjusted'], 6)->add(SugarCurrency::convertWithRate($row['worst_case_adjusted'], $row['base_rate']))->result();
         $return['closed_amount'] = SugarMath::init($return['closed_amount'], 6)->add(SugarCurrency::convertWithRate($row['closed_amount'], $row['base_rate']))->result();
         $return['included_opp_count'] += $row['opp_count'];
         $return['pipeline_opp_count'] += $row['pipeline_opp_count'];
         $return['pipeline_amount'] = SugarMath::init($return['pipeline_amount'], 6)->add($row['pipeline_amount'])->result();
     }
     return $return;
 }
Esempio n. 17
0
 /**
  * Used by the dependency manager to pre-load all the related fields required
  * to load an entire view.
  */
 public function getRelatedValues($api, $args)
 {
     if (empty($args['module']) || empty($args['fields'])) {
         return;
     }
     $fields = json_decode(html_entity_decode($args['fields']), true);
     $focus = $this->loadBean($api, $args);
     $ret = array();
     foreach ($fields as $rfDef) {
         if (!isset($rfDef['link']) || !isset($rfDef['type'])) {
             continue;
         }
         $link = $rfDef['link'];
         $type = $rfDef['type'];
         $rField = '';
         if (!isset($ret[$link])) {
             $ret[$link] = array();
         }
         if (empty($ret[$link][$type])) {
             $ret[$link][$type] = array();
         }
         // count formulas don't have a relate attribute
         if (isset($rfDef['relate'])) {
             $rField = $rfDef['relate'];
         }
         switch ($type) {
             //The Related function is used for pulling a sing field from a related record
             case "related":
                 //Default it to a blank value
                 $ret[$link]['related'][$rfDef['relate']] = "";
                 //If we have neither a focus id nor a related record id, we can't retrieve anything
                 $relBean = null;
                 if (empty($rfDef['relId']) || empty($rfDef['relModule'])) {
                     //If the relationship is invalid, just move onto another field
                     if (!$focus->load_relationship($link)) {
                         break;
                     }
                     $beans = $focus->{$link}->getBeans(array("enforce_teams" => true));
                     //No related beans means no value
                     if (empty($beans)) {
                         break;
                     }
                     //Grab the first bean on the list
                     reset($beans);
                     $relBean = current($beans);
                 } else {
                     $relBean = BeanFactory::getBean($rfDef['relModule'], $rfDef['relId']);
                 }
                 //If we found a bean and the current user has access to the related field, grab a value from it
                 if (!empty($relBean) && ACLField::hasAccess($rfDef['relate'], $relBean->module_dir, $GLOBALS['current_user']->id, true)) {
                     $validFields = FormulaHelper::cleanFields($relBean->field_defs, false, true, true);
                     if (isset($validFields[$rfDef['relate']])) {
                         $ret[$link]['relId'] = $relBean->id;
                         $ret[$link]['related'][$rfDef['relate']] = FormulaHelper::getFieldValue($relBean, $rfDef['relate']);
                     }
                 }
                 break;
             case "count":
                 if ($focus->load_relationship($link)) {
                     $ret[$link][$type] = count($focus->{$link}->get());
                 } else {
                     $ret[$link][$type] = 0;
                 }
                 break;
             case "rollupSum":
             case "rollupAve":
             case "rollupMin":
             case "rollupMax":
                 //If we are going to calculate one rollup, calculate all the rollups since there is so little cost
                 if ($focus->load_relationship($link)) {
                     $relBeans = $focus->{$link}->getBeans(array("enforce_teams" => true));
                     $sum = 0;
                     $count = 0;
                     $min = false;
                     $max = false;
                     if (!empty($relBeans)) {
                         //Check if the related record vardef has banned this field from formulas
                         $relBean = reset($relBeans);
                         $validFields = FormulaHelper::cleanFields($relBean->field_defs, false, true, true);
                         if (!isset($validFields[$rField])) {
                             $ret[$link][$type][$rField] = 0;
                             break;
                         }
                     }
                     foreach ($relBeans as $bean) {
                         if (isset($bean->{$rField}) && is_numeric($bean->{$rField}) && ACLField::hasAccess($rField, $bean->module_dir, $GLOBALS['current_user']->id, true)) {
                             $count++;
                             $sum += floatval($bean->{$rField});
                             if ($min === false || $bean->{$rField} < $min) {
                                 $min = floatval($bean->{$rField});
                             }
                             if ($max === false || $bean->{$rField} > $max) {
                                 $max = floatval($bean->{$rField});
                             }
                         }
                     }
                     if ($type == "rollupSum") {
                         $ret[$link][$type][$rField] = $sum;
                     }
                     if ($type == "rollupAve") {
                         $ret[$link][$type][$rField] = $count == 0 ? 0 : $sum / $count;
                     }
                     if ($type == "rollupMin") {
                         $ret[$link][$type][$rField] = $min;
                     }
                     if ($type == "rollupMax") {
                         $ret[$link][$type][$rField] = $max;
                     }
                 } else {
                     $ret[$link][$type][$rField] = 0;
                 }
                 break;
             case "rollupCurrencySum":
                 $ret[$link][$type][$rField] = 0;
                 if ($focus->load_relationship($link)) {
                     $toRate = isset($focus->base_rate) ? $focus->base_rate : null;
                     $relBeans = $focus->{$link}->getBeans(array("enforce_teams" => true));
                     $sum = 0;
                     foreach ($relBeans as $bean) {
                         if (!empty($bean->{$rField}) && is_numeric($bean->{$rField}) && ACLField::hasAccess($rField, $bean->module_dir, $GLOBALS['current_user']->id, true)) {
                             $sum = SugarMath::init($sum)->add(SugarCurrency::convertWithRate($bean->{$rField}, $bean->base_rate, $toRate))->result();
                         }
                     }
                     $ret[$link][$type][$rField] = $sum;
                 }
                 break;
         }
     }
     return $ret;
 }
Esempio n. 18
0
 protected function setDiscountPrice()
 {
     if (!is_numeric($this->discount_price) && empty($this->product_template_id) && is_numeric($this->likely_case)) {
         $quantity = floatval($this->quantity);
         if (empty($quantity)) {
             $quantity = 1;
         }
         $this->discount_price = SugarMath::init($this->likely_case)->div($quantity)->result();
     }
 }
Esempio n. 19
0
 /**
  * Converts (copies) a Products (QuotedLineItem) to a Revenue Line Item
  * @return RevenueLineItem
  */
 public function convertToRevenueLineItem()
 {
     /* @var $rli RevenueLineItem */
     $rli = BeanFactory::getBean('RevenueLineItems');
     $rli->id = create_guid();
     $rli->new_with_id = true;
     $rli->fetched_row = array();
     foreach ($this->getFieldDefinitions() as $field) {
         if ($field['name'] != 'id' && isset($this->fetched_row[$field['name']])) {
             $rli->{$field}['name'] = $this->fetched_row[$field['name']];
             // set the fetched row, so we prevent the product_template from fetching again
             // when the re-save happens because of the relationships
             $rli->fetched_row[$field['name']] = $this->fetched_row[$field['name']];
         }
     }
     if ($this->discount_select == 1) {
         // we have a percentage discount, but we don't allow the use of percentages on
         // the RevenueLineItem module yet, so we need to set discount_select to 0
         // and calculate out the correct discount_amount.
         $rli->discount_select = 0;
         $rli->discount_amount = SugarMath::init()->exp('(?*?)*(?/100)', array($this->discount_price, $this->quantity, $this->discount_amount))->result();
     }
     // since we don't have a likely_case on products,
     if ($rli->likely_case == '0.00') {
         //undo bad math from quotes.
         $rli->likely_case = $this->total_amount;
     }
     $this->revenuelineitem_id = $rli->id;
     $this->ignoreQuoteSave = true;
     $this->save();
     return $rli;
 }
Esempio n. 20
0
 /**
  * @static
  *
  * @param Array $timeperiods Array of $timeperiod instances to build forecast data for
  */
 public static function populateSeedData($timeperiods)
 {
     require_once 'modules/Forecasts/Common.php';
     global $timedate, $current_user, $app_list_strings;
     $user = BeanFactory::getBean('Users');
     $comm = new Common();
     $commit_order = $comm->get_forecast_commit_order();
     // get what we are forecasting on
     /* @var $admin Administration */
     $admin = BeanFactory::getBean('Administration');
     $settings = $admin->getConfigForModule('Forecasts');
     $forecast_by = $settings['forecast_by'];
     foreach ($timeperiods as $timeperiod_id => $timeperiod) {
         foreach ($commit_order as $commit_type_array) {
             //direct entry per user, and some user will have a Rollup entry too.
             $ratio = array('.8', '1', '1.2', '1.4');
             $key = array_rand($ratio);
             if ($commit_type_array[1] == 'Direct') {
                 // get the worksheet total for a given user
                 /* @var $worksheet ForecastWorksheet */
                 $worksheet = BeanFactory::getBean('ForecastWorksheets');
                 $totals = $worksheet->worksheetTotals($timeperiod_id, $commit_type_array[0], $forecast_by, true);
                 if ($totals['total_opp_count'] == 0) {
                     continue;
                 }
                 /* @var $quota Quota */
                 $quota = BeanFactory::getBean('Quotas');
                 $quota->timeperiod_id = $timeperiod_id;
                 $quota->user_id = $commit_type_array[0];
                 $quota->quota_type = 'Direct';
                 $quota->currency_id = -99;
                 $quota->amount = SugarMath::init()->exp('?*?', array($totals['amount'], $ratio[$key]))->result();
                 $quota->amount_base_currency = $quota->amount;
                 $quota->committed = 1;
                 $quota->set_created_by = false;
                 if ($commit_type_array[0] == 'seed_sarah_id' || $commit_type_array[0] == 'seed_will_id' || $commit_type_array[0] == 'seed_jim_id') {
                     $quota->created_by = 'seed_jim_id';
                 } else {
                     if ($commit_type_array[0] == 'seed_sally_id' || $commit_type_array[0] == 'seed_max_id') {
                         $quota->created_by = 'seed_sarah_id';
                     } else {
                         if ($commit_type_array[0] == 'seed_chris_id') {
                             $quota->created_by = 'seed_will_id';
                         } else {
                             $quota->created_by = $current_user->id;
                         }
                     }
                 }
                 $quota->save();
                 if (!$user->isManager($commit_type_array[0])) {
                     /* @var $quotaRollup Quota */
                     $quotaRollup = BeanFactory::getBean('Quotas');
                     $quotaRollup->timeperiod_id = $timeperiod_id;
                     $quotaRollup->user_id = $commit_type_array[0];
                     $quotaRollup->quota_type = 'Rollup';
                     $quota->currency_id = -99;
                     $quotaRollup->amount = $quota->amount;
                     $quotaRollup->amount_base_currency = $quotaRollup->amount;
                     $quotaRollup->committed = 1;
                     $quotaRollup->set_created_by = false;
                     if ($commit_type_array[0] == 'seed_sarah_id' || $commit_type_array[0] == 'seed_will_id' || $commit_type_array[0] == 'seed_jim_id') {
                         $quotaRollup->created_by = 'seed_jim_id';
                     } else {
                         if ($commit_type_array[0] == 'seed_sally_id' || $commit_type_array[0] == 'seed_max_id') {
                             $quotaRollup->created_by = 'seed_sarah_id';
                         } else {
                             if ($commit_type_array[0] == 'seed_chris_id') {
                                 $quotaRollup->created_by = 'seed_will_id';
                             } else {
                                 $quotaRollup->created_by = $current_user->id;
                             }
                         }
                     }
                     $quotaRollup->save();
                 }
                 // create a previous forecast to simulate a change
                 /* @var $forecast Forecast */
                 $forecast = BeanFactory::getBean('Forecasts');
                 $forecast->timeperiod_id = $timeperiod_id;
                 $forecast->user_id = $commit_type_array[0];
                 $forecast->opp_count = $totals['included_opp_count'];
                 if ($totals['included_opp_count'] > 0) {
                     $forecast->opp_weigh_value = SugarMath::init()->setScale(0)->exp('(?/?)/?', array($totals['amount'], $ratio[$key], $totals['included_opp_count']))->result();
                 } else {
                     $forecast->opp_weigh_value = '0';
                 }
                 $forecast->best_case = SugarMath::init()->exp('(?+?)/?', array($totals['best_case'], $totals['won_best'], $ratio[$key]))->result();
                 $forecast->worst_case = SugarMath::init()->exp('(?+?)/?', array($totals['worst_case'], $totals['won_worst'], $ratio[$key]))->result();
                 $forecast->likely_case = SugarMath::init()->exp('(?+?)/?', array($totals['amount'], $totals['won_amount'], $ratio[$key]))->result();
                 $forecast->forecast_type = 'Direct';
                 $forecast->date_committed = $timedate->asDb($timedate->getNow()->modify("-1 day"));
                 $forecast->date_entered = $timedate->asDb($timedate->getNow()->modify("-1 day"));
                 $forecast->date_modified = $timedate->asDb($timedate->getNow()->modify("-1 day"));
                 $forecast->calculatePipelineData(SugarMath::init()->exp('?/?', array($totals['includedClosedAmount'], $ratio[$key]))->result(), $totals['includedClosedCount']);
                 $forecast->save();
                 self::createManagerWorksheet($commit_type_array[0], $forecast->toArray());
                 // create the current forecast
                 /* @var $forecast2 Forecast */
                 $forecast2 = BeanFactory::getBean('Forecasts');
                 $forecast2->timeperiod_id = $timeperiod_id;
                 $forecast2->user_id = $commit_type_array[0];
                 $forecast2->opp_count = $totals['included_opp_count'];
                 if ($totals['included_opp_count'] > 0) {
                     $forecast2->opp_weigh_value = SugarMath::init()->setScale(0)->exp('?/?', array($totals['amount'], $totals['included_opp_count']))->result();
                 } else {
                     $forecast2->opp_weigh_value = '0';
                 }
                 $forecast2->best_case = SugarMath::init($totals['best_case'])->add($totals['won_best'])->result();
                 $forecast2->worst_case = SugarMath::init($totals['worst_case'])->add($totals['won_worst'])->result();
                 $forecast2->likely_case = SugarMath::init($totals['amount'])->add($totals['won_amount'])->result();
                 $forecast2->forecast_type = 'Direct';
                 $forecast2->date_committed = $timedate->asDb($timedate->getNow());
                 $forecast2->calculatePipelineData($totals['includedClosedAmount'], $totals['includedClosedCount']);
                 $forecast2->save();
                 self::createManagerWorksheet($commit_type_array[0], $forecast2->toArray());
             } else {
                 /* @var $mgr_worksheet ForecastManagerWorksheet */
                 $mgr_worksheet = BeanFactory::getBean('ForecastManagerWorksheets');
                 $totals = $mgr_worksheet->worksheetTotals($commit_type_array[0], $timeperiod_id, true);
                 if ($totals['included_opp_count'] == 0) {
                     continue;
                 }
                 /* @var $quota Quota */
                 $quota = BeanFactory::getBean('Quotas');
                 $quota->timeperiod_id = $timeperiod_id;
                 $quota->user_id = $commit_type_array[0];
                 $quota->quota_type = 'Rollup';
                 $quota->currency_id = -99;
                 $quota->amount = SugarMath::init($totals['quota'], 6)->mul($ratio[$key])->result();
                 $quota->amount_base_currency = $quota->amount;
                 $quota->committed = 1;
                 $quota->save();
                 /* @var $forecast Forecast */
                 $forecast = BeanFactory::getBean('Forecasts');
                 $forecast->timeperiod_id = $timeperiod_id;
                 $forecast->user_id = $commit_type_array[0];
                 $forecast->opp_count = $totals['included_opp_count'];
                 $forecast->opp_weigh_value = SugarMath::init()->setScale(0)->exp('?/?', array($totals['likely_adjusted'], $totals['included_opp_count']))->result();
                 $forecast->likely_case = $totals['likely_adjusted'];
                 $forecast->best_case = $totals['best_adjusted'];
                 $forecast->worst_case = $totals['worst_adjusted'];
                 $forecast->forecast_type = 'Rollup';
                 $forecast->pipeline_opp_count = $totals['pipeline_opp_count'];
                 $forecast->pipeline_amount = $totals['pipeline_amount'];
                 $forecast->closed_amount = $totals['closed_amount'];
                 $forecast->date_entered = $timedate->asDb($timedate->getNow());
                 $forecast->save();
                 self::createManagerWorksheet($commit_type_array[0], $forecast->toArray());
             }
             self::commitRepItems($commit_type_array[0], $timeperiod_id, $forecast_by);
         }
         // loop though all the managers and commit their forecast
         $managers = array('seed_sarah_id', 'seed_will_id', 'seed_jim_id');
         foreach ($managers as $manager) {
             /* @var $user User */
             $user = BeanFactory::getBean('Users', $manager);
             /* @var $worksheet ForecastManagerWorksheet */
             $worksheet = BeanFactory::getBean('ForecastManagerWorksheets');
             $worksheet->commitManagerForecast($user, $timeperiod_id);
         }
     }
     $admin = BeanFactory::getBean('Administration');
     $admin->saveSetting('Forecasts', 'is_setup', 1, 'base');
     // TODO-sfa - remove this once the ability to map buckets when they get changed is implemented (SFA-215).
     // this locks the forecasts ranges configs if the apps is installed with demo data and already has commits
     $admin->saveSetting('Forecasts', 'has_commits', 1, 'base');
 }
Esempio n. 21
0
 /**
  * @param ServiceBase $api
  * @param array $args
  * @return array
  * @throws SugarApiExceptionInvalidParameter
  * @throws SugarApiExceptionNotAuthorized
  * @throws SugarApiExceptionNotFound
  */
 public function pipeline(ServiceBase $api, $args)
 {
     // if not in the allowed module list, then we throw a 404 not found
     if (!in_array($args['module'], $this->allowedModules)) {
         throw new SugarApiExceptionNotFound();
     }
     // make sure we can view the module first
     // since we don't have a proper record just make an empty one
     $args['record'] = '';
     /* @var $seed Opportunity|Product|RevenueLineItem */
     $seed = $this->loadBean($api, $args, 'view');
     if (!$seed->ACLAccess('view')) {
         throw new SugarApiExceptionNotAuthorized();
     }
     $tp = $this->getTimeperiod($args['timeperiod_id']);
     // check the type param
     if (!isset($args['type']) || $args['type'] != 'user' && $args['type'] != 'group') {
         $args['type'] = 'user';
     }
     $settings = $this->getForecastSettings();
     // get sales_stages to ignore
     $ignore_stages = array_merge($settings['sales_stage_won'], $settings['sales_stage_lost']);
     // get the amount field here
     $amount_field = $this->moduleAmountField[$seed->module_name];
     $sq = $this->buildQuery($api, $seed, $tp, $amount_field, $args['type']);
     // run the query
     $rows = $sq->execute();
     // data storage
     $data = array();
     // keep track of the total for later user
     $total = SugarMath::init('0', 0);
     foreach ($rows as $row) {
         // if the sales stage is one we need to ignore, the just continue to the next record
         if (in_array($row['sales_stage'], $ignore_stages)) {
             continue;
         }
         // if we have not seen this sales stage before, set the value to zero (0)
         if (!isset($data[$row['sales_stage']])) {
             $data[$row['sales_stage']] = array('count' => 0, 'total' => '0');
         }
         // if customers have made amount not required, it saves to the DB as NULL
         // make sure we set it to 0 for the math ahead
         if (empty($row['amount'])) {
             $row['amount'] = 0;
         }
         // convert to the base currency
         $base_amount = SugarCurrency::convertWithRate($row[$amount_field], $row['base_rate']);
         // add the new value into what was already there
         $data[$row['sales_stage']]['total'] = SugarMath::init($data[$row['sales_stage']]['total'], 0)->add($base_amount)->result();
         $data[$row['sales_stage']]['count']++;
         // add to the total
         $total->add($base_amount);
     }
     // get the default currency
     /* @var $currency Currency */
     $currency = SugarCurrency::getBaseCurrency();
     // setup for return format
     $return_data = array();
     $series = 0;
     $previous_value = SugarMath::init('0', 0);
     foreach ($data as $key => $item) {
         $value = $item['total'];
         // set up each return key
         $return_data[] = array('key' => $key, 'count' => $item['count'], 'values' => array(array('series' => $series++, 'label' => SugarCurrency::formatAmount($value, $currency->id, 0), 'value' => intval($value), 'x' => 0, 'y' => intval($value), 'y0' => intval($previous_value->result()))));
         // save the previous value for use in the next item in the series
         $previous_value->add($value);
     }
     // actually return the formatted data
     $mod_strings = return_module_language($GLOBALS['current_language'], $seed->module_name);
     //return the total from the SugarMath Object.
     $total = $total->result();
     return array('properties' => array('title' => $mod_strings['LBL_PIPELINE_TOTAL_IS'] . SugarCurrency::formatAmount($total, $currency->id), 'total' => $total, 'scale' => 1000, 'units' => $currency->symbol), 'data' => $return_data);
 }
Esempio n. 22
0
 /**
  * Returns the negative of the expression that it contains.
  */
 public function evaluate()
 {
     return SugarMath::init('-1')->mul($this->getParameters()->evaluate())->result();
 }
Esempio n. 23
0
 /**
  * This method emulates the Forecast Rep Worksheet calculateTotals method.
  *
  * @param string $timeperiod_id
  * @param string $user_id
  * @param string|null $forecast_by
  * @param boolean $useDraftRecords
  * @return array|bool
  */
 public function worksheetTotals($timeperiod_id, $user_id, $forecast_by = null, $useDraftRecords = false)
 {
     /* @var $tp TimePeriod */
     $tp = BeanFactory::getBean('TimePeriods', $timeperiod_id);
     if (empty($tp->id)) {
         // timeperiod not found
         return false;
     }
     /* @var $admin Administration */
     $admin = BeanFactory::getBean('Administration');
     $settings = $admin->getConfigForModule('Forecasts');
     if (is_null($forecast_by)) {
         $forecast_by = $settings['forecast_by'];
     }
     // setup the return array
     $return = array('amount' => '0', 'best_case' => '0', 'worst_case' => '0', 'overall_amount' => '0', 'overall_best' => '0', 'overall_worst' => '0', 'timeperiod_id' => $tp->id, 'lost_count' => '0', 'lost_amount' => '0', 'lost_best' => '0', 'lost_worst' => '0', 'won_count' => '0', 'won_amount' => '0', 'won_best' => '0', 'won_worst' => '0', 'included_opp_count' => 0, 'total_opp_count' => 0, 'includedClosedCount' => 0, 'includedClosedAmount' => '0', 'includedClosedBest' => '0', 'includedClosedWorst' => '0', 'pipeline_amount' => '0', 'pipeline_opp_count' => 0, 'closed_amount' => '0', 'includedIdsInLikelyTotal' => array());
     global $current_user;
     $sq = new SugarQuery();
     $bean_obj = BeanFactory::getBean($this->module_name);
     $sq->select(array($bean_obj->getTableName() . '.*'));
     $sq->from($bean_obj)->where()->equals('assigned_user_id', $user_id)->equals('parent_type', $forecast_by)->equals('deleted', 0)->equals('draft', $current_user->id == $user_id || $useDraftRecords === true ? 1 : 0)->queryAnd()->gte('date_closed_timestamp', $tp->start_date_timestamp)->lte('date_closed_timestamp', $tp->end_date_timestamp);
     $results = $sq->execute();
     foreach ($results as $row) {
         // if customers have made likely_case, best_case, or worst_case not required,
         // it saves to the DB as NULL, make sure we set it to 0 for the math ahead
         if (empty($row['likely_case'])) {
             $row['likely_case'] = 0;
         }
         if (empty($row['best_case'])) {
             $row['best_case'] = 0;
         }
         if (empty($row['worst_case'])) {
             $row['worst_case'] = 0;
         }
         $worst_base = SugarCurrency::convertWithRate($row['worst_case'], $row['base_rate']);
         $amount_base = SugarCurrency::convertWithRate($row['likely_case'], $row['base_rate']);
         $best_base = SugarCurrency::convertWithRate($row['best_case'], $row['base_rate']);
         $closed = false;
         if (in_array($row['sales_stage'], $settings['sales_stage_won']) && in_array($row['commit_stage'], $settings['commit_stages_included'])) {
             $return['won_amount'] = SugarMath::init($return['won_amount'], 6)->add($amount_base)->result();
             $return['won_best'] = SugarMath::init($return['won_best'], 6)->add($best_base)->result();
             $return['won_worst'] = SugarMath::init($return['won_worst'], 6)->add($worst_base)->result();
             $return['won_count']++;
             $return['includedClosedCount']++;
             $return['includedClosedAmount'] = SugarMath::init($return['includedClosedAmount'], 6)->add($amount_base)->result();
             $closed = true;
         } elseif (in_array($row['sales_stage'], $settings['sales_stage_lost'])) {
             $return['lost_amount'] = SugarMath::init($return['lost_amount'], 6)->add($amount_base)->result();
             $return['lost_best'] = SugarMath::init($return['lost_best'], 6)->add($best_base)->result();
             $return['lost_worst'] = SugarMath::init($return['lost_worst'], 6)->add($worst_base)->result();
             $return['lost_count']++;
             $closed = true;
         }
         if (in_array($row['commit_stage'], $settings['commit_stages_included'])) {
             if (!$closed) {
                 $return['amount'] = SugarMath::init($return['amount'], 6)->add($amount_base)->result();
                 $return['best_case'] = SugarMath::init($return['best_case'], 6)->add($best_base)->result();
                 $return['worst_case'] = SugarMath::init($return['worst_case'], 6)->add($worst_base)->result();
                 // add RLI/Opp id to includedIds array
                 array_push($return['includedIdsInLikelyTotal'], $row['parent_id']);
             }
             $return['included_opp_count']++;
             if ($closed) {
                 $return['includedClosedBest'] = SugarMath::init($return['includedClosedBest'], 6)->add($best_base)->result();
                 $return['includedClosedWorst'] = SugarMath::init($return['includedClosedWorst'], 6)->add($worst_base)->result();
             }
         }
         $return['total_opp_count']++;
         $return['overall_amount'] = SugarMath::init($return['overall_amount'], 6)->add($amount_base)->result();
         $return['overall_best'] = SugarMath::init($return['overall_best'], 6)->add($best_base)->result();
         $return['overall_worst'] = SugarMath::init($return['overall_worst'], 6)->add($worst_base)->result();
     }
     // send back the totals
     return $return;
 }
 /**
  * Take a list of RLI's and make them into a new Product Bundle
  *
  * @param array $rlis
  * @param string $quote_id      The id for the quote we are creating
  * @return ProductBundle
  */
 protected function createProductBundleFromRLIList(array $rlis, $quote_id = null)
 {
     /* @var $product_bundle ProductBundle */
     $product_bundle = BeanFactory::getBean('ProductBundles');
     $product_bundle->id = create_guid();
     $product_bundle->new_with_id = true;
     $subtotal = SugarMath::init(0);
     $deal_tot = SugarMath::init(0);
     foreach ($rlis as $key => $rli_id) {
         /* @var $rli RevenueLineItem */
         $rli = BeanFactory::getBean('RevenueLineItems', $rli_id);
         /* @var $product Product */
         $product = $rli->convertToQuotedLineItem();
         $product_bundle->set_relationship('product_bundle_product', array('bundle_id' => $product_bundle->id, 'product_id' => $product->id, 'product_index' => $key + 1));
         if (!is_null($quote_id)) {
             $product->quote_id = $quote_id;
             $product->status = Product::STATUS_QUOTED;
             # Set the quote_id on the product so we know it's linked
             $rli->quote_id = $quote_id;
             $rli->status = RevenueLineItem::STATUS_QUOTED;
             $rli->save();
         }
         $product->save();
     }
     $product_bundle->bundle_stage = 'Draft';
     $product_bundle->currency_id = $product->currency_id;
     $product_bundle->base_rate = $product->base_rate;
     $product_bundle->save();
     return $product_bundle;
 }
Esempio n. 25
0
 /**
  * Save any committed values
  *
  * @return array|mixed
  */
 public function save()
 {
     global $current_user;
     $args = $this->getArgs();
     $db = DBManagerFactory::getInstance();
     if (!isset($args['timeperiod_id']) || empty($args['timeperiod_id'])) {
         $args['timeperiod_id'] = TimePeriod::getCurrentId();
     }
     $commit_type = strtolower($this->getArg('commit_type'));
     /* @var $mgr_worksheet ForecastManagerWorksheet */
     $mgr_worksheet = BeanFactory::getBean('ForecastManagerWorksheets');
     /* @var $worksheet ForecastWorksheet */
     $worksheet = BeanFactory::getBean('ForecastWorksheets');
     $field_ext = '_case';
     if ($commit_type == "manager") {
         $worksheet_totals = $mgr_worksheet->worksheetTotals($current_user->id, $args['timeperiod_id']);
         // we don't need the *_case values so lets make them the same as the *_adjusted values
         $field_ext = '_adjusted';
     } else {
         $worksheet_totals = $worksheet->worksheetTotals($args['timeperiod_id'], $current_user->id);
         // set likely
         $worksheet_totals['likely_case'] = SugarMath::init($worksheet_totals['amount'], 6)->add($worksheet_totals['includedClosedAmount'])->result();
         $worksheet_totals['best_case'] = SugarMath::init($worksheet_totals['best_case'], 6)->add($worksheet_totals['includedClosedBest'])->result();
         $worksheet_totals['worst_case'] = SugarMath::init($worksheet_totals['worst_case'], 6)->add($worksheet_totals['includedClosedWorst'])->result();
     }
     /* @var $forecast Forecast */
     $forecast = BeanFactory::getBean('Forecasts');
     $forecast->user_id = $current_user->id;
     $forecast->timeperiod_id = $args['timeperiod_id'];
     $forecast->best_case = $worksheet_totals['best' . $field_ext];
     $forecast->likely_case = $worksheet_totals['likely' . $field_ext];
     $forecast->worst_case = $worksheet_totals['worst' . $field_ext];
     $forecast->forecast_type = $args['forecast_type'];
     $forecast->opp_count = $worksheet_totals['included_opp_count'];
     $forecast->currency_id = '-99';
     $forecast->base_rate = '1';
     //If we are committing a rep forecast, calculate things.  Otherwise, for a manager, just use what is passed in.
     if ($args['commit_type'] == 'sales_rep') {
         $forecast->calculatePipelineData($worksheet_totals['includedClosedAmount'], $worksheet_totals['includedClosedCount']);
         //push the pipeline numbers back into the args
         $args['pipeline_opp_count'] = $forecast->pipeline_opp_count;
         $args['pipeline_amount'] = $forecast->pipeline_amount;
         $worksheet_totals['closed_amount'] = $forecast->closed_amount;
     } else {
         $forecast->pipeline_opp_count = $worksheet_totals['pipeline_opp_count'];
         $forecast->pipeline_amount = $worksheet_totals['pipeline_amount'];
         $forecast->closed_amount = $worksheet_totals['closed_amount'];
     }
     if ($worksheet_totals['likely_case'] != 0 && $worksheet_totals['included_opp_count'] != 0) {
         $forecast->opp_weigh_value = $worksheet_totals['likely_case'] / $worksheet_totals['included_opp_count'];
     }
     $forecast->save();
     // roll up the committed forecast to that person manager view
     // copy the object so we can set some needed values
     $mgr_rollup_data = $worksheet_totals;
     $mgr_rollup_data['forecast_type'] = $args['forecast_type'];
     // pass same timeperiod as the other data to the manager's rollup
     $mgr_rollup_data['timeperiod_id'] = $args['timeperiod_id'];
     $mgr_worksheet->reporteeForecastRollUp($current_user, $mgr_rollup_data);
     if ($this->getArg('commit_type') == "sales_rep") {
         $worksheet->commitWorksheet($current_user->id, $args['timeperiod_id']);
     } elseif ($this->getArg('commit_type') == "manager") {
         $mgr_worksheet->commitManagerForecast($current_user, $args['timeperiod_id']);
     }
     //TODO-sfa remove this once the ability to map buckets when they get changed is implemented (SFA-215).
     $admin = BeanFactory::getBean('Administration');
     $settings = $admin->getConfigForModule('Forecasts');
     if (!isset($settings['has_commits']) || !$settings['has_commits']) {
         $admin->saveSetting('Forecasts', 'has_commits', true, 'base');
         MetaDataManager::refreshModulesCache(array('Forecasts'));
     }
     $forecast->date_entered = $this->convertDateTimeToISO($db->fromConvert($forecast->date_entered, 'datetime'));
     $forecast->date_modified = $this->convertDateTimeToISO($db->fromConvert($forecast->date_modified, 'datetime'));
     return $worksheet_totals;
 }