· 8 years ago · Dec 17, 2017, 06:58 AM
1<?php
2
3namespace app\models;
4
5use app\components\events\Mailer;
6use app\helpers\UserHelper;
7use kartik\mpdf\Pdf;
8use Yii;
9use yii\helpers\HtmlPurifier;
10use yii\db\ActiveRecord;
11use DateTime;
12use DateInterval;
13use yii\helpers\Json;
14use yii\helpers\ArrayHelper;
15use app\helpers\AdHelper;
16use app\helpers\PhotoHelper;
17use yii\helpers\Url;
18use yii\web\Cookie;
19use yii\db\Query;
20use yii\web\UploadedFile;
21use app\interfaces\NotificationInterface;
22use app\components\events\EmailEvent;
23use yii\base\UnknownPropertyException;
24use app\interfaces\AdminLogInterface;
25use app\components\events\AdminLogEvent;
26use yii\helpers\Inflector;
27use yii\helpers\Html;
28use yii\db\Expression;
29
30/**
31 * Ad ActiveRecord model.
32 *
33 * @property integer $id
34 * @property integer $status
35 * @property integer $user_id
36 * @property integer $type
37 * @property integer $subtype
38 * @property integer $usecase
39 * @property integer $floor
40 * @property string $name
41 * @property datetime $created_at
42 * @property datetime $updated_at
43 * @property datetime $published_at
44 * @property datetime $expires_at
45 * @property datetime $confirmed_at
46 * @property string $description
47 * @property integer $price
48 * @property integer $sale_price
49 * @property integer $price_type
50 * @property datetime $sale_finish
51 * @property string $sale_text
52 * @property string $spec_text
53 * @property boolean $is_vip
54 * @property string $contact_name
55 * @property string $last_name
56 * @property string $patronymic
57 * @property string $contact_phone
58 * @property string $contact_email
59 * @property integer $places_min
60 * @property integer $places_max
61 * @property float $area
62 * @property integer $bc_id
63 * @property integer $legal_contract_id
64 * @property integer $person_contract_id
65 * @property array $images
66 * @property array $files
67 * @property integer $state
68 * @property integer $rent_period
69 * @property string $entity
70 * @property boolean $service_post
71 * @property boolean $service_phone
72 * @property boolean $clerk_phone
73 * @property boolean $clerk_auto
74 * @property boolean $code_499
75 * @property boolean $code_495
76 * @property boolean $code_8800
77 * @property boolean $code_other
78 * @property integer $code_custom
79 * @property string $custom_contract
80 * @property string $custom_contract_name
81 *
82 * @property string $coverPhoto
83 * @property BusinessCenter $businessCenter
84 * @property LegalEntityContract $legalContract
85 * @property PersonContract $personContract
86 * @property boolean $isPublished
87 * @property boolean $isReadyForPublish
88 * @property OptionToAd[] $mainOptions
89 * @property OptionToAd[] $secondaryOptions
90 * @property array $options
91 * @property Ad[] $complexAds Ads that connected to complex ad
92 * @property array $formatedComplexAds
93 * @property array $connectList
94 * @property string $composition
95 * @property string $furnitureName
96 * @property integer $views
97 * @property boolean $inCompare
98 * @property boolean $inFav
99 * @property User $user
100 * @property boolean $isSale
101 * @property string $actualPrice
102 * @property string $oldPrice
103 * @property AdFurniture $furniture
104 * @property array $dealContract
105 */
106class Ad extends ActiveRecord implements NotificationInterface, AdminLogInterface
107{
108 use \app\traits\ActiveRecordTrait;
109
110 const STATUS_DRAFT = 0;
111 const STATUS_PUBLISHED = 1;
112 const STATUS_HIDDEN = 2;
113 const STATUS_DECLINED = 3;
114 const STATUS_DELETED = 4;
115 const STATUS_MODERATE = 5;
116
117 const TYPE_OFFICE = 1;
118 const TYPE_MEETING_ROOM = 2;
119 const TYPE_VIRTUAL = 3;
120 const TYPE_COMPLEX = 4;
121
122 const SUBTYPE_OFFICE = 1;
123 const SUBTYPE_WORKPLACE = 2;
124
125 const STATE_READY = 1;
126 const STATE_NEED_COSMETIC = 2;
127 const STATE_NEED_REPAIR = 3;
128
129 const USECASE_OFFICE = 1;
130 const USECASE_STORAGE = 2;
131 const USECASE_SHOWROOM = 3;
132
133 const PRICE_PERIOD_DAY = 1;
134 const PRICE_PERIOD_WEEK = 2;
135 const PRICE_PERIOD_MONTH = 3;
136 const PRICE_PERIOD_YEAR = 4;
137 const PRICE_PERIOD_HOUR = 5;
138
139 const LOCATION_OFFICE = 1;
140 const LOCATION_OPENSPACE = 2;
141
142 const CONTRACT_ENTITY_JUR = 'jur';
143 const CONTRACT_EMTITY_PERSON = 'prs';
144
145 const STORAGE_CONTRACT_DIR = '/storage/contract/';
146
147 /**
148 *
149 * @var array
150 */
151 public $furnitures;
152
153 /**
154 * Views of this ad
155 * @var integer
156 */
157 private $_views;
158
159 /**
160 * @inheritdoc
161 */
162 public function rules()
163 {
164 return [
165 ['status', 'default', 'value' => self::STATUS_DRAFT],
166 ['type', 'required'],
167 ['service_post', 'default', 'value' => 1],
168 ['service_phone', 'default', 'value' => 1],
169 ['type', 'in', 'range' => [
170 self::TYPE_OFFICE, self::TYPE_MEETING_ROOM, self::TYPE_VIRTUAL, self::TYPE_COMPLEX
171 ]],
172 ['status', 'in', 'range' => [
173 self::STATUS_DRAFT, self::STATUS_PUBLISHED, self::STATUS_HIDDEN,
174 self::STATUS_DECLINED, self::STATUS_DELETED, self::STATUS_MODERATE,
175 ]],
176 ['price_type', 'in', 'range' => [
177 self::PRICE_PERIOD_DAY,
178 self::PRICE_PERIOD_WEEK,
179 self::PRICE_PERIOD_MONTH,
180 self::PRICE_PERIOD_YEAR,
181 self::PRICE_PERIOD_HOUR,
182 ]],
183 ['usecase', 'in', 'range' => [
184 self::USECASE_OFFICE,
185 self::USECASE_STORAGE,
186 self::USECASE_SHOWROOM,
187 ]],
188 ['subtype', 'in', 'range' => [
189 self::SUBTYPE_OFFICE,
190 self::SUBTYPE_WORKPLACE,
191 ]],
192 ['state', 'in', 'range' => [
193 self::STATE_READY,
194 self::STATE_NEED_COSMETIC,
195 self::STATE_NEED_REPAIR,
196 ]],
197 ['location', 'in', 'range' => [
198 self::LOCATION_OFFICE,
199 self::LOCATION_OPENSPACE,
200 ]],
201 ['entity', 'in', 'range' => [
202 self::CONTRACT_ENTITY_JUR,
203 self::CONTRACT_EMTITY_PERSON,
204 ]],
205 ['rent_period', 'integer', 'min' => 1, 'max' => 15],
206 [['name',], 'string', 'length' => [3, 200]],
207 ['description', 'string', 'min' => 5],
208 ['description', function ($attribute, $params, $validator) {
209 if (strlen(strip_tags($this->$attribute)) > 2000) {
210 $this->addError($attribute, 'МакÑимум 2000 Ñимволов');
211 }
212 }],
213 [['description'], 'filter', 'filter' => function ($value) {
214 return HtmlPurifier::process($value, [
215 'HTML.Allowed' => 'p,ul,ol,li,b,strong,em,i,a[href],table,tbody,thead,td,tr,th',
216 ]);
217 }],
218 ['expires_at', 'date', 'format' => 'php:Y-m-d H:i:s'],
219 [['price', 'sale_price'], 'integer', 'min' => 0, 'max' => 2140000000],
220 ['sale_price', function ($attribute, $params) {
221 if ($this->$attribute >= $this->price) {
222 $this->addError($attribute, 'Скидка не может превышать или быть равной цене');
223 }
224 }],
225 ['sale_finish', 'date', 'format' => 'php:Y-m-d H:i:s'],
226 ['sale_text', 'string', 'length' => [1, 100]],
227 ['spec_text', 'string', 'length' => [1, 300]],
228 [['is_vip', 'service_post', 'service_phone', 'clerk_phone', 'clerk_auto', 'code_499', 'code_495', 'code_8800', 'code_other'], 'boolean'],
229 [['contact_name', 'last_name', 'patronymic'], 'string', 'length' => [1, 100]],
230 ['contact_email', 'email'],
231 ['contact_phone', 'string', 'length' => [1, 20]],
232 [
233 [
234 'places_min', 'places_max', 'price_type', 'bc_id', 'code_custom',
235 'subtype', 'usecase', 'floor', 'legal_contract_id', 'person_contract_id', 'state', 'location',
236 ],
237 'integer'
238 ],
239 ['area', 'double', 'max' => $this->maxArea],
240 [
241 [
242 'name', 'sale_text', 'spec_text', 'contact_name', 'contact_phone', 'last_name', 'patronymic'
243 ],
244 'filter',
245 'filter' => 'strip_tags'
246 ],
247 'requiredFields' => [['status', 'name', 'description', 'price', 'price_type', 'contact_name', 'contact_email', 'contact_phone', 'bc_id'],
248 'required', 'when' => function ($model) {
249 return $model->status == self::STATUS_PUBLISHED;
250 }],
251 ['furnitures', 'each', 'rule' => ['integer']],
252 [['images', 'files',], 'safe'],
253 [['custom_contract', 'custom_contract_name'], 'string'],
254 ];
255 }
256
257 /**
258 * @inheritdoc
259 */
260 public function attributeLabels()
261 {
262 return [
263 'id' => 'ID',
264 'type' => 'Тип',
265 'status' => 'СтатуÑ',
266 'name' => 'Ðазвание',
267 'created_at' => 'Создано',
268 'updated_at' => 'Обновлено',
269 'published_at' => 'Опубликовано',
270 'expires_at' => 'ИÑтекает',
271 'description' => 'ОпиÑание',
272 'price' => 'Цена',
273 'sale_price' => 'Скидка',
274 'price_type' => 'за',
275 'sale_finish' => 'Ñрок Ñкидки',
276 'sale_text' => 'Примечание к Ñкидке',
277 'spec_text' => 'Специальное предложение',
278 'is_vip' => 'VIP',
279 'contact_name' => 'ИмÑ',
280 'last_name' => 'ФамилиÑ',
281 'patronymic' => 'ОтчеÑтво',
282 'contact_phone' => 'Телефон',
283 'contact_email' => 'Email',
284 'places_min' => 'Рабочих меÑÑ‚',
285 'places_max' => 'ВмеÑтимоÑть',
286 'area' => 'Площадь, м2',
287 'images' => 'Фотографии',
288 'files' => 'Файлы',
289 'location' => 'РаÑположение',
290 'state' => 'СоÑтоÑние',
291 'floor' => 'Ðтаж',
292 'subtype' => 'Подтип',
293 'usecase' => 'Ðазначение',
294 'rent_period' => 'Срок аренды',
295 'bc_id' => 'БизнеÑ-центр',
296 'entity' => 'Тип договора',
297 'service_post' => 'Почтовое обÑлуживание',
298 'service_phone' => 'Телефонное обÑлуживание',
299 'clerk_phone' => 'Секретарь',
300 'clerk_auto' => 'ÐвтоматичеÑкое приветÑтвие',
301 'code_499' => 'Код 499',
302 'code_495' => 'Код 495',
303 'code_8800' => 'Код 8800',
304 'furniture' => 'РаÑÑтановка мебели',
305 'custom_contract' => 'Файл договора',
306 'custom_contract_name' => 'Отображаемое Ð¸Ð¼Ñ Ð´Ð¾Ð³Ð¾Ð²Ð¾Ñ€Ð°',
307 ];
308 }
309
310 /**
311 * @inheritdoc
312 * @return AdQuery the active query used by this AR class.
313 */
314 public static function find()
315 {
316 return new AdQuery(static::class);
317 }
318
319 /** @inheritdoc */
320 public function beforeSave($insert)
321 {
322 if (parent::beforeSave($insert)) {
323 $now = new DateTime('NOW');
324 $this->updated_at = $now->format('Y-m-d H:i:s');
325 if ($this->isAttributeChanged('status', false)) {
326 if ($this->status == self::STATUS_PUBLISHED) {
327 $moderation = Settings::findOneByAlias('global_moderation')['value'];
328 if (!Yii::$app->request->isConsoleRequest && $moderation
329 && !(User::isAdmin() || UserHelper::isPartner()
330 || UserHelper::isModerator() || UserHelper::isManager()
331 )
332 ) {
333 $this->status = self::STATUS_MODERATE;
334 } else {
335 $this->updatePublishInterval();
336 }
337 } elseif ($this->status == self::STATUS_HIDDEN) {
338 $this->expires_at = $now->format('Y-m-d H:i:s');
339 }
340 } elseif ($this->status == self::STATUS_PUBLISHED && $this->expires_at < date('Y-m-d H:i:s')) {
341 $this->updatePublishInterval();
342 }
343 if (!$this->user_id) {
344 $this->user_id = Yii::$app->user->id;
345 }
346 if ($this->images && is_array($this->images)) {
347 $this->images = Json::encode(array_values($this->images));
348 }
349 if ($this->files && is_array($this->files)) {
350 $this->files = Json::encode(array_values($this->files));
351 }
352 return true;
353 } else {
354 return false;
355 }
356 }
357
358 /**
359 * Set new values to published and expires values
360 */
361 protected function updatePublishInterval()
362 {
363 $now = new DateTime('NOW');
364 $this->published_at = $now->format('Y-m-d H:i:s');
365 $this->expires_at = $now->add(new DateInterval('P1M'))->format('Y-m-d H:i:s');
366 }
367
368 /** @inheritdoc */
369 public function afterSave($insert, $changedAttributes)
370 {
371 Yii::$app->cache->delete('allCounters');
372 //Save furniture for meeting room
373 if ($this->type == self::TYPE_MEETING_ROOM) {
374 $this->saveFurniture();
375 }
376
377 $changed = false;
378 $filteredValues = [];
379 $filtersInCache = ['price', 'places_min', 'places_max', 'area', 'sale_price', 'sale_finish'];
380 foreach ($changedAttributes as $attr => $value) {
381 if ($attr != 'updated_at' && $this->$attr != $value) {
382 $changed = true;
383 break;
384 }
385 $filteredValues[$attr] = $value;
386 }
387 if ($changed) {
388 if (isset($changedAttributes['status']) && $changedAttributes['status'] == self::STATUS_MODERATE && $this->status == self::STATUS_PUBLISHED) {
389 (new Notification([
390 'title' => Notification::getTypeLabels(Notification::TYPE_AD_PUBLISHED),
391 'status' => Notification::STATUS_LIVE,
392 'user_id' => $this->user_id,
393 'type' => Notification::TYPE_AD_PUBLISHED,
394 'data' => Json::encode(['adId' => $this->id]),
395 ]))->save();
396 }
397
398 //Favorites changed processing
399 $favs = Favorite::find()->select('user_id')->where(['ad' => $this->id])->asArray()->all();
400 if ($favs) {
401 foreach ($favs as $fav) {
402 $data = [
403 'user_id' => $fav['user_id'],
404 'data' => Json::encode(['adId' => $this->id]),
405 'type' => Notification::TYPE_FAV_CHANGED,
406 ];
407 if (!Notification::find()->where(array_merge($data, ['status' => Notification::STATUS_HIDDEN]))->exists()) {
408 (new Notification(array_merge($data, [
409 'title' => Notification::getTypeLabels(Notification::TYPE_FAV_CHANGED),
410 'send_at' => YII_ENV_DEV ? time() : time() + 600,
411 ])))->save();
412 }
413 }
414 }
415 if (!Yii::$app->request->isConsoleRequest
416 && isset($changedAttributes['status']) && $this->status == self::STATUS_MODERATE
417 && Settings::findOneByAlias('global_moderation')['value']
418 && $changedAttributes['status'] != self::STATUS_MODERATE
419 && !(User::isAdmin() || UserHelper::isPartner()
420 || UserHelper::isModerator() || UserHelper::isManager())
421 ) {
422 $emails = explode(',', Settings::findOneByAlias('contact_admin_emails')['value']);
423 foreach ($emails as $email) {
424 $this->trigger(NotificationInterface::EVENT_EMIT_EMAIL_BY_MANDRILL, new Mailer([
425 'message' => [
426 'to' => [
427 ['email' => trim($email)]
428 ],
429 'global_merge_vars' => [
430 [
431 'name' => 'data',
432 'content' => [
433 'name' => $this->contact_name,
434 'email' => $this->contact_email,
435 'ad' => $this->name,
436 'link' => Url::to([
437 '/offer/moderate', 'id' => $this->id
438 ], true)
439 ]
440 ]
441 ]
442 ],
443 'templateName' => Mailer::TEMPLATE_NEW_AD
444 ]));
445 }
446 }
447
448 //Premoderation processing
449 if (!Yii::$app->request->isConsoleRequest
450 && Settings::findOneByAlias('global_moderation')['value']
451 && !(User::isAdmin() || UserHelper::isPartner()
452 || UserHelper::isModerator() || UserHelper::isManager())
453 && isset($changedAttributes['status'])
454 && $this->status != self::STATUS_DRAFT
455 ) {
456 $this->updateAttributes(['status' => self::STATUS_MODERATE]);
457 }
458
459 //Admin action processing
460 if (Yii::$app->controller->action->id == 'moderate') {
461 //Logging
462 $this->trigger(AdminLogInterface::EVENT_ADMIN_ACTION, new AdminLogEvent([
463 'sender' => $this,
464 'logString' => 'ОбъÑвление обновлено. ',
465 'labels' => $this->attributeLabels(),
466 'changedAttributes' => $changedAttributes,
467 'labelsStorage' => '\app\helpers\AdHelper',
468 ]));
469
470 /**
471 * @todo Inform user about publishing his ad
472 */
473 }
474 }
475 $needleAttributes = [
476 'type', 'name', 'price', 'sale_price', 'sale_finish', 'sale_text',
477 'places_min', 'places_max', 'area', 'furniture', 'service_post', 'service_phone',
478 'clerk_phone', 'clerk_auto', 'code_499', 'code_495', 'code_8080'
479 ];
480 $checkboxAttributes = ['service_post', 'service_phone', 'clerk_phone', 'clerk_auto', 'code_499', 'code_495', 'code_8800'];
481 $logs = [];
482 $isCacheUpdated = false;
483 foreach ($changedAttributes as $attribute => $value) {
484 if ($value != $this->$attribute) {
485 if (in_array($attribute, $needleAttributes)) {
486 if ($attribute == 'type') {
487 $newValue = AdHelper::getOfficeTypeLabels($this->$attribute);
488 $value = $value ? AdHelper::getOfficeTypeLabels($value) : null;
489 } else {
490 $newValue = $this->$attribute;
491 }
492 $fromValue = $value ? ' c ' . $value : '';
493 if (in_array($attribute, $checkboxAttributes)) {
494 $logs[] = $this->getAttributeLabel($attribute) . ($newValue ? ' включено' : ' не включено');
495 } else {
496 $logs[] = 'Значение ' . $this->getAttributeLabel($attribute) . ' изменилоÑÑŒ' . $fromValue . ' на ' . $newValue;
497 }
498 }
499 if (in_array($attribute, $filtersInCache) && !$isCacheUpdated) {
500 Yii::$app->cache->delete('filterLimits' . $this->type);
501 $isCacheUpdated = true;
502 }
503 }
504 }
505 if ($logs && in_array($this->status, [self::STATUS_PUBLISHED, self::STATUS_MODERATE])) {
506 $adInFavorites = Favorite::find()->where(['user_id' => $this->user_id])->andWhere(['ad' => $this->id]);
507 if ($adInFavorites->exists()) {
508 $model = AdChanges::findOne(['ad_id' => $this->id, 'is_favorite' => true]) ?: new AdChanges([
509 'ad_id' => $this->id,
510 'user_id' => $this->user_id,
511 'is_favorite' => true
512 ]);
513 $model->ad_changes = Json::encode($logs);
514 $model->save();
515 }
516 $model = AdChanges::findOne(['ad_id' => $this->id, 'is_favorite' => false]) ?: new AdChanges([
517 'ad_id' => $this->id,
518 'user_id' => $this->user_id,
519 'is_favorite' => false
520 ]);
521 $model->ad_changes = Json::encode($logs);
522 $model->save();
523 }
524
525 parent::afterSave($insert, $changedAttributes);
526 }
527
528 /**
529 * @return array
530 */
531 public function getChangedFurnitureNames()
532 {
533 $setting = Settings::findByValue($this->furnitures);
534 return ArrayHelper::getColumn($setting, 'value');
535 }
536
537 /**
538 * Add to favorite_change table changes in furniture
539 * @param null|array $newValue
540 */
541 private function setFurnitureChanges($newValue = null)
542 {
543 if (in_array($this->status, [self::STATUS_PUBLISHED, self::STATUS_MODERATE])) {
544 $newValue = $newValue ? Json::encode($newValue) : null;
545 $userInFavorites = Favorite::find()->where(['user_id' => $this->user_id])->andWhere(['ad' => $this->id]);
546 if ($userInFavorites->exists()) {
547 $model = AdChanges::findOne(['ad_id' => $this->id, 'is_favorite' => true]) ?: new AdChanges([
548 'ad_id' => $this->id,
549 'user_id' => $this->user_id,
550 'is_favorite' => true
551 ]);
552 $model->furniture_changes = $newValue;
553 $model->save();
554 }
555 $model = AdChanges::findOne(['ad_id' => $this->id, 'is_favorite' => false]) ?: new AdChanges([
556 'ad_id' => $this->id,
557 'user_id' => $this->user_id,
558 'is_favorite' => false
559 ]);
560 $model->furniture_changes = $newValue;
561 $model->save();
562 }
563 }
564 /**
565 * Save furniture to ad
566 * @throws \Exception
567 */
568 protected function saveFurniture()
569 {
570 if (!$this->furnitures) {
571 $this->setFurnitureChanges();
572 }
573 $furnitureNames = implode('/', $this->getChangedFurnitureNames());
574 $savedFurniture = ArrayHelper::getColumn($this->furniture, 'furniture_id');
575 if ($this->furnitures && (array_diff($savedFurniture, $this->furnitures) || array_diff($this->furnitures, $savedFurniture))) {
576 $furnitureChanges[] = $furnitureNames;
577 $this->setFurnitureChanges($furnitureChanges);
578 $transaction = Yii::$app->db->beginTransaction();
579 try {
580 AdFurniture::deleteAll(['ad_id' => $this->id]);
581 foreach ($this->furnitures as $furniture) {
582 $model = new AdFurniture([
583 'ad_id' => $this->id,
584 'furniture_id' => $furniture,
585 ]);
586 if (!$model->save()) {
587 throw new \Exception;
588 }
589 }
590 $transaction->commit();
591 } catch (\Exception $e) {
592 $transaction->rollBack();
593 }
594 }
595 }
596
597 /**
598 * @inheritdoc
599 */
600 public function init()
601 {
602 parent::init();
603 if (!Yii::$app->request->isConsoleRequest && !Yii::$app->user->isGuest) {
604 $user = Yii::$app->user->identity;
605 if (!$this->contact_name) {
606 $this->contact_name = $user->profile->name;
607 $this->last_name = $user->profile->last_name;
608 $this->patronymic = $user->profile->patronymic;
609 }
610 if (!$this->contact_phone) {
611 $this->contact_phone = $user->profile->phone;
612 }
613 if (!$this->contact_email) {
614 $this->contact_email = $user->email;
615 }
616 }
617
618 }
619
620 /**
621 * @inheritdoc
622 */
623 public function beforeValidate()
624 {
625 $this->price_type = $this->type == self::TYPE_MEETING_ROOM ? self::PRICE_PERIOD_HOUR : self::PRICE_PERIOD_MONTH;
626 if (is_array($this->rent_period)) {
627 $this->rent_period = $this->rent_period['id'];
628 }
629 if ($this->sale_finish) {
630 $date = new DateTime($this->sale_finish);
631 //Hot fix. Need to be fixed in angular
632 $this->sale_finish = $date->add(new DateInterval('P1D'))->format('Y-m-d H:i:s');
633 }
634 if (!Yii::$app->request->isConsoleRequest) {
635 $this->saveBusinessCenter();
636 if ($this->entity) {
637 if ($this->entity == self::CONTRACT_ENTITY_JUR) {
638 $this->saveLegalContract();
639 } elseif ($this->entity == self::CONTRACT_EMTITY_PERSON) {
640 $this->savePersonalContract();
641 }
642 }
643 $this->saveOptions();
644 }
645 $file = UploadedFile::getInstance($this, 'custom_contract');
646 if ($file) {
647 $fileHash = md5_file($file->tempName);
648 $folder = Yii::getAlias('@webroot') . DefaultDealContractForm::CONTRACT_DIR;
649 if (!file_exists($folder)) {
650 mkdir($folder, 0777, true);
651 }
652 $file->saveAs($folder . $fileHash . '.' . $file->extension);
653 $this->custom_contract = $fileHash . '.' . $file->extension;
654 }
655 return parent::beforeValidate();
656 }
657
658 /**
659 * Create business center record
660 */
661 protected function saveBusinessCenter()
662 {
663 if (!$this->bc_id
664 || !($center = BusinessCenter::findOne($this->bc_id))
665 || $center->address != Yii::$app->request->post('BusinessCenter')['address']
666 ) {
667 $center = new BusinessCenter();
668 }
669 if ($center->status != BusinessCenter::STATUS_MAJOR_PUBLISHED && $center->load(Yii::$app->request->post())) {
670 $center->status = BusinessCenter::STATUS_MINOR_PUBLISHED;
671 if ($center->save()) {
672 $this->bc_id = $center->id;
673 } else {
674 $this->addErrors($center->errors);
675 }
676 }
677
678 }
679
680 /**
681 * Save contract
682 */
683 protected function saveLegalContract()
684 {
685 if (!$this->legal_contract_id || !($contract = LegalEntityContract::findOne($this->legal_contract_id))) {
686 $contract = new LegalEntityContract();
687 }
688 if ($contract->load(Yii::$app->request->post())) {
689 //We should set owner to null because we can have only 5 contracts in our profile
690 $contract->owner = null;
691 if ($contract->save()) {
692 $this->legal_contract_id = $contract->id;
693 } else {
694 $this->addErrors($contract->errors);
695 }
696 }
697
698 }
699
700 /**
701 * Save person contract
702 */
703 protected function savePersonalContract()
704 {
705 if ($this->person_contract_id) {
706 $contract = PersonContract::findOne($this->person_contract_id);
707 } else {
708 $contract = new PersonContract();
709 }
710 if ($contract->load(Yii::$app->request->post())) {
711 //We should set owner to null because we can have only 5 contracts in our profile
712 $contract->owner_user = null;
713 if ($contract->save()) {
714 $this->person_contract_id = $contract->id;
715 } else {
716 $this->addErrors($contract->errors);
717 }
718 }
719 }
720
721 /**
722 * Raw version of saving ad's options
723 * @return boolean
724 */
725 protected function saveOptions()
726 {
727 if (!($options = Yii::$app->request->post('options'))) {
728 return false;
729 }
730
731 $userInFavorites = Favorite::find()->where(['user_id' => $this->user_id])->andWhere(['ad' => $this->id]);
732 $optionIds = ArrayHelper::getColumn($options, 'id');
733 $optionsToAd = OptionToAd::find()->where(['id' => $optionIds])->indexBy('id')->all();
734 $newOptionIds = [];
735 foreach ($options as $item) {
736 if ($item['include_type'] == OptionToAd::INCLUDE_IN_PRICE || $item['include_type'] == OptionToAd::INCLUDE_ADDITIONAL_PRICE) {
737 if (array_key_exists('optionId', $item)) {
738 $newOptionIds[] = $item['optionId'];
739 } else {
740 $newOptionIds[] = $item['id'];
741 }
742 }
743 }
744
745 $oldOptionToAdIds = OptionToAd::find()
746 ->select('option_id')
747 ->where(['ad_id' => $this->id])
748 ->andWhere(['include_type' => [OptionToAd::INCLUDE_IN_PRICE, OptionToAd::INCLUDE_ADDITIONAL_PRICE]])
749 ->all();
750
751 $oldOptionIds = ArrayHelper::getColumn($oldOptionToAdIds, 'option_id');
752 $addedOptions = array_diff($newOptionIds, $oldOptionIds);
753
754 $arrayLog = [];
755 if ($addedOptions) {
756 $optionNames = Option::find()
757 ->select('name')
758 ->where(['id' => $addedOptions])
759 ->all();
760 foreach ($optionNames as $option) {
761 $arrayLog[] = $option->name;
762 }
763 }
764 if (in_array($this->status, [self::STATUS_PUBLISHED, self::STATUS_MODERATE])) {
765 if ($userInFavorites->exists()) {
766 AdChanges::saveOptionChanges(true, $arrayLog, $this->id, $this->user_id);
767 }
768 AdChanges::saveOptionChanges(false, $arrayLog, $this->id, $this->user_id);
769 }
770
771 foreach ($options as $optionData) {
772 $option = isset($optionsToAd[$optionData['id']]) ? $optionsToAd[$optionData['id']] : (new OptionToAd);
773 $option->ad_type = $this->type;
774 $option->ad_id = $this->id;
775 if ($option->load(['OptionToAd' => $optionData])) {
776 if (!$option->save()) {
777 $this->addErrors($option->errors);
778 }
779 }
780 $processedIds[] = $option->id;
781 }
782
783 //Delete others
784 $toDelete = OptionToAd::find()
785 ->joinWith('option')
786 ->where([Option::tableName() . '.type' => Option::TYPE_ADDITIONAL])
787 ->andWhere(['not', [OptionToAd::tableName() . '.id' => $processedIds]])
788 ->andWhere(['ad_id' => $this->id])
789 ->all();
790 if ($toDelete) {
791 OptionToAd::deleteAll(['id' => ArrayHelper::getColumn($toDelete, 'id')]);
792 }
793 }
794
795 /**
796 * Get min and max values for ad with selected type
797 * On dev we have only test data and caching interfere in tests
798 * @param integer $type
799 * @return array
800 * @throws \yii\base\InvalidParamException
801 */
802 public static function getLimits($type)
803 {
804 if (!in_array($type, [self::TYPE_OFFICE, self::TYPE_MEETING_ROOM, self::TYPE_VIRTUAL, self::TYPE_COMPLEX])) {
805 throw new \yii\base\InvalidParamException('Invalid type for limits');
806 }
807 if (($limits = Yii::$app->cache->get('filterLimits' . $type)) === false || YII_ENV_DEV) {
808 $limits = (new Query)
809 ->select([
810 'minPrice' => new Expression('MIN(IF(' . Ad::field('sale_finish') . ' >= NOW(), '
811 . Ad::field('price') . ' - ' . Ad::field('sale_price') . ','
812 . Ad::field('price') . '))'),
813 'maxPrice' => 'MAX(price)',
814 'minPlace' => 'MIN(places_min)',
815 'maxPlace' => 'MAX(places_max)',
816 'minArea' => 'MIN(area)',
817 'maxArea' => 'MAX(area)',
818 ])
819 ->from(self::tableName())
820 ->where([
821 'and',
822 [self::field('status') => self::STATUS_PUBLISHED],
823 [self::field('type') => $type],
824 ['>=', self::field('expires_at'), new Expression('NOW()')],
825 ])
826 ->one();
827 $limits = array_combine(array_keys($limits), array_map(function ($key, $item) {
828 switch ($key) {
829 case 'minArea':
830 $item = round($item);
831 break;
832 case 'maxArea':
833 $item = ceil($item);
834 break;
835 }
836 return $item;
837 }, array_keys($limits), $limits));
838 Yii::$app->cache->set('filterLimits' . $type, $limits, 3600);
839 }
840 return $limits;
841 }
842
843 /**
844 * Returns link to cover photo
845 * @return string
846 */
847 public function getCoverPhoto($thumb = false)
848 {
849 if ($this->images) {
850 $photos = Json::decode($this->images);
851 $file = array_shift($photos)['file'];
852 if ($thumb) {
853 $file = str_replace(PhotoHelper::DIR_PHOTO, PhotoHelper::DIR_PHOTO . 'thumb/', $file);
854 }
855 return $file && file_exists(Yii::getAlias('@webroot'). $file) ? $file : '/static/img/nophoto_office.svg';
856 }
857 return '/static/img/nophoto_office.svg';
858 }
859
860 /**
861 *
862 * @return string
863 */
864 public function getStateDescription()
865 {
866 if ($this->state) {
867 return AdHelper::getStateLabels($this->state);
868 }
869 return '';
870 }
871
872 /**
873 * @return BusinessCenter
874 */
875 public function getBusinessCenter()
876 {
877 return $this->hasOne(BusinessCenter::className(), ['id' => 'bc_id']);
878 }
879
880 /**
881 * @return LegalEntityContract
882 */
883 public function getLegalContract()
884 {
885 return $this->hasOne(LegalEntityContract::className(), ['id' => 'legal_contract_id']);
886 }
887
888 /**
889 * @return PersonContract
890 */
891 public function getPersonContract()
892 {
893 return $this->hasOne(PersonContract::className(), ['id' => 'person_contract_id']);
894 }
895
896 /**
897 *
898 * @return boolean
899 */
900 public function getIsPublished()
901 {
902 return $this->status == self::STATUS_PUBLISHED && $this->expires_at > date('Y-m-d H:i:s');
903 }
904
905 /**
906 *
907 * @return boolean
908 */
909 public function getIsReadyForPublish()
910 {
911 foreach ($this->rules()['requiredFields'][0] as $attr) {
912 if (!$this->$attr && $attr != 'status') {
913 return false;
914 }
915 }
916 return true;
917 }
918
919 /**
920 * @return OptionToAd[]
921 */
922 public function getOptionObjects()
923 {
924 return $this->hasMany(OptionToAd::className(), ['ad_id' => 'id'])->joinWith('option');
925 }
926
927 /**
928 * Returns main options
929 * @return OptionToAd[]
930 */
931 public function getIncludedMainOptions()
932 {
933 return $this->getOptionObjects()
934 ->andWhere([Option::field('type') => Option::TYPE_BASE]);
935 }
936
937 /**
938 * Returns main options except options with additional price
939 * @return OptionToAd[]
940 */
941 public function getMainOptions()
942 {
943 return $this->getOptionObjects()
944 ->where(['not', [OptionToAd::field('include_type') => OptionToAd::INCLUDE_ADDITIONAL_PRICE]])
945 ->andWhere([Option::field('type') => Option::TYPE_BASE]);
946 }
947
948 /**
949 * Returns secondary options
950 * @return OptionToAd[]
951 */
952 public function getSecondaryOptions()
953 {
954 return $this->getOptionObjects()
955 ->where([OptionToAd::field('include_type') => OptionToAd::INCLUDE_ADDITIONAL_PRICE])
956 ->orWhere([Option::field('type') => Option::TYPE_ADDITIONAL]);
957 }
958
959 /**
960 * @return integer
961 */
962 public function getSecondaryOptionsExist()
963 {
964 return $this->getSecondaryOptions()->exists();
965 }
966
967 /**
968 * @return integer
969 */
970 public function getMainOptionsExist()
971 {
972 return $this->getMainOptions()
973 ->andWhere(['not', [OptionToAd::field('include_type') => OptionToAd::INCLUDE_NOT]])
974 ->exists();
975 }
976
977 /**
978 * Formats array of options for advertisment
979 * @return array
980 */
981 public function getOptions()
982 {
983 $optionsToAd = OptionToAd::find()->where(['ad_id' => $this->id])->with('option')->all();
984 if (!$optionsToAd) {
985 $baseOptions = Option::findAll([
986 'type' => [Option::TYPE_BASE, Option::TYPE_ADDITIONAL_ADMIN],
987 'status' => Option::STATUS_ACTIVE,
988 'for' => $this->type
989 ]);
990 foreach ($baseOptions as $baseOption) {
991 $newOptionToAd = new OptionToAd([
992 'option_id' => $baseOption->id,
993 'ad_id' => $this->id,
994 'include_type' => OptionToAd::INCLUDE_NOT,
995 'option_price_period' => OptionToAd::PRICE_PERIOD_MONTHLY,
996 ]);
997 if ($newOptionToAd->save()) {
998 $optionsToAd[] = $newOptionToAd;
999 }
1000 }
1001 }
1002 return ArrayHelper::toArray($optionsToAd, [
1003 'app\models\OptionToAd' => [
1004 'id',
1005 'name' => 'option.name',
1006 'optionId' => 'option.id',
1007 'include_type',
1008 'description',
1009 'price',
1010 'option_price_period',
1011 'formatedPrice' => function ($data) {
1012 return number_format($data->price, 0, ',', ' ')
1013 . ($data->option_price_period ? ' Ð /'
1014 . OptionToAd::getPaymentLabels($data->option_price_period) : '');
1015 },
1016 'old' => function ($model) {
1017 return true;
1018 },
1019 'is_main' => function ($model) {
1020 return $model->option->type == Option::TYPE_BASE;
1021 }
1022 ]
1023 ]);
1024 }
1025
1026 /**
1027 * Clone options for Ad
1028 * @param integer $oldAdId Id of initial Ad
1029 */
1030 public function cloneOptions($oldAdId)
1031 {
1032 $optionsToAd = OptionToAd::findAll(['ad_id' => $oldAdId]);
1033 foreach ($optionsToAd as $option) {
1034 $option->isNewRecord = true;
1035 $option->id = null;
1036 $option->ad_id = $this->id;
1037 $option->save();
1038 }
1039 }
1040
1041 /**
1042 * Clone contacts
1043 */
1044 public function cloneContracts()
1045 {
1046 $contractsObjects = [
1047 'legal_contract_id' => 'legalContract',
1048 'person_contract_id' => 'personContract',
1049 ];
1050 foreach ($contractsObjects as $id => $objName) {
1051 if ($this->$objName) {
1052 $this->$objName->isNewRecord = true;
1053 $this->$objName->id = null;
1054 $this->$objName->created_at = null;
1055 if ($this->$objName->save()) {
1056 $this->$id = $this->$objName->id;
1057 }
1058 }
1059 }
1060 }
1061
1062 /**
1063 * @return boolean
1064 */
1065 public function getIsSale()
1066 {
1067 return $this->sale_price && $this->sale_finish >= date('Y-m-d H:i:s');
1068 }
1069
1070 /**
1071 * @return \yii\db\ActiveQuery
1072 */
1073 public function getComplexAds()
1074 {
1075 return $this->hasMany(Ad::className(), ['id' => 'ad'])->viaTable(ComplexAd::tableName(), ['complex' => 'id']);
1076 }
1077
1078 /**
1079 * @return array
1080 */
1081 public function getFormatedComplexAds()
1082 {
1083 return AdHelper::getFormatResult($this->complexAds);
1084 }
1085
1086 /**
1087 * Get list of ads that we can connect to complex ad
1088 * @return Ad[]
1089 */
1090 public function getConnectList()
1091 {
1092 if (count($this->complexAds) >= 3) {
1093 return null;
1094 }
1095 $condition = [
1096 'and',
1097 ['user_id' => $this->user_id],
1098 ['status' => self::STATUS_PUBLISHED],
1099 ['not', ['type' => self::TYPE_COMPLEX]],
1100 ];
1101 if ($this->complexAds) {
1102 $condition[] = ['bc_id' => $this->complexAds[0]->bc_id];
1103 $condition[] = ['not', ['type' => ArrayHelper::getColumn($this->complexAds, 'type')]];
1104 }
1105 return Ad::find()->where($condition)->all();
1106 }
1107
1108 /**
1109 * Clone relations for complex ads
1110 * @param integer $oldId
1111 */
1112 public function cloneComplexAds($oldId)
1113 {
1114 $ads = ComplexAd::findAll(['complex' => $oldId]);
1115 if ($ads) {
1116 $rows = [];
1117 foreach ($ads as $ad) {
1118 $rows[] = [$this->id, $ad->ad];
1119 }
1120 Yii::$app->db->createCommand()->batchInsert(ComplexAd::tableName(), ['complex', 'ad'], $rows)->execute();
1121 }
1122 }
1123
1124 /**
1125 * Formating composition string for virtual office
1126 * @return string
1127 */
1128 public function getComposition()
1129 {
1130 $data = [];
1131 $fields = [
1132 'service_post' => 'Почтовое обÑлуживание',
1133 'service_phone' => 'Телефонное обÑлуживание',
1134 'clerk_phone' => 'Живой Ñекретарь',
1135 'clerk_auto' => 'ÐвтоматичеÑкое приветÑтвие',
1136 'code_499' => 'Ðомер в коде 499',
1137 'code_495' => 'Ðомер в коде 495',
1138 'code_8800' => 'Ðомер в коде 8800',
1139 ];
1140 $data = array_map(function ($key, $value) {
1141 if ($this->$key) {
1142 return $value;
1143 }
1144 }, array_keys($fields),
1145 array_values($fields));
1146 return implode(' + ', array_filter($data));
1147 }
1148
1149 /**
1150 * Retturn furniture type name
1151 * @return string
1152 */
1153 public function getFurnitureName()
1154 {
1155 $setting = Settings::findByValue(ArrayHelper::getColumn($this->furniture, 'furniture_id'));
1156 if (!$setting) {
1157 return 'не выбрано';
1158 }
1159 return implode('/', ArrayHelper::getColumn($setting, 'value'));
1160 }
1161
1162 /**
1163 * Count and return all views for this ad
1164 * @return integer
1165 */
1166 public function getViews()
1167 {
1168 if (!$this->_views) {
1169 $this->_views = View::find()
1170 ->where([View::field('ad') => $this->id])
1171 ->sum(View::field('views'));
1172 }
1173 return (int)$this->_views;
1174 }
1175
1176 /**
1177 * Save views for this ad
1178 * @return boolean
1179 */
1180 public function saveView()
1181 {
1182 //Some metrics try access page with ajax after we finish normal request
1183 if (Yii::$app->request->isAjax) {
1184 return false;
1185 }
1186 $ids = Yii::$app->request->cookies->getValue('watched', []);
1187 $ids[] = $this->id;
1188 Yii::$app->response->cookies->add(new Cookie(['name' => 'watched', 'value' => array_unique($ids)]));
1189 if ($this->user_id == Yii::$app->user->id) {
1190 return true;
1191 }
1192 //If user is guest we wright all they views in one record for this ad
1193 $userId = Yii::$app->user->id ?: -1;
1194 $view = View::findOne(['user_id' => $userId, 'ad' => $this->id]);
1195 if (!$view) {
1196 $view = new View(['user_id' => $userId, 'ad' => $this->id, 'views' => 0]);
1197 }
1198 ++$view->views;
1199 return $view->save();
1200 }
1201
1202 /**
1203 * Check if ad in compare list
1204 * @return boolean
1205 */
1206 public function getInCompare()
1207 {
1208 $storedData = Yii::$app->request->cookies->getValue('compareList', false);
1209 if (!$storedData
1210 || !isset($storedData[$this->type])
1211 || array_search($this->id, $storedData[$this->type]) === false
1212 ) {
1213 return false;
1214 }
1215 return true;
1216 }
1217
1218 /**
1219 * Delete record from compare list
1220 * @return boolean
1221 */
1222 public function deleteFromCompare()
1223 {
1224 $storedData = Yii::$app->request->cookies->getValue('compareList', []);
1225 if (isset($storedData[$this->type]) && ($key = array_search($this->id, $storedData[$this->type])) !== false) {
1226 unset($storedData[$this->type][$key]);
1227 return $storedData;
1228 }
1229 return false;
1230 }
1231
1232 /**
1233 * Add ad to favorites
1234 * @return boolean|integer
1235 */
1236 public function addToFavorites()
1237 {
1238 if (Yii::$app->user->isGuest) {
1239 $storedData = Yii::$app->request->cookies->getValue('favList', []);
1240 if (array_search($this->id, $storedData) !== false) {
1241 return false;
1242 }
1243 $storedData[] = $this->id;
1244 Yii::$app->response->cookies->add(new Cookie(['name' => 'favList', 'value' => $storedData]));
1245 return count($storedData);
1246 }
1247 $fav = Favorite::findOne(['user_id' => Yii::$app->user->id, 'ad' => $this->id]);
1248 if ($fav || !(new Favorite(['user_id' => Yii::$app->user->id, 'ad' => $this->id]))->save()) {
1249 return false;
1250 }
1251 return Favorite::find()->where(['user_id' => Yii::$app->user->id])->count();
1252 }
1253
1254 /**
1255 * Delete ad from favorites
1256 * @return boolean|integer
1257 */
1258 public function deleteFromFav()
1259 {
1260 if (Yii::$app->user->isGuest) {
1261 $storedData = Yii::$app->request->cookies->getValue('favList', []);
1262 if (($key = array_search($this->id, $storedData)) !== false) {
1263 unset($storedData[$key]);
1264 }
1265 $storedData = array_unique($storedData);
1266 Yii::$app->response->cookies->add(new Cookie(['name' => 'favList', 'value' => $storedData]));
1267 return count($storedData);
1268 }
1269 $fav = Favorite::findOne(['user_id' => Yii::$app->user->id, 'ad' => $this->id]);
1270 if (!$fav || !$fav->delete()) {
1271 return false;
1272 }
1273 return Favorite::find()->where(['user_id' => Yii::$app->user->id])->count();
1274 }
1275
1276 /**
1277 * Check if ad in favorite list
1278 * @return boolean
1279 */
1280 public function getInFav()
1281 {
1282 if (Yii::$app->user->isGuest) {
1283 $storedData = Yii::$app->request->cookies->getValue('favList', []);
1284 if (($key = array_search($this->id, $storedData) !== false)) {
1285 return true;
1286 }
1287 return false;
1288 }
1289 return Favorite::find()->where(['user_id' => Yii::$app->user->id, 'ad' => $this->id])->exists();
1290 }
1291
1292 /**
1293 * @return \yii\db\ActiveQuery
1294 */
1295 public function getUser()
1296 {
1297 return $this->hasOne(User::className(), ['id' => 'user_id']);
1298 }
1299
1300 /**
1301 * @return integer
1302 */
1303 public function getComplainsCount()
1304 {
1305 return $this->hasMany(AdComplain::className(), ['ad_id' => 'id'])->count();
1306 }
1307
1308 /**
1309 * Formated price based on sale
1310 * @param integer $modificator
1311 * @return string
1312 */
1313 public function getActualPrice($modificator = 1)
1314 {
1315 $price = ($this->isSale ? $this->price - $this->sale_price : $this->price) * (int)$modificator;
1316 return number_format($price, 0, ',', ' ');
1317 }
1318
1319 /**
1320 * Formated old price
1321 * @param integer $modificator
1322 * @return string
1323 */
1324 public function getOldPrice($modificator = 1)
1325 {
1326 return number_format($this->price * (int)$modificator, 0, ',', ' ');
1327 }
1328
1329 /**
1330 * Unpublish ad and its connection
1331 */
1332 public function unpublish()
1333 {
1334 if ($this->type == self::TYPE_COMPLEX) {
1335 if ($this->complexAds) {
1336 $ids = [];
1337 foreach ($this->complexAds as $ad) {
1338 if ($ad->type == self::TYPE_OFFICE) {
1339 $ids[] = $ad->id;
1340 }
1341 }
1342 if ($ids) {
1343 $ids[] = $this->id;
1344 }
1345 self::updateAll(['status' => self::STATUS_HIDDEN], ['id' => $ids]);
1346 }
1347 } elseif ($this->type == self::TYPE_OFFICE) {
1348 $inComplex = ComplexAd::find()
1349 ->joinWith('mainAd')
1350 ->select('complex')
1351 ->where([ComplexAd::field('ad') => $this->id, Ad::field('status') => self::STATUS_PUBLISHED])
1352 ->asArray()->all();
1353 if ($inComplex) {
1354 $ids = ArrayHelper::getColumn($inComplex, ['complex']);
1355 Ad::updateAll(['status' => self::STATUS_HIDDEN], ['id' => $ids]);
1356 foreach ($ids as $id) {
1357 (new Notification([
1358 'title' => Notification::getTypeLabels(Notification::TYPE_COMPLEX_UNPUBLISH),
1359 'status' => Notification::STATUS_LIVE,
1360 'user_id' => $this->user_id,
1361 'type' => Notification::TYPE_COMPLEX_UNPUBLISH,
1362 'data' => Json::encode(['adId' => $id]),
1363 ]))->save();
1364 }
1365 }
1366 $this->updateAttributes(['status' => self::STATUS_HIDDEN]);
1367 }
1368 }
1369
1370 /**
1371 * Generate new presentation
1372 * @param string $destination
1373 * @return mixed
1374 */
1375 public function generatePresentation($destination)
1376 {
1377 $pdf = new Pdf([
1378 'destination' => $destination,
1379 'format' => Pdf::FORMAT_A4,
1380 'content' => Yii::$app->controller->renderPartial('@app/views/offer/presentation', ['ad' => $this]),
1381 'cssFile' => 'static/features/offers/css/presentation.css',
1382 'filename' => 'presentation.pdf',
1383 'marginLeft' => 0,
1384 'marginRight' => 0,
1385 'marginTop' => 5,
1386 'marginBottom' => 0,
1387 'marginHeader' => 0,
1388 'marginFooter' => 0
1389 ]);
1390
1391 return $pdf->render();
1392 }
1393
1394 /**
1395 * @param $email
1396 */
1397 public function sendPresentation($email)
1398 {
1399 $this->generatePresentation(Pdf::DEST_FILE);
1400 $this->trigger(NotificationInterface::EVENT_EMIT_EMAIL_BY_MANDRILL, new Mailer([
1401 'message' => [
1402 'to' => [
1403 ['email' => $email]
1404 ],
1405 'global_merge_vars' => [
1406 [
1407 'name' => 'data',
1408 'content' => [
1409 'ad' => Url::to(['/offer/view', 'id' => $this->id], true),
1410 'name' => $this->name,
1411 'unsubscribe' => Url::to(['/profile/settings'], true)
1412 ]
1413 ]
1414 ],
1415 'attachments' => [
1416 [
1417 'type' => 'application/pdf',
1418 'name' => $this->name . '.pdf',
1419 'content' => base64_encode(file_get_contents('presentation.pdf'))
1420 ]
1421 ],
1422 ],
1423 'templateName' => Mailer::TEMPLATE_PRESENTATION
1424 ]));
1425 unlink('presentation.pdf');
1426 }
1427
1428 /**
1429 * @return \yii\db\ActiveQuery
1430 */
1431 public function getFurniture()
1432 {
1433 return $this->hasMany(AdFurniture::className(), ['ad_id' => 'id']);
1434 }
1435
1436 /**
1437 * Returns contract for deal
1438 * @return array
1439 */
1440 public function getDealContract()
1441 {
1442 $defaultContract = Settings::findOneByAlias('default_deal_contract')['value'];
1443 return [
1444 'name' => $this->custom_contract_name ?: 'Договор аренды',
1445 'file' => self::STORAGE_CONTRACT_DIR . ($this->custom_contract ?: $defaultContract),
1446 ];
1447 }
1448
1449 /**
1450 * Get max limit to area
1451 * @return integer
1452 */
1453 public function getMaxArea()
1454 {
1455 try {
1456 $maxArea = (int)Settings::findOneByAlias('ad_limit_area')['value'];
1457 } catch (UnknownPropertyException $exc) {
1458 $maxArea = 250;
1459 }
1460 return $maxArea;
1461 }
1462
1463 /**
1464 * @return \yii\db\ActiveQuery
1465 */
1466 public function getComplains()
1467 {
1468 return $this->hasMany(AdComplain::className(), ['ad_id' => 'id']);
1469 }
1470
1471 /**
1472 * Generate tags for ad
1473 */
1474 public function generateTags()
1475 {
1476 $tags = [];
1477 //Office type tag
1478 if ($this->type && ($typeLabel = AdHelper::getOfficeTypeLabels($this->type)) && !is_array($typeLabel)) {
1479 $tags['type'] = mb_strtolower($typeLabel, 'UTF-8');
1480 }
1481 //Business center class tag
1482 if (!empty($tags['type']) && $this->businessCenter && $this->businessCenter->class) {
1483 $tags['class'] = $tags['type'] . ' в бизнеÑ-центре клаÑÑа ' . $this->businessCenter->class;
1484 }
1485 //Office tags
1486 if ($this->type && $this->type == self::TYPE_OFFICE) {
1487 $fields = ['subtype', 'location'];
1488 foreach ($fields as $field) {
1489 $getLabelMethodName = 'get' . Inflector::camelize($field) . 'Labels';
1490 if ($this->$field
1491 && method_exists('\app\helpers\AdHelper', $getLabelMethodName)
1492 && ($value = AdHelper::$getLabelMethodName($this->$field ))
1493 && !is_array($value)
1494 ) {
1495 $tags[$field] = mb_strtolower($value, 'UTF-8');
1496 }
1497 }
1498 }
1499
1500 $ids = [];
1501 foreach ($tags as $tag) {
1502 $tag = trim($tag);
1503 $tagModel = Tag::findOne(['name' => $tag]);
1504 if (!$tagModel) {
1505 $tagModel = new Tag(['name' => $tag]);
1506 $tagModel->save();
1507 }
1508 $ids[] = $tagModel->id;
1509 }
1510 TagConnection::deleteAll(['relation_id' => $this->id, 'type' => TagConnection::TYPE_AD]);
1511 foreach ($ids as $id) {
1512 (new TagConnection(['tag_id' => $id, 'relation_id' => $this->id, 'type' => TagConnection::TYPE_AD]))->save();
1513 }
1514 }
1515
1516 /**
1517 * @return \yii\db\ActiveQuery
1518 */
1519 public function getTagConnection()
1520 {
1521 return $this->hasMany(TagConnection::className(), ['relation_id' => 'id'])->onCondition([TagConnection::field('type') => TagConnection::TYPE_AD]);
1522 }
1523
1524 /**
1525 * @return \yii\db\ActiveQuery
1526 */
1527 public function getTagObjects()
1528 {
1529 return $this->hasMany(Tag::className(), ['id' => 'tag_id'])->via('tagConnection');
1530 }
1531
1532 public function getTagsRow($links = false, $glue = ' / ')
1533 {
1534 $alias = 'tags_' . AdHelper::getOfficeTypeInfo($this->type)['class'];
1535 $settings = Settings::findByCategory(Settings::getCategoryIdByAlias($alias));
1536 $data = [];
1537 foreach ($settings[$alias] as $setting) {
1538 $data[] = Html::a($setting['description'], $setting['value']);
1539 }
1540 return implode(' / ', $data);
1541 }
1542
1543 /**
1544 * @return \yii\db\ActiveQuery
1545 */
1546 public function getDeals()
1547 {
1548 return $this->hasMany(Deal::className(), ['ad_id' => 'id']);
1549 }
1550
1551 /**
1552 * @return integer
1553 */
1554 public function getDealsCount()
1555 {
1556 return $this->getDeals()->count();
1557 }
1558}