Passed
Push — 1.11.x ( f020d6...19be68 )
by Angel Fernando Quiroz
10:52
created

verifyPaypalAccountByBeneficiary()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 37
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 20
nc 4
nop 1
dl 0
loc 37
rs 9.6
c 0
b 0
f 0
1
<?php
2
/* For license terms, see /license.txt */
3
4
use Chamilo\CoreBundle\Entity\Course;
5
use Chamilo\CoreBundle\Entity\Session;
6
use Doctrine\ORM\Query\Expr\Join;
7
use Symfony\Component\HttpFoundation\Request as HttpRequest;
8
9
/**
10
 * Plugin class for the BuyCourses plugin.
11
 *
12
 * @package chamilo.plugin.buycourses
13
 *
14
 * @author  Jose Angel Ruiz <[email protected]>
15
 * @author  Imanol Losada <[email protected]>
16
 * @author  Alex Aragón <[email protected]>
17
 * @author  Angel Fernando Quiroz Campos <[email protected]>
18
 * @author  José Loguercio Silva  <[email protected]>
19
 * @author  Julio Montoya
20
 */
21
class BuyCoursesPlugin extends Plugin
22
{
23
    const TABLE_PAYPAL = 'plugin_buycourses_paypal_account';
24
    const TABLE_CURRENCY = 'plugin_buycourses_currency';
25
    const TABLE_ITEM = 'plugin_buycourses_item';
26
    const TABLE_ITEM_BENEFICIARY = 'plugin_buycourses_item_rel_beneficiary';
27
    const TABLE_SALE = 'plugin_buycourses_sale';
28
    const TABLE_TRANSFER = 'plugin_buycourses_transfer';
29
    const TABLE_COMMISSION = 'plugin_buycourses_commission';
30
    const TABLE_PAYPAL_PAYOUTS = 'plugin_buycourses_paypal_payouts';
31
    const TABLE_SERVICES = 'plugin_buycourses_services';
32
    const TABLE_SERVICES_SALE = 'plugin_buycourses_service_sale';
33
    const TABLE_CULQI = 'plugin_buycourses_culqi';
34
    const TABLE_GLOBAL_CONFIG = 'plugin_buycourses_global_config';
35
    const TABLE_INVOICE = 'plugin_buycourses_invoices';
36
    const TABLE_TPV_REDSYS = 'plugin_buycourses_tpvredsys_account';
37
    const TABLE_COUPON = 'plugin_buycourses_coupon';
38
    const TABLE_COUPON_ITEM = 'plugin_buycourses_coupon_rel_item';
39
    const TABLE_COUPON_SERVICE = 'plugin_buycourses_coupon_rel_service';
40
    const TABLE_COUPON_SALE = 'plugin_buycourses_coupon_rel_sale';
41
    const TABLE_COUPON_SERVICE_SALE = 'plugin_buycourses_coupon_rel_service_sale';
42
    const TABLE_STRIPE = 'plugin_buycourses_stripe_account';
43
    const PRODUCT_TYPE_COURSE = 1;
44
    const PRODUCT_TYPE_SESSION = 2;
45
    const PRODUCT_TYPE_SERVICE = 3;
46
    const PAYMENT_TYPE_PAYPAL = 1;
47
    const PAYMENT_TYPE_TRANSFER = 2;
48
    const PAYMENT_TYPE_CULQI = 3;
49
    const PAYMENT_TYPE_TPV_REDSYS = 4;
50
    const PAYMENT_TYPE_STRIPE = 5;
51
    const PAYOUT_STATUS_CANCELED = 2;
52
    const PAYOUT_STATUS_PENDING = 0;
53
    const PAYOUT_STATUS_COMPLETED = 1;
54
    const SALE_STATUS_CANCELED = -1;
55
    const SALE_STATUS_PENDING = 0;
56
    const SALE_STATUS_COMPLETED = 1;
57
    const SERVICE_STATUS_PENDING = 0;
58
    const SERVICE_STATUS_COMPLETED = 1;
59
    const SERVICE_STATUS_CANCELLED = -1;
60
    const SERVICE_TYPE_USER = 1;
61
    const SERVICE_TYPE_COURSE = 2;
62
    const SERVICE_TYPE_SESSION = 3;
63
    const SERVICE_TYPE_LP_FINAL_ITEM = 4;
64
    const CULQI_INTEGRATION_TYPE = 'INTEG';
65
    const CULQI_PRODUCTION_TYPE = 'PRODUC';
66
    const TAX_APPLIES_TO_ALL = 1;
67
    const TAX_APPLIES_TO_ONLY_COURSE = 2;
68
    const TAX_APPLIES_TO_ONLY_SESSION = 3;
69
    const TAX_APPLIES_TO_ONLY_SERVICES = 4;
70
    const PAGINATION_PAGE_SIZE = 6;
71
    const COUPON_DISCOUNT_TYPE_PERCENTAGE = 1;
72
    const COUPON_DISCOUNT_TYPE_AMOUNT = 2;
73
    const COUPON_STATUS_ACTIVE = 1;
74
    const COUPON_STATUS_DISABLE = 0;
75
76
    public $isAdminPlugin = true;
77
78
    /**
79
     * BuyCoursesPlugin constructor.
80
     */
81
    public function __construct()
82
    {
83
        parent::__construct(
84
            '7.1',
85
            "
86
                Jose Angel Ruiz - NoSoloRed (original author) <br/>
87
                Francis Gonzales and Yannick Warnier - BeezNest (integration) <br/>
88
                Alex Aragón - BeezNest (Design icons and css styles) <br/>
89
                Imanol Losada - BeezNest (introduction of sessions purchase) <br/>
90
                Angel Fernando Quiroz Campos - BeezNest (cleanup and new reports) <br/>
91
                José Loguercio Silva - BeezNest (Payouts and buy Services) <br/>
92
                Julio Montoya
93
            ",
94
            [
95
                'show_main_menu_tab' => 'boolean',
96
                'public_main_menu_tab' => 'boolean',
97
                'include_sessions' => 'boolean',
98
                'include_services' => 'boolean',
99
                'paypal_enable' => 'boolean',
100
                'transfer_enable' => 'boolean',
101
                'culqi_enable' => 'boolean',
102
                'commissions_enable' => 'boolean',
103
                'unregistered_users_enable' => 'boolean',
104
                'hide_free_text' => 'boolean',
105
                'invoicing_enable' => 'boolean',
106
                'tax_enable' => 'boolean',
107
                'use_currency_symbol' => 'boolean',
108
                'tpv_redsys_enable' => 'boolean',
109
                'stripe_enable' => 'boolean',
110
            ]
111
        );
112
    }
113
114
    /**
115
     * @return BuyCoursesPlugin
116
     */
117
    public static function create()
118
    {
119
        static $result = null;
120
121
        return $result ? $result : $result = new self();
122
    }
123
124
    /**
125
     * Check if plugin is enabled.
126
     *
127
     * @param bool $checkEnabled Check if, additionnally to being installed, the plugin is enabled
128
     *
129
     * @return bool
130
     */
131
    public function isEnabled($checkEnabled = false)
132
    {
133
        return $this->get('paypal_enable') || $this->get('transfer_enable') || $this->get('culqi_enable') || $this->get('stripe_enable');
134
    }
135
136
    /**
137
     * This method creates the tables required to this plugin.
138
     */
139
    public function install()
140
    {
141
        $tablesToBeCompared = [
142
            self::TABLE_PAYPAL,
143
            self::TABLE_TRANSFER,
144
            self::TABLE_CULQI,
145
            self::TABLE_ITEM_BENEFICIARY,
146
            self::TABLE_ITEM,
147
            self::TABLE_SALE,
148
            self::TABLE_CURRENCY,
149
            self::TABLE_COMMISSION,
150
            self::TABLE_PAYPAL_PAYOUTS,
151
            self::TABLE_SERVICES,
152
            self::TABLE_SERVICES_SALE,
153
            self::TABLE_GLOBAL_CONFIG,
154
            self::TABLE_INVOICE,
155
            self::TABLE_TPV_REDSYS,
156
            self::TABLE_COUPON,
157
            self::TABLE_COUPON_ITEM,
158
            self::TABLE_COUPON_SERVICE,
159
            self::TABLE_COUPON_SALE,
160
            self::TABLE_COUPON_SERVICE_SALE,
161
            self::TABLE_STRIPE,
162
        ];
163
        $em = Database::getManager();
164
        $cn = $em->getConnection();
165
        $sm = $cn->getSchemaManager();
166
        $tables = $sm->tablesExist($tablesToBeCompared);
167
168
        if ($tables) {
169
            return false;
170
        }
171
172
        require_once api_get_path(SYS_PLUGIN_PATH).'buycourses/database.php';
173
    }
174
175
    /**
176
     * This method drops the plugin tables.
177
     */
178
    public function uninstall()
179
    {
180
        $tablesToBeDeleted = [
181
            self::TABLE_PAYPAL,
182
            self::TABLE_TRANSFER,
183
            self::TABLE_CULQI,
184
            self::TABLE_ITEM_BENEFICIARY,
185
            self::TABLE_ITEM,
186
            self::TABLE_SALE,
187
            self::TABLE_CURRENCY,
188
            self::TABLE_COMMISSION,
189
            self::TABLE_PAYPAL_PAYOUTS,
190
            self::TABLE_SERVICES_SALE,
191
            self::TABLE_SERVICES,
192
            self::TABLE_GLOBAL_CONFIG,
193
            self::TABLE_INVOICE,
194
            self::TABLE_TPV_REDSYS,
195
            self::TABLE_COUPON,
196
            self::TABLE_COUPON_ITEM,
197
            self::TABLE_COUPON_SERVICE,
198
            self::TABLE_COUPON_SALE,
199
            self::TABLE_COUPON_SERVICE_SALE,
200
            self::TABLE_STRIPE,
201
        ];
202
203
        foreach ($tablesToBeDeleted as $tableToBeDeleted) {
204
            $table = Database::get_main_table($tableToBeDeleted);
205
            $sql = "DROP TABLE IF EXISTS $table";
206
            Database::query($sql);
207
        }
208
        $this->manageTab(false);
209
    }
210
211
    public function update()
212
    {
213
        $table = self::TABLE_GLOBAL_CONFIG;
214
        $sql = "SHOW COLUMNS FROM $table WHERE Field = 'global_tax_perc'";
215
        $res = Database::query($sql);
216
217
        if (Database::num_rows($res) === 0) {
218
            $sql = "ALTER TABLE $table ADD (
219
                sale_email varchar(255) NOT NULL,
220
                global_tax_perc int unsigned NOT NULL,
221
                tax_applies_to int unsigned NOT NULL,
222
                tax_name varchar(255) NOT NULL,
223
                seller_name varchar(255) NOT NULL,
224
                seller_id varchar(255) NOT NULL,
225
                seller_address varchar(255) NOT NULL,
226
                seller_email varchar(255) NOT NULL,
227
                next_number_invoice int unsigned NOT NULL,
228
                invoice_series varchar(255) NOT NULL
229
            )";
230
            $res = Database::query($sql);
231
            if (!$res) {
0 ignored issues
show
introduced by
$res is of type Doctrine\DBAL\Driver\Statement, thus it always evaluated to true.
Loading history...
232
                echo Display::return_message($this->get_lang('ErrorUpdateFieldDB'), 'warning');
233
            }
234
        }
235
236
        $sql = "SHOW COLUMNS FROM $table WHERE Field = 'info_email_extra'";
237
        $res = Database::query($sql);
238
239
        if (Database::num_rows($res) === 0) {
240
            $sql = "ALTER TABLE $table ADD (info_email_extra TEXT NOT NULL)";
241
            $res = Database::query($sql);
242
            if (!$res) {
0 ignored issues
show
introduced by
$res is of type Doctrine\DBAL\Driver\Statement, thus it always evaluated to true.
Loading history...
243
                echo Display::return_message($this->get_lang('ErrorUpdateFieldDB'), 'warning');
244
            }
245
        }
246
247
        $table = self::TABLE_ITEM;
248
        $sql = "SHOW COLUMNS FROM $table WHERE Field = 'tax_perc'";
249
        $res = Database::query($sql);
250
251
        if (Database::num_rows($res) === 0) {
252
            $sql = "ALTER TABLE $table ADD tax_perc int unsigned NULL";
253
            $res = Database::query($sql);
254
            if (!$res) {
0 ignored issues
show
introduced by
$res is of type Doctrine\DBAL\Driver\Statement, thus it always evaluated to true.
Loading history...
255
                echo Display::return_message($this->get_lang('ErrorUpdateFieldDB'), 'warning');
256
            }
257
        }
258
259
        $table = self::TABLE_SERVICES;
260
        $sql = "SHOW COLUMNS FROM $table WHERE Field = 'tax_perc'";
261
        $res = Database::query($sql);
262
263
        if (Database::num_rows($res) === 0) {
264
            $sql = "ALTER TABLE $table ADD tax_perc int unsigned NULL";
265
            $res = Database::query($sql);
266
            if (!$res) {
0 ignored issues
show
introduced by
$res is of type Doctrine\DBAL\Driver\Statement, thus it always evaluated to true.
Loading history...
267
                echo Display::return_message($this->get_lang('ErrorUpdateFieldDB'), 'warning');
268
            }
269
        }
270
271
        $table = self::TABLE_SALE;
272
        $sql = "SHOW COLUMNS FROM $table WHERE Field = 'tax_perc'";
273
        $res = Database::query($sql);
274
275
        if (Database::num_rows($res) === 0) {
276
            $sql = "ALTER TABLE $table ADD (
277
                price_without_tax decimal(10,2) NULL,
278
                tax_perc int unsigned NULL,
279
                tax_amount decimal(10,2) NULL,
280
                invoice int unsigned NULL
281
            )";
282
            $res = Database::query($sql);
283
            if (!$res) {
0 ignored issues
show
introduced by
$res is of type Doctrine\DBAL\Driver\Statement, thus it always evaluated to true.
Loading history...
284
                echo Display::return_message($this->get_lang('ErrorUpdateFieldDB'), 'warning');
285
            }
286
        }
287
288
        $sql = "SHOW COLUMNS FROM $table WHERE Field = 'price_without_discount'";
289
        $res = Database::query($sql);
290
291
        if (Database::num_rows($res) === 0) {
292
            $sql = "ALTER TABLE $table ADD (
293
                price_without_discount decimal(10,2) NULL,
294
                discount_amount decimal(10,2) NULL
295
            )";
296
            $res = Database::query($sql);
297
            if (!$res) {
0 ignored issues
show
introduced by
$res is of type Doctrine\DBAL\Driver\Statement, thus it always evaluated to true.
Loading history...
298
                echo Display::return_message($this->get_lang('ErrorUpdateFieldDB'), 'warning');
299
            }
300
        }
301
302
        $table = self::TABLE_SERVICES_SALE;
303
        $sql = "SHOW COLUMNS FROM $table WHERE Field = 'tax_perc'";
304
        $res = Database::query($sql);
305
306
        if (Database::num_rows($res) === 0) {
307
            $sql = "ALTER TABLE $table ADD (
308
                price_without_tax decimal(10,2) NULL,
309
                tax_perc int unsigned NULL,
310
                tax_amount decimal(10,2) NULL,
311
                invoice int unsigned NULL
312
            )";
313
            $res = Database::query($sql);
314
            if (!$res) {
0 ignored issues
show
introduced by
$res is of type Doctrine\DBAL\Driver\Statement, thus it always evaluated to true.
Loading history...
315
                echo Display::return_message($this->get_lang('ErrorUpdateFieldDB'), 'warning');
316
            }
317
        }
318
319
        $sql = "SHOW COLUMNS FROM $table WHERE Field = 'price_without_discount'";
320
        $res = Database::query($sql);
321
322
        if (Database::num_rows($res) === 0) {
323
            $sql = "ALTER TABLE $table ADD (
324
                price_without_discount decimal(10,2) NULL,
325
                discount_amount decimal(10,2) NULL
326
            )";
327
            $res = Database::query($sql);
328
            if (!$res) {
0 ignored issues
show
introduced by
$res is of type Doctrine\DBAL\Driver\Statement, thus it always evaluated to true.
Loading history...
329
                echo Display::return_message($this->get_lang('ErrorUpdateFieldDB'), 'warning');
330
            }
331
        }
332
333
        $table = self::TABLE_INVOICE;
334
        $sql = "CREATE TABLE IF NOT EXISTS $table (
335
            id int unsigned NOT NULL AUTO_INCREMENT,
336
            sale_id int unsigned NOT NULL,
337
            is_service int unsigned NOT NULL,
338
            num_invoice int unsigned NOT NULL,
339
            year int(4) unsigned NOT NULL,
340
            serie varchar(255) NOT NULL,
341
            date_invoice datetime NOT NULL,
342
            PRIMARY KEY (id)
343
        )";
344
        Database::query($sql);
345
346
        $table = self::TABLE_TPV_REDSYS;
347
        $sql = "CREATE TABLE IF NOT EXISTS $table (
348
            id int unsigned NOT NULL AUTO_INCREMENT,
349
            merchantcode varchar(255) NOT NULL,
350
            terminal varchar(255) NOT NULL,
351
            currency varchar(255) NOT NULL,
352
            kc varchar(255) NOT NULL,
353
            url_redsys varchar(255) NOT NULL,
354
            url_redsys_sandbox varchar(255) NOT NULL,
355
            sandbox int unsigned NULL,
356
            PRIMARY KEY (id)
357
        )";
358
        Database::query($sql);
359
360
        $sql = "SELECT * FROM $table";
361
        $res = Database::query($sql);
362
        if (Database::num_rows($res) == 0) {
363
            Database::insert($table, [
364
                'url_redsys' => 'https://sis.redsys.es/sis/realizarPago',
365
                'url_redsys_sandbox' => 'https://sis-t.redsys.es:25443/sis/realizarPago',
366
            ]);
367
        }
368
369
        $table = self::TABLE_COUPON;
370
        $sql = "CREATE TABLE IF NOT EXISTS $table (
371
            id int unsigned NOT NULL AUTO_INCREMENT,
372
            code varchar(255) NOT NULL,
373
            discount_type int unsigned NOT NULL,
374
            discount_amount decimal(10, 2) NOT NULL,
375
            valid_start datetime NOT NULL,
376
            valid_end datetime NOT NULL,
377
            delivered varchar(255) NOT NULL,
378
            active tinyint NOT NULL,
379
            PRIMARY KEY (id)
380
        )";
381
        Database::query($sql);
382
383
        $table = self::TABLE_COUPON_ITEM;
384
        $sql = "CREATE TABLE IF NOT EXISTS $table (
385
            id int unsigned NOT NULL AUTO_INCREMENT,
386
            coupon_id int unsigned NOT NULL,
387
            product_type int unsigned NOT NULL,
388
            product_id int unsigned NOT NULL,
389
            PRIMARY KEY (id)
390
        )";
391
        Database::query($sql);
392
393
        $table = self::TABLE_COUPON_SERVICE;
394
        $sql = "CREATE TABLE IF NOT EXISTS $table (
395
            id int unsigned NOT NULL AUTO_INCREMENT,
396
            coupon_id int unsigned NOT NULL,
397
            service_id int unsigned NOT NULL,
398
            PRIMARY KEY (id)
399
        )";
400
        Database::query($sql);
401
402
        $table = self::TABLE_COUPON_SALE;
403
        $sql = "CREATE TABLE IF NOT EXISTS $table (
404
            id int unsigned NOT NULL AUTO_INCREMENT,
405
            coupon_id int unsigned NOT NULL,
406
            sale_id int unsigned NOT NULL,
407
            PRIMARY KEY (id)
408
        )";
409
        Database::query($sql);
410
411
        $table = self::TABLE_COUPON_SERVICE_SALE;
412
        $sql = "CREATE TABLE IF NOT EXISTS $table (
413
            id int unsigned NOT NULL AUTO_INCREMENT,
414
            coupon_id int unsigned NOT NULL,
415
            service_sale_id int unsigned NOT NULL,
416
            PRIMARY KEY (id)
417
        )";
418
        Database::query($sql);
419
420
        $table = self::TABLE_STRIPE;
421
        $sql = "CREATE TABLE IF NOT EXISTS $table (
422
            id int unsigned NOT NULL AUTO_INCREMENT,
423
            account_id varchar(255) NOT NULL,
424
            secret_key varchar(255) NOT NULL,
425
            endpoint_secret varchar(255) NOT NULL,
426
            PRIMARY KEY (id)
427
        )";
428
        Database::query($sql);
429
430
        $sql = "SELECT * FROM $table";
431
        $res = Database::query($sql);
432
        if (Database::num_rows($res) == 0) {
433
            Database::insert($table, [
434
                'account_id' => '',
435
                'secret_key' => '',
436
                'endpoint_secret' => '',
437
            ]);
438
        }
439
440
        Display::addFlash(
441
            Display::return_message(
442
                $this->get_lang('Updated'),
443
                'info',
444
                false
445
            )
446
        );
447
448
        $fieldlabel = 'buycourses_company';
449
        $fieldtype = '1';
450
        $fieldtitle = $this->get_lang('Company');
451
        $fielddefault = '';
452
        UserManager::create_extra_field($fieldlabel, $fieldtype, $fieldtitle, $fielddefault);
453
454
        $fieldlabel = 'buycourses_vat';
455
        $fieldtype = '1';
456
        $fieldtitle = $this->get_lang('VAT');
457
        $fielddefault = '';
458
        UserManager::create_extra_field($fieldlabel, $fieldtype, $fieldtitle, $fielddefault);
459
460
        $fieldlabel = 'buycourses_address';
461
        $fieldtype = '1';
462
        $fieldtitle = $this->get_lang('Address');
463
        $fielddefault = '';
464
        UserManager::create_extra_field($fieldlabel, $fieldtype, $fieldtitle, $fielddefault);
465
466
        header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses');
467
        exit;
468
    }
469
470
    /**
471
     * This function verify if the plugin is enable and return the price info for a course or session in the new grid
472
     * 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
473
     * course catalog so the old buycourses plugin catalog can be deprecated.
474
     *
475
     * @param int $productId   course or session id
476
     * @param int $productType course or session type
477
     *
478
     * @return mixed bool|string html
479
     */
480
    public function buyCoursesForGridCatalogValidator($productId, $productType)
481
    {
482
        $return = [];
483
        $paypal = $this->get('paypal_enable') === 'true';
484
        $transfer = $this->get('transfer_enable') === 'true';
485
        $hideFree = $this->get('hide_free_text') === 'true';
486
487
        if ($paypal || $transfer) {
488
            $item = $this->getItemByProduct($productId, $productType);
489
            $html = '<div class="buycourses-price">';
490
            if ($item) {
491
                $html .= '<span class="label label-primary label-price">
492
                            <strong>'.$item['total_price_formatted'].'</strong>
493
                          </span>';
494
                $return['verificator'] = true;
495
            } else {
496
                if ($hideFree == false) {
497
                    $html .= '<span class="label label-primary label-free">
498
                                <strong>'.$this->get_lang('Free').'</strong>
499
                              </span>';
500
                }
501
                $return['verificator'] = false;
502
            }
503
            $html .= '</div>';
504
            $return['html'] = $html;
505
506
            return $return;
507
        }
508
509
        return false;
510
    }
511
512
    /**
513
     * Return the buyCourses plugin button to buy the course.
514
     *
515
     * @param int $productId
516
     * @param int $productType
517
     *
518
     * @return string $html
519
     */
520
    public function returnBuyCourseButton($productId, $productType)
521
    {
522
        $productId = (int) $productId;
523
        $productType = (int) $productType;
524
        $url = api_get_path(WEB_PLUGIN_PATH).'buycourses/src/process.php?i='.$productId.'&t='.$productType;
525
        $html = '<a class="btn btn-success btn-sm" title="'.$this->get_lang('Buy').'" href="'.$url.'">'.
526
            Display::returnFontAwesomeIcon('shopping-cart').'</a>';
527
528
        return $html;
529
    }
530
531
    /**
532
     * Get the currency for sales.
533
     *
534
     * @return array The selected currency. Otherwise return false
535
     */
536
    public function getSelectedCurrency()
537
    {
538
        return Database::select(
539
            '*',
540
            Database::get_main_table(self::TABLE_CURRENCY),
541
            [
542
                'where' => ['status = ?' => true],
543
            ],
544
            'first'
545
        );
546
    }
547
548
    /**
549
     * Get a list of currencies.
550
     *
551
     * @return array The currencies. Otherwise return false
552
     */
553
    public function getCurrencies()
554
    {
555
        return Database::select(
556
            '*',
557
            Database::get_main_table(self::TABLE_CURRENCY)
558
        );
559
    }
560
561
    /**
562
     * Save the selected currency.
563
     *
564
     * @param int $selectedId The currency Id
565
     */
566
    public function saveCurrency($selectedId)
567
    {
568
        $currencyTable = Database::get_main_table(
569
            self::TABLE_CURRENCY
570
        );
571
572
        Database::update(
573
            $currencyTable,
574
            ['status' => 0]
575
        );
576
        Database::update(
577
            $currencyTable,
578
            ['status' => 1],
579
            ['id = ?' => (int) $selectedId]
580
        );
581
    }
582
583
    /**
584
     * Save the PayPal configuration params.
585
     *
586
     * @param array $params
587
     *
588
     * @return int Rows affected. Otherwise return false
589
     */
590
    public function savePaypalParams($params)
591
    {
592
        return Database::update(
593
            Database::get_main_table(self::TABLE_PAYPAL),
594
            [
595
                'username' => $params['username'],
596
                'password' => $params['password'],
597
                'signature' => $params['signature'],
598
                'sandbox' => isset($params['sandbox']),
599
            ],
600
            ['id = ?' => 1]
601
        );
602
    }
603
604
    /**
605
     * Gets the stored PayPal params.
606
     *
607
     * @return array
608
     */
609
    public function getPaypalParams()
610
    {
611
        return Database::select(
612
            '*',
613
            Database::get_main_table(self::TABLE_PAYPAL),
614
            ['id = ?' => 1],
615
            'first'
616
        );
617
    }
618
619
    /**
620
     * Gets the stored TPV Redsys params.
621
     *
622
     * @return array
623
     */
624
    public function getTpvRedsysParams()
625
    {
626
        return Database::select(
627
            '*',
628
            Database::get_main_table(self::TABLE_TPV_REDSYS),
629
            ['id = ?' => 1],
630
            'first'
631
        );
632
    }
633
634
    /**
635
     * Save the tpv Redsys configuration params.
636
     *
637
     * @param array $params
638
     *
639
     * @return int Rows affected. Otherwise return false
640
     */
641
    public function saveTpvRedsysParams($params)
642
    {
643
        return Database::update(
644
            Database::get_main_table(self::TABLE_TPV_REDSYS),
645
            [
646
                'merchantcode' => $params['merchantcode'],
647
                'terminal' => $params['terminal'],
648
                'currency' => $params['currency'],
649
                'kc' => $params['kc'],
650
                'url_redsys' => $params['url_redsys'],
651
                'url_redsys_sandbox' => $params['url_redsys_sandbox'],
652
                'sandbox' => isset($params['sandbox']),
653
            ],
654
            ['id = ?' => 1]
655
        );
656
    }
657
658
    /**
659
     * Save Stripe configuration params.
660
     *
661
     * @param array $params
662
     *
663
     * @return int Rows affected. Otherwise return false
664
     */
665
    public function saveStripeParameters($params)
666
    {
667
        return Database::update(
668
            Database::get_main_table(self::TABLE_STRIPE),
669
            [
670
                'account_id' => $params['account_id'],
671
                'secret_key' => $params['secret_key'],
672
                'endpoint_secret' => $params['endpoint_secret'],
673
            ],
674
            ['id = ?' => 1]
675
        );
676
    }
677
678
    /**
679
     * Gets the stored Stripe params.
680
     *
681
     * @return array
682
     */
683
    public function getStripeParams()
684
    {
685
        return Database::select(
686
            '*',
687
            Database::get_main_table(self::TABLE_STRIPE),
688
            ['id = ?' => 1],
689
            'first'
690
        );
691
    }
692
693
    /**
694
     * Save a transfer account information.
695
     *
696
     * @param array $params The transfer account
697
     *
698
     * @return int Rows affected. Otherwise return false
699
     */
700
    public function saveTransferAccount($params)
701
    {
702
        return Database::insert(
703
            Database::get_main_table(self::TABLE_TRANSFER),
704
            [
705
                'name' => $params['tname'],
706
                'account' => $params['taccount'],
707
                'swift' => $params['tswift'],
708
            ]
709
        );
710
    }
711
712
    /**
713
     * Save email message information in transfer.
714
     *
715
     * @param array $params The transfer message
716
     *
717
     * @return int Rows affected. Otherwise return false
718
     */
719
    public function saveTransferInfoEmail($params)
720
    {
721
        return Database::update(
722
            Database::get_main_table(self::TABLE_GLOBAL_CONFIG),
723
            ['info_email_extra' => $params['tinfo_email_extra']],
724
            ['id = ?' => 1]
725
        );
726
    }
727
728
    /**
729
     * Gets message information for transfer email.
730
     *
731
     * @return array
732
     */
733
    public function getTransferInfoExtra()
734
    {
735
        return Database::select(
736
            'info_email_extra AS tinfo_email_extra',
737
            Database::get_main_table(self::TABLE_GLOBAL_CONFIG),
738
            ['id = ?' => 1],
739
            'first'
740
        );
741
    }
742
743
    /**
744
     * Get a list of transfer accounts.
745
     *
746
     * @return array
747
     */
748
    public function getTransferAccounts()
749
    {
750
        return Database::select(
751
            '*',
752
            Database::get_main_table(self::TABLE_TRANSFER)
753
        );
754
    }
755
756
    /**
757
     * Remove a transfer account.
758
     *
759
     * @param int $id The transfer account ID
760
     *
761
     * @return int Rows affected. Otherwise return false
762
     */
763
    public function deleteTransferAccount($id)
764
    {
765
        return Database::delete(
766
            Database::get_main_table(self::TABLE_TRANSFER),
767
            ['id = ?' => (int) $id]
768
        );
769
    }
770
771
    /**
772
     * Get registered item data.
773
     *
774
     * @param int $itemId The item ID
775
     *
776
     * @return array
777
     */
778
    public function getItem($itemId)
779
    {
780
        return Database::select(
781
            '*',
782
            Database::get_main_table(self::TABLE_ITEM),
783
            [
784
                'where' => ['id = ?' => (int) $itemId],
785
            ],
786
            'first'
787
        );
788
    }
789
790
    /**
791
     * Get the item data.
792
     *
793
     * @param int   $productId The item ID
794
     * @param int   $itemType  The item type
795
     * @param array $coupon
796
     *
797
     * @return array
798
     */
799
    public function getItemByProduct($productId, $itemType, $coupon = null)
800
    {
801
        $buyItemTable = Database::get_main_table(self::TABLE_ITEM);
802
        $buyCurrencyTable = Database::get_main_table(self::TABLE_CURRENCY);
803
804
        $fakeItemFrom = "
805
            $buyItemTable i
806
            INNER JOIN $buyCurrencyTable c
807
                ON i.currency_id = c.id
808
        ";
809
810
        $product = Database::select(
811
            ['i.*', 'c.iso_code'],
812
            $fakeItemFrom,
813
            [
814
                'where' => [
815
                    'i.product_id = ? AND i.product_type = ?' => [
816
                        (int) $productId,
817
                        (int) $itemType,
818
                    ],
819
                ],
820
            ],
821
            'first'
822
        );
823
824
        if (empty($product)) {
825
            return false;
826
        }
827
828
        $this->setPriceSettings($product, self::TAX_APPLIES_TO_ONLY_COURSE, $coupon);
829
830
        return $product;
831
    }
832
833
    /**
834
     * List courses details from the configuration page.
835
     *
836
     * @return array
837
     */
838
    public function getCourseList($first, $maxResults)
839
    {
840
        return $this->getCourses($first, $maxResults);
841
    }
842
843
    /**
844
     * Lists current user session details, including each session course details.
845
     *
846
     * It can return the number of rows when $typeResult is 'count'.
847
     *
848
     * @param int    $start
849
     * @param int    $end
850
     * @param string $name       Optional. The name filter.
851
     * @param int    $min        Optional. The minimum price filter.
852
     * @param int    $max        Optional. The maximum price filter.
853
     * @param string $typeResult Optional. 'all', 'first' or 'count'.
854
     *
855
     * @return array|int
856
     */
857
    public function getCatalogSessionList($start, $end, $name = null, $min = 0, $max = 0, $typeResult = 'all', $sessionCategory = 0)
858
    {
859
        $sessions = $this->filterSessionList($start, $end, $name, $min, $max, $typeResult, $sessionCategory);
860
861
        if ($typeResult === 'count') {
862
            return $sessions;
863
        }
864
865
        $sessionCatalog = [];
866
        // loop through all sessions
867
        foreach ($sessions as $session) {
868
            $sessionCourses = $session->getCourses();
869
870
            if (empty($sessionCourses)) {
871
                continue;
872
            }
873
874
            $item = $this->getItemByProduct(
875
                $session->getId(),
876
                self::PRODUCT_TYPE_SESSION
877
            );
878
879
            if (empty($item)) {
880
                continue;
881
            }
882
883
            $sessionData = $this->getSessionInfo($session->getId());
884
            $sessionData['coach'] = $session->getGeneralCoach()->getCompleteName();
885
            $sessionData['enrolled'] = $this->getUserStatusForSession(
886
                api_get_user_id(),
887
                $session
888
            );
889
            $sessionData['courses'] = [];
890
891
            foreach ($sessionCourses as $sessionCourse) {
892
                $course = $sessionCourse->getCourse();
893
894
                $sessionCourseData = [
895
                    'title' => $course->getTitle(),
896
                    'coaches' => [],
897
                ];
898
899
                $userCourseSubscriptions = $session->getUserCourseSubscriptionsByStatus(
900
                    $course,
901
                    Chamilo\CoreBundle\Entity\Session::COACH
902
                );
903
904
                foreach ($userCourseSubscriptions as $userCourseSubscription) {
905
                    $user = $userCourseSubscription->getUser();
906
                    $sessionCourseData['coaches'][] = $user->getCompleteName();
907
                }
908
                $sessionData['courses'][] = $sessionCourseData;
909
            }
910
911
            $sessionCatalog[] = $sessionData;
912
        }
913
914
        return $sessionCatalog;
915
    }
916
917
    /**
918
     * Lists current user course details.
919
     *
920
     * @param string $name Optional. The name filter
921
     * @param int    $min  Optional. The minimum price filter
922
     * @param int    $max  Optional. The maximum price filter
923
     *
924
     * @return array|int
925
     */
926
    public function getCatalogCourseList($first, $pageSize, $name = null, $min = 0, $max = 0, $typeResult = 'all')
927
    {
928
        $courses = $this->filterCourseList($first, $pageSize, $name, $min, $max, $typeResult);
929
930
        if ($typeResult === 'count') {
931
            return $courses;
932
        }
933
934
        if (empty($courses)) {
935
            return [];
936
        }
937
938
        $courseCatalog = [];
939
        foreach ($courses as $course) {
940
            $item = $this->getItemByProduct(
941
                $course->getId(),
942
                self::PRODUCT_TYPE_COURSE
943
            );
944
945
            if (empty($item)) {
946
                continue;
947
            }
948
949
            $courseItem = [
950
                'id' => $course->getId(),
951
                'title' => $course->getTitle(),
952
                'code' => $course->getCode(),
953
                'course_img' => null,
954
                'item' => $item,
955
                'teachers' => [],
956
                'enrolled' => $this->getUserStatusForCourse(api_get_user_id(), $course),
957
            ];
958
959
            foreach ($course->getTeachers() as $courseUser) {
960
                $teacher = $courseUser->getUser();
961
                $courseItem['teachers'][] = $teacher->getCompleteName();
962
            }
963
964
            // Check images
965
            $possiblePath = api_get_path(SYS_COURSE_PATH);
966
            $possiblePath .= $course->getDirectory();
967
            $possiblePath .= '/course-pic.png';
968
969
            if (file_exists($possiblePath)) {
970
                $courseItem['course_img'] = api_get_path(WEB_COURSE_PATH).$course->getDirectory().'/course-pic.png';
971
            }
972
            $courseCatalog[] = $courseItem;
973
        }
974
975
        return $courseCatalog;
976
    }
977
978
    /**
979
     * @param $price
980
     * @param $isoCode
981
     *
982
     * @return string
983
     */
984
    public function getPriceWithCurrencyFromIsoCode($price, $isoCode)
985
    {
986
        $useSymbol = $this->get('use_currency_symbol') === 'true';
987
988
        $result = $isoCode.' '.$price;
989
        if ($useSymbol) {
990
            if ($isoCode === 'BRL') {
991
                $symbol = 'R$';
992
            } else {
993
                $symbol = Symfony\Component\Intl\Intl::getCurrencyBundle()->getCurrencySymbol($isoCode);
994
            }
995
            $result = $symbol.' '.$price;
996
        }
997
998
        return $result;
999
    }
1000
1001
    /**
1002
     * Get course info.
1003
     *
1004
     * @param int $courseId The course ID
1005
     *
1006
     * @return array
1007
     */
1008
    public function getCourseInfo($courseId, $coupon = null)
1009
    {
1010
        $entityManager = Database::getManager();
1011
        $course = $entityManager->find('ChamiloCoreBundle:Course', $courseId);
1012
1013
        if (empty($course)) {
1014
            return [];
1015
        }
1016
1017
        $item = $this->getItemByProduct(
1018
            $course->getId(),
1019
            self::PRODUCT_TYPE_COURSE,
1020
            $coupon
1021
        );
1022
1023
        if (empty($item)) {
1024
            return [];
1025
        }
1026
1027
        $courseDescription = $entityManager->getRepository('ChamiloCourseBundle:CCourseDescription')
1028
            ->findOneBy(
1029
                [
1030
                    'cId' => $course->getId(),
1031
                    'sessionId' => 0,
1032
                ],
1033
                [
1034
                    'descriptionType' => 'ASC',
1035
                ]
1036
            );
1037
1038
        $globalParameters = $this->getGlobalParameters();
1039
        $courseInfo = [
1040
            'id' => $course->getId(),
1041
            'title' => $course->getTitle(),
1042
            'description' => $courseDescription ? $courseDescription->getContent() : null,
1043
            'code' => $course->getCode(),
1044
            'visual_code' => $course->getVisualCode(),
1045
            'teachers' => [],
1046
            'item' => $item,
1047
            'tax_name' => $globalParameters['tax_name'],
1048
            'tax_enable' => $this->checkTaxEnabledInProduct(self::TAX_APPLIES_TO_ONLY_COURSE),
1049
            'course_img' => null,
1050
        ];
1051
1052
        $courseTeachers = $course->getTeachers();
1053
1054
        foreach ($courseTeachers as $teachers) {
1055
            $user = $teachers->getUser();
1056
            $teacher['id'] = $user->getId();
1057
            $teacher['name'] = $user->getCompleteName();
1058
            $courseInfo['teachers'][] = $teacher;
1059
        }
1060
1061
        $possiblePath = api_get_path(SYS_COURSE_PATH);
1062
        $possiblePath .= $course->getDirectory();
1063
        $possiblePath .= '/course-pic.png';
1064
1065
        if (file_exists($possiblePath)) {
1066
            $courseInfo['course_img'] = api_get_path(WEB_COURSE_PATH).$course->getDirectory().'/course-pic.png';
1067
        }
1068
1069
        return $courseInfo;
1070
    }
1071
1072
    /**
1073
     * Get session info.
1074
     *
1075
     * @param array $sessionId The session ID
1076
     *
1077
     * @return array
1078
     */
1079
    public function getSessionInfo($sessionId, $coupon = null)
1080
    {
1081
        $entityManager = Database::getManager();
1082
        $session = $entityManager->find('ChamiloCoreBundle:Session', $sessionId);
1083
1084
        if (empty($session)) {
1085
            return [];
1086
        }
1087
1088
        $item = $this->getItemByProduct(
1089
            $session->getId(),
1090
            self::PRODUCT_TYPE_SESSION,
1091
            $coupon
1092
        );
1093
1094
        if (empty($item)) {
1095
            return [];
1096
        }
1097
1098
        $sessionDates = SessionManager::parseSessionDates(
1099
            [
1100
                'display_start_date' => $session->getDisplayStartDate(),
1101
                'display_end_date' => $session->getDisplayEndDate(),
1102
                'access_start_date' => $session->getAccessStartDate(),
1103
                'access_end_date' => $session->getAccessEndDate(),
1104
                'coach_access_start_date' => $session->getCoachAccessStartDate(),
1105
                'coach_access_end_date' => $session->getCoachAccessEndDate(),
1106
            ]
1107
        );
1108
1109
        $globalParameters = $this->getGlobalParameters();
1110
        $sessionInfo = [
1111
            'id' => $session->getId(),
1112
            'name' => $session->getName(),
1113
            'description' => $session->getDescription(),
1114
            'dates' => $sessionDates,
1115
            'courses' => [],
1116
            'tax_name' => $globalParameters['tax_name'],
1117
            'tax_enable' => $this->checkTaxEnabledInProduct(self::TAX_APPLIES_TO_ONLY_SESSION),
1118
            'image' => null,
1119
            'nbrCourses' => $session->getNbrCourses(),
1120
            'nbrUsers' => $session->getNbrUsers(),
1121
            'item' => $item,
1122
            'duration' => $session->getDuration(),
1123
        ];
1124
1125
        $fieldValue = new ExtraFieldValue('session');
1126
        $sessionImage = $fieldValue->get_values_by_handler_and_field_variable(
1127
            $session->getId(),
1128
            'image'
1129
        );
1130
1131
        if (!empty($sessionImage)) {
1132
            $sessionInfo['image'] = api_get_path(WEB_UPLOAD_PATH).$sessionImage['value'];
1133
        }
1134
1135
        $sessionCourses = $session->getCourses();
1136
        foreach ($sessionCourses as $sessionCourse) {
1137
            $course = $sessionCourse->getCourse();
1138
            $sessionCourseData = [
1139
                'title' => $course->getTitle(),
1140
                'coaches' => [],
1141
            ];
1142
1143
            $userCourseSubscriptions = $session->getUserCourseSubscriptionsByStatus(
1144
                $course,
1145
                Chamilo\CoreBundle\Entity\Session::COACH
1146
            );
1147
1148
            foreach ($userCourseSubscriptions as $userCourseSubscription) {
1149
                $user = $userCourseSubscription->getUser();
1150
                $coaches['id'] = $user->getUserId();
1151
                $coaches['name'] = $user->getCompleteName();
1152
                $sessionCourseData['coaches'][] = $coaches;
1153
            }
1154
1155
            $sessionInfo['courses'][] = $sessionCourseData;
1156
        }
1157
1158
        return $sessionInfo;
1159
    }
1160
1161
    /**
1162
     * Register a sale.
1163
     *
1164
     * @param int    $itemId      The product ID
1165
     * @param int    $paymentType The payment type
1166
     * @param string $couponId    The coupon ID
1167
     *
1168
     * @return bool
1169
     */
1170
    public function registerSale($itemId, $paymentType, $couponId = null)
1171
    {
1172
        if (!in_array(
1173
                $paymentType,
1174
                [
1175
                    self::PAYMENT_TYPE_PAYPAL,
1176
                    self::PAYMENT_TYPE_TRANSFER,
1177
                    self::PAYMENT_TYPE_CULQI,
1178
                    self::PAYMENT_TYPE_TPV_REDSYS,
1179
                    self::PAYMENT_TYPE_STRIPE,
1180
                ]
1181
            )
1182
        ) {
1183
            return false;
1184
        }
1185
1186
        $entityManager = Database::getManager();
1187
        $item = $this->getItem($itemId);
1188
1189
        if (empty($item)) {
1190
            return false;
1191
        }
1192
1193
        $productName = '';
1194
        if ($item['product_type'] == self::PRODUCT_TYPE_COURSE) {
1195
            $course = $entityManager->find('ChamiloCoreBundle:Course', $item['product_id']);
1196
1197
            if (empty($course)) {
1198
                return false;
1199
            }
1200
1201
            $productName = $course->getTitle();
1202
        } elseif ($item['product_type'] == self::PRODUCT_TYPE_SESSION) {
1203
            $session = $entityManager->find('ChamiloCoreBundle:Session', $item['product_id']);
1204
1205
            if (empty($session)) {
1206
                return false;
1207
            }
1208
1209
            $productName = $session->getName();
1210
        }
1211
1212
        if ($couponId != null) {
1213
            $coupon = $this->getCoupon($couponId, $item['product_type'], $item['product_id']);
1214
        }
1215
1216
        $couponDiscount = 0;
1217
        $priceWithoutDiscount = 0;
1218
        if ($coupon != null) {
1219
            if ($coupon['discount_type'] == self::COUPON_DISCOUNT_TYPE_AMOUNT) {
1220
                $couponDiscount = $coupon['discount_amount'];
1221
            } elseif ($coupon['discount_type'] == self::COUPON_DISCOUNT_TYPE_PERCENTAGE) {
1222
                $couponDiscount = ($item['price'] * $coupon['discount_amount']) / 100;
1223
            }
1224
            $priceWithoutDiscount = $item['price'];
1225
        }
1226
        $item['price'] = $item['price'] - $couponDiscount;
1227
        $price = $item['price'];
1228
        $priceWithoutTax = null;
1229
        $taxPerc = null;
1230
        $taxAmount = 0;
1231
        $taxEnable = $this->get('tax_enable') === 'true';
1232
        $globalParameters = $this->getGlobalParameters();
1233
        $taxAppliesTo = $globalParameters['tax_applies_to'];
1234
1235
        if ($taxEnable &&
1236
            (
1237
                $taxAppliesTo == self::TAX_APPLIES_TO_ALL ||
1238
                ($taxAppliesTo == self::TAX_APPLIES_TO_ONLY_COURSE && $item['product_type'] == self::PRODUCT_TYPE_COURSE) ||
1239
                ($taxAppliesTo == self::TAX_APPLIES_TO_ONLY_SESSION && $item['product_type'] == self::PRODUCT_TYPE_SESSION)
1240
            )
1241
        ) {
1242
            $priceWithoutTax = $item['price'];
1243
            $globalTaxPerc = $globalParameters['global_tax_perc'];
1244
            $precision = 2;
1245
            $taxPerc = is_null($item['tax_perc']) ? $globalTaxPerc : $item['tax_perc'];
1246
            $taxAmount = round($priceWithoutTax * $taxPerc / 100, $precision);
1247
            $price = $priceWithoutTax + $taxAmount;
1248
        }
1249
1250
        $values = [
1251
            'reference' => $this->generateReference(
1252
                api_get_user_id(),
1253
                $item['product_type'],
1254
                $item['product_id']
1255
            ),
1256
            'currency_id' => $item['currency_id'],
1257
            'date' => api_get_utc_datetime(),
1258
            'user_id' => api_get_user_id(),
1259
            'product_type' => $item['product_type'],
1260
            'product_name' => $productName,
1261
            'product_id' => $item['product_id'],
1262
            'price' => $price,
1263
            'price_without_tax' => $priceWithoutTax,
1264
            'tax_perc' => $taxPerc,
1265
            'tax_amount' => $taxAmount,
1266
            'status' => self::SALE_STATUS_PENDING,
1267
            'payment_type' => (int) $paymentType,
1268
            'price_without_discount' => $priceWithoutDiscount,
1269
            'discount_amount' => $couponDiscount,
1270
        ];
1271
1272
        return Database::insert(self::TABLE_SALE, $values);
1273
    }
1274
1275
    /**
1276
     * Update the sale reference.
1277
     *
1278
     * @param int $saleId        The sale ID
1279
     * @param int $saleReference The new saleReference
1280
     *
1281
     * @return bool
1282
     */
1283
    public function updateSaleReference($saleId, $saleReference)
1284
    {
1285
        $saleTable = Database::get_main_table(self::TABLE_SALE);
1286
1287
        return Database::update(
1288
            $saleTable,
1289
            ['reference' => $saleReference],
1290
            ['id = ?' => (int) $saleId]
1291
        );
1292
    }
1293
1294
    /**
1295
     * Get sale data by ID.
1296
     *
1297
     * @param int $saleId The sale ID
1298
     *
1299
     * @return array
1300
     */
1301
    public function getSale($saleId)
1302
    {
1303
        return Database::select(
1304
            '*',
1305
            Database::get_main_table(self::TABLE_SALE),
1306
            [
1307
                'where' => ['id = ?' => (int) $saleId],
1308
            ],
1309
            'first'
1310
        );
1311
    }
1312
1313
    /**
1314
     * Get sale data by reference.
1315
     *
1316
     * @param int $reference The sale reference
1317
     *
1318
     * @return array
1319
     */
1320
    public function getSaleFromReference($reference)
1321
    {
1322
        return Database::select(
1323
            '*',
1324
            Database::get_main_table(self::TABLE_SALE),
1325
            [
1326
                'where' => ['reference = ?' => $reference],
1327
            ],
1328
            'first'
1329
        );
1330
    }
1331
1332
    /**
1333
     * Get a list of sales by the payment type.
1334
     *
1335
     * @param int $paymentType The payment type to filter (default : Paypal)
1336
     *
1337
     * @return array The sale list. Otherwise return false
1338
     */
1339
    public function getSaleListByPaymentType($paymentType = self::PAYMENT_TYPE_PAYPAL)
1340
    {
1341
        $saleTable = Database::get_main_table(self::TABLE_SALE);
1342
        $currencyTable = Database::get_main_table(self::TABLE_CURRENCY);
1343
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
1344
1345
        $innerJoins = "
1346
            INNER JOIN $currencyTable c ON s.currency_id = c.id
1347
            INNER JOIN $userTable u ON s.user_id = u.id
1348
        ";
1349
1350
        return Database::select(
1351
            ['c.iso_code', 'u.firstname', 'u.lastname', 's.*'],
1352
            "$saleTable s $innerJoins",
1353
            [
1354
                'where' => [
1355
                    's.payment_type = ? AND s.status = ?' => [
1356
                        (int) $paymentType,
1357
                        self::SALE_STATUS_COMPLETED,
1358
                    ],
1359
                ],
1360
                'order' => 'id DESC',
1361
            ]
1362
        );
1363
    }
1364
1365
    /**
1366
     * Get data of sales.
1367
     *
1368
     * @param int $saleId    The sale id
1369
     * @param int $isService Check if a service
1370
     *
1371
     * @return array The sale data
1372
     */
1373
    public function getDataSaleInvoice($saleId, $isService)
1374
    {
1375
        if ($isService) {
1376
            $sale = $this->getServiceSale($saleId);
1377
            $sale['reference'] = $sale['reference'];
1378
            $sale['product_name'] = $sale['service']['name'];
1379
            $sale['payment_type'] = $sale['payment_type'];
1380
            $sale['user_id'] = $sale['buyer']['id'];
1381
            $sale['date'] = $sale['buy_date'];
1382
        } else {
1383
            $sale = $this->getSale($saleId);
1384
        }
1385
1386
        return $sale;
1387
    }
1388
1389
    /**
1390
     * Get data of invoice.
1391
     *
1392
     * @param int $saleId    The sale id
1393
     * @param int $isService Check if a service
1394
     *
1395
     * @return array The invoice data
1396
     */
1397
    public function getDataInvoice($saleId, $isService)
1398
    {
1399
        return Database::select(
1400
            '*',
1401
            Database::get_main_table(self::TABLE_INVOICE),
1402
            [
1403
                'where' => [
1404
                    'sale_id = ? AND ' => (int) $saleId,
1405
                    'is_service = ?' => (int) $isService,
1406
                ],
1407
            ],
1408
            'first'
1409
        );
1410
    }
1411
1412
    /**
1413
     * Get invoice numbering.
1414
     *
1415
     * @param int $saleId    The sale id
1416
     * @param int $isService Check if a service
1417
     *
1418
     * @return string
1419
     */
1420
    public function getNumInvoice($saleId, $isService)
1421
    {
1422
        $dataInvoice = $this->getDataInvoice($saleId, $isService);
1423
        if (empty($dataInvoice)) {
1424
            return '-';
1425
        }
1426
1427
        return $dataInvoice['serie'].$dataInvoice['year'].'/'.$dataInvoice['num_invoice'];
1428
    }
1429
1430
    /**
1431
     * Get currency data by ID.
1432
     *
1433
     * @param int $currencyId The currency ID
1434
     *
1435
     * @return array
1436
     */
1437
    public function getCurrency($currencyId)
1438
    {
1439
        return Database::select(
1440
            '*',
1441
            Database::get_main_table(self::TABLE_CURRENCY),
1442
            [
1443
                'where' => ['id = ?' => (int) $currencyId],
1444
            ],
1445
            'first'
1446
        );
1447
    }
1448
1449
    /**
1450
     * Complete sale process. Update sale status to completed.
1451
     *
1452
     * @param int $saleId The sale ID
1453
     *
1454
     * @return bool
1455
     */
1456
    public function completeSale($saleId)
1457
    {
1458
        $sale = $this->getSale($saleId);
1459
1460
        if ($sale['status'] == self::SALE_STATUS_COMPLETED) {
1461
            return true;
1462
        }
1463
1464
        $saleIsCompleted = false;
1465
        switch ($sale['product_type']) {
1466
            case self::PRODUCT_TYPE_COURSE:
1467
                $course = api_get_course_info_by_id($sale['product_id']);
1468
                $saleIsCompleted = CourseManager::subscribeUser($sale['user_id'], $course['code']);
1469
                break;
1470
            case self::PRODUCT_TYPE_SESSION:
1471
                SessionManager::subscribeUsersToSession(
1472
                    $sale['product_id'],
1473
                    [$sale['user_id']],
1474
                    api_get_session_visibility($sale['product_id']),
1475
                    false
1476
                );
1477
1478
                $saleIsCompleted = true;
1479
                break;
1480
        }
1481
1482
        if ($saleIsCompleted) {
1483
            $this->updateSaleStatus($sale['id'], self::SALE_STATUS_COMPLETED);
1484
            if ($this->get('invoicing_enable') === 'true') {
1485
                $this->setInvoice($sale['id']);
1486
            }
1487
        }
1488
1489
        return $saleIsCompleted;
1490
    }
1491
1492
    /**
1493
     * Update sale status to canceled.
1494
     *
1495
     * @param int $saleId The sale ID
1496
     */
1497
    public function cancelSale($saleId)
1498
    {
1499
        $this->updateSaleStatus($saleId, self::SALE_STATUS_CANCELED);
1500
    }
1501
1502
    /**
1503
     * Get payment types.
1504
     */
1505
    public function getPaymentTypes(bool $onlyActive = false): array
1506
    {
1507
        $types = [
1508
            self::PAYMENT_TYPE_PAYPAL => 'PayPal',
1509
            self::PAYMENT_TYPE_TRANSFER => $this->get_lang('BankTransfer'),
1510
            self::PAYMENT_TYPE_CULQI => 'Culqi',
1511
            self::PAYMENT_TYPE_TPV_REDSYS => $this->get_lang('TpvPayment'),
1512
            self::PAYMENT_TYPE_STRIPE => 'Stripe',
1513
        ];
1514
1515
        if (!$onlyActive) {
1516
            return $types;
1517
        }
1518
1519
        if ($this->get('paypal_enable') !== 'true') {
1520
            unset($types[BuyCoursesPlugin::PAYMENT_TYPE_PAYPAL]);
1521
        }
1522
1523
        if ($this->get('transfer_enable') !== 'true') {
1524
            unset($types[BuyCoursesPlugin::PAYMENT_TYPE_TRANSFER]);
1525
        }
1526
1527
        if ($this->get('culqi_enable') !== 'true') {
1528
            unset($types[BuyCoursesPlugin::PAYMENT_TYPE_CULQI]);
1529
        }
1530
1531
        if ($this->get('tpv_redsys_enable') !== 'true'
1532
            || !file_exists(api_get_path(SYS_PLUGIN_PATH).'buycourses/resources/apiRedsys.php')
1533
        ) {
1534
            unset($types[BuyCoursesPlugin::PAYMENT_TYPE_TPV_REDSYS]);
1535
        }
1536
1537
        if ($this->get('stripe_enable') !== 'true') {
1538
            unset($types[BuyCoursesPlugin::PAYMENT_TYPE_STRIPE]);
1539
        }
1540
1541
        return $types;
1542
    }
1543
1544
    /**
1545
     * Register a invoice.
1546
     *
1547
     * @param int $saleId    The sale ID
1548
     * @param int $isService The service type to filter (default : 0)
1549
     */
1550
    public function setInvoice($saleId, $isService = 0)
1551
    {
1552
        $invoiceTable = Database::get_main_table(self::TABLE_INVOICE);
1553
        $year = date('Y');
1554
1555
        $globalParameters = $this->getGlobalParameters();
1556
        $numInvoice = $globalParameters['next_number_invoice'];
1557
        $serie = $globalParameters['invoice_series'];
1558
1559
        if (empty($numInvoice)) {
1560
            $item = Database::select(
1561
                ['MAX(num_invoice) AS num_invoice'],
1562
                $invoiceTable,
1563
                [
1564
                    'where' => ['year = ?' => $year],
1565
                ],
1566
                'first'
1567
            );
1568
1569
            $numInvoice = 1;
1570
            if ($item !== false) {
1571
                $numInvoice = (int) ($item['num_invoice'] + 1);
1572
            }
1573
        } else {
1574
            Database::update(
1575
                Database::get_main_table(self::TABLE_GLOBAL_CONFIG),
1576
                ['next_number_invoice' => 0],
1577
                ['id = ?' => 1]
1578
            );
1579
        }
1580
1581
        Database::insert(
1582
            $invoiceTable,
1583
            [
1584
                'sale_id' => $saleId,
1585
                'is_service' => $isService,
1586
                'num_invoice' => $numInvoice,
1587
                'year' => $year,
1588
                'serie' => $serie,
1589
                'date_invoice' => api_get_utc_datetime(),
1590
            ]
1591
        );
1592
1593
        // Record invoice in the sales table
1594
        $table = Database::get_main_table(self::TABLE_SALE);
1595
        if (!empty($isService)) {
1596
            $table = Database::get_main_table(self::TABLE_SERVICES_SALE);
1597
        }
1598
1599
        Database::update(
1600
            $table,
1601
            ['invoice' => 1],
1602
            ['id = ?' => $saleId]
1603
        );
1604
    }
1605
1606
    /**
1607
     * Get Tax's types.
1608
     *
1609
     * @return array
1610
     */
1611
    public function getTaxAppliesTo()
1612
    {
1613
        return [
1614
            self::TAX_APPLIES_TO_ALL => $this->get_lang('AllCoursesSessionsAndServices'),
1615
            self::TAX_APPLIES_TO_ONLY_COURSE => $this->get_lang('OnlyCourses'),
1616
            self::TAX_APPLIES_TO_ONLY_SESSION => $this->get_lang('OnlySessions'),
1617
            self::TAX_APPLIES_TO_ONLY_SERVICES => $this->get_lang('OnlyServices'),
1618
        ];
1619
    }
1620
1621
    /**
1622
     * Get a list of sales by the status.
1623
     *
1624
     * @param int $status The status to filter
1625
     *
1626
     * @return array The sale list. Otherwise return false
1627
     */
1628
    public function getSaleListByStatus($status = self::SALE_STATUS_PENDING)
1629
    {
1630
        $saleTable = Database::get_main_table(self::TABLE_SALE);
1631
        $currencyTable = Database::get_main_table(self::TABLE_CURRENCY);
1632
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
1633
1634
        $innerJoins = "
1635
            INNER JOIN $currencyTable c ON s.currency_id = c.id
1636
            INNER JOIN $userTable u ON s.user_id = u.id
1637
        ";
1638
1639
        return Database::select(
1640
            ['c.iso_code', 'u.firstname', 'u.lastname', 'u.email', 's.*'],
1641
            "$saleTable s $innerJoins",
1642
            [
1643
                'where' => ['s.status = ?' => (int) $status],
1644
                'order' => 'id DESC',
1645
            ]
1646
        );
1647
    }
1648
1649
    /**
1650
     * Get the list statuses for sales.
1651
     *
1652
     * @param string $dateStart
1653
     * @param string $dateEnd
1654
     *
1655
     * @throws Exception
1656
     *
1657
     * @return array
1658
     */
1659
    public function getSaleListReport($dateStart = null, $dateEnd = null)
1660
    {
1661
        $saleTable = Database::get_main_table(self::TABLE_SALE);
1662
        $currencyTable = Database::get_main_table(self::TABLE_CURRENCY);
1663
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
1664
        $innerJoins = "
1665
            INNER JOIN $currencyTable c ON s.currency_id = c.id
1666
            INNER JOIN $userTable u ON s.user_id = u.id
1667
        ";
1668
        $list = Database::select(
1669
            ['c.iso_code', 'u.firstname', 'u.lastname', 'u.email', 's.*'],
1670
            "$saleTable s $innerJoins",
1671
            [
1672
                'order' => 'id DESC',
1673
            ]
1674
        );
1675
        $listExportTemp = [];
1676
        $listExport = [];
1677
        $textStatus = null;
1678
        $paymentTypes = $this->getPaymentTypes();
1679
        $productTypes = $this->getProductTypes();
1680
        foreach ($list as $item) {
1681
            $statusSaleOrder = $item['status'];
1682
            switch ($statusSaleOrder) {
1683
                case 0:
1684
                    $textStatus = $this->get_lang('SaleStatusPending');
1685
                    break;
1686
                case 1:
1687
                    $textStatus = $this->get_lang('SaleStatusCompleted');
1688
                    break;
1689
                case -1:
1690
                    $textStatus = $this->get_lang('SaleStatusCanceled');
1691
                    break;
1692
            }
1693
            $dateFilter = new DateTime($item['date']);
1694
            $listExportTemp[] = [
1695
                'id' => $item['id'],
1696
                'reference' => $item['reference'],
1697
                'status' => $textStatus,
1698
                'status_filter' => $item['status'],
1699
                'date' => $dateFilter->format('Y-m-d'),
1700
                'order_time' => $dateFilter->format('H:i:s'),
1701
                'price' => $item['iso_code'].' '.$item['price'],
1702
                'product_type' => $productTypes[$item['product_type']],
1703
                'product_name' => $item['product_name'],
1704
                'payment_type' => $paymentTypes[$item['payment_type']],
1705
                'complete_user_name' => api_get_person_name($item['firstname'], $item['lastname']),
1706
                'email' => $item['email'],
1707
            ];
1708
        }
1709
        $listExport[] = [
1710
            get_lang('Number'),
1711
            $this->get_lang('OrderStatus'),
1712
            $this->get_lang('OrderDate'),
1713
            $this->get_lang('OrderTime'),
1714
            $this->get_lang('PaymentMethod'),
1715
            $this->get_lang('SalePrice'),
1716
            $this->get_lang('ProductType'),
1717
            $this->get_lang('ProductName'),
1718
            $this->get_lang('UserName'),
1719
            get_lang('Email'),
1720
        ];
1721
        //Validation Export
1722
        $dateStart = strtotime($dateStart);
1723
        $dateEnd = strtotime($dateEnd);
1724
        foreach ($listExportTemp as $item) {
1725
            $dateFilter = strtotime($item['date']);
1726
            if (($dateFilter >= $dateStart) && ($dateFilter <= $dateEnd)) {
1727
                $listExport[] = [
1728
                    'id' => $item['id'],
1729
                    'status' => $item['status'],
1730
                    'date' => $item['date'],
1731
                    'order_time' => $item['order_time'],
1732
                    'payment_type' => $item['payment_type'],
1733
                    'price' => $item['price'],
1734
                    'product_type' => $item['product_type'],
1735
                    'product_name' => $item['product_name'],
1736
                    'complete_user_name' => $item['complete_user_name'],
1737
                    'email' => $item['email'],
1738
                ];
1739
            }
1740
        }
1741
1742
        return $listExport;
1743
    }
1744
1745
    /**
1746
     * Get the statuses for sales.
1747
     *
1748
     * @return array
1749
     */
1750
    public function getSaleStatuses()
1751
    {
1752
        return [
1753
            self::SALE_STATUS_CANCELED => $this->get_lang('SaleStatusCanceled'),
1754
            self::SALE_STATUS_PENDING => $this->get_lang('SaleStatusPending'),
1755
            self::SALE_STATUS_COMPLETED => $this->get_lang('SaleStatusCompleted'),
1756
        ];
1757
    }
1758
1759
    /**
1760
     * Get the statuses for Payouts.
1761
     *
1762
     * @return array
1763
     */
1764
    public function getPayoutStatuses()
1765
    {
1766
        return [
1767
            self::PAYOUT_STATUS_CANCELED => $this->get_lang('PayoutStatusCanceled'),
1768
            self::PAYOUT_STATUS_PENDING => $this->get_lang('PayoutStatusPending'),
1769
            self::PAYOUT_STATUS_COMPLETED => $this->get_lang('PayoutStatusCompleted'),
1770
        ];
1771
    }
1772
1773
    /**
1774
     * Get the list of product types.
1775
     *
1776
     * @return array
1777
     */
1778
    public function getProductTypes()
1779
    {
1780
        return [
1781
            self::PRODUCT_TYPE_COURSE => get_lang('Course'),
1782
            self::PRODUCT_TYPE_SESSION => get_lang('Session'),
1783
        ];
1784
    }
1785
1786
    /**
1787
     * Get the list of service types.
1788
     *
1789
     * @return array
1790
     */
1791
    public function getServiceTypes()
1792
    {
1793
        return [
1794
            self::SERVICE_TYPE_USER => get_lang('User'),
1795
            self::SERVICE_TYPE_COURSE => get_lang('Course'),
1796
            self::SERVICE_TYPE_SESSION => get_lang('Session'),
1797
            self::SERVICE_TYPE_LP_FINAL_ITEM => get_lang('TemplateTitleCertificate'),
1798
        ];
1799
    }
1800
1801
    /**
1802
     * Get the list of coupon status.
1803
     *
1804
     * @return array
1805
     */
1806
    public function getCouponStatuses()
1807
    {
1808
        return [
1809
            self::COUPON_STATUS_ACTIVE => $this->get_lang('CouponActive'),
1810
            self::COUPON_STATUS_DISABLE => $this->get_lang('CouponDisabled'),
1811
        ];
1812
    }
1813
1814
    /**
1815
     * Get the list of coupon discount types.
1816
     *
1817
     * @return array
1818
     */
1819
    public function getCouponDiscountTypes()
1820
    {
1821
        return [
1822
            self::COUPON_DISCOUNT_TYPE_PERCENTAGE => $this->get_lang('CouponPercentage'),
1823
            self::COUPON_DISCOUNT_TYPE_AMOUNT => $this->get_lang('CouponAmount'),
1824
        ];
1825
    }
1826
1827
    /**
1828
     * Generates a random text (used for order references).
1829
     *
1830
     * @param int  $length    Optional. Length of characters
1831
     * @param bool $lowercase Optional. Include lowercase characters
1832
     * @param bool $uppercase Optional. Include uppercase characters
1833
     * @param bool $numbers   Optional. Include numbers
1834
     *
1835
     * @return string
1836
     */
1837
    public static function randomText(
1838
        $length = 6,
1839
        $lowercase = true,
1840
        $uppercase = true,
1841
        $numbers = true
1842
    ) {
1843
        $salt = $lowercase ? 'abchefghknpqrstuvwxyz' : '';
1844
        $salt .= $uppercase ? 'ACDEFHKNPRSTUVWXYZ' : '';
1845
        $salt .= $numbers ? (strlen($salt) ? '2345679' : '0123456789') : '';
1846
1847
        if (strlen($salt) == 0) {
1848
            return '';
1849
        }
1850
1851
        $str = '';
1852
1853
        srand((float) microtime() * 1000000);
1854
1855
        for ($i = 0; $i < $length; $i++) {
1856
            $numbers = rand(0, strlen($salt) - 1);
1857
            $str .= substr($salt, $numbers, 1);
1858
        }
1859
1860
        return $str;
1861
    }
1862
1863
    /**
1864
     * Generates an order reference.
1865
     *
1866
     * @param int $userId      The user ID
1867
     * @param int $productType The course/session type
1868
     * @param int $productId   The course/session ID
1869
     *
1870
     * @return string
1871
     */
1872
    public function generateReference($userId, $productType, $productId)
1873
    {
1874
        return vsprintf(
1875
            '%d-%d-%d-%s',
1876
            [$userId, $productType, $productId, self::randomText()]
1877
        );
1878
    }
1879
1880
    /**
1881
     * Get a list of sales by the user.
1882
     *
1883
     * @param string $term The search term
1884
     *
1885
     * @return array The sale list. Otherwise return false
1886
     */
1887
    public function getSaleListByUser($term)
1888
    {
1889
        $term = trim($term);
1890
1891
        if (empty($term)) {
1892
            return [];
1893
        }
1894
1895
        $saleTable = Database::get_main_table(self::TABLE_SALE);
1896
        $currencyTable = Database::get_main_table(self::TABLE_CURRENCY);
1897
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
1898
        $innerJoins = "
1899
            INNER JOIN $currencyTable c ON s.currency_id = c.id
1900
            INNER JOIN $userTable u ON s.user_id = u.id
1901
        ";
1902
1903
        return Database::select(
1904
            ['c.iso_code', 'u.firstname', 'u.lastname', 'u.email', 's.*'],
1905
            "$saleTable s $innerJoins",
1906
            [
1907
                'where' => [
1908
                    'u.username LIKE %?% OR ' => $term,
1909
                    'u.lastname LIKE %?% OR ' => $term,
1910
                    'u.firstname LIKE %?%' => $term,
1911
                ],
1912
                'order' => 'id DESC',
1913
            ]
1914
        );
1915
    }
1916
1917
    /**
1918
     * Get a list of sales by the user id.
1919
     *
1920
     * @param int $id The user id
1921
     *
1922
     * @return array The sale list. Otherwise return false
1923
     */
1924
    public function getSaleListByUserId($id)
1925
    {
1926
        if (empty($id)) {
1927
            return [];
1928
        }
1929
1930
        $saleTable = Database::get_main_table(self::TABLE_SALE);
1931
        $currencyTable = Database::get_main_table(self::TABLE_CURRENCY);
1932
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
1933
1934
        $innerJoins = "
1935
            INNER JOIN $currencyTable c ON s.currency_id = c.id
1936
            INNER JOIN $userTable u ON s.user_id = u.id
1937
        ";
1938
1939
        return Database::select(
1940
            ['c.iso_code', 'u.firstname', 'u.lastname', 's.*'],
1941
            "$saleTable s $innerJoins",
1942
            [
1943
                'where' => [
1944
                    'u.id = ? AND s.status = ?' => [(int) $id, self::SALE_STATUS_COMPLETED],
1945
                ],
1946
                'order' => 'id DESC',
1947
            ]
1948
        );
1949
    }
1950
1951
    /**
1952
     * Get a list of sales by date range.
1953
     *
1954
     * @param string $dateStart
1955
     * @param string $dateEnd
1956
     *
1957
     * @return array The sale list. Otherwise return false
1958
     */
1959
    public function getSaleListByDate($dateStart, $dateEnd)
1960
    {
1961
        $dateStart = trim($dateStart);
1962
        $dateEnd = trim($dateEnd);
1963
        if (empty($dateStart)) {
1964
            return [];
1965
        }
1966
        if (empty($dateEnd)) {
1967
            return [];
1968
        }
1969
        $saleTable = Database::get_main_table(self::TABLE_SALE);
1970
        $currencyTable = Database::get_main_table(self::TABLE_CURRENCY);
1971
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
1972
        $innerJoins = "
1973
            INNER JOIN $currencyTable c ON s.currency_id = c.id
1974
            INNER JOIN $userTable u ON s.user_id = u.id
1975
        ";
1976
1977
        return Database::select(
1978
            ['c.iso_code', 'u.firstname', 'u.lastname', 'u.email', 's.*'],
1979
            "$saleTable s $innerJoins",
1980
            [
1981
                'where' => [
1982
                    's.date BETWEEN ? AND ' => $dateStart,
1983
                    ' ? ' => $dateEnd,
1984
                ],
1985
                'order' => 'id DESC',
1986
            ]
1987
        );
1988
    }
1989
1990
    /**
1991
     * Get a list of sales by the user Email.
1992
     *
1993
     * @param string $term The search term
1994
     *
1995
     * @return array The sale list. Otherwise return false
1996
     */
1997
    public function getSaleListByEmail($term)
1998
    {
1999
        $term = trim($term);
2000
        if (empty($term)) {
2001
            return [];
2002
        }
2003
        $saleTable = Database::get_main_table(self::TABLE_SALE);
2004
        $currencyTable = Database::get_main_table(self::TABLE_CURRENCY);
2005
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
2006
        $innerJoins = "
2007
            INNER JOIN $currencyTable c ON s.currency_id = c.id
2008
            INNER JOIN $userTable u ON s.user_id = u.id
2009
        ";
2010
2011
        return Database::select(
2012
            ['c.iso_code', 'u.firstname', 'u.lastname', 'u.email', 's.*'],
2013
            "$saleTable s $innerJoins",
2014
            [
2015
                'where' => [
2016
                    'u.email LIKE %?% ' => $term,
2017
                ],
2018
                'order' => 'id DESC',
2019
            ]
2020
        );
2021
    }
2022
2023
    /**
2024
     * Convert the course info to array with necessary course data for save item.
2025
     *
2026
     * @param array $defaultCurrency Optional. Currency data
2027
     *
2028
     * @return array
2029
     */
2030
    public function getCourseForConfiguration(Course $course, $defaultCurrency = null)
2031
    {
2032
        $courseItem = [
2033
            'item_id' => null,
2034
            'course_id' => $course->getId(),
2035
            'course_visual_code' => $course->getVisualCode(),
2036
            'course_code' => $course->getCode(),
2037
            'course_title' => $course->getTitle(),
2038
            'course_directory' => $course->getDirectory(),
2039
            'course_visibility' => $course->getVisibility(),
2040
            'visible' => false,
2041
            'currency' => empty($defaultCurrency) ? null : $defaultCurrency['iso_code'],
2042
            'price' => 0.00,
2043
            'tax_perc' => null,
2044
        ];
2045
2046
        $item = $this->getItemByProduct($course->getId(), self::PRODUCT_TYPE_COURSE);
2047
2048
        if ($item !== false) {
2049
            $courseItem['item_id'] = $item['id'];
2050
            $courseItem['visible'] = true;
2051
            $courseItem['currency'] = $item['iso_code'];
2052
            $courseItem['price'] = $item['price'];
2053
            $courseItem['tax_perc'] = $item['tax_perc'];
2054
        }
2055
2056
        return $courseItem;
2057
    }
2058
2059
    /**
2060
     * Convert the session info to array with necessary session data for save item.
2061
     *
2062
     * @param Session $session         The session data
2063
     * @param array   $defaultCurrency Optional. Currency data
2064
     *
2065
     * @return array
2066
     */
2067
    public function getSessionForConfiguration(Session $session, $defaultCurrency = null)
2068
    {
2069
        $buyItemTable = Database::get_main_table(self::TABLE_ITEM);
2070
        $buyCurrencyTable = Database::get_main_table(self::TABLE_CURRENCY);
2071
2072
        $fakeItemFrom = "
2073
            $buyItemTable i
2074
            INNER JOIN $buyCurrencyTable c ON i.currency_id = c.id
2075
        ";
2076
2077
        $sessionItem = [
2078
            'item_id' => null,
2079
            'session_id' => $session->getId(),
2080
            'session_name' => $session->getName(),
2081
            'session_visibility' => $session->getVisibility(),
2082
            'session_display_start_date' => null,
2083
            'session_display_end_date' => null,
2084
            'visible' => false,
2085
            'currency' => empty($defaultCurrency) ? null : $defaultCurrency['iso_code'],
2086
            'price' => 0.00,
2087
            'tax_perc' => null,
2088
        ];
2089
2090
        $displayStartDate = $session->getDisplayStartDate();
2091
2092
        if (!empty($displayStartDate)) {
2093
            $sessionItem['session_display_start_date'] = api_format_date(
2094
                $session->getDisplayStartDate()->format('Y-m-d h:i:s')
2095
            );
2096
        }
2097
2098
        $displayEndDate = $session->getDisplayEndDate();
2099
2100
        if (!empty($displayEndDate)) {
2101
            $sessionItem['session_display_end_date'] = api_format_date(
2102
                $session->getDisplayEndDate()->format('Y-m-d h:i:s'),
2103
                DATE_TIME_FORMAT_LONG_24H
2104
            );
2105
        }
2106
2107
        $item = Database::select(
2108
            ['i.*', 'c.iso_code'],
2109
            $fakeItemFrom,
2110
            [
2111
                'where' => [
2112
                    'i.product_id = ? AND ' => $session->getId(),
2113
                    'i.product_type = ?' => self::PRODUCT_TYPE_SESSION,
2114
                ],
2115
            ],
2116
            'first'
2117
        );
2118
2119
        if ($item !== false) {
2120
            $sessionItem['item_id'] = $item['id'];
2121
            $sessionItem['visible'] = true;
2122
            $sessionItem['currency'] = $item['iso_code'];
2123
            $sessionItem['price'] = $item['price'];
2124
            $sessionItem['tax_perc'] = $item['tax_perc'];
2125
        }
2126
2127
        return $sessionItem;
2128
    }
2129
2130
    /**
2131
     * Get all beneficiaries for a item.
2132
     *
2133
     * @param int $itemId The item ID
2134
     *
2135
     * @return array The beneficiaries. Otherwise return false
2136
     */
2137
    public function getItemBeneficiaries($itemId)
2138
    {
2139
        $beneficiaryTable = Database::get_main_table(self::TABLE_ITEM_BENEFICIARY);
2140
2141
        return Database::select(
2142
            '*',
2143
            $beneficiaryTable,
2144
            [
2145
                'where' => [
2146
                    'item_id = ?' => (int) $itemId,
2147
                ],
2148
            ]
2149
        );
2150
    }
2151
2152
    /**
2153
     * Delete a item with its beneficiaries.
2154
     *
2155
     * @param int $itemId The item ID
2156
     *
2157
     * @return int The number of affected rows. Otherwise return false
2158
     */
2159
    public function deleteItem($itemId)
2160
    {
2161
        $itemTable = Database::get_main_table(self::TABLE_ITEM);
2162
        $affectedRows = Database::delete(
2163
            $itemTable,
2164
            ['id = ?' => (int) $itemId]
2165
        );
2166
2167
        if (!$affectedRows) {
2168
            return false;
2169
        }
2170
2171
        return $this->deleteItemBeneficiaries($itemId);
2172
    }
2173
2174
    /**
2175
     * Register a item.
2176
     *
2177
     * @param array $itemData The item data
2178
     *
2179
     * @return int The item ID. Otherwise return false
2180
     */
2181
    public function registerItem(array $itemData)
2182
    {
2183
        $itemTable = Database::get_main_table(self::TABLE_ITEM);
2184
2185
        return Database::insert($itemTable, $itemData);
2186
    }
2187
2188
    /**
2189
     * Update the item data by product.
2190
     *
2191
     * @param array $itemData    The item data to be updated
2192
     * @param int   $productId   The product ID
2193
     * @param int   $productType The type of product
2194
     *
2195
     * @return int The number of affected rows. Otherwise return false
2196
     */
2197
    public function updateItem(array $itemData, $productId, $productType)
2198
    {
2199
        $itemTable = Database::get_main_table(self::TABLE_ITEM);
2200
2201
        return Database::update(
2202
            $itemTable,
2203
            $itemData,
2204
            [
2205
                'product_id = ? AND ' => (int) $productId,
2206
                'product_type' => $productType,
2207
            ]
2208
        );
2209
    }
2210
2211
    /**
2212
     * Remove all beneficiaries for a item.
2213
     *
2214
     * @param int $itemId The user ID
2215
     *
2216
     * @return int The number of affected rows. Otherwise return false
2217
     */
2218
    public function deleteItemBeneficiaries($itemId)
2219
    {
2220
        $beneficiaryTable = Database::get_main_table(self::TABLE_ITEM_BENEFICIARY);
2221
2222
        return Database::delete(
2223
            $beneficiaryTable,
2224
            ['item_id = ?' => (int) $itemId]
2225
        );
2226
    }
2227
2228
    /**
2229
     * Register the beneficiaries users with the sale of item.
2230
     *
2231
     * @param int   $itemId  The item ID
2232
     * @param array $userIds The beneficiary user ID and Teachers commissions if enabled
2233
     */
2234
    public function registerItemBeneficiaries($itemId, array $userIds)
2235
    {
2236
        $beneficiaryTable = Database::get_main_table(self::TABLE_ITEM_BENEFICIARY);
2237
2238
        $this->deleteItemBeneficiaries($itemId);
2239
2240
        foreach ($userIds as $userId => $commissions) {
2241
            Database::insert(
2242
                $beneficiaryTable,
2243
                [
2244
                    'item_id' => (int) $itemId,
2245
                    'user_id' => (int) $userId,
2246
                    'commissions' => (int) $commissions,
2247
                ]
2248
            );
2249
        }
2250
    }
2251
2252
    /**
2253
     * Check if a course is valid for sale.
2254
     *
2255
     * @param Course $course The course
2256
     *
2257
     * @return bool
2258
     */
2259
    public function isValidCourse(Course $course)
2260
    {
2261
        $courses = $this->getCourses();
2262
2263
        foreach ($courses as $_c) {
2264
            if ($_c->getCode() === $course->getCode()) {
2265
                return true;
2266
            }
2267
        }
2268
2269
        return false;
2270
    }
2271
2272
    /**
2273
     * Gets the beneficiaries with commissions and current paypal accounts by sale.
2274
     *
2275
     * @param int $saleId The sale ID
2276
     *
2277
     * @return array
2278
     */
2279
    public function getBeneficiariesBySale($saleId)
2280
    {
2281
        $sale = $this->getSale($saleId);
2282
        $item = $this->getItemByProduct($sale['product_id'], $sale['product_type']);
2283
        $itemBeneficiaries = $this->getItemBeneficiaries($item['id']);
2284
2285
        return $itemBeneficiaries;
2286
    }
2287
2288
    /**
2289
     * gets all payouts.
2290
     *
2291
     * @param int $status   - default 0 - pending
2292
     * @param int $payoutId - for get an individual payout if want all then false
2293
     * @param int $userId
2294
     *
2295
     * @return array
2296
     */
2297
    public function getPayouts(
2298
        $status = self::PAYOUT_STATUS_PENDING,
2299
        $payoutId = false,
2300
        $userId = false
2301
    ) {
2302
        $condition = ($payoutId) ? 'AND p.id = '.((int) $payoutId) : '';
2303
        $condition2 = ($userId) ? ' AND p.user_id = '.((int) $userId) : '';
2304
        $typeResult = ($condition) ? 'first' : 'all';
2305
        $payoutsTable = Database::get_main_table(self::TABLE_PAYPAL_PAYOUTS);
2306
        $saleTable = Database::get_main_table(self::TABLE_SALE);
2307
        $currencyTable = Database::get_main_table(self::TABLE_CURRENCY);
2308
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
2309
        $extraFieldTable = Database::get_main_table(TABLE_EXTRA_FIELD);
2310
        $extraFieldValues = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2311
2312
        $paypalExtraField = Database::select(
2313
            "*",
2314
            $extraFieldTable,
2315
            [
2316
                'where' => ['variable = ?' => 'paypal'],
2317
            ],
2318
            'first'
2319
        );
2320
2321
        if (!$paypalExtraField) {
2322
            return false;
2323
        }
2324
2325
        $innerJoins = "
2326
            INNER JOIN $userTable u ON p.user_id = u.id
2327
            INNER JOIN $saleTable s ON s.id = p.sale_id
2328
            INNER JOIN $currencyTable c ON s.currency_id = c.id
2329
            LEFT JOIN  $extraFieldValues efv ON p.user_id = efv.item_id
2330
            AND field_id = ".((int) $paypalExtraField['id'])."
2331
        ";
2332
2333
        $payouts = Database::select(
2334
            "p.* , u.firstname, u.lastname, efv.value as paypal_account, s.reference as sale_reference, s.price as item_price, c.iso_code",
2335
            "$payoutsTable p $innerJoins",
2336
            [
2337
                'where' => ['p.status = ? '.$condition.' '.$condition2 => $status],
2338
            ],
2339
            $typeResult
2340
        );
2341
2342
        return $payouts;
2343
    }
2344
2345
    /**
2346
     * Verify if the beneficiary have a paypal account.
2347
     *
2348
     * @param int $userId
2349
     *
2350
     * @return true if the user have a paypal account, false if not
2351
     */
2352
    public function verifyPaypalAccountByBeneficiary($userId)
2353
    {
2354
        $extraFieldTable = Database::get_main_table(TABLE_EXTRA_FIELD);
2355
        $extraFieldValues = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2356
2357
        $paypalExtraField = Database::select(
2358
            '*',
2359
            $extraFieldTable,
2360
            [
2361
                'where' => ['variable = ?' => 'paypal'],
2362
            ],
2363
            'first'
2364
        );
2365
2366
        if (!$paypalExtraField) {
2367
            return false;
2368
        }
2369
2370
        $paypalFieldId = $paypalExtraField['id'];
2371
        $paypalAccount = Database::select(
2372
            'value',
2373
            $extraFieldValues,
2374
            [
2375
                'where' => ['field_id = ? AND item_id = ?' => [(int) $paypalFieldId, (int) $userId]],
2376
            ],
2377
            'first'
2378
        );
2379
2380
        if (!$paypalAccount) {
2381
            return false;
2382
        }
2383
2384
        if ($paypalAccount['value'] === '') {
2385
            return false;
2386
        }
2387
2388
        return true;
2389
    }
2390
2391
    /**
2392
     * Register the users payouts.
2393
     *
2394
     * @param int $saleId The sale ID
2395
     *
2396
     * @return array
2397
     */
2398
    public function storePayouts($saleId)
2399
    {
2400
        $payoutsTable = Database::get_main_table(self::TABLE_PAYPAL_PAYOUTS);
2401
        $platformCommission = $this->getPlatformCommission();
2402
2403
        $sale = $this->getSale($saleId);
2404
        $commission = (int) $platformCommission['commission'];
2405
        $teachersCommission = number_format(
2406
            (floatval($sale['price']) * $commission) / 100,
2407
            2
2408
        );
2409
2410
        $beneficiaries = $this->getBeneficiariesBySale($saleId);
2411
        foreach ($beneficiaries as $beneficiary) {
2412
            $beneficiaryCommission = (int) $beneficiary['commissions'];
2413
            Database::insert(
2414
                $payoutsTable,
2415
                [
2416
                    'date' => $sale['date'],
2417
                    'payout_date' => api_get_utc_datetime(),
2418
                    'sale_id' => (int) $saleId,
2419
                    'user_id' => $beneficiary['user_id'],
2420
                    'commission' => number_format(
2421
                        (floatval($teachersCommission) * $beneficiaryCommission) / 100,
2422
                        2
2423
                    ),
2424
                    'status' => self::PAYOUT_STATUS_PENDING,
2425
                ]
2426
            );
2427
        }
2428
    }
2429
2430
    /**
2431
     * Register the users payouts.
2432
     *
2433
     * @param int $payoutId The payout ID
2434
     * @param int $status   The status to set (-1 to cancel, 0 to pending, 1 to completed)
2435
     *
2436
     * @return array
2437
     */
2438
    public function setStatusPayouts($payoutId, $status)
2439
    {
2440
        $payoutsTable = Database::get_main_table(self::TABLE_PAYPAL_PAYOUTS);
2441
2442
        Database::update(
2443
            $payoutsTable,
2444
            ['status' => (int) $status],
2445
            ['id = ?' => (int) $payoutId]
2446
        );
2447
    }
2448
2449
    /**
2450
     * Gets the stored platform commission params.
2451
     *
2452
     * @return array
2453
     */
2454
    public function getPlatformCommission()
2455
    {
2456
        return Database::select(
2457
            '*',
2458
            Database::get_main_table(self::TABLE_COMMISSION),
2459
            ['id = ?' => 1],
2460
            'first'
2461
        );
2462
    }
2463
2464
    /**
2465
     * Update the platform commission.
2466
     *
2467
     * @param int $params platform commission
2468
     *
2469
     * @return int The number of affected rows. Otherwise return false
2470
     */
2471
    public function updateCommission($params)
2472
    {
2473
        $commissionTable = Database::get_main_table(self::TABLE_COMMISSION);
2474
2475
        return Database::update(
2476
            $commissionTable,
2477
            ['commission' => (int) $params['commission']]
2478
        );
2479
    }
2480
2481
    /**
2482
     * Register additional service.
2483
     *
2484
     * @param array $service params
2485
     *
2486
     * @return mixed response
2487
     */
2488
    public function storeService($service)
2489
    {
2490
        $servicesTable = Database::get_main_table(self::TABLE_SERVICES);
2491
2492
        $return = Database::insert(
2493
            $servicesTable,
2494
            [
2495
                'name' => Security::remove_XSS($service['name']),
2496
                'description' => Security::remove_XSS($service['description']),
2497
                'price' => $service['price'],
2498
                'tax_perc' => $service['tax_perc'] != '' ? (int) $service['tax_perc'] : null,
2499
                'duration_days' => (int) $service['duration_days'],
2500
                'applies_to' => (int) $service['applies_to'],
2501
                'owner_id' => (int) $service['owner_id'],
2502
                'visibility' => (int) $service['visibility'],
2503
                'image' => '',
2504
                'video_url' => $service['video_url'],
2505
                'service_information' => $service['service_information'],
2506
            ]
2507
        );
2508
2509
        if ($return && !empty($service['picture_crop_image_base_64']) &&
2510
            !empty($service['picture_crop_result'])
2511
        ) {
2512
            $img = str_replace('data:image/png;base64,', '', $service['picture_crop_image_base_64']);
2513
            $img = str_replace(' ', '+', $img);
2514
            $data = base64_decode($img);
2515
            $file = api_get_path(SYS_PLUGIN_PATH).'buycourses/uploads/services/images/simg-'.$return.'.png';
2516
            file_put_contents($file, $data);
2517
2518
            Database::update(
2519
                $servicesTable,
2520
                ['image' => 'simg-'.$return.'.png'],
2521
                ['id = ?' => (int) $return]
2522
            );
2523
        }
2524
2525
        return $return;
2526
    }
2527
2528
    /**
2529
     * update a service.
2530
     *
2531
     * @param array $service
2532
     * @param int   $id
2533
     *
2534
     * @return mixed response
2535
     */
2536
    public function updateService($service, $id)
2537
    {
2538
        $servicesTable = Database::get_main_table(self::TABLE_SERVICES);
2539
        if (!empty($service['picture_crop_image_base_64'])) {
2540
            $img = str_replace('data:image/png;base64,', '', $service['picture_crop_image_base_64']);
2541
            $img = str_replace(' ', '+', $img);
2542
            $data = base64_decode($img);
2543
            $file = api_get_path(SYS_PLUGIN_PATH).'buycourses/uploads/services/images/simg-'.$id.'.png';
2544
            file_put_contents($file, $data);
2545
        }
2546
2547
        return Database::update(
2548
            $servicesTable,
2549
            [
2550
                'name' => Security::remove_XSS($service['name']),
2551
                'description' => Security::remove_XSS($service['description']),
2552
                'price' => $service['price'],
2553
                'tax_perc' => $service['tax_perc'] != '' ? (int) $service['tax_perc'] : null,
2554
                'duration_days' => (int) $service['duration_days'],
2555
                'applies_to' => (int) $service['applies_to'],
2556
                'owner_id' => (int) $service['owner_id'],
2557
                'visibility' => (int) $service['visibility'],
2558
                'image' => 'simg-'.$id.'.png',
2559
                'video_url' => $service['video_url'],
2560
                'service_information' => $service['service_information'],
2561
            ],
2562
            ['id = ?' => (int) $id]
2563
        );
2564
    }
2565
2566
    /**
2567
     * Remove a service.
2568
     *
2569
     * @param int $id The transfer account ID
2570
     *
2571
     * @return int Rows affected. Otherwise return false
2572
     */
2573
    public function deleteService($id)
2574
    {
2575
        Database::delete(
2576
            Database::get_main_table(self::TABLE_SERVICES_SALE),
2577
            ['service_id = ?' => (int) $id]
2578
        );
2579
2580
        return Database::delete(
2581
            Database::get_main_table(self::TABLE_SERVICES),
2582
            ['id = ?' => (int) $id]
2583
        );
2584
    }
2585
2586
    /**
2587
     * @param array $product
2588
     * @param int   $productType
2589
     * @param array $coupon
2590
     *
2591
     * @return bool
2592
     */
2593
    public function setPriceSettings(&$product, $productType, $coupon = null)
2594
    {
2595
        if (empty($product)) {
2596
            return false;
2597
        }
2598
2599
        $taxPerc = null;
2600
        $product['has_coupon'] = $coupon != null ? true : false;
2601
        $couponDiscount = 0;
2602
        if ($coupon != null) {
2603
            if ($coupon['discount_type'] == self::COUPON_DISCOUNT_TYPE_AMOUNT) {
2604
                $couponDiscount = $coupon['discount_amount'];
2605
            } elseif ($coupon['discount_type'] == self::COUPON_DISCOUNT_TYPE_PERCENTAGE) {
2606
                $couponDiscount = ($product['price'] * $coupon['discount_amount']) / 100;
2607
            }
2608
            $product['price_without_discount'] = $product['price'];
2609
        }
2610
        $product['discount_amount'] = $couponDiscount;
2611
        $product['price'] = $product['price'] - $couponDiscount;
2612
        $priceWithoutTax = $product['price'];
2613
        $product['total_price'] = $product['price'];
2614
        $product['tax_amount'] = 0;
2615
        $precision = 2;
2616
        if ($this->checkTaxEnabledInProduct($productType)) {
2617
            if (is_null($product['tax_perc'])) {
2618
                $globalParameters = $this->getGlobalParameters();
2619
                $globalTaxPerc = $globalParameters['global_tax_perc'];
2620
                $taxPerc = $globalTaxPerc;
2621
            } else {
2622
                $taxPerc = $product['tax_perc'];
2623
            }
2624
            //$taxPerc = is_null($product['tax_perc']) ? $globalTaxPerc : $product['tax_perc'];
2625
2626
            $taxAmount = round($priceWithoutTax * $taxPerc / 100, $precision);
2627
            $product['tax_amount'] = $taxAmount;
2628
            $priceWithTax = $priceWithoutTax + $taxAmount;
2629
            $product['total_price'] = $priceWithTax;
2630
        }
2631
2632
        $product['tax_perc_show'] = $taxPerc;
2633
        $product['price_formatted'] = $this->getPriceWithCurrencyFromIsoCode(
2634
            number_format($product['price'], $precision),
2635
            $product['iso_code']
2636
        );
2637
2638
        $product['tax_amount_formatted'] = number_format($product['tax_amount'], $precision);
2639
2640
        $product['total_price_formatted'] = $this->getPriceWithCurrencyFromIsoCode(
2641
            number_format($product['total_price'], $precision),
2642
            $product['iso_code']
2643
        );
2644
2645
        if ($coupon != null) {
2646
            $product['discount_amount_formatted'] = $this->getPriceWithCurrencyFromIsoCode(
2647
                number_format($product['discount_amount'], $precision),
2648
                $product['iso_code']
2649
            );
2650
2651
            $product['price_without_discount_formatted'] = $this->getPriceWithCurrencyFromIsoCode(
2652
                number_format($product['price_without_discount'], $precision),
2653
                $product['iso_code']
2654
            );
2655
        }
2656
    }
2657
2658
    /**
2659
     * @param int   $id
2660
     * @param array $coupon
2661
     *
2662
     * @return array
2663
     */
2664
    public function getService($id, $coupon = null)
2665
    {
2666
        $id = (int) $id;
2667
2668
        if (empty($id)) {
2669
            return [];
2670
        }
2671
2672
        $servicesTable = Database::get_main_table(self::TABLE_SERVICES);
2673
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
2674
        $conditions = ['WHERE' => ['s.id = ?' => $id]];
2675
        $showData = 'first';
2676
        $innerJoins = "INNER JOIN $userTable u ON s.owner_id = u.id";
2677
        $currency = $this->getSelectedCurrency();
2678
        $isoCode = $currency['iso_code'];
2679
        $service = Database::select(
2680
            "s.*, '$isoCode' as currency, u.firstname, u.lastname",
2681
            "$servicesTable s $innerJoins",
2682
            $conditions,
2683
            $showData
2684
        );
2685
2686
        $service['iso_code'] = $isoCode;
2687
        $globalParameters = $this->getGlobalParameters();
2688
2689
        $this->setPriceSettings($service, self::TAX_APPLIES_TO_ONLY_SERVICES, $coupon);
2690
2691
        $service['tax_name'] = $globalParameters['tax_name'];
2692
        $service['tax_enable'] = $this->checkTaxEnabledInProduct(self::TAX_APPLIES_TO_ONLY_SERVICES);
2693
        $service['owner_name'] = api_get_person_name($service['firstname'], $service['lastname']);
2694
        $service['image'] = !empty($service['image']) ? api_get_path(WEB_PLUGIN_PATH).'buycourses/uploads/services/images/'.$service['image'] : null;
2695
2696
        return $service;
2697
    }
2698
2699
    /**
2700
     * List additional services.
2701
     *
2702
     * @return array
2703
     */
2704
    public function getAllServices()
2705
    {
2706
        $servicesTable = Database::get_main_table(self::TABLE_SERVICES);
2707
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
2708
2709
        $innerJoins = "INNER JOIN $userTable u ON s.owner_id = u.id";
2710
        $return = Database::select(
2711
            's.id',
2712
            "$servicesTable s $innerJoins",
2713
            [],
2714
            'all'
2715
        );
2716
2717
        $services = [];
2718
        foreach ($return as $index => $service) {
2719
            $services[$index] = $this->getService($service['id']);
2720
        }
2721
2722
        return $services;
2723
    }
2724
2725
    /**
2726
     * List additional services.
2727
     *
2728
     * @param int    $start
2729
     * @param int    $end
2730
     * @param string $typeResult
2731
     *
2732
     * @return array|int
2733
     */
2734
    public function getServices($start, $end, $typeResult = 'all')
2735
    {
2736
        $servicesTable = Database::get_main_table(self::TABLE_SERVICES);
2737
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
2738
2739
        $start = (int) $start;
2740
        $end = (int) $end;
2741
2742
        $conditions = ['limit' => "$start, $end"];
2743
        $innerJoins = "INNER JOIN $userTable u ON s.owner_id = u.id";
2744
        $return = Database::select(
2745
            's.id',
2746
            "$servicesTable s $innerJoins",
2747
            $conditions,
2748
            $typeResult
2749
        );
2750
2751
        if ($typeResult === 'count') {
2752
            return $return;
2753
        }
2754
2755
        $services = [];
2756
        foreach ($return as $index => $service) {
2757
            $services[$index] = $this->getService($service['id']);
2758
        }
2759
2760
        return $services;
2761
    }
2762
2763
    /**
2764
     * Get the statuses for sales.
2765
     *
2766
     * @return array
2767
     */
2768
    public function getServiceSaleStatuses()
2769
    {
2770
        return [
2771
            self::SERVICE_STATUS_CANCELLED => $this->get_lang('SaleStatusCancelled'),
2772
            self::SERVICE_STATUS_PENDING => $this->get_lang('SaleStatusPending'),
2773
            self::SERVICE_STATUS_COMPLETED => $this->get_lang('SaleStatusCompleted'),
2774
        ];
2775
    }
2776
2777
    /**
2778
     * List services sales.
2779
     *
2780
     * @param int $buyerId  buyer id
2781
     * @param int $status   status
2782
     * @param int $nodeType The node Type ( User = 1 , Course = 2 , Session = 3 )
2783
     * @param int $nodeId   the nodeId
2784
     *
2785
     * @return array
2786
     */
2787
    public function getServiceSales(
2788
        $buyerId = 0,
2789
        $status = 0,
2790
        $nodeType = 0,
2791
        $nodeId = 0
2792
    ) {
2793
        $conditions = null;
2794
        $groupBy = '';
2795
        $buyerId = (int) $buyerId;
2796
        $status = (int) $status;
2797
        $nodeType = (int) $nodeType;
2798
        $nodeId = (int) $nodeId;
2799
2800
        $servicesTable = Database::get_main_table(self::TABLE_SERVICES);
2801
        $servicesSaleTable = Database::get_main_table(self::TABLE_SERVICES_SALE);
2802
2803
        $defaultOrder = 'id ASC';
2804
2805
        if (!empty($buyerId)) {
2806
            $conditions = ['WHERE' => ['ss.buyer_id = ?' => $buyerId], 'ORDER' => $defaultOrder];
2807
        }
2808
2809
        if (is_numeric($status)) {
2810
            $conditions = ['WHERE' => ['ss.status = ?' => $status], 'ORDER' => $defaultOrder];
2811
        }
2812
2813
        if ($buyerId) {
2814
            $conditions = ['WHERE' => ['ss.buyer_id = ?' => [$buyerId]], 'ORDER' => $defaultOrder];
2815
        }
2816
2817
        if ($nodeType && $nodeId) {
2818
            $conditions = [
2819
                'WHERE' => ['ss.node_type = ? AND ss.node_id = ?' => [$nodeType, $nodeId]],
2820
                'ORDER' => $defaultOrder,
2821
            ];
2822
        }
2823
2824
        if ($nodeType && $nodeId && $buyerId && is_numeric($status)) {
2825
            $conditions = [
2826
                'WHERE' => [
2827
                    'ss.node_type = ? AND ss.node_id = ? AND ss.buyer_id = ? AND ss.status = ?' => [
2828
                        $nodeType,
2829
                        $nodeId,
2830
                        $buyerId,
2831
                        $status,
2832
                    ],
2833
                ],
2834
                'ORDER' => 'ss.service_id ASC',
2835
            ];
2836
        }
2837
2838
        $innerJoins = "INNER JOIN $servicesTable s ON ss.service_id = s.id $groupBy";
2839
        $return = Database::select(
2840
            'DISTINCT ss.id ',
2841
            "$servicesSaleTable ss $innerJoins",
2842
            $conditions
2843
            //, "all", null, true
2844
        );
2845
2846
        $list = [];
2847
        foreach ($return as $service) {
2848
            $list[] = $this->getServiceSale($service['id']);
2849
        }
2850
2851
        return $list;
2852
    }
2853
2854
    /**
2855
     * @param int $id service sale id
2856
     *
2857
     * @return array
2858
     */
2859
    public function getServiceSale($id)
2860
    {
2861
        $servicesTable = Database::get_main_table(self::TABLE_SERVICES);
2862
        $servicesSaleTable = Database::get_main_table(self::TABLE_SERVICES_SALE);
2863
2864
        if (empty($id)) {
2865
            return [];
2866
        }
2867
2868
        $conditions = ['WHERE' => ['ss.id = ?' => $id]];
2869
        $innerJoins = "INNER JOIN $servicesTable s ON ss.service_id = s.id ";
2870
        $currency = $this->getSelectedCurrency();
2871
        $isoCode = $currency['iso_code'];
2872
2873
        $servicesSale = Database::select(
2874
            'ss.*, s.name, s.description, s.price as service_price, s.duration_days, s.applies_to, s.owner_id, s.visibility, s.image',
2875
            "$servicesSaleTable ss $innerJoins",
2876
            $conditions,
2877
            'first'
2878
        );
2879
        $owner = api_get_user_info($servicesSale['owner_id']);
2880
        $buyer = api_get_user_info($servicesSale['buyer_id']);
2881
2882
        $servicesSale['service']['id'] = $servicesSale['service_id'];
2883
        $servicesSale['service']['name'] = $servicesSale['name'];
2884
        $servicesSale['service']['description'] = $servicesSale['description'];
2885
        $servicesSale['service']['price'] = $servicesSale['service_price'];
2886
        $servicesSale['service']['currency'] = $isoCode;
2887
2888
        $servicesSale['service']['total_price'] = $this->getPriceWithCurrencyFromIsoCode(
2889
            $servicesSale['price'],
2890
            $isoCode
2891
        );
2892
2893
        $servicesSale['service']['duration_days'] = $servicesSale['duration_days'];
2894
        $servicesSale['service']['applies_to'] = $servicesSale['applies_to'];
2895
        $servicesSale['service']['owner']['id'] = $servicesSale['owner_id'];
2896
        $servicesSale['service']['owner']['name'] = api_get_person_name($owner['firstname'], $owner['lastname']);
2897
        $servicesSale['service']['visibility'] = $servicesSale['visibility'];
2898
        $servicesSale['service']['image'] = $servicesSale['image'];
2899
        $servicesSale['item'] = $this->getService($servicesSale['service_id']);
2900
        $servicesSale['buyer']['id'] = $buyer['user_id'];
2901
        $servicesSale['buyer']['name'] = api_get_person_name($buyer['firstname'], $buyer['lastname']);
2902
        $servicesSale['buyer']['username'] = $buyer['username'];
2903
2904
        return $servicesSale;
2905
    }
2906
2907
    /**
2908
     * Update service sale status to cancelled.
2909
     *
2910
     * @param int $serviceSaleId The sale ID
2911
     *
2912
     * @return bool
2913
     */
2914
    public function cancelServiceSale($serviceSaleId)
2915
    {
2916
        $this->updateServiceSaleStatus(
2917
            $serviceSaleId,
2918
            self::SERVICE_STATUS_CANCELLED
2919
        );
2920
2921
        return true;
2922
    }
2923
2924
    /**
2925
     * Complete service sale process. Update service sale status to completed.
2926
     *
2927
     * @param int $serviceSaleId The service sale ID
2928
     *
2929
     * @return bool
2930
     */
2931
    public function completeServiceSale($serviceSaleId)
2932
    {
2933
        $serviceSale = $this->getServiceSale($serviceSaleId);
2934
        if ($serviceSale['status'] == self::SERVICE_STATUS_COMPLETED) {
2935
            return true;
2936
        }
2937
2938
        $this->updateServiceSaleStatus(
2939
            $serviceSaleId,
2940
            self::SERVICE_STATUS_COMPLETED
2941
        );
2942
2943
        if ($this->get('invoicing_enable') === 'true') {
2944
            $this->setInvoice($serviceSaleId, 1);
2945
        }
2946
2947
        return true;
2948
    }
2949
2950
    /**
2951
     * Lists current service details.
2952
     *
2953
     * @param string $name      Optional. The name filter
2954
     * @param int    $min       Optional. The minimum price filter
2955
     * @param int    $max       Optional. The maximum price filter
2956
     * @param mixed  $appliesTo optional
2957
     *
2958
     * @return array|int
2959
     */
2960
    public function getCatalogServiceList($start, $end, $name = null, $min = 0, $max = 0, $appliesTo = '', $typeResult = 'all')
2961
    {
2962
        $servicesTable = Database::get_main_table(self::TABLE_SERVICES);
2963
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
2964
2965
        $whereConditions = [
2966
            's.visibility <> ? ' => 0,
2967
        ];
2968
2969
        if (!empty($name)) {
2970
            $whereConditions['AND s.name LIKE %?%'] = $name;
2971
        }
2972
2973
        if (!empty($min)) {
2974
            $whereConditions['AND s.price >= ?'] = $min;
2975
        }
2976
2977
        if (!empty($max)) {
2978
            $whereConditions['AND s.price <= ?'] = $max;
2979
        }
2980
2981
        if (!$appliesTo == '') {
2982
            $whereConditions['AND s.applies_to = ?'] = $appliesTo;
2983
        }
2984
2985
        $start = (int) $start;
2986
        $end = (int) $end;
2987
2988
        $innerJoins = "INNER JOIN $userTable u ON s.owner_id = u.id";
2989
        $return = Database::select(
2990
            's.*',
2991
            "$servicesTable s $innerJoins",
2992
            ['WHERE' => $whereConditions, 'limit' => "$start, $end"],
2993
            $typeResult
2994
        );
2995
2996
        if ($typeResult === 'count') {
2997
            return $return;
2998
        }
2999
3000
        $services = [];
3001
        foreach ($return as $index => $service) {
3002
            $services[$index] = $this->getService($service['id']);
3003
        }
3004
3005
        return $services;
3006
    }
3007
3008
    /**
3009
     * Register a Service sale.
3010
     *
3011
     * @param int $serviceId   The service ID
3012
     * @param int $paymentType The payment type
3013
     * @param int $infoSelect  The ID for Service Type
3014
     *
3015
     * @return bool
3016
     */
3017
    public function registerServiceSale($serviceId, $paymentType, $infoSelect, $couponId = null)
3018
    {
3019
        if (!in_array(
3020
            $paymentType,
3021
            [self::PAYMENT_TYPE_PAYPAL, self::PAYMENT_TYPE_TRANSFER, self::PAYMENT_TYPE_CULQI]
3022
        )
3023
        ) {
3024
            return false;
3025
        }
3026
3027
        $userId = api_get_user_id();
3028
        $service = $this->getService($serviceId);
3029
3030
        if (empty($service)) {
3031
            return false;
3032
        }
3033
3034
        if ($couponId != null) {
3035
            $coupon = $this->getCouponService($couponId, $serviceId);
3036
        }
3037
3038
        $couponDiscount = 0;
3039
        $priceWithoutDiscount = 0;
3040
        if ($coupon != null) {
3041
            if ($coupon['discount_type'] == self::COUPON_DISCOUNT_TYPE_AMOUNT) {
3042
                $couponDiscount = $coupon['discount_amount'];
3043
            } elseif ($coupon['discount_type'] == self::COUPON_DISCOUNT_TYPE_PERCENTAGE) {
3044
                $couponDiscount = ($service['price'] * $coupon['discount_amount']) / 100;
3045
            }
3046
            $priceWithoutDiscount = $service['price'];
3047
        }
3048
        $service['price'] = $service['price'] - $couponDiscount;
3049
        $currency = $this->getSelectedCurrency();
3050
        $price = $service['price'];
3051
        $priceWithoutTax = null;
3052
        $taxPerc = null;
3053
        $taxEnable = $this->get('tax_enable') === 'true';
3054
        $globalParameters = $this->getGlobalParameters();
3055
        $taxAppliesTo = $globalParameters['tax_applies_to'];
3056
        $taxAmount = 0;
3057
3058
        if ($taxEnable &&
3059
            ($taxAppliesTo == self::TAX_APPLIES_TO_ALL || $taxAppliesTo == self::TAX_APPLIES_TO_ONLY_SERVICES)
3060
        ) {
3061
            $priceWithoutTax = $service['price'];
3062
            $globalTaxPerc = $globalParameters['global_tax_perc'];
3063
            $precision = 2;
3064
            $taxPerc = is_null($service['tax_perc']) ? $globalTaxPerc : $service['tax_perc'];
3065
            $taxAmount = round($priceWithoutTax * $taxPerc / 100, $precision);
3066
            $price = $priceWithoutTax + $taxAmount;
3067
        }
3068
3069
        $values = [
3070
            'service_id' => $serviceId,
3071
            'reference' => $this->generateReference(
3072
                $userId,
3073
                $service['applies_to'],
3074
                $infoSelect
3075
            ),
3076
            'currency_id' => $currency['id'],
3077
            'price' => $price,
3078
            'price_without_tax' => $priceWithoutTax,
3079
            'tax_perc' => $taxPerc,
3080
            'tax_amount' => $taxAmount,
3081
            'node_type' => $service['applies_to'],
3082
            'node_id' => (int) $infoSelect,
3083
            'buyer_id' => $userId,
3084
            'buy_date' => api_get_utc_datetime(),
3085
            'date_start' => api_get_utc_datetime(),
3086
            'date_end' => date_format(
3087
                date_add(
3088
                    date_create(api_get_utc_datetime()),
3089
                    date_interval_create_from_date_string($service['duration_days'].' days')
3090
                ),
3091
                'Y-m-d H:i:s'
3092
            ),
3093
            'status' => self::SERVICE_STATUS_PENDING,
3094
            'payment_type' => (int) $paymentType,
3095
            'price_without_discount' => $priceWithoutDiscount,
3096
            'discount_amount' => $couponDiscount,
3097
        ];
3098
3099
        $returnedServiceSaleId = Database::insert(self::TABLE_SERVICES_SALE, $values);
3100
3101
        return $returnedServiceSaleId;
3102
    }
3103
3104
    /**
3105
     * Save Culqi configuration params.
3106
     *
3107
     * @param array $params
3108
     *
3109
     * @return int Rows affected. Otherwise return false
3110
     */
3111
    public function saveCulqiParameters($params)
3112
    {
3113
        return Database::update(
3114
            Database::get_main_table(self::TABLE_CULQI),
3115
            [
3116
                'commerce_code' => $params['commerce_code'],
3117
                'api_key' => $params['api_key'],
3118
                'integration' => $params['integration'],
3119
            ],
3120
            ['id = ?' => 1]
3121
        );
3122
    }
3123
3124
    /**
3125
     * Gets the stored Culqi params.
3126
     *
3127
     * @return array
3128
     */
3129
    public function getCulqiParams()
3130
    {
3131
        return Database::select(
3132
            '*',
3133
            Database::get_main_table(self::TABLE_CULQI),
3134
            ['id = ?' => 1],
3135
            'first'
3136
        );
3137
    }
3138
3139
    /**
3140
     * Save Global Parameters.
3141
     *
3142
     * @param array $params
3143
     *
3144
     * @return int Rows affected. Otherwise return false
3145
     */
3146
    public function saveGlobalParameters($params)
3147
    {
3148
        $sqlParams = [
3149
            'terms_and_conditions' => $params['terms_and_conditions'],
3150
            'sale_email' => $params['sale_email'],
3151
        ];
3152
3153
        if ($this->get('tax_enable') === 'true') {
3154
            $sqlParams['global_tax_perc'] = $params['global_tax_perc'];
3155
            $sqlParams['tax_applies_to'] = $params['tax_applies_to'];
3156
            $sqlParams['tax_name'] = $params['tax_name'];
3157
        }
3158
3159
        if ($this->get('invoicing_enable') === 'true') {
3160
            $sqlParams['seller_name'] = $params['seller_name'];
3161
            $sqlParams['seller_id'] = $params['seller_id'];
3162
            $sqlParams['seller_address'] = $params['seller_address'];
3163
            $sqlParams['seller_email'] = $params['seller_email'];
3164
            $sqlParams['next_number_invoice'] = $params['next_number_invoice'];
3165
            $sqlParams['invoice_series'] = $params['invoice_series'];
3166
        }
3167
3168
        return Database::update(
3169
            Database::get_main_table(self::TABLE_GLOBAL_CONFIG),
3170
            $sqlParams,
3171
            ['id = ?' => 1]
3172
        );
3173
    }
3174
3175
    /**
3176
     * get Global Parameters.
3177
     *
3178
     * @return array
3179
     */
3180
    public function getGlobalParameters()
3181
    {
3182
        return Database::select(
3183
            '*',
3184
            Database::get_main_table(self::TABLE_GLOBAL_CONFIG),
3185
            ['id = ?' => 1],
3186
            'first'
3187
        );
3188
    }
3189
3190
    /**
3191
     * @param int $productType
3192
     *
3193
     * @return bool
3194
     */
3195
    public function checkTaxEnabledInProduct($productType)
3196
    {
3197
        if (empty($this->get('tax_enable') === 'true')) {
3198
            return false;
3199
        }
3200
3201
        $globalParameters = $this->getGlobalParameters();
3202
        $taxAppliesTo = $globalParameters['tax_applies_to'];
3203
        if ($taxAppliesTo == self::TAX_APPLIES_TO_ALL) {
3204
            return true;
3205
        }
3206
3207
        if ($taxAppliesTo == $productType) {
3208
            return true;
3209
        }
3210
3211
        return false;
3212
    }
3213
3214
    /**
3215
     * Get the path.
3216
     *
3217
     * @param string $var path variable
3218
     *
3219
     * @return string path
3220
     */
3221
    public function getPath($var)
3222
    {
3223
        $pluginPath = api_get_path(WEB_PLUGIN_PATH).'buycourses/';
3224
        $paths = [
3225
            'SERVICE_IMAGES' => $pluginPath.'uploads/services/images/',
3226
            'SRC' => $pluginPath.'src/',
3227
            'VIEW' => $pluginPath.'view/',
3228
            'UPLOADS' => $pluginPath.'uploads/',
3229
            'LANGUAGES' => $pluginPath.'lang/',
3230
            'RESOURCES' => $pluginPath.'resources/',
3231
            'RESOURCES_IMG' => $pluginPath.'resources/img/',
3232
            'RESOURCES_CSS' => $pluginPath.'resources/css/',
3233
            'RESOURCES_JS' => $pluginPath.'resources/js/',
3234
        ];
3235
3236
        return $paths[$var];
3237
    }
3238
3239
    /**
3240
     * @return array
3241
     */
3242
    public function getBuyCoursePluginPrice(Session $session)
3243
    {
3244
        // start buycourse validation
3245
        // display the course price and buy button if the buycourses plugin is enabled and this course is configured
3246
        $isThisCourseInSale = $this->buyCoursesForGridCatalogValidator($session->getId(), self::PRODUCT_TYPE_SESSION);
3247
        $return = [];
3248
3249
        if ($isThisCourseInSale) {
3250
            // set the Price label
3251
            $return['html'] = $isThisCourseInSale['html'];
3252
            // set the Buy button instead register.
3253
            if ($isThisCourseInSale['verificator']) {
3254
                $return['buy_button'] = $this->returnBuyCourseButton($session->getId(), self::PRODUCT_TYPE_SESSION);
3255
            }
3256
        }
3257
        // end buycourse validation
3258
        return $return;
3259
    }
3260
3261
    /**
3262
     * Register a coupon sale.
3263
     *
3264
     * @param int $saleId   The sale ID
3265
     * @param int $couponId The coupon ID
3266
     *
3267
     * @return int
3268
     */
3269
    public function registerCouponSale($saleId, $couponId)
3270
    {
3271
        $sale = $this->getSale($saleId);
3272
3273
        if (empty($sale)) {
3274
            return false;
3275
        }
3276
3277
        $values = [
3278
            'coupon_id' => (int) $couponId,
3279
            'sale_id' => (int) $saleId,
3280
        ];
3281
3282
        return Database::insert(self::TABLE_COUPON_SALE, $values);
3283
    }
3284
3285
    /**
3286
     * Register a coupon service sale.
3287
     *
3288
     * @param int $saleId   The sale ID
3289
     * @param int $couponId The coupon ID
3290
     *
3291
     * @return int
3292
     */
3293
    public function registerCouponServiceSale($saleId, $couponId)
3294
    {
3295
        $sale = $this->getSale($saleId);
3296
3297
        if (empty($sale)) {
3298
            return false;
3299
        }
3300
3301
        $values = [
3302
            'coupon_id' => (int) $couponId,
3303
            'service_sale_id' => (int) $saleId,
3304
        ];
3305
3306
        return Database::insert(self::TABLE_COUPON_SERVICE_SALE, $values);
3307
    }
3308
3309
    /**
3310
     * Add a new coupon.
3311
     *
3312
     * @param int $coupon
3313
     *
3314
     * @return bool
3315
     */
3316
    public function addNewCoupon($coupon)
3317
    {
3318
        $couponId = $this->registerCoupon($coupon);
3319
        if ($couponId) {
3320
            if (isset($coupon['courses'])) {
3321
                foreach ($coupon['courses'] as $course) {
3322
                    $this->registerCouponItem($couponId, self::PRODUCT_TYPE_COURSE, $course);
3323
                }
3324
            }
3325
3326
            if (isset($coupon['sessions'])) {
3327
                foreach ($coupon['sessions'] as $session) {
3328
                    $this->registerCouponItem($couponId, self::PRODUCT_TYPE_SESSION, $session);
3329
                }
3330
            }
3331
3332
            if (isset($coupon['services'])) {
3333
                foreach ($coupon['services'] as $service) {
3334
                    $this->registerCouponService($couponId, $service);
3335
                }
3336
            }
3337
3338
            return true;
3339
        } else {
3340
            Display::addFlash(
3341
                Display::return_message(
3342
                    $this->get_lang('CouponErrorInsert'),
3343
                    'error',
3344
                    false
3345
                )
3346
            );
3347
3348
            return false;
3349
        }
3350
    }
3351
3352
    /**
3353
     * Add a new coupon.
3354
     *
3355
     * @param array $coupon
3356
     *
3357
     * @return bool
3358
     */
3359
    public function updateCouponData($coupon)
3360
    {
3361
        $this->updateCoupon($coupon);
3362
        $this->deleteCouponItemsByCoupon(self::PRODUCT_TYPE_COURSE, $coupon['id']);
3363
        $this->deleteCouponItemsByCoupon(self::PRODUCT_TYPE_SESSION, $coupon['id']);
3364
        $this->deleteCouponServicesByCoupon($coupon['id']);
3365
3366
        if (isset($coupon['courses'])) {
3367
            foreach ($coupon['courses'] as $course) {
3368
                $this->registerCouponItem($coupon['id'], self::PRODUCT_TYPE_COURSE, $course);
3369
            }
3370
        }
3371
3372
        if (isset($coupon['sessions'])) {
3373
            foreach ($coupon['sessions'] as $session) {
3374
                $this->registerCouponItem($coupon['id'], self::PRODUCT_TYPE_SESSION, $session);
3375
            }
3376
        }
3377
3378
        if (isset($coupon['services'])) {
3379
            foreach ($coupon['services'] as $service) {
3380
                $this->registerCouponService($coupon['id'], $service);
3381
            }
3382
        }
3383
3384
        return true;
3385
    }
3386
3387
    /**
3388
     * Update coupons delivered.
3389
     *
3390
     * @param int $couponId The coupon ID
3391
     *
3392
     * @return bool
3393
     */
3394
    public function updateCouponDelivered($couponId)
3395
    {
3396
        $couponTable = Database::get_main_table(self::TABLE_COUPON);
3397
        $couponId = (int) $couponId;
3398
3399
        $sql = "UPDATE $couponTable
3400
        SET delivered = delivered+1
3401
        WHERE
3402
            id = $couponId";
3403
3404
        Database::query($sql);
3405
    }
3406
3407
    /**
3408
     * Get coupon info.
3409
     *
3410
     * @param int $couponCode The coupon ID
3411
     *
3412
     * @return array The coupon data
3413
     */
3414
    public function getCouponInfo($couponId)
3415
    {
3416
        $coupon = $this->getDataCoupon($couponId);
3417
3418
        $couponRelCourses = $this->getItemsCoupons($couponId, self::PRODUCT_TYPE_COURSE);
3419
        $couponRelSessions = $this->getItemsCoupons($couponId, self::PRODUCT_TYPE_SESSION);
3420
        $couponRelServices = $this->getServicesCoupons($couponId);
3421
3422
        $coupon['courses'] = $couponRelCourses;
3423
        $coupon['sessions'] = $couponRelSessions;
3424
        $coupon['services'] = $couponRelServices;
3425
3426
        return $coupon;
3427
    }
3428
3429
    /**
3430
     * Get a list of coupons.
3431
     *
3432
     * @param int $status The coupons activation status
3433
     *
3434
     * @return array Coupons data
3435
     */
3436
    public function getCouponsListByStatus($status)
3437
    {
3438
        $coupons = $this->getDataCoupons($status);
3439
3440
        return $coupons;
3441
    }
3442
3443
    /**
3444
     * Get the coupon data.
3445
     *
3446
     * @param int $couponCode  The coupon ID
3447
     * @param int $productId   The product ID
3448
     * @param int $productType The product type
3449
     *
3450
     * @return array The coupon data
3451
     */
3452
    public function getCoupon($couponId, $productType, $productId)
3453
    {
3454
        $coupon = $this->getDataCoupon($couponId, $productType, $productId);
3455
3456
        return $coupon;
3457
    }
3458
3459
    /**
3460
     * Get data of the coupon code.
3461
     *
3462
     * @param string $couponCode  The coupon code
3463
     * @param int    $productId   The product ID
3464
     * @param int    $productType The product type
3465
     *
3466
     * @return array The coupon data
3467
     */
3468
    public function getCouponByCode($couponCode, $productType = null, $productId = null)
3469
    {
3470
        $coupon = $this->getDataCouponByCode($couponCode, $productType, $productId);
3471
3472
        return $coupon;
3473
    }
3474
3475
    /**
3476
     * Get data of the coupon code for a service.
3477
     *
3478
     * @param int $couponId  The coupon ID
3479
     * @param int $serviceId The product ID
3480
     *
3481
     * @return array The coupon data
3482
     */
3483
    public function getCouponService($couponId, $serviceId)
3484
    {
3485
        $coupon = $this->getDataCouponService($couponId, $serviceId);
3486
3487
        return $coupon;
3488
    }
3489
3490
    /**
3491
     * Get data of the coupon code for a service.
3492
     *
3493
     * @param string $couponCode The coupon code code
3494
     * @param int    $serviceId  The product id
3495
     *
3496
     * @return array The coupon data
3497
     */
3498
    public function getCouponServiceByCode($couponCode, $serviceId)
3499
    {
3500
        $coupon = $this->getDataCouponServiceByCode($couponCode, $serviceId);
3501
3502
        return $coupon;
3503
    }
3504
3505
    /**
3506
     * Get the coupon code of a item sale.
3507
     *
3508
     * @param string $saleId The sale ID
3509
     *
3510
     * @return string The coupon code
3511
     */
3512
    public function getSaleCouponCode($saleId)
3513
    {
3514
        $couponTable = Database::get_main_table(self::TABLE_COUPON);
3515
        $couponSaleTable = Database::get_main_table(self::TABLE_COUPON_SALE);
3516
3517
        $couponFrom = "
3518
            $couponTable c
3519
            INNER JOIN $couponSaleTable s
3520
                on c.id = s.coupon_id
3521
        ";
3522
3523
        $couponCode = Database::select(
3524
            ['c.code'],
3525
            $couponFrom,
3526
            [
3527
                'where' => [
3528
                    's.sale_id = ? ' => (int) $saleId,
3529
                ],
3530
            ],
3531
            'first'
3532
        );
3533
3534
        return $couponCode['code'];
3535
    }
3536
3537
    /**
3538
     * Get the coupon code of a service sale.
3539
     *
3540
     * @param string $serviceSaleId The service sale ID
3541
     *
3542
     * @return string The coupon code
3543
     */
3544
    public function getServiceSaleCouponCode($serviceSaleId)
3545
    {
3546
        $couponTable = Database::get_main_table(self::TABLE_COUPON);
3547
        $couponServiceSaleTable = Database::get_main_table(self::TABLE_COUPON_SERVICE_SALE);
3548
3549
        $couponFrom = "
3550
            $couponTable c
3551
            INNER JOIN $couponServiceSaleTable s
3552
                on c.id = s.coupon_id
3553
        ";
3554
3555
        $couponCode = Database::select(
3556
            ['c.code'],
3557
            $couponFrom,
3558
            [
3559
                'where' => [
3560
                    's.service_sale_id = ? ' => (int) $serviceSaleId,
3561
                ],
3562
            ],
3563
            'first'
3564
        );
3565
3566
        return $couponCode['code'];
3567
    }
3568
3569
    /**
3570
     * @return string
3571
     */
3572
    public function getSubscriptionSuccessMessage(array $saleInfo)
3573
    {
3574
        switch ($saleInfo['product_type']) {
3575
            case self::PRODUCT_TYPE_COURSE:
3576
                $courseInfo = api_get_course_info_by_id($saleInfo['product_id']);
3577
                $url = api_get_course_url($courseInfo['code']);
3578
                break;
3579
            case self::PRODUCT_TYPE_SESSION:
3580
                $sessionId = (int) $saleInfo['product_id'];
3581
                $url = api_get_path(WEB_CODE_PATH).'session/index.php?session_id='.$sessionId;
3582
                break;
3583
            default:
3584
                $url = '#';
3585
        }
3586
3587
        return Display::return_message(
3588
            sprintf(
3589
                $this->get_lang('SubscriptionToCourseXSuccessful'),
3590
                $url,
3591
                $saleInfo['product_name']
3592
            ),
3593
            'success',
3594
            false
3595
        );
3596
    }
3597
3598
    /**
3599
     * @param string $baseUrl
3600
     * @param string $currentPage
3601
     * @param string $pagesCount
3602
     * @param string $totalItems
3603
     *
3604
     * @return string
3605
     */
3606
    public static function returnPagination(
3607
        $baseUrl,
3608
        $currentPage,
3609
        $pagesCount,
3610
        $totalItems,
3611
        array $extraQueryParams = []
3612
    ) {
3613
        $queryParams = HttpRequest::createFromGlobals()->query->all();
3614
3615
        unset($queryParams['page']);
3616
3617
        $url = $baseUrl.'?'.http_build_query(
3618
            array_merge($queryParams, $extraQueryParams)
3619
        );
3620
3621
        return Display::getPagination($url, $currentPage, $pagesCount, $totalItems);
3622
    }
3623
3624
    /**
3625
     * Returns the javascript to set the sales report table for courses.
3626
     *
3627
     * @param array $sales
3628
     * @param bool  $invoicingEnable
3629
     */
3630
    public static function getSalesReportScript($sales = [], $invoicingEnable = false)
3631
    {
3632
        $cols = "
3633
    '".preg_replace("/'/", "\\'", get_plugin_lang('OrderReference', 'BuyCoursesPlugin'))."',
3634
    '".preg_replace("/'/", "\\'", get_plugin_lang('OrderStatus', 'BuyCoursesPlugin'))."',
3635
    '".preg_replace("/'/", "\\'", get_plugin_lang('OrderDate', 'BuyCoursesPlugin'))."',
3636
    '".preg_replace("/'/", "\\'", get_plugin_lang('PaymentMethod', 'BuyCoursesPlugin'))."',
3637
    '".preg_replace("/'/", "\\'", get_plugin_lang('Price', 'BuyCoursesPlugin'))."',
3638
    '".preg_replace("/'/", "\\'", get_plugin_lang('CouponDiscount', 'BuyCoursesPlugin'))."',
3639
    '".preg_replace("/'/", "\\'", get_plugin_lang('Coupon', 'BuyCoursesPlugin'))."',
3640
    '".preg_replace("/'/", "\\'", get_plugin_lang('ProductType', 'BuyCoursesPlugin'))."',
3641
    '".preg_replace("/'/", "\\'", get_plugin_lang('Name', 'BuyCoursesPlugin'))."',
3642
    '".preg_replace("/'/", "\\'", get_lang('UserName'))."',
3643
    '".preg_replace("/'/", "\\'", get_lang('Email'))."',";
3644
        $model = "
3645
        {name:'reference', index:'reference', height:'auto', width:70, sorttype:'string', align:'center'},
3646
        {name:'status', index:'status', height:'auto', width:70, sorttype:'string', align:'center'},
3647
        {name:'date', index:'date', height:'auto', width:70, sorttype:'date', align:'center'},
3648
        {name:'payment_type', index:'payment_type', height:'auto', width:70, sorttype:'string', align:'center'},
3649
        {name:'total_price', index:'total_price', height:'auto', width:70, sorttype:'string', align:'center'},
3650
        {name:'coupon_discount', index:'coupon_discount', height:'auto', width:40, sorttype:'string', align: 'center'},
3651
        {name:'coupon', index:'coupon', height:'auto', width:60, sorttype:'string', align:'center'},
3652
        {name:'product_type', index:'product_type', height:'auto', width:40, sorttype:'string'},
3653
        {name:'product_name', index:'product_name', height:'auto', /*width:60,*/ sorttype:'string'},
3654
        {name:'complete_user_name', index:'complete_user_name', height:'auto', width:70, sorttype:'string'},
3655
        {name:'email', index:'email', height:'auto', /*width:60,*/ sorttype:'string'}, ";
3656
        if ($invoicingEnable) {
3657
            $model .= "{name:'invoice', index:'invoice', height:'auto', width:70, sorttype:'string'},";
3658
            $cols .= "'".get_plugin_lang('Invoice', 'BuyCoursesPlugin')."',";
3659
        }
3660
        $cols .= "'".get_lang('Options')."',";
3661
        $model .= "
3662
        {name:'options', index:'options', height:'auto', width:60, sortable:false},";
3663
        $data = '';
3664
        foreach ($sales as $item) {
3665
            $option = '';
3666
            if (!isset($item['complete_user_name'])) {
3667
                $item['complete_user_name'] = api_get_person_name($item['firstname'], $item['lastname']);
3668
            }
3669
            if ($item['invoice'] == 1) {
3670
                if ($invoicingEnable) {
3671
                    $item['invoice'] = "<a href='".api_get_path(WEB_PLUGIN_PATH).'buycourses/src/invoice.php?invoice='.$item['id']."&is_service=0"
3672
                        ."' title='".get_plugin_lang('InvoiceView', 'BuyCoursesPlugin')."'>".
3673
                        Display::return_icon('default.png', get_plugin_lang('InvoiceView', 'BuyCoursesPlugin'), '', ICON_SIZE_MEDIUM).
3674
                        "<br/>".$item['num_invoice'].
3675
                        "</a>";
3676
                }
3677
            } else {
3678
                $item['invoice'] = null;
3679
            }
3680
            if ($item['status'] == BuyCoursesPlugin::SALE_STATUS_CANCELED) {
3681
                $item['status'] = get_plugin_lang('SaleStatusCanceled', 'BuyCoursesPlugin');
3682
            } elseif ($item['status'] == BuyCoursesPlugin::SALE_STATUS_PENDING) {
3683
                $item['status'] = get_plugin_lang('SaleStatusPending', 'BuyCoursesPlugin');
3684
                $option = "<div class='btn-group btn-group-xs' role='group'>".
3685
                    "<a title='".get_plugin_lang('SubscribeUser', 'BuyCoursesPlugin')."'".
3686
                    " href='".api_get_self()."?order=".$item['id']."&action=confirm'".
3687
                    " class='btn btn-default'>".
3688
                    Display::return_icon('user_subscribe_session.png', get_plugin_lang('SubscribeUser', 'BuyCoursesPlugin'), '', ICON_SIZE_SMALL)
3689
                    ."</a>".
3690
                    "<a title='".get_plugin_lang('DeleteOrder', 'BuyCoursesPlugin')."'".
3691
                    " href='".api_get_self()."?order=".$item['id']."&action=cancel'".
3692
                    " class='btn btn-default'>".
3693
                    Display::return_icon('delete.png', get_plugin_lang('DeleteOrder', 'BuyCoursesPlugin'), '', ICON_SIZE_SMALL)
3694
                    ."</a>".
3695
                    "</div>";
3696
            } elseif ($item['status'] == BuyCoursesPlugin::SALE_STATUS_COMPLETED) {
3697
                $item['status'] = get_plugin_lang('SaleStatusCompleted', 'BuyCoursesPlugin');
3698
            }
3699
            $item['options'] = $option;
3700
            $item['date'] = api_get_local_time($item['date']);
3701
            $data .= json_encode($item).",";
3702
        }
3703
3704
        return "
3705
<script>
3706
    $(window).load( function () {
3707
        $('#table_report').jqGrid({
3708
            height: '100%',
3709
            autowidth: true,
3710
            LoadOnce: true,
3711
            rowNum:10,
3712
            rowList: [10, 25, 50, 100],
3713
            pager: 'tblGridPager',
3714
            datatype: 'local',
3715
            viewrecords: true,
3716
            gridview: true,
3717
            colNames:[ $cols ],
3718
            colModel:[ $model ],
3719
            caption: '".get_plugin_lang('SalesReport', 'BuyCoursesPlugin')."'
3720
        });
3721
        var mydata = [ $data ];
3722
        for(var i=0;i<=mydata.length;i++){
3723
            $('#table_report').jqGrid('addRowData',i+1,mydata[i]);
3724
            if(i==mydata.length){
3725
                $('#table_report').trigger('reloadGrid',[{page:1}])
3726
            }
3727
        }
3728
    });
3729
</script>";
3730
    }
3731
3732
    /**
3733
     * Filter the registered courses for show in plugin catalog.
3734
     */
3735
    private function getCourses($first, $maxResults)
3736
    {
3737
        $em = Database::getManager();
3738
        $urlId = api_get_current_access_url_id();
3739
3740
        $qb = $em->createQueryBuilder();
3741
        $qb2 = $em->createQueryBuilder();
3742
        $qb3 = $em->createQueryBuilder();
3743
3744
        $qb = $qb
3745
            ->select('c')
3746
            ->from('ChamiloCoreBundle:Course', 'c')
3747
            ->where(
3748
                $qb->expr()->notIn(
3749
                    'c',
3750
                    $qb2
3751
                        ->select('course2')
3752
                        ->from('ChamiloCoreBundle:SessionRelCourse', 'sc')
3753
                        ->join('sc.course', 'course2')
3754
                        ->innerJoin(
3755
                            'ChamiloCoreBundle:AccessUrlRelSession',
3756
                            'us',
3757
                            Join::WITH,
3758
                            'us.sessionId = sc.session'
3759
                        )->where(
3760
                            $qb->expr()->eq('us.accessUrlId ', $urlId)
3761
                        )
3762
                        ->getDQL()
3763
                )
3764
            )->andWhere(
3765
                $qb->expr()->in(
3766
                    'c',
3767
                    $qb3
3768
                        ->select('course3')
3769
                        ->from('ChamiloCoreBundle:AccessUrlRelCourse', 'uc')
3770
                        ->join('uc.course', 'course3')
3771
                        ->where(
3772
                            $qb3->expr()->eq('uc.url ', $urlId)
3773
                        )
3774
                        ->getDQL()
3775
                )
3776
            )
3777
        ->setFirstResult($first)
3778
        ->setMaxResults($maxResults);
3779
3780
        return $qb;
3781
    }
3782
3783
    /**
3784
     * Get the user status for the session.
3785
     *
3786
     * @param int     $userId  The user ID
3787
     * @param Session $session The session
3788
     *
3789
     * @return string
3790
     */
3791
    private function getUserStatusForSession($userId, Session $session)
3792
    {
3793
        if (empty($userId)) {
3794
            return 'NO';
3795
        }
3796
3797
        $entityManager = Database::getManager();
3798
        $scuRepo = $entityManager->getRepository('ChamiloCoreBundle:SessionRelCourseRelUser');
3799
3800
        $buySaleTable = Database::get_main_table(self::TABLE_SALE);
3801
3802
        // Check if user bought the course
3803
        $sale = Database::select(
3804
            'COUNT(1) as qty',
3805
            $buySaleTable,
3806
            [
3807
                'where' => [
3808
                    'user_id = ? AND product_type = ? AND product_id = ? AND status = ?' => [
3809
                        $userId,
3810
                        self::PRODUCT_TYPE_SESSION,
3811
                        $session->getId(),
3812
                        self::SALE_STATUS_PENDING,
3813
                    ],
3814
                ],
3815
            ],
3816
            'first'
3817
        );
3818
3819
        if ($sale['qty'] > 0) {
3820
            return 'TMP';
3821
        }
3822
3823
        // Check if user is already subscribe to session
3824
        $userSubscription = $scuRepo->findBy([
3825
            'session' => $session,
3826
            'user' => $userId,
3827
        ]);
3828
3829
        if (!empty($userSubscription)) {
3830
            return 'YES';
3831
        }
3832
3833
        return 'NO';
3834
    }
3835
3836
    /**
3837
     * Get the user status for the course.
3838
     *
3839
     * @param int    $userId The user Id
3840
     * @param Course $course The course
3841
     *
3842
     * @return string
3843
     */
3844
    private function getUserStatusForCourse($userId, Course $course)
3845
    {
3846
        if (empty($userId)) {
3847
            return 'NO';
3848
        }
3849
3850
        $entityManager = Database::getManager();
3851
        $cuRepo = $entityManager->getRepository('ChamiloCoreBundle:CourseRelUser');
3852
        $buySaleTable = Database::get_main_table(self::TABLE_SALE);
3853
3854
        // Check if user bought the course
3855
        $sale = Database::select(
3856
            'COUNT(1) as qty',
3857
            $buySaleTable,
3858
            [
3859
                'where' => [
3860
                    'user_id = ? AND product_type = ? AND product_id = ? AND status = ?' => [
3861
                        $userId,
3862
                        self::PRODUCT_TYPE_COURSE,
3863
                        $course->getId(),
3864
                        self::SALE_STATUS_PENDING,
3865
                    ],
3866
                ],
3867
            ],
3868
            'first'
3869
        );
3870
3871
        if ($sale['qty'] > 0) {
3872
            return 'TMP';
3873
        }
3874
3875
        // Check if user is already subscribe to course
3876
        $userSubscription = $cuRepo->findBy([
3877
            'course' => $course,
3878
            'user' => $userId,
3879
        ]);
3880
3881
        if (!empty($userSubscription)) {
3882
            return 'YES';
3883
        }
3884
3885
        return 'NO';
3886
    }
3887
3888
    /**
3889
     * Update the sale status.
3890
     *
3891
     * @param int $saleId    The sale ID
3892
     * @param int $newStatus The new status
3893
     *
3894
     * @return bool
3895
     */
3896
    private function updateSaleStatus($saleId, $newStatus = self::SALE_STATUS_PENDING)
3897
    {
3898
        $saleTable = Database::get_main_table(self::TABLE_SALE);
3899
3900
        return Database::update(
3901
            $saleTable,
3902
            ['status' => (int) $newStatus],
3903
            ['id = ?' => (int) $saleId]
3904
        );
3905
    }
3906
3907
    /**
3908
     * Search filtered sessions by name, and range of price.
3909
     *
3910
     * @param int    $start
3911
     * @param int    $end
3912
     * @param string $name            Optional. The name filter
3913
     * @param int    $min             Optional. The minimun price filter
3914
     * @param int    $max             Optional. The maximum price filter
3915
     * @param string $max             Optional. all and count
3916
     * @param int    $sessionCategory Optional. Session category id
3917
     *
3918
     * @return array
3919
     */
3920
    private function filterSessionList($start, $end, $name = null, $min = 0, $max = 0, $typeResult = 'all', $sessionCategory = 0)
3921
    {
3922
        $itemTable = Database::get_main_table(self::TABLE_ITEM);
3923
        $sessionTable = Database::get_main_table(TABLE_MAIN_SESSION);
3924
3925
        $min = floatval($min);
3926
        $max = floatval($max);
3927
        $sessionCategory = (int) $sessionCategory;
3928
3929
        $innerJoin = "$itemTable i ON s.id = i.product_id";
3930
        $whereConditions = [
3931
            'i.product_type = ? ' => self::PRODUCT_TYPE_SESSION,
3932
        ];
3933
3934
        if (!empty($name)) {
3935
            $whereConditions['AND s.name LIKE %?%'] = $name;
3936
        }
3937
3938
        if (!empty($min)) {
3939
            $whereConditions['AND i.price >= ?'] = $min;
3940
        }
3941
3942
        if (!empty($max)) {
3943
            $whereConditions['AND i.price <= ?'] = $max;
3944
        }
3945
3946
        $start = (int) $start;
3947
        $end = (int) $end;
3948
3949
        if ($sessionCategory != 0) {
3950
            $whereConditions['AND s.session_category_id = ?'] = $sessionCategory;
3951
        }
3952
3953
        $sessionIds = Database::select(
3954
            's.id',
3955
            "$sessionTable s INNER JOIN $innerJoin",
3956
            ['where' => $whereConditions, 'limit' => "$start, $end"],
3957
            $typeResult
3958
        );
3959
3960
        if ($typeResult === 'count') {
3961
            return $sessionIds;
3962
        }
3963
3964
        if (!$sessionIds) {
3965
            return [];
3966
        }
3967
3968
        $sessions = [];
3969
3970
        foreach ($sessionIds as $sessionId) {
3971
            $sessions[] = Database::getManager()->find(
3972
                'ChamiloCoreBundle:Session',
3973
                $sessionId
3974
            );
3975
        }
3976
3977
        return $sessions;
3978
    }
3979
3980
    /**
3981
     * Search filtered courses by name, and range of price.
3982
     *
3983
     * @param string $name Optional. The name filter
3984
     * @param int    $min  Optional. The minimun price filter
3985
     * @param int    $max  Optional. The maximum price filter
3986
     *
3987
     * @return array
3988
     */
3989
    private function filterCourseList($start, $end, $name = '', $min = 0, $max = 0, $typeResult = 'all')
3990
    {
3991
        $itemTable = Database::get_main_table(self::TABLE_ITEM);
3992
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
3993
        $urlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
3994
3995
        $urlId = api_get_current_access_url_id();
3996
3997
        $min = floatval($min);
3998
        $max = floatval($max);
3999
4000
        $whereConditions = [
4001
            'i.product_type = ? ' => self::PRODUCT_TYPE_COURSE,
4002
        ];
4003
4004
        if (!empty($name)) {
4005
            $whereConditions['AND c.title LIKE %?%'] = $name;
4006
        }
4007
4008
        if (!empty($min)) {
4009
            $whereConditions['AND i.price >= ?'] = $min;
4010
        }
4011
4012
        if (!empty($max)) {
4013
            $whereConditions['AND i.price <= ?'] = $max;
4014
        }
4015
4016
        $whereConditions['AND url.access_url_id = ?'] = $urlId;
4017
        $start = (int) $start;
4018
        $end = (int) $end;
4019
4020
        $courseIds = Database::select(
4021
            'c.id',
4022
            "$courseTable c
4023
            INNER JOIN $itemTable i
4024
            ON c.id = i.product_id
4025
            INNER JOIN $urlTable url
4026
            ON c.id = url.c_id
4027
            ",
4028
            ['where' => $whereConditions, 'limit' => "$start, $end"],
4029
            $typeResult
4030
        );
4031
4032
        if ($typeResult === 'count') {
4033
            return $courseIds;
4034
        }
4035
4036
        if (!$courseIds) {
4037
            return [];
4038
        }
4039
4040
        $courses = [];
4041
        foreach ($courseIds as $courseId) {
4042
            $courses[] = Database::getManager()->find(
4043
                'ChamiloCoreBundle:Course',
4044
                $courseId
4045
            );
4046
        }
4047
4048
        return $courses;
4049
    }
4050
4051
    /**
4052
     * Update the service sale status.
4053
     *
4054
     * @param int $serviceSaleId The service sale ID
4055
     * @param int $newStatus     The new status
4056
     *
4057
     * @return bool
4058
     */
4059
    private function updateServiceSaleStatus(
4060
        $serviceSaleId,
4061
        $newStatus = self::SERVICE_STATUS_PENDING
4062
    ) {
4063
        $serviceSaleTable = Database::get_main_table(self::TABLE_SERVICES_SALE);
4064
4065
        return Database::update(
4066
            $serviceSaleTable,
4067
            ['status' => (int) $newStatus],
4068
            ['id = ?' => (int) $serviceSaleId]
4069
        );
4070
    }
4071
4072
    /**
4073
     * Get the items (courses or sessions) of a coupon.
4074
     *
4075
     * @param string $couponId    The coupon ID
4076
     * @param int    $productType The product type
4077
     *
4078
     * @return array The item data
4079
     */
4080
    private function getItemsCoupons($couponId, $productType)
4081
    {
4082
        $couponItemTable = Database::get_main_table(self::TABLE_COUPON_ITEM);
4083
        $productType = (int) $productType;
4084
        $couponId = (int) $couponId;
4085
4086
        if ($productType == self::PRODUCT_TYPE_COURSE) {
4087
            $itemTable = Database::get_main_table(TABLE_MAIN_COURSE);
4088
            $select = ['ci.product_id as id', 'it.title'];
4089
        } elseif ($productType == self::PRODUCT_TYPE_SESSION) {
4090
            $itemTable = Database::get_main_table(TABLE_MAIN_SESSION);
4091
            $select = ['ci.product_id as id', 'it.name'];
4092
        }
4093
4094
        $couponFrom = "
4095
            $couponItemTable ci
4096
            INNER JOIN $itemTable it
4097
                on it.id = ci.product_id and ci.product_type = $productType
4098
        ";
4099
4100
        return Database::select(
4101
            $select,
4102
            $couponFrom,
4103
            [
4104
                'where' => [
4105
                    'ci.coupon_id = ? ' => $couponId,
4106
                ],
4107
            ]
4108
        );
4109
    }
4110
4111
    /**
4112
     * Get the services of a coupon.
4113
     *
4114
     * @param string $couponId The coupon ID
4115
     *
4116
     * @return array The service data
4117
     */
4118
    private function getServicesCoupons($couponId)
4119
    {
4120
        $couponServiceTable = Database::get_main_table(self::TABLE_COUPON_SERVICE);
4121
        $serviceTable = Database::get_main_table(self::TABLE_SERVICES);
4122
4123
        $couponFrom = "
4124
            $couponServiceTable cs
4125
            INNER JOIN $serviceTable s
4126
                on s.id = cs.service_id
4127
        ";
4128
4129
        return Database::select(
4130
            ['cs.service_id as id', 's.name'],
4131
            $couponFrom,
4132
            [
4133
                'where' => [
4134
                    'cs.coupon_id = ? ' => (int) $couponId,
4135
                ],
4136
            ]
4137
        );
4138
    }
4139
4140
    /**
4141
     * Get an array of coupons filtered by their status.
4142
     *
4143
     * @param int $status The coupon activation status
4144
     *
4145
     * @return array Coupons data
4146
     */
4147
    private function getDataCoupons($status = null)
4148
    {
4149
        $couponTable = Database::get_main_table(self::TABLE_COUPON);
4150
4151
        if ($status != null) {
4152
            return Database::select(
4153
                ['*'],
4154
                $couponTable,
4155
                [
4156
                    'where' => [
4157
                        ' active = ? ' => (int) $status,
4158
                    ],
4159
                    'order' => 'id DESC',
4160
                ]
4161
            );
4162
        } else {
4163
            return Database::select(
4164
                ['*'],
4165
                $couponTable,
4166
                [
4167
                    'order' => 'id DESC',
4168
                ]
4169
            );
4170
        }
4171
    }
4172
4173
    /**
4174
     * Get data of a coupon for a product (course or service) by the coupon ID.
4175
     *
4176
     * @param int $couponId    The coupon code code
4177
     * @param int $productType The product type
4178
     * @param int $productId   The product ID
4179
     *
4180
     * @return array The coupon data
4181
     */
4182
    private function getDataCoupon($couponId, $productType = null, $productId = null)
4183
    {
4184
        $couponTable = Database::get_main_table(self::TABLE_COUPON);
4185
4186
        if ($productType == null || $productId == null) {
4187
            return Database::select(
4188
                ['*'],
4189
                $couponTable,
4190
                [
4191
                    'where' => [
4192
                        'id = ? ' => (int) $couponId,
4193
                    ],
4194
                ],
4195
                'first'
4196
            );
4197
        } else {
4198
            $couponItemTable = Database::get_main_table(self::TABLE_COUPON_ITEM);
4199
            $dtmNow = api_get_utc_datetime();
4200
4201
            $couponFrom = "
4202
                $couponTable c
4203
                INNER JOIN $couponItemTable ci
4204
                    on ci.coupon_id = c.id
4205
            ";
4206
4207
            return Database::select(
4208
                ['c.*'],
4209
                $couponFrom,
4210
                [
4211
                    'where' => [
4212
                        'c.id = ? AND ' => (int) $couponId,
4213
                        'c.valid_start <= ? AND ' => $dtmNow,
4214
                        'c.valid_end >= ? AND ' => $dtmNow,
4215
                        'ci.product_type = ? AND ' => (int) $productType,
4216
                        'ci.product_id = ?' => (int) $productId,
4217
                    ],
4218
                ],
4219
                'first'
4220
            );
4221
        }
4222
    }
4223
4224
    /**
4225
     * Get data of a coupon for a product (course or service) by the coupon code.
4226
     *
4227
     * @param string $couponCode  The coupon code code
4228
     * @param int    $productType The product type
4229
     * @param int    $productId   The product ID
4230
     *
4231
     * @return array The coupon data
4232
     */
4233
    private function getDataCouponByCode($couponCode, $productType = null, $productId = null)
4234
    {
4235
        $couponTable = Database::get_main_table(self::TABLE_COUPON);
4236
        $couponItemTable = Database::get_main_table(self::TABLE_COUPON_ITEM);
4237
        $dtmNow = api_get_utc_datetime();
4238
4239
        if ($productType == null || $productId == null) {
4240
            return Database::select(
4241
                ['*'],
4242
                $couponTable,
4243
                [
4244
                    'where' => [
4245
                        'code = ? ' => (string) $couponCode,
4246
                    ],
4247
                ],
4248
                'first'
4249
            );
4250
        } else {
4251
            $couponFrom = "
4252
                $couponTable c
4253
                INNER JOIN $couponItemTable ci
4254
                    on ci.coupon_id = c.id
4255
            ";
4256
4257
            return Database::select(
4258
                ['c.*'],
4259
                $couponFrom,
4260
                [
4261
                    'where' => [
4262
                        'c.code = ? AND ' => (string) $couponCode,
4263
                        'c.valid_start <= ? AND ' => $dtmNow,
4264
                        'c.valid_end >= ? AND ' => $dtmNow,
4265
                        'ci.product_type = ? AND ' => (int) $productType,
4266
                        'ci.product_id = ?' => (int) $productId,
4267
                    ],
4268
                ],
4269
                'first'
4270
            );
4271
        }
4272
    }
4273
4274
    /**
4275
     * Get data of a coupon for a service by the coupon ID.
4276
     *
4277
     * @param int $couponId  The coupon ID
4278
     * @param int $serviceId The service ID
4279
     *
4280
     * @return array The coupon data
4281
     */
4282
    private function getDataCouponService($couponId, $serviceId)
4283
    {
4284
        $couponTable = Database::get_main_table(self::TABLE_COUPON);
4285
        $couponServiceTable = Database::get_main_table(self::TABLE_COUPON_SERVICE);
4286
        $dtmNow = api_get_utc_datetime();
4287
4288
        $couponFrom = "
4289
            $couponTable c
4290
            INNER JOIN $couponServiceTable cs
4291
                on cs.coupon_id = c.id
4292
        ";
4293
4294
        return Database::select(
4295
            ['c.*'],
4296
            $couponFrom,
4297
            [
4298
                'where' => [
4299
                    'c.id = ? AND ' => (int) $couponId,
4300
                    'c.valid_start <= ? AND ' => $dtmNow,
4301
                    'c.valid_end >= ? AND ' => $dtmNow,
4302
                    'cs.service_id = ?' => (int) $serviceId,
4303
                ],
4304
            ],
4305
            'first'
4306
        );
4307
    }
4308
4309
    /**
4310
     * Get data of coupon for a service by the coupon code.
4311
     *
4312
     * @param string $couponCode The coupon code
4313
     * @param int    $serviceId  The service ID
4314
     *
4315
     * @return array The coupon data
4316
     */
4317
    private function getDataCouponServiceByCode($couponCode, $serviceId)
4318
    {
4319
        $couponTable = Database::get_main_table(self::TABLE_COUPON);
4320
        $couponServiceTable = Database::get_main_table(self::TABLE_COUPON_SERVICE);
4321
        $dtmNow = api_get_utc_datetime();
4322
4323
        $couponFrom = "
4324
            $couponTable c
4325
            INNER JOIN $couponServiceTable cs
4326
                on cs.coupon_id = c.id
4327
        ";
4328
4329
        return Database::select(
4330
            ['c.*'],
4331
            $couponFrom,
4332
            [
4333
                'where' => [
4334
                    'c.code = ? AND ' => (string) $couponCode,
4335
                    'c.valid_start <= ? AND ' => $dtmNow,
4336
                    'c.valid_end >= ? AND ' => $dtmNow,
4337
                    'cs.service_id = ?' => (int) $serviceId,
4338
                ],
4339
            ],
4340
            'first'
4341
        );
4342
    }
4343
4344
    /**
4345
     * Update a coupon.
4346
     *
4347
     * @param int $coupon
4348
     *
4349
     * @return int
4350
     */
4351
    private function updateCoupon($coupon)
4352
    {
4353
        $couponExist = $this->getCouponByCode($coupon['code']);
4354
        if (!$couponExist) {
4355
            Display::addFlash(
4356
                Display::return_message(
4357
                    $this->get_lang('CouponNoExists'),
4358
                    'error',
4359
                    false
4360
                )
4361
            );
4362
4363
            return false;
4364
        }
4365
4366
        $values = [
4367
            'valid_start' => $coupon['valid_start'],
4368
            'valid_end' => $coupon['valid_end'],
4369
            'active' => $coupon['active'],
4370
        ];
4371
4372
        return Database::update(
4373
            self::TABLE_COUPON,
4374
            $values,
4375
            ['id = ?' => $coupon['id']]
4376
        );
4377
    }
4378
4379
    /**
4380
     * Register a coupon.
4381
     *
4382
     * @param int $coupon
4383
     *
4384
     * @return int
4385
     */
4386
    private function registerCoupon($coupon)
4387
    {
4388
        $couponExist = $this->getCouponByCode($coupon['code']);
4389
        if ($couponExist) {
4390
            Display::addFlash(
4391
                Display::return_message(
4392
                    $this->get_lang('CouponCodeUsed'),
4393
                    'error',
4394
                    false
4395
                )
4396
            );
4397
4398
            return false;
4399
        }
4400
4401
        $values = [
4402
            'code' => (string) $coupon['code'],
4403
            'discount_type' => (int) $coupon['discount_type'],
4404
            'discount_amount' => $coupon['discount_amount'],
4405
            'valid_start' => $coupon['valid_start'],
4406
            'valid_end' => $coupon['valid_end'],
4407
            'delivered' => 0,
4408
            'active' => $coupon['active'],
4409
        ];
4410
4411
        return Database::insert(self::TABLE_COUPON, $values);
4412
    }
4413
4414
    /**
4415
     * Register a coupon item.
4416
     *
4417
     * @param int $couponId    The coupon ID
4418
     * @param int $productType The product type
4419
     * @param int $productId   The product ID
4420
     *
4421
     * @return int
4422
     */
4423
    private function registerCouponItem($couponId, $productType, $productId)
4424
    {
4425
        $coupon = $this->getDataCoupon($couponId);
4426
        if (empty($coupon)) {
4427
            Display::addFlash(
4428
                Display::return_message(
4429
                    $this->get_lang('CouponNoExists'),
4430
                    'error',
4431
                    false
4432
                )
4433
            );
4434
4435
            return false;
4436
        }
4437
4438
        $values = [
4439
            'coupon_id' => (int) $couponId,
4440
            'product_type' => (int) $productType,
4441
            'product_id' => (int) $productId,
4442
        ];
4443
4444
        return Database::insert(self::TABLE_COUPON_ITEM, $values);
4445
    }
4446
4447
    /**
4448
     * Remove all coupon items for a product type and coupon ID.
4449
     *
4450
     * @param int $productType The product type
4451
     * @param int $coupon_id   The coupon ID
4452
     *
4453
     * @return int Rows affected. Otherwise return false
4454
     */
4455
    private function deleteCouponItemsByCoupon($productType, $couponId)
4456
    {
4457
        return Database::delete(
4458
            Database::get_main_table(self::TABLE_COUPON_ITEM),
4459
            [
4460
                'product_type = ? AND ' => (int) $productType,
4461
                'coupon_id = ?' => (int) $couponId,
4462
            ]
4463
        );
4464
    }
4465
4466
    /**
4467
     * Register a coupon service.
4468
     *
4469
     * @param int $couponId  The coupon ID
4470
     * @param int $serviceId The service ID
4471
     *
4472
     * @return int
4473
     */
4474
    private function registerCouponService($couponId, $serviceId)
4475
    {
4476
        $coupon = $this->getDataCoupon($couponId);
4477
        if (empty($coupon)) {
4478
            Display::addFlash(
4479
                Display::return_message(
4480
                    $this->get_lang('CouponNoExists'),
4481
                    'error',
4482
                    false
4483
                )
4484
            );
4485
4486
            return false;
4487
        }
4488
4489
        $values = [
4490
            'coupon_id' => (int) $couponId,
4491
            'service_id' => (int) $serviceId,
4492
        ];
4493
4494
        return Database::insert(self::TABLE_COUPON_SERVICE, $values);
4495
    }
4496
4497
    /**
4498
     * Remove all coupon services for a product type and coupon ID.
4499
     *
4500
     * @param int $productType The product type
4501
     * @param int $coupon_id   The coupon ID
4502
     *
4503
     * @return int Rows affected. Otherwise return false
4504
     */
4505
    private function deleteCouponServicesByCoupon($couponId)
4506
    {
4507
        return Database::delete(
4508
            Database::get_main_table(self::TABLE_COUPON_SERVICE),
4509
            [
4510
                'coupon_id = ?' => (int) $couponId,
4511
            ]
4512
        );
4513
    }
4514
}
4515