Skip to content

Commit

Permalink
CRM-18854 [needs-review] Added support for recurring contributions fo…
Browse files Browse the repository at this point in the history
…r online pledges (#8558)

* MISC-110 Added pledge start date for pledge block

* MISC-110 Added pledge start date to online contribution form

* MISC-110 Added recur record for pledge

* MISC-110 Added changes suggested by Eileen

* MISC-110 Added upgrade code for pledge_start_date

* MISC-110 Added unit test for createRecur function

* MISC-110 Removed use of form and set payment instrument id

* MISC-110 Added webtest for Pledge Recurring contribution

* MISC-110 Removed unfinished unit test

* MISC-110 Resolved Jenkins errors

* MISC-110 Added apiSuccess assertion

* MISC-110 Added changes from UP-3

* MISC-113 Added UI for frontend and set defaults

* MISC-113 Bug fixes and improvements

* MISC-113 Added date fields for start date on online contribution form

* MISC-113 Added fields in payment processor table

* MISC-113 Added processing for start date

* MISC-110 Added changes to set contribution receive date for future payments

* MISC-110 Typo error fixes

* MISC-110 Added future start date to payment processor

MISC-113 Added JS for adjust recurring start date

MISC-110 Added future start date values on install

MISC-113 Added script on install and upgrade

MISC-110 Added form rule for pledge start date

MISC-110 Removed explicit create of recur record

-- minor change

-- minor change

-- minor change

MISC-113 Added unit test

MISC-113 Removed old webtest

MISC-113 Resolved jenkins errors

MISC-110 Style changes

MISC-113 Allowed NULL values for future start date

MISC-110 Removed future_start_date field from payment processor

MISC-110 Removed future_start_date field from data

* MISC-113 Changed serialize to json_encode

MISC-113 Jenkins style fixes

* MISC-113 Bug fixes for start date

* MISC-113 Added unit test for contribution page submit with pledge with future start date

MISC-110 Bug fix

* MISC-113 Added id = 0 instead of pay_later text

* MISC-110 Refactored assignment of contributionRecurID

Conflicts:
	CRM/Contribute/BAO/Contribution/Utils.php

* MISC-113 Handled issue with membership recur payments

* MISC-110 Added upgrade script to 4.7.10
  • Loading branch information
Edzelopez authored and colemanw committed Jul 8, 2016
1 parent 29ef094 commit dccd9f4
Show file tree
Hide file tree
Showing 17 changed files with 559 additions and 24 deletions.
5 changes: 0 additions & 5 deletions CRM/Contribute/BAO/Contribution/Utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,6 @@ public static function processConfirm(
$paymentParams['contributionTypeID'] = $contributionTypeId;
$paymentParams['item_name'] = $form->_params['description'];

if ($contribution && $form->_values['is_recur'] && $contribution->contribution_recur_id
) {
$form->_params['contributionRecurID'] = $contribution->contribution_recur_id;
}

$paymentParams['qfKey'] = $form->controller->_key;
if ($component == 'membership') {
return array('contribution' => $contribution);
Expand Down
30 changes: 30 additions & 0 deletions CRM/Contribute/Form/Contribution/Confirm.php
Original file line number Diff line number Diff line change
Expand Up @@ -932,6 +932,11 @@ public static function processFormContribution(
$pledgeParams['frequency_day'] = 1;
}
$pledgeParams['create_date'] = $pledgeParams['start_date'] = $pledgeParams['scheduled_date'] = date("Ymd");
$pledgeBlock = CRM_Pledge_BAO_PledgeBlock::getPledgeBlock($contribution->contribution_page_id);
if (CRM_Utils_Array::value('start_date', $params) || !CRM_Utils_Array::value('is_pledge_start_date_visible', $pledgeBlock)) {
$pledgeStartDate = CRM_Utils_Array::value('start_date', $params, NULL);
$pledgeParams['start_date'] = $pledgeParams['scheduled_date'] = CRM_Pledge_BAO_Pledge::getPledgeStartDate($pledgeStartDate, $pledgeBlock);
}
$pledgeParams['status_id'] = $contribution->contribution_status_id;
$pledgeParams['max_reminders'] = $form->_values['max_reminders'];
$pledgeParams['initial_reminder_day'] = $form->_values['initial_reminder_day'];
Expand Down Expand Up @@ -1104,6 +1109,10 @@ public static function processRecurringContribution(&$form, &$params, $contactID
}
CRM_Utils_System::redirect(CRM_Utils_System::url($urlString, $urlParams));
}
// Only set contribution recur ID for contributions since offline membership recur payments are handled somewhere else.
if (!is_a($form, "CRM_Member_Form_Membership")) {
$form->_params['contributionRecurID'] = $recurring->id;
}

return $recurring->id;
}
Expand Down Expand Up @@ -1846,6 +1855,15 @@ public static function submit($params) {
// hack these in for test support.
$form->_fields['billing_first_name'] = 1;
$form->_fields['billing_last_name'] = 1;
// CRM-18854 - Set form values to allow pledge to be created for api test.
if (CRM_Utils_Array::value('pledge_block_id', $params)) {
$form->_values['pledge_block_id'] = $params['pledge_block_id'];
$pledgeBlock = CRM_Pledge_BAO_PledgeBlock::getPledgeBlock($params['id']);
$form->_values['max_reminders'] = $pledgeBlock['max_reminders'];
$form->_values['initial_reminder_day'] = $pledgeBlock['initial_reminder_day'];
$form->_values['additional_reminder_day'] = $pledgeBlock['additional_reminder_day'];
$form->_values['is_email_receipt'] = FALSE;
}
$priceSetID = $form->_params['priceSetId'] = $paramsProcessedForForm['price_set_id'];
$priceFields = CRM_Price_BAO_PriceSet::getSetDetail($priceSetID);
$priceSetFields = reset($priceFields);
Expand Down Expand Up @@ -1929,6 +1947,17 @@ protected function processFormSubmission($contactID) {
// fix currency ID
$this->_params['currencyID'] = CRM_Core_Config::singleton()->defaultCurrency;

// CRM-18854
if (CRM_Utils_Array::value('adjust_recur_start_date', $this->_values)) {
$pledgeBlock = CRM_Pledge_BAO_PledgeBlock::getPledgeBlock($this->_id);
if (CRM_Utils_Array::value('start_date', $this->_params) || !CRM_Utils_Array::value('is_pledge_start_date_visible', $pledgeBlock)) {
$pledgeStartDate = CRM_Utils_Array::value('start_date', $this->_params, NULL);
$this->_params['receive_date'] = CRM_Pledge_BAO_Pledge::getPledgeStartDate($pledgeStartDate, $pledgeBlock);
$recurParams = CRM_Pledge_BAO_Pledge::buildRecurParams($this->_params);
$this->_params = array_merge($this->_params, $recurParams);
}
}

//carry payment processor id.
if (CRM_Utils_Array::value('id', $this->_paymentProcessor)) {
$this->_params['payment_processor_id'] = $this->_paymentProcessor['id'];
Expand Down Expand Up @@ -2315,6 +2344,7 @@ protected function completeTransaction($result, $contributionID) {
'payment_processor_id' => $this->_paymentProcessor['id'],
'is_transactional' => FALSE,
'fee_amount' => CRM_Utils_Array::value('fee_amount', $result),
'receive_date' => CRM_Utils_Array::value('receive_date', $result),
)
);
}
Expand Down
17 changes: 17 additions & 0 deletions CRM/Contribute/Form/ContributionPage.php
Original file line number Diff line number Diff line change
Expand Up @@ -293,9 +293,26 @@ public function setDefaultValues() {
'max_reminders',
'initial_reminder_day',
'additional_reminder_day',
'pledge_start_date',
'is_pledge_start_date_visible',
'is_pledge_start_date_editable',
);
foreach ($pledgeBlock as $key) {
$defaults[$key] = CRM_Utils_Array::value($key, $pledgeBlockDefaults);
if ($key == 'pledge_start_date' && CRM_Utils_Array::value($key, $pledgeBlockDefaults)) {
$defaultPledgeDate = (array) json_decode($pledgeBlockDefaults['pledge_start_date']);
$pledgeDateFields = array(
'pledge_calendar_date' => 'calendar_date',
'pledge_calendar_month' => 'calendar_month',
);
$defaults['pledge_default_toggle'] = key($defaultPledgeDate);
foreach ($pledgeDateFields as $key => $value) {
if (array_key_exists($value, $defaultPledgeDate)) {
$defaults[$key] = reset($defaultPledgeDate);
$this->assign($key, reset($defaultPledgeDate));
}
}
}
}
if (!empty($pledgeBlockDefaults['pledge_frequency_unit'])) {
$defaults['pledge_frequency_unit'] = array_fill_keys(explode(CRM_Core_DAO::VALUE_SEPARATOR,
Expand Down
96 changes: 82 additions & 14 deletions CRM/Contribute/Form/ContributionPage/Amount.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,22 +87,28 @@ public function buildQuickForm() {

$this->addElement('checkbox', 'is_monetary', ts('Execute real-time monetary transactions'));

$paymentProcessor = CRM_Core_PseudoConstant::paymentProcessor();
$recurringPaymentProcessor = array();

if (!empty($paymentProcessor)) {
$paymentProcessorIds = implode(',', array_keys($paymentProcessor));
$query = "
SELECT id
FROM civicrm_payment_processor
WHERE id IN ({$paymentProcessorIds})
AND is_recur = 1";
$dao = CRM_Core_DAO::executeQuery($query);
while ($dao->fetch()) {
$recurringPaymentProcessor[] = $dao->id;
$paymentProcessors = CRM_Financial_BAO_PaymentProcessor::getAllPaymentProcessors('live');
$recurringPaymentProcessor = $futurePaymentProcessor = $paymentProcessor = array();

if (!empty($paymentProcessors)) {
foreach ($paymentProcessors as $id => $processor) {
if ($id != 0) {
$paymentProcessor[$id] = $processor['name'];
}
if (CRM_Utils_Array::value('is_recur', $processor)) {
$recurringPaymentProcessor[] = $id;
}
if (CRM_Utils_Array::value('object', $processor) && $processor['object']->supports('FutureRecurStartDate')) {
$futurePaymentProcessor[] = $id;
}
}
}
$this->assign('recurringPaymentProcessor', $recurringPaymentProcessor);
if (count($recurringPaymentProcessor)) {
$this->assign('recurringPaymentProcessor', $recurringPaymentProcessor);
}
if (count($futurePaymentProcessor)) {
$this->assign('futurePaymentProcessor', $futurePaymentProcessor);
}
if (count($paymentProcessor)) {
$this->assign('paymentProcessor', $paymentProcessor);
}
Expand Down Expand Up @@ -168,6 +174,23 @@ public function buildQuickForm() {
$this->addElement('text', 'initial_reminder_day', ts('Send payment reminder'), array('size' => 3));
$this->addElement('text', 'max_reminders', ts('Send up to'), array('size' => 3));
$this->addElement('text', 'additional_reminder_day', ts('Send additional reminders'), array('size' => 3));
if (!empty($futurePaymentProcessor)) {
// CRM-18854
$this->addElement('checkbox', 'adjust_recur_start_date', ts('Adjust Recurring Start Date'), NULL,
array('onclick' => "showHideByValue('adjust_recur_start_date',true,'recurDefaults','table-row','radio',false);")
);
$this->addDate('pledge_calendar_date', ts('Specific Calendar Date'));
$month = CRM_Utils_Date::getCalendarDayOfMonth();
$this->add('select', 'pledge_calendar_month', ts('Specific day of Month'), $month);
$pledgeDefaults = array(
'contribution_date' => ts('Day of Contribution'),
'calendar_date' => ts('Specific Calendar Date'),
'calendar_month' => ts('Specific day of Month'),
);
$this->addRadio('pledge_default_toggle', ts('Recurring Contribution Start Date Default'), $pledgeDefaults, array('allowClear' => FALSE), '<br/><br/>');
$this->addElement('checkbox', 'is_pledge_start_date_visible', ts('Show Recurring Donation Start Date?'), NULL);
$this->addElement('checkbox', 'is_pledge_start_date_editable', ts('Allow Edits to Recurring Donation Start date?'), NULL);
}
}

//add currency element.
Expand Down Expand Up @@ -320,6 +343,13 @@ public static function formRule($fields, $files, $self) {
}
}

// CRM-18854 Check if recurring start date is in the future.
if (CRM_Utils_Array::value('pledge_calendar_date', $fields)) {
if (date('Ymd') > date('Ymd', strtotime($fields['pledge_calendar_date']))) {
$errors['pledge_calendar_date'] = ts('The recurring start date cannot be prior to the current date.');
}
}

//check for the amount label (mandatory)
if (!empty($fields['amount_block_is_active']) && empty($fields['amount_label'])) {
$errors['amount_label'] = ts('Please enter the contribution amount label.');
Expand Down Expand Up @@ -481,6 +511,38 @@ public function postProcess() {
$params['is_recur_installments'] = CRM_Utils_Array::value('is_recur_installments', $params, FALSE);
}

if (CRM_Utils_Array::value('adjust_recur_start_date', $params)) {
$fieldValue = '';
$pledgeDateFields = array(
'calendar_date' => 'pledge_calendar_date',
'calendar_month' => 'pledge_calendar_month',
);
if ($params['pledge_default_toggle'] == 'contribution_date') {
$fieldValue = json_encode(array('contribution_date' => date('Ymd')));
}
else {
foreach ($pledgeDateFields as $key => $pledgeDateField) {
if (CRM_Utils_Array::value($pledgeDateField, $params) && $params['pledge_default_toggle'] == $key) {
$fieldValue = json_encode(array($key => $params[$pledgeDateField]));
break;
}
}
}
$params['pledge_start_date'] = $fieldValue;
}
else {
$params['pledge_start_date'] = '';
$params['adjust_recur_start_date'] = 0;
$params['is_pledge_start_date_visible'] = 0;
$params['is_pledge_start_date_editable'] = 0;
}
if (!CRM_Utils_Array::value('is_pledge_start_date_visible', $params)) {
$params['is_pledge_start_date_visible'] = 0;
}
if (!CRM_Utils_Array::value('is_pledge_start_date_editable', $params)) {
$params['is_pledge_start_date_editable'] = 0;
}

if (array_key_exists('payment_processor', $params) &&
!CRM_Utils_System::isNull($params['payment_processor'])
) {
Expand Down Expand Up @@ -712,13 +774,19 @@ public function postProcess() {
'max_reminders',
'initial_reminder_day',
'additional_reminder_day',
'pledge_start_date',
'is_pledge_start_date_visible',
'is_pledge_start_date_editable',
);
foreach ($pledgeBlock as $key) {
$pledgeBlockParams[$key] = CRM_Utils_Array::value($key, $params);
}
$pledgeBlockParams['is_pledge_interval'] = CRM_Utils_Array::value('is_pledge_interval',
$params, FALSE
);
$pledgeBlockParams['pledge_start_date'] = CRM_Utils_Array::value('pledge_start_date',
$params, FALSE
);
// create pledge block.
CRM_Pledge_BAO_PledgeBlock::create($pledgeBlockParams);
}
Expand Down
86 changes: 86 additions & 0 deletions CRM/Pledge/BAO/Pledge.php
Original file line number Diff line number Diff line change
Expand Up @@ -1202,4 +1202,90 @@ protected static function getTransactionalStatus() {
return array_diff(array_flip($paymentStatus), self::getNonTransactionalStatus());
}

/**
* Create array for recur record for pledge.
* @return array
* params for recur record
*/
public static function buildRecurParams($params) {
$recurParams = array(
'is_recur' => TRUE,
'auto_renew' => TRUE,
'frequency_unit' => $params['pledge_frequency_unit'],
'frequency_interval' => $params['pledge_frequency_interval'],
'installments' => $params['pledge_installments'],
'start_date' => $params['receive_date'],
);
return $recurParams;
}

/**
* Get pledge start date.
*
* @return string
* start date
*/
public static function getPledgeStartDate($date, $pledgeBlock) {
$startDate = (array) json_decode($pledgeBlock['pledge_start_date']);
list($field, $value) = each($startDate);
if (!CRM_Utils_Array::value('is_pledge_start_date_visible', $pledgeBlock)) {
return date('Ymd', strtotime($value));
}
if (!CRM_Utils_Array::value('is_pledge_start_date_editable', $pledgeBlock)) {
return $date;
}
switch ($field) {
case 'contribution_date':
$date = date('Ymd');
break;

case 'calendar_date':
$date = date('Ymd', strtotime($date));
break;

case 'calendar_month':
$date = self::getPaymentDate($date);
$date = date('Ymd', strtotime($date));
break;

default:
break;

}
return $date;
}

/**
* Get first payment date for pledge.
*
*/
public static function getPaymentDate($day) {
if ($day == 31) {
// Find out if current month has 31 days, if not, set it to 30 (last day).
$t = date('t');
if ($t != $day) {
$day = $t;
}
}
$current = date('d');
switch (TRUE) {
case ($day == $current):
$date = date('m/d/Y');
break;

case ($day > $current):
$date = date('m/d/Y', mktime(0, 0, 0, date('m'), $day, date('Y')));
break;

case ($day < $current):
$date = date('m/d/Y', mktime(0, 0, 0, date('m', strtotime("+1 month")), $day, date('Y')));
break;

default:
break;

}
return $date;
}

}
45 changes: 45 additions & 0 deletions CRM/Pledge/BAO/PledgeBlock.php
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ public static function buildPledgeBlock($form) {
'scheduled_date',
'scheduled_amount',
'currency',
'pledge_start_date',
);
CRM_Core_DAO::commonRetrieveAll('CRM_Pledge_DAO_PledgePayment', 'pledge_id',
$form->_values['pledge_id'], $allPayments, $returnProperties
Expand Down Expand Up @@ -300,6 +301,50 @@ public static function buildPledgeBlock($form) {
}
}
$form->addElement('select', 'pledge_frequency_unit', NULL, $freqUnits);
// CRM-18854
if (CRM_Utils_Array::value('is_pledge_start_date_visible', $pledgeBlock)) {
if (CRM_Utils_Array::value('pledge_start_date', $pledgeBlock)) {
$defaults = array();
$date = (array) json_decode($pledgeBlock['pledge_start_date']);
list($field, $value) = each($date);
switch ($field) {
case 'contribution_date':
$form->addDate('start_date', ts('First installment payment'));
$paymentDate = $value = date('d/m/Y');
list($defaults['start_date'], $defaults['start_date_time']) = CRM_Utils_Date::setDateDefaults($value);
$form->assign('is_date', TRUE);
break;

case 'calendar_date':
$form->addDate('start_date', ts('First installment payment'));
list($defaults['start_date'], $defaults['start_date_time']) = CRM_Utils_Date::setDateDefaults($value);
$form->assign('is_date', TRUE);
$paymentDate = $value;
break;

case 'calendar_month':
$month = CRM_Utils_Date::getCalendarDayOfMonth();
$form->add('select', 'start_date', ts('Day of month installments paid'), $month);
$paymentDate = CRM_Pledge_BAO_Pledge::getPaymentDate($value);
list($defaults['start_date'], $defaults['start_date_time']) = CRM_Utils_Date::setDateDefaults($paymentDate);
break;

default:
break;

}
$form->setDefaults($defaults);
$form->assign('start_date_display', $paymentDate);
$form->assign('start_date_editable', FALSE);
if (CRM_Utils_Array::value('is_pledge_start_date_editable', $pledgeBlock)) {
$form->assign('start_date_editable', TRUE);
if ($field == 'calendar_month') {
$form->assign('is_date', FALSE);
$form->setDefaults(array('start_date' => $value));
}
}
}
}
}
}

Expand Down
Loading

0 comments on commit dccd9f4

Please sign in to comment.