BuyCoursesPlugin::getTaxAppliesTo()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
nc 1
nop 0
dl 0
loc 7
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
15
/**
16
 * Plugin class for the BuyCourses plugin.
17
 *
18
 * @author  Jose Angel Ruiz <[email protected]>
19
 * @author  Imanol Losada <[email protected]>
20
 * @author  Alex Aragón <[email protected]>
21
 * @author  Angel Fernando Quiroz Campos <[email protected]>
22
 * @author  José Loguercio Silva  <[email protected]>
23
 * @author  Julio Montoya
24
 */
25
class BuyCoursesPlugin extends Plugin
26
{
27
    const TABLE_PAYPAL = 'plugin_buycourses_paypal_account';
28
    const TABLE_CURRENCY = 'plugin_buycourses_currency';
29
    const TABLE_ITEM = 'plugin_buycourses_item';
30
    const TABLE_ITEM_BENEFICIARY = 'plugin_buycourses_item_rel_beneficiary';
31
    const TABLE_SALE = 'plugin_buycourses_sale';
32
    const TABLE_TRANSFER = 'plugin_buycourses_transfer';
33
    const TABLE_COMMISSION = 'plugin_buycourses_commission';
34
    const TABLE_PAYPAL_PAYOUTS = 'plugin_buycourses_paypal_payouts';
35
    const TABLE_SERVICES = 'plugin_buycourses_services';
36
    const TABLE_SERVICES_SALE = 'plugin_buycourses_service_sale';
37
    const TABLE_CULQI = 'plugin_buycourses_culqi';
38
    const TABLE_GLOBAL_CONFIG = 'plugin_buycourses_global_config';
39
    const TABLE_INVOICE = 'plugin_buycourses_invoices';
40
    const PRODUCT_TYPE_COURSE = 1;
41
    const PRODUCT_TYPE_SESSION = 2;
42
    const PAYMENT_TYPE_PAYPAL = 1;
43
    const PAYMENT_TYPE_TRANSFER = 2;
44
    const PAYMENT_TYPE_CULQI = 3;
45
    const PAYOUT_STATUS_CANCELED = 2;
46
    const PAYOUT_STATUS_PENDING = 0;
47
    const PAYOUT_STATUS_COMPLETED = 1;
48
    const SALE_STATUS_CANCELED = -1;
49
    const SALE_STATUS_PENDING = 0;
50
    const SALE_STATUS_COMPLETED = 1;
51
    const SERVICE_STATUS_PENDING = 0;
52
    const SERVICE_STATUS_COMPLETED = 1;
53
    const SERVICE_STATUS_CANCELLED = -1;
54
    const SERVICE_TYPE_USER = 1;
55
    const SERVICE_TYPE_COURSE = 2;
56
    const SERVICE_TYPE_SESSION = 3;
57
    const SERVICE_TYPE_LP_FINAL_ITEM = 4;
58
    const CULQI_INTEGRATION_TYPE = 'INTEG';
59
    const CULQI_PRODUCTION_TYPE = 'PRODUC';
60
    const TAX_APPLIES_TO_ALL = 1;
61
    const TAX_APPLIES_TO_ONLY_COURSE = 2;
62
    const TAX_APPLIES_TO_ONLY_SESSION = 3;
63
    const TAX_APPLIES_TO_ONLY_SERVICES = 4;
64
    const PAGINATION_PAGE_SIZE = 5;
65
66
    public $isAdminPlugin = true;
67
68
    /**
69
     * BuyCoursesPlugin constructor.
70
     */
71
    public function __construct()
72
    {
73
        parent::__construct(
74
            '5.0',
75
            "
76
                Jose Angel Ruiz - NoSoloRed (original author) <br/>
77
                Francis Gonzales and Yannick Warnier - BeezNest (integration) <br/>
78
                Alex Aragón - BeezNest (Design icons and css styles) <br/>
79
                Imanol Losada - BeezNest (introduction of sessions purchase) <br/>
80
                Angel Fernando Quiroz Campos - BeezNest (cleanup and new reports) <br/>
81
                José Loguercio Silva - BeezNest (Payouts and buy Services) <br/>
82
                Julio Montoya
83
            ",
84
            [
85
                'show_main_menu_tab' => 'boolean',
86
                'public_main_menu_tab' => 'boolean',
87
                'include_sessions' => 'boolean',
88
                'include_services' => 'boolean',
89
                'paypal_enable' => 'boolean',
90
                'transfer_enable' => 'boolean',
91
                'culqi_enable' => 'boolean',
92
                'commissions_enable' => 'boolean',
93
                'unregistered_users_enable' => 'boolean',
94
                'hide_free_text' => 'boolean',
95
                'invoicing_enable' => 'boolean',
96
                'tax_enable' => 'boolean',
97
                'use_currency_symbol' => 'boolean',
98
            ]
99
        );
100
    }
101
102
    /**
103
     * @return BuyCoursesPlugin
104
     */
105
    public static function create()
106
    {
107
        static $result = null;
108
109
        return $result ? $result : $result = new self();
110
    }
111
112
    /**
113
     * Check if plugin is enabled.
114
     *
115
     * @param bool $checkEnabled Check if, additionnally to being installed, the plugin is enabled
116
     *
117
     * @return bool
118
     */
119
    public function isEnabled($checkEnabled = false)
120
    {
121
        return $this->get('paypal_enable') || $this->get('transfer_enable') || $this->get('culqi_enable');
122
    }
123
124
    /**
125
     * This method creates the tables required to this plugin.
126
     *
127
     * @throws \Doctrine\DBAL\Exception
128
     */
129
    public function install()
130
    {
131
        $tablesToBeCompared = [
132
            self::TABLE_PAYPAL,
133
            self::TABLE_TRANSFER,
134
            self::TABLE_CULQI,
135
            self::TABLE_ITEM_BENEFICIARY,
136
            self::TABLE_ITEM,
137
            self::TABLE_SALE,
138
            self::TABLE_CURRENCY,
139
            self::TABLE_COMMISSION,
140
            self::TABLE_PAYPAL_PAYOUTS,
141
            self::TABLE_SERVICES,
142
            self::TABLE_SERVICES_SALE,
143
            self::TABLE_GLOBAL_CONFIG,
144
            self::TABLE_INVOICE,
145
        ];
146
        $em = Database::getManager();
147
        $cn = $em->getConnection();
148
        $sm = $cn->createSchemaManager();
149
        $tables = $sm->tablesExist($tablesToBeCompared);
150
151
        if ($tables) {
152
            return false;
153
        }
154
155
        require_once api_get_path(SYS_PLUGIN_PATH).'BuyCourses/database.php';
156
    }
157
158
    /**
159
     * This method drops the plugin tables.
160
     */
161
    public function uninstall()
162
    {
163
        $tablesToBeDeleted = [
164
            self::TABLE_PAYPAL,
165
            self::TABLE_TRANSFER,
166
            self::TABLE_CULQI,
167
            self::TABLE_ITEM_BENEFICIARY,
168
            self::TABLE_ITEM,
169
            self::TABLE_SALE,
170
            self::TABLE_CURRENCY,
171
            self::TABLE_COMMISSION,
172
            self::TABLE_PAYPAL_PAYOUTS,
173
            self::TABLE_SERVICES_SALE,
174
            self::TABLE_SERVICES,
175
            self::TABLE_GLOBAL_CONFIG,
176
            self::TABLE_INVOICE,
177
        ];
178
179
        foreach ($tablesToBeDeleted as $tableToBeDeleted) {
180
            $table = Database::get_main_table($tableToBeDeleted);
181
            $sql = "DROP TABLE IF EXISTS $table";
182
            Database::query($sql);
183
        }
184
        $this->manageTab(false);
185
    }
186
187
    public function update()
188
    {
189
        $table = self::TABLE_GLOBAL_CONFIG;
190
        $sql = "SHOW COLUMNS FROM $table WHERE Field = 'global_tax_perc'";
191
        $res = Database::query($sql);
192
193
        if (0 === Database::num_rows($res)) {
194
            $sql = "ALTER TABLE $table ADD (
195
                sale_email varchar(255) NOT NULL,
196
                global_tax_perc int unsigned NOT NULL,
197
                tax_applies_to int unsigned NOT NULL,
198
                tax_name varchar(255) NOT NULL,
199
                seller_name varchar(255) NOT NULL,
200
                seller_id varchar(255) NOT NULL,
201
                seller_address varchar(255) NOT NULL,
202
                seller_email varchar(255) NOT NULL,
203
                next_number_invoice int unsigned NOT NULL,
204
                invoice_series varchar(255) NOT NULL
205
            )";
206
            $res = Database::query($sql);
207
            if (!$res) {
208
                echo Display::return_message($this->get_lang('ErrorUpdateFieldDB'), 'warning');
209
            }
210
        }
211
212
        $table = self::TABLE_ITEM;
213
        $sql = "SHOW COLUMNS FROM $table WHERE Field = 'tax_perc'";
214
        $res = Database::query($sql);
215
216
        if (0 === Database::num_rows($res)) {
217
            $sql = "ALTER TABLE $table ADD tax_perc int unsigned NULL";
218
            $res = Database::query($sql);
219
            if (!$res) {
220
                echo Display::return_message($this->get_lang('ErrorUpdateFieldDB'), 'warning');
221
            }
222
        }
223
224
        $table = self::TABLE_SERVICES;
225
        $sql = "SHOW COLUMNS FROM $table WHERE Field = 'tax_perc'";
226
        $res = Database::query($sql);
227
228
        if (0 === Database::num_rows($res)) {
229
            $sql = "ALTER TABLE $table ADD tax_perc int unsigned NULL";
230
            $res = Database::query($sql);
231
            if (!$res) {
232
                echo Display::return_message($this->get_lang('ErrorUpdateFieldDB'), 'warning');
233
            }
234
        }
235
236
        $table = self::TABLE_SALE;
237
        $sql = "SHOW COLUMNS FROM $table WHERE Field = 'tax_perc'";
238
        $res = Database::query($sql);
239
240
        if (0 === Database::num_rows($res)) {
241
            $sql = "ALTER TABLE $table ADD (
242
                price_without_tax decimal(10,2) NULL,
243
                tax_perc int unsigned NULL,
244
                tax_amount decimal(10,2) NULL,
245
                invoice int unsigned NULL
246
            )";
247
            $res = Database::query($sql);
248
            if (!$res) {
249
                echo Display::return_message($this->get_lang('ErrorUpdateFieldDB'), 'warning');
250
            }
251
        }
252
253
        $table = self::TABLE_SERVICES_SALE;
254
        $sql = "SHOW COLUMNS FROM $table WHERE Field = 'tax_perc'";
255
        $res = Database::query($sql);
256
257
        if (0 === Database::num_rows($res)) {
258
            $sql = "ALTER TABLE $table ADD (
259
                price_without_tax decimal(10,2) NULL,
260
                tax_perc int unsigned NULL,
261
                tax_amount decimal(10,2) NULL,
262
                invoice int unsigned NULL
263
            )";
264
            $res = Database::query($sql);
265
            if (!$res) {
266
                echo Display::return_message($this->get_lang('ErrorUpdateFieldDB'), 'warning');
267
            }
268
        }
269
270
        $table = self::TABLE_INVOICE;
271
        $sql = "CREATE TABLE IF NOT EXISTS $table (
272
            id int unsigned NOT NULL AUTO_INCREMENT,
273
            sale_id int unsigned NOT NULL,
274
            is_service int unsigned NOT NULL,
275
            num_invoice int unsigned NOT NULL,
276
            year int(4) unsigned NOT NULL,
277
            serie varchar(255) NOT NULL,
278
            date_invoice datetime NOT NULL,
279
            PRIMARY KEY (id)
280
        )";
281
        Database::query($sql);
282
283
        Display::addFlash(
284
            Display::return_message(
285
                $this->get_lang('Updated'),
286
                'info',
287
                false
288
            )
289
        );
290
291
        $fieldlabel = 'buycourses_company';
292
        $fieldtype = '1';
293
        $fieldtitle = $this->get_lang('Company');
294
        $fielddefault = '';
295
        UserManager::create_extra_field($fieldlabel, $fieldtype, $fieldtitle, $fielddefault);
296
297
        $fieldlabel = 'buycourses_vat';
298
        $fieldtype = '1';
299
        $fieldtitle = $this->get_lang('VAT');
300
        $fielddefault = '';
301
        UserManager::create_extra_field($fieldlabel, $fieldtype, $fieldtitle, $fielddefault);
302
303
        $fieldlabel = 'buycourses_address';
304
        $fieldtype = '1';
305
        $fieldtitle = $this->get_lang('Address');
306
        $fielddefault = '';
307
        UserManager::create_extra_field($fieldlabel, $fieldtype, $fieldtitle, $fielddefault);
308
309
        $table = self::TABLE_TRANSFER;
310
        $sql = "ALTER TABLE $table CHANGE COLUMN name title varchar(255)";
311
        Database::query($sql);
312
        $table = self::TABLE_SERVICES;
313
        $sql = "ALTER TABLE $table CHANGE COLUMN name title varchar(255)";
314
        Database::query($sql);
315
316
        header('Location: '.api_get_path(WEB_PLUGIN_PATH).'BuyCourses');
317
        exit;
318
    }
319
320
    /**
321
     * This function verify if the plugin is enable and return the price info for a course or session in the new grid
322
     * 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
323
     * course catalog so the old BuyCourses plugin catalog can be deprecated.
324
     *
325
     * @param int $productId   course or session id
326
     * @param int $productType course or session type
327
     *
328
     * @return mixed bool|string html
329
     */
330
    public function buyCoursesForGridCatalogValidator($productId, $productType)
331
    {
332
        $return = [];
333
        $paypal = 'true' === $this->get('paypal_enable');
334
        $transfer = 'true' === $this->get('transfer_enable');
335
        $hideFree = 'true' === $this->get('hide_free_text');
336
337
        if ($paypal || $transfer) {
338
            $item = $this->getItemByProduct($productId, $productType);
339
            $html = '<div class="buycourses-price">';
340
            if ($item) {
341
                $html .= '<span class="label label-primary label-price">
342
                            <strong>'.$item['total_price_formatted'].'</strong>
343
                          </span>';
344
                $return['verificator'] = true;
345
            } else {
346
                if (false == $hideFree) {
347
                    $html .= '<span class="label label-primary label-free">
348
                                <strong>'.$this->get_lang('Free').'</strong>
349
                              </span>';
350
                }
351
                $return['verificator'] = false;
352
            }
353
            $html .= '</div>';
354
            $return['html'] = $html;
355
356
            return $return;
357
        }
358
359
        return false;
360
    }
361
362
    /**
363
     * Return the buyCourses plugin button to buy the course.
364
     *
365
     * @param int $productId
366
     * @param int $productType
367
     *
368
     * @return string $html
369
     */
370
    public function returnBuyCourseButton($productId, $productType)
371
    {
372
        $productId = (int) $productId;
373
        $productType = (int) $productType;
374
        $url = api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/process.php?i='.$productId.'&t='.$productType;
375
        $html = '<a class="btn btn--success btn-sm" title="'.$this->get_lang('Buy').'" href="'.$url.'">'.
376
            Display::getMdiIcon('cart').'</a>';
377
378
        return $html;
379
    }
380
381
    /**
382
     * Get the currency for sales.
383
     *
384
     * @return array The selected currency. Otherwise return false
385
     */
386
    public function getSelectedCurrency()
387
    {
388
        return Database::select(
389
            '*',
390
            Database::get_main_table(self::TABLE_CURRENCY),
391
            [
392
                'where' => ['status = ?' => true],
393
            ],
394
            'first'
395
        );
396
    }
397
398
    /**
399
     * Get a list of currencies.
400
     *
401
     * @return array The currencies. Otherwise return false
402
     */
403
    public function getCurrencies()
404
    {
405
        return Database::select(
406
            '*',
407
            Database::get_main_table(self::TABLE_CURRENCY)
408
        );
409
    }
410
411
    /**
412
     * Save the selected currency.
413
     *
414
     * @param int $selectedId The currency Id
415
     */
416
    public function saveCurrency($selectedId)
417
    {
418
        $currencyTable = Database::get_main_table(
419
            self::TABLE_CURRENCY
420
        );
421
422
        Database::update(
423
            $currencyTable,
424
            ['status' => 0]
425
        );
426
        Database::update(
427
            $currencyTable,
428
            ['status' => 1],
429
            ['id = ?' => (int) $selectedId]
430
        );
431
    }
432
433
    /**
434
     * Save the PayPal configuration params.
435
     *
436
     * @param array $params
437
     *
438
     * @return int Rows affected. Otherwise return false
439
     */
440
    public function savePaypalParams($params)
441
    {
442
        return Database::update(
443
            Database::get_main_table(self::TABLE_PAYPAL),
444
            [
445
                'username' => $params['username'],
446
                'password' => $params['password'],
447
                'signature' => $params['signature'],
448
                'sandbox' => isset($params['sandbox']),
449
            ],
450
            ['id = ?' => 1]
451
        );
452
    }
453
454
    /**
455
     * Gets the stored PayPal params.
456
     *
457
     * @return array
458
     */
459
    public function getPaypalParams()
460
    {
461
        return Database::select(
462
            '*',
463
            Database::get_main_table(self::TABLE_PAYPAL),
464
            ['id = ?' => 1],
465
            'first'
466
        );
467
    }
468
469
    /**
470
     * Save a transfer account information.
471
     *
472
     * @param array $params The transfer account
473
     *
474
     * @return int Rows affected. Otherwise return false
475
     */
476
    public function saveTransferAccount($params)
477
    {
478
        return Database::insert(
479
            Database::get_main_table(self::TABLE_TRANSFER),
480
            [
481
                'name' => $params['tname'],
482
                'account' => $params['taccount'],
483
                'swift' => $params['tswift'],
484
            ]
485
        );
486
    }
487
488
    /**
489
     * Get a list of transfer accounts.
490
     *
491
     * @return array
492
     */
493
    public function getTransferAccounts()
494
    {
495
        return Database::select(
496
            '*',
497
            Database::get_main_table(self::TABLE_TRANSFER)
498
        );
499
    }
500
501
    /**
502
     * Remove a transfer account.
503
     *
504
     * @param int $id The transfer account ID
505
     *
506
     * @return int Rows affected. Otherwise return false
507
     */
508
    public function deleteTransferAccount($id)
509
    {
510
        return Database::delete(
511
            Database::get_main_table(self::TABLE_TRANSFER),
512
            ['id = ?' => (int) $id]
513
        );
514
    }
515
516
    /**
517
     * Get registered item data.
518
     *
519
     * @param int $itemId The item ID
520
     *
521
     * @return array
522
     */
523
    public function getItem($itemId)
524
    {
525
        return Database::select(
526
            '*',
527
            Database::get_main_table(self::TABLE_ITEM),
528
            [
529
                'where' => ['id = ?' => (int) $itemId],
530
            ],
531
            'first'
532
        );
533
    }
534
535
    /**
536
     * Get the item data.
537
     *
538
     * @param int $productId The item ID
539
     * @param int $itemType  The item type
540
     *
541
     * @return array
542
     */
543
    public function getItemByProduct($productId, $itemType)
544
    {
545
        $buyItemTable = Database::get_main_table(self::TABLE_ITEM);
546
        $buyCurrencyTable = Database::get_main_table(self::TABLE_CURRENCY);
547
548
        $fakeItemFrom = "
549
            $buyItemTable i
550
            INNER JOIN $buyCurrencyTable c
551
                ON i.currency_id = c.id
552
        ";
553
554
        $product = Database::select(
555
            ['i.*', 'c.iso_code'],
556
            $fakeItemFrom,
557
            [
558
                'where' => [
559
                    'i.product_id = ? AND i.product_type = ?' => [
560
                        (int) $productId,
561
                        (int) $itemType,
562
                    ],
563
                ],
564
            ],
565
            'first'
566
        );
567
568
        if (empty($product)) {
569
            return false;
570
        }
571
572
        $this->setPriceSettings($product, self::TAX_APPLIES_TO_ONLY_COURSE);
573
574
        return $product;
575
    }
576
577
    /**
578
     * List courses details from the configuration page.
579
     *
580
     * @return array
581
     */
582
    public function getCourseList($first, $maxResults)
583
    {
584
        return $this->getCourses($first, $maxResults);
585
    }
586
587
    /**
588
     * Lists current user session details, including each session course details.
589
     *
590
     * It can return the number of rows when $typeResult is 'count'.
591
     *
592
     * @param int    $start
593
     * @param int    $end
594
     * @param string $name       Optional. The name filter.
595
     * @param int    $min        Optional. The minimum price filter.
596
     * @param int    $max        Optional. The maximum price filter.
597
     * @param string $typeResult Optional. 'all', 'first' or 'count'.
598
     *
599
     * @return array|int
600
     */
601
    public function getCatalogSessionList($start, $end, $name = null, $min = 0, $max = 0, $typeResult = 'all')
602
    {
603
        $sessions = $this->filterSessionList($start, $end, $name, $min, $max, $typeResult);
604
605
        if ('count' === $typeResult) {
606
            return $sessions;
607
        }
608
609
        $sessionCatalog = [];
610
        /** @var Session $session */
611
        foreach ($sessions as $session) {
612
            $sessionCourses = $session->getCourses();
613
614
            if (empty($sessionCourses)) {
615
                continue;
616
            }
617
618
            $item = $this->getItemByProduct(
619
                $session->getId(),
620
                self::PRODUCT_TYPE_SESSION
621
            );
622
623
            if (empty($item)) {
624
                continue;
625
            }
626
627
            $sessionData = $this->getSessionInfo($session->getId());
628
            $sessionData['coaches'] = $session->getGeneralCoaches()->map(fn(User $coach) => $coach->getFullname());
629
            $sessionData['enrolled'] = $this->getUserStatusForSession(
630
                api_get_user_id(),
631
                $session
632
            );
633
            $sessionData['courses'] = [];
634
635
            foreach ($sessionCourses as $sessionCourse) {
636
                $course = $sessionCourse->getCourse();
637
638
                $sessionCourseData = [
639
                    'title' => $course->getTitle(),
640
                    'coaches' => [],
641
                ];
642
643
                $userCourseSubscriptions = $session->getSessionRelCourseRelUsersByStatus(
644
                    $course,
645
                    Chamilo\CoreBundle\Entity\Session::COURSE_COACH
646
                );
647
648
                foreach ($userCourseSubscriptions as $userCourseSubscription) {
649
                    $user = $userCourseSubscription->getUser();
650
                    $sessionCourseData['coaches'][] = $user->getCompleteName();
651
                }
652
                $sessionData['courses'][] = $sessionCourseData;
653
            }
654
655
            $sessionCatalog[] = $sessionData;
656
        }
657
658
        return $sessionCatalog;
659
    }
660
661
    /**
662
     * Lists current user course details.
663
     *
664
     * @param string $name Optional. The name filter
665
     * @param int    $min  Optional. The minimum price filter
666
     * @param int    $max  Optional. The maximum price filter
667
     *
668
     * @return array
669
     */
670
    public function getCatalogCourseList($first, $pageSize, $name = null, $min = 0, $max = 0, $typeResult = 'all')
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
            $this->get_lang('UserName'),
1357
            get_lang('Email'),
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
     * @return array
2515
     */
2516
    public function getCatalogServiceList($start, $end, $name = null, $min = 0, $max = 0, $appliesTo = '', $typeResult = 'all')
2517
    {
2518
        $servicesTable = Database::get_main_table(self::TABLE_SERVICES);
2519
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
2520
2521
        $whereConditions = [
2522
            's.visibility <> ? ' => 0,
2523
        ];
2524
2525
        if (!empty($name)) {
2526
            $whereConditions['AND s.title LIKE %?%'] = $name;
2527
        }
2528
2529
        if (!empty($min)) {
2530
            $whereConditions['AND s.price >= ?'] = $min;
2531
        }
2532
2533
        if (!empty($max)) {
2534
            $whereConditions['AND s.price <= ?'] = $max;
2535
        }
2536
2537
        if ('' == !$appliesTo) {
2538
            $whereConditions['AND s.applies_to = ?'] = $appliesTo;
2539
        }
2540
2541
        $start = (int) $start;
2542
        $end = (int) $end;
2543
2544
        $innerJoins = "INNER JOIN $userTable u ON s.owner_id = u.id";
2545
        $return = Database::select(
2546
            's.*',
2547
            "$servicesTable s $innerJoins",
2548
            ['WHERE' => $whereConditions, 'limit' => "$start, $end"],
2549
            $typeResult
2550
        );
2551
2552
        if ('count' === $typeResult) {
2553
            return $return;
2554
        }
2555
2556
        $services = [];
2557
        foreach ($return as $index => $service) {
2558
            $services[$index] = $this->getService($service['id']);
2559
        }
2560
2561
        return $services;
2562
    }
2563
2564
    /**
2565
     * Register a Service sale.
2566
     *
2567
     * @param int $serviceId   The service ID
2568
     * @param int $paymentType The payment type
2569
     * @param int $infoSelect  The ID for Service Type
2570
     *
2571
     * @return bool
2572
     */
2573
    public function registerServiceSale($serviceId, $paymentType, $infoSelect)
2574
    {
2575
        if (!in_array(
2576
            $paymentType,
2577
            [self::PAYMENT_TYPE_PAYPAL, self::PAYMENT_TYPE_TRANSFER, self::PAYMENT_TYPE_CULQI]
2578
        )
2579
        ) {
2580
            return false;
2581
        }
2582
2583
        $userId = api_get_user_id();
2584
        $service = $this->getService($serviceId);
2585
2586
        if (empty($service)) {
2587
            return false;
2588
        }
2589
2590
        $currency = $this->getSelectedCurrency();
2591
        $price = $service['price'];
2592
        $priceWithoutTax = null;
2593
        $taxPerc = null;
2594
        $taxEnable = 'true' === $this->get('tax_enable');
2595
        $globalParameters = $this->getGlobalParameters();
2596
        $taxAppliesTo = $globalParameters['tax_applies_to'];
2597
        $taxAmount = 0;
2598
2599
        if ($taxEnable &&
2600
            (self::TAX_APPLIES_TO_ALL == $taxAppliesTo || self::TAX_APPLIES_TO_ONLY_SERVICES == $taxAppliesTo)
2601
        ) {
2602
            $priceWithoutTax = $service['price'];
2603
            $globalTaxPerc = $globalParameters['global_tax_perc'];
2604
            $precision = 2;
2605
            $taxPerc = is_null($service['tax_perc']) ? $globalTaxPerc : $service['tax_perc'];
2606
            $taxAmount = round($priceWithoutTax * $taxPerc / 100, $precision);
2607
            $price = $priceWithoutTax + $taxAmount;
2608
        }
2609
2610
        $values = [
2611
            'service_id' => $serviceId,
2612
            'reference' => $this->generateReference(
2613
                $userId,
2614
                $service['applies_to'],
2615
                $infoSelect
2616
            ),
2617
            'currency_id' => $currency['id'],
2618
            'price' => $price,
2619
            'price_without_tax' => $priceWithoutTax,
2620
            'tax_perc' => $taxPerc,
2621
            'tax_amount' => $taxAmount,
2622
            'node_type' => $service['applies_to'],
2623
            'node_id' => (int) $infoSelect,
2624
            'buyer_id' => $userId,
2625
            'buy_date' => api_get_utc_datetime(),
2626
            'date_start' => api_get_utc_datetime(),
2627
            'date_end' => date_format(
2628
                date_add(
2629
                    date_create(api_get_utc_datetime()),
2630
                    date_interval_create_from_date_string($service['duration_days'].' days')
2631
                ),
2632
                'Y-m-d H:i:s'
2633
            ),
2634
            'status' => self::SERVICE_STATUS_PENDING,
2635
            'payment_type' => (int) $paymentType,
2636
        ];
2637
2638
        $returnedServiceSaleId = Database::insert(self::TABLE_SERVICES_SALE, $values);
2639
2640
        return $returnedServiceSaleId;
2641
    }
2642
2643
    /**
2644
     * Save Culqi configuration params.
2645
     *
2646
     * @param array $params
2647
     *
2648
     * @return int Rows affected. Otherwise return false
2649
     */
2650
    public function saveCulqiParameters($params)
2651
    {
2652
        return Database::update(
2653
            Database::get_main_table(self::TABLE_CULQI),
2654
            [
2655
                'commerce_code' => $params['commerce_code'],
2656
                'api_key' => $params['api_key'],
2657
                'integration' => $params['integration'],
2658
            ],
2659
            ['id = ?' => 1]
2660
        );
2661
    }
2662
2663
    /**
2664
     * Gets the stored Culqi params.
2665
     *
2666
     * @return array
2667
     */
2668
    public function getCulqiParams()
2669
    {
2670
        return Database::select(
2671
            '*',
2672
            Database::get_main_table(self::TABLE_CULQI),
2673
            ['id = ?' => 1],
2674
            'first'
2675
        );
2676
    }
2677
2678
    /**
2679
     * Save Global Parameters.
2680
     *
2681
     * @param array $params
2682
     *
2683
     * @return int Rows affected. Otherwise return false
2684
     */
2685
    public function saveGlobalParameters($params)
2686
    {
2687
        $sqlParams = [
2688
            'terms_and_conditions' => $params['terms_and_conditions'],
2689
            'sale_email' => $params['sale_email'],
2690
        ];
2691
2692
        if ('true' === $this->get('tax_enable')) {
2693
            $sqlParams['global_tax_perc'] = $params['global_tax_perc'];
2694
            $sqlParams['tax_applies_to'] = $params['tax_applies_to'];
2695
            $sqlParams['tax_name'] = $params['tax_name'];
2696
        }
2697
2698
        if ('true' === $this->get('invoicing_enable')) {
2699
            $sqlParams['seller_name'] = $params['seller_name'];
2700
            $sqlParams['seller_id'] = $params['seller_id'];
2701
            $sqlParams['seller_address'] = $params['seller_address'];
2702
            $sqlParams['seller_email'] = $params['seller_email'];
2703
            $sqlParams['next_number_invoice'] = $params['next_number_invoice'];
2704
            $sqlParams['invoice_series'] = $params['invoice_series'];
2705
        }
2706
2707
        return Database::update(
2708
            Database::get_main_table(self::TABLE_GLOBAL_CONFIG),
2709
            $sqlParams,
2710
            ['id = ?' => 1]
2711
        );
2712
    }
2713
2714
    /**
2715
     * get Global Parameters.
2716
     *
2717
     * @return array
2718
     */
2719
    public function getGlobalParameters()
2720
    {
2721
        return Database::select(
2722
            '*',
2723
            Database::get_main_table(self::TABLE_GLOBAL_CONFIG),
2724
            ['id = ?' => 1],
2725
            'first'
2726
        );
2727
    }
2728
2729
    /**
2730
     * @param int $productType
2731
     *
2732
     * @return bool
2733
     */
2734
    public function checkTaxEnabledInProduct($productType)
2735
    {
2736
        if (empty('true' === $this->get('tax_enable'))) {
2737
            return false;
2738
        }
2739
2740
        $globalParameters = $this->getGlobalParameters();
2741
        $taxAppliesTo = $globalParameters['tax_applies_to'];
2742
        if (self::TAX_APPLIES_TO_ALL == $taxAppliesTo) {
2743
            return true;
2744
        }
2745
2746
        if ($taxAppliesTo == $productType) {
2747
            return true;
2748
        }
2749
2750
        return false;
2751
    }
2752
2753
    /**
2754
     * Get the path.
2755
     *
2756
     * @param string $var path variable
2757
     *
2758
     * @return string path
2759
     */
2760
    public function getPath($var)
2761
    {
2762
        $pluginPath = api_get_path(WEB_PLUGIN_PATH).'BuyCourses/';
2763
        $paths = [
2764
            'SERVICE_IMAGES' => $pluginPath.'uploads/services/images/',
2765
            'SRC' => $pluginPath.'src/',
2766
            'VIEW' => $pluginPath.'view/',
2767
            'UPLOADS' => $pluginPath.'uploads/',
2768
            'LANGUAGES' => $pluginPath.'lang/',
2769
            'RESOURCES' => $pluginPath.'resources/',
2770
            'RESOURCES_IMG' => $pluginPath.'resources/img/',
2771
            'RESOURCES_CSS' => $pluginPath.'resources/css/',
2772
            'RESOURCES_JS' => $pluginPath.'resources/js/',
2773
        ];
2774
2775
        return $paths[$var];
2776
    }
2777
2778
    /**
2779
     * @return array
2780
     */
2781
    public function getBuyCoursePluginPrice(Session $session)
2782
    {
2783
        // start buycourse validation
2784
        // display the course price and buy button if the BuyCourses plugin is enabled and this course is configured
2785
        $isThisCourseInSale = $this->buyCoursesForGridCatalogValidator($session->getId(), self::PRODUCT_TYPE_SESSION);
2786
        $return = [];
2787
2788
        if ($isThisCourseInSale) {
2789
            // set the Price label
2790
            $return['html'] = $isThisCourseInSale['html'];
2791
            // set the Buy button instead register.
2792
            if ($isThisCourseInSale['verificator']) {
2793
                $return['buy_button'] = $this->returnBuyCourseButton($session->getId(), self::PRODUCT_TYPE_SESSION);
2794
            }
2795
        }
2796
        // end buycourse validation
2797
        return $return;
2798
    }
2799
2800
    /**
2801
     * @return string
2802
     */
2803
    public function getSubscriptionSuccessMessage(array $saleInfo)
2804
    {
2805
        switch ($saleInfo['product_type']) {
2806
            case self::PRODUCT_TYPE_COURSE:
2807
                $courseInfo = api_get_course_info_by_id($saleInfo['product_id']);
2808
                $url = api_get_course_url($courseInfo['real_id']);
2809
                break;
2810
            case self::PRODUCT_TYPE_SESSION:
2811
                $sessionId = (int) $saleInfo['product_id'];
2812
                $url = api_get_path(WEB_CODE_PATH).'session/index.php?session_id='.$sessionId;
2813
                break;
2814
            default:
2815
                $url = '#';
2816
        }
2817
2818
        return Display::return_message(
2819
            sprintf(
2820
                $this->get_lang('SubscriptionToCourseXSuccessful'),
2821
                $url,
2822
                $saleInfo['product_name']
2823
            ),
2824
            'success',
2825
            false
2826
        );
2827
    }
2828
2829
    /**
2830
     * Filter the registered courses for show in plugin catalog.
2831
     */
2832
    private function getCourses($first, $maxResults)
2833
    {
2834
        $em = Database::getManager();
2835
        $urlId = api_get_current_access_url_id();
2836
2837
        $qb = $em->createQueryBuilder();
2838
        $qb2 = $em->createQueryBuilder();
2839
        $qb3 = $em->createQueryBuilder();
2840
2841
        $qb = $qb
2842
            ->select('c')
2843
            ->from(Course::class, 'c')
2844
            ->where(
2845
                $qb->expr()->notIn(
2846
                    'c',
2847
                    $qb2
2848
                        ->select('course2')
2849
                        ->from(SessionRelCourse::class, 'sc')
2850
                        ->join('sc.course', 'course2')
2851
                        ->innerJoin(
2852
                            AccessUrlRelSession::class,
2853
                            'us',
2854
                            Join::WITH,
2855
                            'us.sessionId = sc.session'
2856
                        )->where(
2857
                            $qb->expr()->eq('us.accessUrlId ', $urlId)
2858
                        )
2859
                        ->getDQL()
2860
                )
2861
            )->andWhere(
2862
                $qb->expr()->in(
2863
                    'c',
2864
                    $qb3
2865
                        ->select('course3')
2866
                        ->from(AccessUrlRelCourse::class, 'uc')
2867
                        ->join('uc.course', 'course3')
2868
                        ->where(
2869
                            $qb3->expr()->eq('uc.url ', $urlId)
2870
                        )
2871
                        ->getDQL()
2872
                )
2873
            )
2874
        ->setFirstResult($first)
2875
        ->setMaxResults($maxResults);
2876
2877
        return $qb;
2878
    }
2879
2880
    /**
2881
     * Get the user status for the session.
2882
     *
2883
     * @param int     $userId  The user ID
2884
     * @param Session $session The session
2885
     *
2886
     * @return string
2887
     */
2888
    private function getUserStatusForSession($userId, Session $session)
2889
    {
2890
        if (empty($userId)) {
2891
            return 'NO';
2892
        }
2893
2894
        $entityManager = Database::getManager();
2895
        $scuRepo = $entityManager->getRepository(SessionRelCourseRelUser::class);
2896
2897
        $buySaleTable = Database::get_main_table(self::TABLE_SALE);
2898
2899
        // Check if user bought the course
2900
        $sale = Database::select(
2901
            'COUNT(1) as qty',
2902
            $buySaleTable,
2903
            [
2904
                'where' => [
2905
                    'user_id = ? AND product_type = ? AND product_id = ? AND status = ?' => [
2906
                        $userId,
2907
                        self::PRODUCT_TYPE_SESSION,
2908
                        $session->getId(),
2909
                        self::SALE_STATUS_PENDING,
2910
                    ],
2911
                ],
2912
            ],
2913
            'first'
2914
        );
2915
2916
        if ($sale['qty'] > 0) {
2917
            return 'TMP';
2918
        }
2919
2920
        // Check if user is already subscribe to session
2921
        $userSubscription = $scuRepo->findBy([
2922
            'session' => $session,
2923
            'user' => $userId,
2924
        ]);
2925
2926
        if (!empty($userSubscription)) {
2927
            return 'YES';
2928
        }
2929
2930
        return 'NO';
2931
    }
2932
2933
    /**
2934
     * Get the user status for the course.
2935
     *
2936
     * @param int    $userId The user Id
2937
     * @param Course $course The course
2938
     *
2939
     * @return string
2940
     */
2941
    private function getUserStatusForCourse($userId, Course $course)
2942
    {
2943
        if (empty($userId)) {
2944
            return 'NO';
2945
        }
2946
2947
        $entityManager = Database::getManager();
2948
        $cuRepo = $entityManager->getRepository(CourseRelUser::class);
2949
        $buySaleTable = Database::get_main_table(self::TABLE_SALE);
2950
2951
        // Check if user bought the course
2952
        $sale = Database::select(
2953
            'COUNT(1) as qty',
2954
            $buySaleTable,
2955
            [
2956
                'where' => [
2957
                    'user_id = ? AND product_type = ? AND product_id = ? AND status = ?' => [
2958
                        $userId,
2959
                        self::PRODUCT_TYPE_COURSE,
2960
                        $course->getId(),
2961
                        self::SALE_STATUS_PENDING,
2962
                    ],
2963
                ],
2964
            ],
2965
            'first'
2966
        );
2967
2968
        if ($sale['qty'] > 0) {
2969
            return 'TMP';
2970
        }
2971
2972
        // Check if user is already subscribe to course
2973
        $userSubscription = $cuRepo->findBy([
2974
            'course' => $course,
2975
            'user' => $userId,
2976
        ]);
2977
2978
        if (!empty($userSubscription)) {
2979
            return 'YES';
2980
        }
2981
2982
        return 'NO';
2983
    }
2984
2985
    /**
2986
     * Update the sale status.
2987
     *
2988
     * @param int $saleId    The sale ID
2989
     * @param int $newStatus The new status
2990
     *
2991
     * @return bool
2992
     */
2993
    private function updateSaleStatus($saleId, $newStatus = self::SALE_STATUS_PENDING)
2994
    {
2995
        $saleTable = Database::get_main_table(self::TABLE_SALE);
2996
2997
        return Database::update(
2998
            $saleTable,
2999
            ['status' => (int) $newStatus],
3000
            ['id = ?' => (int) $saleId]
3001
        );
3002
    }
3003
3004
    /**
3005
     * Search filtered sessions by name, and range of price.
3006
     *
3007
     * @param int    $start
3008
     * @param int    $end
3009
     * @param string $name  Optional. The name filter
3010
     * @param int    $min   Optional. The minimun price filter
3011
     * @param int    $max   Optional. The maximum price filter
3012
     *
3013
     * @return array
3014
     */
3015
    private function filterSessionList($start, $end, $name = null, $min = 0, $max = 0, $typeResult = 'all')
3016
    {
3017
        $itemTable = Database::get_main_table(self::TABLE_ITEM);
3018
        $sessionTable = Database::get_main_table(TABLE_MAIN_SESSION);
3019
3020
        $min = floatval($min);
3021
        $max = floatval($max);
3022
3023
        $innerJoin = "$itemTable i ON s.id = i.product_id";
3024
        $whereConditions = [
3025
            'i.product_type = ? ' => self::PRODUCT_TYPE_SESSION,
3026
        ];
3027
3028
        if (!empty($name)) {
3029
            $whereConditions['AND s.title LIKE %?%'] = $name;
3030
        }
3031
3032
        if (!empty($min)) {
3033
            $whereConditions['AND i.price >= ?'] = $min;
3034
        }
3035
3036
        if (!empty($max)) {
3037
            $whereConditions['AND i.price <= ?'] = $max;
3038
        }
3039
3040
        $start = (int) $start;
3041
        $end = (int) $end;
3042
3043
        $sessionIds = Database::select(
3044
            's.id',
3045
            "$sessionTable s INNER JOIN $innerJoin",
3046
            ['where' => $whereConditions, 'limit' => "$start, $end"],
3047
            $typeResult
3048
        );
3049
3050
        if ('count' === $typeResult) {
3051
            return $sessionIds;
3052
        }
3053
3054
        if (!$sessionIds) {
3055
            return [];
3056
        }
3057
3058
        $sessions = [];
3059
3060
        foreach ($sessionIds as $sessionId) {
3061
            $sessions[] = Database::getManager()->find(
3062
                Session::class,
3063
                $sessionId
3064
            );
3065
        }
3066
3067
        return $sessions;
3068
    }
3069
3070
    /**
3071
     * Search filtered courses by name, and range of price.
3072
     *
3073
     * @param string $name Optional. The name filter
3074
     * @param int    $min  Optional. The minimun price filter
3075
     * @param int    $max  Optional. The maximum price filter
3076
     *
3077
     * @return array
3078
     */
3079
    private function filterCourseList($start, $end, $name = '', $min = 0, $max = 0, $typeResult = 'all')
3080
    {
3081
        $itemTable = Database::get_main_table(self::TABLE_ITEM);
3082
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
3083
        $urlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
3084
3085
        $urlId = api_get_current_access_url_id();
3086
3087
        $min = floatval($min);
3088
        $max = floatval($max);
3089
3090
        $whereConditions = [
3091
            'i.product_type = ? ' => self::PRODUCT_TYPE_COURSE,
3092
        ];
3093
3094
        if (!empty($name)) {
3095
            $whereConditions['AND c.title LIKE %?%'] = $name;
3096
        }
3097
3098
        if (!empty($min)) {
3099
            $whereConditions['AND i.price >= ?'] = $min;
3100
        }
3101
3102
        if (!empty($max)) {
3103
            $whereConditions['AND i.price <= ?'] = $max;
3104
        }
3105
3106
        $whereConditions['AND url.access_url_id = ?'] = $urlId;
3107
        $start = (int) $start;
3108
        $end = (int) $end;
3109
3110
        $courseIds = Database::select(
3111
            'c.id',
3112
            "$courseTable c
3113
            INNER JOIN $itemTable i
3114
            ON c.id = i.product_id
3115
            INNER JOIN $urlTable url
3116
            ON c.id = url.c_id
3117
            ",
3118
            ['where' => $whereConditions, 'limit' => "$start, $end"],
3119
            $typeResult
3120
        );
3121
3122
        if ('count' === $typeResult) {
3123
            return $courseIds;
3124
        }
3125
3126
        if (!$courseIds) {
3127
            return [];
3128
        }
3129
3130
        $courses = [];
3131
        foreach ($courseIds as $courseId) {
3132
            $courses[] = Database::getManager()->find(
3133
                Course::class,
3134
                $courseId
3135
            );
3136
        }
3137
3138
        return $courses;
3139
    }
3140
3141
    /**
3142
     * Update the service sale status.
3143
     *
3144
     * @param int $serviceSaleId The service sale ID
3145
     * @param int $newStatus     The new status
3146
     *
3147
     * @return bool
3148
     */
3149
    private function updateServiceSaleStatus(
3150
        $serviceSaleId,
3151
        $newStatus = self::SERVICE_STATUS_PENDING
3152
    ) {
3153
        $serviceSaleTable = Database::get_main_table(self::TABLE_SERVICES_SALE);
3154
3155
        return Database::update(
3156
            $serviceSaleTable,
3157
            ['status' => (int) $newStatus],
3158
            ['id = ?' => (int) $serviceSaleId]
3159
        );
3160
    }
3161
3162
    public function get_name()
3163
    {
3164
        return 'BuyCourses'; // TODO: Change the autogenerated stub
3165
    }
3166
}
3167