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;
95 static function create($params) {
96 $transaction = new CRM_Core_Transaction();
97 $contributionStatus = CRM_Contribute_PseudoConstant
::contributionStatus(NULL, 'name');
99 //calculate the scheduled date for every installment
100 $now = date('Ymd') . '000000';
101 $statues = $prevScheduledDate = array();
102 $prevScheduledDate[1] = CRM_Utils_Date
::processDate($params['scheduled_date']);
104 if (CRM_Utils_Date
::overdue($prevScheduledDate[1], $now)) {
105 $statues[1] = array_search('Overdue', $contributionStatus);
108 $statues[1] = array_search('Pending', $contributionStatus);
111 for ($i = 1; $i < $params['installments']; $i++
) {
112 $prevScheduledDate[$i +
1] = self
::calculateNextScheduledDate($params, $i);
113 if (CRM_Utils_Date
::overdue($prevScheduledDate[$i +
1], $now)) {
114 $statues[$i +
1] = array_search('Overdue', $contributionStatus);
117 $statues[$i +
1] = array_search('Pending', $contributionStatus);
121 if ($params['installment_amount']) {
122 $params['scheduled_amount'] = $params['installment_amount'];
125 $params['scheduled_amount'] = round(($params['amount'] / $params['installments']), 2);
128 for ($i = 1; $i <= $params['installments']; $i++
) {
129 //calculate the scheduled amount for every installment.
130 if ($i == $params['installments']) {
131 $params['scheduled_amount'] = $params['amount'] - ($i - 1) * $params['scheduled_amount'];
133 if (!isset($params['contribution_id']) && $params['installments'] > 1) {
134 $params['status_id'] = $statues[$i];
137 $params['scheduled_date'] = $prevScheduledDate[$i];
138 $payment = self
::add($params);
139 if (is_a($payment, 'CRM_Core_Error')) {
140 $transaction->rollback();
144 // we should add contribution id to only first payment record
145 if (isset($params['contribution_id'])) {
146 unset($params['contribution_id']);
147 unset($params['actual_amount']);
151 //update pledge status
152 self
::updatePledgePaymentStatus($params['pledge_id']);
154 $transaction->commit();
161 * @param array $params associate array of field
163 * @return pledge payment id
166 static function add($params) {
167 if (!empty($params['id'])) {
168 CRM_Utils_Hook
::pre('edit', 'PledgePayment', $params['id'], $params);
171 CRM_Utils_Hook
::pre('create', 'PledgePayment', NULL, $params);
174 $payment = new CRM_Pledge_DAO_PledgePayment();
175 $payment->copyValues($params);
177 // set currency for CRM-1496
178 if (!isset($payment->currency
)) {
179 $config = CRM_Core_Config
::singleton();
180 $payment->currency
= $config->defaultCurrency
;
183 $result = $payment->save();
185 if (!empty($params['id'])) {
186 CRM_Utils_Hook
::post('edit', 'PledgePayment', $payment->id
, $payment);
189 CRM_Utils_Hook
::post('create', 'PledgePayment', $payment->id
, $payment);
197 * Takes a bunch of params that are needed to match certain criteria and
198 * retrieves the relevant objects. Typically the valid params are only
199 * pledge id. We'll tweak this function to be more full featured over a period
200 * of time. This is the inverse function of create. It also stores all the retrieved
201 * values in the default array
203 * @param array $params (reference ) an assoc array of name/value pairs
204 * @param array $defaults (reference ) an assoc array to hold the flattened values
206 * @return object CRM_Pledge_BAO_PledgePayment object
210 static function retrieve(&$params, &$defaults) {
211 $payment = new CRM_Pledge_BAO_PledgePayment
;
212 $payment->copyValues($params);
213 if ($payment->find(TRUE)) {
214 CRM_Core_DAO
::storeValues($payment, $defaults);
221 * Delete pledge payment
225 * @internal param array $params associate array of field
227 * @return pledge payment id
230 static function del($id) {
231 $payment = new CRM_Pledge_DAO_PledgePayment();
233 if ($payment->find()) {
236 CRM_Utils_Hook
::pre('delete', 'PledgePayment', $id, $payment);
238 $result = $payment->delete();
240 CRM_Utils_Hook
::post('delete', 'PledgePayment', $id, $payment);
250 * Function to delete all pledge payments
252 * @param int $id pledge id
259 static function deletePayments($id) {
260 if (!CRM_Utils_Rule
::positiveInteger($id)) {
264 $transaction = new CRM_Core_Transaction();
266 $payment = new CRM_Pledge_DAO_PledgePayment();
267 $payment->pledge_id
= $id;
269 if ($payment->find()) {
270 while ($payment->fetch()) {
271 //also delete associated contribution.
272 if ($payment->contribution_id
) {
273 CRM_Contribute_BAO_Contribution
::deleteContribution($payment->contribution_id
);
279 $transaction->commit();
285 * On delete contribution record update associated pledge payment and pledge.
287 * @param int $contributionID contribution id
293 static function resetPledgePayment($contributionID) {
295 $allStatus = CRM_Contribute_PseudoConstant
::contributionStatus(NULL, 'name');
297 $transaction = new CRM_Core_Transaction();
299 $payment = new CRM_Pledge_DAO_PledgePayment();
300 $payment->contribution_id
= $contributionID;
301 if ($payment->find(TRUE)) {
302 $payment->contribution_id
= 'null';
303 $payment->status_id
= array_search('Pending', $allStatus);
304 $payment->scheduled_date
= NULL;
305 $payment->reminder_date
= NULL;
306 $payment->scheduled_amount
= $payment->actual_amount
;
307 $payment->actual_amount
= 'null';
310 //update pledge status.
311 $pledgeID = $payment->pledge_id
;
312 $pledgeStatusID = self
::calculatePledgeStatus($pledgeID);
313 CRM_Core_DAO
::setFieldValue('CRM_Pledge_DAO_Pledge', $pledgeID, 'status_id', $pledgeStatusID);
318 $transaction->commit();
323 * update Pledge Payment Status
325 * @param int $pledgeID, id of pledge
326 * @param array $paymentIDs, ids of pledge payment(s) to update
327 * @param int $paymentStatusID, payment status to set
328 * @param int $pledgeStatus, pledge status to change (if needed)
329 * @param float $actualAmount, actual amount being paid
330 * @param bool $adjustTotalAmount, is amount being paid different from scheduled amount?
331 * @param bool $isScriptUpdate, is function being called from bin script?
333 * @return int $newStatus, updated status id (or 0)
335 static function updatePledgePaymentStatus(
338 $paymentStatusID = NULL,
339 $pledgeStatusID = NULL,
341 $adjustTotalAmount = FALSE,
342 $isScriptUpdate = FALSE
344 $totalAmountClause = '';
345 $paymentContributionId = NULL;
346 $editScheduled = FALSE;
349 $allStatus = CRM_Contribute_PseudoConstant
::contributionStatus(NULL, 'name');
351 // if we get do not get contribution id means we are editing the scheduled payment.
352 if (!empty($paymentIDs)) {
353 $editScheduled = FALSE;
354 $payments = implode(',', $paymentIDs);
355 $paymentContributionId = CRM_Core_DAO
::getFieldValue('CRM_Pledge_DAO_PledgePayment',
361 if (!$paymentContributionId) {
362 $editScheduled = TRUE;
366 // if payment ids are passed, we update payment table first, since payments statuses are not dependent on pledge status
367 if ((!empty($paymentIDs) ||
$pledgeStatusID == array_search('Cancelled', $allStatus)) && (!$editScheduled ||
$isScriptUpdate)) {
368 if ($pledgeStatusID == array_search('Cancelled', $allStatus)) {
369 $paymentStatusID = $pledgeStatusID;
372 self
::updatePledgePayments($pledgeID, $paymentStatusID, $paymentIDs, $actualAmount, $paymentContributionId, $isScriptUpdate);
374 if (!empty($paymentIDs) && $actualAmount) {
375 $payments = implode(',', $paymentIDs);
376 $pledgeScheduledAmount = CRM_Core_DAO
::getFieldValue('CRM_Pledge_DAO_PledgePayment',
382 $pledgeStatusId = self
::calculatePledgeStatus($pledgeID);
383 // Actual Pledge Amount
384 $actualPledgeAmount = CRM_Core_DAO
::getFieldValue('CRM_Pledge_DAO_Pledge',
389 // while editing scheduled we need to check if we are editing last pending
390 $lastPending = FALSE;
391 if (!$paymentContributionId) {
392 $checkPendingCount = self
::getOldestPledgePayment($pledgeID, 2);
393 if ($checkPendingCount['count'] == 1) {
398 // check if this is the last payment and adjust the actual amount.
399 if ($pledgeStatusId && $pledgeStatusId == array_search('Completed', $allStatus) ||
$lastPending) {
400 // last scheduled payment
401 if ($actualAmount >= $pledgeScheduledAmount) {
402 $adjustTotalAmount = TRUE;
404 elseif (!$adjustTotalAmount) {
405 // actual amount is less than the scheduled amount, so enter new pledge payment record
406 $pledgeFrequencyUnit = CRM_Core_DAO
::getFieldValue('CRM_Pledge_DAO_Pledge', $pledgeID, 'frequency_unit', 'id');
407 $pledgeFrequencyInterval = CRM_Core_DAO
::getFieldValue('CRM_Pledge_DAO_Pledge', $pledgeID, 'frequency_interval', 'id');
408 $pledgeScheduledDate = CRM_Core_DAO
::getFieldValue('CRM_Pledge_DAO_PledgePayment', $payments, 'scheduled_date', 'id');
409 $scheduled_date = CRM_Utils_Date
::processDate($pledgeScheduledDate);
410 $date['year'] = (int) substr($scheduled_date, 0, 4);
411 $date['month'] = (int) substr($scheduled_date, 4, 2);
412 $date['day'] = (int) substr($scheduled_date, 6, 2);
413 $newDate = date('YmdHis', mktime(0, 0, 0, $date['month'], $date['day'], $date['year']));
414 $ScheduledDate = CRM_Utils_Date
::format(CRM_Utils_Date
::intervalAdd($pledgeFrequencyUnit,
415 $pledgeFrequencyInterval, $newDate
417 $pledgeParams = array(
418 'status_id' => array_search('Pending', $allStatus),
419 'pledge_id' => $pledgeID,
420 'scheduled_amount' => ($pledgeScheduledAmount - $actualAmount),
421 'scheduled_date' => $ScheduledDate,
423 $payment = self
::add($pledgeParams);
424 // while editing schedule, after adding a new pledge payemnt update the scheduled amount of the current payment
425 if (!$paymentContributionId) {
426 CRM_Core_DAO
::setFieldValue('CRM_Pledge_DAO_PledgePayment', $payments, 'scheduled_amount', $actualAmount);
430 elseif (!$adjustTotalAmount) {
431 // not last schedule amount and also not selected to adjust Total
432 $paymentContributionId = CRM_Core_DAO
::getFieldValue('CRM_Pledge_DAO_PledgePayment',
437 self
::adjustPledgePayment($pledgeID, $actualAmount, $pledgeScheduledAmount, $paymentContributionId, $payments, $paymentStatusID);
438 // while editing schedule, after adding a new pledge payemnt update the scheduled amount of the current payment
439 if (!$paymentContributionId) {
440 CRM_Core_DAO
::setFieldValue('CRM_Pledge_DAO_PledgePayment', $payments, 'scheduled_amount', $actualAmount);
442 // after adjusting all payments check if the actual amount was greater than the actual remaining amount , if so then update the total pledge amount.
443 $pledgeStatusId = self
::calculatePledgeStatus($pledgeID);
445 SELECT sum( civicrm_pledge_payment.actual_amount )
446 FROM civicrm_pledge_payment
447 WHERE civicrm_pledge_payment.pledge_id = %1
448 AND civicrm_pledge_payment.status_id = 1
450 $totalPaidParams = array(1 => array($pledgeID, 'Integer'));
451 $totalPaidAmount = CRM_Core_DAO
::singleValueQuery($balanceQuery, $totalPaidParams);
452 $remainingTotalAmount = ($actualPledgeAmount - $totalPaidAmount);
453 if (($pledgeStatusId && $pledgeStatusId == array_search('Completed', $allStatus)) && (($actualAmount > $remainingTotalAmount) ||
($actualAmount >= $actualPledgeAmount))) {
454 $totalAmountClause = ", civicrm_pledge.amount = {$totalPaidAmount}";
457 if ($adjustTotalAmount) {
458 $newTotalAmount = ($actualPledgeAmount +
($actualAmount - $pledgeScheduledAmount));
459 $totalAmountClause = ", civicrm_pledge.amount = {$newTotalAmount}";
460 if (!$paymentContributionId) {
461 CRM_Core_DAO
::setFieldValue('CRM_Pledge_DAO_PledgePayment', $payments, 'scheduled_amount', $actualAmount);
466 $cancelDateClause = $endDateClause = NULL;
467 //update pledge and payment status if status is Completed/Cancelled.
468 if ($pledgeStatusID && $pledgeStatusID == array_search('Cancelled', $allStatus)) {
469 $paymentStatusID = $pledgeStatusID;
470 $cancelDateClause = ", civicrm_pledge.cancel_date = CURRENT_TIMESTAMP ";
474 $pledgeStatusID = self
::calculatePledgeStatus($pledgeID);
477 if ($pledgeStatusID == array_search('Completed', $allStatus)) {
478 $endDateClause = ", civicrm_pledge.end_date = CURRENT_TIMESTAMP ";
481 //update pledge status
483 UPDATE civicrm_pledge
484 SET civicrm_pledge.status_id = %1
485 {$cancelDateClause} {$endDateClause} {$totalAmountClause}
486 WHERE civicrm_pledge.id = %2
490 1 => array($pledgeStatusID, 'Integer'),
491 2 => array($pledgeID, 'Integer'),
494 $dao = CRM_Core_DAO
::executeQuery($query, $params);
496 return $pledgeStatusID;
500 * Calculate the base scheduled date. This function effectively 'rounds' the $params['scheduled_date'] value
501 * to the first payment date with respect to the frequency day - ie. if payments are on the 15th of the month the date returned
502 * will be the 15th of the relevant month. Then to calculate the payments you can use intervalAdd ie.
503 * CRM_Utils_Date::intervalAdd( $params['frequency_unit'], $i * ($params['frequency_interval']) , calculateBaseScheduledDate( &$params )))
506 * @param array $params
508 * @return array $newdate Next scheduled date as an array
511 static function calculateBaseScheduleDate(&$params) {
513 $scheduled_date = CRM_Utils_Date
::processDate($params['scheduled_date']);
514 $date['year'] = (int) substr($scheduled_date, 0, 4);
515 $date['month'] = (int) substr($scheduled_date, 4, 2);
516 $date['day'] = (int) substr($scheduled_date, 6, 2);
517 //calculation of schedule date according to frequency day of period
518 //frequency day is not applicable for daily installments
519 if ($params['frequency_unit'] != 'day' && $params['frequency_unit'] != 'year') {
520 if ($params['frequency_unit'] != 'week') {
522 //for month use day of next month as next payment date
523 $date['day'] = $params['frequency_day'];
525 elseif ($params['frequency_unit'] == 'week') {
527 //for week calculate day of week ie. Sunday,Monday etc. as next payment date
528 $dayOfWeek = date('w', mktime(0, 0, 0, $date['month'], $date['day'], $date['year']));
529 $frequencyDay = $params['frequency_day'] - $dayOfWeek;
531 $scheduleDate = explode("-", date('n-j-Y', mktime(0, 0, 0, $date['month'],
532 $date['day'] +
$frequencyDay, $date['year']
534 $date['month'] = $scheduleDate[0];
535 $date['day'] = $scheduleDate[1];
536 $date['year'] = $scheduleDate[2];
539 $newdate = date('YmdHis', mktime(0, 0, 0, $date['month'], $date['day'], $date['year']));
544 * Calculate next scheduled pledge payment date. Function calculates next pledge payment date.
546 * @param array params - must include frequency unit & frequency interval
547 * @param int paymentNo number of payment in sequence (e.g. 1 for first calculated payment (treat initial payment as 0)
548 * @param datestring basePaymentDate - date to calculate payments from. This would normally be the
549 * first day of the pledge (default) & is calculated off the 'scheduled date' param. Returned date will
550 * be equal to basePaymentDate normalised to fit the 'pledge pattern' + number of installments
552 * @return formatted date
555 static function calculateNextScheduledDate(&$params, $paymentNo, $basePaymentDate = NULL) {
556 if (!$basePaymentDate) {
557 $basePaymentDate = self
::calculateBaseScheduleDate($params);
559 return CRM_Utils_Date
::format(
560 CRM_Utils_Date
::intervalAdd(
561 $params['frequency_unit'],
562 $paymentNo * ($params['frequency_interval']),
569 * Calculate the pledge status
571 * @param int $pledgeId pledge id
573 * @return int $statusId calculated status id of pledge
576 static function calculatePledgeStatus($pledgeId) {
577 $paymentStatusTypes = CRM_Contribute_PseudoConstant
::contributionStatus(NULL, 'name');
579 //retrieve all pledge payments for this particular pledge
580 $allPledgePayments = $allStatus = array();
581 $returnProperties = array('status_id');
582 CRM_Core_DAO
::commonRetrieveAll('CRM_Pledge_DAO_PledgePayment', 'pledge_id', $pledgeId, $allPledgePayments, $returnProperties);
584 // build pledge payment statuses
585 foreach ($allPledgePayments as $key => $value) {
586 $allStatus[$value['id']] = $paymentStatusTypes[$value['status_id']];
589 if (array_search('Overdue', $allStatus)) {
590 $statusId = array_search('Overdue', $paymentStatusTypes);
592 elseif (array_search('Completed', $allStatus)) {
593 if (count(array_count_values($allStatus)) == 1) {
594 $statusId = array_search('Completed', $paymentStatusTypes);
597 $statusId = array_search('In Progress', $paymentStatusTypes);
601 $statusId = array_search('Pending', $paymentStatusTypes);
608 * Function to update pledge payment table
610 * @param int $pledgeId pledge id
611 * @param int $paymentStatusId payment status id to set
612 * @param array $paymentIds payment ids to be updated
613 * @param float|int $actualAmount , actual amount being paid
614 * @param int $contributionId , Id of associated contribution when payment is recorded
615 * @param bool $isScriptUpdate , is function being called from bin script?
619 static function updatePledgePayments($pledgeId,
623 $contributionId = NULL,
624 $isScriptUpdate = FALSE
626 $allStatus = CRM_Contribute_PseudoConstant
::contributionStatus(NULL, 'name');
627 $paymentClause = NULL;
628 if (!empty($paymentIds)) {
629 $payments = implode(',', $paymentIds);
630 $paymentClause = " AND civicrm_pledge_payment.id IN ( {$payments} )";
632 $actualAmountClause = NULL;
633 $contributionIdClause = NULL;
634 if (isset($contributionId) && !$isScriptUpdate) {
635 $contributionIdClause = ", civicrm_pledge_payment.contribution_id = {$contributionId}";
636 $actualAmountClause = ", civicrm_pledge_payment.actual_amount = {$actualAmount}";
640 UPDATE civicrm_pledge_payment
641 SET civicrm_pledge_payment.status_id = {$paymentStatusId}
642 {$actualAmountClause} {$contributionIdClause}
643 WHERE civicrm_pledge_payment.pledge_id = %1
648 $params = array(1 => array($pledgeId, 'Integer'));
650 $dao = CRM_Core_DAO
::executeQuery($query, $params);
654 * Function to update pledge payment table when reminder is sent
656 * @param int $paymentId payment id
660 static function updateReminderDetails($paymentId) {
662 UPDATE civicrm_pledge_payment
663 SET civicrm_pledge_payment.reminder_date = CURRENT_TIMESTAMP,
664 civicrm_pledge_payment.reminder_count = civicrm_pledge_payment.reminder_count + 1
665 WHERE civicrm_pledge_payment.id = {$paymentId}
667 $dao = CRM_Core_DAO
::executeQuery($query);
671 * Function to get oldest pending or in progress pledge payments
673 * @param int $pledgeID pledge id
675 * @return array associated array of pledge details
678 static function getOldestPledgePayment($pledgeID, $limit = 1) {
679 //get pending / overdue statuses
680 $pledgeStatuses = CRM_Contribute_PseudoConstant
::contributionStatus(NULL, 'name');
682 //get pending and overdue payments
683 $status[] = array_search('Pending', $pledgeStatuses);
684 $status[] = array_search('Overdue', $pledgeStatuses);
686 $statusClause = " IN (" . implode(',', $status) . ")";
689 SELECT civicrm_pledge_payment.id id, civicrm_pledge_payment.scheduled_amount amount, civicrm_pledge_payment.currency
690 FROM civicrm_pledge, civicrm_pledge_payment
691 WHERE civicrm_pledge.id = civicrm_pledge_payment.pledge_id
692 AND civicrm_pledge_payment.status_id {$statusClause}
693 AND civicrm_pledge.id = %1
694 ORDER BY civicrm_pledge_payment.scheduled_date ASC
698 $params[1] = array($pledgeID, 'Integer');
699 $params[2] = array($limit, 'Integer');
700 $payment = CRM_Core_DAO
::executeQuery($query, $params);
702 $paymentDetails = array();
703 while ($payment->fetch()) {
704 $paymentDetails[] = array(
705 'id' => $payment->id
,
706 'amount' => $payment->amount
,
707 'currency' => $payment->currency
,
712 return end($paymentDetails);
715 static function adjustPledgePayment($pledgeID, $actualAmount, $pledgeScheduledAmount, $paymentContributionId = NULL, $pPaymentId = NULL, $paymentStatusID = NULL) {
716 $allStatus = CRM_Contribute_PseudoConstant
::contributionStatus(NULL, 'name');
717 if ($paymentStatusID == array_search('Cancelled', $allStatus) ||
$paymentStatusID == array_search('Refunded', $allStatus)) {
719 SELECT civicrm_pledge_payment.id id
720 FROM civicrm_pledge_payment
721 WHERE civicrm_pledge_payment.contribution_id = {$paymentContributionId}
723 $paymentsAffected = CRM_Core_DAO
::executeQuery($query);
724 $paymentIDs = array();
725 while ($paymentsAffected->fetch()) {
726 $paymentIDs[] = $paymentsAffected->id
;
728 // Reset the affected values by the amount paid more than the scheduled amount
729 foreach ($paymentIDs as $key => $value) {
730 $payment = new CRM_Pledge_DAO_PledgePayment();
731 $payment->id
= $value;
732 if ($payment->find(TRUE)) {
733 $payment->contribution_id
= 'null';
734 $payment->status_id
= array_search('Pending', $allStatus);
735 $payment->scheduled_date
= NULL;
736 $payment->reminder_date
= NULL;
737 $payment->scheduled_amount
= $pledgeScheduledAmount;
738 $payment->actual_amount
= 'null';
743 //Cancel the initial paid amount
744 CRM_Core_DAO
::setFieldValue('CRM_Pledge_DAO_PledgePayment', reset($paymentIDs), 'status_id', $paymentStatusID, 'id');
745 CRM_Core_DAO
::setFieldValue('CRM_Pledge_DAO_PledgePayment', reset($paymentIDs), 'actual_amount', $actualAmount, 'id');
747 //Add new payment after the last payment for the pledge
748 $allPayments = self
::getPledgePayments($pledgeID);
749 $lastPayment = array_pop($allPayments);
751 $pledgeFrequencyUnit = CRM_Core_DAO
::getFieldValue('CRM_Pledge_DAO_Pledge', $pledgeID, 'frequency_unit', 'id');
752 $pledgeFrequencyInterval = CRM_Core_DAO
::getFieldValue('CRM_Pledge_DAO_Pledge', $pledgeID, 'frequency_interval', 'id');
753 $pledgeScheduledDate = $lastPayment['scheduled_date'];
754 $scheduled_date = CRM_Utils_Date
::processDate($pledgeScheduledDate);
755 $date['year'] = (int) substr($scheduled_date, 0, 4);
756 $date['month'] = (int) substr($scheduled_date, 4, 2);
757 $date['day'] = (int) substr($scheduled_date, 6, 2);
758 $newDate = date('YmdHis', mktime(0, 0, 0, $date['month'], $date['day'], $date['year']));
759 $ScheduledDate = CRM_Utils_Date
::format(CRM_Utils_Date
::intervalAdd($pledgeFrequencyUnit, $pledgeFrequencyInterval, $newDate));
760 $pledgeParams = array(
761 'status_id' => array_search('Pending', $allStatus),
762 'pledge_id' => $pledgeID,
763 'scheduled_amount' => $pledgeScheduledAmount,
764 'scheduled_date' => $ScheduledDate,
766 $payment = self
::add($pledgeParams);
769 $oldestPayment = self
::getOldestPledgePayment($pledgeID);
770 if (!$paymentContributionId) {
771 // means we are editing payment scheduled payment, so get the second pending to update.
772 $oldestPayment = self
::getOldestPledgePayment($pledgeID, 2);
773 if (($oldestPayment['count'] != 1) && ($oldestPayment['id'] == $pPaymentId)) {
774 $oldestPayment = self
::getOldestPledgePayment($pledgeID);
778 if ($oldestPayment) {
779 // not the last scheduled payment and the actual amount is less than the expected , add it to oldest pending.
780 if (($actualAmount != $pledgeScheduledAmount) && (($actualAmount < $pledgeScheduledAmount) ||
(($actualAmount - $pledgeScheduledAmount) < $oldestPayment['amount']))) {
781 $oldScheduledAmount = $oldestPayment['amount'];
782 $newScheduledAmount = $oldScheduledAmount +
($pledgeScheduledAmount - $actualAmount);
783 //store new amount in oldest pending payment record.
784 CRM_Core_DAO
::setFieldValue('CRM_Pledge_DAO_PledgePayment',
785 $oldestPayment['id'],
789 if (CRM_Core_DAO
::getFieldValue('CRM_Pledge_DAO_PledgePayment', $oldestPayment['id'], 'contribution_id', 'id')) {
790 CRM_Core_DAO
::setFieldValue('CRM_Pledge_DAO_PledgePayment',
791 $oldestPayment['id'],
793 $paymentContributionId
797 elseif (($actualAmount > $pledgeScheduledAmount) && (($actualAmount - $pledgeScheduledAmount) >= $oldestPayment['amount'])) {
798 // 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
799 // set the actual amount of the next pending to '0', set contribution Id to current contribution Id and status as completed
800 $paymentId = array($oldestPayment['id']);
801 self
::updatePledgePayments($pledgeID, array_search('Completed', $allStatus), $paymentId, 0, $paymentContributionId);
802 CRM_Core_DAO
::setFieldValue('CRM_Pledge_DAO_PledgePayment', $oldestPayment['id'], 'scheduled_amount', 0, 'id');
803 $oldestPayment = self
::getOldestPledgePayment($pledgeID);
804 if (!$paymentContributionId) {
805 // means we are editing payment scheduled payment.
806 $oldestPaymentAmount = self
::getOldestPledgePayment($pledgeID, 2);
808 $newActualAmount = ($actualAmount - $pledgeScheduledAmount);
809 $newPledgeScheduledAmount = $oldestPayment['amount'];
810 if (!$paymentContributionId) {
811 $newActualAmount = ($actualAmount - $pledgeScheduledAmount);
812 $newPledgeScheduledAmount = $oldestPaymentAmount['amount'];
813 // means we are editing payment scheduled payment, so update scheduled amount.
814 CRM_Core_DAO
::setFieldValue('CRM_Pledge_DAO_PledgePayment',
815 $oldestPaymentAmount['id'],
820 if ($newActualAmount > 0) {
821 self
::adjustPledgePayment($pledgeID, $newActualAmount, $newPledgeScheduledAmount, $paymentContributionId);