Passed
Push — master ( c392a5...5b69b7 )
by
unknown
19:41 queued 09:13
created

BuyCoursesPlugin::getSessionIllustrationUrl()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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