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