Passed
Push — 1.11.x ( 31fff3...86e7ae )
by Yannick
15:37 queued 10s
created

plugin/buycourses/src/buy_course_plugin.class.php (6 issues)

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