buy_course_plugin.class.php 90 KB

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