3 +--------------------------------------------------------------------+
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2020 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
29 * Class api_v3_TaxContributionPageTest
32 class api_v3_TaxContributionPageTest
extends CiviUnitTestCase
{
33 protected $_apiversion = 3;
35 protected $financialtypeID;
36 protected $financialAccountId;
37 protected $_entity = 'contribution_page';
38 protected $_priceSetParams = [];
39 protected $_paymentProcessorType;
40 protected $payParams = [];
41 protected $paymentProceParams = [];
42 protected $settingValue = [];
43 protected $setInvoiceSettings;
45 protected $_individualId;
46 protected $financialAccHalftax;
47 protected $financialtypeHalftax;
48 protected $financialRelationHalftax;
49 protected $halfFinancialAccId;
50 protected $halfFinancialTypeId;
51 public $DBResetRequired = TRUE;
53 public function setUp() {
55 $this->_individualId
= $this->individualCreate();
56 $this->_orgId
= $this->organizationCreate(NULL);
59 'title' => "Test Contribution Page" . substr(sha1(rand()), 0, 7),
60 'financial_type_id' => 1,
61 'payment_processor' => 1,
65 'pay_later_text' => 'I will pay later',
66 'pay_later_receipt' => "I will pay later",
67 'is_monetary' => TRUE,
68 'is_billing_required' => TRUE,
71 $this->_priceSetParams
= [
72 'name' => 'tax_contribution' . substr(sha1(rand()), 0, 7),
73 'title' => 'contributiontax' . substr(sha1(rand()), 0, 7),
75 'help_pre' => "Where does your goat sleep",
76 'help_post' => "thank you for your time",
78 'financial_type_id' => 3,
79 'is_quick_config' => 0,
82 // Financial Account with 20% tax rate
83 $financialAccountSetparams = [
85 'name' => 'vat full taxrate account' . substr(sha1(rand()), 0, 7),
86 'contact_id' => $this->_orgId
,
87 'financial_account_type_id' => 2,
95 $financialAccount = $this->callAPISuccess('financial_account', 'create', $financialAccountSetparams);
96 $this->financialAccountId
= $financialAccount['id'];
98 // Financial type having 'Sales Tax Account is' with liability financail account
100 'name' => 'grassvariety1' . substr(sha1(rand()), 0, 7),
104 $priceField = $this->callAPISuccess('financial_type', 'create', $financialType);
105 $this->financialtypeID
= $priceField['id'];
106 $financialRelationParams = [
107 'entity_table' => 'civicrm_financial_type',
108 'entity_id' => $this->financialtypeID
,
109 'account_relationship' => 10,
110 'financial_account_id' => $this->financialAccountId
,
112 $financialRelation = CRM_Financial_BAO_FinancialTypeAccount
::add($financialRelationParams);
114 // Financial type with 5% tax rate
115 $financialAccHalftax = [
116 'name' => 'vat half taxrate account' . substr(sha1(rand()), 0, 7),
117 'contact_id' => $this->_orgId
,
118 'financial_account_type_id' => 2,
125 $halfFinancialAccount = CRM_Financial_BAO_FinancialAccount
::add($financialAccHalftax);
126 $this->halfFinancialAccId
= $halfFinancialAccount->id
;
127 $halfFinancialtypeHalftax = [
128 'name' => 'grassvariety2' . substr(sha1(rand()), 0, 7),
133 $halfFinancialType = CRM_Financial_BAO_FinancialType
::add($halfFinancialtypeHalftax);
134 $this->halfFinancialTypeId
= $halfFinancialType->id
;
135 $financialRelationHalftax = [
136 'entity_table' => 'civicrm_financial_type',
137 'entity_id' => $this->halfFinancialTypeId
,
138 'account_relationship' => 10,
139 'financial_account_id' => $this->halfFinancialAccId
,
142 $halfFinancialRelation = CRM_Financial_BAO_FinancialTypeAccount
::add($financialRelationHalftax);
144 // Enable component contribute setting
145 $setInvoiceSettings = $this->enableTaxAndInvoicing();
148 $paymentProceParams = [
150 'name' => 'dummy' . substr(sha1(rand()), 0, 7),
151 'payment_processor_type_id' => CRM_Core_PseudoConstant
::getKey('CRM_Financial_BAO_PaymentProcessor', 'payment_processor_type_id', 'Dummy'),
152 'financial_account_id' => 12,
155 'user_name' => 'dummy',
156 'url_site' => 'http://dummy.com',
157 'url_recur' => 'http://dummyrecur.com',
158 'class_name' => 'Payment_Dummy',
163 $result = $this->callAPISuccess('payment_processor', 'create', $paymentProceParams);
164 $this->_ids
['paymentProcessID'] = $result['id'];
165 require_once 'api/v3/examples/PaymentProcessor/Create.ex.php';
166 $this->assertAPISuccess($result);
170 * Cleanup after function.
172 public function tearDown() {
173 $this->quickCleanUpFinancialEntities();
177 public function setUpContributionPage() {
178 $contributionPageResult = $this->callAPISuccess($this->_entity
, 'create', $this->params
);
179 if (empty($this->_ids
['price_set'])) {
180 $priceSet = $this->callAPISuccess('price_set', 'create', $this->_priceSetParams
);
181 $this->_ids
['price_set'][] = $priceSet['id'];
183 $priceSetID = $this->_price
= reset($this->_ids
['price_set']);
184 CRM_Price_BAO_PriceSet
::addTo('civicrm_contribution_page', $contributionPageResult['id'], $priceSetID);
186 if (empty($this->_ids
['price_field'])) {
187 $priceField = $this->callAPISuccess('price_field', 'create', [
188 'price_set_id' => $priceSetID,
189 'label' => 'Goat Breed',
190 'html_type' => 'Radio',
192 $this->_ids
['price_field'] = [$priceField['id']];
194 if (empty($this->_ids
['price_field_value'])) {
195 $this->callAPISuccess('price_field_value', 'create', [
196 'price_set_id' => $priceSetID,
197 'price_field_id' => $priceField['id'],
198 'label' => 'Long Haired Goat',
200 'financial_type_id' => $this->financialtypeID
,
202 $priceFieldValue = $this->callAPISuccess('price_field_value', 'create', [
203 'price_set_id' => $priceSetID,
204 'price_field_id' => $priceField['id'],
205 'label' => 'Shoe-eating Goat',
207 'financial_type_id' => $this->halfFinancialTypeId
,
209 $this->_ids
['price_field_value'] = [$priceFieldValue['id']];
211 $this->_ids
['contribution_page'] = $contributionPageResult['id'];
215 * Online and offline contrbution from above created contribution page.
217 * @param string $thousandSeparator
218 * punctuation used to refer to thousands.
220 * @dataProvider getThousandSeparators
222 public function testCreateContributionOnline($thousandSeparator) {
223 $this->setCurrencySeparators($thousandSeparator);
224 $this->setUpContributionPage();
226 'contact_id' => $this->_individualId
,
227 'receive_date' => '20120511',
228 'total_amount' => $this->formatMoneyInput(100.00),
229 'financial_type_id' => $this->financialtypeID
,
230 'contribution_page_id' => $this->_ids
['contribution_page'],
231 'payment_processor' => $this->_ids
['paymentProcessID'],
233 'invoice_id' => 67890,
235 'contribution_status_id' => 1,
238 $contribution = $this->callAPISuccess('contribution', 'create', $params);
239 $this->_ids
['contributionId'] = $contribution['id'];
240 $this->assertEquals($contribution['values'][$contribution['id']]['contact_id'], $this->_individualId
);
241 $this->assertEquals($contribution['values'][$contribution['id']]['total_amount'], 120.00);
242 $this->assertEquals($contribution['values'][$contribution['id']]['financial_type_id'], $this->financialtypeID
);
243 $this->assertEquals($contribution['values'][$contribution['id']]['trxn_id'], 12345);
244 $this->assertEquals($contribution['values'][$contribution['id']]['invoice_id'], 67890);
245 $this->assertEquals($contribution['values'][$contribution['id']]['source'], 'SSF');
246 $this->assertEquals($contribution['values'][$contribution['id']]['tax_amount'], 20);
247 $this->assertEquals($contribution['values'][$contribution['id']]['contribution_status_id'], 1);
248 $this->_checkFinancialRecords($contribution, 'online');
252 * Create contribution with chained line items.
254 * @param string $thousandSeparator
255 * punctuation used to refer to thousands.
257 * @dataProvider getThousandSeparators
259 public function testCreateContributionChainedLineItems($thousandSeparator) {
260 $this->setCurrencySeparators($thousandSeparator);
261 $this->setUpContributionPage();
263 'contact_id' => $this->_individualId
,
264 'receive_date' => '20120511',
265 'total_amount' => 400.00,
266 'financial_type_id' => $this->financialtypeID
,
268 'invoice_id' => 67890,
270 'contribution_status_id' => 1,
272 'api.line_item.create' => [
274 'price_field_id' => $this->_ids
['price_field'],
276 'line_total' => '100',
277 'unit_price' => '100',
278 'financial_type_id' => $this->financialtypeID
,
281 'price_field_id' => $this->_ids
['price_field'],
283 'line_total' => '300',
284 'unit_price' => '300',
285 'financial_type_id' => $this->halfFinancialTypeId
,
290 $contribution = $this->callAPISuccess('contribution', 'create', $params);
292 $lineItems = $this->callAPISuccess('line_item', 'get', [
293 'entity_id' => $contribution['id'],
294 'contribution_id' => $contribution['id'],
295 'entity_table' => 'civicrm_contribution',
298 $this->assertEquals(2, $lineItems['count']);
301 public function testCreateContributionPayLaterOnline() {
302 $this->setUpContributionPage();
304 'contact_id' => $this->_individualId
,
305 'receive_date' => '20120511',
306 'total_amount' => 100.00,
307 'financial_type_id' => $this->financialtypeID
,
308 'contribution_page_id' => $this->_ids
['contribution_page'],
311 'invoice_id' => 67890,
313 'contribution_status_id' => 2,
315 $contribution = $this->callAPISuccess('contribution', 'create', $params, __FUNCTION__
, __FILE__
);
316 $this->assertEquals($contribution['values'][$contribution['id']]['contact_id'], $this->_individualId
);
317 $this->assertEquals($contribution['values'][$contribution['id']]['total_amount'], 120.00);
318 $this->assertEquals($contribution['values'][$contribution['id']]['financial_type_id'], $this->financialtypeID
);
319 $this->assertEquals($contribution['values'][$contribution['id']]['trxn_id'], 12345);
320 $this->assertEquals($contribution['values'][$contribution['id']]['invoice_id'], 67890);
321 $this->assertEquals($contribution['values'][$contribution['id']]['source'], 'SSF');
322 $this->assertEquals($contribution['values'][$contribution['id']]['tax_amount'], 20);
323 $this->assertEquals($contribution['values'][$contribution['id']]['contribution_status_id'], 2);
324 $this->_checkFinancialRecords($contribution, 'payLater');
328 * Test online pending contributions.
330 * @param string $thousandSeparator
331 * punctuation used to refer to thousands.
333 * @dataProvider getThousandSeparators
335 public function testCreateContributionPendingOnline($thousandSeparator) {
336 $this->setCurrencySeparators($thousandSeparator);
337 $this->setUpContributionPage();
339 'contact_id' => $this->_individualId
,
340 'receive_date' => '20120511',
341 'total_amount' => $this->formatMoneyInput(100.00),
342 'financial_type_id' => $this->financialtypeID
,
343 'contribution_page_id' => $this->_ids
['contribution_page'],
345 'invoice_id' => 67890,
347 'contribution_status_id' => 2,
350 $contribution = $this->callAPISuccess('contribution', 'create', $params, __FUNCTION__
, __FILE__
);
351 $this->assertEquals($contribution['values'][$contribution['id']]['contact_id'], $this->_individualId
);
352 $this->assertEquals($contribution['values'][$contribution['id']]['total_amount'], 120.00);
353 $this->assertEquals($contribution['values'][$contribution['id']]['financial_type_id'], $this->financialtypeID
);
354 $this->assertEquals($contribution['values'][$contribution['id']]['trxn_id'], 12345);
355 $this->assertEquals($contribution['values'][$contribution['id']]['invoice_id'], 67890);
356 $this->assertEquals($contribution['values'][$contribution['id']]['source'], 'SSF');
357 $this->assertEquals($contribution['values'][$contribution['id']]['tax_amount'], 20);
358 $this->assertEquals($contribution['values'][$contribution['id']]['contribution_status_id'], 2);
359 $this->_checkFinancialRecords($contribution, 'pending');
360 $this->setCurrencySeparators($thousandSeparator);
364 * Update a contribution.
366 * Function tests that line items, financial records are updated when contribution amount is changed
368 public function testCreateUpdateContributionChangeTotal() {
369 $this->setUpContributionPage();
370 $this->contributionParams
= [
371 'contact_id' => $this->_individualId
,
372 'receive_date' => '20120511',
373 'total_amount' => 100.00,
374 'financial_type_id' => $this->financialtypeID
,
376 'contribution_status_id' => 1,
378 $contribution = $this->callAPISuccess('contribution', 'create', $this->contributionParams
);
379 $lineItems = $this->callAPISuccess('line_item', 'getvalue', [
380 'entity_id' => $contribution['id'],
381 'entity_table' => 'civicrm_contribution',
383 'return' => 'line_total',
385 $this->assertEquals('100.00', $lineItems);
386 $trxnAmount = $this->_getFinancialTrxnAmount($contribution['id']);
387 $this->assertEquals('120.00', $trxnAmount);
389 'id' => $contribution['id'],
390 // without tax rate i.e Donation
391 'financial_type_id' => 1,
392 'total_amount' => '300',
394 $contribution = $this->callAPISuccess('contribution', 'create', $newParams);
396 $lineItems = $this->callAPISuccess('line_item', 'getvalue', [
397 'entity_id' => $contribution['id'],
398 'entity_table' => 'civicrm_contribution',
400 'return' => 'line_total',
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);
413 * @return null|string
415 public function _getFinancialTrxnAmount($contId) {
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);
429 * @return null|string
431 public function _getFinancialItemAmount($contId) {
432 $lineItem = key(CRM_Price_BAO_LineItem
::getLineItems($contId, 'contribution'));
435 FROM civicrm_financial_item
436 WHERE entity_table = 'civicrm_line_item'
437 AND entity_id = {$lineItem}";
438 $result = CRM_Core_DAO
::singleValueQuery($query);
443 * @param array $params
446 public function _checkFinancialRecords($params, $context) {
448 'entity_id' => $params['id'],
449 'entity_table' => 'civicrm_contribution',
451 if ($context == 'pending') {
452 $trxn = CRM_Financial_BAO_FinancialItem
::retrieveEntityFinancialTrxn($entityParams);
453 $this->assertNull($trxn, 'No Trxn to be created until IPN callback');
456 $trxn = current(CRM_Financial_BAO_FinancialItem
::retrieveEntityFinancialTrxn($entityParams));
458 'id' => $trxn['financial_trxn_id'],
460 if ($context != 'online' && $context != 'payLater') {
462 'to_financial_account_id' => 6,
463 'total_amount' => 120,
467 if ($context == 'online') {
469 'to_financial_account_id' => 12,
470 'total_amount' => 120,
474 elseif ($context == 'payLater') {
476 'to_financial_account_id' => 7,
477 'total_amount' => 120,
481 $this->assertDBCompareValues('CRM_Financial_DAO_FinancialTrxn', $trxnParams, $compareParams);
483 'financial_trxn_id' => $trxn['financial_trxn_id'],
484 'entity_table' => 'civicrm_financial_item',
486 $entityTrxn = current(CRM_Financial_BAO_FinancialItem
::retrieveEntityFinancialTrxn($entityParams));
488 'id' => $entityTrxn['entity_id'],
493 'financial_account_id' => $this->_getFinancialAccountId($this->financialtypeID
),
495 if ($context == 'payLater') {
499 'financial_account_id' => $this->_getFinancialAccountId($this->financialtypeID
),
502 $this->assertDBCompareValues('CRM_Financial_DAO_FinancialItem', $fitemParams, $compareParams);
506 * @param int $financialTypeId
509 public function _getFinancialAccountId($financialTypeId) {
510 $accountRel = key(CRM_Core_PseudoConstant
::accountOptionValues('account_relationship', NULL, " AND v.name LIKE 'Income Account is' "));
513 'entity_table' => 'civicrm_financial_type',
514 'entity_id' => $financialTypeId,
515 'account_relationship' => $accountRel,
519 CRM_Financial_BAO_FinancialTypeAccount
::retrieve($searchParams, $result);
520 return CRM_Utils_Array
::value('financial_account_id', $result);
524 * Test deleting a contribution.
526 * (It is unclear why this is in this class - it seems like maybe it doesn't test anything not
527 * on the contribution test class & might be copy and paste....).
529 public function testDeleteContribution() {
530 $contributionID = $this->contributionCreate([
531 'contact_id' => $this->_individualId
,
533 'financial_type_id' => $this->financialtypeID
,
534 'invoice_id' => 'dfsdf',
536 $this->callAPISuccess('contribution', 'delete', ['id' => $contributionID]);