3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.5 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2014 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
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. |
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. |
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 +--------------------------------------------------------------------+
31 * @copyright CiviCRM LLC (c) 2004-2014
35 class CRM_Pledge_BAO_PledgePayment
extends CRM_Pledge_DAO_PledgePayment
{
40 function __construct() {
41 parent
::__construct();
45 * Function to get pledge payment details
47 * @param int $pledgeId pledge id
49 * @return array associated array of pledge payment details
52 static function getPledgePayments($pledgeId) {
54 SELECT civicrm_pledge_payment.id id,
61 civicrm_pledge_payment.currency,
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
67 LEFT JOIN civicrm_contribution ON civicrm_pledge_payment.contribution_id = civicrm_contribution.id
68 LEFT JOIN civicrm_option_group ON ( civicrm_option_group.name = 'contribution_status' )
69 LEFT JOIN civicrm_option_value ON ( civicrm_pledge_payment.status_id = civicrm_option_value.value AND
70 civicrm_option_group.id = civicrm_option_value.option_group_id )
74 $params[1] = array($pledgeId, 'Integer');
75 $payment = CRM_Core_DAO
::executeQuery($query, $params);
77 $paymentDetails = array();
78 while ($payment->fetch()) {
79 $paymentDetails[$payment->id
]['scheduled_amount'] = $payment->scheduled_amount
;
80 $paymentDetails[$payment->id
]['scheduled_date'] = $payment->scheduled_date
;
81 $paymentDetails[$payment->id
]['reminder_date'] = $payment->reminder_date
;
82 $paymentDetails[$payment->id
]['reminder_count'] = $payment->reminder_count
;
83 $paymentDetails[$payment->id
]['total_amount'] = $payment->actual_amount
;
84 $paymentDetails[$payment->id
]['receive_date'] = $payment->receive_date
;
85 $paymentDetails[$payment->id
]['status'] = $payment->status
;
86 $paymentDetails[$payment->id
]['label'] = $payment->label
;
87 $paymentDetails[$payment->id
]['id'] = $payment->id
;
88 $paymentDetails[$payment->id
]['contribution_id'] = $payment->contribution_id
;
89 $paymentDetails[$payment->id
]['currency'] = $payment->currency
;
92 return $paymentDetails;
100 static function create($params) {
101 $transaction = new CRM_Core_Transaction();
102 $contributionStatus = CRM_Contribute_PseudoConstant
::contributionStatus(NULL, 'name');
104 //calculate the scheduled date for every installment
105 $now = date('Ymd') . '000000';
106 $statues = $prevScheduledDate = array();
107 $prevScheduledDate[1] = CRM_Utils_Date
::processDate($params['scheduled_date']);
109 if (CRM_Utils_Date
::overdue($prevScheduledDate[1], $now)) {
110 $statues[1] = array_search('Overdue', $contributionStatus);
113 $statues[1] = array_search('Pending', $contributionStatus);
116 for ($i = 1; $i < $params['installments']; $i++
) {
117 $prevScheduledDate[$i +
1] = self
::calculateNextScheduledDate($params, $i);
118 if (CRM_Utils_Date
::overdue($prevScheduledDate[$i +
1], $now)) {
119 $statues[$i +
1] = array_search('Overdue', $contributionStatus);
122 $statues[$i +
1] = array_search('Pending', $contributionStatus);
126 if ($params['installment_amount']) {
127 $params['scheduled_amount'] = $params['installment_amount'];
130 $params['scheduled_amount'] = round(($params['amount'] / $params['installments']), 2);
133 for ($i = 1; $i <= $params['installments']; $i++
) {
134 //calculate the scheduled amount for every installment.
135 if ($i == $params['installments']) {
136 $params['scheduled_amount'] = $params['amount'] - ($i - 1) * $params['scheduled_amount'];
138 if (!isset($params['contribution_id']) && $params['installments'] > 1) {
139 $params['status_id'] = $statues[$i];
142 $params['scheduled_date'] = $prevScheduledDate[$i];
143 $payment = self
::add($params);
144 if (is_a($payment, 'CRM_Core_Error')) {
145 $transaction->rollback();
149 // we should add contribution id to only first payment record
150 if (isset($params['contribution_id'])) {
151 unset($params['contribution_id']);
152 unset($params['actual_amount']);
156 //update pledge status
157 self
::updatePledgePaymentStatus($params['pledge_id']);
159 $transaction->commit();
166 * @param array $params associate array of field
168 * @return pledge payment id
171 static function add($params) {
172 if (!empty($params['id'])) {
173 CRM_Utils_Hook
::pre('edit', 'PledgePayment', $params['id'], $params);
176 CRM_Utils_Hook
::pre('create', 'PledgePayment', NULL, $params);
179 $payment = new CRM_Pledge_DAO_PledgePayment();
180 $payment->copyValues($params);
182 // set currency for CRM-1496
183 if (!isset($payment->currency
)) {
184 $config = CRM_Core_Config
::singleton();
185 $payment->currency
= $config->defaultCurrency
;
188 $result = $payment->save();
190 if (!empty($params['id'])) {
191 CRM_Utils_Hook
::post('edit', 'PledgePayment', $payment->id
, $payment);
194 CRM_Utils_Hook
::post('create', 'PledgePayment', $payment->id
, $payment);
202 * Takes a bunch of params that are needed to match certain criteria and
203 * retrieves the relevant objects. Typically the valid params are only
204 * pledge id. We'll tweak this function to be more full featured over a period
205 * of time. This is the inverse function of create. It also stores all the retrieved
206 * values in the default array
208 * @param array $params (reference ) an assoc array of name/value pairs
209 * @param array $defaults (reference ) an assoc array to hold the flattened values
211 * @return object CRM_Pledge_BAO_PledgePayment object
215 static function retrieve(&$params, &$defaults) {
216 $payment = new CRM_Pledge_BAO_PledgePayment
;
217 $payment->copyValues($params);
218 if ($payment->find(TRUE)) {
219 CRM_Core_DAO
::storeValues($payment, $defaults);
226 * Delete pledge payment
230 * @internal param array $params associate array of field
232 * @return pledge payment id
235 static function del($id) {
236 $payment = new CRM_Pledge_DAO_PledgePayment();
238 if ($payment->find()) {
241 CRM_Utils_Hook
::pre('delete', 'PledgePayment', $id, $payment);
243 $result = $payment->delete();
245 CRM_Utils_Hook
::post('delete', 'PledgePayment', $id, $payment);
255 * Function to delete all pledge payments
257 * @param int $id pledge id
264 static function deletePayments($id) {
265 if (!CRM_Utils_Rule
::positiveInteger($id)) {
269 $transaction = new CRM_Core_Transaction();
271 $payment = new CRM_Pledge_DAO_PledgePayment();
272 $payment->pledge_id
= $id;
274 if ($payment->find()) {
275 while ($payment->fetch()) {
276 //also delete associated contribution.
277 if ($payment->contribution_id
) {
278 CRM_Contribute_BAO_Contribution
::deleteContribution($payment->contribution_id
);
284 $transaction->commit();
290 * On delete contribution record update associated pledge payment and pledge.
292 * @param int $contributionID contribution id
298 static function resetPledgePayment($contributionID) {
300 $allStatus = CRM_Contribute_PseudoConstant
::contributionStatus(NULL, 'name');
302 $transaction = new CRM_Core_Transaction();
304 $payment = new CRM_Pledge_DAO_PledgePayment();
305 $payment->contribution_id
= $contributionID;
306 if ($payment->find(TRUE)) {
307 $payment->contribution_id
= 'null';
308 $payment->status_id
= array_search('Pending', $allStatus);
309 $payment->scheduled_date
= NULL;
310 $payment->reminder_date
= NULL;
311 $payment->scheduled_amount
= $payment->actual_amount
;
312 $payment->actual_amount
= 'null';
315 //update pledge status.
316 $pledgeID = $payment->pledge_id
;
317 $pledgeStatusID = self
::calculatePledgeStatus($pledgeID);
318 CRM_Core_DAO
::setFieldValue('CRM_Pledge_DAO_Pledge', $pledgeID, 'status_id', $pledgeStatusID);
323 $transaction->commit();
328 * update Pledge Payment Status
330 * @param int $pledgeID , id of pledge
331 * @param array $paymentIDs , ids of pledge payment(s) to update
332 * @param int $paymentStatusID , payment status to set
333 * @param null $pledgeStatusID
334 * @param float|int $actualAmount , actual amount being paid
335 * @param bool $adjustTotalAmount , is amount being paid different from scheduled amount?
336 * @param bool $isScriptUpdate , is function being called from bin script?
338 * @internal param int $pledgeStatus , pledge status to change (if needed)
339 * @return int $newStatus, updated status id (or 0)
341 static function updatePledgePaymentStatus(
344 $paymentStatusID = NULL,
345 $pledgeStatusID = NULL,
347 $adjustTotalAmount = FALSE,
348 $isScriptUpdate = FALSE
350 $totalAmountClause = '';
351 $paymentContributionId = NULL;
352 $editScheduled = FALSE;
355 $allStatus = CRM_Contribute_PseudoConstant
::contributionStatus(NULL, 'name');
357 // if we get do not get contribution id means we are editing the scheduled payment.
358 if (!empty($paymentIDs)) {
359 $editScheduled = FALSE;
360 $payments = implode(',', $paymentIDs);
361 $paymentContributionId = CRM_Core_DAO
::getFieldValue('CRM_Pledge_DAO_PledgePayment',
367 if (!$paymentContributionId) {
368 $editScheduled = TRUE;
372 // if payment ids are passed, we update payment table first, since payments statuses are not dependent on pledge status
373 if ((!empty($paymentIDs) ||
$pledgeStatusID == array_search('Cancelled', $allStatus)) && (!$editScheduled ||
$isScriptUpdate)) {
374 if ($pledgeStatusID == array_search('Cancelled', $allStatus)) {
375 $paymentStatusID = $pledgeStatusID;
378 self
::updatePledgePayments($pledgeID, $paymentStatusID, $paymentIDs, $actualAmount, $paymentContributionId, $isScriptUpdate);
380 if (!empty($paymentIDs) && $actualAmount) {
381 $payments = implode(',', $paymentIDs);
382 $pledgeScheduledAmount = CRM_Core_DAO
::getFieldValue('CRM_Pledge_DAO_PledgePayment',
388 $pledgeStatusId = self
::calculatePledgeStatus($pledgeID);
389 // Actual Pledge Amount
390 $actualPledgeAmount = CRM_Core_DAO
::getFieldValue('CRM_Pledge_DAO_Pledge',
395 // while editing scheduled we need to check if we are editing last pending
396 $lastPending = FALSE;
397 if (!$paymentContributionId) {
398 $checkPendingCount = self
::getOldestPledgePayment($pledgeID, 2);
399 if ($checkPendingCount['count'] == 1) {
404 // check if this is the last payment and adjust the actual amount.
405 if ($pledgeStatusId && $pledgeStatusId == array_search('Completed', $allStatus) ||
$lastPending) {
406 // last scheduled payment
407 if ($actualAmount >= $pledgeScheduledAmount) {
408 $adjustTotalAmount = TRUE;
410 elseif (!$adjustTotalAmount) {
411 // actual amount is less than the scheduled amount, so enter new pledge payment record
412 $pledgeFrequencyUnit = CRM_Core_DAO
::getFieldValue('CRM_Pledge_DAO_Pledge', $pledgeID, 'frequency_unit', 'id');
413 $pledgeFrequencyInterval = CRM_Core_DAO
::getFieldValue('CRM_Pledge_DAO_Pledge', $pledgeID, 'frequency_interval', 'id');
414 $pledgeScheduledDate = CRM_Core_DAO
::getFieldValue('CRM_Pledge_DAO_PledgePayment', $payments, 'scheduled_date', 'id');
415 $scheduled_date = CRM_Utils_Date
::processDate($pledgeScheduledDate);
416 $date['year'] = (int) substr($scheduled_date, 0, 4);
417 $date['month'] = (int) substr($scheduled_date, 4, 2);
418 $date['day'] = (int) substr($scheduled_date, 6, 2);
419 $newDate = date('YmdHis', mktime(0, 0, 0, $date['month'], $date['day'], $date['year']));
420 $ScheduledDate = CRM_Utils_Date
::format(CRM_Utils_Date
::intervalAdd($pledgeFrequencyUnit,
421 $pledgeFrequencyInterval, $newDate
423 $pledgeParams = array(
424 'status_id' => array_search('Pending', $allStatus),
425 'pledge_id' => $pledgeID,
426 'scheduled_amount' => ($pledgeScheduledAmount - $actualAmount),
427 'scheduled_date' => $ScheduledDate,
429 $payment = self
::add($pledgeParams);
430 // while editing schedule, after adding a new pledge payemnt update the scheduled amount of the current payment
431 if (!$paymentContributionId) {
432 CRM_Core_DAO
::setFieldValue('CRM_Pledge_DAO_PledgePayment', $payments, 'scheduled_amount', $actualAmount);
436 elseif (!$adjustTotalAmount) {
437 // not last schedule amount and also not selected to adjust Total
438 $paymentContributionId = CRM_Core_DAO
::getFieldValue('CRM_Pledge_DAO_PledgePayment',
443 self
::adjustPledgePayment($pledgeID, $actualAmount, $pledgeScheduledAmount, $paymentContributionId, $payments, $paymentStatusID);
444 // while editing schedule, after adding a new pledge payemnt update the scheduled amount of the current payment
445 if (!$paymentContributionId) {
446 CRM_Core_DAO
::setFieldValue('CRM_Pledge_DAO_PledgePayment', $payments, 'scheduled_amount', $actualAmount);
448 // after adjusting all payments check if the actual amount was greater than the actual remaining amount , if so then update the total pledge amount.
449 $pledgeStatusId = self
::calculatePledgeStatus($pledgeID);
451 SELECT sum( civicrm_pledge_payment.actual_amount )
452 FROM civicrm_pledge_payment
453 WHERE civicrm_pledge_payment.pledge_id = %1
454 AND civicrm_pledge_payment.status_id = 1
456 $totalPaidParams = array(1 => array($pledgeID, 'Integer'));
457 $totalPaidAmount = CRM_Core_DAO
::singleValueQuery($balanceQuery, $totalPaidParams);
458 $remainingTotalAmount = ($actualPledgeAmount - $totalPaidAmount);
459 if (($pledgeStatusId && $pledgeStatusId == array_search('Completed', $allStatus)) && (($actualAmount > $remainingTotalAmount) ||
($actualAmount >= $actualPledgeAmount))) {
460 $totalAmountClause = ", civicrm_pledge.amount = {$totalPaidAmount}";
463 if ($adjustTotalAmount) {
464 $newTotalAmount = ($actualPledgeAmount +
($actualAmount - $pledgeScheduledAmount));
465 $totalAmountClause = ", civicrm_pledge.amount = {$newTotalAmount}";
466 if (!$paymentContributionId) {
467 CRM_Core_DAO
::setFieldValue('CRM_Pledge_DAO_PledgePayment', $payments, 'scheduled_amount', $actualAmount);
472 $cancelDateClause = $endDateClause = NULL;
473 //update pledge and payment status if status is Completed/Cancelled.
474 if ($pledgeStatusID && $pledgeStatusID == array_search('Cancelled', $allStatus)) {
475 $paymentStatusID = $pledgeStatusID;
476 $cancelDateClause = ", civicrm_pledge.cancel_date = CURRENT_TIMESTAMP ";
480 $pledgeStatusID = self
::calculatePledgeStatus($pledgeID);
483 if ($pledgeStatusID == array_search('Completed', $allStatus)) {
484 $endDateClause = ", civicrm_pledge.end_date = CURRENT_TIMESTAMP ";
487 //update pledge status
489 UPDATE civicrm_pledge
490 SET civicrm_pledge.status_id = %1
491 {$cancelDateClause} {$endDateClause} {$totalAmountClause}
492 WHERE civicrm_pledge.id = %2
496 1 => array($pledgeStatusID, 'Integer'),
497 2 => array($pledgeID, 'Integer'),
500 $dao = CRM_Core_DAO
::executeQuery($query, $params);
502 return $pledgeStatusID;
506 * Calculate the base scheduled date. This function effectively 'rounds' the $params['scheduled_date'] value
507 * to the first payment date with respect to the frequency day - ie. if payments are on the 15th of the month the date returned
508 * will be the 15th of the relevant month. Then to calculate the payments you can use intervalAdd ie.
509 * CRM_Utils_Date::intervalAdd( $params['frequency_unit'], $i * ($params['frequency_interval']) , calculateBaseScheduledDate( &$params )))
512 * @param array $params
514 * @return array $newdate Next scheduled date as an array
517 static function calculateBaseScheduleDate(&$params) {
519 $scheduled_date = CRM_Utils_Date
::processDate($params['scheduled_date']);
520 $date['year'] = (int) substr($scheduled_date, 0, 4);
521 $date['month'] = (int) substr($scheduled_date, 4, 2);
522 $date['day'] = (int) substr($scheduled_date, 6, 2);
523 //calculation of schedule date according to frequency day of period
524 //frequency day is not applicable for daily installments
525 if ($params['frequency_unit'] != 'day' && $params['frequency_unit'] != 'year') {
526 if ($params['frequency_unit'] != 'week') {
528 //for month use day of next month as next payment date
529 $date['day'] = $params['frequency_day'];
531 elseif ($params['frequency_unit'] == 'week') {
533 //for week calculate day of week ie. Sunday,Monday etc. as next payment date
534 $dayOfWeek = date('w', mktime(0, 0, 0, $date['month'], $date['day'], $date['year']));
535 $frequencyDay = $params['frequency_day'] - $dayOfWeek;
537 $scheduleDate = explode("-", date('n-j-Y', mktime(0, 0, 0, $date['month'],
538 $date['day'] +
$frequencyDay, $date['year']
540 $date['month'] = $scheduleDate[0];
541 $date['day'] = $scheduleDate[1];
542 $date['year'] = $scheduleDate[2];
545 $newdate = date('YmdHis', mktime(0, 0, 0, $date['month'], $date['day'], $date['year']));
550 * Calculate next scheduled pledge payment date. Function calculates next pledge payment date.
552 * @param array params - must include frequency unit & frequency interval
553 * @param int paymentNo number of payment in sequence (e.g. 1 for first calculated payment (treat initial payment as 0)
554 * @param datestring basePaymentDate - date to calculate payments from. This would normally be the
555 * first day of the pledge (default) & is calculated off the 'scheduled date' param. Returned date will
556 * be equal to basePaymentDate normalised to fit the 'pledge pattern' + number of installments
558 * @return formatted date
561 static function calculateNextScheduledDate(&$params, $paymentNo, $basePaymentDate = NULL) {
562 if (!$basePaymentDate) {
563 $basePaymentDate = self
::calculateBaseScheduleDate($params);
565 return CRM_Utils_Date
::format(
566 CRM_Utils_Date
::intervalAdd(
567 $params['frequency_unit'],
568 $paymentNo * ($params['frequency_interval']),
575 * Calculate the pledge status
577 * @param int $pledgeId pledge id
579 * @return int $statusId calculated status id of pledge
582 static function calculatePledgeStatus($pledgeId) {
583 $paymentStatusTypes = CRM_Contribute_PseudoConstant
::contributionStatus(NULL, 'name');
585 //retrieve all pledge payments for this particular pledge
586 $allPledgePayments = $allStatus = array();
587 $returnProperties = array('status_id');
588 CRM_Core_DAO
::commonRetrieveAll('CRM_Pledge_DAO_PledgePayment', 'pledge_id', $pledgeId, $allPledgePayments, $returnProperties);
590 // build pledge payment statuses
591 foreach ($allPledgePayments as $key => $value) {
592 $allStatus[$value['id']] = $paymentStatusTypes[$value['status_id']];
595 if (array_search('Overdue', $allStatus)) {
596 $statusId = array_search('Overdue', $paymentStatusTypes);
598 elseif (array_search('Completed', $allStatus)) {
599 if (count(array_count_values($allStatus)) == 1) {
600 $statusId = array_search('Completed', $paymentStatusTypes);
603 $statusId = array_search('In Progress', $paymentStatusTypes);
607 $statusId = array_search('Pending', $paymentStatusTypes);
614 * Function to update pledge payment table
616 * @param int $pledgeId pledge id
617 * @param int $paymentStatusId payment status id to set
618 * @param array $paymentIds payment ids to be updated
619 * @param float|int $actualAmount , actual amount being paid
620 * @param int $contributionId , Id of associated contribution when payment is recorded
621 * @param bool $isScriptUpdate , is function being called from bin script?
625 static function updatePledgePayments($pledgeId,
629 $contributionId = NULL,
630 $isScriptUpdate = FALSE
632 $allStatus = CRM_Contribute_PseudoConstant
::contributionStatus(NULL, 'name');
633 $paymentClause = NULL;
634 if (!empty($paymentIds)) {
635 $payments = implode(',', $paymentIds);
636 $paymentClause = " AND civicrm_pledge_payment.id IN ( {$payments} )";
638 $actualAmountClause = NULL;
639 $contributionIdClause = NULL;
640 if (isset($contributionId) && !$isScriptUpdate) {
641 $contributionIdClause = ", civicrm_pledge_payment.contribution_id = {$contributionId}";
642 $actualAmountClause = ", civicrm_pledge_payment.actual_amount = {$actualAmount}";
646 UPDATE civicrm_pledge_payment
647 SET civicrm_pledge_payment.status_id = {$paymentStatusId}
648 {$actualAmountClause} {$contributionIdClause}
649 WHERE civicrm_pledge_payment.pledge_id = %1
654 $params = array(1 => array($pledgeId, 'Integer'));
656 $dao = CRM_Core_DAO
::executeQuery($query, $params);
660 * Function to update pledge payment table when reminder is sent
662 * @param int $paymentId payment id
666 static function updateReminderDetails($paymentId) {
668 UPDATE civicrm_pledge_payment
669 SET civicrm_pledge_payment.reminder_date = CURRENT_TIMESTAMP,
670 civicrm_pledge_payment.reminder_count = civicrm_pledge_payment.reminder_count + 1
671 WHERE civicrm_pledge_payment.id = {$paymentId}
673 $dao = CRM_Core_DAO
::executeQuery($query);
677 * Function to get oldest pending or in progress pledge payments
679 * @param int $pledgeID pledge id
683 * @return array associated array of pledge details
686 static function getOldestPledgePayment($pledgeID, $limit = 1) {
687 //get pending / overdue statuses
688 $pledgeStatuses = CRM_Contribute_PseudoConstant
::contributionStatus(NULL, 'name');
690 //get pending and overdue payments
691 $status[] = array_search('Pending', $pledgeStatuses);
692 $status[] = array_search('Overdue', $pledgeStatuses);
694 $statusClause = " IN (" . implode(',', $status) . ")";
697 SELECT civicrm_pledge_payment.id id, civicrm_pledge_payment.scheduled_amount amount, civicrm_pledge_payment.currency
698 FROM civicrm_pledge, civicrm_pledge_payment
699 WHERE civicrm_pledge.id = civicrm_pledge_payment.pledge_id
700 AND civicrm_pledge_payment.status_id {$statusClause}
701 AND civicrm_pledge.id = %1
702 ORDER BY civicrm_pledge_payment.scheduled_date ASC
706 $params[1] = array($pledgeID, 'Integer');
707 $params[2] = array($limit, 'Integer');
708 $payment = CRM_Core_DAO
::executeQuery($query, $params);
710 $paymentDetails = array();
711 while ($payment->fetch()) {
712 $paymentDetails[] = array(
713 'id' => $payment->id
,
714 'amount' => $payment->amount
,
715 'currency' => $payment->currency
,
720 return end($paymentDetails);
725 * @param $actualAmount
726 * @param $pledgeScheduledAmount
727 * @param null $paymentContributionId
728 * @param null $pPaymentId
729 * @param null $paymentStatusID
731 static function adjustPledgePayment($pledgeID, $actualAmount, $pledgeScheduledAmount, $paymentContributionId = NULL, $pPaymentId = NULL, $paymentStatusID = NULL) {
732 $allStatus = CRM_Contribute_PseudoConstant
::contributionStatus(NULL, 'name');
733 if ($paymentStatusID == array_search('Cancelled', $allStatus) ||
$paymentStatusID == array_search('Refunded', $allStatus)) {
735 SELECT civicrm_pledge_payment.id id
736 FROM civicrm_pledge_payment
737 WHERE civicrm_pledge_payment.contribution_id = {$paymentContributionId}
739 $paymentsAffected = CRM_Core_DAO
::executeQuery($query);
740 $paymentIDs = array();
741 while ($paymentsAffected->fetch()) {
742 $paymentIDs[] = $paymentsAffected->id
;
744 // Reset the affected values by the amount paid more than the scheduled amount
745 foreach ($paymentIDs as $key => $value) {
746 $payment = new CRM_Pledge_DAO_PledgePayment();
747 $payment->id
= $value;
748 if ($payment->find(TRUE)) {
749 $payment->contribution_id
= 'null';
750 $payment->status_id
= array_search('Pending', $allStatus);
751 $payment->scheduled_date
= NULL;
752 $payment->reminder_date
= NULL;
753 $payment->scheduled_amount
= $pledgeScheduledAmount;
754 $payment->actual_amount
= 'null';
759 //Cancel the initial paid amount
760 CRM_Core_DAO
::setFieldValue('CRM_Pledge_DAO_PledgePayment', reset($paymentIDs), 'status_id', $paymentStatusID, 'id');
761 CRM_Core_DAO
::setFieldValue('CRM_Pledge_DAO_PledgePayment', reset($paymentIDs), 'actual_amount', $actualAmount, 'id');
763 //Add new payment after the last payment for the pledge
764 $allPayments = self
::getPledgePayments($pledgeID);
765 $lastPayment = array_pop($allPayments);
767 $pledgeFrequencyUnit = CRM_Core_DAO
::getFieldValue('CRM_Pledge_DAO_Pledge', $pledgeID, 'frequency_unit', 'id');
768 $pledgeFrequencyInterval = CRM_Core_DAO
::getFieldValue('CRM_Pledge_DAO_Pledge', $pledgeID, 'frequency_interval', 'id');
769 $pledgeScheduledDate = $lastPayment['scheduled_date'];
770 $scheduled_date = CRM_Utils_Date
::processDate($pledgeScheduledDate);
771 $date['year'] = (int) substr($scheduled_date, 0, 4);
772 $date['month'] = (int) substr($scheduled_date, 4, 2);
773 $date['day'] = (int) substr($scheduled_date, 6, 2);
774 $newDate = date('YmdHis', mktime(0, 0, 0, $date['month'], $date['day'], $date['year']));
775 $ScheduledDate = CRM_Utils_Date
::format(CRM_Utils_Date
::intervalAdd($pledgeFrequencyUnit, $pledgeFrequencyInterval, $newDate));
776 $pledgeParams = array(
777 'status_id' => array_search('Pending', $allStatus),
778 'pledge_id' => $pledgeID,
779 'scheduled_amount' => $pledgeScheduledAmount,
780 'scheduled_date' => $ScheduledDate,
782 $payment = self
::add($pledgeParams);
785 $oldestPayment = self
::getOldestPledgePayment($pledgeID);
786 if (!$paymentContributionId) {
787 // means we are editing payment scheduled payment, so get the second pending to update.
788 $oldestPayment = self
::getOldestPledgePayment($pledgeID, 2);
789 if (($oldestPayment['count'] != 1) && ($oldestPayment['id'] == $pPaymentId)) {
790 $oldestPayment = self
::getOldestPledgePayment($pledgeID);
794 if ($oldestPayment) {
795 // not the last scheduled payment and the actual amount is less than the expected , add it to oldest pending.
796 if (($actualAmount != $pledgeScheduledAmount) && (($actualAmount < $pledgeScheduledAmount) ||
(($actualAmount - $pledgeScheduledAmount) < $oldestPayment['amount']))) {
797 $oldScheduledAmount = $oldestPayment['amount'];
798 $newScheduledAmount = $oldScheduledAmount +
($pledgeScheduledAmount - $actualAmount);
799 //store new amount in oldest pending payment record.
800 CRM_Core_DAO
::setFieldValue('CRM_Pledge_DAO_PledgePayment',
801 $oldestPayment['id'],
805 if (CRM_Core_DAO
::getFieldValue('CRM_Pledge_DAO_PledgePayment', $oldestPayment['id'], 'contribution_id', 'id')) {
806 CRM_Core_DAO
::setFieldValue('CRM_Pledge_DAO_PledgePayment',
807 $oldestPayment['id'],
809 $paymentContributionId
813 elseif (($actualAmount > $pledgeScheduledAmount) && (($actualAmount - $pledgeScheduledAmount) >= $oldestPayment['amount'])) {
814 // 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
815 // set the actual amount of the next pending to '0', set contribution Id to current contribution Id and status as completed
816 $paymentId = array($oldestPayment['id']);
817 self
::updatePledgePayments($pledgeID, array_search('Completed', $allStatus), $paymentId, 0, $paymentContributionId);
818 CRM_Core_DAO
::setFieldValue('CRM_Pledge_DAO_PledgePayment', $oldestPayment['id'], 'scheduled_amount', 0, 'id');
819 $oldestPayment = self
::getOldestPledgePayment($pledgeID);
820 if (!$paymentContributionId) {
821 // means we are editing payment scheduled payment.
822 $oldestPaymentAmount = self
::getOldestPledgePayment($pledgeID, 2);
824 $newActualAmount = ($actualAmount - $pledgeScheduledAmount);
825 $newPledgeScheduledAmount = $oldestPayment['amount'];
826 if (!$paymentContributionId) {
827 $newActualAmount = ($actualAmount - $pledgeScheduledAmount);
828 $newPledgeScheduledAmount = $oldestPaymentAmount['amount'];
829 // means we are editing payment scheduled payment, so update scheduled amount.
830 CRM_Core_DAO
::setFieldValue('CRM_Pledge_DAO_PledgePayment',
831 $oldestPaymentAmount['id'],
836 if ($newActualAmount > 0) {
837 self
::adjustPledgePayment($pledgeID, $newActualAmount, $newPledgeScheduledAmount, $paymentContributionId);