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