3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
19 * This class generates form components for processing a pledge
21 class CRM_Pledge_Form_Pledge
extends CRM_Core_Form
{
25 * The id of the pledge that we are proceessing.
32 * The id of the contact associated with this pledge.
39 * The Pledge values if an existing pledge.
45 * The Pledge frequency Units.
51 * Is current pledge pending.
54 public $_isPending = FALSE;
57 * Set variables up before form is built.
59 * @throws \CRM_Core_Exception
61 public function preProcess() {
62 $this->_contactID
= CRM_Utils_Request
::retrieve('cid', 'Positive', $this);
63 $this->_action
= CRM_Utils_Request
::retrieve('action', 'String',
66 $this->_id
= CRM_Utils_Request
::retrieve('id', 'Positive', $this);
67 $this->_context
= CRM_Utils_Request
::retrieve('context', 'Alphanumeric', $this);
69 // check for action permissions.
70 if (!CRM_Core_Permission
::checkActionPermission('CiviPledge', $this->_action
)) {
71 CRM_Core_Error
::statusBounce(ts('You do not have permission to access this page.'));
74 $this->assign('action', $this->_action
);
75 $this->assign('context', $this->_context
);
76 if ($this->_action
& CRM_Core_Action
::DELETE
) {
80 $this->userDisplayName
= $this->userEmail
= NULL;
81 if ($this->_contactID
) {
82 list($this->userDisplayName
,
84 ) = CRM_Contact_BAO_Contact_Location
::getEmailDetails($this->_contactID
);
87 $this->setPageTitle(ts('Pledge'));
90 CRM_Custom_Form_CustomData
::preProcess($this, NULL, NULL, 1, 'Pledge', $this->_id
);
94 // get the contribution id
95 $this->_contributionID
= CRM_Core_DAO
::getFieldValue('CRM_Pledge_DAO_PledgePayment',
96 $this->_id
, 'contribution_id', 'pledge_id'
98 $params = ['id' => $this->_id
];
99 CRM_Pledge_BAO_Pledge
::getValues($params, $this->_values
);
101 $this->_isPending
= (CRM_Pledge_BAO_Pledge
::pledgeHasFinancialTransactions($this->_id
, CRM_Utils_Array
::value('status_id', $this->_values
))) ?
FALSE : TRUE;
104 // get the pledge frequency units.
105 $this->_freqUnits
= CRM_Core_OptionGroup
::values('recur_frequency_units');
107 $this->_fromEmails
= CRM_Core_BAO_Email
::getFromEmail();
111 * Set default values for the form.
112 * The default values are retrieved from the database.
114 public function setDefaultValues() {
115 $defaults = $this->_values
;
117 if ($this->_action
& CRM_Core_Action
::DELETE
) {
121 if (!empty($defaults['is_test'])) {
122 $this->assign('is_test', TRUE);
126 // check is this pledge pending.
127 // fix the display of the monetary value, CRM-4038.
128 if ($this->_isPending
) {
129 $defaults['eachPaymentAmount'] = $this->_values
['amount'] / $this->_values
['installments'];
130 $defaults['eachPaymentAmount'] = CRM_Utils_Money
::format($defaults['eachPaymentAmount'], NULL, '%a');
133 // fix the display of the monetary value, CRM-4038
134 if (isset($this->_values
['amount'])) {
135 $defaults['amount'] = CRM_Utils_Money
::format($this->_values
['amount'], NULL, '%a');
137 $this->assign('amount', $this->_values
['amount']);
138 $this->assign('installments', $defaults['installments']);
141 if ($this->_contactID
) {
142 $defaults['contact_id'] = $this->_contactID
;
145 $defaults['create_date'] = date('Y-m-d');
146 $defaults['start_date'] = date('Y-m-d');
147 $defaults['installments'] = 12;
148 $defaults['frequency_interval'] = 1;
149 $defaults['frequency_day'] = 1;
150 $defaults['initial_reminder_day'] = 5;
151 $defaults['max_reminders'] = 1;
152 $defaults['additional_reminder_day'] = 5;
153 $defaults['frequency_unit'] = array_search('month', $this->_freqUnits
);
154 $defaults['financial_type_id'] = array_search('Donation', CRM_Contribute_PseudoConstant
::financialType());
157 $pledgeStatus = CRM_Pledge_BAO_Pledge
::buildOptions('status_id');
158 $pledgeStatusNames = CRM_Core_OptionGroup
::values('pledge_status',
159 FALSE, FALSE, FALSE, NULL, 'name', TRUE
161 // get default status label (pending)
162 $defaultPledgeStatus = CRM_Utils_Array
::value(array_search('Pending', $pledgeStatusNames),
167 $this->assign('status', CRM_Utils_Array
::value(CRM_Utils_Array
::value('status_id', $this->_values
),
172 if (isset($this->userEmail
)) {
173 $this->assign('email', $this->userEmail
);
176 // custom data set defaults
177 $defaults +
= CRM_Custom_Form_CustomData
::setDefaultValues($this);
183 * Build the form object.
185 public function buildQuickForm() {
186 if ($this->_action
& CRM_Core_Action
::DELETE
) {
190 'name' => ts('Delete'),
191 'spacing' => ' ',
196 'name' => ts('Cancel'),
202 $contactField = $this->addEntityRef('contact_id', ts('Pledge by'), ['create' => TRUE, 'api' => ['extra' => ['email']]], TRUE);
203 if ($this->_context
!= 'standalone') {
204 $contactField->freeze();
207 $showAdditionalInfo = FALSE;
208 $this->_formType
= $_GET['formType'] ??
NULL;
213 'Payment Reminders' => 'PaymentReminders',
215 foreach ($paneNames as $name => $type) {
216 $urlParams = "snippet=4&formType={$type}";
218 'url' => CRM_Utils_System
::url('civicrm/contact/view/pledge', $urlParams),
222 // see if we need to include this paneName in the current form
223 if ($this->_formType
== $type ||
!empty($_POST["hidden_{$type}"]) ||
224 CRM_Utils_Array
::value("hidden_{$type}", $defaults)
226 $showAdditionalInfo = TRUE;
227 $allPanes[$name]['open'] = 'true';
229 $fnName = "build{$type}";
230 CRM_Contribute_Form_AdditionalInfo
::$fnName($this);
233 $this->assign('allPanes', $allPanes);
234 $this->assign('showAdditionalInfo', $showAdditionalInfo);
236 if ($this->_formType
) {
237 $this->assign('formType', $this->_formType
);
241 $this->applyFilter('__ALL__', 'trim');
244 $attributes = CRM_Core_DAO
::getAttribute('CRM_Pledge_DAO_Pledge');
246 $this->assign('isPending', $this->_isPending
);
249 'onblur' => "calculatedPaymentAmount( );",
250 'onkeyup' => "calculatedPaymentAmount( );",
253 $amount = $this->addMoney('amount', ts('Total Pledge Amount'), TRUE,
254 array_merge($attributes['pledge_amount'], $js), TRUE,
255 'currency', NULL, $this->_id
&& !$this->_isPending
258 $installments = &$this->add('text', 'installments', ts('To be paid in'),
259 array_merge($attributes['installments'], $js), TRUE
261 $this->addRule('installments', ts('Please enter a valid number of installments.'), 'positiveInteger');
263 $frequencyInterval = $this->add('number', 'frequency_interval', ts('every'),
264 $attributes['pledge_frequency_interval'], TRUE
266 $this->addRule('frequency_interval', ts('Please enter a number for frequency (e.g. every "3" months).'), 'positiveInteger');
268 // Fix frequency unit display for use with frequency_interval
269 $freqUnitsDisplay = [];
270 foreach ($this->_freqUnits
as $val => $label) {
271 $freqUnitsDisplay[$val] = ts('%1(s)', [1 => $label]);
273 $frequencyUnit = $this->add('select', 'frequency_unit',
275 ['' => ts('- select -')] +
$freqUnitsDisplay,
279 $frequencyDay = $this->add('number', 'frequency_day', ts('Payments are due on the'), $attributes['frequency_day'], TRUE);
280 $this->addRule('frequency_day', ts('Please enter a valid payment due day.'), 'positiveInteger');
282 $this->add('text', 'eachPaymentAmount', ts('each'), [
284 'style' => "background-color:#EBECE4",
285 // WTF, preserved because its inexplicable
290 $createDate = $this->add('datepicker', 'create_date', ts('Pledge Made'), [], TRUE, ['time' => FALSE]);
291 $startDate = $this->add('datepicker', 'start_date', ts('Payments Start'), [], TRUE, ['time' => FALSE]);
293 if (!empty($this->_values
['currency'])) {
294 $this->assign('currency', $this->_values
['currency']);
296 elseif (!empty($this->_submitValues
['currency'])) {
297 $this->assign('currency', $this->_submitValues
['currency']);
300 if ($this->_id
&& !$this->_isPending
) {
302 $installments->freeze();
303 $createDate->freeze();
304 $startDate->freeze();
305 $frequencyInterval->freeze();
306 $frequencyUnit->freeze();
307 $frequencyDay->freeze();
308 $eachPaymentAmount = $this->_values
['original_installment_amount'];
309 $this->assign('eachPaymentAmount', $eachPaymentAmount);
312 if (CRM_Utils_Array
::value('status_id', $this->_values
) !=
313 CRM_Core_PseudoConstant
::getKey('CRM_Pledge_BAO_Pledge', 'status_id', 'Cancelled')
316 $this->addElement('checkbox', 'is_acknowledge', ts('Send Acknowledgment?'), NULL,
317 ['onclick' => "showHideByValue( 'is_acknowledge', '', 'acknowledgeDate', 'table-row', 'radio', true); showHideByValue( 'is_acknowledge', '', 'fromEmail', 'table-row', 'radio', false );"]
320 $this->add('select', 'from_email_address', ts('Receipt From'), $this->_fromEmails
);
323 $this->add('datepicker', 'acknowledge_date', ts('Acknowledgment Date'), [], FALSE, ['time' => FALSE]);
325 $this->add('select', 'financial_type_id',
326 ts('Financial Type'),
327 ['' => ts('- select -')] + CRM_Contribute_PseudoConstant
::financialType(),
331 // CRM-7362 --add campaigns.
332 CRM_Campaign_BAO_Campaign
::addCampaign($this, CRM_Utils_Array
::value('campaign_id', $this->_values
));
335 CRM_Core_DAO
::commonRetrieveAll('CRM_Pledge_DAO_PledgeBlock', 'entity_table',
336 'civicrm_contribution_page', $pageIds, ['entity_id']
338 $pages = CRM_Contribute_PseudoConstant
::contributionPage();
340 foreach ($pageIds as $key => $value) {
341 $pledgePages[$value['entity_id']] = $pages[$value['entity_id']];
343 $this->add('select', 'contribution_page_id', ts('Self-service Payments Page'),
344 ['' => ts('- select -')] +
$pledgePages
347 $mailingInfo = Civi
::settings()->get('mailing_backend');
348 $this->assign('outBound_option', $mailingInfo['outBound_option']);
351 CRM_Custom_Form_CustomData
::buildQuickForm($this);
353 // make this form an upload since we dont know if the custom data injected dynamically
354 // is of type file etc $uploadNames = $this->get( 'uploadNames' );
358 'name' => ts('Save'),
359 'js' => ['onclick' => "return verify( );"],
364 'name' => ts('Save and New'),
365 'js' => ['onclick' => "return verify( );"],
370 'name' => ts('Cancel'),
374 $this->addFormRule(['CRM_Pledge_Form_Pledge', 'formRule'], $this);
376 if ($this->_action
& CRM_Core_Action
::VIEW
) {
384 * @param array $fields
385 * The input form values.
386 * @param array $files
387 * The uploaded files if any.
392 * true if no errors, else array of errors
394 public static function formRule($fields, $files, $self) {
397 if ($fields['amount'] <= 0) {
398 $errors['amount'] = ts('Total Pledge Amount should be greater than zero.');
400 if ($fields['installments'] <= 0) {
401 $errors['installments'] = ts('Installments should be greater than zero.');
404 if ($fields['frequency_unit'] != 'week') {
405 if ($fields['frequency_day'] > 31 ||
$fields['frequency_day'] == 0) {
406 $errors['frequency_day'] = ts('Please enter a valid frequency day ie. 1 through 31.');
409 elseif ($fields['frequency_unit'] == 'week') {
410 if ($fields['frequency_day'] > 7 ||
$fields['frequency_day'] == 0) {
411 $errors['frequency_day'] = ts('Please enter a valid frequency day ie. 1 through 7.');
418 * Process the form submission.
420 public function postProcess() {
421 if ($this->_action
& CRM_Core_Action
::DELETE
) {
422 CRM_Pledge_BAO_Pledge
::deletePledge($this->_id
);
426 // get the submitted form values.
427 $formValues = $this->controller
->exportValues($this->_name
);
429 // set the contact, when contact is selected
430 if (!empty($formValues['contact_id'])) {
431 $this->_contactID
= $formValues['contact_id'];
434 $session = CRM_Core_Session
::singleton();
438 'frequency_interval',
442 'initial_reminder_day',
444 'additional_reminder_day',
445 'contribution_page_id',
448 foreach ($fields as $f) {
449 $params[$f] = $formValues[$f] ??
NULL;
453 $params['amount'] = CRM_Utils_Rule
::cleanMoney(CRM_Utils_Array
::value('amount', $formValues));
454 $params['currency'] = $formValues['currency'] ??
NULL;
455 $params['original_installment_amount'] = ($params['amount'] / $params['installments']);
457 $dates = ['create_date', 'start_date', 'acknowledge_date', 'cancel_date'];
458 foreach ($dates as $d) {
459 if ($this->_id
&& (!$this->_isPending
) && !empty($this->_values
[$d])) {
460 if ($d == 'start_date') {
461 $params['scheduled_date'] = CRM_Utils_Date
::processDate($this->_values
[$d]);
463 $params[$d] = CRM_Utils_Date
::processDate($this->_values
[$d]);
465 elseif (!empty($formValues[$d]) && !CRM_Utils_System
::isNull($formValues[$d])) {
466 if ($d == 'start_date') {
467 $params['scheduled_date'] = CRM_Utils_Date
::processDate($formValues[$d]);
469 $params[$d] = CRM_Utils_Date
::processDate($formValues[$d]);
472 $params[$d] = 'null';
476 if (!empty($formValues['is_acknowledge'])) {
477 $params['acknowledge_date'] = date('Y-m-d');
480 // assign id only in update mode
481 if ($this->_action
& CRM_Core_Action
::UPDATE
) {
482 $params['id'] = $this->_id
;
485 $params['contact_id'] = $this->_contactID
;
487 // format custom data
488 if (!empty($formValues['hidden_custom'])) {
489 $params['hidden_custom'] = 1;
491 $customFields = CRM_Core_BAO_CustomField
::getFields('Pledge');
492 $params['custom'] = CRM_Core_BAO_CustomField
::postProcess($formValues,
498 // handle pending pledge.
499 $params['is_pledge_pending'] = $this->_isPending
;
501 // create pledge record.
502 $pledge = CRM_Pledge_BAO_Pledge
::create($params);
507 // set the status msg.
508 if ($this->_action
& CRM_Core_Action
::ADD
) {
509 $statusMsg = ts('Pledge has been recorded and the payment schedule has been created.<br />');
511 elseif ($this->_action
& CRM_Core_Action
::UPDATE
) {
512 $statusMsg = ts('Pledge has been updated.<br />');
516 // handle Acknowledgment.
517 if (!empty($formValues['is_acknowledge']) && $pledge->id
) {
519 // calculate scheduled amount.
520 $params['scheduled_amount'] = round($params['amount'] / $params['installments']);
521 $params['total_pledge_amount'] = $params['amount'];
522 // get some required pledge values in params.
523 $params['id'] = $pledge->id
;
524 $params['acknowledge_date'] = $pledge->acknowledge_date
;
525 $params['is_test'] = $pledge->is_test
;
526 $params['currency'] = $pledge->currency
;
527 // retrieve 'from email id' for acknowledgement
528 $params['from_email_id'] = $formValues['from_email_address'];
530 $this->paymentId
= NULL;
531 // send Acknowledgment mail.
532 CRM_Pledge_BAO_Pledge
::sendAcknowledgment($this, $params);
534 $statusMsg .= ' ' . ts("An acknowledgment email has been sent to %1.<br />", [1 => $this->userEmail
]);
536 // build the payment urls.
537 if ($this->paymentId
) {
538 $urlParams = "reset=1&action=add&cid={$this->_contactID}&ppid={$this->paymentId}&context=pledge";
539 $contribURL = CRM_Utils_System
::url('civicrm/contact/view/contribution', $urlParams);
540 $urlParams .= "&mode=live";
541 $creditURL = CRM_Utils_System
::url('civicrm/contact/view/contribution', $urlParams);
543 // check if we can process credit card payment.
544 $processors = CRM_Core_PseudoConstant
::paymentProcessor(FALSE, FALSE,
545 "billing_mode IN ( 1, 3 )"
547 if (count($processors) > 0) {
548 $statusMsg .= ' ' . ts("If a payment is due now, you can record <a href='%1'>a check, EFT, or cash payment for this pledge</a> OR <a href='%2'>submit a credit card payment</a>.", [
554 $statusMsg .= ' ' . ts("If a payment is due now, you can record <a href='%1'>a check, EFT, or cash payment for this pledge</a>.", [1 => $contribURL]);
558 CRM_Core_Session
::setStatus($statusMsg, ts('Payment Due'), 'info');
560 $buttonName = $this->controller
->getButtonName();
561 if ($this->_context
== 'standalone') {
562 if ($buttonName == $this->getButtonName('upload', 'new')) {
563 $session->replaceUserContext(CRM_Utils_System
::url('civicrm/pledge/add',
564 'reset=1&action=add&context=standalone'
568 $session->replaceUserContext(CRM_Utils_System
::url('civicrm/contact/view',
569 "reset=1&cid={$this->_contactID}&selectedChild=pledge"
573 elseif ($buttonName == $this->getButtonName('upload', 'new')) {
574 $session->replaceUserContext(CRM_Utils_System
::url('civicrm/contact/view/pledge',
575 "reset=1&action=add&context=pledge&cid={$this->_contactID}"