Merge pull request #4511 from giant-rabbit/ajax-fatal-log-fix
[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 /**
100fef9d 38 * Class constructor
6a488035
TO
39 */
40 function __construct() {
41 parent::__construct();
42 }
43
44 /**
100fef9d 45 * Get pledge payment details
6a488035
TO
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
ffd93213 95 /**
c490a46a 96 * @param array $params
ffd93213
EM
97 *
98 * @return pledge
99 */
6a488035
TO
100 static function create($params) {
101 $transaction = new CRM_Core_Transaction();
102 $contributionStatus = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name');
103
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']);
108
109 if (CRM_Utils_Date::overdue($prevScheduledDate[1], $now)) {
110 $statues[1] = array_search('Overdue', $contributionStatus);
111 }
112 else {
113 $statues[1] = array_search('Pending', $contributionStatus);
114 }
115
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);
120 }
121 else {
122 $statues[$i + 1] = array_search('Pending', $contributionStatus);
123 }
124 }
125
126 if ($params['installment_amount']) {
127 $params['scheduled_amount'] = $params['installment_amount'];
128 }
129 else {
130 $params['scheduled_amount'] = round(($params['amount'] / $params['installments']), 2);
131 }
132
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'];
137 }
138 if (!isset($params['contribution_id']) && $params['installments'] > 1) {
139 $params['status_id'] = $statues[$i];
140 }
141
142 $params['scheduled_date'] = $prevScheduledDate[$i];
143 $payment = self::add($params);
144 if (is_a($payment, 'CRM_Core_Error')) {
145 $transaction->rollback();
146 return $payment;
147 }
148
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']);
153 }
154 }
155
156 //update pledge status
157 self::updatePledgePaymentStatus($params['pledge_id']);
158
159 $transaction->commit();
160 return $payment;
161 }
162
163 /**
164 * Add pledge payment
165 *
166 * @param array $params associate array of field
167 *
168 * @return pledge payment id
169 * @static
170 */
171 static function add($params) {
a7488080 172 if (!empty($params['id'])) {
6a488035
TO
173 CRM_Utils_Hook::pre('edit', 'PledgePayment', $params['id'], $params);
174 }
175 else {
176 CRM_Utils_Hook::pre('create', 'PledgePayment', NULL, $params);
177 }
178
179 $payment = new CRM_Pledge_DAO_PledgePayment();
180 $payment->copyValues($params);
181
182 // set currency for CRM-1496
183 if (!isset($payment->currency)) {
184 $config = CRM_Core_Config::singleton();
185 $payment->currency = $config->defaultCurrency;
186 }
187
188 $result = $payment->save();
189
a7488080 190 if (!empty($params['id'])) {
6a488035
TO
191 CRM_Utils_Hook::post('edit', 'PledgePayment', $payment->id, $payment);
192 }
193 else {
194 CRM_Utils_Hook::post('create', 'PledgePayment', $payment->id, $payment);
195 }
196
197
198 return $result;
199 }
200
201 /**
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
207 *
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
210 *
c490a46a 211 * @return CRM_Pledge_BAO_PledgePayment object
6a488035
TO
212 * @access public
213 * @static
214 */
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);
220 return $payment;
221 }
222 return NULL;
223 }
224
225 /**
226 * Delete pledge payment
227 *
100fef9d 228 * @param int $id
77b97be7 229 *
c490a46a 230 * @return int pledge payment id
6a488035
TO
231 * @static
232 */
233 static function del($id) {
234 $payment = new CRM_Pledge_DAO_PledgePayment();
235 $payment->id = $id;
236 if ($payment->find()) {
237 $payment->fetch();
238
239 CRM_Utils_Hook::pre('delete', 'PledgePayment', $id, $payment);
240
241 $result = $payment->delete();
242
243 CRM_Utils_Hook::post('delete', 'PledgePayment', $id, $payment);
244
245 return $result;
246 }
247 else {
248 return FALSE;
249 }
250 }
251
252 /**
100fef9d 253 * Delete all pledge payments
6a488035 254 *
77b97be7 255 * @param int $id pledge id
6a488035 256 *
77b97be7 257 * @return bool
6a488035
TO
258 * @access public
259 * @static
260 *
261 */
262 static function deletePayments($id) {
263 if (!CRM_Utils_Rule::positiveInteger($id)) {
264 return FALSE;
265 }
266
267 $transaction = new CRM_Core_Transaction();
268
269 $payment = new CRM_Pledge_DAO_PledgePayment();
270 $payment->pledge_id = $id;
271
272 if ($payment->find()) {
273 while ($payment->fetch()) {
274 //also delete associated contribution.
275 if ($payment->contribution_id) {
276 CRM_Contribute_BAO_Contribution::deleteContribution($payment->contribution_id);
277 }
278 $payment->delete();
279 }
280 }
281
282 $transaction->commit();
283
284 return TRUE;
285 }
286
287 /**
288 * On delete contribution record update associated pledge payment and pledge.
289 *
77b97be7 290 * @param int $contributionID contribution id
6a488035 291 *
77b97be7 292 * @return bool
6a488035
TO
293 * @access public
294 * @static
295 */
296 static function resetPledgePayment($contributionID) {
297 //get all status
298 $allStatus = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name');
299
300 $transaction = new CRM_Core_Transaction();
301
302 $payment = new CRM_Pledge_DAO_PledgePayment();
303 $payment->contribution_id = $contributionID;
304 if ($payment->find(TRUE)) {
305 $payment->contribution_id = 'null';
306 $payment->status_id = array_search('Pending', $allStatus);
307 $payment->scheduled_date = NULL;
308 $payment->reminder_date = NULL;
309 $payment->scheduled_amount = $payment->actual_amount;
310 $payment->actual_amount = 'null';
311 $payment->save();
312
313 //update pledge status.
314 $pledgeID = $payment->pledge_id;
315 $pledgeStatusID = self::calculatePledgeStatus($pledgeID);
316 CRM_Core_DAO::setFieldValue('CRM_Pledge_DAO_Pledge', $pledgeID, 'status_id', $pledgeStatusID);
317
318 $payment->free();
319 }
320
321 $transaction->commit();
322 return TRUE;
323 }
324
325 /**
100fef9d 326 * Update Pledge Payment Status
6a488035 327 *
fd31fa4c
EM
328 * @param int $pledgeID , id of pledge
329 * @param array $paymentIDs , ids of pledge payment(s) to update
330 * @param int $paymentStatusID , payment status to set
c490a46a 331 * @param int $pledgeStatusID pledge status to change (if needed)
fd31fa4c
EM
332 * @param float|int $actualAmount , actual amount being paid
333 * @param bool $adjustTotalAmount , is amount being paid different from scheduled amount?
334 * @param bool $isScriptUpdate , is function being called from bin script?
6a488035
TO
335 *
336 * @return int $newStatus, updated status id (or 0)
337 */
338 static function updatePledgePaymentStatus(
339 $pledgeID,
64041b14 340 $paymentIDs = NULL,
341 $paymentStatusID = NULL,
342 $pledgeStatusID = NULL,
343 $actualAmount = 0,
6a488035 344 $adjustTotalAmount = FALSE,
64041b14 345 $isScriptUpdate = FALSE
6a488035
TO
346 ) {
347 $totalAmountClause = '';
348 $paymentContributionId = NULL;
349 $editScheduled = FALSE;
350
351 //get all statuses
352 $allStatus = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name');
353
354 // if we get do not get contribution id means we are editing the scheduled payment.
355 if (!empty($paymentIDs)) {
356 $editScheduled = FALSE;
357 $payments = implode(',', $paymentIDs);
358 $paymentContributionId = CRM_Core_DAO::getFieldValue('CRM_Pledge_DAO_PledgePayment',
359 $payments,
360 'contribution_id',
361 'id'
362 );
363
364 if (!$paymentContributionId) {
365 $editScheduled = TRUE;
366 }
367 }
368
369 // if payment ids are passed, we update payment table first, since payments statuses are not dependent on pledge status
370 if ((!empty($paymentIDs) || $pledgeStatusID == array_search('Cancelled', $allStatus)) && (!$editScheduled || $isScriptUpdate)) {
371 if ($pledgeStatusID == array_search('Cancelled', $allStatus)) {
372 $paymentStatusID = $pledgeStatusID;
373 }
374
375 self::updatePledgePayments($pledgeID, $paymentStatusID, $paymentIDs, $actualAmount, $paymentContributionId, $isScriptUpdate);
376 }
377 if (!empty($paymentIDs) && $actualAmount) {
378 $payments = implode(',', $paymentIDs);
379 $pledgeScheduledAmount = CRM_Core_DAO::getFieldValue('CRM_Pledge_DAO_PledgePayment',
380 $payments,
381 'scheduled_amount',
382 'id'
383 );
384
385 $pledgeStatusId = self::calculatePledgeStatus($pledgeID);
386 // Actual Pledge Amount
387 $actualPledgeAmount = CRM_Core_DAO::getFieldValue('CRM_Pledge_DAO_Pledge',
388 $pledgeID,
389 'amount',
390 'id'
391 );
392 // while editing scheduled we need to check if we are editing last pending
393 $lastPending = FALSE;
394 if (!$paymentContributionId) {
395 $checkPendingCount = self::getOldestPledgePayment($pledgeID, 2);
396 if ($checkPendingCount['count'] == 1) {
397 $lastPending = TRUE;
398 }
399 }
400
401 // check if this is the last payment and adjust the actual amount.
402 if ($pledgeStatusId && $pledgeStatusId == array_search('Completed', $allStatus) || $lastPending) {
403 // last scheduled payment
404 if ($actualAmount >= $pledgeScheduledAmount) {
64041b14 405 $adjustTotalAmount = TRUE;
406 }
6a488035
TO
407 elseif (!$adjustTotalAmount) {
408 // actual amount is less than the scheduled amount, so enter new pledge payment record
409 $pledgeFrequencyUnit = CRM_Core_DAO::getFieldValue('CRM_Pledge_DAO_Pledge', $pledgeID, 'frequency_unit', 'id');
410 $pledgeFrequencyInterval = CRM_Core_DAO::getFieldValue('CRM_Pledge_DAO_Pledge', $pledgeID, 'frequency_interval', 'id');
411 $pledgeScheduledDate = CRM_Core_DAO::getFieldValue('CRM_Pledge_DAO_PledgePayment', $payments, 'scheduled_date', 'id');
412 $scheduled_date = CRM_Utils_Date::processDate($pledgeScheduledDate);
413 $date['year'] = (int) substr($scheduled_date, 0, 4);
414 $date['month'] = (int) substr($scheduled_date, 4, 2);
415 $date['day'] = (int) substr($scheduled_date, 6, 2);
416 $newDate = date('YmdHis', mktime(0, 0, 0, $date['month'], $date['day'], $date['year']));
417 $ScheduledDate = CRM_Utils_Date::format(CRM_Utils_Date::intervalAdd($pledgeFrequencyUnit,
64041b14 418 $pledgeFrequencyInterval, $newDate
419 ));
6a488035
TO
420 $pledgeParams = array(
421 'status_id' => array_search('Pending', $allStatus),
422 'pledge_id' => $pledgeID,
423 'scheduled_amount' => ($pledgeScheduledAmount - $actualAmount),
424 'scheduled_date' => $ScheduledDate,
425 );
426 $payment = self::add($pledgeParams);
427 // while editing schedule, after adding a new pledge payemnt update the scheduled amount of the current payment
428 if (!$paymentContributionId) {
429 CRM_Core_DAO::setFieldValue('CRM_Pledge_DAO_PledgePayment', $payments, 'scheduled_amount', $actualAmount);
430 }
431 }
64041b14 432 }
6a488035
TO
433 elseif (!$adjustTotalAmount) {
434 // not last schedule amount and also not selected to adjust Total
435 $paymentContributionId = CRM_Core_DAO::getFieldValue('CRM_Pledge_DAO_PledgePayment',
436 $payments,
437 'contribution_id',
438 'id'
439 );
a9ae3b2c 440 self::adjustPledgePayment($pledgeID, $actualAmount, $pledgeScheduledAmount, $paymentContributionId, $payments, $paymentStatusID);
6a488035
TO
441 // while editing schedule, after adding a new pledge payemnt update the scheduled amount of the current payment
442 if (!$paymentContributionId) {
443 CRM_Core_DAO::setFieldValue('CRM_Pledge_DAO_PledgePayment', $payments, 'scheduled_amount', $actualAmount);
444 }
445 // after adjusting all payments check if the actual amount was greater than the actual remaining amount , if so then update the total pledge amount.
446 $pledgeStatusId = self::calculatePledgeStatus($pledgeID);
447 $balanceQuery = "
448 SELECT sum( civicrm_pledge_payment.actual_amount )
449 FROM civicrm_pledge_payment
450 WHERE civicrm_pledge_payment.pledge_id = %1
451 AND civicrm_pledge_payment.status_id = 1
452 ";
453 $totalPaidParams = array(1 => array($pledgeID, 'Integer'));
454 $totalPaidAmount = CRM_Core_DAO::singleValueQuery($balanceQuery, $totalPaidParams);
455 $remainingTotalAmount = ($actualPledgeAmount - $totalPaidAmount);
456 if (($pledgeStatusId && $pledgeStatusId == array_search('Completed', $allStatus)) && (($actualAmount > $remainingTotalAmount) || ($actualAmount >= $actualPledgeAmount))) {
457 $totalAmountClause = ", civicrm_pledge.amount = {$totalPaidAmount}";
458 }
459 }
460 if ($adjustTotalAmount) {
461 $newTotalAmount = ($actualPledgeAmount + ($actualAmount - $pledgeScheduledAmount));
462 $totalAmountClause = ", civicrm_pledge.amount = {$newTotalAmount}";
463 if (!$paymentContributionId) {
464 CRM_Core_DAO::setFieldValue('CRM_Pledge_DAO_PledgePayment', $payments, 'scheduled_amount', $actualAmount);
465 }
466 }
467 }
468
469 $cancelDateClause = $endDateClause = NULL;
470 //update pledge and payment status if status is Completed/Cancelled.
471 if ($pledgeStatusID && $pledgeStatusID == array_search('Cancelled', $allStatus)) {
472 $paymentStatusID = $pledgeStatusID;
473 $cancelDateClause = ", civicrm_pledge.cancel_date = CURRENT_TIMESTAMP ";
474 }
475 else {
476 // get pledge status
477 $pledgeStatusID = self::calculatePledgeStatus($pledgeID);
478 }
479
480 if ($pledgeStatusID == array_search('Completed', $allStatus)) {
481 $endDateClause = ", civicrm_pledge.end_date = CURRENT_TIMESTAMP ";
482 }
483
484 //update pledge status
485 $query = "
486UPDATE civicrm_pledge
487 SET civicrm_pledge.status_id = %1
488 {$cancelDateClause} {$endDateClause} {$totalAmountClause}
489WHERE civicrm_pledge.id = %2
490";
491
64041b14 492 $params = array(
493 1 => array($pledgeStatusID, 'Integer'),
6a488035
TO
494 2 => array($pledgeID, 'Integer'),
495 );
496
497 $dao = CRM_Core_DAO::executeQuery($query, $params);
498
499 return $pledgeStatusID;
500 }
501
502 /**
503 * Calculate the base scheduled date. This function effectively 'rounds' the $params['scheduled_date'] value
504 * to the first payment date with respect to the frequency day - ie. if payments are on the 15th of the month the date returned
505 * will be the 15th of the relevant month. Then to calculate the payments you can use intervalAdd ie.
506 * CRM_Utils_Date::intervalAdd( $params['frequency_unit'], $i * ($params['frequency_interval']) , calculateBaseScheduledDate( &$params )))
507 *
508 *
509 * @param array $params
510 *
511 * @return array $newdate Next scheduled date as an array
512 * @static
513 */
514 static function calculateBaseScheduleDate(&$params) {
64041b14 515 $date = array();
6a488035 516 $scheduled_date = CRM_Utils_Date::processDate($params['scheduled_date']);
64041b14 517 $date['year'] = (int) substr($scheduled_date, 0, 4);
518 $date['month'] = (int) substr($scheduled_date, 4, 2);
519 $date['day'] = (int) substr($scheduled_date, 6, 2);
6a488035
TO
520 //calculation of schedule date according to frequency day of period
521 //frequency day is not applicable for daily installments
522 if ($params['frequency_unit'] != 'day' && $params['frequency_unit'] != 'year') {
523 if ($params['frequency_unit'] != 'week') {
524
525 //for month use day of next month as next payment date
526 $date['day'] = $params['frequency_day'];
527 }
528 elseif ($params['frequency_unit'] == 'week') {
529
530 //for week calculate day of week ie. Sunday,Monday etc. as next payment date
531 $dayOfWeek = date('w', mktime(0, 0, 0, $date['month'], $date['day'], $date['year']));
532 $frequencyDay = $params['frequency_day'] - $dayOfWeek;
533
534 $scheduleDate = explode("-", date('n-j-Y', mktime(0, 0, 0, $date['month'],
64041b14 535 $date['day'] + $frequencyDay, $date['year']
536 )));
6a488035 537 $date['month'] = $scheduleDate[0];
64041b14 538 $date['day'] = $scheduleDate[1];
539 $date['year'] = $scheduleDate[2];
6a488035
TO
540 }
541 }
542 $newdate = date('YmdHis', mktime(0, 0, 0, $date['month'], $date['day'], $date['year']));
543 return $newdate;
544 }
545
546 /**
547 * Calculate next scheduled pledge payment date. Function calculates next pledge payment date.
548 *
549 * @param array params - must include frequency unit & frequency interval
550 * @param int paymentNo number of payment in sequence (e.g. 1 for first calculated payment (treat initial payment as 0)
551 * @param datestring basePaymentDate - date to calculate payments from. This would normally be the
552 * first day of the pledge (default) & is calculated off the 'scheduled date' param. Returned date will
553 * be equal to basePaymentDate normalised to fit the 'pledge pattern' + number of installments
554 *
555 * @return formatted date
556 *
557 */
558 static function calculateNextScheduledDate(&$params, $paymentNo, $basePaymentDate = NULL) {
559 if (!$basePaymentDate) {
560 $basePaymentDate = self::calculateBaseScheduleDate($params);
561 }
562 return CRM_Utils_Date::format(
563 CRM_Utils_Date::intervalAdd(
564 $params['frequency_unit'],
565 $paymentNo * ($params['frequency_interval']),
566 $basePaymentDate
567 )
568 );
569 }
570
571 /**
572 * Calculate the pledge status
573 *
574 * @param int $pledgeId pledge id
575 *
576 * @return int $statusId calculated status id of pledge
577 * @static
578 */
579 static function calculatePledgeStatus($pledgeId) {
580 $paymentStatusTypes = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name');
581
582 //retrieve all pledge payments for this particular pledge
583 $allPledgePayments = $allStatus = array();
584 $returnProperties = array('status_id');
585 CRM_Core_DAO::commonRetrieveAll('CRM_Pledge_DAO_PledgePayment', 'pledge_id', $pledgeId, $allPledgePayments, $returnProperties);
586
587 // build pledge payment statuses
588 foreach ($allPledgePayments as $key => $value) {
589 $allStatus[$value['id']] = $paymentStatusTypes[$value['status_id']];
590 }
591
592 if (array_search('Overdue', $allStatus)) {
593 $statusId = array_search('Overdue', $paymentStatusTypes);
594 }
595 elseif (array_search('Completed', $allStatus)) {
596 if (count(array_count_values($allStatus)) == 1) {
597 $statusId = array_search('Completed', $paymentStatusTypes);
598 }
599 else {
600 $statusId = array_search('In Progress', $paymentStatusTypes);
601 }
602 }
603 else {
604 $statusId = array_search('Pending', $paymentStatusTypes);
605 }
606
607 return $statusId;
608 }
609
610 /**
100fef9d 611 * Update pledge payment table
6a488035 612 *
64041b14 613 * @param int $pledgeId pledge id
64041b14 614 * @param int $paymentStatusId payment status id to set
77b97be7
EM
615 * @param array $paymentIds payment ids to be updated
616 * @param float|int $actualAmount , actual amount being paid
617 * @param int $contributionId , Id of associated contribution when payment is recorded
618 * @param bool $isScriptUpdate , is function being called from bin script?
619 *
6a488035
TO
620 * @static
621 */
622 static function updatePledgePayments($pledgeId,
64041b14 623 $paymentStatusId,
624 $paymentIds = NULL,
625 $actualAmount = 0,
626 $contributionId = NULL,
627 $isScriptUpdate = FALSE
6a488035
TO
628 ) {
629 $allStatus = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name');
630 $paymentClause = NULL;
631 if (!empty($paymentIds)) {
632 $payments = implode(',', $paymentIds);
633 $paymentClause = " AND civicrm_pledge_payment.id IN ( {$payments} )";
634 }
635 $actualAmountClause = NULL;
636 $contributionIdClause = NULL;
637 if (isset($contributionId) && !$isScriptUpdate) {
638 $contributionIdClause = ", civicrm_pledge_payment.contribution_id = {$contributionId}";
639 $actualAmountClause = ", civicrm_pledge_payment.actual_amount = {$actualAmount}";
640 }
641
642 $query = "
643UPDATE civicrm_pledge_payment
644SET civicrm_pledge_payment.status_id = {$paymentStatusId}
645 {$actualAmountClause} {$contributionIdClause}
646WHERE civicrm_pledge_payment.pledge_id = %1
647 {$paymentClause}
648";
649
650 //get all status
651 $params = array(1 => array($pledgeId, 'Integer'));
652
653 $dao = CRM_Core_DAO::executeQuery($query, $params);
654 }
655
656 /**
100fef9d 657 * Update pledge payment table when reminder is sent
6a488035
TO
658 *
659 * @param int $paymentId payment id
660 *
661 * @static
662 */
663 static function updateReminderDetails($paymentId) {
664 $query = "
665UPDATE civicrm_pledge_payment
666SET civicrm_pledge_payment.reminder_date = CURRENT_TIMESTAMP,
667 civicrm_pledge_payment.reminder_count = civicrm_pledge_payment.reminder_count + 1
668WHERE civicrm_pledge_payment.id = {$paymentId}
669";
670 $dao = CRM_Core_DAO::executeQuery($query);
671 }
672
673 /**
100fef9d 674 * Get oldest pending or in progress pledge payments
6a488035
TO
675 *
676 * @param int $pledgeID pledge id
677 *
2a6da8d7
EM
678 * @param int $limit
679 *
6a488035
TO
680 * @return array associated array of pledge details
681 * @static
682 */
683 static function getOldestPledgePayment($pledgeID, $limit = 1) {
684 //get pending / overdue statuses
685 $pledgeStatuses = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name');
686
687 //get pending and overdue payments
688 $status[] = array_search('Pending', $pledgeStatuses);
689 $status[] = array_search('Overdue', $pledgeStatuses);
690
691 $statusClause = " IN (" . implode(',', $status) . ")";
692
693 $query = "
694SELECT civicrm_pledge_payment.id id, civicrm_pledge_payment.scheduled_amount amount, civicrm_pledge_payment.currency
695FROM civicrm_pledge, civicrm_pledge_payment
696WHERE civicrm_pledge.id = civicrm_pledge_payment.pledge_id
697 AND civicrm_pledge_payment.status_id {$statusClause}
698 AND civicrm_pledge.id = %1
699ORDER BY civicrm_pledge_payment.scheduled_date ASC
700LIMIT 0, %2
701";
702
64041b14 703 $params[1] = array($pledgeID, 'Integer');
704 $params[2] = array($limit, 'Integer');
705 $payment = CRM_Core_DAO::executeQuery($query, $params);
706 $count = 1;
6a488035
TO
707 $paymentDetails = array();
708 while ($payment->fetch()) {
709 $paymentDetails[] = array(
710 'id' => $payment->id,
711 'amount' => $payment->amount,
712 'currency' => $payment->currency,
713 'count' => $count,
714 );
715 $count++;
716 }
717 return end($paymentDetails);
718 }
719
ffd93213 720 /**
100fef9d 721 * @param int $pledgeID
ffd93213
EM
722 * @param $actualAmount
723 * @param $pledgeScheduledAmount
100fef9d
CW
724 * @param int $paymentContributionId
725 * @param int $pPaymentId
726 * @param int $paymentStatusID
ffd93213 727 */
a9ae3b2c 728 static function adjustPledgePayment($pledgeID, $actualAmount, $pledgeScheduledAmount, $paymentContributionId = NULL, $pPaymentId = NULL, $paymentStatusID = NULL) {
6a488035 729 $allStatus = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name');
a9ae3b2c
DG
730 if ($paymentStatusID == array_search('Cancelled', $allStatus) || $paymentStatusID == array_search('Refunded', $allStatus)) {
731 $query = "
732SELECT civicrm_pledge_payment.id id
733FROM civicrm_pledge_payment
734WHERE civicrm_pledge_payment.contribution_id = {$paymentContributionId}
735";
736 $paymentsAffected = CRM_Core_DAO::executeQuery($query);
737 $paymentIDs = array();
738 while ($paymentsAffected->fetch()) {
739 $paymentIDs[] = $paymentsAffected->id;
740 }
2efcf0c2 741 // Reset the affected values by the amount paid more than the scheduled amount
64041b14 742 foreach ($paymentIDs as $key => $value) {
a9ae3b2c
DG
743 $payment = new CRM_Pledge_DAO_PledgePayment();
744 $payment->id = $value;
745 if ($payment->find(TRUE)) {
746 $payment->contribution_id = 'null';
747 $payment->status_id = array_search('Pending', $allStatus);
748 $payment->scheduled_date = NULL;
749 $payment->reminder_date = NULL;
750 $payment->scheduled_amount = $pledgeScheduledAmount;
751 $payment->actual_amount = 'null';
752 $payment->save();
753 }
754 }
2efcf0c2 755
a9ae3b2c
DG
756 //Cancel the initial paid amount
757 CRM_Core_DAO::setFieldValue('CRM_Pledge_DAO_PledgePayment', reset($paymentIDs), 'status_id', $paymentStatusID, 'id');
758 CRM_Core_DAO::setFieldValue('CRM_Pledge_DAO_PledgePayment', reset($paymentIDs), 'actual_amount', $actualAmount, 'id');
2efcf0c2 759
a9ae3b2c
DG
760 //Add new payment after the last payment for the pledge
761 $allPayments = self::getPledgePayments($pledgeID);
762 $lastPayment = array_pop($allPayments);
763
764 $pledgeFrequencyUnit = CRM_Core_DAO::getFieldValue('CRM_Pledge_DAO_Pledge', $pledgeID, 'frequency_unit', 'id');
765 $pledgeFrequencyInterval = CRM_Core_DAO::getFieldValue('CRM_Pledge_DAO_Pledge', $pledgeID, 'frequency_interval', 'id');
64041b14 766 $pledgeScheduledDate = $lastPayment['scheduled_date'];
a9ae3b2c
DG
767 $scheduled_date = CRM_Utils_Date::processDate($pledgeScheduledDate);
768 $date['year'] = (int) substr($scheduled_date, 0, 4);
769 $date['month'] = (int) substr($scheduled_date, 4, 2);
770 $date['day'] = (int) substr($scheduled_date, 6, 2);
771 $newDate = date('YmdHis', mktime(0, 0, 0, $date['month'], $date['day'], $date['year']));
772 $ScheduledDate = CRM_Utils_Date::format(CRM_Utils_Date::intervalAdd($pledgeFrequencyUnit, $pledgeFrequencyInterval, $newDate));
773 $pledgeParams = array(
774 'status_id' => array_search('Pending', $allStatus),
775 'pledge_id' => $pledgeID,
776 'scheduled_amount' => $pledgeScheduledAmount,
777 'scheduled_date' => $ScheduledDate,
778 );
779 $payment = self::add($pledgeParams);
780 }
781 else {
64041b14 782 $oldestPayment = self::getOldestPledgePayment($pledgeID);
783 if (!$paymentContributionId) {
784 // means we are editing payment scheduled payment, so get the second pending to update.
785 $oldestPayment = self::getOldestPledgePayment($pledgeID, 2);
786 if (($oldestPayment['count'] != 1) && ($oldestPayment['id'] == $pPaymentId)) {
787 $oldestPayment = self::getOldestPledgePayment($pledgeID);
788 }
6a488035 789 }
6a488035 790
64041b14 791 if ($oldestPayment) {
792 // not the last scheduled payment and the actual amount is less than the expected , add it to oldest pending.
793 if (($actualAmount != $pledgeScheduledAmount) && (($actualAmount < $pledgeScheduledAmount) || (($actualAmount - $pledgeScheduledAmount) < $oldestPayment['amount']))) {
794 $oldScheduledAmount = $oldestPayment['amount'];
795 $newScheduledAmount = $oldScheduledAmount + ($pledgeScheduledAmount - $actualAmount);
796 //store new amount in oldest pending payment record.
7ca3c666 797 CRM_Core_DAO::setFieldValue('CRM_Pledge_DAO_PledgePayment',
798 $oldestPayment['id'],
799 'scheduled_amount',
800 $newScheduledAmount
801 );
802 if (CRM_Core_DAO::getFieldValue('CRM_Pledge_DAO_PledgePayment', $oldestPayment['id'], 'contribution_id', 'id')) {
803 CRM_Core_DAO::setFieldValue('CRM_Pledge_DAO_PledgePayment',
804 $oldestPayment['id'],
805 'contribution_id',
806 $paymentContributionId
807 );
808 }
6a488035 809 }
64041b14 810 elseif (($actualAmount > $pledgeScheduledAmount) && (($actualAmount - $pledgeScheduledAmount) >= $oldestPayment['amount'])) {
811 // 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
812 // set the actual amount of the next pending to '0', set contribution Id to current contribution Id and status as completed
813 $paymentId = array($oldestPayment['id']);
814 self::updatePledgePayments($pledgeID, array_search('Completed', $allStatus), $paymentId, 0, $paymentContributionId);
815 CRM_Core_DAO::setFieldValue('CRM_Pledge_DAO_PledgePayment', $oldestPayment['id'], 'scheduled_amount', 0, 'id');
816 $oldestPayment = self::getOldestPledgePayment($pledgeID);
817 if (!$paymentContributionId) {
818 // means we are editing payment scheduled payment.
819 $oldestPaymentAmount = self::getOldestPledgePayment($pledgeID, 2);
820 }
6a488035 821 $newActualAmount = ($actualAmount - $pledgeScheduledAmount);
64041b14 822 $newPledgeScheduledAmount = $oldestPayment['amount'];
823 if (!$paymentContributionId) {
824 $newActualAmount = ($actualAmount - $pledgeScheduledAmount);
825 $newPledgeScheduledAmount = $oldestPaymentAmount['amount'];
826 // means we are editing payment scheduled payment, so update scheduled amount.
827 CRM_Core_DAO::setFieldValue('CRM_Pledge_DAO_PledgePayment',
828 $oldestPaymentAmount['id'],
829 'scheduled_amount',
830 $newActualAmount
831 );
832 }
833 if ($newActualAmount > 0) {
834 self::adjustPledgePayment($pledgeID, $newActualAmount, $newPledgeScheduledAmount, $paymentContributionId);
835 }
6a488035
TO
836 }
837 }
838 }
839 }
840}
841