Bug 25261: DBRev 20.06.00.043
[srvgit] / installer / data / mysql / updatedatabase.pl
index abb4032..63a54c2 100755 (executable)
@@ -53,14 +53,10 @@ use File::Slurp;
 my $debug = 0;
 
 my (
-    $sth, $sti,
+    $sth,
     $query,
-    %existingtables,    # tables already in database
-    %types,
     $table,
-    $column,
-    $type, $null, $key, $default, $extra,
-    $prefitem,          # preference item in systempreferences table
+    $type,
 );
 
 my $schema = Koha::Database->new()->schema();
@@ -14666,10 +14662,10 @@ if( CheckVersion( $DBversion ) ) {
 $DBversion = '17.06.00.009';
 if( CheckVersion( $DBversion ) ) {
     $dbh->do(q{
-        ALTER TABLE borrowers MODIFY COLUMN login_attempts int(4) AFTER lang;
+        ALTER TABLE borrowers MODIFY COLUMN login_attempts int(4) DEFAULT 0 AFTER lang;
     });
     $dbh->do(q{
-        ALTER TABLE deletedborrowers MODIFY COLUMN login_attempts int(4) AFTER lang;
+        ALTER TABLE deletedborrowers MODIFY COLUMN login_attempts int(4) DEFAULT 0 AFTER lang;
     });
 
     SetVersion( $DBversion );
@@ -20719,6 +20715,9 @@ if( CheckVersion( $DBversion ) ) {
                 FROM issuingrules
             ");
         }
+        $dbh->do("
+            DELETE FROM circulation_rules WHERE rule_name='holdallowed' AND rule_value='';
+        ");
         $dbh->do("DROP TABLE issuingrules");
     }
 
@@ -21261,12 +21260,17 @@ if( CheckVersion( $DBversion ) ) {
 
 $DBversion = '19.12.00.052';
 if( CheckVersion( $DBversion ) ) {
+    my $finesCalendar = C4::Context->preference('finesCalendar');
+    my $value = $finesCalendar eq 'noFinesWhenClosed' ? 1 : 0;
+
     if( !column_exists( 'itemtypes', 'rentalcharge_daily_calendar' ) ) {
         $dbh->do(q{
             ALTER TABLE itemtypes ADD COLUMN
             rentalcharge_daily_calendar tinyint(1) NOT NULL DEFAULT 1
             AFTER rentalcharge_daily;
         });
+
+        $dbh->do("UPDATE itemtypes SET rentalcharge_daily_calendar = $value");
     }
 
     if( !column_exists( 'itemtypes', 'rentalcharge_hourly_calendar' ) ) {
@@ -21275,11 +21279,9 @@ if( CheckVersion( $DBversion ) ) {
             rentalcharge_hourly_calendar tinyint(1) NOT NULL DEFAULT 1
             AFTER rentalcharge_hourly;
         });
-    }
 
-    my $finesCalendar = C4::Context->preference('finesCalendar');
-    my $value = $finesCalendar eq 'noFinesWhenClosed' ? 1 : 0;
-    $dbh->do("UPDATE itemtypes SET rentalcharge_hourly_calendar = $value, rentalcharge_daily_calendar = $value");
+        $dbh->do("UPDATE itemtypes SET rentalcharge_hourly_calendar = $value");
+    }
 
     NewVersion( $DBversion, 21443, "Add ability to exclude holidays when calculating rentals fees by time period");
 }
@@ -22228,6 +22230,652 @@ if( CheckVersion( $DBversion ) ) {
     NewVersion( $DBversion, 24156, "Add new table tables_settings" );
 }
 
+$DBversion = '20.06.00.004';
+if( CheckVersion( $DBversion ) ) {
+    $dbh->do("
+        DELETE FROM circulation_rules WHERE rule_name='holdallowed' AND rule_value='';
+    ");
+    NewVersion( $DBversion, 25851, "Remove holdallowed rule if value is an empty string");
+}
+
+$DBversion = '20.06.00.005';
+if( CheckVersion( $DBversion ) ) {
+    $dbh->do( "UPDATE borrowers SET login_attempts=0 WHERE login_attempts IS NULL" );
+    $dbh->do( "ALTER TABLE borrowers MODIFY COLUMN login_attempts int(4) NOT NULL DEFAULT 0" );
+    $dbh->do( "UPDATE deletedborrowers SET login_attempts=0 WHERE login_attempts IS NULL" );
+    $dbh->do( "ALTER TABLE deletedborrowers MODIFY COLUMN login_attempts int(4) NOT NULL DEFAULT 0" );
+    NewVersion( $DBversion, 24379, "Set login_attempts NOT NULL" );
+}
+
+$DBversion = '20.06.00.006';
+if( CheckVersion( $DBversion ) ) {
+    unless( TableExists( 'pseudonymized_transactions' ) ) {
+        $dbh->do(q|
+            CREATE TABLE `pseudonymized_transactions` (
+              `id` INT(11) NOT NULL AUTO_INCREMENT,
+              `hashed_borrowernumber` VARCHAR(60) NOT NULL,
+              `has_cardnumber` TINYINT(1) NOT NULL DEFAULT 0,
+              `title` LONGTEXT,
+              `city` LONGTEXT,
+              `state` MEDIUMTEXT default NULL,
+              `zipcode` varchar(25) default NULL,
+              `country` MEDIUMTEXT,
+              `branchcode` varchar(10) NOT NULL default '',
+              `categorycode` varchar(10) NOT NULL default '',
+              `dateenrolled` date default NULL,
+              `sex` varchar(1) default NULL,
+              `sort1` varchar(80) default NULL,
+              `sort2` varchar(80) default NULL,
+              `datetime` datetime default NULL,
+              `transaction_branchcode` varchar(10) default NULL,
+              `transaction_type` varchar(16) default NULL,
+              `itemnumber` int(11) default NULL,
+              `itemtype` varchar(10) default NULL,
+              `holdingbranch` varchar(10) default null,
+              `homebranch` varchar(10) default null,
+              `location` varchar(80) default NULL,
+              `itemcallnumber` varchar(255) default NULL,
+              `ccode` varchar(80) default NULL,
+              PRIMARY KEY (`id`),
+              CONSTRAINT `pseudonymized_transactions_ibfk_1` FOREIGN KEY (`categorycode`) REFERENCES `categories` (`categorycode`),
+              CONSTRAINT `pseudonymized_transactions_borrowers_ibfk_2` FOREIGN KEY (`branchcode`) REFERENCES `branches` (`branchcode`),
+              CONSTRAINT `pseudonymized_transactions_borrowers_ibfk_3` FOREIGN KEY (`transaction_branchcode`) REFERENCES `branches` (`branchcode`)
+            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+        |);
+    }
+
+    $dbh->do(q|
+        INSERT IGNORE INTO systempreferences (variable,value,options,explanation,type)
+        VALUES ('Pseudonymization','0',NULL,'If enabled patrons and transactions will be copied in a separate table for statistics purpose','YesNo')
+    |);
+    $dbh->do(q|
+        INSERT IGNORE INTO systempreferences (variable,value,options,explanation,type)
+        VALUES ('PseudonymizationPatronFields','','title,city,state,zipcode,country,branchcode,categorycode,dateenrolled,sex,sort1,sort2','Patron fields to copy to the pseudonymized_transactions table','multiple')
+    |);
+    $dbh->do(q|
+        INSERT IGNORE INTO systempreferences (variable,value,options,explanation,type)
+        VALUES ('PseudonymizationTransactionFields','','datetime,transaction_branchcode,transaction_type,itemnumber,itemtype,holdingbranch,homebranch,location,itemcallnumber,ccode','Transaction fields to copy to the pseudonymized_transactions table','multiple')
+    |);
+
+    unless( TableExists( 'pseudonymized_borrower_attributes' ) ) {
+        $dbh->do(q|
+            CREATE TABLE pseudonymized_borrower_attributes (
+              `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, -- Row id field
+              `transaction_id` int(11) NOT NULL,
+              `code` varchar(10) NOT NULL,
+              `attribute` varchar(255) default NULL,
+              CONSTRAINT `pseudonymized_borrower_attributes_ibfk_1` FOREIGN KEY (`transaction_id`) REFERENCES `pseudonymized_transactions` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+              CONSTRAINT `anonymized_borrower_attributes_ibfk_2` FOREIGN KEY (`code`) REFERENCES `borrower_attribute_types` (`code`) ON DELETE CASCADE ON UPDATE CASCADE
+            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+        |);
+    }
+
+    unless( column_exists('borrower_attribute_types', 'keep_for_pseudonymization') ) {
+        $dbh->do(q|
+            ALTER TABLE borrower_attribute_types ADD COLUMN `keep_for_pseudonymization` TINYINT(1) NOT NULL DEFAULT 0 AFTER `class`
+        |);
+    }
+
+    NewVersion( $DBversion, 24151, "Add pseudonymized_transactions tables and sysprefs for Pseudonymization" );
+}
+
+$DBversion = '20.06.00.007';
+if( CheckVersion( $DBversion ) ) {
+    if( !column_exists( 'borrower_attribute_types', 'mandatory' ) ) {
+        $dbh->do(q|
+            ALTER TABLE borrower_attribute_types
+            ADD COLUMN mandatory TINYINT(1) NOT NULL DEFAULT 0
+            AFTER keep_for_pseudonymization
+        |);
+    }
+
+    NewVersion( $DBversion, 22844, "Add borrower_attribute_types.mandatory" );
+}
+
+$DBversion = '20.06.00.008';
+if( CheckVersion( $DBversion ) ) {
+    $dbh->do( "UPDATE itemtypes SET imageurl = REPLACE (imageurl, '.gif', '.png') WHERE imageurl LIKE 'bridge/%'" );
+
+    NewVersion( $DBversion, 23148, "Replace Bridge icons with transparent PNG files" );
+}
+
+$DBversion = '20.06.00.009';
+if( CheckVersion( $DBversion ) ) {
+    $dbh->do( q{
+            INSERT IGNORE INTO systempreferences (variable,value,options,explanation,type)
+            VALUES ('ILLHiddenRequestStatuses',NULL,NULL,'ILL statuses that are considered finished and should not be displayed in the ILL module','multiple')
+    });
+
+    NewVersion( $DBversion, 23391, "Hide finished ILL requests" );
+}
+
+$DBversion = '20.06.00.010';
+if( CheckVersion( $DBversion ) ) {
+    $dbh->do(q{
+        INSERT IGNORE INTO systempreferences ( `variable`, `value`, `options`, `explanation`, `type` ) VALUES
+        ('NoRefundOnLostReturnedItemsAge','','','Do not refund lost item fees if item is lost for more than this number of days','Integer')
+    });
+
+    NewVersion( $DBversion, 20815, "Add NoRefundOnLostReturnedItemsAge system preference" );
+}
+
+$DBversion = '20.06.00.011';
+if( CheckVersion( $DBversion ) ) {
+    unless( column_exists( 'export_format', 'staff_only' ) ) {
+        $dbh->do(q|
+            ALTER TABLE export_format
+                ADD staff_only TINYINT(1) NOT NULL DEFAULT 0 AFTER used_for,
+                ADD KEY `staff_only_idx` (`staff_only`);
+        |);
+    }
+
+    unless ( index_exists( 'export_format', 'used_for_idx' ) ) {
+        $dbh->do(q|
+            ALTER TABLE export_format
+                ADD KEY `used_for_idx` (`used_for` (191));
+        |);
+    }
+
+    NewVersion( $DBversion, 5087, "Add export_format.staff_only" );
+}
+
+$DBversion = '20.06.00.012';
+if( CheckVersion( $DBversion ) ) {
+
+    # get list of installed translations
+    require C4::Languages;
+    my @langs;
+    my $tlangs = C4::Languages::getTranslatedLanguages('opac','bootstrap');
+
+    foreach my $language ( @$tlangs ) {
+        foreach my $sublanguage ( @{$language->{'sublanguages_loop'}} ) {
+            push @langs, $sublanguage->{'rfc4646_subtag'};
+        }
+    }
+
+    # Get any existing value from the opaccredits system preference
+    my ($opaccredits) = $dbh->selectrow_array( q|
+        SELECT value FROM systempreferences WHERE variable='opaccredits';
+    |);
+    if( $opaccredits ){
+        foreach my $lang ( @langs ) {
+            # If there is a value in the opaccredits preference, insert it into opac_news
+            $dbh->do("INSERT IGNORE INTO opac_news (branchcode, lang, title, content ) VALUES (NULL, ?, '', ?)", undef, "opaccredits_$lang", $opaccredits);
+        }
+    }
+    # Remove the opaccredits system preference
+    $dbh->do("DELETE FROM systempreferences WHERE variable='opaccredits'");
+
+    NewVersion( $DBversion, 23795, "Convert OpacCredits system preference to news block" );
+}
+
+$DBversion = '20.06.00.013';
+if( CheckVersion( $DBversion ) ) {
+
+    # get list of installed translations
+    require C4::Languages;
+    my @langs;
+    my $tlangs = C4::Languages::getTranslatedLanguages('opac','bootstrap');
+
+    foreach my $language ( @$tlangs ) {
+        foreach my $sublanguage ( @{$language->{'sublanguages_loop'}} ) {
+            push @langs, $sublanguage->{'rfc4646_subtag'};
+        }
+    }
+
+    # Get any existing value from the OpacCustomSearch system preference
+    my ($OpacCustomSearch) = $dbh->selectrow_array( q|
+        SELECT value FROM systempreferences WHERE variable='OpacCustomSearch';
+    |);
+    if( $OpacCustomSearch ){
+        foreach my $lang ( @langs ) {
+            # If there is a value in the OpacCustomSearch preference, insert it into opac_news
+            $dbh->do("INSERT INTO opac_news (branchcode, lang, title, content ) VALUES (NULL, ?, '', ?)", undef, "OpacCustomSearch_$lang", $OpacCustomSearch);
+        }
+    }
+    # Remove the OpacCustomSearch system preference
+    $dbh->do("DELETE FROM systempreferences WHERE variable='OpacCustomSearch'");
+
+    NewVersion( $DBversion, 23795, "Convert OpacCustomSearch system preference to news block" );
+}
+
+$DBversion = '20.06.00.014';
+if( CheckVersion( $DBversion ) ) {
+
+    $dbh->do( "ALTER TABLE opac_news CHANGE lang lang VARCHAR(50) NOT NULL DEFAULT ''" );
+
+    NewVersion( $DBversion, 23797, "Extend the opac_news lang column to accommodate longer values" );
+}
+
+$DBversion = '20.06.00.015';
+if( CheckVersion( $DBversion ) ) {
+
+    # get list of installed translations
+    require C4::Languages;
+    my @langs;
+    my $tlangs = C4::Languages::getTranslatedLanguages('opac','bootstrap');
+
+    foreach my $language ( @$tlangs ) {
+        foreach my $sublanguage ( @{$language->{'sublanguages_loop'}} ) {
+            push @langs, $sublanguage->{'rfc4646_subtag'};
+        }
+    }
+
+    # Get any existing value from the OpacLoginInstructions system preference
+    my ($opaclogininstructions) = $dbh->selectrow_array( q|
+        SELECT value FROM systempreferences WHERE variable='OpacLoginInstructions';
+    |);
+    if( $opaclogininstructions ){
+        foreach my $lang ( @langs ) {
+            # If there is a value in the OpacLoginInstructions preference, insert it into opac_news
+            $dbh->do("INSERT INTO opac_news (branchcode, lang, title, content ) VALUES (NULL, ?, '', ?)", undef, "OpacLoginInstructions_$lang", $opaclogininstructions);
+        }
+    }
+    # Remove the OpacLoginInstructions system preference
+    $dbh->do("DELETE FROM systempreferences WHERE variable='OpacLoginInstructions'");
+
+    NewVersion( $DBversion, 23797, "Convert OpacLoginInstructions system preference to news block" );
+}
+
+$DBversion = '20.06.00.016';
+if( CheckVersion( $DBversion ) ) {
+
+    unless ( column_exists('branchtransfers', 'daterequested') ) {
+        $dbh->do(
+            qq{
+                ALTER TABLE branchtransfers
+                ADD
+                  `daterequested` timestamp NOT NULL default CURRENT_TIMESTAMP
+                AFTER
+                  `itemnumber`
+              }
+        );
+    }
+
+    NewVersion( $DBversion, 23092, "Add 'daterequested' field to transfers table" );
+}
+
+$DBversion = '20.06.00.017';
+if( CheckVersion( $DBversion ) ) {
+    $dbh->do( "UPDATE systempreferences SET variable='NotesToHide' WHERE variable = 'NotesBlacklist'" );
+    NewVersion( $DBversion, 25709, "Rename systempreference to NotesToHide");
+}
+
+$DBversion = '20.06.00.018';
+if( CheckVersion( $DBversion ) ) {
+    $dbh->do(q|
+        INSERT IGNORE INTO permissions (module_bit, code, description) VALUES
+        (11, 'reopen_closed_invoices', 'Reopen closed invoices')
+    |);
+
+    $dbh->do(q|
+        INSERT IGNORE INTO permissions (module_bit, code, description) VALUES
+        (11, 'edit_invoices', 'Edit invoices')
+    |);
+
+    $dbh->do(q|
+        INSERT IGNORE INTO permissions (module_bit, code, description) VALUES
+        (11, 'delete_baskets', 'Delete baskets')
+    |);
+
+    $dbh->do(q|
+        INSERT IGNORE INTO permissions (module_bit, code, description) VALUES
+        (11, 'delete_invoices', 'Delete invoices')
+    |);
+
+    $dbh->do(q|
+        INSERT IGNORE INTO permissions (module_bit, code, description) VALUES
+        (11, 'merge_invoices', 'Merge invoices')
+    |);
+
+    NewVersion( $DBversion, 24157, "Add new permissions reopen_closed_invoices, edit_invoices, delete_invoices, merge_invoices, delete_basket");
+}
+
+$DBversion = '20.06.00.019';
+if( CheckVersion( $DBversion ) ) {
+    $dbh->do( "INSERT IGNORE INTO systempreferences (variable,value,explanation,options,type) VALUES('NewsToolEditor','tinymce', 'Choose tool for editing News','tinymce|codemirror','Choice')" );
+
+    NewVersion( $DBversion, 22660, "Adds NewsToolEditor system preference");
+}
+
+$DBversion = '20.06.00.020';
+if( CheckVersion( $DBversion ) ) {
+    # Remove from the systempreferences table
+    $dbh->do("DELETE FROM systempreferences WHERE variable = 'GoogleIndicTransliteration'");
+
+    NewVersion( $DBversion, 26070, "Remove references to deprecated Google Transliterate API");
+}
+
+$DBversion = '20.06.00.021';
+if( CheckVersion( $DBversion ) ) {
+    $dbh->do(q{
+        UPDATE systempreferences SET options = "callnum|ccode|location|library"
+        WHERE variable = "OpacItemLocation"
+    });
+    NewVersion( $DBversion, 25871, "Add library option to OpacItemLocation");
+}
+
+$DBversion = '20.06.00.022';
+if( CheckVersion( $DBversion ) ) {
+    unless ( column_exists('itemtypes', 'parent_type') ) {
+        $dbh->do(q{
+            ALTER TABLE itemtypes
+                ADD COLUMN parent_type VARCHAR(10) NULL DEFAULT NULL
+                AFTER itemtype;
+
+        });
+    }
+    unless ( foreign_key_exists( 'itemtypes', 'itemtypes_ibfk_1') ){
+        $dbh->do(q{
+            ALTER TABLE itemtypes
+            ADD CONSTRAINT itemtypes_ibfk_1
+            FOREIGN KEY (parent_type) REFERENCES itemtypes (itemtype)
+        });
+    }
+
+    NewVersion( $DBversion, 21946, "Add parent type to itemtypes" );
+}
+
+$DBversion = '20.06.00.023';
+if( CheckVersion( $DBversion ) ) {
+
+    my $QuoteOfTheDay = C4::Context->preference('QuoteOfTheDay');
+    $dbh->do( q|
+        UPDATE systempreferences
+        SET options = 'intranet,opac',
+            explanation = 'Enable or disable display of Quote of the Day on the OPAC and staff interface home page',
+            type = 'multiple'
+        WHERE variable = 'QuoteOfTheDay'
+    | );
+
+    C4::Context->set_preference('QuoteOfTheDay', $QuoteOfTheDay ? 'opac' : '');
+
+    NewVersion( $DBversion, 16371, "Quote of the Day (QOTD) for the staff interface " );
+}
+
+$DBversion = '20.06.00.024';
+if( CheckVersion( $DBversion ) ) {
+
+    $dbh->do( "UPDATE marc_subfield_structure SET liblibrarian = 'Home library' WHERE liblibrarian = 'Permanent location'
+        AND tagfield = 952 and tagsubfield = 'a'" );
+    $dbh->do( "UPDATE marc_subfield_structure SET libopac = 'Home library' WHERE libopac = 'Permanent location'
+        AND tagfield = 952 and tagsubfield = 'a'" );
+    $dbh->do( "UPDATE marc_subfield_structure SET liblibrarian = 'Current library' WHERE liblibrarian = 'Current location'
+        AND tagfield = 952 and tagsubfield = 'b'" );
+    $dbh->do( "UPDATE marc_subfield_structure SET libopac = 'Current library' WHERE libopac = 'Current location'
+        AND tagfield = 952 and tagsubfield = 'b'" );
+
+    NewVersion( $DBversion, 25867, "Update subfield descriptions for 952\$a and 952\$b");
+}
+
+$DBversion = '20.06.00.025';
+if( CheckVersion( $DBversion ) ) {
+
+    $dbh->do( q{
+        INSERT IGNORE INTO systempreferences (variable,value,options,explanation,type) VALUES
+        ('PatronDuplicateMatchingAddFields','surname|firstname|dateofbirth', NULL,'A list of fields separated by "|" to deduplicate patrons when created','Free')
+    });
+
+    NewVersion( $DBversion, 6725, "Adds PatronDuplicateMatchingAddFields system preference");
+}
+
+$DBversion = '20.06.00.026';
+if (CheckVersion($DBversion)) {
+    unless (column_exists('accountlines', 'credit_number')) {
+        $dbh->do('ALTER TABLE accountlines ADD COLUMN credit_number VARCHAR(20) NULL DEFAULT NULL COMMENT "autogenerated number for credits" AFTER debit_type_code');
+    }
+
+    unless (column_exists('account_credit_types', 'credit_number_enabled')) {
+        $dbh->do(q{
+            ALTER TABLE account_credit_types
+            ADD COLUMN credit_number_enabled TINYINT(1) NOT NULL DEFAULT 0
+                COMMENT "Is autogeneration of credit number enabled for this credit type"
+                AFTER can_be_added_manually
+        });
+    }
+
+    $dbh->do('INSERT IGNORE INTO systempreferences (variable, value, options, explanation, type) VALUES(?, ?, ?, ?, ?)', undef, 'AutoCreditNumber', '', '', 'Automatically generate a number for account credits', 'Choice');
+
+    NewVersion( $DBversion, 19036, "Add accountlines.credit_number, account_credit_types.credit_number_enabled and syspref AutoCreditNumber" );
+}
+
+$DBversion = '20.06.00.027';
+if( CheckVersion( $DBversion ) ) {
+    $dbh->do( "INSERT IGNORE INTO systempreferences (variable,value,explanation,options,type) VALUES('BiblioItemtypeInfo', '0','Control whether biblio level itemtype image displays','0','YesNo')" );
+
+    NewVersion( $DBversion, 8732, 'Add new BiblioItemtypeInfo to system preferences' );
+}
+
+$DBversion = '20.06.00.028';
+if( CheckVersion( $DBversion ) ) {
+    $dbh->do(q{
+        INSERT IGNORE INTO systempreferences ( `variable`, `value`, `options`, `explanation`, `type` ) VALUES
+        ('DefaultLongOverdueSkipLostStatuses', '', NULL, 'Skip these lost statuses by default in longoverdue.pl', 'Free')
+    });
+
+    NewVersion( $DBversion, 25958, "Allow LongOverdue cron to exclude specified lost values");
+}
+
+$DBversion = '20.06.00.029';
+if ( CheckVersion( $DBversion ) ) {
+    $dbh->do(q{
+        INSERT IGNORE INTO authorised_value_categories( category_name, is_system ) VALUES ('HOLD_CANCELLATION', 0);
+    });
+
+    $dbh->do(q{
+INSERT IGNORE INTO `letter` VALUES ('reserves','HOLD_CANCELLATION','','Hold Cancellation',0,'Your hold was canceled.','[%- USE AuthorisedValues -%]\r\nDear [% borrower.firstname %] [% borrower.surname %],\r\n\r\nYour hold for [% biblio.title %] was canceled for the following reason: [% AuthorisedValues.GetByCode( \'HOLD_CANCELLATION\', hold.cancellation_reason ) %]','email','default');
+    });
+
+    if ( !column_exists( 'reserves', 'cancellation_reason' ) ) {
+        $dbh->do(q{
+            ALTER TABLE reserves ADD COLUMN `cancellation_reason` varchar(80) default NULL AFTER cancellationdate;
+        });
+    }
+
+    if ( !column_exists( 'old_reserves', 'cancellation_reason' ) ) {
+        $dbh->do(q{
+            ALTER TABLE old_reserves ADD COLUMN `cancellation_reason` varchar(80) default NULL AFTER cancellationdate;
+        });
+    }
+
+    NewVersion( $DBversion, 25534, "Add ability to send an email specifying a reason when canceling a hold");
+}
+
+$DBversion = '20.06.00.030';
+if ( CheckVersion( $DBversion ) ) {
+
+    $dbh->do(q{
+        INSERT IGNORE INTO systempreferences ( `variable`, `value`, `options`, `explanation`, `type`) VALUES
+        ('AutoApprovePatronProfileSettings', '0', '', 'Automatically approve patron profile changes from the OPAC.', 'YesNo');
+    });
+
+    NewVersion( $DBversion, 20057, "Add new system preference 'AutoApprovePatronProfileSettings'");
+}
+
+$DBversion = '20.06.00.031';
+if( CheckVersion( $DBversion ) ) {
+
+    if( !column_exists( 'reserves', 'non_priority' ) ) {
+        $dbh->do("ALTER TABLE reserves ADD COLUMN `non_priority` tinyint(1) NOT NULL DEFAULT 0 AFTER `item_level_hold`");
+    }
+
+    if( !column_exists( 'old_reserves', 'non_priority' ) ) {
+        $dbh->do("ALTER TABLE old_reserves ADD COLUMN `non_priority` tinyint(1) NOT NULL DEFAULT 0 AFTER `item_level_hold`");
+    }
+
+    NewVersion( $DBversion, 22789, "Add non_priority column on reserves and old_reserves tables");
+}
+
+$DBversion = '20.06.00.032';
+if( CheckVersion( $DBversion ) ) {
+    if( !column_exists( 'items', 'exclude_from_local_holds_priority' ) ) {
+        $dbh->do(q{
+            ALTER TABLE `items` ADD COLUMN `exclude_from_local_holds_priority` tinyint(1) default NULL AFTER `new_status`
+        });
+    }
+
+    if( !column_exists( 'deleteditems', 'exclude_from_local_holds_priority' ) ) {
+        $dbh->do(q{
+            ALTER TABLE `deleteditems` ADD COLUMN `exclude_from_local_holds_priority` tinyint(1) default NULL AFTER `new_status`
+        });
+    }
+
+    if( !column_exists( 'categories', 'exclude_from_local_holds_priority' ) ) {
+        $dbh->do(q{
+            ALTER TABLE `categories` ADD COLUMN `exclude_from_local_holds_priority` tinyint(1) default NULL AFTER `change_password`
+        });
+    }
+    NewVersion( $DBversion, 19889, "Add exclude_from_local_holds_priority column to items, deleteditems and categories tables");
+}
+
+$DBversion = '20.06.00.033';
+if( CheckVersion( $DBversion ) ) {
+    if( column_exists( 'opac_news', 'timestamp' ) ) {
+        $dbh->do(q|
+            ALTER TABLE opac_news
+            CHANGE COLUMN timestamp published_on date DEFAULT NULL
+        |);
+    }
+    if( !column_exists( 'opac_news', 'updated_on' ) ) {
+        $dbh->do(q|
+            ALTER TABLE opac_news
+            ADD COLUMN updated_on timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP AFTER published_on
+        |);
+    }
+
+    $dbh->do(q|
+        UPDATE letter
+        SET content = REPLACE(content,?,?)
+        WHERE content LIKE ?
+    |, undef, 'opac_news.timestamp', 'opac_news.published_on', '%opac_news.timestamp%' );
+
+    NewVersion( $DBversion, 21066, ["Rename column opac_news.timestamp with published_on", "Add new column opac_news.updated_on", "Replace timestamp references in letters table"] );
+}
+
+$DBversion = '20.06.00.034';
+if( CheckVersion( $DBversion ) ) {
+    $dbh->do(q|
+        INSERT IGNORE INTO systempreferences (variable, value, options, explanation, type)
+        VALUES ('AddressForFailedOverdueNotices', '', NULL, 'Destination email for failed overdue notices. If left empty then it will fallback to the first defined address in the following list: Library ReplyTo, Library Email, ReplytoDefault and KohaAdminEmailAddress', 'free')
+    |);
+
+    NewVersion( $DBversion, 24197, "Add new system preference 'AddressForFailedOverdueNotices'" );
+}
+
+$DBversion = '20.06.00.035';
+if ( CheckVersion( $DBversion ) ) {
+    $dbh->do(q{
+        INSERT IGNORE INTO systempreferences ( `variable`, `value`, `options`, `explanation`, `type` ) VALUES
+        ('EdifactInvoiceImport', 'automatic', 'automatic|manual', "If on, don't auto-import EDI invoices, just keep them in the database with the status 'new'", 'Choice')
+    });
+
+    NewVersion( $DBversion, 23682, "Add new system preference 'EdifactInvoiceImport'" );
+}
+
+$DBversion = '20.06.00.036';
+if( CheckVersion( $DBversion ) ) {
+    # Fix the markup in the OPACSearchForTitleIn system preference
+    $dbh->do("UPDATE systempreferences SET VALUE = replace( value, '</li>', ''), value = REPLACE( value, '<li>', '') WHERE VARIABLE = 'OPACSearchForTitleIn';");
+
+    NewVersion( $DBversion, 20168, "Update OPACSearchForTitleIn to work with Bootstrap 4");
+}
+
+$DBversion = '20.06.00.037';
+if( CheckVersion( $DBversion ) ) {
+    if( !column_exists( 'categories', 'min_password_length' ) ) {
+        $dbh->do("ALTER TABLE categories ADD COLUMN `min_password_length` smallint(6) NULL DEFAULT NULL AFTER `change_password` -- set minimum password length for patrons in this category");
+    }
+    if( !column_exists( 'categories', 'require_strong_password' ) ) {
+        $dbh->do("ALTER TABLE categories ADD COLUMN `require_strong_password` TINYINT(1) NULL DEFAULT NULL AFTER `min_password_length` -- set required password strength for patrons in this category");
+    }
+
+    NewVersion( $DBversion, 23816, "Add min_password_length and require_strong_password columns in categories table");
+}
+
+$DBversion = '20.06.00.038';
+if( CheckVersion( $DBversion ) ) {
+    $dbh->do( "ALTER TABLE `search_field` MODIFY COLUMN `type` enum('','string','date','number','boolean','sum','isbn','stdno','year') NOT NULL" );
+    $dbh->do( "UPDATE `search_field` SET type = 'year' WHERE name = 'date-of-publication'" );
+
+    NewVersion( $DBversion, 24807, "Add 'year' type to improve sorting behaviour" );
+}
+
+$DBversion = '20.06.00.039';
+if( CheckVersion( $DBversion ) ) {
+
+    if( !column_exists( 'hold_fill_targets', 'reserve_id' ) ) {
+        $dbh->do( "ALTER TABLE hold_fill_targets ADD COLUMN reserve_id int(11) DEFAULT NULL AFTER item_level_request" );
+    }
+
+    NewVersion( $DBversion, 18958, "Add reserve_id to hold_fill_targets");
+}
+
+$DBversion = '20.06.00.040';
+if( CheckVersion( $DBversion ) ) {
+    $dbh->do( "INSERT IGNORE INTO systempreferences (variable, value, options, explanation, type) VALUES ('OpacMetaDescription','','','This description will show in search engine results (160 characters).','Textarea');" );
+
+    NewVersion( $DBversion, 26454, "Add system preference to set meta description for the OPAC");
+}
+
+$DBversion = '20.06.00.041';
+if ( CheckVersion($DBversion) ) {
+
+    if ( column_exists( 'items', 'paidfor' ) ) {
+        my ($count) = $dbh->selectrow_array(
+            q|
+                SELECT COUNT(*)
+                FROM items
+                WHERE paidfor IS NOT NULL AND paidfor <> ""
+            |
+        );
+        if ($count) {
+            warn "Warning - Cannot remove column items.paidfor. At least one value exists";
+        }
+        else {
+            $dbh->do(q|ALTER TABLE items DROP COLUMN paidfor|);
+            $dbh->do(q|UPDATE marc_subfield_structure SET kohafield = '' WHERE kohafield = 'items.paidfor'|);
+        }
+    }
+
+    if ( column_exists( 'deleteditems', 'paidfor' ) ) {
+        my ($count) = $dbh->selectrow_array(
+            q|
+                SELECT COUNT(*)
+                FROM deleteditems
+                WHERE paidfor IS NOT NULL AND paidfor <> ""
+            |
+        );
+        if ($count) {
+            warn "Warning - Cannot remove column deleteditems.paidfor. At least one value exists";
+        }
+        else {
+            $dbh->do(q|ALTER TABLE deleteditems DROP COLUMN paidfor|);
+        }
+    }
+
+    NewVersion( $DBversion, 26268, "Remove items.paidfor field" );
+}
+
+$DBversion = '20.06.00.042';
+if( CheckVersion( $DBversion ) ) {
+    unless ( column_exists('letter', 'updated_on') ) {
+        $dbh->do(q|
+            ALTER TABLE letter ADD COLUMN updated_on timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP AFTER lang
+        |);
+    }
+
+    NewVersion( $DBversion, 25776, "Add letter.updated_on");
+}
+
+$DBversion = '20.06.00.043';
+if( CheckVersion( $DBversion ) ) {
+    $dbh->do(q{
+        INSERT IGNORE INTO systempreferences (variable,value,options,explanation,type) VALUES ('CircConfirmItemParts', '0', NULL, 'Require staff to confirm that all parts of an item are present at checkin/checkout.', 'YesNo')
+    });
+
+    NewVersion( $DBversion, 25261, "Add CircConfirmItemParts syspref");
+}
+
 # SEE bug 13068
 # if there is anything in the atomicupdate, read and execute it.
 my $update_dir = C4::Context->config('intranetdir') . '/installer/data/mysql/atomicupdate/';
@@ -22241,7 +22889,7 @@ foreach my $file ( sort readdir $dirh ) {
         my $rv = $installer->load_sql( $update_dir . $file ) ? 0 : 1;
     } elsif ( $file =~ /\.perl$/ ) {
         my $code = read_file( $update_dir . $file );
-        eval $code;
+        eval $code; ## no critic (StringyEval)
         say "Atomic update generated errors: $@" if $@;
     }
 }