/app/model/class-ms-model-invoice.php

  1. <?php 
  2. /** 
  3. * Invoice model. 
  4. * 
  5. * Persisted by parent class MS_Model_CustomPostType. 
  6. * 
  7. * @since 1.0.0 
  8. * 
  9. * @package Membership2 
  10. * @subpackage Model 
  11. */ 
  12. class MS_Model_Invoice extends MS_Model_CustomPostType { 
  13.  
  14. /** 
  15. * Model custom post type. 
  16. * 
  17. * Both static and class property are used to handle php 5.2 limitations. 
  18. * 
  19. * @since 1.0.0 
  20. * 
  21. * @var string 
  22. */ 
  23. protected static $POST_TYPE = 'ms_invoice'; 
  24.  
  25. /** 
  26. * Invoice status constants. 
  27. * 
  28. * @since 1.0.0 
  29. * 
  30. * @see $status property. 
  31. * @var string 
  32. */ 
  33. // Invoice was created but user did not yet confirm that he wants to sign up/pay. 
  34. const STATUS_NEW = 'new'; 
  35.  
  36. // Invoice was created but user did not make any attempt to pay. 
  37. const STATUS_BILLED = 'billed'; 
  38.  
  39. // User confirmed payment and it was successful. 
  40. const STATUS_PAID = 'paid'; 
  41.  
  42. // User confirmed payment but gateway returned a "pending" notification. 
  43. const STATUS_PENDING = 'pending'; 
  44.  
  45. // User confirmed payment but gateway returned some error (dispute, wrong amount, etc). 
  46. const STATUS_DENIED = 'denied'; 
  47.  
  48. // Archived invoices are hidden from invoice lists, i.e. "deleted" 
  49. const STATUS_ARCHIVED = 'archived'; 
  50.  
  51. /** 
  52. * External transaction ID. 
  53. * 
  54. * Used to link 3rd party transaction ID to $this->id 
  55. * 
  56. * @since 1.0.0 
  57. * @var string 
  58. */ 
  59. protected $external_id = ''; 
  60.  
  61. /** 
  62. * Gateway ID. 
  63. * 
  64. * Gateway used to pay this invoice. 
  65. * 
  66. * @since 1.0.0 
  67. * @var string 
  68. */ 
  69. protected $gateway_id = ''; 
  70.  
  71. /** 
  72. * Membership ID. 
  73. * 
  74. * Invoice for membership. 
  75. * 
  76. * @since 1.0.0 
  77. * @var int 
  78. */ 
  79. protected $membership_id = 0; 
  80.  
  81. /** 
  82. * User ID. 
  83. * 
  84. * Invoice for this user/member. 
  85. * 
  86. * @since 1.0.0 
  87. * @var int 
  88. */ 
  89. protected $user_id = 0; 
  90.  
  91. /** 
  92. * Log the users IP address once he visits the checkout page. 
  93. * This way we can also see if the user visited the checkout page to pay the 
  94. * invoice. 
  95. * 
  96. * @since 1.0.2.0 
  97. * @var string 
  98. */ 
  99. protected $checkout_ip = ''; 
  100.  
  101. /** 
  102. * Log the timestamp when the user visits the checkout page. 
  103. * 
  104. * @since 1.0.2.0 
  105. * @var string 
  106. */ 
  107. protected $checkout_date = ''; 
  108.  
  109. /** 
  110. * Membership Relationship ID. 
  111. * 
  112. * @since 1.0.0 
  113. * @var int 
  114. */ 
  115. protected $ms_relationship_id = 0; 
  116.  
  117. /** 
  118. * Coupon ID. 
  119. * 
  120. * Used coupon ID. 
  121. * 
  122. * @since 1.0.0 
  123. * @var int 
  124. */ 
  125. protected $coupon_id = 0; 
  126.  
  127. /** 
  128. * Currency of this invoice. 
  129. * 
  130. * @since 1.0.0 
  131. * @var string 
  132. */ 
  133. protected $currency = ''; 
  134.  
  135. /** 
  136. * Amount value not including discounts. 
  137. * 
  138. * @since 1.0.0 
  139. * @var float 
  140. */ 
  141. protected $amount = 0; 
  142.  
  143. /** 
  144. * Discount value. 
  145. * 
  146. * @since 1.0.0 
  147. * @var float 
  148. */ 
  149. protected $discount = 0; 
  150.  
  151. /** 
  152. * Pro rate value. 
  153. * 
  154. * @since 1.0.0 
  155. * @var float 
  156. */ 
  157. protected $pro_rate = 0; 
  158.  
  159. /** 
  160. * READ-ONLY. Invoice amount including all discounts but no taxes. 
  161. * 
  162. * To modify this value change any of these properties: 
  163. * amount, discount, pro_rate 
  164. * 
  165. * @since 1.0.0 
  166. * @var float 
  167. */ 
  168. protected $subtotal = 0; 
  169.  
  170. /** 
  171. * READ-ONLY. Total value (= subtotal + taxes). 
  172. * 
  173. * To modify this value change any of these properties: 
  174. * amount, discount, pro_rate, tax_rate 
  175. * 
  176. * @since 1.0.0 
  177. * @var float 
  178. */ 
  179. protected $total = 0; 
  180.  
  181. /** 
  182. * Inovoice status. 
  183. * 
  184. * @since 1.0.0 
  185. * @var string 
  186. */ 
  187. protected $status = ''; 
  188.  
  189. /** 
  190. * Invoice for trial period. 
  191. * 
  192. * @since 1.0.0 
  193. * @var boolean 
  194. */ 
  195. protected $uses_trial = false; 
  196.  
  197. /** 
  198. * The trial period price. 
  199. * 
  200. * @since 1.0.0 
  201. * @var numeric 
  202. */ 
  203. protected $trial_price = 0; 
  204.  
  205. /** 
  206. * This is the last day of the trial period. The next day is paid. 
  207. * 
  208. * @since 1.0.0 
  209. * @var date 
  210. */ 
  211. protected $trial_ends = ''; 
  212.  
  213. /** 
  214. * Invoice date. 
  215. * 
  216. * This is the date when the INVOICE WAS CREATED. It may be differe than the 
  217. * due date if the subscription uses a trial period. 
  218. * 
  219. * @since 1.0.0 
  220. * @var string 
  221. */ 
  222. protected $invoice_date = ''; 
  223.  
  224. /** 
  225. * Defines date WHEN PAYMENT IS DUE. 
  226. * When invoice uses_trial is true then this is the first day that is paid. 
  227. * 
  228. * @since 1.0.0 
  229. * @var string 
  230. */ 
  231. protected $due_date = ''; 
  232.  
  233. /** 
  234. * Date when the invoice was MARKED AS PAID. 
  235. * 
  236. * Note that free invoices do not have a pay-date! The pay-date is only set 
  237. * when something was actually paid ;) 
  238. * 
  239. * @since 1.0.2.0 
  240. * @var string 
  241. */ 
  242. protected $pay_date = ''; 
  243.  
  244. /** 
  245. * Invoice notes. 
  246. * 
  247. * @since 1.0.0 
  248. * @var string 
  249. */ 
  250. protected $notes = ''; 
  251.  
  252. /** 
  253. * Invoice number. 
  254. * 
  255. * @since 1.0.0 
  256. * @var int 
  257. */ 
  258. protected $invoice_number = 0; 
  259.  
  260. /** 
  261. * Tax rate value. 
  262. * 
  263. * @since 1.0.0 
  264. * @var float 
  265. */ 
  266. protected $tax_rate = 0; 
  267.  
  268. /** 
  269. * Tax name. 
  270. * 
  271. * @since 1.0.0 
  272. * @var string 
  273. */ 
  274. protected $tax_name = ''; 
  275.  
  276. /** 
  277. * Short, compact version of the payment description 
  278. * 
  279. * @since 1.0.0 
  280. * @var string 
  281. */ 
  282. protected $short_description = ''; 
  283.  
  284. /** 
  285. * Where the data came from. Can only be changed by data import tool 
  286. * 
  287. * @since 1.0.0 
  288. * @var string 
  289. */ 
  290. protected $source = ''; 
  291.  
  292. /** 
  293. * Timestamp of price calculation. 
  294. * This information is used when price-options of the memberhsip is changed. 
  295. * 
  296. * @since 1.0.0 
  297. * @var int 
  298. */ 
  299. protected $price_date = 0; 
  300.  
  301. // 
  302. // 
  303. // 
  304. // -------------------------------------------------------------- COLLECTION 
  305.  
  306. /** 
  307. * Returns the post-type of the current object. 
  308. * 
  309. * @since 1.0.0 
  310. * @return string The post-type name. 
  311. */ 
  312. public static function get_post_type() { 
  313. return parent::_post_type( self::$POST_TYPE ); 
  314.  
  315. /** 
  316. * Get custom register post type args for this model. 
  317. * 
  318. * @since 1.0.0 
  319. */ 
  320. public static function get_register_post_type_args() { 
  321. $args = array( 
  322. 'label' => __( 'Membership2 Invoices', 'membership2' ),  
  323. 'description' => __( 'Member Invoices', 'membership2' ),  
  324. 'public' => true,  
  325. 'show_ui' => false,  
  326. 'show_in_menu' => false,  
  327. 'has_archive' => false,  
  328. 'publicly_queryable' => true,  
  329. 'supports' => false,  
  330. 'hierarchical' => false,  
  331. ); 
  332.  
  333. return apply_filters( 
  334. 'ms_customposttype_register_args',  
  335. $args,  
  336. self::get_post_type() 
  337. ); 
  338.  
  339. /** 
  340. * Get invoice status types. 
  341. * 
  342. * @since 1.0.0 
  343. * @param bool $extended Optional. If true, additional details will be 
  344. * returned, not only the status name. 
  345. * @return array A list of status IDs with status name/description. 
  346. */ 
  347. public static function get_status_types( $extended = false ) { 
  348. if ( $extended ) { 
  349. $result = array( 
  350. self::STATUS_NEW => __( 'Draft - Invoice is prepared but user cannot see it yet', 'membership2' ),  
  351. self::STATUS_BILLED => __( 'Billed - User can see the invoice and needs to pay', 'membership2' ),  
  352. self::STATUS_PENDING => __( 'Pending - Waiting for confirmation from payment gateway', 'membership2' ),  
  353. self::STATUS_PAID => __( 'Paid - Payment arrived on our account!', 'membership2' ),  
  354. self::STATUS_DENIED => __( 'Denied - Payment was denied', 'membership2' ),  
  355. ); 
  356. } else { 
  357. $result = array( 
  358. self::STATUS_NEW => __( 'Draft', 'membership2' ),  
  359. self::STATUS_BILLED => __( 'Billed', 'membership2' ),  
  360. self::STATUS_PENDING => __( 'Pending', 'membership2' ),  
  361. self::STATUS_PAID => __( 'Paid', 'membership2' ),  
  362. self::STATUS_DENIED => __( 'Denied', 'membership2' ),  
  363. ); 
  364.  
  365. return apply_filters( 
  366. 'ms_model_invoice_get_status_types',  
  367. $result,  
  368. $extended 
  369. ); 
  370.  
  371. /** 
  372. * Returns the default query-arg array 
  373. * 
  374. * @since 1.0.0 
  375. * @return array 
  376. */ 
  377. public static function get_query_args() { 
  378. $args = array(); 
  379.  
  380. if ( ! empty( $_REQUEST['orderby'] ) && ! empty( $_REQUEST['order'] ) ) { 
  381. $args['orderby'] = $_REQUEST['orderby']; 
  382. $args['order'] = $_REQUEST['order']; 
  383. } else { 
  384. $args['orderby'] = 'ID'; 
  385. $args['order'] = 'DESC'; 
  386.  
  387. // Prepare order by statement. 
  388. $orderby = $args['orderby']; 
  389. if ( ! empty( $orderby ) 
  390. && ! in_array( $orderby, array( 'ID', 'author' ) ) 
  391. && property_exists( 'MS_Model_Invoice', $orderby ) 
  392. ) { 
  393. $args['meta_key'] = $orderby; 
  394. if ( in_array( $orderby, array( 'amount', 'total' ) ) ) { 
  395. $args['orderby'] = 'meta_value_num'; 
  396. } else { 
  397. $args['orderby'] = 'meta_value'; 
  398.  
  399. // Search string. 
  400. if ( ! empty( $_REQUEST['s'] ) ) { 
  401. $user_args = array( 
  402. 'search' => '*' . $_REQUEST['s'] . '*',  
  403. ); 
  404. $user_list = new WP_User_Query( $user_args ); 
  405. $user_ids = array(); 
  406. foreach ( $user_list->results as $user ) { 
  407. $user_ids[] = $user->ID; 
  408. $args['author__in'] = $user_ids; 
  409.  
  410. $args['meta_query'] = array(); 
  411.  
  412. // Gateway filter. 
  413. if ( ! empty( $_REQUEST['gateway_id'] ) ) { 
  414. $args['meta_query']['gateway_id'] = array( 
  415. 'key' => 'gateway_id',  
  416. 'value' => $_REQUEST['gateway_id'],  
  417. ); 
  418.  
  419. // Payment status filter. 
  420. if ( ! empty( $_REQUEST['status'] ) ) { 
  421. if ( 'default' === $_REQUEST['status'] ) { 
  422. $args['meta_query']['status'] = array( 
  423. 'key' => 'status',  
  424. 'value' => array( 
  425. self::STATUS_BILLED,  
  426. self::STATUS_PENDING,  
  427. self::STATUS_PAID,  
  428. self::STATUS_DENIED,  
  429. ),  
  430. 'compare' => 'IN',  
  431. ); 
  432. } elseif ( 'open' === $_REQUEST['status'] ) { 
  433. $args['meta_query']['status'] = array( 
  434. 'key' => 'status',  
  435. 'value' => array( 
  436. self::STATUS_BILLED,  
  437. self::STATUS_PENDING,  
  438. ),  
  439. 'compare' => 'IN',  
  440. ); 
  441. } else { 
  442. $args['meta_query']['status'] = array( 
  443. 'key' => 'status',  
  444. 'value' => $_REQUEST['status'],  
  445. ); 
  446.  
  447. return $args; 
  448.  
  449. /** 
  450. * Get the number of invoices. 
  451. * 
  452. * @since 1.0.0 
  453. * @param array $args The query post args 
  454. * @see http://codex.wordpress.org/Class_Reference/WP_Query 
  455. * @return int 
  456. */ 
  457. public static function get_invoice_count( $args = null ) { 
  458. $defaults = array( 
  459. 'post_type' => self::get_post_type(),  
  460. 'post_status' => 'any',  
  461. ); 
  462. $args = apply_filters( 
  463. 'ms_model_invoice_get_invoice_count_args',  
  464. wp_parse_args( $args, $defaults ) 
  465. ); 
  466.  
  467. MS_Factory::select_blog(); 
  468. $query = new WP_Query( $args ); 
  469. MS_Factory::revert_blog(); 
  470.  
  471. return apply_filters( 
  472. 'ms_model_invoice_get_invoice_count',  
  473. $query->found_posts,  
  474. $args 
  475. ); 
  476.  
  477. /** 
  478. * Count the number of unpaid invoices. Unpaid is any invoice with status 
  479. * BILLED or PENDING. 
  480. * 
  481. * @since 1.0.0 
  482. * @param array $args The query post args 
  483. * @see http://codex.wordpress.org/Class_Reference/WP_Query 
  484. * @param bool $for_badge if true then return value is a string 
  485. * @return int|string 
  486. */ 
  487. public static function get_unpaid_invoice_count( $args = null, $for_badge = false ) { 
  488. $defaults = self::get_query_args(); 
  489.  
  490. $args = apply_filters( 
  491. 'ms_model_invoice_get_unpaid_invoice_count_args',  
  492. wp_parse_args( $args, $defaults ) 
  493. ); 
  494.  
  495. $args['meta_query']['status']['value'] = array( 
  496. self::STATUS_BILLED,  
  497. self::STATUS_PENDING,  
  498. ); 
  499. $args['meta_query']['status']['compare'] = 'IN'; 
  500.  
  501. $bill_count = self::get_invoice_count( $args ); 
  502.  
  503. $res = $bill_count; 
  504. if ( $for_badge ) { 
  505. if ( $bill_count > 99 ) { 
  506. $res = '99+'; 
  507. } elseif ( ! $bill_count ) { 
  508. $res = ''; 
  509.  
  510. return apply_filters( 
  511. 'ms_model_invoice_get_unpaid_invoice_count',  
  512. $res,  
  513. $bill_count,  
  514. $args,  
  515. $for_badge 
  516. ); 
  517.  
  518. /** 
  519. * Get invoices. 
  520. * 
  521. * @since 1.0.0 
  522. * 
  523. * @param mixed $args The arguments to select data. 
  524. * @return array $invoices 
  525. */ 
  526. public static function get_invoices( $args = null ) { 
  527. $defaults = array( 
  528. 'post_type' => self::get_post_type(),  
  529. 'posts_per_page' => 10,  
  530. 'post_status' => 'any',  
  531. 'fields' => 'ids',  
  532. 'order' => 'DESC',  
  533. 'orderby' => 'ID',  
  534. ); 
  535. $args = apply_filters( 
  536. 'ms_model_invoice_get_invoices_args',  
  537. wp_parse_args( $args, $defaults ) 
  538. ); 
  539.  
  540. MS_Factory::select_blog(); 
  541. $query = new WP_Query( $args ); 
  542. $items = $query->posts; 
  543. $invoices = array(); 
  544. MS_Factory::revert_blog(); 
  545.  
  546. foreach ( $items as $item ) { 
  547. $invoices[] = MS_Factory::load( 'MS_Model_Invoice', $item ); 
  548.  
  549. return apply_filters( 
  550. 'ms_model_invoice_get_invoices',  
  551. $invoices,  
  552. $args 
  553. ); 
  554.  
  555. /** 
  556. * Returns all invoices of the specified user that are "public" for the 
  557. * user. This means that some internal invoices will not be displayed: 
  558. * - Invoices with 0.00 total amount are not displayed 
  559. * - Invoices with status New are not displayed 
  560. * 
  561. * @since 1.0.0 
  562. * @param int $user_id 
  563. * @param int $limit 
  564. * @return array List of MS_Model_Invoice objects. 
  565. */ 
  566. public static function get_public_invoices( $user_id, $limit = -1 ) { 
  567. $list = self::get_invoices( 
  568. array( 
  569. 'author' => $user_id,  
  570. 'posts_per_page' => $limit,  
  571. 'meta_query' => array( 
  572. 'relation' => 'AND',  
  573. // Do not display invoices for free memberships. 
  574. array( 
  575. 'key' => 'amount',  
  576. 'value' => '0',  
  577. 'compare' => '!=',  
  578. ),  
  579. // Do not display and Invoice with status "New". 
  580. array( 
  581. 'key' => 'status',  
  582. 'value' => MS_Model_Invoice::STATUS_NEW,  
  583. 'compare' => '!=',  
  584. ),  
  585. ); 
  586.  
  587. return $list; 
  588.  
  589. /** 
  590. * Get specific invoice. 
  591. * 
  592. * Get invoice of a user and membership. 
  593. * 
  594. * @since 1.0.0 
  595. * 
  596. * @param int $subscription_id The membership relationship id. 
  597. * @param int $invoice_number Optional. The invoice number. Get the current number if null. 
  598. * @param string $status Optional. The invoice status. 
  599. * @return MS_Model_Invoice The found invoice or null if not found. 
  600. */ 
  601. public static function get_invoice( $subscription_id, $invoice_number = null, $status = null ) { 
  602. $args = array( 
  603. 'post_type' => self::get_post_type(),  
  604. 'post_status' => 'any',  
  605. 'fields' => 'ids',  
  606. 'order' => 'DESC',  
  607. ); 
  608.  
  609. $args['meta_query']['ms_relationship_id'] = array( 
  610. 'key' => 'ms_relationship_id',  
  611. 'value' => $subscription_id,  
  612. ); 
  613. if ( ! empty( $status ) ) { 
  614. $args['meta_query']['status'] = array( 
  615. 'key' => 'status',  
  616. 'value' => $status,  
  617. ); 
  618. if ( ! empty( $invoice_number ) ) { 
  619. $args['meta_query']['invoice_number'] = array( 
  620. 'key' => 'invoice_number',  
  621. 'value' => $invoice_number,  
  622. ); 
  623.  
  624. MS_Factory::select_blog(); 
  625. $args = apply_filters( 'ms_model_invoice_get_invoice_args', $args ); 
  626. $query = new WP_Query( $args ); 
  627. $item = $query->posts; 
  628. MS_Factory::revert_blog(); 
  629.  
  630. $invoice = null; 
  631. if ( ! empty( $item[0] ) ) { 
  632. $invoice = MS_Factory::load( 'MS_Model_Invoice', $item[0] ); 
  633.  
  634. return apply_filters( 
  635. 'ms_model_invoice_get_invoice',  
  636. $invoice,  
  637. $subscription_id,  
  638. $invoice_number,  
  639. $status 
  640. ); 
  641.  
  642. /** 
  643. * Get current member membership invoice. 
  644. * 
  645. * The current invoice is the not paid one. Every time a invoice is paid,  
  646. * the current invoice number is incremented. 
  647. * 
  648. * @since 1.0.0 
  649. * 
  650. * @param MS_Model_Relationship $subscription The membership relationship. 
  651. * @param bool $create_missing Optional. True to overwrite existing 
  652. * invoice or false to create a new one if doesn't exist. 
  653. * @return MS_Model_Invoice 
  654. */ 
  655. public static function get_current_invoice( $subscription, $create_missing = true ) { 
  656. $invoice = self::get_invoice( 
  657. $subscription->id,  
  658. $subscription->current_invoice_number 
  659. ); 
  660.  
  661. if ( ! $invoice && $create_missing ) { 
  662. // Create a new invoice. 
  663. $invoice = self::create_invoice( 
  664. $subscription,  
  665. $subscription->current_invoice_number 
  666. ); 
  667.  
  668. return apply_filters( 
  669. 'ms_model_invoice_get_current_invoice',  
  670. $invoice,  
  671. $subscription,  
  672. $create_missing 
  673. ); 
  674.  
  675. /** 
  676. * Get next invoice for the membership. 
  677. * 
  678. * @since 1.0.0 
  679. * 
  680. * @param MS_Model_Relationship $subscription The membership relationship. 
  681. * @param bool $create_missing Optional. True to overwrite existing 
  682. * invoice or false to create a new one if doesn't exist. 
  683. * @return MS_Model_Invoice 
  684. */ 
  685. public static function get_next_invoice( $subscription, $create_missing = true ) { 
  686. $invoice = self::get_invoice( 
  687. $subscription->id,  
  688. $subscription->current_invoice_number + 1 
  689. ); 
  690.  
  691. if ( ! $invoice && $create_missing ) { 
  692. // Create a new invoice. 
  693. $invoice = self::create_invoice( 
  694. $subscription,  
  695. $subscription->current_invoice_number + 1 
  696. ); 
  697.  
  698. /** 
  699. * Since only the *first* invoice can have discount/pro-rating we 
  700. * manually set those values to 0. 
  701. */ 
  702. $invoice->discount = 0; 
  703. $invoice->pro_rate = 0; 
  704. $invoice->notes = array(); 
  705.  
  706. return apply_filters( 
  707. 'ms_model_invoice_get_next_invoice',  
  708. $invoice,  
  709. $subscription,  
  710. $create_missing 
  711. ); 
  712.  
  713. /** 
  714. * Get previous invoice for the membership. 
  715. * 
  716. * @since 1.0.0 
  717. * 
  718. * @param MS_Model_Relationship $subscription The membership relationship. 
  719. * @param string $status The invoice status to find. Optional 
  720. * @return MS_Model_Invoice 
  721. */ 
  722. public static function get_previous_invoice( $subscription, $status = null ) { 
  723. $invoice = self::get_invoice( 
  724. $subscription->id,  
  725. $subscription->current_invoice_number - 1,  
  726. $status 
  727. ); 
  728.  
  729. return apply_filters( 
  730. 'ms_model_invoice_get_previous_invoice',  
  731. $invoice,  
  732. $subscription,  
  733. $status 
  734. ); 
  735.  
  736. /** 
  737. * Create invoice. 
  738. * 
  739. * Create a new invoice using the membership information. 
  740. * 
  741. * @since 1.0.0 
  742. * 
  743. * @param MS_Model_Relationship $subscription The membership to create invoice for. 
  744. * @param int $invoice_number Optional. The invoice number. 
  745. * 
  746. * @return object $invoice 
  747. */ 
  748. public static function create_invoice( $subscription, $invoice_number = false ) { 
  749. $membership = $subscription->get_membership(); 
  750.  
  751. if ( ! MS_Model_Membership::is_valid_membership( $membership->id ) ) { 
  752. throw new Exception( 'Invalid Membership.' ); 
  753.  
  754. $invoice = null; 
  755. $member = MS_Factory::load( 'MS_Model_Member', $subscription->user_id ); 
  756. $invoice_status = self::STATUS_NEW; 
  757. $notes = null; 
  758.  
  759. if ( empty( $invoice_number ) ) { 
  760. $invoice_number = $subscription->current_invoice_number; 
  761.  
  762. $invoice = self::get_invoice( $subscription->id, $invoice_number ); 
  763.  
  764. // No existing invoice, create a new one. 
  765. if ( ! $invoice || ! $invoice->id ) { 
  766. $invoice = MS_Factory::create( 'MS_Model_Invoice' ); 
  767. $invoice = apply_filters( 'ms_model_invoice', $invoice ); 
  768.  
  769. // Update invoice info. 
  770. $invoice->ms_relationship_id = $subscription->id; 
  771. $invoice->gateway_id = $subscription->gateway_id; 
  772. $invoice->status = $invoice_status; 
  773. $invoice->invoice_date = MS_Helper_Period::current_date(); 
  774. $invoice->membership_id = $membership->id; 
  775. $invoice->currency = MS_Plugin::instance()->settings->currency; 
  776. $invoice->user_id = $member->id; 
  777. $invoice->name = apply_filters( 
  778. 'ms_model_invoice_name',  
  779. sprintf( 
  780. __( 'Invoice for %s - %s', 'membership2' ),  
  781. $membership->name,  
  782. $member->username 
  783. ); 
  784. $invoice->invoice_number = $invoice_number; 
  785. $invoice->discount = 0; 
  786. $invoice->notes = $notes; 
  787. $invoice->amount = $membership->price; // Without taxes! 
  788.  
  789. // Check for trial period in the first period. 
  790. if ( $subscription->is_trial_eligible() 
  791. && $invoice_number === $subscription->current_invoice_number 
  792. ) { 
  793. $invoice->trial_price = $membership->trial_price; // Without taxes! 
  794. $invoice->uses_trial = true; 
  795. $invoice->trial_ends = $subscription->trial_expire_date; 
  796.  
  797. $invoice->set_due_date(); 
  798.  
  799. $invoice = apply_filters( 
  800. 'ms_model_invoice_create_before_save',  
  801. $invoice,  
  802. $subscription 
  803. ); 
  804.  
  805. $invoice->save(); 
  806.  
  807. // Refresh the tax-rate and payment description. 
  808. $invoice->total_amount_changed(); 
  809.  
  810. $invoice->save(); 
  811.  
  812. return apply_filters( 
  813. 'ms_model_relationship_create_invoice',  
  814. $invoice,  
  815. $subscription,  
  816. $invoice_number 
  817. ); 
  818.  
  819.  
  820. // 
  821. // 
  822. // 
  823. // ------------------------------------------------------------- SINGLE ITEM 
  824.  
  825.  
  826. /** 
  827. * Save model. 
  828. * 
  829. * @since 1.0.0 
  830. */ 
  831. public function save() { 
  832. // Validate the pay_date attribute of the invoice. 
  833. $this->validate_pay_date(); 
  834.  
  835. parent::save(); 
  836. parent::store_singleton(); 
  837.  
  838. /** 
  839. * Move an invoice to tha archive - i.e. hide it from the user. 
  840. * 
  841. * @since 1.0.2.0 
  842. */ 
  843. public function archive() { 
  844. if ( $this->id ) { 
  845. $this->add_notes( '----------' ); 
  846. $this->add_notes( 
  847. sprintf( 
  848. __( 'Archived on: %s', 'membership2' ),  
  849. MS_Helper_Period::current_date() 
  850. ); 
  851. $this->add_notes( 
  852. sprintf( 
  853. __( 'Former status: %s', 'membership2' ),  
  854. $this->status 
  855. ); 
  856.  
  857. $this->status = self::STATUS_ARCHIVED; 
  858. $this->save(); 
  859.  
  860. /** 
  861. * Registers the payment and marks the invoice as paid. 
  862. * 
  863. * This should be the only place that sets an invoice status to PAID. 
  864. * 
  865. * @since 1.0.0 
  866. * @param string $gateway_id The payment gateway. 
  867. * @param string $external_id Payment-ID provided by the gateway 
  868. */ 
  869. public function pay_it( $gateway_id = null, $external_id = null ) { 
  870. if ( $gateway_id ) { 
  871. $this->gateway_id = $gateway_id; 
  872. if ( $external_id ) { 
  873. $this->external_id = $external_id; 
  874. $is_paid = false; 
  875.  
  876. $subscription = $this->get_subscription(); 
  877.  
  878. // Save details on the payment. 
  879. if ( 0 == $this->total || MS_Gateway_Free::ID == $gateway_id ) { 
  880. $is_paid = $subscription->add_payment( 
  881. 0,  
  882. MS_Gateway_Free::ID,  
  883. 'free' 
  884. ); 
  885. } else { 
  886. $is_paid = $subscription->add_payment( 
  887. $this->total,  
  888. $gateway_id,  
  889. $external_id 
  890. ); 
  891.  
  892. if ( $is_paid ) { 
  893. $this->status = self::STATUS_PAID; 
  894. $this->pay_date = MS_Helper_Period::current_date(); 
  895. } else { 
  896. $this->status = self::STATUS_BILLED; 
  897.  
  898. // Manual gateway works differently. This conditon avoids infinite loop. 
  899. if ( MS_Gateway_Manual::ID != $gateway_id ) { 
  900. /** 
  901. * Process the payment and update the subscription. 
  902. * This function will call the config_period() function to calculate 
  903. * the new expire date of the subscription. 
  904. * 
  905. * All changes above are also saved at the end of changed() 
  906. */ 
  907. $this->changed(); 
  908.  
  909. /** 
  910. * Notify Add-ons that an invoice was paid. 
  911. * 
  912. * @since 1.0.0 
  913. */ 
  914. do_action( 'ms_invoice_paid', $this, $subscription ); 
  915.  
  916. /** 
  917. * Returns true if the invoice was paid. 
  918. * 
  919. * @since 1.0.0 
  920. * @return bool Payment status. 
  921. */ 
  922. public function is_paid() { 
  923. return $this->status == self::STATUS_PAID; 
  924.  
  925. /** 
  926. * Makes sure that the pay_date attribtue has a valid value. 
  927. * 
  928. * @since 1.0.2.0 
  929. */ 
  930. protected function validate_pay_date() { 
  931. if ( $this->is_paid() && $this->amount ) { 
  932. if ( ! $this->pay_date ) { 
  933. $subscription = $this->get_subscription(); 
  934. $payments = $subscription->get_payments(); 
  935. $last_payment = end( $payments ); 
  936. $this->pay_date = $last_payment['date']; 
  937. if ( ! $this->pay_date ) { 
  938. $this->pay_date = $this->due_date; 
  939. } elseif ( $this->pay_date ) { 
  940. $this->pay_date = ''; 
  941.  
  942. /** 
  943. * Update the subscription details after the invoice has changed. 
  944. * 
  945. * Process transaction status change related to this membership relationship. 
  946. * Change status accordinly to transaction status. 
  947. * 
  948. * @since 1.0.0 
  949. * @param MS_Model_Invoice $invoice The invoice to process. 
  950. * @return MS_Model_Invoice The processed invoice. 
  951. */ 
  952. public function changed() { 
  953. do_action( 
  954. 'ms_model_invoice_changed_before',  
  955. $this 
  956. ); 
  957.  
  958. if ( ! $this->ms_relationship_id ) { 
  959. MS_Helper_Debug::log( 'Cannot process transaction: No relationship defined (inv #' . $this->id .')' ); 
  960. } else { 
  961. $subscription = $this->get_subscription(); 
  962. $member = MS_Factory::load( 'MS_Model_Member', $this->user_id ); 
  963. $membership = $subscription->get_membership(); 
  964.  
  965. switch ( $this->status ) { 
  966. case self::STATUS_NEW: 
  967. case self::STATUS_BILLED: 
  968. break; 
  969.  
  970. case self::STATUS_PAID: 
  971. if ( $this->total > 0 ) { 
  972. MS_Model_Event::save_event( 
  973. MS_Model_Event::TYPE_PAID,  
  974. $subscription 
  975. ); 
  976.  
  977. do_action( 
  978. 'ms_model_invoice_changed-paid',  
  979. $this,  
  980. $member 
  981. ); 
  982.  
  983. // Check for moving memberships 
  984. if ( $subscription->move_from_id ) { 
  985. $ids = explode( ', ', $subscription->move_from_id ); 
  986. foreach ( $ids as $id ) { 
  987. $move_from = MS_Model_Relationship::get_subscription( 
  988. $subscription->user_id,  
  989. $id 
  990. ); 
  991.  
  992. if ( $move_from->is_valid() ) { 
  993. /** 
  994. * @since 1.0.1.2 The old subscription will be 
  995. * deactivated instantly, and not cancelled. 
  996. * When the subscription is cancelled the user 
  997. * still has full access to the membership 
  998. * contents. When it is deactivated he cannot 
  999. * access protected content anymore (instantly). 
  1000. */ 
  1001. $move_from->deactivate_membership(); 
  1002.  
  1003. $subscription->cancelled_memberships = $subscription->move_from_id; 
  1004. $subscription->move_from_id = ''; 
  1005.  
  1006. /** 
  1007. * Memberships with those payment types can have multiple 
  1008. * invoices for a single subscription. 
  1009. */ 
  1010. $multi_invoice = array( 
  1011. MS_Model_Membership::PAYMENT_TYPE_RECURRING,  
  1012. MS_Model_Membership::PAYMENT_TYPE_FINITE,  
  1013. ); 
  1014.  
  1015. if ( in_array( $membership->payment_type, $multi_invoice ) ) { 
  1016. // Update the current_invoice_number counter. 
  1017. $subscription->current_invoice_number = max( 
  1018. $subscription->current_invoice_number,  
  1019. $this->invoice_number + 1 
  1020. ); 
  1021.  
  1022. if ( MS_Gateway_Manual::ID == $this->gateway_id ) { 
  1023. $this->pay_it( $this->gateway_id ); 
  1024. break; 
  1025.  
  1026. case self::STATUS_DENIED: 
  1027. MS_Model_Event::save_event( MS_Model_Event::TYPE_PAYMENT_DENIED, $subscription ); 
  1028. break; 
  1029.  
  1030. case self::STATUS_PENDING: 
  1031. MS_Model_Event::save_event( MS_Model_Event::TYPE_PAYMENT_PENDING, $subscription ); 
  1032. break; 
  1033.  
  1034. default: 
  1035. do_action( 'ms_model_invoice_changed-unknown', $this ); 
  1036. break; 
  1037.  
  1038. $member->save(); 
  1039. $subscription->gateway_id = $this->gateway_id; 
  1040. $subscription->save(); 
  1041. $this->gateway_id = $this->gateway_id; 
  1042. $this->save(); 
  1043.  
  1044. return apply_filters( 
  1045. 'ms_model_invoice_changed',  
  1046. $this,  
  1047. $this 
  1048. ); 
  1049.  
  1050. /** 
  1051. * Add invoice notes. 
  1052. * 
  1053. * @since 1.0.0 
  1054. * 
  1055. * @param string $notes 
  1056. */ 
  1057. public function add_notes( $notes ) { 
  1058. $this->notes[] = apply_filters( 
  1059. 'ms_model_invoice_add_notes',  
  1060. $notes,  
  1061. $this 
  1062. ); 
  1063.  
  1064. /** 
  1065. * Get notes array as string. 
  1066. * 
  1067. * @since 1.0.0 
  1068. * 
  1069. * @return string The notes as text description. 
  1070. */ 
  1071. public function get_notes_desc() { 
  1072. $desc = $this->notes; 
  1073. if ( is_array( $desc ) ) { 
  1074. $desc = implode( "\n", $desc ); 
  1075.  
  1076. return apply_filters( 
  1077. 'ms_model_invoice_get_notes_desc',  
  1078. $desc,  
  1079. $this 
  1080. ); 
  1081.  
  1082. /** 
  1083. * Returns a translated version of the invoice status 
  1084. * 
  1085. * @since 1.0.0 
  1086. * @return string 
  1087. */ 
  1088. public function status_text() { 
  1089. static $Status = null; 
  1090.  
  1091. if ( null === $Status ) { 
  1092. $Status = self::get_status_types(); 
  1093.  
  1094. $result = $this->status; 
  1095.  
  1096. if ( isset( $Status[$this->status] ) ) { 
  1097. $result = $Status[$this->status]; 
  1098.  
  1099. return apply_filters( 
  1100. 'ms_invoice_status_text',  
  1101. $result,  
  1102. $this->status 
  1103. ); 
  1104.  
  1105. /** 
  1106. * Updates various fields that display/depend on the invoice total amount. 
  1107. * 
  1108. * @since 1.0.0 
  1109. */ 
  1110. public function total_amount_changed() { 
  1111. $subscription = $this->get_subscription(); 
  1112.  
  1113. // Allow add-ons or other plugins to set the tax infos for this invoice. 
  1114. $this->tax_rate = apply_filters( 
  1115. 'ms_invoice_tax_rate',  
  1116. 0,  
  1117. $this 
  1118. ); 
  1119. $this->tax_name = apply_filters( 
  1120. 'ms_invoice_tax_name',  
  1121. '',  
  1122. $this 
  1123. ); 
  1124.  
  1125. // Update the invoice descriptions that are displayed to the user. 
  1126. $this->description = apply_filters( 
  1127. 'ms_model_invoice_description',  
  1128. $subscription->get_payment_description( $this ) 
  1129. ); 
  1130. $this->short_description = apply_filters( 
  1131. 'ms_model_invoice_short_description',  
  1132. $subscription->get_payment_description( $this, true ) 
  1133. ); 
  1134.  
  1135. /** 
  1136. * Sets the invoice amount to the price defined by the membership settings. 
  1137. * 
  1138. * Provides the filter `ms_model_invoice_price_timeout` which can be used to 
  1139. * define the price-timeout value. The price will not be updated before the 
  1140. * timeout is reached. 
  1141. * 
  1142. * Only unpaid invoices are updated! 
  1143. * 
  1144. * @since 1.0.0 
  1145. */ 
  1146. private function refresh_amount() { 
  1147. // Never change the amount of paid invoices. 
  1148. if ( $this->is_paid() ) { return; } 
  1149.  
  1150. /** 
  1151. * Define a timeout for the price in unpaid invoices. 
  1152. * The price will not change before the timeout expires, after this 
  1153. * it is updated again based on the current membership settings. 
  1154. * 
  1155. * @var int 
  1156. */ 
  1157. $timeout = apply_filters( 
  1158. 'ms_model_invoice_price_timeout',  
  1159. 604800, // 604800 = 7 days 
  1160. $this 
  1161. ); 
  1162.  
  1163. $expire_timestamp = absint( $this->price_date ) + absint( $timeout ); 
  1164.  
  1165. // Do not change price before timeout is reached. 
  1166. if ( $expire_timestamp > time() ) { return; } 
  1167.  
  1168. // Store the current timestamp, so we don't refresh the price until 
  1169. // the timeout expires again. 
  1170. $this->price_date = time(); 
  1171. $membership = $this->get_membership(); 
  1172.  
  1173. // The invoice always has the real membership price as amount, never 
  1174. // the trial amount. 
  1175. $this->amount = $membership->price; // Without taxes! 
  1176.  
  1177. // Re-Calculate the subscription dates 
  1178. $this->set_due_date(); 
  1179.  
  1180. /** 
  1181. * Refreshes the due-date of the invoice. 
  1182. * 
  1183. * @since 1.0.0 
  1184. */ 
  1185. public function set_due_date() { 
  1186. // Never change due-date of paid invoices. 
  1187. if ( $this->is_paid() ) { return; } 
  1188.  
  1189. $subscription = $this->get_subscription(); 
  1190.  
  1191. $due_date = false; 
  1192.  
  1193. // Handle special cases in due date calculation. 
  1194. switch ( $subscription->status ) { 
  1195. case MS_Model_Relationship::STATUS_TRIAL: 
  1196. $due_date = $subscription->trial_expire_date; 
  1197. break; 
  1198.  
  1199. case MS_Model_Relationship::STATUS_ACTIVE: 
  1200. case MS_Model_Relationship::STATUS_CANCELED: 
  1201. $due_date = $subscription->expire_date; 
  1202. break; 
  1203.  
  1204. // Default due date is today. 
  1205. if ( empty( $due_date ) ) { 
  1206. if ( $subscription->is_trial_eligible() ) { 
  1207. /** 
  1208. * This invoice includes a trial period. 
  1209. * Payment is due on last day of trial 
  1210. */ 
  1211. $due_date = $subscription->trial_expire_date; 
  1212. } else { 
  1213. // No trial period is used for this invoice. Due now. 
  1214. $due_date = MS_Helper_Period::current_date(); 
  1215.  
  1216. // Update the trial expiration date. 
  1217. $this->trial_ends = $subscription->trial_expire_date; 
  1218.  
  1219. $this->due_date = $due_date; 
  1220.  
  1221. /** 
  1222. * Get invoice net amount: Amount excluding taxes. 
  1223. * 
  1224. * Discounting coupon and pro-rating. 
  1225. * Add taxes. 
  1226. * 
  1227. * @since 1.0.0 
  1228. */ 
  1229. private function get_net_amount() { 
  1230. if ( ! $this->is_paid() ) { 
  1231. $this->refresh_amount(); 
  1232.  
  1233. $net_amount = $this->amount; // Net amount 
  1234. $net_amount -= $this->discount; // Remove discount 
  1235. $net_amount -= $this->pro_rate; // Remove Pro-Rate 
  1236.  
  1237. if ( $net_amount < 0 ) { 
  1238. $net_amount = 0; 
  1239.  
  1240. // Set precission to 2 decimal points. 
  1241. $net_amount = round( $net_amount, 2 ); 
  1242.  
  1243. return apply_filters( 
  1244. 'ms_model_invoice_get_net_amount',  
  1245. $net_amount,  
  1246. $this 
  1247. ); 
  1248.  
  1249. /** 
  1250. * Returns the tax-value in currency (opposed to the percentage value) 
  1251. * 
  1252. * @since 1.0.0 
  1253. * @return float Total tax amount 
  1254. */ 
  1255. private function get_tax() { 
  1256. $tax_rate = $this->tax_rate; 
  1257.  
  1258. if ( ! is_numeric( $tax_rate ) ) { 
  1259. $tax_rate = 0; 
  1260.  
  1261. $value = $this->get_net_amount() * ( $tax_rate / 100 ); 
  1262. if ( $value < 0 ) { 
  1263. $value = 0; 
  1264.  
  1265. return $value; 
  1266.  
  1267. /** 
  1268. * Returns the tax-value in currency for the trial membership (opposed to 
  1269. * the percentage value) 
  1270. * 
  1271. * @since 1.0.0 
  1272. * @return float Total tax amount (trial membership) 
  1273. */ 
  1274. private function get_trial_tax() { 
  1275. $tax_rate = $this->tax_rate; 
  1276.  
  1277. if ( ! is_numeric( $tax_rate ) ) { 
  1278. $tax_rate = 0; 
  1279.  
  1280. $value = floatval( $this->trial_price ) * ( $tax_rate / 100 ); 
  1281. if ( $value < 0 ) { 
  1282. $value = 0; 
  1283.  
  1284. return $value; 
  1285.  
  1286. /** 
  1287. * Get invoice total. 
  1288. * 
  1289. * Discounting coupon and pro-rating. 
  1290. * Add taxes. 
  1291. * 
  1292. * @since 1.0.0 
  1293. */ 
  1294. private function get_total() { 
  1295. $total = $this->get_net_amount(); // Net amount 
  1296. $total += $this->get_tax(); // Tax-Rate was defined in `create_invoice()` 
  1297.  
  1298. if ( $total < 0 ) { 
  1299. $total = 0; 
  1300.  
  1301. // Set precission to 2 decimal points. 
  1302. $total = round( $total, 2 ); 
  1303.  
  1304. $this->total = apply_filters( 
  1305. 'ms_model_invoice_get_total',  
  1306. $total,  
  1307. $this 
  1308. ); 
  1309.  
  1310. return $this->total; 
  1311.  
  1312. /** 
  1313. * Get invoice trial price. 
  1314. * 
  1315. * @since 1.0.0 
  1316. */ 
  1317. private function get_trial_price() { 
  1318. $membership = $this->get_membership(); 
  1319. $trial_price = $membership->trial_price; // Net amount 
  1320. $trial_price += $this->get_trial_tax(); // Tax-Rate was defined in `create_invoice()` 
  1321.  
  1322. if ( $trial_price < 0 ) { 
  1323. $trial_price = 0; 
  1324.  
  1325. // Set precission to 2 decimal points. 
  1326. $trial_price = round( $trial_price, 2 ); 
  1327.  
  1328. $this->trial_price = apply_filters( 
  1329. 'ms_model_invoice_get_trial_price',  
  1330. $trial_price,  
  1331. $this 
  1332. ); 
  1333.  
  1334. return $this->trial_price; 
  1335.  
  1336. /** 
  1337. * Returns the public invoice number for this invoice. 
  1338. * The public invoice number is the official identifier that is displayed 
  1339. * to the end user that refers to an invoice 
  1340. * 
  1341. * @since 1.0.0 
  1342. * @return string The public invoice number. 
  1343. */ 
  1344. public function get_invoice_number() { 
  1345. $identifier = '#' . $this->id . '-' . $this->invoice_number; 
  1346.  
  1347. return apply_filters( 
  1348. 'ms_model_invoice_the_number',  
  1349. $identifier,  
  1350. $this 
  1351. ); 
  1352.  
  1353. /** 
  1354. * Returns the membership model that is linked to this invoice. 
  1355. * 
  1356. * @since 1.0.0 
  1357. * @return MS_Model_Membership 
  1358. */ 
  1359. public function get_membership() { 
  1360. return MS_Factory::load( 'MS_Model_Membership', $this->membership_id ); 
  1361.  
  1362. /** 
  1363. * Returns the membership model that is linked to this invoice. 
  1364. * 
  1365. * @since 1.0.0 
  1366. * @return MS_Model_Membership 
  1367. */ 
  1368. public function get_member() { 
  1369. return MS_Factory::load( 'MS_Model_Member', $this->user_id ); 
  1370.  
  1371. /** 
  1372. * Returns the subscription model that is linked to this invoice. 
  1373. * 
  1374. * @since 1.0.0 
  1375. * @return MS_Model_Relationship 
  1376. */ 
  1377. public function get_subscription() { 
  1378. return MS_Factory::load( 'MS_Model_Relationship', $this->ms_relationship_id ); 
  1379.  
  1380. /** 
  1381. * Returns property associated with the render. 
  1382. * 
  1383. * @since 1.0.0 
  1384. * @internal 
  1385. * @param string $property The name of a property. 
  1386. * @return mixed Returns mixed value of a property or NULL if a property doesn't exist. 
  1387. */ 
  1388. public function __get( $property ) { 
  1389. $value = null; 
  1390.  
  1391. switch ( $property ) { 
  1392. case 'total': 
  1393. $value = $this->get_total(); 
  1394. break; 
  1395.  
  1396. case 'trial_price': 
  1397. $value = $this->get_trial_price(); 
  1398. break; 
  1399.  
  1400. case 'invoice': 
  1401. $value = $this->id; 
  1402. break; 
  1403.  
  1404. case 'short_description': 
  1405. if ( empty( $this->short_description ) ) { 
  1406. $value = $this->description; 
  1407. } else { 
  1408. $value = $this->short_description; 
  1409. break; 
  1410.  
  1411. case 'invoice_date': 
  1412. $value = $this->invoice_date; 
  1413.  
  1414. if ( empty( $value ) ) { 
  1415. $value = get_the_date( 'Y-m-d', $this->id ); 
  1416. break; 
  1417.  
  1418. case 'pay_date': 
  1419. $this->validate_pay_date(); 
  1420. $value = $this->pay_date; 
  1421. break; 
  1422.  
  1423. case 'tax': 
  1424. $value = $this->get_tax(); 
  1425. break; 
  1426.  
  1427. case 'trial_tax': 
  1428. $value = $this->get_trial_tax(); 
  1429. break; 
  1430.  
  1431. case 'subtotal': 
  1432. $value = $this->get_net_amount(); 
  1433. break; 
  1434.  
  1435. default: 
  1436. if ( property_exists( $this, $property ) ) { 
  1437. $value = $this->$property; 
  1438. break; 
  1439.  
  1440. return apply_filters( 
  1441. 'ms_model_invoice__get',  
  1442. $value,  
  1443. $property,  
  1444. $this 
  1445. ); 
  1446.  
  1447. /** 
  1448. * Set specific property. 
  1449. * 
  1450. * @since 1.0.0 
  1451. * @internal 
  1452. * @param string $property The name of a property to associate. 
  1453. * @param mixed $value The value of a property. 
  1454. */ 
  1455. public function __set( $property, $value ) { 
  1456. switch ( $property ) { 
  1457. case 'name': 
  1458. case 'currency': 
  1459. $this->$property = sanitize_text_field( $value ); 
  1460. break; 
  1461.  
  1462. case 'notes': 
  1463. if ( is_array( $value ) ) { 
  1464. $this->notes = array_map( 'sanitize_text_field', $value ); 
  1465. } else { 
  1466. $this->notes = array( sanitize_text_field( $value ) ); 
  1467. break; 
  1468.  
  1469. case 'status': 
  1470. if ( array_key_exists( $value, self::get_status_types() ) ) { 
  1471. $this->$property = $value; 
  1472. break; 
  1473.  
  1474. case 'due_date': 
  1475. $this->$property = $this->validate_date( $value ); 
  1476. break; 
  1477.  
  1478. case 'amount': 
  1479. case 'discount': 
  1480. case 'pro_rate': 
  1481. case 'trial_price': 
  1482. $this->$property = floatval( $value ); 
  1483. $this->total_amount_changed(); 
  1484. $this->get_total(); 
  1485. $this->get_trial_price(); 
  1486. break; 
  1487.  
  1488. default: 
  1489. if ( property_exists( $this, $property ) ) { 
  1490. $this->$property = $value; 
  1491. break; 
  1492.  
  1493. do_action( 
  1494. 'ms_model_invoice__set_after',  
  1495. $property,  
  1496. $value,  
  1497. $this 
  1498. ); 
  1499.  
.