Overview

Packages

  • application
    • commands
    • components
      • actions
      • filters
      • leftWidget
      • permissions
      • sortableWidget
      • util
      • webupdater
      • x2flow
        • actions
        • triggers
      • X2GridView
      • X2Settings
    • controllers
    • models
      • embedded
    • modules
      • accounts
        • controllers
        • models
      • actions
        • controllers
        • models
      • calendar
        • controllers
        • models
      • charts
        • models
      • contacts
        • controllers
        • models
      • docs
        • components
        • controllers
        • models
      • groups
        • controllers
        • models
      • marketing
        • components
        • controllers
        • models
      • media
        • controllers
        • models
      • mobile
        • components
      • opportunities
        • controllers
        • models
      • products
        • controllers
        • models
      • quotes
        • controllers
        • models
      • services
        • controllers
        • models
      • template
        • models
      • users
        • controllers
        • models
      • workflow
        • controllers
        • models
      • x2Leads
        • controllers
        • models
  • None
  • system
    • base
    • caching
    • console
    • db
      • ar
      • schema
    • validators
    • web
      • actions
      • auth
      • helpers
      • widgets
        • captcha
        • pagers
  • zii
    • widgets
      • grid

Classes

  • Quote
  • QuoteProduct
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: 
  3: /*****************************************************************************************
  4:  * X2Engine Open Source Edition is a customer relationship management program developed by
  5:  * X2Engine, Inc. Copyright (C) 2011-2016 X2Engine Inc.
  6:  * 
  7:  * This program is free software; you can redistribute it and/or modify it under
  8:  * the terms of the GNU Affero General Public License version 3 as published by the
  9:  * Free Software Foundation with the addition of the following permission added
 10:  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
 11:  * IN WHICH THE COPYRIGHT IS OWNED BY X2ENGINE, X2ENGINE DISCLAIMS THE WARRANTY
 12:  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
 13:  * 
 14:  * This program is distributed in the hope that it will be useful, but WITHOUT
 15:  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 16:  * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
 17:  * details.
 18:  * 
 19:  * You should have received a copy of the GNU Affero General Public License along with
 20:  * this program; if not, see http://www.gnu.org/licenses or write to the Free
 21:  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 22:  * 02110-1301 USA.
 23:  * 
 24:  * You can contact X2Engine, Inc. P.O. Box 66752, Scotts Valley,
 25:  * California 95067, USA. or at email address contact@x2engine.com.
 26:  * 
 27:  * The interactive user interfaces in modified source and object code versions
 28:  * of this program must display Appropriate Legal Notices, as required under
 29:  * Section 5 of the GNU Affero General Public License version 3.
 30:  * 
 31:  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
 32:  * these Appropriate Legal Notices must retain the display of the "Powered by
 33:  * X2Engine" logo. If the display of the logo is not reasonably feasible for
 34:  * technical reasons, the Appropriate Legal Notices must display the words
 35:  * "Powered by X2Engine".
 36:  *****************************************************************************************/
 37: 
 38: Yii::import('application.models.X2Model');
 39: 
 40: /**
 41:  * This is the model class for table "x2_quotes".
 42:  *
 43:  * @property array $adjustmentLines (read-only) Line items that are adjustments to the subtotal
 44:  * @property Contacts $contact First contact associated with this quote.
 45:  * @property array $lineItems All line items for the quote.
 46:  * @property array $productLines (read-only) Line items that are products/services.
 47:  * @package application.modules.quotes.models
 48:  * @author David Visbal, Demitri Morgan <demitri@x2engine.com>
 49:  */
 50: class Quote extends X2Model {
 51: 
 52:     public $supportsWorkflow = false;
 53: 
 54:     /**
 55:      * Holds the set of line items
 56:      * @var array
 57:      */
 58:     private $_lineItems;
 59: 
 60:     private $_contact;
 61: 
 62:     /**
 63:      * Holds the set of line items to be deleted
 64:      * @var array
 65:      */
 66:     private $_deleteLineItems;
 67: 
 68:     /**
 69:      * Value stored for {@link productLines}
 70:      * @var array
 71:      */
 72:     private $_productLines;
 73:     /**
 74:      * Value stored for {@link adjustmentLines}
 75:      * @var array
 76:      */
 77:     private $_adjustmentLines;
 78: 
 79: 
 80:     /**
 81:      * Whether the line item set has errors in it.
 82:      * @var bool
 83:      */
 84:     public $hasLineItemErrors = false;
 85:     public $lineItemErrors = array();
 86: 
 87:     public static function lineItemOrder($i0,$i1) {
 88:         return $i0->lineNumber < $i1->lineNumber ? -1 : 1;
 89:     }
 90: 
 91:     /**
 92:      * Magic getter for {@link lineItems}.
 93:      */
 94:     public function getLineItems() {
 95:         if (!isset($this->_lineItems)) {
 96:             $lineItems = $this->getRelated('products');
 97:             if(count(array_filter($lineItems,function($li){return empty($li->lineNumber);})) > 0) {
 98:                 // Cannot abide null line numbers. Use indexes to set initial line numbers!
 99:                 foreach($lineItems as $i => $li) {
100:                     $li->lineNumber = $i;
101:                     $li->save();
102:                 }
103:             }
104:             usort($lineItems,'self::lineItemOrder');
105:             $this->_lineItems = array();
106:             foreach($lineItems as $li) {
107:                 $this->_lineItems[(int) $li->lineNumber] = $li;
108:             }
109:         }
110:         return $this->_lineItems;
111:     }
112: 
113:     /**
114:      * Magic getter for {@link adjustmentLines}
115:      */
116:     public function getAdjustmentLines(){
117:         if(!isset($this->_adjustmentLines))
118:             $this->_adjustmentLines = array_filter(
119:                 $this->lineItems,function($li){return $li->isTotalAdjustment;});
120:         return $this->_adjustmentLines;
121:     }
122: 
123:     /**
124:      * Magic getter for {@link productLines}
125:      */
126:     public function getProductLines(){
127:         if(!isset($this->_productLines))
128:             $this->_productLines = array_filter(
129:                 $this->lineItems,function($li){return !$li->isTotalAdjustment;});
130:         return $this->_productLines;
131:     }
132: 
133:     /**
134:      * Returns the static model of the specified AR class.
135:      * @return Quotes the static model class
136:      */
137:     public static function model($className = __CLASS__) {
138:         return parent::model($className);
139:     }
140: 
141:     /**
142:      * @return array relational rules.
143:      */
144:     public function relations(){
145:         // NOTE: you may need to adjust the relation name and the related
146:         // class name for the relations automatically generated below.
147:         return array_merge(parent::relations(), array(
148:             'products' => array(
149:                 self::HAS_MANY, 'QuoteProduct', 'quoteId', 'order' => 'lineNumber ASC'),
150:             'contact' => array(
151:                 self::BELONGS_TO, 'Contacts', array('associatedContacts' => 'nameId'))
152:         ));
153:     }
154: 
155:     /**
156:      * @return string the associated database table name
157:      */
158:     public function tableName() {
159:         return 'x2_quotes';
160:     }
161: 
162:     public function behaviors() {
163:         return array_merge(parent::behaviors(), array(
164:             'X2LinkableBehavior' => array(
165:                 'class' => 'X2LinkableBehavior',
166:                 'module' => 'quotes'
167:             ),
168:             'ERememberFiltersBehavior' => array(
169:                 'class' => 'application.components.ERememberFiltersBehavior',
170:                 'defaults' => array(),
171:                 'defaultStickOnClear' => false
172:             )
173:         ));
174:     }
175: 
176:     /**
177:      * Check a new set of line items against the existing set and update/delete as necessary
178:      *
179:      * Note: line numbers should be computed client-side and thus shouldn't need to be recalculated.
180:      *
181:      * @param array $items Each entry is an associative array of QuoteProduct [attribute]=>[value] 
182:      *  pairs
183:      * @param integer $quoteId ID of quote for which to update items
184:      * @param bool $save Whether or not to save changes in the database after finishing
185:      * @return array Array of QuoteProduct instances representing the item set after changes.
186:      * @throws CException
187:      */
188:     public function setLineItems(array $items, $save = false, $skipProcessing=false) {
189:         if ($skipProcessing) {
190:             $this->_lineItems = $items;
191:             return;
192:         }
193: 
194:         $this->_deleteLineItems = array();
195:         if (count($items) === 0) {
196:             QuoteProduct::model()->deleteAllByAttributes(array('quoteId' => $this->id));
197:             return true;
198:         }
199: 
200:         // Check for valid input:
201:         $typeErrMsg = 'The setter of Quote.lineItems requires an array of QuoteProduct objects or '.
202:             '[attribute]=>[value] arrays.';
203:         $firstElt = reset($items);
204:         $type = gettype($firstElt);
205:         if ($type != 'object' && $type != 'array') // Must be one or the other
206:             throw new Exception($typeErrMsg);
207:         if ($type == 'object') // If object, must be of the QuoteProduct class
208:             if (get_class($firstElt) != 'QuoteProduct')
209:                 throw new Exception($typeErrMsg);
210: 
211:         // Gather existing line items into an array indexed by ID.
212:         $existingItemIds = array();
213:         $newItems = array();
214:         $itemSet = array();
215:         $existingItems = array();
216:         foreach ($this->lineItems as $item) {
217:             if ($item->isNewRecord) {
218:                 // this line might not be needed anymore. Used to be used for record duplication,
219:                 // but now now skipProcessing is used instead, bypassing this line.
220:                 $item->save();
221:             }
222:             $existingItems[$item->id] = $item;
223:             $existingItemIds[] = (int) $item->id;
224:         }
225: 
226:         // Gather the new set of line items into arrays
227:         if (isset($items['']))
228:             unset($items['']);
229:         if ($type == 'object') {
230:             foreach ($items as $item) {
231:                 if (in_array($item->id, $existingItemIds)) {
232:                     $itemSet[$item->id] = $existingItems[$item->id];
233:                     $itemSet[$item->id]->attributes = $item->attributes;
234:                 } else {
235:                     $newItems[] = $item;
236:                 }
237:             }
238:         } else if ($type == 'array') {
239:             foreach ($items as $item) {
240:                 $new = false;
241:                 if (isset($item['id'])) {
242:                     $id = $item['id'];
243:                     if (in_array($id, $existingItemIds)) {
244:                         $itemSet[$id] = $existingItems[$item['id']];
245:                         $itemSet[$id]->attributes = $item;
246:                     } else
247:                         $new = true;
248:                 } else
249:                     $new = true;
250: 
251:                 if ($new) {
252:                     $itemObj = new QuoteProduct;
253:                     $itemObj->attributes = $item;
254:                     $newItems[] = $itemObj;
255:                 }
256:             }
257:         }
258: 
259:         // Compute set changes:
260:         $itemIds = array_keys($itemSet);
261:         $deleteItemIds = array_diff($existingItemIds, $itemIds);
262:         $updateItemIds = array_intersect($existingItemIds, $itemIds);
263: 
264:         // Put all the items together into the same arrays
265:         $this->_lineItems = array_merge($newItems, array_values($itemSet));
266:         usort($this->_lineItems,'self::lineItemOrder');
267:         $this->_deleteLineItems = array_map(
268:             function($id) use($existingItems) {return $existingItems[$id];}, $deleteItemIds);
269: 
270:         // Remove symbols from numerical input values and convert to numeric.
271:         // Behavior:
272:         // - Use the quote's currency if it isn't empty.
273:         // - Use the app's currency otherwise.
274:         $defaultCurrency = empty($this->currency)?
275:             Yii::app()->settings->currency:$this->currency;
276: 
277:         $curSym = Yii::app()->locale->getCurrencySymbol($defaultCurrency);
278:         if (is_null($curSym))
279:             $curSym = $defaultCurrency;
280: 
281:         foreach($this->_lineItems as $lineItem) {
282:             $lineItem->quoteId = $this->id;
283:             $product = X2Model::model('Products')->findByAttributes(array('name'=>$lineItem->name));
284:             if (isset($product))
285:                 $lineItem->productId = $product->id;
286:             if(empty($lineItem->currency))
287:                 $lineItem->currency = $defaultCurrency;
288:             if($lineItem->isPercentAdjustment) {
289:                 $lineItem->adjustment = Fields::strToNumeric(
290:                     $lineItem->adjustment,'percentage');
291:             } else {
292:                 $lineItem->adjustment = Fields::strToNumeric(
293:                     $lineItem->adjustment,'currency',$curSym);
294:             }
295:             $lineItem->price = Fields::strToNumeric($lineItem->price,'currency',$curSym);
296:             $lineItem->total = Fields::strToNumeric($lineItem->total,'currency',$curSym);
297:         }
298: 
299:         // Validate
300:         $this->hasLineItemErrors = false;
301:         $this->lineItemErrors = array();
302:         foreach ($this->_lineItems as $item) {
303:             $itemValid = $item->validate();
304:             if (!$itemValid) {
305:                 $this->hasLineItemErrors = true;
306:                 foreach ($item->errors as $attribute => $errors)
307:                     foreach ($errors as $error)
308:                         $this->lineItemErrors[] = $error;
309:             }
310:         }
311:         $this->lineItemErrors = array_unique($this->lineItemErrors);
312: 
313:         // Reset derived properties:
314:         $this->_adjustmentLines = null;
315:         $this->_productLines = null;
316: 
317:         // Save
318:         if($save && !$this->hasLineItemErrors)
319:             $this->saveLineItems();
320:     }
321: 
322:     /**
323:      * Saves line item set changes to the database.
324:      */
325:     public function saveLineItems(){
326:         // Insert/update new/existing items:
327:         if(isset($this->_lineItems)){
328:             foreach($this->_lineItems as $item){
329:                 $item->quoteId = $this->id;
330:                 $product = X2Model::model('Products')->findByAttributes(array(
331:                     'name'=>$item->name
332:                 ));
333:                 if (isset($product))
334:                     $item->productId = $product->id;
335:                 $item->save();
336:             }
337:         }
338:         if(isset($this->_deleteLineItems)) {
339:             // Delete all deleted items:
340:             foreach($this->_deleteLineItems as $item)
341:                 $item->delete();
342:             $this->_deleteLineItems = null;
343:         }
344:     }
345: 
346:     public function getContactId () {
347:         list ($name, $id) = Fields::nameAndId ($this->associatedContacts);
348:         return $id;
349:     }
350: 
351:     public function getAccountId () {
352:         list ($name, $id) = Fields::nameAndId ($this->accountName);
353:         return $id;
354:     }
355: 
356:     /**
357:      * Creates an action history event record in the contact/account
358:      */
359:     public function createActionRecord() {
360:         if(!empty($this->contactId)) {
361:             $this->createAssociatedAction ('contacts', $this->contactId);
362:         }
363:         if(!empty($this->accountName)) {
364:             $this->createAssociatedAction ('accounts', $this->accountId);
365:         }
366:     }
367: 
368:     public function createAssociatedAction ($type, $id) {
369:         $now = time();
370:         $actionAttributes = array(
371:             'type' => 'quotes',
372:             'actionDescription' => $this->id,
373:             'completeDate' => $now,
374:             'dueDate' => $now,
375:             'createDate' => $now,
376:             'lastUpdated' => $now,
377:             'complete' => 'Yes',
378:             'completedBy' => $this->createdBy,
379:             'updatedBy' => $this->updatedBy
380:         );
381:         $action = new Actions();
382:         $action->attributes = $actionAttributes;
383:         $action->associationType = $type;
384:         $action->associationId = $id;
385:         $action->save();
386:     }
387: 
388:     /**
389:      * Creates an event record for the creation of the model.
390:      */
391:     public function createEventRecord() {
392: //      $event = new Events();
393: //      $event->type = 'record_create';
394: //      $event->subtype = 'quote';
395: //      $event->associationId = $this->id;
396: //      $event->associationType = 'Quote';
397: //      $event->timestamp = time();
398: //      $event->lastUpdated = $event->timestamp;
399: //      $event->user = $this->createdBy;
400: //      $event->save();
401:     }
402: 
403:     public static function getStatusList() {
404:         $field = Fields::model()->findByAttributes(array('modelName' => 'Quote', 'fieldName' => 'status'));
405:         $dropdown = Dropdowns::model()->findByPk($field->linkType);
406:         return CJSON::decode($dropdown->options, true);
407: 
408:         /*
409:           return array(
410:           'Draft'=>Yii::t('quotes','Draft'),
411:           'Presented'=>Yii::t('quotes','Presented'),
412:           "Issued"=>Yii::t('quotes','Issued'),
413:           "Won"=>Yii::t('quotes','Won')
414:           ); */
415:     }
416: 
417:     /**
418:      * Generates markup for a quote line items table.
419:      *
420:      * @param type $emailTable Style hooks for emailing the quote
421:      * @return string
422:      */
423:     public function productTable($emailTable = false) {
424:         if (!YII_UNIT_TESTING)
425:             Yii::app()->clientScript->registerCssFile (
426:                 Yii::app()->getModule('quotes')->assetsUrl.'/css/productTable.css'
427:             );
428:         $pad = 4;
429:         // Declare styles
430:         $tableStyle = 'border-collapse: collapse; width: 100%;';
431:         $thStyle = 'padding: 5px; border: 1px solid black; background:#eee;';
432:         $thProductStyle = $thStyle;
433:         if(!$emailTable)
434:             $tableStyle .= 'display: inline;';
435:         else
436:             $thProductStyle .=  "width:60%;";
437:         $defaultStyle =  'padding: 5px;border-spacing:0;';
438:         $tdStyle = "$defaultStyle;border-left: 1px solid black; border-right: 1px solid black;";
439:         $tdFooterStyle = "$tdStyle;border-bottom: 1px solid black";
440:         $tdBoxStyle = "$tdFooterStyle;border-top: 1px solid black";
441: 
442:         // Declare element templates
443:         $thProduct = '<th style="'.$thProductStyle.'">{c}</th>';
444:         $tdDef = '<td style="'.$defaultStyle.'">{c}</td>';
445:         $td = '<td style="'.$tdStyle.'">{c}</td>';
446:         $tdFooter = '<td style="'.$tdFooterStyle.'">{c}</td>';
447:         $tdBox = '<td style="'.$tdBoxStyle.'">{c}</td>';
448:         $hr = '<hr style="width: 100%;height:2px;background:black;" />';
449:         $tr = '<tr>{c}</tr>';
450:         $colRange = range(2,7);
451:         $span = array_combine($colRange,array_map(function($s){
452:             return "<td colspan=\"$s\"></td>";},$colRange));
453:         $span[1] = '<td></td>';
454: 
455:         $markup = array();
456: 
457:         // Table opening and header
458:         $markup[] = "<table class='quotes-product-table' style=\"$tableStyle\"><thead>";
459:         $row = array ();
460:         foreach(array(
461:             'Line Item' => '20%; min-width: 200px;',
462:             'Unit Price' => '17.5%',
463:             'Quantity' => '15%',
464:             'Adjustment' => '15%',
465:             'Comments' => '15%',
466:             'Price' => '20%'
467:         ) as $columnHeader => $width) {
468:             $row[] = 
469:                 '<th style="'.$thStyle."width: $width;".'">'.
470:                     Yii::t('products',$columnHeader).
471:                 '</th>';
472:         }
473:         $markup[] = str_replace('{c}',implode("\n",$row),$tr);
474: 
475:         // Table header ending and body
476:         $markup[] = "</thead>";
477: 
478:         // Number of non-adjustment line items:
479:         $n_li = count($this->productLines);
480:         $i = 1;
481: 
482:         // Run through line items:
483:         $markup[] = '<tbody>';
484:         foreach($this->productLines as $ln=>$li) {
485:             // Begin row.
486:             $row = array();
487:             // Add columns for this line
488:             foreach(array('name','price','quantity','adjustment','description','total') as $attr) {
489:                 $row[] = str_replace('{c}',$li->renderAttribute($attr),($i==$n_li?$tdFooter:$td));
490:             }
491:             // Row done.
492:             $markup[] = str_replace('{c}',implode('',$row),$tr);
493:             $i++;
494:         }
495: 
496:         $markup[] = '</tbody>';
497:         $markup[] = '<tbody>';
498:         // The subtotal and adjustment rows, if applicable:
499:         $i = 1;
500:         $n_adj = count($this->adjustmentLines);
501: 
502:         if($n_adj) {
503:             // Subtotal:
504:             $row = array($span[$pad]);
505:             $row[] = str_replace('{c}','<strong>'.Yii::t('quotes','Subtotal').'</strong>',$tdDef);
506:             $row[] = str_replace('{c}','<strong>'.Yii::app()->locale->numberFormatter->formatCurrency($this->subtotal,$this->currency).'</strong>',$tdDef);
507:             $markup[] = str_replace('{c}',implode('',$row),$tr);
508:             $markup[] = '</tbody>';
509:             // Adjustments:
510:             $markup[] = '<tbody>';
511:             foreach($this->adjustmentLines as $ln => $li) {
512:                 // Begin row
513:                 $row = array($span[$pad]);
514:                 $row[] = str_replace('{c}',$li->renderAttribute('name').(!empty($li->description) ? ' ('.$li->renderAttribute('description').')':''),$tdDef);
515:                 $row[] = str_replace('{c}',$li->renderAttribute('adjustment'),$tdDef);
516:                 // Row done
517:                 $markup[] = str_replace('{c}',implode('',$row),$tr);
518:                 $i++;
519:             }
520:             $markup[] = '</tbody>';
521:             $markup[] = '<tbody>';
522:         }
523: 
524:         // Total:
525:         $row = array($span[$pad]);
526:         $row[] = str_replace('{c}','<strong>'.Yii::t('quotes','Total').'</strong>',$tdDef);
527:         $row[] = str_replace('{c}','<strong>'.Yii::app()->locale->numberFormatter->formatCurrency($this->total,$this->currency).'</strong>',$tdBox);
528:         $markup[] = str_replace('{c}',implode('',$row),$tr);
529:         $markup[] = '</tbody>';
530: 
531:         // Done.
532:         $markup[] = '</table>';
533: 
534:         return implode("\n",$markup);
535:     }
536: 
537:     public static function getNames() {
538: 
539:         $names = array(0 => "None");
540: 
541:         foreach (Yii::app()->db->createCommand()->select('id,name')->from('x2_quotes')->queryAll(false) as $row)
542:             $names[$row[0]] = $row[1];
543: 
544:         return $names;
545:     }
546: 
547:     public static function parseUsers($userArray) {
548:         return implode(', ', $userArray);
549:     }
550: 
551:     public static function parseUsersTwo($arr) {
552:         $str = "";
553:         if(is_array($arr)){
554:             $arr=array_keys($arr);
555:             $str=implode(', ',$arr);
556:         }
557:         $str = substr($str, 0, strlen($str) - 2);
558: 
559:         return $str;
560:     }
561: 
562:     public static function parseContacts($contactArray) {
563:         return implode(' ', $contactArray);
564:     }
565: 
566:     public static function parseContactsTwo($arr) {
567:         $str = "";
568:         foreach ($arr as $id => $contact) {
569:             $str.=$id . " ";
570:         }
571:         return $str;
572:     }
573: 
574:     public static function getQuotesLinks($accountId) {
575: 
576:         $quotesList = X2Model::model('Quote')->findAllByAttributes(array('accountName' => $accountId));
577:         // $quotesList = $this->model()->findAllByAttributes(array('accountId'),'=',array($accountId));
578: 
579:         $links = array();
580:         foreach ($quotesList as $model) {
581:             $links[] = CHtml::link($model->name, array('/quotes/quotes/view', 'id' => $model->id));
582:         }
583:         return implode(', ', $links);
584:     }
585: 
586:     public static function editContactArray($arr, $model) {
587: 
588:         $pieces = explode(" ", $model->associatedContacts);
589:         unset($arr[0]);
590: 
591:         foreach ($pieces as $contact) {
592:             if (array_key_exists($contact, $arr)) {
593:                 unset($arr[$contact]);
594:             }
595:         }
596: 
597:         return $arr;
598:     }
599: 
600:     public static function editUserArray($arr, $model) {
601: 
602:         $pieces = explode(', ', $model->assignedTo);
603:         unset($arr['Anyone']);
604:         unset($arr['admin']);
605:         foreach ($pieces as $user) {
606:             if (array_key_exists($user, $arr)) {
607:                 unset($arr[$user]);
608:             }
609:         }
610:         return $arr;
611:     }
612: 
613:     public static function editUsersInverse($arr) {
614: 
615:         $data = array();
616: 
617:         foreach ($arr as $username) {
618:             if ($username != '')
619:                 $data[] = User::model()->findByAttributes(array('username' => $username));
620:         }
621: 
622:         $temp = array();
623:         if (isset($data)) {
624:             foreach ($data as $item) {
625:                 if (isset($item))
626:                     $temp[$item->username] = $item->firstName . ' ' . $item->lastName;
627:             }
628:         }
629:         return $temp;
630:     }
631: 
632:     public static function editContactsInverse($arr) {
633:         $data = array();
634: 
635:         foreach ($arr as $id) {
636:             if ($id != '')
637:                 $data[] = X2Model::model('Contacts')->findByPk($id);
638:         }
639:         $temp = array();
640: 
641:         foreach ($data as $item) {
642:             $temp[$item->id] = $item->firstName . ' ' . $item->lastName;
643:         }
644:         return $temp;
645:     }
646: 
647:     public function search($pageSize=null, $uniqueId=null) {
648:         $pageSize = $pageSize === null ? Profile::getResultsPerPage() : $pageSize;
649:         $criteria = new CDbCriteria;
650:         $parameters = array('limit' => ceil($pageSize));
651:         $criteria->scopes = array('findAll' => array($parameters));
652:         $criteria->addCondition("(t.type!='invoice' and t.type!='dummyQuote') OR t.type IS NULL");
653: 
654:         return $this->searchBase($criteria, $pageSize);
655:     }
656: 
657:     public function searchInvoice() {
658:         $criteria = new CDbCriteria;
659:         $parameters = array('limit' => ceil(Profile::getResultsPerPage()));
660:         $criteria->scopes = array('findAll' => array($parameters));
661:         $criteria->addCondition("t.type='invoice'");
662: 
663:         return $this->searchBase($criteria);
664:     }
665: 
666:     public function getName () {
667:         if ($this->name == '') {
668:             return $this->id;
669:         } else {
670:             return $this->name;
671:         }
672:     }
673: 
674:     public function searchAdmin() {
675:         $criteria = new CDbCriteria;
676: 
677:         return $this->searchBase($criteria);
678:     }
679: 
680:     public function searchBase(
681:         $criteria, $pageSize=null, $showHidden = false) {
682: 
683:         return parent::searchBase($criteria, $pageSize, $showHidden);
684:     }
685: 
686:     /**
687:      * Get all active products indexed by their id,
688:      * and any inactive products still in this quote
689:      */
690:     public function productNames() {
691:         $products = Product::model()->findAll(
692:                 array(
693:                     'select' => 'id, name',
694:                     'condition' => 'status=:active',
695:                     'params' => array(':active' => 'Active'),
696:                 )
697:         );
698:         $productNames = array(0 => '');
699:         foreach ($products as $product)
700:             $productNames[$product->id] = $product->name;
701: 
702:         // get any inactive products in this quote
703:         $quoteProducts = QuoteProduct::model()->findAll(
704:                 array(
705:                     'select' => 'productId, name',
706:                     'condition' => 'quoteId=:quoteId',
707:                     'params' => array(':quoteId' => $this->id),
708:                 )
709:         );
710:         foreach ($quoteProducts as $qp)
711:             if (!isset($productNames[$qp->productId]))
712:                 $productNames[$qp->productId] = $qp->name;
713: 
714:         return $productNames;
715:     }
716: 
717:     public function productPrices() {
718:         $products = Product::model()->findAll(
719:                 array(
720:                     'select' => 'id, price',
721:                     'condition' => 'status=:active',
722:                     'params' => array(':active' => 'Active'),
723:                 )
724:         );
725:         $productPrices = array(0 => '');
726:         foreach ($products as $product)
727:             $productPrices[$product->id] = $product->price;
728: 
729:         // get any inactive products in this quote
730:         $quoteProducts = QuoteProduct::model()->findAll(
731:                 array(
732:                     'select' => 'productId, price',
733:                     'condition' => 'quoteId=:quoteId',
734:                     'params' => array(':quoteId' => $this->id),
735:                 )
736:         );
737:         foreach ($quoteProducts as $qp)
738:             if (!isset($productPrices[$qp->productId]))
739:                 $productPrices[$qp->productId] = $qp->price;
740: 
741:         return $productPrices;
742:     }
743: 
744:     public function activeProducts() {
745:         $products = Product::model()->findAllByAttributes(array('status' => 'Active'));
746:         $inactive = Product::model()->findAllByAttributes(array('status' => 'Inactive'));
747:         $quoteProducts = QuoteProduct::model()->findAll(
748:                 array(
749:                     'select' => 'productId',
750:                     'condition' => 'quoteId=:quoteId',
751:                     'params' => array(':quoteId' => $this->id),
752:                 )
753:         );
754:         foreach ($quoteProducts as $qp)
755:             foreach ($inactive as $i)
756:                 if ($qp->productId == $i->id)
757:                     $products[] = $i;
758:         return $products;
759:     }
760: 
761:     /**
762:      * Clear out records associated with this quote before deletion.
763:      */
764:     public function beforeDelete(){
765:         QuoteProduct::model()->deleteAllByAttributes(array('quoteId'=>$this->id));
766: 
767:         // for old relationships generated with incorrect type name
768:         Relationships::model()->deleteAllByAttributes(
769:             array('firstType' => 'quotes', 'firstId' => $this->id));
770: 
771:         // generate action record for history
772:         $contact = $this->contact;
773:         if(!empty($contact)){
774:             $action = new Actions;
775:             $action->associationType = 'contacts';
776:             $action->type = 'quotesDeleted';
777:             $action->associationId = $contact->id;
778:             $action->associationName = $contact->name;
779:             $action->assignedTo = Yii::app()->getSuModel()->username; 
780:             $action->completedBy = Yii::app()->getSuModel()->username;
781:             $action->createDate = time();
782:             $action->dueDate = time();
783:             $action->completeDate = time();
784:             $action->visibility = 1;
785:             $action->complete = 'Yes';
786:             $action->actionDescription = 
787:                 "Deleted Quote: <span style=\"font-weight:bold;\">{$this->id}</span> {$this->name}";
788:             // Save after deletion of the model so that this action itself doensn't get deleted
789:             $action->save(); 
790:         }
791:         return parent::beforeDelete();
792:     }
793: }
794: 
X2CRM Documentation API documentation generated by ApiGen 2.8.0