Completed
Push — master ( a73e85...13dd2e )
by Julito
08:28
created

BuyCoursesPlugin   F

Complexity

Total Complexity 260

Size/Duplication

Total Lines 3136
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 1461
c 0
b 0
f 0
dl 0
loc 3136
rs 0.8
wmc 260

92 Methods

Rating   Name   Duplication   Size   Complexity  
A getItemByProduct() 0 32 2
A getCurrencies() 0 5 1
A saveCurrency() 0 14 1
A savePaypalParams() 0 11 1
A deleteTransferAccount() 0 5 1
A getTransferAccounts() 0 5 1
A getSelectedCurrency() 0 9 1
A getCourseList() 0 3 1
A getPaypalParams() 0 7 1
A getItem() 0 9 1
A saveTransferAccount() 0 8 1
A isEnabled() 0 3 3
A uninstall() 0 24 2
A buyCoursesForGridCatalogValidator() 0 30 5
C update() 0 124 11
A install() 0 27 2
A __construct() 0 27 1
A updateServiceSaleStatus() 0 10 1
A getPath() 0 16 1
A deleteService() 0 10 1
A registerItem() 0 5 1
A setStatusPayouts() 0 8 1
A getPayoutStatuses() 0 6 1
A saveCulqiParameters() 0 10 1
A storeService() 0 38 5
B getCatalogSessionList() 0 58 7
A getProductTypes() 0 5 1
A getSaleListByPaymentType() 0 22 1
A getItemBeneficiaries() 0 10 1
A getUserStatusForCourse() 0 42 4
A getBeneficiariesBySale() 0 7 1
A checkTaxEnabledInProduct() 0 17 4
A getNumInvoice() 0 8 2
B getServiceSales() 0 65 11
A storePayouts() 0 27 2
A getCulqiParams() 0 7 1
A getTaxAppliesTo() 0 7 1
B filterCourseList() 0 60 7
A getUserStatusForSession() 0 43 4
A getDataInvoice() 0 12 1
A deleteItemBeneficiaries() 0 7 1
A getPayouts() 0 46 5
B getSaleListReport() 0 84 8
A setInvoice() 0 53 4
A verifyPaypalAccountByBeneficiary() 0 37 4
A getSaleStatuses() 0 6 1
A updateItem() 0 10 1
B getCatalogServiceList() 0 46 7
A returnBuyCourseButton() 0 9 1
A generateReference() 0 5 1
A getSaleListByDate() 0 27 3
A getPaymentTypes() 0 6 1
A getSubscriptionSuccessMessage() 0 23 3
A getServiceSaleStatuses() 0 6 1
A updateSaleStatus() 0 8 1
A getPriceWithCurrencyFromIsoCode() 0 15 3
A registerItemBeneficiaries() 0 13 2
A getSaleListByStatus() 0 17 1
A getService() 0 33 3
B registerServiceSale() 0 68 7
B getCatalogCourseList() 0 50 7
B getSessionInfo() 0 78 6
A deleteItem() 0 13 2
B filterSessionList() 0 53 7
B getSessionForConfiguration() 0 61 5
B completeSale() 0 34 6
A getSaleListByUserId() 0 23 2
A getServices() 0 27 3
A updateCommission() 0 7 1
A getServiceSale() 0 46 2
A getGlobalParameters() 0 7 1
A updateService() 0 27 3
A setPriceSettings() 0 38 4
B getCourseInfo() 0 61 6
A create() 0 5 2
C registerSale() 0 80 14
A getBuyCoursePluginPrice() 0 17 3
A getCurrency() 0 9 1
A getSaleListByUser() 0 26 2
A getCourseForConfiguration() 0 27 3
A getSaleListByEmail() 0 22 2
A getCourses() 0 46 1
A getServiceTypes() 0 7 1
A completeServiceSale() 0 17 3
A saveGlobalParameters() 0 26 3
A isValidCourse() 0 11 3
B randomText() 0 24 7
A getSale() 0 9 1
A cancelServiceSale() 0 8 1
A getPlatformCommission() 0 7 1
A cancelSale() 0 3 1
A getDataSaleInvoice() 0 14 2

How to fix   Complexity   

Complex Class

Complex classes like BuyCoursesPlugin often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use BuyCoursesPlugin, and based on these observations, apply Extract Interface, too.

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