Пример #1
0
 /**
  * Check a new set of line items against the existing set and update/delete as necessary
  *
  * Note: line numbers should be computed client-side and thus shouldn't need to be recalculated.
  *
  * @param array $items Each entry is an associative array of QuoteProduct [attribute]=>[value] 
  *  pairs
  * @param integer $quoteId ID of quote for which to update items
  * @param bool $save Whether or not to save changes in the database after finishing
  * @return array Array of QuoteProduct instances representing the item set after changes.
  * @throws CException
  */
 public function setLineItems(array $items, $save = false, $skipProcessing = false)
 {
     if ($skipProcessing) {
         $this->_lineItems = $items;
         return;
     }
     $this->_deleteLineItems = array();
     if (count($items) === 0) {
         QuoteProduct::model()->deleteAllByAttributes(array('quoteId' => $this->id));
         return true;
     }
     // Check for valid input:
     $typeErrMsg = 'The setter of Quote.lineItems requires an array of QuoteProduct objects or ' . '[attribute]=>[value] arrays.';
     $firstElt = reset($items);
     $type = gettype($firstElt);
     if ($type != 'object' && $type != 'array') {
         // Must be one or the other
         throw new Exception($typeErrMsg);
     }
     if ($type == 'object') {
         // If object, must be of the QuoteProduct class
         if (get_class($firstElt) != 'QuoteProduct') {
             throw new Exception($typeErrMsg);
         }
     }
     // Gather existing line items into an array indexed by ID.
     $existingItemIds = array();
     $newItems = array();
     $itemSet = array();
     $existingItems = array();
     foreach ($this->lineItems as $item) {
         if ($item->isNewRecord) {
             // this line might not be needed anymore. Used to be used for record duplication,
             // but now now skipProcessing is used instead, bypassing this line.
             $item->save();
         }
         $existingItems[$item->id] = $item;
         $existingItemIds[] = (int) $item->id;
     }
     // Gather the new set of line items into arrays
     if (isset($items[''])) {
         unset($items['']);
     }
     if ($type == 'object') {
         foreach ($items as $item) {
             if (in_array($item->id, $existingItemIds)) {
                 $itemSet[$item->id] = $existingItems[$item->id];
                 $itemSet[$item->id]->attributes = $item->attributes;
             } else {
                 $newItems[] = $item;
             }
         }
     } else {
         if ($type == 'array') {
             foreach ($items as $item) {
                 $new = false;
                 if (isset($item['id'])) {
                     $id = $item['id'];
                     if (in_array($id, $existingItemIds)) {
                         $itemSet[$id] = $existingItems[$item['id']];
                         $itemSet[$id]->attributes = $item;
                     } else {
                         $new = true;
                     }
                 } else {
                     $new = true;
                 }
                 if ($new) {
                     $itemObj = new QuoteProduct();
                     $itemObj->attributes = $item;
                     $newItems[] = $itemObj;
                 }
             }
         }
     }
     // Compute set changes:
     $itemIds = array_keys($itemSet);
     $deleteItemIds = array_diff($existingItemIds, $itemIds);
     $updateItemIds = array_intersect($existingItemIds, $itemIds);
     // Put all the items together into the same arrays
     $this->_lineItems = array_merge($newItems, array_values($itemSet));
     usort($this->_lineItems, 'self::lineItemOrder');
     $this->_deleteLineItems = array_map(function ($id) use($existingItems) {
         return $existingItems[$id];
     }, $deleteItemIds);
     // Remove symbols from numerical input values and convert to numeric.
     // Behavior:
     // - Use the quote's currency if it isn't empty.
     // - Use the app's currency otherwise.
     $defaultCurrency = empty($this->currency) ? Yii::app()->settings->currency : $this->currency;
     $curSym = Yii::app()->locale->getCurrencySymbol($defaultCurrency);
     if (is_null($curSym)) {
         $curSym = $defaultCurrency;
     }
     foreach ($this->_lineItems as $lineItem) {
         $lineItem->quoteId = $this->id;
         $product = X2Model::model('Products')->findByAttributes(array('name' => $lineItem->name));
         if (isset($product)) {
             $lineItem->productId = $product->id;
         }
         if (empty($lineItem->currency)) {
             $lineItem->currency = $defaultCurrency;
         }
         if ($lineItem->isPercentAdjustment) {
             $lineItem->adjustment = Fields::strToNumeric($lineItem->adjustment, 'percentage');
         } else {
             $lineItem->adjustment = Fields::strToNumeric($lineItem->adjustment, 'currency', $curSym);
         }
         $lineItem->price = Fields::strToNumeric($lineItem->price, 'currency', $curSym);
         $lineItem->total = Fields::strToNumeric($lineItem->total, 'currency', $curSym);
     }
     // Validate
     $this->hasLineItemErrors = false;
     $this->lineItemErrors = array();
     foreach ($this->_lineItems as $item) {
         $itemValid = $item->validate();
         if (!$itemValid) {
             $this->hasLineItemErrors = true;
             foreach ($item->errors as $attribute => $errors) {
                 foreach ($errors as $error) {
                     $this->lineItemErrors[] = $error;
                 }
             }
         }
     }
     $this->lineItemErrors = array_unique($this->lineItemErrors);
     // Reset derived properties:
     $this->_adjustmentLines = null;
     $this->_productLines = null;
     // Save
     if ($save && !$this->hasLineItemErrors) {
         $this->saveLineItems();
     }
 }
Пример #2
0
 /**
  * Sets attributes using X2Fields
  * @param array &$data array of attributes to be set (eg. $_POST['Contacts'])
  * @param bool $filter encode all HTML special characters in input
  * @param bool $bypassPermissions (optional)
  */
 public function setX2Fields(&$data, $filter = false, $bypassPermissions = false)
 {
     $editableFieldsFieldNames = $this->getEditableFieldNames();
     // loop through fields to deal with special types
     foreach (self::$_fields[$this->tableName()] as &$field) {
         $fieldName = $field->fieldName;
         // skip fields that are read-only or haven't been set
         if (!isset($data[$fieldName]) || !$bypassPermissions && !in_array($fieldName, $editableFieldsFieldNames)) {
             if (isset($data[$fieldName]) && !in_array($fieldName, $editableFieldsFieldNames)) {
                 //if (YII_DEBUG)
                 //printR('setX2Fields: Warning: ' . $fieldName . ' set');
             }
             continue;
         }
         // eliminate placeholder values
         if ($data[$fieldName] === $this->getAttributeLabel($fieldName) && $field->type !== 'dropdown') {
             $data[$fieldName] = null;
         }
         if ($field->type === 'currency') {
             $defaultCurrency = Yii::app()->settings->currency;
             $curSym = Yii::app()->locale->getCurrencySymbol($defaultCurrency);
             if (is_null($curSym)) {
                 $curSym = $defaultCurrency;
             }
             $data[$fieldName] = Fields::strToNumeric($data[$fieldName], 'currency', $curSym);
         }
         if ($field->type === 'link') {
             // Do a preliminary lookup for linkId in case there are
             // duplicates (similar name) and the user selects one of them,
             // in which case there is an ID from the form that was populated
             // via the auto-complete input widget:
             $linkId = null;
             if (isset($data[$fieldName . '_id'])) {
                 // get the linked model's ID from the hidden autocomplete field
                 $linkId = $data[$fieldName . '_id'];
             }
             if (ctype_digit((string) $linkId)) {
                 $link = Yii::app()->db->createCommand()->select('name,nameId')->from(X2Model::model($field->linkType)->tableName())->where('id=?', array($linkId))->queryRow(true);
                 // Make sure the linked model exists and that the name matches:
                 if (isset($link['name']) && $link['name'] === $data[$fieldName]) {
                     $data[$fieldName] = $link['nameId'];
                 }
             }
         }
         $this->{$fieldName} = $field->parseValue($data[$fieldName], $filter);
     }
     // Set default values.
     //
     // This should only happen in the case that the field was not included
     // in the form submission data (with the exception of assignment fields), and the field is
     // empty, and the record is new.
     if ($this->getIsNewRecord() && $this->scenario == 'insert') {
         // Set default values
         foreach ($this->getFields(true) as $fieldName => $field) {
             if (!isset($data[$fieldName]) && $this->{$fieldName} == '' && $field->defaultValue != null && !$field->readOnly) {
                 $this->{$fieldName} = $field->defaultValue;
             } else {
                 if ($this->{$fieldName} === null && $field->defaultValue === null && $field->type === 'assignment') {
                     $this->{$fieldName} = self::getDefaultAssignment();
                 }
             }
         }
     }
 }
Пример #3
0
 public function testStrToNumeric()
 {
     $cur = Yii::app()->locale->getCurrencySymbol(Yii::app()->settings->currency);
     $input = " {$cur} 123.45 % ";
     $this->assertEquals(123.45, Fields::strToNumeric($input, 'currency'));
     $this->assertEquals(123, Fields::strToNumeric($input, 'int'));
     $this->assertEquals(123.45, Fields::strToNumeric($input, 'float'));
     $this->assertEquals(123.45, Fields::strToNumeric($input, 'percentage'));
     $this->assertEquals(0, Fields::strToNumeric(null, 'float'));
     $type = 'notanint';
     $value = Fields::strToNumeric($input, $type);
     $this->assertEquals(123.45, $value);
     // Randumb string comes back as itself
     $input = 'cockadoodledoo';
     $value = Fields::strToNumeric($input, 'int');
     $this->assertEquals($input, $value);
     // Null always evaluates to zero
     $value = Fields::strToNumeric('');
     $this->assertEquals(0, $value);
     // Parsing of parenthesized notation for negative currency values
     $value = Fields::strToNumeric('($45.82)', 'currency');
     $this->assertEquals(-45.82, $value);
     // Negative percentage values:
     $value = Fields::strToNumeric('-12.5%', 'percentage');
     $this->assertEquals(-12.5, $value);
     // Comma notation for thousands:
     $value = Fields::strToNumeric('$9,888.77', 'currency');
     $this->assertEquals(9888.77, $value);
     // Comma plus parentheses notation
     $value = Fields::strToNumeric('($9,888.77)', 'currency');
     $this->assertEquals(-9888.77, $value);
     // Comma and minus sign notation:
     $value = Fields::strToNumeric('-$9,888.77', 'currency');
     $this->assertEquals(-9888.77, $value);
     // Rounded to integer, over 10^6:
     $value = Fields::strToNumeric('$10,000,000', 'currency');
     $this->assertEquals(10000000, $value);
     // ...negative
     $value = Fields::strToNumeric('($10,000,000)', 'currency');
     $this->assertEquals(-10000000, $value);
     // ...with decimal places
     $value = Fields::strToNumeric('($10,000,000.01)', 'currency');
     $this->assertEquals(-10000000.01, $value);
     // Multibyte support:
     $curSym = Yii::app()->locale->getCurrencySymbol('INR');
     $value = Fields::strToNumeric("({$curSym}" . "9,888.77)", 'currency', $curSym);
     $this->assertEquals(-9888.77, $value, 'Failed asserting proper conversion of multibyte strings to numbers.');
 }