From 65fd86f83327316a46969dd1935bbc8385c84891 Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos <1697880+AngelFQC@users.noreply.github.com> Date: Wed, 5 Nov 2025 16:26:27 -0500 Subject: [PATCH] Plugin: update BuyCourses from 1.11.x #2562 --- public/plugin/BuyCourses/CHANGELOG.md | 50 +- public/plugin/BuyCourses/README.md | 38 +- public/plugin/BuyCourses/admin.php | 2 + public/plugin/BuyCourses/config.php | 2 + public/plugin/BuyCourses/database.php | 454 +- public/plugin/BuyCourses/index.php | 4 + public/plugin/BuyCourses/install.php | 5 +- public/plugin/BuyCourses/lang/brazilian.php | 196 +- public/plugin/BuyCourses/lang/dutch.php | 268 +- public/plugin/BuyCourses/lang/english.php | 498 +- public/plugin/BuyCourses/lang/french.php | 335 +- public/plugin/BuyCourses/lang/spanish.php | 529 +- public/plugin/BuyCourses/plugin.php | 3 + .../plugin/BuyCourses/resources/css/style.css | 34 +- .../resources/img/128/buysubscriptions.png | Bin 0 -> 3816 bytes .../BuyCourses/resources/img/128/discount.png | Bin 0 -> 15680 bytes .../img/128/subscriptionssettings.png | Bin 0 -> 6629 bytes .../resources/img/32/buysubscription.png | Bin 0 -> 1263 bytes .../resources/img/32/buysubscriptions.png | Bin 0 -> 1263 bytes .../BuyCourses/resources/img/32/discount.png | Bin 0 -> 4181 bytes .../resources/img/32/subscriptionsettings.png | Bin 0 -> 1680 bytes .../img/32/subscriptionssettings.png | Bin 0 -> 1680 bytes .../resources/img/42/buysubscriptions.png | Bin 0 -> 1263 bytes .../BuyCourses/resources/img/42/discount.png | Bin 0 -> 5070 bytes .../img/42/subscriptionssettings.png | Bin 0 -> 1680 bytes .../resources/img/64/buysubscriptions.png | Bin 0 -> 2148 bytes .../BuyCourses/resources/img/64/discount.png | Bin 0 -> 6449 bytes .../img/64/subscriptionssettings.png | Bin 0 -> 3455 bytes .../plugin/BuyCourses/resources/js/modals.js | 2 +- .../src/buy_course_plugin.class.php | 4304 ++++++++++++++--- .../plugin/BuyCourses/src/buycourses.ajax.php | 169 +- .../plugin/BuyCourses/src/cecabank_cancel.php | 31 + .../BuyCourses/src/cecabank_response.php | 56 + .../BuyCourses/src/cecabank_success.php | 31 + .../BuyCourses/src/configure_coupon.php | 200 + .../BuyCourses/src/configure_course.php | 37 +- .../BuyCourses/src/configure_frequency.php | 120 + .../BuyCourses/src/configure_subscription.php | 253 + public/plugin/BuyCourses/src/coupon_add.php | 165 + public/plugin/BuyCourses/src/coupons.php | 111 + .../plugin/BuyCourses/src/course_catalog.php | 7 +- public/plugin/BuyCourses/src/course_panel.php | 6 +- public/plugin/BuyCourses/src/error.php | 9 +- .../plugin/BuyCourses/src/export_report.php | 15 +- .../src/export_subscription_report.php | 65 + .../plugin/BuyCourses/src/expresscheckout.php | 34 +- .../BuyCourses/src/index.buycourses.php | 37 +- public/plugin/BuyCourses/src/index.php | 2 + public/plugin/BuyCourses/src/invoice.php | 31 +- public/plugin/BuyCourses/src/list.php | 19 +- public/plugin/BuyCourses/src/list_coupon.php | 98 + public/plugin/BuyCourses/src/list_service.php | 16 +- public/plugin/BuyCourses/src/list_session.php | 25 +- public/plugin/BuyCourses/src/panel.ajax.php | 44 +- public/plugin/BuyCourses/src/paymentsetup.php | 220 +- public/plugin/BuyCourses/src/payout_panel.php | 8 +- .../plugin/BuyCourses/src/payout_report.php | 10 +- .../plugin/BuyCourses/src/paypal_payout.php | 8 +- .../plugin/BuyCourses/src/paypalfunctions.php | 282 +- public/plugin/BuyCourses/src/process.php | 115 +- .../plugin/BuyCourses/src/process_confirm.php | 334 +- .../src/process_subscription_confirm.php | 388 ++ public/plugin/BuyCourses/src/sales_report.php | 36 +- .../plugin/BuyCourses/src/service_catalog.php | 21 +- .../plugin/BuyCourses/src/service_error.php | 10 +- .../BuyCourses/src/service_information.php | 8 +- .../plugin/BuyCourses/src/service_panel.php | 7 + .../plugin/BuyCourses/src/service_process.php | 145 +- .../src/service_process_confirm.php | 120 +- .../BuyCourses/src/service_sales_report.php | 36 +- .../plugin/BuyCourses/src/service_success.php | 49 +- public/plugin/BuyCourses/src/services_add.php | 23 +- .../plugin/BuyCourses/src/services_edit.php | 31 +- .../plugin/BuyCourses/src/session_catalog.php | 48 +- .../plugin/BuyCourses/src/session_panel.php | 6 +- .../plugin/BuyCourses/src/stripe_cancel.php | 31 + .../plugin/BuyCourses/src/stripe_response.php | 77 + .../plugin/BuyCourses/src/stripe_success.php | 79 + .../BuyCourses/src/subscription_add.php | 238 + .../src/subscription_course_catalog.php | 88 + .../BuyCourses/src/subscription_process.php | 272 ++ .../src/subscription_sales_report.php | 212 + .../src/subscription_session_catalog.php | 114 + .../BuyCourses/src/subscriptions_courses.php | 97 + .../BuyCourses/src/subscriptions_sessions.php | 101 + public/plugin/BuyCourses/src/success.php | 46 +- public/plugin/BuyCourses/src/tpv_error.php | 31 + public/plugin/BuyCourses/src/tpv_response.php | 71 + public/plugin/BuyCourses/src/tpv_success.php | 32 + public/plugin/BuyCourses/uninstall.php | 2 + public/plugin/BuyCourses/update.php | 3 + public/plugin/BuyCourses/view/catalog.tpl | 66 +- .../BuyCourses/view/configure_frequency.tpl | 34 + .../view/configure_subscription.tpl | 46 + public/plugin/BuyCourses/view/coupons.tpl | 41 + .../plugin/BuyCourses/view/course_panel.tpl | 2 - public/plugin/BuyCourses/view/index.tpl | 58 +- public/plugin/BuyCourses/view/list.tpl | 40 +- public/plugin/BuyCourses/view/list_coupon.tpl | 51 + .../BuyCourses/view/list_subscription.tpl | 0 .../BuyCourses/view/message_confirm.tpl | 4 +- .../BuyCourses/view/message_confirm_buyer.tpl | 18 + .../BuyCourses/view/message_transfer.tpl | 5 +- .../plugin/BuyCourses/view/paymentsetup.tpl | 57 +- .../plugin/BuyCourses/view/payout_panel.tpl | 4 +- .../plugin/BuyCourses/view/payout_report.tpl | 10 +- .../plugin/BuyCourses/view/paypal_payout.tpl | 14 +- public/plugin/BuyCourses/view/process.tpl | 42 +- .../BuyCourses/view/process_confirm.tpl | 14 +- .../plugin/BuyCourses/view/sales_report.tpl | 74 +- .../BuyCourses/view/service_information.tpl | 2 +- .../view/service_message_transfer.tpl | 8 +- .../plugin/BuyCourses/view/service_panel.tpl | 4 +- .../BuyCourses/view/service_process.tpl | 24 + .../BuyCourses/view/service_sales_report.tpl | 12 +- .../BuyCourses/view/subscription_add.tpl | 38 + .../BuyCourses/view/subscription_catalog.tpl | 124 + .../BuyCourses/view/subscription_process.tpl | 189 + .../view/subscription_process_confirm.tpl | 285 ++ .../view/subscription_sales_report.tpl | 124 + .../plugin/BuyCourses/view/subscriptions.tpl | 154 + public/plugin/BuyCourses/view/success.tpl | 2 +- 122 files changed, 10997 insertions(+), 2253 deletions(-) create mode 100644 public/plugin/BuyCourses/resources/img/128/buysubscriptions.png create mode 100644 public/plugin/BuyCourses/resources/img/128/discount.png create mode 100644 public/plugin/BuyCourses/resources/img/128/subscriptionssettings.png create mode 100644 public/plugin/BuyCourses/resources/img/32/buysubscription.png create mode 100644 public/plugin/BuyCourses/resources/img/32/buysubscriptions.png create mode 100644 public/plugin/BuyCourses/resources/img/32/discount.png create mode 100644 public/plugin/BuyCourses/resources/img/32/subscriptionsettings.png create mode 100644 public/plugin/BuyCourses/resources/img/32/subscriptionssettings.png create mode 100644 public/plugin/BuyCourses/resources/img/42/buysubscriptions.png create mode 100644 public/plugin/BuyCourses/resources/img/42/discount.png create mode 100644 public/plugin/BuyCourses/resources/img/42/subscriptionssettings.png create mode 100644 public/plugin/BuyCourses/resources/img/64/buysubscriptions.png create mode 100644 public/plugin/BuyCourses/resources/img/64/discount.png create mode 100644 public/plugin/BuyCourses/resources/img/64/subscriptionssettings.png create mode 100644 public/plugin/BuyCourses/src/cecabank_cancel.php create mode 100644 public/plugin/BuyCourses/src/cecabank_response.php create mode 100644 public/plugin/BuyCourses/src/cecabank_success.php create mode 100644 public/plugin/BuyCourses/src/configure_coupon.php create mode 100644 public/plugin/BuyCourses/src/configure_frequency.php create mode 100644 public/plugin/BuyCourses/src/configure_subscription.php create mode 100644 public/plugin/BuyCourses/src/coupon_add.php create mode 100644 public/plugin/BuyCourses/src/coupons.php create mode 100644 public/plugin/BuyCourses/src/export_subscription_report.php create mode 100644 public/plugin/BuyCourses/src/list_coupon.php create mode 100644 public/plugin/BuyCourses/src/process_subscription_confirm.php create mode 100644 public/plugin/BuyCourses/src/stripe_cancel.php create mode 100644 public/plugin/BuyCourses/src/stripe_response.php create mode 100644 public/plugin/BuyCourses/src/stripe_success.php create mode 100644 public/plugin/BuyCourses/src/subscription_add.php create mode 100644 public/plugin/BuyCourses/src/subscription_course_catalog.php create mode 100644 public/plugin/BuyCourses/src/subscription_process.php create mode 100644 public/plugin/BuyCourses/src/subscription_sales_report.php create mode 100644 public/plugin/BuyCourses/src/subscription_session_catalog.php create mode 100644 public/plugin/BuyCourses/src/subscriptions_courses.php create mode 100644 public/plugin/BuyCourses/src/subscriptions_sessions.php create mode 100644 public/plugin/BuyCourses/src/tpv_error.php create mode 100644 public/plugin/BuyCourses/src/tpv_response.php create mode 100644 public/plugin/BuyCourses/src/tpv_success.php create mode 100644 public/plugin/BuyCourses/view/configure_frequency.tpl create mode 100644 public/plugin/BuyCourses/view/configure_subscription.tpl create mode 100644 public/plugin/BuyCourses/view/coupons.tpl create mode 100644 public/plugin/BuyCourses/view/list_coupon.tpl create mode 100644 public/plugin/BuyCourses/view/list_subscription.tpl create mode 100644 public/plugin/BuyCourses/view/message_confirm_buyer.tpl create mode 100644 public/plugin/BuyCourses/view/subscription_add.tpl create mode 100644 public/plugin/BuyCourses/view/subscription_catalog.tpl create mode 100644 public/plugin/BuyCourses/view/subscription_process.tpl create mode 100644 public/plugin/BuyCourses/view/subscription_process_confirm.tpl create mode 100644 public/plugin/BuyCourses/view/subscription_sales_report.tpl create mode 100644 public/plugin/BuyCourses/view/subscriptions.tpl diff --git a/public/plugin/BuyCourses/CHANGELOG.md b/public/plugin/BuyCourses/CHANGELOG.md index ed1ef9f9f75..1938e12eace 100644 --- a/public/plugin/BuyCourses/CHANGELOG.md +++ b/public/plugin/BuyCourses/CHANGELOG.md @@ -1,3 +1,45 @@ +v7.4 - 2022-04-28 +==== +Add subscriptions support. + +If the plugin has already been installed, the update.php script must be executed (load plugin/BuyCourses/update.php in your browser) to update the database structure by adding the plugin_buycourses_subscription, plugin_buycourses_subscription_rel_sale, plugin_buycourses_subscription_period and plugin_buycourses_coupon_rel_subscription_sale. + +v7.3 - 2022-04-28 +==== +Add Cecabank payments support. + +If the plugin has already been installed, the update.php script must be executed (load plugin/BuyCourses/update.php in your browser) to update the structure of the tables in the database. + +v7.2 - 2021-11-22 +==== +Add Stripe payments support. + +If the plugin has already been installed, the update.php script must be executed (load plugin/BuyCourses/update.php in your browser) to update the structure of the tables in the database. + +v7.1 - 2021-10-26 +==== +Fix install issue with DB field type. + +v7.0 - 2021-08-12 +==== +Added support for discount coupons. +Added a better table view for the sales report. +Multiple fixes to navigation-related issues. + +WARNING: Updating this plugin (or Chamilo) without going through the specific update procedure for this plugin will break your sales pages. + +The file [your-host]/plugin/BuyCourses/update.php *MUST* be executed to update the structure of the tables +in the database. + +v6.0 - 2020-11-29 +==== +Added support for purchase instructions e-mail customization (although this +does not support multiple languages at the moment). +This requires changes to the DB tables: +```sql +ALTER TABLE plugin_buycourses_global_config ADD COLUMN info_email_extra TEXT; +``` + v5.0 - 2019-02-06 ==== @@ -11,12 +53,12 @@ The file update.php must be executed to update the structure of the tables v4.0 - 2017-04-25 ==== -This version includes the Culqi payment gateway and introduces an additional -option to show the Buy Courses tab to anonymous users. +This version includes the Culqi payment gateway v1.0 (now expired) and introduces +an additional option to show the Buy Courses tab to anonymous users. To enable these features, if you have already installed this plugin on your -portal prior to this version, you will need to add the corresponding configuration -to your access_url_rel_plugin table. No documentation is available at this time on +portal prior to this version, you will need to add the corresponding settings +to your settings_current table. No documentation is available at this time on how to do that, so please check up the code. Sorry about that. diff --git a/public/plugin/BuyCourses/README.md b/public/plugin/BuyCourses/README.md index 1f3157daa4e..6e927e3cc6e 100644 --- a/public/plugin/BuyCourses/README.md +++ b/public/plugin/BuyCourses/README.md @@ -1,13 +1,37 @@ -Buy Courses plugin for Chamilo LMS -======================================= -Users can access the purchases catalog to buy courses or sessions (since v2 of this plugin) -enabled for sale. +Buy Courses (course sales) plugin +================================= +This plugin transforms your Chamilo installation in an online shop by adding a catalogue + of courses and sessions that you have previously configured for sales. If the user is not registered or logged in, he/she will be requested to register/login before he/she can resume buying items. -Once the course or session is chosen, shows available payment types (currently only PayPal works) +Do not enable this plugin in any "Region". This is a known issue, but it works without +region assignation. + +Once the course or session is chosen, the plugin displays the available payment methods and lets the user proceed with the purchase. +Currently, the plugin allows users to pay through: + - PayPal (requires a merchant account on PayPal at configuration time) + - Bank payments (requires manual confirmation of payments' reception) + - RedSys payments (Spanish payment gateway) (requires the download of an external file) + - Stripe payments (requieres a merchant account oin Stripe at configuration time) + - Cecabank payments (Spanish payment gateway) + +The user receives an e-mail confirming the purchase and she/he can immediately +access to the course or session. + +We recommend using sessions as this gives you more time-related availability options +(in the session configuration). + +Updates +========= + +You must load the update.php script for installations that were in +production before updating the code, as it will update the database structure to +enable new features. + +Please note that updating Chamilo does *NOT* automatically update the plugins +structure. -Finally, the user receives an e-mail confirming the purchase and him/her is can access to the -course/session. +You can find a history of changes in the [CHANGELOG.md file](../../plugin/BuyCourses/CHANGELOG.md) diff --git a/public/plugin/BuyCourses/admin.php b/public/plugin/BuyCourses/admin.php index 13d165b2b84..a148dda8ac6 100644 --- a/public/plugin/BuyCourses/admin.php +++ b/public/plugin/BuyCourses/admin.php @@ -1,3 +1,5 @@ createTable(BuyCoursesPlugin::TABLE_PAYPAL); $paypalTable->addColumn( 'id', - 'integer', + Types::INTEGER, ['autoincrement' => true, 'unsigned' => true] ); - $paypalTable->addColumn('username', 'string'); - $paypalTable->addColumn('password', 'string'); - $paypalTable->addColumn('signature', 'string'); - $paypalTable->addColumn('sandbox', 'boolean'); + $paypalTable->addColumn('username', Types::STRING); + $paypalTable->addColumn('password', Types::STRING); + $paypalTable->addColumn('signature', Types::STRING); + $paypalTable->addColumn('sandbox', Types::BOOLEAN); $paypalTable->setPrimaryKey(['id']); } @@ -39,38 +42,55 @@ $transferTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_TRANSFER); $transferTable->addColumn( 'id', - 'integer', + Types::INTEGER, ['autoincrement' => true, 'unsigned' => true] ); - $transferTable->addColumn('title', 'string'); - $transferTable->addColumn('account', 'string'); - $transferTable->addColumn('swift', 'string'); + $transferTable->addColumn('name', Types::STRING); + $transferTable->addColumn('account', Types::STRING); + $transferTable->addColumn('swift', Types::STRING); $transferTable->setPrimaryKey(['id']); } +if (false === $sm->tablesExist(BuyCoursesPlugin::TABLE_TPV_REDSYS)) { + $tpvRedsysTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_TPV_REDSYS); + $tpvRedsysTable->addColumn( + 'id', + Types::INTEGER, + ['autoincrement' => true, 'unsigned' => true] + ); + $tpvRedsysTable->addColumn('merchantcode', Types::STRING); + $tpvRedsysTable->addColumn('terminal', Types::STRING); + $tpvRedsysTable->addColumn('currency', Types::STRING); + $tpvRedsysTable->addColumn('kc', Types::STRING); + $tpvRedsysTable->addColumn('url_redsys', Types::STRING); + $tpvRedsysTable->addColumn('url_redsys_sandbox', Types::STRING); + $tpvRedsysTable->addColumn('sandbox', Types::BOOLEAN); + $tpvRedsysTable->setPrimaryKey(['id']); +} + if (false === $sm->tablesExist(BuyCoursesPlugin::TABLE_CURRENCY)) { $currencyTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_CURRENCY); $currencyTable->addColumn( 'id', - 'integer', + Types::INTEGER, ['autoincrement' => true, 'unsigned' => true] ); $currencyTable->addColumn( 'country_code', - 'string', + Types::STRING, ['length' => 2] ); $currencyTable->addColumn( 'country_name', - 'string', + Types::STRING, ['length' => 255] ); $currencyTable->addColumn( 'iso_code', - 'string', - ['length' => 4] + Types::STRING, + ['length' => 3] ); - $currencyTable->addColumn('status', 'boolean'); + $currencyTable->addColumn('status', Types::BOOLEAN); $currencyTable->addUniqueIndex(['country_code']); $currencyTable->addIndex(['iso_code']); $currencyTable->setPrimaryKey(['id']); @@ -80,28 +100,28 @@ $itemTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_ITEM); $itemTable->addColumn( 'id', - 'integer', + Types::INTEGER, ['autoincrement' => true, 'unsigned' => true] ); - $itemTable->addColumn('product_type', 'integer'); + $itemTable->addColumn('product_type', Types::INTEGER); $itemTable->addColumn( 'product_id', - 'integer', + Types::INTEGER, ['unsigned' => true] ); $itemTable->addColumn( 'price', - 'decimal', + Types::DECIMAL, ['scale' => 2] ); $itemTable->addColumn( 'currency_id', - 'integer', + Types::INTEGER, ['unsigned' => true] ); $itemTable->addColumn( 'tax_perc', - 'integer', + Types::INTEGER, ['unsigned' => true, 'notnull' => false] ); $itemTable->setPrimaryKey(['id']); @@ -117,22 +137,22 @@ $itemBeneficiary = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_ITEM_BENEFICIARY); $itemBeneficiary->addColumn( 'id', - 'integer', + Types::INTEGER, ['autoincrement' => true, 'unsigned' => true] ); $itemBeneficiary->addColumn( 'item_id', - 'integer', + Types::INTEGER, ['unsigned' => true] ); $itemBeneficiary->addColumn( 'user_id', - 'integer', + Types::INTEGER, ['unsigned' => true] ); $itemBeneficiary->addColumn( 'commissions', - 'integer', + Types::INTEGER, ['unsigned' => true] ); $itemBeneficiary->setPrimaryKey(['id']); @@ -147,12 +167,12 @@ $commissions = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_COMMISSION); $commissions->addColumn( 'id', - 'integer', + Types::INTEGER, ['autoincrement' => true, 'unsigned' => true] ); $commissions->addColumn( 'commission', - 'integer', + Types::INTEGER, ['unsigned' => true] ); $commissions->setPrimaryKey(['id']); @@ -162,29 +182,29 @@ $saleCommissions = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_PAYPAL_PAYOUTS); $saleCommissions->addColumn( 'id', - 'integer', + Types::INTEGER, ['autoincrement' => true, 'unsigned' => true] ); - $saleCommissions->addColumn('date', 'datetime'); - $saleCommissions->addColumn('payout_date', 'datetime'); + $saleCommissions->addColumn('date', Types::DATETIME_MUTABLE); + $saleCommissions->addColumn('payout_date', Types::DATETIME_MUTABLE); $saleCommissions->addColumn( 'sale_id', - 'integer', + Types::INTEGER, ['unsigned' => true] ); $saleCommissions->addColumn( 'user_id', - 'integer', + Types::INTEGER, ['unsigned' => true] ); $saleCommissions->addColumn( 'commission', - 'decimal', + Types::DECIMAL, ['scale' => 2] ); $saleCommissions->addColumn( 'status', - 'integer', + Types::INTEGER, ['unsigned' => true] ); $saleCommissions->setPrimaryKey(['id']); @@ -194,51 +214,61 @@ $saleTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_SALE); $saleTable->addColumn( 'id', - 'integer', + Types::INTEGER, ['autoincrement' => true, 'unsigned' => true] ); - $saleTable->addColumn('reference', 'string'); - $saleTable->addColumn('date', 'datetime'); + $saleTable->addColumn('reference', Types::STRING); + $saleTable->addColumn('date', Types::DATETIME_MUTABLE); $saleTable->addColumn( 'user_id', - 'integer', + Types::INTEGER, ['unsigned' => true] ); - $saleTable->addColumn('product_type', 'integer'); - $saleTable->addColumn('product_name', 'string'); + $saleTable->addColumn('product_type', Types::INTEGER); + $saleTable->addColumn('product_name', Types::STRING); $saleTable->addColumn( 'product_id', - 'integer', + Types::INTEGER, ['unsigned' => true] ); $saleTable->addColumn( 'price', - 'decimal', + Types::DECIMAL, ['scale' => 2] ); $saleTable->addColumn( 'price_without_tax', - 'decimal', + Types::DECIMAL, ['scale' => 2, 'notnull' => false] ); $saleTable->addColumn( 'tax_perc', - 'integer', + Types::INTEGER, ['unsigned' => true, 'notnull' => false] ); $saleTable->addColumn( 'tax_amount', - 'decimal', + Types::DECIMAL, ['scale' => 2, 'notnull' => false] ); $saleTable->addColumn( 'currency_id', - 'integer', + Types::INTEGER, ['unsigned' => true] ); - $saleTable->addColumn('status', 'integer'); - $saleTable->addColumn('payment_type', 'integer'); - $saleTable->addColumn('invoice', 'integer'); + $saleTable->addColumn('status', Types::INTEGER); + $saleTable->addColumn('payment_type', Types::INTEGER); + $saleTable->addColumn('invoice', Types::INTEGER); + $saleTable->addColumn( + 'price_without_discount', + Types::DECIMAL, + ['scale' => 2, 'notnull' => false] + ); + $saleTable->addColumn( + 'discount_amount', + Types::DECIMAL, + ['scale' => 2, 'notnull' => false] + ); $saleTable->setPrimaryKey(['id']); $saleTable->addForeignKeyConstraint( $currencyTable, @@ -252,23 +282,24 @@ $servicesTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_SERVICES); $servicesTable->addColumn( 'id', - 'integer', + Types::INTEGER, ['autoincrement' => true, 'unsigned' => true] ); - $servicesTable->addColumn('title', 'string'); - $servicesTable->addColumn('description', 'text'); + $servicesTable->addColumn('name', Types::STRING); + $servicesTable->addColumn('description', Types::TEXT); $servicesTable->addColumn( 'price', - 'decimal', + Types::DECIMAL, ['scale' => 2] ); - $servicesTable->addColumn('duration_days', 'integer'); - $servicesTable->addColumn('applies_to', 'integer'); - $servicesTable->addColumn('owner_id', 'integer'); - $servicesTable->addColumn('visibility', 'integer'); - $servicesTable->addColumn('video_url', 'string'); - $servicesTable->addColumn('image', 'string'); - $servicesTable->addColumn('service_information', 'text'); + $servicesTable->addColumn('duration_days', Types::INTEGER); + $servicesTable->addColumn('applies_to', Types::INTEGER); + $servicesTable->addColumn('owner_id', Types::INTEGER); + $servicesTable->addColumn('visibility', Types::INTEGER); + $servicesTable->addColumn('video_url', Types::STRING); + $servicesTable->addColumn('image', Types::STRING); + $servicesTable->addColumn('service_information', Types::TEXT); + $servicesTable->addColumn('tax_perc', Types::INTEGER); $servicesTable->setPrimaryKey(['id']); } @@ -276,52 +307,62 @@ $servicesNodeTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_SERVICES_SALE); $servicesNodeTable->addColumn( 'id', - 'integer', + Types::INTEGER, ['autoincrement' => true, 'unsigned' => true] ); $servicesNodeTable->addColumn( 'service_id', - 'integer', + Types::INTEGER, ['unsigned' => true] ); - $servicesNodeTable->addColumn('reference', 'string'); - $servicesNodeTable->addColumn('currency_id', 'integer'); + $servicesNodeTable->addColumn('reference', Types::STRING); + $servicesNodeTable->addColumn('currency_id', Types::INTEGER); $servicesNodeTable->addColumn( 'price', - 'decimal', + Types::DECIMAL, ['scale' => 2] ); $servicesNodeTable->addColumn( 'price_without_tax', - 'decimal', + Types::DECIMAL, ['scale' => 2, 'notnull' => false] ); $servicesNodeTable->addColumn( 'tax_perc', - 'integer', + Types::INTEGER, ['unsigned' => true, 'notnull' => false] ); $servicesNodeTable->addColumn( 'tax_amount', - 'decimal', + Types::DECIMAL, ['scale' => 2, 'notnull' => false] ); - $servicesNodeTable->addColumn('node_type', 'integer'); - $servicesNodeTable->addColumn('node_id', 'integer'); - $servicesNodeTable->addColumn('buyer_id', 'integer'); - $servicesNodeTable->addColumn('buy_date', 'datetime'); + $servicesNodeTable->addColumn('node_type', Types::INTEGER); + $servicesNodeTable->addColumn('node_id', Types::INTEGER); + $servicesNodeTable->addColumn('buyer_id', Types::INTEGER); + $servicesNodeTable->addColumn('buy_date', Types::DATETIME_MUTABLE); $servicesNodeTable->addColumn( 'date_start', - 'datetime', + Types::DATETIME_MUTABLE, ['notnull' => false] ); $servicesNodeTable->addColumn( 'date_end', - 'datetime' + Types::DATETIME_MUTABLE + ); + $servicesNodeTable->addColumn('status', Types::INTEGER); + $servicesNodeTable->addColumn('payment_type', Types::INTEGER); + $servicesNodeTable->addColumn('invoice', Types::INTEGER); + $servicesNodeTable->addColumn( + 'price_without_discount', + Types::DECIMAL, + ['scale' => 2, 'notnull' => false] + ); + $servicesNodeTable->addColumn( + 'discount_amount', + Types::DECIMAL, + ['scale' => 2, 'notnull' => false] ); - $servicesNodeTable->addColumn('status', 'integer'); - $servicesNodeTable->addColumn('payment_type', 'integer'); - $servicesNodeTable->addColumn('invoice', 'integer'); $servicesNodeTable->setPrimaryKey(['id']); $servicesNodeTable->addForeignKeyConstraint( $servicesTable, @@ -335,12 +376,12 @@ $culqiTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_CULQI); $culqiTable->addColumn( 'id', - 'integer', + Types::INTEGER, ['autoincrement' => true, 'unsigned' => true] ); - $culqiTable->addColumn('commerce_code', 'string'); - $culqiTable->addColumn('api_key', 'string'); - $culqiTable->addColumn('integration', 'integer'); + $culqiTable->addColumn('commerce_code', Types::STRING); + $culqiTable->addColumn('api_key', Types::STRING); + $culqiTable->addColumn('integration', Types::INTEGER); $culqiTable->setPrimaryKey(['id']); } @@ -348,47 +389,225 @@ $globalTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_GLOBAL_CONFIG); $globalTable->addColumn( 'id', - 'integer', + Types::INTEGER, ['autoincrement' => true, 'unsigned' => true] ); - $globalTable->addColumn('terms_and_conditions', 'text'); - $globalTable->addColumn('global_tax_perc', 'integer'); - $globalTable->addColumn('tax_applies_to', 'integer'); - $globalTable->addColumn('tax_name', 'string'); - $globalTable->addColumn('seller_name', 'string'); - $globalTable->addColumn('seller_id', 'string'); - $globalTable->addColumn('seller_address', 'string'); - $globalTable->addColumn('seller_email', 'string'); - $globalTable->addColumn('next_number_invoice', 'integer'); - $globalTable->addColumn('invoice_series', 'string'); - $globalTable->addColumn('sale_email', 'string'); + $globalTable->addColumn('terms_and_conditions', Types::TEXT); + $globalTable->addColumn('global_tax_perc', Types::INTEGER); + $globalTable->addColumn('tax_applies_to', Types::INTEGER); + $globalTable->addColumn('tax_name', Types::STRING); + $globalTable->addColumn('seller_name', Types::STRING); + $globalTable->addColumn('seller_id', Types::STRING); + $globalTable->addColumn('seller_address', Types::STRING); + $globalTable->addColumn('seller_email', Types::STRING); + $globalTable->addColumn('next_number_invoice', Types::INTEGER); + $globalTable->addColumn('invoice_series', Types::STRING); + $globalTable->addColumn('sale_email', Types::STRING); + $globalTable->addColumn('info_email_extra', Types::TEXT); $globalTable->setPrimaryKey(['id']); +} else { + $globalTable = $pluginSchema->getTable(BuyCoursesPlugin::TABLE_GLOBAL_CONFIG); + + if (!$globalTable->hasColumn('info_email_extra')) { + $globalTable->addColumn('info_email_extra', Types::TEXT); + } } if (false === $sm->tablesExist(BuyCoursesPlugin::TABLE_INVOICE)) { $invoiceTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_INVOICE); $invoiceTable->addColumn( 'id', - 'integer', + Types::INTEGER, ['autoincrement' => true, 'unsigned' => true] ); - $invoiceTable->addColumn('sale_id', 'integer'); - $invoiceTable->addColumn('is_service', 'integer'); + $invoiceTable->addColumn('sale_id', Types::INTEGER); + $invoiceTable->addColumn('is_service', Types::INTEGER); $invoiceTable->addColumn( 'num_invoice', - 'integer', + Types::INTEGER, ['unsigned' => true, 'notnull' => false] ); $invoiceTable->addColumn( 'year', - 'integer', + Types::INTEGER, ['unsigned' => true, 'notnull' => false] ); - $invoiceTable->addColumn('serie', 'string'); - $invoiceTable->addColumn('date_invoice', 'datetime'); + $invoiceTable->addColumn('serie', Types::STRING); + $invoiceTable->addColumn('date_invoice', Types::DATETIME_MUTABLE); $invoiceTable->setPrimaryKey(['id']); } +if (false === $sm->tablesExist(BuyCoursesPlugin::TABLE_COUPON)) { + $couponTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_COUPON); + $couponTable->addColumn( + 'id', + Types::INTEGER, + ['autoincrement' => true, 'unsigned' => true] + ); + $couponTable->addColumn('code', Types::STRING); + $couponTable->addColumn('discount_type', Types::INTEGER); + $couponTable->addColumn('discount_amount', Types::INTEGER); + $couponTable->addColumn('valid_start', Types::DATETIME_MUTABLE); + $couponTable->addColumn('valid_end', Types::DATETIME_MUTABLE); + $couponTable->addColumn('delivered', Types::INTEGER); + $couponTable->addColumn('active', Types::BOOLEAN); + $couponTable->setPrimaryKey(['id']); +} + +if (false === $sm->tablesExist(BuyCoursesPlugin::TABLE_COUPON_ITEM)) { + $couponItemTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_COUPON_ITEM); + $couponItemTable->addColumn( + 'id', + Types::INTEGER, + ['autoincrement' => true, 'unsigned' => true] + ); + $couponItemTable->addColumn('coupon_id', Types::INTEGER); + $couponItemTable->addColumn('product_type', Types::INTEGER); + $couponItemTable->addColumn('product_id', Types::INTEGER); + $couponItemTable->setPrimaryKey(['id']); +} + +if (false === $sm->tablesExist(BuyCoursesPlugin::TABLE_COUPON_SERVICE)) { + $couponService = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_COUPON_SERVICE); + $couponService->addColumn( + 'id', + Types::INTEGER, + ['autoincrement' => true, 'unsigned' => true] + ); + $couponService->addColumn('coupon_id', Types::INTEGER); + $couponService->addColumn('service_id', Types::INTEGER); + $couponService->setPrimaryKey(['id']); +} + +if (false === $sm->tablesExist(BuyCoursesPlugin::TABLE_SUBSCRIPTION)) { + $subscriptionTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_SUBSCRIPTION); + $subscriptionTable->addColumn( + 'product_type', + Types::INTEGER, + ['unsigned' => true] + ); + $subscriptionTable->addColumn( + 'product_id', + Types::INTEGER, + ['unsigned' => true] + ); + $subscriptionTable->addColumn( + 'duration', + Types::INTEGER, + ['unsigned' => true] + ); + $subscriptionTable->addColumn('currency_id', Types::INTEGER); + $subscriptionTable->addColumn('price', Types::DECIMAL); + $subscriptionTable->addColumn('tax_perc', Types::INTEGER); + $subscriptionTable->setPrimaryKey(['product_type', 'product_id', 'duration']); +} + +if (false === $sm->tablesExist(BuyCoursesPlugin::TABLE_SUBSCRIPTION_SALE)) { + $subscriptionSaleTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_SUBSCRIPTION_SALE); + $subscriptionSaleTable->addColumn( + 'id', + Types::INTEGER, + ['autoincrement' => true, 'unsigned' => true] + ); + $subscriptionSaleTable->addColumn('currency_id', Types::INTEGER); + $subscriptionSaleTable->addColumn('reference', Types::STRING); + $subscriptionSaleTable->addColumn('date', Types::DATETIME_MUTABLE); + $subscriptionSaleTable->addColumn('user_id', Types::INTEGER); + $subscriptionSaleTable->addColumn('product_type', Types::INTEGER); + $subscriptionSaleTable->addColumn('product_name', Types::STRING); + $subscriptionSaleTable->addColumn('product_id', Types::INTEGER); + $subscriptionSaleTable->addColumn('price', Types::DECIMAL); + $subscriptionSaleTable->addColumn('price_without_tax', Types::DECIMAL, ['notnull' => false]); + $subscriptionSaleTable->addColumn('tax_perc', Types::INTEGER, ['notnull' => false]); + $subscriptionSaleTable->addColumn('tax_amount', Types::DECIMAL, ['notnull' => false]); + $subscriptionSaleTable->addColumn('status', Types::INTEGER); + $subscriptionSaleTable->addColumn('payment_type', Types::INTEGER); + $subscriptionSaleTable->addColumn('invoice', Types::INTEGER); + $subscriptionSaleTable->addColumn('price_without_discount', Types::DECIMAL); + $subscriptionSaleTable->addColumn('discount_amount', Types::DECIMAL); + $subscriptionSaleTable->addColumn('subscription_end', Types::DATETIME_MUTABLE); + $subscriptionSaleTable->addColumn('expired', Types::BOOLEAN); + $subscriptionSaleTable->setPrimaryKey(['id']); +} + +if (false === $sm->tablesExist(BuyCoursesPlugin::TABLE_SUBSCRIPTION_PERIOD)) { + $subscriptionPeriodTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_SUBSCRIPTION_PERIOD); + $subscriptionPeriodTable->addColumn( + 'duration', + Types::INTEGER, + ['unsigned' => true] + ); + $subscriptionPeriodTable->addColumn('name', Types::STRING); + $subscriptionPeriodTable->setPrimaryKey(['duration']); +} + +if (false === $sm->tablesExist(BuyCoursesPlugin::TABLE_COUPON_SALE)) { + $couponSaleTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_COUPON_SALE); + $couponSaleTable->addColumn( + 'id', + Types::INTEGER, + ['autoincrement' => true, 'unsigned' => true] + ); + $couponSaleTable->addColumn('coupon_id', Types::INTEGER); + $couponSaleTable->addColumn('sale_id', Types::INTEGER); + $couponSaleTable->setPrimaryKey(['id']); +} + +if (false === $sm->tablesExist(BuyCoursesPlugin::TABLE_COUPON_SERVICE_SALE)) { + $couponSaleTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_COUPON_SERVICE_SALE); + $couponSaleTable->addColumn( + 'id', + Types::INTEGER, + ['autoincrement' => true, 'unsigned' => true] + ); + $couponSaleTable->addColumn('coupon_id', Types::INTEGER); + $couponSaleTable->addColumn('service_sale_id', Types::INTEGER); + $couponSaleTable->setPrimaryKey(['id']); +} + +if (false === $sm->tablesExist(BuyCoursesPlugin::TABLE_STRIPE)) { + $stripeTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_STRIPE); + $stripeTable->addColumn( + 'id', + Types::INTEGER, + ['autoincrement' => true, 'unsigned' => true] + ); + $stripeTable->addColumn('account_id', Types::STRING); + $stripeTable->addColumn('secret_key', Types::STRING); + $stripeTable->addColumn('endpoint_secret', Types::STRING); + $stripeTable->setPrimaryKey(['id']); +} + +if (false === $sm->tablesExist(BuyCoursesPlugin::TABLE_TPV_CECABANK)) { + $tpvCecabankTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_TPV_CECABANK); + $tpvCecabankTable->addColumn( + 'id', + Types::INTEGER, + ['autoincrement' => true, 'unsigned' => true] + ); + $tpvCecabankTable->addColumn('crypto_key', Types::STRING); + $tpvCecabankTable->addColumn('merchant_id', Types::STRING); + $tpvCecabankTable->addColumn('acquirer_bin', Types::STRING); + $tpvCecabankTable->addColumn('terminal_id', Types::STRING); + $tpvCecabankTable->addColumn('cypher', Types::STRING); + $tpvCecabankTable->addColumn('exponent', Types::STRING); + $tpvCecabankTable->addColumn('supported_payment', Types::STRING); + $tpvCecabankTable->addColumn('url', Types::STRING); + $tpvCecabankTable->setPrimaryKey(['id']); +} + +if (false === $sm->tablesExist(BuyCoursesPlugin::TABLE_COUPON_SUBSCRIPTION_SALE)) { + $couponSubscriptionSaleTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_COUPON_SUBSCRIPTION_SALE); + $couponSubscriptionSaleTable->addColumn( + 'id', + Types::INTEGER, + ['autoincrement' => true, 'unsigned' => true] + ); + $couponSubscriptionSaleTable->addColumn('coupon_id', Types::INTEGER); + $couponSubscriptionSaleTable->addColumn('sale_id', Types::INTEGER); + $couponSubscriptionSaleTable->setPrimaryKey(['id']); +} + $queries = $pluginSchema->toSql($platform); foreach ($queries as $query) { @@ -404,9 +623,11 @@ $extraFieldTable = Database::get_main_table(TABLE_EXTRA_FIELD); $culqiTable = Database::get_main_table(BuyCoursesPlugin::TABLE_CULQI); $globalTable = Database::get_main_table(BuyCoursesPlugin::TABLE_GLOBAL_CONFIG); +$tpvRedsysTable = Database::get_main_table(BuyCoursesPlugin::TABLE_TPV_REDSYS); +$stripeTable = Database::get_main_table(BuyCoursesPlugin::TABLE_STRIPE); $paypalExtraField = Database::select( - "*", + '*', $extraFieldTable, [ 'where' => ['variable = ?' => 'paypal'], @@ -442,6 +663,14 @@ ] ); +Database::insert( + $tpvRedsysTable, + [ + 'url_redsys' => 'https://sis.redsys.es/sis/realizarPago', + 'url_redsys_sandbox' => 'https://sis-t.redsys.es:25443/sis/realizarPago', + ] +); + Database::insert( $culqiTable, [ @@ -455,16 +684,6 @@ $globalTable, [ 'terms_and_conditions' => '', - 'global_tax_perc' => 0, - 'tax_applies_to' => 0, - 'tax_name' => '', - 'seller_name' => '', - 'seller_id' => '', - 'seller_address' => '', - 'seller_email' => '', - 'next_number_invoice' => 1, - 'invoice_series' => '', - 'sale_email' => '', ] ); @@ -475,6 +694,15 @@ ] ); +Database::insert( + $stripeTable, + [ + 'account_id' => '', + 'secret_key' => '', + 'endpoint_secret' => '', + ] +); + $currencies = [ ['AD', 'Andorra', 'EUR', 'AND', 0], ['AE', 'United Arab Emirates', 'AED', 'ARE', 0], @@ -731,7 +959,7 @@ foreach ($currencies as $currency) { $value = Database::select( - "*", + '*', $currencyTable, [ 'where' => ['country_code = ?' => $currency[0]], diff --git a/public/plugin/BuyCourses/index.php b/public/plugin/BuyCourses/index.php index fefacd452bd..45974575ad6 100644 --- a/public/plugin/BuyCourses/index.php +++ b/public/plugin/BuyCourses/index.php @@ -1,7 +1,11 @@ configurar pagamento - Defina a moeda com a qual você gostaria de vender seus cursos ou sessões"; -$strings['InstructionsStepThree'] = "Para configurar cursos para vender. Vá em: configurar cursos e preços."; -$strings['BuyCourses'] = "Comprar cursos"; -$strings['ConfigurationOfCoursesAndPrices'] = "Configurar cursos e preços "; -$strings['SalesReport'] = "Relatório de vendas"; -$strings['UserInformation'] = "Detalhes do comprador"; -$strings['PaymentMethods'] = "Métodos de pagamento"; -$strings['ConfirmOrder'] = "Confirmar pedido"; -$strings['PurchaseData'] = "Dados de Compra"; -$strings['bc_subject'] = "Confirmação de ordem do curso"; -$strings['PurchaseStatusX'] = "Estado de compra: %s"; -$strings['PendingReasonByTransfer'] = " Pendente Aguardando confirmação da transferência."; -$strings['CancelOrder'] = "Anular ordem"; -$strings['BankAccountInformation'] = "Detalhes da conta bancária"; -$strings['BankAccount'] = "Contas bancárias"; -$strings['OnceItIsConfirmedYouWillReceiveAnEmailWithTheBankInformationAndAnOrderReference'] = "Uma vez confirmada, você receberá um e-mail com os dados bancários e uma referência de ordem."; -$strings['SubscriptionToCourseXSuccessful'] = "Sua inscrição para \"%s\" foi concluída com sucesso."; -$strings['OrderCanceled'] = "Ordem cancelada"; -$strings['OrderStatus'] = "Status do pedido"; -$strings['SearchByStatus'] = "Pesquisar por estado"; -$strings['OrderReference'] = "Código de encomenda"; -$strings['OrderDate'] = "Data do pedido"; -$strings['ProductType'] = "Tipo de produto"; -$strings['SubscribeUser'] = "Inscrever utilizador"; -$strings['DeleteOrder'] = "Excluir a ordem"; -$strings['ErrorContactPlatformAdmin'] = "Por favor entre em contato com o administrador da plataforma de erro desconhecido.."; -$strings['PendingReasonByAddress'] = ". Pendente Você não digitou um endereço de entrega confirmado."; -$strings['PendingReasonByAuthorization'] = " Pendentes fundos Nós ainda não capturados.."; -$strings['PendingReasonByEcheck'] = " Pendente O pagamento foi feito por um eCheck que ainda não foi eliminada."; -$strings['PendingReasonByIntl'] = " Pendente Nós não temos um mecanismo de retirada de fundos.."; -$strings['PendingReasonByMulticurrency'] = " Pendente Nós não equilibrar na moeda enviado."; -$strings['PendingReasonByOrder'] = " Pendente Ordem feita Nós ainda não capturado fundos..."; -$strings['PendingReasonByPaymentReview'] = " Pendente O pagamento está sendo revisto pelo PayPal para o risco."; -$strings['PendingReasonByRegulatoryReview'] = " Pendente O pagamento está sendo revisado para conformidade com regulamentações governamentais.."; -$strings['PendingReasonByUnilateral'] = " Pendente O e-mail ainda não está registrado o confirmou.."; -$strings['PendingReasonByUpgrade'] = " Pendente O pagamento foi feito por cartão de crédito.."; -$strings['PendingReasonByVerify'] = " Pendente Desculpe Nós ainda não são verificados no PayPal..."; -$strings['PendingReasonByOther'] = " Pendente Por favor, entre em contato com o administrador da plataforma.."; + +declare(strict_types=1); +$strings['plugin_title'] = 'Vender cursos'; +$strings['plugin_comment'] = 'Venda cursos diretamente através de seu portal Chamilo, usando uma conta do PayPal para receber fundos Nem a associação Chamilo nem os desenvolvedores envolvidos poderia ser considerado responsável de qualquer problema que você pode sofrer de usar este plugin..'; +$strings['show_main_menu_tab'] = 'Mostrar guia no menu principal'; +$strings['show_main_menu_tab_help'] = 'No caso de não querer mostrar a guia, você pode criar esse link em sua página Chamilo: %s'; +$strings['include_sessions'] = 'Incluir sessões'; +$strings['paypal_enable'] = 'Ativar PayPal'; +$strings['transfer_enable'] = 'Permitir transferência bancária'; +$strings['unregistered_users_enable'] = 'Permitir que usuários anônimos'; +$strings['Free'] = 'GRÁTIS'; +$strings['banktransfer'] = 'Transferência Bancária'; +$strings['SaleStatusPending'] = 'Venda pendente'; +$strings['SaleStatusCanceled'] = 'Venda cancelada'; +$strings['SaleStatusCompleted'] = 'Venda concluída'; +$strings['CourseListOnSale'] = 'Lista de cursos à venda'; +$strings['AvailableCourses'] = 'Campos disponíveis'; +$strings['Price'] = 'Preço'; +$strings['SearchFilter'] = 'Filtrar Pesquisa'; +$strings['MinimumPrice'] = 'Preço mínimo'; +$strings['MaximumPrice'] = 'Preço máximo'; +$strings['AvailableCoursesConfiguration'] = 'Configuração cursos disponíveis'; +$strings['PaymentsConfiguration'] = 'Configurar pagamentos'; +$strings['TheUserIsAlreadyRegisteredInTheCourse'] = 'Você está registrado no curso.'; +$strings['SeeDescription'] = 'Descriçao'; +$strings['Buy'] = 'Comprar'; +$strings['WaitingToReceiveThePayment'] = 'Atualmente pendente de pagamento'; +$strings['TheUserIsAlreadyRegisteredInTheSession'] = 'Você já está registrado na sessão'; +$strings['ItemNotSaved'] = 'Item não salvo'; +$strings['TitlePlugin'] = 'Tudo o que você precisa para ensinar e vender cursos on-line'; +$strings['PluginPresentation'] = 'O BuyCourses Plugin dá-lhe os meios para vender seus cursos ou sessões e ensinar on-line, através de apenas alguns passos e configurações simples. O que você está esperando para começar a vender cursos através Chamilo LMS?'; +$strings['Instructions'] = 'Instruções'; +$strings['InstructionsStepOne'] = 'Criar um curso ou sessão na plataforma.'; +$strings['InstructionsStepTwo'] = 'Em - configurar pagamento - Defina a moeda com a qual você gostaria de vender seus cursos ou sessões'; +$strings['InstructionsStepThree'] = 'Para configurar cursos para vender. Vá em: configurar cursos e preços.'; +$strings['BuyCourses'] = 'Comprar cursos'; +$strings['ConfigurationOfCoursesAndPrices'] = 'Configurar cursos e preços '; +$strings['SalesReport'] = 'Relatório de vendas'; +$strings['UserInformation'] = 'Detalhes do comprador'; +$strings['PaymentMethods'] = 'Métodos de pagamento'; +$strings['ConfirmOrder'] = 'Confirmar pedido'; +$strings['PurchaseData'] = 'Dados de Compra'; +$strings['bc_subject'] = 'Confirmação de ordem do curso'; +$strings['PurchaseStatusX'] = 'Estado de compra: %s'; +$strings['PendingReasonByTransfer'] = ' Pendente Aguardando confirmação da transferência.'; +$strings['CancelOrder'] = 'Anular ordem'; +$strings['BankAccountInformation'] = 'Detalhes da conta bancária'; +$strings['BankAccount'] = 'Contas bancárias'; +$strings['OnceItIsConfirmedYouWillReceiveAnEmailWithTheBankInformationAndAnOrderReference'] = 'Uma vez confirmada, você receberá um e-mail com os dados bancários e uma referência de ordem.'; +$strings['SubscriptionToCourseXSuccessful'] = 'Sua inscrição para "%s" foi concluída com sucesso.'; +$strings['OrderCanceled'] = 'Ordem cancelada'; +$strings['OrderStatus'] = 'Status do pedido'; +$strings['SearchByStatus'] = 'Pesquisar por estado'; +$strings['OrderReference'] = 'Código de encomenda'; +$strings['OrderDate'] = 'Data do pedido'; +$strings['ProductType'] = 'Tipo de produto'; +$strings['SubscribeUser'] = 'Inscrever utilizador'; +$strings['DeleteOrder'] = 'Excluir a ordem'; +$strings['ErrorContactPlatformAdmin'] = 'Por favor entre em contato com o administrador da plataforma de erro desconhecido..'; +$strings['PendingReasonByAddress'] = '. Pendente Você não digitou um endereço de entrega confirmado.'; +$strings['PendingReasonByAuthorization'] = ' Pendentes fundos Nós ainda não capturados..'; +$strings['PendingReasonByEcheck'] = ' Pendente O pagamento foi feito por um eCheck que ainda não foi eliminada.'; +$strings['PendingReasonByIntl'] = ' Pendente Nós não temos um mecanismo de retirada de fundos..'; +$strings['PendingReasonByMulticurrency'] = ' Pendente Nós não equilibrar na moeda enviado.'; +$strings['PendingReasonByOrder'] = ' Pendente Ordem feita Nós ainda não capturado fundos...'; +$strings['PendingReasonByPaymentReview'] = ' Pendente O pagamento está sendo revisto pelo PayPal para o risco.'; +$strings['PendingReasonByRegulatoryReview'] = ' Pendente O pagamento está sendo revisado para conformidade com regulamentações governamentais..'; +$strings['PendingReasonByUnilateral'] = ' Pendente O e-mail ainda não está registrado o confirmou..'; +$strings['PendingReasonByUpgrade'] = ' Pendente O pagamento foi feito por cartão de crédito..'; +$strings['PendingReasonByVerify'] = ' Pendente Desculpe Nós ainda não são verificados no PayPal...'; +$strings['PendingReasonByOther'] = ' Pendente Por favor, entre em contato com o administrador da plataforma..'; $strings['PayPalPaymentOKPleaseConfirm'] = "PayPal relata a transação está pronto para ser executado. Para confirmar que você está OK para prosseguir, clique no botão de confirmação abaixo. Uma vez clicado, você será registrado para o curso e os fundos serão transferido da sua conta PayPal para a nossa loja. Você sempre pode acessar seus cursos através da aba 'Meus cursos' Obrigado por seu costume.!"; -$strings['Sandbox'] = "Ambiente de teste"; -$strings['PayPalConfig'] = "Configuração PayPal:"; -$strings['TransfersConfig'] = "Configurar transferências bancárias:"; -$strings['PluginInstruction'] = "Você pode ativar ou desativar a opção de pagar via PayPal ou transferência bancária na seção de configuração do plugin."; -$strings['ClickHere'] = " Clique aqui para obter mais detalhes"; -$strings['CurrencyType'] = "Tipo de moeda"; -$strings['InfoCurrency'] = "Define a moeda para o pagamento de seus cursos."; -$strings['ApiUsername'] = "Nome de usuário API"; -$strings['ApiPassword'] = "Senha API"; -$strings['ApiSignature'] = "Assinatura API"; -$strings['InfoApiCredentials'] = "Para gerar as suas credenciais de API para integrar Chamilo com a sua conta PayPal, você deve seguir os seguintes passos"; -$strings['InfoApiStepOne'] = "Vá para a sua conta do PayPal, Resumo , então preferências do vendedor na Ferramentas de Vendas menu (se o fizer não ter este item de menu, você pode precisar de obter a autorização para vender o material através do PayPal em primeiro lugar)."; -$strings['InfoApiStepTwo'] = "No parágrafo Acesso API , clique em Atualizar "; -$strings['InfoApiStepThree'] = "Na opção 2 (credenciais de solicitação de API para criar o seu próprio nome de usuário e senha API), clique no botão Exibir API Assinatura link , e copiar as credenciais apresentadas à direita no formulário do plugin BuyCourses."; -$strings['ErrorOccurred'] = " Ocorreu um erro Código: %s. Mensagem: %s. Por favor, entre em contato com o Admin da plataforma."; -$strings['VisibleInCatalog'] = "Visível no catálogo"; -$strings['Beneficiaries'] = "Beneficiários"; -$strings['AvailableCourse'] = "Campo disponível"; -$strings['ShowOnCourseCatalog'] = "Mostrar no catálogo de cursos"; -$strings['ByStatus'] = "Por estado"; -$strings['ByUser'] = "Por usuário"; -$strings['ByEmail'] = "Por email"; -$strings['PaymentMethod'] = "Método de pagamento"; -$strings['SWIFT'] = "Código SWIFT"; -$strings['SWIFT_help'] = "Formato padrão de Códigos de Identificação Bancária (BIC) e serve como um identificador exclusivo de um banco ou instituição financeira"; -$strings['ExportReport'] = "Exportar Relatório de Vendas"; -$strings['OrderTime'] = "Hora do pedido"; -$strings['SelectDateRange'] = "Selecione uma data de início e uma data de término para o relatório"; +$strings['Sandbox'] = 'Ambiente de teste'; +$strings['PayPalConfig'] = 'Configuração PayPal:'; +$strings['TransfersConfig'] = 'Configurar transferências bancárias:'; +$strings['PluginInstruction'] = 'Você pode ativar ou desativar a opção de pagar via PayPal ou transferência bancária na seção de configuração do plugin.'; +$strings['ClickHere'] = ' Clique aqui para obter mais detalhes'; +$strings['CurrencyType'] = 'Tipo de moeda'; +$strings['InfoCurrency'] = 'Define a moeda para o pagamento de seus cursos.'; +$strings['ApiUsername'] = 'Nome de usuário API'; +$strings['ApiPassword'] = 'Senha API'; +$strings['ApiSignature'] = 'Assinatura API'; +$strings['InfoApiCredentials'] = 'Para gerar as suas credenciais de API para integrar Chamilo com a sua conta PayPal, você deve seguir os seguintes passos'; +$strings['InfoApiStepOne'] = 'Vá para a sua conta do PayPal, Resumo , então preferências do vendedor na Ferramentas de Vendas menu (se o fizer não ter este item de menu, você pode precisar de obter a autorização para vender o material através do PayPal em primeiro lugar).'; +$strings['InfoApiStepTwo'] = 'No parágrafo Acesso API , clique em Atualizar '; +$strings['InfoApiStepThree'] = 'Na opção 2 (credenciais de solicitação de API para criar o seu próprio nome de usuário e senha API), clique no botão Exibir API Assinatura link , e copiar as credenciais apresentadas à direita no formulário do plugin BuyCourses.'; +$strings['ErrorOccurred'] = ' Ocorreu um erro Código: %s. Mensagem: %s. Por favor, entre em contato com o Admin da plataforma.'; +$strings['VisibleInCatalog'] = 'Visível no catálogo'; +$strings['Beneficiaries'] = 'Beneficiários'; +$strings['AvailableCourse'] = 'Campo disponível'; +$strings['ShowOnCourseCatalog'] = 'Mostrar no catálogo de cursos'; +$strings['ByStatus'] = 'Por estado'; +$strings['ByUser'] = 'Por usuário'; +$strings['ByEmail'] = 'Por email'; +$strings['PaymentMethod'] = 'Método de pagamento'; +$strings['SWIFT'] = 'Código SWIFT'; +$strings['SWIFT_help'] = 'Formato padrão de Códigos de Identificação Bancária (BIC) e serve como um identificador exclusivo de um banco ou instituição financeira'; +$strings['ExportReport'] = 'Exportar Relatório de Vendas'; +$strings['OrderTime'] = 'Hora do pedido'; +$strings['SelectDateRange'] = 'Selecione uma data de início e uma data de término para o relatório'; diff --git a/public/plugin/BuyCourses/lang/dutch.php b/public/plugin/BuyCourses/lang/dutch.php index d265f6c109e..5379ebe6f23 100644 --- a/public/plugin/BuyCourses/lang/dutch.php +++ b/public/plugin/BuyCourses/lang/dutch.php @@ -1,135 +1,137 @@ %s . Deze percentage zal invermindering gebracht worden van de totale prijs van het product. Het verschil in het bedrag zal aan de volgende commissies toegepast worden."; -$strings['CoursesInSessionsDoesntDisplayHere'] = "De cursussen die binnen een training zitten worden niet weergegeven in de lijst van de cursussen als afzonderlijke producten."; -$strings['WantToSellCourses'] = "Wilt u leren en wat geld verdienen met uw eigen cursussen? Deze lijst kan worden gevuld met de verkoop van uw cursussen via dit platform. Neem contact op met ons."; -$strings['SelectOptionToProceed'] = "Selecteer de optie om verder te gaan"; -$strings['VerifyTotalAmountToProceedPayout'] = "Controleer het totale bedrag voor over te gaan tot de betaling van commissies. Dit bedrag heeft geen extra kosten voor PayPal rekening houder. De cursussen verkopen die niet meetellen met een Paypal-account voor de begunstigden niet in aanmerking genomen."; -$strings['TotalAcounts'] = "Totaal van de rekeningen:"; -$strings['TotalPayout'] = "Totaal te betalen:"; -$strings['PayoutDate'] = "Betaaldatum:"; -$strings['CautionThisProcessCantBeCanceled'] = "Let op:Dit proces kan een paar minuten duren en kan niet worden geannuleerd."; -$strings['ProcessingPayoutsDontCloseThisWindow'] = "Verder gaan met betalingen. Dit venster niet sluiten totdat dit proces is afgerond."; -$strings['PayoutSuccess'] = "Betalingen met succes verwerkt"; -$strings['Buyer'] = "Koper"; -$strings['BankTransfer'] = "Bankoverschrijving"; -$strings['SaleInfo'] = "Verkoop Informatie"; -$strings['SaleStatusPending'] = "Verkoop in behandeling"; -$strings['SaleStatusCanceled'] = "Verkoop geannuleerd"; -$strings['SaleStatusCompleted'] = "verkoop afgerond"; -$strings['PayoutStatusPending'] = "Betaling in behandeling"; -$strings['PayoutStatusCanceled'] = "Betaling geannuleerd"; -$strings['PayoutStatusCompleted'] = "Betaling afgerond"; -$strings['PayoutsTotalPending'] = "Onafgehandelde betalingen:"; -$strings['PayoutsTotalCanceled'] = "Geannuleerde betalingen:"; -$strings['PayoutsTotalCompleted'] = "Afgeronde betalingen:"; -$strings['TotalAmount'] = "Totaalbedrag:"; -$strings['CourseListOnSale'] = "Lijst van cursussen in verkoop"; -$strings['AvailableCourses'] = "Beschikbare Cursussen"; -$strings['Price'] = "Prijs"; -$strings['SearchFilter'] = "Zoekmachine"; -$strings['MinimumPrice'] = "Minimuum prijs"; -$strings['MaximumPrice'] = "Maximuumm prijs"; -$strings['AvailableCoursesConfiguration'] = "Beschikbare cursussen overzicht"; -$strings['PaymentsConfiguration'] = "Betalingsoverzicht"; -$strings['TheUserIsAlreadyRegisteredInTheCourse'] = "Je bent geregistreerd voor de cursus."; -$strings['SeeDescription'] = "Omschrijving"; -$strings['Buy'] = "Koop / inschrijven"; -$strings['WaitingToReceiveThePayment'] = "In afwachting van uw betaling"; -$strings['TheUserIsAlreadyRegisteredInTheSession'] = "Je bent geregistreerd voor de sessie"; -$strings['ItemNotSaved'] = "Item niet opgeslagen"; -$strings['TitlePlugin'] = "Alles wat je nodig hebt om online cursussen te onderwijzen en te verkopen"; -$strings['PluginPresentation'] = "The BuyCourses Plugin gives you the means to sell your courses or sessions and teach online, through only a few simple steps and settings. What are you waiting for? Start selling courses through Chamilo LMS!"; -$strings['Instructions'] = "Instructies"; -$strings['InstructionsStepOne'] = "Maak een cursus of een sessie op het platform."; -$strings['InstructionsStepTwo'] = "In de betaling , stelt de valuta waarmee u wilt uw cursussen of sessies te verkopen."; -$strings['InstructionsStepThree'] = "Overzicht van cursussen om te verkopen in de Cursussen en prijzen Instellingen."; -$strings['BuyCourses'] = "Cursussen Kopen"; -$strings['ConfigurationOfCoursesAndPrices'] = "Cursussen en Prijzen overzicht"; -$strings['SalesReport'] = "Verkoopcijfers"; -$strings['UserInformation'] = "Gegevens van de koper"; -$strings['PaymentMethods'] = "Betaal methodes"; -$strings['ConfirmOrder'] = "Bevestig bestelling"; -$strings['PurchaseData'] = "Aankoopgegevens"; -$strings['bc_subject'] = "Bevestiging cursus order"; -$strings['PurchaseStatusX'] = "Aankoop status: %s"; -$strings['PendingReasonByTransfer'] = " In afwachting . In afwachting van de overdracht bevestiging"; -$strings['CancelOrder'] = "Annuleer order"; -$strings['BankAccountInformation'] = "Bankgegevens"; -$strings['BankAccount'] = "Bankrekening"; -$strings['OnceItIsConfirmedYouWillReceiveAnEmailWithTheBankInformationAndAnOrderReference'] = "Eenmaal bevestigd, ontvangt u een e-mail met de bankgegevens en een orderreferentie."; -$strings['SubscriptionToCourseXSuccessful'] = "Uw aankoop op \"%s\" werd met succes afgerond."; -$strings['OrderCanceled'] = "Order geannuleerd"; -$strings['OrderStatus'] = "Bestelstatus"; -$strings['PayoutStatus'] = "Betalingsstatus"; -$strings['SearchByStatus'] = "Zoeken op status"; -$strings['OrderReference'] = "Bestelreferentie"; -$strings['OrderDate'] = "Besteldatum"; -$strings['OrderPrice'] = "Bestel prijs"; -$strings['ProductType'] = "Artikel type"; -$strings['SubscribeUser'] = "Onderteken gebruiker"; -$strings['DeleteOrder'] = "Verwijder bestelling"; -$strings['ErrorContactPlatformAdmin'] = "Onbekende fout. Neem contact op met het platform administrator."; -$strings['PendingReasonByAddress'] = "in afwachting. Je hebt een bevestigde verzendadres niet in te voeren."; -$strings['PendingReasonByAuthorization'] = "in afwachting. We hebben nog geen betalingen."; -$strings['PendingReasonByEcheck'] = "in afwachting. De betaling is verricht door een eCheck die nog niet is vrijgemaakt"; -$strings['PendingReasonByIntl'] = "in afwachting.We hebben geen terugtrekking mechanisme voor betalingen."; -$strings['PendingReasonByMulticurrency'] = "in afwachting. We hebben niet in evenwicht te brengen in de valuta verzonden."; -$strings['PendingReasonByOrder'] = "in afwachting. Bestelling. We hebben nog geen betalingen."; -$strings['PendingReasonByPaymentReview'] = "in afwachting. De betaling wordt beoordeeld door PayPal voor risico."; -$strings['PendingReasonByRegulatoryReview'] = "in afwachting. De betaling wordt beoordeeld voor de naleving van regelgeving van de overheid."; -$strings['PendingReasonByUnilateral'] = "in afwachting. Het e-mailadres is nog niet geregistreerd of bevestigd."; -$strings['PendingReasonByUpgrade'] = "in afwachting. De betaling werd gedaan via credit card."; -$strings['PendingReasonByVerify'] = "in afwachting. Sorry. We zijn nog niet in Paypal geverifieerd."; -$strings['PendingReasonByOther'] = "in afwachting. Neem contact op met het platform admin."; + +declare(strict_types=1); +$strings['plugin_title'] = 'Sell courses'; +$strings['plugin_comment'] = 'Sell courses directly through your Chamilo portal, using a PayPal account to receive funds. Neither the Chamilo association nor the developers involved could be considered responsible of any issue you might suffer using this plugin.'; +$strings['show_main_menu_tab'] = 'Show tab in hoofdmenu'; +$strings['show_main_menu_tab_help'] = 'In case of not wanting to show the tab, you can create this link in your Chamilo homepage : %s'; +$strings['public_main_menu_tab'] = 'Show tab in main menu aan anonyme gebruikers'; +$strings['include_sessions'] = 'Inclusief sessies'; +$strings['paypal_enable'] = 'Instellen van PayPal'; +$strings['commissions_enable'] = 'Instellen van Opdrachten'; +$strings['transfer_enable'] = 'Instellen van bankoverschrijving'; +$strings['unregistered_users_enable'] = 'Sta anonieme gebruikers toe'; +$strings['Free'] = 'FREE'; +$strings['PaypalPayoutCommissions'] = 'Paypal Uitbetaal Opdrachten'; +$strings['MyPayouts'] = 'Mijn Betalingsoverzicht'; +$strings['Commission'] = 'Commissie'; +$strings['Commissions'] = 'Commissies'; +$strings['SetCommissions'] = 'Set commissies'; +$strings['CommissionsConfig'] = 'Configureren Commissies'; +$strings['PayoutReport'] = 'Uitbetalingen rapport'; +$strings['Stats'] = 'Statistieken'; +$strings['InfoCommissions'] = 'Vul hier de omzet commissie als een percentage(% ) , de organisatie die het platform controleert. Deze percentage wordt in mindering gebracht op de door docenten ontvangen bedrag voor elke cursus of sessie dat verkocht is.'; +$strings['NeedToSelectPaymentType'] = 'Hier u betalings type selecteren'; +$strings['IndividualPayout'] = 'Individuele uitbetalingen'; +$strings['CancelPayout'] = 'Annuleer Uitbetalingen'; +$strings['ContinuePayout'] = 'Continue Uitbetalingen'; +$strings['ProceedPayout'] = 'Ga verder met de betaling'; +$strings['TheActualPlatformCommissionIsX'] = 'De huidige platform commissie %s . Deze percentage zal invermindering gebracht worden van de totale prijs van het product. Het verschil in het bedrag zal aan de volgende commissies toegepast worden.'; +$strings['CoursesInSessionsDoesntDisplayHere'] = 'De cursussen die binnen een training zitten worden niet weergegeven in de lijst van de cursussen als afzonderlijke producten.'; +$strings['WantToSellCourses'] = 'Wilt u leren en wat geld verdienen met uw eigen cursussen? Deze lijst kan worden gevuld met de verkoop van uw cursussen via dit platform. Neem contact op met ons.'; +$strings['SelectOptionToProceed'] = 'Selecteer de optie om verder te gaan'; +$strings['VerifyTotalAmountToProceedPayout'] = 'Controleer het totale bedrag voor over te gaan tot de betaling van commissies. Dit bedrag heeft geen extra kosten voor PayPal rekening houder. De cursussen verkopen die niet meetellen met een Paypal-account voor de begunstigden niet in aanmerking genomen.'; +$strings['TotalAcounts'] = 'Totaal van de rekeningen:'; +$strings['TotalPayout'] = 'Totaal te betalen:'; +$strings['PayoutDate'] = 'Betaaldatum:'; +$strings['CautionThisProcessCantBeCanceled'] = 'Let op:Dit proces kan een paar minuten duren en kan niet worden geannuleerd.'; +$strings['ProcessingPayoutsDontCloseThisWindow'] = 'Verder gaan met betalingen. Dit venster niet sluiten totdat dit proces is afgerond.'; +$strings['PayoutSuccess'] = 'Betalingen met succes verwerkt'; +$strings['Buyer'] = 'Koper'; +$strings['BankTransfer'] = 'Bankoverschrijving'; +$strings['SaleInfo'] = 'Verkoop Informatie'; +$strings['SaleStatusPending'] = 'Verkoop in behandeling'; +$strings['SaleStatusCanceled'] = 'Verkoop geannuleerd'; +$strings['SaleStatusCompleted'] = 'verkoop afgerond'; +$strings['PayoutStatusPending'] = 'Betaling in behandeling'; +$strings['PayoutStatusCanceled'] = 'Betaling geannuleerd'; +$strings['PayoutStatusCompleted'] = 'Betaling afgerond'; +$strings['PayoutsTotalPending'] = 'Onafgehandelde betalingen:'; +$strings['PayoutsTotalCanceled'] = 'Geannuleerde betalingen:'; +$strings['PayoutsTotalCompleted'] = 'Afgeronde betalingen:'; +$strings['TotalAmount'] = 'Totaalbedrag:'; +$strings['CourseListOnSale'] = 'Lijst van cursussen in verkoop'; +$strings['AvailableCourses'] = 'Beschikbare Cursussen'; +$strings['Price'] = 'Prijs'; +$strings['SearchFilter'] = 'Zoekmachine'; +$strings['MinimumPrice'] = 'Minimuum prijs'; +$strings['MaximumPrice'] = 'Maximuumm prijs'; +$strings['AvailableCoursesConfiguration'] = 'Beschikbare cursussen overzicht'; +$strings['PaymentsConfiguration'] = 'Betalingsoverzicht'; +$strings['TheUserIsAlreadyRegisteredInTheCourse'] = 'Je bent geregistreerd voor de cursus.'; +$strings['SeeDescription'] = 'Omschrijving'; +$strings['Buy'] = 'Koop / inschrijven'; +$strings['WaitingToReceiveThePayment'] = 'In afwachting van uw betaling'; +$strings['TheUserIsAlreadyRegisteredInTheSession'] = 'Je bent geregistreerd voor de sessie'; +$strings['ItemNotSaved'] = 'Item niet opgeslagen'; +$strings['TitlePlugin'] = 'Alles wat je nodig hebt om online cursussen te onderwijzen en te verkopen'; +$strings['PluginPresentation'] = 'The BuyCourses Plugin gives you the means to sell your courses or sessions and teach online, through only a few simple steps and settings. What are you waiting for? Start selling courses through Chamilo LMS!'; +$strings['Instructions'] = 'Instructies'; +$strings['InstructionsStepOne'] = 'Maak een cursus of een sessie op het platform.'; +$strings['InstructionsStepTwo'] = 'In de betaling , stelt de valuta waarmee u wilt uw cursussen of sessies te verkopen.'; +$strings['InstructionsStepThree'] = 'Overzicht van cursussen om te verkopen in de Cursussen en prijzen Instellingen.'; +$strings['BuyCourses'] = 'Cursussen Kopen'; +$strings['ConfigurationOfCoursesAndPrices'] = 'Cursussen en Prijzen overzicht'; +$strings['SalesReport'] = 'Verkoopcijfers'; +$strings['UserInformation'] = 'Gegevens van de koper'; +$strings['PaymentMethods'] = 'Betaal methodes'; +$strings['ConfirmOrder'] = 'Bevestig bestelling'; +$strings['PurchaseData'] = 'Aankoopgegevens'; +$strings['bc_subject'] = 'Bevestiging cursus order'; +$strings['PurchaseStatusX'] = 'Aankoop status: %s'; +$strings['PendingReasonByTransfer'] = ' In afwachting . In afwachting van de overdracht bevestiging'; +$strings['CancelOrder'] = 'Annuleer order'; +$strings['BankAccountInformation'] = 'Bankgegevens'; +$strings['BankAccount'] = 'Bankrekening'; +$strings['OnceItIsConfirmedYouWillReceiveAnEmailWithTheBankInformationAndAnOrderReference'] = 'Eenmaal bevestigd, ontvangt u een e-mail met de bankgegevens en een orderreferentie.'; +$strings['SubscriptionToCourseXSuccessful'] = 'Uw aankoop op "%s" werd met succes afgerond.'; +$strings['OrderCanceled'] = 'Order geannuleerd'; +$strings['OrderStatus'] = 'Bestelstatus'; +$strings['PayoutStatus'] = 'Betalingsstatus'; +$strings['SearchByStatus'] = 'Zoeken op status'; +$strings['OrderReference'] = 'Bestelreferentie'; +$strings['OrderDate'] = 'Besteldatum'; +$strings['OrderPrice'] = 'Bestel prijs'; +$strings['ProductType'] = 'Artikel type'; +$strings['SubscribeUser'] = 'Onderteken gebruiker'; +$strings['DeleteOrder'] = 'Verwijder bestelling'; +$strings['ErrorContactPlatformAdmin'] = 'Onbekende fout. Neem contact op met het platform administrator.'; +$strings['PendingReasonByAddress'] = 'in afwachting. Je hebt een bevestigde verzendadres niet in te voeren.'; +$strings['PendingReasonByAuthorization'] = 'in afwachting. We hebben nog geen betalingen.'; +$strings['PendingReasonByEcheck'] = 'in afwachting. De betaling is verricht door een eCheck die nog niet is vrijgemaakt'; +$strings['PendingReasonByIntl'] = 'in afwachting.We hebben geen terugtrekking mechanisme voor betalingen.'; +$strings['PendingReasonByMulticurrency'] = 'in afwachting. We hebben niet in evenwicht te brengen in de valuta verzonden.'; +$strings['PendingReasonByOrder'] = 'in afwachting. Bestelling. We hebben nog geen betalingen.'; +$strings['PendingReasonByPaymentReview'] = 'in afwachting. De betaling wordt beoordeeld door PayPal voor risico.'; +$strings['PendingReasonByRegulatoryReview'] = 'in afwachting. De betaling wordt beoordeeld voor de naleving van regelgeving van de overheid.'; +$strings['PendingReasonByUnilateral'] = 'in afwachting. Het e-mailadres is nog niet geregistreerd of bevestigd.'; +$strings['PendingReasonByUpgrade'] = 'in afwachting. De betaling werd gedaan via credit card.'; +$strings['PendingReasonByVerify'] = 'in afwachting. Sorry. We zijn nog niet in Paypal geverifieerd.'; +$strings['PendingReasonByOther'] = 'in afwachting. Neem contact op met het platform admin.'; $strings['PayPalPaymentOKPleaseConfirm'] = "PayPal rapporteert de transactie nu worden gestart. Om te erkennen dat u op OK om verder te gaan, klikt u op de knop bevestiging hieronder. Eenmaal geklikt, wordt u ingeschreven voor de cursus en het geld zal naar onze winkel worden overgedragen van uw PayPal-rekening. U kunt altijd toegang tot uw cursussen via het tabblad 'Mijn cursussen'. Dank u voor uw aankoop"; -$strings['Sandbox'] = "Test omgeving"; -$strings['PayPalAccount'] = "Paypal rekening"; -$strings['NoPayPalAccountDetected'] = "Geen Paypal account gedetecteerd"; -$strings['PayPalConfig'] = "PayPal bevestiging:"; -$strings['TransfersConfig'] = "Bank overschrijving bevestiging:"; -$strings['PluginInstruction'] = "U kunt de optie in- of uitschakelen om te betalen via PayPal of overschrijving instellen in de configuratie sectie van de plugin."; -$strings['ClickHere'] = "Click here voor meer details"; -$strings['CurrencyType'] = "Valuta type"; -$strings['InfoCurrency'] = "Stel in de valuta voor de betaling van uw cursussen."; -$strings['ApiUsername'] = "API Gebruikersnaam"; -$strings['ApiPassword'] = "API Paswoord"; -$strings['ApiSignature'] = "API Handtekening"; -$strings['InfoApiCredentials'] = "To generate your API credentials to integrate Chamilo with your PayPal account, you must follow the following steps"; -$strings['InfoApiStepOne'] = "Ga naar je PayPal-account, Overzicht en verkoper voorkeuren in het Verkoop Tools menu (als je niet dit menu-item hebt, om spullen te verkopen moet u eerst via PayPal om de toestemming te krijgen )."; -$strings['InfoApiStepTwo'] = "In paragraaf API-toegang , klikt u op Bijwerken "; -$strings['InfoApiStepThree'] = "In optie 2 (API Request referenties aan uw eigen API gebruikersnaam en wachtwoord), klikt u op de Bekijk API Handtekening , en kopieer de getoonde rechtsaf de Cursusshop plugin vorm referenties"; -$strings['ErrorOccurred'] = " Er is een fout opgetreden . Code:% s. Bericht:% s. Neem contact op met naar platform admin"; -$strings['VisibleInCatalog'] = "Zichtbaar in catalogus"; -$strings['Beneficiaries'] = "begunstigden"; -$strings['AvailableCourse'] = "beschikbare cursus"; -$strings['ShowOnCourseCatalog'] = "Toon op de cursusoverzicht"; -$strings['ByStatus'] = "Door de status"; -$strings['ByUser'] = "Door de gebruiker"; -$strings['PaymentMethod'] = "Betaal methodes"; -$strings['SWIFT'] = "SWIFT code"; -$strings['SWIFT_help'] = "Standaardformaat van Bank Identifier Codes (BIC) en dient als een unieke identificatiecode voor een bank of financiële instelling"; +$strings['Sandbox'] = 'Test omgeving'; +$strings['PayPalAccount'] = 'Paypal rekening'; +$strings['NoPayPalAccountDetected'] = 'Geen Paypal account gedetecteerd'; +$strings['PayPalConfig'] = 'PayPal bevestiging:'; +$strings['TransfersConfig'] = 'Bank overschrijving bevestiging:'; +$strings['PluginInstruction'] = 'U kunt de optie in- of uitschakelen om te betalen via PayPal of overschrijving instellen in de configuratie sectie van de plugin.'; +$strings['ClickHere'] = 'Click here voor meer details'; +$strings['CurrencyType'] = 'Valuta type'; +$strings['InfoCurrency'] = 'Stel in de valuta voor de betaling van uw cursussen.'; +$strings['ApiUsername'] = 'API Gebruikersnaam'; +$strings['ApiPassword'] = 'API Paswoord'; +$strings['ApiSignature'] = 'API Handtekening'; +$strings['InfoApiCredentials'] = 'To generate your API credentials to integrate Chamilo with your PayPal account, you must follow the following steps'; +$strings['InfoApiStepOne'] = 'Ga naar je PayPal-account, Overzicht en verkoper voorkeuren in het Verkoop Tools menu (als je niet dit menu-item hebt, om spullen te verkopen moet u eerst via PayPal om de toestemming te krijgen ).'; +$strings['InfoApiStepTwo'] = 'In paragraaf API-toegang , klikt u op Bijwerken '; +$strings['InfoApiStepThree'] = 'In optie 2 (API Request referenties aan uw eigen API gebruikersnaam en wachtwoord), klikt u op de Bekijk API Handtekening , en kopieer de getoonde rechtsaf de Cursusshop plugin vorm referenties'; +$strings['ErrorOccurred'] = ' Er is een fout opgetreden . Code:% s. Bericht:% s. Neem contact op met naar platform admin'; +$strings['VisibleInCatalog'] = 'Zichtbaar in catalogus'; +$strings['Beneficiaries'] = 'begunstigden'; +$strings['AvailableCourse'] = 'beschikbare cursus'; +$strings['ShowOnCourseCatalog'] = 'Toon op de cursusoverzicht'; +$strings['ByStatus'] = 'Door de status'; +$strings['ByUser'] = 'Door de gebruiker'; +$strings['PaymentMethod'] = 'Betaal methodes'; +$strings['SWIFT'] = 'SWIFT code'; +$strings['SWIFT_help'] = 'Standaardformaat van Bank Identifier Codes (BIC) en dient als een unieke identificatiecode voor een bank of financiële instelling'; diff --git a/public/plugin/BuyCourses/lang/english.php b/public/plugin/BuyCourses/lang/english.php index cf04d491b3a..cdbd3edc452 100644 --- a/public/plugin/BuyCourses/lang/english.php +++ b/public/plugin/BuyCourses/lang/english.php @@ -1,195 +1,323 @@ %s . This percentage will be discounted from the total product price. The difference will be the amount to apply the following commissions."; + +declare(strict_types=1); +$strings['plugin_title'] = 'Sell courses'; +$strings['plugin_comment'] = 'Sell courses directly through your Chamilo portal, using a PayPal account to receive funds. Neither the Chamilo association nor the developers involved could be considered responsible of any issue you might suffer using this plugin.'; +$strings['show_main_menu_tab'] = 'Show tab in main menu'; +$strings['show_main_menu_tab_help'] = 'In case of not wanting to show the tab, you can create this link in your Chamilo homepage : %s'; +$strings['public_main_menu_tab'] = 'Show tab in main menu to anonymous users too'; +$strings['include_sessions'] = 'Include sessions'; +$strings['paypal_enable'] = 'Enable PayPal'; +$strings['commissions_enable'] = 'Enable Commissions'; +$strings['transfer_enable'] = 'Enable bank transfer'; +$strings['unregistered_users_enable'] = 'Allow anonymous users'; +$strings['invoicing_enable'] = 'Enable Invoicing'; +$strings['tax_enable'] = 'Enable Taxation'; +$strings['Free'] = 'FREE'; +$strings['PaypalPayoutCommissions'] = 'Paypal Payout Commissions'; +$strings['MyPayouts'] = 'My payments'; +$strings['Commission'] = 'Commission'; +$strings['Commissions'] = 'Commissions'; +$strings['SetCommissions'] = 'Set commissions'; +$strings['CommissionsConfig'] = 'Configure Commissions'; +$strings['PayoutReport'] = 'Payouts report'; +$strings['Stats'] = 'Statistics'; +$strings['InfoCommissions'] = 'Enter here the sales commission as a percentage (% ) , the organization that controls the platform. This percentage is deducted from the amount received by teachers for each course or Session sold on the platform.'; +$strings['NeedToSelectPaymentType'] = 'Need to select payment type'; +$strings['IndividualPayout'] = 'Individual payout'; +$strings['CancelPayout'] = 'Cancel payout'; +$strings['ContinuePayout'] = 'Continue Payout'; +$strings['ProceedPayout'] = 'Proceed with payment'; +$strings['TheActualPlatformCommissionIsX'] = 'The current platform commission is %s . This percentage will be discounted from the total product price. The difference will be the amount to apply the following commissions.'; $strings['CoursesInSessionsDoesntDisplayHere'] = "The courses which are inside a training session don't appear in the list of courses to configure as individual products."; -$strings['WantToSellCourses'] = "Do you want to teach and earn some money with your own courses? This list could be filled with your earnings selling courses through this platform. Get in touch with us."; -$strings['SelectOptionToProceed'] = "Select option to proceed"; +$strings['WantToSellCourses'] = 'Do you want to teach and earn some money with your own courses? This list could be filled with your earnings selling courses through this platform. Get in touch with us.'; +$strings['SelectOptionToProceed'] = 'Select option to proceed'; $strings['VerifyTotalAmountToProceedPayout'] = "Please check the total amount to proceed with the payment of commissions. This amount doesn't take extra costs for PayPal into account. The courses sales that do not count with a Paypal account for the beneficiaries will not be considered."; -$strings['TotalAcounts'] = "Total of accounts:"; -$strings['TotalPayout'] = "Total to be paid:"; -$strings['PayoutDate'] = "Payment date:"; -$strings['CautionThisProcessCantBeCanceled'] = "Caution: this process can take a few minutes and cannot be cancelled."; +$strings['TotalAcounts'] = 'Total of accounts:'; +$strings['TotalPayout'] = 'Total to be paid:'; +$strings['PayoutDate'] = 'Payment date:'; +$strings['CautionThisProcessCantBeCanceled'] = 'Caution: this process can take a few minutes and cannot be cancelled.'; $strings['ProcessingPayoutsDontCloseThisWindow'] = "Proceeding with payments. Don't close this window until this process is finalized."; -$strings['PayoutSuccess'] = "Payments processed successfully"; -$strings['Buyer'] = "Buyer"; -$strings['BankTransfer'] = "Bank transfer"; -$strings['SaleInfo'] = "Sale information"; -$strings['SaleStatusPending'] = "Sale pending"; -$strings['SaleStatusCanceled'] = "Sale canceled"; -$strings['SaleStatusCompleted'] = "Sale completed"; -$strings['PayoutStatusPending'] = "Payment pending"; -$strings['PayoutStatusCanceled'] = "Payment cancelled"; -$strings['PayoutStatusCompleted'] = "Payment completed"; -$strings['PayoutsTotalPending'] = "Pending payments:"; -$strings['PayoutsTotalCanceled'] = "Cancelled payments:"; -$strings['PayoutsTotalCompleted'] = "Completed payments:"; -$strings['Total'] = "Total"; -$strings['TotalAmount'] = "Total amount:"; -$strings['CourseListOnSale'] = "List of courses on sale"; -$strings['AvailableCourses'] = "Available Courses"; -$strings['Price'] = "Price"; -$strings['SearchFilter'] = "Search filter"; -$strings['MinimumPrice'] = "Minimum price"; -$strings['MaximumPrice'] = "Maximum price"; -$strings['AvailableCoursesConfiguration'] = "Available courses configuration"; -$strings['PaymentsConfiguration'] = "Payments configuration"; -$strings['TheUserIsAlreadyRegisteredInTheCourse'] = "You are already registered in the course."; -$strings['SeeDescription'] = "Description"; -$strings['Buy'] = "Buy"; -$strings['WaitingToReceiveThePayment'] = "Currently pending payment"; -$strings['TheUserIsAlreadyRegisteredInTheSession'] = "You are already registered in the session"; -$strings['ItemNotSaved'] = "Item not saved"; -$strings['TitlePlugin'] = "Everything you need to teach and sell courses online"; -$strings['PluginPresentation'] = "The BuyCourses Plugin gives you the means to sell your courses or sessions and teach online, through only a few simple steps and settings. What are you waiting for? Start selling courses through Chamilo LMS!"; -$strings['Instructions'] = "Instructions"; -$strings['InstructionsStepOne'] = "Create a course or session on the platform."; -$strings['InstructionsStepTwo'] = "In the payment settings, set the currency with which you would like to sell your courses or sessions."; -$strings['InstructionsStepThree'] = "Configure courses to sell in the Courses and prices settings."; -$strings['BuyCourses'] = "Buy courses"; -$strings['ConfigurationOfCoursesAndPrices'] = "Courses and prices configuration"; -$strings['SalesReport'] = "Sales report"; +$strings['PayoutSuccess'] = 'Payments processed successfully'; +$strings['Buyer'] = 'Buyer'; +$strings['BankTransfer'] = 'Bank transfer'; +$strings['SaleInfo'] = 'Sale information'; +$strings['SaleStatusPending'] = 'Sale pending'; +$strings['SaleStatusCanceled'] = 'Sale canceled'; +$strings['SaleStatusCompleted'] = 'Sale completed'; +$strings['PayoutStatusPending'] = 'Payment pending'; +$strings['PayoutStatusCanceled'] = 'Payment cancelled'; +$strings['PayoutStatusCompleted'] = 'Payment completed'; +$strings['PayoutsTotalPending'] = 'Pending payments:'; +$strings['PayoutsTotalCanceled'] = 'Cancelled payments:'; +$strings['PayoutsTotalCompleted'] = 'Completed payments:'; +$strings['Total'] = 'Total'; +$strings['TotalAmount'] = 'Total amount:'; +$strings['CourseListOnSale'] = 'List of courses on sale'; +$strings['AvailableCourses'] = 'Available Courses'; +$strings['Price'] = 'Price'; +$strings['SearchFilter'] = 'Search filter'; +$strings['MinimumPrice'] = 'Minimum price'; +$strings['MaximumPrice'] = 'Maximum price'; +$strings['AvailableCoursesConfiguration'] = 'Available courses configuration'; +$strings['PaymentsConfiguration'] = 'Payments configuration'; +$strings['TheUserIsAlreadyRegisteredInTheCourse'] = 'You are already registered in the course.'; +$strings['SeeDescription'] = 'Description'; +$strings['Buy'] = 'Buy'; +$strings['WaitingToReceiveThePayment'] = 'Currently pending payment'; +$strings['TheUserIsAlreadyRegisteredInTheSession'] = 'You are already registered in the session'; +$strings['ItemNotSaved'] = 'Item not saved'; +$strings['TitlePlugin'] = 'Everything you need to teach and sell courses online'; +$strings['PluginPresentation'] = 'The BuyCourses Plugin gives you the means to sell your courses or sessions and teach online, through only a few simple steps and settings. What are you waiting for? Start selling courses through Chamilo LMS!'; +$strings['Instructions'] = 'Instructions'; +$strings['InstructionsStepOne'] = 'Create a course or session on the platform.'; +$strings['InstructionsStepTwo'] = 'In the payment settings, set the currency with which you would like to sell your courses or sessions.'; +$strings['InstructionsStepThree'] = 'Configure courses to sell in the Courses and prices settings.'; +$strings['BuyCourses'] = 'Buy courses'; +$strings['ConfigurationOfCoursesAndPrices'] = 'Courses and prices configuration'; +$strings['SalesReport'] = 'Sales report'; $strings['UserInformation'] = "Buyer's details"; -$strings['PaymentMethods'] = "Payment methods"; -$strings['ConfirmOrder'] = "Confirm order"; -$strings['PurchaseData'] = "Purchase data"; -$strings['bc_subject'] = "Confirmation of course order"; -$strings['PurchaseStatusX'] = "Purchase status: %s"; -$strings['PendingReasonByTransfer'] = "Pending. Awaiting for transfer confirmation"; -$strings['CancelOrder'] = "Cancel order"; -$strings['BankAccountInformation'] = "Bank account details"; -$strings['BankAccount'] = "Bank account"; -$strings['OnceItIsConfirmedYouWillReceiveAnEmailWithTheBankInformationAndAnOrderReference'] = "Once confirmed, you will receive an e-mail with the bank details and an order reference."; -$strings['SubscriptionToCourseXSuccessful'] = "Your subscription to \"%s\" was completed successfully."; -$strings['OrderCanceled'] = "Order canceled"; -$strings['OrderStatus'] = "Order status"; -$strings['PayoutStatus'] = "Payment status"; -$strings['SearchByStatus'] = "Search by status"; -$strings['OrderReference'] = "Order reference"; -$strings['OrderDate'] = "Order date"; -$strings['OrderPrice'] = "Order price"; -$strings['ProductType'] = "Product type"; -$strings['SubscribeUser'] = "Subscribe user"; -$strings['DeleteOrder'] = "Delete order"; -$strings['ErrorContactPlatformAdmin'] = "Unknown error. Please contact the platform administrator."; -$strings['PendingReasonByAddress'] = "Pending. You did not enter a confirmed shipping address."; -$strings['PendingReasonByAuthorization'] = "Pending. We have not yet captured funds."; -$strings['PendingReasonByEcheck'] = "Pending. The payment was made by an eCheck that has not yet cleared"; -$strings['PendingReasonByIntl'] = "Pending. We have no a withdrawal mechanism for funds."; -$strings['PendingReasonByMulticurrency'] = "Pending. We have not balance in the currency sent."; -$strings['PendingReasonByOrder'] = "Pending. Order made. We have not yet captured funds."; -$strings['PendingReasonByPaymentReview'] = "Pending. The payment is being reviewed by PayPal for risk."; -$strings['PendingReasonByRegulatoryReview'] = "Pending. The payment is being reviewed for compliance with government regulations."; -$strings['PendingReasonByUnilateral'] = "Pending. The email address is not yet registered o confirmed."; -$strings['PendingReasonByUpgrade'] = "Pending. The payment was made via credit card."; -$strings['PendingReasonByVerify'] = "Pending. Sorry. We are not yet verified in PayPal."; -$strings['PendingReasonByOther'] = "Pending. Please contact with the platform admin."; +$strings['PaymentMethods'] = 'Payment methods'; +$strings['ConfirmOrder'] = 'Confirm order'; +$strings['PurchaseData'] = 'Purchase data'; +$strings['bc_subject'] = 'Confirmation of course order'; +$strings['PurchaseStatusX'] = 'Purchase status: %s'; +$strings['PendingReasonByTransfer'] = 'Pending. Awaiting for transfer confirmation'; +$strings['CancelOrder'] = 'Cancel order'; +$strings['BankAccountInformation'] = 'Bank account details'; +$strings['BankAccount'] = 'Bank account'; +$strings['OnceItIsConfirmedYouWillReceiveAnEmailWithTheBankInformationAndAnOrderReference'] = 'Once confirmed, you will receive an e-mail with the bank details and an order reference.'; +$strings['SubscriptionToCourseXSuccessful'] = 'Completed process. Your subscription to "%s" was completed successfully.'; +$strings['OrderCanceled'] = 'Order canceled'; +$strings['OrderStatus'] = 'Order status'; +$strings['PayoutStatus'] = 'Payment status'; +$strings['SearchByStatus'] = 'Search by status'; +$strings['OrderReference'] = 'Order reference'; +$strings['OrderDate'] = 'Order date'; +$strings['OrderPrice'] = 'Order price'; +$strings['ProductType'] = 'Product type'; +$strings['SubscribeUser'] = 'Subscribe user'; +$strings['DeleteOrder'] = 'Delete order'; +$strings['ErrorContactPlatformAdmin'] = 'Unknown error. Please contact the platform administrator.'; +$strings['PendingReasonByAddress'] = 'Pending. You did not enter a confirmed shipping address.'; +$strings['PendingReasonByAuthorization'] = 'Pending. We have not yet captured funds.'; +$strings['PendingReasonByEcheck'] = 'Pending. The payment was made by an eCheck that has not yet cleared'; +$strings['PendingReasonByIntl'] = 'Pending. We have no a withdrawal mechanism for funds.'; +$strings['PendingReasonByMulticurrency'] = 'Pending. We have not balance in the currency sent.'; +$strings['PendingReasonByOrder'] = 'Pending. Order made. We have not yet captured funds.'; +$strings['PendingReasonByPaymentReview'] = 'Pending. The payment is being reviewed by PayPal for risk.'; +$strings['PendingReasonByRegulatoryReview'] = 'Pending. The payment is being reviewed for compliance with government regulations.'; +$strings['PendingReasonByUnilateral'] = 'Pending. The email address is not yet registered o confirmed.'; +$strings['PendingReasonByUpgrade'] = 'Pending. The payment was made via credit card.'; +$strings['PendingReasonByVerify'] = 'Pending. Sorry. We are not yet verified in PayPal.'; +$strings['PendingReasonByOther'] = 'Pending. Please contact with the platform admin.'; $strings['PayPalPaymentOKPleaseConfirm'] = "PayPal reports the transaction is ready to be executed. To acknowledge that you are OK to proceed, please click the confirmation button below. Once clicked, you will be registered to the course and the funds will be transferred from your PayPal account to our shop. You can always access your courses through the 'My courses' tab. Thank you for your custom!"; -$strings['Sandbox'] = "Test environment"; -$strings['PayPalAccount'] = "Paypal account"; -$strings['NoPayPalAccountDetected'] = "No Paypal account detected"; -$strings['PayPalConfig'] = "PayPal configuration:"; -$strings['TransfersConfig'] = "Bank transfers configuration:"; -$strings['PluginInstruction'] = "You can enable or disable the option to pay via PayPal, Culqi or bank transfer settings in the configuration section of the plugin."; -$strings['ClickHere'] = "Click here for more details"; -$strings['CurrencyType'] = "Currency type"; -$strings['InfoCurrency'] = "Sets the currency for the payment of your courses."; -$strings['ApiUsername'] = "API Username"; -$strings['ApiPassword'] = "API Password"; -$strings['ApiSignature'] = "API Signature"; -$strings['InfoApiCredentials'] = "To generate your API credentials to integrate Chamilo with your PayPal account, you must follow the following steps"; -$strings['InfoApiStepOne'] = "Go to your PayPal account, Summary, then Seller preferences in the Selling Tools menu (if you do not have this menu entry, you might need to get the authorization to sell stuff through PayPal first)."; -$strings['InfoApiStepTwo'] = "In paragraph API access, click Update"; -$strings['InfoApiStepThree'] = "In Option 2 (Request API credentials to create your own API username and password), click the View API Signature link, and copy the credentials shown right into the BuyCourses plugin form"; -$strings['ErrorOccurred'] = "An error ocurred. Code: %s. Message: %s. Please contact to platform admin"; -$strings['VisibleInCatalog'] = "Visible in catalog"; -$strings['Beneficiaries'] = "Beneficiaries"; -$strings['AvailableCourse'] = "Available course"; -$strings['ShowOnCourseCatalog'] = "Show on course catalog"; -$strings['ByStatus'] = "By status"; -$strings['ByUser'] = "By user"; -$strings['ByEmail'] = "By email"; -$strings['PaymentMethod'] = "Payment method"; -$strings['SWIFT'] = "SWIFT code"; -$strings['SWIFT_help'] = "Standard format of Bank Identifier Codes (BIC) and serves as a unique identifier for a bank or financial institution"; -$strings['PleaseSelectThePaymentMethodBeforeConfirmYourOrder'] = "Please select your favorite payment method before confirming your order"; +$strings['Sandbox'] = 'Test environment'; +$strings['PayPalAccount'] = 'Paypal account'; +$strings['NoPayPalAccountDetected'] = 'No Paypal account detected'; +$strings['PayPalConfig'] = 'PayPal configuration:'; +$strings['TransfersConfig'] = 'Bank transfers configuration:'; +$strings['PluginInstruction'] = 'You can enable or disable the option to pay via PayPal, Culqi or bank transfer settings in the configuration section of the plugin.'; +$strings['ClickHere'] = 'Click here for more details'; +$strings['CurrencyType'] = 'Currency type'; +$strings['InfoCurrency'] = 'Sets the currency for the payment of your courses.'; +$strings['ApiUsername'] = 'API Username'; +$strings['ApiPassword'] = 'API Password'; +$strings['ApiSignature'] = 'API Signature'; +$strings['InfoApiCredentials'] = 'To generate your API credentials to integrate Chamilo with your PayPal account, you must follow the following steps'; +$strings['InfoApiStepOne'] = 'Go to your PayPal account, Summary, then Seller preferences in the Selling Tools menu (if you do not have this menu entry, you might need to get the authorization to sell stuff through PayPal first).'; +$strings['InfoApiStepTwo'] = 'In paragraph API access, click Update'; +$strings['InfoApiStepThree'] = 'In Option 2 (Request API credentials to create your own API username and password), click the View API Signature link, and copy the credentials shown right into the BuyCourses plugin form'; +$strings['ErrorOccurred'] = 'An error ocurred. Code: %s. Message: %s. Please contact to platform admin'; +$strings['VisibleInCatalog'] = 'Visible in catalog'; +$strings['Beneficiaries'] = 'Beneficiaries'; +$strings['AvailableCourse'] = 'Available course'; +$strings['ShowOnCourseCatalog'] = 'Show on course catalog'; +$strings['ByStatus'] = 'By status'; +$strings['ByUser'] = 'By user'; +$strings['ByEmail'] = 'By email'; +$strings['PaymentMethod'] = 'Payment method'; +$strings['SWIFT'] = 'SWIFT code'; +$strings['SWIFT_help'] = 'Standard format of Bank Identifier Codes (BIC) and serves as a unique identifier for a bank or financial institution'; +$strings['PleaseSelectThePaymentMethodBeforeConfirmYourOrder'] = 'Please select your favorite payment method before confirming your order'; $strings['NoPaymentOptionAvailable'] = 'No payment option available. Please report to the administrator.'; $strings['XIsOnlyPaymentMethodAvailable'] = '%s is the only payment method available for this purchase.'; $strings['hide_free_text'] = "Hide 'Free' text"; -$strings['culqi_enable'] = "Enable culqi"; -$strings['include_services'] = "Include services"; -$strings['CurrencyIsNotConfigured'] = "Please, configure a currency before continuing."; -$strings['Services'] = "Services"; -$strings['ServiceName'] = "Service name"; -$strings['AppliesTo'] = "Applies to"; -$strings['ListOfServicesOnSale'] = "List of services on sale"; -$strings['GlobalConfig'] = "Global configuration"; -$strings['WriteHereTheTermsAndConditionsOfYourECommerce'] = "Write here the terms and conditions of your e-commerce"; -$strings['NewService'] = "New service"; -$strings['Service'] = "Service"; -$strings['ServiceInformation'] = "Service information"; -$strings['EditService'] = "Edit service"; -$strings['DeleteThisService'] = "Delete this service"; -$strings['IConfirmIReadAndAcceptTermsAndCondition'] = "I confirm I read and accept the terms and conditions"; -$strings['PleaseSelectTheCorrectInfoToApplyTheService'] = "Please select the correct info to apply the service"; -$strings['SaleStatusCancelled'] = "Sale cancelled"; -$strings['ServiceSaleInfo'] = "Service sale info"; -$strings['ServiceId'] = "Service Id"; -$strings['BoughtBy'] = "Bought by"; -$strings['PurchaserUser'] = "Purchaser user"; -$strings['Pending'] = "Pending"; -$strings['Names'] = "Names"; -$strings['SellerName'] = "Seller name"; -$strings['SellerId'] = "Seller id"; -$strings['SellerAddress'] = "Seller address"; -$strings['SellerEmail'] = "Seller e-mail"; -$strings['NextNumberInvoice'] = "Next invoice number"; -$strings['NextNumberInvoiceDescription'] = "Number of the following invoice"; -$strings['InvoiceSeries'] = "Invoice series"; -$strings['InvoiceSeriesDescription'] = "Optional parameter: Example invoice Id <series><year>/<number>"; -$strings['InvoiceView'] = "View invoice"; -$strings['NoInvoiceEnable'] = "No invoicing block enable"; -$strings['Company'] = "Company"; -$strings['VAT'] = "VAT"; -$strings['Address'] = "Address"; -$strings['InvoiceNumber'] = "Invoice number"; -$strings['InvoiceDate'] = "Invoice date"; -$strings['Invoice'] = "Invoice"; -$strings['SaleEmail'] = "Sales e-mail"; -$strings['PurchaseDetailsIntro'] = "Purchase details"; -$strings['PurchaseDetailsEnd'] = "Regards"; -$strings['ProductName'] = "Product name"; -$strings['BankAccountIntro'] = "Bank Account Info"; +$strings['culqi_enable'] = 'Enable culqi'; +$strings['include_services'] = 'Include services'; +$strings['CurrencyIsNotConfigured'] = 'Please, configure a currency before continuing.'; +$strings['Services'] = 'Services'; +$strings['Service'] = 'Service'; +$strings['NewService'] = 'New service'; +$strings['ServiceName'] = 'Service name'; +$strings['AppliesTo'] = 'Applies to'; +$strings['ServiceInformation'] = 'Service information'; +$strings['ListOfServicesOnSale'] = 'List of services on sale'; +$strings['GlobalConfig'] = 'Global configuration'; +$strings['WriteHereTheTermsAndConditionsOfYourECommerce'] = 'Write here the terms and conditions of your e-commerce'; +$strings['EditService'] = 'Edit service'; +$strings['DeleteThisService'] = 'Delete this service'; +$strings['IConfirmIReadAndAcceptTermsAndCondition'] = 'I confirm I read and accept the terms and conditions'; +$strings['PleaseSelectTheCorrectInfoToApplyTheService'] = 'Please select the correct info to apply the service'; +$strings['SaleStatusCancelled'] = 'Sale cancelled'; +$strings['ServiceSaleInfo'] = 'Service sale info'; +$strings['ServiceId'] = 'Service Id'; +$strings['BoughtBy'] = 'Bought by'; +$strings['PurchaserUser'] = 'Purchaser user'; +$strings['Pending'] = 'Pending'; +$strings['Names'] = 'Names'; +$strings['SellerName'] = 'Seller name'; +$strings['SellerId'] = 'Seller id'; +$strings['SellerAddress'] = 'Seller address'; +$strings['SellerEmail'] = 'Seller e-mail'; +$strings['NextNumberInvoice'] = 'Next invoice number'; +$strings['NextNumberInvoiceDescription'] = 'Number of the following invoice'; +$strings['InvoiceSeries'] = 'Invoice series'; +$strings['InvoiceSeriesDescription'] = 'Optional parameter: Example invoice Id <series><year>/<number>'; +$strings['InvoiceView'] = 'View invoice'; +$strings['NoInvoiceEnable'] = 'No invoicing block enable'; +$strings['Company'] = 'Company'; +$strings['VAT'] = 'VAT'; +$strings['Address'] = 'Address'; +$strings['InvoiceNumber'] = 'Invoice number'; +$strings['InvoiceDate'] = 'Invoice date'; +$strings['Invoice'] = 'Invoice'; +$strings['SaleEmail'] = 'Sales e-mail'; +$strings['PurchaseDetailsIntro'] = 'Purchase details'; +$strings['PurchaseDetailsEnd'] = 'Regards'; +$strings['ProductName'] = 'Product name'; +$strings['BankAccountIntro'] = 'Bank Account Info'; $strings['AdditionalInfoRequired'] = 'More information needed'; -$strings['SubscriptionToServiceXSuccessful'] = "Subscription to service %s completed."; -$strings['ClickHereToFinish'] = "Click here to finish"; -$strings['OrderCancelled'] = "Order cancelled"; -$strings['use_currency_symbol'] = "Use currency symbol"; -$strings['ExportReport'] = "Export Sales Report"; -$strings['OrderTime'] = "Order time"; -$strings['SelectDateRange'] = "Select a start date and end date for the report"; +$strings['SubscriptionToServiceXSuccessful'] = 'Subscription to service %s completed.'; +$strings['ClickHereToFinish'] = 'Click here to finish'; +$strings['OrderCancelled'] = 'Order cancelled'; +$strings['use_currency_symbol'] = 'Use currency symbol'; +$strings['ExportReport'] = 'Export Sales Report'; +$strings['OrderTime'] = 'Order time'; +$strings['SelectDateRange'] = 'Select a start date and end date for the report'; +$strings['ServiceAdded'] = 'Service added'; +$strings['ServiceEdited'] = 'Service updated'; +$strings['ListOfServicesOnSale'] = 'List of services for sale'; +$strings['AdditionalInfo'] = 'Additional information'; +$strings['culqi_enable'] = 'Enable Culqi'; +$strings['CulqiConfig'] = 'Culqi configuration:'; +$strings['InfoCulqiCredentials'] = 'To obtain your credentials, you will need to create an account on Culqi and enter the development mode, copy the merchant code in your dashboard, then enter the API Keys section and copy the corresponding key to paste it here.'; +$strings['CommerceCode'] = 'Merchant code'; +$strings['NoTermsAndConditionsProvided'] = 'No defined terms and conditions'; +$strings['GlobalConfig'] = 'Global configuration:'; +$strings['MyServices'] = 'My services'; +$strings['SalePrice'] = 'Sale price'; +$strings['YouNeedToBeRegisteredInAtLeastOneCourse'] = 'You need to be registered in at least one course'; +$strings['YouNeedToBeRegisteredInAtLeastOneSession'] = 'You need to be registered in at least one session'; +$strings['IfYouWantToGetTheCertificateAndOrSkillsAsociatedToThisCourseYouNeedToBuyTheCertificateServiceYouCanGoToServiceCatalogClickingHere'] = "To obtain the certificate and/or the skills associated to this course, you need to buy the Certificate service. Go to the services catalogue to buy it by clicking here"; +$strings['ServiceDeleted'] = 'Service deleted'; +$strings['YourCoursesNeedAtLeastOneLearningPath'] = 'The courses to which you are subscribed need at least one learning path that contains a final certificate item'; +$strings['GlobalTaxPerc'] = 'Global tax rate'; +$strings['GlobalTaxPercDescription'] = 'Default tax rate that will be used unless there is a specific tax rate for the course, session or service.'; +$strings['TaxPerc'] = 'Tax rate'; +$strings['TaxPercDescription'] = 'If left blank, the global tax rate will be used.'; +$strings['ByDefault'] = 'by default (global value)'; +$strings['OnlyCourses'] = 'Only courses'; +$strings['OnlySessions'] = 'Only sessions'; +$strings['OnlyServices'] = 'Only services'; +$strings['TaxAppliesTo'] = 'Tax applied to'; +$strings['AllCoursesSessionsAndServices'] = 'All (courses, sessions and services)'; +$strings['TaxNameCustom'] = 'Tax name'; +$strings['TaxNameExamples'] = 'VAT, IVA, IGV, TVA, IV ...'; +$strings['ErrorUpdateFieldDB'] = 'Error updating the database fields'; +$strings['tpv_redsys_enable'] = 'Enable RedSys POS'; +$strings['tpv_redsys_enable_help'] = "In order to use the RedSys POS payment method, it is necessary to download the \"REST INTEGRATION - PHP API \" files at the following link web de RedSys and locate the file apiRedSys.php in the plugin/BuyCourses/resources directory."; +$strings['NotFindRedsysFile'] = 'The apiRedsys.php file cannot be found in plugin/BuyCourses/resources directory'; +$strings['TpvPayment'] = 'POS payment'; +$strings['TpvRedsysConfig'] = 'Redsys POS configuration'; +$strings['DS_MERCHANT_MERCHANTCODE'] = 'Trade number (FUC)'; +$strings['DS_MERCHANT_TERMINAL'] = 'Terminal number'; +$strings['DS_MERCHANT_CURRENCY'] = 'Terminal currency'; +$strings['kc'] = 'Secret encryption key'; +$strings['url_redsys'] = 'Redsys connection URL'; +$strings['url_redsys_sandbox'] = 'Redsys connection URL (Sandbox)'; +$strings['InfoTpvRedsysApiCredentials'] = 'You must complete the following form fields with the information provided by the Redsys POS Technical Support:'; +$strings['InfoEmailExtra'] = 'Extra info in payment e-mail'; +$strings['Coupon'] = 'Coupon'; +$strings['DiscountAmount'] = 'Discount amount'; +$strings['RedeemCoupon'] = 'Redeem'; +$strings['NeedToAddCouponCode'] = 'A coupon code has not been entered'; +$strings['CouponNotValid'] = 'This coupon is not valid'; +$strings['CouponRedeemed'] = 'Coupon redeemed'; +$strings['CouponDiscount'] = 'Discount'; +$strings['CouponStatus'] = 'Coupon status'; +$strings['CouponPercentage'] = 'Percentage'; +$strings['CouponAmount'] = 'Amount'; +$strings['CouponList'] = 'Coupon list'; +$strings['CouponCode'] = 'Coupon code'; +$strings['CouponDiscountType'] = 'Discount type'; +$strings['CouponDateStart'] = 'Valid from'; +$strings['CouponDateEnd'] = 'Expires on'; +$strings['CouponDelivered'] = 'Delivered'; +$strings['CouponDisable'] = 'Disable'; +$strings['CouponEnable'] = 'Enable'; +$strings['CouponCodeUsed'] = 'Coupon code already used'; +$strings['CouponNoExists'] = 'Coupon no exists'; +$strings['CouponErrorInsert'] = 'Error inserting new coupon'; +$strings['CouponActive'] = 'Active'; +$strings['CouponDisabled'] = 'Disabled'; +$strings['CouponUpdate'] = 'Coupon updated'; +$strings['CouponsConfiguration'] = 'Coupons configuration'; +$strings['CouponAdd'] = 'New coupon'; +$strings['ConfigureCoupon'] = 'Configure coupon'; +$strings['DiscountCoupon'] = 'Discount coupons'; +$strings['CouponsCode'] = 'Code'; +$strings['DoYouHaveACoupon'] = 'Do you have a coupon?'; +$strings['stripe_enable'] = 'Enable Stripe'; +$strings['StripeConfig'] = 'Stripe configuration:'; +$strings['InfoStripeCredentials'] = 'To obtain the credentials you must first create an account in Stripe, copy the account id from your profile, go to the API Keys configuration section and copy the secret key, finally you must go to the Developers button (top right), register a new Endpoint in the Webhooks section, pointing to https://{site}/plugin/BuyCourses/src/stripe_response.php for the checkout.session.completed event type and copy the secret of the Endpoint.'; +$strings['StripeAccountId'] = 'Account id:'; +$strings['StripeSecret'] = 'Secret key:'; +$strings['StripeEndpointSecret'] = 'Endpoint secret:'; +$strings['PendingReasonByStripe'] = 'Pending. Awaiting for payment confirmation ...'; +$strings['cecabank_enable'] = 'Enable Cecabank POS'; +$strings['TpvCecabank'] = 'POS payment (Cecabank)'; +$strings['CecaSecret'] = 'Secret'; +$strings['CecaUrl'] = 'Url'; +$strings['CecaMerchanId'] = 'Merchant'; +$strings['CecaAcquirerId'] = 'Adquirer'; +$strings['CecaTerminalId'] = 'Terminal'; +$strings['CecaCypher'] = 'Cypher'; +$strings['CecaCurrency'] = 'Currency'; +$strings['CecaExponent'] = 'Exponent'; +$strings['CecaSupportedPayment'] = 'Payment supported'; +$strings['CecabankConfig'] = 'Cecabank configuration'; +$strings['Country'] = 'Country'; +$strings['PaymentType'] = 'Payment type'; +$strings['CountryRelPaymentConfig'] = 'Payment type configuration for the country'; +$strings['CountryRelPaymentMessage'] = 'To process an order, the type of payment per country must be defined, otherwise the order will not be allowed to go through.'; +$strings['CountryEmpty'] = 'To process an order, you need to define the Country field in the user profile.'; +$strings['Duration'] = 'Duration'; +$strings['SubscriptionAdd'] = 'Add subscription'; +$strings['SubscriptionList'] = 'Subscription list'; +$strings['SubscriptionListOnSale'] = 'Subscriptions on sale'; +$strings['SelectSubscription'] = 'Select duration'; +$strings['SubscriptionNotValid'] = 'Subscription not valid'; +$strings['SubscriptionSalesReport'] = 'Sales report'; +$strings['BuySubscriptions'] = 'Buy subscriptions'; +$strings['ConfigurationOfSubscriptionsAndPrices'] = 'Subscriptions and prices configuration'; +$strings['FrequencyConfig'] = 'Duration config'; +$strings['Subscriptions'] = 'Subscriptions'; +$strings['HasSubscriptions'] = 'Has subscriptions'; +$strings['FrequencyRemoved'] = 'Period removed'; +$strings['SubscriptionPeriodOnUse'] = 'Subscription period in use'; +$strings['FrequencyNotExits'] = 'Period does not exist'; +$strings['FrequencyIncorrect'] = 'Period incorrect'; +$strings['SubscriptionFrequencyValueDays'] = 'Value on days'; +$strings['FrequencyNotUpdated'] = 'Period not updated'; +$strings['FrequencyNotSaved'] = 'Period not saved'; +$strings['NeedToAddDuration'] = 'Need to add duration'; +$strings['SubscriptionNotValid'] = 'Subscription not valid'; +$strings['SelecSubscription'] = 'Select a subscription'; +$strings['ConfigureSubscriptionsFrequencies'] = 'Configure subscriptions periods'; +$strings['FrequencyAdd'] = 'Add periods'; +$strings['SubscriptionAlreadyExists'] = 'Subscription already exists'; +$strings['SubscriptionPeriodDuration'] = 'Subscription duration (in days)'; +$strings['Product'] = 'Product name'; +$strings['YouProductIsActivatedYouCanNowAccessIt'] = 'Your product is now activated and you can now have access to it.'; +$strings['hide_shopping_cart_from_course_catalogue'] = 'Hide shopping cart from the course catalogue and leave the subscribe button'; diff --git a/public/plugin/BuyCourses/lang/french.php b/public/plugin/BuyCourses/lang/french.php index c2e8bee86ca..ca7ff4675d3 100644 --- a/public/plugin/BuyCourses/lang/french.php +++ b/public/plugin/BuyCourses/lang/french.php @@ -1,165 +1,252 @@ %s . Ce pourcentage sera soustrait du prix total du produit. La différence sera répartie selon les commissions définies ci-dessous."; +$strings['NeedToSelectPaymentType'] = 'Besoin pour sélectionner le type de paiement'; +$strings['IndividualPayout'] = 'Paiement individuel'; +$strings['CancelPayout'] = 'Annuler paiement'; +$strings['ContinuePayout'] = 'Continuer paiement'; +$strings['ProceedPayout'] = 'Confirmer le paiement'; +$strings['TheActualPlatformCommissionIsX'] = 'La commission de la plateforme est actuellement établie à %s . Ce pourcentage sera soustrait du prix total du produit. La différence sera répartie selon les commissions définies ci-dessous.'; $strings['CoursesInSessionsDoesntDisplayHere'] = "Les cours qui se trouvent dans une session n'apparaissent pas dans la liste de cours individuels."; $strings['WantToSellCourses'] = "Envie d'enseigner et de gagner un peu d'argent avec vos propres cours? Cette liste pourraît être pleine de ventes de vos cours. Contactez-nous."; -$strings['SelectOptionToProceed'] = "Sélectionnez une option pour continuer"; +$strings['SelectOptionToProceed'] = 'Sélectionnez une option pour continuer'; $strings['VerifyTotalAmountToProceedPayout'] = "Veuillez vérifier le montant total avant de continuer le paiement des commissions. Ce montant ne prend pas en compte le coût additionnel éventuellement perçu par PayPal. Les ventes de cours pour lesquelles les bénéficiaires n'ont pas de compte PayPal configuré ne seront pas prises en compte."; -$strings['TotalAcounts'] = "Total des comptes:"; -$strings['TotalPayout'] = "Total à payer:"; -$strings['PayoutDate'] = "Date de paiement:"; -$strings['CautionThisProcessCantBeCanceled'] = "Attention: ce processus peut prendre quelques minutes et ne peut pas être interrompu."; -$strings['ProcessingPayoutsDontCloseThisWindow'] = "Exécution des paiements. Ne fermez pas cette fenêtre avant que le processus ne soit finalisé."; -$strings['PayoutSuccess'] = "Paiements effectués avec succès"; -$strings['Buyer'] = "Acheteur"; -$strings['BankTransfer'] = "Transfert bancaire"; -$strings['SaleInfo'] = "Information vente"; -$strings['SaleStatusPending'] = "Vente non confirmée"; -$strings['SaleStatusCanceled'] = "Vente annulée"; -$strings['SaleStatusCompleted'] = "Vente finalisée"; -$strings['PayoutStatusPending'] = "Paiement en attente"; -$strings['PayoutStatusCanceled'] = "Paiement annulé"; -$strings['PayoutStatusCompleted'] = "Paiement complété"; -$strings['PayoutsTotalPending'] = "Paiements en attente:"; -$strings['PayoutsTotalCanceled'] = "Paiements annulés:"; -$strings['PayoutsTotalCompleted'] = "Paiements complétés:"; -$strings['TotalAmount'] = "Montant total:"; -$strings['CourseListOnSale'] = "Liste de cours en vente"; -$strings['AvailableCourses'] = "Cours disponibles"; -$strings['Price'] = "Prix"; -$strings['SearchFilter'] = "Filtre de recherche"; -$strings['MinimumPrice'] = "Prix minimum"; -$strings['MaximumPrice'] = "Prix maximum"; -$strings['AvailableCoursesConfiguration'] = "Configuration des cours disponibles"; -$strings['PaymentsConfiguration'] = "Configuration des paiements"; +$strings['TotalAcounts'] = 'Total des comptes:'; +$strings['TotalPayout'] = 'Total à payer:'; +$strings['PayoutDate'] = 'Date de paiement:'; +$strings['CautionThisProcessCantBeCanceled'] = 'Attention: ce processus peut prendre quelques minutes et ne peut pas être interrompu.'; +$strings['ProcessingPayoutsDontCloseThisWindow'] = 'Exécution des paiements. Ne fermez pas cette fenêtre avant que le processus ne soit finalisé.'; +$strings['PayoutSuccess'] = 'Paiements effectués avec succès'; +$strings['Buyer'] = 'Acheteur'; +$strings['BankTransfer'] = 'Transfert bancaire'; +$strings['SaleInfo'] = 'Information vente'; +$strings['SaleStatusPending'] = 'Vente non confirmée'; +$strings['SaleStatusCanceled'] = 'Vente annulée'; +$strings['SaleStatusCompleted'] = 'Vente finalisée'; +$strings['PayoutStatusPending'] = 'Paiement en attente'; +$strings['PayoutStatusCanceled'] = 'Paiement annulé'; +$strings['PayoutStatusCompleted'] = 'Paiement complété'; +$strings['PayoutsTotalPending'] = 'Paiements en attente:'; +$strings['PayoutsTotalCanceled'] = 'Paiements annulés:'; +$strings['PayoutsTotalCompleted'] = 'Paiements complétés:'; +$strings['TotalAmount'] = 'Montant total:'; +$strings['CourseListOnSale'] = 'Liste de cours en vente'; +$strings['AvailableCourses'] = 'Cours disponibles'; +$strings['Price'] = 'Prix'; +$strings['SearchFilter'] = 'Filtre de recherche'; +$strings['MinimumPrice'] = 'Prix minimum'; +$strings['MaximumPrice'] = 'Prix maximum'; +$strings['AvailableCoursesConfiguration'] = 'Configuration des cours disponibles'; +$strings['PaymentsConfiguration'] = 'Configuration des paiements'; $strings['TheUserIsAlreadyRegisteredInTheCourse'] = "L'utilisateur est déjà inscrit au cours"; -$strings['SeeDescription'] = "Voir description"; -$strings['Buy'] = "Acheter"; -$strings['WaitingToReceiveThePayment'] = "En attente de réception du paiement"; -$strings['TheUserIsAlreadyRegisteredInTheSession'] = "Vous êtes déjà inscrit à cette session"; -$strings['ItemNotSaved'] = "Article non sauvé"; -$strings['TitlePlugin'] = "Tout le nécessaire pour enseigner et vendre des cours en ligne"; +$strings['SeeDescription'] = 'Voir description'; +$strings['Buy'] = 'Acheter'; +$strings['WaitingToReceiveThePayment'] = 'En attente de réception du paiement'; +$strings['TheUserIsAlreadyRegisteredInTheSession'] = 'Vous êtes déjà inscrit à cette session'; +$strings['ItemNotSaved'] = 'Article non sauvé'; +$strings['TitlePlugin'] = 'Tout le nécessaire pour enseigner et vendre des cours en ligne'; $strings['PluginPresentation'] = "Le plugin BuyCourses vous donne les moyens nécessaires pour vendre vos cours ou vos sessions existants et d'enseigner en ligne, en quelques étapes très simples. Qu'attendez-vous? Commencez à vendre des cours grâce à Chamilo dès maintenant!"; $strings['Instructions'] = "Instructions d'utilisation"; -$strings['InstructionsStepOne'] = "Créer un cours ou une session de formation sur la plateforme."; -$strings['InstructionsStepTwo'] = "Dans la section configuration de paiements, configurer le type de devise dans lequel vous souhaitez vendre vos cours et sessions"; -$strings['InstructionsStepThree'] = "Configurer les cours à vendre dans la section Configuration de cours et prix"; -$strings['BuyCourses'] = "Acheter des cours"; -$strings['ConfigurationOfCoursesAndPrices'] = "Configuration des cours et prix"; -$strings['SalesReport'] = "Rapport des ventes"; -$strings['UserInformation'] = "Fiche acheteur"; -$strings['PaymentMethods'] = "Méthodes de paiement"; -$strings['ConfirmOrder'] = "Confirmer commande"; +$strings['InstructionsStepOne'] = 'Créer un cours ou une session de formation sur la plateforme.'; +$strings['InstructionsStepTwo'] = 'Dans la section configuration de paiements, configurer le type de devise dans lequel vous souhaitez vendre vos cours et sessions'; +$strings['InstructionsStepThree'] = 'Configurer les cours à vendre dans la section Configuration de cours et prix'; +$strings['BuyCourses'] = 'Acheter des cours'; +$strings['ConfigurationOfCoursesAndPrices'] = 'Configuration des cours et prix'; +$strings['SalesReport'] = 'Rapport des ventes'; +$strings['UserInformation'] = 'Fiche acheteur'; +$strings['PaymentMethods'] = 'Méthodes de paiement'; +$strings['ConfirmOrder'] = 'Confirmer commande'; $strings['PurchaseData'] = "Détails d'achat"; -$strings['bc_subject'] = "Confirmation de commande de cours"; -$strings['PurchaseStatusX'] = "État de la vente: %s"; -$strings['PendingReasonByTransfer'] = "En attente. Transfert pas encore confirmé"; -$strings['CancelOrder'] = "Annuler la commande"; -$strings['BankAccountInformation'] = "Détails du compte bancaire"; -$strings['BankAccount'] = "Compte bancaire"; -$strings['OnceItIsConfirmedYouWillReceiveAnEmailWithTheBankInformationAndAnOrderReference'] = "Une fois confirmée, vous recevrez un e-mail avec les données bancaires et la référence de la commande"; -$strings['SubscriptionToCourseXSuccessful'] = "Votre inscription au \"%s\" est terminée."; -$strings['OrderCanceled'] = "Commande annulée"; -$strings['OrderStatus'] = "Statut de commande"; -$strings['PayoutStatus'] = "État du paiement"; -$strings['SearchByStatus'] = "Recherche par statut"; -$strings['OrderReference'] = "Référence de la commande"; -$strings['OrderDate'] = "Date de commande"; -$strings['OrderPrice'] = "Commande Prix"; -$strings['ProductType'] = "Type de produit"; -$strings['SubscribeUser'] = "Inscrire utilisateur"; -$strings['DeleteOrder'] = "Éliminer la commande"; +$strings['bc_subject'] = 'Confirmation de commande de cours'; +$strings['PurchaseStatusX'] = 'État de la vente: %s'; +$strings['PendingReasonByTransfer'] = 'En attente. Transfert pas encore confirmé'; +$strings['CancelOrder'] = 'Annuler la commande'; +$strings['BankAccountInformation'] = 'Détails du compte bancaire'; +$strings['BankAccount'] = 'Compte bancaire'; +$strings['OnceItIsConfirmedYouWillReceiveAnEmailWithTheBankInformationAndAnOrderReference'] = 'Une fois confirmée, vous recevrez un e-mail avec les données bancaires et la référence de la commande'; +$strings['SubscriptionToCourseXSuccessful'] = 'Votre inscription au "%s" est terminée.'; +$strings['OrderCanceled'] = 'Commande annulée'; +$strings['OrderStatus'] = 'Statut de commande'; +$strings['PayoutStatus'] = 'État du paiement'; +$strings['SearchByStatus'] = 'Recherche par statut'; +$strings['OrderReference'] = 'Référence de la commande'; +$strings['OrderDate'] = 'Date de commande'; +$strings['OrderPrice'] = 'Commande Prix'; +$strings['ProductType'] = 'Type de produit'; +$strings['SubscribeUser'] = 'Inscrire utilisateur'; +$strings['DeleteOrder'] = 'Éliminer la commande'; $strings['ErrorContactPlatformAdmin'] = "Une erreur inconnue s'est produite. Veuillez contacter l'administrateur de la plateforme."; -$strings['PendingReasonByAddress'] = "En attente. Il manque une adresse de livraison."; +$strings['PendingReasonByAddress'] = 'En attente. Il manque une adresse de livraison.'; $strings['PendingReasonByAuthorization'] = "En attente. Nous n'avons pas confirmé la réception des fonds."; $strings['PendingReasonByEcheck'] = "En attente. Le paiement a été fait via un chèque qui n'a pas encore été libéré"; -$strings['PendingReasonByIntl'] = "En attente. Aucun moyen de toucher les fonds."; +$strings['PendingReasonByIntl'] = 'En attente. Aucun moyen de toucher les fonds.'; $strings['PendingReasonByMulticurrency'] = "En attente. Nous n'avons pas de compte dans la devise indiquée."; $strings['PendingReasonByOrder'] = "En attente. Commande enregistrée. Les fonds n'ont pas encore été libérés."; -$strings['PendingReasonByPaymentReview'] = "En attente. Le paiement est en révision anti-fraude chez PayPal."; -$strings['PendingReasonByRegulatoryReview'] = "En attente. Le paiement est actuellement revu pour correspondre aux règles mises en vigueur par le gouvernement."; +$strings['PendingReasonByPaymentReview'] = 'En attente. Le paiement est en révision anti-fraude chez PayPal.'; +$strings['PendingReasonByRegulatoryReview'] = 'En attente. Le paiement est actuellement revu pour correspondre aux règles mises en vigueur par le gouvernement.'; $strings['PendingReasonByUnilateral'] = "En attente. L'adresse e-mail n'est pas encore confirmée ou enregistrée."; -$strings['PendingReasonByUpgrade'] = "En attente. Le paiement a été fait par carte de crédit."; +$strings['PendingReasonByUpgrade'] = 'En attente. Le paiement a été fait par carte de crédit.'; $strings['PendingReasonByVerify'] = "En attente. Désolé, nous n'avons pas encore vérifié sur PayPal."; $strings['PendingReasonByOther'] = "En attente. Veuillez contacter l'administrateur."; $strings['PayPalPaymentOKPleaseConfirm'] = "PayPal nous indique que la transaction est prête à être exécutée. Par mesure de sécurité, nous vous demandons de bien vouloir confirmer une dernière fois la transaction en cliquant sur le bouton de confirmation ci-dessous. Une fois cliqué, vous serez immédiatement enregistré au cours, et les fonds correspondants seront soustraits de votre compte PayPal. Vous pouvez accéder à vos cours à tout moment à partir de l'onglet 'Mes cours'. Merci de votre fidélité!"; -$strings['Sandbox'] = "Environnement de test"; -$strings['PayPalAccount'] = "Compte Paypal"; -$strings['NoPayPalAccountDetected'] = "Pas de compte paypal détecté"; -$strings['PayPalConfig'] = "Configuration PayPal:"; -$strings['TransfersConfig'] = "Configuration des transfers bancaires:"; +$strings['Sandbox'] = 'Environnement de test'; +$strings['PayPalAccount'] = 'Compte Paypal'; +$strings['NoPayPalAccountDetected'] = 'Pas de compte paypal détecté'; +$strings['PayPalConfig'] = 'Configuration PayPal:'; +$strings['TransfersConfig'] = 'Configuration des transfers bancaires:'; $strings['PluginInstruction'] = "Vous pouvez activer ou désactiver l'option de paiements via PayPal, Culqi ou de transferts bancaires dans la section de configuration des plugins."; $strings['ClickHere'] = "Cliquez ici pour plus d'infos"; -$strings['CurrencyType'] = "Type de devise"; +$strings['CurrencyType'] = 'Type de devise'; $strings['InfoCurrency'] = "Permet de configurer la devise pour l'achat des cours."; $strings['ApiUsername'] = "Nom d'utilisateur de l'API"; $strings['ApiPassword'] = "Mot de passe de l'API"; $strings['ApiSignature'] = "Signature de l'API"; -$strings['InfoApiCredentials'] = "Pour générer vos données API pour intégrer votre compte PayPal à Chamilo, il vous faudra suivre les étapes suivantes"; +$strings['InfoApiCredentials'] = 'Pour générer vos données API pour intégrer votre compte PayPal à Chamilo, il vous faudra suivre les étapes suivantes'; $strings['InfoApiStepOne'] = "Aller dans l'option profil de PayPal, et ensuite dans Outils de vente"; $strings['InfoApiStepTwo'] = "Dans la section API d'accès, cliquer sur l'option Mettre à jour"; $strings['InfoApiStepThree'] = "Dans l'option 2 de Configuration des données et permissions API, cliquer sur Voir signature API. Copier ces donées dans le formulaire de configuration de ce plugin"; $strings['ErrorOccurred'] = "Une erreur est survenue. Code: %s. Message: %s. Veuillez contacter l'administrateur de la plateforme"; -$strings['VisibleInCatalog'] = "Visible dans le catalogue"; -$strings['Beneficiaries'] = "Bénéficiaires"; -$strings['AvailableCourse'] = "Cours disponibles"; -$strings['ShowOnCourseCatalog'] = "Afficher dans le catalogue de cours"; -$strings['ByStatus'] = "Par statut"; -$strings['ByUser'] = "Par utilisateur"; -$strings['ByEmail'] = "Par email"; -$strings['PaymentMethod'] = "Méthodes de paiement"; -$strings['SWIFT'] = "Code SWIFT"; +$strings['VisibleInCatalog'] = 'Visible dans le catalogue'; +$strings['Beneficiaries'] = 'Bénéficiaires'; +$strings['AvailableCourse'] = 'Cours disponibles'; +$strings['ShowOnCourseCatalog'] = 'Afficher dans le catalogue de cours'; +$strings['ByStatus'] = 'Par statut'; +$strings['ByUser'] = 'Par utilisateur'; +$strings['ByEmail'] = 'Par email'; +$strings['PaymentMethod'] = 'Méthodes de paiement'; +$strings['SWIFT'] = 'Code SWIFT'; $strings['SWIFT_help'] = "Format standard des codes d'identification de banque (BIC) et sert un identifiant unique pour une banque ou une institution financière."; $strings['PleaseSelectThePaymentMethodBeforeConfirmYourOrder'] = 'Veuillez sélectionner votre méthode de paiement préférée avant de confirmer'; $strings['NoPaymentOptionAvailable'] = 'Aucune méthode de paiement disponible. Merci de bien vouloir rapporter ce problème à l\'administrateur.'; $strings['XIsOnlyPaymentMethodAvailable'] = '%s est la seule méthode de paiement disponible pour cet achat.'; $strings['public_main_menu_tab'] = "Montrer l'onglet dans le menu principal aux utilisateurs anonyme également"; -$strings['culqi_enable'] = "Activé culqi"; -$strings['include_services'] = "Inclure les services"; +$strings['culqi_enable'] = 'Activé culqi'; +$strings['include_services'] = 'Inclure les services'; $strings['hide_free_text'] = "Cacher le texte 'Free' ou 'Gratuit'"; -$strings['Services'] = "Services"; -$strings['ServiceName'] = "Nom du service"; +$strings['Services'] = 'Services'; +$strings['ServiceName'] = 'Nom du service'; $strings['AppliesTo'] = "S'applique à"; -$strings['ListOfServicesOnSale'] = "Liste de services en vente"; -$strings['GlobalConfig'] = "Configuration globale"; -$strings['WriteHereTheTermsAndConditionsOfYourECommerce'] = "Écrire les conditions de ventes de votre e-commerce"; -$strings['NewService'] = "Nouveau service"; -$strings['Service'] = "Service"; -$strings['ServiceInformation'] = "Information sur le service"; -$strings['EditService'] = "Edition du service"; -$strings['DeleteThisService'] = "Supprimer ce service"; +$strings['ListOfServicesOnSale'] = 'Liste de services en vente'; +$strings['GlobalConfig'] = 'Configuration globale'; +$strings['WriteHereTheTermsAndConditionsOfYourECommerce'] = 'Écrire les conditions de ventes de votre e-commerce'; +$strings['NewService'] = 'Nouveau service'; +$strings['Service'] = 'Service'; +$strings['ServiceInformation'] = 'Information sur le service'; +$strings['EditService'] = 'Edition du service'; +$strings['DeleteThisService'] = 'Supprimer ce service'; $strings['IConfirmIReadAndAcceptTermsAndCondition'] = "Je confirme que j'ai lu et que j'accepte les termes et conditions"; $strings['PleaseSelectTheCorrectInfoToApplyTheService'] = "Veuillez choisir l'information à appliquer au service"; -$strings['SaleStatusCancelled'] = "Vente annulée"; -$strings['ServiceSaleInfo'] = "Information de vente"; -$strings['ServiceId'] = "Id du service"; -$strings['BoughtBy'] = "Acheté par"; -$strings['PurchaserUser'] = "Utilisateur acheteur"; -$strings['Pending'] = "En attente"; -$strings['Names'] = "Nom"; -$strings['ExportReport'] = "Export du rapport des ventes"; -$strings['OrderTime'] = "Heure de commande"; -$strings['SelectDateRange'] = "Sélectionnez une date de début et une date de fin pour le rapport"; +$strings['SaleStatusCancelled'] = 'Vente annulée'; +$strings['ServiceSaleInfo'] = 'Information de vente'; +$strings['ServiceId'] = 'Id du service'; +$strings['BoughtBy'] = 'Acheté par'; +$strings['PurchaserUser'] = 'Utilisateur acheteur'; +$strings['Pending'] = 'En attente'; +$strings['Names'] = 'Nom'; +$strings['ExportReport'] = 'Export du rapport des ventes'; +$strings['OrderTime'] = 'Heure de commande'; +$strings['SelectDateRange'] = 'Sélectionnez une date de début et une date de fin pour le rapport'; +$strings['InfoEmailExtra'] = "Informations additionnelles dans l'e-mail de paiement"; +$strings['Coupon'] = 'Coupon'; +$strings['DiscountAmount'] = 'Ristourne totale'; +$strings['RedeemCoupon'] = 'Échanger'; +$strings['NeedToAddCouponCode'] = "Aucun code de coupon n'a été introduit"; +$strings['CouponNotValid'] = "Ce coupon n'est pas valable"; +$strings['CouponRedeemed'] = 'Coupon échangé'; +$strings['CouponDiscount'] = 'Ristourne'; +$strings['CouponStatus'] = 'Statut du coupon'; +$strings['CouponPercentage'] = 'Pourcentage'; +$strings['CouponAmount'] = 'Montant'; +$strings['CouponList'] = 'Liste des coupons'; +$strings['CouponCode'] = 'Code du coupon'; +$strings['CouponDiscountType'] = 'Type de ristourne'; +$strings['CouponDateStart'] = 'Valable apd'; +$strings['CouponDateEnd'] = 'Expire le'; +$strings['CouponDelivered'] = 'Livré'; +$strings['CouponDisable'] = 'Désactivé'; +$strings['CouponEnable'] = 'Activé'; +$strings['CouponCodeUsed'] = 'Code de coupon déjà utilisé'; +$strings['CouponNoExists'] = "Ce coupon n'existe pas"; +$strings['CouponErrorInsert'] = "Erreur lors de l'insertion du coupon"; +$strings['CouponActive'] = 'Actif'; +$strings['CouponDisabled'] = 'Désactivé'; +$strings['CouponUpdate'] = 'Coupon mis à jour'; +$strings['CouponsConfiguration'] = 'Configuration coupons'; +$strings['CouponAdd'] = 'Nouveau coupon'; +$strings['ConfigureCoupon'] = 'Configurer le coupon'; +$strings['DiscountCoupon'] = 'Coupons de ristourne'; +$strings['CouponsCode'] = 'Code'; +$strings['DoYouHaveACoupon'] = 'Vous avez un coupon?'; +$strings['stripe_enable'] = 'Activer Stripe'; +$strings['StripeConfig'] = 'Configuration de Stripe:'; +$strings['InfoStripeCredentials'] = "Pour obtenir les données de connexion, vous devez d'abord réer un compte Stripe (ce qui prend un certain temps), puis copier l'ID du compte depuis votre profil et le renseigner ici. Puis aller dans la configuration des clefs API et copier la clef secrète. Enfin, suivre le bouton 'Développeurs' (haut droite), enregistrer un nouveau Endpoint dans la section Webhooks, le pointer vers l'URL https://{site}/plugin/BuyCourses/src/stripe_response.php et l'associer à l'événement checkout.session.completed et copier le secret du Endpoint ici."; +$strings['StripeAccountId'] = 'ID du compte:'; +$strings['StripeSecret'] = 'Clef secrète:'; +$strings['StripeEndpointSecret'] = 'Clef secrète du Endpoint:'; +$strings['PendingReasonByStripe'] = 'Un instant.... En attente de confirmation du paiement...'; +$strings['cecabank_enable'] = 'Activer Cecabank POS'; +$strings['TpvCecabank'] = 'Paiement POS (Cecabank)'; +$strings['CecaSecret'] = 'Clef secrète'; +$strings['CecaUrl'] = 'URL'; +$strings['CecaMerchanId'] = 'Marchand'; +$strings['CecaAcquirerId'] = 'Acquéreur'; +$strings['CecaTerminalId'] = 'Terminal'; +$strings['CecaCypher'] = 'Chiffrage'; +$strings['CecaCurrency'] = 'Devise'; +$strings['CecaExponent'] = 'Exposant'; +$strings['CecaSupportedPayment'] = 'Paiement supporté'; +$strings['CecabankConfig'] = 'Configuration de Cecabank'; +$strings['Country'] = 'Pays'; +$strings['PaymentType'] = 'Type de paiement'; +$strings['CountryRelPaymentConfig'] = 'Configuration du type de paiement par pays'; +$strings['CountryRelPaymentMessage'] = 'Pour faire une commande, il est nécessaire de définir le type de paiement par pays. Sinon, il est impossible de clôturer la commande'; +$strings['CountryEmpty'] = 'Pour passer commande, il est nécessaire de définir le champ Pays dans le profil utilisateur.'; +$strings['Duration'] = 'Durée'; +$strings['SubscriptionAdd'] = 'Ajouter souscription'; +$strings['SubscriptionList'] = 'Liste des souscriptions'; +$strings['SubscriptionListOnSale'] = 'Souscriptions en promo'; +$strings['SelectSubscription'] = 'Sélectionner la durée'; +$strings['SubscriptionNotValid'] = 'Souscription non valable'; +$strings['SubscriptionSalesReport'] = 'Rapport de ventes'; +$strings['BuySubscriptions'] = 'Souscrire'; +$strings['ConfigurationOfSubscriptionsAndPrices'] = 'Configuration des souscriptions et prix'; +$strings['FrequencyConfig'] = 'Configuration périodes'; +$strings['Subscriptions'] = 'Souscriptions'; +$strings['HasSubscriptions'] = 'A des souscriptions'; +$strings['FrequencyRemoved'] = 'Période supprimée'; +$strings['SubscriptionPeriodOnUse'] = 'Période de souscription utilisée'; +$strings['FrequencyNotExits'] = "Cette période n'existe pas"; +$strings['FrequencyIncorrect'] = 'Période incorrecte'; +$strings['SubscriptionFrequencyValueDays'] = 'Valeur en jours'; +$strings['FrequencyNotUpdated'] = 'Période non mise à jour'; +$strings['FrequencyNotSaved'] = 'Période non sauvegardée'; +$strings['NeedToAddDuration'] = 'Une durée est nécessaire'; +$strings['SubscriptionNotValid'] = 'Souscription non valable'; +$strings['SelecSubscription'] = 'Sélectionnez une souscription'; +$strings['ConfigureSubscriptionsFrequencies'] = 'Configurez des périodes de souscription'; +$strings['FrequencyAdd'] = 'Ajouter période'; +$strings['SubscriptionAlreadyExists'] = 'La souscription existe déjà'; +$strings['SubscriptionPeriodDuration'] = 'Durée de souscription (en jours)'; +$strings['Product'] = 'Produit'; +$strings['SalePrice'] = 'Prix'; +$strings['YouProductIsActivatedYouCanNowAccessIt'] = 'Votre abonnement a été activé, vous avez maintenant accès au module choisi.'; +$strings['hide_shopping_cart_from_course_catalogue'] = "Cacher le chariot de vente dans le catalogue de cours et laisse le bouton s'inscrire"; diff --git a/public/plugin/BuyCourses/lang/spanish.php b/public/plugin/BuyCourses/lang/spanish.php index af4e0368c20..d50a949ee07 100644 --- a/public/plugin/BuyCourses/lang/spanish.php +++ b/public/plugin/BuyCourses/lang/spanish.php @@ -1,216 +1,323 @@ %s , este porcentaje será descontado del total del precio del producto cuya diferencia será el monto base para aplicar estas comisiones."; -$strings['CoursesInSessionsDoesntDisplayHere'] = "Los cursos que se encuentren dentro de una sesión de formación no aparecerán en la lista de cursos a configurar como productos individuales."; -$strings['WantToSellCourses'] = "¿Quieres enseñar y ganar dinero con tus propios cursos?, Esta lista puede estar llena de tus ganancias vendiendo cursos a través de esta plataforma!. Informate con nosotros! "; -$strings['SelectOptionToProceed'] = "Selecciona una opción para proceder"; -$strings['VerifyTotalAmountToProceedPayout'] = "Por favor verificar el monto total para proceder con el pago de comisiones, este monto no considera cargos extras por paypal, las ventas de cursos que no cuenten con una cuenta de Paypal para el o los beneficiarios no serán consideradas."; -$strings['TotalAcounts'] = "Total de cuentas:"; -$strings['TotalPayout'] = "Total a pagar:"; -$strings['PayoutDate'] = "Fecha del Pago:"; -$strings['CautionThisProcessCantBeCanceled'] = "Precaución: este proceso puede tomar unos minutos y no puede ser cancelado."; -$strings['ProcessingPayoutsDontCloseThisWindow'] = "Procesando los pagos, no cierre esta ventana hasta que haya finalizado"; -$strings['PayoutSuccess'] = "Los pagos se han realizado con éxito"; -$strings['Buyer'] = "Comprador"; -$strings['BankTransfer'] = "Transferencia Bancaria"; -$strings['SaleInfo'] = "Información de la venta"; -$strings['SaleStatusPending'] = "Venta pendiente"; -$strings['SaleStatusCanceled'] = "Venta cancelada"; -$strings['SaleStatusCompleted'] = "Venta completada"; -$strings['PayoutStatusPending'] = "Pago pendiente"; -$strings['PayoutStatusCanceled'] = "Pago cancelado"; -$strings['PayoutStatusCompleted'] = "Pago completado"; -$strings['PayoutsTotalPending'] = "Pagos pendientes:"; -$strings['PayoutsTotalCanceled'] = "Pagos cancelados:"; -$strings['PayoutsTotalCompleted'] = "Pagos completados:"; -$strings['Total'] = "Total"; -$strings['TotalAmount'] = "Monto total:"; -$strings['CourseListOnSale'] = "Lista de cursos a la venta"; -$strings['AvailableCourses'] = "Cursos disponibles"; -$strings['Price'] = "Precio"; -$strings['SearchFilter'] = "Filtro de búsqueda"; -$strings['MinimumPrice'] = "Precio mínimo"; -$strings['MaximumPrice'] = "Precio máximo"; -$strings['AvailableCoursesConfiguration'] = "Configuración de cursos disponibles"; -$strings['PaymentsConfiguration'] = "Configuración de Pagos"; -$strings['TheUserIsAlreadyRegisteredInTheCourse'] = "Usted ya está registrado en el curso."; -$strings['SeeDescription'] = "Ver descripción"; -$strings['Buy'] = "Comprar"; -$strings['WaitingToReceiveThePayment'] = "Se encuentra a la espera de recibir el pago"; -$strings['TheUserIsAlreadyRegisteredInTheSession'] = "Usted ya está registrado en la sesión"; -$strings['ItemNotSaved'] = "Elemento no guardado"; -$strings['TitlePlugin'] = "Todo lo que necesita para enseñar y vender cursos en línea"; -$strings['PluginPresentation'] = "El Plugin BuyCourses le da los recursos para vender sus cursos o sus sesiones ya creados y enseñar en línea, todo en unos simples pasos y configuraciones. ¿Qué espera? Empiece a vender cursos a través de Chamilo ahora!"; -$strings['Instructions'] = "Instrucciones de uso"; -$strings['InstructionsStepOne'] = "Crea un curso o una sesión de formación en la plataforma."; -$strings['InstructionsStepTwo'] = "En la sección configuración de pagos, configure el tipo de moneda con el que venderá sus cursos o sesiones."; -$strings['InstructionsStepThree'] = "Configure los cursos a vender en la sección Configuración de cursos y precios"; -$strings['BuyCourses'] = "Comprar cursos"; -$strings['ConfigurationOfCoursesAndPrices'] = "Configuración de cursos y precios"; -$strings['SalesReport'] = "Reporte de ventas"; -$strings['UserInformation'] = "Ficha del comprador"; -$strings['PaymentMethods'] = "Métodos de pago"; -$strings['ConfirmOrder'] = "Confirmar Orden"; -$strings['PurchaseData'] = "Datos de la compra"; -$strings['bc_subject'] = "Confirmación pedido de cursos"; -$strings['PurchaseStatusX'] = "Estado de la compra: %s"; -$strings['PendingReasonByTransfer'] = "Pendiente. Esperando la confirmación de la transferencia"; -$strings['CancelOrder'] = "Cancelar Orden"; -$strings['BankAccountInformation'] = "Información de la Cuenta Bancaria"; -$strings['BankAccount'] = "Cuenta Bancaria"; -$strings['OnceItIsConfirmedYouWillReceiveAnEmailWithTheBankInformationAndAnOrderReference'] = "Una vez confirmado, recibira un e-mail con los datos bancarios y una referencia del pedido."; -$strings['SubscriptionToCourseXSuccessful'] = "

Tu subscripción a \"%s\" se realizó correctamente.

"; -$strings['OrderCanceled'] = "Pedido cancelado"; -$strings['OrderStatus'] = "Estado del pedido"; -$strings['PayoutStatus'] = "Estado del pago"; -$strings['SearchByStatus'] = "Buscar por estado"; -$strings['OrderReference'] = "Referencia del pedido"; -$strings['OrderDate'] = "Fecha del pedido"; -$strings['OrderPrice'] = "Precio del pedido"; -$strings['ProductType'] = "Tipo de producto"; -$strings['SubscribeUser'] = "Inscribir usuario"; -$strings['DeleteOrder'] = "Eliminar pedido"; -$strings['ErrorContactPlatformAdmin'] = "Se ha producido un error desconocido. Por favor, póngase en contacto con el administrador de la plataforma."; -$strings['PendingReasonByAddress'] = "Pendiente. No has ingresado una dirección de correo confirmada."; -$strings['PendingReasonByAuthorization'] = "Pendiente. No hemos recibido los fondos."; -$strings['PendingReasonByEcheck'] = "Pendiente. El pago fue hecho a través de un eCheck que aún no está declarado"; -$strings['PendingReasonByIntl'] = "Pendiente. No tenemos un mecanismo de retiro de fondos."; -$strings['PendingReasonByMulticurrency'] = "Pending. No podemos realizar el cambio de moneda."; -$strings['PendingReasonByOrder'] = "Pendiente. Pedido realizado. No hemos recibido los fondos."; -$strings['PendingReasonByPaymentReview'] = "Pendiente. El pago está siendo revisado por PayPal por riesgo."; -$strings['PendingReasonByRegulatoryReview'] = "Pendiente. El pago está siendo revisado por cumplimiento con regulaciones gubernamentales."; -$strings['PendingReasonByUnilateral'] = "Pendiente. La dirección de correo electrónico aún no está registrada o confirmada."; -$strings['PendingReasonByUpgrade'] = "Pendiente. El pago fue realizado a través de una tarjeta de crédito."; -$strings['PendingReasonByVerify'] = "Pendiente. Lo sentimos. Todavía no estamos verificados en PayPal."; -$strings['PendingReasonByOther'] = "Pendiente. Por favor contacta al administrador de la plataforma."; + +declare(strict_types=1); +$strings['plugin_title'] = 'Venta de cursos'; +$strings['plugin_comment'] = 'Vender cursos a través de PayPal directamente desde su portal Chamilo. La asociación Chamilo y los desarrolladores involucrados no pueden ser considerados responsables de cualquier inconveniente que se presente.'; +$strings['show_main_menu_tab'] = 'Mostrar pestaña en el menu principal'; +$strings['show_main_menu_tab_help'] = 'En caso de no querer mostrar la pestaña, puede agregar el siguiente enlace a su portal Chamilo: %s'; +$strings['public_main_menu_tab'] = 'Mostrar pestaña a usuarios sin login'; +$strings['include_sessions'] = 'Incluir sesiones'; +$strings['paypal_enable'] = 'Habilitar PayPal'; +$strings['commissions_enable'] = 'Habilitar Comisiones'; +$strings['transfer_enable'] = 'Habilitar transferencia'; +$strings['unregistered_users_enable'] = 'Permitir usuarios sin registro en la plataforma'; +$strings['invoicing_enable'] = 'Habilitar Facturación'; +$strings['tax_enable'] = 'Habilitar Impuestos'; +$strings['Free'] = 'GRATIS'; +$strings['PaypalPayoutCommissions'] = 'Pagar comisiones por Paypal'; +$strings['MyPayouts'] = 'Mis Pagos'; +$strings['Commission'] = 'Comisión'; +$strings['Commissions'] = 'Comisiones'; +$strings['SetCommissions'] = 'Aplicar comisiones'; +$strings['CommissionsConfig'] = 'Configurar Comisiones'; +$strings['PayoutReport'] = 'Reporte de Pagos'; +$strings['Stats'] = 'Estadísticas'; +$strings['InfoCommissions'] = 'Ingrese aquí la comisión de ventas, en porcentaje (%), para la organización que controla la plataforma. Este porcentaje se deducirá del monto percibido por los docentes por cada curso o sesión vendido en la plataforma.'; +$strings['NeedToSelectPaymentType'] = 'Necesita seleccionar el tipo de pago'; +$strings['IndividualPayout'] = 'Pago individual'; +$strings['CancelPayout'] = 'Cancelar Payout'; +$strings['ContinuePayout'] = 'Continuar con el Pago'; +$strings['ProceedPayout'] = 'Proceder con el Pago'; +$strings['TheActualPlatformCommissionIsX'] = 'La actual comisión de la plataforma es de %s , este porcentaje será descontado del total del precio del producto cuya diferencia será el monto base para aplicar estas comisiones.'; +$strings['CoursesInSessionsDoesntDisplayHere'] = 'Los cursos que se encuentren dentro de una sesión de formación no aparecerán en la lista de cursos a configurar como productos individuales.'; +$strings['WantToSellCourses'] = '¿Quieres enseñar y ganar dinero con tus propios cursos?, Esta lista puede estar llena de tus ganancias vendiendo cursos a través de esta plataforma!. Informate con nosotros! '; +$strings['SelectOptionToProceed'] = 'Selecciona una opción para proceder'; +$strings['VerifyTotalAmountToProceedPayout'] = 'Por favor verificar el monto total para proceder con el pago de comisiones, este monto no considera cargos extras por paypal, las ventas de cursos que no cuenten con una cuenta de Paypal para el o los beneficiarios no serán consideradas.'; +$strings['TotalAcounts'] = 'Total de cuentas:'; +$strings['TotalPayout'] = 'Total a pagar:'; +$strings['PayoutDate'] = 'Fecha del Pago:'; +$strings['CautionThisProcessCantBeCanceled'] = 'Precaución: este proceso puede tomar unos minutos y no puede ser cancelado.'; +$strings['ProcessingPayoutsDontCloseThisWindow'] = 'Procesando los pagos, no cierre esta ventana hasta que haya finalizado'; +$strings['PayoutSuccess'] = 'Los pagos se han realizado con éxito'; +$strings['Buyer'] = 'Comprador'; +$strings['BankTransfer'] = 'Transferencia Bancaria'; +$strings['SaleInfo'] = 'Información de la venta'; +$strings['SaleStatusPending'] = 'Venta pendiente'; +$strings['SaleStatusCanceled'] = 'Venta cancelada'; +$strings['SaleStatusCompleted'] = 'Venta completada'; +$strings['PayoutStatusPending'] = 'Pago pendiente'; +$strings['PayoutStatusCanceled'] = 'Pago cancelado'; +$strings['PayoutStatusCompleted'] = 'Pago completado'; +$strings['PayoutsTotalPending'] = 'Pagos pendientes:'; +$strings['PayoutsTotalCanceled'] = 'Pagos cancelados:'; +$strings['PayoutsTotalCompleted'] = 'Pagos completados:'; +$strings['Total'] = 'Total'; +$strings['TotalAmount'] = 'Monto total:'; +$strings['CourseListOnSale'] = 'Lista de cursos a la venta'; +$strings['AvailableCourses'] = 'Cursos disponibles'; +$strings['Price'] = 'Precio'; +$strings['SearchFilter'] = 'Filtro de búsqueda'; +$strings['MinimumPrice'] = 'Precio mínimo'; +$strings['MaximumPrice'] = 'Precio máximo'; +$strings['AvailableCoursesConfiguration'] = 'Configuración de cursos disponibles'; +$strings['PaymentsConfiguration'] = 'Configuración de Pagos'; +$strings['TheUserIsAlreadyRegisteredInTheCourse'] = 'Usted ya está registrado en el curso.'; +$strings['SeeDescription'] = 'Ver descripción'; +$strings['Buy'] = 'Comprar'; +$strings['WaitingToReceiveThePayment'] = 'Se encuentra a la espera de recibir el pago'; +$strings['TheUserIsAlreadyRegisteredInTheSession'] = 'Usted ya está registrado en la sesión'; +$strings['ItemNotSaved'] = 'Elemento no guardado'; +$strings['TitlePlugin'] = 'Todo lo que necesita para enseñar y vender cursos en línea'; +$strings['PluginPresentation'] = 'El Plugin BuyCourses le da los recursos para vender sus cursos o sus sesiones ya creados y enseñar en línea, todo en unos simples pasos y configuraciones. ¿Qué espera? Empiece a vender cursos a través de Chamilo ahora!'; +$strings['Instructions'] = 'Instrucciones de uso'; +$strings['InstructionsStepOne'] = 'Crea un curso o una sesión de formación en la plataforma.'; +$strings['InstructionsStepTwo'] = 'En la sección configuración de pagos, configure el tipo de moneda con el que venderá sus cursos o sesiones.'; +$strings['InstructionsStepThree'] = 'Configure los cursos a vender en la sección Configuración de cursos y precios'; +$strings['BuyCourses'] = 'Comprar cursos'; +$strings['ConfigurationOfCoursesAndPrices'] = 'Configuración de cursos y precios'; +$strings['SalesReport'] = 'Reporte de ventas'; +$strings['UserInformation'] = 'Ficha del comprador'; +$strings['PaymentMethods'] = 'Métodos de pago'; +$strings['ConfirmOrder'] = 'Confirmar Orden'; +$strings['PurchaseData'] = 'Datos de la compra'; +$strings['bc_subject'] = 'Confirmación pedido de cursos'; +$strings['PurchaseStatusX'] = 'Estado de la compra: %s'; +$strings['PendingReasonByTransfer'] = 'Pendiente. Esperando la confirmación de la transferencia'; +$strings['CancelOrder'] = 'Cancelar Orden'; +$strings['BankAccountInformation'] = 'Información de la Cuenta Bancaria'; +$strings['BankAccount'] = 'Cuenta Bancaria'; +$strings['OnceItIsConfirmedYouWillReceiveAnEmailWithTheBankInformationAndAnOrderReference'] = 'Una vez confirmado, recibira un e-mail con los datos bancarios y una referencia del pedido.'; +$strings['SubscriptionToCourseXSuccessful'] = '

Proceso completado. Tu subscripción a "%s" se realizó correctamente.

'; +$strings['OrderCanceled'] = 'Pedido cancelado'; +$strings['OrderStatus'] = 'Estado del pedido'; +$strings['PayoutStatus'] = 'Estado del pago'; +$strings['SearchByStatus'] = 'Buscar por estado'; +$strings['OrderReference'] = 'Referencia del pedido'; +$strings['OrderDate'] = 'Fecha del pedido'; +$strings['OrderPrice'] = 'Precio del pedido'; +$strings['ProductType'] = 'Tipo de producto'; +$strings['SubscribeUser'] = 'Inscribir usuario'; +$strings['DeleteOrder'] = 'Eliminar pedido'; +$strings['ErrorContactPlatformAdmin'] = 'Se ha producido un error desconocido. Por favor, póngase en contacto con el administrador de la plataforma.'; +$strings['PendingReasonByAddress'] = 'Pendiente. No has ingresado una dirección de correo confirmada.'; +$strings['PendingReasonByAuthorization'] = 'Pendiente. No hemos recibido los fondos.'; +$strings['PendingReasonByEcheck'] = 'Pendiente. El pago fue hecho a través de un eCheck que aún no está declarado'; +$strings['PendingReasonByIntl'] = 'Pendiente. No tenemos un mecanismo de retiro de fondos.'; +$strings['PendingReasonByMulticurrency'] = 'Pending. No podemos realizar el cambio de moneda.'; +$strings['PendingReasonByOrder'] = 'Pendiente. Pedido realizado. No hemos recibido los fondos.'; +$strings['PendingReasonByPaymentReview'] = 'Pendiente. El pago está siendo revisado por PayPal por riesgo.'; +$strings['PendingReasonByRegulatoryReview'] = 'Pendiente. El pago está siendo revisado por cumplimiento con regulaciones gubernamentales.'; +$strings['PendingReasonByUnilateral'] = 'Pendiente. La dirección de correo electrónico aún no está registrada o confirmada.'; +$strings['PendingReasonByUpgrade'] = 'Pendiente. El pago fue realizado a través de una tarjeta de crédito.'; +$strings['PendingReasonByVerify'] = 'Pendiente. Lo sentimos. Todavía no estamos verificados en PayPal.'; +$strings['PendingReasonByOther'] = 'Pendiente. Por favor contacta al administrador de la plataforma.'; $strings['PayPalPaymentOKPleaseConfirm'] = "PayPal nos indicó que todo estaba listo para ejecutar el pago. Por seguridad, le pedimos confirme una última vez su pedido dando clic en el botón de confirmación a bajo. Una vez le haya dado clic, será registrado al curso y el monto correspondiente será retirado de su cuenta PayPal. Siempre puede acceder a sus cursos a partir de la pestaña 'Mis cursos'. Gracias por su compra!"; -$strings['Sandbox'] = "Entorno de Pruebas"; -$strings['PayPalAccount'] = "Cuenta de Paypal"; -$strings['NoPayPalAccountDetected'] = "No se ingresó una cuenta de Paypal"; -$strings['PayPalConfig'] = "Configuración PayPal:"; -$strings['TransfersConfig'] = "Configuración de transferencias:"; -$strings['PluginInstruction'] = "Puede activar o desactivar la opción de pagos vía PayPal, Culqi o configuración de transferencias bancarias, en la sección del configuración del plugin."; -$strings['ClickHere'] = "Clic aquí para más detalles"; -$strings['CurrencyType'] = "Tipo de moneda"; -$strings['InfoCurrency'] = "Permite configurar el tipo de moneda (de pago) para sus cursos."; -$strings['ApiUsername'] = "Nombre de usuario de API"; -$strings['ApiPassword'] = "Contraseña de API"; -$strings['ApiSignature'] = "Firma"; -$strings['InfoApiCredentials'] = "Para generar tu credenciales API para integrar su cuenta PayPal con Chamilo LMS, deberá seguir los siguientes pasos"; -$strings['InfoApiStepOne'] = "Ir a la opción de Perfil de PayPal, luego en Mis herramientas de venta"; -$strings['InfoApiStepTwo'] = "En el apartado Acceso API, dar clic en la opción Actualizar"; -$strings['InfoApiStepThree'] = "En la opción 2, de Configuración de credenciales y permisos de API, dar clic en Ver firma de API. Copiar los datos de las credenciales en el formulario de configuración de este plugin"; -$strings['ErrorOccurred'] = "Ocurrio un error. Codigo: %s. Mensaje: %s. Por favor, contacta al administrador de la plataforma."; -$strings['VisibleInCatalog'] = "Visible en el catálogo"; -$strings['Beneficiaries'] = "Beneficiarios"; -$strings['AvailableCourse'] = "Curso disponible"; -$strings['ShowOnCourseCatalog'] = "Mostrar en el catálogo de cursos"; -$strings['ByStatus'] = "Por estado"; -$strings['ByUser'] = "Por usuario"; -$strings['ByEmail'] = "Por email"; -$strings['PaymentMethod'] = "Método de pago"; -$strings['SWIFT'] = "Código SWIFT"; -$strings['SWIFT_help'] = "Formato estándar de los Códigos de Identificación Bancaria (BIC) que sirve como identificador único para un banco o institución financiera."; -$strings['include_services'] = "Incluir Servicios"; -$strings['Services'] = "Servicios"; -$strings['Service'] = "Servicio"; -$strings['NewService'] = "Nuevo servicio"; -$strings['ServiceName'] = "Nombre de servicio"; -$strings['AppliesTo'] = "Aplicado a"; -$strings['ServiceInformation'] = "Información del servicio"; -$strings['EditService'] = "Editar servicio"; -$strings['ServiceAdded'] = "Servicio agregado"; -$strings['ServiceEdited'] = "Servicio editado"; -$strings['ServiceSaleInfo'] = "Información del servicio"; -$strings['ListOfServicesOnSale'] = "Lista de servicios a la venta"; -$strings['AdditionalInfo'] = "Información adicional"; -$strings['PleaseSelectTheCorrectInfoToApplyTheService'] = "Porfavor Seleccione la información correcta para aplicar el servicio"; -$strings['SubscriptionToServiceXSuccessful'] = "La subscripción al servicio %s ha sido satisfactoria"; -$strings['culqi_enable'] = "Habilitar Culqi"; -$strings['CulqiConfig'] = "Configuración de CULQI:"; -$strings['InfoCulqiCredentials'] = "Para obtener las credenciales es necesario crearse una cuenta en Culqi e ingresar en modo desarrollo, copiar el código de comercio que se encuentra en su panel de control, luego ingresar al apartado de API Keys y generar la Key correspondiente para copiarla aquí"; -$strings['CommerceCode'] = "Codigo de comercio"; -$strings['IConfirmIReadAndAcceptTermsAndCondition'] = "He leído y aceptado los terminos y condiciones del servicio"; -$strings['NoTermsAndConditionsProvided'] = "Terminos y condiciones no establecidos"; -$strings['GlobalConfig'] = "Configuración global:"; -$strings['WriteHereTheTermsAndConditionsOfYourECommerce'] = "Escriba aquí los terminos y condiciones para su tienda virtual"; -$strings['PleaseSelectThePaymentMethodBeforeConfirmYourOrder'] = "Por favor seleccione el método de pago de su preferencia antes de confirmar su orden"; -$strings['MyServices'] = "Mis servicios"; -$strings['ServiceId'] = "Id de servicio"; -$strings['BoughtBy'] = "Comprado por"; -$strings['PurchaserUser'] = "Usuario comprador"; -$strings['SalePrice'] = "Precio de venta"; -$strings['Pending'] = "Pendiente"; -$strings['YouNeedToBeRegisteredInAtLeastOneCourse'] = "Necesitas estar registrado en al menos un curso"; -$strings['YouNeedToBeRegisteredInAtLeastOneSession'] = "Necesitas estar registrado en al menos una sesión"; -$strings['IfYouWantToGetTheCertificateAndOrSkillsAsociatedToThisCourseYouNeedToBuyTheCertificateServiceYouCanGoToServiceCatalogClickingHere'] = "Si quieres obtener el certificado y/o las competencias asociadas a este curso, necesitas comprar el servicio de Certificado , puedes ir al catálogo de servicios para comprarlo haciendo click AQUÍ"; +$strings['Sandbox'] = 'Entorno de Pruebas'; +$strings['PayPalAccount'] = 'Cuenta de Paypal'; +$strings['NoPayPalAccountDetected'] = 'No se ingresó una cuenta de Paypal'; +$strings['PayPalConfig'] = 'Configuración PayPal:'; +$strings['TransfersConfig'] = 'Configuración de transferencias:'; +$strings['PluginInstruction'] = 'Puede activar o desactivar la opción de pagos vía PayPal, Culqi o configuración de transferencias bancarias, en la sección del configuración del plugin.'; +$strings['ClickHere'] = 'Clic aquí para más detalles'; +$strings['CurrencyType'] = 'Tipo de moneda'; +$strings['InfoCurrency'] = 'Permite configurar el tipo de moneda (de pago) para sus cursos.'; +$strings['ApiUsername'] = 'Nombre de usuario de API'; +$strings['ApiPassword'] = 'Contraseña de API'; +$strings['ApiSignature'] = 'Firma'; +$strings['InfoApiCredentials'] = 'Para generar tu credenciales API para integrar su cuenta PayPal con Chamilo LMS, deberá seguir los siguientes pasos'; +$strings['InfoApiStepOne'] = 'Ir a la opción de Perfil de PayPal, luego en Mis herramientas de venta'; +$strings['InfoApiStepTwo'] = 'En el apartado Acceso API, dar clic en la opción Actualizar'; +$strings['InfoApiStepThree'] = 'En la opción 2, de Configuración de credenciales y permisos de API, dar clic en Ver firma de API. Copiar los datos de las credenciales en el formulario de configuración de este plugin'; +$strings['ErrorOccurred'] = 'Ocurrio un error. Codigo: %s. Mensaje: %s. Por favor, contacta al administrador de la plataforma.'; +$strings['VisibleInCatalog'] = 'Visible en el catálogo'; +$strings['Beneficiaries'] = 'Beneficiarios'; +$strings['AvailableCourse'] = 'Curso disponible'; +$strings['ShowOnCourseCatalog'] = 'Mostrar en el catálogo de cursos'; +$strings['ByStatus'] = 'Por estado'; +$strings['ByUser'] = 'Por usuario'; +$strings['ByEmail'] = 'Por email'; +$strings['PaymentMethod'] = 'Método de pago'; +$strings['SWIFT'] = 'Código SWIFT'; +$strings['SWIFT_help'] = 'Formato estándar de los Códigos de Identificación Bancaria (BIC) que sirve como identificador único para un banco o institución financiera.'; +$strings['PleaseSelectThePaymentMethodBeforeConfirmYourOrder'] = 'Seleccione su método de pago preferido antes de confirmar su pedido'; +$strings['NoPaymentOptionAvailable'] = 'No hay opción de pago disponible. Por favor reporte este problema al administrador.'; +$strings['XIsOnlyPaymentMethodAvailable'] = '%s es la única opción de pago disponible para esta compra.'; +$strings['hide_free_text'] = "Esconder texto 'Gratis'"; +$strings['culqi_enable'] = 'Activar culqi'; +$strings['include_services'] = 'Incluir Servicios'; +$strings['CurrencyIsNotConfigured'] = 'Configure una moneda antes de seguir.'; +$strings['Services'] = 'Servicios'; +$strings['Service'] = 'Servicio'; +$strings['NewService'] = 'Nuevo servicio'; +$strings['ServiceName'] = 'Nombre de servicio'; +$strings['AppliesTo'] = 'Aplicado a'; +$strings['ServiceInformation'] = 'Información del servicio'; +$strings['ListOfServicesOnSale'] = 'Lista de servicios en venta'; +$strings['GlobalConfig'] = 'Configuración global'; +$strings['WriteHereTheTermsAndConditionsOfYourECommerce'] = 'Escriba aquí los términos y condiciones de su portal e-commerce'; +$strings['EditService'] = 'Editar servicio'; +$strings['DeleteThisService'] = 'Borrar servicio'; +$strings['IConfirmIReadAndAcceptTermsAndCondition'] = 'He leído y acepto los términos y condiciones'; +$strings['PleaseSelectTheCorrectInfoToApplyTheService'] = 'Porfavor Seleccione la información correcta para aplicar el servicio'; +$strings['SaleStatusCancelled'] = 'Venta anulada'; +$strings['ServiceSaleInfo'] = 'Información del servicio'; +$strings['ServiceId'] = 'Id de servicio'; +$strings['BoughtBy'] = 'Comprado por'; +$strings['PurchaserUser'] = 'Usuario comprador'; +$strings['Pending'] = 'Pendiente'; +$strings['Names'] = 'Nombres'; +$strings['SellerName'] = 'Nombre vendedor'; +$strings['SellerId'] = 'Identificador vendedor'; +$strings['SellerAddress'] = 'Dirección vendedor'; +$strings['SellerEmail'] = 'E-mail vendedor'; +$strings['NextNumberInvoice'] = 'Número siguiente factura'; +$strings['NextNumberInvoiceDescription'] = 'Número de la siguiente factura asignado de forma manual'; +$strings['InvoiceSeries'] = 'Serie factura'; +$strings['InvoiceSeriesDescription'] = 'Parámetro opcional: Ejemplo de numeración factura <serie><año>/<número>'; +$strings['InvoiceView'] = 'Ver factura'; +$strings['NoInvoiceEnable'] = 'No está habilitado el bloque de facturación'; +$strings['Company'] = 'Empresa'; +$strings['VAT'] = 'CIF'; +$strings['Address'] = 'Dirección'; +$strings['InvoiceNumber'] = 'Num. factura'; +$strings['InvoiceDate'] = 'Fecha de emisión'; +$strings['Invoice'] = 'Factura'; +$strings['SaleEmail'] = 'E-mail de ventas'; +$strings['PurchaseDetailsIntro'] = 'Detalles de la comprar'; +$strings['PurchaseDetailsEnd'] = 'Atentamente'; +$strings['ProductName'] = 'Nombre producto'; +$strings['BankAccountIntro'] = 'Información cuentas bancarias'; $strings['AdditionalInfoRequired'] = 'Se requiere que se elija la información adicional antes de proceder'; +$strings['SubscriptionToServiceXSuccessful'] = 'La subscripción al servicio %s ha sido satisfactoria'; +$strings['ClickHereToFinish'] = 'De clic aquí para terminar'; +$strings['OrderCancelled'] = 'Pedido anulado'; +$strings['use_currency_symbol'] = 'Usar símbolo de la moneda'; +$strings['ExportReport'] = 'Exportar reporte de ventas'; +$strings['OrderTime'] = 'Fecha del pedido'; +$strings['SelectDateRange'] = 'Seleccione una fecha de inicio y una fecha de fin para el reporte'; +$strings['ServiceAdded'] = 'Servicio agregado'; +$strings['ServiceEdited'] = 'Servicio editado'; +$strings['ListOfServicesOnSale'] = 'Lista de servicios a la venta'; +$strings['AdditionalInfo'] = 'Información adicional'; +$strings['culqi_enable'] = 'Habilitar Culqi'; +$strings['CulqiConfig'] = 'Configuración de CULQI:'; +$strings['InfoCulqiCredentials'] = 'Para obtener las credenciales es necesario crearse una cuenta en Culqi e ingresar en modo desarrollo, copiar el código de comercio que se encuentra en su panel de control, luego ingresar al apartado de API Keys y generar la Key correspondiente para copiarla aquí'; +$strings['CommerceCode'] = 'Codigo de comercio'; +$strings['NoTermsAndConditionsProvided'] = 'Terminos y condiciones no establecidos'; +$strings['GlobalConfig'] = 'Configuración global:'; +$strings['MyServices'] = 'Mis servicios'; +$strings['SalePrice'] = 'Precio de venta'; +$strings['YouNeedToBeRegisteredInAtLeastOneCourse'] = 'Necesitas estar registrado en al menos un curso'; +$strings['YouNeedToBeRegisteredInAtLeastOneSession'] = 'Necesitas estar registrado en al menos una sesión'; +$strings['IfYouWantToGetTheCertificateAndOrSkillsAsociatedToThisCourseYouNeedToBuyTheCertificateServiceYouCanGoToServiceCatalogClickingHere'] = "Si quieres obtener el certificado y/o las competencias asociadas a este curso, necesitas comprar el servicio de Certificado , puedes ir al catálogo de servicios para comprarlo haciendo click AQUÍ"; $strings['ServiceDeleted'] = 'Servicio eliminado'; -$strings['DeleteThisService'] = 'Eliminar este servicio'; $strings['YourCoursesNeedAtLeastOneLearningPath'] = 'Los cursos en los que estás registrado necesitan tener al menos una lección que contenga un item de cerficado final'; -$strings['NoPaymentOptionAvailable'] = 'No existen opciones de pago. Por favor reporte este problema al administrador.'; -$strings['XIsOnlyPaymentMethodAvailable'] = '%s es el único método de pago disponible para esta compra.'; -$strings['GlobalTaxPerc'] = "Porcentaje del impuesto global"; -$strings['GlobalTaxPercDescription'] = "Porcentaje por defecto que se usará, excepto si existe un impuesto específico en el curso, sesión o servicio."; -$strings['TaxPerc'] = "Porcentaje del impuesto"; -$strings['TaxPercDescription'] = "Si se deja vacío se usará valor global por defecto."; -$strings['ByDefault'] = "por defecto (valor global)"; -$strings['OnlyCourses'] = "Solo Cursos"; -$strings['OnlySessions'] = "Solo Sesiones"; -$strings['OnlyServices'] = "Solo Servicios"; -$strings['TaxAppliesTo'] = "Impuestos aplicados a"; -$strings['AllCoursesSessionsAndServices'] = "Todos (Cursos, sesiones y servicios)"; -$strings['TaxNameCustom'] = "Nombre del impuesto"; -$strings['TaxNameExamples'] = "VAT, IVA, IGV, TVA, IV ..."; -$strings['ErrorUpdateFieldDB'] = "Error al actualizar los campos de la base de datos"; -$strings['SellerName'] = "Nombre vendedor"; -$strings['SellerId'] = "Identificador vendedor"; -$strings['SellerAddress'] = "Dirección vendedor"; -$strings['SellerEmail'] = "E-mail vendedor"; -$strings['NextNumberInvoice'] = "Número siguiente factura"; -$strings['NextNumberInvoiceDescription'] = "Número de la siguiente factura asignado de forma manual"; -$strings['InvoiceSeries'] = "Serie factura"; -$strings['InvoiceSeriesDescription'] = "Parámetro opcional: Ejemplo de numeración factura <serie><año>/<número>"; -$strings['InvoiceView'] = "Ver factura"; -$strings['NoInvoiceEnable'] = "No está habilitado el bloque de facturación"; -$strings['Company'] = "Empresa"; -$strings['VAT'] = "CIF"; -$strings['Address'] = "Dirección"; -$strings['InvoiceNumber'] = "Num. factura"; -$strings['InvoiceDate'] = "Fecha de emisión"; -$strings['Invoice'] = "Factura"; -$strings['SaleEmail'] = "E-mail de ventas"; -$strings['PurchaseDetailsIntro'] = "Detalles de la comprar"; -$strings['PurchaseDetailsEnd'] = "Atentamente"; -$strings['ProductName'] = "Nombre producto"; -$strings['BankAccountIntro'] = "Información cuentas bancarias"; -$strings['CurrencyIsNotConfigured'] = "Por favor, confogure una moneda antes de continuar."; -$strings['ExportReport'] = "Exportar reporte de ventas"; -$strings['OrderTime'] = "Hora de pedido"; -$strings['SelectDateRange'] = "Seleccione una fecha de inicio y fecha de fin para el reporte"; +$strings['GlobalTaxPerc'] = 'Porcentaje del impuesto global'; +$strings['GlobalTaxPercDescription'] = 'Porcentaje por defecto que se usará, excepto si existe un impuesto específico en el curso, sesión o servicio.'; +$strings['TaxPerc'] = 'Porcentaje del impuesto'; +$strings['TaxPercDescription'] = 'Si se deja vacío se usará valor global por defecto.'; +$strings['ByDefault'] = 'por defecto (valor global)'; +$strings['OnlyCourses'] = 'Solo Cursos'; +$strings['OnlySessions'] = 'Solo Sesiones'; +$strings['OnlyServices'] = 'Solo Servicios'; +$strings['TaxAppliesTo'] = 'Impuestos aplicados a'; +$strings['AllCoursesSessionsAndServices'] = 'Todos (Cursos, sesiones y servicios)'; +$strings['TaxNameCustom'] = 'Nombre del impuesto'; +$strings['TaxNameExamples'] = 'VAT, IVA, IGV, TVA, IV ...'; +$strings['ErrorUpdateFieldDB'] = 'Error al actualizar los campos de la base de datos'; +$strings['tpv_redsys_enable'] = 'Habilitar TPV RedSys'; +$strings['tpv_redsys_enable_help'] = "Para poder utilizar la modalidad de pago del TPV de RedSys es necesario descargar los ficheros de \"INTEGRACIÓN REST - API PHP\" en el siguiente enlace web de RedSys y ubicar el fichero el fichero apiRedSys.php en el directorio plugin/BuyCourses/resources."; +$strings['NotFindRedsysFile'] = 'No se encuentra en el directorio plugin/BuyCourses/resources el fichero apiRedsys.php'; +$strings['TpvPayment'] = 'Pago con tarjeta'; +$strings['TpvRedsysConfig'] = 'Configuración TPV Redsys'; +$strings['DS_MERCHANT_MERCHANTCODE'] = 'Número de comercio (FUC)'; +$strings['DS_MERCHANT_TERMINAL'] = 'Número de terminal'; +$strings['DS_MERCHANT_CURRENCY'] = 'Moneda del terminal'; +$strings['kc'] = 'Clave secreta de encriptación'; +$strings['url_redsys'] = 'URL conexión Redsys'; +$strings['url_redsys_sandbox'] = 'URL conexión Redsys (Pruebas)'; +$strings['InfoTpvRedsysApiCredentials'] = 'Deberá completar los siguientes campos del formulario con la información que les facilite el Soporte Técnico del TPV Redsys:'; +$strings['InfoEmailExtra'] = 'Información extra en e-mail'; +$strings['Coupon'] = 'Cupón'; +$strings['DiscountAmount'] = 'Descuento total'; +$strings['RedeemCoupon'] = 'Canjear'; +$strings['NeedToAddCouponCode'] = 'No se ha introducido un código de cupón'; +$strings['CouponNotValid'] = 'El cupón no es valido'; +$strings['CouponRedeemed'] = 'Cupón canjeado'; +$strings['CouponDiscount'] = 'Descuento'; +$strings['CouponStatus'] = 'Estado cupón'; +$strings['CouponPercentage'] = 'Porcentaje'; +$strings['CouponAmount'] = 'Valor'; +$strings['CouponList'] = 'Lista de cupones'; +$strings['CouponCode'] = 'Código del cupón'; +$strings['CouponDiscountType'] = 'Tipo de descuento'; +$strings['CouponDateStart'] = 'Valido desde'; +$strings['CouponDateEnd'] = 'Caduca el'; +$strings['CouponDelivered'] = 'Canjeados'; +$strings['CouponDisable'] = 'Desactivar'; +$strings['CouponEnable'] = 'Activar'; +$strings['CouponCodeUsed'] = 'Código de cupón ya usado'; +$strings['CouponNoExists'] = 'El cupón no existe'; +$strings['CouponErrorInsert'] = 'Error añadiendo nuevo cupón'; +$strings['CouponActive'] = 'Activo'; +$strings['CouponDisabled'] = 'Desactivado'; +$strings['CouponUpdate'] = 'Cupón actualizado'; +$strings['CouponsConfiguration'] = 'Configuración de cupones'; +$strings['CouponAdd'] = 'Nuevo cupón'; +$strings['ConfigureCoupon'] = 'Configurar cupón'; +$strings['DiscountCoupons'] = 'Cupones descuento'; +$strings['CouponsCode'] = 'Código'; +$strings['DoYouHaveACoupon'] = '¿Tienes un cupón?'; +$strings['stripe_enable'] = 'Activar Stripe'; +$strings['StripeConfig'] = 'Configuración de Stripe:'; +$strings['InfoStripeCredentials'] = 'Para obtener las credenciales debes crear una cuenta en Stripe, copiar el id de cuenta de tu perfil, ir a la sección de configuración de las claves API y copiar la secret key, por último deberá registrar un nuevo Endpoint en la sección de webhooks a https://{site}/plugin/BuyCourses/src/stripe_response.php para el tipo de evento checkout.session.completed y copiar el secret del Endpoint.'; +$strings['StripeAccountId'] = 'Id de la cuenta:'; +$strings['StripeSecret'] = 'Secret key:'; +$strings['StripeEndpointSecret'] = 'Endpoint secret:'; +$strings['PendingReasonByStripe'] = 'Pendiente. Esperando confirmación del pago ...'; +$strings['cecabank_enable'] = 'Habilitar TPV Cecabank'; +$strings['TpvCecabank'] = 'Pago con tarjeta (Cecabank)'; +$strings['CecaSecret'] = 'Secret'; +$strings['CecaUrl'] = 'Url'; +$strings['CecaMerchanId'] = 'Merchant'; +$strings['CecaAcquirerId'] = 'Adquirer'; +$strings['CecaTerminalId'] = 'Terminal'; +$strings['CecaCypher'] = 'Cypher'; +$strings['CecaCurrency'] = 'Moneda'; +$strings['CecaExponent'] = 'Exponent'; +$strings['CecaSupportedPayment'] = 'Pago soportado'; +$strings['CecabankConfig'] = 'Configuración Cecabank'; +$strings['Country'] = 'País'; +$strings['PaymentType'] = 'Tipo de pago'; +$strings['CountryRelPaymentConfig'] = 'Configuración del tipo de pago por país'; +$strings['CountryRelPaymentMessage'] = 'Para realizar un pedido es necesario definir el tipo de pago por país, en caso contrario no se permite la realización del pedido'; +$strings['CountryEmpty'] = 'Para realizar un pedido es necesario definir en el perfil del usuario el campo país'; +$strings['Duration'] = 'Duración'; +$strings['SubscriptionAdd'] = 'Añadir suscripción'; +$strings['SubscriptionList'] = 'Lista de suscripciones'; +$strings['SubscriptionListOnSale'] = 'Lista de suscripciones a la venta'; +$strings['SelectSubscription'] = 'Seleciona duración'; +$strings['SubscriptionNotValid'] = 'Suscripción no valida'; +$strings['SubscriptionSalesReport'] = 'Reporte de ventas'; +$strings['BuySubscriptions'] = 'Compra suscripciones'; +$strings['ConfigurationOfSubscriptionsAndPrices'] = 'Configuración de suscripciones y precios'; +$strings['FrequencyConfig'] = 'Configuración periodos'; +$strings['Subscriptions'] = 'Suscripciones'; +$strings['HasSubscriptions'] = 'Tiene suscripciones'; +$strings['FrequencyRemoved'] = 'Periodo eliminado'; +$strings['SubscriptionPeriodOnUse'] = 'Periodo de suscription en uso'; +$strings['FrequencyNotExits'] = 'El periodo no existe'; +$strings['FrequencyIncorrect'] = 'Periodo incorrecto'; +$strings['SubscriptionFrequencyValueDays'] = 'Valor de la suscripción en días'; +$strings['FrequencyNotUpdated'] = 'Periodo no actualizado'; +$strings['FrequencyNotSaved'] = 'Periodo no guardado'; +$strings['NeedToAddDuration'] = 'Hay que especificar una duración'; +$strings['SubscriptionNotValid'] = 'Suscripción no valida'; +$strings['SelecSubscription'] = 'Seleccione una suscripción'; +$strings['ConfigureSubscriptionsFrequencies'] = 'Configurar los períodos de suscripciones'; +$strings['FrequencyAdd'] = 'Agregar período'; +$strings['SubscriptionAlreadyExists'] = 'La suscripción ya existe'; +$strings['SubscriptionPeriodDuration'] = 'Duración de suscripción (en días)'; +$strings['Product'] = 'Producto'; +$strings['YouProductIsActivatedYouCanNowAccessIt'] = 'Tu producto esta activado y puedes ahora acceder a este.'; +$strings['hide_shopping_cart_from_course_catalogue'] = 'Esconder el carito de compra del catalogo de curso y dejar el boton de subscripcion a curso'; diff --git a/public/plugin/BuyCourses/plugin.php b/public/plugin/BuyCourses/plugin.php index 828ee671239..844d1409ae1 100644 --- a/public/plugin/BuyCourses/plugin.php +++ b/public/plugin/BuyCourses/plugin.php @@ -1,4 +1,7 @@ P)Kf!i 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 my?8$W-s5;b0N&Fu=KljWh15#Q(Eh6c0000 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 literal 0 HcmV?d00001 diff --git a/public/plugin/BuyCourses/resources/img/32/buysubscription.png b/public/plugin/BuyCourses/resources/img/32/buysubscription.png new file mode 100644 index 0000000000000000000000000000000000000000..359169ef617a13a8bae29a3f6340a407c6d2caf7 GIT binary patch literal 1263 zcmVwKexM1hP7#F&6XJX<;UF!ljhK*5IL=pvcV~C){L{SI^lwla!MwmxG>T&D1 zD7vS+r>DCI#y{!gR@b@z`R}>^sk$X}!J&8098lpHAl>5HQHv~d)d+x`?@b+lXV14Q z#hMSUYHq->2SmgJh!uZ2VrPPSbLVEZ&Ha9(CAs70FzZH_5veNi?Q3Xa$$Bme)tCfj z%adb=KbovS(Zmgn_om64c(X1q{lySeS5dV_PyL=?aZaDn#~1 zoFqAL9GfH=0N0@^B8e;O1My^?W8A!#^|Im$W#rD`XxMhc9wE21|XmFnA$aVSHjxS5b$T9 zlx*sQI1wh=w*S;mvn*Cq0mFHpsa<1~1NiXk2zzH<9h*iRrgpoH>v96W){@5ccvDpy z0_H9JoARTojC-whdwS~#3zf9BhVniDgBf`11&_EjKfVLgO@D@oHn;)SJE<941dMuM zB>gwk<&FRH#)lrN%fre;VZPrJF$+N>##jJBS9i_s!W~4ObH`Zts{T7;Ujj ziddJIAHbU7#<)849C3Xk)#5E)#CqdI16zA0ZQ>8j{aH?zYD^}QZbt|njy#?RXFzxs zc(D^nG_akZu=G-@4NrDXCcNi6oNUaqWWDEheXuGMHp5>`4VhUeVS);7SfMsFfho-( zwF+0C*hk{@_Cr@!sV?_NC%9n+fb9G%CJAtF6`8nxzMdhtYXQyxpQ^+2xzd$aH~jGV z)xxD8zv!w&6*3tRVYGNz4Dtivk-@5>%r;}TdB&h)7o$n{S zw*V8JgYFNL76i5Q6T|mDTN45D)V?C{cHaVO)zqtH|Kl|g03bUj-$&&L5cLeMR>02| zW&i$LYbF3dcJ2KHIMg-BT8LvX=1jl5t2`0e?#60-8*2Ig5s>I2NVwwtxGumgt{BsX Z{{XFxsll@FFLnR`002ovPDHLkV1faFN3H+> literal 0 HcmV?d00001 diff --git a/public/plugin/BuyCourses/resources/img/32/buysubscriptions.png b/public/plugin/BuyCourses/resources/img/32/buysubscriptions.png new file mode 100644 index 0000000000000000000000000000000000000000..359169ef617a13a8bae29a3f6340a407c6d2caf7 GIT binary patch literal 1263 zcmVwKexM1hP7#F&6XJX<;UF!ljhK*5IL=pvcV~C){L{SI^lwla!MwmxG>T&D1 zD7vS+r>DCI#y{!gR@b@z`R}>^sk$X}!J&8098lpHAl>5HQHv~d)d+x`?@b+lXV14Q z#hMSUYHq->2SmgJh!uZ2VrPPSbLVEZ&Ha9(CAs70FzZH_5veNi?Q3Xa$$Bme)tCfj z%adb=KbovS(Zmgn_om64c(X1q{lySeS5dV_PyL=?aZaDn#~1 zoFqAL9GfH=0N0@^B8e;O1My^?W8A!#^|Im$W#rD`XxMhc9wE21|XmFnA$aVSHjxS5b$T9 zlx*sQI1wh=w*S;mvn*Cq0mFHpsa<1~1NiXk2zzH<9h*iRrgpoH>v96W){@5ccvDpy z0_H9JoARTojC-whdwS~#3zf9BhVniDgBf`11&_EjKfVLgO@D@oHn;)SJE<941dMuM zB>gwk<&FRH#)lrN%fre;VZPrJF$+N>##jJBS9i_s!W~4ObH`Zts{T7;Ujj ziddJIAHbU7#<)849C3Xk)#5E)#CqdI16zA0ZQ>8j{aH?zYD^}QZbt|njy#?RXFzxs zc(D^nG_akZu=G-@4NrDXCcNi6oNUaqWWDEheXuGMHp5>`4VhUeVS);7SfMsFfho-( zwF+0C*hk{@_Cr@!sV?_NC%9n+fb9G%CJAtF6`8nxzMdhtYXQyxpQ^+2xzd$aH~jGV z)xxD8zv!w&6*3tRVYGNz4Dtivk-@5>%r;}TdB&h)7o$n{S zw*V8JgYFNL76i5Q6T|mDTN45D)V?C{cHaVO)zqtH|Kl|g03bUj-$&&L5cLeMR>02| zW&i$LYbF3dcJ2KHIMg-BT8LvX=1jl5t2`0e?#60-8*2Ig5s>I2NVwwtxGumgt{BsX Z{{XFxsll@FFLnR`002ovPDHLkV1faFN3H+> literal 0 HcmV?d00001 diff --git a/public/plugin/BuyCourses/resources/img/32/discount.png b/public/plugin/BuyCourses/resources/img/32/discount.png new file mode 100644 index 0000000000000000000000000000000000000000..ec27b0093be29408d5eff2073e658c72be3e1c63 GIT binary patch literal 4181 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 literal 0 HcmV?d00001 diff --git a/public/plugin/BuyCourses/resources/img/32/subscriptionssettings.png b/public/plugin/BuyCourses/resources/img/32/subscriptionssettings.png new file mode 100644 index 0000000000000000000000000000000000000000..21f37ff019db9ff36684650041f37cb3beaa61a1 GIT binary patch literal 1680 zcmV;B25K~z|U&6itjTt^wle{=Ti&E9qFU3=}dBgctzHz*=OEwofL zRcVD#=mSt96$uH^0#7`E;E~WGR0tR$@j{Cbaat50K}0kp!ic5{O;yp9IIxZrCr-1D zZ)@+a_rB*cXLva4wKuU{0%^tn>6|m)I6k$G>!6&-wN20kri771|SCzc*l8R(o&r^nLFZEND3Ue16|1 z$8mHV`IUFG2M_@2z&AIeiJzhvSUGs{(~m|HANpWxAy?wS$ul!OH1ys)`FbZ@F#LOrUgJWk;0x^vBCnhpaP5Dm3~X}Y1D!M-xi0hlOxD8ipbnN;dpt? z3tX zzYBn(r82w^6}VP{s-*mpsAyjLw(dz-EE|kY%#bU%=Z};QV>f1*o?oonyfd9<{AQ~D zE>amhm&OrQ6M#z9WOQPN^kQk%9P1Joeq)ruBg6dh><9qk$rL~O)t~vS8ls~uOuQE3cKm;9;Qc@Yfx)3+o<20h=)?>F z-jlD<{Mm2ed-*ir3htTj9_4#KKUMen`t!fxYtOw*q2zAw7mlCf=|e-jec3$`|Mrh- zeEB;=y!6VOs}g9?fMMDw1(qcN5QeK{YKpcjiQ^~?bGb{JmW^dgZ0UBweciJ>*?)~r z@jCjg79N#zeDo83lA?s<{yPXXXrNLxDOF6Ots%E%*b^8%1wED`YQGza|v z6iQWW=@1E)@!$L*o>HO~SwM6E7XIb-fP%ng1EV{hukPvT4X#RH)f)W=5XLM`sl!mX z304Iv0t}!MfC><_q00u@3~il?^z?4=z=<)qO#^@U>qQ2KhM7ub0eJgzlEEXxoWFP- zfcb2JpB()Q$4>u!g_k0bJPgGt2=+i)gS-GkfNn#R33)FhLJ;bN)SRGboLlm4An?!e zDMqi)Fgu?I;GL-jMki*to}33@AzLJIZJLoQx9UFn;_GPTF)#{n;|46rBN&98+achC z)+%Tc-b#U|1QzY;)lYo`Y9h29e7XjofBeJDW%BIW5(nU8d$$p73$d*?2Edk$9enYL z2Z*#ZtswBK`%n>J*BZXp3w?c%&BB(=aCID74VVc*#tXUyws;2ku?~>2&|`ks-q*?Y zzRr5^SR_m=60X1deL4^A-P*AJTKTH`V614OUN2ZC%+7&rgQkH;g4cnZ7rHF4-TRUb zFj*m2l*ED_?iyP+#6iR%dlitiYLvk+Vb@N`WWjRpiFh2aHR#_BwhYt>I53aT?)ehN4Qm(xD2K;>I6`HRSB+YFrdWUT}T|^?eADAB4*A7k-cv%#W zoOrxpNwrS^0000T+ literal 0 HcmV?d00001 diff --git a/public/plugin/BuyCourses/resources/img/42/buysubscriptions.png b/public/plugin/BuyCourses/resources/img/42/buysubscriptions.png new file mode 100644 index 0000000000000000000000000000000000000000..359169ef617a13a8bae29a3f6340a407c6d2caf7 GIT binary patch literal 1263 zcmVwKexM1hP7#F&6XJX<;UF!ljhK*5IL=pvcV~C){L{SI^lwla!MwmxG>T&D1 zD7vS+r>DCI#y{!gR@b@z`R}>^sk$X}!J&8098lpHAl>5HQHv~d)d+x`?@b+lXV14Q z#hMSUYHq->2SmgJh!uZ2VrPPSbLVEZ&Ha9(CAs70FzZH_5veNi?Q3Xa$$Bme)tCfj z%adb=KbovS(Zmgn_om64c(X1q{lySeS5dV_PyL=?aZaDn#~1 zoFqAL9GfH=0N0@^B8e;O1My^?W8A!#^|Im$W#rD`XxMhc9wE21|XmFnA$aVSHjxS5b$T9 zlx*sQI1wh=w*S;mvn*Cq0mFHpsa<1~1NiXk2zzH<9h*iRrgpoH>v96W){@5ccvDpy z0_H9JoARTojC-whdwS~#3zf9BhVniDgBf`11&_EjKfVLgO@D@oHn;)SJE<941dMuM zB>gwk<&FRH#)lrN%fre;VZPrJF$+N>##jJBS9i_s!W~4ObH`Zts{T7;Ujj ziddJIAHbU7#<)849C3Xk)#5E)#CqdI16zA0ZQ>8j{aH?zYD^}QZbt|njy#?RXFzxs zc(D^nG_akZu=G-@4NrDXCcNi6oNUaqWWDEheXuGMHp5>`4VhUeVS);7SfMsFfho-( zwF+0C*hk{@_Cr@!sV?_NC%9n+fb9G%CJAtF6`8nxzMdhtYXQyxpQ^+2xzd$aH~jGV z)xxD8zv!w&6*3tRVYGNz4Dtivk-@5>%r;}TdB&h)7o$n{S zw*V8JgYFNL76i5Q6T|mDTN45D)V?C{cHaVO)zqtH|Kl|g03bUj-$&&L5cLeMR>02| zW&i$LYbF3dcJ2KHIMg-BT8LvX=1jl5t2`0e?#60-8*2Ig5s>I2NVwwtxGumgt{BsX Z{{XFxsll@FFLnR`002ovPDHLkV1faFN3H+> literal 0 HcmV?d00001 diff --git a/public/plugin/BuyCourses/resources/img/42/discount.png b/public/plugin/BuyCourses/resources/img/42/discount.png new file mode 100644 index 0000000000000000000000000000000000000000..9846e3eff2fe640c7d3902c0bea4c5d8db63ba30 GIT binary patch literal 5070 zcmV;<6EW004&%004{+008|`004nN004b?008NW002DY000@xb3BE2000Uv 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

cq7 zefxZvEy$0#CE#&T#;mYs|BEN3Wz^pH2k2Wwx7>KcmLV}3WI{8)_f>Q`)rWvw45<*7 zo=uxCd%%k4*Ux#z4-Z?0#AuK=w%*KBF?osDoKN$YK%R(_T0=hVg~dnsS<=W;aM+(a zE5l+m$b{wXD$41PVMcB*`6+V|=*8&1C^F9--8}Cd(HjZ*P>u$vYT`E+xyX>jXqx1M kzT}@fzU>D8w?wJ`0{K~z|U&6itjTt^wle{=Ti&E9qFU3=}dBgctzHz*=OEwofL zRcVD#=mSt96$uH^0#7`E;E~WGR0tR$@j{Cbaat50K}0kp!ic5{O;yp9IIxZrCr-1D zZ)@+a_rB*cXLva4wKuU{0%^tn>6|m)I6k$G>!6&-wN20kri771|SCzc*l8R(o&r^nLFZEND3Ue16|1 z$8mHV`IUFG2M_@2z&AIeiJzhvSUGs{(~m|HANpWxAy?wS$ul!OH1ys)`FbZ@F#LOrUgJWk;0x^vBCnhpaP5Dm3~X}Y1D!M-xi0hlOxD8ipbnN;dpt? z3tX zzYBn(r82w^6}VP{s-*mpsAyjLw(dz-EE|kY%#bU%=Z};QV>f1*o?oonyfd9<{AQ~D zE>amhm&OrQ6M#z9WOQPN^kQk%9P1Joeq)ruBg6dh><9qk$rL~O)t~vS8ls~uOuQE3cKm;9;Qc@Yfx)3+o<20h=)?>F z-jlD<{Mm2ed-*ir3htTj9_4#KKUMen`t!fxYtOw*q2zAw7mlCf=|e-jec3$`|Mrh- zeEB;=y!6VOs}g9?fMMDw1(qcN5QeK{YKpcjiQ^~?bGb{JmW^dgZ0UBweciJ>*?)~r z@jCjg79N#zeDo83lA?s<{yPXXXrNLxDOF6Ots%E%*b^8%1wED`YQGza|v z6iQWW=@1E)@!$L*o>HO~SwM6E7XIb-fP%ng1EV{hukPvT4X#RH)f)W=5XLM`sl!mX z304Iv0t}!MfC><_q00u@3~il?^z?4=z=<)qO#^@U>qQ2KhM7ub0eJgzlEEXxoWFP- zfcb2JpB()Q$4>u!g_k0bJPgGt2=+i)gS-GkfNn#R33)FhLJ;bN)SRGboLlm4An?!e zDMqi)Fgu?I;GL-jMki*to}33@AzLJIZJLoQx9UFn;_GPTF)#{n;|46rBN&98+achC z)+%Tc-b#U|1QzY;)lYo`Y9h29e7XjofBeJDW%BIW5(nU8d$$p73$d*?2Edk$9enYL z2Z*#ZtswBK`%n>J*BZXp3w?c%&BB(=aCID74VVc*#tXUyws;2ku?~>2&|`ks-q*?Y zzRr5^SR_m=60X1deL4^A-P*AJTKTH`V614OUN2ZC%+7&rgQkH;g4cnZ7rHF4-TRUb zFj*m2l*ED_?iyP+#6iR%dlitiYLvk+Vb@N`WWjRpiFh2aHR#_BwhYt>I53aT?)ehN4Qm(xD2K;>I6`HRSB+YFrdWUT}T|^?eADAB4*A7k-cv%#W zoOrxpNwrS^0000T+ literal 0 HcmV?d00001 diff --git a/public/plugin/BuyCourses/resources/img/64/buysubscriptions.png b/public/plugin/BuyCourses/resources/img/64/buysubscriptions.png new file mode 100644 index 0000000000000000000000000000000000000000..be1bed82d16c3c3f17acf258ae67bd48edcf83d3 GIT binary patch literal 2148 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"btn--primary":"btn--plain")}),a}function i(a,b){var c=a.length,d={};if(1>c||c>2)throw new Error("Invalid argument length");return 2===c||"string"==typeof a[0]?(d[b[0]]=a[0],d[b[1]]=a[1]):d=a[0],d}function j(a,c,d){return b.extend(!0,{},a,i(c,d))}function k(a,b,c,d){var e={className:"bootbox-"+a,buttons:l.apply(null,b)};return m(j(e,d,c),b)}function l(){for(var a={},b=0,c=arguments.length;c>b;b++){var e=arguments[b],f=e.toLowerCase(),g=e.toUpperCase();a[f]={label:d(g)}}return a}function m(a,b){var d={};return g(b,function(a,b){d[b]=!0}),g(a.buttons,function(a){if(d[a]===c)throw new Error("button key "+a+" is not allowed (options are "+b.join("\n")+")")}),a}var n={dialog:"

",header:"",footer:"",closeButton:"",form:"
",inputs:{text:"",textarea:"",email:"",select:"",checkbox:"
",date:"",time:"",number:"",password:""}},o={locale:"en",backdrop:"static",animate:!0,className:null,closeButton:!0,show:!0,container:"body"},p={};p.alert=function(){var a;if(a=k("alert",["ok"],["message","callback"],arguments),a.callback&&!b.isFunction(a.callback))throw new Error("alert requires callback property to be a function when provided");return a.buttons.ok.callback=a.onEscape=function(){return b.isFunction(a.callback)?a.callback.call(this):!0},p.dialog(a)},p.confirm=function(){var a;if(a=k("confirm",["cancel","confirm"],["message","callback"],arguments),a.buttons.cancel.callback=a.onEscape=function(){return a.callback.call(this,!1)},a.buttons.confirm.callback=function(){return a.callback.call(this,!0)},!b.isFunction(a.callback))throw new Error("confirm requires a callback");return p.dialog(a)},p.prompt=function(){var a,d,e,f,h,i,k;if(f=b(n.form),d={className:"bootbox-prompt",buttons:l("cancel","confirm"),value:"",inputType:"text"},a=m(j(d,arguments,["title","callback"]),["cancel","confirm"]),i=a.show===c?!0:a.show,a.message=f,a.buttons.cancel.callback=a.onEscape=function(){return a.callback.call(this,null)},a.buttons.confirm.callback=function(){var c;switch(a.inputType){case"text":case"textarea":case"email":case"select":case"date":case"time":case"number":case"password":c=h.val();break;case"checkbox":var d=h.find("input:checked");c=[],g(d,function(a,d){c.push(b(d).val())})}return a.callback.call(this,c)},a.show=!1,!a.title)throw new Error("prompt requires a title");if(!b.isFunction(a.callback))throw new Error("prompt requires a callback");if(!n.inputs[a.inputType])throw new Error("invalid prompt type");switch(h=b(n.inputs[a.inputType]),a.inputType){case"text":case"textarea":case"email":case"date":case"time":case"number":case"password":h.val(a.value);break;case"select":var o={};if(k=a.inputOptions||[],!b.isArray(k))throw new Error("Please pass an array of input options");if(!k.length)throw new Error("prompt with select requires options");g(k,function(a,d){var e=h;if(d.value===c||d.text===c)throw new Error("given options in wrong format");d.group&&(o[d.group]||(o[d.group]=b("").attr("label",d.group)),e=o[d.group]),e.append("")}),g(o,function(a,b){h.append(b)}),h.val(a.value);break;case"checkbox":var q=b.isArray(a.value)?a.value:[a.value];if(k=a.inputOptions||[],!k.length)throw new Error("prompt with checkbox requires options");if(!k[0].value||!k[0].text)throw new Error("given options in wrong format");h=b("
"),g(k,function(c,d){var e=b(n.inputs[a.inputType]);e.find("input").attr("value",d.value),e.find("label").append(d.text),g(q,function(a,b){b===d.value&&e.find("input").prop("checked",!0)}),h.append(e)})}return a.placeholder&&h.attr("placeholder",a.placeholder),a.pattern&&h.attr("pattern",a.pattern),a.maxlength&&h.attr("maxlength",a.maxlength),f.append(h),f.on("submit",function(a){a.preventDefault(),a.stopPropagation(),e.find(".btn--primary").click()}),e=p.dialog(a),e.off("shown.bs.modal"),e.on("shown.bs.modal",function(){h.focus()}),i===!0&&e.modal("show"),e},p.dialog=function(a){a=h(a);var d=b(n.dialog),f=d.find(".modal-dialog"),i=d.find(".modal-body"),j=a.buttons,k="",l={onEscape:a.onEscape};if(b.fn.modal===c)throw new Error("$.fn.modal is not defined; please double check you have included the Bootstrap JavaScript library. See http://getbootstrap.com/javascript/ for more details.");if(g(j,function(a,b){k+="",l[a]=b.callback}),i.find(".bootbox-body").html(a.message),a.animate===!0&&d.addClass("fade"),a.className&&d.addClass(a.className),"large"===a.size?f.addClass("modal-lg"):"small"===a.size&&f.addClass("modal-sm"),a.title&&i.before(n.header),a.closeButton){var m=b(n.closeButton);a.title?d.find(".modal-header").prepend(m):m.css("margin-top","-10px").prependTo(i)}return a.title&&d.find(".modal-title").html(a.title),k.length&&(i.after(n.footer),d.find(".modal-footer").html(k)),d.on("hidden.bs.modal",function(a){a.target===this&&d.remove()}),d.on("shown.bs.modal",function(){d.find(".btn--primary:first").focus()}),"static"!==a.backdrop&&d.on("click.dismiss.bs.modal",function(a){d.children(".modal-backdrop").length&&(a.currentTarget=d.children(".modal-backdrop").get(0)),a.target===a.currentTarget&&d.trigger("escape.close.bb")}),d.on("escape.close.bb",function(a){l.onEscape&&e(a,d,l.onEscape)}),d.on("click",".modal-footer button",function(a){var c=b(this).data("bb-handler");e(a,d,l[c])}),d.on("click",".bootbox-close-button",function(a){e(a,d,l.onEscape)}),d.on("keyup",function(a){27===a.which&&d.trigger("escape.close.bb")}),b(a.container).append(d),d.modal({backdrop:a.backdrop?"static":!1,keyboard:!1,show:!1}),a.show&&d.modal("show"),d},p.setDefaults=function(){var a={};2===arguments.length?a[arguments[0]]=arguments[1]:a=arguments[0],b.extend(o,a)},p.hideAll=function(){return b(".bootbox").modal("hide"),p};var q={bg_BG:{OK:"Ок",CANCEL:"Отказ",CONFIRM:"Потвърждавам"},br:{OK:"OK",CANCEL:"Cancelar",CONFIRM:"Sim"},cs:{OK:"OK",CANCEL:"Zrušit",CONFIRM:"Potvrdit"},da:{OK:"OK",CANCEL:"Annuller",CONFIRM:"Accepter"},de:{OK:"OK",CANCEL:"Abbrechen",CONFIRM:"Akzeptieren"},el:{OK:"Εντάξει",CANCEL:"Ακύρωση",CONFIRM:"Επιβεβαίωση"},en:{OK:"OK",CANCEL:"Cancel",CONFIRM:"OK"},es:{OK:"OK",CANCEL:"Cancelar",CONFIRM:"Aceptar"},et:{OK:"OK",CANCEL:"Katkesta",CONFIRM:"OK"},fa:{OK:"قبول",CANCEL:"لغو",CONFIRM:"تایید"},fi:{OK:"OK",CANCEL:"Peruuta",CONFIRM:"OK"},fr:{OK:"OK",CANCEL:"Annuler",CONFIRM:"D'accord"},he:{OK:"אישור",CANCEL:"ביטול",CONFIRM:"אישור"},hu:{OK:"OK",CANCEL:"Mégsem",CONFIRM:"Megerősít"},hr:{OK:"OK",CANCEL:"Odustani",CONFIRM:"Potvrdi"},id:{OK:"OK",CANCEL:"Batal",CONFIRM:"OK"},it:{OK:"OK",CANCEL:"Annulla",CONFIRM:"Conferma"},ja:{OK:"OK",CANCEL:"キャンセル",CONFIRM:"確認"},lt:{OK:"Gerai",CANCEL:"Atšaukti",CONFIRM:"Patvirtinti"},lv:{OK:"Labi",CANCEL:"Atcelt",CONFIRM:"Apstiprināt"},nl:{OK:"OK",CANCEL:"Annuleren",CONFIRM:"Accepteren"},no:{OK:"OK",CANCEL:"Avbryt",CONFIRM:"OK"},pl:{OK:"OK",CANCEL:"Anuluj",CONFIRM:"Potwierdź"},pt:{OK:"OK",CANCEL:"Cancelar",CONFIRM:"Confirmar"},ru:{OK:"OK",CANCEL:"Отмена",CONFIRM:"Применить"},sq:{OK:"OK",CANCEL:"Anulo",CONFIRM:"Prano"},sv:{OK:"OK",CANCEL:"Avbryt",CONFIRM:"OK"},th:{OK:"ตกลง",CANCEL:"ยกเลิก",CONFIRM:"ยืนยัน"},tr:{OK:"Tamam",CANCEL:"İptal",CONFIRM:"Onayla"},zh_CN:{OK:"OK",CANCEL:"取消",CONFIRM:"确认"},zh_TW:{OK:"OK",CANCEL:"取消",CONFIRM:"確認"}};return p.addLocale=function(a,c){return b.each(["OK","CANCEL","CONFIRM"],function(a,b){if(!c[b])throw new Error("Please supply a translation for '"+b+"'")}),q[a]={OK:c.OK,CANCEL:c.CANCEL,CONFIRM:c.CONFIRM},p},p.removeLocale=function(a){return delete q[a],p},p.setLocale=function(a){return p.setDefaults("locale",a)},p.init=function(c){return a(c||b)},p}); +!function(a,b){"use strict";"function"==typeof define&&define.amd?define(["jquery"],b):"object"==typeof exports?module.exports=b(require("jquery")):a.bootbox=b(a.jQuery)}(this,function a(b,c){"use strict";function d(a){var b=q[o.locale];return b?b[a]:q.en[a]}function e(a,c,d){a.stopPropagation(),a.preventDefault();var e=b.isFunction(d)&&d.call(c,a)===!1;e||c.modal("hide")}function f(a){var b,c=0;for(b in a)c++;return c}function g(a,c){var d=0;b.each(a,function(a,b){c(a,b,d++)})}function h(a){var c,d;if("object"!=typeof a)throw new Error("Please supply an object of options");if(!a.message)throw new Error("Please specify a message");return a=b.extend({},o,a),a.buttons||(a.buttons={}),c=a.buttons,d=f(c),g(c,function(a,e,f){if(b.isFunction(e)&&(e=c[a]={callback:e}),"object"!==b.type(e))throw new Error("button with key "+a+" must be an object");e.label||(e.label=a),e.className||(e.className=2>=d&&f===d-1?"btn-primary":"btn-default")}),a}function i(a,b){var c=a.length,d={};if(1>c||c>2)throw new Error("Invalid argument length");return 2===c||"string"==typeof a[0]?(d[b[0]]=a[0],d[b[1]]=a[1]):d=a[0],d}function j(a,c,d){return b.extend(!0,{},a,i(c,d))}function k(a,b,c,d){var e={className:"bootbox-"+a,buttons:l.apply(null,b)};return m(j(e,d,c),b)}function l(){for(var a={},b=0,c=arguments.length;c>b;b++){var e=arguments[b],f=e.toLowerCase(),g=e.toUpperCase();a[f]={label:d(g)}}return a}function m(a,b){var d={};return g(b,function(a,b){d[b]=!0}),g(a.buttons,function(a){if(d[a]===c)throw new Error("button key "+a+" is not allowed (options are "+b.join("\n")+")")}),a}var n={dialog:"",header:"",footer:"",closeButton:"",form:"
",inputs:{text:"",textarea:"",email:"",select:"",checkbox:"
",date:"",time:"",number:"",password:""}},o={locale:"en",backdrop:"static",animate:!0,className:null,closeButton:!0,show:!0,container:"body"},p={};p.alert=function(){var a;if(a=k("alert",["ok"],["message","callback"],arguments),a.callback&&!b.isFunction(a.callback))throw new Error("alert requires callback property to be a function when provided");return a.buttons.ok.callback=a.onEscape=function(){return b.isFunction(a.callback)?a.callback.call(this):!0},p.dialog(a)},p.confirm=function(){var a;if(a=k("confirm",["cancel","confirm"],["message","callback"],arguments),a.buttons.cancel.callback=a.onEscape=function(){return a.callback.call(this,!1)},a.buttons.confirm.callback=function(){return a.callback.call(this,!0)},!b.isFunction(a.callback))throw new Error("confirm requires a callback");return p.dialog(a)},p.prompt=function(){var a,d,e,f,h,i,k;if(f=b(n.form),d={className:"bootbox-prompt",buttons:l("cancel","confirm"),value:"",inputType:"text"},a=m(j(d,arguments,["title","callback"]),["cancel","confirm"]),i=a.show===c?!0:a.show,a.message=f,a.buttons.cancel.callback=a.onEscape=function(){return a.callback.call(this,null)},a.buttons.confirm.callback=function(){var c;switch(a.inputType){case"text":case"textarea":case"email":case"select":case"date":case"time":case"number":case"password":c=h.val();break;case"checkbox":var d=h.find("input:checked");c=[],g(d,function(a,d){c.push(b(d).val())})}return a.callback.call(this,c)},a.show=!1,!a.title)throw new Error("prompt requires a title");if(!b.isFunction(a.callback))throw new Error("prompt requires a callback");if(!n.inputs[a.inputType])throw new Error("invalid prompt type");switch(h=b(n.inputs[a.inputType]),a.inputType){case"text":case"textarea":case"email":case"date":case"time":case"number":case"password":h.val(a.value);break;case"select":var o={};if(k=a.inputOptions||[],!b.isArray(k))throw new Error("Please pass an array of input options");if(!k.length)throw new Error("prompt with select requires options");g(k,function(a,d){var e=h;if(d.value===c||d.text===c)throw new Error("given options in wrong format");d.group&&(o[d.group]||(o[d.group]=b("").attr("label",d.group)),e=o[d.group]),e.append("")}),g(o,function(a,b){h.append(b)}),h.val(a.value);break;case"checkbox":var q=b.isArray(a.value)?a.value:[a.value];if(k=a.inputOptions||[],!k.length)throw new Error("prompt with checkbox requires options");if(!k[0].value||!k[0].text)throw new Error("given options in wrong format");h=b("
"),g(k,function(c,d){var e=b(n.inputs[a.inputType]);e.find("input").attr("value",d.value),e.find("label").append(d.text),g(q,function(a,b){b===d.value&&e.find("input").prop("checked",!0)}),h.append(e)})}return a.placeholder&&h.attr("placeholder",a.placeholder),a.pattern&&h.attr("pattern",a.pattern),a.maxlength&&h.attr("maxlength",a.maxlength),f.append(h),f.on("submit",function(a){a.preventDefault(),a.stopPropagation(),e.find(".btn-primary").click()}),e=p.dialog(a),e.off("shown.bs.modal"),e.on("shown.bs.modal",function(){h.focus()}),i===!0&&e.modal("show"),e},p.dialog=function(a){a=h(a);var d=b(n.dialog),f=d.find(".modal-dialog"),i=d.find(".modal-body"),j=a.buttons,k="",l={onEscape:a.onEscape};if(b.fn.modal===c)throw new Error("$.fn.modal is not defined; please double check you have included the Bootstrap JavaScript library. See http://getbootstrap.com/javascript/ for more details.");if(g(j,function(a,b){k+="",l[a]=b.callback}),i.find(".bootbox-body").html(a.message),a.animate===!0&&d.addClass("fade"),a.className&&d.addClass(a.className),"large"===a.size?f.addClass("modal-lg"):"small"===a.size&&f.addClass("modal-sm"),a.title&&i.before(n.header),a.closeButton){var m=b(n.closeButton);a.title?d.find(".modal-header").prepend(m):m.css("margin-top","-10px").prependTo(i)}return a.title&&d.find(".modal-title").html(a.title),k.length&&(i.after(n.footer),d.find(".modal-footer").html(k)),d.on("hidden.bs.modal",function(a){a.target===this&&d.remove()}),d.on("shown.bs.modal",function(){d.find(".btn-primary:first").focus()}),"static"!==a.backdrop&&d.on("click.dismiss.bs.modal",function(a){d.children(".modal-backdrop").length&&(a.currentTarget=d.children(".modal-backdrop").get(0)),a.target===a.currentTarget&&d.trigger("escape.close.bb")}),d.on("escape.close.bb",function(a){l.onEscape&&e(a,d,l.onEscape)}),d.on("click",".modal-footer button",function(a){var c=b(this).data("bb-handler");e(a,d,l[c])}),d.on("click",".bootbox-close-button",function(a){e(a,d,l.onEscape)}),d.on("keyup",function(a){27===a.which&&d.trigger("escape.close.bb")}),b(a.container).append(d),d.modal({backdrop:a.backdrop?"static":!1,keyboard:!1,show:!1}),a.show&&d.modal("show"),d},p.setDefaults=function(){var a={};2===arguments.length?a[arguments[0]]=arguments[1]:a=arguments[0],b.extend(o,a)},p.hideAll=function(){return b(".bootbox").modal("hide"),p};var q={bg_BG:{OK:"Ок",CANCEL:"Отказ",CONFIRM:"Потвърждавам"},br:{OK:"OK",CANCEL:"Cancelar",CONFIRM:"Sim"},cs:{OK:"OK",CANCEL:"Zrušit",CONFIRM:"Potvrdit"},da:{OK:"OK",CANCEL:"Annuller",CONFIRM:"Accepter"},de:{OK:"OK",CANCEL:"Abbrechen",CONFIRM:"Akzeptieren"},el:{OK:"Εντάξει",CANCEL:"Ακύρωση",CONFIRM:"Επιβεβαίωση"},en:{OK:"OK",CANCEL:"Cancel",CONFIRM:"OK"},es:{OK:"OK",CANCEL:"Cancelar",CONFIRM:"Aceptar"},et:{OK:"OK",CANCEL:"Katkesta",CONFIRM:"OK"},fa:{OK:"قبول",CANCEL:"لغو",CONFIRM:"تایید"},fi:{OK:"OK",CANCEL:"Peruuta",CONFIRM:"OK"},fr:{OK:"OK",CANCEL:"Annuler",CONFIRM:"D'accord"},he:{OK:"אישור",CANCEL:"ביטול",CONFIRM:"אישור"},hu:{OK:"OK",CANCEL:"Mégsem",CONFIRM:"Megerősít"},hr:{OK:"OK",CANCEL:"Odustani",CONFIRM:"Potvrdi"},id:{OK:"OK",CANCEL:"Batal",CONFIRM:"OK"},it:{OK:"OK",CANCEL:"Annulla",CONFIRM:"Conferma"},ja:{OK:"OK",CANCEL:"キャンセル",CONFIRM:"確認"},lt:{OK:"Gerai",CANCEL:"Atšaukti",CONFIRM:"Patvirtinti"},lv:{OK:"Labi",CANCEL:"Atcelt",CONFIRM:"Apstiprināt"},nl:{OK:"OK",CANCEL:"Annuleren",CONFIRM:"Accepteren"},no:{OK:"OK",CANCEL:"Avbryt",CONFIRM:"OK"},pl:{OK:"OK",CANCEL:"Anuluj",CONFIRM:"Potwierdź"},pt:{OK:"OK",CANCEL:"Cancelar",CONFIRM:"Confirmar"},ru:{OK:"OK",CANCEL:"Отмена",CONFIRM:"Применить"},sq:{OK:"OK",CANCEL:"Anulo",CONFIRM:"Prano"},sv:{OK:"OK",CANCEL:"Avbryt",CONFIRM:"OK"},th:{OK:"ตกลง",CANCEL:"ยกเลิก",CONFIRM:"ยืนยัน"},tr:{OK:"Tamam",CANCEL:"İptal",CONFIRM:"Onayla"},zh_CN:{OK:"OK",CANCEL:"取消",CONFIRM:"确认"},zh_TW:{OK:"OK",CANCEL:"取消",CONFIRM:"確認"}};return p.addLocale=function(a,c){return b.each(["OK","CANCEL","CONFIRM"],function(a,b){if(!c[b])throw new Error("Please supply a translation for '"+b+"'")}),q[a]={OK:c.OK,CANCEL:c.CANCEL,CONFIRM:c.CONFIRM},p},p.removeLocale=function(a){return delete q[a],p},p.setLocale=function(a){return p.setDefaults("locale",a)},p.init=function(c){return a(c||b)},p}); \ No newline at end of file diff --git a/public/plugin/BuyCourses/src/buy_course_plugin.class.php b/public/plugin/BuyCourses/src/buy_course_plugin.class.php index d9660d77ee3..5124ddb8fca 100644 --- a/public/plugin/BuyCourses/src/buy_course_plugin.class.php +++ b/public/plugin/BuyCourses/src/buy_course_plugin.class.php @@ -1,4 +1,6 @@ Francis Gonzales and Yannick Warnier - BeezNest (integration)
Alex Aragón - BeezNest (Design icons and css styles)
@@ -84,7 +106,7 @@ public function __construct() Angel Fernando Quiroz Campos - BeezNest (cleanup and new reports)
José Loguercio Silva - BeezNest (Payouts and buy Services)
Julio Montoya - ", + ', [ 'show_main_menu_tab' => 'boolean', 'public_main_menu_tab' => 'boolean', @@ -96,17 +118,18 @@ public function __construct() '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() + public static function create(): self { static $result = null; @@ -117,20 +140,16 @@ public static function create() * Check if plugin is enabled. * * @param bool $checkEnabled Check if, additionnally to being installed, the plugin is enabled - * - * @return bool */ - public function isEnabled($checkEnabled = false) + public function isEnabled(bool $checkEnabled = false): bool { - return $this->get('paypal_enable') || $this->get('transfer_enable') || $this->get('culqi_enable'); + 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. - * - * @throws \Doctrine\DBAL\Exception */ - public function install() + public function install(): void { $tablesToBeCompared = [ self::TABLE_PAYPAL, @@ -146,6 +165,18 @@ public function install() 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(); @@ -153,18 +184,16 @@ public function install() $tables = $sm->tablesExist($tablesToBeCompared); if ($tables) { - return false; + return; } require_once api_get_path(SYS_PLUGIN_PATH).'BuyCourses/database.php'; - - return true; } /** * This method drops the plugin tables. */ - public function uninstall() + public function uninstall(): void { $tablesToBeDeleted = [ self::TABLE_PAYPAL, @@ -180,6 +209,17 @@ public function uninstall() 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) { @@ -190,7 +230,7 @@ public function uninstall() $this->manageTab(false); } - public function update() + public function update(): void { $table = self::TABLE_GLOBAL_CONFIG; $sql = "SHOW COLUMNS FROM $table WHERE Field = 'global_tax_perc'"; @@ -215,6 +255,15 @@ public function update() } } + $res = Database::query("SHOW COLUMNS FROM $table WHERE Field = 'info_email_extra'"); + + if (0 === Database::num_rows($res)) { + $res = Database::query("ALTER TABLE $table ADD (info_email_extra TEXT NOT NULL)"); + 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); @@ -256,6 +305,18 @@ public function update() } } + $res = Database::query("SHOW COLUMNS FROM $table WHERE Field = 'price_without_discount'"); + + if (0 === Database::num_rows($res)) { + $res = Database::query("ALTER TABLE $table ADD ( + price_without_discount decimal(10,2) NULL, + discount_amount decimal(10,2) NULL + )"); + 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); @@ -273,6 +334,18 @@ public function update() } } + $res = Database::query("SHOW COLUMNS FROM $table WHERE Field = 'price_without_discount'"); + + if (0 === Database::num_rows($res)) { + $res = Database::query("ALTER TABLE $table ADD ( + price_without_discount decimal(10,2) NULL, + discount_amount decimal(10,2) NULL + )"); + 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, @@ -286,6 +359,168 @@ public function update() )"; 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); + + $res = Database::query("SELECT * FROM $table"); + if (0 == Database::num_rows($res)) { + 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 (0 == Database::num_rows($res)) { + 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'), @@ -312,45 +547,40 @@ public function update() $fielddefault = ''; UserManager::create_extra_field($fieldlabel, $fieldtype, $fieldtitle, $fielddefault); - $table = self::TABLE_TRANSFER; - $sql = "ALTER TABLE $table CHANGE COLUMN name title varchar(255)"; - Database::query($sql); - $table = self::TABLE_SERVICES; - $sql = "ALTER TABLE $table CHANGE COLUMN name title varchar(255)"; - Database::query($sql); - 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 + * This function verifies if the plugin is enabled 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 $productId course or session id * @param int $productType course or session type - * - * @return mixed bool|string html - * @throws Exception */ - public function buyCoursesForGridCatalogValidator($productId, $productType) + public function buyCoursesForGridCatalogValidator(int $productId, int $productType): ?array { $return = []; $paypal = 'true' === $this->get('paypal_enable'); $transfer = 'true' === $this->get('transfer_enable'); + $stripe = 'true' === $this->get('stripe_enable'); + $culqi = 'true' === $this->get('culqi_enable'); + $cecabank = 'true' === $this->get('cecabank_enable'); + $tpv_redsys = 'true' === $this->get('tpv_redsys_enable'); $hideFree = 'true' === $this->get('hide_free_text'); - if ($paypal || $transfer) { + if ($paypal || $transfer || $stripe || $culqi || $cecabank || $tpv_redsys) { $item = $this->getItemByProduct($productId, $productType); $html = '
'; - if (!empty($item)) { + if ($item) { $html .= ' '.$item['total_price_formatted'].' '; $return['verificator'] = true; } else { - if (false == $hideFree) { + if (!$hideFree) { $html .= ' '.$this->get_lang('Free').' '; @@ -363,34 +593,30 @@ public function buyCoursesForGridCatalogValidator($productId, $productType) return $return; } - return false; + return null; } /** * Return the buyCourses plugin button to buy the course. - * - * @param int $productId - * @param int $productType - * - * @return string $html */ - public function returnBuyCourseButton($productId, $productType) + public function returnBuyCourseButton(int $productId, int $productType): string { - $productId = (int) $productId; - $productType = (int) $productType; - $url = api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/process.php?i='.$productId.'&t='.$productType; - $html = ''. - Display::getMdiIcon('cart').''; + $url = api_get_path(WEB_PLUGIN_PATH).'buycourses/src/process.php?i='.$productId.'&t='.$productType; + $buyButton = Display::returnFontAwesomeIcon('shopping-cart'); + if ('true' === $this->get('hide_shopping_cart_from_course_catalogue')) { + $buyButton = Display::returnFontAwesomeIcon('check').\PHP_EOL.get_lang('Subscribe'); + } - return $html; + return ''. + $buyButton.''; } /** * Get the currency for sales. * - * @return array The selected currency. Otherwise return false + * @return array The selected currency. Otherwise, return false */ - public function getSelectedCurrency() + public function getSelectedCurrency(): array { return Database::select( '*', @@ -404,10 +630,8 @@ public function getSelectedCurrency() /** * Get a list of currencies. - * - * @return array The currencies. Otherwise return false */ - public function getCurrencies() + public function getCurrencies(): array { return Database::select( '*', @@ -418,9 +642,9 @@ public function getCurrencies() /** * Save the selected currency. * - * @param int $selectedId The currency Id + * @param int $selectedId The currency ID */ - public function saveCurrency($selectedId) + public function saveCurrency(int $selectedId): void { $currencyTable = Database::get_main_table( self::TABLE_CURRENCY @@ -433,18 +657,16 @@ public function saveCurrency($selectedId) Database::update( $currencyTable, ['status' => 1], - ['id = ?' => (int) $selectedId] + ['id = ?' => $selectedId] ); } /** * Save the PayPal configuration params. * - * @param array $params - * - * @return int Rows affected. Otherwise return false + * @return int Rows affected. Otherwise, return false */ - public function savePaypalParams($params) + public function savePaypalParams(array $params): int { return Database::update( Database::get_main_table(self::TABLE_PAYPAL), @@ -474,24 +696,119 @@ public function getPaypalParams() } /** - * Save a transfer account information. + * Gets the stored TPV Redsys params. + */ + public function getTpvRedsysParams(): array + { + 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): int + { + 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): int + { + 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. + */ + public function getStripeParams(): array + { + return Database::select( + '*', + Database::get_main_table(self::TABLE_STRIPE), + ['id = ?' => 1], + 'first' + ); + } + + /** + * Save transfer account information. * * @param array $params The transfer account * - * @return int Rows affected. Otherwise return false + * @return int Rows affected. Otherwise, return false */ - public function saveTransferAccount($params) + public function saveTransferAccount(array $params): int { return Database::insert( Database::get_main_table(self::TABLE_TRANSFER), [ - 'title' => $params['tname'], + '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): int + { + 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. + */ + public function getTransferInfoExtra(): array + { + 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. * @@ -510,13 +827,13 @@ public function getTransferAccounts() * * @param int $id The transfer account ID * - * @return int Rows affected. Otherwise return false + * @return int Rows affected. Otherwise, return false */ - public function deleteTransferAccount($id) + public function deleteTransferAccount(int $id): int { return Database::delete( Database::get_main_table(self::TABLE_TRANSFER), - ['id = ?' => (int) $id] + ['id = ?' => $id] ); } @@ -524,16 +841,14 @@ public function deleteTransferAccount($id) * Get registered item data. * * @param int $itemId The item ID - * - * @return array */ - public function getItem($itemId) + public function getItem(int $itemId): array { return Database::select( '*', Database::get_main_table(self::TABLE_ITEM), [ - 'where' => ['id = ?' => (int) $itemId], + 'where' => ['id = ?' => $itemId], ], 'first' ); @@ -544,11 +859,8 @@ public function getItem($itemId) * * @param int $productId The item ID * @param int $itemType The item type - * - * @return array - * @throws Exception */ - public function getItemByProduct(int $productId, int $itemType): array + public function getItemByProduct(int $productId, int $itemType, ?array $coupon = null): ?array { $buyItemTable = Database::get_main_table(self::TABLE_ITEM); $buyCurrencyTable = Database::get_main_table(self::TABLE_CURRENCY); @@ -574,75 +886,183 @@ public function getItemByProduct(int $productId, int $itemType): array ); if (empty($product)) { - return []; + return null; } - $this->setPriceSettings($product, self::TAX_APPLIES_TO_ONLY_COURSE); + $this->setPriceSettings($product, self::TAX_APPLIES_TO_ONLY_COURSE, $coupon); return $product; } /** - * List courses details from the configuration page. + * Get registered item data. * - * @return array + * @param int $itemId The product ID + * @param int $productType The product type */ - public function getCourseList($first, $maxResults) + public function getSubscriptionItem(int $itemId, int $productType): array { - return $this->getCourses($first, $maxResults); + return Database::select( + '*', + Database::get_main_table(self::TABLE_SUBSCRIPTION), + [ + 'where' => ['product_id = ? AND product_type = ?' => [ + $itemId, + $productType, + ], + ], + ], + 'first' + ); } /** - * Lists current user session details, including each session course details. - * - * It can return the number of rows when $typeResult is 'count'. - * - * @param int $start - * @param int $end - * @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'. + * Get the item data. * - * @return array|int - * @throws Exception + * @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 */ - public function getCatalogSessionList($start, $end, $name = null, $min = 0, $max = 0, $typeResult = 'all') + public function getSubscriptionItemByProduct(int $productId, int $itemType, ?array $coupon = null): ?array { - $sessions = $this->filterSessionList($start, $end, $name, $min, $max, $typeResult); - - if ('count' === $typeResult) { - return $sessions; - } + $buySubscriptionItemTable = Database::get_main_table(self::TABLE_SUBSCRIPTION); + $buyCurrencyTable = Database::get_main_table(self::TABLE_CURRENCY); - $sessionCatalog = []; - /** @var Session $session */ - foreach ($sessions as $session) { - $sessionCourses = $session->getCourses(); + $fakeItemFrom = " + $buySubscriptionItemTable s + INNER JOIN $buyCurrencyTable c + ON s.currency_id = c.id + "; - if (empty($sessionCourses)) { - continue; - } + $item = Database::select( + ['s.*', 'c.iso_code'], + $fakeItemFrom, + [ + 'where' => [ + 's.product_id = ? AND s.product_type = ?' => [ + $productId, + $itemType, + ], + ], + ], + 'first' + ); - $item = $this->getItemByProduct( - $session->getId(), - self::PRODUCT_TYPE_SESSION - ); + if (empty($item)) { + return null; + } - if (empty($item)) { - continue; - } + $this->setPriceSettings($item, self::TAX_APPLIES_TO_ONLY_COURSE, $coupon); - $sessionData = $this->getSessionInfo($session->getId()); - $sessionData['coaches'] = $session->getGeneralCoaches()->map(fn(User $coach) => $coach->getFullName()); - $sessionData['enrolled'] = $this->getUserStatusForSession( - api_get_user_id(), - $session - ); - $sessionData['courses'] = []; + return $item; + } - foreach ($sessionCourses as $sessionCourse) { - $course = $sessionCourse->getCourse(); + /** + * Get the item data. + * + * @param int $productId The item ID + * @param int $itemType The item type + */ + public function getSubscriptionsItemsByProduct(int $productId, int $itemType): ?array + { + $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 null; + } + + return $items; + } + + /** + * Get registered item data by duration. + * + * @param int $duration The subscription duration + */ + public function getSubscriptionsItemsByDuration(int $duration): array + { + return Database::select( + '*', + Database::get_main_table(self::TABLE_SUBSCRIPTION), + [ + 'where' => [ + 'duration = ?' => [$duration], + ], + ] + ); + } + + /** + * Lists current user session details, including each session course detail. + * + * It can return the number of rows when $typeResult is 'count'. + * + * @param string|null $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'. + * @param int $sessionCategory + */ + public function getCatalogSessionList(int $start, int $end, ?string $name = null, int $min = 0, int $max = 0, string $typeResult = 'all', $sessionCategory = 0): array|int + { + $sessions = $this->filterSessionList($start, $end, $name, $min, $max, $typeResult, $sessionCategory); + + if ('count' === $typeResult) { + 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['coaches'] = $session->getGeneralCoaches()->map(fn (User $coach) => $coach->getFullName()); + $sessionData['enrolled'] = $this->getUserStatusForSession( + api_get_user_id(), + $session + ); + $sessionData['courses'] = []; + + foreach ($sessionCourses as $sessionCourse) { + $course = $sessionCourse->getCourse(); $sessionCourseData = [ 'title' => $course->getTitle(), @@ -651,7 +1071,7 @@ public function getCatalogSessionList($start, $end, $name = null, $min = 0, $max $userCourseSubscriptions = $session->getSessionRelCourseRelUsersByStatus( $course, - Chamilo\CoreBundle\Entity\Session::COURSE_COACH + Session::COURSE_COACH ); foreach ($userCourseSubscriptions as $userCourseSubscription) { @@ -671,11 +1091,10 @@ public function getCatalogSessionList($start, $end, $name = null, $min = 0, $max * 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 - * @throws Exception + * @param int $min Optional. The minimum price filter + * @param int $max Optional. The maximum price filter */ - public function getCatalogCourseList($first, $pageSize, $name = null, $min = 0, $max = 0, $typeResult = 'all'): array|int + public function getCatalogCourseList(int $first, int $pageSize, ?string $name = null, int $min = 0, int $max = 0, string $typeResult = 'all'): array|int { $courses = $this->filterCourseList($first, $pageSize, $name, $min, $max, $typeResult); @@ -688,7 +1107,6 @@ public function getCatalogCourseList($first, $pageSize, $name = null, $min = 0, } $courseCatalog = []; - /* @var Course $course */ foreach ($courses as $course) { $item = $this->getItemByProduct( $course->getId(), @@ -710,7 +1128,7 @@ public function getCatalogCourseList($first, $pageSize, $name = null, $min = 0, ]; foreach ($course->getTeachersSubscriptions() as $courseUser) { - /* @var CourseRelUser $courseUser */ + /** @var CourseRelUser $courseUser */ $teacher = $courseUser->getUser(); $courseItem['teachers'][] = $teacher->getFullName(); } @@ -743,22 +1161,141 @@ private function getCourseIllustrationUrl(Course $course): ?string } /** - * @param $price - * @param $isoCode + * Lists current user subscription session details, including each session course details. * - * @return string + * 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 ('count' === $typeResult) { + 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['coaches'] = $session->getGeneralCoaches()->map(fn (User $coach) => $coach->getFullName()); + $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->getSessionRelCourseRelUsersByStatus( + $course, + Session::COURSE_COACH + ); + + foreach ($userCourseSubscriptions as $userCourseSubscription) { + $user = $userCourseSubscription->getUser(); + $sessionCourseData['coaches'][] = $user->getFullName(); + } + $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 getPriceWithCurrencyFromIsoCode($price, $isoCode) + public function getCatalogSubscriptionCourseList(int $first, int $pageSize, ?string $name = null, string $typeResult = 'all') + { + $courses = $this->filterSubscriptionCourseList($first, $pageSize, $name, $typeResult); + + if ('count' === $typeResult) { + 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->getTeachersSubscriptions() as $courseUser) { + $teacher = $courseUser->getUser(); + $courseItem['teachers'][] = $teacher->getFullName(); + } + + // Check images + $imgUrl = $this->getCourseIllustrationUrl($course); + if (!empty($imgUrl)) { + $courseItem['course_img'] = $imgUrl; + } + $courseCatalog[] = $courseItem; + } + + return $courseCatalog; + } + + public function getPriceWithCurrencyFromIsoCode(float $price, string $isoCode): string { $useSymbol = 'true' === $this->get('use_currency_symbol'); $result = $isoCode.' '.$price; if ($useSymbol) { - if ('BRL' === $isoCode) { - $symbol = 'R$'; - } else { - $symbol = Currencies::getSymbol($isoCode); - } + $symbol = 'BRL' === $isoCode ? 'R$' : Currencies::getSymbol($isoCode); $result = $symbol.' '.$price; } @@ -768,12 +1305,10 @@ public function getPriceWithCurrencyFromIsoCode($price, $isoCode) /** * Get course info. * - * @param int $courseId The course ID - * * @return array - * @throws Exception + * @throws \Doctrine\ORM\NonUniqueResultException */ - public function getCourseInfo($courseId) + public function getCourseInfo(int $courseId, ?array $coupon = null) { $entityManager = Database::getManager(); $course = $entityManager->find(Course::class, $courseId); @@ -784,18 +1319,28 @@ public function getCourseInfo($courseId) $item = $this->getItemByProduct( $course->getId(), - self::PRODUCT_TYPE_COURSE + self::PRODUCT_TYPE_COURSE, + $coupon ); if (empty($item)) { return []; } + /** @var CCourseDescription $courseDescription */ + $courseDescription = Container::getCourseDescriptionRepository() + ->getResourcesByCourse($course) + ->addOrderBy('descriptionType', 'ASC') + ->setMaxResults(1) + ->getQuery() + ->getOneOrNullResult() + ; + $globalParameters = $this->getGlobalParameters(); $courseInfo = [ 'id' => $course->getId(), 'title' => $course->getTitle(), - 'description' => $course->getDescription(), + 'description' => $courseDescription?->getContent(), 'code' => $course->getCode(), 'visual_code' => $course->getVisualCode(), 'teachers' => [], @@ -824,12 +1369,9 @@ public function getCourseInfo($courseId) /** * Get session info. * - * @param int $sessionId The session ID - * * @return array - * @throws Exception */ - public function getSessionInfo(int $sessionId): array + public function getSessionInfo(int $sessionId, ?array $coupon = null) { $entityManager = Database::getManager(); $session = $entityManager->find(Session::class, $sessionId); @@ -838,7 +1380,11 @@ public function getSessionInfo(int $sessionId): array return []; } - $item = $this->getItemByProduct($session->getId(), self::PRODUCT_TYPE_SESSION); + $item = $this->getItemByProduct( + $session->getId(), + self::PRODUCT_TYPE_SESSION, + $coupon + ); if (empty($item)) { return []; @@ -849,7 +1395,7 @@ public function getSessionInfo(int $sessionId): array $globalParameters = $this->getGlobalParameters(); $sessionInfo = [ 'id' => $session->getId(), - 'name' => $session->getTitle(), + 'name' => $session->getName(), 'description' => $session->getDescription(), 'dates' => $sessionDates, 'courses' => [], @@ -859,6 +1405,7 @@ public function getSessionInfo(int $sessionId): array 'nbrCourses' => $session->getNbrCourses(), 'nbrUsers' => $session->getNbrUsers(), 'item' => $item, + 'duration' => $session->getDuration(), ]; $imgUrl = $this->getSessionIllustrationUrl($session); @@ -876,12 +1423,12 @@ public function getSessionInfo(int $sessionId): array $userCourseSubscriptions = $session->getSessionRelCourseRelUsersByStatus( $course, - Chamilo\CoreBundle\Entity\Session::COURSE_COACH + Session::COURSE_COACH ); foreach ($userCourseSubscriptions as $userCourseSubscription) { $user = $userCourseSubscription->getUser(); - $coaches['id'] = $user->getUserId(); + $coaches['id'] = $user->getId(); $coaches['name'] = $user->getFullName(); $sessionCourseData['coaches'][] = $coaches; } @@ -910,69 +1457,232 @@ private function getSessionIllustrationUrl(Session $session): ?string } /** - * Register a sale. - * - * @param int $itemId The product ID - * @param int $paymentType The payment type + * Get course info. * - * @return bool + * @throws \Doctrine\ORM\NonUniqueResultException */ - public function registerSale($itemId, $paymentType) + public function getSubscriptionCourseInfo(int $courseId, ?array $coupon = null): array { - if (!in_array( - $paymentType, - [self::PAYMENT_TYPE_PAYPAL, self::PAYMENT_TYPE_TRANSFER, self::PAYMENT_TYPE_CULQI] - ) - ) { - return false; + $entityManager = Database::getManager(); + $course = $entityManager->find(Course::class, $courseId); + + if (empty($course)) { + return []; } - $entityManager = Database::getManager(); - $item = $this->getItem($itemId); + $item = $this->getSubscriptionItemByProduct( + $course->getId(), + self::PRODUCT_TYPE_COURSE, + $coupon + ); if (empty($item)) { - return false; + return []; } - $productName = ''; - if (self::PRODUCT_TYPE_COURSE == $item['product_type']) { - $course = $entityManager->find(Course::class, $item['product_id']); - - if (empty($course)) { - return false; - } + /** @var CCourseDescription $courseDescription */ + $courseDescription = Container::getCourseDescriptionRepository() + ->getResourcesByCourse($course) + ->addOrderBy('descriptionType', 'ASC') + ->setMaxResults(1) + ->getQuery() + ->getOneOrNullResult() + ; - $productName = $course->getTitle(); - } elseif (self::PRODUCT_TYPE_SESSION == $item['product_type']) { - $session = $entityManager->find(Session::class, $item['product_id']); + $globalParameters = $this->getGlobalParameters(); + $courseInfo = [ + 'id' => $course->getId(), + 'title' => $course->getTitle(), + 'description' => $courseDescription?->getContent(), + '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, + ]; - if (empty($session)) { - return false; - } + $courseTeachers = $course->getTeachersSubscriptions(); - $productName = $session->getTitle(); + foreach ($courseTeachers as $teachers) { + $user = $teachers->getUser(); + $teacher['id'] = $user->getId(); + $teacher['name'] = $user->getFullName(); + $courseInfo['teachers'][] = $teacher; } - $price = $item['price']; - $priceWithoutTax = null; - $taxPerc = null; - $taxAmount = 0; - $taxEnable = 'true' === $this->get('tax_enable'); - $globalParameters = $this->getGlobalParameters(); - $taxAppliesTo = $globalParameters['tax_applies_to']; + $imgUrl = $this->getCourseIllustrationUrl($course); - if ($taxEnable && - ( - self::TAX_APPLIES_TO_ALL == $taxAppliesTo || - (self::TAX_APPLIES_TO_ONLY_COURSE == $taxAppliesTo && self::PRODUCT_TYPE_COURSE == $item['product_type']) || - (self::TAX_APPLIES_TO_ONLY_SESSION == $taxAppliesTo && self::PRODUCT_TYPE_SESSION == $item['product_type']) + if (!empty($imgUrl)) { + $courseInfo['course_img'] = $imgUrl; + } + + 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(Session::class, $sessionId); + + if (empty($session)) { + return []; + } + + $item = $this->getSubscriptionItemByProduct( + $session->getId(), + self::PRODUCT_TYPE_SESSION, + $coupon + ); + + if (empty($item)) { + return []; + } + + $sessionDates = SessionManager::parseSessionDates($session); + + $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(), + ]; + + $imgUrl = $this->getSessionIllustrationUrl($session); + if (!empty($imgUrl)) { + $sessionInfo['image'] = $imgUrl; + } + + $sessionCourses = $session->getCourses(); + foreach ($sessionCourses as $sessionCourse) { + $course = $sessionCourse->getCourse(); + $sessionCourseData = [ + 'title' => $course->getTitle(), + 'coaches' => [], + ]; + + $userCourseSubscriptions = $session->getSessionRelCourseRelUsersByStatus( + $course, + Session::COURSE_COACH + ); + + foreach ($userCourseSubscriptions as $userCourseSubscription) { + $user = $userCourseSubscription->getUser(); + $coaches['id'] = $user->getId(); + $coaches['name'] = $user->getFullName(); + $sessionCourseData['coaches'][] = $coaches; + } + + $sessionInfo['courses'][] = $sessionCourseData; + } + + return $sessionInfo; + } + + /** + * Register a sale. + * + * @param int $itemId The product ID + * @param int $paymentType The payment type + * @param int $couponId The coupon ID + */ + public function registerSale(int $itemId, int $paymentType, ?int $couponId = null): ?int + { + 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 null; + } + + $entityManager = Database::getManager(); + $item = $this->getItem($itemId); + + if (empty($item)) { + return null; + } + + $productName = ''; + if (self::PRODUCT_TYPE_COURSE == $item['product_type']) { + $course = $entityManager->find(Course::class, $item['product_id']); + + if (empty($course)) { + return null; + } + + $productName = $course->getTitle(); + } elseif (self::PRODUCT_TYPE_SESSION == $item['product_type']) { + $session = $entityManager->find(Session::class, $item['product_id']); + + if (empty($session)) { + return null; + } + + $productName = $session->getName(); + } + + $coupon = null; + + if (null != $couponId) { + $coupon = $this->getCoupon($couponId, $item['product_type'], $item['product_id']); + } + + $couponDiscount = 0; + $priceWithoutDiscount = 0; + if (null != $coupon) { + if (self::COUPON_DISCOUNT_TYPE_AMOUNT == $coupon['discount_type']) { + $couponDiscount = $coupon['discount_amount']; + } elseif (self::COUPON_DISCOUNT_TYPE_PERCENTAGE == $coupon['discount_type']) { + $couponDiscount = ($item['price'] * $coupon['discount_amount']) / 100; + } + $priceWithoutDiscount = $item['price']; + } + $item['price'] -= $couponDiscount; + $price = $item['price']; + $priceWithoutTax = null; + $taxPerc = null; + $taxAmount = 0; + $taxEnable = 'true' === $this->get('tax_enable'); + $globalParameters = $this->getGlobalParameters(); + $taxAppliesTo = $globalParameters['tax_applies_to']; + + if ($taxEnable + && ( + self::TAX_APPLIES_TO_ALL == $taxAppliesTo + || (self::TAX_APPLIES_TO_ONLY_COURSE == $taxAppliesTo && self::PRODUCT_TYPE_COURSE == $item['product_type']) + || (self::TAX_APPLIES_TO_ONLY_SESSION == $taxAppliesTo && self::PRODUCT_TYPE_SESSION == $item['product_type']) ) ) { $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, PHP_ROUND_HALF_UP); + $taxPerc = null === $item['tax_perc'] ? $globalTaxPerc : $item['tax_perc']; + $taxAmount = round($priceWithoutTax * $taxPerc / 100, $precision); $price = $priceWithoutTax + $taxAmount; } @@ -993,21 +1703,36 @@ public function registerSale($itemId, $paymentType) 'tax_perc' => $taxPerc, 'tax_amount' => $taxAmount, 'status' => self::SALE_STATUS_PENDING, - 'payment_type' => (int) $paymentType, - 'invoice' => 0, //default value if no invoice + 'payment_type' => $paymentType, + 'price_without_discount' => $priceWithoutDiscount, + 'discount_amount' => $couponDiscount, ]; return Database::insert(self::TABLE_SALE, $values); } /** - * Get sale data by ID. + * Update the sale reference. * - * @param int $saleId The sale ID + * @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($saleId) + public function getSale(int $saleId) { return Database::select( '*', @@ -1019,14 +1744,31 @@ public function getSale($saleId) ); } + /** + * 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 + * @return array The sale list. Otherwise, return false */ - public function getSaleListByPaymentType($paymentType = self::PAYMENT_TYPE_PAYPAL) + 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); @@ -1043,7 +1785,7 @@ public function getSaleListByPaymentType($paymentType = self::PAYMENT_TYPE_PAYPA [ 'where' => [ 's.payment_type = ? AND s.status = ?' => [ - (int) $paymentType, + $paymentType, self::SALE_STATUS_COMPLETED, ], ], @@ -1060,7 +1802,7 @@ public function getSaleListByPaymentType($paymentType = self::PAYMENT_TYPE_PAYPA * * @return array The sale data */ - public function getDataSaleInvoice($saleId, $isService) + public function getDataSaleInvoice(int $saleId, int $isService) { if ($isService) { $sale = $this->getServiceSale($saleId); @@ -1084,7 +1826,7 @@ public function getDataSaleInvoice($saleId, $isService) * * @return array The invoice data */ - public function getDataInvoice($saleId, $isService) + public function getDataInvoice(int $saleId, int $isService) { return Database::select( '*', @@ -1107,7 +1849,7 @@ public function getDataInvoice($saleId, $isService) * * @return string */ - public function getNumInvoice($saleId, $isService) + public function getNumInvoice(int $saleId, int $isService) { $dataInvoice = $this->getDataInvoice($saleId, $isService); if (empty($dataInvoice)) { @@ -1124,13 +1866,13 @@ public function getNumInvoice($saleId, $isService) * * @return array */ - public function getCurrency($currencyId) + public function getCurrency(int $currencyId) { return Database::select( '*', Database::get_main_table(self::TABLE_CURRENCY), [ - 'where' => ['id = ?' => (int) $currencyId], + 'where' => ['id = ?' => $currencyId], ], 'first' ); @@ -1139,11 +1881,9 @@ public function getCurrency($currencyId) /** * Complete sale process. Update sale status to completed. * - * @param int $saleId The sale ID - * * @return bool */ - public function completeSale($saleId) + public function completeSale(int $saleId) { $sale = $this->getSale($saleId); @@ -1152,10 +1892,14 @@ public function completeSale($saleId) } $saleIsCompleted = false; + switch ($sale['product_type']) { case self::PRODUCT_TYPE_COURSE: - $saleIsCompleted = CourseManager::subscribeUser($sale['user_id'], $sale['product_id']); + $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'], @@ -1165,6 +1909,7 @@ public function completeSale($saleId) ); $saleIsCompleted = true; + break; } @@ -1183,23 +1928,56 @@ public function completeSale($saleId) * * @param int $saleId The sale ID */ - public function cancelSale($saleId) + public function cancelSale(int $saleId): void { $this->updateSaleStatus($saleId, self::SALE_STATUS_CANCELED); } /** * Get payment types. - * - * @return array */ - public function getPaymentTypes() + public function getPaymentTypes(bool $onlyActive = false): array { - return [ + $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 ('true' !== $this->get('paypal_enable')) { + unset($types[self::PAYMENT_TYPE_PAYPAL]); + } + + if ('true' !== $this->get('transfer_enable')) { + unset($types[self::PAYMENT_TYPE_TRANSFER]); + } + + if ('true' !== $this->get('culqi_enable')) { + unset($types[self::PAYMENT_TYPE_CULQI]); + } + + if ('true' !== $this->get('tpv_redsys_enable') + || !file_exists(api_get_path(SYS_PLUGIN_PATH).'buycourses/resources/apiRedsys.php') + ) { + unset($types[self::PAYMENT_TYPE_TPV_REDSYS]); + } + + if ('true' !== $this->get('stripe_enable')) { + unset($types[self::PAYMENT_TYPE_STRIPE]); + } + + if ('true' !== $this->get('cecabank_enable')) { + unset($types[self::PAYMENT_TYPE_TPV_CECABANK]); + } + + return $types; } /** @@ -1208,7 +1986,7 @@ public function getPaymentTypes() * @param int $saleId The sale ID * @param int $isService The service type to filter (default : 0) */ - public function setInvoice($saleId, $isService = 0) + public function setInvoice(int $saleId, int $isService = 0): void { $invoiceTable = Database::get_main_table(self::TABLE_INVOICE); $year = date('Y'); @@ -1284,9 +2062,9 @@ public function getTaxAppliesTo() * * @param int $status The status to filter * - * @return array The sale list. Otherwise return false + * @return array The sale list. Otherwise, return false */ - public function getSaleListByStatus($status = self::SALE_STATUS_PENDING) + 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); @@ -1301,7 +2079,7 @@ public function getSaleListByStatus($status = self::SALE_STATUS_PENDING) ['c.iso_code', 'u.firstname', 'u.lastname', 'u.email', 's.*'], "$saleTable s $innerJoins", [ - 'where' => ['s.status = ?' => (int) $status], + 'where' => ['s.status = ?' => $status], 'order' => 'id DESC', ] ); @@ -1310,14 +2088,11 @@ public function getSaleListByStatus($status = self::SALE_STATUS_PENDING) /** * Get the list statuses for sales. * - * @param string $dateStart - * @param string $dateEnd + * @return array * * @throws Exception - * - * @return array */ - public function getSaleListReport($dateStart = null, $dateEnd = null) + 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); @@ -1340,15 +2115,21 @@ public function getSaleListReport($dateStart = null, $dateEnd = null) $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']); @@ -1376,10 +2157,10 @@ public function getSaleListReport($dateStart = null, $dateEnd = null) $this->get_lang('SalePrice'), $this->get_lang('ProductType'), $this->get_lang('ProductName'), - get_lang('Username'), - get_lang('E-mail'), + $this->get_lang('UserName'), + get_lang('Email'), ]; - //Validation Export + // Validation Export $dateStart = strtotime($dateStart); $dateEnd = strtotime($dateEnd); foreach ($listExportTemp as $item) { @@ -1459,22 +2240,46 @@ public function getServiceTypes() ]; } + /** + * 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 + * @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 - * - * @return string */ public static function randomText( - $length = 6, - $lowercase = true, - $uppercase = true, - $numbers = true - ) { + 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') : ''; @@ -1485,7 +2290,7 @@ public static function randomText( $str = ''; - srand((float) microtime() * 1000000); + srand(microtime() * 1000000); for ($i = 0; $i < $length; $i++) { $numbers = rand(0, strlen($salt) - 1); @@ -1501,10 +2306,8 @@ public static function randomText( * @param int $userId The user ID * @param int $productType The course/session type * @param int $productId The course/session ID - * - * @return string */ - public function generateReference($userId, $productType, $productId) + public function generateReference(int $userId, int $productType, int $productId): string { return vsprintf( '%d-%d-%d-%s', @@ -1517,9 +2320,9 @@ public function generateReference($userId, $productType, $productId) * * @param string $term The search term * - * @return array The sale list. Otherwise return false + * @return array The sale list. Otherwise, return false */ - public function getSaleListByUser($term) + public function getSaleListByUser(string $term) { $term = trim($term); @@ -1554,9 +2357,9 @@ public function getSaleListByUser($term) * * @param int $id The user id * - * @return array The sale list. Otherwise return false + * @return array The sale list. Otherwise, return false */ - public function getSaleListByUserId($id) + public function getSaleListByUserId(int $id) { if (empty($id)) { return []; @@ -1586,12 +2389,9 @@ public function getSaleListByUserId($id) /** * Get a list of sales by date range. * - * @param string $dateStart - * @param string $dateEnd - * - * @return array The sale list. Otherwise return false + * @return array The sale list. Otherwise, return false */ - public function getSaleListByDate($dateStart, $dateEnd) + public function getSaleListByDate(string $dateStart, string $dateEnd) { $dateStart = trim($dateStart); $dateEnd = trim($dateEnd); @@ -1627,9 +2427,9 @@ public function getSaleListByDate($dateStart, $dateEnd) * * @param string $term The search term * - * @return array The sale list. Otherwise return false + * @return array The sale list. Otherwise, return false */ - public function getSaleListByEmail($term) + public function getSaleListByEmail(string $term) { $term = trim($term); if (empty($term)) { @@ -1661,9 +2461,8 @@ public function getSaleListByEmail($term) * @param array $defaultCurrency Optional. Currency data * * @return array - * @throws Exception */ - public function getCourseForConfiguration(Course $course, $defaultCurrency = null) + public function getCourseForConfiguration(Course $course, ?array $defaultCurrency = null) { $courseItem = [ 'item_id' => null, @@ -1681,7 +2480,7 @@ public function getCourseForConfiguration(Course $course, $defaultCurrency = nul $item = $this->getItemByProduct($course->getId(), self::PRODUCT_TYPE_COURSE); - if (!empty($item)) { + if (false !== $item) { $courseItem['item_id'] = $item['id']; $courseItem['visible'] = true; $courseItem['currency'] = $item['iso_code']; @@ -1700,7 +2499,7 @@ public function getCourseForConfiguration(Course $course, $defaultCurrency = nul * * @return array */ - public function getSessionForConfiguration(Session $session, $defaultCurrency = null) + public function getSessionForConfiguration(Session $session, ?array $defaultCurrency = null) { $buyItemTable = Database::get_main_table(self::TABLE_ITEM); $buyCurrencyTable = Database::get_main_table(self::TABLE_CURRENCY); @@ -1713,7 +2512,7 @@ public function getSessionForConfiguration(Session $session, $defaultCurrency = $sessionItem = [ 'item_id' => null, 'session_id' => $session->getId(), - 'session_name' => $session->getTitle(), + 'session_name' => $session->getName(), 'session_visibility' => $session->getVisibility(), 'session_display_start_date' => null, 'session_display_end_date' => null, @@ -1768,9 +2567,9 @@ public function getSessionForConfiguration(Session $session, $defaultCurrency = * * @param int $itemId The item ID * - * @return array The beneficiaries. Otherwise return false + * @return array The beneficiaries. Otherwise, return false */ - public function getItemBeneficiaries($itemId) + public function getItemBeneficiaries(int $itemId) { $beneficiaryTable = Database::get_main_table(self::TABLE_ITEM_BENEFICIARY); @@ -1779,7 +2578,7 @@ public function getItemBeneficiaries($itemId) $beneficiaryTable, [ 'where' => [ - 'item_id = ?' => (int) $itemId, + 'item_id = ?' => $itemId, ], ] ); @@ -1790,14 +2589,14 @@ public function getItemBeneficiaries($itemId) * * @param int $itemId The item ID * - * @return int The number of affected rows. Otherwise return false + * @return int The number of affected rows. Otherwise, return false */ - public function deleteItem($itemId) + public function deleteItem(int $itemId) { $itemTable = Database::get_main_table(self::TABLE_ITEM); $affectedRows = Database::delete( $itemTable, - ['id = ?' => (int) $itemId] + ['id = ?' => $itemId] ); if (!$affectedRows) { @@ -1812,7 +2611,7 @@ public function deleteItem($itemId) * * @param array $itemData The item data * - * @return int The item ID. Otherwise return false + * @return int The item ID. Otherwise, return false */ public function registerItem(array $itemData) { @@ -1828,9 +2627,9 @@ public function registerItem(array $itemData) * @param int $productId The product ID * @param int $productType The type of product * - * @return int The number of affected rows. Otherwise return false + * @return int The number of affected rows. Otherwise, return false */ - public function updateItem(array $itemData, $productId, $productType) + public function updateItem(array $itemData, int $productId, int $productType) { $itemTable = Database::get_main_table(self::TABLE_ITEM); @@ -1838,7 +2637,7 @@ public function updateItem(array $itemData, $productId, $productType) $itemTable, $itemData, [ - 'product_id = ? AND ' => (int) $productId, + 'product_id = ? AND ' => $productId, 'product_type' => $productType, ] ); @@ -1849,15 +2648,15 @@ public function updateItem(array $itemData, $productId, $productType) * * @param int $itemId The user ID * - * @return int The number of affected rows. Otherwise return false + * @return int The number of affected rows. Otherwise, return false */ - public function deleteItemBeneficiaries($itemId) + public function deleteItemBeneficiaries(int $itemId) { $beneficiaryTable = Database::get_main_table(self::TABLE_ITEM_BENEFICIARY); return Database::delete( $beneficiaryTable, - ['item_id = ?' => (int) $itemId] + ['item_id = ?' => $itemId] ); } @@ -1867,7 +2666,7 @@ public function deleteItemBeneficiaries($itemId) * @param int $itemId The item ID * @param array $userIds The beneficiary user ID and Teachers commissions if enabled */ - public function registerItemBeneficiaries($itemId, array $userIds) + public function registerItemBeneficiaries(int $itemId, array $userIds): void { $beneficiaryTable = Database::get_main_table(self::TABLE_ITEM_BENEFICIARY); @@ -1877,7 +2676,7 @@ public function registerItemBeneficiaries($itemId, array $userIds) Database::insert( $beneficiaryTable, [ - 'item_id' => (int) $itemId, + 'item_id' => $itemId, 'user_id' => (int) $userId, 'commissions' => (int) $commissions, ] @@ -1911,17 +2710,13 @@ public function isValidCourse(Course $course) * @param int $saleId The sale ID * * @return array - * @throws Exception */ - public function getBeneficiariesBySale(int $saleId): array + public function getBeneficiariesBySale(int $saleId) { $sale = $this->getSale($saleId); $item = $this->getItemByProduct($sale['product_id'], $sale['product_type']); - if (!empty($item)) { - return $this->getItemBeneficiaries($item['id']); - } - return []; + return $this->getItemBeneficiaries($item['id']); } /** @@ -1929,7 +2724,6 @@ public function getBeneficiariesBySale(int $saleId): array * * @param int $status - default 0 - pending * @param int $payoutId - for get an individual payout if want all then false - * @param int $userId * * @return array */ @@ -1937,10 +2731,9 @@ public function getPayouts( int $status = self::PAYOUT_STATUS_PENDING, int $payoutId = 0, int $userId = 0 - ): array - { - $condition = ($payoutId > 0) ? 'AND p.id = '.$payoutId : ''; - $condition2 = ($userId > 0) ? ' AND p.user_id = '.$userId : ''; + ) { + $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); @@ -1950,7 +2743,7 @@ public function getPayouts( $extraFieldValues = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES); $paypalExtraField = Database::select( - "*", + '*', $extraFieldTable, [ 'where' => ['variable = ?' => 'paypal'], @@ -1967,29 +2760,25 @@ public function getPayouts( 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'])." - "; + AND field_id = ".((int) $paypalExtraField['id']).' + '; - $payouts = Database::select( - "p.* , u.firstname, u.lastname, efv.field_value as paypal_account, s.reference as sale_reference, s.price as item_price, c.iso_code", + return 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. * - * @param int $userId - * * @return true if the user have a paypal account, false if not */ - public function verifyPaypalAccountByBeneficiary($userId) + public function verifyPaypalAccountByBeneficiary(int $userId) { $extraFieldTable = Database::get_main_table(TABLE_EXTRA_FIELD); $extraFieldValues = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES); @@ -2009,10 +2798,10 @@ public function verifyPaypalAccountByBeneficiary($userId) $paypalFieldId = $paypalExtraField['id']; $paypalAccount = Database::select( - 'field_value', + 'value', $extraFieldValues, [ - 'where' => ['field_id = ? AND item_id = ?' => [(int) $paypalFieldId, (int) $userId]], + 'where' => ['field_id = ? AND item_id = ?' => [(int) $paypalFieldId, $userId]], ], 'first' ); @@ -2021,7 +2810,7 @@ public function verifyPaypalAccountByBeneficiary($userId) return false; } - if ('' === $paypalAccount['field_value']) { + if ('' === $paypalAccount['value']) { return false; } @@ -2031,11 +2820,9 @@ public function verifyPaypalAccountByBeneficiary($userId) /** * Register the users payouts. * - * @param int $saleId The sale ID - * * @return array */ - public function storePayouts($saleId) + public function storePayouts(int $saleId) { $payoutsTable = Database::get_main_table(self::TABLE_PAYPAL_PAYOUTS); $platformCommission = $this->getPlatformCommission(); @@ -2043,7 +2830,7 @@ public function storePayouts($saleId) $sale = $this->getSale($saleId); $commission = (int) $platformCommission['commission']; $teachersCommission = number_format( - (floatval($sale['price']) * $commission) / 100, + ((float) $sale['price'] * $commission) / 100, 2 ); @@ -2054,11 +2841,11 @@ public function storePayouts($saleId) $payoutsTable, [ 'date' => $sale['date'], - 'payout_date' => getdate(), - 'sale_id' => (int) $saleId, + 'payout_date' => api_get_utc_datetime(), + 'sale_id' => $saleId, 'user_id' => $beneficiary['user_id'], 'commission' => number_format( - (floatval($teachersCommission) * $beneficiaryCommission) / 100, + ((float) $teachersCommission * $beneficiaryCommission) / 100, 2 ), 'status' => self::PAYOUT_STATUS_PENDING, @@ -2070,27 +2857,66 @@ public function storePayouts($saleId) /** * 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) + * @param int $saleId The subscription sale ID * * @return array */ - public function setStatusPayouts($payoutId, $status) + public function storeSubscriptionPayouts(int $saleId) { $payoutsTable = Database::get_main_table(self::TABLE_PAYPAL_PAYOUTS); + $platformCommission = $this->getPlatformCommission(); - Database::update( - $payoutsTable, - ['status' => (int) $status], - ['id = ?' => (int) $payoutId] + $sale = $this->getSubscriptionSale($saleId); + $commission = (int) $platformCommission['commission']; + $teachersCommission = number_format( + ((float) $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( + ((float) $teachersCommission * $beneficiaryCommission) / 100, + 2 + ), + 'status' => self::PAYOUT_STATUS_PENDING, + ] + ); + } } /** - * Gets the stored platform commission params. + * Register the users payouts. * - * @return array - */ + * @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( @@ -2104,11 +2930,11 @@ public function getPlatformCommission() /** * Update the platform commission. * - * @param int $params platform commission + * @param array $params platform commission * - * @return int The number of affected rows. Otherwise return false + * @return int The number of affected rows. Otherwise, return false */ - public function updateCommission($params) + public function updateCommission(array $params) { $commissionTable = Database::get_main_table(self::TABLE_COMMISSION); @@ -2125,14 +2951,14 @@ public function updateCommission($params) * * @return mixed response */ - public function storeService($service) + public function storeService(array $service) { $servicesTable = Database::get_main_table(self::TABLE_SERVICES); $return = Database::insert( $servicesTable, [ - 'title' => Security::remove_XSS($service['name']), + '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, @@ -2146,19 +2972,19 @@ public function storeService($service) ] ); - if ($return && !empty($service['picture_crop_image_base_64']) && - !empty($service['picture_crop_result']) + 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 = 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 = ?' => (int) $return] + ['id = ?' => $return] ); } @@ -2168,26 +2994,23 @@ public function storeService($service) /** * update a service. * - * @param array $service - * @param int $id - * * @return mixed response */ - public function updateService($service, $id) + 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 = api_get_path(SYS_PLUGIN_PATH).'buycourses/uploads/services/images/simg-'.$id.'.png'; file_put_contents($file, $data); } return Database::update( $servicesTable, [ - 'title' => Security::remove_XSS($service['name']), + '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, @@ -2199,7 +3022,7 @@ public function updateService($service, $id) 'video_url' => $service['video_url'], 'service_information' => $service['service_information'], ], - ['id = ?' => (int) $id] + ['id = ?' => $id] ); } @@ -2208,49 +3031,58 @@ public function updateService($service, $id) * * @param int $id The transfer account ID * - * @return int Rows affected. Otherwise return false + * @return int Rows affected. Otherwise, return false */ - public function deleteService($id) + public function deleteService(int $id) { Database::delete( Database::get_main_table(self::TABLE_SERVICES_SALE), - ['service_id = ?' => (int) $id] + ['service_id = ?' => $id] ); return Database::delete( Database::get_main_table(self::TABLE_SERVICES), - ['id = ?' => (int) $id] + ['id = ?' => $id] ); } /** - * @param array $product - * @param int $productType - * - * @return bool + * @param array|null $coupon Array with at least 'discount_type' and 'discount_amount' elements */ - public function setPriceSettings(&$product, $productType) + public function setPriceSettings(array &$product, int $productType, ?array $coupon = null): bool { if (empty($product)) { return false; } $taxPerc = null; + $product['has_coupon'] = null != $coupon ? true : false; + $couponDiscount = 0; + if (null != $coupon) { + if (self::COUPON_DISCOUNT_TYPE_AMOUNT == $coupon['discount_type']) { + $couponDiscount = $coupon['discount_amount']; + } elseif (self::COUPON_DISCOUNT_TYPE_PERCENTAGE == $coupon['discount_type']) { + $couponDiscount = ($product['price'] * $coupon['discount_amount']) / 100; + } + $product['price_without_discount'] = $product['price']; + } + $product['discount_amount'] = $couponDiscount; + $product['price'] -= $couponDiscount; $priceWithoutTax = $product['price']; $product['total_price'] = $product['price']; $product['tax_amount'] = 0; - $precision = 2; + if ($this->checkTaxEnabledInProduct($productType)) { - if (is_null($product['tax_perc'])) { + if (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']; + // $taxPerc = is_null($product['tax_perc']) ? $globalTaxPerc : $product['tax_perc']; - $taxAmount = round($priceWithoutTax * $taxPerc / 100, $precision, PHP_ROUND_HALF_UP); + $taxAmount = round($priceWithoutTax * $taxPerc / 100, 2); $product['tax_amount'] = $taxAmount; $priceWithTax = $priceWithoutTax + $taxAmount; $product['total_price'] = $priceWithTax; @@ -2258,27 +3090,37 @@ public function setPriceSettings(&$product, $productType) $product['tax_perc_show'] = $taxPerc; $product['price_formatted'] = $this->getPriceWithCurrencyFromIsoCode( - number_format($product['price'], $precision), + $product['price'], $product['iso_code'] ); - $product['tax_amount_formatted'] = number_format($product['tax_amount'], $precision); + $product['tax_amount_formatted'] = number_format($product['tax_amount'], 2); $product['total_price_formatted'] = $this->getPriceWithCurrencyFromIsoCode( - number_format($product['total_price'], $precision), + $product['total_price'], $product['iso_code'] ); + + if (null != $coupon) { + $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 int $id - * * @return array */ - public function getService($id) + public function getService(int $id, ?array $coupon = null) { - $id = (int) $id; - if (empty($id)) { return []; } @@ -2300,12 +3142,12 @@ public function getService($id) $service['iso_code'] = $isoCode; $globalParameters = $this->getGlobalParameters(); - $this->setPriceSettings($service, self::TAX_APPLIES_TO_ONLY_SERVICES); + $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; + $service['image'] = !empty($service['image']) ? api_get_path(WEB_PLUGIN_PATH).'buycourses/uploads/services/images/'.$service['image'] : null; return $service; } @@ -2315,13 +3157,36 @@ public function getService($id) * * @return array */ - public function getServices($start, $end, $typeResult = 'all') + public function getAllServices() { $servicesTable = Database::get_main_table(self::TABLE_SERVICES); $userTable = Database::get_main_table(TABLE_MAIN_USER); - $start = (int) $start; - $end = (int) $end; + $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"; @@ -2369,19 +3234,18 @@ public function getServiceSaleStatuses() * @return array */ public function getServiceSales( - $buyerId = 0, - $status = 0, - $nodeType = 0, - $nodeId = 0 + int $buyerId = 0, + int $status = 0, + int $nodeType = 0, + int $nodeId = 0 ) { $conditions = null; $groupBy = ''; - $buyerId = (int) $buyerId; - $status = (int) $status; - $nodeType = (int) $nodeType; - $nodeId = (int) $nodeId; - $defaultOrder = 'ss.id ASC'; + $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]; @@ -2412,19 +3276,16 @@ public function getServiceSales( $status, ], ], - 'ORDER' => 'ss.service_id ASC', + 'ORDER' => $defaultOrder, ]; } - $servicesTable = Database::get_main_table(self::TABLE_SERVICES); - $servicesSaleTable = Database::get_main_table(self::TABLE_SERVICES_SALE); - $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 + // , "all", null, true ); $list = []; @@ -2440,7 +3301,7 @@ public function getServiceSales( * * @return array */ - public function getServiceSale($id) + public function getServiceSale(int $id) { $servicesTable = Database::get_main_table(self::TABLE_SERVICES); $servicesSaleTable = Database::get_main_table(self::TABLE_SERVICES_SALE); @@ -2455,7 +3316,7 @@ public function getServiceSale($id) $isoCode = $currency['iso_code']; $servicesSale = Database::select( - 'ss.*, s.title, s.description, s.price as service_price, s.duration_days, s.applies_to, s.owner_id, s.visibility, s.image', + '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' @@ -2464,7 +3325,7 @@ public function getServiceSale($id) $buyer = api_get_user_info($servicesSale['buyer_id']); $servicesSale['service']['id'] = $servicesSale['service_id']; - $servicesSale['service']['title'] = $servicesSale['title']; + $servicesSale['service']['name'] = $servicesSale['name']; $servicesSale['service']['description'] = $servicesSale['description']; $servicesSale['service']['price'] = $servicesSale['service_price']; $servicesSale['service']['currency'] = $isoCode; @@ -2495,7 +3356,7 @@ public function getServiceSale($id) * * @return bool */ - public function cancelServiceSale($serviceSaleId) + public function cancelServiceSale(int $serviceSaleId) { $this->updateServiceSaleStatus( $serviceSaleId, @@ -2512,7 +3373,7 @@ public function cancelServiceSale($serviceSaleId) * * @return bool */ - public function completeServiceSale($serviceSaleId) + public function completeServiceSale(int $serviceSaleId) { $serviceSale = $this->getServiceSale($serviceSaleId); if (self::SERVICE_STATUS_COMPLETED == $serviceSale['status']) { @@ -2534,13 +3395,19 @@ public function completeServiceSale($serviceSaleId) /** * Lists current service details. * - * @param string $name Optional. The name filter - * @param int $min Optional. The minimum price filter - * @param int $max Optional. The maximum price filter - * @param mixed $appliesTo optional + * @param mixed $appliesTo + * + * @return array|int */ - public function getCatalogServiceList($start, $end, $name = null, $min = 0, $max = 0, $appliesTo = '', $typeResult = 'all'): 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); @@ -2549,7 +3416,7 @@ public function getCatalogServiceList($start, $end, $name = null, $min = 0, $max ]; if (!empty($name)) { - $whereConditions['AND s.title LIKE %?%'] = $name; + $whereConditions['AND s.name LIKE %?%'] = $name; } if (!empty($min)) { @@ -2564,9 +3431,6 @@ public function getCatalogServiceList($start, $end, $name = null, $min = 0, $max $whereConditions['AND s.applies_to = ?'] = $appliesTo; } - $start = (int) $start; - $end = (int) $end; - $innerJoins = "INNER JOIN $userTable u ON s.owner_id = u.id"; $return = Database::select( 's.*', @@ -2596,7 +3460,7 @@ public function getCatalogServiceList($start, $end, $name = null, $min = 0, $max * * @return bool */ - public function registerServiceSale($serviceId, $paymentType, $infoSelect) + public function registerServiceSale(int $serviceId, int $paymentType, int $infoSelect, ?int $couponId = null) { if (!in_array( $paymentType, @@ -2613,6 +3477,21 @@ public function registerServiceSale($serviceId, $paymentType, $infoSelect) return false; } + if (null != $couponId) { + $coupon = $this->getCouponService($couponId, $serviceId); + } + + $couponDiscount = 0; + $priceWithoutDiscount = 0; + if (null != $coupon) { + if (self::COUPON_DISCOUNT_TYPE_AMOUNT == $coupon['discount_type']) { + $couponDiscount = $coupon['discount_amount']; + } elseif (self::COUPON_DISCOUNT_TYPE_PERCENTAGE == $coupon['discount_type']) { + $couponDiscount = ($service['price'] * $coupon['discount_amount']) / 100; + } + $priceWithoutDiscount = $service['price']; + } + $service['price'] -= $couponDiscount; $currency = $this->getSelectedCurrency(); $price = $service['price']; $priceWithoutTax = null; @@ -2622,14 +3501,14 @@ public function registerServiceSale($serviceId, $paymentType, $infoSelect) $taxAppliesTo = $globalParameters['tax_applies_to']; $taxAmount = 0; - if ($taxEnable && - (self::TAX_APPLIES_TO_ALL == $taxAppliesTo || self::TAX_APPLIES_TO_ONLY_SERVICES == $taxAppliesTo) + if ($taxEnable + && (self::TAX_APPLIES_TO_ALL == $taxAppliesTo || self::TAX_APPLIES_TO_ONLY_SERVICES == $taxAppliesTo) ) { $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, PHP_ROUND_HALF_UP); + $taxPerc = null === $service['tax_perc'] ? $globalTaxPerc : $service['tax_perc']; + $taxAmount = round($priceWithoutTax * $taxPerc / 100, $precision); $price = $priceWithoutTax + $taxAmount; } @@ -2646,7 +3525,7 @@ public function registerServiceSale($serviceId, $paymentType, $infoSelect) 'tax_perc' => $taxPerc, 'tax_amount' => $taxAmount, 'node_type' => $service['applies_to'], - 'node_id' => (int) $infoSelect, + 'node_id' => $infoSelect, 'buyer_id' => $userId, 'buy_date' => api_get_utc_datetime(), 'date_start' => api_get_utc_datetime(), @@ -2658,22 +3537,20 @@ public function registerServiceSale($serviceId, $paymentType, $infoSelect) 'Y-m-d H:i:s' ), 'status' => self::SERVICE_STATUS_PENDING, - 'payment_type' => (int) $paymentType, + 'payment_type' => $paymentType, + 'price_without_discount' => $priceWithoutDiscount, + 'discount_amount' => $couponDiscount, ]; - $returnedServiceSaleId = Database::insert(self::TABLE_SERVICES_SALE, $values); - - return $returnedServiceSaleId; + return Database::insert(self::TABLE_SERVICES_SALE, $values); } /** * Save Culqi configuration params. * - * @param array $params - * - * @return int Rows affected. Otherwise return false + * @return int Rows affected. Otherwise, return false */ - public function saveCulqiParameters($params) + public function saveCulqiParameters(array $params) { return Database::update( Database::get_main_table(self::TABLE_CULQI), @@ -2702,13 +3579,49 @@ public function getCulqiParams() } /** - * Save Global Parameters. + * 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. * - * @param array $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 + * @return int Rows affected. Otherwise, return false */ - public function saveGlobalParameters($params) + public function saveGlobalParameters(array $params) { $sqlParams = [ 'terms_and_conditions' => $params['terms_and_conditions'], @@ -2753,11 +3666,9 @@ public function getGlobalParameters() } /** - * @param int $productType - * * @return bool */ - public function checkTaxEnabledInProduct($productType) + public function checkTaxEnabledInProduct(int $productType) { if (empty('true' === $this->get('tax_enable'))) { return false; @@ -2778,14 +3689,10 @@ public function checkTaxEnabledInProduct($productType) /** * Get the path. - * - * @param string $var path variable - * - * @return string path */ - public function getPath($var) + public function getPath(string $var): string { - $pluginPath = api_get_path(WEB_PLUGIN_PATH).'BuyCourses/'; + $pluginPath = api_get_path(WEB_PLUGIN_PATH).'buycourses/'; $paths = [ 'SERVICE_IMAGES' => $pluginPath.'uploads/services/images/', 'SRC' => $pluginPath.'src/', @@ -2801,410 +3708,2337 @@ public function getPath($var) return $paths[$var]; } - /** - * @return array - */ - public function getBuyCoursePluginPrice(Session $session) + public function getBuyCoursePluginPrice(Session $session): array { // start buycourse validation - // display the course price and buy button if the BuyCourses plugin is enabled and this course is configured + // 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. + // set the Buy button instead of register. if ($isThisCourseInSale['verificator']) { $return['buy_button'] = $this->returnBuyCourseButton($session->getId(), self::PRODUCT_TYPE_SESSION); } } + // end buycourse validation return $return; } /** - * @return string + * Register a coupon sale. + * + * @param int $saleId The sale ID + * @param int $couponId The coupon ID */ - public function getSubscriptionSuccessMessage(array $saleInfo) + public function registerCouponSale(int $saleId, int $couponId): bool { - 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['real_id']); - 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 = '#'; + $sale = $this->getSale($saleId); + + if (empty($sale)) { + return false; } - return Display::return_message( - sprintf( - $this->get_lang('SubscriptionToCourseXSuccessful'), - $url, - $saleInfo['product_name'] - ), - 'success', - false - ); + $values = [ + 'coupon_id' => $couponId, + 'sale_id' => $saleId, + ]; + + return Database::insert(self::TABLE_COUPON_SALE, $values) > 0; } - public static function returnPagination( - string $baseUrl, - string $currentPage, - string $pagesCount, - string $totalItems, - array $extraQueryParams = [] - ): string { - $queryParams = HttpRequest::createFromGlobals()->query->all(); + /** + * Register a coupon service sale. + * + * @param int $saleId The sale ID + * @param int $couponId The coupon ID + */ + public function registerCouponServiceSale(int $saleId, int $couponId): bool + { + $sale = $this->getSale($saleId); - unset($queryParams['page']); + if (empty($sale)) { + return false; + } - $url = $baseUrl.'?'.http_build_query( - array_merge($queryParams, $extraQueryParams) - ); + $values = [ + 'coupon_id' => $couponId, + 'service_sale_id' => $saleId, + ]; - return Display::getPagination($url, $currentPage, $pagesCount, $totalItems); + return Database::insert(self::TABLE_COUPON_SERVICE_SALE, $values) > 0; } /** - * Filter the registered courses for show in plugin catalog. + * Register a coupon sale. + * + * @param int $saleId The sale ID + * @param int $couponId The coupon ID */ - private function getCourses($first, $maxResults): QueryBuilder + public function registerCouponSubscriptionSale(int $saleId, int $couponId): bool { - $em = Database::getManager(); - $urlId = api_get_current_access_url_id(); + $sale = $this->getSubscriptionSale($saleId); - $qb = $em->createQueryBuilder(); - $qb2 = $em->createQueryBuilder(); - $qb3 = $em->createQueryBuilder(); + if (empty($sale)) { + return false; + } - $qb = $qb - ->select('c') - ->from(Course::class, 'c') - ->where( - $qb->expr()->notIn( - 'c', - $qb2 - ->select('course2') - ->from(SessionRelCourse::class, 'sc') - ->join('sc.course', 'course2') - ->innerJoin( - AccessUrlRelSession::class, - 'us', - Join::WITH, - 'us.session = sc.session' - )->where( - $qb->expr()->eq('us.url ', $urlId) - ) - ->getDQL() - ) - )->andWhere( - $qb->expr()->in( - 'c', - $qb3 - ->select('course3') - ->from(AccessUrlRelCourse::class, 'uc') - ->join('uc.course', 'course3') - ->where( - $qb3->expr()->eq('uc.url ', $urlId) - ) - ->getDQL() - ) - ) - ->setFirstResult($first) - ->setMaxResults($maxResults); + $values = [ + 'coupon_id' => (int) $couponId, + 'sale_id' => (int) $saleId, + ]; - return $qb; + return Database::insert(self::TABLE_COUPON_SUBSCRIPTION_SALE, $values) > 0; } /** - * Get the user status for the session. - * - * @param int $userId The user ID - * @param Session $session The session - * - * @return string + * Add a new coupon. */ - private function getUserStatusForSession($userId, Session $session) + public function addNewCoupon(array $coupon): bool { - if (empty($userId)) { - return 'NO'; - } + $couponId = $this->registerCoupon($coupon); - $entityManager = Database::getManager(); - $scuRepo = $entityManager->getRepository(SessionRelCourseRelUser::class); + if ($couponId) { + if (isset($coupon['courses'])) { + foreach ($coupon['courses'] as $course) { + $this->registerCouponItem($couponId, self::PRODUCT_TYPE_COURSE, $course); + } + } - $buySaleTable = Database::get_main_table(self::TABLE_SALE); + if (isset($coupon['sessions'])) { + foreach ($coupon['sessions'] as $session) { + $this->registerCouponItem($couponId, self::PRODUCT_TYPE_SESSION, $session); + } + } - // 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 (isset($coupon['services'])) { + foreach ($coupon['services'] as $service) { + $this->registerCouponService($couponId, $service); + } + } - if ($sale['qty'] > 0) { - return 'TMP'; + return true; } - // Check if user is already subscribe to session - $userSubscription = $scuRepo->findBy([ - 'session' => $session, - 'user' => $userId, - ]); - - if (!empty($userSubscription)) { - return 'YES'; - } + Display::addFlash( + Display::return_message( + $this->get_lang('CouponErrorInsert'), + 'error', + false + ) + ); - return 'NO'; + return false; } /** - * Get the user status for the course. - * - * @param int $userId The user Id - * @param Course $course The course + * Add a new coupon. * - * @return string + * @return bool */ - private function getUserStatusForCourse($userId, Course $course) + public function updateCouponData(array $coupon) { - if (empty($userId)) { - return 'NO'; + $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); + } } - $entityManager = Database::getManager(); - $cuRepo = $entityManager->getRepository(CourseRelUser::class); - $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'; + if (isset($coupon['sessions'])) { + foreach ($coupon['sessions'] as $session) { + $this->registerCouponItem($coupon['id'], self::PRODUCT_TYPE_SESSION, $session); + } } - // Check if user is already subscribe to course - $userSubscription = $cuRepo->findBy([ - 'course' => $course, - 'user' => $userId, - ]); - - if (!empty($userSubscription)) { - return 'YES'; + if (isset($coupon['services'])) { + foreach ($coupon['services'] as $service) { + $this->registerCouponService($coupon['id'], $service); + } } - return 'NO'; + return true; } /** - * Update the sale status. + * Update coupons delivered. * - * @param int $saleId The sale ID - * @param int $newStatus The new status + * @param int $couponId The coupon ID * * @return bool */ - private function updateSaleStatus($saleId, $newStatus = self::SALE_STATUS_PENDING) + public function updateCouponDelivered(int $couponId) { - $saleTable = Database::get_main_table(self::TABLE_SALE); + $couponTable = Database::get_main_table(self::TABLE_COUPON); - return Database::update( - $saleTable, - ['status' => (int) $newStatus], - ['id = ?' => (int) $saleId] - ); + $sql = "UPDATE $couponTable + SET delivered = delivered+1 + WHERE id = $couponId"; + + Database::query($sql); } /** - * Search filtered sessions by name, and range of price. + * Get coupon info. * - * @param int $start - * @param int $end - * @param string $name Optional. The name filter - * @param int $min Optional. The minimun price filter - * @param int $max Optional. The maximum price filter + * @param int $couponId The coupon ID * - * @return array + * @return array The coupon data */ - private function filterSessionList($start, $end, $name = null, $min = 0, $max = 0, $typeResult = 'all') + public function getCouponInfo(int $couponId) { - $itemTable = Database::get_main_table(self::TABLE_ITEM); - $sessionTable = Database::get_main_table(TABLE_MAIN_SESSION); + $coupon = $this->getDataCoupon($couponId); - $min = floatval($min); - $max = floatval($max); + $couponRelCourses = $this->getItemsCoupons($couponId, self::PRODUCT_TYPE_COURSE); + $couponRelSessions = $this->getItemsCoupons($couponId, self::PRODUCT_TYPE_SESSION); + $couponRelServices = $this->getServicesCoupons($couponId); - $innerJoin = "$itemTable i ON s.id = i.product_id"; - $whereConditions = [ - 'i.product_type = ? ' => self::PRODUCT_TYPE_SESSION, - ]; + $coupon['courses'] = $couponRelCourses; + $coupon['sessions'] = $couponRelSessions; + $coupon['services'] = $couponRelServices; - if (!empty($name)) { - $whereConditions['AND s.title LIKE %?%'] = $name; - } + return $coupon; + } - if (!empty($min)) { - $whereConditions['AND i.price >= ?'] = $min; - } + /** + * Get a list of coupons. + * + * @param int $status The coupons activation status + * + * @return array Coupons data + */ + public function getCouponsListByStatus(int $status) + { + return $this->getDataCoupons($status); + } - if (!empty($max)) { - $whereConditions['AND i.price <= ?'] = $max; - } + /** + * Get the coupon data. + * + * @return array The coupon data + */ + public function getCoupon(int $couponId, int $productType, int $productId) + { + return $this->getDataCoupon($couponId, $productType, $productId); + } - $start = (int) $start; - $end = (int) $end; + /** + * 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) + { + return $this->getDataCouponByCode($couponCode, $productType, $productId); + } - $sessionIds = Database::select( - 's.id', - "$sessionTable s INNER JOIN $innerJoin", - ['where' => $whereConditions, 'limit' => "$start, $end"], - $typeResult - ); + /** + * 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) + { + return $this->getDataCouponService($couponId, $serviceId); + } - if ('count' === $typeResult) { - return $sessionIds; - } + /** + * 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) + { + return $this->getDataCouponServiceByCode($couponCode, $serviceId); + } - if (!$sessionIds) { - return []; - } + /** + * 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); - $sessions = []; + $couponFrom = " + $couponTable c + INNER JOIN $couponSaleTable s + on c.id = s.coupon_id + "; - foreach ($sessionIds as $sessionId) { - $sessions[] = Database::getManager()->find( - Session::class, - $sessionId - ); - } + $couponCode = Database::select( + ['c.code'], + $couponFrom, + [ + 'where' => [ + 's.sale_id = ? ' => $saleId, + ], + ], + 'first' + ); - return $sessions; + return $couponCode['code']; } /** - * Search filtered courses by name, and range of price. + * Get the coupon code of a service sale. * - * @param string $name Optional. The name filter - * @param int $min Optional. The minimun price filter - * @param int $max Optional. The maximum price filter + * @param int $serviceSaleId The service sale ID * - * @return array + * @return string The coupon code */ - private function filterCourseList($start, $end, $name = '', $min = 0, $max = 0, $typeResult = 'all') + public function getServiceSaleCouponCode(int $serviceSaleId) { - $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); + $couponTable = Database::get_main_table(self::TABLE_COUPON); + $couponServiceSaleTable = Database::get_main_table(self::TABLE_COUPON_SERVICE_SALE); - $urlId = api_get_current_access_url_id(); + $couponFrom = " + $couponTable c + INNER JOIN $couponServiceSaleTable s + on c.id = s.coupon_id + "; - $min = floatval($min); - $max = floatval($max); + $couponCode = Database::select( + ['c.code'], + $couponFrom, + [ + 'where' => [ + 's.service_sale_id = ? ' => $serviceSaleId, + ], + ], + 'first' + ); - $whereConditions = [ - 'i.product_type = ? ' => self::PRODUCT_TYPE_COURSE, - ]; + return $couponCode['code']; + } - if (!empty($name)) { - $whereConditions['AND c.title LIKE %?%'] = $name; - } + /** + * @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); + + return strtolower($sha256); + } - if (!empty($min)) { - $whereConditions['AND i.price >= ?'] = $min; + /** + * 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; } - if (!empty($max)) { - $whereConditions['AND i.price <= ?'] = $max; + $entityManager = Database::getManager(); + $item = $this->getSubscriptionItem($productId, $productType); + + if (empty($item)) { + return false; } - $whereConditions['AND url.access_url_id = ?'] = $urlId; - $start = (int) $start; - $end = (int) $end; + $productName = ''; + if (self::PRODUCT_TYPE_COURSE == $item['product_type']) { + $course = $entityManager->find(Course::class, $item['product_id']); - $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 (empty($course)) { + return false; + } - if ('count' === $typeResult) { - return $courseIds; + $productName = $course->getTitle(); + } elseif (self::PRODUCT_TYPE_SESSION == $item['product_type']) { + $session = $entityManager->find(Session::class, $item['product_id']); + + if (empty($session)) { + return false; + } + + $productName = $session->getName(); } - if (!$courseIds) { - return []; + if (null != $couponId) { + $coupon = $this->getCoupon($couponId, $item['product_type'], $item['product_id']); } - $courses = []; - foreach ($courseIds as $courseId) { - $courses[] = Database::getManager()->find( - Course::class, - $courseId['id'] - ); + $couponDiscount = 0; + $priceWithoutDiscount = 0; + if (null != $coupon) { + if (self::COUPON_DISCOUNT_TYPE_AMOUNT == $coupon['discount_type']) { + $couponDiscount = $coupon['discount_amount']; + } elseif (self::COUPON_DISCOUNT_TYPE_PERCENTAGE == $coupon['discount_type']) { + $couponDiscount = ($item['price'] * $coupon['discount_amount']) / 100; + } + $priceWithoutDiscount = $item['price']; } + $item['price'] -= $couponDiscount; + $price = $item['price']; + $priceWithoutTax = null; + $taxPerc = null; + $taxAmount = 0; + $taxEnable = 'true' === $this->get('tax_enable'); + $globalParameters = $this->getGlobalParameters(); + $taxAppliesTo = $globalParameters['tax_applies_to']; - return $courses; + if ($taxEnable + && ( + self::TAX_APPLIES_TO_ALL == $taxAppliesTo + || (self::TAX_APPLIES_TO_ONLY_COURSE == $taxAppliesTo && self::PRODUCT_TYPE_COURSE == $item['product_type']) + || (self::TAX_APPLIES_TO_ONLY_SESSION == $taxAppliesTo && self::PRODUCT_TYPE_SESSION == $item['product_type']) + ) + ) { + $priceWithoutTax = $item['price']; + $globalTaxPerc = $globalParameters['global_tax_perc']; + $precision = 2; + $taxPerc = 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); } /** - * Update the service sale status. - * - * @param int $serviceSaleId The service sale ID - * @param int $newStatus The new status + * Add a new subscription. * * @return bool */ - private function updateServiceSaleStatus( - $serviceSaleId, - $newStatus = self::SERVICE_STATUS_PENDING - ) { + 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; + } + $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; + } + + /** + * 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) + { + return $this->getDataSubscriptions($productType, $productId); + } + + /** + * 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 (self::SALE_STATUS_COMPLETED == $sale['status']) { + 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 ('true' === $this->get('invoicing_enable')) { + $this->setInvoice($sale['id']); + } + } + + return $saleIsCompleted; + } + + /** + * Update subscription sale status to canceled. + * + * @param int $saleId The subscription sale ID + */ + public function cancelSubscriptionSale(int $saleId): void + { + $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. + * + * @return array + * + * @throws Exception + */ + 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, + int $currentPage, + int $pagesCount, + int $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 (1 == $item['invoice']) { + if ($invoicingEnable) { + $item['invoice'] = "". + Display::return_icon('default.png', get_plugin_lang('InvoiceView', 'BuyCoursesPlugin'), '', ICON_SIZE_MEDIUM). + '
'.$item['num_invoice']. + '
'; + } + } else { + $item['invoice'] = null; + } + if (self::SALE_STATUS_CANCELED == $item['status']) { + $item['status'] = get_plugin_lang('SaleStatusCanceled', 'BuyCoursesPlugin'); + } elseif (self::SALE_STATUS_PENDING == $item['status']) { + $item['status'] = get_plugin_lang('SaleStatusPending', 'BuyCoursesPlugin'); + $option = "'; + } elseif (self::SALE_STATUS_COMPLETED == $item['status']) { + $item['status'] = get_plugin_lang('SaleStatusCompleted', 'BuyCoursesPlugin'); + } + $item['options'] = $option; + $item['date'] = api_get_local_time($item['date']); + $data .= json_encode($item).','; + } + + return " +"; + } + + /** + * Filter the registered courses for show in the plugin catalog. + */ + public function getCourses(int $first, int $maxResults): QueryBuilder + { + $em = Database::getManager(); + $urlId = api_get_current_access_url_id(); + + $qb = $em->createQueryBuilder(); + $qb2 = $em->createQueryBuilder(); + $qb3 = $em->createQueryBuilder(); + + return $qb + ->select('c') + ->from(Course::class, 'c') + ->where( + $qb->expr()->notIn( + 'c', + $qb2 + ->select('course2') + ->from(SessionRelCourse::class, 'sc') + ->innerJoin('sc.course', 'course2') + ->innerJoin( + AccessUrlRelSession::class, + 'us', + Join::WITH, + 'us.session = sc.session' + )->where( + $qb->expr()->eq('us.url ', $urlId) + ) + ->getDQL() + ) + )->andWhere( + $qb->expr()->in( + 'c', + $qb3 + ->select('course3') + ->from(AccessUrlRelCourse::class, 'uc') + ->innerJoin('uc.course', 'course3') + ->where( + $qb3->expr()->eq('uc.url ', $urlId) + ) + ->getDQL() + ) + ) + ->setFirstResult($first) + ->setMaxResults($maxResults) + ; + } + + /** + * 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(SessionRelCourseRelUser::class); + + $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(CourseRelUser::class); + $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|int + */ + private function filterSessionList( + int $start, + int $end, + ?string $name = null, + int $min = 0, + int $max = 0, + string $typeResult = 'all', + int $sessionCategory = 0 + ): array|int { + $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.title LIKE %?%'] = $name; + } + + if (!empty($min)) { + $whereConditions['AND i.price >= ?'] = $min; + } + + if (!empty($max)) { + $whereConditions['AND i.price <= ?'] = $max; + } + + if (0 != $sessionCategory) { + $whereConditions['AND s.session_category_id = ?'] = $sessionCategory; + } + + $sessionIds = Database::select( + 's.id', + "$sessionTable s INNER JOIN $innerJoin", + ['where' => $whereConditions, 'limit' => "$start, $end"], + $typeResult + ); + + if ('count' === $typeResult) { + return $sessionIds; + } + + if (!$sessionIds) { + return []; + } + + $sessions = []; + + foreach ($sessionIds as $sessionId) { + $sessions[] = Database::getManager()->find( + Session::class, + $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|int + */ + private function filterCourseList( + int $start, + int $end, + ?string $name = null, + int $min = 0, + int $max = 0, + string $typeResult = 'all' + ): array|int { + $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 = (float) $min; + $max = (float) $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 ('count' === $typeResult) { + return $courseIds; + } + + if (!$courseIds) { + return []; + } + + $courses = []; + foreach ($courseIds as $courseId) { + $courses[] = Database::getManager()->find( + Course::class, + $courseId['id'] + ); + } + + 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|int + */ + private function filterSubscriptionSessionList( + int $start, + int $end, + ?string $name = null, + string $typeResult = 'all', + int $sessionCategory = 0 + ): array|int { + $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 (0 != $sessionCategory) { + $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 ('count' === $typeResult) { + return $sessionIds; + } + + if (!$sessionIds) { + return []; + } + + $sessions = []; + + foreach ($sessionIds as $sessionId) { + $sessions[] = Database::getManager()->find( + Session::class, + $sessionId + ); + } + + return $sessions; + } + + /** + * Search filtered subscription courses by name, and range of price. + * + * @param string $name Optional. The name filter + * + * @return array|int + */ + private function filterSubscriptionCourseList( + int $start, + int $end, + string $name = '', + string $typeResult = 'all' + ): array|int { + $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 ('count' === $typeResult) { + return $courseIds; + } + + if (!$courseIds) { + return []; + } + + $courses = []; + foreach ($courseIds as $courseId) { + $courses[] = Database::getManager()->find( + Course::class, + $courseId + ); + } + + return $courses; + } + + /** + * Update the service sale status. + * + * @param int $serviceSaleId The service sale ID + * @param int $newStatus The new status + */ + private function updateServiceSaleStatus( + int $serviceSaleId, + int $newStatus = self::SERVICE_STATUS_PENDING + ): void { $serviceSaleTable = Database::get_main_table(self::TABLE_SERVICES_SALE); + 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): array + { + $couponItemTable = Database::get_main_table(self::TABLE_COUPON_ITEM); + + if (self::PRODUCT_TYPE_COURSE == $productType) { + $itemTable = Database::get_main_table(TABLE_MAIN_COURSE); + $select = ['ci.product_id as id', 'it.title']; + } elseif (self::PRODUCT_TYPE_SESSION == $productType) { + $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): array + { + $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): array + { + $couponTable = Database::get_main_table(self::TABLE_COUPON); + + if (null != $status) { + return Database::select( + ['*'], + $couponTable, + [ + 'where' => [ + ' active = ? ' => (int) $status, + ], + 'order' => 'id DESC', + ] + ); + } + + 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): array + { + $couponTable = Database::get_main_table(self::TABLE_COUPON); + + if (null == $productType || null == $productId) { + return Database::select( + ['*'], + $couponTable, + [ + 'where' => [ + 'id = ? ' => $couponId, + ], + ], + 'first' + ); + } + + $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 (null == $productType || null == $productId) { + return Database::select( + ['*'], + $couponTable, + [ + 'where' => [ + 'code = ? ' => $couponCode, + ], + ], + 'first' + ); + } + + $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): array + { + $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): array + { + $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. + */ + private function updateCoupon(array $coupon): void + { + $couponExist = $this->getCouponByCode($coupon['code']); + if (!$couponExist) { + Display::addFlash( + Display::return_message( + $this->get_lang('CouponNoExists'), + 'error', + false + ) + ); + + return; + } + + $values = [ + 'valid_start' => $coupon['valid_start'], + 'valid_end' => $coupon['valid_end'], + 'active' => $coupon['active'], + ]; + + Database::update( + self::TABLE_COUPON, + $values, + ['id = ?' => $coupon['id']] + ); + } + + /** + * Register a coupon. + */ + private function registerCoupon(array $coupon): int + { + $couponExist = $this->getCouponByCode($coupon['code']); + if ($couponExist) { + Display::addFlash( + Display::return_message( + $this->get_lang('CouponCodeUsed'), + 'error', + false + ) + ); + + return 0; + } + + $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 + */ + private function registerCouponItem(int $couponId, int $productType, int $productId): void + { + $coupon = $this->getDataCoupon($couponId); + if (empty($coupon)) { + Display::addFlash( + Display::return_message( + $this->get_lang('CouponNoExists'), + 'error', + false + ) + ); + + return; + } + + $values = [ + 'coupon_id' => $couponId, + 'product_type' => $productType, + 'product_id' => $productId, + ]; + + 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 + */ + private function deleteCouponItemsByCoupon(int $productType, int $couponId): void + { + 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 + */ + private function registerCouponService(int $couponId, int $serviceId): void + { + $coupon = $this->getDataCoupon($couponId); + if (empty($coupon)) { + Display::addFlash( + Display::return_message( + $this->get_lang('CouponNoExists'), + 'error', + false + ) + ); + + return; + } + + $values = [ + 'coupon_id' => $couponId, + 'service_id' => $serviceId, + ]; + + Database::insert(self::TABLE_COUPON_SERVICE, $values); + } + + /** + * Remove all coupon services for a product type and coupon ID. + */ + private function deleteCouponServicesByCoupon(int $couponId): void + { + 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): array + { + $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): array + { + $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. + */ + public function updateSubscription(int $productType, int $productId, int $taxPerc): bool + { + $values = [ + 'tax_perc' => $taxPerc, + ]; + return Database::update( - $serviceSaleTable, - ['status' => (int) $newStatus], - ['id = ?' => (int) $serviceSaleId] + self::TABLE_SUBSCRIPTION, + $values, + [ + 'product_type = ? AND ' => $productType, + 'product_id = ?' => $productId, + ] + ) > 0; + } + + /** + * Register a subscription. + */ + private function registerSubscription(array $subscription, array $frequency): bool + { + $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'], + ]; + + return Database::insert(self::TABLE_SUBSCRIPTION, $values) > 0; + } + + /** + * Update the subscription sale status. + * + * @param int $saleId The sale ID + * @param int $newStatus The new status + */ + private function updateSubscriptionSaleStatus(int $saleId, int $newStatus = self::SALE_STATUS_PENDING): void + { + $saleTable = Database::get_main_table(self::TABLE_SUBSCRIPTION_SALE); + + 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 + */ + private function getUserStatusForSubscriptionSession(int $userId, Session $session): string + { + if (empty($userId)) { + return 'NO'; + } + + $scuRepo = Database::getManager()->getRepository(SessionRelCourseRelUser::class); + + $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 subscribed to session + $userSubscription = $scuRepo->findBy([ + 'session' => $session, + 'user' => $userId, + ]); + + if (!empty($userSubscription)) { + return 'YES'; + } + + return 'NO'; } - public function get_name() + /** + * Get the user status for the subscription course. + * + * @param int $userId The user Id + * @param Course $course The course + */ + private function getUserStatusForSubscriptionCourse(int $userId, Course $course): string { - return 'BuyCourses'; // TODO: Change the autogenerated stub + if (empty($userId)) { + return 'NO'; + } + + $cuRepo = Database::getManager()->getRepository(CourseRelUser::class); + $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 a user is already subscribed to course + $userSubscription = $cuRepo->findBy([ + 'course' => $course, + 'user' => $userId, + ]); + + if (!empty($userSubscription)) { + return 'YES'; + } + + return 'NO'; } } diff --git a/public/plugin/BuyCourses/src/buycourses.ajax.php b/public/plugin/BuyCourses/src/buycourses.ajax.php index 998b1476d6e..91f3afe3000 100644 --- a/public/plugin/BuyCourses/src/buycourses.ajax.php +++ b/public/plugin/BuyCourses/src/buycourses.ajax.php @@ -1,4 +1,6 @@ * '.$plugin->get_lang('NoPayPalAccountDetected').''; } + break; + case 'saleInfo': if (api_is_anonymous()) { break; @@ -52,8 +56,7 @@ $productImage = $productInfo['course_image_large']; } else { $productImage = ($productInfo['image']) - ? $productInfo['image'] - : Template::get_icon_path('session_default.png'); + ?: Template::get_icon_path('session_default.png'); } $userInfo = api_get_user_info($sale['user_id']); @@ -80,7 +83,9 @@ $html .= '
'; echo $html; + break; + case 'stats': if (api_is_anonymous()) { break; @@ -120,20 +125,22 @@ $html = '
' .'

' .'

    ' - .'
  • '.get_plugin_lang("PayoutsTotalCompleted", "BuyCoursesPlugin").' '.$stats['completed_count'] - .' - '.get_plugin_lang("TotalAmount", "BuyCoursesPlugin").' '.$stats['completed_total_amount'].' ' + .'
  • '.get_plugin_lang('PayoutsTotalCompleted', 'BuyCoursesPlugin').' '.$stats['completed_count'] + .' - '.get_plugin_lang('TotalAmount', 'BuyCoursesPlugin').' '.$stats['completed_total_amount'].' ' .$currency['iso_code'].'
  • ' - .'
  • '.get_plugin_lang("PayoutsTotalPending", "BuyCoursesPlugin").' '.$stats['pending_count'].' - ' - .get_plugin_lang("TotalAmount", "BuyCoursesPlugin").' '.$stats['pending_total_amount'].' ' + .'
  • '.get_plugin_lang('PayoutsTotalPending', 'BuyCoursesPlugin').' '.$stats['pending_count'].' - ' + .get_plugin_lang('TotalAmount', 'BuyCoursesPlugin').' '.$stats['pending_total_amount'].' ' .$currency['iso_code'].'
  • ' - .'
  • '.get_plugin_lang("PayoutsTotalCanceled", "BuyCoursesPlugin").' '.$stats['canceled_count'] - .' - '.get_plugin_lang("TotalAmount", "BuyCoursesPlugin").' '.$stats['canceled_total_amount'].' ' + .'
  • '.get_plugin_lang('PayoutsTotalCanceled', 'BuyCoursesPlugin').' '.$stats['canceled_count'] + .' - '.get_plugin_lang('TotalAmount', 'BuyCoursesPlugin').' '.$stats['canceled_total_amount'].' ' .$currency['iso_code'].'
  • ' .'
' .'

'; $html .= '
'; echo $html; + break; + case 'processPayout': if (api_is_anonymous()) { break; @@ -148,10 +155,11 @@ if (!$payouts) { echo Display::return_message( - get_plugin_lang("SelectOptionToProceed", "BuyCoursesPlugin"), + get_plugin_lang('SelectOptionToProceed', 'BuyCoursesPlugin'), 'error', false ); + break; } @@ -166,20 +174,22 @@ $currentCurrency = $plugin->getSelectedCurrency(); $isoCode = $currentCurrency['iso_code']; - $html .= '

'.get_plugin_lang("VerifyTotalAmountToProceedPayout", "BuyCoursesPlugin").'

'; + $html .= '

'.get_plugin_lang('VerifyTotalAmountToProceedPayout', 'BuyCoursesPlugin').'

'; $html .= '' .'

' .'

    ' - .'
  • '.get_plugin_lang("TotalAcounts", "BuyCoursesPlugin").' '.$totalAccounts.'
  • ' - .'
  • '.get_plugin_lang("TotalPayout", "BuyCoursesPlugin").' '.$isoCode.' '.$totalPayout.'
  • ' + .'
  • '.get_plugin_lang('TotalAcounts', 'BuyCoursesPlugin').' '.$totalAccounts.'
  • ' + .'
  • '.get_plugin_lang('TotalPayout', 'BuyCoursesPlugin').' '.$isoCode.' '.$totalPayout.'
  • ' .'
' .'

'; - $html .= '

'.get_plugin_lang("CautionThisProcessCantBeCanceled", "BuyCoursesPlugin").'

'; + $html .= '

'.get_plugin_lang('CautionThisProcessCantBeCanceled', 'BuyCoursesPlugin').'

'; $html .= '

'; $html .= '
'; echo $html; + break; + case 'proceedPayout': if (api_is_anonymous()) { break; @@ -192,7 +202,7 @@ $paypalPassword = $paypalParams['password']; $paypalSignature = $paypalParams['signature']; - require_once "paypalfunctions.php"; + require_once 'paypalfunctions.php'; $allPayouts = []; $totalAccounts = 0; @@ -202,10 +212,11 @@ if (!$payouts) { echo Display::return_message( - get_plugin_lang("SelectOptionToProceed", "BuyCoursesPlugin"), + get_plugin_lang('SelectOptionToProceed', 'BuyCoursesPlugin'), 'error', false ); + break; } @@ -231,7 +242,7 @@ } echo Display::return_message( - get_plugin_lang("PayoutSuccess", "BuyCoursesPlugin"), + get_plugin_lang('PayoutSuccess', 'BuyCoursesPlugin'), 'success', false ); @@ -243,7 +254,9 @@ false ); } + break; + case 'cancelPayout': if (api_is_anonymous()) { break; @@ -259,6 +272,7 @@ echo ''; break; + case 'culqi_cargo': if (!$culqiEnable) { break; @@ -277,6 +291,7 @@ require_once 'Requests.php'; Requests::register_autoloader(); + require_once 'culqi.php'; $culqiParams = $plugin->getCulqiParams(); @@ -297,19 +312,19 @@ try { $cargo = $culqi->Cargos->create([ - "moneda" => $currency['iso_code'], - "monto" => intval(floatval($sale['price']) * 100), - "usuario" => $user['username'], - "descripcion" => $sale['product_name'], - "pedido" => $sale['reference'], - "codigo_pais" => "PE", - "direccion" => get_lang('None'), - "ciudad" => get_lang('None'), - "telefono" => 0, - "nombres" => $user['firstname'], - "apellidos" => $user['lastname'], - "correo_electronico" => $user['email'], - "token" => $tokenId, + 'moneda' => $currency['iso_code'], + 'monto' => (int) ((float) $sale['price'] * 100), + 'usuario' => $user['username'], + 'descripcion' => $sale['product_name'], + 'pedido' => $sale['reference'], + 'codigo_pais' => 'PE', + 'direccion' => get_lang('None'), + 'ciudad' => get_lang('None'), + 'telefono' => 0, + 'nombres' => $user['firstname'], + 'apellidos' => $user['lastname'], + 'correo_electronico' => $user['email'], + 'token' => $tokenId, ]); if (is_object($cargo)) { @@ -345,7 +360,9 @@ ); } } + break; + case 'culqi_cargo_service': if (!$culqiEnable) { break; @@ -366,6 +383,7 @@ require_once 'Requests.php'; Requests::register_autoloader(); + require_once 'culqi.php'; $culqiParams = $plugin->getCulqiParams(); @@ -383,19 +401,19 @@ try { $cargo = $culqi->Cargos->create([ - "moneda" => $serviceSale['currency'], - "monto" => intval(floatval($serviceSale['price']) * 100), - "usuario" => $user['username'], - "descripcion" => $serviceSale['service']['title'], - "pedido" => $serviceSale['reference'], - "codigo_pais" => "PE", - "direccion" => get_lang('None'), - "ciudad" => get_lang('None'), - "telefono" => 0, - "nombres" => $user['firstname'], - "apellidos" => $user['lastname'], - "correo_electronico" => $user['email'], - "token" => $tokenId, + 'moneda' => $serviceSale['currency'], + 'monto' => (int) ((float) $serviceSale['price'] * 100), + 'usuario' => $user['username'], + 'descripcion' => $serviceSale['service']['title'], + 'pedido' => $serviceSale['reference'], + 'codigo_pais' => 'PE', + 'direccion' => get_lang('None'), + 'ciudad' => get_lang('None'), + 'telefono' => 0, + 'nombres' => $user['firstname'], + 'apellidos' => $user['lastname'], + 'correo_electronico' => $user['email'], + 'token' => $tokenId, ]); if (is_object($cargo)) { @@ -438,7 +456,9 @@ ); } } + break; + case 'service_sale_info': $id = isset($_POST['id']) ? (int) $_POST['id'] : 0; $serviceSale = $plugin->getServiceSale($id); @@ -450,9 +470,9 @@ $ajaxCallFile = $plugin->getPath('SRC').'buycourses.ajax.php'; $serviceImg = $plugin->getPath('SERVICE_IMAGES').$serviceSale['service']['image']; $html = ""; - $html .= "
"; + $html .= '
'; $html .= "{$plugin->get_lang('ServiceInformation')}"; - $html .= "
    "; + $html .= '
      '; $html .= "
    • {$plugin->get_lang('ServiceName')}: {$serviceSale['service']['title']}
    • "; $html .= "
    • {$plugin->get_lang('Description')}: {$serviceSale['service']['description']}
    • "; $nodeType = $serviceSale['node_type']; @@ -464,6 +484,7 @@ } else { if (BuyCoursesPlugin::SERVICE_TYPE_COURSE == $nodeType) { $nodeType = get_lang('Course'); + /** @var Course $course */ $course = $em->find(Course::class, $serviceSale['node_id']); $nodeName = $course ? $course->getTitle() : null; @@ -475,6 +496,7 @@ } else { if (BuyCoursesPlugin::SERVICE_TYPE_LP_FINAL_ITEM == $nodeType) { $nodeType = get_lang('TemplateTitleCertificate'); + /** @var CLp $lp */ $lp = $em->find(CLp::class, $serviceSale['node_id']); $nodeName = $lp ? $lp->getTitle() : null; @@ -483,9 +505,9 @@ } } - $html .= "
    "; + $html .= '
'; $html .= "{$plugin->get_lang('SaleInfo')}"; - $html .= "
    "; + $html .= '
      '; $html .= "
    • {$plugin->get_lang('BoughtBy')}: {$serviceSale['buyer']['name']}
    • "; $html .= "
    • {$plugin->get_lang('PurchaserUser')}: {$serviceSale['buyer']['username']}
    • "; $html .= "
    • {$plugin->get_lang('Total')}: {$serviceSale['service']['total_price']}
    • "; @@ -522,39 +544,41 @@ } } $html .= "
    • {$plugin->get_lang('Status')}: $status
    • "; - $html .= "
    "; - $html .= "
    "; + $html .= '
'; + $html .= '
'; $html .= "
"; $html .= "
"; $html .= "
"; $html .= "
"; $html .= $buttons; - $html .= "
"; - $html .= "
"; + $html .= '
'; + $html .= '
'; $html .= "
"; - $html .= ""; + $html .= '},'; + $html .= '});'; + $html .= '});'; + $html .= ''; echo $html; + break; + case 'service_sale_confirm': $id = isset($_POST['id']) ? (int) $_POST['id'] : 0; $serviceSale = $plugin->getServiceSale($id); @@ -570,15 +594,17 @@ $html .= Display::return_message('Error - '.$plugin->get_lang('ErrorContactPlatformAdmin'), 'error'); } - $html .= "".$plugin->get_lang('ClickHereToFinish').""; - $html .= "
"; - $html .= ""; + $html .= 'location.reload();'; + $html .= '});'; + $html .= ''; echo $html; + break; + case 'service_sale_cancel': $id = isset($_POST['id']) ? (int) $_POST['id'] : 0; $response = $plugin->cancelServiceSale($id); @@ -594,13 +620,14 @@ $html .= Display::return_message('Error - '.$plugin->get_lang('ErrorContactPlatformAdmin'), 'error'); } - $html .= "".$plugin->get_lang('ClickHereToFinish').""; - $html .= ""; - $html .= ""; + $html .= 'location.reload();'; + $html .= '});'; + $html .= ''; echo $html; + break; } diff --git a/public/plugin/BuyCourses/src/cecabank_cancel.php b/public/plugin/BuyCourses/src/cecabank_cancel.php new file mode 100644 index 00000000000..501793501c8 --- /dev/null +++ b/public/plugin/BuyCourses/src/cecabank_cancel.php @@ -0,0 +1,31 @@ +get('cecabank_enable'); + +if (!$cecabankEnabled) { + api_not_allowed(true); +} + +$sale = $plugin->getSale($_SESSION['bc_sale_id']); + +if (empty($sale)) { + api_not_allowed(true); +} + +Display::addFlash( + Display::return_message($plugin->get_lang('ErrorContactPlatformAdmin'), 'error') +); + +unset($_SESSION['bc_sale_id']); +header('Location: '.api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/course_catalog.php'); + +exit; diff --git a/public/plugin/BuyCourses/src/cecabank_response.php b/public/plugin/BuyCourses/src/cecabank_response.php new file mode 100644 index 00000000000..9a3be272eca --- /dev/null +++ b/public/plugin/BuyCourses/src/cecabank_response.php @@ -0,0 +1,56 @@ +get('cecabank_enable'); + +if (!$cecabankEnabled) { + api_not_allowed(true); +} + +$receivedAmount = (float) $_POST['Importe']; + +if (empty($_POST['Num_operacion']) || empty($_POST['Firma']) || empty($receivedAmount)) { + api_not_allowed(true); +} + +$signature = $plugin->getCecabankSignature($_POST['Num_operacion'], $receivedAmount); + +if ($signature != $_POST['Firma']) { + api_not_allowed(true); +} + +$sale = $plugin->getSaleFromReference($_POST['Num_operacion']); + +if (empty($sale)) { + api_not_allowed(true); +} + +$buyingCourse = false; +$buyingSession = false; + +switch ($sale['product_type']) { + case BuyCoursesPlugin::PRODUCT_TYPE_COURSE: + $buyingCourse = true; + $course = $plugin->getCourseInfo($sale['product_id']); + + break; + + case BuyCoursesPlugin::PRODUCT_TYPE_SESSION: + $buyingSession = true; + $session = $plugin->getSessionInfo($sale['product_id']); + + break; +} + +$saleIsCompleted = $plugin->completeSale($sale['id']); +if ($saleIsCompleted) { + $plugin->storePayouts($sale['id']); +} diff --git a/public/plugin/BuyCourses/src/cecabank_success.php b/public/plugin/BuyCourses/src/cecabank_success.php new file mode 100644 index 00000000000..6ed8611f379 --- /dev/null +++ b/public/plugin/BuyCourses/src/cecabank_success.php @@ -0,0 +1,31 @@ +get('cecabank_enable'); + +if (!$cecabankEnabled) { + api_not_allowed(true); +} + +$sale = $plugin->getSale($_SESSION['bc_sale_id']); + +if (empty($sale)) { + api_not_allowed(true); +} + +Display::addFlash( + $plugin->getSubscriptionSuccessMessage($sale) +); + +unset($_SESSION['bc_sale_id']); +header('Location: '.api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/course_catalog.php'); + +exit; diff --git a/public/plugin/BuyCourses/src/configure_coupon.php b/public/plugin/BuyCourses/src/configure_coupon.php new file mode 100644 index 00000000000..0dfbb359390 --- /dev/null +++ b/public/plugin/BuyCourses/src/configure_coupon.php @@ -0,0 +1,200 @@ +getCouponInfo($couponId); + +if (!isset($coupon)) { + api_not_allowed(); +} + +$couponDateRangeFrom = $coupon['valid_start']; +$couponDateRangeTo = $coupon['valid_end']; + +$includeSession = 'true' === $plugin->get('include_sessions'); +$includeServices = 'true' === $plugin->get('include_services'); + +$entityManager = Database::getManager(); +$userRepo = UserManager::getRepository(); +$currency = $plugin->getSelectedCurrency(); + +if (empty($currency)) { + Display::addFlash( + Display::return_message($plugin->get_lang('CurrencyIsNotConfigured'), 'error') + ); +} + +$currencyIso = null; + +$coursesList = CourseManager::get_courses_list( + 0, + 0, + 'title', + 'asc', + -1, + null, + api_get_current_access_url_id() +); + +foreach ($coursesList as $course) { + $courses[$course['id']] = $course['title']; +} + +$sessionsList = SessionManager::get_sessions_list( + [], + [], + null, + null, + api_get_current_access_url_id() +); + +foreach ($sessionsList as $session) { + $sessions[$session['id']] = $session['name']; +} + +$servicesList = $plugin->getAllServices(); + +foreach ($servicesList as $service) { + $services[$service['id']] = $service['name']; +} + +$discountTypes = $plugin->getCouponDiscountTypes(); + +// Build the form +$form = new FormValidator('add_coupon'); +$form->addText('code', $plugin->get_lang('CouponCode'), false); +$form->addText('discount_type', $plugin->get_lang('CouponDiscountType'), false); +$form->addText('discount_amount', $plugin->get_lang('CouponDiscount'), false); +$form->addDateRangePicker( + 'date', + get_lang('Date'), + true, + [ + 'value' => "$couponDateRangeFrom / $couponDateRangeTo", + ] +); + +$form->addCheckBox('active', $plugin->get_lang('CouponActive')); +$form->addElement( + 'advmultiselect', + 'courses', + get_lang('Courses'), + $courses +); + +if ($includeSession) { + $form->addElement( + 'advmultiselect', + 'sessions', + get_lang('Sessions'), + $sessions + ); +} + +if ($includeServices) { + $form->addElement( + 'advmultiselect', + 'services', + get_lang('Services'), + $services + ); +} + +$form->addHidden('id', null); + +$coursesAdded = $coupon['courses']; +if (!empty($coursesAdded)) { + $coursesAdded = array_column($coursesAdded, 'id'); +} + +$sessionsAdded = $coupon['sessions']; +if (!empty($sessionsAdded)) { + $sessionsAdded = array_column($sessionsAdded, 'id'); +} + +$servicesAdded = $coupon['services']; +if (!empty($servicesAdded)) { + $servicesAdded = array_column($servicesAdded, 'id'); +} + +$formDefaults = [ + 'id' => $coupon['id'], + 'code' => $coupon['code'], + 'discount_type' => $discountTypes[$coupon['discount_type']], + 'discount_amount' => $coupon['discount_amount'], + 'date' => "$couponDateRangeFrom / $couponDateRangeTo", + 'active' => $coupon['active'], + 'courses' => $coursesAdded, + 'sessions' => $sessionsAdded, + 'services' => $servicesAdded, +]; + +$button = $form->addButtonSave(get_lang('Save')); +if (empty($currency)) { + $button->setAttribute('disabled'); +} + +$form->freeze(['code', 'discount_type', 'discount_amount']); + +if ($form->validate()) { + $formValues = $form->exportValues(); + + $coupon['id'] = $formValues['id']; + $coupon['valid_start'] = $formValues['date_start']; + $coupon['valid_end'] = $formValues['date_end']; + $coupon['active'] = $formValues['active']; + $coupon['courses'] = $formValues['courses'] ?? []; + $coupon['sessions'] = $formValues['sessions'] ?? []; + $coupon['services'] = $formValues['services'] ?? []; + + $result = $plugin->updateCouponData($coupon); + + if ($result) { + Display::addFlash( + Display::return_message( + $plugin->get_lang('CouponUpdate'), + 'success', + false + ) + ); + + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/configure_coupon.php?id='.$coupon['id']); + } else { + header('Location:'.api_get_self().'?'.$queryString); + } + + exit; +} + +$form->setDefaults($formDefaults); + +$templateName = $plugin->get_lang('ConfigureCoupon'); +$interbreadcrumb[] = [ + 'url' => 'paymentsetup.php', + 'name' => get_lang('Configuration'), +]; +$interbreadcrumb[] = [ + 'url' => 'coupons.php', + 'name' => $plugin->get_lang('CouponList'), +]; + +$template = new Template($templateName); +$template->assign('header', $templateName); +$template->assign('content', $form->returnForm()); +$template->display_one_col_template(); diff --git a/public/plugin/BuyCourses/src/configure_course.php b/public/plugin/BuyCourses/src/configure_course.php index d6956da5150..81f6fd10bb2 100644 --- a/public/plugin/BuyCourses/src/configure_course.php +++ b/public/plugin/BuyCourses/src/configure_course.php @@ -1,10 +1,10 @@ $courseItem['tax_perc'], 'beneficiaries' => $defaultBeneficiaries, ]; - - if ('true' == $commissionsEnable) { - $formDefaults['commissions'] = $commissions; - } } else { $formDefaults = [ 'product_type' => get_lang('Course'), @@ -117,10 +113,9 @@ 'tax_perc' => 0, 'beneficiaries' => [], ]; - - if ('true' == $commissionsEnable) { - $formDefaults['commissions'] = $commissions; - } + } + if ('true' == $commissionsEnable) { + $formDefaults['commissions'] = $commissions; } } elseif ($editingSession) { if (!$includeSession) { @@ -276,10 +271,10 @@
'.Display::return_message( - sprintf($plugin->get_lang('TheActualPlatformCommissionIsX'), $platformCommission['commission'].'%'), - 'info', - false - ).' + sprintf($plugin->get_lang('TheActualPlatformCommissionIsX'), $platformCommission['commission'].'%'), + 'info', + false + ).'
' @@ -308,7 +303,7 @@ if (!empty($productItem)) { $plugin->updateItem( [ - 'price' => floatval($formValues['price']), + 'price' => (float) $formValues['price'], 'tax_perc' => $taxPerc, ], $id, @@ -319,7 +314,7 @@ 'currency_id' => (int) $currency['id'], 'product_type' => $type, 'product_id' => $id, - 'price' => floatval($_POST['price']), + 'price' => (float) $_POST['price'], 'tax_perc' => $taxPerc, ]); $productItem['id'] = $itemId; @@ -328,7 +323,7 @@ $plugin->deleteItemBeneficiaries($productItem['id']); if (isset($formValues['beneficiaries'])) { - if ($commissionsEnable === 'true') { + if ('true' === $commissionsEnable) { $usersId = $formValues['beneficiaries']; $commissions = explode(',', $formValues['commissions']); $commissions = (count($usersId) != count($commissions)) @@ -348,10 +343,11 @@ $url = 'list.php'; - if ($type == 2) { + if (2 == $type) { $url = 'list_session.php'; } header('Location: '.api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/'.$url); + exit; } @@ -362,6 +358,7 @@ 'url' => 'paymentsetup.php', 'name' => get_lang('Configuration'), ]; + switch ($type) { case 2: $interbreadcrumb[] = [ @@ -369,7 +366,9 @@ 'name' => $plugin->get_lang('Sessions'), ]; $templateName = $plugin->get_lang('Sessions'); + break; + default: $interbreadcrumb[] = [ 'url' => 'list.php', diff --git a/public/plugin/BuyCourses/src/configure_frequency.php b/public/plugin/BuyCourses/src/configure_frequency.php new file mode 100644 index 00000000000..bea090c8cb0 --- /dev/null +++ b/public/plugin/BuyCourses/src/configure_frequency.php @@ -0,0 +1,120 @@ +selectFrequency($_GET['d']); + + if (!empty($frequency)) { + $subscriptionsItems = $plugin->getSubscriptionsItemsByDuration($_GET['d']); + + if (empty($subscriptionsItems)) { + $plugin->deleteFrequency($_GET['d']); + + Display::addFlash( + Display::return_message($plugin->get_lang('FrequencyRemoved'), 'success') + ); + } else { + Display::addFlash( + Display::return_message($plugin->get_lang('SubscriptionPeriodOnUse'), 'error') + ); + } + } else { + Display::addFlash( + Display::return_message($plugin->get_lang('FrequencyNotExits'), 'error') + ); + } + } else { + Display::addFlash( + Display::return_message($plugin->get_lang('FrequencyIncorrect'), 'error') + ); + } + + header('Location: '.api_get_self()); + + exit; + } +} + +$frequencies = $plugin->getFrequenciesList(); + +$globalSettingsParams = $plugin->getGlobalParameters(); + +$form = new FormValidator('add_frequency'); + +$form->addText('name', get_lang('Name'), false); + +$form->addElement( + 'number', + 'duration', + [$plugin->get_lang('Duration'), $plugin->get_lang('Days')], + ['step' => 1, 'placeholder' => $plugin->get_lang('SubscriptionFrequencyValueDays')] +); + +$button = $form->addButtonSave(get_lang('Save')); + +if ($form->validate()) { + $formValues = $form->getSubmitValues(); + $duration = $formValues['duration']; + $name = $formValues['name']; + + $frequency = $plugin->selectFrequency($duration); + + if (!empty($frequency)) { + $result = $plugin->updateFrequency($duration, $name); + + if (!isset($result)) { + Display::addFlash( + Display::return_message($plugin->get_lang('FrequencyNotUpdated'), 'error') + ); + } + } else { + $result = $plugin->addFrequency($duration, $name); + + if (!isset($result)) { + Display::addFlash( + Display::return_message($plugin->get_lang('FrequencyNotSaved'), 'error') + ); + } + } + + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/configure_frequency.php'); + + exit; +} + +// $form->setDefaults($formDefaults); + +$templateName = $plugin->get_lang('FrequencyAdd'); +$interbreadcrumb[] = [ + 'url' => 'subscriptions_courses.php', + 'name' => get_lang('Configuration'), +]; +$interbreadcrumb[] = [ + 'url' => 'subscriptions_courses.php', + 'name' => $plugin->get_lang('SubscriptionList'), +]; + +$template = new Template($templateName); +$template->assign('header', $templateName); +$template->assign('items_form', $form->returnForm()); +$template->assign('frequencies_list', $frequencies); + +$content = $template->fetch('BuyCourses/view/configure_frequency.tpl'); +$template->assign('content', $content); + +$template->display_one_col_template(); diff --git a/public/plugin/BuyCourses/src/configure_subscription.php b/public/plugin/BuyCourses/src/configure_subscription.php new file mode 100644 index 00000000000..452327dfc97 --- /dev/null +++ b/public/plugin/BuyCourses/src/configure_subscription.php @@ -0,0 +1,253 @@ +get('include_sessions'); + +if (isset($_GET['action'], $_GET['d'])) { + if ('delete_frequency' == $_GET['action']) { + $plugin->deleteSubscription($type, $id, $_GET['d']); + + Display::addFlash( + Display::return_message(get_lang('ItemRemoved'), 'success') + ); + + header('Location: '.api_get_self().'?'.$queryString); + + exit; + } +} + +$entityManager = Database::getManager(); +$userRepo = UserManager::getRepository(); +$currency = $plugin->getSelectedCurrency(); + +if (empty($currency)) { + Display::addFlash( + Display::return_message($plugin->get_lang('CurrencyIsNotConfigured'), 'error') + ); +} + +$subscriptions = $plugin->getSubscriptions($type, $id); + +$taxtPerc = 0; + +if (isset($subscriptions) && !empty($subscriptions)) { + $taxtPerc = $subscriptions[0]['tax_perc']; +} + +$currencyIso = null; + +if ($editingCourse) { + $course = $entityManager->find(Course::class, $id); + if (!$course) { + api_not_allowed(true); + } + + $courseItem = $plugin->getCourseForConfiguration($course, $currency); + + $currencyIso = $courseItem['currency']; + $formDefaults = [ + 'product_type' => get_lang('Course'), + 'id' => $courseItem['course_id'], + 'type' => BuyCoursesPlugin::PRODUCT_TYPE_COURSE, + 'name' => $courseItem['course_title'], + 'visible' => $courseItem['visible'], + 'tax_perc' => $taxtPerc, + ]; +} elseif ($editingSession) { + if (!$includeSession) { + api_not_allowed(true); + } + + $session = $entityManager->find(Session::class, $id); + if (!$session) { + api_not_allowed(true); + } + + $sessionItem = $plugin->getSessionForConfiguration($session, $currency); + + $currencyIso = $sessionItem['currency']; + $formDefaults = [ + 'product_type' => get_lang('Session'), + 'id' => $session->getId(), + 'type' => BuyCoursesPlugin::PRODUCT_TYPE_SESSION, + 'name' => $sessionItem['session_name'], + 'visible' => $sessionItem['visible'], + 'tax_perc' => $taxtPerc, + ]; +} else { + api_not_allowed(true); +} + +$globalSettingsParams = $plugin->getGlobalParameters(); + +$form = new FormValidator('add_subscription'); + +$form->addText('product_type', $plugin->get_lang('ProductType'), false); +$form->addText('name', get_lang('Name'), false); + +$form->freeze(['product_type', 'name']); + +$form->addElement( + 'number', + 'tax_perc', + [$plugin->get_lang('TaxPerc'), $plugin->get_lang('TaxPercDescription'), '%'], + ['step' => 1, 'placeholder' => $globalSettingsParams['global_tax_perc'].'% '.$plugin->get_lang('ByDefault')] +); + +$frequenciesOptions = $plugin->getFrequencies(); + +$frequencyForm = new FormValidator('frequency_config', 'post', api_get_self().'?'.$queryString); + +$frequencyFormDefaults = [ + 'id' => $id, + 'type' => $type, + 'tax_perc' => $taxtPerc, + 'currency_id' => $currency['id'], +]; + +$frequencyForm->setDefaults($frequencyFormDefaults); + +if ($frequencyForm->validate()) { + $frequencyFormValues = $frequencyForm->getSubmitValues(); + + $subscription['product_id'] = $frequencyFormValues['id']; + $subscription['product_type'] = $frequencyFormValues['type']; + $subscription['tax_perc'] = '' != $frequencyFormValues['tax_perc'] ? (int) $frequencyFormValues['tax_perc'] : null; + $subscription['currency_id'] = $currency['id']; + $duration = $frequencyFormValues['duration']; + $price = $frequencyFormValues['price']; + + for ($i = 0; $i <= count($subscriptions); $i++) { + if ($duration == $subscriptions[$i]['duration']) { + Display::addFlash( + Display::return_message($plugin->get_lang('SubscriptionAlreadyExists'), 'error') + ); + + header('Location:'.api_get_self().'?'.$queryString); + + exit; + } + } + + $subscription['frequencies'] = [['duration' => $duration, 'price' => $price]]; + + $result = $plugin->addNewSubscription($subscription); + + Display::addFlash( + Display::return_message(get_lang('Saved'), 'success') + ); + + header('Location:'.api_get_self().'?'.$queryString); + + exit; +} + +$frequencyForm->addElement( + 'select', + 'duration', + $plugin->get_lang('Duration'), + $frequenciesOptions, + ['cols-size' => [2, 8, 2]] +); + +$frequencyForm->addElement( + 'number', + 'price', + [$plugin->get_lang('Price'), null, $currencyIso], + false, + [ + 'step' => 1, + 'cols-size' => [3, 8, 1], + ] +); + +$frequencyForm->addHidden('type', $type); +$frequencyForm->addHidden('id', $id); +$frequencyForm->addHidden('tax_perc', $taxtPerc); +$frequencyForm->addHidden('currency_id', $currency['id']); +$frequencyForm->addButtonCreate('Add'); + +for ($i = 0; $i < count($subscriptions); $i++) { + if ($subscriptions[$i]['duration'] > 0) { + $subscriptions[$i]['durationName'] = $frequenciesOptions[$subscriptions[$i]['duration']]; + } +} + +$form->addHidden('type', $type); +$form->addHidden('id', $id); +$button = $form->addButtonSave(get_lang('Save')); + +if (empty($currency)) { + $button->setAttribute('disabled'); +} + +if ($form->validate()) { + $formValues = $form->getSubmitValues(); + $id = $formValues['id']; + $type = $formValues['type']; + $taxPerc = '' != $formValues['tax_perc'] ? (int) $formValues['tax_perc'] : null; + + $result = $plugin->updateSubscription($type, $id, $taxPerc); + + if ($result) { + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/subscriptions_courses.php'); + } else { + header('Location:'.api_get_self().'?'.$queryString); + } + + exit; +} + +$form->setDefaults($formDefaults); + +$templateName = $plugin->get_lang('SubscriptionAdd'); +$interbreadcrumb[] = [ + 'url' => 'subscriptions_courses.php', + 'name' => get_lang('Configuration'), +]; +$interbreadcrumb[] = [ + 'url' => 'subscriptions_courses.php', + 'name' => $plugin->get_lang('SubscriptionList'), +]; + +$template = new Template($templateName); +$template->assign('header', $templateName); +$template->assign('items_form', $form->returnForm()); +$template->assign('frequency_form', $frequencyForm->returnForm()); +$template->assign('subscriptions', $subscriptions); +$template->assign('currencyIso', $currencyIso); + +$content = $template->fetch('BuyCourses/view/configure_subscription.tpl'); +$template->assign('content', $content); + +$template->display_one_col_template(); diff --git a/public/plugin/BuyCourses/src/coupon_add.php b/public/plugin/BuyCourses/src/coupon_add.php new file mode 100644 index 00000000000..7b0e117e168 --- /dev/null +++ b/public/plugin/BuyCourses/src/coupon_add.php @@ -0,0 +1,165 @@ +get('include_sessions'); +$includeServices = 'true' === $plugin->get('include_services'); + +$currency = $plugin->getSelectedCurrency(); + +if (empty($currency)) { + Display::addFlash( + Display::return_message($plugin->get_lang('CurrencyIsNotConfigured'), 'error') + ); +} + +$currencyIso = null; + +$coursesList = CourseManager::get_courses_list( + 0, + 0, + 'title', + 'asc', + -1, + null, + api_get_current_access_url_id(), + false, + [], + [] +); + +foreach ($coursesList as $course) { + $courses[$course['id']] = $course['title']; +} + +$sessionsList = SessionManager::get_sessions_list( + [], + [], + null, + null, + api_get_current_access_url_id(), + [] +); + +foreach ($sessionsList as $session) { + $sessions[$session['id']] = $session['name']; +} + +$servicesList = $plugin->getAllServices(); + +foreach ($servicesList as $service) { + $services[$service['id']] = $service['name']; +} + +$discountTypes = $plugin->getCouponDiscountTypes(); + +// Build the form +$form = new FormValidator('add_coupon'); +$form->addText('code', $plugin->get_lang('CouponCode'), true); +$form->addRadio('discount_type', $plugin->get_lang('CouponDiscountType'), $discountTypes); +$form->addElement( + 'number', + 'discount_amount', + [$plugin->get_lang('CouponDiscount'), null, $currencyIso], + ['step' => 1] +); +$form->addDateRangePicker('date', get_lang('Date'), true); +$form->addCheckBox('active', get_lang('Active')); +$form->addElement( + 'advmultiselect', + 'courses', + get_lang('Courses'), + $courses +); + +if ($includeSession) { + $form->addElement( + 'advmultiselect', + 'sessions', + get_lang('Sessions'), + $sessions + ); +} + +if ($includeServices) { + $form->addElement( + 'advmultiselect', + 'services', + get_lang('Services'), + $services + ); +} + +$button = $form->addButtonSave(get_lang('Save')); + +if (empty($currency)) { + $button->setAttribute('disabled'); +} + +if ($form->validate()) { + $formValues = $form->exportValues(); + + $coupon['code'] = $formValues['code']; + $coupon['discount_type'] = $formValues['discount_type']; + $coupon['discount_amount'] = $formValues['discount_amount']; + $coupon['valid_start'] = $formValues['date_start']; + $coupon['valid_end'] = $formValues['date_end']; + $coupon['active'] = $formValues['active']; + + if (BuyCoursesPlugin::COUPON_DISCOUNT_TYPE_PERCENTAGE == $coupon['discount_type'] && $coupon['discount_amount'] > 100) { + Display::addFlash( + Display::return_message($plugin->get_lang('CouponDiscountExceed100'), 'error', false) + ); + } + + $coupon['courses'] = $formValues['courses'] ?? []; + $coupon['sessions'] = $formValues['sessions'] ?? []; + $coupon['services'] = $formValues['services'] ?? []; + + $result = $plugin->addNewCoupon($coupon); + + if ($result) { + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/coupons.php'); + } else { + header('Location:'.api_get_self().'?'.$queryString); + } + + exit; +} + +$formDefaults = [ + 'code' => '', + 'discount_type' => null, + 'discount_amount' => 0, + 'active' => 0, + 'courses' => [], + 'sessions' => [], + 'services' => [], +]; + +$form->setDefaults($formDefaults); + +$templateName = $plugin->get_lang('CouponAdd'); +$interbreadcrumb[] = [ + 'url' => 'paymentsetup.php', + 'name' => get_lang('Configuration'), +]; +$interbreadcrumb[] = [ + 'url' => 'coupons.php', + 'name' => $plugin->get_lang('CouponList'), +]; + +$template = new Template($templateName); +$template->assign('header', $templateName); +$template->assign('content', $form->returnForm()); +$template->display_one_col_template(); diff --git a/public/plugin/BuyCourses/src/coupons.php b/public/plugin/BuyCourses/src/coupons.php new file mode 100644 index 00000000000..b8af5a8e2f2 --- /dev/null +++ b/public/plugin/BuyCourses/src/coupons.php @@ -0,0 +1,111 @@ +getCouponInfo($_GET['coupon_id']); + + if (empty($coupon)) { + api_not_allowed(true); + } + + $urlToRedirect = api_get_self().'?'; + + switch ($_GET['action']) { + case 'deactivate': + // activate coupon + break; + + case 'activate': + // deactivate coupon + break; + } + + header("Location: $urlToRedirect"); + + exit; +} + +$discountTypes = $plugin->getCouponDiscountTypes(); +$couponStatuses = $plugin->getCouponStatuses(); + +$selectedFilterType = '0'; +$selectedStatus = $_GET['status'] ?? BuyCoursesPlugin::COUPON_STATUS_ACTIVE; + +$form = new FormValidator('search', 'get'); + +if ($form->validate()) { + $selectedStatus = $form->getSubmitValue('status'); + + if (false === $selectedStatus) { + $selectedStatus = BuyCoursesPlugin::COUPON_STATUS_ACTIVE; + } + + if (false === $selectedFilterType) { + $selectedFilterType = '0'; + } +} + +$form->addHtml('
'); +$form->addSelect('status', $plugin->get_lang('CouponStatus'), $couponStatuses); +$form->addHtml('
'); +$form->addButtonFilter(get_lang('Search')); +$form->setDefaults([ + 'filter_type' => $selectedFilterType, + 'status' => $selectedStatus, +]); + +$coupons = $plugin->getCouponsListByStatus($selectedStatus); +$currency = $plugin->getSelectedCurrency(); + +foreach ($coupons as &$coupon) { + if (BuyCoursesPlugin::COUPON_DISCOUNT_TYPE_PERCENTAGE == $coupon['discount_type']) { + $coupon['discount_value'] = $coupon['discount_amount'].' %'; + } elseif (BuyCoursesPlugin::COUPON_DISCOUNT_TYPE_AMOUNT == $coupon['discount_type']) { + $coupon['discount_value'] = $plugin->getPriceWithCurrencyFromIsoCode($coupon['discount_amount'], $currency['iso_code']); + } + $coupon['discount_type'] = $discountTypes[$coupon['discount_type']]; +} + +$interbreadcrumb[] = ['url' => '../index.php', 'name' => $plugin->get_lang('plugin_title')]; + +$htmlHeadXtra[] = api_get_css(api_get_path(WEB_PLUGIN_PATH).'BuyCourses/resources/css/style.css'); + +$templateName = $plugin->get_lang('CouponList'); +$template = new Template($templateName); + +$toolbar = Display::url( + Display::returnFontAwesomeIcon('fas fa-plus'). + $plugin->get_lang('CouponAdd'), + api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/coupon_add.php', + ['class' => 'btn btn-primary'] +); + +$template->assign( + 'actions', + Display::toolbarAction('toolbar', [$toolbar]) +); + +$template->assign('form', $form->returnForm()); +$template->assign('selected_status', $selectedStatus); +$template->assign('coupon_list', $coupons); +$template->assign('coupon_status_active', BuyCoursesPlugin::COUPON_STATUS_ACTIVE); +$template->assign('coupon_status_disable', BuyCoursesPlugin::COUPON_STATUS_DISABLE); + +$content = $template->fetch('BuyCourses/view/coupons.tpl'); + +$template->assign('header', $templateName); +$template->assign('content', $content); +$template->display_one_col_template(); diff --git a/public/plugin/BuyCourses/src/course_catalog.php b/public/plugin/BuyCourses/src/course_catalog.php index 8d827ccad4f..d26bd859b34 100644 --- a/public/plugin/BuyCourses/src/course_catalog.php +++ b/public/plugin/BuyCourses/src/course_catalog.php @@ -1,4 +1,6 @@ getCatalogCourseList($first, $pageSize, $nameFilter, $minFilter, $maxFilter, 'count'); $pagesCount = ceil($totalItems / $pageSize); -$url = api_get_self().'?'; -$pagination = Display::getPagination($url, $currentPage, $pagesCount, $totalItems); +$pagination = Display::getPagination(api_get_self().'?', $currentPage, $pagesCount, $totalItems); // View if (api_is_platform_admin()) { @@ -72,7 +73,7 @@ } else { $interbreadcrumb[] = [ 'url' => 'course_panel.php', - 'name' => get_lang('Dashboard'), + 'name' => get_lang('TabsDashboard'), ]; } diff --git a/public/plugin/BuyCourses/src/course_panel.php b/public/plugin/BuyCourses/src/course_panel.php index b98085c21df..87209319ed9 100644 --- a/public/plugin/BuyCourses/src/course_panel.php +++ b/public/plugin/BuyCourses/src/course_panel.php @@ -1,4 +1,6 @@ $plugin->get_lang('CourseListOnSale')] ); -$templateName = get_lang('Dashboard'); +$htmlHeadXtra[] = api_get_css(api_get_path(WEB_PLUGIN_PATH).'BuyCourses/resources/css/style.css'); + +$templateName = get_lang('TabsDashboard'); $tpl = new Template($templateName); $tpl->assign('showing_courses', true); $tpl->assign('sessions_are_included', $includeSessions); diff --git a/public/plugin/BuyCourses/src/error.php b/public/plugin/BuyCourses/src/error.php index 290274166f5..2cc57a1117e 100644 --- a/public/plugin/BuyCourses/src/error.php +++ b/public/plugin/BuyCourses/src/error.php @@ -1,12 +1,15 @@ addDatePicker('date_start', get_lang('Start date'), false); -$form->addDatePicker('date_end', get_lang('End date'), false); -$form->addButton('export_sales', get_lang('Excel export'), 'check', 'primary'); +$form->addDatePicker('date_start', get_lang('DateStart'), false); +$form->addDatePicker('date_end', get_lang('DateEnd'), false); +$form->addButton('export_sales', get_lang('ExportExcel'), 'check', 'primary'); $salesStatus = []; if ($form->validate()) { @@ -51,7 +50,7 @@ $templateName = $plugin->get_lang('ExportReport'); $toolbar = Display::url( - Display::getMdiIcon(ActionIcon::BACK, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Back')), + Display::return_icon('back.png', get_lang('GoBack'), [], ICON_SIZE_MEDIUM), api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/sales_report.php' ); $template = new Template($templateName); diff --git a/public/plugin/BuyCourses/src/export_subscription_report.php b/public/plugin/BuyCourses/src/export_subscription_report.php new file mode 100644 index 00000000000..55052efd031 --- /dev/null +++ b/public/plugin/BuyCourses/src/export_subscription_report.php @@ -0,0 +1,65 @@ +addDatePicker('date_start', get_lang('DateStart'), false); +$form->addDatePicker('date_end', get_lang('DateEnd'), false); +$form->addButton('export_sales', get_lang('ExportExcel'), 'check', 'primary'); +$salesStatus = []; + +if ($form->validate()) { + $reportValues = $form->getSubmitValues(); + + $dateStart = $reportValues['date_start']; + $dateEnd = $reportValues['date_end']; + + if (null == $dateStart || null == $dateEnd) { + Display::addFlash( + Display::return_message($plugin->get_lang('SelectDateRange'), 'error', false) + ); + } elseif ($dateStart > $dateEnd) { + Display::addFlash( + Display::return_message(get_lang('EndDateCannotBeBeforeTheStartDate'), 'error', false) + ); + } else { + $salesStatus = $plugin->getSubscriptionSaleListReport($dateStart, $dateEnd); + } +} + +if (!empty($salesStatus)) { + $archiveFile = 'export_report_sales_'.api_get_local_time(); + Export::arrayToXls($salesStatus, $archiveFile); +} +$interbreadcrumb[] = [ + 'url' => '../index.php', 'name' => $plugin->get_lang('plugin_title'), +]; +$interbreadcrumb[] = [ + 'url' => api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/subscription_sales_report.php', + 'name' => $plugin->get_lang('SubscriptionSalesReport'), +]; + +$templateName = $plugin->get_lang('ExportReport'); +$toolbar = Display::url( + Display::return_icon('back.png', get_lang('GoBack'), [], ICON_SIZE_MEDIUM), + api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/subscription_sales_report.php' +); +$template = new Template($templateName); +$template->assign( + 'actions', + Display::toolbarAction('toolbar', [$toolbar]) +); +$template->assign('form', $form->returnForm()); +$content = $template->fetch('BuyCourses/view/export_report.tpl'); +$template->assign('header', $templateName); +$template->assign('content', $content); +$template->display_one_col_template(); diff --git a/public/plugin/BuyCourses/src/expresscheckout.php b/public/plugin/BuyCourses/src/expresscheckout.php index e98dff6067e..95d0f12a575 100644 --- a/public/plugin/BuyCourses/src/expresscheckout.php +++ b/public/plugin/BuyCourses/src/expresscheckout.php @@ -1,4 +1,7 @@ get('unregistered_users_enable'); +$userIsAdmin = api_is_platform_admin(); + if (('true' === $allow && api_is_anonymous()) || !api_is_anonymous()) { + $webPluginPath = api_get_path(WEB_PLUGIN_PATH).'BuyCourses/'; + + $countCourses = $plugin->getCatalogCourseList( + 0, + BuyCoursesPlugin::PAGINATION_PAGE_SIZE, + null, + 0, + 0, + 'count' + ); + + if ($countCourses > 0 && !$userIsAdmin) { + api_location($webPluginPath.'src/course_catalog.php'); + } + + $countSessions = $plugin->getCatalogSessionList( + 0, + BuyCoursesPlugin::PAGINATION_PAGE_SIZE, + null, + 0, + 0, + 'count' + ); + + if ($countSessions > 0 && !$userIsAdmin) { + api_location($webPluginPath.'src/session_catalog.php'); + } + + $htmlHeadXtra[] = api_get_css(api_get_path(WEB_PLUGIN_PATH).'BuyCourses/resources/css/style.css'); + $tpl = new Template(); $content = $tpl->fetch('BuyCourses/view/index.tpl'); $tpl->assign('content', $content); - $tpl->display_one_col_template(); + $tpl->display_one_col_template(false); } diff --git a/public/plugin/BuyCourses/src/index.php b/public/plugin/BuyCourses/src/index.php index b3d9bbc7f37..174d7fd709f 100644 --- a/public/plugin/BuyCourses/src/index.php +++ b/public/plugin/BuyCourses/src/index.php @@ -1 +1,3 @@ getDataInvoice($saleId, $isService); $taxAppliesTo = $globalParameters['tax_applies_to']; -$taxEnable = 'true' === $plugin->get('tax_enable') && - (BuyCoursesPlugin::TAX_APPLIES_TO_ALL == $taxAppliesTo || - (BuyCoursesPlugin::TAX_APPLIES_TO_ONLY_COURSE == $taxAppliesTo && !$isService) || - (BuyCoursesPlugin::TAX_APPLIES_TO_ONLY_SESSION == $taxAppliesTo && $isService)); +$taxEnable = 'true' === $plugin->get('tax_enable') + && (BuyCoursesPlugin::TAX_APPLIES_TO_ALL == $taxAppliesTo + || (BuyCoursesPlugin::TAX_APPLIES_TO_ONLY_COURSE == $taxAppliesTo && !$isService) + || (BuyCoursesPlugin::TAX_APPLIES_TO_ONLY_SESSION == $taxAppliesTo && $isService)); $htmlText = ''; $htmlText .= ''; $htmlText .= ''; $htmlText .= ''; +$organization = ChamiloApi::getPlatformLogo('', [], true); +// Use custom logo image. +$pdfLogo = api_get_setting('pdf_logo_header'); +if ('true' === $pdfLogo) { + $visualTheme = api_get_visual_theme(); + $img = api_get_path(SYS_CSS_PATH).'themes/'.$visualTheme.'/images/pdf_logo_header.png'; + if (file_exists($img)) { + $organization = ""; + } +} +$htmlText .= $organization; + // Seller and customer info $htmlText .= ''; $htmlText .= ''; @@ -82,7 +98,7 @@ $infoSale['product_name'], ]; -//var_dump($infoSale);exit; +// var_dump($infoSale);exit; $isoCode = $plugin->getCurrency($infoSale['currency_id'])['iso_code']; if ($taxEnable) { @@ -120,7 +136,7 @@ } $data[] = $row; $attr = []; -$attr['class'] = 'table data_table'; +$attr['class'] = 'table table-hover table-striped data_table'; $attr['width'] = '100%'; $htmlText .= Display::table($header, $data, $attr); $htmlText .= ''; @@ -135,5 +151,6 @@ 'orientation' => 'P', ]; $pdf = new PDF($params['format'], $params['orientation'], $params); -@$pdf->content_to_pdf($htmlText, null, $fileName, null, 'D', false, null, false, false, false); +@$pdf->content_to_pdf($htmlText, '', $fileName, null, 'D', false, null, false, false, false); + exit; diff --git a/public/plugin/BuyCourses/src/list.php b/public/plugin/BuyCourses/src/list.php index 495431d38d0..9d6271b53d3 100644 --- a/public/plugin/BuyCourses/src/list.php +++ b/public/plugin/BuyCourses/src/list.php @@ -1,8 +1,11 @@ getCourseList($first, $pageSize); +$qb = $plugin->getCourses($first, $pageSize); $query = $qb->getQuery(); -$courses = new Paginator($query, true); +$courses = new Paginator($query, $fetchJoinCollection = true); foreach ($courses as $course) { $item = $plugin->getItemByProduct($course->getId(), BuyCoursesPlugin::PRODUCT_TYPE_COURSE); $course->buyCourseData = []; - if (!empty($item)) { + if (false !== $item) { $course->buyCourseData = $item; } } $totalItems = count($courses); -$pagesCount = ceil($totalItems / $pageSize); +$pagesCount = (int) ceil($totalItems / $pageSize); $pagination = BuyCoursesPlugin::returnPagination( api_get_self(), @@ -58,15 +61,13 @@ $templateName = $plugin->get_lang('AvailableCourses'); +$htmlHeadXtra[] = api_get_css(api_get_path(WEB_PLUGIN_PATH).'BuyCourses/resources/css/style.css'); + $tpl = new Template($templateName); $tpl->assign('product_type_course', BuyCoursesPlugin::PRODUCT_TYPE_COURSE); $tpl->assign('product_type_session', BuyCoursesPlugin::PRODUCT_TYPE_SESSION); $tpl->assign('courses', $courses); -$tpl->assign('sessions', []); -$tpl->assign('services', []); -$tpl->assign('session_pagination', ''); -$tpl->assign('service_pagination', ''); $tpl->assign('course_pagination', $pagination); $tpl->assign('sessions_are_included', $includeSession); $tpl->assign('services_are_included', $includeServices); diff --git a/public/plugin/BuyCourses/src/list_coupon.php b/public/plugin/BuyCourses/src/list_coupon.php new file mode 100644 index 00000000000..473cd79cda1 --- /dev/null +++ b/public/plugin/BuyCourses/src/list_coupon.php @@ -0,0 +1,98 @@ +getCouponInfo($_GET['coupon_id']); + + if (empty($coupon)) { + api_not_allowed(true); + } + + $urlToRedirect = api_get_self().'?'; + + switch ($_GET['action']) { + case 'deactivate': + // activate coupon + break; + + case 'activate': + // deactivate coupon + break; + } + + header("Location: $urlToRedirect"); + + exit; +} + +$discountTypes = $plugin->getCouponDiscountTypes(); +$couponStatuses = $plugin->getCouponStatuses(); + +$selectedFilterType = '0'; +$selectedStatus = isset($_GET['status']) ? $_GET['status'] : BuyCoursesPlugin::COUPON_STATUS_ACTIVE; + +$form = new FormValidator('search', 'get'); + +if ($form->validate()) { + $selectedStatus = $form->getSubmitValue('status'); + + if (false === $selectedStatus) { + $selectedStatus = BuyCoursesPlugin::COUPON_STATUS_ACTIVE; + } + + if (false === $selectedFilterType) { + $selectedFilterType = '0'; + } +} + +$form->addHtml('
'); +$form->addSelect('status', $plugin->get_lang('CouponStatus'), $couponStatuses); +$form->addHtml('
'); +$form->addButtonFilter(get_lang('Search')); +$form->setDefaults([ + 'filter_type' => $selectedFilterType, + 'status' => $selectedStatus, +]); + +$coupons = $plugin->getCouponsListByStatus($selectedStatus); + +foreach ($coupons as &$coupon) { + if (BuyCoursesPlugin::COUPON_DISCOUNT_TYPE_PERCENTAGE == $coupon['discount_type']) { + $coupon['discount_value'] = $coupon['discount_amount'].' %'; + } elseif (BuyCoursesPlugin::COUPON_DISCOUNT_TYPE_AMOUNT == $coupon['discount_type']) { + $coupon['discount_value'] = $plugin->getPriceWithCurrencyFromIsoCode($coupon['discount_amount'], $coupon['iso_code']); + } + $coupon['discount_type'] = $discountTypes[$coupon['discount_type']]; +} + +$interbreadcrumb[] = ['url' => '../index.php', 'name' => $plugin->get_lang('plugin_title')]; + +$htmlHeadXtra[] = api_get_css(api_get_path(WEB_PLUGIN_PATH).'BuyCourses/resources/css/style.css'); + +$templateName = $plugin->get_lang('CouponList'); +$template = new Template($templateName); + +$template->assign('form', $form->returnForm()); +$template->assign('selected_status', $selectedStatus); +$template->assign('coupon_list', $coupons); +$template->assign('coupon_status_active', BuyCoursesPlugin::COUPON_STATUS_ACTIVE); +$template->assign('coupon_status_disable', BuyCoursesPlugin::COUPON_STATUS_DISABLE); + +$content = $template->fetch('BuyCourses/view/list_coupon.tpl'); + +$template->assign('header', $templateName); +$template->assign('content', $content); +$template->display_one_col_template(); diff --git a/public/plugin/BuyCourses/src/list_service.php b/public/plugin/BuyCourses/src/list_service.php index 65879f1f4b6..4f9e3262ea2 100644 --- a/public/plugin/BuyCourses/src/list_service.php +++ b/public/plugin/BuyCourses/src/list_service.php @@ -1,4 +1,7 @@ getServices($first, $pageSize); -$totalItems = $plugin->getServices(null, null, 'count'); +$totalItems = $plugin->getServices(0, 1000000000, 'count'); $pagesCount = ceil($totalItems / $pageSize); -$url = api_get_self().'?'; -$pagination = Display::getPagination($url, $currentPage, $pagesCount, $totalItems); +$pagination = BuyCoursesPlugin::returnPagination(api_get_self(), $currentPage, $pagesCount, $totalItems); // breadcrumbs $interbreadcrumb[] = [ @@ -42,7 +44,9 @@ 'name' => $plugin->get_lang('plugin_title'), ]; -$templateName = $plugin->get_lang('AvailableCourses'); +$templateName = $plugin->get_lang('Services'); + +$htmlHeadXtra[] = api_get_css(api_get_path(WEB_PLUGIN_PATH).'BuyCourses/resources/css/style.css'); $tpl = new Template($templateName); @@ -51,12 +55,8 @@ $tpl->assign('sessions_are_included', $includeSession); $tpl->assign('services_are_included', $includeServices); $tpl->assign('tax_enable', $taxEnable); -$tpl->assign('courses', []); -$tpl->assign('sessions', []); $tpl->assign('services', $services); $tpl->assign('service_pagination', $pagination); -$tpl->assign('course_pagination', ''); -$tpl->assign('session_pagination', ''); if ($taxEnable) { $globalParameters = $plugin->getGlobalParameters(); diff --git a/public/plugin/BuyCourses/src/list_session.php b/public/plugin/BuyCourses/src/list_session.php index bc3ca795f30..eeba01e484b 100644 --- a/public/plugin/BuyCourses/src/list_session.php +++ b/public/plugin/BuyCourses/src/list_session.php @@ -1,6 +1,8 @@ $plugin->get_lang('plugin_title'), ]; -$templateName = $plugin->get_lang('AvailableCourses'); +$htmlHeadXtra[] = api_get_css(api_get_path(WEB_PLUGIN_PATH).'BuyCourses/resources/css/style.css'); + +$templateName = $plugin->get_lang('Sessions'); $tpl = new Template($templateName); @@ -48,11 +52,11 @@ $tpl->assign('tax_enable', $taxEnable); $query = CoursesAndSessionsCatalog::browseSessions(null, ['start' => $first, 'length' => $pageSize], true); -$sessions = new Paginator($query, true); +$sessions = new Paginator($query, $fetchJoinCollection = true); foreach ($sessions as $session) { $item = $plugin->getItemByProduct($session->getId(), BuyCoursesPlugin::PRODUCT_TYPE_SESSION); $session->buyCourseData = []; - if (!empty($item)) { + if (false !== $item) { $session->buyCourseData = $item; } } @@ -60,15 +64,16 @@ $totalItems = count($sessions); $pagesCount = ceil($totalItems / $pageSize); -$url = api_get_self().'?type='.BuyCoursesPlugin::PRODUCT_TYPE_SESSION; -$pagination = Display::getPagination($url, $currentPage, $pagesCount, $totalItems); +$pagination = BuyCoursesPlugin::returnPagination( + api_get_self(), + $currentPage, + $pagesCount, + $totalItems, + ['type' => BuyCoursesPlugin::PRODUCT_TYPE_SESSION] +); -$tpl->assign('courses', []); -$tpl->assign('services', []); $tpl->assign('sessions', $sessions); $tpl->assign('session_pagination', $pagination); -$tpl->assign('course_pagination', ''); -$tpl->assign('service_pagination', ''); if ($taxEnable) { $globalParameters = $plugin->getGlobalParameters(); diff --git a/public/plugin/BuyCourses/src/panel.ajax.php b/public/plugin/BuyCourses/src/panel.ajax.php index 06355551fa3..516ca0bf6af 100644 --- a/public/plugin/BuyCourses/src/panel.ajax.php +++ b/public/plugin/BuyCourses/src/panel.ajax.php @@ -1,4 +1,6 @@ getSale($saleId); $productType = (1 == $sale['product_type']) ? get_lang('Course') : get_lang('Session'); @@ -32,8 +34,7 @@ $productImage = $productInfo['course_image_large']; } else { $productImage = ($productInfo['image']) - ? $productInfo['image'] - : Template::get_icon_path('session_default.png'); + ?: Template::get_icon_path('session_default.png'); } $userInfo = api_get_user_info($sale['user_id']); @@ -56,7 +57,9 @@ $html .= ''; $html .= ''; echo $html; + break; + case 'stats': $stats = []; $stats['completed_count'] = 0; @@ -94,16 +97,16 @@

  • - '.get_plugin_lang("PayoutsTotalCompleted", "BuyCoursesPlugin").' - '.$stats['completed_count'].' - '.get_plugin_lang("TotalAmount", "BuyCoursesPlugin").' + '.get_plugin_lang('PayoutsTotalCompleted', 'BuyCoursesPlugin').' + '.$stats['completed_count'].' - '.get_plugin_lang('TotalAmount', 'BuyCoursesPlugin').' '.$stats['completed_total_amount'].' '.$currency['iso_code'].'
  • -
  • '.get_plugin_lang("PayoutsTotalPending", "BuyCoursesPlugin").' - '.$stats['pending_count'].' - '.get_plugin_lang("TotalAmount", "BuyCoursesPlugin").' +
  • '.get_plugin_lang('PayoutsTotalPending', 'BuyCoursesPlugin').' + '.$stats['pending_count'].' - '.get_plugin_lang('TotalAmount', 'BuyCoursesPlugin').' '.$stats['pending_total_amount'].' '.$currency['iso_code'].'
  • -
  • '.get_plugin_lang("PayoutsTotalCanceled", "BuyCoursesPlugin").' - '.$stats['canceled_count'].' - '.get_plugin_lang("TotalAmount", "BuyCoursesPlugin").' +
  • '.get_plugin_lang('PayoutsTotalCanceled', 'BuyCoursesPlugin').' + '.$stats['canceled_count'].' - '.get_plugin_lang('TotalAmount', 'BuyCoursesPlugin').' '.$stats['canceled_total_amount'].' '.$currency['iso_code'].'
@@ -111,7 +114,9 @@ '; echo $html; + break; + case 'processPayout': if (api_is_anonymous()) { break; @@ -124,7 +129,7 @@ $payouts = isset($_POST['payouts']) ? $_POST['payouts'] : ''; if (!$payouts) { - echo Display::return_message(get_plugin_lang("SelectOptionToProceed", "BuyCoursesPlugin"), 'error', false); + echo Display::return_message(get_plugin_lang('SelectOptionToProceed', 'BuyCoursesPlugin'), 'error', false); break; } @@ -142,20 +147,21 @@ $isoCode = $currentCurrency['iso_code']; - $html .= '

'.get_plugin_lang("VerifyTotalAmountToProceedPayout", "BuyCoursesPlugin").'

'; + $html .= '

'.get_plugin_lang('VerifyTotalAmountToProceedPayout', 'BuyCoursesPlugin').'

'; $html .= '

    -
  • '.get_plugin_lang("TotalAcounts", "BuyCoursesPlugin").' '.$totalAccounts.'
  • -
  • '.get_plugin_lang("TotalPayout", "BuyCoursesPlugin").' '.$isoCode.' '.$totalPayout.'
  • +
  • '.get_plugin_lang('TotalAcounts', 'BuyCoursesPlugin').' '.$totalAccounts.'
  • +
  • '.get_plugin_lang('TotalPayout', 'BuyCoursesPlugin').' '.$isoCode.' '.$totalPayout.'

-

'.get_plugin_lang("CautionThisProcessCantBeCanceled", "BuyCoursesPlugin").'

+

'.get_plugin_lang('CautionThisProcessCantBeCanceled', 'BuyCoursesPlugin').'



'; echo $html; + break; case 'proceedPayout': @@ -168,7 +174,8 @@ $paypalUsername = $paypalParams['username']; $paypalPassword = $paypalParams['password']; $paypalSignature = $paypalParams['signature']; - require_once "paypalfunctions.php"; + + require_once 'paypalfunctions.php'; $allPayouts = []; $totalAccounts = 0; $totalPayout = 0; @@ -176,7 +183,7 @@ $payouts = isset($_POST['payouts']) ? $_POST['payouts'] : ''; if (!$payouts) { - echo Display::return_message(get_plugin_lang("SelectOptionToProceed", "BuyCoursesPlugin"), 'error', false); + echo Display::return_message(get_plugin_lang('SelectOptionToProceed', 'BuyCoursesPlugin'), 'error', false); break; } @@ -195,7 +202,7 @@ foreach ($allPayouts as $payout) { $plugin->setStatusPayouts($payout['id'], BuyCoursesPlugin::PAYOUT_STATUS_COMPLETED); } - echo Display::return_message(get_plugin_lang("PayoutSuccess", "BuyCoursesPlugin"), 'success', false); + echo Display::return_message(get_plugin_lang('PayoutSuccess', 'BuyCoursesPlugin'), 'success', false); } else { echo Display::return_message( ''.$result['L_SEVERITYCODE0'].' '.$result['L_ERRORCODE0'].' - ' @@ -204,6 +211,7 @@ false ); } + break; case 'cancelPayout': @@ -214,6 +222,8 @@ $payoutId = isset($_POST['id']) ? $_POST['id'] : ''; $plugin->setStatusPayouts($payoutId, BuyCoursesPlugin::PAYOUT_STATUS_CANCELED); echo ''; + break; } + exit; diff --git a/public/plugin/BuyCourses/src/paymentsetup.php b/public/plugin/BuyCourses/src/paymentsetup.php index 6b7699524e3..10b25bb7d58 100644 --- a/public/plugin/BuyCourses/src/paymentsetup.php +++ b/public/plugin/BuyCourses/src/paymentsetup.php @@ -1,4 +1,6 @@ get('paypal_enable'); $transferEnable = $plugin->get('transfer_enable'); +$tpvRedsysEnable = $plugin->get('tpv_redsys_enable'); $commissionsEnable = $plugin->get('commissions_enable'); $culqiEnable = $plugin->get('culqi_enable'); +$stripeEnable = 'true' === $plugin->get('stripe_enable'); +$cecabankEnable = 'true' === $plugin->get('cecabank_enable'); if (isset($_GET['action'], $_GET['id'])) { if ('delete_taccount' == $_GET['action']) { $plugin->deleteTransferAccount($_GET['id']); Display::addFlash( - Display::return_message(get_lang('The learning object has been removed'), 'success') + Display::return_message(get_lang('ItemRemoved'), 'success') ); header('Location: '.api_get_self()); + exit; } } @@ -44,6 +50,7 @@ ); header('Location:'.api_get_self()); + exit; } @@ -182,6 +189,7 @@ ); header('Location:'.api_get_self()); + exit; } @@ -207,6 +215,71 @@ $paypalForm->addButtonSave(get_lang('Save')); $paypalForm->setDefaults($plugin->getPaypalParams()); +// TPV Redsys +$htmlTpvRedsys = Display::return_message($plugin->get_lang('NotFindRedsysFile'), 'warning', false); +if (file_exists(api_get_path(SYS_PLUGIN_PATH).'BuyCourses/resources/apiRedsys.php')) { + $tpvRedsysForm = new FormValidator('tpv_redsys'); + $tpvRedsysForm->addHtml( + Display::return_message($plugin->get_lang('InfoTpvRedsysApiCredentials'), 'info', false) + ); + + if ($tpvRedsysForm->validate()) { + $tpvRedsysFormValues = $tpvRedsysForm->getSubmitValues(); + + $plugin->saveTpvRedsysParams($tpvRedsysFormValues); + + Display::addFlash( + Display::return_message(get_lang('Saved'), 'success') + ); + + header('Location:'.api_get_self()); + + exit; + } + + $tpvRedsysForm->addText( + 'merchantcode', + [$plugin->get_lang('DS_MERCHANT_MERCHANTCODE'), 'DS_MERCHANT_MERCHANTCODE'], + false, + ['cols-size' => [3, 8, 1]] + ); + $tpvRedsysForm->addText( + 'terminal', + [$plugin->get_lang('DS_MERCHANT_TERMINAL'), 'DS_MERCHANT_TERMINAL'], + false, + ['cols-size' => [3, 8, 1]] + ); + $tpvRedsysForm->addText( + 'currency', + [$plugin->get_lang('DS_MERCHANT_CURRENCY'), 'DS_MERCHANT_CURRENCY'], + false, + ['cols-size' => [3, 8, 1]] + ); + $tpvRedsysForm->addText( + 'kc', + $plugin->get_lang('kc'), + false, + ['cols-size' => [3, 8, 1]] + ); + $tpvRedsysForm->addText( + 'url_redsys', + $plugin->get_lang('url_redsys'), + false, + ['cols-size' => [3, 8, 1]] + ); + $tpvRedsysForm->addText( + 'url_redsys_sandbox', + $plugin->get_lang('url_redsys_sandbox'), + false, + ['cols-size' => [3, 8, 1]] + ); + $tpvRedsysForm->addCheckBox('sandbox', null, $plugin->get_lang('Sandbox')); + $tpvRedsysForm->addButtonSave(get_lang('Save')); + $tpvRedsysForm->setDefaults($plugin->getTpvRedsysParams()); + + $htmlTpvRedsys = $tpvRedsysForm->returnForm(); +} + // Platform Commissions $commissionForm = new FormValidator('commissions'); @@ -221,6 +294,7 @@ ); header('Location:'.api_get_self()); + exit; } @@ -246,6 +320,7 @@ ); header('Location:'.api_get_self()); + exit; } @@ -271,6 +346,31 @@ $transferAccounts = $plugin->getTransferAccounts(); +$transferInfoForm = new FormValidator('transfer_info'); + +if ($transferInfoForm->validate()) { + $transferInfoFormValues = $transferInfoForm->getSubmitValues(); + + $plugin->saveTransferInfoEmail($transferInfoFormValues); + + Display::addFlash( + Display::return_message(get_lang('Saved'), 'success') + ); + + header('Location:'.api_get_self()); + + exit; +} +$transferInfoForm->addHtmlEditor( + 'tinfo_email_extra', + $plugin->get_lang('InfoEmailExtra'), + false, + false, + ['ToolbarSet' => 'Minimal'] +); +$transferInfoForm->addButtonCreate(get_lang('Save')); +$transferInfoForm->setDefaults($plugin->getTransferInfoExtra()); + // Culqi main configuration $culqiForm = new FormValidator('culqi_config'); @@ -285,6 +385,7 @@ ); header('Location:'.api_get_self()); + exit; } @@ -304,12 +405,122 @@ $culqiForm->addButtonSave(get_lang('Save')); $culqiForm->setDefaults($plugin->getCulqiParams()); +// Stripe main configuration + +$stripeForm = new FormValidator('stripe_config'); + +if ($stripeForm->validate()) { + $stripeFormValues = $stripeForm->getSubmitValues(); + + $plugin->saveStripeParameters($stripeFormValues); + + Display::addFlash( + Display::return_message(get_lang('Saved'), 'success') + ); + + header('Location:'.api_get_self()); + + exit; +} + +$stripeForm->addText( + 'account_id', + $plugin->get_lang('StripeAccountId'), + false, + ['cols-size' => [3, 8, 1]] +); +$stripeForm->addText( + 'secret_key', + $plugin->get_lang('StripeSecret'), + false, + ['cols-size' => [3, 8, 1]] +); +$stripeForm->addText( + 'endpoint_secret', + $plugin->get_lang('StripeEndpointSecret'), + false, + ['cols-size' => [3, 8, 1]] +); +$stripeForm->addButtonSave(get_lang('Save')); +$stripeForm->setDefaults($plugin->getStripeParams()); + +// Cecabank main configuration + +$cecabankForm = new FormValidator('cecabank_config'); + +if ($cecabankForm->validate()) { + $cecabankFormValues = $cecabankForm->getSubmitValues(); + + $plugin->saveCecabankParameters($cecabankFormValues); + + Display::addFlash( + Display::return_message(get_lang('Saved'), 'success') + ); + + header('Location:'.api_get_self()); + + exit; +} + +$cecabankForm->addText( + 'crypto_key', + $plugin->get_lang('CecaSecret'), + false, + ['cols-size' => [3, 8, 1]] +); +$cecabankForm->addText( + 'url', + $plugin->get_lang('CecaUrl'), + false, + ['cols-size' => [3, 8, 1]] +); +$cecabankForm->addText( + 'merchant_id', + $plugin->get_lang('CecaMerchanId'), + false, + ['cols-size' => [3, 8, 1]] +); +$cecabankForm->addText( + 'acquirer_bin', + $plugin->get_lang('CecaAcquirerId'), + false, + ['cols-size' => [3, 8, 1]] +); +$cecabankForm->addText( + 'terminal_id', + $plugin->get_lang('CecaTerminalId'), + false, + ['cols-size' => [3, 8, 1]] +); +$cecabankForm->addText( + 'cypher', + $plugin->get_lang('CecaCypher'), + false, + ['cols-size' => [3, 8, 1]] +); +$cecabankForm->addText( + 'exponent', + $plugin->get_lang('CecaExponent'), + false, + ['cols-size' => [3, 8, 1]] +); +$cecabankForm->addText( + 'supported_payment', + $plugin->get_lang('CecaSupportedPayment'), + false, + ['cols-size' => [3, 8, 1]] +); +$cecabankForm->addButtonSave(get_lang('Save')); +$cecabankForm->setDefaults($plugin->getCecabankParams()); + // breadcrumbs $interbreadcrumb[] = [ 'url' => api_get_path(WEB_PLUGIN_PATH).'BuyCourses/index.php', 'name' => $plugin->get_lang('plugin_title'), ]; +$htmlHeadXtra[] = api_get_css(api_get_path(WEB_PLUGIN_PATH).'BuyCourses/resources/css/style.css'); + $templateName = $plugin->get_lang('PaymentsConfiguration'); $tpl = new Template($templateName); $tpl->assign('header', $templateName); @@ -317,12 +528,19 @@ $tpl->assign('paypal_form', $paypalForm->returnForm()); $tpl->assign('commission_form', $commissionForm->returnForm()); $tpl->assign('transfer_form', $transferForm->returnForm()); +$tpl->assign('transfer_info_form', $transferInfoForm->returnForm()); $tpl->assign('culqi_form', $culqiForm->returnForm()); $tpl->assign('transfer_accounts', $transferAccounts); $tpl->assign('paypal_enable', $paypalEnable); $tpl->assign('commissions_enable', $commissionsEnable); $tpl->assign('transfer_enable', $transferEnable); $tpl->assign('culqi_enable', $culqiEnable); +$tpl->assign('tpv_redsys_enable', $tpvRedsysEnable); +$tpl->assign('tpv_redsys_form', $htmlTpvRedsys); +$tpl->assign('stripe_enable', $stripeEnable); +$tpl->assign('stripe_form', $stripeForm->returnForm()); +$tpl->assign('cecabank_enable', $cecabankEnable); +$tpl->assign('cecabank_form', $cecabankForm->returnForm()); $content = $tpl->fetch('BuyCourses/view/paymentsetup.tpl'); diff --git a/public/plugin/BuyCourses/src/payout_panel.php b/public/plugin/BuyCourses/src/payout_panel.php index ef5ec7584e1..b6a82ec121f 100644 --- a/public/plugin/BuyCourses/src/payout_panel.php +++ b/public/plugin/BuyCourses/src/payout_panel.php @@ -1,4 +1,7 @@ getPayouts(BuyCoursesPlugin::PAYOUT_STATUS_COMPLETED, false, $userInfo['id']); +$payouts = $plugin->getPayouts(BuyCoursesPlugin::PAYOUT_STATUS_COMPLETED, 0, $userInfo['id']); $payoutList = []; @@ -44,6 +47,9 @@ ); $templateName = get_lang('Dashboard'); + +$htmlHeadXtra[] = api_get_css(api_get_path(WEB_PLUGIN_PATH).'BuyCourses/resources/css/style.css'); + $tpl = new Template($templateName); $tpl->assign('showing_courses', true); $tpl->assign('sessions_are_included', $includeSessions); diff --git a/public/plugin/BuyCourses/src/payout_report.php b/public/plugin/BuyCourses/src/payout_report.php index c85935beccc..34809121e6d 100644 --- a/public/plugin/BuyCourses/src/payout_report.php +++ b/public/plugin/BuyCourses/src/payout_report.php @@ -1,15 +1,17 @@ '; +$htmlHeadXtra[] = api_get_css(api_get_path(WEB_PLUGIN_PATH).'BuyCourses/resources/css/style.css'); $htmlHeadXtra[] = ''; @@ -21,7 +23,7 @@ $payoutStatuses = $plugin->getPayoutStatuses(); $selectedStatus = isset($_GET['status']) ? $_GET['status'] : BuyCoursesPlugin::SALE_STATUS_COMPLETED; -if ("true" !== $commissionsEnable) { +if ('true' !== $commissionsEnable) { api_not_allowed(true); } @@ -46,10 +48,12 @@ $payouts = $plugin->getPayouts($selectedStatus); break; + case '1': $payouts = $plugin->getPayouts($selectedStatus); break; + case '0': default: $payouts = $plugin->getPayouts(); diff --git a/public/plugin/BuyCourses/src/paypal_payout.php b/public/plugin/BuyCourses/src/paypal_payout.php index 231ce1b4f17..caff5c2feac 100644 --- a/public/plugin/BuyCourses/src/paypal_payout.php +++ b/public/plugin/BuyCourses/src/paypal_payout.php @@ -1,8 +1,10 @@ '; +$htmlHeadXtra[] = api_get_css(api_get_path(WEB_PLUGIN_PATH).'BuyCourses/resources/css/style.css'); api_protect_admin_script(true); @@ -20,7 +22,7 @@ $paypalEnable = $plugin->get('paypal_enable'); $commissionsEnable = $plugin->get('commissions_enable'); -if ("true" !== $paypalEnable && "true" !== $commissionsEnable) { +if ('true' !== $paypalEnable && 'true' !== $commissionsEnable) { api_not_allowed(true); } diff --git a/public/plugin/BuyCourses/src/paypalfunctions.php b/public/plugin/BuyCourses/src/paypalfunctions.php index 15153fb7155..32b2f4414e6 100644 --- a/public/plugin/BuyCourses/src/paypalfunctions.php +++ b/public/plugin/BuyCourses/src/paypalfunctions.php @@ -1,4 +1,6 @@ $token, - 'PAYERID' => $payerID, - 'PAYMENTACTION' => $paymentType, - 'PAYMENTREQUEST_0_AMT' => $FinalPaymentAmt, - 'PAYMENTREQUEST_0_CURRENCYCODE' => $currencyCodeType, - 'IPADDRESS' => $serverName, - 'paymentactionspecified' => 'true', - ]); + 'TOKEN' => $token, + 'PAYERID' => $payerID, + 'PAYMENTACTION' => $paymentType, + 'PAYMENTREQUEST_0_AMT' => $FinalPaymentAmt, + 'PAYMENTREQUEST_0_CURRENCYCODE' => $currencyCodeType, + 'IPADDRESS' => $serverName, + 'paymentactionspecified' => 'true', + ]); /** * Make the call to PayPal to finalize payment - * If an error occurred, show the resulting errors. + * If an error occured, show the resulting errors. */ - $resArray = hash_call("DoExpressCheckoutPayment", $nvpstr); + $resArray = hash_call('DoExpressCheckoutPayment', $nvpstr); /** * Display the API response back to the browser. * If the response from PayPal was a success, display the response parameters * If the response was an error, display the errors received using APIError.php. */ - $ack = strtoupper($resArray["ACK"]); + $ack = strtoupper($resArray['ACK']); return $resArray; } @@ -268,6 +295,21 @@ function ConfirmPayment($FinalPaymentAmt) * cvv2: Card Verification Value * Returns: * The NVP Collection object of the DoDirectPayment Call Response. + * + * @param mixed $paymentType + * @param mixed $paymentAmount + * @param mixed $creditCardType + * @param mixed $creditCardNumber + * @param mixed $expDate + * @param mixed $cvv2 + * @param mixed $firstName + * @param mixed $lastName + * @param mixed $street + * @param mixed $city + * @param mixed $state + * @param mixed $zip + * @param mixed $countryCode + * @param mixed $currencyCode */ function DirectPayment( $paymentType, @@ -285,23 +327,23 @@ function DirectPayment( $countryCode, $currencyCode ) { - //Construct the parameter string that describes DoDirectPayment - $nvpstr = "&AMT=".$paymentAmount; - $nvpstr = $nvpstr."&CURRENCYCODE=".$currencyCode; - $nvpstr = $nvpstr."&PAYMENTACTION=".$paymentType; - $nvpstr = $nvpstr."&CREDITCARDTYPE=".$creditCardType; - $nvpstr = $nvpstr."&ACCT=".$creditCardNumber; - $nvpstr = $nvpstr."&EXPDATE=".$expDate; - $nvpstr = $nvpstr."&CVV2=".$cvv2; - $nvpstr = $nvpstr."&FIRSTNAME=".$firstName; - $nvpstr = $nvpstr."&LASTNAME=".$lastName; - $nvpstr = $nvpstr."&STREET=".$street; - $nvpstr = $nvpstr."&CITY=".$city; - $nvpstr = $nvpstr."&STATE=".$state; - $nvpstr = $nvpstr."&COUNTRYCODE=".$countryCode; - $nvpstr = $nvpstr."&IPADDRESS=".$_SERVER['REMOTE_ADDR']; - - $resArray = hash_call("DoDirectPayment", $nvpstr); + // Construct the parameter string that describes DoDirectPayment + $nvpstr = '&AMT='.$paymentAmount; + $nvpstr = $nvpstr.'&CURRENCYCODE='.$currencyCode; + $nvpstr = $nvpstr.'&PAYMENTACTION='.$paymentType; + $nvpstr = $nvpstr.'&CREDITCARDTYPE='.$creditCardType; + $nvpstr = $nvpstr.'&ACCT='.$creditCardNumber; + $nvpstr = $nvpstr.'&EXPDATE='.$expDate; + $nvpstr = $nvpstr.'&CVV2='.$cvv2; + $nvpstr = $nvpstr.'&FIRSTNAME='.$firstName; + $nvpstr = $nvpstr.'&LASTNAME='.$lastName; + $nvpstr = $nvpstr.'&STREET='.$street; + $nvpstr = $nvpstr.'&CITY='.$city; + $nvpstr = $nvpstr.'&STATE='.$state; + $nvpstr = $nvpstr.'&COUNTRYCODE='.$countryCode; + $nvpstr = $nvpstr.'&IPADDRESS='.$_SERVER['REMOTE_ADDR']; + + $resArray = hash_call('DoDirectPayment', $nvpstr); return $resArray; } @@ -313,65 +355,69 @@ function DirectPayment( * Currency Code: The currency Iso code * Returns: * The NVP Collection object of the MassPay Call Response. + * + * @param mixed $currencyCode */ function MassPayment(array $beneficiaries, $currencyCode) { - $nvpstr = "&RECEIVERTYPE=EmailAddress"; - $nvpstr .= "&CURRENCYCODE=".$currencyCode; + $nvpstr = '&RECEIVERTYPE=EmailAddress'; + $nvpstr .= '&CURRENCYCODE='.$currencyCode; $index = 0; foreach ($beneficiaries as $beneficiary) { - $nvpstr .= "&L_EMAIL".$index."=".$beneficiary['paypal_account']; - $nvpstr .= "&L_AMT".$index."=".$beneficiary['commission']; + $nvpstr .= '&L_EMAIL'.$index.'='.$beneficiary['paypal_account']; + $nvpstr .= '&L_AMT'.$index.'='.$beneficiary['commission']; $index++; } - $resArray = hash_call("MassPay", $nvpstr); - - return $resArray; + return hash_call('MassPay', $nvpstr); } /** * hash_call: Function to perform the API call to PayPal using API signature. * * @methodName is name of API method. + * * @nvpStr is nvp string. * returns an associtive array containing the response from the server. + * + * @param mixed $methodName + * @param mixed $nvpStr */ function hash_call($methodName, $nvpStr) { - //declaring of global variables + // declaring of global variables global $API_Endpoint, $version, $API_UserName, $API_Password, $API_Signature; global $USE_PROXY, $PROXY_HOST, $PROXY_PORT; global $sBNCode; - //setting the curl parameters. + // setting the curl parameters. $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $API_Endpoint); - curl_setopt($ch, CURLOPT_VERBOSE, 1); - //turning off the server and peer verification(TrustManager Concept). - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_POST, 1); - - //if USE_PROXY constant set to TRUE in Constants.php, then only proxy will be enabled. - //Set proxy name to PROXY_HOST and port number to PROXY_PORT in constants.php + curl_setopt($ch, \CURLOPT_URL, $API_Endpoint); + curl_setopt($ch, \CURLOPT_VERBOSE, 1); + // turning off the server and peer verification(TrustManager Concept). + curl_setopt($ch, \CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, \CURLOPT_SSL_VERIFYHOST, false); + curl_setopt($ch, \CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, \CURLOPT_POST, 1); + + // if USE_PROXY constant set to TRUE in Constants.php, then only proxy will be enabled. + // Set proxy name to PROXY_HOST and port number to PROXY_PORT in constants.php if ($USE_PROXY) { - curl_setopt($ch, CURLOPT_PROXY, $PROXY_HOST.":".$PROXY_PORT); + curl_setopt($ch, \CURLOPT_PROXY, $PROXY_HOST.':'.$PROXY_PORT); } - //NVPRequest for submitting to server - $nvpreq = "METHOD=".urlencode($methodName)."&VERSION=".urlencode($version). - "&PWD=".urlencode($API_Password)."&USER=".urlencode($API_UserName). - "&SIGNATURE=".urlencode($API_Signature).$nvpStr."&BUTTONSOURCE=".urlencode($sBNCode); + // NVPRequest for submitting to server + $nvpreq = 'METHOD='.urlencode($methodName).'&VERSION='.urlencode($version). + '&PWD='.urlencode($API_Password).'&USER='.urlencode($API_UserName). + '&SIGNATURE='.urlencode($API_Signature).$nvpStr.'&BUTTONSOURCE='.urlencode($sBNCode); - //setting the nvpreq as POST FIELD to curl - curl_setopt($ch, CURLOPT_POSTFIELDS, $nvpreq); - //getting response from server + // setting the nvpreq as POST FIELD to curl + curl_setopt($ch, \CURLOPT_POSTFIELDS, $nvpreq); + // getting response from server $response = curl_exec($ch); - //converting NVPResponse to an Associative Array + // converting NVPResponse to an Associative Array $nvpResArray = deformatNVP($response); $nvpReqArray = deformatNVP($nvpreq); @@ -381,9 +427,9 @@ function hash_call($methodName, $nvpStr) // moving to display page to display curl errors $_SESSION['curl_error_no'] = curl_errno($ch); $_SESSION['curl_error_msg'] = curl_error($ch); - //Execute the Error handling module to display errors. + // Execute the Error handling module to display errors. } else { - //closing the curl + // closing the curl curl_close($ch); } @@ -393,13 +439,16 @@ function hash_call($methodName, $nvpStr) /** * Purpose: Redirects to PayPal.com site. * Inputs: NVP string. + * + * @param mixed $token */ -function RedirectToPayPal($token) +function RedirectToPayPal($token): void { global $PAYPAL_URL; // Redirect to paypal.com here $payPalURL = $PAYPAL_URL.$token; - header("Location: ".$payPalURL); + header('Location: '.$payPalURL); + exit; } @@ -408,7 +457,10 @@ function RedirectToPayPal($token) * It is usefull to search for a particular key and displaying arrays. * * @nvpstr is NVPString. + * * @nvpArray is Associative Array. + * + * @param mixed $nvpstr */ function deformatNVP($nvpstr) { @@ -416,14 +468,14 @@ function deformatNVP($nvpstr) $nvpArray = []; while (strlen($nvpstr)) { - //postion of Key + // postion of Key $keypos = strpos($nvpstr, '='); - //position of value - $valuepos = strpos($nvpstr, '&') ? strpos($nvpstr, '&') : strlen($nvpstr); - /*getting the Key and Value values and storing in a Associative Array*/ + // position of value + $valuepos = strpos($nvpstr, '&') ?: strlen($nvpstr); + /* getting the Key and Value values and storing in a Associative Array */ $keyval = substr($nvpstr, $intial, $keypos); $valval = substr($nvpstr, $keypos + 1, $valuepos - $keypos - 1); - //decoding the respose + // decoding the respose $nvpArray[urldecode($keyval)] = urldecode($valval); $nvpstr = substr($nvpstr, $valuepos + 1, strlen($nvpstr)); } diff --git a/public/plugin/BuyCourses/src/process.php b/public/plugin/BuyCourses/src/process.php index 4a48632d1b9..30912ea5ae4 100644 --- a/public/plugin/BuyCourses/src/process.php +++ b/public/plugin/BuyCourses/src/process.php @@ -1,4 +1,6 @@ '; + WEB_PLUGIN_PATH +).'BuyCourses/resources/css/style.css"/>'; $plugin = BuyCoursesPlugin::create(); $includeSession = 'true' === $plugin->get('include_sessions'); $paypalEnabled = 'true' === $plugin->get('paypal_enable'); $transferEnabled = 'true' === $plugin->get('transfer_enable'); $culqiEnabled = 'true' === $plugin->get('culqi_enable'); +$tpvRedsysEnable = 'true' === $plugin->get('tpv_redsys_enable'); +$stripeEnable = 'true' === $plugin->get('stripe_enable'); +$tpvCecabankEnable = 'true' === $plugin->get('cecabank_enable'); -if (!$paypalEnabled && !$transferEnabled && !$culqiEnabled) { +if (!$paypalEnabled && !$transferEnabled && !$culqiEnabled && !$tpvRedsysEnable && !$stripeEnable && !$tpvCecabankEnable) { api_not_allowed(true); } @@ -27,21 +32,34 @@ api_not_allowed(true); } -$buyingCourse = BuyCoursesPlugin::PRODUCT_TYPE_COURSE === intval($_REQUEST['t']); -$buyingSession = BuyCoursesPlugin::PRODUCT_TYPE_SESSION === intval($_REQUEST['t']); -$queryString = 'i='.intval($_REQUEST['i']).'&t='.intval($_REQUEST['t']); +$currency = $plugin->getSelectedCurrency(); +$buyingCourse = BuyCoursesPlugin::PRODUCT_TYPE_COURSE === (int) $_REQUEST['t']; +$buyingSession = BuyCoursesPlugin::PRODUCT_TYPE_SESSION === (int) $_REQUEST['t']; +$queryString = 'i='.(int) $_REQUEST['i'].'&t='.(int) $_REQUEST['t']; + +$coupon = null; + +if (isset($_REQUEST['c'])) { + $couponId = (int) $_REQUEST['c']; + if ($buyingCourse) { + $coupon = $plugin->getCoupon($couponId, BuyCoursesPlugin::PRODUCT_TYPE_COURSE, $_REQUEST['i']); + } else { + $coupon = $plugin->getCoupon($couponId, BuyCoursesPlugin::PRODUCT_TYPE_SESSION, $_REQUEST['i']); + } +} if (empty($currentUserId)) { Session::write('buy_course_redirect', api_get_self().'?'.$queryString); header('Location: '.api_get_path(WEB_CODE_PATH).'auth/inscription.php'); + exit; } if ($buyingCourse) { - $courseInfo = $plugin->getCourseInfo($_REQUEST['i']); + $courseInfo = $plugin->getCourseInfo($_REQUEST['i'], $coupon); $item = $plugin->getItemByProduct($_REQUEST['i'], BuyCoursesPlugin::PRODUCT_TYPE_COURSE); } elseif ($buyingSession) { - $sessionInfo = $plugin->getSessionInfo($_REQUEST['i']); + $sessionInfo = $plugin->getSessionInfo($_REQUEST['i'], $coupon); $item = $plugin->getItemByProduct($_REQUEST['i'], BuyCoursesPlugin::PRODUCT_TYPE_SESSION); } @@ -54,35 +72,30 @@ Display::return_message($plugin->get_lang('NeedToSelectPaymentType'), 'error', false) ); header('Location:'.api_get_self().'?'.$queryString); + exit; } - $saleId = false; - if (!empty($item)) { - $saleId = $plugin->registerSale($item['id'], $formValues['payment_type']); - } + $saleId = $plugin->registerSale($item['id'], $formValues['payment_type'], (int) $formValues['c']); - if (false !== $saleId) { + if ($saleId) { $_SESSION['bc_sale_id'] = $saleId; + + if (isset($formValues['c'])) { + $couponSaleId = $plugin->registerCouponSale($saleId, $formValues['c']); + if (false !== $couponSaleId) { + $plugin->updateCouponDelivered($formValues['c']); + $_SESSION['bc_coupon_id'] = $formValues['c']; + } + } + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/process_confirm.php'); } exit; } -$paymentTypesOptions = $plugin->getPaymentTypes(); - -if (!$paypalEnabled) { - unset($paymentTypesOptions[BuyCoursesPlugin::PAYMENT_TYPE_PAYPAL]); -} - -if (!$transferEnabled) { - unset($paymentTypesOptions[BuyCoursesPlugin::PAYMENT_TYPE_TRANSFER]); -} - -if (!$culqiEnabled) { - unset($paymentTypesOptions[BuyCoursesPlugin::PAYMENT_TYPE_CULQI]); -} +$paymentTypesOptions = $plugin->getPaymentTypes(true); $count = count($paymentTypesOptions); if (0 === $count) { @@ -107,18 +120,64 @@ $form->addRadio('payment_type', null, $paymentTypesOptions); } -$form->addHidden('t', intval($_GET['t'])); -$form->addHidden('i', intval($_GET['i'])); +$form->addHidden('t', (int) $_REQUEST['t']); +$form->addHidden('i', (int) $_REQUEST['i']); +if (null != $coupon) { + $form->addHidden('c', (int) $coupon['id']); +} $form->addButton('submit', $plugin->get_lang('ConfirmOrder'), 'check', 'success', 'btn-lg pull-right'); +$formCoupon = new FormValidator('confirm_coupon'); +if ($formCoupon->validate()) { + $formCouponValues = $formCoupon->getSubmitValues(); + + if (!$formCouponValues['coupon_code']) { + Display::addFlash( + Display::return_message($plugin->get_lang('NeedToAddCouponCode'), 'error', false) + ); + header('Location:'.api_get_self().'?'.$queryString); + + exit; + } + + if ($buyingCourse) { + $coupon = $plugin->getCouponByCode($formCouponValues['coupon_code'], BuyCoursesPlugin::PRODUCT_TYPE_COURSE, $_REQUEST['i']); + } else { + $coupon = $plugin->getCouponByCode($formCouponValues['coupon_code'], BuyCoursesPlugin::PRODUCT_TYPE_SESSION, $_REQUEST['i']); + } + + if (null == $coupon) { + Display::addFlash( + Display::return_message($plugin->get_lang('CouponNotValid'), 'error', false) + ); + header('Location:'.api_get_self().'?'.$queryString); + + exit; + } + + Display::addFlash( + Display::return_message($plugin->get_lang('CouponRedeemed'), 'success', false) + ); + + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/process.php?i='.$_REQUEST['i'].'&t='.$_REQUEST['t'].'&c='.$coupon['id']); + + exit; +} +$formCoupon->addText('coupon_code', $plugin->get_lang('CouponsCode'), true); +$formCoupon->addHidden('t', (int) $_GET['t']); +$formCoupon->addHidden('i', (int) $_GET['i']); +$formCoupon->addButton('submit', $plugin->get_lang('RedeemCoupon'), 'check', 'success', 'btn-lg pull-right'); + // View $templateName = $plugin->get_lang('PaymentMethods'); $interbreadcrumb[] = ['url' => 'course_catalog.php', 'name' => $plugin->get_lang('CourseListOnSale')]; $tpl = new Template($templateName); +$tpl->assign('item_type', (int) $_GET['t']); $tpl->assign('buying_course', $buyingCourse); $tpl->assign('buying_session', $buyingSession); $tpl->assign('user', api_get_user_info()); +$tpl->assign('form_coupon', $formCoupon->returnForm()); $tpl->assign('form', $form->returnForm()); if ($buyingCourse) { diff --git a/public/plugin/BuyCourses/src/process_confirm.php b/public/plugin/BuyCourses/src/process_confirm.php index 191c1e98cef..686e07e4baa 100644 --- a/public/plugin/BuyCourses/src/process_confirm.php +++ b/public/plugin/BuyCourses/src/process_confirm.php @@ -1,4 +1,10 @@ getSale($saleId); +$coupon = []; +if (!empty($couponId)) { + $coupon = $plugin->getCoupon($couponId, $sale['product_type'], $sale['product_id']); +} + $userInfo = api_get_user_info($sale['user_id']); if (empty($sale)) { @@ -34,12 +46,12 @@ $paypalPassword = $paypalParams['password']; $paypalSignature = $paypalParams['signature']; - require_once "paypalfunctions.php"; + require_once 'paypalfunctions.php'; $i = 0; $extra = "&L_PAYMENTREQUEST_0_NAME0={$sale['product_name']}"; $extra .= "&L_PAYMENTREQUEST_0_AMT0={$sale['price']}"; - $extra .= "&L_PAYMENTREQUEST_0_QTY0=1"; + $extra .= '&L_PAYMENTREQUEST_0_QTY0=1'; $expressCheckout = CallShortcutExpressCheckout( $sale['price'], @@ -50,15 +62,16 @@ $extra ); - if ('Success' !== $expressCheckout["ACK"]) { + if ('Success' !== $expressCheckout['ACK']) { $erroMessage = vsprintf( - get_lang('An error occurred.'), + $plugin->get_lang('ErrorOccurred'), [$expressCheckout['L_ERRORCODE0'], $expressCheckout['L_LONGMESSAGE0']] ); Display::addFlash( Display::return_message($erroMessage, 'error', false) ); header('Location: ../index.php'); + exit; } @@ -84,8 +97,10 @@ ); } - RedirectToPayPal($expressCheckout["TOKEN"]); + RedirectToPayPal($expressCheckout['TOKEN']); + break; + case BuyCoursesPlugin::PAYMENT_TYPE_TRANSFER: $buyingCourse = false; $buyingSession = false; @@ -93,15 +108,19 @@ switch ($sale['product_type']) { case BuyCoursesPlugin::PRODUCT_TYPE_COURSE: $buyingCourse = true; - $course = $plugin->getCourseInfo($sale['product_id']); + $course = $plugin->getCourseInfo($sale['product_id'], $coupon); + break; + case BuyCoursesPlugin::PRODUCT_TYPE_SESSION: $buyingSession = true; - $session = $plugin->getSessionInfo($sale['product_id']); + $session = $plugin->getSessionInfo($sale['product_id'], $coupon); + break; } $transferAccounts = $plugin->getTransferAccounts(); + $infoEmailExtra = $plugin->getTransferInfoExtra()['tinfo_email_extra']; $form = new FormValidator( 'success', @@ -118,9 +137,10 @@ if (isset($formValues['cancel'])) { $plugin->cancelSale($sale['id']); - unset($_SESSION['bc_sale_id']); + unset($_SESSION['bc_sale_id'], $_SESSION['bc_coupon_id']); header('Location: '.api_get_path(WEB_PLUGIN_PATH).'BuyCourses/index.php'); + exit; } @@ -137,6 +157,7 @@ ] ); $messageTemplate->assign('transfer_accounts', $transferAccounts); + $messageTemplate->assign('info_email_extra', $infoEmailExtra); MessageManager::send_message_simple( $userInfo['user_id'], @@ -177,8 +198,10 @@ ) ); - unset($_SESSION['bc_sale_id']); + unset($_SESSION['bc_sale_id'], $_SESSION['bc_coupon_id']); + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/course_catalog.php'); + exit; } @@ -216,7 +239,9 @@ $template->assign('content', $content); $template->display_one_col_template(); + break; + case BuyCoursesPlugin::PAYMENT_TYPE_CULQI: // We need to include the main online script, acording to the Culqi documentation the JS needs to be loeaded // directly from the main url "https://integ-pago.culqi.com" because a local copy of this JS is not supported @@ -228,11 +253,14 @@ switch ($sale['product_type']) { case BuyCoursesPlugin::PRODUCT_TYPE_COURSE: $buyingCourse = true; - $course = $plugin->getCourseInfo($sale['product_id']); + $course = $plugin->getCourseInfo($sale['product_id'], $coupon); + break; + case BuyCoursesPlugin::PRODUCT_TYPE_SESSION: $buyingSession = true; - $session = $plugin->getSessionInfo($sale['product_id']); + $session = $plugin->getSessionInfo($sale['product_id'], $coupon); + break; } @@ -251,7 +279,7 @@ if (isset($formValues['cancel'])) { $plugin->cancelSale($sale['id']); - unset($_SESSION['bc_sale_id']); + unset($_SESSION['bc_sale_id'], $_SESSION['bc_coupon_id']); Display::addFlash( Display::return_message( @@ -262,6 +290,7 @@ ); header('Location: '.api_get_path(WEB_PLUGIN_PATH).'BuyCourses/index.php'); + exit; } } @@ -296,7 +325,7 @@ $template->assign('buying_session', $buyingSession); $template->assign('terms', $globalParameters['terms_and_conditions']); $template->assign('title', $sale['product_name']); - $template->assign('price', floatval($sale['price'])); + $template->assign('price', (float) $sale['price']); $template->assign('currency', $plugin->getSelectedCurrency()); $template->assign('user', $userInfo); $template->assign('sale', $sale); @@ -309,5 +338,284 @@ $template->assign('content', $content); $template->display_one_col_template(); + break; + + case BuyCoursesPlugin::PAYMENT_TYPE_TPV_REDSYS: + $tpvRedsysParams = $plugin->getTpvRedsysParams(); + + require_once '../resources/apiRedsys.php'; + $tpv = new RedsysAPI(); + + $merchantcode = $tpvRedsysParams['merchantcode']; + $terminal = $tpvRedsysParams['terminal']; + $currency = $tpvRedsysParams['currency']; + $transactionType = '0'; + $urlMerchant = api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/tpv_response.php'; + $urlSuccess = api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/tpv_success.php'; + $urlFailed = api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/tpv_error.php'; + $order = str_pad((string) $saleId, 4, '0', \STR_PAD_LEFT); + $amount = $sale['price'] * 100; + $description = $plugin->get_lang('OrderReference').': '.$sale['reference']; + $tpv->setParameter('DS_MERCHANT_AMOUNT', $amount); + $tpv->setParameter('DS_MERCHANT_ORDER', $order); + $tpv->setParameter('DS_MERCHANT_MERCHANTCODE', $merchantcode); + $tpv->setParameter('DS_MERCHANT_CURRENCY', $currency); + $tpv->setParameter('DS_MERCHANT_TRANSACTIONTYPE', $transactionType); + $tpv->setParameter('DS_MERCHANT_TERMINAL', $terminal); + $tpv->setParameter('DS_MERCHANT_MERCHANTURL', $urlMerchant); + $tpv->setParameter('DS_MERCHANT_URLOK', $urlSuccess); + $tpv->setParameter('DS_MERCHANT_URLKO', $urlFailed); + $tpv->setParameter('DS_MERCHANT_PRODUCTDESCRIPTION', $description); + + $version = 'HMAC_SHA256_V1'; + $kc = $tpvRedsysParams['kc']; + + $urlTpv = $tpvRedsysParams['url_redsys']; + $sandboxFlag = 1 == $tpvRedsysParams['sandbox']; + if (true === $sandboxFlag) { + $urlTpv = $tpvRedsysParams['url_redsys_sandbox']; + } + + $params = $tpv->createMerchantParameters(); + $signature = $tpv->createMerchantSignature($kc); + + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + + echo ''; + + break; + + case BuyCoursesPlugin::PAYMENT_TYPE_STRIPE: + $buyingCourse = false; + $buyingSession = false; + + switch ($sale['product_type']) { + case BuyCoursesPlugin::PRODUCT_TYPE_COURSE: + $buyingCourse = true; + $course = $plugin->getCourseInfo($sale['product_id'], $coupon); + + break; + + case BuyCoursesPlugin::PRODUCT_TYPE_SESSION: + $buyingSession = true; + $session = $plugin->getSessionInfo($sale['product_id'], $coupon); + + break; + } + + $form = new FormValidator( + 'success', + 'POST', + api_get_self(), + null, + null, + FormValidator::LAYOUT_INLINE + ); + + if ($form->validate()) { + $formValues = $form->getSubmitValues(); + + if (isset($formValues['cancel'])) { + $plugin->cancelSale($sale['id']); + + unset($_SESSION['bc_sale_id'], $_SESSION['bc_coupon_id']); + + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'BuyCourses/index.php'); + + exit; + } + + $stripeParams = $plugin->getStripeParams(); + $currency = $plugin->getCurrency($sale['currency_id']); + + Stripe::setApiKey($stripeParams['secret_key']); + Stripe::setAppInfo('ChamiloBuyCoursesPlugin'); + + $session = Session::create([ + 'payment_method_types' => ['card'], + 'line_items' => [[ + 'price_data' => [ + 'unit_amount_decimal' => $sale['price'] * 100, + 'currency' => $currency['iso_code'], + 'product_data' => [ + 'name' => $sale['product_name'], + ], + ], + 'quantity' => 1, + ]], + 'customer_email' => $_SESSION['_user']['email'], + 'mode' => 'payment', + 'success_url' => api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/stripe_success.php', + 'cancel_url' => api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/stripe_cancel.php', + ]); + + if (!empty($session)) { + $plugin->updateSaleReference($saleId, $session->id); + + unset($_SESSION['bc_coupon_id']); + + header('HTTP/1.1 301 Moved Permanently'); + header('Location: '.$session->url); + } else { + Display::addFlash( + Display::return_message( + $plugin->get_lang('ErrorOccurred'), + 'error', + false + ) + ); + header('Location: ../index.php'); + } + + exit; + } + + $form->addButton( + 'confirm', + $plugin->get_lang('ConfirmOrder'), + 'check', + 'success', + 'default', + null, + ['id' => 'confirm'] + ); + $form->addButtonCancel($plugin->get_lang('CancelOrder'), 'cancel'); + + $template = new Template(); + + if ($buyingCourse) { + $template->assign('course', $course); + } elseif ($buyingSession) { + $template->assign('session', $session); + } + + $template->assign('buying_course', $buyingCourse); + $template->assign('buying_session', $buyingSession); + $template->assign('terms', $globalParameters['terms_and_conditions']); + $template->assign('title', $sale['product_name']); + $template->assign('price', $sale['price']); + $template->assign('currency', $sale['currency_id']); + $template->assign('user', $userInfo); + $template->assign('transfer_accounts', $transferAccounts); + $template->assign('form', $form->returnForm()); + $template->assign('is_bank_transfer', false); + + $content = $template->fetch('BuyCourses/view/process_confirm.tpl'); + + $template->assign('content', $content); + $template->display_one_col_template(); + + break; + + case BuyCoursesPlugin::PAYMENT_TYPE_TPV_CECABANK: + $buyingCourse = false; + $buyingSession = false; + + switch ($sale['product_type']) { + case BuyCoursesPlugin::PRODUCT_TYPE_COURSE: + $buyingCourse = true; + $course = $plugin->getCourseInfo($sale['product_id']); + + break; + + case BuyCoursesPlugin::PRODUCT_TYPE_SESSION: + $buyingSession = true; + $session = $plugin->getSessionInfo($sale['product_id']); + + break; + } + + $cecabankParams = $plugin->getcecabankParams(); + $currency = $plugin->getCurrency($sale['currency_id']); + + $form = new FormValidator( + 'success', + 'POST', + api_get_self(), + null, + null, + FormValidator::LAYOUT_INLINE + ); + + if ($form->validate()) { + $formValues = $form->getSubmitValues(); + + if (isset($formValues['cancel'])) { + $plugin->cancelSale($sale['id']); + + unset($_SESSION['bc_sale_id'], $_SESSION['bc_coupon_id']); + + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'BuyCourses/index.php'); + + exit; + } + + $urlTpv = $cecabankParams['url']; + $currency = $plugin->getCurrency($sale['currency_id']); + $signature = $plugin->getCecabankSignature($sale['reference'], $sale['price']); + + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + + echo ''; + + exit; + } + + $form->addButton( + 'confirm', + $plugin->get_lang('ConfirmOrder'), + 'check', + 'success', + 'default', + null, + ['id' => 'confirm'] + ); + $form->addButtonCancel($plugin->get_lang('CancelOrder'), 'cancel'); + + $template = new Template(); + + if ($buyingCourse) { + $template->assign('course', $course); + } elseif ($buyingSession) { + $template->assign('session', $session); + } + + $template->assign('buying_course', $buyingCourse); + $template->assign('buying_session', $buyingSession); + $template->assign('terms', $globalParameters['terms_and_conditions']); + $template->assign('title', $sale['product_name']); + $template->assign('price', $sale['price']); + $template->assign('currency', $sale['currency_id']); + $template->assign('user', $userInfo); + $template->assign('transfer_accounts', $transferAccounts); + $template->assign('form', $form->returnForm()); + $template->assign('is_bank_transfer', false); + + $content = $template->fetch('BuyCourses/view/process_confirm.tpl'); + + $template->assign('content', $content); + $template->display_one_col_template(); + break; } diff --git a/public/plugin/BuyCourses/src/process_subscription_confirm.php b/public/plugin/BuyCourses/src/process_subscription_confirm.php new file mode 100644 index 00000000000..efddf3ad305 --- /dev/null +++ b/public/plugin/BuyCourses/src/process_subscription_confirm.php @@ -0,0 +1,388 @@ +getSubscriptionSale($saleId); + +if (!empty($couponId)) { + $coupon = $plugin->getCoupon($couponId, $sale['product_type'], $sale['product_id']); +} + +$userInfo = api_get_user_info($sale['user_id']); + +if (empty($sale)) { + api_not_allowed(true); +} + +$currency = $plugin->getCurrency($sale['currency_id']); +$globalParameters = $plugin->getGlobalParameters(); + +switch ($sale['payment_type']) { + case BuyCoursesPlugin::PAYMENT_TYPE_PAYPAL: + $paypalParams = $plugin->getPaypalParams(); + + $pruebas = 1 == $paypalParams['sandbox']; + $paypalUsername = $paypalParams['username']; + $paypalPassword = $paypalParams['password']; + $paypalSignature = $paypalParams['signature']; + + require_once 'paypalfunctions.php'; + + $i = 0; + $extra = "&L_PAYMENTREQUEST_0_NAME0={$sale['product_name']}"; + $extra .= "&L_PAYMENTREQUEST_0_AMT0={$sale['price']}"; + $extra .= '&L_PAYMENTREQUEST_0_QTY0=1'; + + $expressCheckout = CallShortcutExpressCheckout( + $sale['price'], + $currency['iso_code'], + 'paypal', + api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/success.php', + api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/error.php', + $extra + ); + + if ('Success' !== $expressCheckout['ACK']) { + $erroMessage = vsprintf( + $plugin->get_lang('ErrorOccurred'), + [$expressCheckout['L_ERRORCODE0'], $expressCheckout['L_LONGMESSAGE0']] + ); + Display::addFlash( + Display::return_message($erroMessage, 'error', false) + ); + header('Location: ../index.php'); + + exit; + } + + if (!empty($globalParameters['sale_email'])) { + $messageConfirmTemplate = new Template(); + $messageConfirmTemplate->assign('user', $userInfo); + $messageConfirmTemplate->assign( + 'sale', + [ + 'date' => $sale['date'], + 'product' => $sale['product_name'], + 'currency' => $currency['iso_code'], + 'price' => $sale['price'], + 'reference' => $sale['reference'], + ] + ); + + api_mail_html( + '', + $globalParameters['sale_email'], + $plugin->get_lang('bc_subject'), + $messageConfirmTemplate->fetch('BuyCourses/view/message_confirm.tpl') + ); + } + + RedirectToPayPal($expressCheckout['TOKEN']); + + break; + + case BuyCoursesPlugin::PAYMENT_TYPE_TRANSFER: + $buyingCourse = false; + $buyingSession = false; + + switch ($sale['product_type']) { + case BuyCoursesPlugin::PRODUCT_TYPE_COURSE: + $buyingCourse = true; + $course = $plugin->getSubscriptionCourseInfo($sale['product_id'], $coupon); + + break; + + case BuyCoursesPlugin::PRODUCT_TYPE_SESSION: + $buyingSession = true; + $session = $plugin->getSubscriptionSessionInfo($sale['product_id'], $coupon); + + break; + } + + $transferAccounts = $plugin->getTransferAccounts(); + $infoEmailExtra = $plugin->getTransferInfoExtra()['tinfo_email_extra']; + + $form = new FormValidator( + 'success', + 'POST', + api_get_self(), + null, + null, + FormValidator::LAYOUT_INLINE + ); + + if ($form->validate()) { + $formValues = $form->getSubmitValues(); + + if (isset($formValues['cancel'])) { + $plugin->cancelSubscriptionSale($sale['id']); + + unset($_SESSION['bc_sale_id'], $_SESSION['bc_coupon_id']); + + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'BuyCourses/index.php'); + + exit; + } + + $messageTemplate = new Template(); + $messageTemplate->assign('user', $userInfo); + $messageTemplate->assign( + 'sale', + [ + 'date' => $sale['date'], + 'product' => $sale['product_name'], + 'currency' => $currency['iso_code'], + 'price' => $sale['price'], + 'reference' => $sale['reference'], + ] + ); + $messageTemplate->assign('transfer_accounts', $transferAccounts); + $messageTemplate->assign('info_email_extra', $infoEmailExtra); + + MessageManager::send_message_simple( + $userInfo['user_id'], + $plugin->get_lang('bc_subject'), + $messageTemplate->fetch('BuyCourses/view/message_transfer.tpl') + ); + + if (!empty($globalParameters['sale_email'])) { + $messageConfirmTemplate = new Template(); + $messageConfirmTemplate->assign('user', $userInfo); + $messageConfirmTemplate->assign( + 'sale', + [ + 'date' => $sale['date'], + 'product' => $sale['product_name'], + 'currency' => $currency['iso_code'], + 'price' => $sale['price'], + 'reference' => $sale['reference'], + ] + ); + + api_mail_html( + '', + $globalParameters['sale_email'], + $plugin->get_lang('bc_subject'), + $messageConfirmTemplate->fetch('BuyCourses/view/message_confirm.tpl') + ); + } + + Display::addFlash( + Display::return_message( + sprintf( + $plugin->get_lang('PurchaseStatusX'), + $plugin->get_lang('PendingReasonByTransfer') + ), + 'success', + false + ) + ); + + unset($_SESSION['bc_sale_id'], $_SESSION['bc_coupon_id']); + + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/course_catalog.php'); + + exit; + } + + $form->addButton( + 'confirm', + $plugin->get_lang('ConfirmOrder'), + 'check', + 'success', + 'default', + null, + ['id' => 'confirm'] + ); + $form->addButtonCancel($plugin->get_lang('CancelOrder'), 'cancel'); + + $template = new Template(); + + if ($buyingCourse) { + $template->assign('course', $course); + } elseif ($buyingSession) { + $template->assign('session', $session); + } + + $template->assign('buying_course', $buyingCourse); + $template->assign('buying_session', $buyingSession); + $template->assign('terms', $globalParameters['terms_and_conditions']); + $template->assign('title', $sale['product_name']); + $template->assign('price', $sale['price']); + $template->assign('currency', $sale['currency_id']); + $template->assign('user', $userInfo); + $template->assign('transfer_accounts', $transferAccounts); + $template->assign('form', $form->returnForm()); + $template->assign('is_bank_transfer', true); + + $content = $template->fetch('BuyCourses/view/subscription_process_confirm.tpl'); + + $template->assign('content', $content); + $template->display_one_col_template(); + + break; + + case BuyCoursesPlugin::PAYMENT_TYPE_CULQI: + // We need to include the main online script, acording to the Culqi documentation the JS needs to be loeaded + // directly from the main url "https://integ-pago.culqi.com" because a local copy of this JS is not supported + $htmlHeadXtra[] = ''; + + $buyingCourse = false; + $buyingSession = false; + + switch ($sale['product_type']) { + case BuyCoursesPlugin::PRODUCT_TYPE_COURSE: + $buyingCourse = true; + $course = $plugin->getSubscriptionCourseInfo($sale['product_id'], $coupon); + + break; + + case BuyCoursesPlugin::PRODUCT_TYPE_SESSION: + $buyingSession = true; + $session = $plugin->getSubscriptionSessionInfo($sale['product_id'], $coupon); + + break; + } + + $form = new FormValidator( + 'success', + 'POST', + api_get_self(), + null, + null, + FormValidator::LAYOUT_INLINE + ); + + if ($form->validate()) { + $formValues = $form->getSubmitValues(); + + if (isset($formValues['cancel'])) { + $plugin->cancelSubscriptionSale($sale['id']); + + unset($_SESSION['bc_sale_id'], $_SESSION['bc_coupon_id']); + + Display::addFlash( + Display::return_message( + $plugin->get_lang('OrderCanceled'), + 'warning', + false + ) + ); + + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'BuyCourses/index.php'); + + exit; + } + } + $form->addButton( + 'confirm', + $plugin->get_lang('ConfirmOrder'), + 'check', + 'success', + 'default', + null, + ['id' => 'confirm'] + ); + $form->addButton( + 'cancel', + $plugin->get_lang('CancelOrder'), + 'times', + 'danger', + 'default', + null, + ['id' => 'cancel'] + ); + + $template = new Template(); + + if ($buyingCourse) { + $template->assign('course', $course); + } elseif ($buyingSession) { + $template->assign('session', $session); + } + + $template->assign('buying_course', $buyingCourse); + $template->assign('buying_session', $buyingSession); + $template->assign('terms', $globalParameters['terms_and_conditions']); + $template->assign('title', $sale['product_name']); + $template->assign('price', (float) $sale['price']); + $template->assign('currency', $plugin->getSelectedCurrency()); + $template->assign('user', $userInfo); + $template->assign('sale', $sale); + $template->assign('form', $form->returnForm()); + $template->assign('is_culqi_payment', true); + $template->assign('culqi_params', $culqiParams = $plugin->getCulqiParams()); + + $content = $template->fetch('BuyCourses/view/subscription_process_confirm.tpl'); + + $template->assign('content', $content); + $template->display_one_col_template(); + + break; + + case BuyCoursesPlugin::PAYMENT_TYPE_TPV_REDSYS: + $tpvRedsysParams = $plugin->getTpvRedsysParams(); + + require_once '../resources/apiRedsys.php'; + $tpv = new RedsysAPI(); + + $merchantcode = $tpvRedsysParams['merchantcode']; + $terminal = $tpvRedsysParams['terminal']; + $currency = $tpvRedsysParams['currency']; + $transactionType = '0'; + $urlMerchant = api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/tpv_response.php'; + $urlSuccess = api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/tpv_success.php'; + $urlFailed = api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/tpv_error.php'; + $order = str_pad((string) $saleId, 4, '0', \STR_PAD_LEFT); + $amount = $sale['price'] * 100; + $description = $plugin->get_lang('OrderReference').': '.$sale['reference']; + $tpv->setParameter('DS_MERCHANT_AMOUNT', $amount); + $tpv->setParameter('DS_MERCHANT_ORDER', $order); + $tpv->setParameter('DS_MERCHANT_MERCHANTCODE', $merchantcode); + $tpv->setParameter('DS_MERCHANT_CURRENCY', $currency); + $tpv->setParameter('DS_MERCHANT_TRANSACTIONTYPE', $transactionType); + $tpv->setParameter('DS_MERCHANT_TERMINAL', $terminal); + $tpv->setParameter('DS_MERCHANT_MERCHANTURL', $urlMerchant); + $tpv->setParameter('DS_MERCHANT_URLOK', $urlSuccess); + $tpv->setParameter('DS_MERCHANT_URLKO', $urlFailed); + $tpv->setParameter('DS_MERCHANT_PRODUCTDESCRIPTION', $description); + + $version = 'HMAC_SHA256_V1'; + $kc = $tpvRedsysParams['kc']; + + $urlTpv = $tpvRedsysParams['url_redsys']; + $sandboxFlag = 1 == $tpvRedsysParams['sandbox']; + if (true === $sandboxFlag) { + $urlTpv = $tpvRedsysParams['url_redsys_sandbox']; + } + + $params = $tpv->createMerchantParameters(); + $signature = $tpv->createMerchantSignature($kc); + + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + + echo ''; + + break; +} diff --git a/public/plugin/BuyCourses/src/sales_report.php b/public/plugin/BuyCourses/src/sales_report.php index cdfac700d39..adc0bfa41e8 100644 --- a/public/plugin/BuyCourses/src/sales_report.php +++ b/public/plugin/BuyCourses/src/sales_report.php @@ -1,4 +1,6 @@ getSale($_GET['order']); - if (empty($sale)) { api_not_allowed(true); } @@ -38,7 +39,9 @@ 'status' => BuyCoursesPlugin::SALE_STATUS_COMPLETED, 'sale' => $sale['id'], ]); + break; + case 'cancel': $plugin->cancelSale($sale['id']); @@ -53,10 +56,12 @@ 'status' => BuyCoursesPlugin::SALE_STATUS_CANCELED, 'sale' => $sale['id'], ]); + break; } header("Location: $urlToRedirect"); + exit; } @@ -66,7 +71,7 @@ $selectedFilterType = '0'; $selectedStatus = isset($_GET['status']) ? $_GET['status'] : BuyCoursesPlugin::SALE_STATUS_PENDING; -$selectedSale = isset($_GET['sale']) ? intval($_GET['sale']) : 0; +$selectedSale = isset($_GET['sale']) ? (int) ($_GET['sale']) : 0; $dateStart = isset($_GET['date_start']) ? $_GET['date_start'] : date('Y-m-d H:i', mktime(0, 0, 0)); $dateEnd = isset($_GET['date_end']) ? $_GET['date_end'] : date('Y-m-d H:i', mktime(23, 59, 59)); $searchTerm = ''; @@ -105,13 +110,13 @@ $form->addSelect('status', $plugin->get_lang('OrderStatus'), $saleStatuses); $form->addHtml(''); $form->addHtml('
'); -$form->addText('user', get_lang('Username'), false); +$form->addText('user', get_lang('UserName'), false); $form->addHtml('
'); $form->addHtml('
'); $form->addDateRangePicker('date', get_lang('Date'), false); $form->addHtml('
'); $form->addHtml('
'); -$form->addText('email', get_lang('E-mail'), false); +$form->addText('email', get_lang('Email'), false); $form->addHtml('
'); $form->addButtonFilter(get_lang('Search')); $form->setDefaults([ @@ -125,15 +130,22 @@ switch ($selectedFilterType) { case '0': $sales = $plugin->getSaleListByStatus($selectedStatus); + break; + case '1': $sales = $plugin->getSaleListByUser($searchTerm); + break; + case '2': $sales = $plugin->getSaleListByDate($dateStart, $dateEnd); + break; + case '3': $sales = $plugin->getSaleListByEmail($email); + break; } @@ -143,17 +155,26 @@ $sale['complete_user_name'] = api_get_person_name($sale['firstname'], $sale['lastname']); $sale['num_invoice'] = $plugin->getNumInvoice($sale['id'], 0); $sale['total_price'] = $plugin->getPriceWithCurrencyFromIsoCode($sale['price'], $sale['iso_code']); + if (isset($sale['discount_amount']) && 0 != $sale['discount_amount']) { + $sale['total_discount'] = $plugin->getPriceWithCurrencyFromIsoCode($sale['discount_amount'], $sale['iso_code']); + $sale['coupon_code'] = $plugin->getSaleCouponCode($sale['id']); + } } $interbreadcrumb[] = ['url' => '../index.php', 'name' => $plugin->get_lang('plugin_title')]; + +$htmlHeadXtra[] = api_get_css(api_get_path(WEB_PLUGIN_PATH).'BuyCourses/resources/css/style.css'); +$htmlHeadXtra[] = api_get_jqgrid_js(); +$htmlHeadXtra[] = BuyCoursesPlugin::getSalesReportScript($sales, $invoicingEnable); + $templateName = $plugin->get_lang('SalesReport'); $template = new Template($templateName); $toolbar = Display::url( - Display::getMdiIcon('file-excel-outline'). - get_lang('Generate report'), + Display::returnFontAwesomeIcon('file-excel-o'). + get_lang('GenerateReport'), api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/export_report.php', - ['class' => 'btn btn--primary'] + ['class' => 'btn btn-primary'] ); if ('true' === $paypalEnable && 'true' === $commissionsEnable) { @@ -198,7 +219,6 @@ $template->assign('sale_status_pending', BuyCoursesPlugin::SALE_STATUS_PENDING); $template->assign('sale_status_completed', BuyCoursesPlugin::SALE_STATUS_COMPLETED); $template->assign('invoicing_enable', $invoicingEnable); -$template->assign('showing_services', false); $content = $template->fetch('BuyCourses/view/sales_report.tpl'); diff --git a/public/plugin/BuyCourses/src/service_catalog.php b/public/plugin/BuyCourses/src/service_catalog.php index 05e559e25ab..45682e2cd16 100644 --- a/public/plugin/BuyCourses/src/service_catalog.php +++ b/public/plugin/BuyCourses/src/service_catalog.php @@ -1,4 +1,7 @@ validate()) { $formValues = $form->getSubmitValues(); - $nameFilter = $formValues['name'] ?? null; - $minFilter = $formValues['min'] ?? 0; - $maxFilter = $formValues['max'] ?? 0; - $appliesToFilter = $formValues['applies_to'] ?? ''; + $nameFilter = isset($formValues['name']) ? $formValues['name'] : null; + $minFilter = isset($formValues['min']) ? $formValues['min'] : 0; + $maxFilter = isset($formValues['max']) ? $formValues['max'] : 0; + $appliesToFilter = isset($formValues['applies_to']) ? $formValues['applies_to'] : ''; } $form->addHeader($plugin->get_lang('SearchFilter')); @@ -64,8 +67,7 @@ $serviceList = $plugin->getCatalogServiceList($first, $pageSize, $nameFilter, $minFilter, $maxFilter, $appliesToFilter); $totalItems = $plugin->getCatalogServiceList($first, $pageSize, $nameFilter, $minFilter, $maxFilter, $appliesToFilter, 'count'); $pagesCount = ceil($totalItems / $pageSize); -$url = api_get_self().'?'; -$pagination = Display::getPagination($url, $currentPage, $pagesCount, $totalItems); +$pagination = BuyCoursesPlugin::returnPagination(api_get_self(), $currentPage, $pagesCount, $totalItems); // View if (api_is_platform_admin()) { @@ -87,19 +89,12 @@ $templateName = $plugin->get_lang('ListOfServicesOnSale'); $tpl = new Template($templateName); $tpl->assign('search_filter_form', $form->returnForm()); -$tpl->assign('showing_courses', false); -$tpl->assign('showing_sessions', false); $tpl->assign('showing_services', true); $tpl->assign('services', $serviceList); $tpl->assign('sessions_are_included', $includeSessions); $tpl->assign('services_are_included', $includeServices); $tpl->assign('pagination', $pagination); -$countSessions = $plugin->getCatalogSessionList($first, $pageSize, $nameFilter, $minFilter, $maxFilter, 'count'); - -$tpl->assign('coursesExist', true); -$tpl->assign('sessionExist', $countSessions > 0); - $content = $tpl->fetch('BuyCourses/view/catalog.tpl'); $tpl->assign('header', $templateName); diff --git a/public/plugin/BuyCourses/src/service_error.php b/public/plugin/BuyCourses/src/service_error.php index 47b28a4c9c9..c2fa28039f2 100644 --- a/public/plugin/BuyCourses/src/service_error.php +++ b/public/plugin/BuyCourses/src/service_error.php @@ -1,7 +1,10 @@ getServiceSale($serviceSaleId); - $plugin->cancelServiceSale(intval($serviceSaleId)); + $plugin->cancelServiceSale((int) $serviceSaleId); Display::addFlash( Display::return_message( $plugin->get_lang('OrderCancelled'), @@ -21,11 +24,12 @@ ); header('Location: '.api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/service_catalog.php'); + exit; } Display::addFlash( - Display::return_message(get_lang('An error occurred.'), 'error', false) + Display::return_message($plugin->get_lang('ErrorOccurred'), 'error', false) ); header('Location: '.api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/service_catalog.php'); diff --git a/public/plugin/BuyCourses/src/service_information.php b/public/plugin/BuyCourses/src/service_information.php index fac301228d1..2e03c083f42 100644 --- a/public/plugin/BuyCourses/src/service_information.php +++ b/public/plugin/BuyCourses/src/service_information.php @@ -1,4 +1,6 @@ '; + WEB_PLUGIN_PATH +).'BuyCourses/resources/css/style.css"/>'; $plugin = BuyCoursesPlugin::create(); $includeServices = 'true' === $plugin->get('include_services'); diff --git a/public/plugin/BuyCourses/src/service_panel.php b/public/plugin/BuyCourses/src/service_panel.php index cc17b0d3c76..bfbc54fca9e 100644 --- a/public/plugin/BuyCourses/src/service_panel.php +++ b/public/plugin/BuyCourses/src/service_panel.php @@ -1,4 +1,7 @@ $plugin->get_lang('CourseListOnSale')] ); +$webPluginPath = api_get_path(WEB_PLUGIN_PATH); +$htmlHeadXtra[] = api_get_css($webPluginPath.'BuyCourses/resources/css/style.css'); +$htmlHeadXtra[] = api_get_js_simple($webPluginPath.'BuyCourses/resources/js/modals.js'); + $templateName = $plugin->get_lang('TabsDashboard'); $tpl = new Template($templateName); $tpl->assign('showing_courses', true); diff --git a/public/plugin/BuyCourses/src/service_process.php b/public/plugin/BuyCourses/src/service_process.php index a1748a8d9da..b510b3a6ed0 100644 --- a/public/plugin/BuyCourses/src/service_process.php +++ b/public/plugin/BuyCourses/src/service_process.php @@ -1,9 +1,10 @@ '; + WEB_PLUGIN_PATH +).'BuyCourses/resources/css/style.css"/>'; $em = Database::getManager(); $plugin = BuyCoursesPlugin::create(); $includeServices = $plugin->get('include_services'); -$paypalEnabled = 'true' === $plugin->get('paypal_enable'); -$transferEnabled = 'true' === $plugin->get('transfer_enable'); -$culqiEnabled = 'true' === $plugin->get('culqi_enable'); $additionalQueryString = ''; if ('true' !== $includeServices) { api_not_allowed(true); @@ -45,23 +44,16 @@ $typeFinalLp = BuyCoursesPlugin::SERVICE_TYPE_LP_FINAL_ITEM === $type; $queryString = 'i='.$serviceId.'&t='.$type.$additionalQueryString; -$serviceInfo = $plugin->getService($serviceId); -$userInfo = api_get_user_info($currentUserId); - -$form = new FormValidator('confirm_sale'); -$paymentTypesOptions = $plugin->getPaymentTypes(); - -if (!$paypalEnabled) { - unset($paymentTypesOptions[BuyCoursesPlugin::PAYMENT_TYPE_PAYPAL]); +if (isset($_REQUEST['c'])) { + $couponCode = $_REQUEST['c']; + $coupon = $plugin->getCouponServiceByCode($couponCode, $_REQUEST['i']); } -if (!$transferEnabled) { - unset($paymentTypesOptions[BuyCoursesPlugin::PAYMENT_TYPE_TRANSFER]); -} +$serviceInfo = $plugin->getService($serviceId, $coupon); +$userInfo = api_get_user_info($currentUserId); -if (!$culqiEnabled) { - unset($paymentTypesOptions[BuyCoursesPlugin::PAYMENT_TYPE_CULQI]); -} +$form = new FormValidator('confirm_sale'); +$paymentTypesOptions = $plugin->getPaymentTypes(true); $form->addHtml( Display::return_message( @@ -96,14 +88,15 @@ if (!empty($users)) { /** @var User $user */ foreach ($users as $user) { - if (intval($userInfo['user_id']) !== intval($user->getId())) { - $selectOptions[$user->getId()] = $user->getFullNameWithUsername(); + if ((int) $userInfo['user_id'] !== (int) $user->getId()) { + $selectOptions[$user->getId()] = $user->getCompleteNameWithUsername(); } } } $form->addSelect('info_select', get_lang('User'), $selectOptions); } elseif ($typeCourse) { - $user = api_get_user_entity($currentUserId); + /** @var User $user */ + $user = UserManager::getRepository()->find($currentUserId); $courses = $user->getCourses(); $checker = false; foreach ($courses as $course) { @@ -121,17 +114,21 @@ $form->addSelect('info_select', get_lang('Course'), $selectOptions); } elseif ($typeSession) { $sessions = []; - $user = api_get_user_entity($currentUserId); - $userSubscriptions = $user->getSessionRelCourseRelUsers(); + /** @var User $user */ + $user = UserManager::getRepository()->find($currentUserId); + $userSubscriptions = $user->getSessionCourseSubscriptions(); + + /** @var SessionRelCourseRelUser $userSubscription */ foreach ($userSubscriptions as $userSubscription) { - $sessions[$userSubscription->getSession()->getId()] = $userSubscription->getSession()->getTitle(); + $sessions[$userSubscription->getSession()->getId()] = $userSubscription->getSession()->getName(); } - $sessionsAsGeneralCoach = $user->getSessionsAsGeneralCoach(); + $sessionsAsGeneralCoach = $user->getSessionAsGeneralCoach(); + /** @var Session $sessionAsGeneralCoach */ foreach ($sessionsAsGeneralCoach as $sessionAsGeneralCoach) { - $sessions[$sessionAsGeneralCoach->getId()] = $sessionAsGeneralCoach->getTitle(); + $sessions[$sessionAsGeneralCoach->getId()] = $sessionAsGeneralCoach->getName(); } if (!$sessions) { @@ -142,49 +139,51 @@ } } elseif ($typeFinalLp) { // We need here to check the current user courses first - $user = api_get_user_entity($currentUserId); + /** @var User $user */ + $user = UserManager::getRepository()->find($currentUserId); $courses = $user->getCourses(); $courseLpList = []; $sessionLpList = []; $checker = false; foreach ($courses as $course) { // Now get all the courses lp's - $thisLpList = $em->getRepository(CLp::class)->findBy(['cId' => $course->getCourse()->getId()]); + $thisLpList = $em->getRepository('ChamiloCourseBundle:CLp')->findBy(['cId' => $course->getCourse()->getId()]); foreach ($thisLpList as $lp) { - $courseLpList[$lp->getCId()] = $lp->getTitle().' ('.$course->getCourse()->getTitle().')'; + $courseLpList[$lp->getCId()] = $lp->getName().' ('.$course->getCourse()->getTitle().')'; } } // Here now checking the current user sessions - $sessions = $user->getSessionRelCourseRelUsers(); + $sessions = $user->getSessionCourseSubscriptions(); foreach ($sessions as $session) { $thisLpList = $em - ->getRepository(CLp::class) - ->findBy(['sessionId' => $session->getSession()->getId()]); + ->getRepository('ChamiloCourseBundle:CLp') + ->findBy(['sessionId' => $session->getSession()->getId()]) + ; // Here check all the lpItems foreach ($thisLpList as $lp) { - $thisLpItems = $em->getRepository(CLpItem::class)->findBy(['lpId' => $lp->getId()]); + $thisLpItems = $em->getRepository('ChamiloCourseBundle:CLpItem')->findBy(['lpId' => $lp->getId()]); foreach ($thisLpItems as $item) { - //Now only we need the final item and return the current LP + // Now only we need the final item and return the current LP if (TOOL_LP_FINAL_ITEM == $item->getItemType()) { $checker = true; - $sessionLpList[$lp->getCId()] = $lp->getTitle().' ('.$session->getSession()->getTitle().')'; + $sessionLpList[$lp->getCId()] = $lp->getName().' ('.$session->getSession()->getName().')'; } } } - $thisLpList = $em->getRepository(CLp::class)->findBy(['cId' => $session->getCourse()->getId()]); + $thisLpList = $em->getRepository('ChamiloCourseBundle:CLp')->findBy(['cId' => $session->getCourse()->getId()]); // Here check all the lpItems foreach ($thisLpList as $lp) { - $thisLpItems = $em->getRepository(CLpItem::class)->findBy(['lpId' => $lp->getId()]); + $thisLpItems = $em->getRepository('ChamiloCourseBundle:CLpItem')->findBy(['lpId' => $lp->getId()]); foreach ($thisLpItems as $item) { - //Now only we need the final item and return the current LP + // Now only we need the final item and return the current LP if (TOOL_LP_FINAL_ITEM == $item->getItemType()) { $checker = true; - $sessionLpList[$lp->getCId()] = $lp->getTitle().' ('.$session->getSession()->getTitle().')'; + $sessionLpList[$lp->getCId()] = $lp->getName().' ('.$session->getSession()->getName().')'; } } } @@ -199,11 +198,11 @@ ) ); } - $form->addSelect('info_select', get_lang('Learning path'), $selectOptions); + $form->addSelect('info_select', get_lang('LearningPath'), $selectOptions); } -$form->addHidden('t', intval($_GET['t'])); -$form->addHidden('i', intval($_GET['i'])); +$form->addHidden('t', (int) $_GET['t']); +$form->addHidden('i', (int) $_GET['i']); $form->addButton('submit', $plugin->get_lang('ConfirmOrder'), 'check', 'success'); if ($form->validate()) { @@ -214,6 +213,7 @@ Display::return_message($plugin->get_lang('NeedToSelectPaymentType'), 'error', false) ); header('Location:'.api_get_self().'?'.$queryString); + exit; } @@ -226,6 +226,7 @@ Display::return_message($plugin->get_lang('AdditionalInfoRequired'), 'error', false) ); header('Location:'.api_get_self().'?'.$queryString); + exit; } } @@ -233,15 +234,66 @@ $serviceSaleId = $plugin->registerServiceSale( $serviceId, $formValues['payment_type'], - $infoSelected + $infoSelected, + $formValues['c'] ); if (false !== $serviceSaleId) { $_SESSION['bc_service_sale_id'] = $serviceSaleId; + + if (isset($formValues['c'])) { + $couponSaleId = $plugin->registerCouponServiceSale($serviceSaleId, $formValues['c']); + if (false !== $couponSaleId) { + $plugin->updateCouponDelivered($formValues['c']); + $_SESSION['bc_coupon_id'] = $formValues['c']; + } + } + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/service_process_confirm.php'); } + + exit; +} + +$formCoupon = new FormValidator('confirm_coupon'); +if ($formCoupon->validate()) { + $formCouponValues = $formCoupon->getSubmitValues(); + + if (!$formCouponValues['coupon_code']) { + Display::addFlash( + Display::return_message($plugin->get_lang('NeedToAddCouponCode'), 'error', false) + ); + header('Location:'.api_get_self().'?'.$queryString); + + exit; + } + + $coupon = $plugin->getCouponServiceByCode($formCouponValues['coupon_code'], $formCouponValues['i']); + + if (null == $coupon) { + Display::addFlash( + Display::return_message($plugin->get_lang('CouponNotValid'), 'error', false) + ); + header('Location:'.api_get_self().'?'.$queryString); + + exit; + } + + Display::addFlash( + Display::return_message($plugin->get_lang('CouponRedeemed'), 'success', false) + ); + + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/service_process.php?i='.$_REQUEST['i'].'&t='.$_REQUEST['t'].'&c='.$formCouponValues['coupon_code']); + exit; } +$formCoupon->addText('coupon_code', $plugin->get_lang('CouponsCode'), true); +$formCoupon->addHidden('t', (int) $_GET['t']); +$formCoupon->addHidden('i', (int) $_GET['i']); +if (null != $coupon) { + $form->addHidden('c', (int) $coupon['id']); +} +$formCoupon->addButton('submit', $plugin->get_lang('RedeemCoupon'), 'check', 'success', 'btn-lg pull-right'); // View $templateName = $plugin->get_lang('PaymentMethods'); @@ -254,6 +306,7 @@ $tpl->assign('buying_service', true); $tpl->assign('service', $serviceInfo); $tpl->assign('user', api_get_user_info()); +$tpl->assign('form_coupon', $formCoupon->returnForm()); $tpl->assign('form', $form->returnForm()); $content = $tpl->fetch('BuyCourses/view/service_process.tpl'); $tpl->assign('content', $content); diff --git a/public/plugin/BuyCourses/src/service_process_confirm.php b/public/plugin/BuyCourses/src/service_process_confirm.php index adc8f73cde1..eb6dd2fa0c3 100644 --- a/public/plugin/BuyCourses/src/service_process_confirm.php +++ b/public/plugin/BuyCourses/src/service_process_confirm.php @@ -1,4 +1,6 @@ getServiceSale($serviceSaleId); +$serviceSale = $plugin->getServiceSale($serviceSaleId, $coupon); $userInfo = api_get_user_info($serviceSale['buyer']['id']); +if (!empty($couponId)) { + $coupon = $plugin->getCouponService($couponId, $serviceSale['service_id']); + $serviceSale['item'] = $plugin->getService($serviceSale['service_id'], $coupon); +} + if (empty($serviceSale)) { api_not_allowed(true); } @@ -44,10 +52,11 @@ // The extra params for handle the hard job, this var is VERY IMPORTANT !! $extra = ''; + require_once 'paypalfunctions.php'; $extra .= "&L_PAYMENTREQUEST_0_NAME0={$serviceSale['service']['name']}"; - $extra .= "&L_PAYMENTREQUEST_0_QTY0=1"; + $extra .= '&L_PAYMENTREQUEST_0_QTY0=1'; $extra .= "&L_PAYMENTREQUEST_0_AMT0=$itemPrice"; // Full Checkout express @@ -62,7 +71,7 @@ if ('Success' !== $expressCheckout['ACK']) { $erroMessage = vsprintf( - get_lang('An error occurred.'), + $plugin->get_lang('ErrorOccurred'), [$expressCheckout['L_ERRORCODE0'], $expressCheckout['L_LONGMESSAGE0']] ); Display::addFlash( @@ -71,6 +80,7 @@ $plugin->cancelServiceSale($serviceSale['id']); header('Location: '.api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/service_catalog.php'); + exit; } @@ -97,7 +107,9 @@ } RedirectToPayPal($expressCheckout['TOKEN']); + break; + case BuyCoursesPlugin::PAYMENT_TYPE_TRANSFER: $transferAccounts = $plugin->getTransferAccounts(); @@ -116,11 +128,13 @@ if (isset($formValues['cancel'])) { $plugin->cancelServiceSale($serviceSale['id']); - unset($_SESSION['bc_service_sale_id']); + unset($_SESSION['bc_service_sale_id'], $_SESSION['bc_coupon_id']); + Display::addFlash( Display::return_message($plugin->get_lang('OrderCancelled'), 'error', false) ); header('Location: '.api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/service_catalog.php'); + exit; } @@ -128,7 +142,7 @@ $messageTemplate->assign( 'service_sale', [ - 'name' => $serviceSale['service']['title'], + 'name' => $serviceSale['service']['name'], 'buyer' => $serviceSale['buyer']['name'], 'buy_date' => $serviceSale['buy_date'], 'start_date' => $serviceSale['start_date'], @@ -180,8 +194,10 @@ ) ); - unset($_SESSION['bc_service_sale_id']); + unset($_SESSION['bc_service_sale_id'], $_SESSION['bc_coupon_id']); + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/service_catalog.php'); + exit; } @@ -206,7 +222,7 @@ $template = new Template(); $template->assign('terms', $globalParameters['terms_and_conditions']); - $template->assign('title', $serviceSale['service']['title']); + $template->assign('title', $serviceSale['service']['name']); $template->assign('price', $serviceSale['price']); $template->assign('currency', $serviceSale['currency_id']); $template->assign('buying_service', $serviceSale); @@ -220,7 +236,9 @@ $template->assign('content', $content); $template->display_one_col_template(); + break; + case BuyCoursesPlugin::PAYMENT_TYPE_CULQI: // We need to include the main online script, acording to the Culqi documentation the JS needs to be loeaded // directly from the main url "https://integ-pago.culqi.com" because a local copy of this JS is not supported @@ -240,7 +258,7 @@ if (isset($formValues['cancel'])) { $plugin->cancelServiceSale($serviceSale['id']); - unset($_SESSION['bc_service_sale_id']); + unset($_SESSION['bc_service_sale_id'], $_SESSION['bc_coupon_id']); Display::addFlash( Display::return_message( @@ -251,6 +269,7 @@ ); header('Location: '.api_get_path(WEB_PLUGIN_PATH).'BuyCourses/index.php'); + exit; } } @@ -276,7 +295,7 @@ $template = new Template(); $template->assign('terms', $globalParameters['terms_and_conditions']); $template->assign('title', $serviceSale['service']['name']); - $template->assign('price', floatval($serviceSale['price'])); + $template->assign('price', (float) $serviceSale['price']); $template->assign('currency', $plugin->getSelectedCurrency()); $template->assign('buying_service', $serviceSale); $template->assign('user', $userInfo); @@ -288,5 +307,88 @@ $content = $template->fetch('BuyCourses/view/process_confirm.tpl'); $template->assign('content', $content); $template->display_one_col_template(); + + break; + + case BuyCoursesPlugin::PAYMENT_TYPE_TPV_CECABANK: + $cecabankParams = $plugin->getcecabankParams(); + $currency = $plugin->getCurrency($sale['currency_id']); + + $form = new FormValidator( + 'success', + 'POST', + api_get_self(), + null, + null, + FormValidator::LAYOUT_INLINE + ); + + if ($form->validate()) { + $formValues = $form->getSubmitValues(); + + if (isset($formValues['cancel'])) { + $plugin->cancelServiceSale($sale['id']); + + unset($_SESSION['bc_sale_id'], $_SESSION['bc_coupon_id']); + + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'BuyCourses/index.php'); + + exit; + } + + $urlTpv = $cecabankParams['merchart_id']; + $currency = $plugin->getCurrency($sale['currency_id']); + $signature = $plugin->getCecabankSignature($sale['reference'], $sale['price']); + + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + + echo ''; + + exit; + } + + $form->addButton( + 'confirm', + $plugin->get_lang('ConfirmOrder'), + 'check', + 'success', + 'default', + null, + ['id' => 'confirm'] + ); + $form->addButtonCancel($plugin->get_lang('CancelOrder'), 'cancel'); + + $template = new Template(); + $template->assign('terms', $globalParameters['terms_and_conditions']); + $template->assign('title', $serviceSale['service']['name']); + $template->assign('price', $serviceSale['price']); + $template->assign('currency', $serviceSale['currency_id']); + $template->assign('buying_service', $serviceSale); + $template->assign('user', $userInfo); + $template->assign('service', $serviceSale['service']); + $template->assign('service_item', $serviceSale['item']); + $template->assign('transfer_accounts', $transferAccounts); + $template->assign('form', $form->returnForm()); + + $content = $template->fetch('BuyCourses/view/process_confirm.tpl'); + + $template->assign('content', $content); + $template->display_one_col_template(); + break; } diff --git a/public/plugin/BuyCourses/src/service_sales_report.php b/public/plugin/BuyCourses/src/service_sales_report.php index c34253db1f2..53649a24343 100644 --- a/public/plugin/BuyCourses/src/service_sales_report.php +++ b/public/plugin/BuyCourses/src/service_sales_report.php @@ -1,4 +1,6 @@ addText('user', get_lang('User'), false, ['cols-size' => [0, 0, 0]]); $form->addButtonSearch(get_lang('Search'), 'search'); -$servicesSales = $plugin->getServiceSales(null, $selectedStatus); +$servicesSales = $plugin->getServiceSales(0, $selectedStatus); + +foreach ($servicesSales as &$sale) { + if (isset($sale['discount_amount']) && 0 != $sale['discount_amount']) { + $sale['total_discount'] = $plugin->getPriceWithCurrencyFromIsoCode($sale['discount_amount'], $sale['iso_code']); + $sale['coupon_code'] = $plugin->getServiceSaleCouponCode($sale['id']); + } +} + $interbreadcrumb[] = ['url' => '../index.php', 'name' => $plugin->get_lang('plugin_title')]; +$webPluginPath = api_get_path(WEB_PLUGIN_PATH); +$htmlHeadXtra[] = api_get_css($webPluginPath.'BuyCourses/resources/css/style.css'); +$htmlHeadXtra[] = api_get_css($webPluginPath.'BuyCourses/resources/js/modals.js'); + $templateName = $plugin->get_lang('SalesReport'); $template = new Template($templateName); +$toolbar = Display::url( + Display::returnFontAwesomeIcon('file-excel-o'). + get_lang('GenerateReport'), + api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/export_report.php', + ['class' => 'btn btn-primary'] +); + if ('true' == $paypalEnable && 'true' == $commissionsEnable) { - $toolbar = Display::toolbarButton( + $toolbar .= Display::toolbarButton( $plugin->get_lang('PaypalPayoutCommissions'), api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/paypal_payout.php', 'paypal', 'primary', ['title' => $plugin->get_lang('PaypalPayoutCommissions')] ); - - $template->assign( - 'actions', - Display::toolbarAction('toolbar', [$toolbar]) - ); } +$template->assign( + 'actions', + Display::toolbarAction('toolbar', [$toolbar]) +); + if ('true' == $commissionsEnable) { $toolbar = Display::toolbarButton( $plugin->get_lang('PayoutReport'), @@ -78,4 +99,5 @@ $template->assign('invoicing_enable', $invoicingEnable); $content = $template->fetch('BuyCourses/view/service_sales_report.tpl'); $template->assign('content', $content); +$template->assign('header', $templateName); $template->display_one_col_template(); diff --git a/public/plugin/BuyCourses/src/service_success.php b/public/plugin/BuyCourses/src/service_success.php index f3a3546e45b..1e3be758100 100644 --- a/public/plugin/BuyCourses/src/service_success.php +++ b/public/plugin/BuyCourses/src/service_success.php @@ -1,4 +1,6 @@ completeServiceSale($serviceSale['id']); if ($serviceSaleIsCompleted) { Display::addFlash( Display::return_message( - sprintf($plugin->get_lang('SubscriptionToServiceXSuccessful'), $serviceSale['service']['title']), + sprintf($plugin->get_lang('SubscriptionToServiceXSuccessful'), $serviceSale['service']['name']), 'success' ) ); @@ -95,45 +99,70 @@ Display::addFlash( Display::return_message($plugin->get_lang('ErrorContactPlatformAdmin'), 'error') ); + break; + case 'Pending': - switch ($confirmPayments["PAYMENTINFO_0_PENDINGREASON"]) { + switch ($confirmPayments['PAYMENTINFO_0_PENDINGREASON']) { case 'address': $purchaseStatus = $plugin->get_lang('PendingReasonByAddress'); + break; + case 'authorization': $purchaseStatus = $plugin->get_lang('PendingReasonByAuthorization'); + break; + case 'echeck': $purchaseStatus = $plugin->get_lang('PendingReasonByEcheck'); + break; + case 'intl': $purchaseStatus = $plugin->get_lang('PendingReasonByIntl'); + break; + case 'multicurrency': $purchaseStatus = $plugin->get_lang('PendingReasonByMulticurrency'); + break; + case 'order': $purchaseStatus = $plugin->get_lang('PendingReasonByOrder'); + break; + case 'paymentreview': $purchaseStatus = $plugin->get_lang('PendingReasonByPaymentReview'); + break; + case 'regulatoryreview': $purchaseStatus = $plugin->get_lang('PendingReasonByRegulatoryReview'); + break; + case 'unilateral': $purchaseStatus = $plugin->get_lang('PendingReasonByUnilateral'); + break; + case 'upgrade': $purchaseStatus = $plugin->get_lang('PendingReasonByUpgrade'); + break; + case 'verify': $purchaseStatus = $plugin->get_lang('PendingReasonByVerify'); + break; + case 'other': default: $purchaseStatus = $plugin->get_lang('PendingReasonByOther'); + break; } @@ -144,18 +173,22 @@ false ) ); + break; + default: - $plugin->cancelServiceSale(intval($serviceSale['id'])); + $plugin->cancelServiceSale((int) $serviceSale['id']); Display::addFlash( Display::return_message($plugin->get_lang('ErrorContactPlatformAdmin'), 'error') ); + break; } unset($_SESSION['bc_service_sale_id']); header('Location: '.api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/service_catalog.php'); + exit; } @@ -165,13 +198,13 @@ } $interbreadcrumb[] = [ - "url" => "service_catalog.php", - "name" => $plugin->get_lang('ListOfServicesOnSale'), + 'url' => 'service_catalog.php', + 'name' => $plugin->get_lang('ListOfServicesOnSale'), ]; $templateName = $plugin->get_lang('PaymentMethods'); $tpl = new Template($templateName); -$tpl->assign('title', $serviceSale['service']['title']); +$tpl->assign('title', $serviceSale['service']['name']); $tpl->assign('price', $serviceSale['price']); $tpl->assign('currency', $serviceSale['currency_id']); $tpl->assign('service', $serviceSale); diff --git a/public/plugin/BuyCourses/src/services_add.php b/public/plugin/BuyCourses/src/services_add.php index 4d2ee1e38e2..107700156c3 100644 --- a/public/plugin/BuyCourses/src/services_add.php +++ b/public/plugin/BuyCourses/src/services_add.php @@ -1,4 +1,6 @@ getId()] = $user->getFullNameWithUsername(); + $userOptions[$user->getId()] = $user->getCompleteNameWithUsername(); } } @@ -24,10 +26,14 @@ $htmlHeadXtra[] = api_get_css_asset('cropper/dist/cropper.min.css'); $htmlHeadXtra[] = api_get_asset('cropper/dist/cropper.min.js'); -//view +// view +$interbreadcrumb[] = [ + 'url' => api_get_path(WEB_PLUGIN_PATH).'BuyCourses/index.php', + 'name' => $plugin->get_lang('plugin_title'), +]; $interbreadcrumb[] = [ - 'url' => 'list.php', - 'name' => $plugin->get_lang('Configuration'), + 'url' => 'list_service.php', + 'name' => $plugin->get_lang('Services'), ]; $globalSettingsParams = $plugin->getGlobalParameters(); @@ -104,12 +110,12 @@ $form->addCheckBox('visibility', $plugin->get_lang('VisibleInCatalog')); $form->addFile( 'picture', - (get_lang( + get_lang( 'AddImage' - )), + ), ['id' => 'picture', 'class' => 'picture-form', 'crop_image' => true, 'crop_ratio' => '16 / 9'] ); -$form->addText('video_url', get_lang('Video URL'), false); +$form->addText('video_url', get_lang('VideoUrl'), false); $form->addHtmlEditor('service_information', $plugin->get_lang('ServiceInformation'), false); $form->addButtonSave(get_lang('Add')); $form->setDefaults($formDefaultValues); @@ -123,7 +129,8 @@ Display::return_message($plugin->get_lang('ServiceAdded'), 'success') ); - header('Location: list.php'); + header('Location: list_service.php'); + exit; } diff --git a/public/plugin/BuyCourses/src/services_edit.php b/public/plugin/BuyCourses/src/services_edit.php index c9cacee6a50..5cfb63a976f 100644 --- a/public/plugin/BuyCourses/src/services_edit.php +++ b/public/plugin/BuyCourses/src/services_edit.php @@ -1,4 +1,6 @@ getId()] = $user->getFullNameWithUsername(); + $userOptions[$user->getId()] = $user->getCompleteNameWithUsername(); } } @@ -29,23 +32,28 @@ $htmlHeadXtra[] = api_get_css_asset('cropper/dist/cropper.min.css'); $htmlHeadXtra[] = api_get_asset('cropper/dist/cropper.min.js'); -//view +// view +// breadcrumbs +$interbreadcrumb[] = [ + 'url' => api_get_path(WEB_PLUGIN_PATH).'BuyCourses/index.php', + 'name' => $plugin->get_lang('plugin_title'), +]; $interbreadcrumb[] = [ - 'url' => 'list.php', - 'name' => $plugin->get_lang('Configuration'), + 'url' => 'list_service.php', + 'name' => $plugin->get_lang('Services'), ]; $globalSettingsParams = $plugin->getGlobalParameters(); $service = $plugin->getService($serviceId); $formDefaultValues = [ - 'name' => $service['title'], + 'name' => $service['name'], 'description' => $service['description'], 'price' => $service['price'], 'tax_perc' => $service['tax_perc'], 'duration_days' => $service['duration_days'], - 'owner_id' => intval($service['owner_id']), - 'applies_to' => intval($service['applies_to']), + 'owner_id' => (int) $service['owner_id'], + 'applies_to' => (int) $service['applies_to'], 'visibility' => (1 == $service['visibility']) ? true : false, 'image' => is_file(api_get_path(SYS_PLUGIN_PATH).'BuyCourses/uploads/services/images/simg-'.$serviceId.'.png') ? api_get_path(WEB_PLUGIN_PATH).'BuyCourses/uploads/services/images/simg-'.$serviceId.'.png' @@ -107,7 +115,7 @@ 'radio', 'applies_to', null, - get_lang('Certificate of completion'), + get_lang('TemplateTitleCertificate'), 4 ); $form->addSelect( @@ -118,10 +126,10 @@ $form->addCheckBox('visibility', $plugin->get_lang('VisibleInCatalog')); $form->addFile( 'picture', - '' != $formDefaultValues['image'] ? get_lang('Update Image') : get_lang('Add image'), + '' != $formDefaultValues['image'] ? get_lang('UpdateImage') : get_lang('AddImage'), ['id' => 'picture', 'class' => 'picture-form', 'crop_image' => true, 'crop_ratio' => '16 / 9'] ); -$form->addText('video_url', get_lang('Video URL'), false); +$form->addText('video_url', get_lang('VideoUrl'), false); $form->addHtmlEditor('service_information', $plugin->get_lang('ServiceInformation'), false); $form->addHidden('id', $serviceId); $form->addButtonSave(get_lang('Edit')); @@ -142,7 +150,8 @@ Display::return_message($plugin->get_lang('ServiceEdited'), 'success') ); } - header('Location: list.php'); + header('Location: list_service.php'); + exit; } diff --git a/public/plugin/BuyCourses/src/session_catalog.php b/public/plugin/BuyCourses/src/session_catalog.php index 918eb61b21c..4596b200b8c 100644 --- a/public/plugin/BuyCourses/src/session_catalog.php +++ b/public/plugin/BuyCourses/src/session_catalog.php @@ -1,4 +1,6 @@ removeAttribute('class'); + if ($form->validate()) { $formValues = $form->getSubmitValues(); $nameFilter = isset($formValues['name']) ? $formValues['name'] : null; $minFilter = isset($formValues['min']) ? $formValues['min'] : 0; $maxFilter = isset($formValues['max']) ? $formValues['max'] : 0; + $sessionCategory = isset($formValues['session_category']) ? $formValues['session_category'] : $sessionCategory; } $form->addHeader($plugin->get_lang('SearchFilter')); -$form->addText('name', get_lang('Session name'), false); + +$categoriesOptions = [ + '0' => get_lang('AllCategories'), +]; +$categoriesList = SessionManager::get_all_session_category(); +if (false != $categoriesList) { + foreach ($categoriesList as $categoryItem) { + $categoriesOptions[$categoryItem['id']] = $categoryItem['name']; + } +} +$form->addSelect( + 'session_category', + get_lang('SessionCategory'), + $categoriesOptions, + [ + 'id' => 'session_category', + ] +); + +$form->addText('name', get_lang('SessionName'), false); + $form->addElement( 'number', 'min', @@ -53,14 +78,18 @@ $form->addHtml('
'); $form->addButtonFilter(get_lang('Search')); +$form->setDefaults( + [ + 'session_category' => $sessionCategory, + ] +); $pageSize = BuyCoursesPlugin::PAGINATION_PAGE_SIZE; $currentPage = isset($_GET['page']) ? (int) $_GET['page'] : 1; $first = $pageSize * ($currentPage - 1); -$sessionList = $plugin->getCatalogSessionList($first, $pageSize, $nameFilter, $minFilter, $maxFilter); -$totalItems = $plugin->getCatalogSessionList($first, $pageSize, $nameFilter, $minFilter, $maxFilter, 'count'); +$sessionList = $plugin->getCatalogSessionList($first, $pageSize, $nameFilter, $minFilter, $maxFilter, 'all', $sessionCategory); +$totalItems = $plugin->getCatalogSessionList($first, $pageSize, $nameFilter, $minFilter, $maxFilter, 'count', $sessionCategory); $pagesCount = ceil($totalItems / $pageSize); -$url = api_get_self().'?'; -$pagination = Display::getPagination($url, $currentPage, $pagesCount, $totalItems); +$pagination = BuyCoursesPlugin::returnPagination(api_get_self(), $currentPage, $pagesCount, $totalItems); // View if (api_is_platform_admin()) { @@ -76,6 +105,8 @@ $templateName = $plugin->get_lang('CourseListOnSale'); +$htmlHeadXtra[] = api_get_css(api_get_path(WEB_PLUGIN_PATH).'BuyCourses/resources/css/style.css'); + $template = new Template($templateName); $template->assign('search_filter_form', $form->returnForm()); $template->assign('sessions_are_included', $includeSessions); @@ -84,6 +115,11 @@ $template->assign('sessions', $sessionList); $template->assign('pagination', $pagination); +$countCourses = $plugin->getCatalogCourseList($first, $pageSize, $nameFilter, $minFilter, $maxFilter, 'count'); + +$template->assign('coursesExist', $countCourses > 0); +$template->assign('sessionExist', true); + $content = $template->fetch('BuyCourses/view/catalog.tpl'); $template->assign('header', $templateName); diff --git a/public/plugin/BuyCourses/src/session_panel.php b/public/plugin/BuyCourses/src/session_panel.php index 7e4362bc3d4..49876e30412 100644 --- a/public/plugin/BuyCourses/src/session_panel.php +++ b/public/plugin/BuyCourses/src/session_panel.php @@ -1,4 +1,6 @@ $plugin->get_lang('CourseListOnSale')] ); -$templateName = get_lang('Dashboard'); +$htmlHeadXtra[] = api_get_css(api_get_path(WEB_PLUGIN_PATH).'BuyCourses/resources/css/style.css'); + +$templateName = get_lang('TabsDashboard'); $tpl = new Template($templateName); $tpl->assign('showing_courses', true); $tpl->assign('sessions_are_included', $includeSessions); diff --git a/public/plugin/BuyCourses/src/stripe_cancel.php b/public/plugin/BuyCourses/src/stripe_cancel.php new file mode 100644 index 00000000000..0f033c313a5 --- /dev/null +++ b/public/plugin/BuyCourses/src/stripe_cancel.php @@ -0,0 +1,31 @@ +get('stripe_enable'); + +if (!$stripeEnabled) { + api_not_allowed(true); +} + +$sale = $plugin->getSale($_SESSION['bc_sale_id']); + +if (empty($sale)) { + api_not_allowed(true); +} + +Display::addFlash( + Display::return_message($plugin->get_lang('ErrorContactPlatformAdmin'), 'error') +); + +unset($_SESSION['bc_sale_id']); +header('Location: '.api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/course_catalog.php'); + +exit; diff --git a/public/plugin/BuyCourses/src/stripe_response.php b/public/plugin/BuyCourses/src/stripe_response.php new file mode 100644 index 00000000000..9ae34f81e06 --- /dev/null +++ b/public/plugin/BuyCourses/src/stripe_response.php @@ -0,0 +1,77 @@ +get('stripe_enable'); + +if (!$stripeEnabled) { + api_not_allowed(true); +} + +$stripeParams = $plugin->getStripeParams(); + +$payload = @file_get_contents('php://input'); +$sig_header = $_SERVER['HTTP_STRIPE_SIGNATURE']; +$event = null; + +try { + $event = Webhook::constructEvent( + $payload, + $sig_header, + $stripeParams['endpoint_secret'] + ); +} catch (UnexpectedValueException $e) { + http_response_code(400); + + exit; +} catch (SignatureVerificationException $e) { + http_response_code(400); + + exit; +} + +switch ($event->type) { + case 'checkout.session.completed': + $checkoutSession = $event->data->object; + + $sale = $plugin->getSaleFromReference($checkoutSession->id); + + if (empty($sale)) { + api_not_allowed(true); + } + + $buyingCourse = false; + $buyingSession = false; + + switch ($sale['product_type']) { + case BuyCoursesPlugin::PRODUCT_TYPE_COURSE: + $buyingCourse = true; + $course = $plugin->getCourseInfo($sale['product_id']); + + break; + + case BuyCoursesPlugin::PRODUCT_TYPE_SESSION: + $buyingSession = true; + $session = $plugin->getSessionInfo($sale['product_id']); + + break; + } + + $saleIsCompleted = $plugin->completeSale($sale['id']); + + if ($saleIsCompleted) { + $plugin->storePayouts($sale['id']); + } + + // no break + default: + echo 'Received unknown event type '.$event->type; +} + +http_response_code(200); diff --git a/public/plugin/BuyCourses/src/stripe_success.php b/public/plugin/BuyCourses/src/stripe_success.php new file mode 100644 index 00000000000..95d4c3cc6df --- /dev/null +++ b/public/plugin/BuyCourses/src/stripe_success.php @@ -0,0 +1,79 @@ +get('stripe_enable'); + +if (!$stripeEnabled) { + api_not_allowed(true); +} + +$sale = $plugin->getSale($_SESSION['bc_sale_id']); + +if (empty($sale)) { + api_not_allowed(true); +} + +$userInfo = api_get_user_info($sale['user_id']); +$currency = $plugin->getCurrency($sale['currency_id']); +$globalParameters = $plugin->getGlobalParameters(); + +if (!empty($globalParameters['sale_email'])) { + $messageConfirmBuyerTemplate = new Template(); + $messageConfirmBuyerTemplate->assign('user', $userInfo); + $messageConfirmBuyerTemplate->assign( + 'sale', + [ + 'date' => $sale['date'], + 'product' => $sale['product_name'], + 'currency' => $currency['iso_code'], + 'price' => $sale['price'], + 'reference' => $sale['reference'], + ] + ); + + api_mail_html( + $userInfo['complete_name'], + $userInfo['email'], + $plugin->get_lang('bc_subject'), + $messageConfirmBuyerTemplate->fetch('BuyCourses/view/message_confirm_buyer.tpl'), + '', + $globalParameters['sale_email'] + ); + + $messageConfirmTemplate = new Template(); + $messageConfirmTemplate->assign('user', $userInfo); + $messageConfirmTemplate->assign( + 'sale', + [ + 'date' => $sale['date'], + 'product' => $sale['product_name'], + 'currency' => $currency['iso_code'], + 'price' => $sale['price'], + 'reference' => $sale['reference'], + ] + ); + + api_mail_html( + '', + $globalParameters['sale_email'], + $plugin->get_lang('bc_subject'), + $messageConfirmTemplate->fetch('BuyCourses/view/message_confirm.tpl') + ); +} + +Display::addFlash( + $plugin->getSubscriptionSuccessMessage($sale) +); + +unset($_SESSION['bc_sale_id']); +header('Location: '.api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/course_catalog.php'); + +exit; diff --git a/public/plugin/BuyCourses/src/subscription_add.php b/public/plugin/BuyCourses/src/subscription_add.php new file mode 100644 index 00000000000..9522370b4c8 --- /dev/null +++ b/public/plugin/BuyCourses/src/subscription_add.php @@ -0,0 +1,238 @@ +get('include_sessions'); + +$entityManager = Database::getManager(); +$userRepo = UserManager::getRepository(); +$currency = $plugin->getSelectedCurrency(); + +if (empty($currency)) { + Display::addFlash( + Display::return_message($plugin->get_lang('CurrencyIsNotConfigured'), 'error') + ); +} + +$currencyIso = null; + +if ($editingCourse) { + $course = $entityManager->find(Course::class, $id); + if (!$course) { + api_not_allowed(true); + } + + $courseItem = $plugin->getCourseForConfiguration($course, $currency); + + $currencyIso = $courseItem['currency']; + $formDefaults = [ + 'product_type' => get_lang('Course'), + 'id' => $courseItem['course_id'], + 'type' => BuyCoursesPlugin::PRODUCT_TYPE_COURSE, + 'name' => $courseItem['course_title'], + 'visible' => $courseItem['visible'], + 'price' => $courseItem['price'], + 'tax_perc' => $courseItem['tax_perc'], + 'currency_id' => $currency['id'], + ]; +} elseif ($editingSession) { + if (!$includeSession) { + api_not_allowed(true); + } + + $session = $entityManager->find(Session::class, $id); + if (!$session) { + api_not_allowed(true); + } + + $sessionItem = $plugin->getSessionForConfiguration($session, $currency); + + $currencyIso = $sessionItem['currency']; + $formDefaults = [ + 'product_type' => get_lang('Session'), + 'id' => $session->getId(), + 'type' => BuyCoursesPlugin::PRODUCT_TYPE_SESSION, + 'name' => $sessionItem['session_name'], + 'visible' => $sessionItem['visible'], + 'price' => $sessionItem['price'], + 'tax_perc' => $sessionItem['tax_perc'], + 'currency_id' => $currency['id'], + ]; +} else { + api_not_allowed(true); +} + +$globalSettingsParams = $plugin->getGlobalParameters(); + +$form = new FormValidator('add_subscription'); + +$form->addText('product_type', $plugin->get_lang('ProductType'), false); +$form->addText('name', get_lang('Name'), false); + +$form->freeze(['product_type', 'name']); + +$form->addElement( + 'number', + 'tax_perc', + [$plugin->get_lang('TaxPerc'), $plugin->get_lang('TaxPercDescription'), '%'], + ['step' => 1, 'placeholder' => $globalSettingsParams['global_tax_perc'].'% '.$plugin->get_lang('ByDefault')] +); + +$frequencies = $plugin->getFrequencies(); + +$selectOptions = ''; +foreach ($frequencies as $key => $frequency) { + $selectOptions .= ''; +} + +if (empty($frequencies)) { + Display::addFlash( + Display::return_message($plugin->get_lang('FrequencyIsNotConfigured'), 'error') + ); +} + +$platformCommission = $plugin->getPlatformCommission(); +$form->addHtml( + ' +
+
+
+
+

'.$plugin->get_lang('FrequencyConfig').'

+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ +
+
+ '.$currencyIso.' +
+
+
+
+ Add +
+
+
+
+
+
+
+ + + + + + + + + +
'.$plugin->get_lang('Duration').''.$plugin->get_lang('Price').''.$plugin->get_lang('Actions').'
+ + + + + + +
+
+ + ' +); + +$form->addHidden('type', null); +$form->addHidden('id', null); +$button = $form->addButtonSave(get_lang('Save')); + +if (empty($currency) || empty($frequencies)) { + $button->setAttribute('disabled'); +} + +if ($form->validate()) { + $formValues = $form->getSubmitValues(); + $subscription['product_id'] = $formValues['id']; + $subscription['product_type'] = $formValues['type']; + $subscription['currency_id'] = $currency['id']; + $subscription['tax_perc'] = '' != $formValues['tax_perc'] ? (int) $formValues['tax_perc'] : null; + $subscription['frequencies'] = isset($formValues['frequencies']) ? $formValues['frequencies'] : []; + + $result = $plugin->addNewSubscription($subscription); + + if ($result) { + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/subscriptions_courses.php'); + } else { + header('Location:'.api_get_self().'?'.$queryString); + } + + exit; +} + +$form->setDefaults($formDefaults); + +$templateName = $plugin->get_lang('SubscriptionAdd'); +$interbreadcrumb[] = [ + 'url' => 'subscriptions_courses.php', + 'name' => get_lang('Configuration'), +]; +$interbreadcrumb[] = [ + 'url' => 'subscriptions_courses.php', + 'name' => $plugin->get_lang('SubscriptionList'), +]; + +$template = new Template($templateName); +$template->assign('header', $templateName); +$template->assign('items_form', $form->returnForm()); +$template->assign('currencyIso', $currencyIso); + +$content = $template->fetch('BuyCourses/view/subscription_add.tpl'); +$template->assign('content', $content); + +$template->display_one_col_template(); diff --git a/public/plugin/BuyCourses/src/subscription_course_catalog.php b/public/plugin/BuyCourses/src/subscription_course_catalog.php new file mode 100644 index 00000000000..1f232eea9ec --- /dev/null +++ b/public/plugin/BuyCourses/src/subscription_course_catalog.php @@ -0,0 +1,88 @@ +get('include_sessions'); +$includeServices = 'true' === $plugin->get('include_services'); + +$nameFilter = ''; + +$form = new FormValidator( + 'search_filter_form', + 'get', + null, + null, + [], + FormValidator::LAYOUT_INLINE +); + +if ($form->validate()) { + $formValues = $form->getSubmitValues(); + $nameFilter = isset($formValues['name']) ? $formValues['name'] : null; +} + +$form->addHeader($plugin->get_lang('SearchFilter')); +$form->addText('name', get_lang('CourseName'), false); +$form->addHtml('
'); +$form->addButtonFilter(get_lang('Search')); + +$pageSize = BuyCoursesPlugin::PAGINATION_PAGE_SIZE; +$currentPage = isset($_GET['page']) ? (int) $_GET['page'] : 1; +$first = $pageSize * ($currentPage - 1); +$courseList = $plugin->getCatalogSubscriptionCourseList($first, $pageSize, $nameFilter); +$totalItems = $plugin->getCatalogSubscriptionCourseList($first, $pageSize, $nameFilter, 'count'); +$pagesCount = ceil($totalItems / $pageSize); + +$pagination = BuyCoursesPlugin::returnPagination(api_get_self(), $currentPage, $pagesCount, $totalItems); + +// View +if (api_is_platform_admin()) { + $interbreadcrumb[] = [ + 'url' => 'subscriptions_courses.php', + 'name' => $plugin->get_lang('AvailableCoursesConfiguration'), + ]; + $interbreadcrumb[] = [ + 'url' => 'paymentsetup.php', + 'name' => $plugin->get_lang('PaymentsConfiguration'), + ]; +} else { + $interbreadcrumb[] = [ + 'url' => 'course_panel.php', + 'name' => get_lang('TabsDashboard'), + ]; +} + +$htmlHeadXtra[] = api_get_css(api_get_path(WEB_PLUGIN_PATH).'BuyCourses/resources/css/style.css'); + +$templateName = $plugin->get_lang('SubscriptionListOnSale'); +$tpl = new Template($templateName); +$tpl->assign('search_filter_form', $form->returnForm()); +$tpl->assign('showing_courses', true); +$tpl->assign('courses', $courseList); +$tpl->assign('sessions_are_included', $includeSessions); +$tpl->assign('pagination', $pagination); + +$sessionList = $plugin->getCatalogSubscriptionSessionList($first, $pageSize, $nameFilter, 'first', 0); +$coursesExist = true; +$sessionExist = true; +if (count($sessionList) <= 0) { + $sessionExist = false; +} + +$tpl->assign('coursesExist', $coursesExist); +$tpl->assign('sessionExist', $sessionExist); + +$content = $tpl->fetch('BuyCourses/view/subscription_catalog.tpl'); + +$tpl->assign('header', $templateName); +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/public/plugin/BuyCourses/src/subscription_process.php b/public/plugin/BuyCourses/src/subscription_process.php new file mode 100644 index 00000000000..4343c8a7771 --- /dev/null +++ b/public/plugin/BuyCourses/src/subscription_process.php @@ -0,0 +1,272 @@ +'; +$plugin = BuyCoursesPlugin::create(); +$includeSession = 'true' === $plugin->get('include_sessions'); +$paypalEnabled = 'true' === $plugin->get('paypal_enable'); +$transferEnabled = 'true' === $plugin->get('transfer_enable'); +$culqiEnabled = 'true' === $plugin->get('culqi_enable'); +$tpvRedsysEnable = 'true' === $plugin->get('tpv_redsys_enable'); + +if (!$paypalEnabled && !$transferEnabled && !$culqiEnabled && !$tpvRedsysEnable) { + api_not_allowed(true); +} + +if (!isset($_REQUEST['t'], $_REQUEST['i'])) { + api_not_allowed(true); +} + +$buyingCourse = BuyCoursesPlugin::PRODUCT_TYPE_COURSE === (int) $_REQUEST['t']; +$buyingSession = BuyCoursesPlugin::PRODUCT_TYPE_SESSION === (int) $_REQUEST['t']; +$queryString = 'i='.(int) $_REQUEST['i'].'&t='.(int) $_REQUEST['t']; + +if (isset($_REQUEST['c'])) { + $couponCode = $_REQUEST['c']; + if ($buyingCourse) { + $coupon = $plugin->getCouponByCode($couponCode, BuyCoursesPlugin::PRODUCT_TYPE_COURSE, $_REQUEST['i']); + } else { + $coupon = $plugin->getCouponByCode($couponCode, BuyCoursesPlugin::PRODUCT_TYPE_SESSION, $_REQUEST['i']); + } + + $queryString .= 'c='.$coupon['code']; +} + +if (isset($_REQUEST['d'])) { + $duration = $_REQUEST['d']; + if ($buyingCourse) { + $subscriptionItem = $plugin->getSubscription(BuyCoursesPlugin::PRODUCT_TYPE_COURSE, $_REQUEST['i'], $duration, $coupon); + } else { + $subscriptionItem = $plugin->getSubscription(BuyCoursesPlugin::PRODUCT_TYPE_SESSION, $_REQUEST['i'], $duration, $coupon); + } +} + +if (empty($currentUserId)) { + Session::write('buy_course_redirect', api_get_self().'?'.$queryString); + header('Location: '.api_get_path(WEB_CODE_PATH).'auth/inscription.php'); + + exit; +} + +$subscriptionItems = $plugin->getSubscriptionsItemsByProduct($_REQUEST['i'], $_REQUEST['t']); + +if (!isset($subscriptionItems) || empty($subscriptionItems)) { + api_not_allowed(true); +} + +if (!isset($subscriptionItem) || empty($subscriptionItem)) { + $subscriptionItem = $plugin->getSubscription($subscriptionItems[0]['product_type'], $subscriptionItems[0]['product_id'], $subscriptionItems[0]['duration'], $coupon); +} + +$queryString .= 'd='.(int) $subscriptionItem['duration']; + +if ($buyingCourse) { + $courseInfo = $plugin->getSubscriptionCourseInfo($_REQUEST['i'], $coupon); + $item = $plugin->getSubscriptionItemByProduct($_REQUEST['i'], BuyCoursesPlugin::PRODUCT_TYPE_COURSE); +} elseif ($buyingSession) { + $sessionInfo = $plugin->getSubscriptionSessionInfo($_REQUEST['i'], $coupon); + $item = $plugin->getSubscriptionItemByProduct($_REQUEST['i'], BuyCoursesPlugin::PRODUCT_TYPE_SESSION); +} + +$form = new FormValidator('confirm_sale'); +if ($form->validate()) { + $formValues = $form->getSubmitValues(); + + if (!$formValues['payment_type']) { + Display::addFlash( + Display::return_message($plugin->get_lang('NeedToSelectPaymentType'), 'error', false) + ); + header('Location:'.api_get_self().'?'.$queryString); + + exit; + } + + $saleId = $plugin->registerSubscriptionSale($item['product_id'], $item['product_type'], $formValues['payment_type'], $formValues['d'], $formValues['c']); + + if (false !== $saleId) { + $_SESSION['bc_sale_id'] = $saleId; + + if (isset($formValues['c'])) { + $couponSaleId = $plugin->registerCouponSubscriptionSale($saleId, $formValues['c']); + if (false !== $couponSaleId) { + $plugin->updateCouponDelivered($formValues['c']); + $_SESSION['bc_coupon_id'] = $formValues['c']; + } + } + + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/process_subscription_confirm.php'); + } + + exit; +} + +$paymentTypesOptions = $plugin->getPaymentTypes(true); + +$count = count($paymentTypesOptions); +if (0 === $count) { + $form->addHtml($plugin->get_lang('NoPaymentOptionAvailable')); + $form->addHtml('
'); + $form->addHtml('
'); +} elseif (1 === $count) { + // get the only array item + foreach ($paymentTypesOptions as $type => $value) { + $form->addHtml(sprintf($plugin->get_lang('XIsOnlyPaymentMethodAvailable'), $value)); + $form->addHtml('
'); + $form->addHtml('
'); + $form->addHidden('payment_type', $type); + } +} else { + $form->addHtml( + Display::return_message( + $plugin->get_lang('PleaseSelectThePaymentMethodBeforeConfirmYourOrder'), + 'info' + ) + ); + $form->addRadio('payment_type', null, $paymentTypesOptions); +} + +$form->addHidden('t', (int) $_GET['t']); +$form->addHidden('i', (int) $_GET['i']); +if (null != $coupon) { + $form->addHidden('c', (int) $coupon['id']); +} +$form->addButton('submit', $plugin->get_lang('ConfirmOrder'), 'check', 'success', 'btn-lg pull-right'); + +$formSubscription = new FormValidator('confirm_subscription'); +if ($formSubscription->validate()) { + $formSubscriptionValues = $formSubscription->getSubmitValues(); + + if (!$formSubscriptionValues['duration']) { + Display::addFlash( + Display::return_message($plugin->get_lang('NeedToAddDuration'), 'error', false) + ); + header('Location:'.api_get_self().'?'.$queryString); + + exit; + } + + if ($buyingCourse) { + $subscription = $plugin->getSubscription(BuyCoursesPlugin::PRODUCT_TYPE_COURSE, $_REQUEST['i'], $formSubscriptionValues['duration']); + } else { + $subscription = $plugin->getSubscription(BuyCoursesPlugin::PRODUCT_TYPE_SESSION, $_REQUEST['i'], $formSubscriptionValues['duration']); + } + + if (null == $subscription) { + Display::addFlash( + Display::return_message($plugin->get_lang('SubscriptionNotValid'), 'error', false) + ); + header('Location:'.api_get_self().'?'.$queryString); + + exit; + } + + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/subscription_process.php?i='.$_REQUEST['i'].'&t='.$_REQUEST['t'].'&d='.$formSubscriptionValues['duration']); + + exit; +} + +$frequencies = $plugin->getFrequencies(); +$selectedFrequencies = []; + +foreach ($subscriptionItems as $si) { + if (isset($frequencies[$si['duration']])) { + $selectedFrequencies[$si['duration']] = $frequencies[$si['duration']].' - '.$si['price_formatted'].' '.$si['iso_code']; + } +} + +$formSubscription->addRadio('duration', null, $selectedFrequencies); + +if (!empty($selectedFrequencies)) { + $formSubscriptionDefaults['duration'] = $subscriptionItem['duration']; + $formSubscription->setDefaults($formSubscriptionDefaults); +} + +$selectedDurationName = $frequencies[$subscriptionItem['duration']]; + +$formSubscription->addHidden('t', (int) $_GET['t']); +$formSubscription->addHidden('i', (int) $_GET['i']); + +$form->addHidden('d', $subscriptionItem['duration']); + +$formCoupon = new FormValidator('confirm_coupon'); +if ($formCoupon->validate()) { + $formCouponValues = $formCoupon->getSubmitValues(); + + if (!$formCouponValues['coupon_code']) { + Display::addFlash( + Display::return_message($plugin->get_lang('NeedToAddCouponCode'), 'error', false) + ); + header('Location:'.api_get_self().'?'.$queryString); + + exit; + } + + if ($buyingCourse) { + $coupon = $plugin->getCouponByCode($formCouponValues['coupon_code'], BuyCoursesPlugin::PRODUCT_TYPE_COURSE, $_REQUEST['i']); + } else { + $coupon = $plugin->getCouponByCode($formCouponValues['coupon_code'], BuyCoursesPlugin::PRODUCT_TYPE_SESSION, $_REQUEST['i']); + } + + if (null == $coupon) { + Display::addFlash( + Display::return_message($plugin->get_lang('CouponNotValid'), 'error', false) + ); + header('Location:'.api_get_self().'?'.$queryString); + + exit; + } + + Display::addFlash( + Display::return_message($plugin->get_lang('CouponRedeemed'), 'success', false) + ); + + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/subscription_process.php?i='.$_REQUEST['i'].'&t='.$_REQUEST['t'].'&d='.$_REQUEST['d'].'&c='.$formCouponValues['coupon_code']); + + exit; +} +$formCoupon->addText('coupon_code', $plugin->get_lang('CouponsCode'), true); +$formCoupon->addHidden('t', (int) $_GET['t']); +$formCoupon->addHidden('i', (int) $_GET['i']); +$formCoupon->addHidden('d', $subscriptionItem['duration']); +$formCoupon->addButton('submit', $plugin->get_lang('RedeemCoupon'), 'check', 'success', 'btn-lg pull-right'); + +// View +$templateName = $plugin->get_lang('PaymentMethods'); +$interbreadcrumb[] = ['url' => 'subscription_course_catalog.php', 'name' => $plugin->get_lang('CourseListOnSale')]; + +$tpl = new Template($templateName); +$tpl->assign('item_type', (int) $_GET['t']); +$tpl->assign('buying_course', $buyingCourse); +$tpl->assign('buying_session', $buyingSession); +$tpl->assign('user', api_get_user_info()); +$tpl->assign('form_coupon', $formCoupon->returnForm()); +$tpl->assign('message_payment', $messagePayment); +$tpl->assign('selected_duration_name', $selectedDurationName); +$tpl->assign('form', $form->returnForm()); +$tpl->assign('form_subscription', $formSubscription->returnForm()); + +if ($buyingCourse) { + $tpl->assign('course', $courseInfo); +} elseif ($buyingSession) { + $tpl->assign('session', $sessionInfo); +} + +$tpl->assign('subscription', $subscriptionItem); + +$content = $tpl->fetch('BuyCourses/view/subscription_process.tpl'); + +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/public/plugin/BuyCourses/src/subscription_sales_report.php b/public/plugin/BuyCourses/src/subscription_sales_report.php new file mode 100644 index 00000000000..4a34a9a27ed --- /dev/null +++ b/public/plugin/BuyCourses/src/subscription_sales_report.php @@ -0,0 +1,212 @@ +get('paypal_enable'); +$commissionsEnable = $plugin->get('commissions_enable'); +$includeServices = $plugin->get('include_services'); +$invoicingEnable = 'true' === $plugin->get('invoicing_enable'); + +if (isset($_GET['order'])) { + $sale = $plugin->getSubscriptionSale($_GET['order']); + + if (empty($sale)) { + api_not_allowed(true); + } + + $urlToRedirect = api_get_self().'?'; + + switch ($_GET['action']) { + case 'confirm': + $plugin->completeSubscriptionSale($sale['id']); + $plugin->storeSubscriptionPayouts($sale['id']); + Display::addFlash( + $plugin->getSubscriptionSuccessMessage($sale) + ); + + $urlToRedirect .= http_build_query([ + 'status' => BuyCoursesPlugin::SALE_STATUS_COMPLETED, + 'sale' => $sale['id'], + ]); + + break; + + case 'cancel': + $plugin->cancelSubscriptionSale($sale['id']); + + Display::addFlash( + Display::return_message( + $plugin->get_lang('OrderCanceled'), + 'warning' + ) + ); + + $urlToRedirect .= http_build_query([ + 'status' => BuyCoursesPlugin::SALE_STATUS_CANCELED, + 'sale' => $sale['id'], + ]); + + break; + } + + header("Location: $urlToRedirect"); + + exit; +} + +$productTypes = $plugin->getProductTypes(); +$saleStatuses = $plugin->getSaleStatuses(); +$paymentTypes = $plugin->getPaymentTypes(); + +$selectedFilterType = '0'; +$selectedStatus = isset($_GET['status']) ? $_GET['status'] : BuyCoursesPlugin::SALE_STATUS_PENDING; +$selectedSale = isset($_GET['sale']) ? (int) ($_GET['sale']) : 0; +$dateStart = isset($_GET['date_start']) ? $_GET['date_start'] : date('Y-m-d H:i', mktime(0, 0, 0)); +$dateEnd = isset($_GET['date_end']) ? $_GET['date_end'] : date('Y-m-d H:i', mktime(23, 59, 59)); +$searchTerm = ''; +$email = ''; + +$form = new FormValidator('search', 'get'); + +if ($form->validate()) { + $selectedFilterType = $form->getSubmitValue('filter_type'); + $selectedStatus = $form->getSubmitValue('status'); + $searchTerm = $form->getSubmitValue('user'); + $dateStart = $form->getSubmitValue('date_start'); + $dateEnd = $form->getSubmitValue('date_end'); + $email = $form->getSubmitValue('email'); + + if (false === $selectedStatus) { + $selectedStatus = BuyCoursesPlugin::SALE_STATUS_PENDING; + } + + if (false === $selectedFilterType) { + $selectedFilterType = '0'; + } +} + +$form->addRadio( + 'filter_type', + get_lang('Filter'), + [ + $plugin->get_lang('ByStatus'), + $plugin->get_lang('ByUser'), + $plugin->get_lang('ByDate'), + $plugin->get_lang('ByEmail'), + ] +); +$form->addHtml('
'); +$form->addSelect('status', $plugin->get_lang('OrderStatus'), $saleStatuses); +$form->addHtml('
'); +$form->addHtml('
'); +$form->addText('user', get_lang('UserName'), false); +$form->addHtml('
'); +$form->addHtml('
'); +$form->addDateRangePicker('date', get_lang('Date'), false); +$form->addHtml('
'); +$form->addHtml('
'); +$form->addText('email', get_lang('Email'), false); +$form->addHtml('
'); +$form->addButtonFilter(get_lang('Search')); +$form->setDefaults([ + 'filter_type' => $selectedFilterType, + 'status' => $selectedStatus, + 'date_start' => $dateStart, + 'date_end' => $dateEnd, + 'email' => $email, +]); + +switch ($selectedFilterType) { + case '0': + $sales = $plugin->getSubscriptionSaleListByStatus($selectedStatus); + + break; + + case '1': + $sales = $plugin->getSubscriptionSaleListByUser($searchTerm); + + break; + + case '2': + $sales = $plugin->getSubscriptionSaleListByDate($dateStart, $dateEnd); + + break; + + case '3': + $sales = $plugin->getSubscriptionSaleListByEmail($email); + + break; +} + +foreach ($sales as &$sale) { + $sale['product_type'] = $productTypes[$sale['product_type']]; + $sale['payment_type'] = $paymentTypes[$sale['payment_type']]; + $sale['complete_user_name'] = api_get_person_name($sale['firstname'], $sale['lastname']); + $sale['num_invoice'] = $plugin->getNumInvoice($sale['id'], 0); + $sale['total_price'] = $plugin->getPriceWithCurrencyFromIsoCode($sale['price'], $sale['iso_code']); + if (isset($sale['discount_amount']) && 0 != $sale['discount_amount']) { + $sale['total_discount'] = $plugin->getPriceWithCurrencyFromIsoCode($sale['discount_amount'], $sale['iso_code']); + $sale['coupon_code'] = $plugin->getSaleCouponCode($sale['id']); + } +} + +$interbreadcrumb[] = ['url' => '../index.php', 'name' => $plugin->get_lang('plugin_title')]; + +$htmlHeadXtra[] = api_get_css(api_get_path(WEB_PLUGIN_PATH).'BuyCourses/resources/css/style.css'); + +$templateName = $plugin->get_lang('SalesReport'); +$template = new Template($templateName); + +$toolbar = Display::url( + Display::returnFontAwesomeIcon('file-excel-o'). + get_lang('GenerateReport'), + api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/export_subscription_report.php', + ['class' => 'btn btn-primary'] +); + +if ('true' === $paypalEnable && 'true' === $commissionsEnable) { + $toolbar .= Display::toolbarButton( + $plugin->get_lang('PaypalPayoutCommissions'), + api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/paypal_payout.php', + 'paypal', + 'primary', + ['title' => $plugin->get_lang('PaypalPayoutCommissions')] + ); + + $template->assign( + 'actions', + Display::toolbarAction('toolbar', [$toolbar]) + ); +} + +$template->assign( + 'actions', + Display::toolbarAction('toolbar', [$toolbar]) +); +$template->assign('form', $form->returnForm()); +$template->assign('selected_sale', $selectedSale); +$template->assign('selected_status', $selectedStatus); +$template->assign('services_are_included', $includeServices); +$template->assign('sale_list', $sales); +$template->assign('sale_status_canceled', BuyCoursesPlugin::SALE_STATUS_CANCELED); +$template->assign('sale_status_pending', BuyCoursesPlugin::SALE_STATUS_PENDING); +$template->assign('sale_status_completed', BuyCoursesPlugin::SALE_STATUS_COMPLETED); +$template->assign('invoicing_enable', $invoicingEnable); + +$content = $template->fetch('BuyCourses/view/subscription_sales_report.tpl'); + +$template->assign('header', $templateName); +$template->assign('content', $content); +$template->display_one_col_template(); diff --git a/public/plugin/BuyCourses/src/subscription_session_catalog.php b/public/plugin/BuyCourses/src/subscription_session_catalog.php new file mode 100644 index 00000000000..22fd95538ef --- /dev/null +++ b/public/plugin/BuyCourses/src/subscription_session_catalog.php @@ -0,0 +1,114 @@ +get('include_sessions'); +$includeServices = 'true' === $plugin->get('include_services'); + +if (!$includeSessions) { + api_not_allowed(true); +} + +$nameFilter = null; +$sessionCategory = isset($_GET['session_category']) ? (int) $_GET['session_category'] : 0; +$form = new FormValidator( + 'search_filter_form', + 'get', + null, + null, + [], + FormValidator::LAYOUT_INLINE +); + +$form->removeAttribute('class'); + +if ($form->validate()) { + $formValues = $form->getSubmitValues(); + $nameFilter = isset($formValues['name']) ? $formValues['name'] : null; + $sessionCategory = isset($formValues['session_category']) ? $formValues['session_category'] : $sessionCategory; +} + +$form->addHeader($plugin->get_lang('SearchFilter')); + +$categoriesOptions = [ + '0' => get_lang('AllCategories'), +]; +$categoriesList = SessionManager::get_all_session_category(); +if (false != $categoriesList) { + foreach ($categoriesList as $categoryItem) { + $categoriesOptions[$categoryItem['id']] = $categoryItem['name']; + } +} +$form->addSelect( + 'session_category', + get_lang('SessionCategory'), + $categoriesOptions, + [ + 'id' => 'session_category', + ] +); + +$form->addText('name', get_lang('SessionName'), false); + +$form->addHtml('
'); +$form->addButtonFilter(get_lang('Search')); + +$form->setDefaults( + [ + 'session_category' => $sessionCategory, + ] +); +$pageSize = BuyCoursesPlugin::PAGINATION_PAGE_SIZE; +$currentPage = isset($_GET['page']) ? (int) $_GET['page'] : 1; +$first = $pageSize * ($currentPage - 1); +$sessionList = $plugin->getCatalogSubscriptionSessionList($first, $pageSize, $nameFilter, 'all', $sessionCategory); +$totalItems = $plugin->getCatalogSubscriptionSessionList($first, $pageSize, $nameFilter, 'count', $sessionCategory); +$pagesCount = ceil($totalItems / $pageSize); +$pagination = BuyCoursesPlugin::returnPagination(api_get_self(), $currentPage, $pagesCount, $totalItems); + +// View +if (api_is_platform_admin()) { + $interbreadcrumb[] = [ + 'url' => 'subscriptions_sessions.php', + 'name' => $plugin->get_lang('AvailableCoursesConfiguration'), + ]; + $interbreadcrumb[] = [ + 'url' => 'paymentsetup.php', + 'name' => $plugin->get_lang('PaymentsConfiguration'), + ]; +} + +$templateName = $plugin->get_lang('CourseListOnSale'); + +$htmlHeadXtra[] = api_get_css(api_get_path(WEB_PLUGIN_PATH).'BuyCourses/resources/css/style.css'); + +$template = new Template($templateName); +$template->assign('search_filter_form', $form->returnForm()); +$template->assign('sessions_are_included', $includeSessions); +$template->assign('showing_sessions', true); +$template->assign('sessions', $sessionList); +$template->assign('pagination', $pagination); + +$courseList = $plugin->getCatalogSubscriptionCourseList($first, $pageSize, $nameFilter); +$coursesExist = true; +$sessionExist = true; +if (count($courseList) <= 0) { + $coursesExist = false; +} +$template->assign('coursesExist', $coursesExist); +$template->assign('sessionExist', $sessionExist); + +$content = $template->fetch('BuyCourses/view/subscription_catalog.tpl'); + +$template->assign('header', $templateName); +$template->assign('content', $content); +$template->display_one_col_template(); diff --git a/public/plugin/BuyCourses/src/subscriptions_courses.php b/public/plugin/BuyCourses/src/subscriptions_courses.php new file mode 100644 index 00000000000..d5a2473f489 --- /dev/null +++ b/public/plugin/BuyCourses/src/subscriptions_courses.php @@ -0,0 +1,97 @@ +get('include_sessions'); +$taxEnable = 'true' === $plugin->get('tax_enable'); + +api_protect_admin_script(true); + +Display::addFlash( + Display::return_message( + get_lang('Info').' - '.$plugin->get_lang('CoursesInSessionsDoesntDisplayHere'), + 'info' + ) +); + +$pageSize = BuyCoursesPlugin::PAGINATION_PAGE_SIZE; +$type = isset($_GET['type']) ? (int) $_GET['type'] : BuyCoursesPlugin::PRODUCT_TYPE_COURSE; +$currentPage = isset($_GET['page']) ? (int) $_GET['page'] : 1; +$first = $pageSize * ($currentPage - 1); + +$qb = $plugin->getCourses($first, $pageSize); +$query = $qb->getQuery(); +$courses = new Paginator($query, $fetchJoinCollection = true); +foreach ($courses as $course) { + $item = $plugin->getSubscriptionItemByProduct($course->getId(), BuyCoursesPlugin::PRODUCT_TYPE_COURSE); + $course->buyCourseData = []; + if (false !== $item) { + $course->buyCourseData = $item; + } +} + +$totalItems = count($courses); +$pagesCount = ceil($totalItems / $pageSize); + +$pagination = BuyCoursesPlugin::returnPagination( + api_get_self(), + $currentPage, + $pagesCount, + $totalItems, + ['type' => $type] +); + +// breadcrumbs +$interbreadcrumb[] = [ + 'url' => api_get_path(WEB_PLUGIN_PATH).'BuyCourses/index.php', + 'name' => $plugin->get_lang('plugin_title'), +]; + +$templateName = $plugin->get_lang('AvailableCourses'); + +$htmlHeadXtra[] = api_get_css(api_get_path(WEB_PLUGIN_PATH).'BuyCourses/resources/css/style.css'); + +$tpl = new Template($templateName); + +$toolbar = Display::url( + Display::returnFontAwesomeIcon('fa-calendar-alt'). + $plugin->get_lang('ConfigureSubscriptionsFrequencies'), + api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/configure_frequency.php', + ['class' => 'btn btn-primary'] +); + +$tpl->assign( + 'actions', + Display::toolbarAction('toolbar', [$toolbar]) +); + +$tpl->assign('product_type_course', BuyCoursesPlugin::PRODUCT_TYPE_COURSE); +$tpl->assign('product_type_session', BuyCoursesPlugin::PRODUCT_TYPE_SESSION); +$tpl->assign('courses', $courses); +$tpl->assign('course_pagination', $pagination); +$tpl->assign('sessions_are_included', $includeSession); +$tpl->assign('tax_enable', $taxEnable); + +if ($taxEnable) { + $globalParameters = $plugin->getGlobalParameters(); + $tpl->assign('global_tax_perc', $globalParameters['global_tax_perc']); + $tpl->assign('tax_applies_to', $globalParameters['tax_applies_to']); + $tpl->assign('tax_name', $globalParameters['tax_name']); +} + +$content = $tpl->fetch('BuyCourses/view/subscriptions.tpl'); + +$tpl->assign('header', $templateName); +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/public/plugin/BuyCourses/src/subscriptions_sessions.php b/public/plugin/BuyCourses/src/subscriptions_sessions.php new file mode 100644 index 00000000000..190c588d53f --- /dev/null +++ b/public/plugin/BuyCourses/src/subscriptions_sessions.php @@ -0,0 +1,101 @@ +get('include_sessions'); + +if (!$includeSession) { + api_not_allowed(true); +} +$includeServices = 'true' === $plugin->get('include_services'); +$taxEnable = 'true' === $plugin->get('tax_enable'); + +api_protect_admin_script(true); + +Display::addFlash( + Display::return_message( + get_lang('Info').' - '.$plugin->get_lang('CoursesInSessionsDoesntDisplayHere'), + 'info' + ) +); + +$pageSize = BuyCoursesPlugin::PAGINATION_PAGE_SIZE; +$currentPage = isset($_GET['page']) ? (int) $_GET['page'] : 1; +$first = $pageSize * ($currentPage - 1); + +// breadcrumbs +$interbreadcrumb[] = [ + 'url' => api_get_path(WEB_PLUGIN_PATH).'BuyCourses/index.php', + 'name' => $plugin->get_lang('plugin_title'), +]; + +$htmlHeadXtra[] = api_get_css(api_get_path(WEB_PLUGIN_PATH).'BuyCourses/resources/css/style.css'); + +$templateName = $plugin->get_lang('AvailableCourses'); + +$tpl = new Template($templateName); + +$toolbar = Display::url( + Display::returnFontAwesomeIcon('fa-calendar-alt'). + $plugin->get_lang('ConfigureSubscriptionsFrequencies'), + api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/configure_frequency.php', + ['class' => 'btn btn-primary'] +); + +$tpl->assign( + 'actions', + Display::toolbarAction('toolbar', [$toolbar]) +); + +$tpl->assign('product_type_course', BuyCoursesPlugin::PRODUCT_TYPE_COURSE); +$tpl->assign('product_type_session', BuyCoursesPlugin::PRODUCT_TYPE_SESSION); +$tpl->assign('sessions_are_included', $includeSession); +$tpl->assign('services_are_included', $includeServices); +$tpl->assign('tax_enable', $taxEnable); + +$query = CoursesAndSessionsCatalog::browseSessions(null, ['start' => $first, 'length' => $pageSize], true); +$sessions = new Paginator($query, $fetchJoinCollection = true); +foreach ($sessions as $session) { + $item = $plugin->getSubscriptionItemByProduct($session->getId(), BuyCoursesPlugin::PRODUCT_TYPE_SESSION); + $session->buyCourseData = []; + if (false !== $item) { + $session->buyCourseData = $item; + } +} + +$totalItems = count($sessions); +$pagesCount = ceil($totalItems / $pageSize); + +$pagination = BuyCoursesPlugin::returnPagination( + api_get_self(), + $currentPage, + $pagesCount, + $totalItems, + ['type' => BuyCoursesPlugin::PRODUCT_TYPE_SESSION] +); + +$tpl->assign('sessions', $sessions); +$tpl->assign('session_pagination', $pagination); + +if ($taxEnable) { + $globalParameters = $plugin->getGlobalParameters(); + $tpl->assign('global_tax_perc', $globalParameters['global_tax_perc']); + $tpl->assign('tax_applies_to', $globalParameters['tax_applies_to']); + $tpl->assign('tax_name', $globalParameters['tax_name']); +} + +$content = $tpl->fetch('BuyCourses/view/subscriptions.tpl'); + +$tpl->assign('header', $templateName); +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/public/plugin/BuyCourses/src/success.php b/public/plugin/BuyCourses/src/success.php index 06e8f2d669d..e4edee22bf9 100644 --- a/public/plugin/BuyCourses/src/success.php +++ b/public/plugin/BuyCourses/src/success.php @@ -1,4 +1,6 @@ getCourseInfo($sale['product_id']); + break; + case BuyCoursesPlugin::PRODUCT_TYPE_SESSION: $buyingSession = true; $session = $plugin->getSessionInfo($sale['product_id']); + break; } @@ -39,7 +44,7 @@ $paypalPassword = $paypalParams['password']; $paypalSignature = $paypalParams['signature']; -require_once "paypalfunctions.php"; +require_once 'paypalfunctions.php'; $form = new FormValidator( 'success', @@ -58,6 +63,7 @@ $plugin->cancelSale($sale['id']); unset($_SESSION['bc_sale_id']); header('Location: '.api_get_path(WEB_PLUGIN_PATH).'BuyCourses/index.php'); + exit; } @@ -65,13 +71,14 @@ if ('Success' !== $confirmPayments['ACK']) { $erroMessage = vsprintf( - get_lang('An error occurred.'), + $plugin->get_lang('ErrorOccurred'), [$expressCheckout['L_ERRORCODE0'], $confirmPayments['L_LONGMESSAGE0']] ); Display::addFlash( Display::return_message($erroMessage, 'error', false) ); header('Location: ../index.php'); + exit; } @@ -86,51 +93,77 @@ $plugin->getSubscriptionSuccessMessage($sale) ); $plugin->storePayouts($sale['id']); + break; } Display::addFlash( Display::return_message($plugin->get_lang('ErrorContactPlatformAdmin'), 'error') ); + break; + case 'Pending': - switch ($confirmPayments["PAYMENTINFO_0_PENDINGREASON"]) { + switch ($confirmPayments['PAYMENTINFO_0_PENDINGREASON']) { case 'address': $purchaseStatus = $plugin->get_lang('PendingReasonByAddress'); + break; + case 'authorization': $purchaseStatus = $plugin->get_lang('PendingReasonByAuthorization'); + break; + case 'echeck': $purchaseStatus = $plugin->get_lang('PendingReasonByEcheck'); + break; + case 'intl': $purchaseStatus = $plugin->get_lang('PendingReasonByIntl'); + break; + case 'multicurrency': $purchaseStatus = $plugin->get_lang('PendingReasonByMulticurrency'); + break; + case 'order': $purchaseStatus = $plugin->get_lang('PendingReasonByOrder'); + break; + case 'paymentreview': $purchaseStatus = $plugin->get_lang('PendingReasonByPaymentReview'); + break; + case 'regulatoryreview': $purchaseStatus = $plugin->get_lang('PendingReasonByRegulatoryReview'); + break; + case 'unilateral': $purchaseStatus = $plugin->get_lang('PendingReasonByUnilateral'); + break; + case 'upgrade': $purchaseStatus = $plugin->get_lang('PendingReasonByUpgrade'); + break; + case 'verify': $purchaseStatus = $plugin->get_lang('PendingReasonByVerify'); + break; + case 'other': default: $purchaseStatus = $plugin->get_lang('PendingReasonByOther'); + break; } @@ -141,16 +174,20 @@ false ) ); + break; + default: Display::addFlash( Display::return_message($plugin->get_lang('ErrorContactPlatformAdmin'), 'error') ); + break; } unset($_SESSION['bc_sale_id']); header('Location: '.api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/course_catalog.php'); + exit; } @@ -171,10 +208,11 @@ Display::return_message($erroMessage, 'error', false) ); header('Location: ../index.php'); + exit; } -$interbreadcrumb[] = ["url" => "course_catalog.php", "name" => $plugin->get_lang('CourseListOnSale')]; +$interbreadcrumb[] = ['url' => 'course_catalog.php', 'name' => $plugin->get_lang('CourseListOnSale')]; $templateName = $plugin->get_lang('PaymentMethods'); $tpl = new Template($templateName); diff --git a/public/plugin/BuyCourses/src/tpv_error.php b/public/plugin/BuyCourses/src/tpv_error.php new file mode 100644 index 00000000000..36885442c72 --- /dev/null +++ b/public/plugin/BuyCourses/src/tpv_error.php @@ -0,0 +1,31 @@ +get('tpv_redsys_enable'); + +if (!$tpvRedsysEnabled) { + api_not_allowed(true); +} + +$sale = $plugin->getSale($_SESSION['bc_sale_id']); + +if (empty($sale)) { + api_not_allowed(true); +} + +Display::addFlash( + Display::return_message($plugin->get_lang('ErrorContactPlatformAdmin'), 'error') +); + +unset($_SESSION['bc_sale_id']); +header('Location: '.api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/course_catalog.php'); + +exit; diff --git a/public/plugin/BuyCourses/src/tpv_response.php b/public/plugin/BuyCourses/src/tpv_response.php new file mode 100644 index 00000000000..a4b27fdc423 --- /dev/null +++ b/public/plugin/BuyCourses/src/tpv_response.php @@ -0,0 +1,71 @@ +get('tpv_redsys_enable'); + +if (!$tpvRedsysEnabled) { + api_not_allowed(true); +} + +$tpvRedsysParams = $plugin->getTpvRedsysParams(); + +$version = $_POST['Ds_SignatureVersion']; +$params = $_POST['Ds_MerchantParameters']; +$signatureReceived = $_POST['Ds_Signature']; + +require_once '../resources/apiRedsys.php'; +$tpv = new RedsysAPI(); + +$decodec = $tpv->decodeMerchantParameters($params); +$kc = $tpvRedsysParams['kc']; +$signature = $tpv->createMerchantSignatureNotif($kc, $params); + +if ($signature === $signatureReceived) { + $saleId = (int) $tpv->getParameter('Ds_Order'); + $response = $tpv->getParameter('Ds_Response'); + + // other fields available + // $Ds_Amount=$miObj->getParameter("Ds_Amount"); + // $Ds_MerchantCode=$miObj->getParameter("Ds_MerchantCode"); + // $Ds_TransactionType=$miObj->getParameter("Ds_TransactionType"); + // $Ds_MerchantData=$miObj->getParameter("Ds_MerchantData"); + // $Ds_Date=$miObj->getParameter("Ds_Date"); + // $Ds_Hour=$miObj->getParameter("Ds_Hour"); + + $sale = $plugin->getSale($saleId); + if (empty($sale)) { + api_not_allowed(true); + } + + $buyingCourse = false; + $buyingSession = false; + + switch ($sale['product_type']) { + case BuyCoursesPlugin::PRODUCT_TYPE_COURSE: + $buyingCourse = true; + $course = $plugin->getCourseInfo($sale['product_id']); + + break; + + case BuyCoursesPlugin::PRODUCT_TYPE_SESSION: + $buyingSession = true; + $session = $plugin->getSessionInfo($sale['product_id']); + + break; + } + + if ('0000' == $response) { + $saleIsCompleted = $plugin->completeSale($sale['id']); + if ($saleIsCompleted) { + $plugin->storePayouts($sale['id']); + } + } +} diff --git a/public/plugin/BuyCourses/src/tpv_success.php b/public/plugin/BuyCourses/src/tpv_success.php new file mode 100644 index 00000000000..7398effcaa8 --- /dev/null +++ b/public/plugin/BuyCourses/src/tpv_success.php @@ -0,0 +1,32 @@ +get('tpv_redsys_enable'); + +if (!$tpvRedsysEnabled) { + api_not_allowed(true); +} + +$sale = $plugin->getSale($_SESSION['bc_sale_id']); + +if (empty($sale)) { + api_not_allowed(true); +} + +Display::addFlash( + $plugin->getSubscriptionSuccessMessage($sale) +); +// $plugin->storePayouts($sale['id']); + +unset($_SESSION['bc_sale_id']); +header('Location: '.api_get_path(WEB_PLUGIN_PATH).'BuyCourses/src/course_catalog.php'); + +exit; diff --git a/public/plugin/BuyCourses/uninstall.php b/public/plugin/BuyCourses/uninstall.php index d3198980edd..3d1bf3a4326 100644 --- a/public/plugin/BuyCourses/uninstall.php +++ b/public/plugin/BuyCourses/uninstall.php @@ -1,4 +1,6 @@ {% elseif course.enrolled == "NO" %} @@ -87,13 +87,13 @@
- {{ session.name }}

- {{ session.name }} + {{ session.title }}

{% if 'show_session_coach'|api_get_setting == 'true' %}

{{ session.coach }}

@@ -107,34 +107,34 @@ {% endif %}

- - {{ session.item.total_price_formatted }} - + + {{ session.item.total_price_formatted }} +

+
    + {% for course in session.courses %} +
  • + {{ course.title }} + {% if course.coaches|length %} +
      + {% for coach in course.coaches %} +
    • {{ coach }}
    • + {% endfor %} +
    + {% endif %} +
  • + {% endfor %} +
+ --> {% if session.enrolled == "YES" %}
{{ 'TheUserIsAlreadyRegisteredInTheSession'|get_plugin_lang('BuyCoursesPlugin') }}
{% elseif session.enrolled == "NO" %} @@ -163,7 +163,7 @@

+ href="{{ url('index') ~ 'service/' ~ service.id }}"> {{ service.name }}

@@ -196,17 +196,17 @@
  • {{ service.owner_name }}
  • - - {{ service.total_price_formatted }} - + + {{ service.total_price_formatted }} +

    diff --git a/public/plugin/BuyCourses/view/configure_frequency.tpl b/public/plugin/BuyCourses/view/configure_frequency.tpl new file mode 100644 index 00000000000..4880e7b4e65 --- /dev/null +++ b/public/plugin/BuyCourses/view/configure_frequency.tpl @@ -0,0 +1,34 @@ +{% autoescape false %} +
    +
    + {{ items_form }} +
    +
    +
    + + + + + + + + + + {% for frequency in frequencies_list %} + + + + + + {% endfor %} + +
    {{ 'Name'|get_lang }}{{ 'SubscriptionPeriodDuration'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'Options'|get_lang }}
    {{ frequency.name }}{{ frequency.duration }} +
    + + + +
    +
    +
    +{% endautoescape %} diff --git a/public/plugin/BuyCourses/view/configure_subscription.tpl b/public/plugin/BuyCourses/view/configure_subscription.tpl new file mode 100644 index 00000000000..6668415be4a --- /dev/null +++ b/public/plugin/BuyCourses/view/configure_subscription.tpl @@ -0,0 +1,46 @@ +{% autoescape false %} +
    +
    + {{ items_form }} +
    +
    +
    +
    +

    {{ 'FrequencyConfig'|get_plugin_lang('BuyCoursesPlugin') }}

    +
    +
    +
    +
    + {{ frequency_form }} +
    +
    +
    + + + + + + + + + + {% for subscription in subscriptions %} + + + + + + {% endfor %} + +
    {{ 'Duration'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'Price'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'Actions'|get_lang }}
    {{ subscription.durationName }}{{ subscription.price }} {{ currencyIso }} + + + +
    +
    +
    +
    +
    +
    +{% endautoescape %} diff --git a/public/plugin/BuyCourses/view/coupons.tpl b/public/plugin/BuyCourses/view/coupons.tpl new file mode 100644 index 00000000000..53c269a3fbe --- /dev/null +++ b/public/plugin/BuyCourses/view/coupons.tpl @@ -0,0 +1,41 @@ +{% autoescape false %} +
    +
    +{{ form }} + +
    + + + + + + + + + + + + + + {% for coupon in coupon_list %} + + + + + + + + + + {% endfor %} + +
    {{ 'CouponCode'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'CouponDiscountType'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'CouponDiscount'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'CouponDateStart'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'CouponDateEnd'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'CouponDelivered'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'Options'|get_lang }}
    {{ coupon.code }}{{ coupon.discount_type }}{{ coupon.discount_value }}{{ coupon.valid_start | api_get_local_time }}{{ coupon.valid_end | api_get_local_time }}{{ coupon.delivered }} +
    + + + +
    +
    +
    +{% endautoescape %} diff --git a/public/plugin/BuyCourses/view/course_panel.tpl b/public/plugin/BuyCourses/view/course_panel.tpl index de2dca4ef26..ff483989d64 100644 --- a/public/plugin/BuyCourses/view/course_panel.tpl +++ b/public/plugin/BuyCourses/view/course_panel.tpl @@ -1,6 +1,4 @@ {% autoescape false %} - -
    @@ -65,7 +65,7 @@
    -
    @@ -76,7 +76,7 @@ var id = this.id; $.ajax({ data: 'id=' + id, - url: '{{ url('index') ~ 'plugin/BuyCourses/src/buycourses.ajax.php?' ~ { 'a': 'saleInfo' }|url_encode() }}', + url: '{{ url('index') }}plugin/BuyCourses/src/buycourses.ajax.php?{{ { 'a': 'saleInfo' }|url_encode }}', type: 'POST', success: function (response) { $("#contentSale").html(response); @@ -98,4 +98,4 @@ }); -{% endautoescape %} \ No newline at end of file +{% endautoescape %} diff --git a/public/plugin/BuyCourses/view/paypal_payout.tpl b/public/plugin/BuyCourses/view/paypal_payout.tpl index f1c22469f60..7d143f386b3 100644 --- a/public/plugin/BuyCourses/view/paypal_payout.tpl +++ b/public/plugin/BuyCourses/view/paypal_payout.tpl @@ -29,7 +29,7 @@ {% endif %} + class="btn btn-danger fa fa-ban cancelPayout"> {{ 'CancelPayout'| get_plugin_lang('BuyCoursesPlugin') }} {% endfor %} @@ -47,18 +47,18 @@
    -
    @@ -80,7 +80,7 @@ $.ajax({ data: {payouts: val}, - url: '{{ url('index') ~ 'plugin/BuyCourses/src/buycourses.ajax.php?' ~ { 'a': 'processPayout' }|url_encode() }}', + url: '{{ url('index') ~ 'plugin/BuyCourses/src/buycourses.ajax.php?' ~ { 'a': 'processPayout' }|url_encode }}', type: 'POST', success: function (response) { $("#content").html(response); @@ -117,7 +117,7 @@ var id = this.id; $.ajax({ data: 'id=' + id, - url: '{{ url('index') ~ 'plugin/BuyCourses/src/buycourses.ajax.php?' ~ { 'a': 'cancelPayout' }|url_encode() }}', + url: '{{ url('index') ~ 'plugin/BuyCourses/src/buycourses.ajax.php?' ~ { 'a': 'cancelPayout' }|url_encode }}', type: 'POST', success: function () { window.location.reload(); diff --git a/public/plugin/BuyCourses/view/process.tpl b/public/plugin/BuyCourses/view/process.tpl index 2da6fb2f319..6aeaa557fe4 100644 --- a/public/plugin/BuyCourses/view/process.tpl +++ b/public/plugin/BuyCourses/view/process.tpl @@ -1,6 +1,14 @@ {% autoescape false %}
    - + {% if item_type == 1 %} + {% set back_url = url('index') ~ 'plugin/BuyCourses/src/course_catalog.php' %} + {% elseif item_type == 2 %} + {% set back_url = url('index') ~ 'plugin/BuyCourses/src/session_catalog.php' %} + {% else %} + {% set back_url = url('index') ~ 'plugin/BuyCourses/src/service_catalog.php' %} + {% endif %} + + {{ @@ -17,7 +25,7 @@
    + href="{{ _p.web_ajax ~ 'course_home.ajax.php?' ~ {'a': 'show_course_information', 'code': course.code}|url_encode() }}"> {{ course.title }} @@ -34,12 +42,24 @@ {{ 'Total'|get_plugin_lang('BuyCoursesPlugin') }} : {{ course.item.total_price_formatted }}
    + {% if course.item.has_coupon %} +
    + {{ 'DiscountAmount'|get_plugin_lang('BuyCoursesPlugin') }}: + {{ course.item.discount_amount_formatted }} +
    + {% endif %} +
    + {{ 'DoYouHaveACoupon'|get_plugin_lang('BuyCoursesPlugin') }} +
    +
    + {{ form_coupon }} +

    + href="{{ _p.web_ajax ~ 'course_home.ajax.php?' ~ {'a': 'show_course_information', 'code': course.code}|url_encode() }}"> {{ course.title }}

    @@ -55,7 +75,7 @@ {{ 'Teachers'|get_plugin_lang('BuyCoursesPlugin') }} : {% for teacher in course.teachers %} - {{ teacher.name }}, {% endfor %}

    @@ -82,6 +102,18 @@ {{ 'Total'|get_plugin_lang('BuyCoursesPlugin') }} : {{ session.item.total_price_formatted }}
    + {% if session.item.has_coupon %} +
    + {{ 'DiscountAmount'|get_plugin_lang('BuyCoursesPlugin') }}: + {{ session.item.discount_amount_formatted }} +
    + {% endif %} +
    + {{ 'DoYouHaveACoupon'|get_plugin_lang('BuyCoursesPlugin') }} +
    +
    + {{ form_coupon }} +
    @@ -106,7 +138,7 @@ {% for coach in course.coaches %} - {{ coach.name }}, {% endfor %}

    diff --git a/public/plugin/BuyCourses/view/process_confirm.tpl b/public/plugin/BuyCourses/view/process_confirm.tpl index 96c965f1f4f..7d9ccf6b5d1 100644 --- a/public/plugin/BuyCourses/view/process_confirm.tpl +++ b/public/plugin/BuyCourses/view/process_confirm.tpl @@ -11,7 +11,7 @@
    + href="{{ _p.web_ajax ~ 'course_home.ajax.php?' ~ {'a': 'show_course_information', 'code': course.code}|url_encode() }}"> {{ course.title }} @@ -19,7 +19,7 @@

    {{ course.title }} + href="{{ _p.web_ajax ~ 'course_home.ajax.php?' ~ {'a': 'show_course_information', 'code': course.code}|url_encode() }}">{{ course.title }}

      {% for teacher in course.teachers %} @@ -70,16 +70,16 @@ {% elseif buying_service %}

      - {{ service.title }} + {{ service.name }}

        {% if service.applies_to == 0 %} @@ -166,7 +166,7 @@ {% for account in transfer_accounts %} - {{ account.title }} + {{ account.name }} {{ account.account }} {{ account.swift }} diff --git a/public/plugin/BuyCourses/view/sales_report.tpl b/public/plugin/BuyCourses/view/sales_report.tpl index d7c0d3527f3..9d0d5df9016 100644 --- a/public/plugin/BuyCourses/view/sales_report.tpl +++ b/public/plugin/BuyCourses/view/sales_report.tpl @@ -1,5 +1,4 @@ {% autoescape false %} -

        {{ form }} -
        - - - - - - - - - - - - - {% if invoicing_enable %} - - {% endif %} - - - - - {% for sale in sale_list %} - - - - - - - - - - - {% if invoicing_enable %} - - {% endif %} - - - {% endfor %} - +
        +
        {{ 'OrderReference'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'OrderStatus'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'OrderDate'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'PaymentMethod'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'Price'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'ProductType'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'Name'|get_lang }}{{ 'UserName'|get_lang }}{{ 'Email'|get_lang }}{{ 'Invoice'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'Options'|get_lang }}
        {{ sale.reference }} - {% if sale.status == sale_status_canceled %} - {{ 'SaleStatusCanceled'|get_plugin_lang('BuyCoursesPlugin') }} - {% elseif sale.status == sale_status_pending %} - {{ 'SaleStatusPending'|get_plugin_lang('BuyCoursesPlugin') }} - {% elseif sale.status == sale_status_completed %} - {{ 'SaleStatusCompleted'|get_plugin_lang('BuyCoursesPlugin') }} - {% endif %} - {{ sale.date | api_get_local_time }}{{ sale.payment_type }}{{ sale.total_price }}{{ sale.product_type }}{{ sale.product_name }}{{ sale.complete_user_name }}{{ sale.email }} - {% if sale.invoice == 1 %} - - {{ 'InvoiceView'|get_plugin_lang('BuyCoursesPlugin') }} -
        {{ sale.num_invoice }} -
        - {% endif %} -
        - {% if sale.status == sale_status_pending %} - - {% endif %} -
        +
        +{% endautoescape %} diff --git a/public/plugin/BuyCourses/view/subscription_catalog.tpl b/public/plugin/BuyCourses/view/subscription_catalog.tpl new file mode 100644 index 00000000000..92621ac6e81 --- /dev/null +++ b/public/plugin/BuyCourses/view/subscription_catalog.tpl @@ -0,0 +1,124 @@ +{% autoescape false %} +
        + {% if sessions_are_included %} + + {% endif %} + +
        +
        +
        +
        + {{ search_filter_form }} +
        +
        +
        + {% if showing_courses %} + {% for course in courses %} +
        +
        +
        +
        + {{ course.title }} +
        +
        +
        + {% set course_description_url = _p.web_ajax ~ 'course_home.ajax.php?' ~ {'code': course.code, 'a': 'show_course_information'}|url_encode() %} +

        + {{ course.title }} +

        +
          + {% for teacher in course.teachers %} +
        • {{ teacher }}
        • + {% endfor %} +
        + {% if course.enrolled == "YES" %} +
        + {{ 'TheUserIsAlreadyRegisteredInTheCourse'|get_plugin_lang('BuyCoursesPlugin') }} +
        + {% elseif course.enrolled == "NO" %} + + {% elseif course.enrolled == "TMP" %} +
        {{ 'WaitingToReceiveThePayment'|get_plugin_lang('BuyCoursesPlugin') }}
        + {% endif %} +
        +
        +
        + {% endfor %} + {% endif %} + + {% if showing_sessions %} + {% for session in sessions %} +
        +
        +
        +
        + {{ session.title }} +
        +
        +
        +

        + {{ session.title }} +

        + {% if 'show_session_coach'|api_get_setting == 'true' %} +

        {{ session.coach }}

        + {% endif %} +

        + + {% if session.duration %} + {{ 'SessionDurationXDaysTotal'|get_lang|format(session.duration) }} + {% else %} + {{ session.dates.display }} + {% endif %} +

        + {% if session.enrolled == "YES" %} +
        + {{ 'TheUserIsAlreadyRegisteredInTheSession'|get_plugin_lang('BuyCoursesPlugin') }} +
        + {% elseif session.enrolled == "NO" %} + + {% elseif session.enrolled == "TMP" %} +
        {{ 'WaitingToReceiveThePayment'|get_plugin_lang('BuyCoursesPlugin') }}
        + {% endif %} +
        +
        +
        + {% endfor %} + {% endif %} +
        + {{ pagination }} +
        +
        +
        +
        +
        +{% endautoescape %} diff --git a/public/plugin/BuyCourses/view/subscription_process.tpl b/public/plugin/BuyCourses/view/subscription_process.tpl new file mode 100644 index 00000000000..5ce4bd3521c --- /dev/null +++ b/public/plugin/BuyCourses/view/subscription_process.tpl @@ -0,0 +1,189 @@ +{% autoescape false %} +
        + {% if item_type == 1 %} + {% set back_url = url('index') ~ 'plugin/BuyCourses/src/subscription_course_catalog.php' %} + {% elseif item_type == 2 %} + {% set back_url = url('index') ~ 'plugin/BuyCourses/src/subscription_session_catalog.php' %} + {% endif %} + + + {{ + +
        + +
        +
        +
        +
        +
        + {% if buying_course %} +
        +
        + + {{ course.title }} + + {% if course.tax_enable %} +
        + {{ 'Price'|get_plugin_lang('BuyCoursesPlugin') }} : + {{ subscription.price_formatted }} +
        + {{ course.tax_name }} ({{ subscription.item.tax_perc_show }}%): + {{ subscription.tax_amount_formatted }} +
        + {% endif %} +
        + {{ 'Total'|get_plugin_lang('BuyCoursesPlugin') }} : + {{ subscription.total_price_formatted }} +
        + {% if course.has_coupon %} +
        + {{ 'DiscountAmount'|get_plugin_lang('BuyCoursesPlugin') }}: + {{ course.discount_amount_formatted }} +
        + {% endif %} +
        + {{ 'SelecSubscription'|get_plugin_lang('BuyCoursesPlugin') }} +
        +
        + {{ form_subscription }} +
        +
        + {{ 'DoYouHaveACoupon'|get_plugin_lang('BuyCoursesPlugin') }} +
        +
        + {{ form_coupon }} +
        +
        +
        +
        +

        + + {{ course.title }} + +

        + {% if course.description %} +
        + {{ course.description }} +
        + {% endif %} + + {% if course.teachers %} +
        +

        + {{ 'Teachers'|get_plugin_lang('BuyCoursesPlugin') }} : + {% for teacher in course.teachers %} + + {{ teacher.name }}, + {% endfor %} +

        +
        + {% endif %} +
        +
        +
        + {% elseif buying_session %} +
        +
        + {{ session.title }} + {% if session.tax_enable %} +
        + {{ 'Price'|get_plugin_lang('BuyCoursesPlugin') }} : + {{ subscription.price_formatted }} +
        + {{ session.tax_name }} ({{ subscription.item.tax_perc_show }}%): + {{ subscription.tax_amount_formatted }} +
        + {% endif %} +
        + {{ 'Total'|get_plugin_lang('BuyCoursesPlugin') }} : + {{ subscription.total_price_formatted }} +
        + {% if session.has_coupon %} +
        + {{ 'DiscountAmount'|get_plugin_lang('BuyCoursesPlugin') }}: + {{ session.discount_amount_formatted }} +
        + {% endif %} +
        + {{ 'SelecSubscription'|get_plugin_lang('BuyCoursesPlugin') }} +
        +
        + {{ form_subscription }} +
        +
        + {{ 'DoYouHaveACoupon'|get_plugin_lang('BuyCoursesPlugin') }} +
        +
        + {{ form_coupon }} +
        +
        +
        +
        +

        {{ session.title }}

        + {% if session.description %} +
        + {{ session.description }} +
        + {% endif %} +
        + {{ session.dates.display }} +
        +
        +
        + {% for course in session.courses %} +

        + {{ course.title }} +

        + {% if course.coaches|length %} +

        + {{ 'Teachers'|get_plugin_lang('BuyCoursesPlugin') }} : + + {% for coach in course.coaches %} + + {{ coach.name }}, + {% endfor %} +

        + {% endif %} + {% endfor %} +
        +
        +
        +
        + {% endif %} +
        +
        +

        {{ 'PaymentMethods'|get_plugin_lang('BuyCoursesPlugin') }}

        + {% if message_payment %} + {{ message_payment }} + {% else %} + {{ form }} + {% endif %} +
        +
        +
        +
        +
        + +{% endautoescape %} diff --git a/public/plugin/BuyCourses/view/subscription_process_confirm.tpl b/public/plugin/BuyCourses/view/subscription_process_confirm.tpl new file mode 100644 index 00000000000..dcd6ab67292 --- /dev/null +++ b/public/plugin/BuyCourses/view/subscription_process_confirm.tpl @@ -0,0 +1,285 @@ +{% autoescape false %} +
        +
        +
        +
        +
        +

        {{ 'PurchaseData'|get_plugin_lang('BuyCoursesPlugin') }}

        +
        +
        + {% if buying_course %} +
        +
        + + {{ course.title }} + +
        +
        +

        + {{ course.title }} +

        +
          + {% for teacher in course.teachers %} +
        • {{ teacher.name }}
        • + {% endfor %} +
        +

        + + {{ course.item.total_price_formatted }} + +

        +

        +
        +
        + {% elseif buying_session %} +
        +
        +

        + {{ session.title }} +

        +
        +
        +

        {{ session.title }}

        +

        {{ session.dates.display }}

        +
          + {% for course in session.courses %} +
        • + {{ course.title }} + {% if course.coaches|length %} +
            + {% for coach in course.coaches %} +
          • {{ coach }}
          • + {% endfor %} +
          + {% endif %} +
        • + {% endfor %} +
        +

        + + {{ session.item.total_price_formatted }} + +

        +

        +
        +
        + {% elseif buying_service %} +
        +
        + + {{ service.title }} + +
        +
        +
        +
        +

        + {{ service.name }} +

        +
          + {% if service.applies_to == 0 %} +
        • + {{ 'AppliesTo'|get_plugin_lang('BuyCoursesPlugin') }} {{ 'None'|get_lang }} +
        • + {% elseif service.applies_to == 1 %} +
        • + {{ 'AppliesTo'|get_plugin_lang('BuyCoursesPlugin') }} {{ 'User'|get_lang }} +
        • + {% elseif service.applies_to == 2 %} +
        • + {{ 'AppliesTo'|get_plugin_lang('BuyCoursesPlugin') }} {{ 'Course'|get_lang }} +
        • + {% elseif service.applies_to == 3 %} +
        • + {{ 'AppliesTo'|get_plugin_lang('BuyCoursesPlugin') }} {{ 'Session'|get_lang }} +
        • + {% elseif service.applies_to == 4 %} +
        • + {{ 'AppliesTo'|get_plugin_lang('BuyCoursesPlugin') }} {{ 'TemplateTitleCertificate'|get_lang }} +
        • + {% endif %} +
        • + + {{ 'Price'|get_plugin_lang('BuyCoursesPlugin') }} + : {{ service_item.total_price_formatted }} + / {{ service.duration_days == 0 ? 'NoLimit'|get_lang : service.duration_days ~ ' ' ~ 'Days'|get_lang }} +
        • +
        • {{ service.owner.name }}
        • + {% if service.description %} +
        • {{ service.description }}
        • + {% endif %} +
        +

        + + {{ service_item.total_price_formatted }} + +

        +

        +
        +
        + {% endif %} +
        +
        +
        + {% if terms %} +
        +
        +
        +

        {{ 'TermsAndConditions'|get_plugin_lang('BuyCoursesPlugin') }}

        +
        +
        +
        +
        + +
        +
        + +
        +
        +
        +
        +
        + {% endif %} +
        + +{% if is_bank_transfer %} +
        +
        + +
        + + + + + + + + + + {% for account in transfer_accounts %} + + + + + + {% endfor %} + +
        {{ 'Name'|get_lang }}{{ 'BankAccount'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'SWIFT'|get_plugin_lang('BuyCoursesPlugin') }}
        {{ account.name }}{{ account.account }}{{ account.swift }}
        +
        +

        {{ 'OnceItIsConfirmedYouWillReceiveAnEmailWithTheBankInformationAndAnOrderReference'|get_plugin_lang('BuyCoursesPlugin') }}

        +
        +
        +{% endif %} + +
        +
        + {{ form }} +
        +
        + +{% endautoescape %} diff --git a/public/plugin/BuyCourses/view/subscription_sales_report.tpl b/public/plugin/BuyCourses/view/subscription_sales_report.tpl new file mode 100644 index 00000000000..b81170f9c0b --- /dev/null +++ b/public/plugin/BuyCourses/view/subscription_sales_report.tpl @@ -0,0 +1,124 @@ +{% autoescape false %} + +
        +
        +{{ form }} + +
        + + + + + + + + + + + + + + + {% if invoicing_enable %} + + {% endif %} + + + + + {% for sale in sale_list %} + + + + + + + + + + + + + {% if invoicing_enable %} + + {% endif %} + + + {% endfor %} + +
        {{ 'OrderReference'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'OrderStatus'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'OrderDate'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'PaymentMethod'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'Price'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'CouponDiscount'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'Coupon'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'ProductType'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'Name'|get_lang }}{{ 'UserName'|get_lang }}{{ 'Email'|get_lang }}{{ 'Invoice'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'Options'|get_lang }}
        {{ sale.reference }} + {% if sale.status == sale_status_canceled %} + {{ 'SaleStatusCanceled'|get_plugin_lang('BuyCoursesPlugin') }} + {% elseif sale.status == sale_status_pending %} + {{ 'SaleStatusPending'|get_plugin_lang('BuyCoursesPlugin') }} + {% elseif sale.status == sale_status_completed %} + {{ 'SaleStatusCompleted'|get_plugin_lang('BuyCoursesPlugin') }} + {% endif %} + {{ sale.date | api_get_local_time }}{{ sale.payment_type }}{{ sale.total_price }}{{ sale.total_discount }}{{ sale.coupon_code }}{{ sale.product_type }}{{ sale.product_name }}{{ sale.complete_user_name }}{{ sale.email }} + {% if sale.invoice == 1 %} + + {{ 'InvoiceView'|get_plugin_lang('BuyCoursesPlugin') }} +
        {{ sale.num_invoice }} +
        + {% endif %} +
        + {% if sale.status == sale_status_pending %} + + {% endif %} +
        +
        + + +{% endautoescape %} diff --git a/public/plugin/BuyCourses/view/subscriptions.tpl b/public/plugin/BuyCourses/view/subscriptions.tpl new file mode 100644 index 00000000000..01daaf89c05 --- /dev/null +++ b/public/plugin/BuyCourses/view/subscriptions.tpl @@ -0,0 +1,154 @@ +{% autoescape false %} +{% if sessions_are_included %} + +{% endif %} + +
        +
        +
        + + + + + + + {% if tax_enable and (tax_applies_to == 1 or tax_applies_to == 2) %} + + {% endif %} + + + + + {% for item in courses %} + + + + + {% if tax_enable and (tax_applies_to == 1 or tax_applies_to == 2) %} + + {% endif %} + + + {% endfor %} + +
        {{ 'Title'|get_lang }}{{ 'OfficialCode'|get_lang }}{{ 'HasSubscriptions'|get_plugin_lang('BuyCoursesPlugin') }}{{ tax_name }}{{ 'Options'|get_lang }}
        + {% if item.visibility == 0 %} + {{ 'CourseVisibilityClosed'|get_lang }} + {% elseif item.visibility == 1 %} + {{ 'Private'|get_lang }} + {% elseif item.visibility == 2 %} + {{ 'OpenToThePlatform'|get_lang }} + {% elseif item.visibility == 3 %} + {{ 'OpenToTheWorld'|get_lang }} + {% elseif item.visibility == 4 %} + {{ 'CourseVisibilityHidden'|get_lang }} + {% endif %} + + {{ item.title }} + + {{ item.code }} + + {{ item.code }} + + {% if item.buyCourseData %} + + {% else %} + + {% endif %} + + {{ item.buyCourseData.tax_perc_show }} % + + {% if item.buyCourseData %} + + {{ 'Configure'|get_lang }} + + {% else %} + + {{ 'Configure'|get_lang }} + + {% endif %} +
        +
        + {{ course_pagination }} +
        + + {% if sessions_are_included %} +
        +
        + + + + + + + + {% if tax_enable and (tax_applies_to == 1 or tax_applies_to == 3) %} + + {% endif %} + + + + + {% for item in sessions %} + + + + + + {% if tax_enable and (tax_applies_to == 1 or tax_applies_to == 3) %} + + {% endif %} + + + {% endfor %} + +
        {{ 'Title'|get_lang }}{{ 'StartDate'|get_lang }}{{ 'EndDate'|get_lang }}{{ 'HasSubscriptions'|get_plugin_lang('BuyCoursesPlugin') }}{{ tax_name }}{{ 'Options'|get_lang }}
        + {{ item.name }} + + {{ item.displayStartDate | api_convert_and_format_date(6)}} + + {{ item.displayEndDate |api_convert_and_format_date(6)}} + + {% if item.buyCourseData %} + + {% else %} + + {% endif %} + + {{ item.buyCourseData.tax_perc_show }} % + + {% if item.buyCourseData %} + + {{ 'Configure'|get_lang }} + + {% else %} + + {{ 'Configure'|get_lang }} + + {% endif %} +
        + {{ session_pagination }} +
        +
        + {% endif %} +
        +{% endautoescape %} diff --git a/public/plugin/BuyCourses/view/success.tpl b/public/plugin/BuyCourses/view/success.tpl index bc507a1fcbf..ccd56436ef8 100644 --- a/public/plugin/BuyCourses/view/success.tpl +++ b/public/plugin/BuyCourses/view/success.tpl @@ -35,7 +35,7 @@ {% endfor %}

      - {{ 'Description'|get_lang }}