Merge pull request #4860 from totten/master-ext-cache
[civicrm-core.git] / tests / phpunit / api / v3 / TaxContributionPageTest.php
1 <?php
2
3 /*
4 +--------------------------------------------------------------------+
5 | CiviCRM version 4.6 |
6 +--------------------------------------------------------------------+
7 | Copyright CiviCRM LLC (c) 2004-2014 |
8 +--------------------------------------------------------------------+
9 | This file is a part of CiviCRM. |
10 | |
11 | CiviCRM is free software; you can copy, modify, and distribute it |
12 | under the terms of the GNU Affero General Public License |
13 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
14 | |
15 | CiviCRM is distributed in the hope that it will be useful, but |
16 | WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
18 | See the GNU Affero General Public License for more details. |
19 | |
20 | You should have received a copy of the GNU Affero General Public |
21 | License and the CiviCRM Licensing Exception along |
22 | with this program; if not, contact CiviCRM LLC |
23 | at info[AT]civicrm[DOT]org. If you have questions about the |
24 | GNU Affero General Public License or the licensing of CiviCRM, |
25 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
26 +--------------------------------------------------------------------+
27 */
28
29 require_once 'CiviTest/CiviUnitTestCase.php';
30
31 /**
32 * Class api_v3_TaxContributionPageTest
33 */
34 class api_v3_TaxContributionPageTest extends CiviUnitTestCase {
35 protected $_apiversion = 3;
36 protected $params;
37 protected $financialtypeID;
38 protected $financialAccountId;
39 protected $_entity = 'contribution_page';
40 protected $_priceSetParams = array();
41 protected $_paymentProcessorType;
42 protected $payParams = array();
43 protected $paymentProceParams = array();
44 protected $settingValue = array();
45 protected $setInvoiceSettings;
46 protected $_ids = array();
47 protected $_individualId;
48 protected $financialAccHalftax;
49 protected $financialtypeHalftax;
50 protected $financialRelationHalftax;
51 protected $halfFinancialAccId;
52 protected $halfFinancialTypeId;
53 public $DBResetRequired = TRUE;
54
55 public function setUp() {
56 parent::setUp();
57 $this->_individualId = $this->individualCreate();
58 $this->_orgId = $this->organizationCreate(NULL);
59
60 $this->params = array(
61 'title' => "Test Contribution Page" . substr(sha1(rand()), 0, 7),
62 'financial_type_id' => 1,
63 'payment_processor' => 1,
64 'currency' => 'NZD',
65 'goal_amount' => 350,
66 'is_pay_later' => 1,
67 'pay_later_text' => 'I will pay later',
68 'pay_later_receipt' => "I will pay later",
69 'is_monetary' => TRUE,
70 'is_billing_required' => TRUE
71 );
72
73 $this->_priceSetParams = array(
74 'name' => 'tax_contribution' . substr(sha1(rand()), 0, 7),
75 'title' => 'contributiontax' . substr(sha1(rand()), 0, 7),
76 'is_active' => 1,
77 'help_pre' => "Where does your goat sleep",
78 'help_post' => "thank you for your time",
79 'extends' => 2,
80 'financial_type_id' => 3,
81 'is_quick_config' => 0,
82 'is_reserved' => 0
83 );
84 // Financial Account with 20% tax rate
85 $financialAccountSetparams = array(
86 #[domain_id] =>
87 'name' => 'vat full taxrate account' . substr(sha1(rand()), 0, 7),
88 'contact_id' => $this->_orgId,
89 'financial_account_type_id' => 2,
90 'is_tax' => 1,
91 'tax_rate' => 20.00,
92 'is_reserved' => 0,
93 'is_active' => 1,
94 'is_default' => 0,
95 );
96
97 $financialAccount = $this->callAPISuccess('financial_account', 'create', $financialAccountSetparams);
98 $this->financialAccountId = $financialAccount['id'];
99
100 // Financial type having 'Sales Tax Account is' with liability financail account
101 $financialType = array(
102 'name' => 'grassvariety1' . substr(sha1(rand()), 0, 7),
103 'is_reserved' => 0,
104 'is_active' => 1,
105 );
106 $priceField = $this->callAPISuccess('financial_type', 'create', $financialType);
107 $this->financialtypeID = $priceField['id'];
108 $financialRelationParams = array(
109 'entity_table' => 'civicrm_financial_type',
110 'entity_id' => $this->financialtypeID,
111 'account_relationship' => 10,
112 'financial_account_id' => $this->financialAccountId
113 );
114 $financialRelation = CRM_Financial_BAO_FinancialTypeAccount::add($financialRelationParams);
115
116 // Financial type with 5% tax rate
117 $financialAccHalftax = array(
118 'name' => 'vat half taxrate account' . substr(sha1(rand()), 0, 7),
119 'contact_id' => $this->_orgId,
120 'financial_account_type_id' => 2,
121 'is_tax' => 1,
122 'tax_rate' => 5.00,
123 'is_reserved' => 0,
124 'is_active' => 1,
125 'is_default' => 0
126 );
127 $halfFinancialAccount = CRM_Financial_BAO_FinancialAccount::add($financialAccHalftax);
128 $this->halfFinancialAccId = $halfFinancialAccount->id;
129 $halfFinancialtypeHalftax = array(
130 'name' => 'grassvariety2' . substr(sha1(rand()), 0, 7),
131 'is_reserved' => 0,
132 'is_active' => 1,
133 );
134
135 $halfFinancialType = CRM_Financial_BAO_FinancialType::add($halfFinancialtypeHalftax);
136 $this->halfFinancialTypeId = $halfFinancialType->id;
137 $financialRelationHalftax = array(
138 'entity_table' => 'civicrm_financial_type',
139 'entity_id' => $this->halfFinancialTypeId,
140 'account_relationship' => 10,
141 'financial_account_id' => $this->halfFinancialAccId
142 );
143
144 $halfFinancialRelation = CRM_Financial_BAO_FinancialTypeAccount::add($financialRelationHalftax);
145
146 // Enable component contribute setting
147 $contributeSetting = array(
148 'invoicing' => 1,
149 'invoice_prefix' => 'INV_',
150 'credit_notes_prefix' => 'CN_',
151 'due_date' => 10,
152 'due_date_period' => 'days',
153 'notes' => '',
154 'is_email_pdf' => 1,
155 'tax_term' => 'Sales Tax',
156 'tax_display_settings' => 'Inclusive',
157 );
158 $setInvoiceSettings = CRM_Core_BAO_Setting::setItem($contributeSetting, CRM_Core_BAO_Setting::CONTRIBUTE_PREFERENCES_NAME, 'contribution_invoice_settings');
159
160 // Payment Processor
161 $paymentProceParams = array(
162 'domain_id' => 1,
163 'name' => 'dummy' . substr(sha1(rand()), 0, 7),
164 'payment_processor_type_id' => 10,
165 'financial_account_id' => 12,
166 'is_active' => 1,
167 'is_default' => 1,
168 'is_default' => 1,
169 'user_name' => 'dummy',
170 'url_site' => 'http://dummy.com',
171 'url_recur' => 'http://dummyrecur.com',
172 'class_name' => 'Payment_Dummy',
173 'billing_mode' => 1,
174 'is_recur' => 1,
175 'payment_type' => 1
176 );
177 $result = $this->callAPISuccess('payment_processor', 'create', $paymentProceParams);
178 $this->_ids['paymentProcessID'] = $result['id'];
179 require_once 'api/v3/examples/PaymentProcessor/Create.php';
180 $this->assertAPISuccess($result);
181 }
182
183 public function tearDown() {
184 $this->quickCleanup(array(
185 'civicrm_contribution',
186 'civicrm_contribution_soft',
187 'civicrm_event',
188 'civicrm_contribution_page',
189 'civicrm_participant',
190 'civicrm_participant_payment',
191 'civicrm_line_item',
192 'civicrm_financial_trxn',
193 'civicrm_financial_item',
194 'civicrm_entity_financial_trxn',
195 'civicrm_contact',
196 'civicrm_membership',
197 'civicrm_membership_payment',
198 'civicrm_payment_processor'
199 ));
200 CRM_Core_PseudoConstant::flush('taxRates');
201 }
202
203 public function setUpContributionPage() {
204 $contributionPageResult = $this->callAPISuccess($this->_entity, 'create', $this->params);
205 if (empty($this->_ids['price_set'])) {
206 $priceSet = $this->callAPISuccess('price_set', 'create', $this->_priceSetParams);
207 $this->_ids['price_set'][] = $priceSet['id'];
208 }
209 $priceSetID = $this->_price = reset($this->_ids['price_set']);
210 CRM_Price_BAO_PriceSet::addTo('civicrm_contribution_page', $contributionPageResult['id'], $priceSetID);
211
212 if (empty($this->_ids['price_field'])) {
213 $priceField = $this->callAPISuccess('price_field', 'create', array(
214 'price_set_id' => $priceSetID,
215 'label' => 'Goat Breed',
216 'html_type' => 'Radio',
217 ));
218 $this->_ids['price_field'] = array($priceField['id']);
219 }
220 if (empty($this->_ids['price_field_value'])) {
221 $this->callAPISuccess('price_field_value', 'create', array(
222 'price_set_id' => $priceSetID,
223 'price_field_id' => $priceField['id'],
224 'label' => 'Long Haired Goat',
225 'amount' => 100,
226 'financial_type_id' => $this->financialtypeID
227 ));
228 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', array(
229 'price_set_id' => $priceSetID,
230 'price_field_id' => $priceField['id'],
231 'label' => 'Shoe-eating Goat',
232 'amount' => 300,
233 'financial_type_id' => $this->halfFinancialTypeId
234 ));
235 $this->_ids['price_field_value'] = array($priceFieldValue['id']);
236 }
237 $this->_ids['contribution_page'] = $contributionPageResult['id'];
238 }
239
240 /**
241 * Online and offline contrbution from above created contrbution page
242 */
243 public function testCreateContributionOnline() {
244 $this->setUpContributionPage();
245 $params = array(
246 'contact_id' => $this->_individualId,
247 'receive_date' => '20120511',
248 'total_amount' => 100.00,
249 'financial_type_id' => $this->financialtypeID,
250 'contribution_page_id' => $this->_ids['contribution_page'],
251 'payment_processor' => $this->_ids['paymentProcessID'],
252 'trxn_id' => 12345,
253 'invoice_id' => 67890,
254 'source' => 'SSF',
255 'contribution_status_id' => 1
256 );
257
258 $contribution = $this->callAPIAndDocument('contribution', 'create', $params, __FUNCTION__, __FILE__);
259 $this->_ids['contributionId'] = $contribution['id'];
260 $this->assertEquals($contribution['values'][$contribution['id']]['contact_id'], $this->_individualId, 'In line ' . __LINE__);
261 $this->assertEquals($contribution['values'][$contribution['id']]['total_amount'], 120.00, 'In line ' . __LINE__);
262 $this->assertEquals($contribution['values'][$contribution['id']]['financial_type_id'], $this->financialtypeID, 'In line ' . __LINE__);
263 $this->assertEquals($contribution['values'][$contribution['id']]['trxn_id'], 12345, 'In line ' . __LINE__);
264 $this->assertEquals($contribution['values'][$contribution['id']]['invoice_id'], 67890, 'In line ' . __LINE__);
265 $this->assertEquals($contribution['values'][$contribution['id']]['source'], 'SSF', 'In line ' . __LINE__);
266 $this->assertEquals($contribution['values'][$contribution['id']]['tax_amount'], 20, 'In line ' . __LINE__);
267 $this->assertEquals($contribution['values'][$contribution['id']]['contribution_status_id'], 1, 'In line ' . __LINE__);
268 $this->_checkFinancialRecords($contribution, 'online');
269 }
270
271 public function testCreateContributionChainedLineItems() {
272 $this->setUpContributionPage();
273 $params = array(
274 'contact_id' => $this->_individualId,
275 'receive_date' => '20120511',
276 'total_amount' => 400.00,
277 'financial_type_id' => $this->financialtypeID,
278 'trxn_id' => 12345,
279 'invoice_id' => 67890,
280 'source' => 'SSF',
281 'contribution_status_id' => 1,
282 'skipLineItem' => 1,
283 'api.line_item.create' => array(
284 array(
285 'price_field_id' => $this->_ids['price_field'],
286 'qty' => 1,
287 'line_total' => '100',
288 'unit_price' => '100',
289 'financial_type_id' => $this->financialtypeID,
290 ),
291 array(
292 'price_field_id' => $this->_ids['price_field'],
293 'qty' => 1,
294 'line_total' => '300',
295 'unit_price' => '300',
296 'financial_type_id' => $this->halfFinancialTypeId,
297 ),
298 ),
299 );
300
301 $description = "Create Contribution with Nested Line Items";
302 $subfile = "CreateWithNestedLineItems";
303 $contribution = $this->callAPIAndDocument('contribution', 'create', $params, __FUNCTION__, __FILE__, $description, $subfile);
304
305 $lineItems = $this->callAPISuccess('line_item', 'get', array(
306 'entity_id' => $contribution['id'],
307 'contribution_id' => $contribution['id'],
308 'entity_table' => 'civicrm_contribution',
309 'sequential' => 1,
310 ));
311 $this->assertEquals(2, $lineItems['count']);
312 }
313
314 public function testCreateContributionPayLaterOnline() {
315 $this->setUpContributionPage();
316 $params = array(
317 'contact_id' => $this->_individualId,
318 'receive_date' => '20120511',
319 'total_amount' => 100.00,
320 'financial_type_id' => $this->financialtypeID,
321 'contribution_page_id' => $this->_ids['contribution_page'],
322 'trxn_id' => 12345,
323 'is_pay_later' => 1,
324 'invoice_id' => 67890,
325 'source' => 'SSF',
326 'contribution_status_id' => 2,
327 );
328 $contribution = $this->callAPIAndDocument('contribution', 'create', $params, __FUNCTION__, __FILE__);
329 $this->assertEquals($contribution['values'][$contribution['id']]['contact_id'], $this->_individualId, 'In line ' . __LINE__);
330 $this->assertEquals($contribution['values'][$contribution['id']]['total_amount'], 120.00, 'In line ' . __LINE__);
331 $this->assertEquals($contribution['values'][$contribution['id']]['financial_type_id'], $this->financialtypeID, 'In line ' . __LINE__);
332 $this->assertEquals($contribution['values'][$contribution['id']]['trxn_id'], 12345, 'In line ' . __LINE__);
333 $this->assertEquals($contribution['values'][$contribution['id']]['invoice_id'], 67890, 'In line ' . __LINE__);
334 $this->assertEquals($contribution['values'][$contribution['id']]['source'], 'SSF', 'In line ' . __LINE__);
335 $this->assertEquals($contribution['values'][$contribution['id']]['tax_amount'], 20, 'In line ' . __LINE__);
336 $this->assertEquals($contribution['values'][$contribution['id']]['contribution_status_id'], 2, 'In line ' . __LINE__);
337 $this->_checkFinancialRecords($contribution, 'payLater');
338 }
339 public function testCreateContributionPendingOnline() {
340 $this->setUpContributionPage();
341 $params = array(
342 'contact_id' => $this->_individualId,
343 'receive_date' => '20120511',
344 'total_amount' => 100.00,
345 'financial_type_id' => $this->financialtypeID,
346 'contribution_page_id' => $this->_ids['contribution_page'],
347 'trxn_id' => 12345,
348 'invoice_id' => 67890,
349 'source' => 'SSF',
350 'contribution_status_id' => 2,
351 );
352
353 $contribution = $this->callAPIAndDocument('contribution', 'create', $params, __FUNCTION__, __FILE__);
354 $this->assertEquals($contribution['values'][$contribution['id']]['contact_id'], $this->_individualId, 'In line ' . __LINE__);
355 $this->assertEquals($contribution['values'][$contribution['id']]['total_amount'], 120.00, 'In line ' . __LINE__);
356 $this->assertEquals($contribution['values'][$contribution['id']]['financial_type_id'], $this->financialtypeID, 'In line ' . __LINE__);
357 $this->assertEquals($contribution['values'][$contribution['id']]['trxn_id'], 12345, 'In line ' . __LINE__);
358 $this->assertEquals($contribution['values'][$contribution['id']]['invoice_id'], 67890, 'In line ' . __LINE__);
359 $this->assertEquals($contribution['values'][$contribution['id']]['source'], 'SSF', 'In line ' . __LINE__);
360 $this->assertEquals($contribution['values'][$contribution['id']]['tax_amount'], 20, 'In line ' . __LINE__);
361 $this->assertEquals($contribution['values'][$contribution['id']]['contribution_status_id'], 2, 'In line ' . __LINE__);
362 $this->_checkFinancialRecords($contribution, 'pending');
363 }
364
365 /*
366 * Updation of contrbution
367 * Function tests that line items, financial records are updated when contribution amount is changed
368 */
369 public function testCreateUpdateContributionChangeTotal() {
370 $this->setUpContributionPage();
371 $this->contributionParams = array(
372 'contact_id' => $this->_individualId,
373 'receive_date' => '20120511',
374 'total_amount' => 100.00,
375 'financial_type_id' => $this->financialtypeID,
376 'source' => 'SSF',
377 'contribution_status_id' => 1,
378 );
379 $contribution = $this->callAPISuccess('contribution', 'create', $this->contributionParams);
380 $lineItems = $this->callAPISuccess('line_item', 'getvalue', array(
381 'entity_id' => $contribution['id'],
382 'entity_table' => 'civicrm_contribution',
383 'sequential' => 1,
384 'return' => 'line_total',
385 ));
386 $this->assertEquals('100.00', $lineItems);
387 $trxnAmount = $this->_getFinancialTrxnAmount($contribution['id']);
388 $this->assertEquals('120.00', $trxnAmount);
389 $newParams = array(
390 'id' => $contribution['id'],
391 'financial_type_id' => 1, // without tax rate i.e Donation
392 'total_amount' => '300'
393 );
394 $contribution = $this->callAPISuccess('contribution', 'update', $newParams);
395
396 $lineItems = $this->callAPISuccess('line_item', 'getvalue', array(
397 'entity_id' => $contribution['id'],
398 'entity_table' => 'civicrm_contribution',
399 'sequential' => 1,
400 'return' => 'line_total',
401 ));
402
403 $this->assertEquals('300.00', $lineItems);
404 $trxnAmount = $this->_getFinancialTrxnAmount($contribution['id']);
405 $fitemAmount = $this->_getFinancialItemAmount($contribution['id']);
406 $this->assertEquals('300.00', $trxnAmount);
407 $this->assertEquals('300.00', $fitemAmount);
408 }
409
410 /**
411 * @param int $contId
412 *
413 * @return null|string
414 */
415 public function _getFinancialTrxnAmount($contId) {
416 $query = "SELECT
417 SUM( ft.total_amount ) AS total
418 FROM civicrm_financial_trxn AS ft
419 LEFT JOIN civicrm_entity_financial_trxn AS ceft ON ft.id = ceft.financial_trxn_id
420 WHERE ceft.entity_table = 'civicrm_contribution'
421 AND ceft.entity_id = {$contId}";
422 $result = CRM_Core_DAO::singleValueQuery($query);
423 return $result;
424 }
425
426 /**
427 * @param int $contId
428 *
429 * @return null|string
430 */
431 public function _getFinancialItemAmount($contId) {
432 $lineItem = key(CRM_Price_BAO_LineItem::getLineItems($contId, 'contribution'));
433 $query = "SELECT
434 SUM(amount)
435 FROM civicrm_financial_item
436 WHERE entity_table = 'civicrm_line_item'
437 AND entity_id = {$lineItem}";
438 $result = CRM_Core_DAO::singleValueQuery($query);
439 return $result;
440 }
441
442 /**
443 * @param array $params
444 * @param $context
445 */
446 public function _checkFinancialRecords($params, $context) {
447 $entityParams = array(
448 'entity_id' => $params['id'],
449 'entity_table' => 'civicrm_contribution',
450 );
451 if ($context == 'pending') {
452 $trxn = CRM_Financial_BAO_FinancialItem::retrieveEntityFinancialTrxn($entityParams);
453 $this->assertNull($trxn, 'No Trxn to be created until IPN callback');
454 return;
455 }
456 $trxn = current(CRM_Financial_BAO_FinancialItem::retrieveEntityFinancialTrxn($entityParams));
457 $trxnParams = array(
458 'id' => $trxn['financial_trxn_id'],
459 );
460 if ($context != 'online' && $context != 'payLater') {
461 $compareParams = array(
462 'to_financial_account_id' => 6,
463 'total_amount' => 120,
464 'status_id' => 1,
465 );
466 }
467 if ($context == 'online') {
468 $compareParams = array(
469 'to_financial_account_id' => 12,
470 'total_amount' => 120,
471 'status_id' => 1,
472 );
473 }
474 elseif ($context == 'payLater') {
475 $compareParams = array(
476 'to_financial_account_id' => 7,
477 'total_amount' => 120,
478 'status_id' => 2,
479 );
480 }
481 $this->assertDBCompareValues('CRM_Financial_DAO_FinancialTrxn', $trxnParams, $compareParams);
482 $entityParams = array(
483 'financial_trxn_id' => $trxn['financial_trxn_id'],
484 'entity_table' => 'civicrm_financial_item',
485 );
486 $entityTrxn = current(CRM_Financial_BAO_FinancialItem::retrieveEntityFinancialTrxn($entityParams));
487 $fitemParams = array(
488 'id' => $entityTrxn['entity_id'],
489 );
490 $compareParams = array(
491 'amount' => 100,
492 'status_id' => 1,
493 'financial_account_id' => $this->_getFinancialAccountId($this->financialtypeID),
494 );
495 if ($context == 'payLater') {
496 $compareParams = array(
497 'amount' => 100,
498 'status_id' => 3,
499 'financial_account_id' => $this->_getFinancialAccountId($this->financialtypeID),
500 );
501 }
502 $this->assertDBCompareValues('CRM_Financial_DAO_FinancialItem', $fitemParams, $compareParams);
503 }
504
505 /**
506 * @param array $params
507 * @param int $financialTypeId
508 */
509 public function _getFinancialAccountId($financialTypeId) {
510 $accountRel = key(CRM_Core_PseudoConstant::accountOptionValues('account_relationship', NULL, " AND v.name LIKE 'Income Account is' "));
511
512 $searchParams = array(
513 'entity_table' => 'civicrm_financial_type',
514 'entity_id' => $financialTypeId,
515 'account_relationship' => $accountRel,
516 );
517
518 $result = array();
519 CRM_Financial_BAO_FinancialTypeAccount::retrieve($searchParams, $result);
520 return CRM_Utils_Array::value('financial_account_id', $result);
521 }
522
523 ///////////////// civicrm_contribution_delete methods
524 public function testDeleteContribution() {
525 $contributionID = $this->contributionCreate($this->_individualId, $this->financialtypeID, 'dfsdf', 12389);
526 $params = array(
527 'id' => $contributionID,
528 );
529 $this->callAPIAndDocument('contribution', 'delete', $params, __FUNCTION__, __FILE__);
530 }
531 }