Passed
Push — master ( d41e1e...ec03f3 )
by Angel Fernando Quiroz
08:56
created

BuyCoursesPlugin::getSelectedCurrency()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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