buy_course_plugin.class.php 97 KB

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