Passed
Push — master ( 85f21d...13109f )
by Angel Fernando Quiroz
09:54 queued 17s
created

BuyCoursesPlugin::get_name()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 1
b 0
f 0
1
<?php
2
/* For license terms, see /license.txt */
3
4
use Chamilo\CoreBundle\Entity\Course;
5
use Chamilo\CoreBundle\Entity\Session;
6
use Chamilo\CoreBundle\Entity\User;
7
use Doctrine\ORM\Query\Expr\Join;
8
9
/**
10
 * Plugin class for the BuyCourses plugin.
11
 *
12
 * @author  Jose Angel Ruiz <[email protected]>
13
 * @author  Imanol Losada <[email protected]>
14
 * @author  Alex Aragón <[email protected]>
15
 * @author  Angel Fernando Quiroz Campos <[email protected]>
16
 * @author  José Loguercio Silva  <[email protected]>
17
 * @author  Julio Montoya
18
 */
19
class BuyCoursesPlugin extends Plugin
20
{
21
    const TABLE_PAYPAL = 'plugin_buycourses_paypal_account';
22
    const TABLE_CURRENCY = 'plugin_buycourses_currency';
23
    const TABLE_ITEM = 'plugin_buycourses_item';
24
    const TABLE_ITEM_BENEFICIARY = 'plugin_buycourses_item_rel_beneficiary';
25
    const TABLE_SALE = 'plugin_buycourses_sale';
26
    const TABLE_TRANSFER = 'plugin_buycourses_transfer';
27
    const TABLE_COMMISSION = 'plugin_buycourses_commission';
28
    const TABLE_PAYPAL_PAYOUTS = 'plugin_buycourses_paypal_payouts';
29
    const TABLE_SERVICES = 'plugin_buycourses_services';
30
    const TABLE_SERVICES_SALE = 'plugin_buycourses_service_sale';
31
    const TABLE_CULQI = 'plugin_buycourses_culqi';
32
    const TABLE_GLOBAL_CONFIG = 'plugin_buycourses_global_config';
33
    const TABLE_INVOICE = 'plugin_buycourses_invoices';
34
    const PRODUCT_TYPE_COURSE = 1;
35
    const PRODUCT_TYPE_SESSION = 2;
36
    const PAYMENT_TYPE_PAYPAL = 1;
37
    const PAYMENT_TYPE_TRANSFER = 2;
38
    const PAYMENT_TYPE_CULQI = 3;
39
    const PAYOUT_STATUS_CANCELED = 2;
40
    const PAYOUT_STATUS_PENDING = 0;
41
    const PAYOUT_STATUS_COMPLETED = 1;
42
    const SALE_STATUS_CANCELED = -1;
43
    const SALE_STATUS_PENDING = 0;
44
    const SALE_STATUS_COMPLETED = 1;
45
    const SERVICE_STATUS_PENDING = 0;
46
    const SERVICE_STATUS_COMPLETED = 1;
47
    const SERVICE_STATUS_CANCELLED = -1;
48
    const SERVICE_TYPE_USER = 1;
49
    const SERVICE_TYPE_COURSE = 2;
50
    const SERVICE_TYPE_SESSION = 3;
51
    const SERVICE_TYPE_LP_FINAL_ITEM = 4;
52
    const CULQI_INTEGRATION_TYPE = 'INTEG';
53
    const CULQI_PRODUCTION_TYPE = 'PRODUC';
54
    const TAX_APPLIES_TO_ALL = 1;
55
    const TAX_APPLIES_TO_ONLY_COURSE = 2;
56
    const TAX_APPLIES_TO_ONLY_SESSION = 3;
57
    const TAX_APPLIES_TO_ONLY_SERVICES = 4;
58
    const PAGINATION_PAGE_SIZE = 5;
59
60
    public $isAdminPlugin = true;
61
62
    /**
63
     * BuyCoursesPlugin constructor.
64
     */
65
    public function __construct()
66
    {
67
        parent::__construct(
68
            '5.0',
69
            "
70
                Jose Angel Ruiz - NoSoloRed (original author) <br/>
71
                Francis Gonzales and Yannick Warnier - BeezNest (integration) <br/>
72
                Alex Aragón - BeezNest (Design icons and css styles) <br/>
73
                Imanol Losada - BeezNest (introduction of sessions purchase) <br/>
74
                Angel Fernando Quiroz Campos - BeezNest (cleanup and new reports) <br/>
75
                José Loguercio Silva - BeezNest (Payouts and buy Services) <br/>
76
                Julio Montoya
77
            ",
78
            [
79
                'show_main_menu_tab' => 'boolean',
80
                'public_main_menu_tab' => 'boolean',
81
                'include_sessions' => 'boolean',
82
                'include_services' => 'boolean',
83
                'paypal_enable' => 'boolean',
84
                'transfer_enable' => 'boolean',
85
                'culqi_enable' => 'boolean',
86
                'commissions_enable' => 'boolean',
87
                'unregistered_users_enable' => 'boolean',
88
                'hide_free_text' => 'boolean',
89
                'invoicing_enable' => 'boolean',
90
                'tax_enable' => 'boolean',
91
                'use_currency_symbol' => 'boolean',
92
            ]
93
        );
94
    }
95
96
    /**
97
     * @return BuyCoursesPlugin
98
     */
99
    public static function create()
100
    {
101
        static $result = null;
102
103
        return $result ? $result : $result = new self();
104
    }
105
106
    /**
107
     * Check if plugin is enabled.
108
     *
109
     * @param bool $checkEnabled Check if, additionnally to being installed, the plugin is enabled
110
     *
111
     * @return bool
112
     */
113
    public function isEnabled($checkEnabled = false)
114
    {
115
        return $this->get('paypal_enable') || $this->get('transfer_enable') || $this->get('culqi_enable');
116
    }
117
118
    /**
119
     * This method creates the tables required to this plugin.
120
     *
121
     * @throws \Doctrine\DBAL\Exception
122
     */
123
    public function install()
124
    {
125
        $tablesToBeCompared = [
126
            self::TABLE_PAYPAL,
127
            self::TABLE_TRANSFER,
128
            self::TABLE_CULQI,
129
            self::TABLE_ITEM_BENEFICIARY,
130
            self::TABLE_ITEM,
131
            self::TABLE_SALE,
132
            self::TABLE_CURRENCY,
133
            self::TABLE_COMMISSION,
134
            self::TABLE_PAYPAL_PAYOUTS,
135
            self::TABLE_SERVICES,
136
            self::TABLE_SERVICES_SALE,
137
            self::TABLE_GLOBAL_CONFIG,
138
            self::TABLE_INVOICE,
139
        ];
140
        $em = Database::getManager();
141
        $cn = $em->getConnection();
142
        $sm = $cn->createSchemaManager();
143
        $tables = $sm->tablesExist($tablesToBeCompared);
144
145
        if ($tables) {
146
            return false;
147
        }
148
149
        require_once api_get_path(SYS_PLUGIN_PATH).'BuyCourses/database.php';
150
    }
151
152
    /**
153
     * This method drops the plugin tables.
154
     */
155
    public function uninstall()
156
    {
157
        $tablesToBeDeleted = [
158
            self::TABLE_PAYPAL,
159
            self::TABLE_TRANSFER,
160
            self::TABLE_CULQI,
161
            self::TABLE_ITEM_BENEFICIARY,
162
            self::TABLE_ITEM,
163
            self::TABLE_SALE,
164
            self::TABLE_CURRENCY,
165
            self::TABLE_COMMISSION,
166
            self::TABLE_PAYPAL_PAYOUTS,
167
            self::TABLE_SERVICES_SALE,
168
            self::TABLE_SERVICES,
169
            self::TABLE_GLOBAL_CONFIG,
170
            self::TABLE_INVOICE,
171
        ];
172
173
        foreach ($tablesToBeDeleted as $tableToBeDeleted) {
174
            $table = Database::get_main_table($tableToBeDeleted);
175
            $sql = "DROP TABLE IF EXISTS $table";
176
            Database::query($sql);
177
        }
178
        $this->manageTab(false);
179
    }
180
181
    public function update()
182
    {
183
        $table = self::TABLE_GLOBAL_CONFIG;
184
        $sql = "SHOW COLUMNS FROM $table WHERE Field = 'global_tax_perc'";
185
        $res = Database::query($sql);
186
187
        if (0 === Database::num_rows($res)) {
188
            $sql = "ALTER TABLE $table ADD (
189
                sale_email varchar(255) NOT NULL,
190
                global_tax_perc int unsigned NOT NULL,
191
                tax_applies_to int unsigned NOT NULL,
192
                tax_name varchar(255) NOT NULL,
193
                seller_name varchar(255) NOT NULL,
194
                seller_id varchar(255) NOT NULL,
195
                seller_address varchar(255) NOT NULL,
196
                seller_email varchar(255) NOT NULL,
197
                next_number_invoice int unsigned NOT NULL,
198
                invoice_series varchar(255) NOT NULL
199
            )";
200
            $res = Database::query($sql);
201
            if (!$res) {
202
                echo Display::return_message($this->get_lang('ErrorUpdateFieldDB'), 'warning');
203
            }
204
        }
205
206
        $table = self::TABLE_ITEM;
207
        $sql = "SHOW COLUMNS FROM $table WHERE Field = 'tax_perc'";
208
        $res = Database::query($sql);
209
210
        if (0 === Database::num_rows($res)) {
211
            $sql = "ALTER TABLE $table ADD tax_perc int unsigned NULL";
212
            $res = Database::query($sql);
213
            if (!$res) {
214
                echo Display::return_message($this->get_lang('ErrorUpdateFieldDB'), 'warning');
215
            }
216
        }
217
218
        $table = self::TABLE_SERVICES;
219
        $sql = "SHOW COLUMNS FROM $table WHERE Field = 'tax_perc'";
220
        $res = Database::query($sql);
221
222
        if (0 === Database::num_rows($res)) {
223
            $sql = "ALTER TABLE $table ADD tax_perc int unsigned NULL";
224
            $res = Database::query($sql);
225
            if (!$res) {
226
                echo Display::return_message($this->get_lang('ErrorUpdateFieldDB'), 'warning');
227
            }
228
        }
229
230
        $table = self::TABLE_SALE;
231
        $sql = "SHOW COLUMNS FROM $table WHERE Field = 'tax_perc'";
232
        $res = Database::query($sql);
233
234
        if (0 === Database::num_rows($res)) {
235
            $sql = "ALTER TABLE $table ADD (
236
                price_without_tax decimal(10,2) NULL,
237
                tax_perc int unsigned NULL,
238
                tax_amount decimal(10,2) NULL,
239
                invoice int unsigned NULL
240
            )";
241
            $res = Database::query($sql);
242
            if (!$res) {
243
                echo Display::return_message($this->get_lang('ErrorUpdateFieldDB'), 'warning');
244
            }
245
        }
246
247
        $table = self::TABLE_SERVICES_SALE;
248
        $sql = "SHOW COLUMNS FROM $table WHERE Field = 'tax_perc'";
249
        $res = Database::query($sql);
250
251
        if (0 === Database::num_rows($res)) {
252
            $sql = "ALTER TABLE $table ADD (
253
                price_without_tax decimal(10,2) NULL,
254
                tax_perc int unsigned NULL,
255
                tax_amount decimal(10,2) NULL,
256
                invoice int unsigned NULL
257
            )";
258
            $res = Database::query($sql);
259
            if (!$res) {
260
                echo Display::return_message($this->get_lang('ErrorUpdateFieldDB'), 'warning');
261
            }
262
        }
263
264
        $table = self::TABLE_INVOICE;
265
        $sql = "CREATE TABLE IF NOT EXISTS $table (
266
            id int unsigned NOT NULL AUTO_INCREMENT,
267
            sale_id int unsigned NOT NULL,
268
            is_service int unsigned NOT NULL,
269
            num_invoice int unsigned NOT NULL,
270
            year int(4) unsigned NOT NULL,
271
            serie varchar(255) NOT NULL,
272
            date_invoice datetime NOT NULL,
273
            PRIMARY KEY (id)
274
        )";
275
        Database::query($sql);
276
277
        Display::addFlash(
278
            Display::return_message(
279
                $this->get_lang('Updated'),
280
                'info',
281
                false
282
            )
283
        );
284
285
        $fieldlabel = 'buycourses_company';
286
        $fieldtype = '1';
287
        $fieldtitle = $this->get_lang('Company');
288
        $fielddefault = '';
289
        UserManager::create_extra_field($fieldlabel, $fieldtype, $fieldtitle, $fielddefault);
290
291
        $fieldlabel = 'buycourses_vat';
292
        $fieldtype = '1';
293
        $fieldtitle = $this->get_lang('VAT');
294
        $fielddefault = '';
295
        UserManager::create_extra_field($fieldlabel, $fieldtype, $fieldtitle, $fielddefault);
296
297
        $fieldlabel = 'buycourses_address';
298
        $fieldtype = '1';
299
        $fieldtitle = $this->get_lang('Address');
300
        $fielddefault = '';
301
        UserManager::create_extra_field($fieldlabel, $fieldtype, $fieldtitle, $fielddefault);
302
303
        $table = self::TABLE_TRANSFER;
304
        $sql = "ALTER TABLE $table CHANGE COLUMN name title varchar(255)";
305
        Database::query($sql);
306
        $table = self::TABLE_SERVICES;
307
        $sql = "ALTER TABLE $table CHANGE COLUMN name title varchar(255)";
308
        Database::query($sql);
309
310
        header('Location: '.api_get_path(WEB_PLUGIN_PATH).'BuyCourses');
311
        exit;
312
    }
313
314
    /**
315
     * This function verify if the plugin is enable and return the price info for a course or session in the new grid
316
     * catalog for 1.11.x , the main purpose is to show if a course or session is in sale it shows in the main platform
317
     * course catalog so the old BuyCourses plugin catalog can be deprecated.
318
     *
319
     * @param int $productId   course or session id
320
     * @param int $productType course or session type
321
     *
322
     * @return mixed bool|string html
323
     */
324
    public function buyCoursesForGridCatalogValidator($productId, $productType)
325
    {
326
        $return = [];
327
        $paypal = 'true' === $this->get('paypal_enable');
328
        $transfer = 'true' === $this->get('transfer_enable');
329
        $hideFree = 'true' === $this->get('hide_free_text');
330
331
        if ($paypal || $transfer) {
332
            $item = $this->getItemByProduct($productId, $productType);
333
            $html = '<div class="buycourses-price">';
334
            if ($item) {
335
                $html .= '<span class="label label-primary label-price">
336
                            <strong>'.$item['total_price_formatted'].'</strong>
337
                          </span>';
338
                $return['verificator'] = true;
339
            } else {
340
                if (false == $hideFree) {
341
                    $html .= '<span class="label label-primary label-free">
342
                                <strong>'.$this->get_lang('Free').'</strong>
343
                              </span>';
344
                }
345
                $return['verificator'] = false;
346
            }
347
            $html .= '</div>';
348
            $return['html'] = $html;
349
350
            return $return;
351
        }
352
353
        return false;
354
    }
355
356
    /**
357
     * Return the buyCourses plugin button to buy the course.
358
     *
359
     * @param int $productId
360
     * @param int $productType
361
     *
362
     * @return string $html
363
     */
364
    public function returnBuyCourseButton($productId, $productType)
365
    {
366
        $productId = (int) $productId;
367
        $productType = (int) $productType;
368
        $url = api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/process.php?i='.$productId.'&t='.$productType;
369
        $html = '<a class="btn btn--success btn-sm" title="'.$this->get_lang('Buy').'" href="'.$url.'">'.
370
            Display::getMdiIcon('cart').'</a>';
371
372
        return $html;
373
    }
374
375
    /**
376
     * Get the currency for sales.
377
     *
378
     * @return array The selected currency. Otherwise return false
379
     */
380
    public function getSelectedCurrency()
381
    {
382
        return Database::select(
383
            '*',
384
            Database::get_main_table(self::TABLE_CURRENCY),
385
            [
386
                'where' => ['status = ?' => true],
387
            ],
388
            'first'
389
        );
390
    }
391
392
    /**
393
     * Get a list of currencies.
394
     *
395
     * @return array The currencies. Otherwise return false
396
     */
397
    public function getCurrencies()
398
    {
399
        return Database::select(
400
            '*',
401
            Database::get_main_table(self::TABLE_CURRENCY)
402
        );
403
    }
404
405
    /**
406
     * Save the selected currency.
407
     *
408
     * @param int $selectedId The currency Id
409
     */
410
    public function saveCurrency($selectedId)
411
    {
412
        $currencyTable = Database::get_main_table(
413
            self::TABLE_CURRENCY
414
        );
415
416
        Database::update(
417
            $currencyTable,
418
            ['status' => 0]
419
        );
420
        Database::update(
421
            $currencyTable,
422
            ['status' => 1],
423
            ['id = ?' => (int) $selectedId]
424
        );
425
    }
426
427
    /**
428
     * Save the PayPal configuration params.
429
     *
430
     * @param array $params
431
     *
432
     * @return int Rows affected. Otherwise return false
433
     */
434
    public function savePaypalParams($params)
435
    {
436
        return Database::update(
437
            Database::get_main_table(self::TABLE_PAYPAL),
438
            [
439
                'username' => $params['username'],
440
                'password' => $params['password'],
441
                'signature' => $params['signature'],
442
                'sandbox' => isset($params['sandbox']),
443
            ],
444
            ['id = ?' => 1]
445
        );
446
    }
447
448
    /**
449
     * Gets the stored PayPal params.
450
     *
451
     * @return array
452
     */
453
    public function getPaypalParams()
454
    {
455
        return Database::select(
456
            '*',
457
            Database::get_main_table(self::TABLE_PAYPAL),
458
            ['id = ?' => 1],
459
            'first'
460
        );
461
    }
462
463
    /**
464
     * Save a transfer account information.
465
     *
466
     * @param array $params The transfer account
467
     *
468
     * @return int Rows affected. Otherwise return false
469
     */
470
    public function saveTransferAccount($params)
471
    {
472
        return Database::insert(
473
            Database::get_main_table(self::TABLE_TRANSFER),
474
            [
475
                'name' => $params['tname'],
476
                'account' => $params['taccount'],
477
                'swift' => $params['tswift'],
478
            ]
479
        );
480
    }
481
482
    /**
483
     * Get a list of transfer accounts.
484
     *
485
     * @return array
486
     */
487
    public function getTransferAccounts()
488
    {
489
        return Database::select(
490
            '*',
491
            Database::get_main_table(self::TABLE_TRANSFER)
492
        );
493
    }
494
495
    /**
496
     * Remove a transfer account.
497
     *
498
     * @param int $id The transfer account ID
499
     *
500
     * @return int Rows affected. Otherwise return false
501
     */
502
    public function deleteTransferAccount($id)
503
    {
504
        return Database::delete(
505
            Database::get_main_table(self::TABLE_TRANSFER),
506
            ['id = ?' => (int) $id]
507
        );
508
    }
509
510
    /**
511
     * Get registered item data.
512
     *
513
     * @param int $itemId The item ID
514
     *
515
     * @return array
516
     */
517
    public function getItem($itemId)
518
    {
519
        return Database::select(
520
            '*',
521
            Database::get_main_table(self::TABLE_ITEM),
522
            [
523
                'where' => ['id = ?' => (int) $itemId],
524
            ],
525
            'first'
526
        );
527
    }
528
529
    /**
530
     * Get the item data.
531
     *
532
     * @param int $productId The item ID
533
     * @param int $itemType  The item type
534
     *
535
     * @return array
536
     */
537
    public function getItemByProduct($productId, $itemType)
538
    {
539
        $buyItemTable = Database::get_main_table(self::TABLE_ITEM);
540
        $buyCurrencyTable = Database::get_main_table(self::TABLE_CURRENCY);
541
542
        $fakeItemFrom = "
543
            $buyItemTable i
544
            INNER JOIN $buyCurrencyTable c
545
                ON i.currency_id = c.id
546
        ";
547
548
        $product = Database::select(
549
            ['i.*', 'c.iso_code'],
550
            $fakeItemFrom,
551
            [
552
                'where' => [
553
                    'i.product_id = ? AND i.product_type = ?' => [
554
                        (int) $productId,
555
                        (int) $itemType,
556
                    ],
557
                ],
558
            ],
559
            'first'
560
        );
561
562
        if (empty($product)) {
563
            return false;
564
        }
565
566
        $this->setPriceSettings($product, self::TAX_APPLIES_TO_ONLY_COURSE);
567
568
        return $product;
569
    }
570
571
    /**
572
     * List courses details from the configuration page.
573
     *
574
     * @return array
575
     */
576
    public function getCourseList($first, $maxResults)
577
    {
578
        return $this->getCourses($first, $maxResults);
579
    }
580
581
    /**
582
     * Lists current user session details, including each session course details.
583
     *
584
     * It can return the number of rows when $typeResult is 'count'.
585
     *
586
     * @param int    $start
587
     * @param int    $end
588
     * @param string $name       Optional. The name filter.
589
     * @param int    $min        Optional. The minimum price filter.
590
     * @param int    $max        Optional. The maximum price filter.
591
     * @param string $typeResult Optional. 'all', 'first' or 'count'.
592
     *
593
     * @return array|int
594
     */
595
    public function getCatalogSessionList($start, $end, $name = null, $min = 0, $max = 0, $typeResult = 'all')
596
    {
597
        $sessions = $this->filterSessionList($start, $end, $name, $min, $max, $typeResult);
598
599
        if ('count' === $typeResult) {
600
            return $sessions;
601
        }
602
603
        $sessionCatalog = [];
604
        /** @var Session $session */
605
        foreach ($sessions as $session) {
606
            $sessionCourses = $session->getCourses();
607
608
            if (empty($sessionCourses)) {
609
                continue;
610
            }
611
612
            $item = $this->getItemByProduct(
613
                $session->getId(),
614
                self::PRODUCT_TYPE_SESSION
615
            );
616
617
            if (empty($item)) {
618
                continue;
619
            }
620
621
            $sessionData = $this->getSessionInfo($session->getId());
622
            $sessionData['coaches'] = $session->getGeneralCoaches()->map(fn(User $coach) => $coach->getFullname());
623
            $sessionData['enrolled'] = $this->getUserStatusForSession(
624
                api_get_user_id(),
625
                $session
626
            );
627
            $sessionData['courses'] = [];
628
629
            foreach ($sessionCourses as $sessionCourse) {
630
                $course = $sessionCourse->getCourse();
631
632
                $sessionCourseData = [
633
                    'title' => $course->getTitle(),
634
                    'coaches' => [],
635
                ];
636
637
                $userCourseSubscriptions = $session->getSessionRelCourseRelUsersByStatus(
638
                    $course,
639
                    Chamilo\CoreBundle\Entity\Session::COURSE_COACH
640
                );
641
642
                foreach ($userCourseSubscriptions as $userCourseSubscription) {
643
                    $user = $userCourseSubscription->getUser();
644
                    $sessionCourseData['coaches'][] = $user->getCompleteName();
645
                }
646
                $sessionData['courses'][] = $sessionCourseData;
647
            }
648
649
            $sessionCatalog[] = $sessionData;
650
        }
651
652
        return $sessionCatalog;
653
    }
654
655
    /**
656
     * Lists current user course details.
657
     *
658
     * @param string $name Optional. The name filter
659
     * @param int    $min  Optional. The minimum price filter
660
     * @param int    $max  Optional. The maximum price filter
661
     *
662
     * @return array
663
     */
664
    public function getCatalogCourseList($first, $pageSize, $name = null, $min = 0, $max = 0, $typeResult = 'all')
665
    {
666
        $courses = $this->filterCourseList($first, $pageSize, $name, $min, $max, $typeResult);
667
668
        if ('count' === $typeResult) {
669
            return $courses;
670
        }
671
672
        if (empty($courses)) {
673
            return [];
674
        }
675
676
        $courseCatalog = [];
677
        foreach ($courses as $course) {
678
            $item = $this->getItemByProduct(
679
                $course->getId(),
680
                self::PRODUCT_TYPE_COURSE
681
            );
682
683
            if (empty($item)) {
684
                continue;
685
            }
686
687
            $courseItem = [
688
                'id' => $course->getId(),
689
                'title' => $course->getTitle(),
690
                'code' => $course->getCode(),
691
                'course_img' => null,
692
                'item' => $item,
693
                'teachers' => [],
694
                'enrolled' => $this->getUserStatusForCourse(api_get_user_id(), $course),
695
            ];
696
697
            foreach ($course->getTeachers() as $courseUser) {
698
                $teacher = $courseUser->getUser();
699
                $courseItem['teachers'][] = $teacher->getCompleteName();
700
            }
701
702
            // Check images
703
            $possiblePath = api_get_path(SYS_COURSE_PATH);
704
            $possiblePath .= $course->getDirectory();
705
            $possiblePath .= '/course-pic.png';
706
707
            if (file_exists($possiblePath)) {
708
                $courseItem['course_img'] = api_get_path(WEB_COURSE_PATH).$course->getDirectory().'/course-pic.png';
709
            }
710
            $courseCatalog[] = $courseItem;
711
        }
712
713
        return $courseCatalog;
714
    }
715
716
    /**
717
     * @param $price
718
     * @param $isoCode
719
     *
720
     * @return string
721
     */
722
    public function getPriceWithCurrencyFromIsoCode($price, $isoCode)
723
    {
724
        $useSymbol = 'true' === $this->get('use_currency_symbol');
725
726
        $result = $isoCode.' '.$price;
727
        if ($useSymbol) {
728
            if ('BRL' === $isoCode) {
729
                $symbol = 'R$';
730
            } else {
731
                $symbol = Symfony\Component\Intl\Intl::getCurrencyBundle()->getCurrencySymbol($isoCode);
732
            }
733
            $result = $symbol.' '.$price;
734
        }
735
736
        return $result;
737
    }
738
739
    /**
740
     * Get course info.
741
     *
742
     * @param int $courseId The course ID
743
     *
744
     * @return array
745
     */
746
    public function getCourseInfo($courseId)
747
    {
748
        $entityManager = Database::getManager();
749
        $course = $entityManager->find('ChamiloCoreBundle:Course', $courseId);
750
751
        if (empty($course)) {
752
            return [];
753
        }
754
755
        $item = $this->getItemByProduct(
756
            $course->getId(),
757
            self::PRODUCT_TYPE_COURSE
758
        );
759
760
        if (empty($item)) {
761
            return [];
762
        }
763
764
        $courseDescription = $entityManager->getRepository('ChamiloCourseBundle:CCourseDescription')
765
            ->findOneBy(
766
                [
767
                    'cId' => $course->getId(),
768
                    'sessionId' => 0,
769
                ],
770
                [
771
                    'descriptionType' => 'ASC',
772
                ]
773
            );
774
775
        $globalParameters = $this->getGlobalParameters();
776
        $courseInfo = [
777
            'id' => $course->getId(),
778
            'title' => $course->getTitle(),
779
            'description' => $courseDescription ? $courseDescription->getContent() : null,
780
            'code' => $course->getCode(),
781
            'visual_code' => $course->getVisualCode(),
782
            'teachers' => [],
783
            'item' => $item,
784
            'tax_name' => $globalParameters['tax_name'],
785
            'tax_enable' => $this->checkTaxEnabledInProduct(self::TAX_APPLIES_TO_ONLY_COURSE),
786
            'course_img' => null,
787
        ];
788
789
        $courseTeachers = $course->getTeachersSubscriptions();
790
791
        foreach ($courseTeachers as $teachers) {
792
            $user = $teachers->getUser();
793
            $teacher['id'] = $user->getId();
794
            $teacher['name'] = $user->getCompleteName();
795
            $courseInfo['teachers'][] = $teacher;
796
        }
797
798
        $possiblePath = api_get_path(SYS_COURSE_PATH);
799
        $possiblePath .= $course->getDirectory();
800
        $possiblePath .= '/course-pic.png';
801
802
        if (file_exists($possiblePath)) {
803
            $courseInfo['course_img'] = api_get_path(WEB_COURSE_PATH).$course->getDirectory().'/course-pic.png';
804
        }
805
806
        return $courseInfo;
807
    }
808
809
    /**
810
     * Get session info.
811
     *
812
     * @param array $sessionId The session ID
813
     *
814
     * @return array
815
     */
816
    public function getSessionInfo($sessionId)
817
    {
818
        $entityManager = Database::getManager();
819
        $session = $entityManager->find('ChamiloCoreBundle:Session', $sessionId);
820
821
        if (empty($session)) {
822
            return [];
823
        }
824
825
        $item = $this->getItemByProduct($session->getId(), self::PRODUCT_TYPE_SESSION);
826
827
        if (empty($item)) {
828
            return [];
829
        }
830
831
        $sessionDates = SessionManager::parseSessionDates($session);
832
833
        $globalParameters = $this->getGlobalParameters();
834
        $sessionInfo = [
835
            'id' => $session->getId(),
836
            'name' => $session->getTitle(),
837
            'description' => $session->getDescription(),
838
            'dates' => $sessionDates,
839
            'courses' => [],
840
            'tax_name' => $globalParameters['tax_name'],
841
            'tax_enable' => $this->checkTaxEnabledInProduct(self::TAX_APPLIES_TO_ONLY_SESSION),
842
            'image' => null,
843
            'nbrCourses' => $session->getNbrCourses(),
844
            'nbrUsers' => $session->getNbrUsers(),
845
            'item' => $item,
846
        ];
847
848
        $fieldValue = new ExtraFieldValue('session');
849
        $sessionImage = $fieldValue->get_values_by_handler_and_field_variable(
850
            $session->getId(),
851
            'image'
852
        );
853
854
        if (!empty($sessionImage)) {
855
            $sessionInfo['image'] = api_get_path(WEB_UPLOAD_PATH).$sessionImage['value'];
856
        }
857
858
        $sessionCourses = $session->getCourses();
859
        foreach ($sessionCourses as $sessionCourse) {
860
            $course = $sessionCourse->getCourse();
861
            $sessionCourseData = [
862
                'title' => $course->getTitle(),
863
                'coaches' => [],
864
            ];
865
866
            $userCourseSubscriptions = $session->getSessionRelCourseRelUsersByStatus(
867
                $course,
868
                Chamilo\CoreBundle\Entity\Session::COURSE_COACH
869
            );
870
871
            foreach ($userCourseSubscriptions as $userCourseSubscription) {
872
                $user = $userCourseSubscription->getUser();
873
                $coaches['id'] = $user->getUserId();
874
                $coaches['name'] = $user->getCompleteName();
875
                $sessionCourseData['coaches'][] = $coaches;
876
            }
877
878
            $sessionInfo['courses'][] = $sessionCourseData;
879
        }
880
881
        return $sessionInfo;
882
    }
883
884
    /**
885
     * Register a sale.
886
     *
887
     * @param int $itemId      The product ID
888
     * @param int $paymentType The payment type
889
     *
890
     * @return bool
891
     */
892
    public function registerSale($itemId, $paymentType)
893
    {
894
        if (!in_array(
895
            $paymentType,
896
            [self::PAYMENT_TYPE_PAYPAL, self::PAYMENT_TYPE_TRANSFER, self::PAYMENT_TYPE_CULQI]
897
        )
898
        ) {
899
            return false;
900
        }
901
902
        $entityManager = Database::getManager();
903
        $item = $this->getItem($itemId);
904
905
        if (empty($item)) {
906
            return false;
907
        }
908
909
        $productName = '';
910
        if (self::PRODUCT_TYPE_COURSE == $item['product_type']) {
911
            $course = $entityManager->find('ChamiloCoreBundle:Course', $item['product_id']);
912
913
            if (empty($course)) {
914
                return false;
915
            }
916
917
            $productName = $course->getTitle();
918
        } elseif (self::PRODUCT_TYPE_SESSION == $item['product_type']) {
919
            $session = $entityManager->find('ChamiloCoreBundle:Session', $item['product_id']);
920
921
            if (empty($session)) {
922
                return false;
923
            }
924
925
            $productName = $session->getTitle();
926
        }
927
928
        $price = $item['price'];
929
        $priceWithoutTax = null;
930
        $taxPerc = null;
931
        $taxAmount = 0;
932
        $taxEnable = 'true' === $this->get('tax_enable');
933
        $globalParameters = $this->getGlobalParameters();
934
        $taxAppliesTo = $globalParameters['tax_applies_to'];
935
936
        if ($taxEnable &&
937
            (
938
                self::TAX_APPLIES_TO_ALL == $taxAppliesTo ||
939
                (self::TAX_APPLIES_TO_ONLY_COURSE == $taxAppliesTo && self::PRODUCT_TYPE_COURSE == $item['product_type']) ||
940
                (self::TAX_APPLIES_TO_ONLY_SESSION == $taxAppliesTo && self::PRODUCT_TYPE_SESSION == $item['product_type'])
941
            )
942
        ) {
943
            $priceWithoutTax = $item['price'];
944
            $globalTaxPerc = $globalParameters['global_tax_perc'];
945
            $precision = 2;
946
            $taxPerc = is_null($item['tax_perc']) ? $globalTaxPerc : $item['tax_perc'];
947
            $taxAmount = round($priceWithoutTax * $taxPerc / 100, $precision);
948
            $price = $priceWithoutTax + $taxAmount;
949
        }
950
951
        $values = [
952
            'reference' => $this->generateReference(
953
                api_get_user_id(),
954
                $item['product_type'],
955
                $item['product_id']
956
            ),
957
            'currency_id' => $item['currency_id'],
958
            'date' => api_get_utc_datetime(),
959
            'user_id' => api_get_user_id(),
960
            'product_type' => $item['product_type'],
961
            'product_name' => $productName,
962
            'product_id' => $item['product_id'],
963
            'price' => $price,
964
            'price_without_tax' => $priceWithoutTax,
965
            'tax_perc' => $taxPerc,
966
            'tax_amount' => $taxAmount,
967
            'status' => self::SALE_STATUS_PENDING,
968
            'payment_type' => (int) $paymentType,
969
        ];
970
971
        return Database::insert(self::TABLE_SALE, $values);
972
    }
973
974
    /**
975
     * Get sale data by ID.
976
     *
977
     * @param int $saleId The sale ID
978
     *
979
     * @return array
980
     */
981
    public function getSale($saleId)
982
    {
983
        return Database::select(
984
            '*',
985
            Database::get_main_table(self::TABLE_SALE),
986
            [
987
                'where' => ['id = ?' => (int) $saleId],
988
            ],
989
            'first'
990
        );
991
    }
992
993
    /**
994
     * Get a list of sales by the payment type.
995
     *
996
     * @param int $paymentType The payment type to filter (default : Paypal)
997
     *
998
     * @return array The sale list. Otherwise return false
999
     */
1000
    public function getSaleListByPaymentType($paymentType = self::PAYMENT_TYPE_PAYPAL)
1001
    {
1002
        $saleTable = Database::get_main_table(self::TABLE_SALE);
1003
        $currencyTable = Database::get_main_table(self::TABLE_CURRENCY);
1004
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
1005
1006
        $innerJoins = "
1007
            INNER JOIN $currencyTable c ON s.currency_id = c.id
1008
            INNER JOIN $userTable u ON s.user_id = u.id
1009
        ";
1010
1011
        return Database::select(
1012
            ['c.iso_code', 'u.firstname', 'u.lastname', 's.*'],
1013
            "$saleTable s $innerJoins",
1014
            [
1015
                'where' => [
1016
                    's.payment_type = ? AND s.status = ?' => [
1017
                        (int) $paymentType,
1018
                        self::SALE_STATUS_COMPLETED,
1019
                    ],
1020
                ],
1021
                'order' => 'id DESC',
1022
            ]
1023
        );
1024
    }
1025
1026
    /**
1027
     * Get data of sales.
1028
     *
1029
     * @param int $saleId    The sale id
1030
     * @param int $isService Check if a service
1031
     *
1032
     * @return array The sale data
1033
     */
1034
    public function getDataSaleInvoice($saleId, $isService)
1035
    {
1036
        if ($isService) {
1037
            $sale = $this->getServiceSale($saleId);
1038
            $sale['reference'] = $sale['reference'];
1039
            $sale['product_name'] = $sale['service']['name'];
1040
            $sale['payment_type'] = $sale['payment_type'];
1041
            $sale['user_id'] = $sale['buyer']['id'];
1042
            $sale['date'] = $sale['buy_date'];
1043
        } else {
1044
            $sale = $this->getSale($saleId);
1045
        }
1046
1047
        return $sale;
1048
    }
1049
1050
    /**
1051
     * Get data of invoice.
1052
     *
1053
     * @param int $saleId    The sale id
1054
     * @param int $isService Check if a service
1055
     *
1056
     * @return array The invoice data
1057
     */
1058
    public function getDataInvoice($saleId, $isService)
1059
    {
1060
        return Database::select(
1061
            '*',
1062
            Database::get_main_table(self::TABLE_INVOICE),
1063
            [
1064
                'where' => [
1065
                    'sale_id = ? AND ' => (int) $saleId,
1066
                    'is_service = ?' => (int) $isService,
1067
                ],
1068
            ],
1069
            'first'
1070
        );
1071
    }
1072
1073
    /**
1074
     * Get invoice numbering.
1075
     *
1076
     * @param int $saleId    The sale id
1077
     * @param int $isService Check if a service
1078
     *
1079
     * @return string
1080
     */
1081
    public function getNumInvoice($saleId, $isService)
1082
    {
1083
        $dataInvoice = $this->getDataInvoice($saleId, $isService);
1084
        if (empty($dataInvoice)) {
1085
            return '-';
1086
        }
1087
1088
        return $dataInvoice['serie'].$dataInvoice['year'].'/'.$dataInvoice['num_invoice'];
1089
    }
1090
1091
    /**
1092
     * Get currency data by ID.
1093
     *
1094
     * @param int $currencyId The currency ID
1095
     *
1096
     * @return array
1097
     */
1098
    public function getCurrency($currencyId)
1099
    {
1100
        return Database::select(
1101
            '*',
1102
            Database::get_main_table(self::TABLE_CURRENCY),
1103
            [
1104
                'where' => ['id = ?' => (int) $currencyId],
1105
            ],
1106
            'first'
1107
        );
1108
    }
1109
1110
    /**
1111
     * Complete sale process. Update sale status to completed.
1112
     *
1113
     * @param int $saleId The sale ID
1114
     *
1115
     * @return bool
1116
     */
1117
    public function completeSale($saleId)
1118
    {
1119
        $sale = $this->getSale($saleId);
1120
1121
        if (self::SALE_STATUS_COMPLETED == $sale['status']) {
1122
            return true;
1123
        }
1124
1125
        $saleIsCompleted = false;
1126
        switch ($sale['product_type']) {
1127
            case self::PRODUCT_TYPE_COURSE:
1128
                $saleIsCompleted = CourseManager::subscribeUser($sale['user_id'], $sale['product_id']);
1129
                break;
1130
            case self::PRODUCT_TYPE_SESSION:
1131
                SessionManager::subscribeUsersToSession(
1132
                    $sale['product_id'],
1133
                    [$sale['user_id']],
1134
                    api_get_session_visibility($sale['product_id']),
1135
                    false
1136
                );
1137
1138
                $saleIsCompleted = true;
1139
                break;
1140
        }
1141
1142
        if ($saleIsCompleted) {
1143
            $this->updateSaleStatus($sale['id'], self::SALE_STATUS_COMPLETED);
1144
            if ('true' === $this->get('invoicing_enable')) {
1145
                $this->setInvoice($sale['id']);
1146
            }
1147
        }
1148
1149
        return $saleIsCompleted;
1150
    }
1151
1152
    /**
1153
     * Update sale status to canceled.
1154
     *
1155
     * @param int $saleId The sale ID
1156
     */
1157
    public function cancelSale($saleId)
1158
    {
1159
        $this->updateSaleStatus($saleId, self::SALE_STATUS_CANCELED);
1160
    }
1161
1162
    /**
1163
     * Get payment types.
1164
     *
1165
     * @return array
1166
     */
1167
    public function getPaymentTypes()
1168
    {
1169
        return [
1170
            self::PAYMENT_TYPE_PAYPAL => 'PayPal',
1171
            self::PAYMENT_TYPE_TRANSFER => $this->get_lang('BankTransfer'),
1172
            self::PAYMENT_TYPE_CULQI => 'Culqi',
1173
        ];
1174
    }
1175
1176
    /**
1177
     * Register a invoice.
1178
     *
1179
     * @param int $saleId    The sale ID
1180
     * @param int $isService The service type to filter (default : 0)
1181
     */
1182
    public function setInvoice($saleId, $isService = 0)
1183
    {
1184
        $invoiceTable = Database::get_main_table(self::TABLE_INVOICE);
1185
        $year = date('Y');
1186
1187
        $globalParameters = $this->getGlobalParameters();
1188
        $numInvoice = $globalParameters['next_number_invoice'];
1189
        $serie = $globalParameters['invoice_series'];
1190
1191
        if (empty($numInvoice)) {
1192
            $item = Database::select(
1193
                ['MAX(num_invoice) AS num_invoice'],
1194
                $invoiceTable,
1195
                [
1196
                    'where' => ['year = ?' => $year],
1197
                ],
1198
                'first'
1199
            );
1200
1201
            $numInvoice = 1;
1202
            if (false !== $item) {
1203
                $numInvoice = (int) ($item['num_invoice'] + 1);
1204
            }
1205
        } else {
1206
            Database::update(
1207
                Database::get_main_table(self::TABLE_GLOBAL_CONFIG),
1208
                ['next_number_invoice' => 0],
1209
                ['id = ?' => 1]
1210
            );
1211
        }
1212
1213
        Database::insert(
1214
            $invoiceTable,
1215
            [
1216
                'sale_id' => $saleId,
1217
                'is_service' => $isService,
1218
                'num_invoice' => $numInvoice,
1219
                'year' => $year,
1220
                'serie' => $serie,
1221
                'date_invoice' => api_get_utc_datetime(),
1222
            ]
1223
        );
1224
1225
        // Record invoice in the sales table
1226
        $table = Database::get_main_table(self::TABLE_SALE);
1227
        if (!empty($isService)) {
1228
            $table = Database::get_main_table(self::TABLE_SERVICES_SALE);
1229
        }
1230
1231
        Database::update(
1232
            $table,
1233
            ['invoice' => 1],
1234
            ['id = ?' => $saleId]
1235
        );
1236
    }
1237
1238
    /**
1239
     * Get Tax's types.
1240
     *
1241
     * @return array
1242
     */
1243
    public function getTaxAppliesTo()
1244
    {
1245
        return [
1246
            self::TAX_APPLIES_TO_ALL => $this->get_lang('AllCoursesSessionsAndServices'),
1247
            self::TAX_APPLIES_TO_ONLY_COURSE => $this->get_lang('OnlyCourses'),
1248
            self::TAX_APPLIES_TO_ONLY_SESSION => $this->get_lang('OnlySessions'),
1249
            self::TAX_APPLIES_TO_ONLY_SERVICES => $this->get_lang('OnlyServices'),
1250
        ];
1251
    }
1252
1253
    /**
1254
     * Get a list of sales by the status.
1255
     *
1256
     * @param int $status The status to filter
1257
     *
1258
     * @return array The sale list. Otherwise return false
1259
     */
1260
    public function getSaleListByStatus($status = self::SALE_STATUS_PENDING)
1261
    {
1262
        $saleTable = Database::get_main_table(self::TABLE_SALE);
1263
        $currencyTable = Database::get_main_table(self::TABLE_CURRENCY);
1264
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
1265
1266
        $innerJoins = "
1267
            INNER JOIN $currencyTable c ON s.currency_id = c.id
1268
            INNER JOIN $userTable u ON s.user_id = u.id
1269
        ";
1270
1271
        return Database::select(
1272
            ['c.iso_code', 'u.firstname', 'u.lastname', 'u.email', 's.*'],
1273
            "$saleTable s $innerJoins",
1274
            [
1275
                'where' => ['s.status = ?' => (int) $status],
1276
                'order' => 'id DESC',
1277
            ]
1278
        );
1279
    }
1280
1281
    /**
1282
     * Get the list statuses for sales.
1283
     *
1284
     * @param string $dateStart
1285
     * @param string $dateEnd
1286
     *
1287
     * @throws Exception
1288
     *
1289
     * @return array
1290
     */
1291
    public function getSaleListReport($dateStart = null, $dateEnd = null)
1292
    {
1293
        $saleTable = Database::get_main_table(self::TABLE_SALE);
1294
        $currencyTable = Database::get_main_table(self::TABLE_CURRENCY);
1295
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
1296
        $innerJoins = "
1297
            INNER JOIN $currencyTable c ON s.currency_id = c.id
1298
            INNER JOIN $userTable u ON s.user_id = u.id
1299
        ";
1300
        $list = Database::select(
1301
            ['c.iso_code', 'u.firstname', 'u.lastname', 'u.email', 's.*'],
1302
            "$saleTable s $innerJoins",
1303
            [
1304
                'order' => 'id DESC',
1305
            ]
1306
        );
1307
        $listExportTemp = [];
1308
        $listExport = [];
1309
        $textStatus = null;
1310
        $paymentTypes = $this->getPaymentTypes();
1311
        $productTypes = $this->getProductTypes();
1312
        foreach ($list as $item) {
1313
            $statusSaleOrder = $item['status'];
1314
            switch ($statusSaleOrder) {
1315
                case 0:
1316
                    $textStatus = $this->get_lang('SaleStatusPending');
1317
                    break;
1318
                case 1:
1319
                    $textStatus = $this->get_lang('SaleStatusCompleted');
1320
                    break;
1321
                case -1:
1322
                    $textStatus = $this->get_lang('SaleStatusCanceled');
1323
                    break;
1324
            }
1325
            $dateFilter = new DateTime($item['date']);
1326
            $listExportTemp[] = [
1327
                'id' => $item['id'],
1328
                'reference' => $item['reference'],
1329
                'status' => $textStatus,
1330
                'status_filter' => $item['status'],
1331
                'date' => $dateFilter->format('Y-m-d'),
1332
                'order_time' => $dateFilter->format('H:i:s'),
1333
                'price' => $item['iso_code'].' '.$item['price'],
1334
                'product_type' => $productTypes[$item['product_type']],
1335
                'product_name' => $item['product_name'],
1336
                'payment_type' => $paymentTypes[$item['payment_type']],
1337
                'complete_user_name' => api_get_person_name($item['firstname'], $item['lastname']),
1338
                'email' => $item['email'],
1339
            ];
1340
        }
1341
        $listExport[] = [
1342
            get_lang('Number'),
1343
            $this->get_lang('OrderStatus'),
1344
            $this->get_lang('OrderDate'),
1345
            $this->get_lang('OrderTime'),
1346
            $this->get_lang('PaymentMethod'),
1347
            $this->get_lang('SalePrice'),
1348
            $this->get_lang('ProductType'),
1349
            $this->get_lang('ProductName'),
1350
            $this->get_lang('UserName'),
1351
            get_lang('Email'),
1352
        ];
1353
        //Validation Export
1354
        $dateStart = strtotime($dateStart);
1355
        $dateEnd = strtotime($dateEnd);
1356
        foreach ($listExportTemp as $item) {
1357
            $dateFilter = strtotime($item['date']);
1358
            if (($dateFilter >= $dateStart) && ($dateFilter <= $dateEnd)) {
1359
                $listExport[] = [
1360
                    'id' => $item['id'],
1361
                    'status' => $item['status'],
1362
                    'date' => $item['date'],
1363
                    'order_time' => $item['order_time'],
1364
                    'payment_type' => $item['payment_type'],
1365
                    'price' => $item['price'],
1366
                    'product_type' => $item['product_type'],
1367
                    'product_name' => $item['product_name'],
1368
                    'complete_user_name' => $item['complete_user_name'],
1369
                    'email' => $item['email'],
1370
                ];
1371
            }
1372
        }
1373
1374
        return $listExport;
1375
    }
1376
1377
    /**
1378
     * Get the statuses for sales.
1379
     *
1380
     * @return array
1381
     */
1382
    public function getSaleStatuses()
1383
    {
1384
        return [
1385
            self::SALE_STATUS_CANCELED => $this->get_lang('SaleStatusCanceled'),
1386
            self::SALE_STATUS_PENDING => $this->get_lang('SaleStatusPending'),
1387
            self::SALE_STATUS_COMPLETED => $this->get_lang('SaleStatusCompleted'),
1388
        ];
1389
    }
1390
1391
    /**
1392
     * Get the statuses for Payouts.
1393
     *
1394
     * @return array
1395
     */
1396
    public function getPayoutStatuses()
1397
    {
1398
        return [
1399
            self::PAYOUT_STATUS_CANCELED => $this->get_lang('PayoutStatusCanceled'),
1400
            self::PAYOUT_STATUS_PENDING => $this->get_lang('PayoutStatusPending'),
1401
            self::PAYOUT_STATUS_COMPLETED => $this->get_lang('PayoutStatusCompleted'),
1402
        ];
1403
    }
1404
1405
    /**
1406
     * Get the list of product types.
1407
     *
1408
     * @return array
1409
     */
1410
    public function getProductTypes()
1411
    {
1412
        return [
1413
            self::PRODUCT_TYPE_COURSE => get_lang('Course'),
1414
            self::PRODUCT_TYPE_SESSION => get_lang('Session'),
1415
        ];
1416
    }
1417
1418
    /**
1419
     * Get the list of service types.
1420
     *
1421
     * @return array
1422
     */
1423
    public function getServiceTypes()
1424
    {
1425
        return [
1426
            self::SERVICE_TYPE_USER => get_lang('User'),
1427
            self::SERVICE_TYPE_COURSE => get_lang('Course'),
1428
            self::SERVICE_TYPE_SESSION => get_lang('Session'),
1429
            self::SERVICE_TYPE_LP_FINAL_ITEM => get_lang('TemplateTitleCertificate'),
1430
        ];
1431
    }
1432
1433
    /**
1434
     * Generates a random text (used for order references).
1435
     *
1436
     * @param int  $length    Optional. Length of characters
1437
     * @param bool $lowercase Optional. Include lowercase characters
1438
     * @param bool $uppercase Optional. Include uppercase characters
1439
     * @param bool $numbers   Optional. Include numbers
1440
     *
1441
     * @return string
1442
     */
1443
    public static function randomText(
1444
        $length = 6,
1445
        $lowercase = true,
1446
        $uppercase = true,
1447
        $numbers = true
1448
    ) {
1449
        $salt = $lowercase ? 'abchefghknpqrstuvwxyz' : '';
1450
        $salt .= $uppercase ? 'ACDEFHKNPRSTUVWXYZ' : '';
1451
        $salt .= $numbers ? (strlen($salt) ? '2345679' : '0123456789') : '';
1452
1453
        if (0 == strlen($salt)) {
1454
            return '';
1455
        }
1456
1457
        $str = '';
1458
1459
        srand((float) microtime() * 1000000);
1460
1461
        for ($i = 0; $i < $length; $i++) {
1462
            $numbers = rand(0, strlen($salt) - 1);
1463
            $str .= substr($salt, $numbers, 1);
1464
        }
1465
1466
        return $str;
1467
    }
1468
1469
    /**
1470
     * Generates an order reference.
1471
     *
1472
     * @param int $userId      The user ID
1473
     * @param int $productType The course/session type
1474
     * @param int $productId   The course/session ID
1475
     *
1476
     * @return string
1477
     */
1478
    public function generateReference($userId, $productType, $productId)
1479
    {
1480
        return vsprintf(
1481
            '%d-%d-%d-%s',
1482
            [$userId, $productType, $productId, self::randomText()]
1483
        );
1484
    }
1485
1486
    /**
1487
     * Get a list of sales by the user.
1488
     *
1489
     * @param string $term The search term
1490
     *
1491
     * @return array The sale list. Otherwise return false
1492
     */
1493
    public function getSaleListByUser($term)
1494
    {
1495
        $term = trim($term);
1496
1497
        if (empty($term)) {
1498
            return [];
1499
        }
1500
1501
        $saleTable = Database::get_main_table(self::TABLE_SALE);
1502
        $currencyTable = Database::get_main_table(self::TABLE_CURRENCY);
1503
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
1504
        $innerJoins = "
1505
            INNER JOIN $currencyTable c ON s.currency_id = c.id
1506
            INNER JOIN $userTable u ON s.user_id = u.id
1507
        ";
1508
1509
        return Database::select(
1510
            ['c.iso_code', 'u.firstname', 'u.lastname', 'u.email', 's.*'],
1511
            "$saleTable s $innerJoins",
1512
            [
1513
                'where' => [
1514
                    'u.username LIKE %?% OR ' => $term,
1515
                    'u.lastname LIKE %?% OR ' => $term,
1516
                    'u.firstname LIKE %?%' => $term,
1517
                ],
1518
                'order' => 'id DESC',
1519
            ]
1520
        );
1521
    }
1522
1523
    /**
1524
     * Get a list of sales by the user id.
1525
     *
1526
     * @param int $id The user id
1527
     *
1528
     * @return array The sale list. Otherwise return false
1529
     */
1530
    public function getSaleListByUserId($id)
1531
    {
1532
        if (empty($id)) {
1533
            return [];
1534
        }
1535
1536
        $saleTable = Database::get_main_table(self::TABLE_SALE);
1537
        $currencyTable = Database::get_main_table(self::TABLE_CURRENCY);
1538
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
1539
1540
        $innerJoins = "
1541
            INNER JOIN $currencyTable c ON s.currency_id = c.id
1542
            INNER JOIN $userTable u ON s.user_id = u.id
1543
        ";
1544
1545
        return Database::select(
1546
            ['c.iso_code', 'u.firstname', 'u.lastname', 's.*'],
1547
            "$saleTable s $innerJoins",
1548
            [
1549
                'where' => [
1550
                    'u.id = ? AND s.status = ?' => [(int) $id, self::SALE_STATUS_COMPLETED],
1551
                ],
1552
                'order' => 'id DESC',
1553
            ]
1554
        );
1555
    }
1556
1557
    /**
1558
     * Get a list of sales by date range.
1559
     *
1560
     * @param string $dateStart
1561
     * @param string $dateEnd
1562
     *
1563
     * @return array The sale list. Otherwise return false
1564
     */
1565
    public function getSaleListByDate($dateStart, $dateEnd)
1566
    {
1567
        $dateStart = trim($dateStart);
1568
        $dateEnd = trim($dateEnd);
1569
        if (empty($dateStart)) {
1570
            return [];
1571
        }
1572
        if (empty($dateEnd)) {
1573
            return [];
1574
        }
1575
        $saleTable = Database::get_main_table(self::TABLE_SALE);
1576
        $currencyTable = Database::get_main_table(self::TABLE_CURRENCY);
1577
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
1578
        $innerJoins = "
1579
            INNER JOIN $currencyTable c ON s.currency_id = c.id
1580
            INNER JOIN $userTable u ON s.user_id = u.id
1581
        ";
1582
1583
        return Database::select(
1584
            ['c.iso_code', 'u.firstname', 'u.lastname', 'u.email', 's.*'],
1585
            "$saleTable s $innerJoins",
1586
            [
1587
                'where' => [
1588
                    's.date BETWEEN ? AND ' => $dateStart,
1589
                    ' ? ' => $dateEnd,
1590
                ],
1591
                'order' => 'id DESC',
1592
            ]
1593
        );
1594
    }
1595
1596
    /**
1597
     * Get a list of sales by the user Email.
1598
     *
1599
     * @param string $term The search term
1600
     *
1601
     * @return array The sale list. Otherwise return false
1602
     */
1603
    public function getSaleListByEmail($term)
1604
    {
1605
        $term = trim($term);
1606
        if (empty($term)) {
1607
            return [];
1608
        }
1609
        $saleTable = Database::get_main_table(self::TABLE_SALE);
1610
        $currencyTable = Database::get_main_table(self::TABLE_CURRENCY);
1611
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
1612
        $innerJoins = "
1613
            INNER JOIN $currencyTable c ON s.currency_id = c.id
1614
            INNER JOIN $userTable u ON s.user_id = u.id
1615
        ";
1616
1617
        return Database::select(
1618
            ['c.iso_code', 'u.firstname', 'u.lastname', 'u.email', 's.*'],
1619
            "$saleTable s $innerJoins",
1620
            [
1621
                'where' => [
1622
                    'u.email LIKE %?% ' => $term,
1623
                ],
1624
                'order' => 'id DESC',
1625
            ]
1626
        );
1627
    }
1628
1629
    /**
1630
     * Convert the course info to array with necessary course data for save item.
1631
     *
1632
     * @param array $defaultCurrency Optional. Currency data
1633
     *
1634
     * @return array
1635
     */
1636
    public function getCourseForConfiguration(Course $course, $defaultCurrency = null)
1637
    {
1638
        $courseItem = [
1639
            'item_id' => null,
1640
            'course_id' => $course->getId(),
1641
            'course_visual_code' => $course->getVisualCode(),
1642
            'course_code' => $course->getCode(),
1643
            'course_title' => $course->getTitle(),
1644
            'course_directory' => $course->getDirectory(),
1645
            'course_visibility' => $course->getVisibility(),
1646
            'visible' => false,
1647
            'currency' => empty($defaultCurrency) ? null : $defaultCurrency['iso_code'],
1648
            'price' => 0.00,
1649
            'tax_perc' => null,
1650
        ];
1651
1652
        $item = $this->getItemByProduct($course->getId(), self::PRODUCT_TYPE_COURSE);
1653
1654
        if (false !== $item) {
1655
            $courseItem['item_id'] = $item['id'];
1656
            $courseItem['visible'] = true;
1657
            $courseItem['currency'] = $item['iso_code'];
1658
            $courseItem['price'] = $item['price'];
1659
            $courseItem['tax_perc'] = $item['tax_perc'];
1660
        }
1661
1662
        return $courseItem;
1663
    }
1664
1665
    /**
1666
     * Convert the session info to array with necessary session data for save item.
1667
     *
1668
     * @param Session $session         The session data
1669
     * @param array   $defaultCurrency Optional. Currency data
1670
     *
1671
     * @return array
1672
     */
1673
    public function getSessionForConfiguration(Session $session, $defaultCurrency = null)
1674
    {
1675
        $buyItemTable = Database::get_main_table(self::TABLE_ITEM);
1676
        $buyCurrencyTable = Database::get_main_table(self::TABLE_CURRENCY);
1677
1678
        $fakeItemFrom = "
1679
            $buyItemTable i
1680
            INNER JOIN $buyCurrencyTable c ON i.currency_id = c.id
1681
        ";
1682
1683
        $sessionItem = [
1684
            'item_id' => null,
1685
            'session_id' => $session->getId(),
1686
            'session_name' => $session->getTitle(),
1687
            'session_visibility' => $session->getVisibility(),
1688
            'session_display_start_date' => null,
1689
            'session_display_end_date' => null,
1690
            'visible' => false,
1691
            'currency' => empty($defaultCurrency) ? null : $defaultCurrency['iso_code'],
1692
            'price' => 0.00,
1693
            'tax_perc' => null,
1694
        ];
1695
1696
        $displayStartDate = $session->getDisplayStartDate();
1697
1698
        if (!empty($displayStartDate)) {
1699
            $sessionItem['session_display_start_date'] = api_format_date(
1700
                $session->getDisplayStartDate()->format('Y-m-d h:i:s')
1701
            );
1702
        }
1703
1704
        $displayEndDate = $session->getDisplayEndDate();
1705
1706
        if (!empty($displayEndDate)) {
1707
            $sessionItem['session_display_end_date'] = api_format_date(
1708
                $session->getDisplayEndDate()->format('Y-m-d h:i:s'),
1709
                DATE_TIME_FORMAT_LONG_24H
1710
            );
1711
        }
1712
1713
        $item = Database::select(
1714
            ['i.*', 'c.iso_code'],
1715
            $fakeItemFrom,
1716
            [
1717
                'where' => [
1718
                    'i.product_id = ? AND ' => $session->getId(),
1719
                    'i.product_type = ?' => self::PRODUCT_TYPE_SESSION,
1720
                ],
1721
            ],
1722
            'first'
1723
        );
1724
1725
        if (false !== $item) {
1726
            $sessionItem['item_id'] = $item['id'];
1727
            $sessionItem['visible'] = true;
1728
            $sessionItem['currency'] = $item['iso_code'];
1729
            $sessionItem['price'] = $item['price'];
1730
            $sessionItem['tax_perc'] = $item['tax_perc'];
1731
        }
1732
1733
        return $sessionItem;
1734
    }
1735
1736
    /**
1737
     * Get all beneficiaries for a item.
1738
     *
1739
     * @param int $itemId The item ID
1740
     *
1741
     * @return array The beneficiaries. Otherwise return false
1742
     */
1743
    public function getItemBeneficiaries($itemId)
1744
    {
1745
        $beneficiaryTable = Database::get_main_table(self::TABLE_ITEM_BENEFICIARY);
1746
1747
        return Database::select(
1748
            '*',
1749
            $beneficiaryTable,
1750
            [
1751
                'where' => [
1752
                    'item_id = ?' => (int) $itemId,
1753
                ],
1754
            ]
1755
        );
1756
    }
1757
1758
    /**
1759
     * Delete a item with its beneficiaries.
1760
     *
1761
     * @param int $itemId The item ID
1762
     *
1763
     * @return int The number of affected rows. Otherwise return false
1764
     */
1765
    public function deleteItem($itemId)
1766
    {
1767
        $itemTable = Database::get_main_table(self::TABLE_ITEM);
1768
        $affectedRows = Database::delete(
1769
            $itemTable,
1770
            ['id = ?' => (int) $itemId]
1771
        );
1772
1773
        if (!$affectedRows) {
1774
            return false;
1775
        }
1776
1777
        return $this->deleteItemBeneficiaries($itemId);
1778
    }
1779
1780
    /**
1781
     * Register a item.
1782
     *
1783
     * @param array $itemData The item data
1784
     *
1785
     * @return int The item ID. Otherwise return false
1786
     */
1787
    public function registerItem(array $itemData)
1788
    {
1789
        $itemTable = Database::get_main_table(self::TABLE_ITEM);
1790
1791
        return Database::insert($itemTable, $itemData);
1792
    }
1793
1794
    /**
1795
     * Update the item data by product.
1796
     *
1797
     * @param array $itemData    The item data to be updated
1798
     * @param int   $productId   The product ID
1799
     * @param int   $productType The type of product
1800
     *
1801
     * @return int The number of affected rows. Otherwise return false
1802
     */
1803
    public function updateItem(array $itemData, $productId, $productType)
1804
    {
1805
        $itemTable = Database::get_main_table(self::TABLE_ITEM);
1806
1807
        return Database::update(
1808
            $itemTable,
1809
            $itemData,
1810
            [
1811
                'product_id = ? AND ' => (int) $productId,
1812
                'product_type' => $productType,
1813
            ]
1814
        );
1815
    }
1816
1817
    /**
1818
     * Remove all beneficiaries for a item.
1819
     *
1820
     * @param int $itemId The user ID
1821
     *
1822
     * @return int The number of affected rows. Otherwise return false
1823
     */
1824
    public function deleteItemBeneficiaries($itemId)
1825
    {
1826
        $beneficiaryTable = Database::get_main_table(self::TABLE_ITEM_BENEFICIARY);
1827
1828
        return Database::delete(
1829
            $beneficiaryTable,
1830
            ['item_id = ?' => (int) $itemId]
1831
        );
1832
    }
1833
1834
    /**
1835
     * Register the beneficiaries users with the sale of item.
1836
     *
1837
     * @param int   $itemId  The item ID
1838
     * @param array $userIds The beneficiary user ID and Teachers commissions if enabled
1839
     */
1840
    public function registerItemBeneficiaries($itemId, array $userIds)
1841
    {
1842
        $beneficiaryTable = Database::get_main_table(self::TABLE_ITEM_BENEFICIARY);
1843
1844
        $this->deleteItemBeneficiaries($itemId);
1845
1846
        foreach ($userIds as $userId => $commissions) {
1847
            Database::insert(
1848
                $beneficiaryTable,
1849
                [
1850
                    'item_id' => (int) $itemId,
1851
                    'user_id' => (int) $userId,
1852
                    'commissions' => (int) $commissions,
1853
                ]
1854
            );
1855
        }
1856
    }
1857
1858
    /**
1859
     * Check if a course is valid for sale.
1860
     *
1861
     * @param Course $course The course
1862
     *
1863
     * @return bool
1864
     */
1865
    public function isValidCourse(Course $course)
1866
    {
1867
        $courses = $this->getCourses();
1868
1869
        foreach ($courses as $_c) {
1870
            if ($_c->getCode() === $course->getCode()) {
1871
                return true;
1872
            }
1873
        }
1874
1875
        return false;
1876
    }
1877
1878
    /**
1879
     * Gets the beneficiaries with commissions and current paypal accounts by sale.
1880
     *
1881
     * @param int $saleId The sale ID
1882
     *
1883
     * @return array
1884
     */
1885
    public function getBeneficiariesBySale($saleId)
1886
    {
1887
        $sale = $this->getSale($saleId);
1888
        $item = $this->getItemByProduct($sale['product_id'], $sale['product_type']);
1889
        $itemBeneficiaries = $this->getItemBeneficiaries($item['id']);
1890
1891
        return $itemBeneficiaries;
1892
    }
1893
1894
    /**
1895
     * gets all payouts.
1896
     *
1897
     * @param int $status   - default 0 - pending
1898
     * @param int $payoutId - for get an individual payout if want all then false
1899
     * @param int $userId
1900
     *
1901
     * @return array
1902
     */
1903
    public function getPayouts(
1904
        $status = self::PAYOUT_STATUS_PENDING,
1905
        $payoutId = false,
1906
        $userId = false
1907
    ) {
1908
        $condition = ($payoutId) ? 'AND p.id = '.((int) $payoutId) : '';
1909
        $condition2 = ($userId) ? ' AND p.user_id = '.((int) $userId) : '';
1910
        $typeResult = ($condition) ? 'first' : 'all';
1911
        $payoutsTable = Database::get_main_table(self::TABLE_PAYPAL_PAYOUTS);
1912
        $saleTable = Database::get_main_table(self::TABLE_SALE);
1913
        $currencyTable = Database::get_main_table(self::TABLE_CURRENCY);
1914
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
1915
        $extraFieldTable = Database::get_main_table(TABLE_EXTRA_FIELD);
1916
        $extraFieldValues = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
1917
1918
        $paypalExtraField = Database::select(
1919
            "*",
1920
            $extraFieldTable,
1921
            [
1922
                'where' => ['variable = ?' => 'paypal'],
1923
            ],
1924
            'first'
1925
        );
1926
1927
        if (!$paypalExtraField) {
1928
            return false;
1929
        }
1930
1931
        $innerJoins = "
1932
            INNER JOIN $userTable u ON p.user_id = u.id
1933
            INNER JOIN $saleTable s ON s.id = p.sale_id
1934
            INNER JOIN $currencyTable c ON s.currency_id = c.id
1935
            LEFT JOIN  $extraFieldValues efv ON p.user_id = efv.item_id
1936
            AND field_id = ".((int) $paypalExtraField['id'])."
1937
        ";
1938
1939
        $payouts = Database::select(
1940
            "p.* , u.firstname, u.lastname, efv.value as paypal_account, s.reference as sale_reference, s.price as item_price, c.iso_code",
1941
            "$payoutsTable p $innerJoins",
1942
            [
1943
                'where' => ['p.status = ? '.$condition.' '.$condition2 => $status],
1944
            ],
1945
            $typeResult
1946
        );
1947
1948
        return $payouts;
1949
    }
1950
1951
    /**
1952
     * Verify if the beneficiary have a paypal account.
1953
     *
1954
     * @param int $userId
1955
     *
1956
     * @return true if the user have a paypal account, false if not
1957
     */
1958
    public function verifyPaypalAccountByBeneficiary($userId)
1959
    {
1960
        $extraFieldTable = Database::get_main_table(TABLE_EXTRA_FIELD);
1961
        $extraFieldValues = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
1962
1963
        $paypalExtraField = Database::select(
1964
            '*',
1965
            $extraFieldTable,
1966
            [
1967
                'where' => ['variable = ?' => 'paypal'],
1968
            ],
1969
            'first'
1970
        );
1971
1972
        if (!$paypalExtraField) {
1973
            return false;
1974
        }
1975
1976
        $paypalFieldId = $paypalExtraField['id'];
1977
        $paypalAccount = Database::select(
1978
            'value',
1979
            $extraFieldValues,
1980
            [
1981
                'where' => ['field_id = ? AND item_id = ?' => [(int) $paypalFieldId, (int) $userId]],
1982
            ],
1983
            'first'
1984
        );
1985
1986
        if (!$paypalAccount) {
1987
            return false;
1988
        }
1989
1990
        if ('' === $paypalAccount['value']) {
1991
            return false;
1992
        }
1993
1994
        return true;
1995
    }
1996
1997
    /**
1998
     * Register the users payouts.
1999
     *
2000
     * @param int $saleId The sale ID
2001
     *
2002
     * @return array
2003
     */
2004
    public function storePayouts($saleId)
2005
    {
2006
        $payoutsTable = Database::get_main_table(self::TABLE_PAYPAL_PAYOUTS);
2007
        $platformCommission = $this->getPlatformCommission();
2008
2009
        $sale = $this->getSale($saleId);
2010
        $commission = (int) $platformCommission['commission'];
2011
        $teachersCommission = number_format(
2012
            (floatval($sale['price']) * $commission) / 100,
2013
            2
2014
        );
2015
2016
        $beneficiaries = $this->getBeneficiariesBySale($saleId);
2017
        foreach ($beneficiaries as $beneficiary) {
2018
            $beneficiaryCommission = (int) $beneficiary['commissions'];
2019
            Database::insert(
2020
                $payoutsTable,
2021
                [
2022
                    'date' => $sale['date'],
2023
                    'payout_date' => getdate(),
2024
                    'sale_id' => (int) $saleId,
2025
                    'user_id' => $beneficiary['user_id'],
2026
                    'commission' => number_format(
2027
                        (floatval($teachersCommission) * $beneficiaryCommission) / 100,
2028
                        2
2029
                    ),
2030
                    'status' => self::PAYOUT_STATUS_PENDING,
2031
                ]
2032
            );
2033
        }
2034
    }
2035
2036
    /**
2037
     * Register the users payouts.
2038
     *
2039
     * @param int $payoutId The payout ID
2040
     * @param int $status   The status to set (-1 to cancel, 0 to pending, 1 to completed)
2041
     *
2042
     * @return array
2043
     */
2044
    public function setStatusPayouts($payoutId, $status)
2045
    {
2046
        $payoutsTable = Database::get_main_table(self::TABLE_PAYPAL_PAYOUTS);
2047
2048
        Database::update(
2049
            $payoutsTable,
2050
            ['status' => (int) $status],
2051
            ['id = ?' => (int) $payoutId]
2052
        );
2053
    }
2054
2055
    /**
2056
     * Gets the stored platform commission params.
2057
     *
2058
     * @return array
2059
     */
2060
    public function getPlatformCommission()
2061
    {
2062
        return Database::select(
2063
            '*',
2064
            Database::get_main_table(self::TABLE_COMMISSION),
2065
            ['id = ?' => 1],
2066
            'first'
2067
        );
2068
    }
2069
2070
    /**
2071
     * Update the platform commission.
2072
     *
2073
     * @param int $params platform commission
2074
     *
2075
     * @return int The number of affected rows. Otherwise return false
2076
     */
2077
    public function updateCommission($params)
2078
    {
2079
        $commissionTable = Database::get_main_table(self::TABLE_COMMISSION);
2080
2081
        return Database::update(
2082
            $commissionTable,
2083
            ['commission' => (int) $params['commission']]
2084
        );
2085
    }
2086
2087
    /**
2088
     * Register additional service.
2089
     *
2090
     * @param array $service params
2091
     *
2092
     * @return mixed response
2093
     */
2094
    public function storeService($service)
2095
    {
2096
        $servicesTable = Database::get_main_table(self::TABLE_SERVICES);
2097
2098
        $return = Database::insert(
2099
            $servicesTable,
2100
            [
2101
                'title' => Security::remove_XSS($service['name']),
2102
                'description' => Security::remove_XSS($service['description']),
2103
                'price' => $service['price'],
2104
                'tax_perc' => '' != $service['tax_perc'] ? (int) $service['tax_perc'] : null,
2105
                'duration_days' => (int) $service['duration_days'],
2106
                'applies_to' => (int) $service['applies_to'],
2107
                'owner_id' => (int) $service['owner_id'],
2108
                'visibility' => (int) $service['visibility'],
2109
                'image' => '',
2110
                'video_url' => $service['video_url'],
2111
                'service_information' => $service['service_information'],
2112
            ]
2113
        );
2114
2115
        if ($return && !empty($service['picture_crop_image_base_64']) &&
2116
            !empty($service['picture_crop_result'])
2117
        ) {
2118
            $img = str_replace('data:image/png;base64,', '', $service['picture_crop_image_base_64']);
2119
            $img = str_replace(' ', '+', $img);
2120
            $data = base64_decode($img);
2121
            $file = api_get_path(SYS_PLUGIN_PATH).'BuyCourses/uploads/services/images/simg-'.$return.'.png';
2122
            file_put_contents($file, $data);
2123
2124
            Database::update(
2125
                $servicesTable,
2126
                ['image' => 'simg-'.$return.'.png'],
2127
                ['id = ?' => (int) $return]
2128
            );
2129
        }
2130
2131
        return $return;
2132
    }
2133
2134
    /**
2135
     * update a service.
2136
     *
2137
     * @param array $service
2138
     * @param int   $id
2139
     *
2140
     * @return mixed response
2141
     */
2142
    public function updateService($service, $id)
2143
    {
2144
        $servicesTable = Database::get_main_table(self::TABLE_SERVICES);
2145
        if (!empty($service['picture_crop_image_base_64'])) {
2146
            $img = str_replace('data:image/png;base64,', '', $service['picture_crop_image_base_64']);
2147
            $img = str_replace(' ', '+', $img);
2148
            $data = base64_decode($img);
2149
            $file = api_get_path(SYS_PLUGIN_PATH).'BuyCourses/uploads/services/images/simg-'.$id.'.png';
2150
            file_put_contents($file, $data);
2151
        }
2152
2153
        return Database::update(
2154
            $servicesTable,
2155
            [
2156
                'title' => Security::remove_XSS($service['name']),
2157
                'description' => Security::remove_XSS($service['description']),
2158
                'price' => $service['price'],
2159
                'tax_perc' => '' != $service['tax_perc'] ? (int) $service['tax_perc'] : null,
2160
                'duration_days' => (int) $service['duration_days'],
2161
                'applies_to' => (int) $service['applies_to'],
2162
                'owner_id' => (int) $service['owner_id'],
2163
                'visibility' => (int) $service['visibility'],
2164
                'image' => 'simg-'.$id.'.png',
2165
                'video_url' => $service['video_url'],
2166
                'service_information' => $service['service_information'],
2167
            ],
2168
            ['id = ?' => (int) $id]
2169
        );
2170
    }
2171
2172
    /**
2173
     * Remove a service.
2174
     *
2175
     * @param int $id The transfer account ID
2176
     *
2177
     * @return int Rows affected. Otherwise return false
2178
     */
2179
    public function deleteService($id)
2180
    {
2181
        Database::delete(
2182
            Database::get_main_table(self::TABLE_SERVICES_SALE),
2183
            ['service_id = ?' => (int) $id]
2184
        );
2185
2186
        return Database::delete(
2187
            Database::get_main_table(self::TABLE_SERVICES),
2188
            ['id = ?' => (int) $id]
2189
        );
2190
    }
2191
2192
    /**
2193
     * @param array $product
2194
     * @param int   $productType
2195
     *
2196
     * @return bool
2197
     */
2198
    public function setPriceSettings(&$product, $productType)
2199
    {
2200
        if (empty($product)) {
2201
            return false;
2202
        }
2203
2204
        $taxPerc = null;
2205
        $priceWithoutTax = $product['price'];
2206
        $product['total_price'] = $product['price'];
2207
        $product['tax_amount'] = 0;
2208
        $precision = 2;
2209
        if ($this->checkTaxEnabledInProduct($productType)) {
2210
            if (is_null($product['tax_perc'])) {
2211
                $globalParameters = $this->getGlobalParameters();
2212
                $globalTaxPerc = $globalParameters['global_tax_perc'];
2213
                $taxPerc = $globalTaxPerc;
2214
            } else {
2215
                $taxPerc = $product['tax_perc'];
2216
            }
2217
            //$taxPerc = is_null($product['tax_perc']) ? $globalTaxPerc : $product['tax_perc'];
2218
2219
            $taxAmount = round($priceWithoutTax * $taxPerc / 100, $precision);
2220
            $product['tax_amount'] = $taxAmount;
2221
            $priceWithTax = $priceWithoutTax + $taxAmount;
2222
            $product['total_price'] = $priceWithTax;
2223
        }
2224
2225
        $product['tax_perc_show'] = $taxPerc;
2226
        $product['price_formatted'] = $this->getPriceWithCurrencyFromIsoCode(
2227
            number_format($product['price'], $precision),
2228
            $product['iso_code']
2229
        );
2230
2231
        $product['tax_amount_formatted'] = number_format($product['tax_amount'], $precision);
2232
2233
        $product['total_price_formatted'] = $this->getPriceWithCurrencyFromIsoCode(
2234
            number_format($product['total_price'], $precision),
2235
            $product['iso_code']
2236
        );
2237
    }
2238
2239
    /**
2240
     * @param int $id
2241
     *
2242
     * @return array
2243
     */
2244
    public function getService($id)
2245
    {
2246
        $id = (int) $id;
2247
2248
        if (empty($id)) {
2249
            return [];
2250
        }
2251
2252
        $servicesTable = Database::get_main_table(self::TABLE_SERVICES);
2253
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
2254
        $conditions = ['WHERE' => ['s.id = ?' => $id]];
2255
        $showData = 'first';
2256
        $innerJoins = "INNER JOIN $userTable u ON s.owner_id = u.id";
2257
        $currency = $this->getSelectedCurrency();
2258
        $isoCode = $currency['iso_code'];
2259
        $service = Database::select(
2260
            "s.*, '$isoCode' as currency, u.firstname, u.lastname",
2261
            "$servicesTable s $innerJoins",
2262
            $conditions,
2263
            $showData
2264
        );
2265
2266
        $service['iso_code'] = $isoCode;
2267
        $globalParameters = $this->getGlobalParameters();
2268
2269
        $this->setPriceSettings($service, self::TAX_APPLIES_TO_ONLY_SERVICES);
2270
2271
        $service['tax_name'] = $globalParameters['tax_name'];
2272
        $service['tax_enable'] = $this->checkTaxEnabledInProduct(self::TAX_APPLIES_TO_ONLY_SERVICES);
2273
        $service['owner_name'] = api_get_person_name($service['firstname'], $service['lastname']);
2274
        $service['image'] = !empty($service['image']) ? api_get_path(WEB_PLUGIN_PATH).'BuyCourses/uploads/services/images/'.$service['image'] : null;
2275
2276
        return $service;
2277
    }
2278
2279
    /**
2280
     * List additional services.
2281
     *
2282
     * @return array
2283
     */
2284
    public function getServices($start, $end, $typeResult = 'all')
2285
    {
2286
        $servicesTable = Database::get_main_table(self::TABLE_SERVICES);
2287
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
2288
2289
        $start = (int) $start;
2290
        $end = (int) $end;
2291
2292
        $conditions = ['limit' => "$start, $end"];
2293
        $innerJoins = "INNER JOIN $userTable u ON s.owner_id = u.id";
2294
        $return = Database::select(
2295
            's.id',
2296
            "$servicesTable s $innerJoins",
2297
            $conditions,
2298
            $typeResult
2299
        );
2300
2301
        if ('count' === $typeResult) {
2302
            return $return;
2303
        }
2304
2305
        $services = [];
2306
        foreach ($return as $index => $service) {
2307
            $services[$index] = $this->getService($service['id']);
2308
        }
2309
2310
        return $services;
2311
    }
2312
2313
    /**
2314
     * Get the statuses for sales.
2315
     *
2316
     * @return array
2317
     */
2318
    public function getServiceSaleStatuses()
2319
    {
2320
        return [
2321
            self::SERVICE_STATUS_CANCELLED => $this->get_lang('SaleStatusCancelled'),
2322
            self::SERVICE_STATUS_PENDING => $this->get_lang('SaleStatusPending'),
2323
            self::SERVICE_STATUS_COMPLETED => $this->get_lang('SaleStatusCompleted'),
2324
        ];
2325
    }
2326
2327
    /**
2328
     * List services sales.
2329
     *
2330
     * @param int $buyerId  buyer id
2331
     * @param int $status   status
2332
     * @param int $nodeType The node Type ( User = 1 , Course = 2 , Session = 3 )
2333
     * @param int $nodeId   the nodeId
2334
     *
2335
     * @return array
2336
     */
2337
    public function getServiceSales(
2338
        $buyerId = 0,
2339
        $status = 0,
2340
        $nodeType = 0,
2341
        $nodeId = 0
2342
    ) {
2343
        $conditions = null;
2344
        $groupBy = '';
2345
        $buyerId = (int) $buyerId;
2346
        $status = (int) $status;
2347
        $nodeType = (int) $nodeType;
2348
        $nodeId = (int) $nodeId;
2349
2350
        $defaultOrder = 'ss.id ASC';
2351
2352
        if (!empty($buyerId)) {
2353
            $conditions = ['WHERE' => ['ss.buyer_id = ?' => $buyerId], 'ORDER' => $defaultOrder];
2354
        }
2355
2356
        if (is_numeric($status)) {
2357
            $conditions = ['WHERE' => ['ss.status = ?' => $status], 'ORDER' => $defaultOrder];
2358
        }
2359
2360
        if ($buyerId) {
2361
            $conditions = ['WHERE' => ['ss.buyer_id = ?' => [$buyerId]], 'ORDER' => $defaultOrder];
2362
        }
2363
2364
        if ($nodeType && $nodeId) {
2365
            $conditions = [
2366
                'WHERE' => ['ss.node_type = ? AND ss.node_id = ?' => [$nodeType, $nodeId]],
2367
                'ORDER' => $defaultOrder,
2368
            ];
2369
        }
2370
2371
        if ($nodeType && $nodeId && $buyerId && is_numeric($status)) {
2372
            $conditions = [
2373
                'WHERE' => [
2374
                    'ss.node_type = ? AND ss.node_id = ? AND ss.buyer_id = ? AND ss.status = ?' => [
2375
                        $nodeType,
2376
                        $nodeId,
2377
                        $buyerId,
2378
                        $status,
2379
                    ],
2380
                ],
2381
                'ORDER' => 'ss.service_id ASC',
2382
            ];
2383
        }
2384
2385
        $servicesTable = Database::get_main_table(self::TABLE_SERVICES);
2386
        $servicesSaleTable = Database::get_main_table(self::TABLE_SERVICES_SALE);
2387
2388
        $innerJoins = "INNER JOIN $servicesTable s ON ss.service_id = s.id $groupBy";
2389
        $return = Database::select(
2390
            'DISTINCT ss.id ',
2391
            "$servicesSaleTable ss $innerJoins",
2392
            $conditions
2393
            //, "all", null, true
2394
        );
2395
2396
        $list = [];
2397
        foreach ($return as $service) {
2398
            $list[] = $this->getServiceSale($service['id']);
2399
        }
2400
2401
        return $list;
2402
    }
2403
2404
    /**
2405
     * @param int $id service sale id
2406
     *
2407
     * @return array
2408
     */
2409
    public function getServiceSale($id)
2410
    {
2411
        $servicesTable = Database::get_main_table(self::TABLE_SERVICES);
2412
        $servicesSaleTable = Database::get_main_table(self::TABLE_SERVICES_SALE);
2413
2414
        if (empty($id)) {
2415
            return [];
2416
        }
2417
2418
        $conditions = ['WHERE' => ['ss.id = ?' => $id]];
2419
        $innerJoins = "INNER JOIN $servicesTable s ON ss.service_id = s.id ";
2420
        $currency = $this->getSelectedCurrency();
2421
        $isoCode = $currency['iso_code'];
2422
2423
        $servicesSale = Database::select(
2424
            'ss.*, s.title, s.description, s.price as service_price, s.duration_days, s.applies_to, s.owner_id, s.visibility, s.image',
2425
            "$servicesSaleTable ss $innerJoins",
2426
            $conditions,
2427
            'first'
2428
        );
2429
        $owner = api_get_user_info($servicesSale['owner_id']);
2430
        $buyer = api_get_user_info($servicesSale['buyer_id']);
2431
2432
        $servicesSale['service']['id'] = $servicesSale['service_id'];
2433
        $servicesSale['service']['title'] = $servicesSale['title'];
2434
        $servicesSale['service']['description'] = $servicesSale['description'];
2435
        $servicesSale['service']['price'] = $servicesSale['service_price'];
2436
        $servicesSale['service']['currency'] = $isoCode;
2437
2438
        $servicesSale['service']['total_price'] = $this->getPriceWithCurrencyFromIsoCode(
2439
            $servicesSale['price'],
2440
            $isoCode
2441
        );
2442
2443
        $servicesSale['service']['duration_days'] = $servicesSale['duration_days'];
2444
        $servicesSale['service']['applies_to'] = $servicesSale['applies_to'];
2445
        $servicesSale['service']['owner']['id'] = $servicesSale['owner_id'];
2446
        $servicesSale['service']['owner']['name'] = api_get_person_name($owner['firstname'], $owner['lastname']);
2447
        $servicesSale['service']['visibility'] = $servicesSale['visibility'];
2448
        $servicesSale['service']['image'] = $servicesSale['image'];
2449
        $servicesSale['item'] = $this->getService($servicesSale['service_id']);
2450
        $servicesSale['buyer']['id'] = $buyer['user_id'];
2451
        $servicesSale['buyer']['name'] = api_get_person_name($buyer['firstname'], $buyer['lastname']);
2452
        $servicesSale['buyer']['username'] = $buyer['username'];
2453
2454
        return $servicesSale;
2455
    }
2456
2457
    /**
2458
     * Update service sale status to cancelled.
2459
     *
2460
     * @param int $serviceSaleId The sale ID
2461
     *
2462
     * @return bool
2463
     */
2464
    public function cancelServiceSale($serviceSaleId)
2465
    {
2466
        $this->updateServiceSaleStatus(
2467
            $serviceSaleId,
2468
            self::SERVICE_STATUS_CANCELLED
2469
        );
2470
2471
        return true;
2472
    }
2473
2474
    /**
2475
     * Complete service sale process. Update service sale status to completed.
2476
     *
2477
     * @param int $serviceSaleId The service sale ID
2478
     *
2479
     * @return bool
2480
     */
2481
    public function completeServiceSale($serviceSaleId)
2482
    {
2483
        $serviceSale = $this->getServiceSale($serviceSaleId);
2484
        if (self::SERVICE_STATUS_COMPLETED == $serviceSale['status']) {
2485
            return true;
2486
        }
2487
2488
        $this->updateServiceSaleStatus(
2489
            $serviceSaleId,
2490
            self::SERVICE_STATUS_COMPLETED
2491
        );
2492
2493
        if ('true' === $this->get('invoicing_enable')) {
2494
            $this->setInvoice($serviceSaleId, 1);
2495
        }
2496
2497
        return true;
2498
    }
2499
2500
    /**
2501
     * Lists current service details.
2502
     *
2503
     * @param string $name      Optional. The name filter
2504
     * @param int    $min       Optional. The minimum price filter
2505
     * @param int    $max       Optional. The maximum price filter
2506
     * @param mixed  $appliesTo optional
2507
     *
2508
     * @return array
2509
     */
2510
    public function getCatalogServiceList($start, $end, $name = null, $min = 0, $max = 0, $appliesTo = '', $typeResult = 'all')
2511
    {
2512
        $servicesTable = Database::get_main_table(self::TABLE_SERVICES);
2513
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
2514
2515
        $whereConditions = [
2516
            's.visibility <> ? ' => 0,
2517
        ];
2518
2519
        if (!empty($name)) {
2520
            $whereConditions['AND s.title LIKE %?%'] = $name;
2521
        }
2522
2523
        if (!empty($min)) {
2524
            $whereConditions['AND s.price >= ?'] = $min;
2525
        }
2526
2527
        if (!empty($max)) {
2528
            $whereConditions['AND s.price <= ?'] = $max;
2529
        }
2530
2531
        if ('' == !$appliesTo) {
2532
            $whereConditions['AND s.applies_to = ?'] = $appliesTo;
2533
        }
2534
2535
        $start = (int) $start;
2536
        $end = (int) $end;
2537
2538
        $innerJoins = "INNER JOIN $userTable u ON s.owner_id = u.id";
2539
        $return = Database::select(
2540
            's.*',
2541
            "$servicesTable s $innerJoins",
2542
            ['WHERE' => $whereConditions, 'limit' => "$start, $end"],
2543
            $typeResult
2544
        );
2545
2546
        if ('count' === $typeResult) {
2547
            return $return;
2548
        }
2549
2550
        $services = [];
2551
        foreach ($return as $index => $service) {
2552
            $services[$index] = $this->getService($service['id']);
2553
        }
2554
2555
        return $services;
2556
    }
2557
2558
    /**
2559
     * Register a Service sale.
2560
     *
2561
     * @param int $serviceId   The service ID
2562
     * @param int $paymentType The payment type
2563
     * @param int $infoSelect  The ID for Service Type
2564
     *
2565
     * @return bool
2566
     */
2567
    public function registerServiceSale($serviceId, $paymentType, $infoSelect)
2568
    {
2569
        if (!in_array(
2570
            $paymentType,
2571
            [self::PAYMENT_TYPE_PAYPAL, self::PAYMENT_TYPE_TRANSFER, self::PAYMENT_TYPE_CULQI]
2572
        )
2573
        ) {
2574
            return false;
2575
        }
2576
2577
        $userId = api_get_user_id();
2578
        $service = $this->getService($serviceId);
2579
2580
        if (empty($service)) {
2581
            return false;
2582
        }
2583
2584
        $currency = $this->getSelectedCurrency();
2585
        $price = $service['price'];
2586
        $priceWithoutTax = null;
2587
        $taxPerc = null;
2588
        $taxEnable = 'true' === $this->get('tax_enable');
2589
        $globalParameters = $this->getGlobalParameters();
2590
        $taxAppliesTo = $globalParameters['tax_applies_to'];
2591
        $taxAmount = 0;
2592
2593
        if ($taxEnable &&
2594
            (self::TAX_APPLIES_TO_ALL == $taxAppliesTo || self::TAX_APPLIES_TO_ONLY_SERVICES == $taxAppliesTo)
2595
        ) {
2596
            $priceWithoutTax = $service['price'];
2597
            $globalTaxPerc = $globalParameters['global_tax_perc'];
2598
            $precision = 2;
2599
            $taxPerc = is_null($service['tax_perc']) ? $globalTaxPerc : $service['tax_perc'];
2600
            $taxAmount = round($priceWithoutTax * $taxPerc / 100, $precision);
2601
            $price = $priceWithoutTax + $taxAmount;
2602
        }
2603
2604
        $values = [
2605
            'service_id' => $serviceId,
2606
            'reference' => $this->generateReference(
2607
                $userId,
2608
                $service['applies_to'],
2609
                $infoSelect
2610
            ),
2611
            'currency_id' => $currency['id'],
2612
            'price' => $price,
2613
            'price_without_tax' => $priceWithoutTax,
2614
            'tax_perc' => $taxPerc,
2615
            'tax_amount' => $taxAmount,
2616
            'node_type' => $service['applies_to'],
2617
            'node_id' => (int) $infoSelect,
2618
            'buyer_id' => $userId,
2619
            'buy_date' => api_get_utc_datetime(),
2620
            'date_start' => api_get_utc_datetime(),
2621
            'date_end' => date_format(
2622
                date_add(
2623
                    date_create(api_get_utc_datetime()),
2624
                    date_interval_create_from_date_string($service['duration_days'].' days')
2625
                ),
2626
                'Y-m-d H:i:s'
2627
            ),
2628
            'status' => self::SERVICE_STATUS_PENDING,
2629
            'payment_type' => (int) $paymentType,
2630
        ];
2631
2632
        $returnedServiceSaleId = Database::insert(self::TABLE_SERVICES_SALE, $values);
2633
2634
        return $returnedServiceSaleId;
2635
    }
2636
2637
    /**
2638
     * Save Culqi configuration params.
2639
     *
2640
     * @param array $params
2641
     *
2642
     * @return int Rows affected. Otherwise return false
2643
     */
2644
    public function saveCulqiParameters($params)
2645
    {
2646
        return Database::update(
2647
            Database::get_main_table(self::TABLE_CULQI),
2648
            [
2649
                'commerce_code' => $params['commerce_code'],
2650
                'api_key' => $params['api_key'],
2651
                'integration' => $params['integration'],
2652
            ],
2653
            ['id = ?' => 1]
2654
        );
2655
    }
2656
2657
    /**
2658
     * Gets the stored Culqi params.
2659
     *
2660
     * @return array
2661
     */
2662
    public function getCulqiParams()
2663
    {
2664
        return Database::select(
2665
            '*',
2666
            Database::get_main_table(self::TABLE_CULQI),
2667
            ['id = ?' => 1],
2668
            'first'
2669
        );
2670
    }
2671
2672
    /**
2673
     * Save Global Parameters.
2674
     *
2675
     * @param array $params
2676
     *
2677
     * @return int Rows affected. Otherwise return false
2678
     */
2679
    public function saveGlobalParameters($params)
2680
    {
2681
        $sqlParams = [
2682
            'terms_and_conditions' => $params['terms_and_conditions'],
2683
            'sale_email' => $params['sale_email'],
2684
        ];
2685
2686
        if ('true' === $this->get('tax_enable')) {
2687
            $sqlParams['global_tax_perc'] = $params['global_tax_perc'];
2688
            $sqlParams['tax_applies_to'] = $params['tax_applies_to'];
2689
            $sqlParams['tax_name'] = $params['tax_name'];
2690
        }
2691
2692
        if ('true' === $this->get('invoicing_enable')) {
2693
            $sqlParams['seller_name'] = $params['seller_name'];
2694
            $sqlParams['seller_id'] = $params['seller_id'];
2695
            $sqlParams['seller_address'] = $params['seller_address'];
2696
            $sqlParams['seller_email'] = $params['seller_email'];
2697
            $sqlParams['next_number_invoice'] = $params['next_number_invoice'];
2698
            $sqlParams['invoice_series'] = $params['invoice_series'];
2699
        }
2700
2701
        return Database::update(
2702
            Database::get_main_table(self::TABLE_GLOBAL_CONFIG),
2703
            $sqlParams,
2704
            ['id = ?' => 1]
2705
        );
2706
    }
2707
2708
    /**
2709
     * get Global Parameters.
2710
     *
2711
     * @return array
2712
     */
2713
    public function getGlobalParameters()
2714
    {
2715
        return Database::select(
2716
            '*',
2717
            Database::get_main_table(self::TABLE_GLOBAL_CONFIG),
2718
            ['id = ?' => 1],
2719
            'first'
2720
        );
2721
    }
2722
2723
    /**
2724
     * @param int $productType
2725
     *
2726
     * @return bool
2727
     */
2728
    public function checkTaxEnabledInProduct($productType)
2729
    {
2730
        if (empty('true' === $this->get('tax_enable'))) {
2731
            return false;
2732
        }
2733
2734
        $globalParameters = $this->getGlobalParameters();
2735
        $taxAppliesTo = $globalParameters['tax_applies_to'];
2736
        if (self::TAX_APPLIES_TO_ALL == $taxAppliesTo) {
2737
            return true;
2738
        }
2739
2740
        if ($taxAppliesTo == $productType) {
2741
            return true;
2742
        }
2743
2744
        return false;
2745
    }
2746
2747
    /**
2748
     * Get the path.
2749
     *
2750
     * @param string $var path variable
2751
     *
2752
     * @return string path
2753
     */
2754
    public function getPath($var)
2755
    {
2756
        $pluginPath = api_get_path(WEB_PLUGIN_PATH).'BuyCourses/';
2757
        $paths = [
2758
            'SERVICE_IMAGES' => $pluginPath.'uploads/services/images/',
2759
            'SRC' => $pluginPath.'src/',
2760
            'VIEW' => $pluginPath.'view/',
2761
            'UPLOADS' => $pluginPath.'uploads/',
2762
            'LANGUAGES' => $pluginPath.'lang/',
2763
            'RESOURCES' => $pluginPath.'resources/',
2764
            'RESOURCES_IMG' => $pluginPath.'resources/img/',
2765
            'RESOURCES_CSS' => $pluginPath.'resources/css/',
2766
            'RESOURCES_JS' => $pluginPath.'resources/js/',
2767
        ];
2768
2769
        return $paths[$var];
2770
    }
2771
2772
    /**
2773
     * @return array
2774
     */
2775
    public function getBuyCoursePluginPrice(Session $session)
2776
    {
2777
        // start buycourse validation
2778
        // display the course price and buy button if the BuyCourses plugin is enabled and this course is configured
2779
        $isThisCourseInSale = $this->buyCoursesForGridCatalogValidator($session->getId(), self::PRODUCT_TYPE_SESSION);
2780
        $return = [];
2781
2782
        if ($isThisCourseInSale) {
2783
            // set the Price label
2784
            $return['html'] = $isThisCourseInSale['html'];
2785
            // set the Buy button instead register.
2786
            if ($isThisCourseInSale['verificator']) {
2787
                $return['buy_button'] = $this->returnBuyCourseButton($session->getId(), self::PRODUCT_TYPE_SESSION);
2788
            }
2789
        }
2790
        // end buycourse validation
2791
        return $return;
2792
    }
2793
2794
    /**
2795
     * @return string
2796
     */
2797
    public function getSubscriptionSuccessMessage(array $saleInfo)
2798
    {
2799
        switch ($saleInfo['product_type']) {
2800
            case self::PRODUCT_TYPE_COURSE:
2801
                $courseInfo = api_get_course_info_by_id($saleInfo['product_id']);
2802
                $url = api_get_course_url($courseInfo['real_id']);
2803
                break;
2804
            case self::PRODUCT_TYPE_SESSION:
2805
                $sessionId = (int) $saleInfo['product_id'];
2806
                $url = api_get_path(WEB_CODE_PATH).'session/index.php?session_id='.$sessionId;
2807
                break;
2808
            default:
2809
                $url = '#';
2810
        }
2811
2812
        return Display::return_message(
2813
            sprintf(
2814
                $this->get_lang('SubscriptionToCourseXSuccessful'),
2815
                $url,
2816
                $saleInfo['product_name']
2817
            ),
2818
            'success',
2819
            false
2820
        );
2821
    }
2822
2823
    /**
2824
     * Filter the registered courses for show in plugin catalog.
2825
     */
2826
    private function getCourses($first, $maxResults)
2827
    {
2828
        $em = Database::getManager();
2829
        $urlId = api_get_current_access_url_id();
2830
2831
        $qb = $em->createQueryBuilder();
2832
        $qb2 = $em->createQueryBuilder();
2833
        $qb3 = $em->createQueryBuilder();
2834
2835
        $qb = $qb
2836
            ->select('c')
2837
            ->from('ChamiloCoreBundle:Course', 'c')
2838
            ->where(
2839
                $qb->expr()->notIn(
2840
                    'c',
2841
                    $qb2
2842
                        ->select('course2')
2843
                        ->from('ChamiloCoreBundle:SessionRelCourse', 'sc')
2844
                        ->join('sc.course', 'course2')
2845
                        ->innerJoin(
2846
                            'ChamiloCoreBundle:AccessUrlRelSession',
2847
                            'us',
2848
                            Join::WITH,
2849
                            'us.sessionId = sc.session'
2850
                        )->where(
2851
                            $qb->expr()->eq('us.accessUrlId ', $urlId)
2852
                        )
2853
                        ->getDQL()
2854
                )
2855
            )->andWhere(
2856
                $qb->expr()->in(
2857
                    'c',
2858
                    $qb3
2859
                        ->select('course3')
2860
                        ->from('ChamiloCoreBundle:AccessUrlRelCourse', 'uc')
2861
                        ->join('uc.course', 'course3')
2862
                        ->where(
2863
                            $qb3->expr()->eq('uc.url ', $urlId)
2864
                        )
2865
                        ->getDQL()
2866
                )
2867
            )
2868
        ->setFirstResult($first)
2869
        ->setMaxResults($maxResults);
2870
2871
        return $qb;
2872
    }
2873
2874
    /**
2875
     * Get the user status for the session.
2876
     *
2877
     * @param int     $userId  The user ID
2878
     * @param Session $session The session
2879
     *
2880
     * @return string
2881
     */
2882
    private function getUserStatusForSession($userId, Session $session)
2883
    {
2884
        if (empty($userId)) {
2885
            return 'NO';
2886
        }
2887
2888
        $entityManager = Database::getManager();
2889
        $scuRepo = $entityManager->getRepository('ChamiloCoreBundle:SessionRelCourseRelUser');
2890
2891
        $buySaleTable = Database::get_main_table(self::TABLE_SALE);
2892
2893
        // Check if user bought the course
2894
        $sale = Database::select(
2895
            'COUNT(1) as qty',
2896
            $buySaleTable,
2897
            [
2898
                'where' => [
2899
                    'user_id = ? AND product_type = ? AND product_id = ? AND status = ?' => [
2900
                        $userId,
2901
                        self::PRODUCT_TYPE_SESSION,
2902
                        $session->getId(),
2903
                        self::SALE_STATUS_PENDING,
2904
                    ],
2905
                ],
2906
            ],
2907
            'first'
2908
        );
2909
2910
        if ($sale['qty'] > 0) {
2911
            return 'TMP';
2912
        }
2913
2914
        // Check if user is already subscribe to session
2915
        $userSubscription = $scuRepo->findBy([
2916
            'session' => $session,
2917
            'user' => $userId,
2918
        ]);
2919
2920
        if (!empty($userSubscription)) {
2921
            return 'YES';
2922
        }
2923
2924
        return 'NO';
2925
    }
2926
2927
    /**
2928
     * Get the user status for the course.
2929
     *
2930
     * @param int    $userId The user Id
2931
     * @param Course $course The course
2932
     *
2933
     * @return string
2934
     */
2935
    private function getUserStatusForCourse($userId, Course $course)
2936
    {
2937
        if (empty($userId)) {
2938
            return 'NO';
2939
        }
2940
2941
        $entityManager = Database::getManager();
2942
        $cuRepo = $entityManager->getRepository('ChamiloCoreBundle:CourseRelUser');
2943
        $buySaleTable = Database::get_main_table(self::TABLE_SALE);
2944
2945
        // Check if user bought the course
2946
        $sale = Database::select(
2947
            'COUNT(1) as qty',
2948
            $buySaleTable,
2949
            [
2950
                'where' => [
2951
                    'user_id = ? AND product_type = ? AND product_id = ? AND status = ?' => [
2952
                        $userId,
2953
                        self::PRODUCT_TYPE_COURSE,
2954
                        $course->getId(),
2955
                        self::SALE_STATUS_PENDING,
2956
                    ],
2957
                ],
2958
            ],
2959
            'first'
2960
        );
2961
2962
        if ($sale['qty'] > 0) {
2963
            return 'TMP';
2964
        }
2965
2966
        // Check if user is already subscribe to course
2967
        $userSubscription = $cuRepo->findBy([
2968
            'course' => $course,
2969
            'user' => $userId,
2970
        ]);
2971
2972
        if (!empty($userSubscription)) {
2973
            return 'YES';
2974
        }
2975
2976
        return 'NO';
2977
    }
2978
2979
    /**
2980
     * Update the sale status.
2981
     *
2982
     * @param int $saleId    The sale ID
2983
     * @param int $newStatus The new status
2984
     *
2985
     * @return bool
2986
     */
2987
    private function updateSaleStatus($saleId, $newStatus = self::SALE_STATUS_PENDING)
2988
    {
2989
        $saleTable = Database::get_main_table(self::TABLE_SALE);
2990
2991
        return Database::update(
2992
            $saleTable,
2993
            ['status' => (int) $newStatus],
2994
            ['id = ?' => (int) $saleId]
2995
        );
2996
    }
2997
2998
    /**
2999
     * Search filtered sessions by name, and range of price.
3000
     *
3001
     * @param int    $start
3002
     * @param int    $end
3003
     * @param string $name  Optional. The name filter
3004
     * @param int    $min   Optional. The minimun price filter
3005
     * @param int    $max   Optional. The maximum price filter
3006
     *
3007
     * @return array
3008
     */
3009
    private function filterSessionList($start, $end, $name = null, $min = 0, $max = 0, $typeResult = 'all')
3010
    {
3011
        $itemTable = Database::get_main_table(self::TABLE_ITEM);
3012
        $sessionTable = Database::get_main_table(TABLE_MAIN_SESSION);
3013
3014
        $min = floatval($min);
3015
        $max = floatval($max);
3016
3017
        $innerJoin = "$itemTable i ON s.id = i.product_id";
3018
        $whereConditions = [
3019
            'i.product_type = ? ' => self::PRODUCT_TYPE_SESSION,
3020
        ];
3021
3022
        if (!empty($name)) {
3023
            $whereConditions['AND s.title LIKE %?%'] = $name;
3024
        }
3025
3026
        if (!empty($min)) {
3027
            $whereConditions['AND i.price >= ?'] = $min;
3028
        }
3029
3030
        if (!empty($max)) {
3031
            $whereConditions['AND i.price <= ?'] = $max;
3032
        }
3033
3034
        $start = (int) $start;
3035
        $end = (int) $end;
3036
3037
        $sessionIds = Database::select(
3038
            's.id',
3039
            "$sessionTable s INNER JOIN $innerJoin",
3040
            ['where' => $whereConditions, 'limit' => "$start, $end"],
3041
            $typeResult
3042
        );
3043
3044
        if ('count' === $typeResult) {
3045
            return $sessionIds;
3046
        }
3047
3048
        if (!$sessionIds) {
3049
            return [];
3050
        }
3051
3052
        $sessions = [];
3053
3054
        foreach ($sessionIds as $sessionId) {
3055
            $sessions[] = Database::getManager()->find(
3056
                'ChamiloCoreBundle:Session',
3057
                $sessionId
3058
            );
3059
        }
3060
3061
        return $sessions;
3062
    }
3063
3064
    /**
3065
     * Search filtered courses by name, and range of price.
3066
     *
3067
     * @param string $name Optional. The name filter
3068
     * @param int    $min  Optional. The minimun price filter
3069
     * @param int    $max  Optional. The maximum price filter
3070
     *
3071
     * @return array
3072
     */
3073
    private function filterCourseList($start, $end, $name = '', $min = 0, $max = 0, $typeResult = 'all')
3074
    {
3075
        $itemTable = Database::get_main_table(self::TABLE_ITEM);
3076
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
3077
        $urlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
3078
3079
        $urlId = api_get_current_access_url_id();
3080
3081
        $min = floatval($min);
3082
        $max = floatval($max);
3083
3084
        $whereConditions = [
3085
            'i.product_type = ? ' => self::PRODUCT_TYPE_COURSE,
3086
        ];
3087
3088
        if (!empty($name)) {
3089
            $whereConditions['AND c.title LIKE %?%'] = $name;
3090
        }
3091
3092
        if (!empty($min)) {
3093
            $whereConditions['AND i.price >= ?'] = $min;
3094
        }
3095
3096
        if (!empty($max)) {
3097
            $whereConditions['AND i.price <= ?'] = $max;
3098
        }
3099
3100
        $whereConditions['AND url.access_url_id = ?'] = $urlId;
3101
        $start = (int) $start;
3102
        $end = (int) $end;
3103
3104
        $courseIds = Database::select(
3105
            'c.id',
3106
            "$courseTable c
3107
            INNER JOIN $itemTable i
3108
            ON c.id = i.product_id
3109
            INNER JOIN $urlTable url
3110
            ON c.id = url.c_id
3111
            ",
3112
            ['where' => $whereConditions, 'limit' => "$start, $end"],
3113
            $typeResult
3114
        );
3115
3116
        if ('count' === $typeResult) {
3117
            return $courseIds;
3118
        }
3119
3120
        if (!$courseIds) {
3121
            return [];
3122
        }
3123
3124
        $courses = [];
3125
        foreach ($courseIds as $courseId) {
3126
            $courses[] = Database::getManager()->find(
3127
                'ChamiloCoreBundle:Course',
3128
                $courseId
3129
            );
3130
        }
3131
3132
        return $courses;
3133
    }
3134
3135
    /**
3136
     * Update the service sale status.
3137
     *
3138
     * @param int $serviceSaleId The service sale ID
3139
     * @param int $newStatus     The new status
3140
     *
3141
     * @return bool
3142
     */
3143
    private function updateServiceSaleStatus(
3144
        $serviceSaleId,
3145
        $newStatus = self::SERVICE_STATUS_PENDING
3146
    ) {
3147
        $serviceSaleTable = Database::get_main_table(self::TABLE_SERVICES_SALE);
3148
3149
        return Database::update(
3150
            $serviceSaleTable,
3151
            ['status' => (int) $newStatus],
3152
            ['id = ?' => (int) $serviceSaleId]
3153
        );
3154
    }
3155
3156
    public function get_name()
3157
    {
3158
        return 'BuyCourses'; // TODO: Change the autogenerated stub
3159
    }
3160
}
3161