/app/controller/class-ms-controller-billing.php

  1. <?php 
  2. /** 
  3. * Controller to manage billing and invoices. 
  4. * 
  5. * @since 1.0.0 
  6. * 
  7. * @package Membership2 
  8. * @subpackage Controller 
  9. */ 
  10. class MS_Controller_Billing extends MS_Controller { 
  11.  
  12. /** 
  13. * Default action to open the invoice edit form. 
  14. * 
  15. * @since 1.0.1.0 
  16. * @var string 
  17. */ 
  18. const ACTION_EDIT = 'edit'; 
  19.  
  20. /** 
  21. * Action used to quick-pay an invoice via a link in the billings list. 
  22. * 
  23. * @since 1.0.1.0 
  24. * @var string 
  25. */ 
  26. const ACTION_PAY_IT = 'pay_it'; 
  27.  
  28. /** 
  29. * Ajax action used in the transaction log list. 
  30. * Sets the Manual-State flag of an transaction. 
  31. * 
  32. * @since 1.0.1.0 
  33. * @var string 
  34. */ 
  35. const AJAX_ACTION_TRANSACTION_UPDATE = 'transaction_update'; 
  36.  
  37. /** 
  38. * Ajax action used in the transaction log list. 
  39. * Returns a form to link a transaction with an invoice. 
  40. * 
  41. * @since 1.0.1.0 
  42. * @var string 
  43. */ 
  44. const AJAX_ACTION_TRANSACTION_LINK = 'transaction_link'; 
  45.  
  46. /** 
  47. * Ajax action used in the transaction log list. 
  48. * Returns a list of requested items. 
  49. * 
  50. * @since 1.0.1.0 
  51. * @var string 
  52. */ 
  53. const AJAX_ACTION_TRANSACTION_LINK_DATA = 'transaction_link_data'; 
  54.  
  55. /** 
  56. * Prepare the Billing manager. 
  57. * 
  58. * @since 1.0.0 
  59. */ 
  60. public function __construct() { 
  61. parent::__construct(); 
  62.  
  63. $this->add_ajax_action( 
  64. self::AJAX_ACTION_TRANSACTION_UPDATE,  
  65. 'ajax_change_transaction' 
  66. ); 
  67.  
  68. $this->add_ajax_action( 
  69. self::AJAX_ACTION_TRANSACTION_LINK,  
  70. 'ajax_link_transaction' 
  71. ); 
  72.  
  73. $this->add_ajax_action( 
  74. self::AJAX_ACTION_TRANSACTION_LINK_DATA,  
  75. 'ajax_link_data_transaction' 
  76. ); 
  77.  
  78. /** 
  79. * Initialize the admin-side functions. 
  80. * 
  81. * @since 1.0.0 
  82. */ 
  83. public function admin_init() { 
  84. $hook = MS_Controller_Plugin::admin_page_hook( 'billing' ); 
  85.  
  86. $this->run_action( 'load-' . $hook, 'admin_billing_manager' ); 
  87. $this->run_action( 'admin_print_scripts-' . $hook, 'enqueue_scripts' ); 
  88. $this->run_action( 'admin_print_styles-' . $hook, 'enqueue_styles' ); 
  89.  
  90. /** 
  91. * Show admin notices. 
  92. * 
  93. * @since 1.0.0 
  94. * 
  95. */ 
  96. public function print_admin_message() { 
  97. add_action( 'admin_notices', array( 'MS_Helper_Billing', 'print_admin_message' ) ); 
  98.  
  99. /** 
  100. * Manages billing actions. 
  101. * 
  102. * Verifies GET and POST requests to manage billing. 
  103. * 
  104. * @since 1.0.0 
  105. */ 
  106. public function admin_billing_manager() { 
  107. $this->print_admin_message(); 
  108. $msg = 0; 
  109. $redirect = false; 
  110.  
  111. if ( ! $this->is_admin_user() ) { 
  112. return; 
  113.  
  114. $fields_edit = array( 'user_id', 'membership_id' ); 
  115. $fields_pay = array( 'invoice_id' ); 
  116. $fields_bulk = array( 'action', 'action2', 'invoice_id' ); 
  117.  
  118. // Save details of a single invoice. 
  119. if ( $this->verify_nonce( self::ACTION_EDIT ) 
  120. && self::validate_required( $fields_edit ) 
  121. ) { 
  122. $msg = $this->save_invoice( $_POST ); 
  123.  
  124. $redirect = esc_url_raw( 
  125. add_query_arg( 
  126. array( 'msg' => $msg ),  
  127. remove_query_arg( array( 'invoice_id') ) 
  128. ); 
  129.  
  130. // Quick-Pay an invoice. 
  131. elseif ( $this->verify_nonce( self::ACTION_PAY_IT, 'GET' ) 
  132. && self::validate_required( $fields_pay, 'GET' ) 
  133. ) { 
  134. $msg = $this->billing_do_action( 'pay', $_GET['invoice_id'] ); 
  135. $redirect = esc_url_raw( 
  136. add_query_arg( 
  137. array( 'msg' => $msg ),  
  138. remove_query_arg( 
  139. array( 'action', '_wpnonce', 'invoice_id' ) 
  140. ); 
  141.  
  142. // Bulk edit invoices. 
  143. elseif ( $this->verify_nonce( 'bulk' ) 
  144. && self::validate_required( $fields_bulk ) 
  145. ) { 
  146. $action = $_POST['action'] != -1 ? $_POST['action'] : $_POST['action2']; 
  147. $msg = $this->billing_do_action( $action, $_POST['invoice_id'] ); 
  148. $redirect = esc_url_raw( 
  149. add_query_arg( array( 'msg' => $msg ) ) 
  150. ); 
  151.  
  152. if ( $redirect ) { 
  153. wp_safe_redirect( $redirect ); 
  154. exit; 
  155.  
  156. /** 
  157. * Sets up the 'Billing' navigation and list page. 
  158. * 
  159. * @since 1.0.0 
  160. */ 
  161. public function admin_page() { 
  162. $this->print_admin_message(); 
  163.  
  164. // Action view page request 
  165. $isset = array( 'action', 'invoice_id' ); 
  166. if ( self::validate_required( $isset, 'GET', false ) && 'edit' == $_GET['action'] ) { 
  167. $invoice_id = ! empty( $_GET['invoice_id'] ) ? $_GET['invoice_id'] : 0; 
  168. $data['invoice'] = MS_Factory::load( 'MS_Model_Invoice', $_GET['invoice_id'] ); 
  169. $data['action'] = $_GET['action']; 
  170. $data['memberships'] = MS_Model_Membership::get_membership_names( 
  171. array( 'include_guest' => 0 ) 
  172. ); 
  173. $view = MS_Factory::create( 'MS_View_Billing_Edit' ); 
  174. $view->data = apply_filters( 'ms_view_billing_edit_data', $data ); 
  175. $view->render(); 
  176. } else { 
  177. $view = MS_Factory::create( 'MS_View_Billing_List' ); 
  178. $view->render(); 
  179.  
  180. /** 
  181. * Ajax action handler used by the transaction logs list to change a 
  182. * transaction log entry. 
  183. * 
  184. * Sets the Manual-State flag of an transaction. 
  185. * 
  186. * @since 1.0.1.0 
  187. */ 
  188. public function ajax_change_transaction() { 
  189. $res = MS_Helper_Billing::BILLING_MSG_NOT_UPDATED; 
  190. $fields_state = array( 'id', 'state' ); 
  191. $fields_link = array( 'id', 'link' ); 
  192.  
  193. if ( $this->verify_nonce() ) { 
  194. if ( self::validate_required( $fields_state ) ) { 
  195. $id = intval( $_POST['id'] ); 
  196. $state = $_POST['state']; 
  197.  
  198. $log = MS_Factory::load( 'MS_Model_Transactionlog', $id ); 
  199.  
  200. if ( $log->manual_state( $state ) ) { 
  201. $log->save(); 
  202. $res = MS_Helper_Billing::BILLING_MSG_UPDATED; 
  203. } elseif ( self::validate_required( $fields_link ) ) { 
  204. $id = intval( $_POST['id'] ); 
  205. $link = intval( $_POST['link'] ); 
  206.  
  207. $log = MS_Factory::load( 'MS_Model_Transactionlog', $id ); 
  208.  
  209. $log->invoice_id = $link; 
  210. if ( $log->manual_state( 'ok' ) ) { 
  211. $invoice = $log->get_invoice(); 
  212. if ( $invoice ) { 
  213. $invoice->pay_it( $log->gateway_id, 'manual' ); 
  214. $log->save(); 
  215. $res = MS_Helper_Billing::BILLING_MSG_UPDATED; 
  216.  
  217. echo $res; 
  218. exit; 
  219.  
  220. /** 
  221. * Ajax action handler used by the transaction logs list to change a 
  222. * transaction log entry. 
  223. * 
  224. * Returns a form to link a transaction with an invoice. 
  225. * 
  226. * @since 1.0.1.0 
  227. */ 
  228. public function ajax_link_transaction() { 
  229. $data = array(); 
  230. $resp = ''; 
  231. $fields = array( 'id' ); 
  232.  
  233. if ( self::validate_required( $fields ) && $this->verify_nonce() ) { 
  234. $id = intval( $_POST['id'] ); 
  235.  
  236. $log = MS_Factory::load( 'MS_Model_Transactionlog', $id ); 
  237. if ( $log->member_id ) { 
  238. $data['member'] = $log->get_member(); 
  239. } else { 
  240. $data['member'] = false; 
  241. $data['log'] = $log; 
  242.  
  243. $view = MS_Factory::create( 'MS_View_Billing_Link' ); 
  244. $view->data = apply_filters( 'ms_view_billing_link_data', $data ); 
  245. $resp = $view->to_html(); 
  246.  
  247. echo $resp; 
  248. exit; 
  249.  
  250. /** 
  251. * Ajax action handler used by the transaction logs list to change a 
  252. * transaction log entry. 
  253. * 
  254. * Returns a list of requested items 
  255. * 
  256. * @since 1.0.1.0 
  257. */ 
  258. public function ajax_link_data_transaction() { 
  259. $resp = array(); 
  260. $fields = array( 'get', 'for' ); 
  261.  
  262. if ( self::validate_required( $fields ) && $this->verify_nonce() ) { 
  263. $type = $_POST['get']; 
  264. $id = intval( $_POST['for'] ); 
  265. $settings = MS_Plugin::instance()->settings; 
  266.  
  267. if ( 'subscriptions' == $type ) { 
  268. $member = MS_Factory::load( 'MS_Model_Member', $id ); 
  269.  
  270. $resp[0] = __( 'Select a subscription', 'membership2' ); 
  271. $active = array(); 
  272. $inactive = array(); 
  273. foreach ( $member->subscriptions as $subscription ) { 
  274. if ( $subscription->is_system() ) { continue; } 
  275.  
  276. $membership = $subscription->get_membership(); 
  277. if ( $membership->is_free() ) { 
  278. $price = __( 'Free', 'membership2' ); 
  279. } else { 
  280. $price = sprintf( 
  281. '%s %s',  
  282. $settings->currency,  
  283. MS_Helper_Billing::format_price( $membership->price ) 
  284. ); 
  285. $line = sprintf( 
  286. __( 'Membership: %s, Base price: %s', 'membership2' ),  
  287. $membership->name,  
  288. $price 
  289. ); 
  290. if ( $subscription->is_expired() ) { 
  291. $inactive[$subscription->id] = $line; 
  292. } else { 
  293. $active[$subscription->id] = $line; 
  294. if ( ! count( $active ) && ! count( $inactive ) ) { 
  295. $resp[0] = __( 'No subscriptions found', 'membership2' ); 
  296. } else { 
  297. if ( count( $active ) ) { 
  298. $resp[__( 'Active Subscriptions', 'membership2' )] = $active; 
  299. if ( count( $inactive ) ) { 
  300. $resp[__( 'Expired Subscriptions', 'membership2' )] = $inactive; 
  301. } elseif ( 'invoices' == $type ) { 
  302. $subscription = MS_Factory::load( 'MS_Model_Relationship', $id ); 
  303. $invoices = $subscription->get_invoices(); 
  304.  
  305. $resp[0] = __( 'Select an invoice', 'membership2' ); 
  306. $unpaid = array(); 
  307. $paid = array(); 
  308. foreach ( $invoices as $invoice ) { 
  309. $line = sprintf( 
  310. __( 'Invoice: %s from %s (%s)', 'membership2' ),  
  311. $invoice->get_invoice_number(),  
  312. $invoice->due_date,  
  313. $invoice->currency . ' ' . 
  314. MS_Helper_Billing::format_price( $invoice->total ) 
  315. ); 
  316. if ( $invoice->is_paid() ) { 
  317. $paid[$invoice->id] = $line; 
  318. } else { 
  319. $unpaid[$invoice->id] = $line; 
  320. if ( ! count( $unpaid ) && ! count( $paid ) ) { 
  321. $resp[0] = __( 'No invoices found', 'membership2' ); 
  322. } else { 
  323. if ( count( $unpaid ) ) { 
  324. $resp[__( 'Unpaid Invoices', 'membership2' )] = $unpaid; 
  325. if ( count( $paid ) ) { 
  326. $resp[__( 'Paid Invoices', 'membership2' )] = $paid; 
  327.  
  328. echo json_encode( $resp ); 
  329. exit; 
  330.  
  331. /** 
  332. * Perform actions for each invoice. 
  333. * 
  334. * @since 1.0.0 
  335. * @param string $action The action to perform on selected invoices. 
  336. * @param int[] $invoice_ids The list of invoices ids to process. 
  337. */ 
  338. public function billing_do_action( $action, $invoice_ids ) { 
  339. $msg = MS_Helper_Billing::BILLING_MSG_NOT_UPDATED; 
  340.  
  341. if ( ! is_array( $invoice_ids ) ) { 
  342. $invoice_ids = array( $invoice_ids ); 
  343.  
  344. foreach ( $invoice_ids as $invoice_id ) { 
  345. $invoice = MS_Factory::load( 'MS_Model_Invoice', $invoice_id ); 
  346.  
  347. switch ( $action ) { 
  348. case 'pay': 
  349. $invoice->status = MS_Model_Invoice::STATUS_PAID; 
  350. $invoice->changed(); 
  351. $msg = MS_Helper_Billing::BILLING_MSG_UPDATED; 
  352. break; 
  353.  
  354. case 'archive': 
  355. $invoice->archive(); 
  356. $msg = MS_Helper_Billing::BILLING_MSG_DELETED; 
  357. break; 
  358.  
  359. default: 
  360. do_action( 
  361. 'ms_controller_billing_do_action_' . $action,  
  362. $invoice 
  363. ); 
  364. break; 
  365.  
  366. return apply_filters( 
  367. 'ms_controller_billing_billing_do_action',  
  368. $msg,  
  369. $action,  
  370. $invoice_ids,  
  371. $this 
  372. ); 
  373.  
  374. /** 
  375. * Save invoices using the invoices model. 
  376. * 
  377. * @since 1.0.0 
  378. * 
  379. * @param mixed $fields Transaction fields 
  380. */ 
  381. private function save_invoice( $fields ) { 
  382. $msg = MS_Helper_Billing::BILLING_MSG_NOT_UPDATED; 
  383.  
  384. if ( $this->is_admin_user() 
  385. && is_array( $fields ) 
  386. && ! empty( $fields['user_id'] ) 
  387. && ! empty( $fields['membership_id'] ) 
  388. ) { 
  389. $member = MS_Factory::load( 'MS_Model_Member', $fields['user_id'] ); 
  390. $membership_id = $fields['membership_id']; 
  391. $gateway_id = 'admin'; 
  392.  
  393. $subscription = MS_Model_Relationship::get_subscription( 
  394. $member->id,  
  395. $membership_id 
  396. ); 
  397.  
  398. if ( empty( $subscription ) ) { 
  399. $subscription = MS_Model_Relationship::create_ms_relationship( 
  400. $membership_id,  
  401. $member->id,  
  402. $gateway_id 
  403. ); 
  404. } else { 
  405. $subscription->set_gateway( $gateway_id ); 
  406.  
  407. $invoice_id = intval( $fields['invoice_id'] ); 
  408. $invoice = MS_Factory::load( 'MS_Model_Invoice', $invoice_id ); 
  409. if ( ! $invoice->is_valid() ) { 
  410. $invoice = $subscription->get_current_invoice(); 
  411. $msg = MS_Helper_Billing::BILLING_MSG_ADDED; 
  412. } else { 
  413. $msg = MS_Helper_Billing::BILLING_MSG_UPDATED; 
  414.  
  415. foreach ( $fields as $field => $value ) { 
  416. $invoice->$field = $value; 
  417.  
  418. $invoice->save(); 
  419.  
  420. if ( ! empty( $fields['execute'] ) ) { 
  421. $invoice->changed(); 
  422.  
  423. return apply_filters( 
  424. 'ms_controller_billing_save_invoice',  
  425. $msg,  
  426. $fields,  
  427. $this 
  428. ); 
  429.  
  430. /** 
  431. * Load Billing specific styles. 
  432. * 
  433. * @since 1.0.0 
  434. */ 
  435. public function enqueue_styles() { 
  436. if ( empty( $_GET['action'] ) ) { 
  437. $action = ''; 
  438. } else { 
  439. $action = $_GET['action']; 
  440.  
  441. if ( 'edit' == $action ) { 
  442. lib3()->ui->add( 'jquery-ui' ); 
  443.  
  444. /** 
  445. * Load Billing specific scripts. 
  446. * 
  447. * @since 1.0.0 
  448. */ 
  449. public function enqueue_scripts() { 
  450. $data = array( 
  451. 'ms_init' => array(),  
  452. ); 
  453.  
  454. if ( isset( $_GET['action'] ) && 'edit' == $_GET['action'] ) { 
  455. wp_enqueue_script( 'jquery-ui-datepicker' ); 
  456. wp_enqueue_script( 'jquery-validate' ); 
  457.  
  458. $data['ms_init'][] = 'view_billing_edit'; 
  459. } else { 
  460. $module = ''; 
  461. if ( isset( $_GET['show'] ) ) { 
  462. $module = $_GET['show']; 
  463.  
  464. if ( 'logs' == $module || 'matching' == $module ) { 
  465. $data['ms_init'][] = 'view_billing_transactions'; 
  466. $data['lang'] = array( 
  467. 'link_title' => __( 'Link Transaction', 'membership2' ),  
  468. ); 
  469.  
  470. lib3()->ui->data( 'ms_data', $data ); 
  471. wp_enqueue_script( 'ms-admin' ); 
  472.  
.