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