PageRenderTime 63ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/drupal/sites/all/modules/civicrm/CRM/Pledge/BAO/Payment.php

https://github.com/michaelmcandrew/ste
PHP | 736 lines | 464 code | 81 blank | 191 comment | 99 complexity | 6f8cc9156feaa81b5d0a00c5f33f0e01 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, AGPL-1.0, LGPL-2.1
  1. <?php
  2. /*
  3. +--------------------------------------------------------------------+
  4. | CiviCRM version 3.4 |
  5. +--------------------------------------------------------------------+
  6. | Copyright CiviCRM LLC (c) 2004-2011 |
  7. +--------------------------------------------------------------------+
  8. | This file is a part of CiviCRM. |
  9. | |
  10. | CiviCRM is free software; you can copy, modify, and distribute it |
  11. | under the terms of the GNU Affero General Public License |
  12. | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
  13. | |
  14. | CiviCRM is distributed in the hope that it will be useful, but |
  15. | WITHOUT ANY WARRANTY; without even the implied warranty of |
  16. | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
  17. | See the GNU Affero General Public License for more details. |
  18. | |
  19. | You should have received a copy of the GNU Affero General Public |
  20. | License and the CiviCRM Licensing Exception along |
  21. | with this program; if not, contact CiviCRM LLC |
  22. | at info[AT]civicrm[DOT]org. If you have questions about the |
  23. | GNU Affero General Public License or the licensing of CiviCRM, |
  24. | see the CiviCRM license FAQ at http://civicrm.org/licensing |
  25. +--------------------------------------------------------------------+
  26. */
  27. /**
  28. *
  29. * @package CRM
  30. * @copyright CiviCRM LLC (c) 2004-2011
  31. * $Id$
  32. *
  33. */
  34. require_once 'CRM/Pledge/DAO/Payment.php';
  35. class CRM_Pledge_BAO_Payment extends CRM_Pledge_DAO_Payment
  36. {
  37. /**
  38. * class constructor
  39. */
  40. function __construct( )
  41. {
  42. parent::__construct( );
  43. }
  44. /**
  45. * Function to get pledge payment details
  46. *
  47. * @param int $pledgeId pledge id
  48. *
  49. * @return array associated array of pledge payment details
  50. * @static
  51. */
  52. static function getPledgePayments( $pledgeId )
  53. {
  54. $query = "
  55. SELECT civicrm_pledge_payment.id id,
  56. scheduled_amount,
  57. scheduled_date,
  58. reminder_date,
  59. reminder_count,
  60. actual_amount,
  61. receive_date,
  62. civicrm_option_value.name as status,
  63. civicrm_option_value.label as label,
  64. civicrm_contribution.id as contribution_id
  65. FROM civicrm_pledge_payment
  66. LEFT JOIN civicrm_contribution ON civicrm_pledge_payment.contribution_id = civicrm_contribution.id
  67. LEFT JOIN civicrm_option_group ON ( civicrm_option_group.name = 'contribution_status' )
  68. LEFT JOIN civicrm_option_value ON ( civicrm_pledge_payment.status_id = civicrm_option_value.value AND
  69. civicrm_option_group.id = civicrm_option_value.option_group_id )
  70. WHERE pledge_id = %1
  71. ";
  72. $params[1] = array( $pledgeId, 'Integer' );
  73. $payment = CRM_Core_DAO::executeQuery( $query, $params );
  74. $paymentDetails = array( );
  75. while ( $payment->fetch( ) ) {
  76. $paymentDetails[$payment->id]['scheduled_amount'] = $payment->scheduled_amount;
  77. $paymentDetails[$payment->id]['scheduled_date' ] = $payment->scheduled_date;
  78. $paymentDetails[$payment->id]['reminder_date' ] = $payment->reminder_date;
  79. $paymentDetails[$payment->id]['reminder_count' ] = $payment->reminder_count;
  80. $paymentDetails[$payment->id]['total_amount' ] = $payment->actual_amount;
  81. $paymentDetails[$payment->id]['receive_date' ] = $payment->receive_date;
  82. $paymentDetails[$payment->id]['status' ] = $payment->status;
  83. $paymentDetails[$payment->id]['label' ] = $payment->label;
  84. $paymentDetails[$payment->id]['id' ] = $payment->id;
  85. $paymentDetails[$payment->id]['contribution_id' ] = $payment->contribution_id;
  86. }
  87. return $paymentDetails;
  88. }
  89. static function create( $params )
  90. {
  91. require_once 'CRM/Contribute/PseudoConstant.php';
  92. require_once 'CRM/Core/Transaction.php';
  93. $transaction = new CRM_Core_Transaction( );
  94. $contributionStatus = CRM_Contribute_PseudoConstant::contributionStatus( null, 'name' );
  95. //calculate the scheduled date for every installment
  96. $now = date('Ymd') . '000000';
  97. $statues = $prevScheduledDate = array ( );
  98. $prevScheduledDate[1] = CRM_Utils_Date::processDate( $params['scheduled_date']);
  99. if ( CRM_Utils_Date::overdue( $prevScheduledDate[1], $now ) ) {
  100. $statues[1] = array_search( 'Overdue', $contributionStatus);
  101. } else {
  102. $statues[1] = array_search( 'Pending', $contributionStatus);
  103. }
  104. for ( $i = 1; $i < $params['installments']; $i++ ) {
  105. $prevScheduledDate[$i+1] = self::calculateNextScheduledDate( $params,$i );
  106. if ( CRM_Utils_Date::overdue( $prevScheduledDate[$i+1], $now ) ) {
  107. $statues[$i+1] = array_search( 'Overdue', $contributionStatus);
  108. } else {
  109. $statues[$i+1] = array_search( 'Pending', $contributionStatus);
  110. }
  111. }
  112. if ( $params['installment_amount'] ) {
  113. $params['scheduled_amount'] = $params['installment_amount'];
  114. } else {
  115. $params['scheduled_amount'] = round( ( $params['amount'] / $params['installments'] ), 2 );
  116. }
  117. for ( $i = 1; $i <= $params['installments']; $i++ ) {
  118. //calculate the scheduled amount for every installment.
  119. if ( $i == $params['installments'] ) {
  120. $params['scheduled_amount'] = $params['amount'] - ($i-1) * $params['scheduled_amount'];
  121. }
  122. if ( ! isset( $params['contribution_id'] ) && $params['installments'] > 1 ) {
  123. $params['status_id'] = $statues[$i];
  124. }
  125. $params['scheduled_date'] = $prevScheduledDate[$i];
  126. $payment = self::add( $params );
  127. if ( is_a( $payment, 'CRM_Core_Error') ) {
  128. $transaction->rollback( );
  129. return $payment;
  130. }
  131. // we should add contribution id to only first payment record
  132. if ( isset( $params['contribution_id'] ) ){
  133. unset( $params['contribution_id'] );
  134. unset( $params['actual_amount'] );
  135. }
  136. }
  137. //update pledge status
  138. self::updatePledgePaymentStatus( $params['pledge_id'] );
  139. $transaction->commit( );
  140. return $payment;
  141. }
  142. /**
  143. * Add pledge payment
  144. *
  145. * @param array $params associate array of field
  146. *
  147. * @return pledge payment id
  148. * @static
  149. */
  150. static function add( $params )
  151. {
  152. require_once 'CRM/Utils/Hook.php';
  153. if ( CRM_Utils_Array::value( 'id', $params ) ) {
  154. CRM_Utils_Hook::pre( 'edit', 'PledgePayment', $params['id'], $params );
  155. } else {
  156. CRM_Utils_Hook::pre( 'create', 'PledgePayment', null, $params );
  157. }
  158. require_once 'CRM/Pledge/DAO/Payment.php';
  159. $payment = new CRM_Pledge_DAO_Payment( );
  160. $payment->copyValues( $params );
  161. // set currency for CRM-1496
  162. if ( ! isset( $payment->currency ) ) {
  163. $config =& CRM_Core_Config::singleton( );
  164. $payment->currency = $config->defaultCurrency;
  165. }
  166. $result = $payment->save( );
  167. if ( CRM_Utils_Array::value( 'id', $params ) ) {
  168. CRM_Utils_Hook::post( 'edit', 'PledgePayment', $payment->id, $payment);
  169. } else {
  170. CRM_Utils_Hook::post( 'create', 'PledgePayment', $payment->id, $payment );
  171. }
  172. return $result;
  173. }
  174. /**
  175. * Takes a bunch of params that are needed to match certain criteria and
  176. * retrieves the relevant objects. Typically the valid params are only
  177. * pledge id. We'll tweak this function to be more full featured over a period
  178. * of time. This is the inverse function of create. It also stores all the retrieved
  179. * values in the default array
  180. *
  181. * @param array $params (reference ) an assoc array of name/value pairs
  182. * @param array $defaults (reference ) an assoc array to hold the flattened values
  183. *
  184. * @return object CRM_Pledge_BAO_Payment object
  185. * @access public
  186. * @static
  187. */
  188. static function retrieve( &$params, &$defaults )
  189. {
  190. $payment = new CRM_Pledge_DAO_Payment;
  191. $payment->copyValues( $params );
  192. if ( $payment->find( true ) ) {
  193. CRM_Core_DAO::storeValues( $payment, $defaults );
  194. return $payment;
  195. }
  196. return null;
  197. }
  198. /**
  199. * Function to delete all pledge payments
  200. *
  201. * @param int $id pledge id
  202. *
  203. * @access public
  204. * @static
  205. *
  206. */
  207. static function deletePayments( $id )
  208. {
  209. require_once 'CRM/Utils/Rule.php';
  210. if ( ! CRM_Utils_Rule::positiveInteger( $id ) ) {
  211. return false;
  212. }
  213. require_once 'CRM/Core/Transaction.php';
  214. $transaction = new CRM_Core_Transaction( );
  215. $payment = new CRM_Pledge_DAO_Payment( );
  216. $payment->pledge_id = $id;
  217. if ( $payment->find( ) ) {
  218. while ( $payment->fetch( ) ) {
  219. //also delete associated contribution.
  220. if ( $payment->contribution_id ) {
  221. require_once 'CRM/Contribute/BAO/Contribution.php';
  222. CRM_Contribute_BAO_Contribution::deleteContribution( $payment->contribution_id );
  223. }
  224. $payment->delete( );
  225. }
  226. }
  227. $transaction->commit( );
  228. return true;
  229. }
  230. /**
  231. * On delete contribution record update associated pledge payment and pledge.
  232. *
  233. * @param int $contributionID contribution id
  234. *
  235. * @access public
  236. * @static
  237. */
  238. static function resetPledgePayment( $contributionID )
  239. {
  240. //get all status
  241. require_once 'CRM/Contribute/PseudoConstant.php';
  242. $allStatus = CRM_Contribute_PseudoConstant::contributionStatus( null, 'name' );
  243. require_once 'CRM/Core/Transaction.php';
  244. $transaction = new CRM_Core_Transaction( );
  245. $payment = new CRM_Pledge_DAO_Payment( );
  246. $payment->contribution_id = $contributionID;
  247. if ( $payment->find( true ) ) {
  248. $payment->contribution_id = 'null';
  249. $payment->status_id = array_search( 'Pending', $allStatus );
  250. $payment->scheduled_date = NULL;
  251. $payment->reminder_date = NULL;
  252. $payment->save( );
  253. //update pledge status.
  254. $pledgeID = $payment->pledge_id;
  255. $pledgeStatusID = self::calculatePledgeStatus( $pledgeID );
  256. CRM_Core_DAO::setFieldValue( 'CRM_Pledge_DAO_Pledge', $pledgeID, 'status_id', $pledgeStatusID );
  257. $payment->free( );
  258. }
  259. $transaction->commit( );
  260. return true;
  261. }
  262. /**
  263. * update Pledge Payment Status
  264. *
  265. * @param int $pledgeID, id of pledge
  266. * @param array $paymentIDs, ids of pledge payment(s) to update
  267. * @param int $paymentStatusID, payment status to set
  268. * @param int $pledgeStatus, pledge status to change (if needed)
  269. * @param float $actualAmount, actual amount being paid
  270. * @param bool $adjustTotalAmount, is amount being paid different from scheduled amount?
  271. * @param bool $isScriptUpdate, is function being called from bin script?
  272. *
  273. * @return int $newStatus, updated status id (or 0)
  274. */
  275. function updatePledgePaymentStatus( $pledgeID,
  276. $paymentIDs = null,
  277. $paymentStatusID = null,
  278. $pledgeStatusID = null,
  279. $actualAmount = 0,
  280. $adjustTotalAmount = false,
  281. $isScriptUpdate = false )
  282. {
  283. $totalAmountClause = '';
  284. $paymentContributionId = null;
  285. $editScheduled = false;
  286. //get all statuses
  287. require_once 'CRM/Contribute/PseudoConstant.php';
  288. $allStatus = CRM_Contribute_PseudoConstant::contributionStatus( null, 'name' );
  289. // if we get do not get contribution id means we are editing the scheduled payment.
  290. if ( !empty( $paymentIDs ) ) {
  291. $payments = implode( ',', $paymentIDs );
  292. $paymentContributionId = CRM_Core_DAO::getFieldValue( 'CRM_Pledge_DAO_Payment',
  293. $payments,
  294. 'contribution_id',
  295. 'id'
  296. );
  297. if ( ! $paymentContributionId ) {
  298. $editScheduled = true;
  299. }
  300. }
  301. // if payment ids are passed, we update payment table first, since payments statuses are not dependent on pledge status
  302. if ( ( !empty( $paymentIDs ) || $pledgeStatusID == array_search( 'Cancelled', $allStatus ) ) && ( !$editScheduled || $isScriptUpdate) ) {
  303. if ( $pledgeStatusID == array_search( 'Cancelled', $allStatus ) ) {
  304. $paymentStatusID = $pledgeStatusID ;
  305. }
  306. self::updatePledgePayments( $pledgeID, $paymentStatusID, $paymentIDs, $actualAmount, $paymentContributionId ,$isScriptUpdate );
  307. }
  308. if ( !empty( $paymentIDs ) && $actualAmount ) {
  309. $payments = implode( ',', $paymentIDs );
  310. $pledgeScheduledAmount = CRM_Core_DAO::getFieldValue( 'CRM_Pledge_DAO_Payment',
  311. $payments,
  312. 'scheduled_amount',
  313. 'id'
  314. );
  315. $pledgeStatusId = self::calculatePledgeStatus( $pledgeID );
  316. // Actual Pledge Amount
  317. $actualPledgeAmount = CRM_Core_DAO::getFieldValue( 'CRM_Pledge_DAO_Pledge',
  318. $pledgeID,
  319. 'amount',
  320. 'id'
  321. );
  322. // while editing scheduled we need to check if we are editing last pending
  323. if ( !$paymentContributionId ) {
  324. $checkPendingCount = self::getOldestPledgePayment( $pledgeID, 2 );
  325. if ( $checkPendingCount['count'] == 1 ) {
  326. $lastPending = true;
  327. }
  328. }
  329. // check if this is the last payment and adjust the actual amount.
  330. if ( $pledgeStatusId && $pledgeStatusId == array_search( 'Completed', $allStatus ) || $lastPending ) {
  331. // last scheduled payment
  332. if ( $actualAmount < $pledgeScheduledAmount ) {
  333. // actual amount is less than the scheduled amount, so enter new pledge payment record
  334. $pledgeFrequencyUnit= CRM_Core_DAO::getFieldValue( 'CRM_Pledge_DAO_Pledge',$pledgeID,'frequency_unit','id');
  335. $pledgeFrequencyInterval = CRM_Core_DAO::getFieldValue( 'CRM_Pledge_DAO_Pledge',$pledgeID,'frequency_interval','id');
  336. $pledgeScheduledDate = CRM_Core_DAO::getFieldValue( 'CRM_Pledge_DAO_Payment',$payments,'scheduled_date','id');
  337. $scheduled_date = CRM_Utils_Date::processDate( $pledgeScheduledDate );
  338. $date['year'] = (int) substr($scheduled_date, 0, 4);
  339. $date['month'] = (int) substr($scheduled_date, 4, 2);
  340. $date['day'] = (int) substr($scheduled_date, 6, 2);
  341. $newDate = date( 'YmdHis', mktime ( 0, 0, 0, $date['month'], $date['day'], $date['year'] ));
  342. $ScheduledDate = CRM_Utils_Date::format(CRM_Utils_Date::intervalAdd( $pledgeFrequencyUnit,
  343. $pledgeFrequencyInterval , $newDate ) );
  344. $pledgeParams = array(
  345. 'status_id' => array_search( 'Pending', $allStatus ),
  346. 'pledge_id' => $pledgeID,
  347. 'scheduled_amount' => ( $pledgeScheduledAmount - $actualAmount ),
  348. 'scheduled_date' => $ScheduledDate,
  349. );
  350. $payment = self::add( $pledgeParams );
  351. // while editing schedule, after adding a new pledge payemnt update the scheduled amount of the current payment
  352. if ( !$paymentContributionId ) {
  353. CRM_Core_DAO::setFieldValue( 'CRM_Pledge_DAO_Payment', $payments, 'scheduled_amount', $actualAmount );
  354. }
  355. } else {
  356. $adjustTotalAmount = true;
  357. }
  358. } elseif ( !$adjustTotalAmount ) {
  359. // not last schedule amount and also not selected to adjust Total
  360. $paymentContributionId = CRM_Core_DAO::getFieldValue( 'CRM_Pledge_DAO_Payment',
  361. $payments,
  362. 'contribution_id',
  363. 'id'
  364. );
  365. self::adjustPledgePayment( $pledgeID, $actualAmount , $pledgeScheduledAmount, $paymentContributionId , $payments );
  366. // while editing schedule, after adding a new pledge payemnt update the scheduled amount of the current payment
  367. if ( !$paymentContributionId ) {
  368. CRM_Core_DAO::setFieldValue( 'CRM_Pledge_DAO_Payment', $payments, 'scheduled_amount', $actualAmount );
  369. }
  370. // after adjusting all payments check if the actual amount was greater than the actual remaining amount , if so then update the total pledge amount.
  371. $pledgeStatusId = self::calculatePledgeStatus( $pledgeID );
  372. $balanceQuery = "
  373. SELECT sum( civicrm_pledge_payment.actual_amount )
  374. FROM civicrm_pledge_payment
  375. WHERE civicrm_pledge_payment.pledge_id = %1
  376. AND civicrm_pledge_payment.status_id = 1
  377. ";
  378. $totalPaidParams = array( 1 => array( $pledgeID, 'Integer' ) );
  379. $totalPaidAmount = CRM_Core_DAO::singleValueQuery( $balanceQuery, $totalPaidParams );
  380. $remainingTotalAmount = ( $actualPledgeAmount - $totalPaidAmount );
  381. if ( ( $pledgeStatusId && $pledgeStatusId == array_search( 'Completed', $allStatus ) ) && ( ( $actualAmount > $remainingTotalAmount ) || ( $actualAmount >= $actualPledgeAmount ) ) ) {
  382. $totalAmountClause = ", civicrm_pledge.amount = {$totalPaidAmount}";
  383. }
  384. }
  385. if ( $adjustTotalAmount ) {
  386. $newTotalAmount = ( $actualPledgeAmount + ( $actualAmount - $pledgeScheduledAmount ) );
  387. $totalAmountClause = ", civicrm_pledge.amount = {$newTotalAmount}";
  388. if ( !$paymentContributionId ) {
  389. CRM_Core_DAO::setFieldValue( 'CRM_Pledge_DAO_Payment', $payments, 'scheduled_amount', $actualAmount );
  390. }
  391. }
  392. }
  393. $cancelDateClause = $endDateClause = null;
  394. //update pledge and payment status if status is Completed/Cancelled.
  395. if ( $pledgeStatusID && $pledgeStatusID == array_search( 'Cancelled', $allStatus ) ) {
  396. $paymentStatusID = $pledgeStatusID;
  397. $cancelDateClause = ", civicrm_pledge.cancel_date = CURRENT_TIMESTAMP ";
  398. } else {
  399. // get pledge status
  400. $pledgeStatusID = self::calculatePledgeStatus( $pledgeID );
  401. }
  402. if ( $pledgeStatusID == array_search( 'Completed', $allStatus ) ) {
  403. $endDateClause = ", civicrm_pledge.end_date = CURRENT_TIMESTAMP ";
  404. }
  405. //update pledge status
  406. $query = "
  407. UPDATE civicrm_pledge
  408. SET civicrm_pledge.status_id = %1
  409. {$cancelDateClause} {$endDateClause} {$totalAmountClause}
  410. WHERE civicrm_pledge.id = %2
  411. ";
  412. $params = array( 1 => array( $pledgeStatusID, 'Integer' ),
  413. 2 => array( $pledgeID, 'Integer' ) );
  414. $dao = CRM_Core_DAO::executeQuery( $query, $params );
  415. return $pledgeStatusID;
  416. }
  417. /**
  418. * Calculate the base scheduled date. This function effectively 'rounds' the $params['scheduled_date'] value
  419. * to the first payment date with respect to the frequency day - ie. if payments are on the 15th of the month the date returned
  420. * will be the 15th of the relevant month. Then to calculate the payments you can use intervalAdd ie.
  421. * CRM_Utils_Date::intervalAdd( $params['frequency_unit'], $i * ($params['frequency_interval']) , calculateBaseScheduledDate( &$params )))
  422. *
  423. *
  424. * @param array $params
  425. *
  426. * @return array $newdate Next scheduled date as an array
  427. * @static
  428. */
  429. static function calculateBaseScheduleDate( &$params)
  430. {
  431. $date = array();
  432. $scheduled_date = CRM_Utils_Date::processDate( $params['scheduled_date']);
  433. $date['year'] = (int) substr($scheduled_date, 0, 4);
  434. $date['month'] = (int) substr($scheduled_date, 4, 2);
  435. $date['day'] = (int) substr($scheduled_date, 6, 2);
  436. //calculation of schedule date according to frequency day of period
  437. //frequency day is not applicable for daily installments
  438. if ( $params['frequency_unit'] != 'day' && $params['frequency_unit'] != 'year') {
  439. if ( $params['frequency_unit'] != 'week' ) {
  440. //for month use day of next month as next payment date
  441. $date['day'] = $params['frequency_day'];
  442. } else if ( $params['frequency_unit'] == 'week' ) {
  443. //for week calculate day of week ie. Sunday,Monday etc. as next payment date
  444. $dayOfWeek = date('w',mktime(0, 0, 0, $date['month'], $date['day'], $date['year'] ));
  445. $frequencyDay = $params['frequency_day'] - $dayOfWeek;
  446. $scheduleDate = explode ( "-", date( 'n-j-Y', mktime ( 0, 0, 0, $date['month'],
  447. $date['day'] + $frequencyDay, $date['year'] )) );
  448. $date['month'] = $scheduleDate[0];
  449. $date['day'] = $scheduleDate[1];
  450. $date['year'] = $scheduleDate[2];
  451. }
  452. }
  453. $newdate = date( 'YmdHis', mktime ( 0, 0, 0, $date['month'], $date['day'], $date['year'] ));
  454. return $newdate;
  455. }
  456. /**
  457. * Calculate next scheduled pledge payment date. Function calculates next pledge payment date.
  458. *
  459. * @param array params - must include frequency unit & frequency interval
  460. * @param int paymentNo number of payment in sequence (e.g. 1 for first calculated payment (treat initial payment as 0)
  461. * @param datestring basePaymentDate - date to calculate payments from. This would normally be the
  462. * first day of the pledge (default) & is calculated off the 'scheduled date' param. Returned date will
  463. * be equal to basePaymentDate normalised to fit the 'pledge pattern' + number of installments
  464. *
  465. * @return formatted date
  466. *
  467. */
  468. static function calculateNextScheduledDate( &$params, $paymentNo, $basePaymentDate = null){
  469. if (!$basePaymentDate){
  470. $basePaymentDate = self::calculateBaseScheduleDate( $params );
  471. }
  472. return CRM_Utils_Date::format(
  473. CRM_Utils_Date::intervalAdd(
  474. $params['frequency_unit'],
  475. $paymentNo * ($params['frequency_interval']) ,
  476. $basePaymentDate));
  477. }
  478. /**
  479. * Calculate the pledge status
  480. *
  481. * @param int $pledgeId pledge id
  482. *
  483. * @return int $statusId calculated status id of pledge
  484. * @static
  485. */
  486. static function calculatePledgeStatus( $pledgeId )
  487. {
  488. require_once 'CRM/Contribute/PseudoConstant.php';
  489. $paymentStatusTypes = CRM_Contribute_PseudoConstant::contributionStatus( null, 'name' );
  490. //retrieve all pledge payments for this particular pledge
  491. $allPledgePayments = array( );
  492. $returnProperties = array( 'status_id' );
  493. CRM_Core_DAO::commonRetrieveAll( 'CRM_Pledge_DAO_Payment', 'pledge_id', $pledgeId, $allPledgePayments, $returnProperties );
  494. // build pledge payment statuses
  495. foreach ( $allPledgePayments as $key => $value ) {
  496. $allStatus[$value['id']] = $paymentStatusTypes[$value['status_id']];
  497. }
  498. if ( array_search( 'Overdue', $allStatus ) ) {
  499. $statusId = array_search( 'Overdue', $paymentStatusTypes );
  500. } else if ( array_search( 'Completed', $allStatus ) ) {
  501. if ( count( array_count_values( $allStatus) ) == 1 ) {
  502. $statusId = array_search( 'Completed', $paymentStatusTypes );
  503. } else {
  504. $statusId = array_search( 'In Progress', $paymentStatusTypes );
  505. }
  506. } else {
  507. $statusId = array_search( 'Pending', $paymentStatusTypes );
  508. }
  509. return $statusId;
  510. }
  511. /**
  512. * Function to update pledge payment table
  513. *
  514. * @param int $pledgeId pledge id
  515. * @param array $paymentIds payment ids to be updated
  516. * @param int $paymentStatusId payment status id to set
  517. * @param float $actualAmount, actual amount being paid
  518. * @param int $contributionId, Id of associated contribution when payment is recorded
  519. * @param bool $isScriptUpdate, is function being called from bin script?
  520. * @static
  521. */
  522. static function updatePledgePayments( $pledgeId,
  523. $paymentStatusId,
  524. $paymentIds = null,
  525. $actualAmount = 0,
  526. $contributionId = null,
  527. $isScriptUpdate = false )
  528. {
  529. $allStatus = CRM_Contribute_PseudoConstant::contributionStatus( null, 'name' );
  530. $paymentClause = null;
  531. if ( !empty( $paymentIds ) ) {
  532. $payments = implode( ',', $paymentIds );
  533. $paymentClause = " AND civicrm_pledge_payment.id IN ( {$payments} )";
  534. }
  535. $actualAmountClause = NULL;
  536. $contributionIdClause = NULL;
  537. if ( isset( $contributionId ) && !$isScriptUpdate ) {
  538. $contributionIdClause = ", civicrm_pledge_payment.contribution_id = {$contributionId}";
  539. $actualAmountClause =", civicrm_pledge_payment.actual_amount = {$actualAmount}";
  540. }
  541. $cancelClause = null;
  542. if ( $paymentStatusId == array_search( 'Cancelled', $allStatus ) ) {
  543. $completedStatus = array_search( 'Completed', $allStatus );
  544. $cancelClause = "AND civicrm_pledge_payment.status_id != {$completedStatus}";
  545. }
  546. $query = "
  547. UPDATE civicrm_pledge_payment
  548. SET civicrm_pledge_payment.status_id = {$paymentStatusId}
  549. {$actualAmountClause} {$contributionIdClause}
  550. WHERE civicrm_pledge_payment.pledge_id = %1
  551. {$paymentClause} {$cancelClause}
  552. ";
  553. //get all status
  554. require_once 'CRM/Contribute/PseudoConstant.php';
  555. $params = array( 1 => array( $pledgeId, 'Integer' ) );
  556. $dao = CRM_Core_DAO::executeQuery( $query, $params );
  557. }
  558. /**
  559. * Function to update pledge payment table when reminder is sent
  560. * @param int $paymentId payment id
  561. *
  562. * @static
  563. */
  564. static function updateReminderDetails( $paymentId )
  565. {
  566. $query = "
  567. UPDATE civicrm_pledge_payment
  568. SET civicrm_pledge_payment.reminder_date = CURRENT_TIMESTAMP,
  569. civicrm_pledge_payment.reminder_count = civicrm_pledge_payment.reminder_count + 1
  570. WHERE civicrm_pledge_payment.id = {$paymentId}
  571. ";
  572. $dao = CRM_Core_DAO::executeQuery( $query );
  573. }
  574. /**
  575. * Function to get oldest pending or in progress pledge payments
  576. *
  577. * @param int $pledgeID pledge id
  578. *
  579. * @return array associated array of pledge details
  580. * @static
  581. */
  582. static function getOldestPledgePayment( $pledgeID, $limit = 1 )
  583. {
  584. //get pending / overdue statuses
  585. $pledgeStatuses = CRM_Contribute_PseudoConstant::contributionStatus( null, 'name' );
  586. //get pending and overdue payments
  587. $status[] = array_search( 'Pending', $pledgeStatuses );
  588. $status[] = array_search( 'Overdue', $pledgeStatuses );
  589. $statusClause = " IN (" . implode( ',', $status ) . ")";
  590. $query = "
  591. SELECT civicrm_pledge_payment.id id, civicrm_pledge_payment.scheduled_amount amount
  592. FROM civicrm_pledge, civicrm_pledge_payment
  593. WHERE civicrm_pledge.id = civicrm_pledge_payment.pledge_id
  594. AND civicrm_pledge_payment.status_id {$statusClause}
  595. AND civicrm_pledge.id = %1
  596. ORDER BY civicrm_pledge_payment.scheduled_date ASC
  597. LIMIT 0, %2
  598. ";
  599. $params[1] = array( $pledgeID, 'Integer' );
  600. $params[2] = array( $limit, 'Integer' );
  601. $payment = CRM_Core_DAO::executeQuery( $query, $params );
  602. $count = 1;
  603. $paymentDetails = array();
  604. while ( $payment->fetch( ) ) {
  605. $paymentDetails[] = array( 'id' => $payment->id,
  606. 'amount' => $payment->amount,
  607. 'count' => $count );
  608. $count++;
  609. }
  610. return end($paymentDetails);
  611. }
  612. static function adjustPledgePayment( $pledgeID, $actualAmount, $pledgeScheduledAmount, $paymentContributionId = null, $pPaymentId = null )
  613. {
  614. $allStatus = CRM_Contribute_PseudoConstant::contributionStatus( null, 'name' );
  615. $oldestPayment = self::getOldestPledgePayment( $pledgeID );
  616. if ( !$paymentContributionId ) {
  617. // means we are editing payment scheduled payment, so get the second pending to update.
  618. $oldestPayment = self::getOldestPledgePayment( $pledgeID, 2 );
  619. if ( ( $oldestPayment['count'] != 1 ) && ( $oldestPayment['id'] == $pPaymentId ) ) {
  620. $oldestPayment = CRM_Pledge_BAO_Payment::getOldestPledgePayment( $pledgeID );
  621. }
  622. }
  623. if ( $oldestPayment ) {
  624. // not the last scheduled payment and the actual amount is less than the expected , add it to oldest pending.
  625. if ( ( $actualAmount != $pledgeScheduledAmount ) && ( ( $actualAmount < $pledgeScheduledAmount ) || ( ( $actualAmount - $pledgeScheduledAmount ) < $oldestPayment['amount'] ) ) ) {
  626. $oldScheduledAmount = $oldestPayment['amount'];
  627. $newScheduledAmount = $oldScheduledAmount + ( $pledgeScheduledAmount - $actualAmount );
  628. //store new amount in oldest pending payment record.
  629. CRM_Core_DAO::setFieldValue('CRM_Pledge_DAO_Payment', $oldestPayment['id'], 'scheduled_amount', $newScheduledAmount );
  630. } elseif ( ( $actualAmount > $pledgeScheduledAmount ) && ( ( $actualAmount - $pledgeScheduledAmount ) >= $oldestPayment['amount'] ) ) {
  631. // here the actual amount is greater than expected and also greater than the next installment amount, so update the next installment as complete and again add it to next subsequent pending payment
  632. // set the actual amount of the next pending to '0', set contribution Id to current contribution Id and status as completed
  633. $paymentId = array ( $oldestPayment['id'] );
  634. self::updatePledgePayments( $pledgeID, array_search( 'Completed', $allStatus ) , $paymentId, 0,$paymentContributionId );
  635. CRM_Core_DAO::setFieldValue( 'CRM_Pledge_DAO_Payment', $oldestPayment['id'], 'scheduled_amount', 0 , 'id' );
  636. $oldestPayment = self::getOldestPledgePayment( $pledgeID );
  637. if ( !$paymentContributionId ) {
  638. // means we are editing payment scheduled payment.
  639. $oldestPaymentAmount = self::getOldestPledgePayment( $pledgeID, 2 );
  640. }
  641. $newActualAmount = ( $actualAmount - $pledgeScheduledAmount );
  642. $newPledgeScheduledAmount = $oldestPayment['amount'];
  643. if ( !$paymentContributionId ) {
  644. $newActualAmount = ( $actualAmount - $pledgeScheduledAmount );
  645. $newPledgeScheduledAmount = $oldestPaymentAmount['amount'];
  646. // means we are editing payment scheduled payment, so update scheduled amount.
  647. CRM_Core_DAO::setFieldValue( 'CRM_Pledge_DAO_Payment', $oldestPaymentAmount['id'], 'scheduled_amount', $newActualAmount );
  648. }
  649. if ( $newActualAmount > 0 ) {
  650. self::adjustPledgePayment( $pledgeID, $newActualAmount, $newPledgeScheduledAmount, $paymentContributionId );
  651. }
  652. }
  653. }
  654. }
  655. }