Completed
Push — 1.11.x ( 0671aa...bbb558 )
by José
49:35 queued 25:52
created

BuyCoursesPlugin   F

Complexity

Total Complexity 176

Size/Duplication

Total Lines 2074
Duplicated Lines 18.61 %

Coupling/Cohesion

Components 2
Dependencies 16

Importance

Changes 0
Metric Value
dl 386
loc 2074
rs 0.6314
c 0
b 0
f 0
wmc 176
lcom 2
cbo 16

69 Methods

Rating   Name   Duplication   Size   Complexity  
A create() 0 5 2
A __construct() 0 23 1
B install() 0 25 2
A uninstall() 0 22 2
B buyCoursesForGridCatalogVerificator() 0 22 4
A returnBuyCourseButton() 0 13 1
A getSelectedCurrency() 0 11 1
A getCurrencies() 0 7 1
A selectCurrency() 0 16 1
A savePaypalParams() 0 13 1
A getPaypalParams() 0 9 1
A saveTransferAccount() 0 11 1
A getTransferAccounts() 0 7 1
A deleteTransferAccount() 0 7 1
A getCourses() 0 22 1
B getItemByProduct() 0 25 1
A getCoursesForConfiguration() 0 17 3
A getSessionsForConfiguration() 0 15 2
B getUserStatusForSession() 44 44 4
B getCatalogSessionList() 20 50 6
B getUserStatusForCourse() 44 45 4
B getCatalogCourseList() 5 49 6
B getCourseInfo() 5 44 5
B getSessionInfo() 20 69 6
A getItem() 0 11 1
C registerSale() 0 51 7
A getSale() 0 11 1
A getSaleListByPaymentType() 20 20 1
A getCurrency() 0 11 1
A updateSaleStatus() 0 10 1
B completeSale() 0 34 5
A cancelSale() 0 4 1
A getPaymentTypes() 0 7 1
A getSaleListByStatus() 20 20 1
A getSaleStatuses() 0 8 1
A getPayoutStatuses() 0 8 1
A getProductTypes() 0 7 1
C filterSessionList() 48 48 9
C filterCourseList() 47 47 9
C randomText() 0 26 7
A generateReference() 0 7 1
B getSaleListByUser() 0 30 2
B getSaleListByUserId() 27 27 2
B getCourseForConfiguration() 6 25 3
B getSessionForConfiguration() 6 60 5
A getItemBeneficiaries() 12 12 1
A deleteItem() 0 15 2
A registerItem() 0 6 1
A updateItem() 13 13 1
A deleteItemBeneficiaries() 9 9 1
A registerItemBeneficiaries() 10 17 2
A isValidCourse() 0 12 3
A getBeneficiariesBySale() 0 12 1
B getPayouts() 0 44 5
B verifyPaypalAccountByBeneficiary() 0 39 4
B storePayouts() 0 24 2
A setStatusPayouts() 11 11 1
A getPlatformCommission() 0 9 1
A updateCommission() 9 9 1
B storeService() 0 37 2
B updateService() 0 28 2
A deleteService() 0 7 1
B getServices() 0 61 4
A getServiceSaleStatuses() 0 8 1
D getServiceSale() 0 118 11
A cancelServiceSale() 0 5 1
A completeServiceSale() 0 12 2
B getCatalogServiceList() 0 55 6
A updateServiceSaleStatus() 10 10 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like BuyCoursesPlugin often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use BuyCoursesPlugin, and based on these observations, apply Extract Interface, too.

1
<?php
2
/* For license terms, see /license.txt */
3
4
use Chamilo\CoreBundle\Entity\Session;
5
use Chamilo\CoreBundle\Entity\Course;
6
7
/**
8
 * Plugin class for the BuyCourses plugin
9
 * @package chamilo.plugin.buycourses
10
 * @author Jose Angel Ruiz <[email protected]>
11
 * @author Imanol Losada <[email protected]>
12
 * @author Alex Aragón <[email protected]>
13
 * @author Angel Fernando Quiroz Campos <[email protected]>
14
 * @author José Loguercio Silva  <[email protected]>
15
 */
16
class BuyCoursesPlugin extends Plugin
17
{
18
    const TABLE_PAYPAL = 'plugin_buycourses_paypal_account';
19
    const TABLE_CURRENCY = 'plugin_buycourses_currency';
20
    const TABLE_ITEM = 'plugin_buycourses_item';
21
    const TABLE_ITEM_BENEFICIARY = 'plugin_buycourses_item_rel_beneficiary';
22
    const TABLE_SALE = 'plugin_buycourses_sale';
23
    const TABLE_TRANSFER = 'plugin_buycourses_transfer';
24
    const TABLE_COMMISSION = 'plugin_buycourses_commission';
25
    const TABLE_PAYPAL_PAYOUTS = 'plugin_buycourses_paypal_payouts';
26
    const TABLE_SERVICES = 'plugin_buycourses_services';
27
    const TABLE_SERVICES_SALE = 'plugin_buycourses_service_sale';
28
    const PRODUCT_TYPE_COURSE = 1;
29
    const PRODUCT_TYPE_SESSION = 2;
30
    const PAYMENT_TYPE_PAYPAL = 1;
31
    const PAYMENT_TYPE_TRANSFER = 2;
32
    const PAYOUT_STATUS_CANCELED = 2;
33
    const PAYOUT_STATUS_PENDING = 0;
34
    const PAYOUT_STATUS_COMPLETED = 1;
35
    const SALE_STATUS_CANCELED = -1;
36
    const SALE_STATUS_PENDING = 0;
37
    const SALE_STATUS_COMPLETED = 1;
38
    const SERVICE_STATUS_PENDING = 0;
39
    const SERVICE_STATUS_COMPLETED = 1;
40
    const SERVICE_STATUS_CANCELLED = -1;
41
    const SERVICE_TYPE_USER = 1;
42
    const SERVICE_TYPE_COURSE = 2;
43
    const SERVICE_TYPE_SESSION = 3;
44
45
    /**
46
     *
47
     * @return StaticPlugin
48
     */
49
    static function create()
50
    {
51
        static $result = null;
52
        return $result ? $result : $result = new self();
53
    }
54
55
    protected function __construct()
56
    {
57
        parent::__construct(
58
            '1.0',
59
            "
60
                Jose Angel Ruiz - NoSoloRed (original author) <br/>
61
                Francis Gonzales and Yannick Warnier - BeezNest (integration) <br/>
62
                Alex Aragón - BeezNest (Design icons and css styles) <br/>
63
                Imanol Losada - BeezNest (introduction of sessions purchase) <br/>
64
                Angel Fernando Quiroz Campos - BeezNest (cleanup and new reports) <br/>
65
                José Loguercio Silva - BeezNest (Payouts and buy Services)
66
            ",
67
            array(
68
                'show_main_menu_tab' => 'boolean',
69
                'include_sessions' => 'boolean',
70
                'include_services' => 'boolean',
71
                'paypal_enable' => 'boolean',
72
                'transfer_enable' => 'boolean',
73
                'commissions_enable' => 'boolean',
74
                'unregistered_users_enable' => 'boolean'
75
            )
76
        );
77
    }
78
79
    /**
80
     * This method creates the tables required to this plugin
81
     */
82
    function install()
83
    {
84
        $tablesToBeCompared = array(
85
            self::TABLE_PAYPAL,
86
            self::TABLE_TRANSFER,
87
            self::TABLE_ITEM_BENEFICIARY,
88
            self::TABLE_ITEM,
89
            self::TABLE_SALE,
90
            self::TABLE_CURRENCY,
91
            self::TABLE_COMMISSION,
92
            self::TABLE_PAYPAL_PAYOUTS,
93
            self::TABLE_SERVICES,
94
            self::TABLE_SERVICES_SALE
95
        );
96
        $em = Database::getManager();
97
        $cn = $em->getConnection();
98
        $sm = $cn->getSchemaManager();
99
        $tables = $sm->tablesExist($tablesToBeCompared);
100
101
        if ($tables) {
102
            return false;
103
        }
104
105
        require_once api_get_path(SYS_PLUGIN_PATH) . 'buycourses/database.php';
106
    }
107
108
    /**
109
     * This method drops the plugin tables
110
     */
111
    function uninstall()
112
    {
113
        $tablesToBeDeleted = array(
114
            self::TABLE_PAYPAL,
115
            self::TABLE_TRANSFER,
116
            self::TABLE_ITEM_BENEFICIARY,
117
            self::TABLE_ITEM,
118
            self::TABLE_SALE,
119
            self::TABLE_CURRENCY,
120
            self::TABLE_COMMISSION,
121
            self::TABLE_PAYPAL_PAYOUTS,
122
            self::TABLE_SERVICES_SALE,
123
            self::TABLE_SERVICES
124
        );
125
126
        foreach ($tablesToBeDeleted as $tableToBeDeleted) {
127
            $table = Database::get_main_table($tableToBeDeleted);
128
            $sql = "DROP TABLE IF EXISTS $table";
129
            Database::query($sql);
130
        }
131
        $this->manageTab(false);
132
    }
133
134
    /**
135
     * This function verify if the plugin is enable and return the price info for a course or session in the new grid catalog
136
     * for 1.11.x , the main purpose is to show if a course or session is in sale it shows in the main platform course catalog
137
     * so the old buycourses plugin catalog can be deprecated.
138
     * @param int $productId course or session id
139
     * @param int $productType course or session type
140
     * @return mixed bool|string html
141
     */
142
    public function buyCoursesForGridCatalogVerificator($productId, $productType) {
143
        $return = [];
144
        $paypal = $this->get('paypal_enable') === 'true';
145
        $transfer = $this->get('transfer_enable') === 'true';
146
147
        if ($paypal || $transfer) {
148
            $item = $this->getItemByProduct(intval($productId), $productType);
149
            $return['html'] = '<div class="buycourses-price">';
150
            if ($item) {
151
                $return['html'] .= '<span class="label label-primary"><b>'. $item['iso_code'] .' ' . $item['price'] . '</b></span>';
152
                $return['verificator'] = true;
153
            } else {
154
                $return['html'] .= '<span class="label label-primary"><b>'. $this->get_lang('Free'). '</b></span>';
155
                $return['verificator'] = false;
156
            }
157
            $return['html'] .= '</div>';
158
        } else {
159
            return false;
160
        }
161
162
        return $return;
163
    }
164
165
    /**
166
     * Return the buyCourses plugin button to buy the course
167
     * @param int $productId
168
     * @param int $productType
169
     * @return string $html
170
     */
171
    public function returnBuyCourseButton($productId, $productType) {
172
        $url = api_get_path(WEB_PLUGIN_PATH) .
173
            'buycourses/src/process.php?i=' .
174
            intval($productId) .
175
            '&t=' .
176
            $productType
177
        ;
178
179
        $html = ' <a class="btn btn-success btn-sm" title="' . $this->get_lang('Buy') . '" href="' . $url . '">' .
180
            Display::returnFontAwesomeIcon('fa fa-shopping-cart') . '</a>';
181
182
        return $html;
183
    }
184
185
    /**
186
     * Get the currency for sales
187
     * @return array The selected currency. Otherwise return false
188
     */
189
    public function getSelectedCurrency()
190
    {
191
        return Database::select(
192
            '*',
193
            Database::get_main_table(BuyCoursesPlugin::TABLE_CURRENCY),
194
            [
195
                'where' => ['status = ?' => true]
196
            ],
197
            'first'
198
        );
199
    }
200
201
    /**
202
     * Get a list of currencies
203
     * @return array The currencies. Otherwise return false
204
     */
205
    public function getCurrencies()
206
    {
207
        return Database::select(
208
            '*',
209
            Database::get_main_table(BuyCoursesPlugin::TABLE_CURRENCY)
210
        );
211
    }
212
213
    /**
214
     * Save the selected currency
215
     * @param int $selectedId The currency Id
216
     */
217
    public function selectCurrency($selectedId)
218
    {
219
        $currencyTable = Database::get_main_table(
220
            BuyCoursesPlugin::TABLE_CURRENCY
221
        );
222
223
        Database::update(
224
            $currencyTable,
225
            ['status' => 0]
226
        );
227
        Database::update(
228
            $currencyTable,
229
            ['status' => 1],
230
            ['id = ?' => intval($selectedId)]
231
        );
232
    }
233
234
    /**
235
     * Save the PayPal configuration params
236
     * @param array $params
237
     * @return int Rows affected. Otherwise return false
238
     */
239
    public function savePaypalParams($params)
240
    {
241
        return Database::update(
242
            Database::get_main_table(BuyCoursesPlugin::TABLE_PAYPAL),
243
            [
244
                'username' => $params['username'],
245
                'password' => $params['password'],
246
                'signature' => $params['signature'],
247
                'sandbox' => isset($params['sandbox'])
248
            ],
249
            ['id = ?' => 1]
250
        );
251
    }
252
253
    /**
254
     * Gets the stored PayPal params
255
     * @return array
256
     */
257
    public function getPaypalParams()
258
    {
259
        return Database::select(
260
            '*',
261
            Database::get_main_table(BuyCoursesPlugin::TABLE_PAYPAL),
262
            ['id = ?' => 1],
263
            'first'
264
        );
265
    }
266
267
    /**
268
     * Save a transfer account information
269
     * @param array $params The transfer account
270
     * @return int Rows affected. Otherwise return false
271
     */
272
    public function saveTransferAccount($params)
273
    {
274
        return Database::insert(
275
            Database::get_main_table(self::TABLE_TRANSFER),
276
            [
277
                'name' => $params['tname'],
278
                'account' => $params['taccount'],
279
                'swift' => $params['tswift']
280
            ]
281
        );
282
    }
283
284
    /**
285
     * Get a list of transfer accounts
286
     * @return array
287
     */
288
    public function getTransferAccounts()
289
    {
290
        return Database::select(
291
            '*',
292
            Database::get_main_table(self::TABLE_TRANSFER)
293
        );
294
    }
295
296
    /**
297
     * Remove a transfer account
298
     * @param int $id The transfer account ID
299
     * @return int Rows affected. Otherwise return false
300
     */
301
    public function deleteTransferAccount($id)
302
    {
303
        return Database::delete(
304
            Database::get_main_table(self::TABLE_TRANSFER),
305
            ['id = ?' => intval($id)]
306
        );
307
    }
308
309
    /**
310
     * Filter the registered courses for show in plugin catalog
311
     * @return array
312
     */
313
    private function getCourses()
314
    {
315
        $entityManager = Database::getManager();
316
        $query = $entityManager->createQueryBuilder();
317
318
        $courses = $query
319
            ->select('c')
320
            ->from('ChamiloCoreBundle:Course', 'c')
321
            ->leftJoin(
322
                'ChamiloCoreBundle:SessionRelCourse',
323
                'sc',
324
                \Doctrine\ORM\Query\Expr\Join::WITH,
325
                'c = sc.course'
326
            )
327
            ->where(
328
                $query->expr()->isNull('sc.course')
329
            )
330
            ->getQuery()
331
            ->getResult();
332
333
        return $courses;
334
    }
335
336
    /**
337
     * Get the item data
338
     * @param int $productId The item ID
339
     * @param int $itemType The item type
340
     * @return array
341
     */
342
    public function getItemByProduct($productId, $itemType)
343
    {
344
        $buyItemTable = Database::get_main_table(BuyCoursesPlugin::TABLE_ITEM);
345
        $buyCurrencyTable = Database::get_main_table(BuyCoursesPlugin::TABLE_CURRENCY);
346
347
        $fakeItemFrom = "
348
            $buyItemTable i
349
            INNER JOIN $buyCurrencyTable c
350
                ON i.currency_id = c.id
351
        ";
352
353
        return Database::select(
354
            ['i.*', 'c.iso_code'],
355
            $fakeItemFrom,
356
            [
357
                'where' => [
358
                    'i.product_id = ? AND i.product_type = ?' => [
359
                        intval($productId),
360
                        intval($itemType)
361
                    ]
362
                ]
363
            ],
364
            'first'
365
        );
366
    }
367
368
    /**
369
     * List courses details from the configuration page
370
     * @return array
371
     */
372
    public function getCoursesForConfiguration()
373
    {
374
        $courses = $this->getCourses();
375
376
        if (empty($courses)) {
377
            return[];
378
        }
379
380
        $configurationCourses = [];
381
        $currency = $this->getSelectedCurrency();
382
383
        foreach ($courses as $course) {
384
            $configurationCourses[] = $this->getCourseForConfiguration($course, $currency);
385
        }
386
387
        return $configurationCourses;
388
    }
389
390
    /**
391
     * List sessions details from the buy-session table and the session table
392
     * @return array The sessions. Otherwise return false
393
     */
394
    public function getSessionsForConfiguration()
395
    {
396
        $auth = new Auth();
397
        $sessions = $auth->browseSessions();
398
399
        $currency = $this->getSelectedCurrency();
400
401
        $items = [];
402
403
        foreach ($sessions as $session) {
404
            $items[] = $this->getSessionForConfiguration($session, $currency);
405
        }
406
407
        return $items;
408
    }
409
410
    /**
411
     * Get the user status for the session
412
     * @param int $userId The user ID
413
     * @param Session $session The session
414
     * @return string
415
     */
416 View Code Duplication
    private function getUserStatusForSession($userId, Session $session)
417
    {
418
        if (empty($userId)) {
419
            return 'NO';
420
        }
421
422
        $entityManager = Database::getManager();
423
        $scuRepo = $entityManager->getRepository('ChamiloCoreBundle:SessionRelCourseRelUser');
424
425
        $buySaleTable = Database::get_main_table(self::TABLE_SALE);
426
427
        // Check if user bought the course
428
        $sale = Database::select(
429
            'COUNT(1) as qty',
430
            $buySaleTable,
431
            [
432
                'where' => [
433
                    'user_id = ? AND product_type = ? AND product_id = ? AND status = ?' => [
434
                        $userId,
435
                        self::PRODUCT_TYPE_SESSION,
436
                        $session->getId(),
437
                        self::SALE_STATUS_PENDING
438
                    ]
439
                ]
440
            ],
441
            'first'
442
        );
443
444
        if ($sale['qty'] > 0) {
445
            return "TMP";
446
        }
447
448
        // Check if user is already subscribe to session
449
        $userSubscription = $scuRepo->findBy([
450
            'session' => $session,
451
            'user' => $userId
452
        ]);
453
454
        if (!empty($userSubscription)) {
455
            return 'YES';
456
        }
457
458
        return 'NO';
459
    }
460
461
    /**
462
     * Lists current user session details, including each session course details
463
     * @param string $name Optional. The name filter
464
     * @param int $min Optional. The minimum price filter
465
     * @param int $max Optional. The maximum price filter
466
     * @return array
467
     */
468
    public function getCatalogSessionList($name = null, $min = 0, $max = 0)
469
    {
470
        $sessions = $this->filterSessionList($name, $min, $max);
471
472
        $sessionCatalog = array();
473
        // loop through all sessions
474
        foreach ($sessions as $session) {
475
            $sessionCourses = $session->getCourses();
476
477
            if (empty($sessionCourses)) {
478
                continue;
479
            }
480
481
            $item = $this->getItemByProduct($session->getId(), self::PRODUCT_TYPE_SESSION);
482
483
            if (empty($item)) {
484
                continue;
485
            }
486
487
            $sessionData = $this->getSessionInfo($session->getId());
488
            $sessionData['coach'] = $session->getGeneralCoach()->getCompleteName();
489
            $sessionData['enrolled'] = $this->getUserStatusForSession(api_get_user_id(), $session);
490
            $sessionData['courses'] = array();
491
492 View Code Duplication
            foreach ($sessionCourses as $sessionCourse) {
493
                $course = $sessionCourse->getCourse();
494
495
                $sessionCourseData = [
496
                    'title' => $course->getTitle(),
497
                    'coaches' => []
498
                ];
499
500
                $userCourseSubscriptions = $session->getUserCourseSubscriptionsByStatus(
501
                    $course,
502
                    Chamilo\CoreBundle\Entity\Session::COACH
503
                );
504
505
                foreach ($userCourseSubscriptions as $userCourseSubscription) {
506
                    $user = $userCourseSubscription->getUser();
507
                    $sessionCourseData['coaches'][] = $user->getCompleteName();
508
                }
509
510
                $sessionData['courses'][] = $sessionCourseData;
511
            }
512
513
            $sessionCatalog[] = $sessionData;
514
        }
515
516
        return $sessionCatalog;
517
    }
518
519
    /**
520
     * Get the user status for the course
521
     * @param int $userId The user Id
522
     * @param Course $course The course
523
     *
524
     * @return string
525
     */
526 View Code Duplication
    private function getUserStatusForCourse($userId, Course $course)
527
    {
528
        if (empty($userId)) {
529
            
530
            return 'NO';
531
        }
532
533
        $entityManager = Database::getManager();
534
        $cuRepo = $entityManager->getRepository('ChamiloCoreBundle:CourseRelUser');
535
536
        $buySaleTable = Database::get_main_table(self::TABLE_SALE);
537
538
        // Check if user bought the course
539
        $sale = Database::select(
540
            'COUNT(1) as qty',
541
            $buySaleTable,
542
            [
543
                'where' => [
544
                    'user_id = ? AND product_type = ? AND product_id = ? AND status = ?' => [
545
                        $userId,
546
                        self::PRODUCT_TYPE_COURSE,
547
                        $course->getId(),
548
                        self::SALE_STATUS_PENDING
549
                    ]
550
                ]
551
            ],
552
            'first'
553
        );
554
555
        if ($sale['qty'] > 0) {
556
            return "TMP";
557
        }
558
559
        // Check if user is already subscribe to course
560
        $userSubscription = $cuRepo->findBy([
561
            'course' => $course,
562
            'user' => $userId
563
        ]);
564
565
        if (!empty($userSubscription)) {
566
            return 'YES';
567
        }
568
569
        return 'NO';
570
    }
571
572
    /**
573
     * Lists current user course details
574
     * @param string $name Optional. The name filter
575
     * @param int $min Optional. The minimum price filter
576
     * @param int $max Optional. The maximum price filter
577
     * @return array
578
     */
579
    public function getCatalogCourseList($name = null, $min = 0, $max = 0)
580
    {
581
        $courses = $this->filterCourseList($name, $min, $max);
582
583
        if (empty($courses)) {
584
            return [];
585
        }
586
587
        $courseCatalog = [];
588
589
        foreach ($courses as $course) {
590
            $item = $this->getItemByProduct($course->getId(), self::PRODUCT_TYPE_COURSE);
591
592
            if (empty($item)) {
593
                continue;
594
            }
595
596
            $courseItem = [
597
                'id' => $course->getId(),
598
                'title' => $course->getTitle(),
599
                'code' => $course->getCode(),
600
                'course_img' => null,
601
                'price' => $item['price'],
602
                'currency' => $item['iso_code'],
603
                'teachers' => [],
604
                'enrolled' => $this->getUserStatusForCourse(api_get_user_id(), $course)
605
            ];
606
607
            foreach ($course->getTeachers() as $courseUser) {
608
                $teacher = $courseUser->getUser();
609
                $courseItem['teachers'][] = $teacher->getCompleteName();
610
            }
611
612
            //check images
613
            $possiblePath = api_get_path(SYS_COURSE_PATH);
614
            $possiblePath .= $course->getDirectory();
615
            $possiblePath .= '/course-pic.png';
616
617 View Code Duplication
            if (file_exists($possiblePath)) {
618
                $courseItem['course_img'] = api_get_path(WEB_COURSE_PATH)
619
                    . $course->getDirectory()
620
                    . '/course-pic.png';
621
            }
622
623
            $courseCatalog[] = $courseItem;
624
        }
625
626
        return $courseCatalog;
627
    }
628
629
    /**
630
     * Get course info
631
     * @param int $courseId The course ID
632
     * @return array
633
     */
634
    public function getCourseInfo($courseId)
635
    {
636
        $entityManager = Database::getManager();
637
        $course = $entityManager->find('ChamiloCoreBundle:Course', $courseId);
638
639
        if (empty($course)) {
640
            return [];
641
        }
642
643
        $item = $this->getItemByProduct($course->getId(), self::PRODUCT_TYPE_COURSE);
644
645
        if (empty($item)) {
646
            return [];
647
        }
648
649
        $courseInfo = [
650
            'id' => $course->getId(),
651
            'title' => $course->getTitle(),
652
            'code' => $course->getCode(),
653
            'visual_code' => $course->getVisualCode(),
654
            'teachers' => [],
655
            'price' => $item['price'],
656
            'currency' => $item['iso_code'],
657
            'course_img' => null
658
        ];
659
660
        $courseTeachers = $course->getTeachers();
661
662
        foreach ($courseTeachers as $teacher) {
663
            $courseInfo['teachers'][] = $teacher->getUser()->getCompleteName();
664
        }
665
666
        $possiblePath = api_get_path(SYS_COURSE_PATH);
667
        $possiblePath .= $course->getDirectory();
668
        $possiblePath .= '/course-pic.png';
669
670 View Code Duplication
        if (file_exists($possiblePath)) {
671
            $courseInfo['course_img'] = api_get_path(WEB_COURSE_PATH)
672
                . $course->getDirectory()
673
                . '/course-pic.png';
674
        }
675
676
        return $courseInfo;
677
    }
678
679
    /**
680
     * Get session info
681
     * @param array $sessionId The session ID
682
     * @return array
683
     */
684
    public function getSessionInfo($sessionId)
685
    {
686
        $entityManager = Database::getManager();
687
        $session = $entityManager->find('ChamiloCoreBundle:Session', $sessionId);
688
689
        if (empty($session)) {
690
            return [];
691
        }
692
693
        $item = $this->getItemByProduct($session->getId(), self::PRODUCT_TYPE_SESSION);
694
695
        if (empty($item)) {
696
            return [];
697
        }
698
699
        $sessionDates = SessionManager::parseSessionDates([
700
            'display_start_date' => $session->getDisplayStartDate(),
701
            'display_end_date' => $session->getDisplayEndDate(),
702
            'access_start_date' => $session->getAccessStartDate(),
703
            'access_end_date' => $session->getAccessEndDate(),
704
            'coach_access_start_date' => $session->getCoachAccessStartDate(),
705
            'coach_access_end_date' => $session->getCoachAccessEndDate()
706
        ]);
707
708
        $sessionInfo = [
709
            'id' => $session->getId(),
710
            'name' => $session->getName(),
711
            'dates' => $sessionDates,
712
            'courses' => [],
713
            'price' => $item['price'],
714
            'currency' => $item['iso_code'],
715
            'image' => null
716
        ];
717
718
        $fieldValue = new ExtraFieldValue('session');
719
        $sessionImage = $fieldValue->get_values_by_handler_and_field_variable(
720
            $session->getId(),
721
            'image'
722
        );
723
724
        if (!empty($sessionImage)) {
725
            $sessionInfo['image'] = api_get_path(WEB_UPLOAD_PATH) . $sessionImage['value'];
726
        }
727
728
        $sessionCourses = $session->getCourses();
729
730 View Code Duplication
        foreach ($sessionCourses as $sessionCourse) {
731
            $course = $sessionCourse->getCourse();
732
733
            $sessionCourseData = [
734
                'title' => $course->getTitle(),
735
                'coaches' => []
736
            ];
737
738
            $userCourseSubscriptions = $session->getUserCourseSubscriptionsByStatus(
739
                $course,
740
                Chamilo\CoreBundle\Entity\Session::COACH
741
            );
742
743
            foreach ($userCourseSubscriptions as $userCourseSubscription) {
744
                $user = $userCourseSubscription->getUser();
745
                $sessionCourseData['coaches'][] = $user->getCompleteName();
746
            }
747
748
            $sessionInfo['courses'][] = $sessionCourseData;
749
        }
750
751
        return $sessionInfo;
752
    }
753
754
    /**
755
     * Get registered item data
756
     * @param int $itemId The item ID
757
     * @return array
758
     */
759
    public function getItem($itemId)
760
    {
761
        return Database::select(
762
            '*',
763
            Database::get_main_table(self::TABLE_ITEM),
764
            [
765
                'where' => ['id = ?' => intval($itemId)]
766
            ],
767
            'first'
768
        );
769
    }
770
771
    /**
772
     * Register a sale
773
     * @param int $itemId The product ID
774
     * @param int $paymentType The payment type
775
     * @return boolean
776
     */
777
    public function registerSale($itemId, $paymentType)
778
    {
779
        if (!in_array($paymentType, [self::PAYMENT_TYPE_PAYPAL, self::PAYMENT_TYPE_TRANSFER])) {
780
            return false;
781
        }
782
783
        $entityManager = Database::getManager();
784
785
        $item = $this->getItem($itemId);
786
787
        if (empty($item)) {
788
            return false;
789
        }
790
791
        if ($item['product_type'] == self::PRODUCT_TYPE_COURSE) {
792
            $course = $entityManager->find('ChamiloCoreBundle:Course', $item['product_id']);
793
794
            if (empty($course)) {
795
                return false;
796
            }
797
798
            $productName = $course->getTitle();
799
        } elseif ($item['product_type'] == self::PRODUCT_TYPE_SESSION) {
800
            $session = $entityManager->find('ChamiloCoreBundle:Session', $item['product_id']);
801
802
            if (empty($session)) {
803
                return false;
804
            }
805
806
            $productName = $session->getName();
807
        }
808
809
        $values = [
810
            'reference' => $this->generateReference(
811
                api_get_user_id(),
812
                $item['product_type'],
813
                $item['product_id']
814
            ),
815
            'currency_id' => $item['currency_id'],
816
            'date' => api_get_utc_datetime(),
817
            'user_id' => api_get_user_id(),
818
            'product_type' => $item['product_type'],
819
            'product_name' => $productName,
820
            'product_id' => $item['product_id'],
821
            'price' => $item['price'],
822
            'status' => self::SALE_STATUS_PENDING,
823
            'payment_type' => intval($paymentType)
824
        ];
825
826
        return Database::insert(self::TABLE_SALE, $values);
827
    }
828
829
    /**
830
     * Get sale data by ID
831
     * @param int $saleId The sale ID
832
     * @return array
833
     */
834
    public function getSale($saleId)
835
    {
836
        return Database::select(
837
            '*',
838
            Database::get_main_table(self::TABLE_SALE),
839
            [
840
                'where' => ['id = ?' => intval($saleId)]
841
            ],
842
            'first'
843
        );
844
    }
845
846
    /**
847
     * Get a list of sales by the payment type
848
     * @param int $paymentType The payment type to filter (default : Paypal)
849
     * @return array The sale list. Otherwise return false
850
     */
851 View Code Duplication
    public function getSaleListByPaymentType($paymentType = self::PAYMENT_TYPE_PAYPAL)
852
    {
853
        $saleTable = Database::get_main_table(BuyCoursesPlugin::TABLE_SALE);
854
        $currencyTable = Database::get_main_table(BuyCoursesPlugin::TABLE_CURRENCY);
855
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
856
857
        $innerJoins = "
858
            INNER JOIN $currencyTable c ON s.currency_id = c.id
859
            INNER JOIN $userTable u ON s.user_id = u.id
860
        ";
861
862
        return Database::select(
863
            ['c.iso_code', 'u.firstname', 'u.lastname', 's.*'],
864
            "$saleTable s $innerJoins",
865
            [
866
                'where' => ['s.payment_type = ? AND s.status = ?' => [intval($paymentType), self::SALE_STATUS_COMPLETED]],
867
                'order' => 'id DESC'
868
            ]
869
        );
870
    }
871
872
    /**
873
     * Get currency data by ID
874
     * @param int $currencyId The currency ID
875
     * @return array
876
     */
877
    public function getCurrency($currencyId)
878
    {
879
        return Database::select(
880
            '*',
881
            Database::get_main_table(BuyCoursesPlugin::TABLE_CURRENCY),
882
            [
883
                'where' => ['id = ?' => intval($currencyId)]
884
            ],
885
            'first'
886
        );
887
    }
888
889
    /**
890
     * Update the sale status
891
     * @param int $saleId The sale ID
892
     * @param int $newStatus The new status
893
     * @return boolean
894
     */
895
    private function updateSaleStatus($saleId, $newStatus = self::SALE_STATUS_PENDING)
896
    {
897
        $saleTable = Database::get_main_table(self::TABLE_SALE);
898
899
        return Database::update(
900
            $saleTable,
901
            ['status' => intval($newStatus)],
902
            ['id = ?' => intval($saleId)]
903
        );
904
    }
905
906
    /**
907
     * Complete sale process. Update sale status to completed
908
     * @param int $saleId The sale ID
909
     * @return boolean
910
     */
911
    public function completeSale($saleId)
912
    {
913
        $sale = $this->getSale($saleId);
914
915
        if ($sale['status'] == self::SALE_STATUS_COMPLETED) {
916
            return true;
917
        }
918
919
        $saleIsCompleted = false;
920
921
        switch ($sale['product_type']) {
922
            case self::PRODUCT_TYPE_COURSE:
923
                $course = api_get_course_info_by_id($sale['product_id']);
924
925
                $saleIsCompleted = CourseManager::subscribe_user($sale['user_id'], $course['code']);
926
                break;
927
            case self::PRODUCT_TYPE_SESSION:
928
                SessionManager::subscribe_users_to_session(
929
                    $sale['product_id'],
930
                    [$sale['user_id']],
931
                    api_get_session_visibility($sale['product_id']),
932
                    false
933
                );
934
935
                $saleIsCompleted = true;
936
                break;
937
        }
938
939
        if ($saleIsCompleted) {
940
            $this->updateSaleStatus($sale['id'], self::SALE_STATUS_COMPLETED);
941
        }
942
943
        return $saleIsCompleted;
944
    }
945
946
    /**
947
     * Update sale status to canceled
948
     * @param int $saleId The sale ID
949
     */
950
    public function cancelSale($saleId)
951
    {
952
        $this->updateSaleStatus($saleId, self::SALE_STATUS_CANCELED);
953
    }
954
955
    /**
956
     * Get payment types
957
     * @return array
958
     */
959
    public function getPaymentTypes()
960
    {
961
        return [
962
            self::PAYMENT_TYPE_PAYPAL => 'PayPal',
963
            self::PAYMENT_TYPE_TRANSFER => $this->get_lang('BankTransfer')
964
        ];
965
    }
966
967
    /**
968
     * Get a list of sales by the status
969
     * @param int $status The status to filter
970
     * @return array The sale list. Otherwise return false
971
     */
972 View Code Duplication
    public function getSaleListByStatus($status = self::SALE_STATUS_PENDING)
973
    {
974
        $saleTable = Database::get_main_table(BuyCoursesPlugin::TABLE_SALE);
975
        $currencyTable = Database::get_main_table(BuyCoursesPlugin::TABLE_CURRENCY);
976
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
977
978
        $innerJoins = "
979
            INNER JOIN $currencyTable c ON s.currency_id = c.id
980
            INNER JOIN $userTable u ON s.user_id = u.id
981
        ";
982
983
        return Database::select(
984
            ['c.iso_code', 'u.firstname', 'u.lastname', 's.*'],
985
            "$saleTable s $innerJoins",
986
            [
987
                'where' => ['s.status = ?' => intval($status)],
988
                'order' => 'id DESC'
989
            ]
990
        );
991
    }
992
993
    /**
994
     * Get the statuses for sales
995
     * @return array
996
     */
997
    public function getSaleStatuses()
998
    {
999
        return [
1000
            self::SALE_STATUS_CANCELED => $this->get_lang('SaleStatusCanceled'),
1001
            self::SALE_STATUS_PENDING => $this->get_lang('SaleStatusPending'),
1002
            self::SALE_STATUS_COMPLETED => $this->get_lang('SaleStatusCompleted')
1003
        ];
1004
    }
1005
1006
    /**
1007
     * Get the statuses for Payouts
1008
     * @return array
1009
     */
1010
    public function getPayoutStatuses()
1011
    {
1012
        return [
1013
            self::PAYOUT_STATUS_CANCELED => $this->get_lang('PayoutStatusCanceled'),
1014
            self::PAYOUT_STATUS_PENDING => $this->get_lang('PayoutStatusPending'),
1015
            self::PAYOUT_STATUS_COMPLETED => $this->get_lang('PayoutStatusCompleted')
1016
        ];
1017
    }
1018
1019
    /**
1020
     * Get the list of product types
1021
     * @return array
1022
     */
1023
    public function getProductTypes()
1024
    {
1025
        return [
1026
            self::PRODUCT_TYPE_COURSE => get_lang('Course'),
1027
            self::PRODUCT_TYPE_SESSION => get_lang('Session')
1028
        ];
1029
    }
1030
1031
    /**
1032
     * Search filtered sessions by name, and range of price
1033
     * @param string $name Optional. The name filter
1034
     * @param int $min Optional. The minimun price filter
1035
     * @param int $max Optional. The maximum price filter
1036
     * @return array
1037
     */
1038 View Code Duplication
    private function filterSessionList($name = null, $min = 0, $max = 0)
1039
    {
1040
        if (empty($name) && empty($min) && empty($max)) {
1041
            $auth = new Auth();
1042
            return $auth->browseSessions();
1043
        }
1044
1045
        $itemTable = Database::get_main_table(self::TABLE_ITEM);
1046
        $sessionTable = Database::get_main_table(TABLE_MAIN_SESSION);
1047
1048
        $min = floatval($min);
1049
        $max = floatval($max);
1050
1051
        $innerJoin = "$itemTable i ON s.id = i.product_id";
1052
        $whereConditions = [
1053
            'i.product_type = ? ' => self::PRODUCT_TYPE_SESSION
1054
        ];
1055
1056
        if (!empty($name)) {
1057
            $whereConditions['AND s.name LIKE %?%'] = $name;
1058
        }
1059
1060
        if (!empty($min)) {
1061
            $whereConditions['AND i.price >= ?'] = $min;
1062
        }
1063
1064
        if (!empty($max)) {
1065
            $whereConditions['AND i.price <= ?'] = $max;
1066
        }
1067
1068
        $sessionIds = Database::select(
1069
            's.id',
1070
            "$sessionTable s INNER JOIN $innerJoin",
1071
            ['where' => $whereConditions]
1072
        );
1073
1074
        if (!$sessionIds) {
1075
            return [];
1076
        }
1077
1078
        $sessions = [];
1079
1080
        foreach ($sessionIds as $sessionId) {
1081
            $sessions[] = Database::getManager()->find('ChamiloCoreBundle:Session', $sessionId);
1082
        }
1083
1084
        return $sessions;
1085
    }
1086
1087
    /**
1088
     * Search filtered courses by name, and range of price
1089
     * @param string $name Optional. The name filter
1090
     * @param int $min Optional. The minimun price filter
1091
     * @param int $max Optional. The maximum price filter
1092
     * @return array
1093
     */
1094 View Code Duplication
    private function filterCourseList($name = null, $min = 0, $max = 0)
1095
    {
1096
        if (empty($name) && empty($min) && empty($max)) {
1097
            return $this->getCourses();
1098
        }
1099
1100
        $itemTable = Database::get_main_table(self::TABLE_ITEM);
1101
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
1102
1103
        $min = floatval($min);
1104
        $max = floatval($max);
1105
1106
        $innerJoin = "$itemTable i ON c.id = i.product_id";
1107
        $whereConditions = [
1108
            'i.product_type = ? ' => self::PRODUCT_TYPE_COURSE
1109
        ];
1110
1111
        if (!empty($name)) {
1112
            $whereConditions['AND c.title LIKE %?%'] = $name;
1113
        }
1114
1115
        if (!empty($min)) {
1116
            $whereConditions['AND i.price >= ?'] = $min;
1117
        }
1118
1119
        if (!empty($max)) {
1120
            $whereConditions['AND i.price <= ?'] = $max;
1121
        }
1122
1123
        $courseIds = Database::select(
1124
            'c.id',
1125
            "$courseTable c INNER JOIN $innerJoin",
1126
            ['where' => $whereConditions]
1127
        );
1128
1129
        if (!$courseIds) {
1130
            return [];
1131
        }
1132
1133
        $courses = [];
1134
1135
        foreach ($courseIds as $courseId) {
1136
            $courses[] = Database::getManager()->find('ChamiloCoreBundle:Course', $courseId);
1137
        }
1138
1139
        return $courses;
1140
    }
1141
1142
    /**
1143
     * Generates a random text (used for order references)
1144
     * @param int $length Optional. Length of characters
1145
     * @param boolean $lowercase Optional. Include lowercase characters
1146
     * @param boolean $uppercase Optional. Include uppercase characters
1147
     * @param boolean $numbers Optional. Include numbers
1148
     * @return string
1149
     */
1150
    public static function randomText(
1151
        $length = 6,
1152
        $lowercase = true,
1153
        $uppercase = true,
1154
        $numbers = true
1155
    )
1156
    {
1157
        $salt = $lowercase ? 'abchefghknpqrstuvwxyz' : '';
1158
        $salt .= $uppercase ? 'ACDEFHKNPRSTUVWXYZ' : '';
1159
        $salt .= $numbers ? (strlen($salt) ? '2345679' : '0123456789') : '';
1160
1161
        if (strlen($salt) == 0) {
1162
            return '';
1163
        }
1164
1165
        $str = '';
1166
1167
        srand((double)microtime() * 1000000);
1168
1169
        for ($i = 0; $i < $length; $i++) {
1170
            $numbers = rand(0, strlen($salt) - 1);
1171
            $str .= substr($salt, $numbers, 1);
1172
        }
1173
1174
        return $str;
1175
    }
1176
1177
    /**
1178
     * Generates an order reference
1179
     * @param int $userId The user ID
1180
     * @param int $productType The course/session type
1181
     * @param int $productId The course/session ID
1182
     * @return string
1183
     */
1184
    public function generateReference($userId, $productType, $productId)
1185
    {
1186
        return vsprintf(
1187
            "%d-%d-%d-%s",
1188
            [$userId, $productType, $productId, self::randomText()]
1189
        );
1190
    }
1191
1192
    /**
1193
     * Get a list of sales by the user
1194
     * @param string $term The search term
1195
     * @return array The sale list. Otherwise return false
1196
     */
1197
    public function getSaleListByUser($term)
1198
    {
1199
        $term = trim($term);
1200
1201
        if (empty($term)) {
1202
            return [];
1203
        }
1204
1205
        $saleTable = Database::get_main_table(BuyCoursesPlugin::TABLE_SALE);
1206
        $currencyTable = Database::get_main_table(BuyCoursesPlugin::TABLE_CURRENCY);
1207
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
1208
1209
        $innerJoins = "
1210
            INNER JOIN $currencyTable c ON s.currency_id = c.id
1211
            INNER JOIN $userTable u ON s.user_id = u.id
1212
        ";
1213
1214
        return Database::select(
1215
            ['c.iso_code', 'u.firstname', 'u.lastname', 's.*'],
1216
            "$saleTable s $innerJoins",
1217
            [
1218
                'where' => [
1219
                    'u.username LIKE %?% OR ' => $term,
1220
                    'u.lastname LIKE %?% OR ' => $term,
1221
                    'u.firstname LIKE %?%' => $term
1222
                ],
1223
                'order' => 'id DESC'
1224
            ]
1225
        );
1226
    }
1227
1228
    /**
1229
     * Get a list of sales by the user id
1230
     * @param int $id The user id
1231
     * @return array The sale list. Otherwise return false
1232
     */
1233 View Code Duplication
    public function getSaleListByUserId($id)
1234
    {
1235
1236
        if (empty($id)) {
1237
            return [];
1238
        }
1239
1240
        $saleTable = Database::get_main_table(BuyCoursesPlugin::TABLE_SALE);
1241
        $currencyTable = Database::get_main_table(BuyCoursesPlugin::TABLE_CURRENCY);
1242
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
1243
1244
        $innerJoins = "
1245
            INNER JOIN $currencyTable c ON s.currency_id = c.id
1246
            INNER JOIN $userTable u ON s.user_id = u.id
1247
        ";
1248
1249
        return Database::select(
1250
            ['c.iso_code', 'u.firstname', 'u.lastname', 's.*'],
1251
            "$saleTable s $innerJoins",
1252
            [
1253
                'where' => [
1254
                    'u.id = ? AND s.status = ?' => [intval($id), BuyCoursesPlugin::SALE_STATUS_COMPLETED]
1255
                ],
1256
                'order' => 'id DESC'
1257
            ]
1258
        );
1259
    }
1260
1261
    /**
1262
     * Convert the course info to array with necessary course data for save item
1263
     * @param Course $course
1264
     * @param array $defaultCurrency Optional. Currency data
1265
     * @return array
1266
     */
1267
    public function getCourseForConfiguration(Course $course, $defaultCurrency = null)
1268
    {
1269
        $courseItem = [
1270
            'item_id' => null,
1271
            'course_id' => $course->getId(),
1272
            'course_visual_code' => $course->getVisualCode(),
1273
            'course_code' => $course->getCode(),
1274
            'course_title' => $course->getTitle(),
1275
            'course_visibility' => $course->getVisibility(),
1276
            'visible' => false,
1277
            'currency' =>  empty($defaultCurrency) ? null : $defaultCurrency['iso_code'],
1278
            'price' => 0.00
1279
        ];
1280
1281
        $item = $this->getItemByProduct($course->getId(), self::PRODUCT_TYPE_COURSE);
1282
1283 View Code Duplication
        if ($item !== false) {
1284
            $courseItem['item_id'] = $item['id'];
1285
            $courseItem['visible'] = true;
1286
            $courseItem['currency'] = $item['iso_code'];
1287
            $courseItem['price'] = $item['price'];
1288
        }
1289
1290
        return $courseItem;
1291
    }
1292
1293
    /**
1294
     * Convert the session info to array with necessary session data for save item
1295
     * @param Session $session The session data
1296
     * @param array $defaultCurrency Optional. Currency data
1297
     * @return array
1298
     */
1299
    public function getSessionForConfiguration(Session $session, $defaultCurrency = null)
1300
    {
1301
        $buyItemTable = Database::get_main_table(BuyCoursesPlugin::TABLE_ITEM);
1302
        $buyCurrencyTable = Database::get_main_table(BuyCoursesPlugin::TABLE_CURRENCY);
1303
1304
        $fakeItemFrom = "
1305
            $buyItemTable i
1306
            INNER JOIN $buyCurrencyTable c ON i.currency_id = c.id
1307
        ";
1308
1309
        $sessionItem = [
1310
            'item_id' => null,
1311
            'session_id' => $session->getId(),
1312
            'session_name' => $session->getName(),
1313
            'session_visibility' => $session->getVisibility(),
1314
            'session_display_start_date' => null,
1315
            'session_display_end_date' => null,
1316
            'visible' => false,
1317
            'currency' =>  empty($defaultCurrency) ? null : $defaultCurrency['iso_code'],
1318
            'price' => 0.00
1319
        ];
1320
1321
        $displayStartDate = $session->getDisplayStartDate();
1322
1323
        if (!empty($displayStartDate)) {
1324
            $sessionItem['session_display_start_date'] = api_format_date(
1325
                $session->getDisplayStartDate()->format('Y-m-d h:i:s')
1326
            );
1327
        }
1328
1329
        $displayEndDate = $session->getDisplayEndDate();
1330
1331
        if (!empty($displayEndDate)) {
1332
            $sessionItem['session_display_end_date'] = api_format_date(
1333
                $session->getDisplayEndDate()->format('Y-m-d h:i:s'),
1334
                DATE_TIME_FORMAT_LONG_24H
1335
            );
1336
        }
1337
1338
        $item = Database::select(
1339
            ['i.*', 'c.iso_code'],
1340
            $fakeItemFrom,
1341
            [
1342
                'where' => [
1343
                    'i.product_id = ? AND ' => $session->getId(),
1344
                    'i.product_type = ?' => self::PRODUCT_TYPE_SESSION
1345
                ]
1346
            ],
1347
            'first'
1348
        );
1349
1350 View Code Duplication
        if ($item !== false) {
1351
            $sessionItem['item_id'] = $item['id'];
1352
            $sessionItem['visible'] = true;
1353
            $sessionItem['currency'] = $item['iso_code'];
1354
            $sessionItem['price'] = $item['price'];
1355
        }
1356
1357
        return $sessionItem;
1358
    }
1359
1360
    /**
1361
     * Get all beneficiaries for a item
1362
     * @param int $itemId The item ID
1363
     * @return array The beneficiries. Otherwise return false
1364
     */
1365 View Code Duplication
    public function getItemBeneficiaries($itemId)
1366
    {
1367
        $beneficiaryTable = Database::get_main_table(self::TABLE_ITEM_BENEFICIARY);
1368
1369
        return Database::select(
1370
            '*',
1371
            $beneficiaryTable,
1372
            ['where' => [
1373
                'item_id = ?' => intval($itemId)
1374
            ]]
1375
        );
1376
    }
1377
1378
    /**
1379
     * Delete a item with its beneficiaries
1380
     * @param int $itemId The item ID
1381
     * @return int The number of affected rows. Otherwise return false
1382
     */
1383
    public function deleteItem($itemId)
1384
    {
1385
        $itemTable = Database::get_main_table(BuyCoursesPlugin::TABLE_ITEM);
1386
1387
        $affectedRows = Database::delete(
1388
            $itemTable,
1389
            ['id = ?' => intval($itemId)]
1390
        );
1391
1392
        if (!$affectedRows) {
1393
            return false;
1394
        }
1395
1396
        return $this->deleteItemBeneficiaries($itemId);
1397
    }
1398
1399
    /**
1400
     * Register a item
1401
     * @param array $itemData The item data
1402
     * @return int The item ID. Otherwise return false
1403
     */
1404
    public function registerItem(array $itemData)
1405
    {
1406
        $itemTable = Database::get_main_table(BuyCoursesPlugin::TABLE_ITEM);
1407
1408
        return Database::insert($itemTable, $itemData);
1409
    }
1410
1411
    /**
1412
     * Update the item data by product
1413
     * @param array $itemData The item data to be updated
1414
     * @param int $productId The product ID
1415
     * @param int $productType The type of product
1416
     * @return int The number of affected rows. Otherwise return false
1417
     */
1418 View Code Duplication
    public function updateItem(array $itemData, $productId, $productType)
1419
    {
1420
        $itemTable = Database::get_main_table(BuyCoursesPlugin::TABLE_ITEM);
1421
1422
        return Database::update(
1423
            $itemTable,
1424
            $itemData,
1425
            [
1426
                'product_id = ? AND ' => intval($productId),
1427
                'product_type' => $productType
1428
            ]
1429
        );
1430
    }
1431
1432
    /**
1433
     * Remove all beneficiaries for a item
1434
     * @param int $itemId The user ID
1435
     * @return int The number of affected rows. Otherwise return false
1436
     */
1437 View Code Duplication
    public function deleteItemBeneficiaries($itemId)
1438
    {
1439
        $beneficiaryTable = Database::get_main_table(BuyCoursesPlugin::TABLE_ITEM_BENEFICIARY);
1440
1441
        return Database::delete(
1442
            $beneficiaryTable,
1443
            ['item_id = ?' => intval($itemId)]
1444
        );
1445
    }
1446
1447
    /**
1448
     * Register the beneficiaries users with the sale of item
1449
     * @param int $itemId The item ID
1450
     * @param array $userIds The beneficiary user ID and Teachers commissions if enabled
1451
     */
1452
    public function registerItemBeneficiaries($itemId, array $userIds)
1453
    {
1454
        $beneficiaryTable = Database::get_main_table(BuyCoursesPlugin::TABLE_ITEM_BENEFICIARY);
1455
1456
        $this->deleteItemBeneficiaries($itemId);
1457
1458 View Code Duplication
        foreach ($userIds as $userId => $commissions) {
1459
            Database::insert(
1460
                $beneficiaryTable,
1461
                [
1462
                    'item_id' => intval($itemId),
1463
                    'user_id' => intval($userId),
1464
                    'commissions' => intval($commissions)
1465
                ]
1466
            );
1467
        }
1468
    }
1469
1470
    /**
1471
     * Check if a course is valid for sale
1472
     * @param Course $course The course
1473
     * @return boolean
1474
     */
1475
    public function isValidCourse(Course $course)
1476
    {
1477
        $courses = $this->getCourses();
1478
1479
        foreach ($courses as $_c) {
1480
            if ($_c->getCode() === $course->getCode()) {
1481
                return true;
1482
            }
1483
        }
1484
1485
        return false;
1486
    }
1487
1488
    /**
1489
     * Gets the beneficiaries with commissions and current paypal accounts by sale
1490
     * @param int $saleId The sale ID
1491
     * @return array
1492
     */
1493
    public function getBeneficiariesBySale($saleId)
1494
    {
1495
1496
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
1497
1498
        $beneficiaries = [];
1499
        $sale = $this->getSale($saleId);
1500
        $item = $this->getItemByProduct($sale['product_id'], $sale['product_type']);
1501
        $itemBeneficiaries = $this->getItemBeneficiaries($item['id']);
1502
        return $itemBeneficiaries;
1503
1504
    }
1505
1506
    /**
1507
     * gets all payouts
1508
     * @param int $status - default 0 - pending
1509
     * @param int $payoutId - for get an individual payout if want all then false
1510
     * @return array
1511
     */
1512
    public function getPayouts($status = self::PAYOUT_STATUS_PENDING, $payoutId = false, $userId = false)
1513
    {
1514
        $condition = ($payoutId) ? 'AND p.id = '. intval($payoutId) : '';
1515
        $condition2 = ($userId) ? ' AND p.user_id = ' . intval($userId) : '';
1516
        $typeResult = ($condition) ? 'first' : 'all';
1517
        $payoutsTable = Database::get_main_table(BuyCoursesPlugin::TABLE_PAYPAL_PAYOUTS);
1518
        $saleTable = Database::get_main_table(BuyCoursesPlugin::TABLE_SALE);
1519
        $currencyTable = Database::get_main_table(BuyCoursesPlugin::TABLE_CURRENCY);
1520
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
1521
        $extraFieldTable = Database::get_main_table(TABLE_EXTRA_FIELD);
1522
        $extraFieldValues = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
1523
1524
        $paypalExtraField = Database::select(
1525
            "*",
1526
            $extraFieldTable,
1527
            [
1528
                'where' => ['variable = ?' => 'paypal']
1529
            ],
1530
            'first'
1531
        );
1532
1533
        if (!$paypalExtraField) {
1534
            return false;
1535
        }
1536
1537
        $innerJoins = "
1538
            INNER JOIN $userTable u ON p.user_id = u.id
1539
            INNER JOIN $saleTable s ON s.id = p.sale_id
1540
            INNER JOIN $currencyTable c ON s.currency_id = c.id
1541
            LEFT JOIN  $extraFieldValues efv ON p.user_id = efv.item_id 
1542
            AND field_id = " . intval($paypalExtraField['id']) . "
1543
        ";
1544
1545
        $payouts = Database::select(
1546
            "p.* , u.firstname, u.lastname, efv.value as paypal_account, s.reference as sale_reference, s.price as item_price, c.iso_code",
1547
            "$payoutsTable p $innerJoins",
1548
            [
1549
                'where' => ['p.status = ? '.$condition . ' ' .$condition2 => $status]
1550
            ],
1551
            $typeResult
1552
        );
1553
1554
        return $payouts;
1555
    }
1556
1557
    /**
1558
     * Verify if the beneficiary have a paypal account
1559
     * @param int $userId
1560
     * @return true if the user have a paypal account, false if not
1561
     */
1562
    public function verifyPaypalAccountByBeneficiary($userId)
1563
    {
1564
        $extraFieldTable = Database::get_main_table(TABLE_EXTRA_FIELD);
1565
        $extraFieldValues = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
1566
1567
        $paypalExtraField = Database::select(
1568
            "*",
1569
            $extraFieldTable,
1570
            [
1571
                'where' => ['variable = ?' => 'paypal']
1572
            ],
1573
            'first'
1574
        );
1575
1576
        if (!$paypalExtraField) {
1577
            return false;
1578
        }
1579
1580
        $paypalFieldId = $paypalExtraField['id'];
1581
1582
        $paypalAccount = Database::select(
1583
            "value",
1584
            $extraFieldValues,
1585
            [
1586
                'where' => ['field_id = ? AND item_id = ?' => [intval($paypalFieldId), intval($userId)]]
1587
            ],
1588
            'first'
1589
        );
1590
1591
        if (!$paypalAccount) {
1592
            return false;
1593
        }
1594
1595
        if ($paypalAccount['value'] === '') {
1596
            return false;
1597
        }
1598
1599
        return true;
1600
    }
1601
1602
    /**
1603
     * Register the users payouts
1604
     * @param int $saleId The sale ID
1605
     * @return array
1606
     */
1607
    public function storePayouts($saleId)
1608
    {
1609
        $payoutsTable = Database::get_main_table(BuyCoursesPlugin::TABLE_PAYPAL_PAYOUTS);
1610
        $platformCommission = $this->getPlatformCommission();
1611
1612
        $sale = $this->getSale($saleId);
1613
        $teachersCommission = number_format((floatval($sale['price']) * intval($platformCommission['commission']))/100, 2);
1614
1615
1616
        $beneficiaries = $this->getBeneficiariesBySale($saleId);
1617
        foreach ($beneficiaries as $beneficiary) {
1618
            Database::insert(
1619
                $payoutsTable,
1620
                [
1621
                    'date' => $sale['date'],
1622
                    'payout_date' => getdate(),
1623
                    'sale_id' => intval($saleId),
1624
                    'user_id' => $beneficiary['user_id'],
1625
                    'commission' => number_format((floatval($teachersCommission) * intval($beneficiary['commissions']))/100, 2),
1626
                    'status' => self::PAYOUT_STATUS_PENDING
1627
                ]
1628
            );
1629
        }
1630
    }
1631
1632
    /**
1633
     * Register the users payouts
1634
     * @param int $payoutId The payout ID
1635
     * @param int $status The status to set (-1 to cancel, 0 to pending, 1 to completed)
1636
     * @return array
1637
     */
1638 View Code Duplication
    public function setStatusPayouts($payoutId, $status)
1639
    {
1640
        $payoutsTable = Database::get_main_table(BuyCoursesPlugin::TABLE_PAYPAL_PAYOUTS);
1641
1642
        Database::update(
1643
            $payoutsTable,
1644
            ['status' => intval($status)],
1645
            ['id = ?' => intval($payoutId)]
1646
        );
1647
1648
    }
1649
1650
    /**
1651
     * Gets the stored platform commission params
1652
     * @return array
1653
     */
1654
    public function getPlatformCommission()
1655
    {
1656
        return Database::select(
1657
            '*',
1658
            Database::get_main_table(BuyCoursesPlugin::TABLE_COMMISSION),
1659
            ['id = ?' => 1],
1660
            'first'
1661
        );
1662
    }
1663
1664
    /**
1665
     * Update the platform commission
1666
     * @param int $params platform commission
1667
     * @return int The number of affected rows. Otherwise return false
1668
     */
1669 View Code Duplication
    public function updateCommission($params)
1670
    {
1671
        $commissionTable = Database::get_main_table(BuyCoursesPlugin::TABLE_COMMISSION);
1672
1673
        return Database::update(
1674
            $commissionTable,
1675
            ['commission' => intval($params['commission'])]
1676
        );
1677
    }
1678
1679
    /**
1680
     * Register addicional service
1681
     * @param array params $service
1682
     * @return mixed response
1683
     */
1684
    public function storeService($service)
1685
    {
1686
        $servicesTable = Database::get_main_table(BuyCoursesPlugin::TABLE_SERVICES);
1687
1688
        $return = Database::insert(
1689
            $servicesTable,
1690
            [
1691
                'name' => Security::remove_XSS($service['name']),
1692
                'description' => Security::remove_XSS($service['description']),
1693
                'price' => $service['price'],
1694
                'duration_days' => intval($service['duration_days']),
1695
                'applies_to' => intval($service['applies_to']),
1696
                'owner_id' => intval($service['owner_id']),
1697
                'visibility' => intval($service['visibility']),
1698
                'image' => 'simg.png',
1699
                'video_url' => $service['video_url'],
1700
                'service_information' => $service['service_information']
1701
            ]
1702
        );
1703
1704
        if ($return) {
1705
            $img = str_replace('data:image/png;base64,', '', $service['picture_crop_image_base_64']);
1706
            $img = str_replace(' ', '+', $img);
1707
            $data = base64_decode($img);
1708
            $file = api_get_path(SYS_PLUGIN_PATH).'buycourses/uploads/services/images/simg-'.$return.'.png';
1709
            file_put_contents($file, $data);
1710
1711
            Database::update(
1712
                $servicesTable,
1713
                ['image' => 'simg-'.$return.'.png'],
1714
                ['id = ?' => intval($return)]
1715
            );
1716
            return $return;
1717
        }
1718
1719
        return false;
1720
    }
1721
1722
    /**
1723
     * update a service
1724
     * @param array $service
1725
     * @param integer $id
1726
     * @return mixed response
1727
     */
1728
    public function updateService($service, $id)
1729
    {
1730
        $servicesTable = Database::get_main_table(BuyCoursesPlugin::TABLE_SERVICES);
1731
        if (!empty($service['picture_crop_image_base_64'])) {
1732
            $img = str_replace('data:image/png;base64,', '', $service['picture_crop_image_base_64']);
1733
            $img = str_replace(' ', '+', $img);
1734
            $data = base64_decode($img);
1735
            $file = api_get_path(SYS_PLUGIN_PATH).'buycourses/uploads/services/images/simg-'.$id.'.png';
1736
            file_put_contents($file, $data);
1737
        }
1738
1739
        return Database::update(
1740
            $servicesTable,
1741
            [
1742
                'name' => Security::remove_XSS($service['name']),
1743
                'description' => Security::remove_XSS($service['description']),
1744
                'price' => $service['price'],
1745
                'duration_days' => intval($service['duration_days']),
1746
                'applies_to' => intval($service['applies_to']),
1747
                'owner_id' => intval($service['owner_id']),
1748
                'visibility' => intval($service['visibility']),
1749
                'image' => 'simg-'.$id.'.png',
1750
                'video_url' => $service['video_url'],
1751
                'service_information' => $service['service_information']
1752
            ],
1753
            ['id = ?' => intval($id)]
1754
        );
1755
    }
1756
1757
    /**
1758
     * Remove a service
1759
     * @param int $id The transfer account ID
1760
     * @return int Rows affected. Otherwise return false
1761
     */
1762
    public function deleteService($id)
1763
    {
1764
        return Database::delete(
1765
            Database::get_main_table(self::TABLE_SERVICES),
1766
            ['id = ?' => intval($id)]
1767
        );
1768
    }
1769
1770
    /**
1771
     * List adicional services
1772
     * @param integer $id service id
1773
     * @return array
1774
     */
1775
    public function getServices($id = null)
1776
    {
1777
        $servicesTable = Database::get_main_table(BuyCoursesPlugin::TABLE_SERVICES);
1778
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
1779
1780
        $conditions = null;
1781
        $showData = "all";
1782
1783
        if ($id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $id of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1784
            $conditions = ['WHERE' => ['s.id = ?' => $id]];
1785
            $showData = "first";
1786
        }
1787
1788
        $innerJoins = "INNER JOIN $userTable u ON s.owner_id = u.id";
1789
        $currency = $this->getSelectedCurrency();
1790
        $isoCode = $currency['iso_code'];
1791
        $return = Database::select(
1792
            "s.*, '$isoCode' as currency, u.firstname, u.lastname",
1793
            "$servicesTable s $innerJoins",
1794
            $conditions,
0 ignored issues
show
Bug introduced by
It seems like $conditions defined by null on line 1780 can also be of type null; however, Database::select() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1795
            $showData
1796
        );
1797
1798
        $services = [];
1799
1800
        if ($id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $id of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1801
            $services['id'] = $return['id'];
1802
            $services['name'] = $return['name'];
1803
            $services['description'] = $return['description'];
1804
            $services['price'] = $return['price'];
1805
            $services['currency'] = $return['currency'];
1806
            $services['duration_days'] = $return['duration_days'];
1807
            $services['applies_to'] = $return['applies_to'];
1808
            $services['owner_id'] = $return['owner_id'];
1809
            $services['owner_name'] = api_get_person_name($return['firstname'], $return['lastname']);
1810
            $services['visibility'] = $return['visibility'];
1811
            $services['image'] = $return['image'];
1812
            $services['video_url'] = $return['video_url'];
1813
            $services['service_information'] = $return['service_information'];
1814
1815
            return $services;
1816
        }
1817
1818
        foreach ($return as $index => $service) {
1819
            $services[$index]['id'] = $service['id'];
1820
            $services[$index]['name'] = $service['name'];
1821
            $services[$index]['description'] = $service['description'];
1822
            $services[$index]['price'] = $service['price'];
1823
            $services[$index]['currency'] = $service['currency'];
1824
            $services[$index]['duration_days'] = $service['duration_days'];
1825
            $services[$index]['applies_to'] = $service['applies_to'];
1826
            $services[$index]['owner_id'] = $service['owner_id'];
1827
            $services[$index]['owner_name'] = api_get_person_name($service['firstname'], $service['lastname']);
1828
            $services[$index]['visibility'] = $service['visibility'];
1829
            $services[$index]['image'] = $service['image'];
1830
            $services[$index]['video_url'] = $service['video_url'];
1831
            $services[$index]['service_information'] = $service['service_information'];
1832
        }
1833
1834
        return $services;
1835
    }
1836
1837
    /**
1838
     * Get the statuses for sales
1839
     * @return array
1840
     */
1841
    public function getServiceSaleStatuses()
1842
    {
1843
        return [
1844
            self::SERVICE_STATUS_CANCELLED => $this->get_lang('SaleStatusCancelled'),
1845
            self::SERVICE_STATUS_PENDING => $this->get_lang('SaleStatusPending'),
1846
            self::SERVICE_STATUS_COMPLETED => $this->get_lang('SaleStatusCompleted')
1847
        ];
1848
    }
1849
1850
    /**
1851
     * List services sales
1852
     * @param integer $id service id
1853
     * @param integer $buyerId buyer id
1854
     * @param integer $status status
1855
     * @param integer $nodeType The node Type ( User = 1 , Course = 2 , Session = 3 )
1856
     * @param integer $nodeId the nodeId
1857
     * @param boolean $hot enable hot services
1858
     * @return array
1859
     */
1860
    public function getServiceSale($id = null, $buyerId = null, $status = null, $nodeType = null, $nodeId = null, $hot = false)
1861
    {
1862
        $servicesTable = Database::get_main_table(BuyCoursesPlugin::TABLE_SERVICES);
1863
        $servicesSaleTable = Database::get_main_table(BuyCoursesPlugin::TABLE_SERVICES_SALE);
1864
1865
        $conditions = null;
1866
        $showData = "all";
1867
        $groupBy = "";
1868
1869
        if ($id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $id of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1870
            $conditions = ['WHERE' => ['ss.id = ?' => $id]];
1871
            $showData = "first";
1872
        }
1873
1874
        if ($buyerId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $buyerId of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1875
            $conditions = ['WHERE' => ['ss.buyer_id = ?' => $buyerId], 'ORDER' => 'id ASC'];
1876
        }
1877
1878
        if (is_numeric($status)) {
1879
            $conditions = ['WHERE' => ['ss.status = ?' => $status], 'ORDER' => 'id ASC'];
1880
        }
1881
1882
        if ($id && $buyerId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $id of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
Bug Best Practice introduced by
The expression $buyerId of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1883
            $conditions = ['WHERE' => ['ss.id = ? AND ss.buyer_id = ?' => [$id, $buyerId]], 'ORDER' => 'id ASC'];
1884
        }
1885
1886
        if ($nodeType && $nodeId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $nodeType of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
Bug Best Practice introduced by
The expression $nodeId of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1887
            $conditions = ['WHERE' => ['ss.node_type = ? AND ss.node_id = ?' => [$nodeType, $nodeId]], 'ORDER' => 'id ASC'];
1888
        }
1889
1890
        if ($hot) {
1891
            $hot = "count(ss.service_id) as hot, ";
1892
            $conditions = ['ORDER' => 'hot DESC', 'LIMIT' => '6'];
1893
            $groupBy = "GROUP BY ss.service_id";
1894
            "clean_teacher_files.php";
1895
        }
1896
1897
        $innerJoins = "INNER JOIN $servicesTable s ON ss.service_id = s.id $groupBy";
1898
        $currency = $this->getSelectedCurrency();
1899
        $isoCode = $currency['iso_code'];
1900
        $return = Database::select(
1901
            "ss.*, s.name, s.description, s.price as service_price, s.duration_days, s.applies_to, s.owner_id, s.visibility, s.image, $hot '$isoCode' as currency",
1902
            "$servicesSaleTable ss $innerJoins",
1903
            $conditions,
0 ignored issues
show
Bug introduced by
It seems like $conditions defined by null on line 1865 can also be of type null; however, Database::select() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1904
            $showData
1905
        );
1906
1907
        $servicesSale = [];
1908
1909
        if ($id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $id of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1910
1911
            $owner = api_get_user_info($return['owner_id']);
1912
            $buyer = api_get_user_info($return['buyer_id']);
1913
1914
            $servicesSale['id'] = $return['id'];
1915
            $servicesSale['service']['id'] = $return['service_id'];
1916
            $servicesSale['service']['name'] = $return['name'];
1917
            $servicesSale['service']['description'] = $return['description'];
1918
            $servicesSale['service']['price'] = $return['service_price'];
1919
            $servicesSale['service']['duration_days'] = $return['duration_days'];
1920
            $servicesSale['service']['applies_to'] = $return['applies_to'];
1921
            $servicesSale['service']['owner']['id'] = $return['owner_id'];
1922
            $servicesSale['service']['owner']['name'] = api_get_person_name($owner['firstname'], $owner['lastname']);
1923
            $servicesSale['service']['visibility'] = $return['visibility'];
1924
            $servicesSale['service']['image'] = $return['image'];
1925
            $servicesSale['reference'] = $return['reference'];
1926
            $servicesSale['currency_id'] = $return['currency_id'];
1927
            $servicesSale['currency'] = $return['currency'];
1928
            $servicesSale['price'] = $return['price'];
1929
            $servicesSale['node_type'] = $return['node_type'];
1930
            $servicesSale['node_id'] = $return['node_id'];
1931
            $servicesSale['buyer']['id'] = $buyer['user_id'];
1932
            $servicesSale['buyer']['name'] = api_get_person_name($buyer['firstname'], $buyer['lastname']);
1933
            $servicesSale['buyer']['username'] = $buyer['username'];
1934
            $servicesSale['buy_date'] = $return['buy_date'];
1935
            $servicesSale['date_start'] = $return['date_start'];
1936
            $servicesSale['date_end'] = $return['date_end'];
1937
            $servicesSale['status'] = $return['status'];
1938
            $servicesSale['payment_type'] = $return['payment_type'];
1939
1940
            return $servicesSale;
1941
        }
1942
1943
1944
        foreach ($return as $index => $service) {
1945
1946
            $owner = api_get_user_info($service['owner_id']);
1947
            $buyer = api_get_user_info($service['buyer_id']);
1948
1949
            $servicesSale[$index]['id'] = $service['id'];
1950
            $servicesSale[$index]['service']['id'] = $service['service_id'];
1951
            $servicesSale[$index]['service']['name'] = $service['name'];
1952
            $servicesSale[$index]['service']['description'] = $service['description'];
1953
            $servicesSale[$index]['service']['price'] = $service['service_price'];
1954
            $servicesSale[$index]['service']['duration_days'] = $service['duration_days'];
1955
            $servicesSale[$index]['service']['applies_to'] = $service['applies_to'];
1956
            $servicesSale[$index]['service']['owner']['id'] = $service['owner_id'];
1957
            $servicesSale[$index]['service']['owner']['name'] = api_get_person_name($owner['firstname'], $owner['lastname']);
1958
            $servicesSale[$index]['service']['visibility'] = $service['visibility'];
1959
            $servicesSale[$index]['service']['image'] = $service['image'];
1960
            $servicesSale[$index]['reference'] = $service['reference'];
1961
            $servicesSale[$index]['currency_id'] = $service['currency_id'];
1962
            $servicesSale[$index]['currency'] = $service['currency'];
1963
            $servicesSale[$index]['price'] = $service['price'];
1964
            $servicesSale[$index]['node_type'] = $service['node_type'];
1965
            $servicesSale[$index]['node_id'] = $service['node_id'];
1966
            $servicesSale[$index]['buyer']['id'] = $service['buyer_id'];
1967
            $servicesSale[$index]['buyer']['name'] = api_get_person_name($buyer['firstname'], $buyer['lastname']);
1968
            $servicesSale[$index]['buyer']['username'] = $buyer['username'];
1969
            $servicesSale[$index]['buy_date'] = $service['buy_date'];
1970
            $servicesSale[$index]['date_start'] = $service['date_start'];
1971
            $servicesSale[$index]['date_end'] = $service['date_end'];
1972
            $servicesSale[$index]['status'] = $service['status'];
1973
            $servicesSale[$index]['payment_type'] = $service['payment_type'];
1974
        }
1975
1976
        return $servicesSale;
1977
    }
1978
1979
    /**
1980
     * Update service sale status to cancelled
1981
     * @param int $serviceSaleId The sale ID
1982
     * @return boolean
1983
     */
1984
    public function cancelServiceSale($serviceSaleId)
1985
    {
1986
        $this->updateServiceSaleStatus($serviceSaleId, self::SERVICE_STATUS_CANCELLED);
1987
        return true;
1988
    }
1989
1990
    /**
1991
     * Complete service sale process. Update service sale status to completed
1992
     * @param int $serviceSaleId The service sale ID
1993
     * @return boolean
1994
     */
1995
    public function completeServiceSale($serviceSaleId)
1996
    {
1997
        $serviceSale = $this->getServiceSale($serviceSaleId);
1998
1999
        if ($serviceSale['status'] == self::SERVICE_STATUS_COMPLETED) {
2000
            return true;
2001
        }
2002
2003
        $this->updateServiceSaleStatus($serviceSaleId, self::SERVICE_STATUS_COMPLETED);
2004
2005
        return true;
2006
    }
2007
2008
    /**
2009
     * Lists current service details
2010
     * @param string $name Optional. The name filter
2011
     * @param int $min Optional. The minimum price filter
2012
     * @param int $max Optional. The maximum price filter
2013
     * @param mixed $appliesTo Optional.
2014
     * @return array
2015
     */
2016
    public function getCatalogServiceList($name = null, $min = 0, $max = 0, $appliesTo = '')
2017
    {
2018
        $servicesTable = Database::get_main_table(BuyCoursesPlugin::TABLE_SERVICES);
2019
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
2020
2021
        $whereConditions = [
2022
            's.id <> ? ' => 0
2023
        ];
2024
2025
        if (!empty($name)) {
2026
            $whereConditions['AND s.name LIKE %?%'] = $name;
2027
        }
2028
2029
        if (!empty($min)) {
2030
            $whereConditions['AND s.price >= ?'] = $min;
2031
        }
2032
2033
        if (!empty($max)) {
2034
            $whereConditions['AND s.price <= ?'] = $max;
2035
        }
2036
2037
        if (!$appliesTo == '') {
2038
            $whereConditions['AND s.applies_to = ?'] = $appliesTo;
2039
        }
2040
2041
        $innerJoins = "INNER JOIN $userTable u ON s.owner_id = u.id";
2042
        $currency = $this->getSelectedCurrency();
2043
        $isoCode = $currency['iso_code'];
2044
        $return = Database::select(
2045
            "s.*, '$isoCode' as currency, u.firstname, u.lastname",
2046
            "$servicesTable s $innerJoins",
2047
            ['WHERE' => $whereConditions]
2048
        );
2049
2050
        $services = [];
2051
2052
        foreach ($return as $index => $service) {
2053
            $services[$index]['id'] = $service['id'];
2054
            $services[$index]['name'] = $service['name'];
2055
            $services[$index]['description'] = $service['description'];
2056
            $services[$index]['price'] = $service['price'];
2057
            $services[$index]['currency'] = $service['currency'];
2058
            $services[$index]['duration_days'] = $service['duration_days'];
2059
            $services[$index]['applies_to'] = $service['applies_to'];
2060
            $services[$index]['owner_id'] = $service['owner_id'];
2061
            $services[$index]['owner_name'] = api_get_person_name($service['firstname'], $service['lastname']);
2062
            $services[$index]['visibility'] = $service['visibility'];
2063
            $services[$index]['image'] = api_get_path(WEB_PLUGIN_PATH).'buycourses/uploads/services/images/'.$service['image'];
2064
            $services[$index]['video_url'] = $service['video_url'];
2065
            $services[$index]['service_information'] = $service['service_information'];
2066
        }
2067
2068
        return $services;
2069
2070
    }
2071
2072
    /**
2073
     * Update the service sale status
2074
     * @param int $serviceSaleId The service sale ID
2075
     * @param int $newStatus The new status
2076
     * @return boolean
2077
     */
2078 View Code Duplication
    private function updateServiceSaleStatus($serviceSaleId, $newStatus = self::SERVICE_STATUS_PENDING)
2079
    {
2080
        $serviceSaleTable = Database::get_main_table(self::TABLE_SERVICES_SALE);
2081
2082
        return Database::update(
2083
            $serviceSaleTable,
2084
            ['status' => intval($newStatus)],
2085
            ['id = ?' => intval($serviceSaleId)]
2086
        );
2087
    }
2088
2089
}
2090