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