PageRenderTime 57ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/drupal/sites/all/modules/civicrm/CRM/Contribute/BAO/Contribution.php

https://github.com/michaelmcandrew/ste
PHP | 1755 lines | 1203 code | 236 blank | 316 comment | 183 complexity | 2fe9ef0c4e167831b31584afd5d008da MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, AGPL-1.0, LGPL-2.1

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /*
  3. +--------------------------------------------------------------------+
  4. | CiviCRM version 3.4 |
  5. +--------------------------------------------------------------------+
  6. | Copyright CiviCRM LLC (c) 2004-2011 |
  7. +--------------------------------------------------------------------+
  8. | This file is a part of CiviCRM. |
  9. | |
  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. |
  13. | |
  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. |
  18. | |
  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. +--------------------------------------------------------------------+
  26. */
  27. /**
  28. *
  29. * @package CRM
  30. * @copyright CiviCRM LLC (c) 2004-2011
  31. * $Id$
  32. *
  33. */
  34. require_once 'CRM/Utils/Money.php';
  35. require_once 'CRM/Contribute/PseudoConstant.php';
  36. require_once 'CRM/Contribute/DAO/Contribution.php';
  37. require_once 'CRM/Core/BAO/CustomField.php';
  38. require_once 'CRM/Core/BAO/CustomValue.php';
  39. class CRM_Contribute_BAO_Contribution extends CRM_Contribute_DAO_Contribution
  40. {
  41. /**
  42. * static field for all the contribution information that we can potentially import
  43. *
  44. * @var array
  45. * @static
  46. */
  47. static $_importableFields = null;
  48. /**
  49. * static field for all the contribution information that we can potentially export
  50. *
  51. * @var array
  52. * @static
  53. */
  54. static $_exportableFields = null;
  55. function __construct()
  56. {
  57. parent::__construct();
  58. }
  59. /**
  60. * takes an associative array and creates a contribution object
  61. *
  62. * the function extract all the params it needs to initialize the create a
  63. * contribution object. the params array could contain additional unused name/value
  64. * pairs
  65. *
  66. * @param array $params (reference ) an assoc array of name/value pairs
  67. * @param array $ids the array that holds all the db ids
  68. *
  69. * @return object CRM_Contribute_BAO_Contribution object
  70. * @access public
  71. * @static
  72. */
  73. static function add(&$params, &$ids)
  74. {
  75. if ( empty($params) ) {
  76. return;
  77. }
  78. $duplicates = array( );
  79. if ( self::checkDuplicate( $params, $duplicates,
  80. CRM_Utils_Array::value( 'contribution', $ids ) ) ) {
  81. $error =& CRM_Core_Error::singleton( );
  82. $d = implode( ', ', $duplicates );
  83. $error->push( CRM_Core_Error::DUPLICATE_CONTRIBUTION,
  84. 'Fatal',
  85. array( $d ),
  86. "Duplicate error - existing contribution record(s) have a matching Transaction ID or Invoice ID. Contribution record ID(s) are: $d" );
  87. return $error;
  88. }
  89. // first clean up all the money fields
  90. $moneyFields = array( 'total_amount',
  91. 'net_amount',
  92. 'fee_amount',
  93. 'non_deductible_amount' );
  94. //if priceset is used, no need to cleanup money
  95. if ( CRM_UTils_Array::value('skipCleanMoney', $params) ) {
  96. unset($moneyFields[0]);
  97. }
  98. foreach ( $moneyFields as $field ) {
  99. if ( isset( $params[$field] ) ) {
  100. $params[$field] = CRM_Utils_Rule::cleanMoney( $params[$field] );
  101. }
  102. }
  103. if ( CRM_Utils_Array::value( 'payment_instrument_id', $params ) ) {
  104. require_once 'CRM/Contribute/PseudoConstant.php';
  105. $paymentInstruments = CRM_Contribute_PseudoConstant::paymentInstrument( 'name' );
  106. if ( $params['payment_instrument_id'] != array_search( 'Check', $paymentInstruments ) ) {
  107. $params['check_number'] = 'null';
  108. }
  109. }
  110. require_once 'CRM/Utils/Hook.php';
  111. if ( CRM_Utils_Array::value( 'contribution', $ids ) ) {
  112. CRM_Utils_Hook::pre( 'edit', 'Contribution', $ids['contribution'], $params );
  113. } else {
  114. CRM_Utils_Hook::pre( 'create', 'Contribution', null, $params );
  115. }
  116. $contribution = new CRM_Contribute_BAO_Contribution();
  117. $contribution->copyValues($params);
  118. $contribution->id = CRM_Utils_Array::value( 'contribution', $ids );
  119. // also add financial_trxn details as part of fix for CRM-4724
  120. $contribution->trxn_result_code = CRM_Utils_Array::value('trxn_result_code', $params );
  121. $contribution->payment_processor = CRM_Utils_Array::value('payment_processor', $params );
  122. require_once 'CRM/Utils/Rule.php';
  123. if (!CRM_Utils_Rule::currencyCode($contribution->currency)) {
  124. require_once 'CRM/Core/Config.php';
  125. $config = CRM_Core_Config::singleton();
  126. $contribution->currency = $config->defaultCurrency;
  127. }
  128. $result = $contribution->save();
  129. // reset the group contact cache for this group
  130. require_once 'CRM/Contact/BAO/GroupContactCache.php';
  131. CRM_Contact_BAO_GroupContactCache::remove( );
  132. if ( CRM_Utils_Array::value( 'contribution', $ids ) ) {
  133. CRM_Utils_Hook::post( 'edit', 'Contribution', $contribution->id, $contribution );
  134. } else {
  135. CRM_Utils_Hook::post( 'create', 'Contribution', $contribution->id, $contribution );
  136. }
  137. return $result;
  138. }
  139. /**
  140. * Given the list of params in the params array, fetch the object
  141. * and store the values in the values array
  142. *
  143. * @param array $params input parameters to find object
  144. * @param array $values output values of the object
  145. * @param array $ids the array that holds all the db ids
  146. *
  147. * @return CRM_Contribute_BAO_Contribution|null the found object or null
  148. * @access public
  149. * @static
  150. */
  151. static function &getValues( &$params, &$values, &$ids )
  152. {
  153. if ( empty ( $params ) ) {
  154. return null;
  155. }
  156. $contribution = new CRM_Contribute_BAO_Contribution( );
  157. $contribution->copyValues( $params );
  158. if ( $contribution->find(true) ) {
  159. $ids['contribution'] = $contribution->id;
  160. CRM_Core_DAO::storeValues( $contribution, $values );
  161. return $contribution;
  162. }
  163. return null;
  164. }
  165. /**
  166. * takes an associative array and creates a contribution object
  167. *
  168. * @param array $params (reference ) an assoc array of name/value pairs
  169. * @param array $ids the array that holds all the db ids
  170. *
  171. * @return object CRM_Contribute_BAO_Contribution object
  172. * @access public
  173. * @static
  174. */
  175. static function &create(&$params, &$ids)
  176. {
  177. require_once 'CRM/Utils/Money.php';
  178. require_once 'CRM/Utils/Date.php';
  179. require_once 'CRM/Contribute/PseudoConstant.php';
  180. // FIXME: a cludgy hack to fix the dates to MySQL format
  181. $dateFields = array('receive_date', 'cancel_date', 'receipt_date', 'thankyou_date');
  182. foreach ($dateFields as $df) {
  183. if (isset($params[$df])) {
  184. $params[$df] = CRM_Utils_Date::isoToMysql($params[$df]);
  185. }
  186. }
  187. if ( CRM_Utils_Array::value( 'contribution', $ids ) &&
  188. !CRM_Utils_Array::value( 'softID', $params ) ) {
  189. if ( $softID = CRM_Core_DAO::getFieldValue( 'CRM_Contribute_DAO_ContributionSoft', $ids['contribution'], 'id', 'contribution_id') ) {
  190. $params['softID'] = $softID;
  191. }
  192. }
  193. require_once 'CRM/Core/Transaction.php';
  194. $transaction = new CRM_Core_Transaction( );
  195. // delete the soft credit record if no soft credit contact ID AND no PCP is set in the form
  196. if ( CRM_Utils_Array::value( 'contribution', $ids ) &&
  197. ( !CRM_Utils_Array::value( 'soft_credit_to', $params ) &&
  198. !CRM_Utils_Array::value( 'pcp_made_through_id', $params ) ) &&
  199. CRM_Utils_Array::value( 'softID', $params ) ) {
  200. $softCredit = new CRM_Contribute_DAO_ContributionSoft( );
  201. $softCredit->id = $params['softID'];
  202. $softCredit->delete( );
  203. }
  204. // delete the soft credit record if no soft credit contact ID AND no PCP is set in the form
  205. if ( CRM_Utils_Array::value( 'contribution', $ids ) &&
  206. ( !CRM_Utils_Array::value( 'soft_credit_to', $params ) &&
  207. !CRM_Utils_Array::value( 'pcp_made_through_id', $params ) ) &&
  208. CRM_Utils_Array::value( 'softID', $params ) ) {
  209. $softCredit = new CRM_Contribute_DAO_ContributionSoft( );
  210. $softCredit->id = $params['softID'];
  211. $softCredit->delete( );
  212. }
  213. $contribution = self::add($params, $ids);
  214. if ( is_a( $contribution, 'CRM_Core_Error') ) {
  215. $transaction->rollback( );
  216. return $contribution;
  217. }
  218. $params['contribution_id'] = $contribution->id;
  219. if ( CRM_Utils_Array::value( 'custom', $params ) &&
  220. is_array( $params['custom'] ) ) {
  221. require_once 'CRM/Core/BAO/CustomValueTable.php';
  222. CRM_Core_BAO_CustomValueTable::store( $params['custom'], 'civicrm_contribution', $contribution->id );
  223. }
  224. $session = & CRM_Core_Session::singleton();
  225. if ( CRM_Utils_Array::value('note', $params) ) {
  226. require_once 'CRM/Core/BAO/Note.php';
  227. $noteParams = array(
  228. 'entity_table' => 'civicrm_contribution',
  229. 'note' => $params['note'],
  230. 'entity_id' => $contribution->id,
  231. 'contact_id' => $session->get('userID'),
  232. 'modified_date' => date('Ymd')
  233. );
  234. if( ! $noteParams['contact_id'] ) {
  235. $noteParams['contact_id'] = $params['contact_id'];
  236. }
  237. CRM_Core_BAO_Note::add( $noteParams,
  238. CRM_Utils_Array::value( 'note', $ids ) );
  239. }
  240. // check if activity record exist for this contribution, if
  241. // not add activity
  242. require_once 'CRM/Activity/DAO/Activity.php';
  243. $activity = new CRM_Activity_DAO_Activity( );
  244. $activity->source_record_id = $contribution->id;
  245. $activity->activity_type_id = CRM_Core_OptionGroup::getValue( 'activity_type',
  246. 'Contribution',
  247. 'name' );
  248. if ( ! $activity->find( ) ) {
  249. require_once 'CRM/Activity/BAO/Activity.php';
  250. CRM_Activity_BAO_Activity::addActivity( $contribution, 'Offline' );
  251. }
  252. // Handle soft credit and / or link to personal campaign page
  253. if ( CRM_Utils_Array::value( 'soft_credit_to', $params ) ||
  254. CRM_Utils_Array::value( 'pcp_made_through_id', $params ) ) {
  255. $csParams = array();
  256. if ( $id = CRM_Utils_Array::value( 'softID', $params ) ) {
  257. $csParams['id'] = $params['softID'];
  258. }
  259. $csParams['pcp_display_in_roll'] = $params['pcp_display_in_roll']? 1 : 0;
  260. foreach ( array ('pcp_roll_nickname', 'pcp_personal_note' ) as $val ) {
  261. $csParams[$val] = $params[$val];
  262. }
  263. $csParams['contribution_id'] = $contribution->id;
  264. // If pcp_made_through_id set, we define soft_credit_to contact based on selected PCP,
  265. // else use passed soft_credit_to
  266. if ( CRM_Utils_Array::value( 'pcp_made_through_id', $params ) ) {
  267. $csParams['pcp_id'] = CRM_Utils_Array::value( 'pcp_made_through_id', $params );
  268. require_once 'CRM/Core/DAO.php';
  269. $csParams['contact_id'] = CRM_Core_DAO::getFieldValue( 'CRM_Contribute_DAO_PCP',
  270. $csParams['pcp_id'], 'contact_id' );
  271. } else {
  272. $csParams['contact_id'] = $params['soft_credit_to'];
  273. $csParams['pcp_id'] = '';
  274. }
  275. // first stage: we register whole amount as credited to given person
  276. $csParams['amount'] = $contribution->total_amount;
  277. self::addSoftContribution( $csParams );
  278. }
  279. $transaction->commit( );
  280. // do not add to recent items for import, CRM-4399
  281. if ( !CRM_Utils_Array::value( 'skipRecentView', $params ) ) {
  282. require_once 'CRM/Utils/Recent.php';
  283. require_once 'CRM/Contribute/PseudoConstant.php';
  284. require_once 'CRM/Contact/BAO/Contact.php';
  285. $url = CRM_Utils_System::url( 'civicrm/contact/view/contribution',
  286. "action=view&reset=1&id={$contribution->id}&cid={$contribution->contact_id}&context=home" );
  287. $contributionTypes = CRM_Contribute_PseudoConstant::contributionType();
  288. $title = CRM_Contact_BAO_Contact::displayName( $contribution->contact_id ) .
  289. ' - (' . CRM_Utils_Money::format( $contribution->total_amount, $contribution->currency ) . ' ' .
  290. ' - ' . $contributionTypes[$contribution->contribution_type_id] . ')';
  291. $recentOther = array( );
  292. if ( CRM_Core_Permission::checkActionPermission('CiviContribute', CRM_Core_Action::UPDATE) ) {
  293. $recentOther['editUrl'] = CRM_Utils_System::url( 'civicrm/contact/view/contribution',
  294. "action=update&reset=1&id={$contribution->id}&cid={$contribution->contact_id}&context=home" );
  295. }
  296. if ( CRM_Core_Permission::checkActionPermission('CiviContribute', CRM_Core_Action::DELETE) ) {
  297. $recentOther['deleteUrl'] = CRM_Utils_System::url( 'civicrm/contact/view/contribution',
  298. "action=delete&reset=1&id={$contribution->id}&cid={$contribution->contact_id}&context=home" );
  299. }
  300. // add the recently created Contribution
  301. CRM_Utils_Recent::add( $title,
  302. $url,
  303. $contribution->id,
  304. 'Contribution',
  305. $contribution->contact_id,
  306. null,
  307. $recentOther
  308. );
  309. }
  310. return $contribution;
  311. }
  312. /**
  313. * Get the values for pseudoconstants for name->value and reverse.
  314. *
  315. * @param array $defaults (reference) the default values, some of which need to be resolved.
  316. * @param boolean $reverse true if we want to resolve the values in the reverse direction (value -> name)
  317. *
  318. * @return void
  319. * @access public
  320. * @static
  321. */
  322. static function resolveDefaults(&$defaults, $reverse = false)
  323. {
  324. require_once 'CRM/Contribute/PseudoConstant.php';
  325. self::lookupValue($defaults, 'contribution_type', CRM_Contribute_PseudoConstant::contributionType(), $reverse);
  326. self::lookupValue($defaults, 'payment_instrument', CRM_Contribute_PseudoConstant::paymentInstrument(), $reverse);
  327. self::lookupValue($defaults, 'contribution_status', CRM_Contribute_PseudoConstant::contributionStatus(), $reverse);
  328. self::lookupValue($defaults, 'pcp', CRM_Contribute_PseudoConstant::pcPage(), $reverse);
  329. }
  330. /**
  331. * This function is used to convert associative array names to values
  332. * and vice-versa.
  333. *
  334. * This function is used by both the web form layer and the api. Note that
  335. * the api needs the name => value conversion, also the view layer typically
  336. * requires value => name conversion
  337. */
  338. static function lookupValue(&$defaults, $property, &$lookup, $reverse)
  339. {
  340. $id = $property . '_id';
  341. $src = $reverse ? $property : $id;
  342. $dst = $reverse ? $id : $property;
  343. if (!array_key_exists($src, $defaults)) {
  344. return false;
  345. }
  346. $look = $reverse ? array_flip($lookup) : $lookup;
  347. if(is_array($look)) {
  348. if (!array_key_exists($defaults[$src], $look)) {
  349. return false;
  350. }
  351. }
  352. $defaults[$dst] = $look[$defaults[$src]];
  353. return true;
  354. }
  355. /**
  356. * Takes a bunch of params that are needed to match certain criteria and
  357. * retrieves the relevant objects. We'll tweak this function to be more
  358. * full featured over a period of time. This is the inverse function of
  359. * create. It also stores all the retrieved values in the default array
  360. *
  361. * @param array $params (reference ) an assoc array of name/value pairs
  362. * @param array $defaults (reference ) an assoc array to hold the name / value pairs
  363. * in a hierarchical manner
  364. * @param array $ids (reference) the array that holds all the db ids
  365. *
  366. * @return object CRM_Contribute_BAO_Contribution object
  367. * @access public
  368. * @static
  369. */
  370. static function retrieve( &$params, &$defaults, &$ids )
  371. {
  372. $contribution = CRM_Contribute_BAO_Contribution::getValues( $params, $defaults, $ids );
  373. return $contribution;
  374. }
  375. /**
  376. * combine all the importable fields from the lower levels object
  377. *
  378. * The ordering is important, since currently we do not have a weight
  379. * scheme. Adding weight is super important and should be done in the
  380. * next week or so, before this can be called complete.
  381. *
  382. * @return array array of importable Fields
  383. * @access public
  384. */
  385. function &importableFields( $contacType = 'Individual', $status = true )
  386. {
  387. if ( ! self::$_importableFields ) {
  388. if ( ! self::$_importableFields ) {
  389. self::$_importableFields = array();
  390. }
  391. if (!$status) {
  392. $fields = array( '' => array( 'title' => ts('- do not import -') ) );
  393. } else {
  394. $fields = array( '' => array( 'title' => ts('- Contribution Fields -') ) );
  395. }
  396. require_once 'CRM/Core/DAO/Note.php';
  397. $note = CRM_Core_DAO_Note::import( );
  398. $tmpFields = CRM_Contribute_DAO_Contribution::import( );
  399. unset($tmpFields['option_value']);
  400. require_once 'CRM/Core/OptionValue.php';
  401. $optionFields = CRM_Core_OptionValue::getFields($mode ='contribute' );
  402. require_once 'CRM/Contact/BAO/Contact.php';
  403. $contactFields = CRM_Contact_BAO_Contact::importableFields( $contacType, null );
  404. // Using new Dedupe rule.
  405. $ruleParams = array(
  406. 'contact_type' => $contacType,
  407. 'level' => 'Strict'
  408. );
  409. require_once 'CRM/Dedupe/BAO/Rule.php';
  410. $fieldsArray = CRM_Dedupe_BAO_Rule::dedupeRuleFields($ruleParams);
  411. $tmpConatctField = array();
  412. if( is_array($fieldsArray) ) {
  413. foreach ( $fieldsArray as $value) {
  414. //skip if there is no dupe rule
  415. if ( $value == 'none' ) {
  416. continue;
  417. }
  418. $customFieldId = CRM_Core_DAO::getFieldValue( 'CRM_Core_DAO_CustomField',
  419. $value,
  420. 'id',
  421. 'column_name' );
  422. $value = $customFieldId ? 'custom_'.$customFieldId : $value;
  423. $tmpConatctField[trim($value)] = $contactFields[trim($value)];
  424. if (!$status) {
  425. $title = $tmpConatctField[trim($value)]['title']. ' '. ts('(match to contact)') ;
  426. } else {
  427. $title = $tmpConatctField[trim($value)]['title'];
  428. }
  429. $tmpConatctField[trim($value)]['title'] = $title;
  430. }
  431. }
  432. $tmpConatctField['external_identifier'] = $contactFields['external_identifier'];
  433. $tmpConatctField['external_identifier']['title'] = $contactFields['external_identifier']['title'] . ' '. ts('(match to contact)') ;
  434. $tmpFields['contribution_contact_id']['title'] = $tmpFields['contribution_contact_id']['title'] . ' '. ts('(match to contact)') ;
  435. $fields = array_merge($fields, $tmpConatctField);
  436. $fields = array_merge($fields, $tmpFields);
  437. $fields = array_merge($fields, $note);
  438. $fields = array_merge($fields, $optionFields);
  439. require_once 'CRM/Contribute/DAO/ContributionType.php';
  440. $fields = array_merge($fields, CRM_Contribute_DAO_ContributionType::export( ) );
  441. $fields = array_merge($fields, CRM_Core_BAO_CustomField::getFieldsForImport('Contribution'));
  442. self::$_importableFields = $fields;
  443. }
  444. return self::$_importableFields;
  445. }
  446. function &exportableFields( )
  447. {
  448. if ( ! self::$_exportableFields ) {
  449. if ( ! self::$_exportableFields ) {
  450. self::$_exportableFields = array();
  451. }
  452. require_once 'CRM/Core/OptionValue.php';
  453. require_once 'CRM/Contribute/DAO/Product.php';
  454. require_once 'CRM/Contribute/DAO/ContributionProduct.php';
  455. require_once 'CRM/Contribute/DAO/ContributionType.php';
  456. $impFields = CRM_Contribute_DAO_Contribution::export( );
  457. $expFieldProduct = CRM_Contribute_DAO_Product::export( );
  458. $expFieldsContrib = CRM_Contribute_DAO_ContributionProduct::export( );
  459. $typeField = CRM_Contribute_DAO_ContributionType::export( );
  460. $optionField = CRM_Core_OptionValue::getFields($mode ='contribute' );
  461. $contributionStatus = array( 'contribution_status' => array( 'title' => 'Contribution Status',
  462. 'name' => 'contribution_status',
  463. 'data_type' => CRM_Utils_Type::T_STRING ) );
  464. $contributionNote = array( 'contribution_note' => array( 'title' => ts('Contribution Note'),
  465. 'name' => 'contribution_note',
  466. 'data_type' => CRM_Utils_Type::T_TEXT ) );
  467. $contributionRecurId = array( 'contribution_recur_id' => array ( 'title' => ts('Recurring Contributions ID'),
  468. 'name' => 'contribution_recur_id',
  469. 'where' => 'civicrm_contribution.contribution_recur_id',
  470. 'data_type' => CRM_Utils_Type::T_INT ) );
  471. $campaign = array( 'contribution_campaign' => array( 'title' => ts( 'Campaign Title' ) ) );
  472. $fields = array_merge( $impFields, $typeField, $contributionStatus, $optionField, $expFieldProduct,
  473. $expFieldsContrib, $contributionNote, $contributionRecurId, $campaign,
  474. CRM_Core_BAO_CustomField::getFieldsForImport('Contribution') );
  475. self::$_exportableFields = $fields;
  476. }
  477. return self::$_exportableFields;
  478. }
  479. function getTotalAmountAndCount( $status = null, $startDate = null, $endDate = null )
  480. {
  481. $where = array( );
  482. switch ( $status ) {
  483. case 'Valid':
  484. $where[] = 'contribution_status_id = 1';
  485. break;
  486. case 'Cancelled':
  487. $where[] = 'contribution_status_id = 3';
  488. break;
  489. }
  490. if ( $startDate ) {
  491. $where[] = "receive_date >= '" . CRM_Utils_Type::escape( $startDate, 'Timestamp' ) . "'";
  492. }
  493. if ( $endDate ) {
  494. $where[] = "receive_date <= '" . CRM_Utils_Type::escape( $endDate, 'Timestamp' ) . "'";
  495. }
  496. $whereCond = implode( ' AND ', $where );
  497. $query = "
  498. SELECT sum( total_amount ) as total_amount,
  499. count( civicrm_contribution.id ) as total_count,
  500. currency
  501. FROM civicrm_contribution
  502. INNER JOIN civicrm_contact contact ON ( contact.id = civicrm_contribution.contact_id )
  503. WHERE $whereCond
  504. AND ( is_test = 0 OR is_test IS NULL )
  505. AND contact.is_deleted = 0
  506. GROUP BY currency
  507. ";
  508. $dao = CRM_Core_DAO::executeQuery( $query, CRM_Core_DAO::$_nullArray );
  509. $amount = array( );
  510. $count = 0;
  511. require_once 'CRM/Utils/Money.php';
  512. while ( $dao->fetch( ) ) {
  513. $count += $dao->total_count;
  514. $amount[] = CRM_Utils_Money::format( $dao->total_amount, $dao->currency );
  515. }
  516. if ( $count ) {
  517. return array( 'amount' => implode( ', ', $amount ),
  518. 'count' => $count );
  519. }
  520. return null;
  521. }
  522. /**
  523. * Delete the indirect records associated with this contribution first
  524. *
  525. * @return $results no of deleted Contribution on success, false otherwise
  526. * @access public
  527. * @static
  528. */
  529. static function deleteContribution( $id )
  530. {
  531. require_once 'CRM/Utils/Hook.php';
  532. CRM_Utils_Hook::pre( 'delete', 'Contribution', $id, CRM_Core_DAO::$_nullArray );
  533. require_once 'CRM/Core/Transaction.php';
  534. $transaction = new CRM_Core_Transaction( );
  535. $results = null;
  536. //delete activity record
  537. require_once 'CRM/Activity/BAO/Activity.php';
  538. $params = array( 'source_record_id' => $id,
  539. 'activity_type_id' => 6 );// activity type id for contribution
  540. CRM_Activity_BAO_Activity::deleteActivity( $params );
  541. //delete billing address if exists for this contribution.
  542. self::deleteAddress( $id );
  543. //update pledge and pledge payment, CRM-3961
  544. require_once 'CRM/Pledge/BAO/Payment.php';
  545. CRM_Pledge_BAO_Payment::resetPledgePayment( $id );
  546. // remove entry from civicrm_price_set_entity, CRM-5095
  547. require_once 'CRM/Price/BAO/Set.php';
  548. if ( CRM_Price_BAO_Set::getFor( 'civicrm_contribution', $id ) ) {
  549. CRM_Price_BAO_Set::removeFrom( 'civicrm_contribution', $id );
  550. }
  551. // cleanup line items.
  552. require_once 'CRM/Price/BAO/Field.php';
  553. require_once 'CRM/Event/BAO/ParticipantPayment.php';
  554. $participantId = CRM_Core_DAO::getFieldValue( 'CRM_Event_DAO_ParticipantPayment', $id, 'participant_id' , 'contribution_id');
  555. // delete any related entity_financial_trxn and financial_trxn records.
  556. require_once 'CRM/Core/BAO/FinancialTrxn.php';
  557. CRM_Core_BAO_FinancialTrxn::deleteFinancialTrxn($id, 'civicrm_contribution');
  558. if ( $participantId ) {
  559. require_once 'CRM/Price/BAO/LineItem.php';
  560. CRM_Price_BAO_LineItem::deleteLineItems( $participantId, 'civicrm_participant' );
  561. } else {
  562. require_once 'CRM/Price/BAO/LineItem.php';
  563. CRM_Price_BAO_LineItem::deleteLineItems( $id, 'civicrm_contribution' );
  564. }
  565. //delete note.
  566. require_once 'CRM/Core/BAO/Note.php';
  567. $note = CRM_Core_BAO_Note::getNote( $id, 'civicrm_contribution' );
  568. $noteId = key( $note );
  569. if ( $noteId ) {
  570. CRM_Core_BAO_Note::del( $noteId, false );
  571. }
  572. $dao = new CRM_Contribute_DAO_Contribution( );
  573. $dao->id = $id;
  574. $results = $dao->delete( );
  575. $transaction->commit( );
  576. CRM_Utils_Hook::post( 'delete', 'Contribution', $dao->id, $dao );
  577. // delete the recently created Contribution
  578. require_once 'CRM/Utils/Recent.php';
  579. $contributionRecent = array(
  580. 'id' => $id,
  581. 'type' => 'Contribution'
  582. );
  583. CRM_Utils_Recent::del( $contributionRecent );
  584. return $results;
  585. }
  586. /**
  587. * Check if there is a contribution with the same trxn_id or invoice_id
  588. *
  589. * @param array $params (reference ) an assoc array of name/value pairs
  590. * @param array $duplicates (reference ) store ids of duplicate contribs
  591. *
  592. * @return boolean true if duplicate, false otherwise
  593. * @access public
  594. * static
  595. */
  596. static function checkDuplicate( $input, &$duplicates, $id = null )
  597. {
  598. if ( ! $id ) {
  599. $id = CRM_Utils_Array::value( 'id' , $input );
  600. }
  601. $trxn_id = CRM_Utils_Array::value( 'trxn_id' , $input );
  602. $invoice_id = CRM_Utils_Array::value( 'invoice_id', $input );
  603. $clause = array( );
  604. $input = array( );
  605. if ( $trxn_id ) {
  606. $clause[] = "trxn_id = %1";
  607. $input[1] = array( $trxn_id, 'String' );
  608. }
  609. if ( $invoice_id ) {
  610. $clause[] = "invoice_id = %2";
  611. $input[2] = array( $invoice_id, 'String' );
  612. }
  613. if ( empty( $clause ) ) {
  614. return false;
  615. }
  616. $clause = implode( ' OR ', $clause );
  617. if ( $id ) {
  618. $clause = "( $clause ) AND id != %3";
  619. $input[3] = array( $id, 'Integer' );
  620. }
  621. $query = "SELECT id FROM civicrm_contribution WHERE $clause";
  622. $dao =& CRM_Core_DAO::executeQuery( $query, $input );
  623. $result = false;
  624. while ( $dao->fetch( ) ) {
  625. $duplicates[] = $dao->id;
  626. $result = true;
  627. }
  628. return $result;
  629. }
  630. /**
  631. * takes an associative array and creates a contribution_product object
  632. *
  633. * the function extract all the params it needs to initialize the create a
  634. * contribution_product object. the params array could contain additional unused name/value
  635. * pairs
  636. *
  637. * @param array $params (reference ) an assoc array of name/value pairs
  638. *
  639. * @return object CRM_Contribute_BAO_ContributionProduct object
  640. * @access public
  641. * @static
  642. */
  643. static function addPremium ( &$params )
  644. {
  645. require_once 'CRM/Contribute/DAO/ContributionProduct.php';
  646. $contributionProduct = new CRM_Contribute_DAO_ContributionProduct();
  647. $contributionProduct->copyValues($params);
  648. return $contributionProduct->save();
  649. }
  650. /**
  651. * Function to get list of contribution fields for profile
  652. * For now we only allow custom contribution fields to be in
  653. * profile
  654. *
  655. * @return return the list of contribution fields
  656. * @static
  657. * @access public
  658. */
  659. static function getContributionFields( )
  660. {
  661. $contributionFields =& CRM_Contribute_DAO_Contribution::export( );
  662. require_once 'CRM/Core/OptionValue.php';
  663. $contributionFields = array_merge( $contributionFields, CRM_Core_OptionValue::getFields($mode ='contribute' ) );
  664. require_once 'CRM/Contribute/DAO/ContributionType.php';
  665. $contributionFields = array_merge( $contributionFields, CRM_Contribute_DAO_ContributionType::export( ) );
  666. foreach ($contributionFields as $key => $var) {
  667. if ($key == 'contribution_contact_id') {
  668. continue;
  669. } else if ( $key == 'contribution_campaign_id' ) {
  670. $var['title'] = ts( 'Campaign' );
  671. }
  672. $fields[$key] = $var;
  673. }
  674. $fields = array_merge($fields, CRM_Core_BAO_CustomField::getFieldsForImport('Contribution'));
  675. return $fields;
  676. }
  677. static function getCurrentandGoalAmount( $pageID )
  678. {
  679. $query = "
  680. SELECT p.goal_amount as goal, sum( c.total_amount ) as total
  681. FROM civicrm_contribution_page p,
  682. civicrm_contribution c
  683. WHERE p.id = c.contribution_page_id
  684. AND p.id = %1
  685. AND c.cancel_date is null
  686. GROUP BY p.id
  687. ";
  688. $config = CRM_Core_Config::singleton( );
  689. $params = array( 1 => array( $pageID, 'Integer' ) );
  690. $dao =& CRM_Core_DAO::executeQuery( $query, $params );
  691. if ( $dao->fetch( ) ) {
  692. return array( $dao->goal, $dao->total );
  693. } else {
  694. return array( null, null );
  695. }
  696. }
  697. /**
  698. * Function to create is honor of
  699. *
  700. * @param array $params associated array of fields (by reference)
  701. * @param int $honorId honor Id
  702. *
  703. * @return contact id
  704. */
  705. function createHonorContact( &$params, $honorId = null )
  706. {
  707. $honorParams = array( 'first_name' => $params['honor_first_name'],
  708. 'last_name' => $params['honor_last_name'],
  709. 'prefix_id' => $params['honor_prefix_id'],
  710. 'email-Primary' => $params['honor_email'] );
  711. if ( !$honorId ) {
  712. require_once 'CRM/Core/BAO/UFGroup.php';
  713. $honorParams['email'] = $params['honor_email'];
  714. require_once 'CRM/Dedupe/Finder.php';
  715. $dedupeParams = CRM_Dedupe_Finder::formatParams($honorParams, 'Individual');
  716. $dedupeParams['check_permission'] = false;
  717. $ids = CRM_Dedupe_Finder::dupesByParams($dedupeParams, 'Individual');
  718. // if we find more than one contact, use the first one
  719. $honorId = CRM_Utils_Array::value( 0, $ids );
  720. }
  721. $contact =& CRM_Contact_BAO_Contact::createProfileContact( $honorParams,
  722. CRM_Core_DAO::$_nullArray,
  723. $honorId );
  724. return $contact;
  725. }
  726. /**
  727. * Function to get list of contribution In Honor of contact Ids
  728. *
  729. * @param int $honorId In Honor of Contact ID
  730. *
  731. * @return return the list of contribution fields
  732. *
  733. * @access public
  734. * @static
  735. */
  736. static function getHonorContacts( $honorId )
  737. {
  738. $params=array( );
  739. require_once 'CRM/Contribute/DAO/Contribution.php';
  740. $honorDAO = new CRM_Contribute_DAO_Contribution();
  741. $honorDAO->honor_contact_id = $honorId;
  742. $honorDAO->find( );
  743. require_once 'CRM/Contribute/PseudoConstant.php';
  744. $status = CRM_Contribute_Pseudoconstant::contributionStatus($honorDAO->contribution_status_id);
  745. $type = CRM_Contribute_Pseudoconstant::contributionType();
  746. while( $honorDAO->fetch( ) ) {
  747. $params[$honorDAO->id]['honorId'] = $honorDAO->contact_id;
  748. $params[$honorDAO->id]['display_name'] = CRM_Core_DAO::getFieldValue( 'CRM_Contact_DAO_Contact', $honorDAO->contact_id, 'display_name' );
  749. $params[$honorDAO->id]['type'] = $type[$honorDAO->contribution_type_id];
  750. $params[$honorDAO->id]['type_id'] = $honorDAO->contribution_type_id;
  751. $params[$honorDAO->id]['amount'] = CRM_Utils_Money::format( $honorDAO->total_amount , $honorDAO->currency );
  752. $params[$honorDAO->id]['source'] = $honorDAO->source;
  753. $params[$honorDAO->id]['receive_date'] = $honorDAO->receive_date;
  754. $params[$honorDAO->id]['contribution_status']= CRM_Utils_Array::value($honorDAO->contribution_status_id, $status);
  755. }
  756. return $params;
  757. }
  758. /**
  759. * function to get the sort name of a contact for a particular contribution
  760. *
  761. * @param int $id id of the contribution
  762. *
  763. * @return null|string sort name of the contact if found
  764. * @static
  765. * @access public
  766. */
  767. static function sortName( $id )
  768. {
  769. $id = CRM_Utils_Type::escape( $id, 'Integer' );
  770. $query = "
  771. SELECT civicrm_contact.sort_name
  772. FROM civicrm_contribution, civicrm_contact
  773. WHERE civicrm_contribution.contact_id = civicrm_contact.id
  774. AND civicrm_contribution.id = {$id}
  775. ";
  776. return CRM_Core_DAO::singleValueQuery( $query, CRM_Core_DAO::$_nullArray );
  777. }
  778. static function annual( $contactID ) {
  779. if ( is_array( $contactID ) ) {
  780. $contactIDs = implode( ',', $contactID );
  781. } else {
  782. $contactIDs = $contactID;
  783. }
  784. $config = CRM_Core_Config::singleton( );
  785. $startDate = $endDate = null;
  786. $currentMonth = date( 'm' );
  787. $currentDay = date( 'd' );
  788. if ( (int ) $config->fiscalYearStart['M'] > $currentMonth ||
  789. ( (int ) $config->fiscalYearStart['M'] == $currentMonth &&
  790. (int ) $config->fiscalYearStart['d'] > $currentDay ) ) {
  791. $year = date( 'Y' ) - 1;
  792. } else {
  793. $year = date( 'Y' );
  794. }
  795. $nextYear = $year + 1;
  796. if ( $config->fiscalYearStart ) {
  797. if ( $config->fiscalYearStart['M'] < 10 ) {
  798. $config->fiscalYearStart['M'] = '0' . $config->fiscalYearStart['M'];
  799. }
  800. if ( $config->fiscalYearStart['d'] < 10 ) {
  801. $config->fiscalYearStart['d'] = '0' . $config->fiscalYearStart['d'];
  802. }
  803. $monthDay = $config->fiscalYearStart['M'] . $config->fiscalYearStart['d'];
  804. } else {
  805. $monthDay = '0101';
  806. }
  807. $startDate = "$year$monthDay";
  808. $endDate = "$nextYear$monthDay";
  809. $query = "
  810. SELECT count(*) as count,
  811. sum(total_amount) as amount,
  812. avg(total_amount) as average,
  813. currency
  814. FROM civicrm_contribution b
  815. WHERE b.contact_id IN ( $contactIDs )
  816. AND b.contribution_status_id = 1
  817. AND b.is_test = 0
  818. AND b.receive_date >= $startDate
  819. AND b.receive_date < $endDate
  820. GROUP BY currency
  821. ";
  822. $dao =& CRM_Core_DAO::executeQuery( $query, CRM_Core_DAO::$_nullArray );
  823. $count = 0;
  824. $amount = $average = array( );
  825. require_once 'CRM/Utils/Money.php';
  826. while ( $dao->fetch( ) ) {
  827. if ( $dao->count > 0 && $dao->amount > 0) {
  828. $count += $dao->count;
  829. $amount[] = CRM_Utils_Money::format( $dao->amount , $dao->currency );
  830. $average[] = CRM_Utils_Money::format( $dao->average, $dao->currency );
  831. }
  832. }
  833. if ( $count > 0 ) {
  834. return array( $count,
  835. implode( ',&nbsp;', $amount ),
  836. implode( ',&nbsp;', $average ) );
  837. }
  838. return array( 0, 0, 0 );
  839. }
  840. /**
  841. * Check if there is a contribution with the params passed in.
  842. * Used for trxn_id,invoice_id and contribution_id
  843. *
  844. * @param array $params (reference ) an assoc array of name/value pairs
  845. *
  846. * @return array contribution id if success else NULL
  847. * @access public
  848. * static
  849. */
  850. static function checkDuplicateIds( $params )
  851. {
  852. $dao = new CRM_Contribute_DAO_Contribution();
  853. $clause = array( );
  854. $input = array( );
  855. foreach ( $params as $k=>$v ) {
  856. if( $v ) {
  857. $clause[] = "$k = '$v'";
  858. }
  859. }
  860. $clause = implode( ' AND ', $clause );
  861. $query = "SELECT id FROM civicrm_contribution WHERE $clause";
  862. $dao =& CRM_Core_DAO::executeQuery( $query, $input );
  863. while ( $dao->fetch( ) ) {
  864. $result = $dao->id;
  865. return $result;
  866. }
  867. return NULL;
  868. }
  869. /**
  870. * Function to get the contribution details for component export
  871. *
  872. * @param int $exportMode export mode
  873. * @param string $componentIds component ids
  874. *
  875. * @return array associated array
  876. *
  877. * @static
  878. * @access public
  879. */
  880. static function getContributionDetails( $exportMode, $componentIds )
  881. {
  882. require_once 'CRM/Export/Form/Select.php';
  883. $paymentDetails = array( );
  884. $componentClause = ' IN ( ' . implode( ',', $componentIds ) . ' ) ';
  885. if ( $exportMode == CRM_Export_Form_Select::EVENT_EXPORT ) {
  886. $componentSelect = " civicrm_participant_payment.participant_id id";
  887. $additionalClause = "
  888. INNER JOIN civicrm_participant_payment ON (civicrm_contribution.id = civicrm_participant_payment.contribution_id
  889. AND civicrm_participant_payment.participant_id {$componentClause} )
  890. ";
  891. } else if ( $exportMode == CRM_Export_Form_Select::MEMBER_EXPORT ) {
  892. $componentSelect = " civicrm_membership_payment.membership_id id";
  893. $additionalClause = "
  894. INNER JOIN civicrm_membership_payment ON (civicrm_contribution.id = civicrm_membership_payment.contribution_id
  895. AND civicrm_membership_payment.membership_id {$componentClause} )
  896. ";
  897. } else if ( $exportMode == CRM_Export_Form_Select::PLEDGE_EXPORT ) {
  898. $componentSelect = " civicrm_pledge_payment.id id";
  899. $additionalClause = "
  900. INNER JOIN civicrm_pledge_payment ON (civicrm_contribution.id = civicrm_pledge_payment.contribution_id
  901. AND civicrm_pledge_payment.pledge_id {$componentClause} )
  902. ";
  903. }
  904. $query = " SELECT total_amount, contribution_status.name as status_id, contribution_status.label as status, payment_instrument.name as payment_instrument, receive_date,
  905. trxn_id, {$componentSelect}
  906. FROM civicrm_contribution
  907. LEFT JOIN civicrm_option_group option_group_payment_instrument ON ( option_group_payment_instrument.name = 'payment_instrument')
  908. LEFT JOIN civicrm_option_value payment_instrument ON (civicrm_contribution.payment_instrument_id = payment_instrument.value
  909. AND option_group_payment_instrument.id = payment_instrument.option_group_id )
  910. LEFT JOIN civicrm_option_group option_group_contribution_status ON (option_group_contribution_status.name = 'contribution_status')
  911. LEFT JOIN civicrm_option_value contribution_status ON (civicrm_contribution.contribution_status_id = contribution_status.value
  912. AND option_group_contribution_status.id = contribution_status.option_group_id )
  913. {$additionalClause}
  914. ";
  915. $dao =& CRM_Core_DAO::executeQuery( $query, CRM_Core_DAO::$_nullArray );
  916. while ( $dao->fetch() ) {
  917. $paymentDetails[$dao->id] = array ( 'total_amount' => $dao->total_amount,
  918. 'contribution_status' => $dao->status,
  919. 'receive_date' => $dao->receive_date,
  920. 'pay_instru' => $dao->payment_instrument,
  921. 'trxn_id' => $dao->trxn_id );
  922. }
  923. return $paymentDetails;
  924. }
  925. /**
  926. * Function to create address associated with contribution record.
  927. * @param array $params an associated array
  928. * @param int $billingID $billingLocationTypeID
  929. *
  930. * @return address id
  931. * @static
  932. */
  933. static function createAddress( &$params, $billingLocationTypeID )
  934. {
  935. $billingFields = array( 'street_address',
  936. 'city',
  937. 'state_province_id',
  938. 'postal_code',
  939. 'country_id'
  940. );
  941. //build address array
  942. $addressParams = array( );
  943. $addressParams['location_type_id'] = $billingLocationTypeID;
  944. $addressParams['is_billing'] = 1;
  945. $addressParams['address_name'] = "{$params['billing_first_name']}" . CRM_Core_DAO::VALUE_SEPARATOR . "{$params['billing_middle_name']}" . CRM_Core_DAO::VALUE_SEPARATOR . "{$params['billing_last_name']}";
  946. foreach ( $billingFields as $value ) {
  947. $addressParams[$value] = $params["billing_{$value}-{$billingLocationTypeID}"];
  948. }
  949. require_once 'CRM/Core/BAO/Address.php';
  950. $address = CRM_Core_BAO_Address::add( $addressParams, false );
  951. return $address->id;
  952. }
  953. /**
  954. * Function to create soft contributon with contribution record.
  955. * @param array $params an associated array
  956. *
  957. * @return soft contribution id
  958. * @static
  959. */
  960. static function addSoftContribution( $params )
  961. {
  962. require_once 'CRM/Contribute/DAO/ContributionSoft.php';
  963. $softContribution = new CRM_Contribute_DAO_ContributionSoft();
  964. $softContribution->copyValues($params);
  965. // set currency for CRM-1496
  966. if ( ! isset( $softContribution->currency ) ) {
  967. $config =& CRM_Core_Config::singleton( );
  968. $softContribution->currency = $config->defaultCurrency;
  969. }
  970. return $softContribution->save();
  971. }
  972. /**
  973. * Function to retrieve soft contributon for contribution record.
  974. * @param array $params an associated array
  975. *
  976. * @return soft contribution id
  977. * @static
  978. */
  979. static function getSoftContribution( $params, $all = false )
  980. {
  981. require_once 'CRM/Contribute/DAO/ContributionSoft.php';
  982. $cs = new CRM_Contribute_DAO_ContributionSoft( );
  983. $cs->copyValues( $params );
  984. $softContribution = array();
  985. if ( $cs->find(true) ) {
  986. if ( $all ){
  987. foreach ( array ('pcp_id','pcp_display_in_roll', 'pcp_roll_nickname', 'pcp_personal_note' ) as $key=>$val ) {
  988. $softContribution[$val] = $cs->$val;
  989. }
  990. }
  991. $softContribution['soft_credit_to'] = $cs->contact_id;
  992. $softContribution['soft_credit_id'] = $cs->id;
  993. }
  994. return $softContribution;
  995. }
  996. /**
  997. * Function to retrieve the list of soft contributons for given contact.
  998. * @param int $contact_id contact id
  999. *
  1000. * @return array
  1001. * @static
  1002. */
  1003. static function getSoftContributionList( $contact_id, $isTest = 0 )
  1004. {
  1005. $query = "SELECT ccs.id, ccs.amount as amount,
  1006. ccs.contribution_id,
  1007. ccs.pcp_id,
  1008. ccs.pcp_display_in_roll,
  1009. ccs.pcp_roll_nickname,
  1010. ccs.pcp_personal_note,
  1011. cc.receive_date,
  1012. cc.contact_id as contributor_id,
  1013. cc.contribution_status_id as contribution_status_id,
  1014. cp.title as pcp_title,
  1015. cc.currency,
  1016. contact.display_name,
  1017. cct.name as contributionType
  1018. FROM civicrm_contribution_soft ccs
  1019. LEFT JOIN civicrm_contribution cc
  1020. ON ccs.contribution_id = cc.id
  1021. LEFT JOIN civicrm_pcp cp
  1022. ON ccs.pcp_id = cp.id
  1023. LEFT JOIN civicrm_contact contact
  1024. ON ccs.contribution_id = cc.id AND
  1025. cc.contact_id = contact.id
  1026. LEFT JOIN civicrm_contribution_type cct
  1027. ON cc.contribution_type_id = cct.id
  1028. WHERE cc.is_test = {$isTest} AND ccs.contact_id = " . $contact_id;
  1029. $cs = CRM_Core_DAO::executeQuery( $query, CRM_Core_DAO::$_nullArray );
  1030. require_once 'CRM/Contribute/PseudoConstant.php';
  1031. $contributionStatus = CRM_Contribute_Pseudoconstant::contributionStatus( );
  1032. $result = array();
  1033. while( $cs->fetch( ) ) {
  1034. $result[$cs->id]['amount'] = $cs->amount;
  1035. $result[$cs->id]['currency'] = $cs->currency;
  1036. $result[$cs->id]['contributor_id'] = $cs->contributor_id;
  1037. $result[$cs->id]['contribution_id'] = $cs->contribution_id;
  1038. $result[$cs->id]['contributor_name'] = $cs->display_name;
  1039. $result[$cs->id]['contribution_type'] = $cs->contributionType;
  1040. $result[$cs->id]['receive_date'] = $cs->receive_date;
  1041. $result[$cs->id]['pcp_id'] = $cs->pcp_id;
  1042. $result[$cs->id]['pcp_title'] = $cs->pcp_title;
  1043. $result[$cs->id]['pcp_display_in_roll'] = $cs->pcp_display_in_roll;
  1044. $result[$cs->id]['pcp_roll_nickname'] = $cs-

Large files files are truncated, but you can click here to view the full file