bulk comment fixes
[civicrm-core.git] / CRM / Pledge / BAO / PledgePayment.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
06b69b18 4 | CiviCRM version 4.5 |
6a488035 5 +--------------------------------------------------------------------+
06b69b18 6 | Copyright CiviCRM LLC (c) 2004-2014 |
6a488035
TO
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 *
30 * @package CRM
06b69b18 31 * @copyright CiviCRM LLC (c) 2004-2014
6a488035
TO
32 * $Id$
33 *
34 */
35class CRM_Pledge_BAO_PledgePayment extends CRM_Pledge_DAO_PledgePayment {
36
37 /**
38 * class constructor
39 */
40 function __construct() {
41 parent::__construct();
42 }
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 $query = "
54SELECT civicrm_pledge_payment.id id,
55 scheduled_amount,
56 scheduled_date,
57 reminder_date,
58 reminder_count,
59 actual_amount,
60 receive_date,
a9ae3b2c 61 civicrm_pledge_payment.currency,
6a488035
TO
62 civicrm_option_value.name as status,
63 civicrm_option_value.label as label,
64 civicrm_contribution.id as contribution_id
65FROM civicrm_pledge_payment
66
67LEFT JOIN civicrm_contribution ON civicrm_pledge_payment.contribution_id = civicrm_contribution.id
68LEFT JOIN civicrm_option_group ON ( civicrm_option_group.name = 'contribution_status' )
69LEFT 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 )
71WHERE pledge_id = %1
72";
73
74 $params[1] = array($pledgeId, 'Integer');
75 $payment = CRM_Core_DAO::executeQuery($query, $params);
76
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;
90 }
91
92 return $paymentDetails;
93 }
94
95 static function create($params) {
96 $transaction = new CRM_Core_Transaction();
97 $contributionStatus = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name');
98
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']);
103
104 if (CRM_Utils_Date::overdue($prevScheduledDate[1], $now)) {
105 $statues[1] = array_search('Overdue', $contributionStatus);
106 }
107 else {
108 $statues[1] = array_search('Pending', $contributionStatus);
109 }
110
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);
115 }
116 else {
117 $statues[$i + 1] = array_search('Pending', $contributionStatus);
118 }
119 }
120
121 if ($params['installment_amount']) {
122 $params['scheduled_amount'] = $params['installment_amount'];
123 }
124 else {
125 $params['scheduled_amount'] = round(($params['amount'] / $params['installments']), 2);
126 }
127
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'];
132 }
133 if (!isset($params['contribution_id']) && $params['installments'] > 1) {
134 $params['status_id'] = $statues[$i];
135 }
136
137 $params['scheduled_date'] = $prevScheduledDate[$i];
138 $payment = self::add($params);
139 if (is_a($payment, 'CRM_Core_Error')) {
140 $transaction->rollback();
141 return $payment;
142 }
143
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']);
148 }
149 }
150
151 //update pledge status
152 self::updatePledgePaymentStatus($params['pledge_id']);
153
154 $transaction->commit();
155 return $payment;
156 }
157
158 /**
159 * Add pledge payment
160 *
161 * @param array $params associate array of field
162 *
163 * @return pledge payment id
164 * @static
165 */
166 static function add($params) {
a7488080 167 if (!empty($params['id'])) {
6a488035
TO
168 CRM_Utils_Hook::pre('edit', 'PledgePayment', $params['id'], $params);
169 }
170 else {
171 CRM_Utils_Hook::pre('create', 'PledgePayment', NULL, $params);
172 }
173
174 $payment = new CRM_Pledge_DAO_PledgePayment();
175 $payment->copyValues($params);
176
177 // set currency for CRM-1496
178 if (!isset($payment->currency)) {
179 $config = CRM_Core_Config::singleton();
180 $payment->currency = $config->defaultCurrency;
181 }
182
183 $result = $payment->save();
184
a7488080 185 if (!empty($params['id'])) {
6a488035
TO
186 CRM_Utils_Hook::post('edit', 'PledgePayment', $payment->id, $payment);
187 }
188 else {
189 CRM_Utils_Hook::post('create', 'PledgePayment', $payment->id, $payment);
190 }
191
192
193 return $result;
194 }
195
196 /**
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
202 *
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
205 *
206 * @return object CRM_Pledge_BAO_PledgePayment object
207 * @access public
208 * @static
209 */
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);
215 return $payment;
216 }
217 return NULL;
218 }
219
220 /**
221 * Delete pledge payment
222 *
77b97be7
EM
223 * @param $id
224 *
225 * @internal param array $params associate array of field
6a488035
TO
226 *
227 * @return pledge payment id
228 * @static
229 */
230 static function del($id) {
231 $payment = new CRM_Pledge_DAO_PledgePayment();
232 $payment->id = $id;
233 if ($payment->find()) {
234 $payment->fetch();
235
236 CRM_Utils_Hook::pre('delete', 'PledgePayment', $id, $payment);
237
238 $result = $payment->delete();
239
240 CRM_Utils_Hook::post('delete', 'PledgePayment', $id, $payment);
241
242 return $result;
243 }
244 else {
245 return FALSE;
246 }
247 }
248
249 /**
250 * Function to delete all pledge payments
251 *
77b97be7 252 * @param int $id pledge id
6a488035 253 *
77b97be7 254 * @return bool
6a488035
TO
255 * @access public
256 * @static
257 *
258 */
259 static function deletePayments($id) {
260 if (!CRM_Utils_Rule::positiveInteger($id)) {
261 return FALSE;
262 }
263
264 $transaction = new CRM_Core_Transaction();
265
266 $payment = new CRM_Pledge_DAO_PledgePayment();
267 $payment->pledge_id = $id;
268
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);
274 }
275 $payment->delete();
276 }
277 }
278
279 $transaction->commit();
280
281 return TRUE;
282 }
283
284 /**
285 * On delete contribution record update associated pledge payment and pledge.
286 *
77b97be7 287 * @param int $contributionID contribution id
6a488035 288 *
77b97be7 289 * @return bool
6a488035
TO
290 * @access public
291 * @static
292 */
293 static function resetPledgePayment($contributionID) {
294 //get all status
295 $allStatus = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name');
296
297 $transaction = new CRM_Core_Transaction();
298
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';
308 $payment->save();
309
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);
314
315 $payment->free();
316 }
317
318 $transaction->commit();
319 return TRUE;
320 }
321
322 /**
323 * update Pledge Payment Status
324 *
64041b14 325 * @param int $pledgeID, id of pledge
6a488035 326 * @param array $paymentIDs, ids of pledge payment(s) to update
64041b14 327 * @param int $paymentStatusID, payment status to set
328 * @param int $pledgeStatus, pledge status to change (if needed)
6a488035 329 * @param float $actualAmount, actual amount being paid
64041b14 330 * @param bool $adjustTotalAmount, is amount being paid different from scheduled amount?
331 * @param bool $isScriptUpdate, is function being called from bin script?
6a488035
TO
332 *
333 * @return int $newStatus, updated status id (or 0)
334 */
335 static function updatePledgePaymentStatus(
336 $pledgeID,
64041b14 337 $paymentIDs = NULL,
338 $paymentStatusID = NULL,
339 $pledgeStatusID = NULL,
340 $actualAmount = 0,
6a488035 341 $adjustTotalAmount = FALSE,
64041b14 342 $isScriptUpdate = FALSE
6a488035
TO
343 ) {
344 $totalAmountClause = '';
345 $paymentContributionId = NULL;
346 $editScheduled = FALSE;
347
348 //get all statuses
349 $allStatus = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name');
350
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',
356 $payments,
357 'contribution_id',
358 'id'
359 );
360
361 if (!$paymentContributionId) {
362 $editScheduled = TRUE;
363 }
364 }
365
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;
370 }
371
372 self::updatePledgePayments($pledgeID, $paymentStatusID, $paymentIDs, $actualAmount, $paymentContributionId, $isScriptUpdate);
373 }
374 if (!empty($paymentIDs) && $actualAmount) {
375 $payments = implode(',', $paymentIDs);
376 $pledgeScheduledAmount = CRM_Core_DAO::getFieldValue('CRM_Pledge_DAO_PledgePayment',
377 $payments,
378 'scheduled_amount',
379 'id'
380 );
381
382 $pledgeStatusId = self::calculatePledgeStatus($pledgeID);
383 // Actual Pledge Amount
384 $actualPledgeAmount = CRM_Core_DAO::getFieldValue('CRM_Pledge_DAO_Pledge',
385 $pledgeID,
386 'amount',
387 'id'
388 );
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) {
394 $lastPending = TRUE;
395 }
396 }
397
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) {
64041b14 402 $adjustTotalAmount = TRUE;
403 }
6a488035
TO
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,
64041b14 415 $pledgeFrequencyInterval, $newDate
416 ));
6a488035
TO
417 $pledgeParams = array(
418 'status_id' => array_search('Pending', $allStatus),
419 'pledge_id' => $pledgeID,
420 'scheduled_amount' => ($pledgeScheduledAmount - $actualAmount),
421 'scheduled_date' => $ScheduledDate,
422 );
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);
427 }
428 }
64041b14 429 }
6a488035
TO
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',
433 $payments,
434 'contribution_id',
435 'id'
436 );
a9ae3b2c 437 self::adjustPledgePayment($pledgeID, $actualAmount, $pledgeScheduledAmount, $paymentContributionId, $payments, $paymentStatusID);
6a488035
TO
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);
441 }
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);
444 $balanceQuery = "
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
449 ";
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}";
455 }
456 }
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);
462 }
463 }
464 }
465
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 ";
471 }
472 else {
473 // get pledge status
474 $pledgeStatusID = self::calculatePledgeStatus($pledgeID);
475 }
476
477 if ($pledgeStatusID == array_search('Completed', $allStatus)) {
478 $endDateClause = ", civicrm_pledge.end_date = CURRENT_TIMESTAMP ";
479 }
480
481 //update pledge status
482 $query = "
483UPDATE civicrm_pledge
484 SET civicrm_pledge.status_id = %1
485 {$cancelDateClause} {$endDateClause} {$totalAmountClause}
486WHERE civicrm_pledge.id = %2
487";
488
64041b14 489 $params = array(
490 1 => array($pledgeStatusID, 'Integer'),
6a488035
TO
491 2 => array($pledgeID, 'Integer'),
492 );
493
494 $dao = CRM_Core_DAO::executeQuery($query, $params);
495
496 return $pledgeStatusID;
497 }
498
499 /**
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 )))
504 *
505 *
506 * @param array $params
507 *
508 * @return array $newdate Next scheduled date as an array
509 * @static
510 */
511 static function calculateBaseScheduleDate(&$params) {
64041b14 512 $date = array();
6a488035 513 $scheduled_date = CRM_Utils_Date::processDate($params['scheduled_date']);
64041b14 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);
6a488035
TO
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') {
521
522 //for month use day of next month as next payment date
523 $date['day'] = $params['frequency_day'];
524 }
525 elseif ($params['frequency_unit'] == 'week') {
526
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;
530
531 $scheduleDate = explode("-", date('n-j-Y', mktime(0, 0, 0, $date['month'],
64041b14 532 $date['day'] + $frequencyDay, $date['year']
533 )));
6a488035 534 $date['month'] = $scheduleDate[0];
64041b14 535 $date['day'] = $scheduleDate[1];
536 $date['year'] = $scheduleDate[2];
6a488035
TO
537 }
538 }
539 $newdate = date('YmdHis', mktime(0, 0, 0, $date['month'], $date['day'], $date['year']));
540 return $newdate;
541 }
542
543 /**
544 * Calculate next scheduled pledge payment date. Function calculates next pledge payment date.
545 *
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
551 *
552 * @return formatted date
553 *
554 */
555 static function calculateNextScheduledDate(&$params, $paymentNo, $basePaymentDate = NULL) {
556 if (!$basePaymentDate) {
557 $basePaymentDate = self::calculateBaseScheduleDate($params);
558 }
559 return CRM_Utils_Date::format(
560 CRM_Utils_Date::intervalAdd(
561 $params['frequency_unit'],
562 $paymentNo * ($params['frequency_interval']),
563 $basePaymentDate
564 )
565 );
566 }
567
568 /**
569 * Calculate the pledge status
570 *
571 * @param int $pledgeId pledge id
572 *
573 * @return int $statusId calculated status id of pledge
574 * @static
575 */
576 static function calculatePledgeStatus($pledgeId) {
577 $paymentStatusTypes = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name');
578
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);
583
584 // build pledge payment statuses
585 foreach ($allPledgePayments as $key => $value) {
586 $allStatus[$value['id']] = $paymentStatusTypes[$value['status_id']];
587 }
588
589 if (array_search('Overdue', $allStatus)) {
590 $statusId = array_search('Overdue', $paymentStatusTypes);
591 }
592 elseif (array_search('Completed', $allStatus)) {
593 if (count(array_count_values($allStatus)) == 1) {
594 $statusId = array_search('Completed', $paymentStatusTypes);
595 }
596 else {
597 $statusId = array_search('In Progress', $paymentStatusTypes);
598 }
599 }
600 else {
601 $statusId = array_search('Pending', $paymentStatusTypes);
602 }
603
604 return $statusId;
605 }
606
607 /**
608 * Function to update pledge payment table
609 *
64041b14 610 * @param int $pledgeId pledge id
64041b14 611 * @param int $paymentStatusId payment status id to set
77b97be7
EM
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?
616 *
6a488035
TO
617 * @static
618 */
619 static function updatePledgePayments($pledgeId,
64041b14 620 $paymentStatusId,
621 $paymentIds = NULL,
622 $actualAmount = 0,
623 $contributionId = NULL,
624 $isScriptUpdate = FALSE
6a488035
TO
625 ) {
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} )";
631 }
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}";
637 }
638
639 $query = "
640UPDATE civicrm_pledge_payment
641SET civicrm_pledge_payment.status_id = {$paymentStatusId}
642 {$actualAmountClause} {$contributionIdClause}
643WHERE civicrm_pledge_payment.pledge_id = %1
644 {$paymentClause}
645";
646
647 //get all status
648 $params = array(1 => array($pledgeId, 'Integer'));
649
650 $dao = CRM_Core_DAO::executeQuery($query, $params);
651 }
652
653 /**
654 * Function to update pledge payment table when reminder is sent
655 *
656 * @param int $paymentId payment id
657 *
658 * @static
659 */
660 static function updateReminderDetails($paymentId) {
661 $query = "
662UPDATE civicrm_pledge_payment
663SET civicrm_pledge_payment.reminder_date = CURRENT_TIMESTAMP,
664 civicrm_pledge_payment.reminder_count = civicrm_pledge_payment.reminder_count + 1
665WHERE civicrm_pledge_payment.id = {$paymentId}
666";
667 $dao = CRM_Core_DAO::executeQuery($query);
668 }
669
670 /**
671 * Function to get oldest pending or in progress pledge payments
672 *
673 * @param int $pledgeID pledge id
674 *
675 * @return array associated array of pledge details
676 * @static
677 */
678 static function getOldestPledgePayment($pledgeID, $limit = 1) {
679 //get pending / overdue statuses
680 $pledgeStatuses = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name');
681
682 //get pending and overdue payments
683 $status[] = array_search('Pending', $pledgeStatuses);
684 $status[] = array_search('Overdue', $pledgeStatuses);
685
686 $statusClause = " IN (" . implode(',', $status) . ")";
687
688 $query = "
689SELECT civicrm_pledge_payment.id id, civicrm_pledge_payment.scheduled_amount amount, civicrm_pledge_payment.currency
690FROM civicrm_pledge, civicrm_pledge_payment
691WHERE civicrm_pledge.id = civicrm_pledge_payment.pledge_id
692 AND civicrm_pledge_payment.status_id {$statusClause}
693 AND civicrm_pledge.id = %1
694ORDER BY civicrm_pledge_payment.scheduled_date ASC
695LIMIT 0, %2
696";
697
64041b14 698 $params[1] = array($pledgeID, 'Integer');
699 $params[2] = array($limit, 'Integer');
700 $payment = CRM_Core_DAO::executeQuery($query, $params);
701 $count = 1;
6a488035
TO
702 $paymentDetails = array();
703 while ($payment->fetch()) {
704 $paymentDetails[] = array(
705 'id' => $payment->id,
706 'amount' => $payment->amount,
707 'currency' => $payment->currency,
708 'count' => $count,
709 );
710 $count++;
711 }
712 return end($paymentDetails);
713 }
714
a9ae3b2c 715 static function adjustPledgePayment($pledgeID, $actualAmount, $pledgeScheduledAmount, $paymentContributionId = NULL, $pPaymentId = NULL, $paymentStatusID = NULL) {
6a488035 716 $allStatus = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name');
a9ae3b2c
DG
717 if ($paymentStatusID == array_search('Cancelled', $allStatus) || $paymentStatusID == array_search('Refunded', $allStatus)) {
718 $query = "
719SELECT civicrm_pledge_payment.id id
720FROM civicrm_pledge_payment
721WHERE civicrm_pledge_payment.contribution_id = {$paymentContributionId}
722";
723 $paymentsAffected = CRM_Core_DAO::executeQuery($query);
724 $paymentIDs = array();
725 while ($paymentsAffected->fetch()) {
726 $paymentIDs[] = $paymentsAffected->id;
727 }
2efcf0c2 728 // Reset the affected values by the amount paid more than the scheduled amount
64041b14 729 foreach ($paymentIDs as $key => $value) {
a9ae3b2c
DG
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';
739 $payment->save();
740 }
741 }
2efcf0c2 742
a9ae3b2c
DG
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');
2efcf0c2 746
a9ae3b2c
DG
747 //Add new payment after the last payment for the pledge
748 $allPayments = self::getPledgePayments($pledgeID);
749 $lastPayment = array_pop($allPayments);
750
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');
64041b14 753 $pledgeScheduledDate = $lastPayment['scheduled_date'];
a9ae3b2c
DG
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,
765 );
766 $payment = self::add($pledgeParams);
767 }
768 else {
64041b14 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);
775 }
6a488035 776 }
6a488035 777
64041b14 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.
7ca3c666 784 CRM_Core_DAO::setFieldValue('CRM_Pledge_DAO_PledgePayment',
785 $oldestPayment['id'],
786 'scheduled_amount',
787 $newScheduledAmount
788 );
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'],
792 'contribution_id',
793 $paymentContributionId
794 );
795 }
6a488035 796 }
64041b14 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);
807 }
6a488035 808 $newActualAmount = ($actualAmount - $pledgeScheduledAmount);
64041b14 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'],
816 'scheduled_amount',
817 $newActualAmount
818 );
819 }
820 if ($newActualAmount > 0) {
821 self::adjustPledgePayment($pledgeID, $newActualAmount, $newPledgeScheduledAmount, $paymentContributionId);
822 }
6a488035
TO
823 }
824 }
825 }
826 }
827}
828