INFRA-132 - CRM/Upgrade - Convert single-line @param to multi-line
[civicrm-core.git] / CRM / Upgrade / Incremental / php / FourThree.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. |
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 along 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 *
30 * @package CRM
31 * @copyright CiviCRM LLC (c) 2004-2014
32 * $Id$
33 *
34 */
35 class CRM_Upgrade_Incremental_php_FourThree {
36 const BATCH_SIZE = 5000;
37
38 /**
39 * @param $errors
40 *
41 * @return bool
42 */
43 public function verifyPreDBstate(&$errors) {
44 return TRUE;
45 }
46
47 /**
48 * Compute any messages which should be displayed beforeupgrade
49 *
50 * Note: This function is called iteratively for each upcoming
51 * revision to the database.
52 *
53 * @param $preUpgradeMessage
54 * @param $rev
55 * String, a version number, e.g. '4.3.alpha1', '4.3.beta3', '4.3.0'.
56 * @param null $currentVer
57 *
58 * @return void|bool
59 */
60 public function setPreUpgradeMessage(&$preUpgradeMessage, $rev, $currentVer = NULL) {
61 if ($rev == '4.3.beta3') {
62 //CRM-12084
63 //sql for checking orphaned contribution records
64 $sql = "SELECT COUNT(ct.id) FROM civicrm_contribution ct LEFT JOIN civicrm_contact c ON ct.contact_id = c.id WHERE c.id IS NULL";
65 $count = CRM_Core_DAO::singleValueQuery($sql, array(), TRUE, FALSE);
66
67 if ($count > 0) {
68 $error = ts("There is a data integrity issue with this CiviCRM database. It contains %1 contribution records which are linked to contact records that have been deleted. You will need to correct this manually before you can run the upgrade. Use the following MySQL query to identify the problem records: %2 These records will need to be deleted or linked to an existing contact record.", array(1 => $count, 2 => '<em>SELECT ct.* FROM civicrm_contribution ct LEFT JOIN civicrm_contact c ON ct.contact_id = c.id WHERE c.id IS NULL;</em>'));
69 CRM_Core_Error::fatal($error);
70 return FALSE;
71 }
72 }
73 if ($rev == '4.3.beta4' && CRM_Utils_Constant::value('CIVICRM_UF', FALSE) == 'Drupal6') {
74 // CRM-11823 - Make sure the D6 HTML HEAD technique will work on
75 // upgrade pages ... except when we're in Drush.
76 if (!function_exists('drush_main')) {
77 theme('item_list', array()); // force-load theme registry
78 $theme_registry = theme_get_registry();
79 if (!isset($theme_registry['page']['preprocess functions']) || FALSE === array_search('civicrm_preprocess_page_inject', $theme_registry['page']['preprocess functions'])) {
80 CRM_Core_Error::fatal('Please reset the Drupal cache (Administer => Site Configuration => Performance => Clear cached data))');
81 }
82 }
83 }
84
85 if ($rev == '4.3.6') {
86 $constraintArray = array(
87 'civicrm_contact' => 'contact_id',
88 'civicrm_payment_processor' => 'payment_processor_id',
89 );
90
91 if (version_compare('4.1alpha1', $currentVer) <= 0) {
92 $constraintArray['civicrm_campaign'] = 'campaign_id';
93 }
94
95 if (version_compare('4.3alpha1', $currentVer) <= 0) {
96 $constraintArray['civicrm_financial_type'] = 'financial_type_id';
97 }
98
99 foreach ($constraintArray as $key => $value) {
100 $query = "SELECT contri_recur.id FROM civicrm_contribution_recur contri_recur LEFT JOIN {$key} ON contri_recur.{$value} = {$key}.id
101 WHERE {$key}.id IS NULL";
102 if ($value != 'contact_id') {
103 $query .= " AND contri_recur.{$value} IS NOT NULL ";
104 }
105 $dao = CRM_Core_DAO::executeQuery($query);
106 if ($dao->N) {
107 $invalidDataMessage = '<strong>Oops, it looks like you have orphaned recurring contribution records in your database. Before this upgrade can complete they will need to be fixed or deleted. <a href="http://wiki.civicrm.org/confluence/display/CRMDOC/Fixing+Orphaned+Contribution+Recur+Records" target="_blank">You can review steps to correct this situation on the documentation wiki.</a></strong>';
108 CRM_Core_Error::fatal($invalidDataMessage);
109 return FALSE;
110 }
111 }
112 }
113 }
114
115 /**
116 * Compute any messages which should be displayed after upgrade
117 *
118 * @param $postUpgradeMessage
119 * String, alterable.
120 * @param $rev
121 * String, an intermediate version; note that setPostUpgradeMessage is called repeatedly with different $revs.
122 * @return void
123 */
124 public function setPostUpgradeMessage(&$postUpgradeMessage, $rev) {
125 if ($rev == '4.3.alpha1') {
126 // check if CiviMember component is enabled
127 $config = CRM_Core_Config::singleton();
128 if (in_array('CiviMember', $config->enableComponents)) {
129 $postUpgradeMessage .= '<br />' . ts('Membership renewal reminders must now be configured using the Schedule Reminders feature, which supports multiple renewal reminders (Administer > Communications > Schedule Reminders). The Update Membership Statuses scheduled job will no longer send membershp renewal reminders. You can use your existing renewal reminder message template(s) with the Schedule Reminders feature.');
130 $postUpgradeMessage .= '<br />' . ts('The Set Membership Reminder Dates scheduled job has been deleted since membership reminder dates stored in the membership table are no longer in use.');
131 }
132
133 //CRM-11636
134 //here we do the financial type check and migration
135 $isDefaultsModified = self::_checkAndMigrateDefaultFinancialTypes();
136 if($isDefaultsModified) {
137 $postUpgradeMessage .= '<br />' . ts('Please review all price set financial type assignments.');
138 }
139 list($context, $orgName) = self::createDomainContacts();
140 if ($context == 'added') {
141 $postUpgradeMessage .= '<br />' . ts("A new organization contact has been added as the default domain contact using the information from your Organization Address and Contact Info settings: '%1'.", array(1 => $orgName));
142 }
143 elseif ($context == 'merged') {
144 $postUpgradeMessage .= '<br />' . ts("The existing organization contact record for '%1' has been marked as the default domain contact, and has been updated with information from your Organization Address and Contact Info settings.", array(1 => $orgName));
145 }
146
147 $providerExists = CRM_Core_DAO::singleValueQuery("SELECT id FROM civicrm_sms_provider LIMIT 1");
148 if ($providerExists) {
149 $postUpgradeMessage .= '<br />' . ts('SMS providers were found to setup. Please note Clickatell / Twilio are now shipped as extensions and will require installing them to continue working. Extension could be downloaded and installed from <a href="%1">github</a>.', array(1 => 'https://github.com/civicrm/civicrm-core/tree/master/tools/extensions'));
150 }
151 }
152
153 if ($rev == '4.3.alpha2') {
154 $sql = "
155 SELECT title, id
156 FROM civicrm_action_schedule
157 WHERE entity_value = '' OR entity_value IS NULL
158 ";
159
160 $dao = CRM_Core_DAO::executeQuery($sql);
161 $reminder = array();
162 $list = '';
163 while ($dao->fetch()) {
164 $reminder[$dao->id] = $dao->title;
165 $list .= "<li>{$dao->title}</li>";
166 }
167 if (!empty($reminder)) {
168 $list = "<br /><ul>" . $list . "</ul>";
169 $postUpgradeMessage .= '<br />' .ts("Scheduled Reminders must be linked to one or more 'entities' (Events, Event Templates, Activity Types, Membership Types). The following reminders are not configured properly and will not be run. Please review them and update or delete them: %1", array(1 => $list));
170 }
171 }
172 if ($rev == '4.3.beta2') {
173 $postUpgradeMessage .= '<br />' . ts('Default versions of the following System Workflow Message Templates have been modified to handle new functionality: <ul><li>Events - Registration Confirmation and Receipt (on-line)</li><li>Events - Registration Confirmation and Receipt (off-line)</li><li>Pledges - Acknowledgement</li><li>Pledges - Payment Reminder</li><li>Contributions - Receipt (off-line)</li><li>Contributions - Receipt (on-line)</li><li>Memberships - Signup and Renewal Receipts (off-line)</li><li>Memberships - Receipt (on-line)</li><li>Personal Campaign Pages - Admin Notification</li></ul> If you have modified these templates, please review the new default versions and implement updates as needed to your copies (Administer > Communications > Message Templates > System Workflow Messages).');
174 }
175
176 if ($rev == '4.3.beta5') {
177 $postUpgradeMessage .= '<br />' . ts("If you are interested in trying out the new Accounting Integration features, please review user permissions and assign the new 'manual batch' permissions as appropriate.");
178
179 // CRM-12155
180 $query = "
181 SELECT ceft.id FROM `civicrm_financial_trxn` cft
182 LEFT JOIN civicrm_entity_financial_trxn ceft
183 ON ceft.financial_trxn_id = cft.id AND ceft.entity_table = 'civicrm_contribution'
184 LEFT JOIN civicrm_contribution cc
185 ON cc.id = ceft.entity_id AND ceft.entity_table = 'civicrm_contribution'
186 WHERE cc.id IS NULL
187 ";
188
189 $dao = CRM_Core_DAO::executeQuery($query);
190 $isOrphanData = TRUE;
191 if (!$dao->N) {
192 $query = "
193 SELECT cli.id FROM civicrm_line_item cli
194 LEFT JOIN civicrm_contribution cc ON cli.entity_id = cc.id AND cli.entity_table = 'civicrm_contribution'
195 LEFT JOIN civicrm_participant cp ON cli.entity_id = cp.id AND cli.entity_table = 'civicrm_participant'
196 WHERE CASE WHEN cli.entity_table = 'civicrm_contribution'
197 THEN cc.id IS NULL
198 ELSE cp.id IS NULL
199 END
200 ";
201 $dao = CRM_Core_DAO::executeQuery($query);
202 if (!$dao->N) {
203 $revPattern = '/^((\d{1,2})\.\d{1,2})\.(\d{1,2}|\w{4,7})?$/i';
204 preg_match($revPattern, $currentVer, $version);
205 if ($version[1] >= 4.3) {
206 $query = "
207 SELECT cfi.id
208 FROM civicrm_financial_item cfi
209 LEFT JOIN civicrm_entity_financial_trxn ceft ON ceft.entity_table = 'civicrm_financial_item' and cfi.id = ceft.entity_id
210 WHERE ceft.entity_id IS NULL;
211 ";
212 $dao = CRM_Core_DAO::executeQuery($query);
213 if (!$dao->N) {
214 $isOrphanData = FALSE;
215 }
216 }
217 else {
218 $isOrphanData = FALSE;
219 }
220 }
221 }
222
223 if ($isOrphanData) {
224 $postUpgradeMessage .= "</br> <strong>" . ts('Your database contains extraneous financial records related to deleted contacts and contributions. These records should not affect the site and will not appear in reports, search results or exports. However you may wish to clean them up. Refer to <a href="%1" target="_blank">this wiki page for details</a>.
225 ', array( 1 => 'http://wiki.civicrm.org/confluence/display/CRMDOC/Clean+up+extraneous+financial+data+-+4.3+upgrades')) . "</strong>";
226 }
227 }
228 if ($rev == '4.3.4') {
229 $postUpgradeMessage .= '<br />' . ts('System Administrator Alert: If you are running scheduled jobs using CLI.php, you will need to reconfigure cron tasks to include a password. Scheduled jobs will no longer run if the password is not provided (<a href="%1" target="_blank">learn more</a>).',
230 array( 1 => 'http://wiki.civicrm.org/confluence/display/CRMDOC/Managing+Scheduled+Jobs'));
231 }
232 if ($rev == '4.3.5') {
233 $postUpgradeMessage .= '<br />' . ts('Default versions of the following System Workflow Message Templates have been modified to handle new functionality: <ul><li>Events - Registration Confirmation and Receipt (on-line)</li><li>Events - Registration Confirmation and Receipt (off-line)</li></ul> If you have modified these templates, please review the new default versions and implement updates as needed to your copies (Administer > Communications > Message Templates > System Workflow Messages).');
234 }
235 if ($rev == '4.3.6') {
236 $flag = CRM_Core_DAO::singleValueQuery('SELECT count(ccp.id) FROM civicrm_contribution_product ccp
237 INNER JOIN civicrm_product cp ON ccp.product_id = cp.id
238 WHERE ccp.financial_type_id IS NULL and cp.cost > 0');
239 if ($flag) {
240 $postUpgradeMessage .= '<br />' . ts('Your database contains one or more premiums which have a cost but are not linked to a financial type. If you are exporting transations to an accounting package, this will result in unbalanced transactions. <a href="%1" target="_blank">You can review steps to correct this situation on the wiki.</a>',
241 array( 1 => 'http://wiki.civicrm.org/confluence/display/CRMDOC/Fixing+Issues+Caused+by+Missing+Cost+of+Goods+Account+-+4.3+Upgrades'));
242 }
243 }
244 }
245
246 /**
247 * @param $rev
248 *
249 * @return bool
250 */
251 public function upgrade_4_3_alpha1($rev) {
252 self::task_4_3_alpha1_checkDBConstraints();
253
254 // add indexes for civicrm_entity_financial_trxn
255 // CRM-12141
256 $this->addTask('Check/Add indexes for civicrm_entity_financial_trxn', 'task_4_3_x_checkIndexes', $rev);
257 // task to process sql
258 $this->addTask(ts('Upgrade DB to %1: SQL', array(1 => '4.3.alpha1')), 'task_4_3_x_runSql', $rev);
259
260 //CRM-11636
261 $this->addTask('Populate financial type values for price records', 'assignFinancialTypeToPriceRecords');
262 //CRM-11514 create financial records for contributions
263 $this->addTask('Create financial records for contributions', 'createFinancialRecords');
264
265 $minId = CRM_Core_DAO::singleValueQuery('SELECT coalesce(min(id),0) FROM civicrm_contact');
266 $maxId = CRM_Core_DAO::singleValueQuery('SELECT coalesce(max(id),0) FROM civicrm_contact');
267 for ($startId = $minId; $startId <= $maxId; $startId += self::BATCH_SIZE) {
268 $endId = $startId + self::BATCH_SIZE - 1;
269 $title = ts('Upgrade timestamps (%1 => %2)', array(1 => $startId, 2 => $endId));
270 $this->addTask($title, 'convertTimestamps', $startId, $endId);
271 }
272
273 // CRM-10893
274 // fix WP access control
275 $config = CRM_Core_Config::singleton( );
276 if ($config->userFramework == 'WordPress') {
277 civicrm_wp_set_capabilities( );
278 }
279
280 // Update phones CRM-11292.
281 $this->addTask('Upgrade Phone Numbers', 'phoneNumeric');
282
283 return TRUE;
284 }
285
286 /**
287 * @param $rev
288 */
289 public function upgrade_4_3_alpha2($rev) {
290 //CRM-11847
291 $isColumnPresent = CRM_Core_DAO::checkFieldExists('civicrm_dedupe_rule_group', 'is_default');
292 if ($isColumnPresent) {
293 CRM_Core_DAO::executeQuery('ALTER TABLE civicrm_dedupe_rule_group DROP COLUMN is_default');
294 }
295 $this->addTask(ts('Upgrade DB to %1: SQL', array(1 => '4.3.alpha2')), 'task_4_3_x_runSql', $rev);
296 }
297
298 /**
299 * @param $rev
300 */
301 public function upgrade_4_3_alpha3($rev) {
302 $this->addTask(ts('Upgrade DB to %1: SQL', array(1 => '4.3.alpha3')), 'task_4_3_x_runSql', $rev);
303 }
304
305 /**
306 * @param $rev
307 */
308 public function upgrade_4_3_beta2($rev) {
309 $this->addTask(ts('Upgrade DB to %1: SQL', array(1 => '4.3.beta2')), 'task_4_3_x_runSql', $rev);
310
311 // CRM-12002
312 if (
313 CRM_Core_DAO::checkTableExists('log_civicrm_line_item') &&
314 CRM_Core_DAO::checkFieldExists('log_civicrm_line_item', 'label')
315 ) {
316 CRM_Core_DAO::executeQuery('ALTER TABLE `log_civicrm_line_item` CHANGE `label` `label` VARCHAR(255) NULL DEFAULT NULL');
317 }
318 }
319
320 /**
321 * @param $rev
322 */
323 public function upgrade_4_3_beta3($rev) {
324 $this->addTask(ts('Upgrade DB to %1: SQL', array(1 => '4.3.beta3')), 'task_4_3_x_runSql', $rev);
325 // CRM-12065
326 $query = "SELECT id, form_values FROM civicrm_report_instance WHERE form_values LIKE '%contribution_type%'";
327 $this->addTask('Replace contribution_type to financial_type in table civicrm_report_instance', 'replaceContributionTypeId', $query, 'reportInstance');
328 $query = "SELECT * FROM civicrm_saved_search WHERE form_values LIKE '%contribution_type%'";
329 $this->addTask('Replace contribution_type to financial_type in table civicrm_saved_search', 'replaceContributionTypeId', $query, 'savedSearch');
330 }
331
332 /**
333 * @param $rev
334 */
335 public function upgrade_4_3_beta4($rev) {
336 $this->addTask(ts('Upgrade DB to %1: SQL', array(1 => '4.3.beta4')), 'task_4_3_x_runSql', $rev);
337 // add indexes for civicrm_entity_financial_trxn
338 // CRM-12141
339 $this->addTask('Check/Add indexes for civicrm_entity_financial_trxn', 'task_4_3_x_checkIndexes', $rev);
340 }
341
342 /**
343 * @param $rev
344 */
345 public function upgrade_4_3_beta5($rev) {
346 // CRM-12205
347 if (
348 CRM_Core_DAO::checkTableExists('log_civicrm_financial_trxn') &&
349 CRM_Core_DAO::checkFieldExists('log_civicrm_financial_trxn', 'trxn_id')
350 ) {
351 CRM_Core_DAO::executeQuery('ALTER TABLE `log_civicrm_financial_trxn` CHANGE `trxn_id` `trxn_id` VARCHAR(255) NULL DEFAULT NULL');
352 }
353 // CRM-12142 - some sites didn't get this column added yet, and sites which installed 4.3 from scratch will already have it
354 // CRM-12367 - add this column to single lingual sites only
355 $upgrade = new CRM_Upgrade_Form();
356 if (!$upgrade->multilingual &&
357 !CRM_Core_DAO::checkFieldExists('civicrm_premiums', 'premiums_nothankyou_label')
358 ) {
359 $query = "
360 ALTER TABLE civicrm_premiums
361 ADD COLUMN premiums_nothankyou_label varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL
362 COMMENT 'Label displayed for No Thank-you option in premiums block (e.g. No thank you)'
363 ";
364 CRM_Core_DAO::executeQuery($query, array(), TRUE, NULL, FALSE, FALSE);
365 }
366 $this->addTask(ts('Upgrade DB to %1: SQL', array(1 => '4.3.beta5')), 'task_4_3_x_runSql', $rev);
367 }
368
369 /**
370 * @param $rev
371 */
372 public function upgrade_4_3_4($rev) {
373 $this->addTask(ts('Upgrade DB to %1: SQL', array(1 => '4.3.4')), 'task_4_3_x_runSql', $rev);
374 }
375
376 /**
377 * @param $rev
378 */
379 public function upgrade_4_3_5($rev) {
380 // CRM-12156
381 $config = CRM_Core_Config::singleton();
382 $dbname = DB::parseDSN($config->dsn);
383 $sql = "SELECT DELETE_RULE
384 FROM information_schema.REFERENTIAL_CONSTRAINTS
385 WHERE CONSTRAINT_NAME = 'FK_civicrm_financial_item_contact_id'
386 AND CONSTRAINT_SCHEMA = %1";
387 $params = array(1 => array($dbname['database'], 'String'));
388 $onDelete = CRM_Core_DAO::singleValueQuery($sql, $params, TRUE, FALSE);
389
390 if ($onDelete != 'CASCADE') {
391 $query = "ALTER TABLE `civicrm_financial_item`
392 DROP FOREIGN KEY FK_civicrm_financial_item_contact_id,
393 DROP INDEX FK_civicrm_financial_item_contact_id;";
394 CRM_Core_DAO::executeQuery($query, array(), TRUE, NULL, FALSE, FALSE);
395 $query = "
396 ALTER TABLE `civicrm_financial_item`
397 ADD CONSTRAINT `FK_civicrm_financial_item_contact_id` FOREIGN KEY (`contact_id`) REFERENCES `civicrm_contact` (`id`) ON DELETE CASCADE;
398 ";
399 CRM_Core_DAO::executeQuery($query, array(), TRUE, NULL, FALSE, FALSE);
400 }
401 $this->addTask(ts('Upgrade DB to %1: SQL', array(1 => '4.3.5')), 'task_4_3_x_runSql', $rev);
402 }
403
404 /**
405 * @param $rev
406 */
407 public function upgrade_4_3_6($rev) {
408 //CRM-13094
409 $this->addTask(ts('Add missing constraints'), 'addMissingConstraints', $rev);
410 //CRM-13088
411 $this->addTask('Add ON DELETE Options for constraints', 'task_4_3_x_checkConstraints', $rev);
412 $this->addTask(ts('Upgrade DB to %1: SQL', array(1 => '4.3.6')), 'task_4_3_x_runSql', $rev);
413 // CRM-12844
414 // update line_item, financial_trxn and financial_item table for recurring contributions
415 $this->addTask('Update financial_account_id in financial_trxn table', 'updateFinancialTrxnData', $rev);
416 $this->addTask('Update Line Item Data', 'updateLineItemData', $rev);
417 }
418
419 //CRM-11636
420 /**
421 * @return bool
422 */
423 public function assignFinancialTypeToPriceRecords() {
424 $upgrade = new CRM_Upgrade_Form();
425 //here we update price set entries
426 $sqlFinancialIds = "
427 SELECT id, LCASE(name) name
428 FROM civicrm_financial_type
429 WHERE name IN ('Donation', 'Event Fee', 'Member Dues');
430 ";
431 $daoFinancialIds = CRM_Core_DAO::executeQuery($sqlFinancialIds);
432 while($daoFinancialIds->fetch()) {
433 $financialIds[$daoFinancialIds->name] = $daoFinancialIds->id;
434 }
435 $sqlPriceSetUpdate = "
436 UPDATE civicrm_price_set ps
437 SET ps.financial_type_id =
438 CASE
439 WHEN ps.extends LIKE '%1%' THEN {$financialIds['event fee']}
440 WHEN ps.extends LIKE '2' THEN {$financialIds['donation']}
441 WHEN ps.extends LIKE '3' THEN {$financialIds['member dues']}
442 END
443 WHERE financial_type_id IS NULL
444 ";
445 CRM_Core_DAO::executeQuery($sqlPriceSetUpdate);
446
447 //here we update price field value rows
448 $sqlPriceFieldValueUpdate = "
449 UPDATE civicrm_price_field_value pfv
450 LEFT JOIN civicrm_membership_type mt ON (pfv.membership_type_id = mt.id)
451 INNER JOIN civicrm_price_field pf ON (pfv.price_field_id = pf.id)
452 INNER JOIN civicrm_price_set ps ON (pf.price_set_id = ps.id)
453 SET pfv.financial_type_id =
454 CASE
455 WHEN pfv.membership_type_id IS NOT NULL THEN mt.financial_type_id
456 WHEN pfv.membership_type_id IS NULL THEN ps.financial_type_id
457 END
458 ";
459 CRM_Core_DAO::executeQuery($sqlPriceFieldValueUpdate);
460
461 return TRUE;
462 }
463
464 /**
465 * @return bool
466 */
467 public static function _checkAndMigrateDefaultFinancialTypes() {
468 $modifiedDefaults = FALSE;
469 //insert types if not exists
470 $sqlFetchTypes = "
471 SELECT id, name
472 FROM civicrm_contribution_type
473 WHERE name IN ('Donation', 'Event Fee', 'Member Dues') AND is_active =1
474 ";
475 $daoFetchTypes = CRM_Core_DAO::executeQuery($sqlFetchTypes);
476
477 if ($daoFetchTypes->N < 3) {
478 $modifiedDefaults = TRUE;
479 $insertStatments = array (
480 'Donation' => "('Donation', 0, 1, 1)",
481 'Member' => "('Member Dues', 0, 1, 1)",
482 'Event Fee' => "('Event Fee', 0, 1, 0)",
483 );
484 foreach ($insertStatments as $values) {
485 $query = "
486 INSERT INTO civicrm_contribution_type (name, is_reserved, is_active, is_deductible)
487 VALUES $values
488 ON DUPLICATE KEY UPDATE is_active = 1
489 ";
490 CRM_Core_DAO::executeQuery($query);
491 }
492 }
493 return $modifiedDefaults;
494 }
495
496 /**
497 * @return bool
498 */
499 public function createFinancialRecords() {
500 $upgrade = new CRM_Upgrade_Form();
501
502 // update civicrm_entity_financial_trxn.amount = civicrm_financial_trxn.total_amount
503 $query = "
504 UPDATE civicrm_entity_financial_trxn ceft
505 LEFT JOIN civicrm_financial_trxn cft ON cft.id = ceft.financial_trxn_id
506 SET ceft.amount = total_amount
507 WHERE cft.net_amount IS NOT NULL
508 AND ceft.entity_table = 'civicrm_contribution'
509 ";
510 CRM_Core_DAO::executeQuery($query);
511
512 $contributionStatus = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name');
513 $completedStatus = array_search('Completed', $contributionStatus);
514 $pendingStatus = array_search('Pending', $contributionStatus);
515 $cancelledStatus = array_search('Cancelled', $contributionStatus);
516 $queryParams = array(
517 1 => array($completedStatus, 'Integer'),
518 2 => array($pendingStatus, 'Integer'),
519 3 => array($cancelledStatus, 'Integer')
520 );
521
522 $accountType = key(CRM_Core_PseudoConstant::accountOptionValues('financial_account_type', NULL, " AND v.name = 'Asset' "));
523 $query = "
524 SELECT id
525 FROM civicrm_financial_account
526 WHERE is_default = 1
527 AND financial_account_type_id = {$accountType}
528 ";
529 $financialAccountId = CRM_Core_DAO::singleValueQuery($query);
530
531 $accountRelationsips = CRM_Core_PseudoConstant::get('CRM_Financial_DAO_EntityFinancialAccount',
532 'account_relationship', CRM_Core_DAO::$_nullArray, 'validate');
533
534 $accountsReceivableAccount = array_search('Accounts Receivable Account is', $accountRelationsips);
535 $incomeAccountIs = array_search('Income Account is', $accountRelationsips);
536 $assetAccountIs = array_search('Asset Account is', $accountRelationsips);
537 $expenseAccountIs = array_search('Expense Account is', $accountRelationsips);
538
539 $financialItemStatus = CRM_Core_PseudoConstant::get('CRM_Financial_DAO_FinancialItem', 'status_id',
540 CRM_Core_DAO::$_nullArray, 'validate');
541 $unpaidStatus = array_search('Unpaid', $financialItemStatus);
542 $paidStatus = array_search('Paid', $financialItemStatus);
543
544 $validCurrencyCodes = CRM_Core_PseudoConstant::currencyCode();
545 $validCurrencyCodes = implode("','", $validCurrencyCodes);
546 $config = CRM_Core_Config::singleton();
547 $defaultCurrency = $config->defaultCurrency;
548 $now = date( 'YmdHis' );
549
550 //adding financial_trxn records and entity_financial_trxn records related to contribution
551 //Add temp column for easy entry in entity_financial_trxn
552 $sql = "ALTER TABLE civicrm_financial_trxn ADD COLUMN contribution_id INT DEFAULT NULL";
553 CRM_Core_DAO::executeQuery($sql);
554
555 //pending pay later status handling
556 $sql = "
557 INSERT INTO civicrm_financial_trxn
558 (contribution_id, payment_instrument_id, currency, total_amount, net_amount, fee_amount, trxn_id, status_id,
559 check_number, to_financial_account_id, from_financial_account_id, trxn_date)
560 SELECT con.id as contribution_id, con.payment_instrument_id,
561 IF(con.currency IN ('{$validCurrencyCodes}'), con.currency, '{$defaultCurrency}') as currency,
562 con.total_amount, con.net_amount, con.fee_amount, con.trxn_id, con.contribution_status_id,
563 con.check_number, efa.financial_account_id as to_financial_account_id, NULL as from_financial_account_id,
564 REPLACE(REPLACE(REPLACE(
565 CASE
566 WHEN con.receive_date IS NOT NULL THEN
567 con.receive_date
568 WHEN con.receipt_date IS NOT NULL THEN
569 con.receipt_date
570 ELSE
571 {$now}
572 END
573 , '-', ''), ':', ''), ' ', '') as trxn_date
574 FROM civicrm_contribution con
575 LEFT JOIN civicrm_entity_financial_account efa
576 ON (con.financial_type_id = efa.entity_id AND efa.entity_table = 'civicrm_financial_type'
577 AND efa.account_relationship = {$accountsReceivableAccount})
578 WHERE con.is_pay_later = 1
579 AND con.contribution_status_id = {$pendingStatus}
580 ";
581 CRM_Core_DAO::executeQuery($sql);
582
583 //create a temp table to hold financial account id related to payment instruments
584 $tempTableName1 = CRM_Core_DAO::createTempTableName();
585
586 $sql = "
587 CREATE TEMPORARY TABLE {$tempTableName1}
588 SELECT ceft.financial_account_id financial_account_id, cov.value as instrument_id
589 FROM civicrm_entity_financial_account ceft
590 INNER JOIN civicrm_option_value cov ON cov.id = ceft.entity_id AND ceft.entity_table = 'civicrm_option_value'
591 INNER JOIN civicrm_option_group cog ON cog.id = cov.option_group_id
592 WHERE cog.name = 'payment_instrument'
593 ";
594 CRM_Core_DAO::executeQuery($sql);
595
596 //CRM-12141
597 $sql = "ALTER TABLE {$tempTableName1} ADD INDEX index_instrument_id (instrument_id(200));";
598 CRM_Core_DAO::executeQuery($sql);
599
600 //create temp table to process completed / cancelled contribution
601 $tempTableName2 = CRM_Core_DAO::createTempTableName();
602 $sql = "
603 CREATE TEMPORARY TABLE {$tempTableName2}
604 SELECT con.id as contribution_id, con.payment_instrument_id,
605 IF(con.currency IN ('{$validCurrencyCodes}'), con.currency, '{$defaultCurrency}') as currency,
606 con.total_amount, con.net_amount, con.fee_amount, con.trxn_id, con.contribution_status_id,
607 con.check_number, NULL as from_financial_account_id,
608 REPLACE(REPLACE(REPLACE(
609 CASE
610 WHEN con.receive_date IS NOT NULL THEN
611 con.receive_date
612 WHEN con.receipt_date IS NOT NULL THEN
613 con.receipt_date
614 ELSE
615 {$now}
616 END
617 , '-', ''), ':', ''), ' ', '') as trxn_date,
618 CASE
619 WHEN con.payment_instrument_id IS NULL THEN
620 {$financialAccountId}
621 WHEN con.payment_instrument_id IS NOT NULL THEN
622 tpi.financial_account_id
623 END as to_financial_account_id,
624 IF(eft.financial_trxn_id IS NULL, 'insert', eft.financial_trxn_id) as action
625 FROM civicrm_contribution con
626 LEFT JOIN civicrm_entity_financial_trxn eft
627 ON (eft.entity_table = 'civicrm_contribution' AND eft.entity_id = con.id)
628 LEFT JOIN {$tempTableName1} tpi
629 ON con.payment_instrument_id = tpi.instrument_id
630 WHERE con.contribution_status_id IN ({$completedStatus}, {$cancelledStatus})
631 ";
632 CRM_Core_DAO::executeQuery($sql);
633
634 // CRM-12141
635 $sql = "ALTER TABLE {$tempTableName2} ADD INDEX index_action (action);";
636 CRM_Core_DAO::executeQuery($sql);
637
638 //handling for completed contribution and cancelled contribution
639 //insertion of new records
640 $sql = "
641 INSERT INTO civicrm_financial_trxn
642 (contribution_id, payment_instrument_id, currency, total_amount, net_amount, fee_amount, trxn_id, status_id, check_number,
643 to_financial_account_id, from_financial_account_id, trxn_date)
644 SELECT tempI.contribution_id, tempI.payment_instrument_id, tempI.currency, tempI.total_amount, tempI.net_amount,
645 tempI.fee_amount, tempI.trxn_id, tempI.contribution_status_id, tempI.check_number,
646 tempI.to_financial_account_id, tempI.from_financial_account_id, tempI.trxn_date
647 FROM {$tempTableName2} tempI
648 WHERE tempI.action = 'insert'
649 ";
650 CRM_Core_DAO::executeQuery($sql);
651
652 //update of existing records
653 $sql = "
654 UPDATE civicrm_financial_trxn ft
655 INNER JOIN {$tempTableName2} tempU
656 ON (tempU.action != 'insert' AND ft.id = tempU.action)
657 SET ft.from_financial_account_id = NULL,
658 ft.to_financial_account_id = tempU.to_financial_account_id,
659 ft.status_id = tempU.contribution_status_id,
660 ft.payment_instrument_id = tempU.payment_instrument_id,
661 ft.check_number = tempU.check_number,
662 ft.contribution_id = tempU.contribution_id;";
663 CRM_Core_DAO::executeQuery($sql);
664
665 //insert the -ve transaction rows for cancelled contributions
666 $sql = "
667 INSERT INTO civicrm_financial_trxn
668 (contribution_id, payment_instrument_id, currency, total_amount, net_amount, fee_amount, trxn_id, status_id,
669 check_number, to_financial_account_id, from_financial_account_id, trxn_date)
670 SELECT ft.contribution_id, ft.payment_instrument_id, ft.currency, -ft.total_amount, ft.net_amount, ft.fee_amount, ft.trxn_id,
671 ft.status_id, ft.check_number, ft.to_financial_account_id, ft.from_financial_account_id, ft.trxn_date
672 FROM civicrm_financial_trxn ft
673 WHERE ft.status_id = {$cancelledStatus};";
674 CRM_Core_DAO::executeQuery($sql);
675
676 //inserting entity financial trxn entries if its not present in entity_financial_trxn for completed and pending contribution statuses
677 //this also handles +ve and -ve both transaction entries for a cancelled contribution
678 $sql = "
679 INSERT INTO civicrm_entity_financial_trxn (entity_table, entity_id, financial_trxn_id, amount)
680 SELECT 'civicrm_contribution', ft.contribution_id, ft.id, ft.total_amount as amount
681 FROM civicrm_financial_trxn ft
682 WHERE contribution_id IS NOT NULL AND
683 ft.id NOT IN (SELECT financial_trxn_id
684 FROM civicrm_entity_financial_trxn
685 WHERE entity_table = 'civicrm_contribution'
686 AND entity_id = ft.contribution_id)";
687 CRM_Core_DAO::executeQuery($sql);
688 //end of adding financial_trxn records and entity_financial_trxn records related to contribution
689
690 //update all linked line_item rows
691 // set line_item.financial_type_id = contribution.financial_type_id if contribution page id is null and not participant line item
692 // set line_item.financial_type_id = price_field_value.financial_type_id if contribution page id is set and not participant line item
693 // set line_item.financial_type_id = event.financial_type_id if its participant line item and line_item.price_field_value_id is null
694 // set line_item.financial_type_id = price_field_value.financial_type_id if its participant line item and line_item.price_field_value_id is set
695 $updateLineItemSql = "
696 UPDATE civicrm_line_item li
697 LEFT JOIN civicrm_contribution con
698 ON (li.entity_id = con.id AND li.entity_table = 'civicrm_contribution')
699 LEFT JOIN civicrm_price_field_value cpfv
700 ON li.price_field_value_id = cpfv.id
701 LEFT JOIN civicrm_participant cp
702 ON (li.entity_id = cp.id AND li.entity_table = 'civicrm_participant')
703 LEFT JOIN civicrm_event ce
704 ON ce.id = cp.event_id
705 SET li.financial_type_id = CASE
706 WHEN (con.contribution_page_id IS NULL || li.price_field_value_id IS NULL) AND cp.id IS NULL THEN
707 con.financial_type_id
708 WHEN (con.contribution_page_id IS NOT NULL AND cp.id IS NULL) || (cp.id IS NOT NULL AND li.price_field_value_id IS NOT NULL) THEN
709 cpfv.financial_type_id
710 WHEN cp.id IS NOT NULL AND li.price_field_value_id IS NULL THEN
711 ce.financial_type_id
712 END";
713 CRM_Core_DAO::executeQuery($updateLineItemSql, $queryParams);
714
715 //add the financial_item entries
716 //add a temp column so that inserting entity_financial_trxn entries gets easy
717 $sql = "ALTER TABLE civicrm_financial_item ADD COLUMN f_trxn_id INT DEFAULT NULL";
718 CRM_Core_DAO::executeQuery($sql);
719
720 //add financial_item entries for contribution completed / pending pay later / cancelled
721 $contributionlineItemSql = "
722 INSERT INTO civicrm_financial_item
723 (transaction_date, contact_id, amount, currency, entity_table, entity_id, description, status_id, financial_account_id, f_trxn_id)
724
725 SELECT REPLACE(REPLACE(REPLACE(ft.trxn_date, '-', ''), ':', ''), ' ', ''), con.contact_id,
726 IF(ft.total_amount < 0 AND con.contribution_status_id = %3, -li.line_total, li.line_total) as line_total, con.currency, 'civicrm_line_item',
727 li.id as line_item_id, li.label as line_item_label,
728 IF(con.contribution_status_id = {$pendingStatus}, {$unpaidStatus}, {$paidStatus}) as status_id, efa.financial_account_id as financial_account_id,
729 ft.id as f_trxn_id
730 FROM civicrm_line_item li
731 INNER JOIN civicrm_contribution con
732 ON (li.entity_id = con.id AND li.entity_table = 'civicrm_contribution')
733 INNER JOIN civicrm_financial_trxn ft
734 ON (con.id = ft.contribution_id)
735 LEFT JOIN civicrm_entity_financial_account efa
736 ON (li.financial_type_id = efa.entity_id AND efa.entity_table = 'civicrm_financial_type'
737 AND efa.account_relationship = {$incomeAccountIs})
738 WHERE con.contribution_status_id IN (%1, %3) OR (con.is_pay_later = 1 AND con.contribution_status_id = %2)";
739 CRM_Core_DAO::executeQuery($contributionlineItemSql, $queryParams);
740
741 //add financial_item entries for event
742 $participantLineItemSql = "
743 INSERT INTO civicrm_financial_item
744 (transaction_date, contact_id, amount, currency, entity_table, entity_id, description, status_id, financial_account_id, f_trxn_id)
745
746 SELECT REPLACE(REPLACE(REPLACE(ft.trxn_date, '-', ''), ':', ''), ' ', ''), con.contact_id,
747 IF(ft.total_amount < 0 AND con.contribution_status_id = %3, -li.line_total, li.line_total) as line_total,
748 con.currency, 'civicrm_line_item', li.id as line_item_id, li.label as line_item_label,
749 IF(con.contribution_status_id = {$pendingStatus}, {$unpaidStatus}, {$paidStatus}) as status_id,
750 efa.financial_account_id as financial_account_id, ft.id as f_trxn_id
751 FROM civicrm_line_item li
752 INNER JOIN civicrm_participant par
753 ON (li.entity_id = par.id AND li.entity_table = 'civicrm_participant')
754 INNER JOIN civicrm_participant_payment pp
755 ON (pp.participant_id = par.id)
756 INNER JOIN civicrm_contribution con
757 ON (pp.contribution_id = con.id)
758 INNER JOIN civicrm_financial_trxn ft
759 ON (con.id = ft.contribution_id)
760 LEFT JOIN civicrm_entity_financial_account efa
761 ON (li.financial_type_id = efa.entity_id AND
762 efa.entity_table = 'civicrm_financial_type' AND efa.account_relationship = {$incomeAccountIs})
763 WHERE con.contribution_status_id IN (%1, %3) OR (con.is_pay_later = 1 AND con.contribution_status_id = %2)";
764 CRM_Core_DAO::executeQuery($participantLineItemSql, $queryParams);
765
766 //fee handling for contributions
767 //insert fee entries in financial_trxn for contributions
768 $sql = "ALTER TABLE civicrm_financial_trxn ADD COLUMN is_fee TINYINT DEFAULT NULL";
769 CRM_Core_DAO::executeQuery($sql);
770
771 $sql = "
772 INSERT INTO civicrm_financial_trxn
773 (contribution_id, payment_instrument_id, currency, total_amount, net_amount, fee_amount, trxn_id, status_id, check_number,
774 to_financial_account_id, from_financial_account_id, trxn_date, payment_processor_id, is_fee)
775
776 SELECT con.id, ft.payment_instrument_id, ft.currency, ft.fee_amount, NULL, NULL, ft.trxn_id, %1 as status_id,
777 ft.check_number, efaFT.financial_account_id as to_financial_account_id, CASE
778 WHEN efaPP.financial_account_id IS NOT NULL THEN
779 efaPP.financial_account_id
780 WHEN tpi.financial_account_id IS NOT NULL THEN
781 tpi.financial_account_id
782 ELSE
783 {$financialAccountId}
784 END as from_financial_account_id, ft.trxn_date, ft.payment_processor_id, 1 as is_fee
785 FROM civicrm_contribution con
786 INNER JOIN civicrm_financial_trxn ft
787 ON (ft.contribution_id = con.id)
788 LEFT JOIN civicrm_entity_financial_account efaFT
789 ON (con.financial_type_id = efaFT.entity_id AND efaFT.entity_table = 'civicrm_financial_type'
790 AND efaFT.account_relationship = {$expenseAccountIs})
791 LEFT JOIN civicrm_entity_financial_account efaPP
792 ON (ft.payment_processor_id = efaPP.entity_id AND efaPP.entity_table = 'civicrm_payment_processor'
793 AND efaPP.account_relationship = {$assetAccountIs})
794 LEFT JOIN {$tempTableName1} tpi
795 ON ft.payment_instrument_id = tpi.instrument_id
796 WHERE ft.fee_amount IS NOT NULL AND ft.fee_amount != 0 AND (con.contribution_status_id IN (%1, %3) OR (con.contribution_status_id =%2 AND con.is_pay_later = 1))
797 GROUP BY con.id";
798 CRM_Core_DAO::executeQuery($sql, $queryParams);
799
800 //link financial_trxn to contribution
801 $sql = "
802 INSERT INTO civicrm_entity_financial_trxn
803 (entity_table, entity_id, financial_trxn_id, amount)
804 SELECT 'civicrm_contribution', ft.contribution_id, ft.id, ft.total_amount
805 FROM civicrm_financial_trxn ft
806 WHERE ft.is_fee = 1";
807 CRM_Core_DAO::executeQuery($sql);
808
809 //add fee related entries to financial item table
810 $domainId = CRM_Core_Config::domainID();
811 $domainContactId = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_Domain', $domainId, 'contact_id');
812 $sql = "
813 INSERT INTO civicrm_financial_item
814 (transaction_date, contact_id, amount, currency, entity_table, entity_id, description, status_id, financial_account_id, f_trxn_id)
815 SELECT ft.trxn_date, {$domainContactId} as contact_id, ft.total_amount, ft.currency, 'civicrm_financial_trxn', ft.id,
816 'Fee', {$paidStatus} as status_id, ft.to_financial_account_id as financial_account_id, ft.id as f_trxn_id
817 FROM civicrm_financial_trxn ft
818 WHERE ft.is_fee = 1;";
819 CRM_Core_DAO::executeQuery($sql);
820
821 //add entries to entity_financial_trxn table
822 $sql = "
823 INSERT INTO civicrm_entity_financial_trxn (entity_table, entity_id, financial_trxn_id, amount)
824 SELECT 'civicrm_financial_item' as entity_table, fi.id as entity_id, fi.f_trxn_id as financial_trxn_id, fi.amount
825 FROM civicrm_financial_item fi";
826 CRM_Core_DAO::executeQuery($sql);
827
828 //drop the temparory columns
829 $sql = "ALTER TABLE civicrm_financial_trxn
830 DROP COLUMN contribution_id,
831 DROP COLUMN is_fee;";
832 CRM_Core_DAO::executeQuery($sql);
833
834 $sql = "ALTER TABLE civicrm_financial_item DROP f_trxn_id";
835 CRM_Core_DAO::executeQuery($sql);
836
837 return TRUE;
838 }
839
840 /**
841 * @return array
842 */
843 public function createDomainContacts() {
844 $domainParams = $context = array();
845 $query = "
846 ALTER TABLE civicrm_domain ADD contact_id INT( 10 ) UNSIGNED NULL DEFAULT NULL COMMENT 'FK to Contact ID. This is specifically not an FK to avoid circular constraints',
847 ADD CONSTRAINT FK_civicrm_domain_contact_id FOREIGN KEY (contact_id) REFERENCES civicrm_contact(id);";
848 CRM_Core_DAO::executeQuery($query, CRM_Core_DAO::$_nullArray, TRUE, NULL, FALSE, FALSE);
849
850 $query = '
851 SELECT cd.id, cd.name, ce.email FROM civicrm_domain cd
852 LEFT JOIN civicrm_loc_block clb ON clb.id = cd. loc_block_id
853 LEFT JOIN civicrm_email ce ON ce.id = clb.email_id ;
854 ' ;
855 $dao = CRM_Core_DAO::executeQuery($query);
856 while($dao->fetch()) {
857 $query = "
858 SELECT cc.id FROM civicrm_contact cc
859 LEFT JOIN civicrm_email ce ON ce.contact_id = cc.id
860 WHERE cc.contact_type = 'Organization' AND cc.organization_name = %1
861 ";
862 $params = array(1 => array($dao->name, 'String'));
863 if ($dao->email) {
864 $query .= " AND ce.email = %2 ";
865 $params[2] = array($dao->email, 'String');
866 }
867 $contactID = CRM_Core_DAO::singleValueQuery($query, $params);
868 $context[1] = $dao->name;
869 if (empty($contactID)) {
870 $params = array(
871 'sort_name' => $dao->name,
872 'display_name' => $dao->name,
873 'legal_name' => $dao->name,
874 'organization_name' => $dao->name,
875 'contact_type' => 'Organization'
876 );
877 $contact = CRM_Contact_BAO_Contact::add($params);
878 $contactID = $contact->id;
879 $context[0] = 'added';
880 }
881 else {
882 $context[0] = 'merged';
883 }
884 $domainParams['contact_id'] = $contactID;
885 CRM_Core_BAO_Domain::edit($domainParams, $dao->id);
886 }
887 return $context;
888 }
889
890 public function task_4_3_alpha1_checkDBConstraints() {
891 //checking whether the foreign key exists before dropping it CRM-11260
892 $config = CRM_Core_Config::singleton();
893 $dbUf = DB::parseDSN($config->dsn);
894 $tables = array(
895 'autorenewal_msg_id' => array('tableName' => 'civicrm_membership_type', 'fkey' => 'FK_civicrm_membership_autorenewal_msg_id'),
896 'to_account_id' => array('tableName' => 'civicrm_financial_trxn', 'constraintName' => 'civicrm_financial_trxn_ibfk_2'),
897 'from_account_id' => array('tableName' => 'civicrm_financial_trxn', 'constraintName' => 'civicrm_financial_trxn_ibfk_1'),
898 'contribution_type_id' => array('tableName' => 'civicrm_contribution_recur', 'fkey' => 'FK_civicrm_contribution_recur_contribution_type_id'),
899 );
900 $query = "
901 SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
902 WHERE table_name = 'civicrm_contribution_recur'
903 AND constraint_name = 'FK_civicrm_contribution_recur_contribution_type_id'
904 AND TABLE_SCHEMA = %1
905 ";
906 $params = array(1 => array($dbUf['database'], 'String'));
907 $dao = CRM_Core_DAO::executeQuery($query, $params, TRUE, NULL, FALSE, FALSE);
908 foreach($tables as $columnName => $value){
909 if ($value['tableName'] == 'civicrm_membership_type' || $value['tableName'] == 'civicrm_contribution_recur') {
910 $foreignKeyExists = CRM_Core_DAO::checkConstraintExists($value['tableName'], $value['fkey']);
911 $fKey = $value['fkey'];
912 } else {
913 $foreignKeyExists = CRM_Core_DAO::checkFKConstraintInFormat($value['tableName'], $columnName);
914 $fKey = "`FK_{$value['tableName']}_{$columnName}`";
915 }
916 if ($foreignKeyExists || $value['tableName'] == 'civicrm_financial_trxn') {
917 if ($value['tableName'] != 'civicrm_contribution_recur' || ($value['tableName'] == 'civicrm_contribution_recur' && $dao->N)) {
918 $constraintName = $foreignKeyExists ? $fKey : $value['constraintName'];
919 $query = "ALTER TABLE {$value['tableName']} DROP FOREIGN KEY {$constraintName}";
920 CRM_Core_DAO::executeQuery($query, $params, TRUE, NULL, FALSE, FALSE);
921 }
922 $query = "ALTER TABLE {$value['tableName']} DROP INDEX {$fKey}";
923 CRM_Core_DAO::executeQuery($query, $params, TRUE, NULL, FALSE, FALSE);
924 }
925 }
926 // check if column contact_id is present or not in civicrm_financial_account
927 $fieldExists = CRM_Core_DAO::checkFieldExists('civicrm_financial_account', 'contact_id', FALSE);
928 if (!$fieldExists) {
929 $query = "
930 ALTER TABLE civicrm_financial_account
931 ADD contact_id int(10) unsigned DEFAULT NULL COMMENT 'Version identifier of financial_type' AFTER name,
932 ADD CONSTRAINT FK_civicrm_financial_account_contact_id FOREIGN KEY (contact_id) REFERENCES civicrm_contact(id);
933 ";
934 CRM_Core_DAO::executeQuery($query, $params, TRUE, NULL, FALSE, FALSE);
935 }
936 }
937
938 /**
939 * Read creation and modification times from civicrm_log; add
940 * them to civicrm_contact.
941 */
942 public function convertTimestamps(CRM_Queue_TaskContext $ctx, $startId, $endId) {
943 $sql = "
944 SELECT entity_id, min(modified_date) AS created, max(modified_date) AS modified
945 FROM civicrm_log
946 WHERE entity_table = 'civicrm_contact'
947 AND entity_id BETWEEN %1 AND %2
948 GROUP BY entity_id
949 ";
950 $params = array(
951 1 => array($startId, 'Integer'),
952 2 => array($endId, 'Integer'),
953 );
954 $dao = CRM_Core_DAO::executeQuery($sql, $params);
955 while ($dao->fetch()) {
956 // FIXME civicrm_log.modified_date is DATETIME; civicrm_contact.modified_date is TIMESTAMP
957 CRM_Core_DAO::executeQuery(
958 'UPDATE civicrm_contact SET created_date = FROM_UNIXTIME(UNIX_TIMESTAMP(%1)), modified_date = FROM_UNIXTIME(UNIX_TIMESTAMP(%2)) WHERE id = %3',
959 array(
960 1 => array($dao->created, 'String'),
961 2 => array($dao->modified, 'String'),
962 3 => array($dao->entity_id, 'Integer'),
963 )
964 );
965 }
966
967 return TRUE;
968 }
969
970 /**
971 * Change index and add missing constraints for civicrm_contribution_recur
972 */
973 public function addMissingConstraints(CRM_Queue_TaskContext $ctx) {
974 $query = "SHOW KEYS FROM `civicrm_contribution_recur` WHERE key_name = 'UI_contrib_payment_instrument_id'";
975 $dao = CRM_Core_DAO::executeQuery($query);
976 if ($dao->N) {
977 CRM_Core_DAO::executeQuery('ALTER TABLE civicrm_contribution_recur DROP INDEX UI_contrib_payment_instrument_id');
978 CRM_Core_DAO::executeQuery('ALTER TABLE civicrm_contribution_recur ADD INDEX UI_contribution_recur_payment_instrument_id (payment_instrument_id)');
979 }
980 $constraintArray = array(
981 'contact_id' => " ADD CONSTRAINT `FK_civicrm_contribution_recur_contact_id` FOREIGN KEY (`contact_id`) REFERENCES `civicrm_contact` (`id`) ON DELETE CASCADE ",
982 'payment_processor_id' => " ADD CONSTRAINT `FK_civicrm_contribution_recur_payment_processor_id` FOREIGN KEY (`payment_processor_id`) REFERENCES `civicrm_payment_processor` (`id`) ON DELETE SET NULL ",
983 'financial_type_id' => " ADD CONSTRAINT `FK_civicrm_contribution_recur_financial_type_id` FOREIGN KEY (`financial_type_id`) REFERENCES `civicrm_financial_type` (`id`) ON DELETE SET NULL ",
984 'campaign_id' => " ADD CONSTRAINT `FK_civicrm_contribution_recur_campaign_id` FOREIGN KEY (`campaign_id`) REFERENCES `civicrm_campaign` (`id`) ON DELETE SET NULL ",
985 );
986 $constraint = array();
987 foreach ($constraintArray as $constraintKey => $value) {
988 $foreignKeyExists = CRM_Core_DAO::checkFKConstraintInFormat('civicrm_contribution_recur', $constraintKey);
989 if (!$foreignKeyExists) {
990 $constraint[] = $value;
991 }
992 }
993 if (!empty($constraint)) {
994 $query = "ALTER TABLE civicrm_contribution_recur " . implode(' , ', $constraint);
995 CRM_Core_DAO::executeQuery($query);
996 }
997 return TRUE;
998 }
999
1000 /**
1001 * Update financial_account_id for bad data in financial_trxn table
1002 * CRM-12844
1003 *
1004 */
1005 public function updateFinancialTrxnData(CRM_Queue_TaskContext $ctx) {
1006 $upgrade = new CRM_Upgrade_Form();
1007 $sql = "SELECT cc.id contribution_id, cc.contribution_recur_id, cft.payment_processor_id,
1008 cft.id financial_trxn_id, cfi.entity_table, cft.from_financial_account_id, cft.to_financial_account_id
1009
1010 FROM `civicrm_contribution` cc
1011 LEFT JOIN civicrm_entity_financial_trxn ceft ON ceft.entity_id = cc.id
1012 LEFT JOIN civicrm_financial_trxn cft ON cft.id = ceft.financial_trxn_id
1013 LEFT JOIN civicrm_entity_financial_trxn ceft1 ON ceft1.financial_trxn_id = ceft.financial_trxn_id
1014 LEFT JOIN civicrm_financial_item cfi ON cfi.id = ceft1.entity_id
1015 WHERE ceft.entity_table = 'civicrm_contribution' AND cc.contribution_recur_id IS NOT NULL
1016 AND ceft1.entity_table = 'civicrm_financial_item' AND cft.id IS NOT NULL AND cft.payment_instrument_id = %1
1017
1018 ORDER BY cft.id ";
1019 $paymentInstrument = CRM_Contribute_PseudoConstant::paymentInstrument('name');
1020 $param = array(1 => array(array_search('Credit Card', $paymentInstrument), 'Integer'));
1021 $dao = CRM_Core_DAO::executeQuery($sql, $param);
1022 $financialTrxn = array();
1023 $subsequentPayments = array();
1024 while ($dao->fetch()) {
1025 if (!array_key_exists($dao->contribution_recur_id, $financialTrxn)) {
1026 $financialTrxn[$dao->contribution_recur_id] = array(
1027 'from_financial_account_id' => $dao->to_financial_account_id,
1028 'payment_processor_id' => $dao->payment_processor_id,
1029 $dao->contribution_id => 1,
1030 );
1031 if (!is_null($dao->from_financial_account_id)) {
1032 $sql = 'UPDATE civicrm_financial_trxn SET from_financial_account_id = NULL WHERE id = %1';
1033 $params = array(1 => array($dao->financial_trxn_id, 'Integer'));
1034 CRM_Core_DAO::executeQuery($sql, $params);
1035 }
1036 }
1037 elseif (!array_key_exists($dao->contribution_id, $financialTrxn[$dao->contribution_recur_id])) {
1038 if (($dao->entity_table == 'civicrm_line_item' && $dao->to_financial_account_id == $financialTrxn[$dao->contribution_recur_id]['from_financial_account_id'])
1039 || ($dao->entity_table == 'civicrm_financial_trxn' && $dao->from_financial_account_id == $financialTrxn[$dao->contribution_recur_id]['from_financial_account_id'])) {
1040 continue;
1041 }
1042 $subsequentPayments[$dao->contribution_recur_id][$dao->entity_table][] = $dao->financial_trxn_id;
1043 }
1044 }
1045 foreach ($subsequentPayments as $key => $value) {
1046 foreach ($value as $table => $val) {
1047 if ($table == 'civicrm_financial_trxn') {
1048 $field = 'from_financial_account_id';
1049 }
1050 else {
1051 $field = 'to_financial_account_id';
1052 }
1053 $sql = "UPDATE civicrm_financial_trxn SET $field = " . $financialTrxn[$dao->contribution_recur_id]['from_financial_account_id'] . ',
1054 payment_processor_id = ' . $financialTrxn[$dao->contribution_recur_id]['payment_processor_id'] . ' WHERE
1055 id IN (' . implode(',', $val) . ')';
1056 CRM_Core_DAO::executeQuery($sql);
1057 }
1058 }
1059 return TRUE;
1060 }
1061
1062 /**
1063 * Update financial_account_id for bad data in financial_trxn table
1064 * CRM-12844
1065 *
1066 */
1067 public function updateLineItemData(CRM_Queue_TaskContext $ctx) {
1068 $sql = "SELECT cc.id contribution_id, cc.contribution_recur_id,
1069 cc.financial_type_id contribution_financial_type,
1070 cli.financial_type_id line_financial_type_id,
1071 cli.price_field_id, cli.price_field_value_id, cli.label, cli.id line_item_id,
1072 cfi.financial_account_id
1073 FROM `civicrm_line_item` cli
1074 LEFT JOIN civicrm_contribution cc ON cc.id = cli.entity_id
1075 LEFT JOIN civicrm_financial_item cfi ON cfi.entity_id = cli.id
1076 LEFT JOIN civicrm_price_field cpf ON cpf.id = cli.price_field_id
1077 LEFT JOIN civicrm_price_set cps ON cps.id = cpf.price_set_id
1078 LEFT JOIN civicrm_price_field_value cpfv ON cpfv.id = cli.price_field_value_id
1079 WHERE cfi.entity_table = 'civicrm_line_item'
1080 AND cli.entity_table = 'civicrm_contribution'
1081 AND cps.is_quick_config = 1 AND cc.contribution_recur_id IS NOT NULL
1082 ORDER BY cli.id";
1083 $dao = CRM_Core_DAO::executeQuery($sql);
1084 $financialTrxn = $subsequentPayments = array();
1085 while ($dao->fetch()) {
1086 if (!array_key_exists($dao->contribution_recur_id, $financialTrxn)) {
1087 $financialTrxn[$dao->contribution_recur_id] = array(
1088 'price_field_id' => $dao->price_field_id,
1089 'price_field_value_id' => $dao->price_field_value_id,
1090 'label' => strval($dao->label),
1091 'financial_account_id' => $dao->financial_account_id,
1092 $dao->contribution_id => 1,
1093 );
1094 }
1095 else {
1096 if ($dao->price_field_value_id == $financialTrxn[$dao->contribution_recur_id]['price_field_value_id']) {
1097 continue;
1098 }
1099 $subsequentPayments[$dao->contribution_recur_id][] = $dao->line_item_id;
1100 }
1101 }
1102 foreach ($subsequentPayments as $key => $value) {
1103 $sql = "UPDATE civicrm_line_item cli
1104 LEFT JOIN civicrm_financial_item cfi ON cli.id = cfi.entity_id
1105 SET
1106 cli.label = %1,
1107 cli.price_field_id = %2,
1108 cli.price_field_value_id = %3,
1109 cfi.financial_account_id = %4,
1110 cfi.description = %5,
1111 cli.financial_type_id = %6
1112 WHERE cfi.entity_table = 'civicrm_line_item'
1113 AND cli.entity_table = 'civicrm_contribution' AND cli.id IN (" . implode(',', $value). ');';
1114 $params = array(
1115 1 => array($financialTrxn[$key]['label'], 'String'),
1116 2 => array($financialTrxn[$key]['price_field_id'], 'Integer'),
1117 3 => array($financialTrxn[$key]['price_field_value_id'], 'Integer'),
1118 4 => array($financialTrxn[$key]['financial_account_id'], 'Integer'),
1119 5 => array($financialTrxn[$key]['label'], 'String'),
1120 6 => array($dao->contribution_financial_type, 'Integer'),
1121 );
1122 CRM_Core_DAO::executeQuery($sql, $params);
1123 }
1124 return TRUE;
1125 }
1126
1127 /**
1128 * Replace contribution_type to financial_type in table
1129 * civicrm_saved_search and Structure civicrm_report_instance
1130 */
1131 public function replaceContributionTypeId(CRM_Queue_TaskContext $ctx, $query, $table) {
1132 $dao = CRM_Core_DAO::executeQuery($query);
1133 while ($dao->fetch()) {
1134 $formValues = unserialize($dao->form_values);
1135 foreach (array('contribution_type_id_op', 'contribution_type_id_value', 'contribution_type_id') as $value) {
1136 if (array_key_exists($value, $formValues)) {
1137 $key = preg_replace('/contribution/', 'financial', $value);
1138 $formValues[$key] = $formValues[$value];
1139 unset($formValues[$value]);
1140 }
1141 }
1142 if ($table != 'savedSearch') {
1143 foreach (array('fields', 'group_bys') as $value) {
1144 if (array_key_exists($value, $formValues)) {
1145 if (array_key_exists('contribution_type_id', $formValues[$value])) {
1146 $formValues[$value]['financial_type_id'] = $formValues[$value]['contribution_type_id'];
1147 unset($formValues[$value]['contribution_type_id']);
1148 }
1149 else if (array_key_exists('contribution_type', $formValues[$value])) {
1150 $formValues[$value]['financial_type'] = $formValues[$value]['contribution_type'];
1151 unset($formValues[$value]['contribution_type']);
1152 }
1153 }
1154 }
1155 if (array_key_exists('order_bys', $formValues)) {
1156 foreach ($formValues['order_bys'] as $key => $values) {
1157 if (preg_grep('/contribution_type/', $values)) {
1158 $formValues['order_bys'][$key]['column'] = preg_replace('/contribution_type/', 'financial_type', $values['column']);
1159 }
1160 }
1161 }
1162 }
1163
1164 if ($table == 'savedSearch') {
1165 $saveDao = new CRM_Contact_DAO_SavedSearch();
1166 }
1167 else {
1168 $saveDao = new CRM_Report_DAO_ReportInstance();
1169 }
1170 $saveDao->id = $dao->id;
1171
1172 if ($table == 'savedSearch') {
1173 if (array_key_exists('mapper', $formValues)) {
1174 foreach ($formValues['mapper'] as $key => $values) {
1175 foreach ($values as $k => $v) {
1176 if (preg_grep('/contribution_/', $v)) {
1177 $formValues['mapper'][$key][$k] = preg_replace('/contribution_type/', 'financial_type', $v);
1178 }
1179 }
1180 }
1181 }
1182 foreach (array('select_tables', 'where_tables') as $value) {
1183 if (preg_match('/contribution_type/', $dao->$value)) {
1184 $tempValue = unserialize($dao->$value);
1185 if (array_key_exists('civicrm_contribution_type', $tempValue)) {
1186 $tempValue['civicrm_financial_type'] = $tempValue['civicrm_contribution_type'];
1187 unset($tempValue['civicrm_contribution_type']);
1188 }
1189 $saveDao->$value = serialize($tempValue);
1190 }
1191 }
1192 if (preg_match('/contribution_type/', $dao->where_clause)) {
1193 $saveDao->where_clause = preg_replace('/contribution_type/', 'financial_type', $dao->where_clause);
1194 }
1195 }
1196 $saveDao->form_values = serialize($formValues);
1197
1198 $saveDao->save();
1199 }
1200 return TRUE;
1201 }
1202
1203 /**
1204 * Add ON DELETE options for constraint if not present
1205 * CRM-13088 && CRM-12156
1206 *
1207 * @param CRM_Queue_TaskContext $ctx
1208 *
1209 * @return bool TRUE for success
1210 */
1211 public function task_4_3_x_checkConstraints(CRM_Queue_TaskContext $ctx) {
1212 CRM_Core_DAO::executeQuery('ALTER TABLE `civicrm_financial_account` CHANGE `contact_id` `contact_id` INT( 10 ) UNSIGNED NULL DEFAULT NULL');
1213 $config = CRM_Core_Config::singleton();
1214 $dbname = DB::parseDSN($config->dsn);
1215 $constraintArray = array(
1216 "'FK_civicrm_financial_account_contact_id'",
1217 "'FK_civicrm_financial_item_contact_id'",
1218 "'FK_civicrm_contribution_recur_financial_type_id'",
1219 "'FK_civicrm_line_item_financial_type_id'",
1220 "'FK_civicrm_product_financial_type_id'",
1221 "'FK_civicrm_premiums_product_financial_type_id'",
1222 "'FK_civicrm_price_field_value_financial_type_id'",
1223 "'FK_civicrm_contribution_product_financial_type_id'",
1224 "'FK_civicrm_price_set_financial_type_id'",
1225 "'FK_civicrm_grant_financial_type_id'",
1226 );
1227
1228 $sql = "SELECT DELETE_RULE, TABLE_NAME, CONSTRAINT_NAME
1229 FROM information_schema.REFERENTIAL_CONSTRAINTS
1230 WHERE CONSTRAINT_NAME IN (" . implode(',', $constraintArray) . ")
1231 AND CONSTRAINT_SCHEMA = %1";
1232 $params = array(1 => array($dbname['database'], 'String'));
1233 $onDelete = CRM_Core_DAO::executeQuery($sql, $params, TRUE, FALSE);
1234 while ($onDelete->fetch()) {
1235 if (($onDelete->TABLE_NAME != 'civicrm_financial_item' && $onDelete->DELETE_RULE != 'SET NULL') ||
1236 ($onDelete->TABLE_NAME == 'civicrm_financial_item' && $onDelete->DELETE_RULE != 'CASCADE')) {
1237 $tableName = 'civicrm_financial_type';
1238 $onDeleteOption = ' SET NULL ';
1239 $columnName = 'financial_type_id';
1240 if (preg_match('/contact_id/', $onDelete->CONSTRAINT_NAME)) {
1241 $tableName = 'civicrm_contact';
1242 $columnName = 'contact_id';
1243 if ($onDelete->TABLE_NAME == 'civicrm_financial_item') {
1244 $onDeleteOption = 'CASCADE';
1245 }
1246 }
1247 }
1248 else {
1249 continue;
1250 }
1251 $query = "ALTER TABLE {$onDelete->TABLE_NAME}
1252 DROP FOREIGN KEY {$onDelete->CONSTRAINT_NAME},
1253 DROP INDEX {$onDelete->CONSTRAINT_NAME};";
1254 CRM_Core_DAO::executeQuery($query, array(), TRUE, NULL, FALSE, FALSE);
1255 $query = " ALTER TABLE {$onDelete->TABLE_NAME}
1256 ADD CONSTRAINT {$onDelete->CONSTRAINT_NAME} FOREIGN KEY (`" . $columnName . "`) REFERENCES {$tableName} (`id`) ON DELETE {$onDeleteOption};
1257 ";
1258 CRM_Core_DAO::executeQuery($query, array(), TRUE, NULL, FALSE, FALSE);
1259 }
1260 return TRUE;
1261 }
1262
1263 /**
1264 * Check/Add INDEX CRM-12141
1265 *
1266 * @param CRM_Queue_TaskContext $ctx
1267 *
1268 * @return bool TRUE for success
1269 */
1270 public function task_4_3_x_checkIndexes(CRM_Queue_TaskContext $ctx) {
1271 $query = "
1272 SHOW KEYS
1273 FROM civicrm_entity_financial_trxn
1274 WHERE key_name IN ('UI_entity_financial_trxn_entity_table', 'UI_entity_financial_trxn_entity_id')
1275 ";
1276 $dao = CRM_Core_DAO::executeQuery($query);
1277 if (!$dao->N) {
1278 $query = "
1279 ALTER TABLE civicrm_entity_financial_trxn
1280 ADD INDEX UI_entity_financial_trxn_entity_table (entity_table),
1281 ADD INDEX UI_entity_financial_trxn_entity_id (entity_id);
1282 ";
1283 CRM_Core_DAO::executeQuery($query);
1284 }
1285 return TRUE;
1286 }
1287
1288 /**
1289 * Update phones CRM-11292
1290 *
1291 * @param CRM_Queue_TaskContext $ctx
1292 *
1293 * @return bool TRUE for success
1294 */
1295 public static function phoneNumeric(CRM_Queue_TaskContext $ctx) {
1296 CRM_Core_DAO::executeQuery(CRM_Contact_BAO_Contact::DROP_STRIP_FUNCTION_43);
1297 CRM_Core_DAO::executeQuery(CRM_Contact_BAO_Contact::CREATE_STRIP_FUNCTION_43);
1298 CRM_Core_DAO::executeQuery("UPDATE civicrm_phone SET phone_numeric = civicrm_strip_non_numeric(phone)");
1299 return TRUE;
1300 }
1301
1302 /**
1303 * (Queue Task Callback)
1304 */
1305 public static function task_4_3_x_runSql(CRM_Queue_TaskContext $ctx, $rev) {
1306 $upgrade = new CRM_Upgrade_Form();
1307 $upgrade->processSQL($rev);
1308
1309 return TRUE;
1310 }
1311
1312 /**
1313 * Syntatic sugar for adding a task which (a) is in this class and (b) has
1314 * a high priority.
1315 *
1316 * After passing the $funcName, you can also pass parameters that will go to
1317 * the function. Note that all params must be serializable.
1318 */
1319 protected function addTask($title, $funcName) {
1320 $queue = CRM_Queue_Service::singleton()->load(array(
1321 'type' => 'Sql',
1322 'name' => CRM_Upgrade_Form::QUEUE_NAME,
1323 ));
1324
1325 $args = func_get_args();
1326 $title = array_shift($args);
1327 $funcName = array_shift($args);
1328 $task = new CRM_Queue_Task(
1329 array(get_class($this), $funcName),
1330 $args,
1331 $title
1332 );
1333 $queue->createItem($task, array('weight' => -1));
1334 }
1335 }