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 +--------------------------------------------------------------------+
13 * Test APIv3 civicrm_contribute_* functions
15 * @package CiviCRM_APIv3
16 * @subpackage API_Contribution
19 class CRM_Contribute_Form_Task_PDFLetterCommonTest
extends CiviUnitTestCase
{
21 protected $_individualId;
25 protected $_contactIds;
28 * Count how many times the hookTokens is called.
30 * This only needs to be called once, check refactoring doesn't change this.
34 protected $hookTokensCalled = 0;
36 protected function setUp(): void
{
38 $this->_individualId
= $this->individualCreate(['first_name' => 'Anthony', 'last_name' => 'Collins']);
39 $this->_docTypes
= CRM_Core_SelectValues
::documentApplicationType();
43 * Clean up after each test.
45 * @throws \CRM_Core_Exception
47 public function tearDown(): void
{
48 $this->quickCleanUpFinancialEntities();
49 $this->quickCleanup(['civicrm_uf_match']);
50 CRM_Utils_Hook
::singleton()->reset();
54 * Test thank you send with grouping.
56 * @throws \CRM_Core_Exception
57 * @throws \CiviCRM_API3_Exception
59 public function testGroupedThankYous(): void
{
60 $this->ids
['Contact'][0] = $this->individualCreate();
61 $this->createLoggedInUser();
62 $contribution1ID = $this->callAPISuccess('Contribution', 'create', [
63 'contact_id' => $this->ids
['Contact'][0],
64 'total_amount' => '60',
65 'financial_type_id' => 'Donation',
67 'receive_date' => '2021-01-01 13:21',
69 $contribution2ID = $this->callAPISuccess('Contribution', 'create', [
70 'contact_id' => $this->ids
['Contact'][0],
71 'total_amount' => '70',
72 'financial_type_id' => 'Donation',
73 'receive_date' => '2021-02-01 2:21',
76 $form = $this->getFormObject('CRM_Contribute_Form_Task_PDFLetter', [
80 'paper_size' => 'letter',
81 'orientation' => 'portrait',
83 'margin_left' => '0.75',
84 'margin_right' => '0.75',
85 'margin_top' => '0.75',
86 'margin_bottom' => '0.75',
87 'document_type' => 'pdf',
88 'html_message' => '{contribution.currency} * {contribution.total_amount} * {contribution.receive_date}',
90 'saveTemplateName' => '',
91 'from_email_address' => '185',
92 'thankyou_update' => '1',
93 'group_by' => 'contact_id',
94 'group_by_separator' => 'comma',
95 'email_options' => '',
97 $this->setSearchSelection([$contribution1ID, $contribution2ID], $form);
100 $form->postProcess();
102 catch (CRM_Core_Exception_PrematureExitException
$e) {
103 $this->assertContains('USD, USD * $ 60.00, $ 70.00 * January 1st, 2021 1:21 PM, February 1st, 2021 2:21 AM', $e->errorData
['html']);
108 * Test the buildContributionArray function.
110 * @throws \CRM_Core_Exception|\CiviCRM_API3_Exception
112 public function testBuildContributionArray(): void
{
113 $this->_individualId
= $this->individualCreate();
115 $customGroup = $this->callAPISuccess('CustomGroup', 'create', [
116 'title' => 'Test Custom Set for Contribution',
117 'extends' => 'Contribution',
121 'custom_group_id' => $customGroup['id'],
122 'label' => 'Text field',
123 'html_type' => 'Text',
124 'data_type' => 'String',
128 $customField = $this->callAPISuccess('CustomField', 'create', $params);
129 $customFieldKey = 'custom_' . $customField['id'];
130 $campaignTitle = 'Test Campaign ';
133 'contact_id' => $this->_individualId
,
135 'campaign_id' => $this->campaignCreate(['title' => $campaignTitle], FALSE),
136 'financial_type_id' => 'Donation',
137 $customFieldKey => 'Text_' . substr(sha1(rand()), 0, 7),
139 $contributionIDs = $returnProperties = [];
140 $result = $this->callAPISuccess('Contribution', 'create', $params);
141 $contributionIDs[] = $result['id'];
142 $this->hookClass
->setHook('civicrm_tokenValues', [$this, 'hookTokenValues']);
144 // assume that there are two token {contribution.financial_type} and
145 // {contribution.custom_N} in message content
149 'payment_instrument',
155 $form = $this->getFormObject('CRM_Contribute_Form_Task_PDFLetter');
156 [$contributions, $contacts] = $form->buildContributionArray('contact_id', $contributionIDs, $returnProperties, TRUE, TRUE, $messageToken, 'test', '**', FALSE);
158 $this->assertEquals('Anthony', $contacts[$this->_individualId
]['first_name']);
159 $this->assertEquals('emo', $contacts[$this->_individualId
]['favourite_emoticon']);
160 $this->assertEquals('Donation', $contributions[$result['id']]['financial_type']);
161 $this->assertEquals($campaignTitle, $contributions[$result['id']]['campaign']);
162 $this->assertEquals('Check', $contributions[$result['id']]['payment_instrument']);
163 // CRM-20359: assert that contribution custom field token is rightfully replaced by its value
164 $this->assertEquals($params[$customFieldKey], $contributions[$result['id']][$customFieldKey]);
166 $this->customFieldDelete($customField['id']);
167 $this->customGroupDelete($customGroup['id']);
171 * Implement token values hook.
173 * @param array $details
174 * @param array $contactIDs
176 * @param array $tokens
177 * @param string $className
179 public function hookTokenValues(&$details, $contactIDs, $jobID, $tokens, $className): void
{
180 foreach ($details as $index => $detail) {
181 $details[$index]['favourite_emoticon'] = 'emo';
186 * Test contribution token replacement in
187 * html returned by postProcess function.
189 * @throws \CiviCRM_API3_Exception
190 * @throws \CRM_Core_Exception
192 public function testPostProcess(): void
{
193 $this->createLoggedInUser();
194 $this->_individualId
= $this->individualCreate();
195 foreach (['docx', 'odt'] as $docType) {
197 'is_unit_test' => TRUE,
200 'name' => __DIR__
. "/sample_documents/Template.$docType",
201 'type' => $this->_docTypes
[$docType],
205 $contributionParams = [
206 'contact_id' => $this->_individualId
,
207 'total_amount' => 100,
208 'financial_type_id' => 'Donation',
210 $contribution = $this->callAPISuccess('Contribution', 'create', $contributionParams);
211 $contributionId = $contribution['id'];
212 /* @var $form CRM_Contribute_Form_Task_PDFLetter */
213 $form = $this->getFormObject('CRM_Contribute_Form_Task_PDFLetter', $formValues);
214 $form->setContributionIds([$contributionId]);
215 $format = Civi
::settings()->get('dateformatFull');
216 $date = CRM_Utils_Date
::getToday();
217 $displayDate = CRM_Utils_Date
::customFormat($date, $format);
219 $html = $form->postProcess();
225 'Domain Name - Default Domain Name',
228 foreach ($expectedValues as $val) {
229 $this->assertTrue(strpos($html[$contributionId], $val) !== 0);
235 * Test assignment of variables when using the group by function.
237 * We are looking to see that the contribution aggregate and contributions
238 * arrays reflect the most recent contact rather than a total aggregate,
239 * since we are using group by.
241 * @throws \CiviCRM_API3_Exception
242 * @throws \CRM_Core_Exception
244 public function testPostProcessGroupByContact(): void
{
245 $this->createLoggedInUser();
246 $this->hookClass
->setHook('civicrm_tokenValues', [$this, 'hook_aggregateTokenValues']);
247 $this->hookClass
->setHook('civicrm_tokens', [$this, 'hook_tokens']);
248 $this->mut
= new CiviMailUtils($this, TRUE);
249 $this->_individualId
= $this->individualCreate();
250 $this->_individualId2
= $this->individualCreate();
251 $htmlMessage = "{aggregate.rendered_token}";
253 'is_unit_test' => TRUE,
254 'group_by' => 'contact_id',
255 'html_message' => $htmlMessage,
256 'email_options' => 'both',
257 'subject' => 'Testy test test',
258 'from' => 'info@example.com',
261 $contributionIDs = [];
262 $contribution = $this->callAPISuccess('Contribution', 'create', [
263 'contact_id' => $this->_individualId
,
264 'total_amount' => 100,
265 'financial_type_id' => 'Donation',
266 'receive_date' => '2016-12-25',
268 $contributionIDs[] = $contribution['id'];
269 $contribution = $this->callAPISuccess('Contribution', 'create', [
270 'contact_id' => $this->_individualId2
,
271 'total_amount' => 10,
272 'financial_type_id' => 'Donation',
273 'receive_date' => '2016-12-25',
275 $contributionIDs[] = $contribution['id'];
277 $contribution = $this->callAPISuccess('Contribution', 'create', [
278 'contact_id' => $this->_individualId2
,
280 'financial_type_id' => 'Donation',
281 'receive_date' => '2016-12-25',
283 $contributionIDs[] = $contribution['id'];
285 /* @var \CRM_Contribute_Form_Task_PDFLetter $form */
286 $form = $this->getFormObject('CRM_Contribute_Form_Task_PDFLetter', $formValues);
287 $form->setContributionIds($contributionIDs);
289 $html = $form->postProcess();
290 $this->assertEquals("<table border='1' cellpadding='2' cellspacing='0' class='table'>
295 <th>Financial Type</th>
301 <td>25 December 2016</td>
309 <td><strong>Total</strong></td>
310 <td><strong>$ 100.00</strong></td>
315 </table>", $html[1]);
316 $this->assertEquals("<table border='1' cellpadding='2' cellspacing='0' class='table'>
321 <th>Financial Type</th>
327 <td>25 December 2016</td>
335 <td>25 December 2016</td>
343 <td><strong>Total</strong></td>
344 <td><strong>$ 11.00</strong></td>
349 </table>", $html[2]);
351 $activities = $this->callAPISuccess('Activity', 'get', ['activity_type_id' => 'Print PDF Letter', 'sequential' => 1]);
352 $this->assertEquals(2, $activities['count']);
353 $this->assertEquals($html[1], $activities['values'][0]['details']);
354 $this->assertEquals($html[2], $activities['values'][1]['details']);
355 // Checking it is not called multiple times.
356 // once for each contact create + once for the activities.
357 // & once for the token processor, for now.
358 // By calling the cached function we can get this down to 1
359 $this->assertEquals(4, $this->hookTokensCalled
);
360 $this->mut
->checkAllMailLog($html);
365 * Implements civicrm_tokens().
367 public function hook_tokens(&$tokens) {
368 $this->hookTokensCalled++
;
369 $tokens['aggregate'] = ['rendered_token' => 'rendered_token'];
373 * Get the html message.
377 public function getHtmlMessage() {
378 return '{assign var=\'contact_aggregate\' value=0}
379 <table border=\'1\' cellpadding=\'2\' cellspacing=\'0\' class=\'table\'>
384 <th>Financial Type</th>
388 {foreach from=$contributions item=contribution}
389 {if $contribution.contact_id == $messageContactID}
390 {assign var=\'date\' value=$contribution.receive_date|date_format:\'%d %B %Y\'}
391 {assign var=contact_aggregate
392 value=$contact_aggregate+$contribution.total_amount}
396 <td>{$contribution.total_amount|crmMoney}</td>
397 <td>{$contribution.financial_type}</td>
405 <td><strong>Total</strong></td>
406 <td><strong>{$contact_aggregate|crmMoney}</strong></td>
415 * Implements CiviCRM hook.
417 * @param array $values
418 * @param array $contactIDs
420 * @param array $tokens
421 * @param null $context
423 public function hook_aggregateTokenValues(&$values, $contactIDs, $job = NULL, $tokens = [], $context = NULL) {
424 foreach ($contactIDs as $contactID) {
425 CRM_Core_Smarty
::singleton()->assign('messageContactID', $contactID);
426 $values[$contactID]['aggregate.rendered_token'] = CRM_Core_Smarty
::singleton()
427 ->fetch('string:' . $this->getHtmlMessage());
432 * @param string $token
433 * @param string $entity
434 * @param string $textToSearch
435 * @param bool $expected
437 * @dataProvider isHtmlTokenInTableCellProvider
439 public function testIsHtmlTokenInTableCell($token, $entity, $textToSearch, $expected) {
440 $this->assertEquals($expected,
441 CRM_Contribute_Form_Task_PDFLetter
::isHtmlTokenInTableCell($token, $entity, $textToSearch)
445 public function isHtmlTokenInTableCellProvider() {
451 '<td>{entity.token}</td>',
455 'simplest FALSE' => [
462 'token between two tables' => [
465 ' <table><tr><td>Top</td></tr></table>
467 <table><tr><td>Bottom</td></tr></table>',
471 'token in two tables' => [
474 ' <table><tr><td>{entity.token}</td></tr><tr><td>foo</td></tr></table>
475 <table><tr><td>{entity.token}</td></tr><tr><td>foo</td></tr></table>',
479 'token outside of table and inside of table' => [
483 <table><tr><td>{entity.token}</td></tr><tr><td>foo</td></tr></table>',
487 'token inside more complicated table' => [
490 ' <table><tr><td class="foo"><em>{entity.token}</em></td></tr></table>',
494 'token inside something that looks like table cell' => [
497 ' <tdata>{entity.token}</tdata>
498 <table><tr><td>Bottom</td></tr></table>',
506 * @param array $entities
507 * @param \CRM_Core_Form $form
509 protected function setSearchSelection(array $entities, CRM_Core_Form
$form): void
{
510 $_SESSION['_' . $form->controller
->_name
. '_container']['values']['Search'] = [
511 'radio_ts' => 'ts_sel',
513 foreach ($entities as $entityID) {
514 $_SESSION['_' . $form->controller
->_name
. '_container']['values']['Search']['mark_x_' . $entityID] = TRUE;