%PDF- %PDF-
Mini Shell

Mini Shell

Direktori : /home/tjamichg/cursos.tjamich.gob.mx/plugin/buycourses/src/
Upload File :
Create Path :
Current File : /home/tjamichg/cursos.tjamich.gob.mx/plugin/buycourses/src/buy_course_plugin.class.php

<?php
/* For license terms, see /license.txt */

use Chamilo\CoreBundle\Entity\Course;
use Chamilo\CoreBundle\Entity\Session;
use Doctrine\ORM\Query\Expr\Join;
use Symfony\Component\HttpFoundation\Request as HttpRequest;

/**
 * Plugin class for the BuyCourses plugin.
 *
 * @package chamilo.plugin.buycourses
 *
 * @author  Jose Angel Ruiz <jaruiz@nosolored.com>
 * @author  Imanol Losada <imanol.losada@beeznest.com>
 * @author  Alex Aragón <alex.aragon@beeznest.com>
 * @author  Angel Fernando Quiroz Campos <angel.quiroz@beeznest.com>
 * @author  José Loguercio Silva  <jose.loguercio@beeznest.com>
 * @author  Julio Montoya
 */
class BuyCoursesPlugin extends Plugin
{
    public const TABLE_PAYPAL = 'plugin_buycourses_paypal_account';
    public const TABLE_CURRENCY = 'plugin_buycourses_currency';
    public const TABLE_ITEM = 'plugin_buycourses_item';
    public const TABLE_ITEM_BENEFICIARY = 'plugin_buycourses_item_rel_beneficiary';
    public const TABLE_SALE = 'plugin_buycourses_sale';
    public const TABLE_TRANSFER = 'plugin_buycourses_transfer';
    public const TABLE_COMMISSION = 'plugin_buycourses_commission';
    public const TABLE_PAYPAL_PAYOUTS = 'plugin_buycourses_paypal_payouts';
    public const TABLE_SERVICES = 'plugin_buycourses_services';
    public const TABLE_SERVICES_SALE = 'plugin_buycourses_service_sale';
    public const TABLE_CULQI = 'plugin_buycourses_culqi';
    public const TABLE_GLOBAL_CONFIG = 'plugin_buycourses_global_config';
    public const TABLE_INVOICE = 'plugin_buycourses_invoices';
    public const TABLE_TPV_REDSYS = 'plugin_buycourses_tpvredsys_account';
    public const TABLE_COUPON = 'plugin_buycourses_coupon';
    public const TABLE_COUPON_ITEM = 'plugin_buycourses_coupon_rel_item';
    public const TABLE_COUPON_SERVICE = 'plugin_buycourses_coupon_rel_service';
    public const TABLE_SUBSCRIPTION = 'plugin_buycourses_subscription';
    public const TABLE_SUBSCRIPTION_SALE = 'plugin_buycourses_subscription_rel_sale';
    public const TABLE_SUBSCRIPTION_PERIOD = 'plugin_buycourses_subscription_period';
    public const TABLE_COUPON_SALE = 'plugin_buycourses_coupon_rel_sale';
    public const TABLE_COUPON_SERVICE_SALE = 'plugin_buycourses_coupon_rel_service_sale';
    public const TABLE_COUPON_SUBSCRIPTION_SALE = 'plugin_buycourses_coupon_rel_subscription_sale';
    public const TABLE_STRIPE = 'plugin_buycourses_stripe_account';
    public const TABLE_TPV_CECABANK = 'plugin_buycourses_cecabank_account';
    public const PRODUCT_TYPE_COURSE = 1;
    public const PRODUCT_TYPE_SESSION = 2;
    public const PRODUCT_TYPE_SERVICE = 3;
    public const PAYMENT_TYPE_PAYPAL = 1;
    public const PAYMENT_TYPE_TRANSFER = 2;
    public const PAYMENT_TYPE_CULQI = 3;
    public const PAYMENT_TYPE_TPV_REDSYS = 4;
    public const PAYMENT_TYPE_STRIPE = 5;
    public const PAYMENT_TYPE_TPV_CECABANK = 6;
    public const PAYOUT_STATUS_CANCELED = 2;
    public const PAYOUT_STATUS_PENDING = 0;
    public const PAYOUT_STATUS_COMPLETED = 1;
    public const SALE_STATUS_CANCELED = -1;
    public const SALE_STATUS_PENDING = 0;
    public const SALE_STATUS_COMPLETED = 1;
    public const SERVICE_STATUS_PENDING = 0;
    public const SERVICE_STATUS_COMPLETED = 1;
    public const SERVICE_STATUS_CANCELLED = -1;
    public const SERVICE_TYPE_USER = 1;
    public const SERVICE_TYPE_COURSE = 2;
    public const SERVICE_TYPE_SESSION = 3;
    public const SERVICE_TYPE_LP_FINAL_ITEM = 4;
    public const CULQI_INTEGRATION_TYPE = 'INTEG';
    public const CULQI_PRODUCTION_TYPE = 'PRODUC';
    public const TAX_APPLIES_TO_ALL = 1;
    public const TAX_APPLIES_TO_ONLY_COURSE = 2;
    public const TAX_APPLIES_TO_ONLY_SESSION = 3;
    public const TAX_APPLIES_TO_ONLY_SERVICES = 4;
    public const PAGINATION_PAGE_SIZE = 6;
    public const COUPON_DISCOUNT_TYPE_PERCENTAGE = 1;
    public const COUPON_DISCOUNT_TYPE_AMOUNT = 2;
    public const COUPON_STATUS_ACTIVE = 1;
    public const COUPON_STATUS_DISABLE = 0;

    public $isAdminPlugin = false;

    /**
     * BuyCoursesPlugin constructor.
     */
    public function __construct()
    {
        parent::__construct(
            '7.1',
            "
                Jose Angel Ruiz - NoSoloRed (original author) <br/>
                Francis Gonzales and Yannick Warnier - BeezNest (integration) <br/>
                Alex Aragón - BeezNest (Design icons and css styles) <br/>
                Imanol Losada - BeezNest (introduction of sessions purchase) <br/>
                Angel Fernando Quiroz Campos - BeezNest (cleanup and new reports) <br/>
                José Loguercio Silva - BeezNest (Payouts and buy Services) <br/>
                Julio Montoya
            ",
            [
                'show_main_menu_tab' => 'boolean',
                'public_main_menu_tab' => 'boolean',
                'include_sessions' => 'boolean',
                'include_services' => 'boolean',
                'paypal_enable' => 'boolean',
                'transfer_enable' => 'boolean',
                'culqi_enable' => 'boolean',
                'commissions_enable' => 'boolean',
                'unregistered_users_enable' => 'boolean',
                'hide_free_text' => 'boolean',
                'hide_shopping_cart_from_course_catalogue' => 'boolean',
                'invoicing_enable' => 'boolean',
                'tax_enable' => 'boolean',
                'use_currency_symbol' => 'boolean',
                'tpv_redsys_enable' => 'boolean',
                'stripe_enable' => 'boolean',
                'cecabank_enable' => 'boolean',
            ]
        );
    }

    /**
     * @return BuyCoursesPlugin
     */
    public static function create()
    {
        static $result = null;

        return $result ? $result : $result = new self();
    }

    /**
     * Check if plugin is enabled.
     *
     * @param bool $checkEnabled Check if, additionnally to being installed, the plugin is enabled
     */
    public function isEnabled(bool $checkEnabled = false): bool
    {
        return $this->get('paypal_enable') || $this->get('transfer_enable') || $this->get('culqi_enable') || $this->get('stripe_enable') || $this->get('cecabank_enable');
    }

    /**
     * This method creates the tables required to this plugin.
     */
    public function install()
    {
        $tablesToBeCompared = [
            self::TABLE_PAYPAL,
            self::TABLE_TRANSFER,
            self::TABLE_CULQI,
            self::TABLE_ITEM_BENEFICIARY,
            self::TABLE_ITEM,
            self::TABLE_SALE,
            self::TABLE_CURRENCY,
            self::TABLE_COMMISSION,
            self::TABLE_PAYPAL_PAYOUTS,
            self::TABLE_SERVICES,
            self::TABLE_SERVICES_SALE,
            self::TABLE_GLOBAL_CONFIG,
            self::TABLE_INVOICE,
            self::TABLE_TPV_REDSYS,
            self::TABLE_COUPON,
            self::TABLE_COUPON_ITEM,
            self::TABLE_COUPON_SERVICE,
            self::TABLE_SUBSCRIPTION,
            self::TABLE_SUBSCRIPTION_SALE,
            self::TABLE_SUBSCRIPTION_PERIOD,
            self::TABLE_COUPON_SALE,
            self::TABLE_COUPON_SERVICE_SALE,
            self::TABLE_COUPON_SUBSCRIPTION_SALE,
            self::TABLE_STRIPE,
            self::TABLE_TPV_CECABANK,
        ];
        $em = Database::getManager();
        $cn = $em->getConnection();
        $sm = $cn->getSchemaManager();
        $tables = $sm->tablesExist($tablesToBeCompared);

        if ($tables) {
            return false;
        }

        require_once api_get_path(SYS_PLUGIN_PATH).'buycourses/database.php';
    }

    /**
     * This method drops the plugin tables.
     */
    public function uninstall()
    {
        $tablesToBeDeleted = [
            self::TABLE_PAYPAL,
            self::TABLE_TRANSFER,
            self::TABLE_CULQI,
            self::TABLE_ITEM_BENEFICIARY,
            self::TABLE_ITEM,
            self::TABLE_SALE,
            self::TABLE_CURRENCY,
            self::TABLE_COMMISSION,
            self::TABLE_PAYPAL_PAYOUTS,
            self::TABLE_SERVICES_SALE,
            self::TABLE_SERVICES,
            self::TABLE_GLOBAL_CONFIG,
            self::TABLE_INVOICE,
            self::TABLE_TPV_REDSYS,
            self::TABLE_COUPON,
            self::TABLE_COUPON_ITEM,
            self::TABLE_COUPON_SERVICE,
            self::TABLE_SUBSCRIPTION,
            self::TABLE_SUBSCRIPTION_SALE,
            self::TABLE_SUBSCRIPTION_PERIOD,
            self::TABLE_COUPON_SALE,
            self::TABLE_COUPON_SERVICE_SALE,
            self::TABLE_COUPON_SUBSCRIPTION_SALE,
            self::TABLE_STRIPE,
        ];

        foreach ($tablesToBeDeleted as $tableToBeDeleted) {
            $table = Database::get_main_table($tableToBeDeleted);
            $sql = "DROP TABLE IF EXISTS $table";
            Database::query($sql);
        }
        $this->manageTab(false);
    }

    public function update()
    {
        $table = self::TABLE_GLOBAL_CONFIG;
        $sql = "SHOW COLUMNS FROM $table WHERE Field = 'global_tax_perc'";
        $res = Database::query($sql);

        if (Database::num_rows($res) === 0) {
            $sql = "ALTER TABLE $table ADD (
                sale_email varchar(255) NOT NULL,
                global_tax_perc int unsigned NOT NULL,
                tax_applies_to int unsigned NOT NULL,
                tax_name varchar(255) NOT NULL,
                seller_name varchar(255) NOT NULL,
                seller_id varchar(255) NOT NULL,
                seller_address varchar(255) NOT NULL,
                seller_email varchar(255) NOT NULL,
                next_number_invoice int unsigned NOT NULL,
                invoice_series varchar(255) NOT NULL
            )";
            $res = Database::query($sql);
            if (!$res) {
                echo Display::return_message($this->get_lang('ErrorUpdateFieldDB'), 'warning');
            }
        }

        $sql = "SHOW COLUMNS FROM $table WHERE Field = 'info_email_extra'";
        $res = Database::query($sql);

        if (Database::num_rows($res) === 0) {
            $sql = "ALTER TABLE $table ADD (info_email_extra TEXT NOT NULL)";
            $res = Database::query($sql);
            if (!$res) {
                echo Display::return_message($this->get_lang('ErrorUpdateFieldDB'), 'warning');
            }
        }

        $table = self::TABLE_ITEM;
        $sql = "SHOW COLUMNS FROM $table WHERE Field = 'tax_perc'";
        $res = Database::query($sql);

        if (Database::num_rows($res) === 0) {
            $sql = "ALTER TABLE $table ADD tax_perc int unsigned NULL";
            $res = Database::query($sql);
            if (!$res) {
                echo Display::return_message($this->get_lang('ErrorUpdateFieldDB'), 'warning');
            }
        }

        $table = self::TABLE_SERVICES;
        $sql = "SHOW COLUMNS FROM $table WHERE Field = 'tax_perc'";
        $res = Database::query($sql);

        if (Database::num_rows($res) === 0) {
            $sql = "ALTER TABLE $table ADD tax_perc int unsigned NULL";
            $res = Database::query($sql);
            if (!$res) {
                echo Display::return_message($this->get_lang('ErrorUpdateFieldDB'), 'warning');
            }
        }

        $table = self::TABLE_SALE;
        $sql = "SHOW COLUMNS FROM $table WHERE Field = 'tax_perc'";
        $res = Database::query($sql);

        if (Database::num_rows($res) === 0) {
            $sql = "ALTER TABLE $table ADD (
                price_without_tax decimal(10,2) NULL,
                tax_perc int unsigned NULL,
                tax_amount decimal(10,2) NULL,
                invoice int unsigned NULL
            )";
            $res = Database::query($sql);
            if (!$res) {
                echo Display::return_message($this->get_lang('ErrorUpdateFieldDB'), 'warning');
            }
        }

        $sql = "SHOW COLUMNS FROM $table WHERE Field = 'price_without_discount'";
        $res = Database::query($sql);

        if (Database::num_rows($res) === 0) {
            $sql = "ALTER TABLE $table ADD (
                price_without_discount decimal(10,2) NULL,
                discount_amount decimal(10,2) NULL
            )";
            $res = Database::query($sql);
            if (!$res) {
                echo Display::return_message($this->get_lang('ErrorUpdateFieldDB'), 'warning');
            }
        }

        $table = self::TABLE_SERVICES_SALE;
        $sql = "SHOW COLUMNS FROM $table WHERE Field = 'tax_perc'";
        $res = Database::query($sql);

        if (Database::num_rows($res) === 0) {
            $sql = "ALTER TABLE $table ADD (
                price_without_tax decimal(10,2) NULL,
                tax_perc int unsigned NULL,
                tax_amount decimal(10,2) NULL,
                invoice int unsigned NULL
            )";
            $res = Database::query($sql);
            if (!$res) {
                echo Display::return_message($this->get_lang('ErrorUpdateFieldDB'), 'warning');
            }
        }

        $sql = "SHOW COLUMNS FROM $table WHERE Field = 'price_without_discount'";
        $res = Database::query($sql);

        if (Database::num_rows($res) === 0) {
            $sql = "ALTER TABLE $table ADD (
                price_without_discount decimal(10,2) NULL,
                discount_amount decimal(10,2) NULL
            )";
            $res = Database::query($sql);
            if (!$res) {
                echo Display::return_message($this->get_lang('ErrorUpdateFieldDB'), 'warning');
            }
        }

        $table = self::TABLE_INVOICE;
        $sql = "CREATE TABLE IF NOT EXISTS $table (
            id int unsigned NOT NULL AUTO_INCREMENT,
            sale_id int unsigned NOT NULL,
            is_service int unsigned NOT NULL,
            num_invoice int unsigned NOT NULL,
            year int(4) unsigned NOT NULL,
            serie varchar(255) NOT NULL,
            date_invoice datetime NOT NULL,
            PRIMARY KEY (id)
        )";
        Database::query($sql);

        $table = self::TABLE_TPV_REDSYS;
        $sql = "CREATE TABLE IF NOT EXISTS $table (
            id int unsigned NOT NULL AUTO_INCREMENT,
            merchantcode varchar(255) NOT NULL,
            terminal varchar(255) NOT NULL,
            currency varchar(255) NOT NULL,
            kc varchar(255) NOT NULL,
            url_redsys varchar(255) NOT NULL,
            url_redsys_sandbox varchar(255) NOT NULL,
            sandbox int unsigned NULL,
            PRIMARY KEY (id)
        )";
        Database::query($sql);

        $sql = "SELECT * FROM $table";
        $res = Database::query($sql);
        if (Database::num_rows($res) == 0) {
            Database::insert($table, [
                'url_redsys' => 'https://sis.redsys.es/sis/realizarPago',
                'url_redsys_sandbox' => 'https://sis-t.redsys.es:25443/sis/realizarPago',
            ]);
        }

        $table = self::TABLE_COUPON;
        $sql = "CREATE TABLE IF NOT EXISTS $table (
            id int unsigned NOT NULL AUTO_INCREMENT,
            code varchar(255) NOT NULL,
            discount_type int unsigned NOT NULL,
            discount_amount decimal(10, 2) NOT NULL,
            valid_start datetime NOT NULL,
            valid_end datetime NOT NULL,
            delivered varchar(255) NOT NULL,
            active tinyint NOT NULL,
            PRIMARY KEY (id)
        )";
        Database::query($sql);

        $table = self::TABLE_COUPON_ITEM;
        $sql = "CREATE TABLE IF NOT EXISTS $table (
            id int unsigned NOT NULL AUTO_INCREMENT,
            coupon_id int unsigned NOT NULL,
            product_type int unsigned NOT NULL,
            product_id int unsigned NOT NULL,
            PRIMARY KEY (id)
        )";
        Database::query($sql);

        $table = self::TABLE_COUPON_SERVICE;
        $sql = "CREATE TABLE IF NOT EXISTS $table (
            id int unsigned NOT NULL AUTO_INCREMENT,
            coupon_id int unsigned NOT NULL,
            service_id int unsigned NOT NULL,
            PRIMARY KEY (id)
        )";
        Database::query($sql);

        $table = self::TABLE_SUBSCRIPTION;
        $sql = "CREATE TABLE IF NOT EXISTS $table (
            product_type int unsigned NOT NULL,
            product_id int unsigned NOT NULL,
            duration int unsigned NOT NULL,
            currency_id int unsigned NOT NULL,
            price decimal(10, 2) NOT NULL,
            tax_perc int unsigned,
            PRIMARY KEY (product_type, product_id, duration)
        )";
        Database::query($sql);

        $table = self::TABLE_SUBSCRIPTION_SALE;
        $sql = "CREATE TABLE IF NOT EXISTS $table (
            id int unsigned NOT NULL AUTO_INCREMENT,
            currency_id int unsigned NOT NULL,
            reference varchar(255) NOT NULL,
            date datetime NOT NULL,
            user_id int unsigned NOT NULL,
            product_type int NOT NULL,
            product_name varchar(255) NOT NULL,
            product_id int unsigned NOT NULL,
            price decimal(10,2) NOT NULL,
            price_without_tax decimal(10,2) NULL,
            tax_perc int unsigned NULL,
            tax_amount decimal(10,2) NULL,
            status int NOT NULL,
            payment_type int NOT NULL,
            invoice int NOT NULL,
            price_without_discount decimal(10,2),
            discount_amount decimal(10,2),
            subscription_end datetime NOT NULL,
            expired tinyint NULL,
            PRIMARY KEY (id)
        )";
        Database::query($sql);

        $table = self::TABLE_SUBSCRIPTION_PERIOD;
        $sql = "CREATE TABLE IF NOT EXISTS $table (
            duration int unsigned NOT NULL,
            name varchar(50) NOT NULL,
            PRIMARY KEY (duration)
        )";
        Database::query($sql);

        $table = self::TABLE_COUPON_SALE;
        $sql = "CREATE TABLE IF NOT EXISTS $table (
            id int unsigned NOT NULL AUTO_INCREMENT,
            coupon_id int unsigned NOT NULL,
            sale_id int unsigned NOT NULL,
            PRIMARY KEY (id)
        )";
        Database::query($sql);

        $table = self::TABLE_COUPON_SERVICE_SALE;
        $sql = "CREATE TABLE IF NOT EXISTS $table (
            id int unsigned NOT NULL AUTO_INCREMENT,
            coupon_id int unsigned NOT NULL,
            service_sale_id int unsigned NOT NULL,
            PRIMARY KEY (id)
        )";
        Database::query($sql);

        $table = self::TABLE_COUPON_SUBSCRIPTION_SALE;
        $sql = "CREATE TABLE IF NOT EXISTS $table (
            id int unsigned NOT NULL AUTO_INCREMENT,
            coupon_id int unsigned NOT NULL,
            sale_id int unsigned NOT NULL,
            PRIMARY KEY (id)
        )";
        Database::query($sql);

        $table = self::TABLE_STRIPE;
        $sql = "CREATE TABLE IF NOT EXISTS $table (
            id int unsigned NOT NULL AUTO_INCREMENT,
            account_id varchar(255) NOT NULL,
            secret_key varchar(255) NOT NULL,
            endpoint_secret varchar(255) NOT NULL,
            PRIMARY KEY (id)
        )";
        Database::query($sql);

        $sql = "SELECT * FROM $table";
        $res = Database::query($sql);
        if (Database::num_rows($res) == 0) {
            Database::insert($table, [
                'account_id' => '',
                'secret_key' => '',
                'endpoint_secret' => '',
            ]);
        }

        $table = self::TABLE_TPV_CECABANK;
        $sql = "CREATE TABLE IF NOT EXISTS $table (
            id int unsigned NOT NULL AUTO_INCREMENT,
            crypto_key varchar(255) NOT NULL,
            merchant_id varchar(255) NOT NULL,
            acquirer_bin varchar(255) NOT NULL,
            terminal_id varchar(255) NOT NULL,
            cypher varchar(255) NOT NULL,
            exponent varchar(255) NOT NULL,
            supported_payment varchar(255) NOT NULL,
            url varchar(255) NOT NULL,
            PRIMARY KEY (id)
        )";
        Database::query($sql);

        Display::addFlash(
            Display::return_message(
                $this->get_lang('Updated'),
                'info',
                false
            )
        );

        $fieldlabel = 'buycourses_company';
        $fieldtype = '1';
        $fieldtitle = $this->get_lang('Company');
        $fielddefault = '';
        UserManager::create_extra_field($fieldlabel, $fieldtype, $fieldtitle, $fielddefault);

        $fieldlabel = 'buycourses_vat';
        $fieldtype = '1';
        $fieldtitle = $this->get_lang('VAT');
        $fielddefault = '';
        UserManager::create_extra_field($fieldlabel, $fieldtype, $fieldtitle, $fielddefault);

        $fieldlabel = 'buycourses_address';
        $fieldtype = '1';
        $fieldtitle = $this->get_lang('Address');
        $fielddefault = '';
        UserManager::create_extra_field($fieldlabel, $fieldtype, $fieldtitle, $fielddefault);

        header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses');
        exit;
    }

    /**
     * This function verify if the plugin is enable and return the price info for a course or session in the new grid
     * 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
     * course catalog so the old buycourses plugin catalog can be deprecated.
     *
     * @param int $productId   course or session id
     * @param int $productType course or session type
     *
     * @return mixed bool|string html
     */
    public function buyCoursesForGridCatalogValidator(int $productId, int $productType)
    {
        $return = [];
        $paypal = $this->get('paypal_enable') === 'true';
        $transfer = $this->get('transfer_enable') === 'true';
        $stripe = $this->get('stripe_enable') === 'true';
        $culqi = $this->get('culqi_enable') === 'true';
        $cecabank = $this->get('cecabank_enable') === 'true';
        $tpv_redsys = $this->get('tpv_redsys_enable') === 'true';
        $hideFree = $this->get('hide_free_text') === 'true';

        if ($paypal || $transfer || $stripe || $culqi || $cecabank || $tpv_redsys) {
            $item = $this->getItemByProduct($productId, $productType);
            $html = '<div class="buycourses-price">';
            if ($item) {
                $html .= '<span class="label label-primary label-price">
                            <strong>'.$item['total_price_formatted'].'</strong>
                          </span>';
                $return['verificator'] = true;
            } else {
                if ($hideFree == false) {
                    $html .= '<span class="label label-primary label-free">
                                <strong>'.$this->get_lang('Free').'</strong>
                              </span>';
                }
                $return['verificator'] = false;
            }
            $html .= '</div>';
            $return['html'] = $html;

            return $return;
        }

        return false;
    }

    /**
     * Return the buyCourses plugin button to buy the course.
     *
     * @return string $html
     */
    public function returnBuyCourseButton(int $productId, int $productType)
    {
        $productId = $productId;
        $productType = $productType;
        $url = api_get_path(WEB_PLUGIN_PATH).'buycourses/src/process.php?i='.$productId.'&t='.$productType;
        $buyButton = Display::returnFontAwesomeIcon('shopping-cart');
        if ($this->get('hide_shopping_cart_from_course_catalogue') === 'true') {
            $buyButton = Display::returnFontAwesomeIcon('check').PHP_EOL.get_lang('Subscribe');
        }
        $html = '<a class="btn btn-success btn-sm" title="'.$this->get_lang('Buy').'" href="'.$url.'">'.
            $buyButton.'</a>';

        return $html;
    }

    /**
     * Get the currency for sales.
     *
     * @return array The selected currency. Otherwise return false
     */
    public function getSelectedCurrency()
    {
        return Database::select(
            '*',
            Database::get_main_table(self::TABLE_CURRENCY),
            [
                'where' => ['status = ?' => true],
            ],
            'first'
        );
    }

    /**
     * Get a list of currencies.
     *
     * @return array The currencies. Otherwise return false
     */
    public function getCurrencies()
    {
        return Database::select(
            '*',
            Database::get_main_table(self::TABLE_CURRENCY)
        );
    }

    /**
     * Save the selected currency.
     *
     * @param int $selectedId The currency Id
     */
    public function saveCurrency(int $selectedId)
    {
        $currencyTable = Database::get_main_table(
            self::TABLE_CURRENCY
        );

        Database::update(
            $currencyTable,
            ['status' => 0]
        );
        Database::update(
            $currencyTable,
            ['status' => 1],
            ['id = ?' => $selectedId]
        );
    }

    /**
     * Save the PayPal configuration params.
     *
     * @return int Rows affected. Otherwise return false
     */
    public function savePaypalParams(array $params)
    {
        return Database::update(
            Database::get_main_table(self::TABLE_PAYPAL),
            [
                'username' => $params['username'],
                'password' => $params['password'],
                'signature' => $params['signature'],
                'sandbox' => isset($params['sandbox']),
            ],
            ['id = ?' => 1]
        );
    }

    /**
     * Gets the stored PayPal params.
     *
     * @return array
     */
    public function getPaypalParams()
    {
        return Database::select(
            '*',
            Database::get_main_table(self::TABLE_PAYPAL),
            ['id = ?' => 1],
            'first'
        );
    }

    /**
     * Gets the stored TPV Redsys params.
     *
     * @return array
     */
    public function getTpvRedsysParams()
    {
        return Database::select(
            '*',
            Database::get_main_table(self::TABLE_TPV_REDSYS),
            ['id = ?' => 1],
            'first'
        );
    }

    /**
     * Save the tpv Redsys configuration params.
     *
     * @return int Rows affected. Otherwise return false
     */
    public function saveTpvRedsysParams(array $params)
    {
        return Database::update(
            Database::get_main_table(self::TABLE_TPV_REDSYS),
            [
                'merchantcode' => $params['merchantcode'],
                'terminal' => $params['terminal'],
                'currency' => $params['currency'],
                'kc' => $params['kc'],
                'url_redsys' => $params['url_redsys'],
                'url_redsys_sandbox' => $params['url_redsys_sandbox'],
                'sandbox' => isset($params['sandbox']),
            ],
            ['id = ?' => 1]
        );
    }

    /**
     * Save Stripe configuration params.
     *
     * @return int Rows affected. Otherwise return false
     */
    public function saveStripeParameters(array $params)
    {
        return Database::update(
            Database::get_main_table(self::TABLE_STRIPE),
            [
                'account_id' => $params['account_id'],
                'secret_key' => $params['secret_key'],
                'endpoint_secret' => $params['endpoint_secret'],
            ],
            ['id = ?' => 1]
        );
    }

    /**
     * Gets the stored Stripe params.
     *
     * @return array
     */
    public function getStripeParams()
    {
        return Database::select(
            '*',
            Database::get_main_table(self::TABLE_STRIPE),
            ['id = ?' => 1],
            'first'
        );
    }

    /**
     * Save a transfer account information.
     *
     * @param array $params The transfer account
     *
     * @return int Rows affected. Otherwise, return false
     */
    public function saveTransferAccount(array $params)
    {
        return Database::insert(
            Database::get_main_table(self::TABLE_TRANSFER),
            [
                'name' => $params['tname'],
                'account' => $params['taccount'],
                'swift' => $params['tswift'],
            ]
        );
    }

    /**
     * Save email message information in transfer.
     *
     * @param array $params The transfer message
     *
     * @return int Rows affected. Otherwise return false
     */
    public function saveTransferInfoEmail(array $params)
    {
        return Database::update(
            Database::get_main_table(self::TABLE_GLOBAL_CONFIG),
            ['info_email_extra' => $params['tinfo_email_extra']],
            ['id = ?' => 1]
        );
    }

    /**
     * Gets message information for transfer email.
     *
     * @return array
     */
    public function getTransferInfoExtra()
    {
        return Database::select(
            'info_email_extra AS tinfo_email_extra',
            Database::get_main_table(self::TABLE_GLOBAL_CONFIG),
            ['id = ?' => 1],
            'first'
        );
    }

    /**
     * Get a list of transfer accounts.
     *
     * @return array
     */
    public function getTransferAccounts()
    {
        return Database::select(
            '*',
            Database::get_main_table(self::TABLE_TRANSFER)
        );
    }

    /**
     * Remove a transfer account.
     *
     * @param int $id The transfer account ID
     *
     * @return int Rows affected. Otherwise return false
     */
    public function deleteTransferAccount(int $id)
    {
        return Database::delete(
            Database::get_main_table(self::TABLE_TRANSFER),
            ['id = ?' => $id]
        );
    }

    /**
     * Get registered item data.
     *
     * @param int $itemId The item ID
     *
     * @return array
     */
    public function getItem(int $itemId)
    {
        return Database::select(
            '*',
            Database::get_main_table(self::TABLE_ITEM),
            [
                'where' => ['id = ?' => $itemId],
            ],
            'first'
        );
    }

    /**
     * Get the item data.
     *
     * @param int $productId The item ID
     * @param int $itemType  The item type
     *
     * @return array
     */
    public function getItemByProduct(int $productId, int $itemType, array $coupon = null)
    {
        $buyItemTable = Database::get_main_table(self::TABLE_ITEM);
        $buyCurrencyTable = Database::get_main_table(self::TABLE_CURRENCY);

        $fakeItemFrom = "
            $buyItemTable i
            INNER JOIN $buyCurrencyTable c
                ON i.currency_id = c.id
        ";

        $product = Database::select(
            ['i.*', 'c.iso_code'],
            $fakeItemFrom,
            [
                'where' => [
                    'i.product_id = ? AND i.product_type = ?' => [
                        $productId,
                        $itemType,
                    ],
                ],
            ],
            'first'
        );

        if (empty($product)) {
            return false;
        }

        $this->setPriceSettings($product, self::TAX_APPLIES_TO_ONLY_COURSE, $coupon);

        return $product;
    }

    /**
     * Get registered item data.
     *
     * @param int $itemId      The product ID
     * @param int $productType The product type
     *
     * @return array
     */
    public function getSubscriptionItem(int $itemId, int $productType)
    {
        return Database::select(
            '*',
            Database::get_main_table(self::TABLE_SUBSCRIPTION),
            [
                'where' => ['product_id = ? AND product_type = ?' => [
                        $itemId,
                        $productType,
                    ],
                ],
            ],
            'first'
        );
    }

    /**
     * Get the item data.
     *
     * @param int   $productId The item ID
     * @param int   $itemType  The item type
     * @param array $coupon    Array with at least 'discount_type' and 'discount_amount' elements
     *
     * @return array
     */
    public function getSubscriptionItemByProduct(int $productId, int $itemType, array $coupon = null)
    {
        $buySubscriptionItemTable = Database::get_main_table(self::TABLE_SUBSCRIPTION);
        $buyCurrencyTable = Database::get_main_table(self::TABLE_CURRENCY);

        $fakeItemFrom = "
            $buySubscriptionItemTable s
            INNER JOIN $buyCurrencyTable c
                ON s.currency_id = c.id
        ";

        $item = Database::select(
            ['s.*', 'c.iso_code'],
            $fakeItemFrom,
            [
                'where' => [
                    's.product_id = ? AND s.product_type = ?' => [
                        $productId,
                        $itemType,
                    ],
                ],
            ],
            'first'
        );

        if (empty($item)) {
            return false;
        }

        $this->setPriceSettings($item, self::TAX_APPLIES_TO_ONLY_COURSE, $coupon);

        return $item;
    }

    /**
     * Get the item data.
     *
     * @param int $productId The item ID
     * @param int $itemType  The item type
     *
     * @return array
     */
    public function getSubscriptionsItemsByProduct(int $productId, int $itemType)
    {
        $buySubscriptionItemTable = Database::get_main_table(self::TABLE_SUBSCRIPTION);
        $buyCurrencyTable = Database::get_main_table(self::TABLE_CURRENCY);

        $fakeItemFrom = "
            $buySubscriptionItemTable s
            INNER JOIN $buyCurrencyTable c
                ON s.currency_id = c.id
        ";

        $items = Database::select(
            ['s.*', 'c.iso_code'],
            $fakeItemFrom,
            [
                'where' => [
                    's.product_id = ? AND s.product_type = ?' => [
                        $productId,
                        $itemType,
                    ],
                ],
            ]
        );

        for ($i = 0; $i < count($items); $i++) {
            $this->setPriceSettings($items[$i], self::TAX_APPLIES_TO_ONLY_COURSE);
        }

        if (empty($items)) {
            return false;
        }

        return $items;
    }

    /**
     * Get registered item data by duration.
     *
     * @param int $duration The subscription duration
     *
     * @return array
     */
    public function getSubscriptionsItemsByDuration(int $duration)
    {
        return Database::select(
            '*',
            Database::get_main_table(self::TABLE_SUBSCRIPTION),
            [
                'where' => [
                    'duration = ?' => [$duration],
                ],
            ]
        );
    }

    /**
     * List courses details from the configuration page.
     *
     * @return array
     */
    public function getCourseList(int $first, int $maxResults)
    {
        return $this->getCourses($first, $maxResults);
    }

    /**
     * Lists current user session details, including each session course details.
     *
     * It can return the number of rows when $typeResult is 'count'.
     *
     * @param string $name       Optional. The name filter.
     * @param int    $min        Optional. The minimum price filter.
     * @param int    $max        Optional. The maximum price filter.
     * @param string $typeResult Optional. 'all', 'first' or 'count'.
     *
     * @return array|int
     */
    public function getCatalogSessionList(int $start, int $end, string $name = null, int $min = 0, int $max = 0, string $typeResult = 'all', $sessionCategory = 0)
    {
        $sessions = $this->filterSessionList($start, $end, $name, $min, $max, $typeResult, $sessionCategory);

        if ($typeResult === 'count') {
            return $sessions;
        }

        $sessionCatalog = [];
        // loop through all sessions
        foreach ($sessions as $session) {
            $sessionCourses = $session->getCourses();

            if (empty($sessionCourses)) {
                continue;
            }

            $item = $this->getItemByProduct(
                $session->getId(),
                self::PRODUCT_TYPE_SESSION
            );

            if (empty($item)) {
                continue;
            }

            $sessionData = $this->getSessionInfo($session->getId());
            $sessionData['coach'] = $session->getGeneralCoach()->getCompleteName();
            $sessionData['enrolled'] = $this->getUserStatusForSession(
                api_get_user_id(),
                $session
            );
            $sessionData['courses'] = [];

            foreach ($sessionCourses as $sessionCourse) {
                $course = $sessionCourse->getCourse();

                $sessionCourseData = [
                    'title' => $course->getTitle(),
                    'coaches' => [],
                ];

                $userCourseSubscriptions = $session->getUserCourseSubscriptionsByStatus(
                    $course,
                    Chamilo\CoreBundle\Entity\Session::COACH
                );

                foreach ($userCourseSubscriptions as $userCourseSubscription) {
                    $user = $userCourseSubscription->getUser();
                    $sessionCourseData['coaches'][] = $user->getCompleteName();
                }
                $sessionData['courses'][] = $sessionCourseData;
            }

            $sessionCatalog[] = $sessionData;
        }

        return $sessionCatalog;
    }

    /**
     * Lists current user course details.
     *
     * @param string $name Optional. The name filter
     * @param int    $min  Optional. The minimum price filter
     * @param int    $max  Optional. The maximum price filter
     *
     * @return array|int
     */
    public function getCatalogCourseList(int $first, int $pageSize, string $name = null, int $min = 0, int $max = 0, string $typeResult = 'all')
    {
        $courses = $this->filterCourseList($first, $pageSize, $name, $min, $max, $typeResult);

        if ($typeResult === 'count') {
            return $courses;
        }

        if (empty($courses)) {
            return [];
        }

        $courseCatalog = [];
        foreach ($courses as $course) {
            $item = $this->getItemByProduct(
                $course->getId(),
                self::PRODUCT_TYPE_COURSE
            );

            if (empty($item)) {
                continue;
            }

            $courseItem = [
                'id' => $course->getId(),
                'title' => $course->getTitle(),
                'code' => $course->getCode(),
                'course_img' => null,
                'item' => $item,
                'teachers' => [],
                'enrolled' => $this->getUserStatusForCourse(api_get_user_id(), $course),
            ];

            foreach ($course->getTeachers() as $courseUser) {
                $teacher = $courseUser->getUser();
                $courseItem['teachers'][] = $teacher->getCompleteName();
            }

            // Check images
            $possiblePath = api_get_path(SYS_COURSE_PATH);
            $possiblePath .= $course->getDirectory();
            $possiblePath .= '/course-pic.png';

            if (file_exists($possiblePath)) {
                $courseItem['course_img'] = api_get_path(WEB_COURSE_PATH).$course->getDirectory().'/course-pic.png';
            }
            $courseCatalog[] = $courseItem;
        }

        return $courseCatalog;
    }

    /**
     * Lists current user subscription session details, including each session course details.
     *
     * It can return the number of rows when $typeResult is 'count'.
     *
     * @param int    $start           Pagination start.
     * @param int    $end             Pagination end.
     * @param string $name            Optional. The name filter.
     * @param string $typeResult      Optional. 'all', 'first' or 'count'.
     * @param int    $sessionCategory Optional. Session category id
     *
     * @return array|int
     */
    public function getCatalogSubscriptionSessionList(int $start, int $end, string $name = null, string $typeResult = 'all', int $sessionCategory = 0)
    {
        $sessions = $this->filterSubscriptionSessionList($start, $end, $name, $typeResult, $sessionCategory);

        if ($typeResult === 'count') {
            return $sessions;
        }

        $sessionCatalog = [];
        // loop through all sessions
        foreach ($sessions as $session) {
            $sessionCourses = $session->getCourses();

            if (empty($sessionCourses)) {
                continue;
            }

            $item = $this->getSubscriptionItemByProduct(
                $session->getId(),
                self::PRODUCT_TYPE_SESSION
            );

            if (empty($item)) {
                continue;
            }

            $sessionData = $this->getSubscriptionSessionInfo($session->getId());
            $sessionData['coach'] = $session->getGeneralCoach()->getCompleteName();
            $sessionData['enrolled'] = $this->getUserStatusForSubscriptionSession(
                api_get_user_id(),
                $session
            );
            $sessionData['courses'] = [];

            foreach ($sessionCourses as $sessionCourse) {
                $course = $sessionCourse->getCourse();

                $sessionCourseData = [
                    'title' => $course->getTitle(),
                    'coaches' => [],
                ];

                $userCourseSubscriptions = $session->getUserCourseSubscriptionsByStatus(
                    $course,
                    Chamilo\CoreBundle\Entity\Session::COACH
                );

                foreach ($userCourseSubscriptions as $userCourseSubscription) {
                    $user = $userCourseSubscription->getUser();
                    $sessionCourseData['coaches'][] = $user->getCompleteName();
                }
                $sessionData['courses'][] = $sessionCourseData;
            }

            $sessionCatalog[] = $sessionData;
        }

        return $sessionCatalog;
    }

    /**
     * Lists current user subscription course details.
     *
     * @param string $typeResult Optional. 'all', 'first' or 'count'.
     *
     * @return array|int
     */
    public function getCatalogSubscriptionCourseList(int $first, int $pageSize, string $name = null, string $typeResult = 'all')
    {
        $courses = $this->filterSubscriptionCourseList($first, $pageSize, $name, $typeResult);

        if ($typeResult === 'count') {
            return $courses;
        }

        if (empty($courses)) {
            return [];
        }

        $courseCatalog = [];
        foreach ($courses as $course) {
            $item = $this->getSubscriptionItemByProduct(
                $course->getId(),
                self::PRODUCT_TYPE_COURSE
            );

            if (empty($item)) {
                continue;
            }

            $courseItem = [
                'id' => $course->getId(),
                'title' => $course->getTitle(),
                'code' => $course->getCode(),
                'course_img' => null,
                'item' => $item,
                'teachers' => [],
                'enrolled' => $this->getUserStatusForSubscriptionCourse(api_get_user_id(), $course),
            ];

            foreach ($course->getTeachers() as $courseUser) {
                $teacher = $courseUser->getUser();
                $courseItem['teachers'][] = $teacher->getCompleteName();
            }

            // Check images
            $possiblePath = api_get_path(SYS_COURSE_PATH);
            $possiblePath .= $course->getDirectory();
            $possiblePath .= '/course-pic.png';

            if (file_exists($possiblePath)) {
                $courseItem['course_img'] = api_get_path(WEB_COURSE_PATH).$course->getDirectory().'/course-pic.png';
            }
            $courseCatalog[] = $courseItem;
        }

        return $courseCatalog;
    }

    public function getPriceWithCurrencyFromIsoCode(float $price, string $isoCode): string
    {
        $useSymbol = $this->get('use_currency_symbol') === 'true';

        $result = $isoCode.' '.$price;
        if ($useSymbol) {
            if ($isoCode === 'BRL') {
                $symbol = 'R$';
            } else {
                $symbol = Symfony\Component\Intl\Intl::getCurrencyBundle()->getCurrencySymbol($isoCode);
            }
            $result = $symbol.' '.$price;
        }

        return $result;
    }

    /**
     * Get course info.
     *
     * @return array
     */
    public function getCourseInfo(int $courseId, array $coupon = null)
    {
        $entityManager = Database::getManager();
        $course = $entityManager->find('ChamiloCoreBundle:Course', $courseId);

        if (empty($course)) {
            return [];
        }

        $item = $this->getItemByProduct(
            $course->getId(),
            self::PRODUCT_TYPE_COURSE,
            $coupon
        );

        if (empty($item)) {
            return [];
        }

        $courseDescription = $entityManager->getRepository('ChamiloCourseBundle:CCourseDescription')
            ->findOneBy(
                [
                    'cId' => $course->getId(),
                    'sessionId' => 0,
                ],
                [
                    'descriptionType' => 'ASC',
                ]
            );

        $globalParameters = $this->getGlobalParameters();
        $courseInfo = [
            'id' => $course->getId(),
            'title' => $course->getTitle(),
            'description' => $courseDescription ? $courseDescription->getContent() : null,
            'code' => $course->getCode(),
            'visual_code' => $course->getVisualCode(),
            'teachers' => [],
            'item' => $item,
            'tax_name' => $globalParameters['tax_name'],
            'tax_enable' => $this->checkTaxEnabledInProduct(self::TAX_APPLIES_TO_ONLY_COURSE),
            'course_img' => null,
        ];

        $courseTeachers = $course->getTeachers();

        foreach ($courseTeachers as $teachers) {
            $user = $teachers->getUser();
            $teacher['id'] = $user->getId();
            $teacher['name'] = $user->getCompleteName();
            $courseInfo['teachers'][] = $teacher;
        }

        $possiblePath = api_get_path(SYS_COURSE_PATH);
        $possiblePath .= $course->getDirectory();
        $possiblePath .= '/course-pic.png';

        if (file_exists($possiblePath)) {
            $courseInfo['course_img'] = api_get_path(WEB_COURSE_PATH).$course->getDirectory().'/course-pic.png';
        }

        return $courseInfo;
    }

    /**
     * Get session info.
     *
     * @return array
     */
    public function getSessionInfo(int $sessionId, array $coupon = null)
    {
        $entityManager = Database::getManager();
        $session = $entityManager->find('ChamiloCoreBundle:Session', $sessionId);

        if (empty($session)) {
            return [];
        }

        $item = $this->getItemByProduct(
            $session->getId(),
            self::PRODUCT_TYPE_SESSION,
            $coupon
        );

        if (empty($item)) {
            return [];
        }

        $sessionDates = SessionManager::parseSessionDates(
            [
                'display_start_date' => $session->getDisplayStartDate(),
                'display_end_date' => $session->getDisplayEndDate(),
                'access_start_date' => $session->getAccessStartDate(),
                'access_end_date' => $session->getAccessEndDate(),
                'coach_access_start_date' => $session->getCoachAccessStartDate(),
                'coach_access_end_date' => $session->getCoachAccessEndDate(),
            ]
        );

        $globalParameters = $this->getGlobalParameters();
        $sessionInfo = [
            'id' => $session->getId(),
            'name' => $session->getName(),
            'description' => $session->getDescription(),
            'dates' => $sessionDates,
            'courses' => [],
            'tax_name' => $globalParameters['tax_name'],
            'tax_enable' => $this->checkTaxEnabledInProduct(self::TAX_APPLIES_TO_ONLY_SESSION),
            'image' => null,
            'nbrCourses' => $session->getNbrCourses(),
            'nbrUsers' => $session->getNbrUsers(),
            'item' => $item,
            'duration' => $session->getDuration(),
        ];

        $fieldValue = new ExtraFieldValue('session');
        $sessionImage = $fieldValue->get_values_by_handler_and_field_variable(
            $session->getId(),
            'image'
        );

        if (!empty($sessionImage)) {
            $sessionInfo['image'] = api_get_path(WEB_UPLOAD_PATH).$sessionImage['value'];
        }

        $sessionCourses = $session->getCourses();
        foreach ($sessionCourses as $sessionCourse) {
            $course = $sessionCourse->getCourse();
            $sessionCourseData = [
                'title' => $course->getTitle(),
                'coaches' => [],
            ];

            $userCourseSubscriptions = $session->getUserCourseSubscriptionsByStatus(
                $course,
                Chamilo\CoreBundle\Entity\Session::COACH
            );

            foreach ($userCourseSubscriptions as $userCourseSubscription) {
                $user = $userCourseSubscription->getUser();
                $coaches['id'] = $user->getUserId();
                $coaches['name'] = $user->getCompleteName();
                $sessionCourseData['coaches'][] = $coaches;
            }

            $sessionInfo['courses'][] = $sessionCourseData;
        }

        return $sessionInfo;
    }

    /**
     * Get course info.
     *
     * @return array
     */
    public function getSubscriptionCourseInfo(int $courseId, array $coupon = null)
    {
        $entityManager = Database::getManager();
        $course = $entityManager->find('ChamiloCoreBundle:Course', $courseId);

        if (empty($course)) {
            return [];
        }

        $item = $this->getSubscriptionItemByProduct(
            $course->getId(),
            self::PRODUCT_TYPE_COURSE,
            $coupon
        );

        if (empty($item)) {
            return [];
        }

        $courseDescription = $entityManager->getRepository('ChamiloCourseBundle:CCourseDescription')
            ->findOneBy(
                [
                    'cId' => $course->getId(),
                    'sessionId' => 0,
                ],
                [
                    'descriptionType' => 'ASC',
                ]
            );

        $globalParameters = $this->getGlobalParameters();
        $courseInfo = [
            'id' => $course->getId(),
            'title' => $course->getTitle(),
            'description' => $courseDescription ? $courseDescription->getContent() : null,
            'code' => $course->getCode(),
            'visual_code' => $course->getVisualCode(),
            'teachers' => [],
            'item' => $item,
            'tax_name' => $globalParameters['tax_name'],
            'tax_enable' => $this->checkTaxEnabledInProduct(self::TAX_APPLIES_TO_ONLY_COURSE),
            'course_img' => null,
        ];

        $courseTeachers = $course->getTeachers();

        foreach ($courseTeachers as $teachers) {
            $user = $teachers->getUser();
            $teacher['id'] = $user->getId();
            $teacher['name'] = $user->getCompleteName();
            $courseInfo['teachers'][] = $teacher;
        }

        $possiblePath = api_get_path(SYS_COURSE_PATH);
        $possiblePath .= $course->getDirectory();
        $possiblePath .= '/course-pic.png';

        if (file_exists($possiblePath)) {
            $courseInfo['course_img'] = api_get_path(WEB_COURSE_PATH).$course->getDirectory().'/course-pic.png';
        }

        return $courseInfo;
    }

    /**
     * Get session info.
     *
     * @param array $sessionId The session ID
     *
     * @return array
     */
    public function getSubscriptionSessionInfo(int $sessionId, array $coupon = null)
    {
        $entityManager = Database::getManager();
        $session = $entityManager->find('ChamiloCoreBundle:Session', $sessionId);

        if (empty($session)) {
            return [];
        }

        $item = $this->getSubscriptionItemByProduct(
            $session->getId(),
            self::PRODUCT_TYPE_SESSION,
            $coupon
        );

        if (empty($item)) {
            return [];
        }

        $sessionDates = SessionManager::parseSessionDates(
            [
                'display_start_date' => $session->getDisplayStartDate(),
                'display_end_date' => $session->getDisplayEndDate(),
                'access_start_date' => $session->getAccessStartDate(),
                'access_end_date' => $session->getAccessEndDate(),
                'coach_access_start_date' => $session->getCoachAccessStartDate(),
                'coach_access_end_date' => $session->getCoachAccessEndDate(),
            ]
        );

        $globalParameters = $this->getGlobalParameters();
        $sessionInfo = [
            'id' => $session->getId(),
            'name' => $session->getName(),
            'description' => $session->getDescription(),
            'dates' => $sessionDates,
            'courses' => [],
            'tax_name' => $globalParameters['tax_name'],
            'tax_enable' => $this->checkTaxEnabledInProduct(self::TAX_APPLIES_TO_ONLY_SESSION),
            'image' => null,
            'nbrCourses' => $session->getNbrCourses(),
            'nbrUsers' => $session->getNbrUsers(),
            'item' => $item,
            'duration' => $session->getDuration(),
        ];

        $fieldValue = new ExtraFieldValue('session');
        $sessionImage = $fieldValue->get_values_by_handler_and_field_variable(
            $session->getId(),
            'image'
        );

        if (!empty($sessionImage)) {
            $sessionInfo['image'] = api_get_path(WEB_UPLOAD_PATH).$sessionImage['value'];
        }

        $sessionCourses = $session->getCourses();
        foreach ($sessionCourses as $sessionCourse) {
            $course = $sessionCourse->getCourse();
            $sessionCourseData = [
                'title' => $course->getTitle(),
                'coaches' => [],
            ];

            $userCourseSubscriptions = $session->getUserCourseSubscriptionsByStatus(
                $course,
                Chamilo\CoreBundle\Entity\Session::COACH
            );

            foreach ($userCourseSubscriptions as $userCourseSubscription) {
                $user = $userCourseSubscription->getUser();
                $coaches['id'] = $user->getUserId();
                $coaches['name'] = $user->getCompleteName();
                $sessionCourseData['coaches'][] = $coaches;
            }

            $sessionInfo['courses'][] = $sessionCourseData;
        }

        return $sessionInfo;
    }

    /**
     * Register a sale.
     *
     * @param int    $itemId      The product ID
     * @param int    $paymentType The payment type
     * @param string $couponId    The coupon ID
     *
     * @return bool
     */
    public function registerSale(int $itemId, int $paymentType, string $couponId = null)
    {
        if (!in_array(
                $paymentType,
                [
                    self::PAYMENT_TYPE_PAYPAL,
                    self::PAYMENT_TYPE_TRANSFER,
                    self::PAYMENT_TYPE_CULQI,
                    self::PAYMENT_TYPE_TPV_REDSYS,
                    self::PAYMENT_TYPE_STRIPE,
                    self::PAYMENT_TYPE_TPV_CECABANK,
                ]
            )
        ) {
            return false;
        }

        $entityManager = Database::getManager();
        $item = $this->getItem($itemId);

        if (empty($item)) {
            return false;
        }

        $productName = '';
        if ($item['product_type'] == self::PRODUCT_TYPE_COURSE) {
            $course = $entityManager->find('ChamiloCoreBundle:Course', $item['product_id']);

            if (empty($course)) {
                return false;
            }

            $productName = $course->getTitle();
        } elseif ($item['product_type'] == self::PRODUCT_TYPE_SESSION) {
            $session = $entityManager->find('ChamiloCoreBundle:Session', $item['product_id']);

            if (empty($session)) {
                return false;
            }

            $productName = $session->getName();
        }

        if ($couponId != null) {
            $coupon = $this->getCoupon($couponId, $item['product_type'], $item['product_id']);
        }

        $couponDiscount = 0;
        $priceWithoutDiscount = 0;
        if ($coupon != null) {
            if ($coupon['discount_type'] == self::COUPON_DISCOUNT_TYPE_AMOUNT) {
                $couponDiscount = $coupon['discount_amount'];
            } elseif ($coupon['discount_type'] == self::COUPON_DISCOUNT_TYPE_PERCENTAGE) {
                $couponDiscount = ($item['price'] * $coupon['discount_amount']) / 100;
            }
            $priceWithoutDiscount = $item['price'];
        }
        $item['price'] = $item['price'] - $couponDiscount;
        $price = $item['price'];
        $priceWithoutTax = null;
        $taxPerc = null;
        $taxAmount = 0;
        $taxEnable = $this->get('tax_enable') === 'true';
        $globalParameters = $this->getGlobalParameters();
        $taxAppliesTo = $globalParameters['tax_applies_to'];

        if ($taxEnable &&
            (
                $taxAppliesTo == self::TAX_APPLIES_TO_ALL ||
                ($taxAppliesTo == self::TAX_APPLIES_TO_ONLY_COURSE && $item['product_type'] == self::PRODUCT_TYPE_COURSE) ||
                ($taxAppliesTo == self::TAX_APPLIES_TO_ONLY_SESSION && $item['product_type'] == self::PRODUCT_TYPE_SESSION)
            )
        ) {
            $priceWithoutTax = $item['price'];
            $globalTaxPerc = $globalParameters['global_tax_perc'];
            $precision = 2;
            $taxPerc = is_null($item['tax_perc']) ? $globalTaxPerc : $item['tax_perc'];
            $taxAmount = round($priceWithoutTax * $taxPerc / 100, $precision);
            $price = $priceWithoutTax + $taxAmount;
        }

        $values = [
            'reference' => $this->generateReference(
                api_get_user_id(),
                $item['product_type'],
                $item['product_id']
            ),
            'currency_id' => $item['currency_id'],
            'date' => api_get_utc_datetime(),
            'user_id' => api_get_user_id(),
            'product_type' => $item['product_type'],
            'product_name' => $productName,
            'product_id' => $item['product_id'],
            'price' => $price,
            'price_without_tax' => $priceWithoutTax,
            'tax_perc' => $taxPerc,
            'tax_amount' => $taxAmount,
            'status' => self::SALE_STATUS_PENDING,
            'payment_type' => $paymentType,
            'price_without_discount' => $priceWithoutDiscount,
            'discount_amount' => $couponDiscount,
        ];

        return Database::insert(self::TABLE_SALE, $values);
    }

    /**
     * Update the sale reference.
     *
     * @return bool
     */
    public function updateSaleReference(int $saleId, string $saleReference)
    {
        $saleTable = Database::get_main_table(self::TABLE_SALE);

        return Database::update(
            $saleTable,
            ['reference' => $saleReference],
            ['id = ?' => $saleId]
        );
    }

    /**
     * Get sale data by ID.
     *
     * @return array
     */
    public function getSale(int $saleId)
    {
        return Database::select(
            '*',
            Database::get_main_table(self::TABLE_SALE),
            [
                'where' => ['id = ?' => (int) $saleId],
            ],
            'first'
        );
    }

    /**
     * Get sale data by reference.
     *
     * @return array
     */
    public function getSaleFromReference(string $reference)
    {
        return Database::select(
            '*',
            Database::get_main_table(self::TABLE_SALE),
            [
                'where' => ['reference = ?' => $reference],
            ],
            'first'
        );
    }

    /**
     * Get a list of sales by the payment type.
     *
     * @param int $paymentType The payment type to filter (default : Paypal)
     *
     * @return array The sale list. Otherwise return false
     */
    public function getSaleListByPaymentType(int $paymentType = self::PAYMENT_TYPE_PAYPAL)
    {
        $saleTable = Database::get_main_table(self::TABLE_SALE);
        $currencyTable = Database::get_main_table(self::TABLE_CURRENCY);
        $userTable = Database::get_main_table(TABLE_MAIN_USER);

        $innerJoins = "
            INNER JOIN $currencyTable c ON s.currency_id = c.id
            INNER JOIN $userTable u ON s.user_id = u.id
        ";

        return Database::select(
            ['c.iso_code', 'u.firstname', 'u.lastname', 's.*'],
            "$saleTable s $innerJoins",
            [
                'where' => [
                    's.payment_type = ? AND s.status = ?' => [
                        $paymentType,
                        self::SALE_STATUS_COMPLETED,
                    ],
                ],
                'order' => 'id DESC',
            ]
        );
    }

    /**
     * Get data of sales.
     *
     * @param int $saleId    The sale id
     * @param int $isService Check if a service
     *
     * @return array The sale data
     */
    public function getDataSaleInvoice(int $saleId, int $isService)
    {
        if ($isService) {
            $sale = $this->getServiceSale($saleId);
            $sale['reference'] = $sale['reference'];
            $sale['product_name'] = $sale['service']['name'];
            $sale['payment_type'] = $sale['payment_type'];
            $sale['user_id'] = $sale['buyer']['id'];
            $sale['date'] = $sale['buy_date'];
        } else {
            $sale = $this->getSale($saleId);
        }

        return $sale;
    }

    /**
     * Get data of invoice.
     *
     * @param int $saleId    The sale id
     * @param int $isService Check if a service
     *
     * @return array The invoice data
     */
    public function getDataInvoice(int $saleId, int $isService)
    {
        return Database::select(
            '*',
            Database::get_main_table(self::TABLE_INVOICE),
            [
                'where' => [
                    'sale_id = ? AND ' => (int) $saleId,
                    'is_service = ?' => (int) $isService,
                ],
            ],
            'first'
        );
    }

    /**
     * Get invoice numbering.
     *
     * @param int $saleId    The sale id
     * @param int $isService Check if a service
     *
     * @return string
     */
    public function getNumInvoice(int $saleId, int $isService)
    {
        $dataInvoice = $this->getDataInvoice($saleId, $isService);
        if (empty($dataInvoice)) {
            return '-';
        }

        return $dataInvoice['serie'].$dataInvoice['year'].'/'.$dataInvoice['num_invoice'];
    }

    /**
     * Get currency data by ID.
     *
     * @param int $currencyId The currency ID
     *
     * @return array
     */
    public function getCurrency(int $currencyId)
    {
        return Database::select(
            '*',
            Database::get_main_table(self::TABLE_CURRENCY),
            [
                'where' => ['id = ?' => $currencyId],
            ],
            'first'
        );
    }

    /**
     * Complete sale process. Update sale status to completed.
     *
     * @return bool
     */
    public function completeSale(int $saleId)
    {
        $sale = $this->getSale($saleId);

        if ($sale['status'] == self::SALE_STATUS_COMPLETED) {
            return true;
        }

        $saleIsCompleted = false;
        switch ($sale['product_type']) {
            case self::PRODUCT_TYPE_COURSE:
                $course = api_get_course_info_by_id($sale['product_id']);
                $saleIsCompleted = CourseManager::subscribeUser($sale['user_id'], $course['code']);
                break;
            case self::PRODUCT_TYPE_SESSION:
                SessionManager::subscribeUsersToSession(
                    $sale['product_id'],
                    [$sale['user_id']],
                    api_get_session_visibility($sale['product_id']),
                    false
                );

                $saleIsCompleted = true;
                break;
        }

        if ($saleIsCompleted) {
            $this->updateSaleStatus($sale['id'], self::SALE_STATUS_COMPLETED);
            if ($this->get('invoicing_enable') === 'true') {
                $this->setInvoice($sale['id']);
            }
        }

        return $saleIsCompleted;
    }

    /**
     * Update sale status to canceled.
     *
     * @param int $saleId The sale ID
     */
    public function cancelSale(int $saleId)
    {
        $this->updateSaleStatus($saleId, self::SALE_STATUS_CANCELED);
    }

    /**
     * Get payment types.
     */
    public function getPaymentTypes(bool $onlyActive = false): array
    {
        $types = [
            self::PAYMENT_TYPE_PAYPAL => 'PayPal',
            self::PAYMENT_TYPE_TRANSFER => $this->get_lang('BankTransfer'),
            self::PAYMENT_TYPE_CULQI => 'Culqi',
            self::PAYMENT_TYPE_TPV_REDSYS => $this->get_lang('TpvPayment'),
            self::PAYMENT_TYPE_STRIPE => 'Stripe',
            self::PAYMENT_TYPE_TPV_CECABANK => $this->get_lang('TpvCecabank'),
        ];

        if (!$onlyActive) {
            return $types;
        }

        if ($this->get('paypal_enable') !== 'true') {
            unset($types[BuyCoursesPlugin::PAYMENT_TYPE_PAYPAL]);
        }

        if ($this->get('transfer_enable') !== 'true') {
            unset($types[BuyCoursesPlugin::PAYMENT_TYPE_TRANSFER]);
        }

        if ($this->get('culqi_enable') !== 'true') {
            unset($types[BuyCoursesPlugin::PAYMENT_TYPE_CULQI]);
        }

        if ($this->get('tpv_redsys_enable') !== 'true'
            || !file_exists(api_get_path(SYS_PLUGIN_PATH).'buycourses/resources/apiRedsys.php')
        ) {
            unset($types[BuyCoursesPlugin::PAYMENT_TYPE_TPV_REDSYS]);
        }

        if ($this->get('stripe_enable') !== 'true') {
            unset($types[BuyCoursesPlugin::PAYMENT_TYPE_STRIPE]);
        }

        if ($this->get('cecabank_enable') !== 'true') {
            unset($types[BuyCoursesPlugin::PAYMENT_TYPE_TPV_CECABANK]);
        }

        return $types;
    }

    /**
     * Register a invoice.
     *
     * @param int $saleId    The sale ID
     * @param int $isService The service type to filter (default : 0)
     */
    public function setInvoice(int $saleId, int $isService = 0)
    {
        $invoiceTable = Database::get_main_table(self::TABLE_INVOICE);
        $year = date('Y');

        $globalParameters = $this->getGlobalParameters();
        $numInvoice = $globalParameters['next_number_invoice'];
        $serie = $globalParameters['invoice_series'];

        if (empty($numInvoice)) {
            $item = Database::select(
                ['MAX(num_invoice) AS num_invoice'],
                $invoiceTable,
                [
                    'where' => ['year = ?' => $year],
                ],
                'first'
            );

            $numInvoice = 1;
            if ($item !== false) {
                $numInvoice = (int) ($item['num_invoice'] + 1);
            }
        } else {
            Database::update(
                Database::get_main_table(self::TABLE_GLOBAL_CONFIG),
                ['next_number_invoice' => 0],
                ['id = ?' => 1]
            );
        }

        Database::insert(
            $invoiceTable,
            [
                'sale_id' => $saleId,
                'is_service' => $isService,
                'num_invoice' => $numInvoice,
                'year' => $year,
                'serie' => $serie,
                'date_invoice' => api_get_utc_datetime(),
            ]
        );

        // Record invoice in the sales table
        $table = Database::get_main_table(self::TABLE_SALE);
        if (!empty($isService)) {
            $table = Database::get_main_table(self::TABLE_SERVICES_SALE);
        }

        Database::update(
            $table,
            ['invoice' => 1],
            ['id = ?' => $saleId]
        );
    }

    /**
     * Get Tax's types.
     *
     * @return array
     */
    public function getTaxAppliesTo()
    {
        return [
            self::TAX_APPLIES_TO_ALL => $this->get_lang('AllCoursesSessionsAndServices'),
            self::TAX_APPLIES_TO_ONLY_COURSE => $this->get_lang('OnlyCourses'),
            self::TAX_APPLIES_TO_ONLY_SESSION => $this->get_lang('OnlySessions'),
            self::TAX_APPLIES_TO_ONLY_SERVICES => $this->get_lang('OnlyServices'),
        ];
    }

    /**
     * Get a list of sales by the status.
     *
     * @param int $status The status to filter
     *
     * @return array The sale list. Otherwise return false
     */
    public function getSaleListByStatus(int $status = self::SALE_STATUS_PENDING)
    {
        $saleTable = Database::get_main_table(self::TABLE_SALE);
        $currencyTable = Database::get_main_table(self::TABLE_CURRENCY);
        $userTable = Database::get_main_table(TABLE_MAIN_USER);

        $innerJoins = "
            INNER JOIN $currencyTable c ON s.currency_id = c.id
            INNER JOIN $userTable u ON s.user_id = u.id
        ";

        return Database::select(
            ['c.iso_code', 'u.firstname', 'u.lastname', 'u.email', 's.*'],
            "$saleTable s $innerJoins",
            [
                'where' => ['s.status = ?' => $status],
                'order' => 'id DESC',
            ]
        );
    }

    /**
     * Get the list statuses for sales.
     *
     * @throws Exception
     *
     * @return array
     */
    public function getSaleListReport(string $dateStart = null, string $dateEnd = null)
    {
        $saleTable = Database::get_main_table(self::TABLE_SALE);
        $currencyTable = Database::get_main_table(self::TABLE_CURRENCY);
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
        $innerJoins = "
            INNER JOIN $currencyTable c ON s.currency_id = c.id
            INNER JOIN $userTable u ON s.user_id = u.id
        ";
        $list = Database::select(
            ['c.iso_code', 'u.firstname', 'u.lastname', 'u.email', 's.*'],
            "$saleTable s $innerJoins",
            [
                'order' => 'id DESC',
            ]
        );
        $listExportTemp = [];
        $listExport = [];
        $textStatus = null;
        $paymentTypes = $this->getPaymentTypes();
        $productTypes = $this->getProductTypes();
        foreach ($list as $item) {
            $statusSaleOrder = $item['status'];
            switch ($statusSaleOrder) {
                case 0:
                    $textStatus = $this->get_lang('SaleStatusPending');
                    break;
                case 1:
                    $textStatus = $this->get_lang('SaleStatusCompleted');
                    break;
                case -1:
                    $textStatus = $this->get_lang('SaleStatusCanceled');
                    break;
            }
            $dateFilter = new DateTime($item['date']);
            $listExportTemp[] = [
                'id' => $item['id'],
                'reference' => $item['reference'],
                'status' => $textStatus,
                'status_filter' => $item['status'],
                'date' => $dateFilter->format('Y-m-d'),
                'order_time' => $dateFilter->format('H:i:s'),
                'price' => $item['iso_code'].' '.$item['price'],
                'product_type' => $productTypes[$item['product_type']],
                'product_name' => $item['product_name'],
                'payment_type' => $paymentTypes[$item['payment_type']],
                'complete_user_name' => api_get_person_name($item['firstname'], $item['lastname']),
                'email' => $item['email'],
            ];
        }
        $listExport[] = [
            get_lang('Number'),
            $this->get_lang('OrderStatus'),
            $this->get_lang('OrderDate'),
            $this->get_lang('OrderTime'),
            $this->get_lang('PaymentMethod'),
            $this->get_lang('SalePrice'),
            $this->get_lang('ProductType'),
            $this->get_lang('ProductName'),
            $this->get_lang('UserName'),
            get_lang('Email'),
        ];
        //Validation Export
        $dateStart = strtotime($dateStart);
        $dateEnd = strtotime($dateEnd);
        foreach ($listExportTemp as $item) {
            $dateFilter = strtotime($item['date']);
            if (($dateFilter >= $dateStart) && ($dateFilter <= $dateEnd)) {
                $listExport[] = [
                    'id' => $item['id'],
                    'status' => $item['status'],
                    'date' => $item['date'],
                    'order_time' => $item['order_time'],
                    'payment_type' => $item['payment_type'],
                    'price' => $item['price'],
                    'product_type' => $item['product_type'],
                    'product_name' => $item['product_name'],
                    'complete_user_name' => $item['complete_user_name'],
                    'email' => $item['email'],
                ];
            }
        }

        return $listExport;
    }

    /**
     * Get the statuses for sales.
     *
     * @return array
     */
    public function getSaleStatuses()
    {
        return [
            self::SALE_STATUS_CANCELED => $this->get_lang('SaleStatusCanceled'),
            self::SALE_STATUS_PENDING => $this->get_lang('SaleStatusPending'),
            self::SALE_STATUS_COMPLETED => $this->get_lang('SaleStatusCompleted'),
        ];
    }

    /**
     * Get the statuses for Payouts.
     *
     * @return array
     */
    public function getPayoutStatuses()
    {
        return [
            self::PAYOUT_STATUS_CANCELED => $this->get_lang('PayoutStatusCanceled'),
            self::PAYOUT_STATUS_PENDING => $this->get_lang('PayoutStatusPending'),
            self::PAYOUT_STATUS_COMPLETED => $this->get_lang('PayoutStatusCompleted'),
        ];
    }

    /**
     * Get the list of product types.
     *
     * @return array
     */
    public function getProductTypes()
    {
        return [
            self::PRODUCT_TYPE_COURSE => get_lang('Course'),
            self::PRODUCT_TYPE_SESSION => get_lang('Session'),
        ];
    }

    /**
     * Get the list of service types.
     *
     * @return array
     */
    public function getServiceTypes()
    {
        return [
            self::SERVICE_TYPE_USER => get_lang('User'),
            self::SERVICE_TYPE_COURSE => get_lang('Course'),
            self::SERVICE_TYPE_SESSION => get_lang('Session'),
            self::SERVICE_TYPE_LP_FINAL_ITEM => get_lang('TemplateTitleCertificate'),
        ];
    }

    /**
     * Get the list of coupon status.
     *
     * @return array
     */
    public function getCouponStatuses()
    {
        return [
            self::COUPON_STATUS_ACTIVE => $this->get_lang('CouponActive'),
            self::COUPON_STATUS_DISABLE => $this->get_lang('CouponDisabled'),
        ];
    }

    /**
     * Get the list of coupon discount types.
     *
     * @return array
     */
    public function getCouponDiscountTypes()
    {
        return [
            self::COUPON_DISCOUNT_TYPE_PERCENTAGE => $this->get_lang('CouponPercentage'),
            self::COUPON_DISCOUNT_TYPE_AMOUNT => $this->get_lang('CouponAmount'),
        ];
    }

    /**
     * Generates a random text (used for order references).
     *
     * @param int  $length    Optional. Length of characters (defaults to 6)
     * @param bool $lowercase Optional. Include lowercase characters
     * @param bool $uppercase Optional. Include uppercase characters
     * @param bool $numbers   Optional. Include numbers
     */
    public static function randomText(
        int $length = 6,
        bool $lowercase = true,
        bool $uppercase = true,
        bool $numbers = true
    ): string {
        $salt = $lowercase ? 'abchefghknpqrstuvwxyz' : '';
        $salt .= $uppercase ? 'ACDEFHKNPRSTUVWXYZ' : '';
        $salt .= $numbers ? (strlen($salt) ? '2345679' : '0123456789') : '';

        if (strlen($salt) == 0) {
            return '';
        }

        $str = '';

        srand((float) microtime() * 1000000);

        for ($i = 0; $i < $length; $i++) {
            $numbers = rand(0, strlen($salt) - 1);
            $str .= substr($salt, $numbers, 1);
        }

        return $str;
    }

    /**
     * Generates an order reference.
     *
     * @param int $userId      The user ID
     * @param int $productType The course/session type
     * @param int $productId   The course/session ID
     */
    public function generateReference(int $userId, int $productType, int $productId): string
    {
        return vsprintf(
            '%d-%d-%d-%s',
            [$userId, $productType, $productId, self::randomText()]
        );
    }

    /**
     * Get a list of sales by the user.
     *
     * @param string $term The search term
     *
     * @return array The sale list. Otherwise return false
     */
    public function getSaleListByUser(string $term)
    {
        $term = trim($term);

        if (empty($term)) {
            return [];
        }

        $saleTable = Database::get_main_table(self::TABLE_SALE);
        $currencyTable = Database::get_main_table(self::TABLE_CURRENCY);
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
        $innerJoins = "
            INNER JOIN $currencyTable c ON s.currency_id = c.id
            INNER JOIN $userTable u ON s.user_id = u.id
        ";

        return Database::select(
            ['c.iso_code', 'u.firstname', 'u.lastname', 'u.email', 's.*'],
            "$saleTable s $innerJoins",
            [
                'where' => [
                    'u.username LIKE %?% OR ' => $term,
                    'u.lastname LIKE %?% OR ' => $term,
                    'u.firstname LIKE %?%' => $term,
                ],
                'order' => 'id DESC',
            ]
        );
    }

    /**
     * Get a list of sales by the user id.
     *
     * @param int $id The user id
     *
     * @return array The sale list. Otherwise return false
     */
    public function getSaleListByUserId(int $id)
    {
        if (empty($id)) {
            return [];
        }

        $saleTable = Database::get_main_table(self::TABLE_SALE);
        $currencyTable = Database::get_main_table(self::TABLE_CURRENCY);
        $userTable = Database::get_main_table(TABLE_MAIN_USER);

        $innerJoins = "
            INNER JOIN $currencyTable c ON s.currency_id = c.id
            INNER JOIN $userTable u ON s.user_id = u.id
        ";

        return Database::select(
            ['c.iso_code', 'u.firstname', 'u.lastname', 's.*'],
            "$saleTable s $innerJoins",
            [
                'where' => [
                    'u.id = ? AND s.status = ?' => [(int) $id, self::SALE_STATUS_COMPLETED],
                ],
                'order' => 'id DESC',
            ]
        );
    }

    /**
     * Get a list of sales by date range.
     *
     * @return array The sale list. Otherwise return false
     */
    public function getSaleListByDate(string $dateStart, string $dateEnd)
    {
        $dateStart = trim($dateStart);
        $dateEnd = trim($dateEnd);
        if (empty($dateStart)) {
            return [];
        }
        if (empty($dateEnd)) {
            return [];
        }
        $saleTable = Database::get_main_table(self::TABLE_SALE);
        $currencyTable = Database::get_main_table(self::TABLE_CURRENCY);
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
        $innerJoins = "
            INNER JOIN $currencyTable c ON s.currency_id = c.id
            INNER JOIN $userTable u ON s.user_id = u.id
        ";

        return Database::select(
            ['c.iso_code', 'u.firstname', 'u.lastname', 'u.email', 's.*'],
            "$saleTable s $innerJoins",
            [
                'where' => [
                    's.date BETWEEN ? AND ' => $dateStart,
                    ' ? ' => $dateEnd,
                ],
                'order' => 'id DESC',
            ]
        );
    }

    /**
     * Get a list of sales by the user Email.
     *
     * @param string $term The search term
     *
     * @return array The sale list. Otherwise return false
     */
    public function getSaleListByEmail(string $term)
    {
        $term = trim($term);
        if (empty($term)) {
            return [];
        }
        $saleTable = Database::get_main_table(self::TABLE_SALE);
        $currencyTable = Database::get_main_table(self::TABLE_CURRENCY);
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
        $innerJoins = "
            INNER JOIN $currencyTable c ON s.currency_id = c.id
            INNER JOIN $userTable u ON s.user_id = u.id
        ";

        return Database::select(
            ['c.iso_code', 'u.firstname', 'u.lastname', 'u.email', 's.*'],
            "$saleTable s $innerJoins",
            [
                'where' => [
                    'u.email LIKE %?% ' => $term,
                ],
                'order' => 'id DESC',
            ]
        );
    }

    /**
     * Convert the course info to array with necessary course data for save item.
     *
     * @param array $defaultCurrency Optional. Currency data
     *
     * @return array
     */
    public function getCourseForConfiguration(Course $course, array $defaultCurrency = null)
    {
        $courseItem = [
            'item_id' => null,
            'course_id' => $course->getId(),
            'course_visual_code' => $course->getVisualCode(),
            'course_code' => $course->getCode(),
            'course_title' => $course->getTitle(),
            'course_directory' => $course->getDirectory(),
            'course_visibility' => $course->getVisibility(),
            'visible' => false,
            'currency' => empty($defaultCurrency) ? null : $defaultCurrency['iso_code'],
            'price' => 0.00,
            'tax_perc' => null,
        ];

        $item = $this->getItemByProduct($course->getId(), self::PRODUCT_TYPE_COURSE);

        if ($item !== false) {
            $courseItem['item_id'] = $item['id'];
            $courseItem['visible'] = true;
            $courseItem['currency'] = $item['iso_code'];
            $courseItem['price'] = $item['price'];
            $courseItem['tax_perc'] = $item['tax_perc'];
        }

        return $courseItem;
    }

    /**
     * Convert the session info to array with necessary session data for save item.
     *
     * @param Session $session         The session data
     * @param array   $defaultCurrency Optional. Currency data
     *
     * @return array
     */
    public function getSessionForConfiguration(Session $session, array $defaultCurrency = null)
    {
        $buyItemTable = Database::get_main_table(self::TABLE_ITEM);
        $buyCurrencyTable = Database::get_main_table(self::TABLE_CURRENCY);

        $fakeItemFrom = "
            $buyItemTable i
            INNER JOIN $buyCurrencyTable c ON i.currency_id = c.id
        ";

        $sessionItem = [
            'item_id' => null,
            'session_id' => $session->getId(),
            'session_name' => $session->getName(),
            'session_visibility' => $session->getVisibility(),
            'session_display_start_date' => null,
            'session_display_end_date' => null,
            'visible' => false,
            'currency' => empty($defaultCurrency) ? null : $defaultCurrency['iso_code'],
            'price' => 0.00,
            'tax_perc' => null,
        ];

        $displayStartDate = $session->getDisplayStartDate();

        if (!empty($displayStartDate)) {
            $sessionItem['session_display_start_date'] = api_format_date(
                $session->getDisplayStartDate()->format('Y-m-d h:i:s')
            );
        }

        $displayEndDate = $session->getDisplayEndDate();

        if (!empty($displayEndDate)) {
            $sessionItem['session_display_end_date'] = api_format_date(
                $session->getDisplayEndDate()->format('Y-m-d h:i:s'),
                DATE_TIME_FORMAT_LONG_24H
            );
        }

        $item = Database::select(
            ['i.*', 'c.iso_code'],
            $fakeItemFrom,
            [
                'where' => [
                    'i.product_id = ? AND ' => $session->getId(),
                    'i.product_type = ?' => self::PRODUCT_TYPE_SESSION,
                ],
            ],
            'first'
        );

        if ($item !== false) {
            $sessionItem['item_id'] = $item['id'];
            $sessionItem['visible'] = true;
            $sessionItem['currency'] = $item['iso_code'];
            $sessionItem['price'] = $item['price'];
            $sessionItem['tax_perc'] = $item['tax_perc'];
        }

        return $sessionItem;
    }

    /**
     * Get all beneficiaries for a item.
     *
     * @param int $itemId The item ID
     *
     * @return array The beneficiaries. Otherwise return false
     */
    public function getItemBeneficiaries(int $itemId)
    {
        $beneficiaryTable = Database::get_main_table(self::TABLE_ITEM_BENEFICIARY);

        return Database::select(
            '*',
            $beneficiaryTable,
            [
                'where' => [
                    'item_id = ?' => $itemId,
                ],
            ]
        );
    }

    /**
     * Delete a item with its beneficiaries.
     *
     * @param int $itemId The item ID
     *
     * @return int The number of affected rows. Otherwise return false
     */
    public function deleteItem(int $itemId)
    {
        $itemTable = Database::get_main_table(self::TABLE_ITEM);
        $affectedRows = Database::delete(
            $itemTable,
            ['id = ?' => $itemId]
        );

        if (!$affectedRows) {
            return false;
        }

        return $this->deleteItemBeneficiaries($itemId);
    }

    /**
     * Register a item.
     *
     * @param array $itemData The item data
     *
     * @return int The item ID. Otherwise return false
     */
    public function registerItem(array $itemData)
    {
        $itemTable = Database::get_main_table(self::TABLE_ITEM);

        return Database::insert($itemTable, $itemData);
    }

    /**
     * Update the item data by product.
     *
     * @param array $itemData    The item data to be updated
     * @param int   $productId   The product ID
     * @param int   $productType The type of product
     *
     * @return int The number of affected rows. Otherwise return false
     */
    public function updateItem(array $itemData, int $productId, int $productType)
    {
        $itemTable = Database::get_main_table(self::TABLE_ITEM);

        return Database::update(
            $itemTable,
            $itemData,
            [
                'product_id = ? AND ' => $productId,
                'product_type' => $productType,
            ]
        );
    }

    /**
     * Remove all beneficiaries for a item.
     *
     * @param int $itemId The user ID
     *
     * @return int The number of affected rows. Otherwise return false
     */
    public function deleteItemBeneficiaries(int $itemId)
    {
        $beneficiaryTable = Database::get_main_table(self::TABLE_ITEM_BENEFICIARY);

        return Database::delete(
            $beneficiaryTable,
            ['item_id = ?' => $itemId]
        );
    }

    /**
     * Register the beneficiaries users with the sale of item.
     *
     * @param int   $itemId  The item ID
     * @param array $userIds The beneficiary user ID and Teachers commissions if enabled
     */
    public function registerItemBeneficiaries(int $itemId, array $userIds)
    {
        $beneficiaryTable = Database::get_main_table(self::TABLE_ITEM_BENEFICIARY);

        $this->deleteItemBeneficiaries($itemId);

        foreach ($userIds as $userId => $commissions) {
            Database::insert(
                $beneficiaryTable,
                [
                    'item_id' => $itemId,
                    'user_id' => (int) $userId,
                    'commissions' => (int) $commissions,
                ]
            );
        }
    }

    /**
     * Check if a course is valid for sale.
     *
     * @param Course $course The course
     *
     * @return bool
     */
    public function isValidCourse(Course $course)
    {
        $courses = $this->getCourses();

        foreach ($courses as $_c) {
            if ($_c->getCode() === $course->getCode()) {
                return true;
            }
        }

        return false;
    }

    /**
     * Gets the beneficiaries with commissions and current paypal accounts by sale.
     *
     * @param int $saleId The sale ID
     *
     * @return array
     */
    public function getBeneficiariesBySale(int $saleId)
    {
        $sale = $this->getSale($saleId);
        $item = $this->getItemByProduct($sale['product_id'], $sale['product_type']);
        $itemBeneficiaries = $this->getItemBeneficiaries($item['id']);

        return $itemBeneficiaries;
    }

    /**
     * gets all payouts.
     *
     * @param int $status   - default 0 - pending
     * @param int $payoutId - for get an individual payout if want all then false
     *
     * @return array
     */
    public function getPayouts(
        int $status = self::PAYOUT_STATUS_PENDING,
        int $payoutId = 0,
        int $userId = 0
    ) {
        $condition = ($payoutId) ? 'AND p.id = '.($payoutId) : '';
        $condition2 = ($userId) ? ' AND p.user_id = '.($userId) : '';
        $typeResult = ($condition) ? 'first' : 'all';
        $payoutsTable = Database::get_main_table(self::TABLE_PAYPAL_PAYOUTS);
        $saleTable = Database::get_main_table(self::TABLE_SALE);
        $currencyTable = Database::get_main_table(self::TABLE_CURRENCY);
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
        $extraFieldTable = Database::get_main_table(TABLE_EXTRA_FIELD);
        $extraFieldValues = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);

        $paypalExtraField = Database::select(
            "*",
            $extraFieldTable,
            [
                'where' => ['variable = ?' => 'paypal'],
            ],
            'first'
        );

        if (!$paypalExtraField) {
            return false;
        }

        $innerJoins = "
            INNER JOIN $userTable u ON p.user_id = u.id
            INNER JOIN $saleTable s ON s.id = p.sale_id
            INNER JOIN $currencyTable c ON s.currency_id = c.id
            LEFT JOIN  $extraFieldValues efv ON p.user_id = efv.item_id
            AND field_id = ".((int) $paypalExtraField['id'])."
        ";

        $payouts = Database::select(
            "p.* , u.firstname, u.lastname, efv.value as paypal_account, s.reference as sale_reference, s.price as item_price, c.iso_code",
            "$payoutsTable p $innerJoins",
            [
                'where' => ['p.status = ? '.$condition.' '.$condition2 => $status],
            ],
            $typeResult
        );

        return $payouts;
    }

    /**
     * Verify if the beneficiary have a paypal account.
     *
     * @return true if the user have a paypal account, false if not
     */
    public function verifyPaypalAccountByBeneficiary(int $userId)
    {
        $extraFieldTable = Database::get_main_table(TABLE_EXTRA_FIELD);
        $extraFieldValues = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);

        $paypalExtraField = Database::select(
            '*',
            $extraFieldTable,
            [
                'where' => ['variable = ?' => 'paypal'],
            ],
            'first'
        );

        if (!$paypalExtraField) {
            return false;
        }

        $paypalFieldId = $paypalExtraField['id'];
        $paypalAccount = Database::select(
            'value',
            $extraFieldValues,
            [
                'where' => ['field_id = ? AND item_id = ?' => [(int) $paypalFieldId, $userId]],
            ],
            'first'
        );

        if (!$paypalAccount) {
            return false;
        }

        if ($paypalAccount['value'] === '') {
            return false;
        }

        return true;
    }

    /**
     * Register the users payouts.
     *
     * @return array
     */
    public function storePayouts(int $saleId)
    {
        $payoutsTable = Database::get_main_table(self::TABLE_PAYPAL_PAYOUTS);
        $platformCommission = $this->getPlatformCommission();

        $sale = $this->getSale($saleId);
        $commission = (int) $platformCommission['commission'];
        $teachersCommission = number_format(
            (floatval($sale['price']) * $commission) / 100,
            2
        );

        $beneficiaries = $this->getBeneficiariesBySale($saleId);
        foreach ($beneficiaries as $beneficiary) {
            $beneficiaryCommission = (int) $beneficiary['commissions'];
            Database::insert(
                $payoutsTable,
                [
                    'date' => $sale['date'],
                    'payout_date' => api_get_utc_datetime(),
                    'sale_id' => $saleId,
                    'user_id' => $beneficiary['user_id'],
                    'commission' => number_format(
                        (floatval($teachersCommission) * $beneficiaryCommission) / 100,
                        2
                    ),
                    'status' => self::PAYOUT_STATUS_PENDING,
                ]
            );
        }
    }

    /**
     * Register the users payouts.
     *
     * @param int $saleId The subscription sale ID
     *
     * @return array
     */
    public function storeSubscriptionPayouts(int $saleId)
    {
        $payoutsTable = Database::get_main_table(self::TABLE_PAYPAL_PAYOUTS);
        $platformCommission = $this->getPlatformCommission();

        $sale = $this->getSubscriptionSale($saleId);
        $commission = (int) $platformCommission['commission'];
        $teachersCommission = number_format(
            (floatval($sale['price']) * $commission) / 100,
            2
        );

        $beneficiaries = $this->getBeneficiariesBySale($saleId);
        foreach ($beneficiaries as $beneficiary) {
            $beneficiaryCommission = (int) $beneficiary['commissions'];
            Database::insert(
                $payoutsTable,
                [
                    'date' => $sale['date'],
                    'payout_date' => api_get_utc_datetime(),
                    'sale_id' => $saleId,
                    'user_id' => $beneficiary['user_id'],
                    'commission' => number_format(
                        (floatval($teachersCommission) * $beneficiaryCommission) / 100,
                        2
                    ),
                    'status' => self::PAYOUT_STATUS_PENDING,
                ]
            );
        }
    }

    /**
     * Register the users payouts.
     *
     * @param int $payoutId The payout ID
     * @param int $status   The status to set (-1 to cancel, 0 to pending, 1 to completed)
     *
     * @return array
     */
    public function setStatusPayouts(int $payoutId, int $status)
    {
        $payoutsTable = Database::get_main_table(self::TABLE_PAYPAL_PAYOUTS);

        Database::update(
            $payoutsTable,
            ['status' => (int) $status],
            ['id = ?' => (int) $payoutId]
        );
    }

    /**
     * Gets the stored platform commission params.
     *
     * @return array
     */
    public function getPlatformCommission()
    {
        return Database::select(
            '*',
            Database::get_main_table(self::TABLE_COMMISSION),
            ['id = ?' => 1],
            'first'
        );
    }

    /**
     * Update the platform commission.
     *
     * @param array $params platform commission
     *
     * @return int The number of affected rows. Otherwise return false
     */
    public function updateCommission(array $params)
    {
        $commissionTable = Database::get_main_table(self::TABLE_COMMISSION);

        return Database::update(
            $commissionTable,
            ['commission' => (int) $params['commission']]
        );
    }

    /**
     * Register additional service.
     *
     * @param array $service params
     *
     * @return mixed response
     */
    public function storeService(array $service)
    {
        $servicesTable = Database::get_main_table(self::TABLE_SERVICES);

        $return = Database::insert(
            $servicesTable,
            [
                'name' => Security::remove_XSS($service['name']),
                'description' => Security::remove_XSS($service['description']),
                'price' => $service['price'],
                'tax_perc' => $service['tax_perc'] != '' ? (int) $service['tax_perc'] : null,
                'duration_days' => (int) $service['duration_days'],
                'applies_to' => (int) $service['applies_to'],
                'owner_id' => (int) $service['owner_id'],
                'visibility' => (int) $service['visibility'],
                'image' => '',
                'video_url' => $service['video_url'],
                'service_information' => $service['service_information'],
            ]
        );

        if ($return && !empty($service['picture_crop_image_base_64']) &&
            !empty($service['picture_crop_result'])
        ) {
            $img = str_replace('data:image/png;base64,', '', $service['picture_crop_image_base_64']);
            $img = str_replace(' ', '+', $img);
            $data = base64_decode($img);
            $file = api_get_path(SYS_PLUGIN_PATH).'buycourses/uploads/services/images/simg-'.$return.'.png';
            file_put_contents($file, $data);

            Database::update(
                $servicesTable,
                ['image' => 'simg-'.$return.'.png'],
                ['id = ?' => $return]
            );
        }

        return $return;
    }

    /**
     * update a service.
     *
     * @return mixed response
     */
    public function updateService(array $service, int $id)
    {
        $servicesTable = Database::get_main_table(self::TABLE_SERVICES);
        if (!empty($service['picture_crop_image_base_64'])) {
            $img = str_replace('data:image/png;base64,', '', $service['picture_crop_image_base_64']);
            $img = str_replace(' ', '+', $img);
            $data = base64_decode($img);
            $file = api_get_path(SYS_PLUGIN_PATH).'buycourses/uploads/services/images/simg-'.$id.'.png';
            file_put_contents($file, $data);
        }

        return Database::update(
            $servicesTable,
            [
                'name' => Security::remove_XSS($service['name']),
                'description' => Security::remove_XSS($service['description']),
                'price' => $service['price'],
                'tax_perc' => $service['tax_perc'] != '' ? (int) $service['tax_perc'] : null,
                'duration_days' => (int) $service['duration_days'],
                'applies_to' => (int) $service['applies_to'],
                'owner_id' => (int) $service['owner_id'],
                'visibility' => (int) $service['visibility'],
                'image' => 'simg-'.$id.'.png',
                'video_url' => $service['video_url'],
                'service_information' => $service['service_information'],
            ],
            ['id = ?' => $id]
        );
    }

    /**
     * Remove a service.
     *
     * @param int $id The transfer account ID
     *
     * @return int Rows affected. Otherwise return false
     */
    public function deleteService(int $id)
    {
        Database::delete(
            Database::get_main_table(self::TABLE_SERVICES_SALE),
            ['service_id = ?' => $id]
        );

        return Database::delete(
            Database::get_main_table(self::TABLE_SERVICES),
            ['id = ?' => $id]
        );
    }

    /**
     * @param array|null $coupon Array with at least 'discount_type' and 'discount_amount' elements
     */
    public function setPriceSettings(array &$product, int $productType, array $coupon = null): bool
    {
        if (empty($product)) {
            return false;
        }

        $taxPerc = null;
        $product['has_coupon'] = $coupon != null ? true : false;
        $couponDiscount = 0;
        if ($coupon != null) {
            if ($coupon['discount_type'] == self::COUPON_DISCOUNT_TYPE_AMOUNT) {
                $couponDiscount = $coupon['discount_amount'];
            } elseif ($coupon['discount_type'] == self::COUPON_DISCOUNT_TYPE_PERCENTAGE) {
                $couponDiscount = ($product['price'] * $coupon['discount_amount']) / 100;
            }
            $product['price_without_discount'] = $product['price'];
        }
        $product['discount_amount'] = $couponDiscount;
        $product['price'] = $product['price'] - $couponDiscount;
        $priceWithoutTax = $product['price'];
        $product['total_price'] = $product['price'];
        $product['tax_amount'] = 0;

        if ($this->checkTaxEnabledInProduct($productType)) {
            if (is_null($product['tax_perc'])) {
                $globalParameters = $this->getGlobalParameters();
                $globalTaxPerc = $globalParameters['global_tax_perc'];
                $taxPerc = $globalTaxPerc;
            } else {
                $taxPerc = $product['tax_perc'];
            }
            //$taxPerc = is_null($product['tax_perc']) ? $globalTaxPerc : $product['tax_perc'];

            $taxAmount = round($priceWithoutTax * $taxPerc / 100, 2);
            $product['tax_amount'] = $taxAmount;
            $priceWithTax = $priceWithoutTax + $taxAmount;
            $product['total_price'] = $priceWithTax;
        }

        $product['tax_perc_show'] = $taxPerc;
        $product['price_formatted'] = $this->getPriceWithCurrencyFromIsoCode(
            $product['price'],
            $product['iso_code']
        );

        $product['tax_amount_formatted'] = number_format($product['tax_amount'], 2);

        $product['total_price_formatted'] = $this->getPriceWithCurrencyFromIsoCode(
            $product['total_price'],
            $product['iso_code']
        );

        if ($coupon != null) {
            $product['discount_amount_formatted'] = $this->getPriceWithCurrencyFromIsoCode(
                $product['discount_amount'],
                $product['iso_code']
            );

            $product['price_without_discount_formatted'] = $this->getPriceWithCurrencyFromIsoCode(
                $product['price_without_discount'],
                $product['iso_code']
            );
        }

        return true;
    }

    /**
     * @param array $coupon
     *
     * @return array
     */
    public function getService(int $id, array $coupon = null)
    {
        if (empty($id)) {
            return [];
        }

        $servicesTable = Database::get_main_table(self::TABLE_SERVICES);
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
        $conditions = ['WHERE' => ['s.id = ?' => $id]];
        $showData = 'first';
        $innerJoins = "INNER JOIN $userTable u ON s.owner_id = u.id";
        $currency = $this->getSelectedCurrency();
        $isoCode = $currency['iso_code'];
        $service = Database::select(
            "s.*, '$isoCode' as currency, u.firstname, u.lastname",
            "$servicesTable s $innerJoins",
            $conditions,
            $showData
        );

        $service['iso_code'] = $isoCode;
        $globalParameters = $this->getGlobalParameters();

        $this->setPriceSettings($service, self::TAX_APPLIES_TO_ONLY_SERVICES, $coupon);

        $service['tax_name'] = $globalParameters['tax_name'];
        $service['tax_enable'] = $this->checkTaxEnabledInProduct(self::TAX_APPLIES_TO_ONLY_SERVICES);
        $service['owner_name'] = api_get_person_name($service['firstname'], $service['lastname']);
        $service['image'] = !empty($service['image']) ? api_get_path(WEB_PLUGIN_PATH).'buycourses/uploads/services/images/'.$service['image'] : null;

        return $service;
    }

    /**
     * List additional services.
     *
     * @return array
     */
    public function getAllServices()
    {
        $servicesTable = Database::get_main_table(self::TABLE_SERVICES);
        $userTable = Database::get_main_table(TABLE_MAIN_USER);

        $innerJoins = "INNER JOIN $userTable u ON s.owner_id = u.id";
        $return = Database::select(
            's.id',
            "$servicesTable s $innerJoins",
            [],
            'all'
        );

        $services = [];
        foreach ($return as $index => $service) {
            $services[$index] = $this->getService($service['id']);
        }

        return $services;
    }

    /**
     * List additional services.
     *
     * @return array|int
     */
    public function getServices(int $start, int $end, string $typeResult = 'all')
    {
        $servicesTable = Database::get_main_table(self::TABLE_SERVICES);
        $userTable = Database::get_main_table(TABLE_MAIN_USER);

        $conditions = ['limit' => "$start, $end"];
        $innerJoins = "INNER JOIN $userTable u ON s.owner_id = u.id";
        $return = Database::select(
            's.id',
            "$servicesTable s $innerJoins",
            $conditions,
            $typeResult
        );

        if ($typeResult === 'count') {
            return $return;
        }

        $services = [];
        foreach ($return as $index => $service) {
            $services[$index] = $this->getService($service['id']);
        }

        return $services;
    }

    /**
     * Get the statuses for sales.
     *
     * @return array
     */
    public function getServiceSaleStatuses()
    {
        return [
            self::SERVICE_STATUS_CANCELLED => $this->get_lang('SaleStatusCancelled'),
            self::SERVICE_STATUS_PENDING => $this->get_lang('SaleStatusPending'),
            self::SERVICE_STATUS_COMPLETED => $this->get_lang('SaleStatusCompleted'),
        ];
    }

    /**
     * List services sales.
     *
     * @param int $buyerId  buyer id
     * @param int $status   status
     * @param int $nodeType The node Type ( User = 1 , Course = 2 , Session = 3 )
     * @param int $nodeId   the nodeId
     *
     * @return array
     */
    public function getServiceSales(
        int $buyerId = 0,
        int $status = 0,
        int $nodeType = 0,
        int $nodeId = 0
    ) {
        $conditions = null;
        $groupBy = '';

        $servicesTable = Database::get_main_table(self::TABLE_SERVICES);
        $servicesSaleTable = Database::get_main_table(self::TABLE_SERVICES_SALE);

        $defaultOrder = 'id ASC';

        if (!empty($buyerId)) {
            $conditions = ['WHERE' => ['ss.buyer_id = ?' => $buyerId], 'ORDER' => $defaultOrder];
        }

        if (is_numeric($status)) {
            $conditions = ['WHERE' => ['ss.status = ?' => $status], 'ORDER' => $defaultOrder];
        }

        if ($buyerId) {
            $conditions = ['WHERE' => ['ss.buyer_id = ?' => [$buyerId]], 'ORDER' => $defaultOrder];
        }

        if ($nodeType && $nodeId) {
            $conditions = [
                'WHERE' => ['ss.node_type = ? AND ss.node_id = ?' => [$nodeType, $nodeId]],
                'ORDER' => $defaultOrder,
            ];
        }

        if ($nodeType && $nodeId && $buyerId && is_numeric($status)) {
            $conditions = [
                'WHERE' => [
                    'ss.node_type = ? AND ss.node_id = ? AND ss.buyer_id = ? AND ss.status = ?' => [
                        $nodeType,
                        $nodeId,
                        $buyerId,
                        $status,
                    ],
                ],
                'ORDER' => $defaultOrder,
            ];
        }

        $innerJoins = "INNER JOIN $servicesTable s ON ss.service_id = s.id $groupBy";
        $return = Database::select(
            'DISTINCT ss.id ',
            "$servicesSaleTable ss $innerJoins",
            $conditions
            //, "all", null, true
        );

        $list = [];
        foreach ($return as $service) {
            $list[] = $this->getServiceSale($service['id']);
        }

        return $list;
    }

    /**
     * @param int $id service sale id
     *
     * @return array
     */
    public function getServiceSale(int $id)
    {
        $servicesTable = Database::get_main_table(self::TABLE_SERVICES);
        $servicesSaleTable = Database::get_main_table(self::TABLE_SERVICES_SALE);

        if (empty($id)) {
            return [];
        }

        $conditions = ['WHERE' => ['ss.id = ?' => $id]];
        $innerJoins = "INNER JOIN $servicesTable s ON ss.service_id = s.id ";
        $currency = $this->getSelectedCurrency();
        $isoCode = $currency['iso_code'];

        $servicesSale = Database::select(
            'ss.*, s.name, s.description, s.price as service_price, s.duration_days, s.applies_to, s.owner_id, s.visibility, s.image',
            "$servicesSaleTable ss $innerJoins",
            $conditions,
            'first'
        );
        $owner = api_get_user_info($servicesSale['owner_id']);
        $buyer = api_get_user_info($servicesSale['buyer_id']);

        $servicesSale['service']['id'] = $servicesSale['service_id'];
        $servicesSale['service']['name'] = $servicesSale['name'];
        $servicesSale['service']['description'] = $servicesSale['description'];
        $servicesSale['service']['price'] = $servicesSale['service_price'];
        $servicesSale['service']['currency'] = $isoCode;

        $servicesSale['service']['total_price'] = $this->getPriceWithCurrencyFromIsoCode(
            $servicesSale['price'],
            $isoCode
        );

        $servicesSale['service']['duration_days'] = $servicesSale['duration_days'];
        $servicesSale['service']['applies_to'] = $servicesSale['applies_to'];
        $servicesSale['service']['owner']['id'] = $servicesSale['owner_id'];
        $servicesSale['service']['owner']['name'] = api_get_person_name($owner['firstname'], $owner['lastname']);
        $servicesSale['service']['visibility'] = $servicesSale['visibility'];
        $servicesSale['service']['image'] = $servicesSale['image'];
        $servicesSale['item'] = $this->getService($servicesSale['service_id']);
        $servicesSale['buyer']['id'] = $buyer['user_id'];
        $servicesSale['buyer']['name'] = api_get_person_name($buyer['firstname'], $buyer['lastname']);
        $servicesSale['buyer']['username'] = $buyer['username'];

        return $servicesSale;
    }

    /**
     * Update service sale status to cancelled.
     *
     * @param int $serviceSaleId The sale ID
     *
     * @return bool
     */
    public function cancelServiceSale(int $serviceSaleId)
    {
        $this->updateServiceSaleStatus(
            $serviceSaleId,
            self::SERVICE_STATUS_CANCELLED
        );

        return true;
    }

    /**
     * Complete service sale process. Update service sale status to completed.
     *
     * @param int $serviceSaleId The service sale ID
     *
     * @return bool
     */
    public function completeServiceSale(int $serviceSaleId)
    {
        $serviceSale = $this->getServiceSale($serviceSaleId);
        if ($serviceSale['status'] == self::SERVICE_STATUS_COMPLETED) {
            return true;
        }

        $this->updateServiceSaleStatus(
            $serviceSaleId,
            self::SERVICE_STATUS_COMPLETED
        );

        if ($this->get('invoicing_enable') === 'true') {
            $this->setInvoice($serviceSaleId, 1);
        }

        return true;
    }

    /**
     * Lists current service details.
     *
     * @return array|int
     */
    public function getCatalogServiceList(
        int $start,
        int $end,
        string $name = null,
        int $min = 0,
        int $max = 0,
        $appliesTo = '',
        string $typeResult = 'all'
    ) {
        $servicesTable = Database::get_main_table(self::TABLE_SERVICES);
        $userTable = Database::get_main_table(TABLE_MAIN_USER);

        $whereConditions = [
            's.visibility <> ? ' => 0,
        ];

        if (!empty($name)) {
            $whereConditions['AND s.name LIKE %?%'] = $name;
        }

        if (!empty($min)) {
            $whereConditions['AND s.price >= ?'] = $min;
        }

        if (!empty($max)) {
            $whereConditions['AND s.price <= ?'] = $max;
        }

        if (!$appliesTo == '') {
            $whereConditions['AND s.applies_to = ?'] = $appliesTo;
        }

        $innerJoins = "INNER JOIN $userTable u ON s.owner_id = u.id";
        $return = Database::select(
            's.*',
            "$servicesTable s $innerJoins",
            ['WHERE' => $whereConditions, 'limit' => "$start, $end"],
            $typeResult
        );

        if ($typeResult === 'count') {
            return $return;
        }

        $services = [];
        foreach ($return as $index => $service) {
            $services[$index] = $this->getService($service['id']);
        }

        return $services;
    }

    /**
     * Register a Service sale.
     *
     * @param int $serviceId   The service ID
     * @param int $paymentType The payment type
     * @param int $infoSelect  The ID for Service Type
     *
     * @return bool
     */
    public function registerServiceSale(int $serviceId, int $paymentType, int $infoSelect, int $couponId = null)
    {
        if (!in_array(
            $paymentType,
            [self::PAYMENT_TYPE_PAYPAL, self::PAYMENT_TYPE_TRANSFER, self::PAYMENT_TYPE_CULQI]
        )
        ) {
            return false;
        }

        $userId = api_get_user_id();
        $service = $this->getService($serviceId);

        if (empty($service)) {
            return false;
        }

        if ($couponId != null) {
            $coupon = $this->getCouponService($couponId, $serviceId);
        }

        $couponDiscount = 0;
        $priceWithoutDiscount = 0;
        if ($coupon != null) {
            if ($coupon['discount_type'] == self::COUPON_DISCOUNT_TYPE_AMOUNT) {
                $couponDiscount = $coupon['discount_amount'];
            } elseif ($coupon['discount_type'] == self::COUPON_DISCOUNT_TYPE_PERCENTAGE) {
                $couponDiscount = ($service['price'] * $coupon['discount_amount']) / 100;
            }
            $priceWithoutDiscount = $service['price'];
        }
        $service['price'] = $service['price'] - $couponDiscount;
        $currency = $this->getSelectedCurrency();
        $price = $service['price'];
        $priceWithoutTax = null;
        $taxPerc = null;
        $taxEnable = $this->get('tax_enable') === 'true';
        $globalParameters = $this->getGlobalParameters();
        $taxAppliesTo = $globalParameters['tax_applies_to'];
        $taxAmount = 0;

        if ($taxEnable &&
            ($taxAppliesTo == self::TAX_APPLIES_TO_ALL || $taxAppliesTo == self::TAX_APPLIES_TO_ONLY_SERVICES)
        ) {
            $priceWithoutTax = $service['price'];
            $globalTaxPerc = $globalParameters['global_tax_perc'];
            $precision = 2;
            $taxPerc = is_null($service['tax_perc']) ? $globalTaxPerc : $service['tax_perc'];
            $taxAmount = round($priceWithoutTax * $taxPerc / 100, $precision);
            $price = $priceWithoutTax + $taxAmount;
        }

        $values = [
            'service_id' => $serviceId,
            'reference' => $this->generateReference(
                $userId,
                $service['applies_to'],
                $infoSelect
            ),
            'currency_id' => $currency['id'],
            'price' => $price,
            'price_without_tax' => $priceWithoutTax,
            'tax_perc' => $taxPerc,
            'tax_amount' => $taxAmount,
            'node_type' => $service['applies_to'],
            'node_id' => $infoSelect,
            'buyer_id' => $userId,
            'buy_date' => api_get_utc_datetime(),
            'date_start' => api_get_utc_datetime(),
            'date_end' => date_format(
                date_add(
                    date_create(api_get_utc_datetime()),
                    date_interval_create_from_date_string($service['duration_days'].' days')
                ),
                'Y-m-d H:i:s'
            ),
            'status' => self::SERVICE_STATUS_PENDING,
            'payment_type' => $paymentType,
            'price_without_discount' => $priceWithoutDiscount,
            'discount_amount' => $couponDiscount,
        ];

        $returnedServiceSaleId = Database::insert(self::TABLE_SERVICES_SALE, $values);

        return $returnedServiceSaleId;
    }

    /**
     * Save Culqi configuration params.
     *
     * @return int Rows affected. Otherwise return false
     */
    public function saveCulqiParameters(array $params)
    {
        return Database::update(
            Database::get_main_table(self::TABLE_CULQI),
            [
                'commerce_code' => $params['commerce_code'],
                'api_key' => $params['api_key'],
                'integration' => $params['integration'],
            ],
            ['id = ?' => 1]
        );
    }

    /**
     * Gets the stored Culqi params.
     *
     * @return array
     */
    public function getCulqiParams()
    {
        return Database::select(
            '*',
            Database::get_main_table(self::TABLE_CULQI),
            ['id = ?' => 1],
            'first'
        );
    }

    /**
     * Save Cecabank configuration params.
     *
     * @return array
     */
    public function saveCecabankParameters(array $params)
    {
        return Database::update(
            Database::get_main_table(self::TABLE_TPV_CECABANK),
            [
                'crypto_key' => $params['crypto_key'],
                'merchant_id' => $params['merchart_id'],
                'acquirer_bin' => $params['acquirer_bin'],
                'terminal_id' => $params['terminal_id'],
                'cypher' => $params['cypher'],
                'exponent' => $params['exponent'],
                'supported_payment' => $params['supported_payment'],
                'url' => $params['url'],
            ],
            ['id = ?' => 1]
        );
    }

    /**
     * Gets the stored Cecabank params.
     *
     * @return array
     */
    public function getCecabankParams()
    {
        return Database::select(
            '*',
            Database::get_main_table(self::TABLE_TPV_CECABANK),
            ['id = ?' => 1],
            'first'
        );
    }

    /**
     * Save Global Parameters.
     *
     * @return int Rows affected. Otherwise return false
     */
    public function saveGlobalParameters(array $params)
    {
        $sqlParams = [
            'terms_and_conditions' => $params['terms_and_conditions'],
            'sale_email' => $params['sale_email'],
        ];

        if ($this->get('tax_enable') === 'true') {
            $sqlParams['global_tax_perc'] = $params['global_tax_perc'];
            $sqlParams['tax_applies_to'] = $params['tax_applies_to'];
            $sqlParams['tax_name'] = $params['tax_name'];
        }

        if ($this->get('invoicing_enable') === 'true') {
            $sqlParams['seller_name'] = $params['seller_name'];
            $sqlParams['seller_id'] = $params['seller_id'];
            $sqlParams['seller_address'] = $params['seller_address'];
            $sqlParams['seller_email'] = $params['seller_email'];
            $sqlParams['next_number_invoice'] = $params['next_number_invoice'];
            $sqlParams['invoice_series'] = $params['invoice_series'];
        }

        return Database::update(
            Database::get_main_table(self::TABLE_GLOBAL_CONFIG),
            $sqlParams,
            ['id = ?' => 1]
        );
    }

    /**
     * get Global Parameters.
     *
     * @return array
     */
    public function getGlobalParameters()
    {
        return Database::select(
            '*',
            Database::get_main_table(self::TABLE_GLOBAL_CONFIG),
            ['id = ?' => 1],
            'first'
        );
    }

    /**
     * @return bool
     */
    public function checkTaxEnabledInProduct(int $productType)
    {
        if (empty($this->get('tax_enable') === 'true')) {
            return false;
        }

        $globalParameters = $this->getGlobalParameters();
        $taxAppliesTo = $globalParameters['tax_applies_to'];
        if ($taxAppliesTo == self::TAX_APPLIES_TO_ALL) {
            return true;
        }

        if ($taxAppliesTo == $productType) {
            return true;
        }

        return false;
    }

    /**
     * Get the path.
     *
     * @param string $var path variable
     *
     * @return string path
     */
    public function getPath($var)
    {
        $pluginPath = api_get_path(WEB_PLUGIN_PATH).'buycourses/';
        $paths = [
            'SERVICE_IMAGES' => $pluginPath.'uploads/services/images/',
            'SRC' => $pluginPath.'src/',
            'VIEW' => $pluginPath.'view/',
            'UPLOADS' => $pluginPath.'uploads/',
            'LANGUAGES' => $pluginPath.'lang/',
            'RESOURCES' => $pluginPath.'resources/',
            'RESOURCES_IMG' => $pluginPath.'resources/img/',
            'RESOURCES_CSS' => $pluginPath.'resources/css/',
            'RESOURCES_JS' => $pluginPath.'resources/js/',
        ];

        return $paths[$var];
    }

    /**
     * @return array
     */
    public function getBuyCoursePluginPrice(Session $session)
    {
        // start buycourse validation
        // display the course price and buy button if the buycourses plugin is enabled and this course is configured
        $isThisCourseInSale = $this->buyCoursesForGridCatalogValidator($session->getId(), self::PRODUCT_TYPE_SESSION);
        $return = [];

        if ($isThisCourseInSale) {
            // set the Price label
            $return['html'] = $isThisCourseInSale['html'];
            // set the Buy button instead register.
            if ($isThisCourseInSale['verificator']) {
                $return['buy_button'] = $this->returnBuyCourseButton($session->getId(), self::PRODUCT_TYPE_SESSION);
            }
        }
        // end buycourse validation
        return $return;
    }

    /**
     * Register a coupon sale.
     *
     * @param int $saleId   The sale ID
     * @param int $couponId The coupon ID
     *
     * @return int
     */
    public function registerCouponSale(int $saleId, int $couponId)
    {
        $sale = $this->getSale($saleId);

        if (empty($sale)) {
            return false;
        }

        $values = [
            'coupon_id' => $couponId,
            'sale_id' => $saleId,
        ];

        return Database::insert(self::TABLE_COUPON_SALE, $values);
    }

    /**
     * Register a coupon service sale.
     *
     * @param int $saleId   The sale ID
     * @param int $couponId The coupon ID
     *
     * @return int
     */
    public function registerCouponServiceSale(int $saleId, int $couponId)
    {
        $sale = $this->getSale($saleId);

        if (empty($sale)) {
            return false;
        }

        $values = [
            'coupon_id' => $couponId,
            'service_sale_id' => $saleId,
        ];

        return Database::insert(self::TABLE_COUPON_SERVICE_SALE, $values);
    }

    /**
     * Register a coupon sale.
     *
     * @param int $saleId   The sale ID
     * @param int $couponId The coupon ID
     *
     * @return int
     */
    public function registerCouponSubscriptionSale(int $saleId, int $couponId)
    {
        $sale = $this->getSubscriptionSale($saleId);

        if (empty($sale)) {
            return false;
        }

        $values = [
            'coupon_id' => (int) $couponId,
            'sale_id' => (int) $saleId,
        ];

        return Database::insert(self::TABLE_COUPON_SUBSCRIPTION_SALE, $values);
    }

    /**
     * Add a new coupon.
     */
    public function addNewCoupon(array $coupon): bool
    {
        $couponId = $this->registerCoupon($coupon);
        if ($couponId) {
            if (isset($coupon['courses'])) {
                foreach ($coupon['courses'] as $course) {
                    $this->registerCouponItem($couponId, self::PRODUCT_TYPE_COURSE, $course);
                }
            }

            if (isset($coupon['sessions'])) {
                foreach ($coupon['sessions'] as $session) {
                    $this->registerCouponItem($couponId, self::PRODUCT_TYPE_SESSION, $session);
                }
            }

            if (isset($coupon['services'])) {
                foreach ($coupon['services'] as $service) {
                    $this->registerCouponService($couponId, $service);
                }
            }

            return true;
        } else {
            Display::addFlash(
                Display::return_message(
                    $this->get_lang('CouponErrorInsert'),
                    'error',
                    false
                )
            );

            return false;
        }
    }

    /**
     * Add a new coupon.
     *
     * @return bool
     */
    public function updateCouponData(array $coupon)
    {
        $this->updateCoupon($coupon);
        $this->deleteCouponItemsByCoupon(self::PRODUCT_TYPE_COURSE, $coupon['id']);
        $this->deleteCouponItemsByCoupon(self::PRODUCT_TYPE_SESSION, $coupon['id']);
        $this->deleteCouponServicesByCoupon($coupon['id']);

        if (isset($coupon['courses'])) {
            foreach ($coupon['courses'] as $course) {
                $this->registerCouponItem($coupon['id'], self::PRODUCT_TYPE_COURSE, $course);
            }
        }

        if (isset($coupon['sessions'])) {
            foreach ($coupon['sessions'] as $session) {
                $this->registerCouponItem($coupon['id'], self::PRODUCT_TYPE_SESSION, $session);
            }
        }

        if (isset($coupon['services'])) {
            foreach ($coupon['services'] as $service) {
                $this->registerCouponService($coupon['id'], $service);
            }
        }

        return true;
    }

    /**
     * Update coupons delivered.
     *
     * @param int $couponId The coupon ID
     *
     * @return bool
     */
    public function updateCouponDelivered(int $couponId)
    {
        $couponTable = Database::get_main_table(self::TABLE_COUPON);

        $sql = "UPDATE $couponTable
        SET delivered = delivered+1
        WHERE id = $couponId";

        Database::query($sql);
    }

    /**
     * Get coupon info.
     *
     * @param int $couponId The coupon ID
     *
     * @return array The coupon data
     */
    public function getCouponInfo(int $couponId)
    {
        $coupon = $this->getDataCoupon($couponId);

        $couponRelCourses = $this->getItemsCoupons($couponId, self::PRODUCT_TYPE_COURSE);
        $couponRelSessions = $this->getItemsCoupons($couponId, self::PRODUCT_TYPE_SESSION);
        $couponRelServices = $this->getServicesCoupons($couponId);

        $coupon['courses'] = $couponRelCourses;
        $coupon['sessions'] = $couponRelSessions;
        $coupon['services'] = $couponRelServices;

        return $coupon;
    }

    /**
     * Get a list of coupons.
     *
     * @param int $status The coupons activation status
     *
     * @return array Coupons data
     */
    public function getCouponsListByStatus(int $status)
    {
        $coupons = $this->getDataCoupons($status);

        return $coupons;
    }

    /**
     * Get the coupon data.
     *
     * @return array The coupon data
     */
    public function getCoupon(int $couponId, int $productType, int $productId)
    {
        $coupon = $this->getDataCoupon($couponId, $productType, $productId);

        return $coupon;
    }

    /**
     * Get data of the coupon code.
     *
     * @param string $couponCode  The coupon code
     * @param int    $productId   The product ID
     * @param int    $productType The product type
     *
     * @return array The coupon data
     */
    public function getCouponByCode(string $couponCode, int $productType = null, int $productId = null)
    {
        $coupon = $this->getDataCouponByCode($couponCode, $productType, $productId);

        return $coupon;
    }

    /**
     * Get data of the coupon code for a service.
     *
     * @param int $couponId  The coupon ID
     * @param int $serviceId The product ID
     *
     * @return array The coupon data
     */
    public function getCouponService(int $couponId, int $serviceId)
    {
        $coupon = $this->getDataCouponService($couponId, $serviceId);

        return $coupon;
    }

    /**
     * Get data of the coupon code for a service.
     *
     * @param string $couponCode The coupon code code
     * @param int    $serviceId  The product id
     *
     * @return array The coupon data
     */
    public function getCouponServiceByCode(string $couponCode, int $serviceId)
    {
        $coupon = $this->getDataCouponServiceByCode($couponCode, $serviceId);

        return $coupon;
    }

    /**
     * Get the coupon code of a item sale.
     *
     * @param int $saleId The sale ID
     *
     * @return string The coupon code
     */
    public function getSaleCouponCode(int $saleId)
    {
        $couponTable = Database::get_main_table(self::TABLE_COUPON);
        $couponSaleTable = Database::get_main_table(self::TABLE_COUPON_SALE);

        $couponFrom = "
            $couponTable c
            INNER JOIN $couponSaleTable s
                on c.id = s.coupon_id
        ";

        $couponCode = Database::select(
            ['c.code'],
            $couponFrom,
            [
                'where' => [
                    's.sale_id = ? ' => $saleId,
                ],
            ],
            'first'
        );

        return $couponCode['code'];
    }

    /**
     * Get the coupon code of a service sale.
     *
     * @param int $serviceSaleId The service sale ID
     *
     * @return string The coupon code
     */
    public function getServiceSaleCouponCode(int $serviceSaleId)
    {
        $couponTable = Database::get_main_table(self::TABLE_COUPON);
        $couponServiceSaleTable = Database::get_main_table(self::TABLE_COUPON_SERVICE_SALE);

        $couponFrom = "
            $couponTable c
            INNER JOIN $couponServiceSaleTable s
                on c.id = s.coupon_id
        ";

        $couponCode = Database::select(
            ['c.code'],
            $couponFrom,
            [
                'where' => [
                    's.service_sale_id = ? ' => $serviceSaleId,
                ],
            ],
            'first'
        );

        return $couponCode['code'];
    }

    /**
     * @return array
     */
    public function getCecabankSignature(string $saleReference, float $price)
    {
        $urlOk = api_get_path(WEB_PLUGIN_PATH).'buycourses/src/cecabank_success.php';
        $urlKo = api_get_path(WEB_PLUGIN_PATH).'buycourses/src/cecabank_cancel.php';

        $cecabankParams = $this->getCecabankParams();
        $signature = $cecabankParams['crypto_key']
        .$cecabankParams['merchant_id']
        .$cecabankParams['acquirer_bin']
        .$cecabankParams['terminal_id']
        .$saleReference
        .$price * 100
        .'978'
        .$cecabankParams['exponent']
        .$cecabankParams['cypher']
        .$urlOk
        .$urlKo;

        $sha256 = hash('sha256', $signature);
        $signature = strtolower($sha256);

        return $signature;
    }

    /**
     * Register a subscription sale.
     *
     * @param int $productId   The product ID
     * @param int $productType The product type
     * @param int $paymentType The payment type
     * @param int $duration    The subscription duration
     * @param int $couponId    The coupon ID
     *
     * @return int
     */
    public function registerSubscriptionSale(
        int $productId,
        int $productType,
        int $paymentType,
        int $duration,
        int $couponId = null
    ) {
        if (!in_array(
            $paymentType,
            [
                self::PAYMENT_TYPE_PAYPAL,
                self::PAYMENT_TYPE_TRANSFER,
                self::PAYMENT_TYPE_CULQI,
                self::PAYMENT_TYPE_TPV_REDSYS,
                self::PAYMENT_TYPE_STRIPE,
                self::PAYMENT_TYPE_TPV_CECABANK,
            ]
        )
        ) {
            return false;
        }

        $entityManager = Database::getManager();
        $item = $this->getSubscriptionItem($productId, $productType);

        if (empty($item)) {
            return false;
        }

        $productName = '';
        if ($item['product_type'] == self::PRODUCT_TYPE_COURSE) {
            $course = $entityManager->find('ChamiloCoreBundle:Course', $item['product_id']);

            if (empty($course)) {
                return false;
            }

            $productName = $course->getTitle();
        } elseif ($item['product_type'] == self::PRODUCT_TYPE_SESSION) {
            $session = $entityManager->find('ChamiloCoreBundle:Session', $item['product_id']);

            if (empty($session)) {
                return false;
            }

            $productName = $session->getName();
        }

        if ($couponId != null) {
            $coupon = $this->getCoupon($couponId, $item['product_type'], $item['product_id']);
        }

        $couponDiscount = 0;
        $priceWithoutDiscount = 0;
        if ($coupon != null) {
            if ($coupon['discount_type'] == self::COUPON_DISCOUNT_TYPE_AMOUNT) {
                $couponDiscount = $coupon['discount_amount'];
            } elseif ($coupon['discount_type'] == self::COUPON_DISCOUNT_TYPE_PERCENTAGE) {
                $couponDiscount = ($item['price'] * $coupon['discount_amount']) / 100;
            }
            $priceWithoutDiscount = $item['price'];
        }
        $item['price'] = $item['price'] - $couponDiscount;
        $price = $item['price'];
        $priceWithoutTax = null;
        $taxPerc = null;
        $taxAmount = 0;
        $taxEnable = $this->get('tax_enable') === 'true';
        $globalParameters = $this->getGlobalParameters();
        $taxAppliesTo = $globalParameters['tax_applies_to'];

        if ($taxEnable &&
            (
                $taxAppliesTo == self::TAX_APPLIES_TO_ALL ||
                ($taxAppliesTo == self::TAX_APPLIES_TO_ONLY_COURSE && $item['product_type'] == self::PRODUCT_TYPE_COURSE) ||
                ($taxAppliesTo == self::TAX_APPLIES_TO_ONLY_SESSION && $item['product_type'] == self::PRODUCT_TYPE_SESSION)
            )
        ) {
            $priceWithoutTax = $item['price'];
            $globalTaxPerc = $globalParameters['global_tax_perc'];
            $precision = 2;
            $taxPerc = is_null($item['tax_perc']) ? $globalTaxPerc : $item['tax_perc'];
            $taxAmount = round($priceWithoutTax * $taxPerc / 100, $precision);
            $price = $priceWithoutTax + $taxAmount;
        }

        $subscriptionEnd = date('y:m:d', strtotime('+'.$duration.' days'));

        $values = [
            'reference' => $this->generateReference(
                api_get_user_id(),
                $item['product_type'],
                $item['product_id']
            ),
            'currency_id' => $item['currency_id'],
            'date' => api_get_utc_datetime(),
            'user_id' => api_get_user_id(),
            'product_type' => $item['product_type'],
            'product_name' => $productName,
            'product_id' => $item['product_id'],
            'price' => $price,
            'price_without_tax' => $priceWithoutTax,
            'tax_perc' => $taxPerc,
            'tax_amount' => $taxAmount,
            'status' => self::SALE_STATUS_PENDING,
            'payment_type' => $paymentType,
            'price_without_discount' => $priceWithoutDiscount,
            'discount_amount' => $couponDiscount,
            'subscription_end' => $subscriptionEnd,
        ];

        return Database::insert(self::TABLE_SUBSCRIPTION_SALE, $values);
    }

    /**
     * Add a new subscription.
     *
     * @return bool
     */
    public function addNewSubscription(array $subscription)
    {
        $result = false;

        if (isset($subscription['frequencies'])) {
            foreach ($subscription['frequencies'] as $frequency) {
                $subscriptionDb = $this->getSubscription($subscription['product_type'], $subscription['product_id'], $frequency['duration']);

                if (!isset($subscriptionDb) || empty($subscription)) {
                    Display::addFlash(
                        Display::return_message(
                            $this->get_lang('SubscriptionAlreadyExists').' ('.$frequency['duration'].')',
                            'error',
                            false
                        )
                    );

                    return false;
                } else {
                    $subscriptionId = $this->registerSubscription($subscription, $frequency);
                    if ($subscriptionId) {
                        $result = true;
                    } else {
                        Display::addFlash(
                            Display::return_message(
                                $this->get_lang('SubscriptionErrorInsert'),
                                'error',
                                false
                            )
                        );

                        return false;
                    }
                }
            }
        } else {
            Display::addFlash(
                Display::return_message(
                    $this->get_lang('FrequenciesNotSetError'),
                    'error',
                    false
                )
            );

            return false;
        }

        return $result;
    }

    /**
     * Add a new subscription.
     *
     * @return bool
     */
    public function updateSubscriptions(int $productType, int $productId, int $taxPerc)
    {
        $this->updateSubscription($productType, $productId, $taxPerc);
    }

    /**
     * Delete a subscription.
     *
     * @return int
     */
    public function deleteSubscription(int $productType, int $productId, int $duration)
    {
        return Database::delete(
            Database::get_main_table(self::TABLE_SUBSCRIPTION),
            [
                'product_type = ? AND ' => (int) $productType,
                'product_id = ? AND ' => (int) $productId,
                'duration = ? ' => (int) $duration,
            ]
        );
    }

    /**
     * Get a list of subscriptions by product ID and type.
     *
     * @param string $productId   The product ID
     * @param int    $productType The product type
     *
     * @return array Subscriptions data
     */
    public function getSubscriptions($productType, $productId)
    {
        $subscriptions = $this->getDataSubscriptions($productType, $productId);

        return $subscriptions;
    }

    /**
     * Get data of the subscription.
     *
     * @return array The subscription data
     */
    public function getSubscription(int $productType, int $productId, int $duration, array $coupon = null)
    {
        $subscription = $this->getDataSubscription($productType, $productId, $duration);

        $currency = $this->getSelectedCurrency();
        $isoCode = $currency['iso_code'];

        $subscription['iso_code'] = $isoCode;

        $this->setPriceSettings($subscription, self::TAX_APPLIES_TO_ONLY_COURSE, $coupon);

        return $subscription;
    }

    /**
     * Get subscription sale data by ID.
     *
     * @param int $saleId The sale ID
     *
     * @return array
     */
    public function getSubscriptionSale(int $saleId)
    {
        return Database::select(
            '*',
            Database::get_main_table(self::TABLE_SUBSCRIPTION_SALE),
            [
                'where' => ['id = ?' => $saleId],
            ],
            'first'
        );
    }

    /**
     * Complete subscription sale process. Update sale status to completed.
     *
     * @param int $saleId The subscription sale ID
     *
     * @return bool
     */
    public function completeSubscriptionSale(int $saleId)
    {
        $sale = $this->getSubscriptionSale($saleId);

        if ($sale['status'] == self::SALE_STATUS_COMPLETED) {
            return true;
        }

        $saleIsCompleted = false;
        switch ($sale['product_type']) {
            case self::PRODUCT_TYPE_COURSE:
                $course = api_get_course_info_by_id($sale['product_id']);
                $saleIsCompleted = CourseManager::subscribeUser($sale['user_id'], $course['code']);
                break;
            case self::PRODUCT_TYPE_SESSION:
                SessionManager::subscribeUsersToSession(
                    $sale['product_id'],
                    [$sale['user_id']],
                    api_get_session_visibility($sale['product_id']),
                    false
                );

                $saleIsCompleted = true;
                break;
        }

        if ($saleIsCompleted) {
            $this->updateSubscriptionSaleStatus($sale['id'], self::SALE_STATUS_COMPLETED);
            if ($this->get('invoicing_enable') === 'true') {
                $this->setInvoice($sale['id']);
            }
        }

        return $saleIsCompleted;
    }

    /**
     * Update subscription sale status to canceled.
     *
     * @param int $saleId The subscription sale ID
     */
    public function cancelSubscriptionSale(int $saleId)
    {
        $this->updateSubscriptionSaleStatus($saleId, self::SALE_STATUS_CANCELED);
    }

    /**
     * Get a list of subscription sales by the status.
     *
     * @param int $status The status to filter
     *
     * @return array The sale list. Otherwise return false
     */
    public function getSubscriptionSaleListByStatus(int $status = self::SALE_STATUS_PENDING)
    {
        $saleTable = Database::get_main_table(self::TABLE_SUBSCRIPTION_SALE);
        $currencyTable = Database::get_main_table(self::TABLE_CURRENCY);
        $userTable = Database::get_main_table(TABLE_MAIN_USER);

        $innerJoins = "
            INNER JOIN $currencyTable c ON s.currency_id = c.id
            INNER JOIN $userTable u ON s.user_id = u.id
        ";

        return Database::select(
            ['c.iso_code', 'u.firstname', 'u.lastname', 'u.email', 's.*'],
            "$saleTable s $innerJoins",
            [
                'where' => ['s.status = ?' => $status],
                'order' => 'id DESC',
            ]
        );
    }

    /**
     * Get the list statuses for subscriptions sales.
     *
     * @param string $dateStart
     * @param string $dateEnd
     *
     * @throws Exception
     *
     * @return array
     */
    public function getSubscriptionSaleListReport(string $dateStart = null, string $dateEnd = null)
    {
        $saleTable = Database::get_main_table(self::TABLE_SUBSCRIPTION_SALE);
        $currencyTable = Database::get_main_table(self::TABLE_CURRENCY);
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
        $innerJoins = "
            INNER JOIN $currencyTable c ON s.currency_id = c.id
            INNER JOIN $userTable u ON s.user_id = u.id
        ";
        $list = Database::select(
            ['c.iso_code', 'u.firstname', 'u.lastname', 'u.email', 's.*'],
            "$saleTable s $innerJoins",
            [
                'order' => 'id DESC',
            ]
        );
        $listExportTemp = [];
        $listExport = [];
        $textStatus = null;
        $paymentTypes = $this->getPaymentTypes();
        $productTypes = $this->getProductTypes();
        foreach ($list as $item) {
            $statusSaleOrder = $item['status'];
            switch ($statusSaleOrder) {
                case 0:
                    $textStatus = $this->get_lang('SaleStatusPending');
                    break;
                case 1:
                    $textStatus = $this->get_lang('SaleStatusCompleted');
                    break;
                case -1:
                    $textStatus = $this->get_lang('SaleStatusCanceled');
                    break;
            }
            $dateFilter = new DateTime($item['date']);
            $listExportTemp[] = [
                'id' => $item['id'],
                'reference' => $item['reference'],
                'status' => $textStatus,
                'status_filter' => $item['status'],
                'date' => $dateFilter->format('Y-m-d'),
                'order_time' => $dateFilter->format('H:i:s'),
                'price' => $item['iso_code'].' '.$item['price'],
                'product_type' => $productTypes[$item['product_type']],
                'product_name' => $item['product_name'],
                'payment_type' => $paymentTypes[$item['payment_type']],
                'complete_user_name' => api_get_person_name($item['firstname'], $item['lastname']),
                'email' => $item['email'],
            ];
        }
        $listExport[] = [
            get_lang('Number'),
            $this->get_lang('OrderStatus'),
            $this->get_lang('OrderDate'),
            $this->get_lang('OrderTime'),
            $this->get_lang('PaymentMethod'),
            $this->get_lang('SalePrice'),
            $this->get_lang('ProductType'),
            $this->get_lang('ProductName'),
            $this->get_lang('UserName'),
            get_lang('Email'),
        ];
        //Validation Export
        $dateStart = strtotime($dateStart);
        $dateEnd = strtotime($dateEnd);
        foreach ($listExportTemp as $item) {
            $dateFilter = strtotime($item['date']);
            if (($dateFilter >= $dateStart) && ($dateFilter <= $dateEnd)) {
                $listExport[] = [
                    'id' => $item['id'],
                    'status' => $item['status'],
                    'date' => $item['date'],
                    'order_time' => $item['order_time'],
                    'payment_type' => $item['payment_type'],
                    'price' => $item['price'],
                    'product_type' => $item['product_type'],
                    'product_name' => $item['product_name'],
                    'complete_user_name' => $item['complete_user_name'],
                    'email' => $item['email'],
                ];
            }
        }

        return $listExport;
    }

    /**
     * Get a list of subscription sales by the user.
     *
     * @param string $term The search term
     *
     * @return array The sale list. Otherwise return false
     */
    public function getSubscriptionSaleListByUser(string $term)
    {
        $term = trim($term);

        if (empty($term)) {
            return [];
        }

        $saleTable = Database::get_main_table(self::TABLE_SUBSCRIPTION_SALE);
        $currencyTable = Database::get_main_table(self::TABLE_CURRENCY);
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
        $innerJoins = "
            INNER JOIN $currencyTable c ON s.currency_id = c.id
            INNER JOIN $userTable u ON s.user_id = u.id
        ";

        return Database::select(
            ['c.iso_code', 'u.firstname', 'u.lastname', 'u.email', 's.*'],
            "$saleTable s $innerJoins",
            [
                'where' => [
                    'u.username LIKE %?% OR ' => $term,
                    'u.lastname LIKE %?% OR ' => $term,
                    'u.firstname LIKE %?%' => $term,
                ],
                'order' => 'id DESC',
            ]
        );
    }

    /**
     * Get a list of subscription sales by the user id.
     *
     * @param int $id The user id
     *
     * @return array The sale list. Otherwise return false
     */
    public function getSubscriptionSaleListByUserId(int $id)
    {
        if (empty($id)) {
            return [];
        }

        $saleTable = Database::get_main_table(self::TABLE_SUBSCRIPTION_SALE);
        $currencyTable = Database::get_main_table(self::TABLE_CURRENCY);
        $userTable = Database::get_main_table(TABLE_MAIN_USER);

        $innerJoins = "
            INNER JOIN $currencyTable c ON s.currency_id = c.id
            INNER JOIN $userTable u ON s.user_id = u.id
        ";

        return Database::select(
            ['c.iso_code', 'u.firstname', 'u.lastname', 's.*'],
            "$saleTable s $innerJoins",
            [
                'where' => [
                    'u.id = ? AND s.status = ?' => [$id, self::SALE_STATUS_COMPLETED],
                ],
                'order' => 'id DESC',
            ]
        );
    }

    /**
     * Get a list of subscription sales by date range.
     *
     * @return array The sale list. Otherwise return false
     */
    public function getSubscriptionSaleListByDate(string $dateStart, string $dateEnd)
    {
        $dateStart = trim($dateStart);
        $dateEnd = trim($dateEnd);
        if (empty($dateStart)) {
            return [];
        }
        if (empty($dateEnd)) {
            return [];
        }
        $saleTable = Database::get_main_table(self::TABLE_SUBSCRIPTION_SALE);
        $currencyTable = Database::get_main_table(self::TABLE_CURRENCY);
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
        $innerJoins = "
            INNER JOIN $currencyTable c ON s.currency_id = c.id
            INNER JOIN $userTable u ON s.user_id = u.id
        ";

        return Database::select(
            ['c.iso_code', 'u.firstname', 'u.lastname', 'u.email', 's.*'],
            "$saleTable s $innerJoins",
            [
                'where' => [
                    's.date BETWEEN ? AND ' => $dateStart,
                    ' ? ' => $dateEnd,
                ],
                'order' => 'id DESC',
            ]
        );
    }

    /**
     * Get a list of subscription sales by the user Email.
     *
     * @param string $term The search term
     *
     * @return array The sale list. Otherwise return false
     */
    public function getSubscriptionSaleListByEmail(string $term)
    {
        $term = trim($term);
        if (empty($term)) {
            return [];
        }
        $saleTable = Database::get_main_table(self::TABLE_SUBSCRIPTION_SALE);
        $currencyTable = Database::get_main_table(self::TABLE_CURRENCY);
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
        $innerJoins = "
            INNER JOIN $currencyTable c ON s.currency_id = c.id
            INNER JOIN $userTable u ON s.user_id = u.id
        ";

        return Database::select(
            ['c.iso_code', 'u.firstname', 'u.lastname', 'u.email', 's.*'],
            "$saleTable s $innerJoins",
            [
                'where' => [
                    'u.email LIKE %?% ' => $term,
                ],
                'order' => 'id DESC',
            ]
        );
    }

    /**
     * Get subscription sale data by ID.
     *
     * @param string $date The date
     *
     * @return array
     */
    public function getSubscriptionsDue(string $date)
    {
        return Database::select(
            'id, user_id, product_id, product_type',
            Database::get_main_table(self::TABLE_SUBSCRIPTION_SALE),
            [
                'where' => ['subscription_end < ? AND status <> ? AND (expired is NULL OR expired <> ?)' => [
                    $date,
                    self::SALE_STATUS_COMPLETED,
                    1,
                    ],
                ],
            ],
            'first'
        );
    }

    /**
     * Get subscription sale data by ID.
     *
     * @param int $userId      The user ID
     * @param int $productId   The product ID
     * @param int $productType The product type
     *
     * @return array
     */
    public function checkItemSubscriptionActive(int $userId, int $productId, int $productType)
    {
        return Database::select(
            '*',
            Database::get_main_table(self::TABLE_SUBSCRIPTION_SALE),
            [
                'where' => ['subscription_end >= ? AND userId = ? AND productId = ? AND productType = ? AND status <> ?' => [
                    api_get_utc_datetime(),
                    $userId,
                    $productId,
                    $productType,
                    self::SALE_STATUS_COMPLETED,
                    ],
                ],
            ],
            'first'
        );
    }

    /**
     * Get subscription sale data by ID.
     *
     * @return array
     */
    public function updateSubscriptionSaleExpirationStatus(int $id)
    {
        $saleTable = Database::get_main_table(self::TABLE_SUBSCRIPTION_SALE);

        return Database::update(
            $saleTable,
            ['expired' => 1],
            ['id = ?' => $id]
        );
    }

    /**
     * Get the list of frequencies discount types.
     *
     * @return array
     */
    public function getFrequencies()
    {
        $data = Database::select(
            '*',
            Database::get_main_table(self::TABLE_SUBSCRIPTION_PERIOD),
            []
        );

        $frequenciesList = $this->getFrequenciesList();
        $frequencies = [];

        foreach ($data as $key => $items) {
            $frequencies[$items['duration']] = $items['name'];
        }

        return $frequencies;
    }

    /**
     * Get the list of frequencies discount types.
     *
     * @return array
     */
    public function getFrequenciesList()
    {
        return Database::select(
            '*',
            Database::get_main_table(self::TABLE_SUBSCRIPTION_PERIOD),
            []
        );
    }

    /**
     * Get the a frequency.
     *
     * @param int $duration The duration of the frequency value
     *
     * @return array
     */
    public function selectFrequency(int $duration)
    {
        return Database::select(
            '*',
            Database::get_main_table(self::TABLE_SUBSCRIPTION_PERIOD),
            [
                'where' => [
                    'duration = ?' => [
                        (int) $duration,
                    ],
                ],
            ],
            'first'
        );
    }

    /**
     * Add a new subscription frequency.
     *
     * @return array
     */
    public function addFrequency(int $duration, string $name)
    {
        $values = [
            'duration' => $duration,
            'name' => $name,
        ];

        return Database::insert(self::TABLE_SUBSCRIPTION_PERIOD, $values);
    }

    /**
     * Update a subscription frequency.
     *
     * @return array
     */
    public function updateFrequency(int $duration, string $name)
    {
        $periodTable = Database::get_main_table(self::TABLE_SUBSCRIPTION_PERIOD);

        return Database::update(
            $periodTable,
            ['name' => $name],
            ['duration = ?' => $duration]
        );
    }

    /**
     * Delete a subscription frequency.
     *
     * @return array
     */
    public function deleteFrequency(int $duration)
    {
        return Database::delete(
            Database::get_main_table(self::TABLE_SUBSCRIPTION_PERIOD),
            [
                'duration = ?' => $duration,
            ]
        );
    }

    /**
     * @return string
     */
    public function getSubscriptionSuccessMessage(array $saleInfo)
    {
        switch ($saleInfo['product_type']) {
            case self::PRODUCT_TYPE_COURSE:
                $courseInfo = api_get_course_info_by_id($saleInfo['product_id']);
                $url = api_get_course_url($courseInfo['code']);
                break;
            case self::PRODUCT_TYPE_SESSION:
                $sessionId = (int) $saleInfo['product_id'];
                $url = api_get_path(WEB_CODE_PATH).'session/index.php?session_id='.$sessionId;
                break;
            default:
                $url = '#';
        }

        return Display::return_message(
            sprintf(
                $this->get_lang('SubscriptionToCourseXSuccessful'),
                $url,
                $saleInfo['product_name']
            ),
            'success',
            false
        );
    }

    /**
     * @return string
     */
    public static function returnPagination(
        string $baseUrl,
        string $currentPage,
        string $pagesCount,
        string $totalItems,
        array $extraQueryParams = []
    ) {
        $queryParams = HttpRequest::createFromGlobals()->query->all();

        unset($queryParams['page']);

        $url = $baseUrl.'?'.http_build_query(
            array_merge($queryParams, $extraQueryParams)
        );

        return Display::getPagination($url, $currentPage, $pagesCount, $totalItems);
    }

    /**
     * Returns the javascript to set the sales report table for courses.
     */
    public static function getSalesReportScript(array $sales = [], bool $invoicingEnable = false)
    {
        $cols = "
    '".preg_replace("/'/", "\\'", get_plugin_lang('OrderReference', 'BuyCoursesPlugin'))."',
    '".preg_replace("/'/", "\\'", get_plugin_lang('OrderStatus', 'BuyCoursesPlugin'))."',
    '".preg_replace("/'/", "\\'", get_plugin_lang('OrderDate', 'BuyCoursesPlugin'))."',
    '".preg_replace("/'/", "\\'", get_plugin_lang('PaymentMethod', 'BuyCoursesPlugin'))."',
    '".preg_replace("/'/", "\\'", get_plugin_lang('Price', 'BuyCoursesPlugin'))."',
    '".preg_replace("/'/", "\\'", get_plugin_lang('CouponDiscount', 'BuyCoursesPlugin'))."',
    '".preg_replace("/'/", "\\'", get_plugin_lang('Coupon', 'BuyCoursesPlugin'))."',
    '".preg_replace("/'/", "\\'", get_plugin_lang('ProductType', 'BuyCoursesPlugin'))."',
    '".preg_replace("/'/", "\\'", get_plugin_lang('Name', 'BuyCoursesPlugin'))."',
    '".preg_replace("/'/", "\\'", get_lang('UserName'))."',
    '".preg_replace("/'/", "\\'", get_lang('Email'))."',";
        $model = "
        {name:'reference', index:'reference', height:'auto', width:70, sorttype:'string', align:'center'},
        {name:'status', index:'status', height:'auto', width:70, sorttype:'string', align:'center'},
        {name:'date', index:'date', height:'auto', width:70, sorttype:'date', align:'center'},
        {name:'payment_type', index:'payment_type', height:'auto', width:70, sorttype:'string', align:'center'},
        {name:'total_price', index:'total_price', height:'auto', width:70, sorttype:'string', align:'center'},
        {name:'coupon_discount', index:'coupon_discount', height:'auto', width:40, sorttype:'string', align: 'center'},
        {name:'coupon', index:'coupon', height:'auto', width:60, sorttype:'string', align:'center'},
        {name:'product_type', index:'product_type', height:'auto', width:40, sorttype:'string'},
        {name:'product_name', index:'product_name', height:'auto', /*width:60,*/ sorttype:'string'},
        {name:'complete_user_name', index:'complete_user_name', height:'auto', width:70, sorttype:'string'},
        {name:'email', index:'email', height:'auto', /*width:60,*/ sorttype:'string'}, ";
        if ($invoicingEnable) {
            $model .= "{name:'invoice', index:'invoice', height:'auto', width:70, sorttype:'string'},";
            $cols .= "'".get_plugin_lang('Invoice', 'BuyCoursesPlugin')."',";
        }
        $cols .= "'".get_lang('Options')."',";
        $model .= "
        {name:'options', index:'options', height:'auto', width:60, sortable:false},";
        $data = '';
        foreach ($sales as $item) {
            $option = '';
            if (!isset($item['complete_user_name'])) {
                $item['complete_user_name'] = api_get_person_name($item['firstname'], $item['lastname']);
            }
            if ($item['invoice'] == 1) {
                if ($invoicingEnable) {
                    $item['invoice'] = "<a href='".api_get_path(WEB_PLUGIN_PATH).'buycourses/src/invoice.php?invoice='.$item['id']."&is_service=0"
                        ."' title='".get_plugin_lang('InvoiceView', 'BuyCoursesPlugin')."'>".
                        Display::return_icon('default.png', get_plugin_lang('InvoiceView', 'BuyCoursesPlugin'), '', ICON_SIZE_MEDIUM).
                        "<br/>".$item['num_invoice'].
                        "</a>";
                }
            } else {
                $item['invoice'] = null;
            }
            if ($item['status'] == BuyCoursesPlugin::SALE_STATUS_CANCELED) {
                $item['status'] = get_plugin_lang('SaleStatusCanceled', 'BuyCoursesPlugin');
            } elseif ($item['status'] == BuyCoursesPlugin::SALE_STATUS_PENDING) {
                $item['status'] = get_plugin_lang('SaleStatusPending', 'BuyCoursesPlugin');
                $option = "<div class='btn-group btn-group-xs' role='group'>".
                    "<a title='".get_plugin_lang('SubscribeUser', 'BuyCoursesPlugin')."'".
                    " href='".api_get_self()."?order=".$item['id']."&action=confirm'".
                    " class='btn btn-default'>".
                    Display::return_icon('user_subscribe_session.png', get_plugin_lang('SubscribeUser', 'BuyCoursesPlugin'), '', ICON_SIZE_SMALL)
                    ."</a>".
                    "<a title='".get_plugin_lang('DeleteOrder', 'BuyCoursesPlugin')."'".
                    " href='".api_get_self()."?order=".$item['id']."&action=cancel'".
                    " class='btn btn-default'>".
                    Display::return_icon('delete.png', get_plugin_lang('DeleteOrder', 'BuyCoursesPlugin'), '', ICON_SIZE_SMALL)
                    ."</a>".
                    "</div>";
            } elseif ($item['status'] == BuyCoursesPlugin::SALE_STATUS_COMPLETED) {
                $item['status'] = get_plugin_lang('SaleStatusCompleted', 'BuyCoursesPlugin');
            }
            $item['options'] = $option;
            $item['date'] = api_get_local_time($item['date']);
            $data .= json_encode($item).",";
        }

        return "
<script>
    $(window).load( function () {
        $('#table_report').jqGrid({
            height: '100%',
            autowidth: true,
            LoadOnce: true,
            rowNum:10,
            rowList: [10, 25, 50, 100],
            pager: 'tblGridPager',
            datatype: 'local',
            viewrecords: true,
            gridview: true,
            colNames:[ $cols ],
            colModel:[ $model ],
            caption: '".get_plugin_lang('SalesReport', 'BuyCoursesPlugin')."'
        });
        var mydata = [ $data ];
        for(var i=0;i<=mydata.length;i++){
            $('#table_report').jqGrid('addRowData',i+1,mydata[i]);
            if(i==mydata.length){
                $('#table_report').trigger('reloadGrid',[{page:1}])
            }
        }
    });
</script>";
    }

    /**
     * Filter the registered courses for show in plugin catalog.
     */
    private function getCourses(int $first, int $maxResults)
    {
        $em = Database::getManager();
        $urlId = api_get_current_access_url_id();

        $qb = $em->createQueryBuilder();
        $qb2 = $em->createQueryBuilder();
        $qb3 = $em->createQueryBuilder();

        $qb = $qb
            ->select('c')
            ->from('ChamiloCoreBundle:Course', 'c')
            ->where(
                $qb->expr()->notIn(
                    'c',
                    $qb2
                        ->select('course2')
                        ->from('ChamiloCoreBundle:SessionRelCourse', 'sc')
                        ->join('sc.course', 'course2')
                        ->innerJoin(
                            'ChamiloCoreBundle:AccessUrlRelSession',
                            'us',
                            Join::WITH,
                            'us.sessionId = sc.session'
                        )->where(
                            $qb->expr()->eq('us.accessUrlId ', $urlId)
                        )
                        ->getDQL()
                )
            )->andWhere(
                $qb->expr()->in(
                    'c',
                    $qb3
                        ->select('course3')
                        ->from('ChamiloCoreBundle:AccessUrlRelCourse', 'uc')
                        ->join('uc.course', 'course3')
                        ->where(
                            $qb3->expr()->eq('uc.url ', $urlId)
                        )
                        ->getDQL()
                )
            )
        ->setFirstResult($first)
        ->setMaxResults($maxResults);

        return $qb;
    }

    /**
     * Get the user status for the session.
     *
     * @param int     $userId  The user ID
     * @param Session $session The session
     *
     * @return string
     */
    private function getUserStatusForSession(int $userId, Session $session)
    {
        if (empty($userId)) {
            return 'NO';
        }

        $entityManager = Database::getManager();
        $scuRepo = $entityManager->getRepository('ChamiloCoreBundle:SessionRelCourseRelUser');

        $buySaleTable = Database::get_main_table(self::TABLE_SALE);

        // Check if user bought the course
        $sale = Database::select(
            'COUNT(1) as qty',
            $buySaleTable,
            [
                'where' => [
                    'user_id = ? AND product_type = ? AND product_id = ? AND status = ?' => [
                        $userId,
                        self::PRODUCT_TYPE_SESSION,
                        $session->getId(),
                        self::SALE_STATUS_PENDING,
                    ],
                ],
            ],
            'first'
        );

        if ($sale['qty'] > 0) {
            return 'TMP';
        }

        // Check if user is already subscribe to session
        $userSubscription = $scuRepo->findBy([
            'session' => $session,
            'user' => $userId,
        ]);

        if (!empty($userSubscription)) {
            return 'YES';
        }

        return 'NO';
    }

    /**
     * Get the user status for the course.
     *
     * @param int    $userId The user Id
     * @param Course $course The course
     *
     * @return string
     */
    private function getUserStatusForCourse(int $userId, Course $course)
    {
        if (empty($userId)) {
            return 'NO';
        }

        $entityManager = Database::getManager();
        $cuRepo = $entityManager->getRepository('ChamiloCoreBundle:CourseRelUser');
        $buySaleTable = Database::get_main_table(self::TABLE_SALE);

        // Check if user bought the course
        $sale = Database::select(
            'COUNT(1) as qty',
            $buySaleTable,
            [
                'where' => [
                    'user_id = ? AND product_type = ? AND product_id = ? AND status = ?' => [
                        $userId,
                        self::PRODUCT_TYPE_COURSE,
                        $course->getId(),
                        self::SALE_STATUS_PENDING,
                    ],
                ],
            ],
            'first'
        );

        if ($sale['qty'] > 0) {
            return 'TMP';
        }

        // Check if user is already subscribe to course
        $userSubscription = $cuRepo->findBy([
            'course' => $course,
            'user' => $userId,
        ]);

        if (!empty($userSubscription)) {
            return 'YES';
        }

        return 'NO';
    }

    /**
     * Update the sale status.
     *
     * @param int $saleId    The sale ID
     * @param int $newStatus The new status
     *
     * @return bool
     */
    private function updateSaleStatus(int $saleId, int $newStatus = self::SALE_STATUS_PENDING)
    {
        $saleTable = Database::get_main_table(self::TABLE_SALE);

        return Database::update(
            $saleTable,
            ['status' => (int) $newStatus],
            ['id = ?' => (int) $saleId]
        );
    }

    /**
     * Search filtered sessions by name, and range of price.
     *
     * @param string $name            Optional. The name filter
     * @param int    $min             Optional. The minimum price filter
     * @param int    $max             Optional. The maximum price filter
     * @param string $typeResult      Optional. 'all' and 'count'
     * @param int    $sessionCategory Optional. Session category id
     *
     * @return array
     */
    private function filterSessionList(
        int $start,
        int $end,
        string $name = null,
        int $min = 0,
        int $max = 0,
        string $typeResult = 'all',
        int $sessionCategory = 0
    ) {
        $itemTable = Database::get_main_table(self::TABLE_ITEM);
        $sessionTable = Database::get_main_table(TABLE_MAIN_SESSION);

        $innerJoin = "$itemTable i ON s.id = i.product_id";
        $whereConditions = [
            'i.product_type = ? ' => self::PRODUCT_TYPE_SESSION,
        ];

        if (!empty($name)) {
            $whereConditions['AND s.name LIKE %?%'] = $name;
        }

        if (!empty($min)) {
            $whereConditions['AND i.price >= ?'] = $min;
        }

        if (!empty($max)) {
            $whereConditions['AND i.price <= ?'] = $max;
        }

        if ($sessionCategory != 0) {
            $whereConditions['AND s.session_category_id = ?'] = $sessionCategory;
        }

        $sessionIds = Database::select(
            's.id',
            "$sessionTable s INNER JOIN $innerJoin",
            ['where' => $whereConditions, 'limit' => "$start, $end"],
            $typeResult
        );

        if ($typeResult === 'count') {
            return $sessionIds;
        }

        if (!$sessionIds) {
            return [];
        }

        $sessions = [];

        foreach ($sessionIds as $sessionId) {
            $sessions[] = Database::getManager()->find(
                'ChamiloCoreBundle:Session',
                $sessionId
            );
        }

        return $sessions;
    }

    /**
     * Search filtered courses by name, and range of price.
     *
     * @param string $name Optional. The name filter
     * @param int    $min  Optional. The minimun price filter
     * @param int    $max  Optional. The maximum price filter
     *
     * @return array
     */
    private function filterCourseList(
        int $start,
        int $end,
        string $name = null,
        int $min = 0,
        int $max = 0,
        string $typeResult = 'all'
    ) {
        $itemTable = Database::get_main_table(self::TABLE_ITEM);
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
        $urlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);

        $urlId = api_get_current_access_url_id();

        $min = floatval($min);
        $max = floatval($max);

        $whereConditions = [
            'i.product_type = ? ' => self::PRODUCT_TYPE_COURSE,
        ];

        if (!empty($name)) {
            $whereConditions['AND c.title LIKE %?%'] = $name;
        }

        if (!empty($min)) {
            $whereConditions['AND i.price >= ?'] = $min;
        }

        if (!empty($max)) {
            $whereConditions['AND i.price <= ?'] = $max;
        }

        $whereConditions['AND url.access_url_id = ?'] = $urlId;

        $courseIds = Database::select(
            'c.id',
            "$courseTable c
            INNER JOIN $itemTable i
            ON c.id = i.product_id
            INNER JOIN $urlTable url
            ON c.id = url.c_id
            ",
            ['where' => $whereConditions, 'limit' => "$start, $end"],
            $typeResult
        );

        if ($typeResult === 'count') {
            return $courseIds;
        }

        if (!$courseIds) {
            return [];
        }

        $courses = [];
        foreach ($courseIds as $courseId) {
            $courses[] = Database::getManager()->find(
                'ChamiloCoreBundle:Course',
                $courseId
            );
        }

        return $courses;
    }

    /**
     * Search filtered sessions by name, and range of price.
     *
     * @param string $name            Optional. The name filter
     * @param int    $sessionCategory Optional. Session category id
     *
     * @return array
     */
    private function filterSubscriptionSessionList(
        int $start,
        int $end,
        string $name = null,
        string $typeResult = 'all',
        int $sessionCategory = 0
    ) {
        $subscriptionTable = Database::get_main_table(self::TABLE_SUBSCRIPTION);
        $sessionTable = Database::get_main_table(TABLE_MAIN_SESSION);

        $innerJoin = "$subscriptionTable st ON s.id = st.product_id";
        $whereConditions = [
            'st.product_type = ? ' => self::PRODUCT_TYPE_SESSION,
        ];

        if (!empty($name)) {
            $whereConditions['AND s.name LIKE %?%'] = $name;
        }

        if ($sessionCategory != 0) {
            $whereConditions['AND s.session_category_id = ?'] = $sessionCategory;
        }

        $sessionIds = Database::select(
            'DISTINCT s.id',
            "$sessionTable s INNER JOIN $innerJoin",
            ['where' => $whereConditions, 'limit' => "$start, $end"],
            $typeResult
        );

        if ($typeResult === 'count') {
            return $sessionIds;
        }

        if (!$sessionIds) {
            return [];
        }

        $sessions = [];

        foreach ($sessionIds as $sessionId) {
            $sessions[] = Database::getManager()->find(
                'ChamiloCoreBundle:Session',
                $sessionId
            );
        }

        return $sessions;
    }

    /**
     * Search filtered subscriptions courses by name, and range of price.
     *
     * @param string $name Optional. The name filter
     *
     * @return array
     */
    private function filterSubscriptionCourseList(
        int $start,
        int $end,
        string $name = '',
        string $typeResult = 'all'
    ) {
        $subscriptionTable = Database::get_main_table(self::TABLE_SUBSCRIPTION);
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
        $urlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);

        $urlId = api_get_current_access_url_id();

        $whereConditions = [
            'st.product_type = ? ' => self::PRODUCT_TYPE_COURSE,
        ];

        if (!empty($name)) {
            $whereConditions['AND c.title LIKE %?%'] = $name;
        }

        $whereConditions['AND url.access_url_id = ?'] = $urlId;

        $courseIds = Database::select(
            'DISTINCT c.id',
            "$courseTable c
            INNER JOIN $subscriptionTable st
            ON c.id = st.product_id
            INNER JOIN $urlTable url
            ON c.id = url.c_id
            ",
            ['where' => $whereConditions, 'limit' => "$start, $end"],
            $typeResult
        );

        if ($typeResult === 'count') {
            return $courseIds;
        }

        if (!$courseIds) {
            return [];
        }

        $courses = [];
        foreach ($courseIds as $courseId) {
            $courses[] = Database::getManager()->find(
                'ChamiloCoreBundle:Course',
                $courseId
            );
        }

        return $courses;
    }

    /**
     * Update the service sale status.
     *
     * @param int $serviceSaleId The service sale ID
     * @param int $newStatus     The new status
     *
     * @return bool
     */
    private function updateServiceSaleStatus(
        int $serviceSaleId,
        int $newStatus = self::SERVICE_STATUS_PENDING
    ) {
        $serviceSaleTable = Database::get_main_table(self::TABLE_SERVICES_SALE);

        return Database::update(
            $serviceSaleTable,
            ['status' => $newStatus],
            ['id = ?' => $serviceSaleId]
        );
    }

    /**
     * Get the items (courses or sessions) of a coupon.
     *
     * @return array The item data
     */
    private function getItemsCoupons(int $couponId, int $productType)
    {
        $couponItemTable = Database::get_main_table(self::TABLE_COUPON_ITEM);

        if ($productType == self::PRODUCT_TYPE_COURSE) {
            $itemTable = Database::get_main_table(TABLE_MAIN_COURSE);
            $select = ['ci.product_id as id', 'it.title'];
        } elseif ($productType == self::PRODUCT_TYPE_SESSION) {
            $itemTable = Database::get_main_table(TABLE_MAIN_SESSION);
            $select = ['ci.product_id as id', 'it.name'];
        }

        $couponFrom = "
            $couponItemTable ci
            INNER JOIN $itemTable it
                on it.id = ci.product_id and ci.product_type = $productType
        ";

        return Database::select(
            $select,
            $couponFrom,
            [
                'where' => [
                    'ci.coupon_id = ? ' => $couponId,
                ],
            ]
        );
    }

    /**
     * Get the services of a coupon.
     *
     * @param int $couponId The coupon ID
     *
     * @return array The service data
     */
    private function getServicesCoupons(int $couponId)
    {
        $couponServiceTable = Database::get_main_table(self::TABLE_COUPON_SERVICE);
        $serviceTable = Database::get_main_table(self::TABLE_SERVICES);

        $couponFrom = "
            $couponServiceTable cs
            INNER JOIN $serviceTable s
                on s.id = cs.service_id
        ";

        return Database::select(
            ['cs.service_id as id', 's.name'],
            $couponFrom,
            [
                'where' => [
                    'cs.coupon_id = ? ' => $couponId,
                ],
            ]
        );
    }

    /**
     * Get an array of coupons filtered by their status.
     *
     * @param int $status The coupon activation status
     *
     * @return array Coupons data
     */
    private function getDataCoupons(int $status = null)
    {
        $couponTable = Database::get_main_table(self::TABLE_COUPON);

        if ($status != null) {
            return Database::select(
                ['*'],
                $couponTable,
                [
                    'where' => [
                        ' active = ? ' => (int) $status,
                    ],
                    'order' => 'id DESC',
                ]
            );
        } else {
            return Database::select(
                ['*'],
                $couponTable,
                [
                    'order' => 'id DESC',
                ]
            );
        }
    }

    /**
     * Get data of a coupon for a product (course or service) by the coupon ID.
     *
     * @param int $couponId    The coupon code code
     * @param int $productType The product type
     * @param int $productId   The product ID
     *
     * @return array The coupon data
     */
    private function getDataCoupon(int $couponId, int $productType = null, int $productId = null)
    {
        $couponTable = Database::get_main_table(self::TABLE_COUPON);

        if ($productType == null || $productId == null) {
            return Database::select(
                ['*'],
                $couponTable,
                [
                    'where' => [
                        'id = ? ' => $couponId,
                    ],
                ],
                'first'
            );
        } else {
            $couponItemTable = Database::get_main_table(self::TABLE_COUPON_ITEM);
            $dtmNow = api_get_utc_datetime();

            $couponFrom = "
                $couponTable c
                INNER JOIN $couponItemTable ci
                    on ci.coupon_id = c.id
            ";

            return Database::select(
                ['c.*'],
                $couponFrom,
                [
                    'where' => [
                        'c.id = ? AND ' => $couponId,
                        'c.valid_start <= ? AND ' => $dtmNow,
                        'c.valid_end >= ? AND ' => $dtmNow,
                        'ci.product_type = ? AND ' => $productType,
                        'ci.product_id = ?' => $productId,
                    ],
                ],
                'first'
            );
        }
    }

    /**
     * Get data of a coupon for a product (course or service) by the coupon code.
     *
     * @param string $couponCode  The coupon code code
     * @param int    $productType The product type
     * @param int    $productId   The product ID
     *
     * @return array The coupon data
     */
    private function getDataCouponByCode(string $couponCode, int $productType = null, int $productId = null)
    {
        $couponTable = Database::get_main_table(self::TABLE_COUPON);
        $couponItemTable = Database::get_main_table(self::TABLE_COUPON_ITEM);
        $dtmNow = api_get_utc_datetime();

        if ($productType == null || $productId == null) {
            return Database::select(
                ['*'],
                $couponTable,
                [
                    'where' => [
                        'code = ? ' => $couponCode,
                    ],
                ],
                'first'
            );
        } else {
            $couponFrom = "
                $couponTable c
                INNER JOIN $couponItemTable ci
                    on ci.coupon_id = c.id
            ";

            return Database::select(
                ['c.*'],
                $couponFrom,
                [
                    'where' => [
                        'c.code = ? AND ' => $couponCode,
                        'c.valid_start <= ? AND ' => $dtmNow,
                        'c.valid_end >= ? AND ' => $dtmNow,
                        'ci.product_type = ? AND ' => $productType,
                        'ci.product_id = ?' => $productId,
                    ],
                ],
                'first'
            );
        }
    }

    /**
     * Get data of a coupon for a service by the coupon ID.
     *
     * @param int $couponId  The coupon ID
     * @param int $serviceId The service ID
     *
     * @return array The coupon data
     */
    private function getDataCouponService(int $couponId, int $serviceId)
    {
        $couponTable = Database::get_main_table(self::TABLE_COUPON);
        $couponServiceTable = Database::get_main_table(self::TABLE_COUPON_SERVICE);
        $dtmNow = api_get_utc_datetime();

        $couponFrom = "
            $couponTable c
            INNER JOIN $couponServiceTable cs
                on cs.coupon_id = c.id
        ";

        return Database::select(
            ['c.*'],
            $couponFrom,
            [
                'where' => [
                    'c.id = ? AND ' => $couponId,
                    'c.valid_start <= ? AND ' => $dtmNow,
                    'c.valid_end >= ? AND ' => $dtmNow,
                    'cs.service_id = ?' => $serviceId,
                ],
            ],
            'first'
        );
    }

    /**
     * Get data of coupon for a service by the coupon code.
     *
     * @param string $couponCode The coupon code
     * @param int    $serviceId  The service ID
     *
     * @return array The coupon data
     */
    private function getDataCouponServiceByCode(string $couponCode, int $serviceId)
    {
        $couponTable = Database::get_main_table(self::TABLE_COUPON);
        $couponServiceTable = Database::get_main_table(self::TABLE_COUPON_SERVICE);
        $dtmNow = api_get_utc_datetime();

        $couponFrom = "
            $couponTable c
            INNER JOIN $couponServiceTable cs
                on cs.coupon_id = c.id
        ";

        return Database::select(
            ['c.*'],
            $couponFrom,
            [
                'where' => [
                    'c.code = ? AND ' => $couponCode,
                    'c.valid_start <= ? AND ' => $dtmNow,
                    'c.valid_end >= ? AND ' => $dtmNow,
                    'cs.service_id = ?' => $serviceId,
                ],
            ],
            'first'
        );
    }

    /**
     * Update a coupon.
     *
     * @return int
     */
    private function updateCoupon(array $coupon)
    {
        $couponExist = $this->getCouponByCode($coupon['code']);
        if (!$couponExist) {
            Display::addFlash(
                Display::return_message(
                    $this->get_lang('CouponNoExists'),
                    'error',
                    false
                )
            );

            return false;
        }

        $values = [
            'valid_start' => $coupon['valid_start'],
            'valid_end' => $coupon['valid_end'],
            'active' => $coupon['active'],
        ];

        return Database::update(
            self::TABLE_COUPON,
            $values,
            ['id = ?' => $coupon['id']]
        );
    }

    /**
     * Register a coupon.
     *
     * @return int
     */
    private function registerCoupon(array $coupon)
    {
        $couponExist = $this->getCouponByCode($coupon['code']);
        if ($couponExist) {
            Display::addFlash(
                Display::return_message(
                    $this->get_lang('CouponCodeUsed'),
                    'error',
                    false
                )
            );

            return false;
        }

        $values = [
            'code' => (string) $coupon['code'],
            'discount_type' => (int) $coupon['discount_type'],
            'discount_amount' => $coupon['discount_amount'],
            'valid_start' => $coupon['valid_start'],
            'valid_end' => $coupon['valid_end'],
            'delivered' => 0,
            'active' => $coupon['active'],
        ];

        return Database::insert(self::TABLE_COUPON, $values);
    }

    /**
     * Register a coupon item.
     *
     * @param int $couponId    The coupon ID
     * @param int $productType The product type
     * @param int $productId   The product ID
     *
     * @return int
     */
    private function registerCouponItem(int $couponId, int $productType, int $productId)
    {
        $coupon = $this->getDataCoupon($couponId);
        if (empty($coupon)) {
            Display::addFlash(
                Display::return_message(
                    $this->get_lang('CouponNoExists'),
                    'error',
                    false
                )
            );

            return false;
        }

        $values = [
            'coupon_id' => $couponId,
            'product_type' => $productType,
            'product_id' => $productId,
        ];

        return Database::insert(self::TABLE_COUPON_ITEM, $values);
    }

    /**
     * Remove all coupon items for a product type and coupon ID.
     *
     * @param int $productType The product type
     * @param int $couponId    The coupon ID
     *
     * @return int Rows affected. Otherwise return false
     */
    private function deleteCouponItemsByCoupon(int $productType, int $couponId)
    {
        return Database::delete(
            Database::get_main_table(self::TABLE_COUPON_ITEM),
            [
                'product_type = ? AND ' => $productType,
                'coupon_id = ?' => $couponId,
            ]
        );
    }

    /**
     * Register a coupon service.
     *
     * @param int $couponId  The coupon ID
     * @param int $serviceId The service ID
     *
     * @return int
     */
    private function registerCouponService(int $couponId, int $serviceId)
    {
        $coupon = $this->getDataCoupon($couponId);
        if (empty($coupon)) {
            Display::addFlash(
                Display::return_message(
                    $this->get_lang('CouponNoExists'),
                    'error',
                    false
                )
            );

            return false;
        }

        $values = [
            'coupon_id' => $couponId,
            'service_id' => $serviceId,
        ];

        return Database::insert(self::TABLE_COUPON_SERVICE, $values);
    }

    /**
     * Remove all coupon services for a product type and coupon ID.
     *
     * @return int Rows affected. Otherwise, return false
     */
    private function deleteCouponServicesByCoupon(int $couponId)
    {
        return Database::delete(
            Database::get_main_table(self::TABLE_COUPON_SERVICE),
            [
                'coupon_id = ?' => (int) $couponId,
            ]
        );
    }

    /**
     * Get an array of subscriptions.
     *
     * @return array Subscriptions data
     */
    private function getDataSubscriptions(int $productType, int $productId)
    {
        $subscriptionTable = Database::get_main_table(self::TABLE_SUBSCRIPTION);

        return Database::select(
            ['*'],
            $subscriptionTable,
            [
                'where' => [
                    'product_type = ? AND ' => (int) $productType,
                    'product_id = ?  ' => (int) $productId,
                ],
                'order' => 'duration ASC',
            ]
        );
    }

    /**
     * Get data of a subscription for a product (course or service) by the subscription ID.
     *
     * @param int $productType The product type
     * @param int $productId   The product ID
     * @param int $duration    The duration (in seconds)
     *
     * @return array The subscription data
     */
    private function getDataSubscription(int $productType, int $productId, int $duration)
    {
        $subscriptionTable = Database::get_main_table(self::TABLE_SUBSCRIPTION);

        return Database::select(
            ['*'],
            $subscriptionTable,
            [
                'where' => [
                    'product_type = ? AND ' => $productType,
                    'product_id = ? AND ' => $productId,
                    'duration = ? ' => $duration,
                ],
            ],
            'first'
        );
    }

    /**
     * Update a subscription.
     *
     * @return int
     */
    private function updateSubscription(int $productType, int $productId, int $taxPerc)
    {
        $values = [
            'tax_perc' => $taxPerc,
        ];

        return Database::update(
            self::TABLE_SUBSCRIPTION,
            $values,
            [
                'product_type = ? AND ' => $productType,
                'product_id = ?' => $productId,
            ]
        );

        return true;
    }

    /**
     * Register a subscription.
     *
     * @return int
     */
    private function registerSubscription(array $subscription, array $frequency)
    {
        $values = [
            'product_type' => (int) $subscription['product_type'],
            'product_id' => (int) $subscription['product_id'],
            'duration' => (int) $frequency['duration'],
            'currency_id' => (int) $subscription['currency_id'],
            'tax_perc' => (int) $subscription['tax_perc'],
            'price' => (float) $frequency['price'],
        ];

        Database::insert(self::TABLE_SUBSCRIPTION, $values);

        return true;
    }

    /**
     * Update the subscription sale status.
     *
     * @param int $saleId    The sale ID
     * @param int $newStatus The new status
     *
     * @return bool
     */
    private function updateSubscriptionSaleStatus(int $saleId, int $newStatus = self::SALE_STATUS_PENDING)
    {
        $saleTable = Database::get_main_table(self::TABLE_SUBSCRIPTION_SALE);

        return Database::update(
            $saleTable,
            ['status' => $newStatus],
            ['id = ?' => $saleId]
        );
    }

    /**
     * Get the user status for the subscription session.
     *
     * @param int     $userId  The user ID
     * @param Session $session The session
     *
     * @return string
     */
    private function getUserStatusForSubscriptionSession(int $userId, Session $session)
    {
        if (empty($userId)) {
            return 'NO';
        }

        $entityManager = Database::getManager();
        $scuRepo = $entityManager->getRepository('ChamiloCoreBundle:SessionRelCourseRelUser');

        $buySaleTable = Database::get_main_table(self::TABLE_SUBSCRIPTION_SALE);

        // Check if user bought the course
        $sale = Database::select(
            'COUNT(1) as qty',
            $buySaleTable,
            [
                'where' => [
                    'user_id = ? AND product_type = ? AND product_id = ? AND status = ? AND (expired is NULL OR expired <> ?)' => [
                        $userId,
                        self::PRODUCT_TYPE_SESSION,
                        $session->getId(),
                        self::SALE_STATUS_PENDING,
                        1,
                    ],
                ],
            ],
            'first'
        );

        if ($sale['qty'] > 0) {
            return 'TMP';
        }

        // Check if user is already subscribe to session
        $userSubscription = $scuRepo->findBy([
            'session' => $session,
            'user' => $userId,
        ]);

        if (!empty($userSubscription)) {
            return 'YES';
        }

        return 'NO';
    }

    /**
     * Get the user status for the subscription course.
     *
     * @param int    $userId The user Id
     * @param Course $course The course
     *
     * @return string
     */
    private function getUserStatusForSubscriptionCourse(int $userId, Course $course)
    {
        if (empty($userId)) {
            return 'NO';
        }

        $entityManager = Database::getManager();
        $cuRepo = $entityManager->getRepository('ChamiloCoreBundle:CourseRelUser');
        $buySaleTable = Database::get_main_table(self::TABLE_SUBSCRIPTION_SALE);

        // Check if user bought the course
        $sale = Database::select(
            'COUNT(1) as qty',
            $buySaleTable,
            [
                'where' => [
                    'user_id = ? AND product_type = ? AND product_id = ? AND status = ? AND (expired is NULL OR expired <> ?)' => [
                        $userId,
                        self::PRODUCT_TYPE_COURSE,
                        $course->getId(),
                        self::SALE_STATUS_PENDING,
                        1,
                    ],
                ],
            ],
            'first'
        );

        if ($sale['qty'] > 0) {
            return 'TMP';
        }

        // Check if user is already subscribe to course
        $userSubscription = $cuRepo->findBy([
            'course' => $course,
            'user' => $userId,
        ]);

        if (!empty($userSubscription)) {
            return 'YES';
        }

        return 'NO';
    }
}

Zerion Mini Shell 1.0