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 * Class api_v3_TaxContributionPageTest
16 class api_v3_TaxContributionPageTest
extends CiviUnitTestCase
{
17 protected $_apiversion = 3;
19 protected $financialtypeID;
20 protected $financialAccountId;
21 protected $_entity = 'contribution_page';
22 protected $_priceSetParams = [];
23 protected $_paymentProcessorType;
24 protected $payParams = [];
25 protected $paymentProceParams = [];
26 protected $settingValue = [];
27 protected $setInvoiceSettings;
29 protected $_individualId;
30 protected $financialAccHalftax;
31 protected $financialtypeHalftax;
32 protected $financialRelationHalftax;
33 protected $halfFinancialAccId;
34 protected $halfFinancialTypeId;
35 public $DBResetRequired = TRUE;
37 public function setUp() {
39 $this->_individualId
= $this->individualCreate();
40 $this->_orgId
= $this->organizationCreate(NULL);
43 'title' => "Test Contribution Page" . substr(sha1(rand()), 0, 7),
44 'financial_type_id' => 1,
45 'payment_processor' => 1,
49 'pay_later_text' => 'I will pay later',
50 'pay_later_receipt' => "I will pay later",
51 'is_monetary' => TRUE,
52 'is_billing_required' => TRUE,
55 $this->_priceSetParams
= [
56 'name' => 'tax_contribution' . substr(sha1(rand()), 0, 7),
57 'title' => 'contributiontax' . substr(sha1(rand()), 0, 7),
59 'help_pre' => "Where does your goat sleep",
60 'help_post' => "thank you for your time",
62 'financial_type_id' => 3,
63 'is_quick_config' => 0,
66 // Financial Account with 20% tax rate
67 $financialAccountSetparams = [
69 'name' => 'vat full taxrate account' . substr(sha1(rand()), 0, 7),
70 'contact_id' => $this->_orgId
,
71 'financial_account_type_id' => 2,
79 $financialAccount = $this->callAPISuccess('financial_account', 'create', $financialAccountSetparams);
80 $this->financialAccountId
= $financialAccount['id'];
82 // Financial type having 'Sales Tax Account is' with liability financail account
84 'name' => 'grassvariety1' . substr(sha1(rand()), 0, 7),
88 $priceField = $this->callAPISuccess('financial_type', 'create', $financialType);
89 $this->financialtypeID
= $priceField['id'];
90 $financialRelationParams = [
91 'entity_table' => 'civicrm_financial_type',
92 'entity_id' => $this->financialtypeID
,
93 'account_relationship' => 10,
94 'financial_account_id' => $this->financialAccountId
,
96 $financialRelation = CRM_Financial_BAO_FinancialTypeAccount
::add($financialRelationParams);
98 // Financial type with 5% tax rate
99 $financialAccHalftax = [
100 'name' => 'vat half taxrate account' . substr(sha1(rand()), 0, 7),
101 'contact_id' => $this->_orgId
,
102 'financial_account_type_id' => 2,
109 $halfFinancialAccount = CRM_Financial_BAO_FinancialAccount
::add($financialAccHalftax);
110 $this->halfFinancialAccId
= $halfFinancialAccount->id
;
111 $halfFinancialtypeHalftax = [
112 'name' => 'grassvariety2' . substr(sha1(rand()), 0, 7),
117 $halfFinancialType = CRM_Financial_BAO_FinancialType
::add($halfFinancialtypeHalftax);
118 $this->halfFinancialTypeId
= $halfFinancialType->id
;
119 $financialRelationHalftax = [
120 'entity_table' => 'civicrm_financial_type',
121 'entity_id' => $this->halfFinancialTypeId
,
122 'account_relationship' => 10,
123 'financial_account_id' => $this->halfFinancialAccId
,
126 $halfFinancialRelation = CRM_Financial_BAO_FinancialTypeAccount
::add($financialRelationHalftax);
128 // Enable component contribute setting
129 $setInvoiceSettings = $this->enableTaxAndInvoicing();
132 $paymentProceParams = [
134 'name' => 'dummy' . substr(sha1(rand()), 0, 7),
135 'payment_processor_type_id' => CRM_Core_PseudoConstant
::getKey('CRM_Financial_BAO_PaymentProcessor', 'payment_processor_type_id', 'Dummy'),
136 'financial_account_id' => 12,
139 'user_name' => 'dummy',
140 'url_site' => 'http://dummy.com',
141 'url_recur' => 'http://dummyrecur.com',
142 'class_name' => 'Payment_Dummy',
147 $result = $this->callAPISuccess('payment_processor', 'create', $paymentProceParams);
148 $this->_ids
['paymentProcessID'] = $result['id'];
149 require_once 'api/v3/examples/PaymentProcessor/Create.ex.php';
150 $this->assertAPISuccess($result);
154 * Cleanup after function.
156 public function tearDown() {
157 $this->quickCleanUpFinancialEntities();
161 public function setUpContributionPage() {
162 $contributionPageResult = $this->callAPISuccess($this->_entity
, 'create', $this->params
);
163 if (empty($this->_ids
['price_set'])) {
164 $priceSet = $this->callAPISuccess('price_set', 'create', $this->_priceSetParams
);
165 $this->_ids
['price_set'][] = $priceSet['id'];
167 $priceSetID = $this->_price
= reset($this->_ids
['price_set']);
168 CRM_Price_BAO_PriceSet
::addTo('civicrm_contribution_page', $contributionPageResult['id'], $priceSetID);
170 if (empty($this->_ids
['price_field'])) {
171 $priceField = $this->callAPISuccess('price_field', 'create', [
172 'price_set_id' => $priceSetID,
173 'label' => 'Goat Breed',
174 'html_type' => 'Radio',
176 $this->_ids
['price_field'] = [$priceField['id']];
178 if (empty($this->_ids
['price_field_value'])) {
179 $this->callAPISuccess('price_field_value', 'create', [
180 'price_set_id' => $priceSetID,
181 'price_field_id' => $priceField['id'],
182 'label' => 'Long Haired Goat',
184 'financial_type_id' => $this->financialtypeID
,
186 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', [
187 'price_set_id' => $priceSetID,
188 'price_field_id' => $priceField['id'],
189 'label' => 'Shoe-eating Goat',
191 'financial_type_id' => $this->halfFinancialTypeId
,
193 $this->_ids
['price_field_value'] = [$priceFieldValue['id']];
195 $this->_ids
['contribution_page'] = $contributionPageResult['id'];
199 * Online and offline contrbution from above created contribution page.
201 * @param string $thousandSeparator
202 * punctuation used to refer to thousands.
204 * @dataProvider getThousandSeparators
206 public function testCreateContributionOnline($thousandSeparator) {
207 $this->setCurrencySeparators($thousandSeparator);
208 $this->setUpContributionPage();
210 'contact_id' => $this->_individualId
,
211 'receive_date' => '20120511',
212 'total_amount' => $this->formatMoneyInput(100.00),
213 'financial_type_id' => $this->financialtypeID
,
214 'contribution_page_id' => $this->_ids
['contribution_page'],
215 'payment_processor' => $this->_ids
['paymentProcessID'],
217 'invoice_id' => 67890,
219 'contribution_status_id' => 1,
222 $contribution = $this->callAPISuccess('contribution', 'create', $params);
223 $this->_ids
['contributionId'] = $contribution['id'];
224 $this->assertEquals($contribution['values'][$contribution['id']]['contact_id'], $this->_individualId
);
225 $this->assertEquals($contribution['values'][$contribution['id']]['total_amount'], 120.00);
226 $this->assertEquals($contribution['values'][$contribution['id']]['financial_type_id'], $this->financialtypeID
);
227 $this->assertEquals($contribution['values'][$contribution['id']]['trxn_id'], 12345);
228 $this->assertEquals($contribution['values'][$contribution['id']]['invoice_id'], 67890);
229 $this->assertEquals($contribution['values'][$contribution['id']]['source'], 'SSF');
230 $this->assertEquals($contribution['values'][$contribution['id']]['tax_amount'], 20);
231 $this->assertEquals($contribution['values'][$contribution['id']]['contribution_status_id'], 1);
232 $this->_checkFinancialRecords($contribution, 'online');
236 * Create contribution with chained line items.
238 * @param string $thousandSeparator
239 * punctuation used to refer to thousands.
241 * @dataProvider getThousandSeparators
243 public function testCreateContributionChainedLineItems($thousandSeparator) {
244 $this->setCurrencySeparators($thousandSeparator);
245 $this->setUpContributionPage();
247 'contact_id' => $this->_individualId
,
248 'receive_date' => '20120511',
249 'total_amount' => 400.00,
250 'financial_type_id' => $this->financialtypeID
,
252 'invoice_id' => 67890,
254 'contribution_status_id' => 1,
256 'api.line_item.create' => [
258 'price_field_id' => $this->_ids
['price_field'],
260 'line_total' => '100',
261 'unit_price' => '100',
262 'financial_type_id' => $this->financialtypeID
,
265 'price_field_id' => $this->_ids
['price_field'],
267 'line_total' => '300',
268 'unit_price' => '300',
269 'financial_type_id' => $this->halfFinancialTypeId
,
274 $contribution = $this->callAPISuccess('contribution', 'create', $params);
276 $lineItems = $this->callAPISuccess('line_item', 'get', [
277 'entity_id' => $contribution['id'],
278 'contribution_id' => $contribution['id'],
279 'entity_table' => 'civicrm_contribution',
282 $this->assertEquals(2, $lineItems['count']);
285 public function testCreateContributionPayLaterOnline() {
286 $this->setUpContributionPage();
288 'contact_id' => $this->_individualId
,
289 'receive_date' => '20120511',
290 'total_amount' => 100.00,
291 'financial_type_id' => $this->financialtypeID
,
292 'contribution_page_id' => $this->_ids
['contribution_page'],
295 'invoice_id' => 67890,
297 'contribution_status_id' => 2,
299 $contribution = $this->callAPISuccess('contribution', 'create', $params, __FUNCTION__
, __FILE__
);
300 $this->assertEquals($contribution['values'][$contribution['id']]['contact_id'], $this->_individualId
);
301 $this->assertEquals($contribution['values'][$contribution['id']]['total_amount'], 120.00);
302 $this->assertEquals($contribution['values'][$contribution['id']]['financial_type_id'], $this->financialtypeID
);
303 $this->assertEquals($contribution['values'][$contribution['id']]['trxn_id'], 12345);
304 $this->assertEquals($contribution['values'][$contribution['id']]['invoice_id'], 67890);
305 $this->assertEquals($contribution['values'][$contribution['id']]['source'], 'SSF');
306 $this->assertEquals($contribution['values'][$contribution['id']]['tax_amount'], 20);
307 $this->assertEquals($contribution['values'][$contribution['id']]['contribution_status_id'], 2);
308 $this->_checkFinancialRecords($contribution, 'payLater');
312 * Test online pending contributions.
314 * @param string $thousandSeparator
315 * punctuation used to refer to thousands.
317 * @dataProvider getThousandSeparators
319 public function testCreateContributionPendingOnline($thousandSeparator) {
320 $this->setCurrencySeparators($thousandSeparator);
321 $this->setUpContributionPage();
323 'contact_id' => $this->_individualId
,
324 'receive_date' => '20120511',
325 'total_amount' => $this->formatMoneyInput(100.00),
326 'financial_type_id' => $this->financialtypeID
,
327 'contribution_page_id' => $this->_ids
['contribution_page'],
329 'invoice_id' => 67890,
331 'contribution_status_id' => 2,
334 $contribution = $this->callAPISuccess('contribution', 'create', $params, __FUNCTION__
, __FILE__
);
335 $this->assertEquals($contribution['values'][$contribution['id']]['contact_id'], $this->_individualId
);
336 $this->assertEquals($contribution['values'][$contribution['id']]['total_amount'], 120.00);
337 $this->assertEquals($contribution['values'][$contribution['id']]['financial_type_id'], $this->financialtypeID
);
338 $this->assertEquals($contribution['values'][$contribution['id']]['trxn_id'], 12345);
339 $this->assertEquals($contribution['values'][$contribution['id']]['invoice_id'], 67890);
340 $this->assertEquals($contribution['values'][$contribution['id']]['source'], 'SSF');
341 $this->assertEquals($contribution['values'][$contribution['id']]['tax_amount'], 20);
342 $this->assertEquals($contribution['values'][$contribution['id']]['contribution_status_id'], 2);
343 $this->_checkFinancialRecords($contribution, 'pending');
344 $this->setCurrencySeparators($thousandSeparator);
348 * Update a contribution.
350 * Function tests that line items, financial records are updated when contribution amount is changed
352 public function testCreateUpdateContributionChangeTotal() {
353 $this->setUpContributionPage();
354 $this->contributionParams
= [
355 'contact_id' => $this->_individualId
,
356 'receive_date' => '20120511',
357 'total_amount' => 100.00,
358 'financial_type_id' => $this->financialtypeID
,
360 'contribution_status_id' => 1,
362 $contribution = $this->callAPISuccess('contribution', 'create', $this->contributionParams
);
363 $lineItems = $this->callAPISuccess('line_item', 'getvalue', [
364 'entity_id' => $contribution['id'],
365 'entity_table' => 'civicrm_contribution',
367 'return' => 'line_total',
369 $this->assertEquals('100.00', $lineItems);
370 $trxnAmount = $this->_getFinancialTrxnAmount($contribution['id']);
371 $this->assertEquals('120.00', $trxnAmount);
373 'id' => $contribution['id'],
374 // without tax rate i.e Donation
375 'financial_type_id' => 1,
376 'total_amount' => '300',
378 $contribution = $this->callAPISuccess('contribution', 'create', $newParams);
380 $lineItems = $this->callAPISuccess('line_item', 'getvalue', [
381 'entity_id' => $contribution['id'],
382 'entity_table' => 'civicrm_contribution',
384 'return' => 'line_total',
387 $this->assertEquals('300.00', $lineItems);
388 $trxnAmount = $this->_getFinancialTrxnAmount($contribution['id']);
389 $fitemAmount = $this->_getFinancialItemAmount($contribution['id']);
390 $this->assertEquals('300.00', $trxnAmount);
391 $this->assertEquals('300.00', $fitemAmount);
397 * @return null|string
399 public function _getFinancialTrxnAmount($contId) {
401 SUM( ft.total_amount ) AS total
402 FROM civicrm_financial_trxn AS ft
403 LEFT JOIN civicrm_entity_financial_trxn AS ceft ON ft.id = ceft.financial_trxn_id
404 WHERE ceft.entity_table = 'civicrm_contribution'
405 AND ceft.entity_id = {$contId}";
406 $result = CRM_Core_DAO
::singleValueQuery($query);
413 * @return null|string
415 public function _getFinancialItemAmount($contId) {
416 $lineItem = key(CRM_Price_BAO_LineItem
::getLineItems($contId, 'contribution'));
419 FROM civicrm_financial_item
420 WHERE entity_table = 'civicrm_line_item'
421 AND entity_id = {$lineItem}";
422 $result = CRM_Core_DAO
::singleValueQuery($query);
427 * @param array $params
430 public function _checkFinancialRecords($params, $context) {
432 'entity_id' => $params['id'],
433 'entity_table' => 'civicrm_contribution',
435 if ($context == 'pending') {
436 $trxn = CRM_Financial_BAO_FinancialItem
::retrieveEntityFinancialTrxn($entityParams);
437 $this->assertNull($trxn, 'No Trxn to be created until IPN callback');
440 $trxn = current(CRM_Financial_BAO_FinancialItem
::retrieveEntityFinancialTrxn($entityParams));
442 'id' => $trxn['financial_trxn_id'],
444 if ($context != 'online' && $context != 'payLater') {
446 'to_financial_account_id' => 6,
447 'total_amount' => 120,
451 if ($context == 'online') {
453 'to_financial_account_id' => 12,
454 'total_amount' => 120,
458 elseif ($context == 'payLater') {
460 'to_financial_account_id' => 7,
461 'total_amount' => 120,
465 $this->assertDBCompareValues('CRM_Financial_DAO_FinancialTrxn', $trxnParams, $compareParams);
467 'financial_trxn_id' => $trxn['financial_trxn_id'],
468 'entity_table' => 'civicrm_financial_item',
470 $entityTrxn = current(CRM_Financial_BAO_FinancialItem
::retrieveEntityFinancialTrxn($entityParams));
472 'id' => $entityTrxn['entity_id'],
477 'financial_account_id' => $this->_getFinancialAccountId($this->financialtypeID
),
479 if ($context == 'payLater') {
483 'financial_account_id' => $this->_getFinancialAccountId($this->financialtypeID
),
486 $this->assertDBCompareValues('CRM_Financial_DAO_FinancialItem', $fitemParams, $compareParams);
490 * @param int $financialTypeId
493 public function _getFinancialAccountId($financialTypeId) {
494 $accountRel = key(CRM_Core_PseudoConstant
::accountOptionValues('account_relationship', NULL, " AND v.name LIKE 'Income Account is' "));
497 'entity_table' => 'civicrm_financial_type',
498 'entity_id' => $financialTypeId,
499 'account_relationship' => $accountRel,
503 CRM_Financial_BAO_FinancialTypeAccount
::retrieve($searchParams, $result);
504 return $result['financial_account_id'] ??
NULL;
508 * Test deleting a contribution.
510 * (It is unclear why this is in this class - it seems like maybe it doesn't test anything not
511 * on the contribution test class & might be copy and paste....).
513 public function testDeleteContribution() {
514 $contributionID = $this->contributionCreate([
515 'contact_id' => $this->_individualId
,
517 'financial_type_id' => $this->financialtypeID
,
518 'invoice_id' => 'dfsdf',
520 $this->callAPISuccess('contribution', 'delete', ['id' => $contributionID]);