Merge branch 'master' of /usr/local/git/koha_base.git/
[koha.git] / updater / updatedatabase
1 #!/usr/bin/perl
2
3 # $Id$
4
5 # Database Updater
6 # This script checks for required updates to the database.
7
8 # Part of the Koha Library Software www.koha.org
9 # Licensed under the GPL.
10
11 # Bugs/ToDo:
12 # - Would also be a good idea to offer to do a backup at this time...
13
14 # NOTE:  If you do something more than once in here, make it table driven.
15 use strict;
16
17 # CPAN modules
18 use DBI;
19 use Getopt::Long;
20 # Koha modules
21 use C4::Context;
22
23 use MARC::Record;
24 use MARC::File::XML ( BinaryEncoding => 'utf8' );
25  
26 # FIXME - The user might be installing a new database, so can't rely
27 # on /etc/koha.conf anyway.
28
29 my $debug = 0;
30
31 my (
32     $sth, $sti,
33     $query,
34     %existingtables,    # tables already in database
35     %types,
36     $table,
37     $column,
38     $type, $null, $key, $default, $extra,
39     $prefitem,          # preference item in systempreferences table
40 );
41
42 my $silent;
43 GetOptions(
44     's' =>\$silent
45     );
46 my $dbh = C4::Context->dbh;
47 print "connected to your DB. Checking & modifying it\n" unless $silent;
48 $|=1; # flushes output
49
50 #-------------------
51 # Defines
52
53 # Tables to add if they don't exist
54 my %requiretables = (
55     action_logs     => "(
56                     `timestamp` TIMESTAMP NOT NULL ,
57                     `user` INT( 11 ) NOT NULL ,
58                     `module` TEXT default '',
59                     `action` TEXT default '' ,
60                     `object` INT(11) NULL ,
61                     `info` TEXT default '' ,
62                     PRIMARY KEY ( `timestamp` , `user` )
63                 )",
64     letter        => "(
65                     module varchar(20) NOT NULL default '',
66                     code varchar(20) NOT NULL default '',
67                     name varchar(100) NOT NULL default '',
68                     title varchar(200) NOT NULL default '',
69                     content text,
70                     PRIMARY KEY  (module,code)
71                 )",
72     alert        =>"(
73                     alertid int(11) NOT NULL auto_increment,
74                     borrowernumber int(11) NOT NULL default '0',
75                     type varchar(10) NOT NULL default '',
76                     externalid varchar(20) NOT NULL default '',
77                     PRIMARY KEY  (alertid),
78                     KEY borrowernumber (borrowernumber),
79                     KEY type (type,externalid)
80                 )",
81     opac_news => "(
82                 `idnew` int(10) unsigned NOT NULL auto_increment,
83                 `title` varchar(250) NOT NULL default '',
84                 `new` text NOT NULL,
85                 `lang` varchar(4) NOT NULL default '',
86                 `timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP,
87                 PRIMARY KEY  (`idnew`)
88                 )",
89     repeatable_holidays => "(
90                 `id` int(11) NOT NULL auto_increment,
91                 `branchcode` varchar(4) NOT NULL default '',
92                 `weekday` smallint(6) default NULL,
93                 `day` smallint(6) default NULL,
94                 `month` smallint(6) default NULL,
95                 `title` varchar(50) NOT NULL default '',
96                 `description` text NOT NULL,
97                 PRIMARY KEY  (`id`)
98                 )",
99     special_holidays => "(
100                 `id` int(11) NOT NULL auto_increment,
101                 `branchcode` varchar(4) NOT NULL default '',
102                 `day` smallint(6) NOT NULL default '0',
103                 `month` smallint(6) NOT NULL default '0',
104                 `year` smallint(6) NOT NULL default '0',
105                 `isexception` smallint(1) NOT NULL default '1',
106                 `title` varchar(50) NOT NULL default '',
107                 `description` text NOT NULL,
108                 PRIMARY KEY  (`id`)
109                 )",
110     overduerules    =>"(`branchcode` varchar(255) NOT NULL default '',
111                     `categorycode` char(2) NOT NULL default '',
112                     `delay1` int(4) default '0',
113                     `letter1` varchar(20) default NULL,
114                     `debarred1` char(1) default '0',
115                     `delay2` int(4) default '0',
116                     `debarred2` char(1) default '0',
117                     `letter2` varchar(20) default NULL,
118                     `delay3` int(4) default '0',
119                     `letter3` varchar(20) default NULL,
120                     `debarred3` int(1) default '0',
121                     PRIMARY KEY  (`branchcode`,`categorycode`)
122                     )",
123     cities            => "(`cityid` int auto_increment,
124                         `city_name` char(100) NOT NULL,
125                         `city_zipcode` char(20),
126                         PRIMARY KEY (`cityid`)
127                     )",
128     roadtype            => "(`roadtypeid` int auto_increment,
129                         `road_type` char(100) NOT NULL,
130                         PRIMARY KEY (`roadtypeid`)
131                     )",
132
133     labels                     => "(
134                 labelid int(11) NOT NULL auto_increment,
135                                itemnumber varchar(100) NOT NULL default '',
136                                timestamp timestamp(14) NOT NULL,
137                                PRIMARY KEY  (labelid)
138                                )",
139
140     labels_conf                => "(
141                 id int(4) NOT NULL auto_increment,
142                                barcodetype char(100) default '',
143                                title tinyint(1) default '0',
144                                isbn tinyint(1) default '0',
145                                itemtype tinyint(1) default '0',
146                                barcode tinyint(1) default '0',
147                                dewey tinyint(1) default '0',
148                                class tinyint(1) default '0',
149                                author tinyint(1) default '0',
150                                papertype char(100) default '',
151                                startrow int(2) default NULL,
152                                PRIMARY KEY  (id)
153                                )",
154        reviews                  => "(
155                             reviewid integer NOT NULL auto_increment,
156                             borrowernumber integer,
157                             biblionumber integer,
158                             review text,
159                             approved tinyint,
160                             datereviewed datetime,
161                             PRIMARY KEY (reviewid)
162                             )",
163     subscriptionroutinglist=>"(
164                              routingid integer NOT NULL auto_increment,
165                              borrowernumber integer,
166                              ranking integer,
167                              subscriptionid integer,
168                             PRIMARY KEY (routingid)
169                              )",
170
171     notifys    => "(
172               notify_id int(11) NOT NULL default '0',
173                 `borrowernumber` int(11) NOT NULL default '0',
174               `itemnumber` int(11) NOT NULL default '0',
175               `notify_date` date NOT NULL default '0000-00-00',
176                       `notify_send_date` date default NULL,
177                       `notify_level` int(1) NOT NULL default '0',
178                       `method` varchar(20) NOT NULL default ''
179               )",
180
181    charges    => "(
182               `charge_id` varchar(5) NOT NULL default '',
183                 `description` text NOT NULL,
184                 `amount` decimal(28,6) NOT NULL default '0.000000',
185                           `min` int(4) NOT NULL default '0',
186                 `max` int(4) NOT NULL default '0',
187                           `level` int(1) NOT NULL default '0',
188                           PRIMARY KEY  (`charge_id`)
189               )",
190     tags => "(
191         `entry` varchar(255) NOT NULL default '',
192         `weight` bigint(20) NOT NULL default '0',
193          PRIMARY KEY  (`entry`)
194     )
195     ",
196    zebraqueue    => "(
197                 `id` int NOT NULL auto_increment,
198                 `biblio_auth_number` int NOT NULL,
199                 `operation` char(20) NOT NULL,
200                 `server` char(20) NOT NULL ,
201                 PRIMARY KEY  (`id`)
202               ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci AUTO_INCREMENT=1",
203
204 );
205
206 my %requirefields = (
207     subscription => { 'letter' => 'char(20) NULL', 'distributedto' => 'text NULL', 'firstacquidate'=>'date NOT NULL','irregularity'=>'TEXT NULL default \'\'','numberpattern'=>'TINYINT(3) NULL default 0', 'callnumber'=>'text NULL', 'hemisphere' =>'TINYINT(3) NULL default 0', 'issuesatonce'=>'TINYINT(3) NOT NULL default 1',  'branchcode' =>'varchar(12) NOT NULL default \'\'', 'manualhistory'=>'TINYINT(1) NOT NULL default 0','internalnotes'=>'LONGTEXT NULL default \'\''},
208     itemtypes => { 'imageurl' => 'char(200) NULL'},
209     aqbookfund => { 'branchcode' => 'varchar(4) NULL'},
210     aqbudget => { 'branchcode' => 'varchar(4) NULL'},
211     auth_header => { 'marc' => 'BLOB NOT NULL', 'linkid' => 'BIGINT(20) NULL'},
212     auth_subfield_structure =>{ 'hidden' => 'TINYINT(3) NOT NULL default 0', 'kohafield' => 'VARCHAR(45) NOT NULL', 'linkid' =>  'TINYINT(1) NOT NULL default 0', 'isurl' => 'TINYINT(1)', 'frameworkcode'=>'VARCHAR(8) NOT  NULL'},
213     marc_breeding => { 'isbn' => 'varchar(13) NOT NULL'},
214     serial =>{ 'publisheddate' => 'date', 'claimdate' => 'date', 'itemnumber'=>'text NULL','routingnotes'=>'text NULL',},
215     statistics => { 'associatedborrower' => 'integer'},
216     z3950servers =>{  "name" =>"text",  "description" => "text NOT NULL",
217                     "position" =>"enum('primary','secondary','') NOT NULL default 'primary'",  "icon" =>"text",
218                     "type" =>"enum('zed','opensearch') NOT NULL default 'zed'",
219                     },
220     issues =>{ 'issuedate'=>"date NOT NULL default '0000-00-00'", },
221
222 #    tablename        => { 'field' => 'fieldtype' },
223 );
224
225 # Enter here the table to delete.
226 my @TableToDelete = qw(
227     additionalauthors
228     bibliosubject
229     bibliosubtitle
230     bibliothesaurus
231 );
232
233 my %uselessfields = (
234 # tablename => "field1,field2",
235     borrowers => "suburb,altstreetaddress,altsuburb,altcity,studentnumber,school,area,preferredcont,altcp",
236     deletedborrowers=> "suburb,altstreetaddress,altsuburb,altcity,studentnumber,school,area,preferredcont,altcp",
237     );
238 # the other hash contains other actions that can't be done elsewhere. they are done
239 # either BEFORE of AFTER everything else, depending on "when" entry (default => AFTER)
240
241 # The tabledata hash contains data that should be in the tables.
242 # The uniquefieldrequired hash entry is used to determine which (if any) fields
243 # must not exist in the table for this row to be inserted.  If the
244 # uniquefieldrequired entry is already in the table, the existing data is not
245 # modified, unless the forceupdate hash entry is also set.  Fields in the
246 # anonymous "forceupdate" hash will be forced to be updated to the default
247 # values given in the %tabledata hash.
248
249 my %tabledata = (
250 # tablename => [
251 #    {    uniquefielrequired => 'fieldname', # the primary key in the table
252 #        fieldname => fieldvalue,
253 #        fieldname2 => fieldvalue2,
254 #    },
255 # ],
256     systempreferences => [
257         {
258             uniquefieldrequired => 'variable',
259             variable            => 'useDaysMode',
260             value               => 'Calendar',
261             forceupdate         => { 'explanation' => 1,
262                                      'type' => 1},
263             explanation            => 'How to calculate return dates : Calendar means holidays will be controled, Days means the return date don\'t depend on holidays',
264             type        => 'Choice',
265             options        => 'Calendar|Days'
266         },
267         {
268             uniquefieldrequired => 'variable',
269             variable            => 'BorrowersTitles',
270             value               => 'Mr|Mrs|Miss|Ms',
271             forceupdate         => { 'explanation' => 1,
272                                      'type' => 1},
273             explanation         => 'List all Titles for borrowers',
274             type                => 'free',
275         },
276         {
277             uniquefieldrequired => 'variable',
278             variable            => 'BorrowerMandatoryField',
279             value               => 'cardnumber|surname|address',
280             forceupdate         => { 'explanation' => 1,
281                                      'type' => 1},
282             explanation         => 'List all mandatory fields for borrowers',
283             type                => 'free',
284         },
285         {
286             uniquefieldrequired => 'variable',
287             variable            => 'borrowerRelationship',
288             value               => 'father|mother,grand-mother',
289             forceupdate         => { 'explanation' => 1,
290                                      'type' => 1},
291             explanation         => 'The relationships between a guarantor & a guarantee (separated by | or ,)',
292             type                => 'free',
293         },
294         {
295             uniquefieldrequired => 'variable',
296             variable            => 'ReservesMaxPickUpDelay',
297             value               => '10',
298             forceupdate         => { 'explanation' => 1,
299                                      'type' => 1},
300             explanation         => 'Maximum delay to pick up a reserved document',
301             type                => 'free',
302         },
303         {
304             uniquefieldrequired => 'variable',
305             variable            => 'TransfersMaxDaysWarning',
306             value               => '3',
307             forceupdate         => { 'explanation' => 1,
308                                      'type' => 1},
309             explanation         => 'Max delay before considering the transfer has potentialy a problem',
310             type                => 'free',
311         },
312         {
313             uniquefieldrequired => 'variable',
314             variable            => 'memberofinstitution',
315             value               => '0',
316             forceupdate         => { 'explanation' => 1,
317                                      'type' => 1},
318             explanation         => 'Are your patrons members of institutions',
319             type                => 'YesNo',
320         },
321     {
322             uniquefieldrequired => 'variable',
323             variable            => 'ReadingHistory',
324             value               => '0',
325             forceupdate         => { 'explanation' => 1,
326                                      'type' => 1},
327             explanation         => 'Allow reading record info retrievable from issues and oldissues tables',
328             type                => 'YesNo',
329         },
330     {
331             uniquefieldrequired => 'variable',
332             variable            => 'IssuingInProcess',
333             value               => '0',
334             forceupdate         => { 'explanation' => 1,
335                                      'type' => 1},
336             explanation         => 'Allow no debt alert if the patron is issuing item that accumulate debt',
337             type                => 'YesNo',
338         },
339     {
340             uniquefieldrequired => 'variable',
341             variable            => 'AutomaticItemReturn',
342             value               => '1',
343             forceupdate         => { 'explanation' => 1,
344                                      'type' => 1},
345             explanation         => 'This Variable allow or not to return automaticly to his homebranch',
346             type                => 'YesNo',
347         },
348     {
349             uniquefieldrequired => 'variable',
350             variable            => 'reviewson',
351             value               => '0',
352             forceupdate         => { 'explanation' => 1,
353                                      'type' => 1},
354             explanation         => 'Allows patrons to submit reviews from the opac',
355             type                => 'YesNo',
356         },
357     {
358             uniquefieldrequired => 'variable',
359             variable            => 'intranet_includes',
360             value               => 'includes',
361             forceupdate         => { 'explanation' => 1,
362                                      'type' => 1},
363             explanation         => 'The includes directory you want for specific look of Koha (includes or includes_npl for example)',
364             type                => 'Free',
365         },
366         {
367             uniquefieldrequired => 'variable',
368             variable            => 'AutoLocation',
369             value               => '0',
370             forceupdate         => { 'explanation' => 1,
371                                      'type' => 1},
372             explanation         => 'switch to activate or not Autolocation, if Yes, the Librarian can\'t change his location, it\'s defined by branchip',
373             type                => 'YesNo',
374         },
375         {
376             uniquefieldrequired => 'variable',
377             variable            => 'serialsadditems',
378             value               => '0',
379             forceupdate         => {
380                 'explanation' => 1,
381                 'type' => 1
382             },
383             explanation => 'If set, a new item will be automatically added when receiving an issue',
384             type => 'YesNo',
385         },
386         {
387             uniquefieldrequired => 'variable',
388             variable            => 'expandedSearchOption',
389             value               => '0',
390             forceupdate         => {
391                 'explanation' => 1,
392                 'type' => 1
393             },
394             explanation => 'search among marc field',
395             type => 'YesNo',
396         },
397        {
398             uniquefieldrequired => 'variable',
399             variable            => 'RequestOnOpac',
400             value               => '1',
401             forceupdate         => { 'explanation' => 1,
402                                      'type' => 1},
403             explanation         => 'option to allow reserves on opac',
404             type                => 'YesNo',
405         },
406        {
407             uniquefieldrequired => 'variable',
408             variable            => 'OpacCloud',
409             value               => '1',
410             forceupdate         => { 'explanation' => 1,
411                                      'type' => 1},
412             explanation         => 'Enable / Disable cloud link on OPAC (Require to run misc/cronjobs/build_browser_and_cloud.pl on the server)',
413             type                => 'YesNo',
414         },
415        {
416             uniquefieldrequired => 'variable',
417             variable            => 'OpacBrowser',
418             value               => '1',
419             forceupdate         => { 'explanation' => 1,
420                                      'type' => 1},
421             explanation         => 'Enable/Disable browser link on OPAC (Require to run misc/cronjobs/build_browser_and_cloud.pl on the server)',
422             type                => 'YesNo',
423         },
424        {
425             uniquefieldrequired => 'variable',
426             variable            => 'OpacTopissue',
427             value               => '1',
428             forceupdate         => { 'explanation' => 1,
429                                      'type' => 1},
430             explanation         => 'Enable / Disable the top issue link on OPAC',
431             type                => 'YesNo',
432         },
433        {
434             uniquefieldrequired => 'variable',
435             variable            => 'OpacAuthorities',
436             value               => '1',
437             forceupdate         => { 'explanation' => 1,
438                                      'type' => 1},
439             explanation         => 'Enable / Disable the search authority link on OPAC',
440             type                => 'YesNo',
441         },
442         {
443             uniquefieldrequired => 'variable',
444             variable            => 'CataloguingLog',
445             value               => '0',
446             forceupdate         => {'explanation' => 1, 'type' => 1},
447             explanation         => 'Active this if you want to log cataloguing action.',
448             type                => 'YesNo',
449         },
450         {
451             uniquefieldrequired => 'variable',
452             variable            => 'BorrowersLog',
453             value               => '0',
454             forceupdate         => {'explanation' => 1, 'type' => 1},
455             explanation         => 'Active this if you want to log borrowers edition/creation/deletion...',
456             type                => 'YesNo',
457         },
458         {
459             uniquefieldrequired => 'variable',
460             variable            => 'SubscriptionLog',
461             value               => '0',
462             forceupdate         => {'explanation' => 1, 'type' => 1},
463             explanation         => 'Active this if you want to log Subscription action',
464             type                => 'YesNo',
465         },
466         {
467             uniquefieldrequired => 'variable',
468             variable            => 'IssueLog',
469             value               => '0',
470             forceupdate         => {'explanation' => 1, 'type' => 1},
471             explanation         => 'Active this if you want to log issue.',
472             type                => 'YesNo',
473         },
474         {
475             uniquefieldrequired => 'variable',
476             variable            => 'ReturnLog',
477             value               => '0',
478             forceupdate         => {'explanation' => 1, 'type' => 1},
479             explanation         => 'Active this if you want to log the circulation return',
480             type                => 'YesNo',
481         },
482         {
483             uniquefieldrequired => 'variable',
484             variable            => 'Version',
485             value               => '3.0',
486             forceupdate         => {'explanation' => 1, 'type' => 1},
487             explanation         => 'Koha Version',
488             type                => 'Free',
489         },
490         {   
491             uniquefieldrequired => 'variable',
492             variable            => 'LetterLog',
493             value               => '0',
494             forceupdate         => {'explanation' => 1, 'type' => 1},
495             explanation         => 'Active this if you want to log all the letter sent',
496             type                => 'YesNo',
497         },
498         {
499             uniquefieldrequired => 'variable',
500             variable            => 'FinesLog',
501             value               => '0',
502             forceupdate         => {'explanation' => 1, 'type' => 1},
503             explanation         => 'Active this if you want to log fines',
504             type                => 'YesNo',
505         },
506         {
507             uniquefieldrequired => 'variable',
508             variable            => 'NoZebra',
509             value               => '0',
510             forceupdate         => {'explanation' => 1, 'type' => 1},
511             explanation         => 'Active this if you want NOT to use zebra (large libraries should avoid this parameters)',
512             type                => 'YesNo',
513         },
514         {
515             uniquefieldrequired => 'variable',
516             variable            => 'NoZebraIndexes',
517             value               => '0',
518             forceupdate         => {'explanation' => 1, 'type' => 1},
519             explanation         => "Enter a specific hash for NoZebra indexes. Enter : 'indexname' => '100a,245a,500*','index2' => '...'",
520             type                => 'Free',
521         },
522         {
523             uniquefieldrequired => 'variable',
524             variable            => 'uppercasesurnames',
525             value               => '0',
526             forceupdate         => {'explanation' => 1, 'type' => 1},
527             explanation         => "Force Surnames to be uppercase",
528             type                => 'YesNo',
529         },
530     ],
531     userflags => [
532         {
533             uniquefieldrequired => 'bit',
534             bit                 => '14',
535             flag                => 'editauthorities',
536             flagdesc            => 'allow to edit authorities',
537             defaulton           => '0',
538         },
539         {
540             uniquefieldrequired => 'bit',
541             bit                 => '15',
542             flag                 => 'serials',
543             flagdesc            => 'allow to manage serials subscriptions',
544             defaulton           => '0',
545         },
546         {
547             uniquefieldrequired => 'bit',
548             bit                 => '16',
549             flag                 => 'reports',
550             flagdesc            => 'allow to access to the reports module',
551             defaulton           => '0',
552         },
553     ],
554     authorised_values => [
555         {
556             uniquefieldrequired => 'id',
557             category            => 'SUGGEST',
558             authorised_value    => 'Not enough budget',
559             lib                 => 'This book it too much expensive',
560         }
561     ],
562 );
563
564 my %fielddefinitions = (
565 # fieldname => [
566 #    {          field => 'fieldname',
567 #             type    => 'fieldtype',
568 #             null    => '',
569 #             key     => '',
570 #             default => ''
571 #         },
572 #     ],
573     aqbasket =>  [
574         {
575             field    => 'booksellerid',
576             type    => 'int(11)',
577             null    => 'NOT NULL',
578             key        => '',
579             default    => '1',
580             extra    => '',
581         },
582     ],
583     aqbooksellers =>  [
584         {
585             field    => 'id',
586             type    => 'int(11)',
587             null    => 'NOT NULL',
588             key        => 'PRI',
589             default    => '',
590             extra    => 'auto_increment',
591         },
592         {
593             field    => 'listprice',
594             type    => 'varchar(10)',
595             null    => 'NULL',
596             key        => '',
597             default    => '',
598             extra    => '',
599         },
600         {
601             field    => 'invoiceprice',
602             type    => 'varchar(10)',
603             null    => 'NULL',
604             key        => '',
605             default    => '',
606             extra    => '',
607         },
608     ],
609     
610     accountlines =>  [
611         {
612             field    => 'notify_id',
613             type    => 'int(11)',
614             null    => 'NOT NULL',
615             key        => '',
616             default    => '0',
617             extra    => '',
618         },
619         {
620             field    => 'notify_level',
621             type    => 'int(2)',
622             null    => 'NOT NULL',
623             key        => '',
624             default    => '0',
625             extra    => '',
626         },
627     
628     ],
629     
630     borrowers => [
631         {    field => 'firstname',
632              type => 'text',
633              null => 'NULL',
634          },
635         {    field => 'initials',
636              type => 'text',
637              null => 'NULL',
638          },
639         {    field => 'B_email',
640              type => 'text',
641              null => 'NULL',
642              after => 'B_zipcode',
643          },
644          {
645             field => 'streetnumber', # street number (hidden if streettable table is empty)
646             type => 'char(10)',
647             null => 'NULL',
648             after => 'initials',
649         },
650         {
651             field => 'streettype', # street table, list builded from a system table
652             type => 'char(50)',
653             null => 'NULL',
654             after => 'streetnumber',
655         },
656         {    field => 'phone',
657              type => 'text',
658              null => 'NULL',
659          },
660         {
661             field => 'B_streetnumber', # street number (hidden if streettable table is empty)
662             type => 'char(10)',
663             null => 'NULL',
664             after => 'fax',
665         },
666         {
667             field => 'B_streettype', # street table, list builded from a system table
668             type => 'char(50)',
669             null => 'NULL',
670             after => 'B_streetnumber',
671         },
672         {
673             field => 'phonepro',
674             type => 'text',
675             null => 'NULL',
676             after => 'fax',
677         },
678         {
679             field => 'address2', # complement address
680             type => 'text',
681             null => 'NULL',
682             after => 'address',
683         },
684         {
685             field => 'emailpro',
686             type => 'text',
687             null => 'NULL',
688             after => 'fax',
689         },
690         {
691             field => 'contactfirstname', # contact's firstname
692             type => 'text',
693             null => 'NULL',
694             after => 'contactname',
695         },
696         {
697             field => 'contacttitle', # contact's title
698             type => 'text',
699             null => 'NULL',
700             after => 'contactfirstname',
701         },
702         {
703             field => 'branchcode',
704             type  => 'varchar(10)',
705             null  => 'NOT NULL',
706             default    => '',
707             extra => '',
708         },
709         {
710             field => 'categorycode',
711             type  => 'varchar(10)',
712             null  => 'NOT NULL',
713             default    => '',
714             extra => '',
715         }
716     ],
717     
718     biblioitems =>  [
719         {
720             field    => 'lcsort',
721             type    => 'varchar(25)',
722             null    => 'NULL',
723             key        => '',
724             default    => '',
725             extra    => '',
726         },
727         {
728             field    => 'ccode',
729             type    => 'varchar(4)',
730             null    => 'NULL',
731             key        => '',
732             default    => '',
733             extra    => '',
734         },
735         {
736             field   => 'dewey',
737             type    => 'varchar(30)',
738             null    => 'null',
739             default => '',
740             extra   => '',
741         },
742         {
743             field   => 'publicationyear',
744             type    => 'text',
745             null    => 'null',
746             default => '',
747             extra   => '',
748         },
749         {
750             field   => 'collectiontitle',
751             type    => 'mediumtext',
752             null    => 'null',
753             default => '',
754             extra   => '',
755         },
756         {
757             field   => 'collectionissn',
758             type    => 'mediumtext',
759             null    => 'null',
760             default => '',
761             extra   => '',
762         },
763         {
764             field   => 'collectionvolume',
765             type    => 'mediumtext',
766             null    => 'null',
767             default => '',
768             extra   => '',
769         },
770         {
771             field   => 'editionstatement',
772             type    => 'text',
773             null    => 'null',
774             default => '',
775             extra   => '',
776         },
777         {
778             field   => 'editionresponsability',
779             type    => 'text',
780             null    => 'null',
781             default => '',
782             extra   => '',
783         },
784               
785     ],
786     deletedbiblioitems => [
787         {
788             field   => 'dewey',
789             type    => 'varchar(30)',
790             null    => 'null',
791             default => '',
792             extra   => '',
793         },
794     ],
795     branches =>  [
796         {
797             field    => 'branchip',
798             type    => 'varchar(15)',
799             null    => 'NULL',
800             key        => '',
801             default    => '',
802             extra    => '',
803         },
804         {
805             field    => 'branchprinter',
806             type    => 'varchar(100)',
807             null    => 'NULL',
808             key        => '',
809             default    => '',
810             extra    => '',
811         },
812         {
813             field   => 'branchcode',
814             type    => 'varchar(10)',
815             null    => 'NOT NULL',
816             default => '',
817             extra   => '',
818         }
819     ],
820     branchtransfers =>[
821         {
822             field   => 'frombranch',
823             type    => 'VARCHAR(10)',
824             null    => 'NOT NULL',
825             key     => '',
826             default => '',
827             extra   => '',
828         },
829         {
830             field   => 'tobranch',
831             type    => 'VARCHAR(10)',
832             null    => 'NOT NULL',
833             key     => '',
834             default => '',
835         }
836     ],
837     
838     categories =>  [
839         {
840             field    => 'category_type',
841             type    => 'char(1)',
842             null    => 'NOT NULL',
843             key        => '',
844             default    => 'A',
845             extra    => '',
846         },
847         {
848             field   => 'categorycode',
849             type    => 'varchar(10)',
850             null    => 'NOT NULL',
851             key     => 'PRI',
852             default => '',
853             extra   => '',
854         },
855     ],
856     
857     deletedborrowers => [
858         {    field => 'firstname',
859              type => 'text',
860              null => 'NULL',
861          },
862         {    field => 'initials',
863              type => 'text',
864              null => 'NULL',
865          },
866         {    field => 'B_email',
867              type => 'text',
868              null => 'NULL',
869              after => 'B_zipcode',
870          },
871          {
872             field => 'streetnumber', # street number (hidden if streettable table is empty)
873             type => 'char(10)',
874             null => 'NULL',
875             after => 'initials',
876         },
877         {
878             field => 'streettype', # street table, list builded from a system table
879             type => 'char(50)',
880             null => 'NULL',
881             after => 'streetnumber',
882         },
883         {    field => 'phone',
884              type => 'text',
885              null => 'NULL',
886          },
887          {
888             field => 'B_streetnumber', # street number (hidden if streettable table is empty)
889             type => 'char(10)',
890             null => 'NULL',
891             after => 'fax',
892         },
893         {
894             field => 'B_streettype', # street table, list builded from a system table
895             type => 'char(50)',
896             null => 'NULL',
897             after => 'B_streetnumber',
898         },
899         {
900             field => 'phonepro',
901             type => 'text',
902             null => 'NULL',
903             after => 'fax',
904         },
905         {
906             field => 'address2', # complement address
907             type => 'text',
908             null => 'NULL',
909             after => 'address',
910         },
911         {
912             field => 'emailpro',
913             type => 'text',
914             null => 'NULL',
915             after => 'fax',
916         },
917         {
918             field => 'contactfirstname', # contact's firstname
919             type => 'text',
920             null => 'NULL',
921             after => 'contactname',
922         },
923         {
924             field => 'contacttitle', # contact's title
925             type => 'text',
926             null => 'NULL',
927             after => 'contactfirstname',
928         },
929     ],
930     
931     issues =>  [
932         {
933             field    => 'borrowernumber',
934             type    => 'int(11)',
935             null    => 'NULL', # can be null when a borrower is deleted and the foreign key rule executed
936             key        => '',
937             default    => '',
938             extra    => '',
939         },
940         {
941             field    => 'itemnumber',
942             type    => 'int(11)',
943             null    => 'NULL', # can be null when a borrower is deleted and the foreign key rule executed
944             key        => '',
945             default    => '',
946             extra    => '',
947         },
948         {
949             field   => 'branchcode',
950             type    => 'varchar(10)',
951             null    => 'NULL',
952             key     => '',
953             default => '',
954             extra   => '',
955         },
956         {
957             field   => 'issuedate',
958             type    => 'date',
959             null    => '',
960             key     => '',
961             default => '0000-00-00',
962             extra   => '',
963         },
964     ],
965     
966     items => [
967         {
968             field    => 'onloan',
969             type    => 'date',
970             null    => 'NULL',
971             key        => '',
972             default    => '0000-00-00',
973             extra    => '',
974         },
975         {
976             field    => 'cutterextra',
977             type    => 'varchar(45)',
978             null    => 'NULL',
979             key        => '',
980             default    => '',
981             extra    => '',
982         },
983         {
984             field    => 'issue_date',
985             type    => 'date',
986             null    => 'NULL',
987             key        => '',
988             default    => '',
989             extra    => '',
990         },
991         {
992             field    => 'homebranch',
993             type    => 'varchar(10)',
994             null    => 'NULL',
995             key        => '',
996             default    => '',
997             extra    => '',
998         },
999         {
1000             field    => 'holdingbranch',
1001             type    => 'varchar(10)',
1002             null    => 'NULL',
1003             key        => '',
1004             default    => '',
1005             extra    => '',
1006         },
1007         {
1008             field    => 'itype',
1009             type    => 'varchar(10)',
1010             null    => 'NULL',
1011             key        => '',
1012             default    => '',
1013             extra    => '',
1014         },
1015     ],
1016     itemtypes => [
1017         {
1018             field  => 'itemtype',
1019             type   => 'varchar(10)',
1020             default    => '',
1021             null   => 'NOT NULL',
1022             key    => 'PRI',
1023             extra  => 'UNIQUE',
1024         },
1025         {
1026             field  => 'summary',
1027             type   => 'TEXT',
1028             null   => 'NULL',
1029             key    => '',
1030             extra  => '',
1031         },
1032     ],
1033     marc_breeding => [
1034         {
1035             field => 'marc',
1036             type  => 'LONGBLOB',
1037             null  => 'NULL',
1038             key    => '',
1039             extra  => '',
1040         }
1041     ],
1042     marc_subfield_structure => [
1043         {
1044             field => 'defaultvalue',
1045             type  => 'TEXT',
1046             null  => 'NULL',
1047             key    => '',
1048             extra  => '',
1049         }
1050     ],
1051     opac_news => [
1052         {
1053             field  => 'expirationdate',
1054             type   => 'date',
1055             null   => 'null',
1056             key    => '',
1057             extra  => '',
1058         },
1059         {
1060             field   => 'number',
1061             type    => 'int(11)',
1062             null    => 'NULL',
1063             key     => '',
1064             default => '0',
1065             extra   => '',
1066         },
1067     ],
1068     reserves =>  [
1069         {
1070             field    => 'waitingdate',
1071             type    => 'date',
1072             null    => 'NULL',
1073             key        => '',
1074             default    => '',
1075             extra    => '',
1076         },
1077     ],
1078     serial => [
1079         {
1080             field   => 'notes',
1081             type    => 'TEXT',
1082             null    => 'NULL',
1083             key     => '',
1084             default => '',
1085             extra   => ''
1086         },
1087     ],
1088     shelfcontents => [
1089         {
1090             field => 'dateadded',
1091             type => 'timestamp',
1092             null    => 'NULL',
1093         },
1094     ],
1095     systempreferences =>  [
1096         {
1097             field    => 'value',
1098             type    => 'text',
1099             null    => 'NULL',
1100             key        => '',
1101             default    => '',
1102             extra    => '',
1103         },
1104         {
1105             field    => 'explanation',
1106             type    => 'text',
1107             null    => 'NULL',
1108             key        => '',
1109             default    => '',
1110             extra    => '',
1111         },
1112     ],
1113     suggestions => [
1114         {
1115             field   => 'reason',
1116             type    => 'text',
1117             null    => 'NULL',
1118             key     => ''  ,
1119             default => '',
1120             extra   =>    '',
1121         }
1122     ],
1123 );
1124
1125 my %indexes = (
1126 #    table => [
1127 #         {    indexname => 'index detail'
1128 #         }
1129 #    ],
1130     aqbooksellers => [
1131         {    indexname => 'PRIMARY',
1132             content => 'id',
1133             type => 'PRI',
1134         }
1135     ],
1136     aqbasket => [
1137         {    indexname => 'booksellerid',
1138             content => 'booksellerid',
1139         },
1140     ],
1141     aqorders => [
1142         {    indexname => 'basketno',
1143             content => 'basketno',
1144         },
1145     ],
1146     aqorderbreakdown => [
1147         {    indexname => 'ordernumber',
1148             content => 'ordernumber',
1149         },
1150         {    indexname => 'bookfundid',
1151             content => 'bookfundid',
1152         },
1153     ],
1154     biblioitems => [
1155         {    indexname => 'isbn',
1156             content => 'isbn',
1157         },
1158         {    indexname => 'publishercode',
1159             content => 'publishercode',
1160         },
1161     ],
1162     branches => [
1163         {
1164             indexname => 'branchcode',
1165             content   => 'branchcode',
1166             type => 'PRI',
1167         }
1168     ],
1169     branchrelations => [
1170         {
1171             indexname => 'PRIMARY',
1172             content => 'categorycode',
1173             type => 'PRI',
1174         }
1175     ],
1176     branchrelations => [
1177         {    indexname => 'PRIMARY',
1178             content => 'branchcode,categorycode',
1179             type => 'PRI',
1180         },
1181         {    indexname => 'branchcode',
1182             content => 'branchcode',
1183         },
1184         {    indexname => 'categorycode',
1185             content => 'categorycode',
1186         }
1187     ],
1188     currency => [
1189         {    indexname => 'PRIMARY',
1190             content => 'currency',
1191             type => 'PRI',
1192         }
1193     ],
1194     categories => [
1195         {
1196             indexname => 'categorycode',
1197             content   => 'categorycode',
1198         }
1199     ],
1200     items => [
1201         {    indexname => 'homebranch',
1202             content => 'homebranch',
1203         },
1204         {    indexname => 'holdingbranch',
1205             content => 'holdingbranch',
1206         }
1207     ],
1208     itemtypes => [
1209         {
1210             indexname => 'itemtype',
1211             content   => 'itemtype',
1212         }
1213     ],
1214     shelfcontents => [
1215         {    indexname => 'shelfnumber',
1216             content => 'shelfnumber',
1217         },
1218         {    indexname => 'itemnumber',
1219             content => 'itemnumber',
1220         }
1221     ],
1222         userflags => [
1223                 {       indexname => 'PRIMARY',
1224                         content => 'bit',
1225                         type => 'PRI',
1226                 }
1227         ]
1228 );
1229
1230 my %foreign_keys = (
1231 #    table => [
1232 #         {    key => 'the key in table' (must be indexed)
1233 #            foreigntable => 'the foreigntable name', # (the parent)
1234 #            foreignkey => 'the foreign key column(s)' # (in the parent)
1235 #            onUpdate => 'CASCADE|SET NULL|NO ACTION| RESTRICT',
1236 #            onDelete => 'CASCADE|SET NULL|NO ACTION| RESTRICT',
1237 #         }
1238 #    ],
1239     branchrelations => [
1240         {    key => 'branchcode',
1241             foreigntable => 'branches',
1242             foreignkey => 'branchcode',
1243             onUpdate => 'CASCADE',
1244             onDelete => 'CASCADE',
1245         },
1246         {    key => 'categorycode',
1247             foreigntable => 'branchcategories',
1248             foreignkey => 'categorycode',
1249             onUpdate => 'CASCADE',
1250             onDelete => 'CASCADE',
1251         },
1252     ],
1253     shelfcontents => [
1254         {    key => 'shelfnumber',
1255             foreigntable => 'bookshelf',
1256             foreignkey => 'shelfnumber',
1257             onUpdate => 'CASCADE',
1258             onDelete => 'CASCADE',
1259         },
1260         {    key => 'itemnumber',
1261             foreigntable => 'items',
1262             foreignkey => 'itemnumber',
1263             onUpdate => 'CASCADE',
1264             onDelete => 'CASCADE',
1265         },
1266     ],
1267     # onDelete is RESTRICT on reference tables (branches, itemtype) as we don't want items to be
1268     # easily deleted, but branches/itemtype not too easy to empty...
1269     biblioitems => [
1270         {    key => 'biblionumber',
1271             foreigntable => 'biblio',
1272             foreignkey => 'biblionumber',
1273             onUpdate => 'CASCADE',
1274             onDelete => 'CASCADE',
1275         },
1276         {    key => 'itemtype',
1277             foreigntable => 'itemtypes',
1278             foreignkey => 'itemtype',
1279             onUpdate => 'CASCADE',
1280             onDelete => 'RESTRICT',
1281         },
1282     ],
1283     items => [
1284         {    key => 'biblioitemnumber',
1285             foreigntable => 'biblioitems',
1286             foreignkey => 'biblioitemnumber',
1287             onUpdate => 'CASCADE',
1288             onDelete => 'CASCADE',
1289         },
1290         {    key => 'homebranch',
1291             foreigntable => 'branches',
1292             foreignkey => 'branchcode',
1293             onUpdate => 'CASCADE',
1294             onDelete => 'RESTRICT',
1295         },
1296         {    key => 'holdingbranch',
1297             foreigntable => 'branches',
1298             foreignkey => 'branchcode',
1299             onUpdate => 'CASCADE',
1300             onDelete => 'RESTRICT',
1301         },
1302     ],
1303     aqbasket => [
1304         {    key => 'booksellerid',
1305             foreigntable => 'aqbooksellers',
1306             foreignkey => 'id',
1307             onUpdate => 'CASCADE',
1308             onDelete => 'RESTRICT',
1309         },
1310     ],
1311     aqorders => [
1312         {    key => 'basketno',
1313             foreigntable => 'aqbasket',
1314             foreignkey => 'basketno',
1315             onUpdate => 'CASCADE',
1316             onDelete => 'CASCADE',
1317         },
1318         {    key => 'biblionumber',
1319             foreigntable => 'biblio',
1320             foreignkey => 'biblionumber',
1321             onUpdate => 'SET NULL',
1322             onDelete => 'SET NULL',
1323         },
1324     ],
1325     aqbooksellers => [
1326         {    key => 'listprice',
1327             foreigntable => 'currency',
1328             foreignkey => 'currency',
1329             onUpdate => 'CASCADE',
1330             onDelete => 'CASCADE',
1331         },
1332         {    key => 'invoiceprice',
1333             foreigntable => 'currency',
1334             foreignkey => 'currency',
1335             onUpdate => 'CASCADE',
1336             onDelete => 'CASCADE',
1337         },
1338     ],
1339     aqorderbreakdown => [
1340         {    key => 'ordernumber',
1341             foreigntable => 'aqorders',
1342             foreignkey => 'ordernumber',
1343             onUpdate => 'CASCADE',
1344             onDelete => 'CASCADE',
1345         },
1346         {    key => 'bookfundid',
1347             foreigntable => 'aqbookfund',
1348             foreignkey => 'bookfundid',
1349             onUpdate => 'CASCADE',
1350             onDelete => 'CASCADE',
1351         },
1352     ],
1353     branchtransfers => [
1354         {    key => 'frombranch',
1355             foreigntable => 'branches',
1356             foreignkey => 'branchcode',
1357             onUpdate => 'CASCADE',
1358             onDelete => 'CASCADE',
1359         },
1360         {    key => 'tobranch',
1361             foreigntable => 'branches',
1362             foreignkey => 'branchcode',
1363             onUpdate => 'CASCADE',
1364             onDelete => 'CASCADE',
1365         },
1366         {    key => 'itemnumber',
1367             foreigntable => 'items',
1368             foreignkey => 'itemnumber',
1369             onUpdate => 'CASCADE',
1370             onDelete => 'CASCADE',
1371         },
1372     ],
1373     issuingrules => [
1374         {    key => 'categorycode',
1375             foreigntable => 'categories',
1376             foreignkey => 'categorycode',
1377             onUpdate => 'CASCADE',
1378             onDelete => 'CASCADE',
1379         },
1380         {    key => 'itemtype',
1381             foreigntable => 'itemtypes',
1382             foreignkey => 'itemtype',
1383             onUpdate => 'CASCADE',
1384             onDelete => 'CASCADE',
1385         },
1386     ],
1387     issues => [    # constraint is SET NULL : when a borrower or an item is deleted, we keep the issuing record
1388     # for stat purposes
1389         {    key => 'borrowernumber',
1390             foreigntable => 'borrowers',
1391             foreignkey => 'borrowernumber',
1392             onUpdate => 'SET NULL',
1393             onDelete => 'SET NULL',
1394         },
1395         {    key => 'itemnumber',
1396             foreigntable => 'items',
1397             foreignkey => 'itemnumber',
1398             onUpdate => 'SET NULL',
1399             onDelete => 'SET NULL',
1400         },
1401     ],
1402     reserves => [
1403         {    key => 'borrowernumber',
1404             foreigntable => 'borrowers',
1405             foreignkey => 'borrowernumber',
1406             onUpdate => 'CASCADE',
1407             onDelete => 'CASCADE',
1408         },
1409         {    key => 'biblionumber',
1410             foreigntable => 'biblio',
1411             foreignkey => 'biblionumber',
1412             onUpdate => 'CASCADE',
1413             onDelete => 'CASCADE',
1414         },
1415         {    key => 'itemnumber',
1416             foreigntable => 'items',
1417             foreignkey => 'itemnumber',
1418             onUpdate => 'CASCADE',
1419             onDelete => 'CASCADE',
1420         },
1421         {    key => 'branchcode',
1422             foreigntable => 'branches',
1423             foreignkey => 'branchcode',
1424             onUpdate => 'CASCADE',
1425             onDelete => 'CASCADE',
1426         },
1427     ],
1428     borrowers => [ # foreign keys are RESTRICT as we don't want to delete borrowers when a branch is deleted
1429     # but prevent deleting a branch as soon as it has 1 borrower !
1430         {    key => 'categorycode',
1431             foreigntable => 'categories',
1432             foreignkey => 'categorycode',
1433             onUpdate => 'RESTRICT',
1434             onDelete => 'RESTRICT',
1435         },
1436         {    key => 'branchcode',
1437             foreigntable => 'branches',
1438             foreignkey => 'branchcode',
1439             onUpdate => 'RESTRICT',
1440             onDelete => 'RESTRICT',
1441         },
1442     ],
1443     deletedborrowers => [ # foreign keys are RESTRICT as we don't want to delete borrowers when a branch is deleted
1444     # but prevent deleting a branch as soon as it has 1 borrower !
1445         {    key => 'categorycode',
1446             foreigntable => 'categories',
1447             foreignkey => 'categorycode',
1448             onUpdate => 'RESTRICT',
1449             onDelete => 'RESTRICT',
1450         },
1451         {    key => 'branchcode',
1452             foreigntable => 'branches',
1453             foreignkey => 'branchcode',
1454             onUpdate => 'RESTRICT',
1455             onDelete => 'RESTRICT',
1456         },
1457     ],
1458     accountlines => [
1459         {    key => 'borrowernumber',
1460             foreigntable => 'borrowers',
1461             foreignkey => 'borrowernumber',
1462             onUpdate => 'CASCADE',
1463             onDelete => 'CASCADE',
1464         },
1465         {    key => 'itemnumber',
1466             foreigntable => 'items',
1467             foreignkey => 'itemnumber',
1468             onUpdate => 'SET NULL',
1469             onDelete => 'SET NULL',
1470         },
1471     ],
1472     auth_tag_structure => [
1473         {    key => 'authtypecode',
1474             foreigntable => 'auth_types',
1475             foreignkey => 'authtypecode',
1476             onUpdate => 'CASCADE',
1477             onDelete => 'CASCADE',
1478         },
1479     ],
1480     # FIXME : don't constraint auth_*_table and auth_word, as they may be replaced by zebra
1481 );
1482
1483
1484 # column changes
1485 my %column_change = (
1486     # table
1487     borrowers => [
1488                 {
1489                     from => 'emailaddress',
1490                     to => 'email',
1491                     after => 'city',
1492                 },
1493                 {
1494                     from => 'streetaddress',
1495                     to => 'address',
1496                     after => 'initials',
1497                 },
1498                 {
1499                     from => 'faxnumber',
1500                     to => 'fax',
1501                     after => 'phone',
1502                 },
1503                 {
1504                     from => 'textmessaging',
1505                     to => 'opacnote',
1506                     after => 'userid',
1507                 },
1508                 {
1509                     from => 'altnotes',
1510                     to => 'contactnote',
1511                     after => 'opacnote',
1512                 },
1513                 {
1514                     from => 'physstreet',
1515                     to => 'B_address',
1516                     after => 'fax',
1517                 },
1518                 {
1519                     from => 'streetcity',
1520                     to => 'B_city',
1521                     after => 'B_address',
1522                 },
1523                 {
1524                     from => 'phoneday',
1525                     to => 'mobile',
1526                     after => 'phone',
1527                 },
1528                 {
1529                     from => 'zipcode',
1530                     to => 'zipcode',
1531                     after => 'city',
1532                 },
1533                 {
1534                     from => 'homezipcode',
1535                     to => 'B_zipcode',
1536                     after => 'B_city',
1537                 },
1538                 {
1539                     from => 'altphone',
1540                     to => 'B_phone',
1541                     after => 'B_zipcode',
1542                 },
1543                 {
1544                     from => 'expiry',
1545                     to => 'dateexpiry',
1546                     after => 'dateenrolled',
1547                 },
1548                 {
1549                     from => 'guarantor',
1550                     to => 'guarantorid',
1551                     after => 'contactname',
1552                 },
1553                 {
1554                     from => 'altrelationship',
1555                     to => 'relationship',
1556                     after => 'borrowernotes',
1557                 },
1558             ],
1559
1560     deletedborrowers => [
1561                 {
1562                     from => 'emailaddress',
1563                     to => 'email',
1564                     after => 'city',
1565                 },
1566                 {
1567                     from => 'streetaddress',
1568                     to => 'address',
1569                     after => 'initials',
1570                 },
1571                 {
1572                     from => 'faxnumber',
1573                     to => 'fax',
1574                     after => 'phone',
1575                 },
1576                 {
1577                     from => 'textmessaging',
1578                     to => 'opacnote',
1579                     after => 'userid',
1580                 },
1581                 {
1582                     from => 'altnotes',
1583                     to => 'contactnote',
1584                     after => 'opacnote',
1585                 },
1586                 {
1587                     from => 'physstreet',
1588                     to => 'B_address',
1589                     after => 'fax',
1590                 },
1591                 {
1592                     from => 'streetcity',
1593                     to => 'B_city',
1594                     after => 'B_address',
1595                 },
1596                 {
1597                     from => 'phoneday',
1598                     to => 'mobile',
1599                     after => 'phone',
1600                 },
1601                 {
1602                     from => 'zipcode',
1603                     to => 'zipcode',
1604                     after => 'city',
1605                 },
1606                 {
1607                     from => 'homezipcode',
1608                     to => 'B_zipcode',
1609                     after => 'B_city',
1610                 },
1611                 {
1612                     from => 'altphone',
1613                     to => 'B_phone',
1614                     after => 'B_zipcode',
1615                 },
1616                 {
1617                     from => 'expiry',
1618                     to => 'dateexpiry',
1619                     after => 'dateenrolled',
1620                 },
1621                 {
1622                     from => 'guarantor',
1623                     to => 'guarantorid',
1624                     after => 'contactname',
1625                 },
1626                 {
1627                     from => 'altrelationship',
1628                     to => 'relationship',
1629                     after => 'borrowernotes',
1630                 },
1631             ],
1632         );
1633     
1634
1635 # MOVE all tables TO UTF-8 and innoDB
1636 $sth = $dbh->prepare("show table status");
1637 $sth->execute;
1638 while ( my $table = $sth->fetchrow_hashref ) {
1639     next if $table->{Name} eq 'marc_word';
1640     next if $table->{Name} eq 'marc_subfield_table';
1641     next if $table->{Name} eq 'auth_word';
1642     next if $table->{Name} eq 'auth_subfield_table';
1643      if ($table->{Engine} ne 'InnoDB') {
1644          $dbh->do("ALTER TABLE $table->{Name} TYPE = innodb");
1645          print "moving $table->{Name} to InnoDB\n";
1646      }
1647     unless ($table->{Collation} =~ /^utf8/) {
1648          print "moving $table->{Name} to utf8\n";
1649         $dbh->do("ALTER TABLE $table->{Name} CONVERT TO CHARACTER SET utf8");
1650         $dbh->do("ALTER TABLE $table->{Name} DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci");
1651         # FIXME : maybe a ALTER TABLE tbl_name CONVERT TO CHARACTER SET utf8 would be better, def char set seems to work fine. If any problem encountered, let's try with convert !
1652     } else {
1653     }
1654 }
1655
1656
1657 foreach my $table (keys %column_change) {
1658     $sth = $dbh->prepare("show columns from $table");
1659     $sth->execute();
1660     undef %types;
1661     while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
1662     {
1663         $types{$column}->{type} ="$type";
1664         $types{$column}->{null} = "$null";
1665         $types{$column}->{key} = "$key";
1666         $types{$column}->{default} = "$default";
1667         $types{$column}->{extra} = "$extra";
1668     }    # while
1669     my $tablerows = $column_change{$table};
1670     foreach my $row ( @$tablerows ) {
1671         if ($types{$row->{from}}->{type}) {
1672             print "altering $table $row->{from} to $row->{to}\n";
1673             # ALTER TABLE `borrowers` CHANGE `faxnumber` `fax` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL
1674 #             alter table `borrowers` change `faxnumber` `fax` type text  null after phone
1675             my $sql =
1676                 "alter table `$table` change `$row->{from}` `$row->{to}` $types{$row->{from}}->{type} ".
1677                 ($types{$row->{from}}->{null} eq 'YES'?" NULL":" NOT NULL").
1678                 ($types{$row->{from}}->{default}?" default ".$types{$row->{from}}->{default}:"").
1679                 "$types{$row->{from}}->{extra} after $row->{after} ";
1680 #             print "$sql";
1681             $dbh->do($sql);
1682         }
1683     }
1684 }
1685
1686 # Enter here the field you want to delete from DB.
1687 # FIXME :: there is a %uselessfield before which seems doing the same things.
1688 my %fieldtodelete = (
1689     # tablename => [fieldname1,fieldname2,...]
1690
1691 ); # %fielddelete
1692
1693 print "removing some unused fields...\n";
1694 foreach my $table ( keys %fieldtodelete ) {
1695     foreach my $field ( @{$fieldtodelete{$table}} ){
1696         print "removing ".$field." from ".$table;
1697         my $sth = $dbh->prepare("ALTER TABLE $table DROP $field");
1698         $sth->execute;
1699         if ( $sth->err ) {
1700             print "Error : $sth->errstr \n";
1701         }
1702     }
1703 }
1704
1705 # Enter here the line you want to remove from DB.
1706 my %linetodelete = (
1707     # table name => where clause.
1708     userflags => "bit = 8", # delete the 'reserveforself' flags
1709     
1710 ); # %linetodelete
1711
1712 #-------------------
1713 # Initialize
1714
1715 # Start checking
1716
1717 # Get version of MySQL database engine.
1718 my $mysqlversion = `mysqld --version`;
1719 $mysqlversion =~ /Ver (\S*) /;
1720 $mysqlversion = $1;
1721 if ( $mysqlversion ge '3.23' ) {
1722     print "Could convert to MyISAM database tables...\n" unless $silent;
1723 }
1724
1725 #---------------------------------
1726 # Tables
1727
1728 # Collect all tables into a list
1729 $sth = $dbh->prepare("show tables");
1730 $sth->execute;
1731 while ( my ($table) = $sth->fetchrow ) {
1732     $existingtables{$table} = 1;
1733 }
1734
1735
1736 # Now add any missing tables
1737 foreach $table ( keys %requiretables ) {
1738     unless ( $existingtables{$table} ) {
1739     print "Adding $table table...\n" unless $silent;
1740         my $sth = $dbh->prepare("create table $table $requiretables{$table}");
1741         $sth->execute;
1742         if ( $sth->err ) {
1743             print "Error : $sth->errstr \n";
1744             $sth->finish;
1745         }    # if error
1746     }    # unless exists
1747 }    # foreach
1748
1749 #---------------------------------
1750 # Columns
1751
1752 foreach $table ( keys %requirefields ) {
1753     print "Check table $table\n" if $debug and not $silent;
1754     $sth = $dbh->prepare("show columns from $table");
1755     $sth->execute();
1756     undef %types;
1757     while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
1758     {
1759         $types{$column} = $type;
1760     }    # while
1761     foreach $column ( keys %{ $requirefields{$table} } ) {
1762         print "  Check column $column  [$types{$column}]\n" if $debug and not $silent;
1763         if ( !$types{$column} ) {
1764
1765             # column doesn't exist
1766             print "Adding $column field to $table table...\n" unless $silent;
1767             $query = "alter table $table
1768             add column $column " . $requirefields{$table}->{$column};
1769             print "Execute: $query\n" if $debug;
1770             my $sti = $dbh->prepare($query);
1771             $sti->execute;
1772             if ( $sti->err ) {
1773                 print "**Error : $sti->errstr \n";
1774                 $sti->finish;
1775             }    # if error
1776         }    # if column
1777     }    # foreach column
1778 }    # foreach table
1779
1780 foreach $table ( keys %fielddefinitions ) {
1781     print "Check table $table\n" if $debug;
1782     $sth = $dbh->prepare("show columns from $table");
1783     $sth->execute();
1784     my $definitions;
1785     while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
1786     {
1787         $definitions->{$column}->{type}    = $type;
1788         $definitions->{$column}->{null}    = $null;
1789         $definitions->{$column}->{null}    = 'NULL' if $null eq 'YES';
1790         $definitions->{$column}->{key}     = $key;
1791         $definitions->{$column}->{default} = $default;
1792         $definitions->{$column}->{extra}   = $extra;
1793     }    # while
1794     my $fieldrow = $fielddefinitions{$table};
1795     foreach my $row (@$fieldrow) {
1796         my $field   = $row->{field};
1797         my $type    = $row->{type};
1798         my $null    = $row->{null};
1799 #         $null    = 'YES' if $row->{null} eq 'NULL';
1800         my $key     = $row->{key};
1801         my $default = $row->{default};
1802 #         $default="''" unless $default;
1803         my $extra   = $row->{extra};
1804         my $def     = $definitions->{$field};
1805         my $after    = ($row->{after}?" after ".$row->{after}:"");
1806
1807         unless ( $type eq $def->{type}
1808             && $null eq $def->{null}
1809             && $key eq $def->{key}
1810             && $extra eq $def->{extra} )
1811         {
1812             if ( $null eq '' ) {
1813                 $null = 'NOT NULL';
1814             }
1815             if ( $key eq 'PRI' ) {
1816                 $key = 'PRIMARY KEY';
1817             }
1818             unless ( $extra eq 'auto_increment' ) {
1819                 $extra = '';
1820             }
1821     
1822             # if it's a new column use "add", if it's an old one, use "change".
1823             my $action;
1824             if ($definitions->{$field}->{type}) {
1825                 $action="change $field"
1826             } else {
1827                 $action="add";
1828             }
1829 # if it's a primary key, drop the previous pk, before altering the table
1830             print "  alter or create $field in $table\n" unless $silent;
1831             my $query;
1832             if ($key ne 'PRIMARY KEY') {
1833 #                 warn "alter table $table $action $field $type $null $key $extra default $default $after";
1834                 $query = "alter table $table $action $field $type $null $key $extra ".($default?"default ".$dbh->quote($default):"")." $after";
1835             } else {
1836 #             warn "alter table $table drop primary key, $action $field $type $null $key $extra default $default $after";
1837                  # something strange : for indexes UNIQUE, they are reported as primary key here.
1838                  # but if you try to run with drop primary key, it fails.
1839                  # thus, we run the query twice, one will fail, one will succeed.
1840                  # strange...
1841                 $query="alter table $table drop primary key, $action $field $type $null $key $extra ".($default?"default ".$dbh->quote($default):"")." $after";
1842                 $query="alter table $table $action $field $type $null $key $extra ".($default?"default ".$dbh->quote($default):"")." $after";
1843             }
1844             $dbh->do($query);
1845         }
1846     }
1847 }
1848
1849 print "removing some unused data...\n";
1850 foreach my $table ( keys %linetodelete ) {
1851     foreach my $where ( @{linetodelete{$table}} ){
1852         print "DELETE FROM ".$table." where ".$where;
1853         print "\n";
1854         my $sth = $dbh->prepare("DELETE FROM $table where $where");
1855         $sth->execute;
1856         if ( $sth->err ) {
1857             print "Error : $sth->errstr \n";
1858         }
1859     }
1860 }
1861
1862 # Populate tables with required data
1863
1864 # synch table and deletedtable.
1865 foreach my $table (('borrowers','items','biblio','biblioitems')) {
1866     my %deletedborrowers;
1867     print "synch'ing $table and deleted$table\n";
1868     $sth = $dbh->prepare("show columns from deleted$table");
1869     $sth->execute;
1870     while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow ) {
1871         $deletedborrowers{$column}=1;
1872     }
1873     $sth = $dbh->prepare("show columns from $table");
1874     $sth->execute;
1875     my $previous;
1876     while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow ) {
1877         unless ($deletedborrowers{$column}) {
1878             my $newcol="alter table deleted$table add $column $type";
1879             if ($null eq 'YES') {
1880                 $newcol .= " NULL ";
1881             } else {
1882                 $newcol .= " NOT NULL ";
1883             }
1884             $newcol .= "default ".$dbh->quote($default) if $default;
1885             $newcol .= " after $previous" if $previous;
1886             $previous=$column;
1887             print "creating column $column\n";
1888             $dbh->do($newcol);
1889         }
1890     }
1891 }
1892 #
1893 # update publisheddate 
1894 #
1895 $sth = $dbh->prepare("select count(*) from serial where publisheddate is NULL");
1896 $sth->execute;
1897 my ($emptypublished) = $sth->fetchrow;
1898 if ($emptypublished) {
1899     print "Updating publisheddate\n";
1900     $dbh->do("update serial set publisheddate=planneddate where publisheddate is NULL");
1901 }
1902 foreach my $table ( keys %tabledata ) {
1903     print "Checking for data required in table $table...\n" unless $silent;
1904     my $tablerows = $tabledata{$table};
1905     foreach my $row (@$tablerows) {
1906         my $uniquefieldrequired = $row->{uniquefieldrequired};
1907         my $uniquevalue         = $row->{$uniquefieldrequired};
1908         my $forceupdate         = $row->{forceupdate};
1909         my $sth                 =
1910           $dbh->prepare(
1911 "select $uniquefieldrequired from $table where $uniquefieldrequired=?"
1912         );
1913         $sth->execute($uniquevalue);
1914         if ($sth->rows) {
1915             foreach my $field (keys %$forceupdate) {
1916                 if ($forceupdate->{$field}) {
1917                     my $sth=$dbh->prepare("update systempreferences set $field=? where $uniquefieldrequired=?");
1918                     $sth->execute($row->{$field}, $uniquevalue);
1919                 }
1920             }
1921         } else {
1922             print "Adding row to $table: " unless $silent;
1923             my @values;
1924             my $fieldlist;
1925             my $placeholders;
1926             foreach my $field ( keys %$row ) {
1927                 next if $field eq 'uniquefieldrequired';
1928                 next if $field eq 'forceupdate';
1929                 my $value = $row->{$field};
1930                 push @values, $value;
1931                 print "  $field => $value" unless $silent;
1932                 $fieldlist .= "$field,";
1933                 $placeholders .= "?,";
1934             }
1935             print "\n" unless $silent;
1936             $fieldlist    =~ s/,$//;
1937             $placeholders =~ s/,$//;
1938             print "insert into $table ($fieldlist) values ($placeholders)";
1939             my $sth =
1940             $dbh->prepare(
1941                 "insert into $table ($fieldlist) values ($placeholders)");
1942             $sth->execute(@values);
1943         }
1944     }
1945 }
1946
1947 #
1948 # check indexes and create them when needed
1949 #
1950 print "Checking for index required...\n" unless $silent;
1951 foreach my $table ( keys %indexes ) {
1952     #
1953     # read all indexes from $table
1954     #
1955     $sth = $dbh->prepare("show index from $table");
1956     $sth->execute;
1957     my %existingindexes;
1958     while ( my ( $table, $non_unique, $key_name, $Seq_in_index, $Column_name, $Collation, $cardinality, $sub_part, $Packed, $comment ) = $sth->fetchrow ) {
1959         $existingindexes{$key_name} = 1;
1960     }
1961     # read indexes to check
1962     my $tablerows = $indexes{$table};
1963     foreach my $row (@$tablerows) {
1964         my $key_name=$row->{indexname};
1965         if ($existingindexes{$key_name} eq 1) {
1966 #             print "$key_name existing";
1967         } else {
1968             print "\tCreating index $key_name in $table\n";
1969             my $sql;
1970             if ($row->{indexname} eq 'PRIMARY') {
1971                 $sql = "alter table $table ADD PRIMARY KEY ($row->{content})";
1972             } else {
1973                 $sql = "alter table $table ADD INDEX $key_name ($row->{content}) $row->{type}";
1974             }
1975              $dbh->do($sql);
1976             print "Error $sql : $dbh->err \n" if $dbh->err;
1977         }
1978     }
1979 }
1980
1981 #
1982 # check foreign keys and create them when needed
1983 #
1984 print "Checking for foreign keys required...\n" unless $silent;
1985 foreach my $table ( keys %foreign_keys ) {
1986     #
1987     # read all indexes from $table
1988     #
1989     $sth = $dbh->prepare("show table status like '$table'");
1990     $sth->execute;
1991     my $stat = $sth->fetchrow_hashref;
1992     # read indexes to check
1993     my $tablerows = $foreign_keys{$table};
1994     foreach my $row (@$tablerows) {
1995         my $foreign_table=$row->{foreigntable};
1996         if ($stat->{'Comment'} =~/$foreign_table/) {
1997 #             print "$foreign_table existing\n";
1998         } else {
1999             print "\tCreating foreign key $foreign_table in $table\n";
2000             # first, drop any orphan value in child table
2001             if ($row->{onDelete} ne "RESTRICT") {
2002                 my $sql = "delete from $table where $row->{key} not in (select $row->{foreignkey} from $row->{foreigntable})";
2003                 $dbh->do($sql);
2004                 print "SQL ERROR: $sql : $dbh->err \n" if $dbh->err;
2005             }
2006             my $sql="alter table $table ADD FOREIGN KEY $row->{key} ($row->{key}) REFERENCES $row->{foreigntable} ($row->{foreignkey})";
2007             $sql .= " on update ".$row->{onUpdate} if $row->{onUpdate};
2008             $sql .= " on delete ".$row->{onDelete} if $row->{onDelete};
2009             $dbh->do($sql);
2010             if ($dbh->err) {
2011                 print "====================
2012 An error occured during :
2013 \t$sql
2014 It probably means there is something wrong in your DB : a row ($table.$row->{key}) refers to a value in $row->{foreigntable}.$row->{foreignkey} that does not exist. solve the problem and run updater again (or just the previous SQL statement).
2015 You can find those values with select
2016 \t$table.* from $table where $row->{key} not in (select $row->{foreignkey} from $row->{foreigntable})
2017 ====================\n
2018 ";
2019             }
2020         }
2021     }
2022 }
2023 # now drop useless tables
2024 foreach $table ( @TableToDelete ) {
2025     if ( $existingtables{$table} ) {
2026         print "Dropping unused table $table\n" if $debug and not $silent;
2027         $dbh->do("drop table $table");
2028         if ( $dbh->err ) {
2029             print "Error : $dbh->errstr \n";
2030         }
2031     }
2032 }
2033
2034 #
2035 # SPECIFIC STUFF
2036 #
2037 #
2038 # create frameworkcode row in biblio table & fill it with marc_biblio.frameworkcode.
2039 #
2040
2041 # 1st, get how many biblio we will have to do...
2042 $sth = $dbh->prepare('select count(*) from marc_biblio');
2043 $sth->execute;
2044 my ($totaltodo) = $sth->fetchrow;
2045
2046 $sth = $dbh->prepare("show columns from biblio");
2047 $sth->execute();
2048 my $definitions;
2049 my $bibliofwexist=0;
2050 while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow ){
2051     $bibliofwexist=1 if $column eq 'frameworkcode';
2052 }
2053 unless ($bibliofwexist) {
2054     print "moving biblioframework to biblio table\n";
2055     $dbh->do('ALTER TABLE `biblio` ADD `frameworkcode` VARCHAR( 4 ) NOT NULL AFTER `biblionumber`');
2056     $sth = $dbh->prepare('select biblionumber,frameworkcode from marc_biblio');
2057     $sth->execute;
2058     my $sth_update = $dbh->prepare('update biblio set frameworkcode=? where biblionumber=?');
2059     my $totaldone=0;
2060     while (my ($biblionumber,$frameworkcode) = $sth->fetchrow) {
2061         $sth_update->execute($frameworkcode,$biblionumber);
2062         $totaldone++;
2063         print "\r$totaldone / $totaltodo" unless ($totaldone % 100);
2064     }
2065     print "\rdone\n";
2066 }
2067
2068 # at last, remove useless fields
2069 foreach $table ( keys %uselessfields ) {
2070     my @fields = split /,/,$uselessfields{$table};
2071     my $fields;
2072     my $exists;
2073     foreach my $fieldtodrop (@fields) {
2074         $fieldtodrop =~ s/\t//g;
2075         $fieldtodrop =~ s/\n//g;
2076         $exists =0;
2077         $sth = $dbh->prepare("show columns from $table");
2078         $sth->execute;
2079         while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
2080         {
2081             $exists =1 if ($column eq $fieldtodrop);
2082         }
2083         if ($exists) {
2084             print "deleting $fieldtodrop field in $table...\n" unless $silent;
2085             my $sth = $dbh->prepare("alter table $table drop $fieldtodrop");
2086             $sth->execute;
2087         }
2088     }
2089 }    # foreach
2090
2091 #
2092 # Changing aqbookfund's primary key 
2093 #
2094 $sth=$dbh->prepare("ALTER TABLE `aqbookfund` DROP PRIMARY KEY , ADD PRIMARY KEY ( `bookfundid` , `branchcode` ) ;");
2095 $sth->execute;
2096
2097 $sth->finish;
2098
2099 exit;
2100
2101 # $Log$
2102 # Revision 1.172  2007/07/19 10:21:22  hdl
2103 # Adding some new fields to biblioitems:
2104 # collectiontitle
2105 # collectionissn
2106 # collectionvolume
2107 #
2108 #
2109 # Changing publicationyear to text.
2110 #
2111 # Revision 1.171  2007/07/02 02:30:16  rangi
2112 # Fix for bug 1296, making surnames uppercase a systems preference
2113 #
2114 # Revision 1.170  2007/06/26 13:25:37  tipaul
2115 # removing some useless tables from updatedatabase
2116 #
2117 # Revision 1.169  2007/06/26 09:23:26  tipaul
2118 # improving OpacBrowser systempref explanation
2119 #
2120 # Revision 1.168  2007/06/25 15:02:31  tipaul
2121 # missing field declaration
2122 #
2123 # Revision 1.167  2007/06/15 13:09:08  toins
2124 # bugfix : bibliotitems.dewey & deletedbiblioitems.dewey mustn't be double(8,6).
2125 #
2126 # Revision 1.166  2007/06/08 09:40:12  toins
2127 # bug fix : items.homebranch must be VARCHAR(10)
2128 #
2129 # Revision 1.165  2007/05/23 16:33:10  tipaul
2130 # skip move to innoDB for the 4 22 tables, that are used to store MARC records, are useless in Koha 3.0 The process is very very long, so the updatedatabase should speed up a lot (by long I mean 1 hour on my Dual core with SCSI disk, for a 50 000 biblios long table
2131 #
2132 # Revision 1.164  2007/05/04 16:24:09  tipaul
2133 # various bugfixes on parameters modules + adding default NoZebraIndexes systempreference if it's empty
2134 #
2135 # Revision 1.163  2007/05/02 16:44:31  tipaul
2136 # NoZebra SQL index management :
2137 # * adding 3 subs in Biblio.pm
2138 # - GetNoZebraIndexes, that get the index structure in a new systempreference (added with this commit)
2139 # - _DelBiblioNoZebra, that retrieve all index entries for a biblio and remove in a variable the biblio reference
2140 # - _AddBiblioNoZebra, that add index entries for a biblio.
2141 # Note that the 2 _Add and _Del subs work only in a hash variable, to speed up things in case of a modif (ie : delete+add). The effective SQL update is done in the ModZebra sub (that existed before, and dealed with zebra index).
2142 # I think the code has to be more deeply tested, but it works at least partially.
2143 #
2144 # Revision 1.162  2007/04/30 16:16:50  tipaul
2145 # bugfix for updatedatabase : when there is no default value (NULL fields) + removing bibliothesaurus table+adding NoZebra systempref (False by default)
2146 #
2147 # Revision 1.161  2007/04/13 16:27:55  hdl
2148 # Adding Version variable to systempreferences.
2149 #
2150 # Revision 1.160  2007/03/19 18:35:13  toins
2151 #  - adding default value in marc_subfield_structure.
2152 #  - now marc_subfields_structure displays subfields in tab view.
2153 #
2154 # Revision 1.159  2007/03/16 01:25:09  kados
2155 # Using my precrash CVS copy I did the following:
2156 #
2157 # cvs -z3 -d:ext:kados@cvs.savannah.nongnu.org:/sources/koha co -P koha
2158 # find koha.precrash -type d -name "CVS" -exec rm -v {} \;
2159 # cp -r koha.precrash/* koha/
2160 # cd koha/
2161 # cvs commit
2162 #
2163 # This should in theory put us right back where we were before the crash
2164 #
2165 # Revision 1.159  2007/03/12 17:52:30  rych
2166 # add pri key to userflags
2167 #
2168 # Revision 1.158  2007/03/09 15:14:57  tipaul
2169 # rel_3_0 moved to HEAD
2170 #
2171 # Revision 1.157.2.56  2007/01/31 16:22:54  btoumi
2172 # -add possibility to use isbn with length of 13 characters
2173 # for  Import datas in the reservoir.
2174 # -modify isbn field in marc_breeding table (varchar 13)
2175 # -add isbn filter (no - )when u read a notice from reservoir
2176 # -add filter to have right field 100
2177 #
2178 # Revision 1.157.2.55  2007/01/30 10:50:19  tipaul
2179 # adding 2 usefull indexes to biblioitems table
2180 #
2181 # Revision 1.157.2.54  2007/01/29 16:45:52  toins
2182 # * adding a new default authorised value : SUGGEST.
2183 # SUGGEST give some reasons to accept or reject a suggestion.
2184 #
2185 # * default value for borrowersMandatoryfield syspref is now "cardnumber|surname|adress"
2186 #
2187 # Revision 1.157.2.53  2007/01/26 20:48:37  hdl
2188 # Serials management : Bugfixes + improvements.
2189 # - Partial dates are now managed
2190 # - next Date Calculation with irregularity tested for 1 week and 1 month.
2191 # - manage if subscription is abouttoexpire or expired.
2192 # - Adding some information on serials pages about subscription.
2193 # - Managing irregularity with numbers.
2194 # - Adding Internal Notes in subscription management.
2195 # - Repeating Button above pages.
2196 #
2197 # Please run Updatedatabase to change irregularity and add internalnotes field  to subscription
2198 #
2199 # Revision 1.157.2.52  2007/01/24 13:57:26  tipaul
2200 # - setting supplierid to auto_increment (HDL : could you check that is works, i'm not 100% sure)
2201 # - removing 22 -> 30 marc_subfield_table -> marcxml stuff, it's now in misc/migration_tools/22_to_30/
2202 #
2203 # Revision 1.157.2.51  2007/01/18 09:58:45  tipaul
2204 # defaulting NOT NULL fields (to '')
2205 #
2206 # Revision 1.157.2.50  2007/01/18 09:39:21  tipaul
2207 # issuedate must be defaulted with ' '
2208 #
2209 # Revision 1.157.2.49  2007/01/18 09:37:30  tipaul
2210 # removing 2 field definitions that were here twice
2211 #
2212 # Revision 1.157.2.48  2007/01/15 09:55:40  toins
2213 # adding a new logging systempref : FinesLog.
2214 #
2215 # Revision 1.157.2.47  2007/01/12 18:09:49  toins
2216 # LetterLog added
2217 #
2218 # Revision 1.157.2.46  2007/01/11 14:35:39  tipaul
2219 # adding Opac Browser feature : the build_browser_and_cloud.pl script will :
2220 # - fill the browser table, that enable browsing, digit by digit of a given category, the catalogue. A complete dewey classification is provided in the script, active only for french libraries, of course (although, for instance, the script check that the catalogue is in english for developping convenience)
2221 # - fill the tags table, that contains the subject cloud.
2222 #
2223 # The cloud part is a copy of the previous build_tags.pl script that can be deleted : those 2 scripts require to parse all the catalogue to extract interesting data, so they are long. It's useless to parse the catalogue twice !
2224 #
2225 # The commit also add the systempreference to hide/show the OpacBrowse in database & in systempref management script.
2226 #
2227 # IMPROVEMENTS to do :
2228 # - the script that builds the tables can be improved to update only last week biblios (at the price of a small error in value links, but it's not a problem).
2229 # - add, in parameters section, a place to edit browser descriptions. The build script has to be updated to to avoid deleting existing browser descriptions.
2230 #
2231 # Revision 1.157.2.45  2007/01/10 16:52:52  toins
2232 # Value for Log Features syspref are set to 0 by default.
2233 #
2234 # Revision 1.157.2.44  2007/01/10 16:31:15  toins
2235 # new systems preferences :
2236 #  - CataloguingLog (log the update/creation/deletion of a notice if set to 1)
2237 #  - BorrowersLog ( idem for borrowers )
2238 #  - IssueLog (log all issue if set to 1)
2239 #  - ReturnLog (log all return if set to 1)
2240 #  - SusbcriptionLog (log all creation/deletion/update of a subcription)
2241 #
2242 # All of theses are in a new tab called 'LOGFeatures' in systempreferences.pl
2243 #
2244 # Revision 1.157.2.43  2007/01/10 14:13:17  toins
2245 # opac_news.displayed is replaced by opac_news.number.
2246 # This field say how are ordered the news on the template.
2247 #
2248 # Revision 1.157.2.42  2007/01/09 14:09:01  toins
2249 # 2 field added to opac_news.('expirationdate' and 'displayed').