Merge pull request #15830 from eileenmcnaughton/dedupe4
[civicrm-core.git] / CRM / Pledge / Form / Pledge.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
5 | |
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 +--------------------------------------------------------------------+
10 */
11
12 /**
13 *
14 * @package CRM
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
16 */
17
18 /**
19 * This class generates form components for processing a pledge
20 */
21 class CRM_Pledge_Form_Pledge extends CRM_Core_Form {
22 public $_action;
23
24 /**
25 * The id of the pledge that we are proceessing.
26 *
27 * @var int
28 */
29 public $_id;
30
31 /**
32 * The id of the contact associated with this pledge.
33 *
34 * @var int
35 */
36 public $_contactID;
37
38 /**
39 * The Pledge values if an existing pledge.
40 * @var array
41 */
42 public $_values;
43
44 /**
45 * The Pledge frequency Units.
46 * @var array
47 */
48 public $_freqUnits;
49
50 /**
51 * Is current pledge pending.
52 * @var bool
53 */
54 public $_isPending = FALSE;
55
56 /**
57 * Set variables up before form is built.
58 */
59 public function preProcess() {
60 $this->_contactID = CRM_Utils_Request::retrieve('cid', 'Positive', $this);
61 $this->_action = CRM_Utils_Request::retrieve('action', 'String',
62 $this, FALSE, 'add'
63 );
64 $this->_id = CRM_Utils_Request::retrieve('id', 'Positive', $this);
65 $this->_context = CRM_Utils_Request::retrieve('context', 'Alphanumeric', $this);
66
67 // check for action permissions.
68 if (!CRM_Core_Permission::checkActionPermission('CiviPledge', $this->_action)) {
69 CRM_Core_Error::fatal(ts('You do not have permission to access this page.'));
70 }
71
72 $this->assign('action', $this->_action);
73 $this->assign('context', $this->_context);
74 if ($this->_action & CRM_Core_Action::DELETE) {
75 return;
76 }
77
78 $this->userDisplayName = $this->userEmail = NULL;
79 if ($this->_contactID) {
80 list($this->userDisplayName,
81 $this->userEmail
82 ) = CRM_Contact_BAO_Contact_Location::getEmailDetails($this->_contactID);
83 }
84
85 $this->setPageTitle(ts('Pledge'));
86
87 // build custom data
88 CRM_Custom_Form_CustomData::preProcess($this, NULL, NULL, 1, 'Pledge', $this->_id);
89 $this->_values = [];
90 // current pledge id
91 if ($this->_id) {
92 // get the contribution id
93 $this->_contributionID = CRM_Core_DAO::getFieldValue('CRM_Pledge_DAO_PledgePayment',
94 $this->_id, 'contribution_id', 'pledge_id'
95 );
96 $params = ['id' => $this->_id];
97 CRM_Pledge_BAO_Pledge::getValues($params, $this->_values);
98
99 $this->_isPending = (CRM_Pledge_BAO_Pledge::pledgeHasFinancialTransactions($this->_id, CRM_Utils_Array::value('status_id', $this->_values))) ? FALSE : TRUE;
100 }
101
102 // get the pledge frequency units.
103 $this->_freqUnits = CRM_Core_OptionGroup::values('recur_frequency_units');
104
105 $this->_fromEmails = CRM_Core_BAO_Email::getFromEmail();
106 }
107
108 /**
109 * Set default values for the form.
110 * The default values are retrieved from the database.
111 */
112 public function setDefaultValues() {
113 $defaults = $this->_values;
114
115 if ($this->_action & CRM_Core_Action::DELETE) {
116 return $defaults;
117 }
118
119 if (!empty($defaults['is_test'])) {
120 $this->assign('is_test', TRUE);
121 }
122
123 if ($this->_id) {
124 // check is this pledge pending.
125 // fix the display of the monetary value, CRM-4038.
126 if ($this->_isPending) {
127 $defaults['eachPaymentAmount'] = $this->_values['amount'] / $this->_values['installments'];
128 $defaults['eachPaymentAmount'] = CRM_Utils_Money::format($defaults['eachPaymentAmount'], NULL, '%a');
129 }
130
131 // fix the display of the monetary value, CRM-4038
132 if (isset($this->_values['amount'])) {
133 $defaults['amount'] = CRM_Utils_Money::format($this->_values['amount'], NULL, '%a');
134 }
135 $this->assign('amount', $this->_values['amount']);
136 $this->assign('installments', $defaults['installments']);
137 }
138 else {
139 if ($this->_contactID) {
140 $defaults['contact_id'] = $this->_contactID;
141 }
142 // default values.
143 $defaults['create_date'] = date('Y-m-d');
144 $defaults['start_date'] = date('Y-m-d');
145 $defaults['installments'] = 12;
146 $defaults['frequency_interval'] = 1;
147 $defaults['frequency_day'] = 1;
148 $defaults['initial_reminder_day'] = 5;
149 $defaults['max_reminders'] = 1;
150 $defaults['additional_reminder_day'] = 5;
151 $defaults['frequency_unit'] = array_search('month', $this->_freqUnits);
152 $defaults['financial_type_id'] = array_search('Donation', CRM_Contribute_PseudoConstant::financialType());
153 }
154
155 $pledgeStatus = CRM_Pledge_BAO_Pledge::buildOptions('status_id');
156 $pledgeStatusNames = CRM_Core_OptionGroup::values('pledge_status',
157 FALSE, FALSE, FALSE, NULL, 'name', TRUE
158 );
159 // get default status label (pending)
160 $defaultPledgeStatus = CRM_Utils_Array::value(array_search('Pending', $pledgeStatusNames),
161 $pledgeStatus
162 );
163
164 // assign status.
165 $this->assign('status', CRM_Utils_Array::value(CRM_Utils_Array::value('status_id', $this->_values),
166 $pledgeStatus,
167 $defaultPledgeStatus
168 ));
169
170 if (isset($this->userEmail)) {
171 $this->assign('email', $this->userEmail);
172 }
173
174 // custom data set defaults
175 $defaults += CRM_Custom_Form_CustomData::setDefaultValues($this);
176
177 return $defaults;
178 }
179
180 /**
181 * Build the form object.
182 */
183 public function buildQuickForm() {
184 if ($this->_action & CRM_Core_Action::DELETE) {
185 $this->addButtons([
186 [
187 'type' => 'next',
188 'name' => ts('Delete'),
189 'spacing' => '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;',
190 'isDefault' => TRUE,
191 ],
192 [
193 'type' => 'cancel',
194 'name' => ts('Cancel'),
195 ],
196 ]);
197 return;
198 }
199
200 $contactField = $this->addEntityRef('contact_id', ts('Contact'), ['create' => TRUE, 'api' => ['extra' => ['email']]], TRUE);
201 if ($this->_context != 'standalone') {
202 $contactField->freeze();
203 }
204
205 $showAdditionalInfo = FALSE;
206 $this->_formType = CRM_Utils_Array::value('formType', $_GET);
207
208 $defaults = [];
209
210 $paneNames = [
211 'Payment Reminders' => 'PaymentReminders',
212 ];
213 foreach ($paneNames as $name => $type) {
214 $urlParams = "snippet=4&formType={$type}";
215 $allPanes[$name] = [
216 'url' => CRM_Utils_System::url('civicrm/contact/view/pledge', $urlParams),
217 'open' => 'false',
218 'id' => $type,
219 ];
220 // see if we need to include this paneName in the current form
221 if ($this->_formType == $type || !empty($_POST["hidden_{$type}"]) ||
222 CRM_Utils_Array::value("hidden_{$type}", $defaults)
223 ) {
224 $showAdditionalInfo = TRUE;
225 $allPanes[$name]['open'] = 'true';
226 }
227 $fnName = "build{$type}";
228 CRM_Contribute_Form_AdditionalInfo::$fnName($this);
229 }
230
231 $this->assign('allPanes', $allPanes);
232 $this->assign('showAdditionalInfo', $showAdditionalInfo);
233
234 if ($this->_formType) {
235 $this->assign('formType', $this->_formType);
236 return;
237 }
238
239 $this->applyFilter('__ALL__', 'trim');
240
241 // pledge fields.
242 $attributes = CRM_Core_DAO::getAttribute('CRM_Pledge_DAO_Pledge');
243
244 $this->assign('isPending', $this->_isPending);
245
246 $js = [
247 'onblur' => "calculatedPaymentAmount( );",
248 'onkeyup' => "calculatedPaymentAmount( );",
249 ];
250
251 $amount = $this->addMoney('amount', ts('Total Pledge Amount'), TRUE,
252 array_merge($attributes['pledge_amount'], $js), TRUE,
253 'currency', NULL, $this->_id && !$this->_isPending
254 );
255
256 $installments = &$this->add('text', 'installments', ts('To be paid in'),
257 array_merge($attributes['installments'], $js), TRUE
258 );
259 $this->addRule('installments', ts('Please enter a valid number of installments.'), 'positiveInteger');
260
261 $frequencyInterval = $this->add('number', 'frequency_interval', ts('every'),
262 $attributes['pledge_frequency_interval'], TRUE
263 );
264 $this->addRule('frequency_interval', ts('Please enter a number for frequency (e.g. every "3" months).'), 'positiveInteger');
265
266 // Fix frequency unit display for use with frequency_interval
267 $freqUnitsDisplay = [];
268 foreach ($this->_freqUnits as $val => $label) {
269 $freqUnitsDisplay[$val] = ts('%1(s)', [1 => $label]);
270 }
271 $frequencyUnit = $this->add('select', 'frequency_unit',
272 ts('Frequency'),
273 ['' => ts('- select -')] + $freqUnitsDisplay,
274 TRUE
275 );
276
277 $frequencyDay = $this->add('number', 'frequency_day', ts('Payments are due on the'), $attributes['frequency_day'], TRUE);
278 $this->addRule('frequency_day', ts('Please enter a valid payment due day.'), 'positiveInteger');
279
280 $this->add('text', 'eachPaymentAmount', ts('each'), [
281 'size' => 10,
282 'style' => "background-color:#EBECE4",
283 // WTF, preserved because its inexplicable
284 0 => 'READONLY',
285 ]);
286
287 // add various dates
288 $createDate = $this->add('datepicker', 'create_date', ts('Pledge Made'), [], TRUE, ['time' => FALSE]);
289 $startDate = $this->add('datepicker', 'start_date', ts('Payments Start'), [], TRUE, ['time' => FALSE]);
290
291 if (!empty($this->_values['currency'])) {
292 $this->assign('currency', $this->_values['currency']);
293 }
294 elseif (!empty($this->_submitValues['currency'])) {
295 $this->assign('currency', $this->_submitValues['currency']);
296 }
297
298 if ($this->_id && !$this->_isPending) {
299 $amount->freeze();
300 $installments->freeze();
301 $createDate->freeze();
302 $startDate->freeze();
303 $frequencyInterval->freeze();
304 $frequencyUnit->freeze();
305 $frequencyDay->freeze();
306 $eachPaymentAmount = $this->_values['original_installment_amount'];
307 $this->assign('eachPaymentAmount', $eachPaymentAmount);
308 }
309
310 if (CRM_Utils_Array::value('status_id', $this->_values) !=
311 CRM_Core_PseudoConstant::getKey('CRM_Pledge_BAO_Pledge', 'status_id', 'Cancelled')
312 ) {
313
314 $this->addElement('checkbox', 'is_acknowledge', ts('Send Acknowledgment?'), NULL,
315 ['onclick' => "showHideByValue( 'is_acknowledge', '', 'acknowledgeDate', 'table-row', 'radio', true); showHideByValue( 'is_acknowledge', '', 'fromEmail', 'table-row', 'radio', false );"]
316 );
317
318 $this->add('select', 'from_email_address', ts('Receipt From'), $this->_fromEmails);
319 }
320
321 $this->add('datepicker', 'acknowledge_date', ts('Acknowledgment Date'), [], FALSE, ['time' => FALSE]);
322
323 $this->add('select', 'financial_type_id',
324 ts('Financial Type'),
325 ['' => ts('- select -')] + CRM_Contribute_PseudoConstant::financialType(),
326 TRUE
327 );
328
329 // CRM-7362 --add campaigns.
330 CRM_Campaign_BAO_Campaign::addCampaign($this, CRM_Utils_Array::value('campaign_id', $this->_values));
331
332 $pageIds = [];
333 CRM_Core_DAO::commonRetrieveAll('CRM_Pledge_DAO_PledgeBlock', 'entity_table',
334 'civicrm_contribution_page', $pageIds, ['entity_id']
335 );
336 $pages = CRM_Contribute_PseudoConstant::contributionPage();
337 $pledgePages = [];
338 foreach ($pageIds as $key => $value) {
339 $pledgePages[$value['entity_id']] = $pages[$value['entity_id']];
340 }
341 $this->add('select', 'contribution_page_id', ts('Self-service Payments Page'),
342 ['' => ts('- select -')] + $pledgePages
343 );
344
345 $mailingInfo = Civi::settings()->get('mailing_backend');
346 $this->assign('outBound_option', $mailingInfo['outBound_option']);
347
348 // build custom data
349 CRM_Custom_Form_CustomData::buildQuickForm($this);
350
351 // make this form an upload since we dont know if the custom data injected dynamically
352 // is of type file etc $uploadNames = $this->get( 'uploadNames' );
353 $this->addButtons([
354 [
355 'type' => 'upload',
356 'name' => ts('Save'),
357 'js' => ['onclick' => "return verify( );"],
358 'isDefault' => TRUE,
359 ],
360 [
361 'type' => 'upload',
362 'name' => ts('Save and New'),
363 'js' => ['onclick' => "return verify( );"],
364 'subName' => 'new',
365 ],
366 [
367 'type' => 'cancel',
368 'name' => ts('Cancel'),
369 ],
370 ]);
371
372 $this->addFormRule(['CRM_Pledge_Form_Pledge', 'formRule'], $this);
373
374 if ($this->_action & CRM_Core_Action::VIEW) {
375 $this->freeze();
376 }
377 }
378
379 /**
380 * Global form rule.
381 *
382 * @param array $fields
383 * The input form values.
384 * @param array $files
385 * The uploaded files if any.
386 * @param $self
387 *
388 *
389 * @return bool|array
390 * true if no errors, else array of errors
391 */
392 public static function formRule($fields, $files, $self) {
393 $errors = [];
394
395 if ($fields['amount'] <= 0) {
396 $errors['amount'] = ts('Total Pledge Amount should be greater than zero.');
397 }
398 if ($fields['installments'] <= 0) {
399 $errors['installments'] = ts('Installments should be greater than zero.');
400 }
401
402 if ($fields['frequency_unit'] != 'week') {
403 if ($fields['frequency_day'] > 31 || $fields['frequency_day'] == 0) {
404 $errors['frequency_day'] = ts('Please enter a valid frequency day ie. 1 through 31.');
405 }
406 }
407 elseif ($fields['frequency_unit'] == 'week') {
408 if ($fields['frequency_day'] > 7 || $fields['frequency_day'] == 0) {
409 $errors['frequency_day'] = ts('Please enter a valid frequency day ie. 1 through 7.');
410 }
411 }
412 return $errors;
413 }
414
415 /**
416 * Process the form submission.
417 */
418 public function postProcess() {
419 if ($this->_action & CRM_Core_Action::DELETE) {
420 CRM_Pledge_BAO_Pledge::deletePledge($this->_id);
421 return;
422 }
423
424 // get the submitted form values.
425 $formValues = $this->controller->exportValues($this->_name);
426
427 // set the contact, when contact is selected
428 if (!empty($formValues['contact_id'])) {
429 $this->_contactID = $formValues['contact_id'];
430 }
431
432 $session = CRM_Core_Session::singleton();
433
434 $fields = [
435 'frequency_unit',
436 'frequency_interval',
437 'frequency_day',
438 'installments',
439 'financial_type_id',
440 'initial_reminder_day',
441 'max_reminders',
442 'additional_reminder_day',
443 'contribution_page_id',
444 'campaign_id',
445 ];
446 foreach ($fields as $f) {
447 $params[$f] = CRM_Utils_Array::value($f, $formValues);
448 }
449
450 // format amount
451 $params['amount'] = CRM_Utils_Rule::cleanMoney(CRM_Utils_Array::value('amount', $formValues));
452 $params['currency'] = CRM_Utils_Array::value('currency', $formValues);
453 $params['original_installment_amount'] = ($params['amount'] / $params['installments']);
454
455 $dates = ['create_date', 'start_date', 'acknowledge_date', 'cancel_date'];
456 foreach ($dates as $d) {
457 if ($this->_id && (!$this->_isPending) && !empty($this->_values[$d])) {
458 if ($d == 'start_date') {
459 $params['scheduled_date'] = CRM_Utils_Date::processDate($this->_values[$d]);
460 }
461 $params[$d] = CRM_Utils_Date::processDate($this->_values[$d]);
462 }
463 elseif (!empty($formValues[$d]) && !CRM_Utils_System::isNull($formValues[$d])) {
464 if ($d == 'start_date') {
465 $params['scheduled_date'] = CRM_Utils_Date::processDate($formValues[$d]);
466 }
467 $params[$d] = CRM_Utils_Date::processDate($formValues[$d]);
468 }
469 else {
470 $params[$d] = 'null';
471 }
472 }
473
474 if (!empty($formValues['is_acknowledge'])) {
475 $params['acknowledge_date'] = date('Y-m-d');
476 }
477
478 // assign id only in update mode
479 if ($this->_action & CRM_Core_Action::UPDATE) {
480 $params['id'] = $this->_id;
481 }
482
483 $params['contact_id'] = $this->_contactID;
484
485 // format custom data
486 if (!empty($formValues['hidden_custom'])) {
487 $params['hidden_custom'] = 1;
488
489 $customFields = CRM_Core_BAO_CustomField::getFields('Pledge');
490 $params['custom'] = CRM_Core_BAO_CustomField::postProcess($formValues,
491 $this->_id,
492 'Pledge'
493 );
494 }
495
496 // handle pending pledge.
497 $params['is_pledge_pending'] = $this->_isPending;
498
499 // create pledge record.
500 $pledge = CRM_Pledge_BAO_Pledge::create($params);
501
502 $statusMsg = NULL;
503
504 if ($pledge->id) {
505 // set the status msg.
506 if ($this->_action & CRM_Core_Action::ADD) {
507 $statusMsg = ts('Pledge has been recorded and the payment schedule has been created.<br />');
508 }
509 elseif ($this->_action & CRM_Core_Action::UPDATE) {
510 $statusMsg = ts('Pledge has been updated.<br />');
511 }
512 }
513
514 // handle Acknowledgment.
515 if (!empty($formValues['is_acknowledge']) && $pledge->id) {
516
517 // calculate scheduled amount.
518 $params['scheduled_amount'] = round($params['amount'] / $params['installments']);
519 $params['total_pledge_amount'] = $params['amount'];
520 // get some required pledge values in params.
521 $params['id'] = $pledge->id;
522 $params['acknowledge_date'] = $pledge->acknowledge_date;
523 $params['is_test'] = $pledge->is_test;
524 $params['currency'] = $pledge->currency;
525 // retrieve 'from email id' for acknowledgement
526 $params['from_email_id'] = $formValues['from_email_address'];
527
528 $this->paymentId = NULL;
529 // send Acknowledgment mail.
530 CRM_Pledge_BAO_Pledge::sendAcknowledgment($this, $params);
531
532 $statusMsg .= ' ' . ts("An acknowledgment email has been sent to %1.<br />", [1 => $this->userEmail]);
533
534 // build the payment urls.
535 if ($this->paymentId) {
536 $urlParams = "reset=1&action=add&cid={$this->_contactID}&ppid={$this->paymentId}&context=pledge";
537 $contribURL = CRM_Utils_System::url('civicrm/contact/view/contribution', $urlParams);
538 $urlParams .= "&mode=live";
539 $creditURL = CRM_Utils_System::url('civicrm/contact/view/contribution', $urlParams);
540
541 // check if we can process credit card payment.
542 $processors = CRM_Core_PseudoConstant::paymentProcessor(FALSE, FALSE,
543 "billing_mode IN ( 1, 3 )"
544 );
545 if (count($processors) > 0) {
546 $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>.", [
547 1 => $contribURL,
548 2 => $creditURL,
549 ]);
550 }
551 else {
552 $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]);
553 }
554 }
555 }
556 CRM_Core_Session::setStatus($statusMsg, ts('Payment Due'), 'info');
557
558 $buttonName = $this->controller->getButtonName();
559 if ($this->_context == 'standalone') {
560 if ($buttonName == $this->getButtonName('upload', 'new')) {
561 $session->replaceUserContext(CRM_Utils_System::url('civicrm/pledge/add',
562 'reset=1&action=add&context=standalone'
563 ));
564 }
565 else {
566 $session->replaceUserContext(CRM_Utils_System::url('civicrm/contact/view',
567 "reset=1&cid={$this->_contactID}&selectedChild=pledge"
568 ));
569 }
570 }
571 elseif ($buttonName == $this->getButtonName('upload', 'new')) {
572 $session->replaceUserContext(CRM_Utils_System::url('civicrm/contact/view/pledge',
573 "reset=1&action=add&context=pledge&cid={$this->_contactID}"
574 ));
575 }
576 }
577
578 }