Passed
Push — master ( a4541f...0b8c4c )
by Yannick
14:31 queued 03:49
created

BuyCoursesPlugin::getCourseInfo()   A

Complexity

Conditions 5
Paths 6

Size

Total Lines 51
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 33
c 1
b 0
f 0
nc 6
nop 1
dl 0
loc 51
rs 9.0808

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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