per row inserts
[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     categorytable       => "(categorycode char(5) NOT NULL default '',
56                              description text default '',
57                              itemtypecodes text default '',
58                              PRIMARY KEY (categorycode)
59                             )",
60     subcategorytable       => "(subcategorycode char(5) NOT NULL default '',
61                              description text default '',
62                              itemtypecodes text default '',
63                              PRIMARY KEY (subcategorycode)
64                             )",
65     mediatypetable       => "(mediatypecode char(5) NOT NULL default '',
66                              description text default '',
67                              itemtypecodes text default '',
68                              PRIMARY KEY (mediatypecode)
69                             )",
70     action_logs     => "(
71                     `timestamp` TIMESTAMP NOT NULL ,
72                     `user` INT( 11 ) NOT NULL ,
73                     `module` TEXT default '',
74                     `action` TEXT default '' ,
75                     `object` INT(11) default '' ,
76                     `info` TEXT default '' ,
77                     PRIMARY KEY ( `timestamp` , `user` )
78                 )",
79     letter        => "(
80                     module varchar(20) NOT NULL default '',
81                     code varchar(20) NOT NULL default '',
82                     name varchar(100) NOT NULL default '',
83                     title varchar(200) NOT NULL default '',
84                     content text,
85                     PRIMARY KEY  (module,code)
86                 )",
87     alert        =>"(
88                     alertid int(11) NOT NULL auto_increment,
89                     borrowernumber int(11) NOT NULL default '0',
90                     type varchar(10) NOT NULL default '',
91                     externalid varchar(20) NOT NULL default '',
92                     PRIMARY KEY  (alertid),
93                     KEY borrowernumber (borrowernumber),
94                     KEY type (type,externalid)
95                 )",
96     opac_news => "(
97                 `idnew` int(10) unsigned NOT NULL auto_increment,
98                 `title` varchar(250) NOT NULL default '',
99                 `new` text NOT NULL,
100                 `lang` varchar(4) NOT NULL default '',
101                 `timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP,
102                 PRIMARY KEY  (`idnew`)
103                 )",
104     repeatable_holidays => "(
105                 `id` int(11) NOT NULL auto_increment,
106                 `branchcode` varchar(4) NOT NULL default '',
107                 `weekday` smallint(6) default NULL,
108                 `day` smallint(6) default NULL,
109                 `month` smallint(6) default NULL,
110                 `title` varchar(50) NOT NULL default '',
111                 `description` text NOT NULL,
112                 PRIMARY KEY  (`id`)
113                 )",
114     special_holidays => "(
115                 `id` int(11) NOT NULL auto_increment,
116                 `branchcode` varchar(4) NOT NULL default '',
117                 `day` smallint(6) NOT NULL default '0',
118                 `month` smallint(6) NOT NULL default '0',
119                 `year` smallint(6) NOT NULL default '0',
120                 `isexception` smallint(1) NOT NULL default '1',
121                 `title` varchar(50) NOT NULL default '',
122                 `description` text NOT NULL,
123                 PRIMARY KEY  (`id`)
124                 )",
125     overduerules    =>"(`branchcode` varchar(255) NOT NULL default '',
126                     `categorycode` char(2) NOT NULL default '',
127                     `delay1` int(4) default '0',
128                     `letter1` varchar(20) default NULL,
129                     `debarred1` char(1) default '0',
130                     `delay2` int(4) default '0',
131                     `debarred2` char(1) default '0',
132                     `letter2` varchar(20) default NULL,
133                     `delay3` int(4) default '0',
134                     `letter3` varchar(20) default NULL,
135                     `debarred3` int(1) default '0',
136                     PRIMARY KEY  (`branchcode`,`categorycode`)
137                     )",
138     cities            => "(`cityid` int auto_increment,
139                         `city_name` char(100) NOT NULL,
140                         `city_zipcode` char(20),
141                         PRIMARY KEY (`cityid`)
142                     )",
143     roadtype            => "(`roadtypeid` int auto_increment,
144                         `road_type` char(100) NOT NULL,
145                         PRIMARY KEY (`roadtypeid`)
146                     )",
147
148     labels                     => "(
149                 labelid int(11) NOT NULL auto_increment,
150                                itemnumber varchar(100) NOT NULL default '',
151                                timestamp timestamp(14) NOT NULL,
152                                PRIMARY KEY  (labelid)
153                                )",
154
155     labels_conf                => "(
156                 id int(4) NOT NULL auto_increment,
157                                barcodetype char(100) default '',
158                                title tinyint(1) default '0',
159                                isbn tinyint(1) default '0',
160                                itemtype tinyint(1) default '0',
161                                barcode tinyint(1) default '0',
162                                dewey tinyint(1) default '0',
163                                class tinyint(1) default '0',
164                                author tinyint(1) default '0',
165                                papertype char(100) default '',
166                                startrow int(2) default NULL,
167                                PRIMARY KEY  (id)
168                                )",
169        reviews                  => "(
170                             reviewid integer NOT NULL auto_increment,
171                             borrowernumber integer,
172                             biblionumber integer,
173                             review text,
174                             approved tinyint,
175                             datereviewed datetime,
176                             PRIMARY KEY (reviewid)
177                             )",
178     borrowers_to_borrowers  => "(
179                             borrower1 integer,
180                             borrower2 integer
181                             )",
182     subscriptionroutinglist=>"(
183                              routingid integer NOT NULL auto_increment,
184                              borrowernumber integer,
185                              ranking integer,
186                              subscriptionid integer,
187                             PRIMARY KEY (routingid)
188                              )",
189
190     notifys    => "(
191               notify_id int(11) NOT NULL default '0',
192                 `borrowernumber` int(11) NOT NULL default '0',
193               `itemnumber` int(11) NOT NULL default '0',
194               `notify_date` date NOT NULL default '0000-00-00',
195                       `notify_send_date` date default NULL,
196                       `notify_level` int(1) NOT NULL default '0',
197                       `method` varchar(20) NOT NULL default ''
198               )",
199
200    charges    => "(
201               `charge_id` varchar(5) NOT NULL default '',
202                 `description` text NOT NULL,
203                 `amount` decimal(28,6) NOT NULL default '0.000000',
204                           `min` int(4) NOT NULL default '0',
205                 `max` int(4) NOT NULL default '0',
206                           `level` int(1) NOT NULL default '0',
207                           PRIMARY KEY  (`charge_id`)
208               )",
209     tags => "(
210         `entry` varchar(255) NOT NULL default '',
211         `weight` bigint(20) NOT NULL default '0',
212          PRIMARY KEY  (`entry`)
213     )
214     ",
215    zebraqueue    => "(
216                 `id` int NOT NULL auto_increment,
217                 `biblio_auth_number` int NOT NULL,
218                 `operation` char(20) NOT NULL,
219                 `server` char(20) NOT NULL ,
220                 PRIMARY KEY  (`id`)
221               ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci AUTO_INCREMENT=1",
222
223 );
224
225 my %requirefields = (
226     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 \'\''},
227     itemtypes => { 'imageurl' => 'char(200) NULL'},
228     aqbookfund => { 'branchcode' => 'varchar(4) NULL'},
229     aqbudget => { 'branchcode' => 'varchar(4) NULL'},
230     auth_header => { 'marc' => 'BLOB NOT NULL', 'linkid' => 'BIGINT(20) NULL'},
231     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'},
232     marc_breeding => { 'isbn' => 'varchar(13) NOT NULL'},
233     serial =>{ 'publisheddate' => 'date', 'claimdate' => 'date', 'itemnumber'=>'text NULL','routingnotes'=>'text NULL',},
234     statistics => { 'associatedborrower' => 'integer'},
235     z3950servers =>{  "name" =>"text",  "description" => "text NOT NULL",
236                     "position" =>"enum('primary','secondary','') NOT NULL default 'primary'",  "icon" =>"text",
237                     "type" =>"enum('zed','opensearch') NOT NULL default 'zed'",
238                     },
239     issues =>{ 'issuedate'=>"date NOT NULL default '0000-00-00'", },
240
241 #    tablename        => { 'field' => 'fieldtype' },
242 );
243
244 # Enter here the table to delete.
245 my @TableToDelete = qw(
246     additionalauthors
247     bibliosubject
248     bibliosubtitle
249 );
250
251 my %uselessfields = (
252 # tablename => "field1,field2",
253     borrowers => "suburb,altstreetaddress,altsuburb,altcity,studentnumber,school,area,preferredcont,altcp",
254     deletedborrowers=> "suburb,altstreetaddress,altsuburb,altcity,studentnumber,school,area,preferredcont,altcp",
255     );
256 # the other hash contains other actions that can't be done elsewhere. they are done
257 # either BEFORE of AFTER everything else, depending on "when" entry (default => AFTER)
258
259 # The tabledata hash contains data that should be in the tables.
260 # The uniquefieldrequired hash entry is used to determine which (if any) fields
261 # must not exist in the table for this row to be inserted.  If the
262 # uniquefieldrequired entry is already in the table, the existing data is not
263 # modified, unless the forceupdate hash entry is also set.  Fields in the
264 # anonymous "forceupdate" hash will be forced to be updated to the default
265 # values given in the %tabledata hash.
266
267 my %tabledata = (
268 # tablename => [
269 #    {    uniquefielrequired => 'fieldname', # the primary key in the table
270 #        fieldname => fieldvalue,
271 #        fieldname2 => fieldvalue2,
272 #    },
273 # ],
274     systempreferences => [
275         {
276             uniquefieldrequired => 'variable',
277             variable            => 'useDaysMode',
278             value               => 'Calendar',
279             forceupdate         => { 'explanation' => 1,
280                                      'type' => 1},
281             explanation            => 'How to calculate return dates : Calendar means holidays will be controled, Days means the return date don\'t depend on holidays',
282             type        => 'Choice',
283             options        => 'Calendar|Days'
284         },
285         {
286             uniquefieldrequired => 'variable',
287             variable            => 'BorrowersTitles',
288             value               => 'Mr|Mrs|Miss|Ms',
289             forceupdate         => { 'explanation' => 1,
290                                      'type' => 1},
291             explanation         => 'List all Titles for borrowers',
292             type                => 'free',
293         },
294         {
295             uniquefieldrequired => 'variable',
296             variable            => 'BorrowerMandatoryField',
297             value               => 'cardnumber|surname|address',
298             forceupdate         => { 'explanation' => 1,
299                                      'type' => 1},
300             explanation         => 'List all mandatory fields for borrowers',
301             type                => 'free',
302         },
303         {
304             uniquefieldrequired => 'variable',
305             variable            => 'borrowerRelationship',
306             value               => 'father|mother,grand-mother',
307             forceupdate         => { 'explanation' => 1,
308                                      'type' => 1},
309             explanation         => 'The relationships between a guarantor & a guarantee (separated by | or ,)',
310             type                => 'free',
311         },
312         {
313             uniquefieldrequired => 'variable',
314             variable            => 'ReservesMaxPickUpDelay',
315             value               => '10',
316             forceupdate         => { 'explanation' => 1,
317                                      'type' => 1},
318             explanation         => 'Maximum delay to pick up a reserved document',
319             type                => 'free',
320         },
321         {
322             uniquefieldrequired => 'variable',
323             variable            => 'TransfersMaxDaysWarning',
324             value               => '3',
325             forceupdate         => { 'explanation' => 1,
326                                      'type' => 1},
327             explanation         => 'Max delay before considering the transfer has potentialy a problem',
328             type                => 'free',
329         },
330         {
331             uniquefieldrequired => 'variable',
332             variable            => 'memberofinstitution',
333             value               => '0',
334             forceupdate         => { 'explanation' => 1,
335                                      'type' => 1},
336             explanation         => 'Are your patrons members of institutions',
337             type                => 'YesNo',
338         },
339     {
340             uniquefieldrequired => 'variable',
341             variable            => 'ReadingHistory',
342             value               => '0',
343             forceupdate         => { 'explanation' => 1,
344                                      'type' => 1},
345             explanation         => 'Allow reading record info retrievable from issues and oldissues tables',
346             type                => 'YesNo',
347         },
348     {
349             uniquefieldrequired => 'variable',
350             variable            => 'IssuingInProcess',
351             value               => '0',
352             forceupdate         => { 'explanation' => 1,
353                                      'type' => 1},
354             explanation         => 'Allow no debt alert if the patron is issuing item that accumulate debt',
355             type                => 'YesNo',
356         },
357     {
358             uniquefieldrequired => 'variable',
359             variable            => 'AutomaticItemReturn',
360             value               => '1',
361             forceupdate         => { 'explanation' => 1,
362                                      'type' => 1},
363             explanation         => 'This Variable allow or not to return automaticly to his homebranch',
364             type                => 'YesNo',
365         },
366     {
367             uniquefieldrequired => 'variable',
368             variable            => 'reviewson',
369             value               => '0',
370             forceupdate         => { 'explanation' => 1,
371                                      'type' => 1},
372             explanation         => 'Allows patrons to submit reviews from the opac',
373             type                => 'YesNo',
374         },
375     {
376             uniquefieldrequired => 'variable',
377             variable            => 'intranet_includes',
378             value               => 'includes',
379             forceupdate         => { 'explanation' => 1,
380                                      'type' => 1},
381             explanation         => 'The includes directory you want for specific look of Koha (includes or includes_npl for example)',
382             type                => 'Free',
383         },
384         {
385             uniquefieldrequired => 'variable',
386             variable            => 'AutoLocation',
387             value               => '0',
388             forceupdate         => { 'explanation' => 1,
389                                      'type' => 1},
390             explanation         => 'switch to activate or not Autolocation, if Yes, the Librarian can\'t change his location, it\'s defined by branchip',
391             type                => 'YesNo',
392         },
393         {
394             uniquefieldrequired => 'variable',
395             variable            => 'serialsadditems',
396             value               => '0',
397             forceupdate         => {
398                 'explanation' => 1,
399                 'type' => 1
400             },
401             explanation => 'If set, a new item will be automatically added when receiving an issue',
402             type => 'YesNo',
403         },
404         {
405             uniquefieldrequired => 'variable',
406             variable            => 'expandedSearchOption',
407             value               => '0',
408             forceupdate         => {
409                 'explanation' => 1,
410                 'type' => 1
411             },
412             explanation => 'search among marc field',
413             type => 'YesNo',
414         },
415        {
416             uniquefieldrequired => 'variable',
417             variable            => 'RequestOnOpac',
418             value               => '1',
419             forceupdate         => { 'explanation' => 1,
420                                      'type' => 1},
421             explanation         => 'option to allow reserves on opac',
422             type                => 'YesNo',
423         },
424        {
425             uniquefieldrequired => 'variable',
426             variable            => 'OpacCloud',
427             value               => '1',
428             forceupdate         => { 'explanation' => 1,
429                                      'type' => 1},
430             explanation         => 'Enable / Disable cloud link on OPAC',
431             type                => 'YesNo',
432         },
433        {
434             uniquefieldrequired => 'variable',
435             variable            => 'OpacBrowser',
436             value               => '1',
437             forceupdate         => { 'explanation' => 1,
438                                      'type' => 1},
439             explanation         => 'Enable/Disable browser link on OPAC (needs to set misc/cronjob/build_browser.pl)',
440             type                => 'YesNo',
441         },
442        {
443             uniquefieldrequired => 'variable',
444             variable            => 'OpacTopissue',
445             value               => '1',
446             forceupdate         => { 'explanation' => 1,
447                                      'type' => 1},
448             explanation         => 'Enable / Disable the top issue link on OPAC',
449             type                => 'YesNo',
450         },
451        {
452             uniquefieldrequired => 'variable',
453             variable            => 'OpacAuthorities',
454             value               => '1',
455             forceupdate         => { 'explanation' => 1,
456                                      'type' => 1},
457             explanation         => 'Enable / Disable the search authority link on OPAC',
458             type                => 'YesNo',
459         },
460         {
461             uniquefieldrequired => 'variable',
462             variable            => 'CataloguingLog',
463             value               => '0',
464             forceupdate         => {'explanation' => 1, 'type' => 1},
465             explanation         => 'Active this if you want to log cataloguing action.',
466             type                => 'YesNo',
467         },
468         {
469             uniquefieldrequired => 'variable',
470             variable            => 'BorrowersLog',
471             value               => '0',
472             forceupdate         => {'explanation' => 1, 'type' => 1},
473             explanation         => 'Active this if you want to log borrowers edition/creation/deletion...',
474             type                => 'YesNo',
475         },
476         {
477             uniquefieldrequired => 'variable',
478             variable            => 'SubscriptionLog',
479             value               => '0',
480             forceupdate         => {'explanation' => 1, 'type' => 1},
481             explanation         => 'Active this if you want to log Subscription action',
482             type                => 'YesNo',
483         },
484         {
485             uniquefieldrequired => 'variable',
486             variable            => 'IssueLog',
487             value               => '0',
488             forceupdate         => {'explanation' => 1, 'type' => 1},
489             explanation         => 'Active this if you want to log issue.',
490             type                => 'YesNo',
491         },
492         {
493             uniquefieldrequired => 'variable',
494             variable            => 'ReturnLog',
495             value               => '0',
496             forceupdate         => {'explanation' => 1, 'type' => 1},
497             explanation         => 'Active this if you want to log the circulation return',
498             type                => 'YesNo',
499         },
500         {   
501             uniquefieldrequired => 'variable',
502             variable            => 'LetterLog',
503             value               => '0',
504             forceupdate         => {'explanation' => 1, 'type' => 1},
505             explanation         => 'Active this if you want to log all the letter sent',
506             type                => 'YesNo',
507         },
508         {
509             uniquefieldrequired => 'variable',
510             variable            => 'FinesLog',
511             value               => '0',
512             forceupdate         => {'explanation' => 1, 'type' => 1},
513             explanation         => 'Active this if you want to log fines',
514             type                => 'YesNo',
515         },
516     ],
517     userflags => [
518         {
519             uniquefieldrequired => 'bit',
520             bit                 => '14',
521             flag                => 'editauthorities',
522             flagdesc            => 'allow to edit authorities',
523             defaulton           => '0',
524         },
525         {
526             uniquefieldrequired => 'bit',
527             bit                 => '15',
528             flag                 => 'serials',
529             flagdesc            => 'allow to manage serials subscriptions',
530             defaulton           => '0',
531         },
532         {
533             uniquefieldrequired => 'bit',
534             bit                 => '16',
535             flag                 => 'reports',
536             flagdesc            => 'allow to access to the reports module',
537             defaulton           => '0',
538         },
539     ],
540     authorised_values => [
541         {
542             uniquefieldrequired => 'id',
543             category            => 'SUGGEST',
544             authorised_value    => 'Not enoug budget',
545             lib                 => 'This book it too much expensive',
546         }
547     ]
548 );
549
550 my %fielddefinitions = (
551 # fieldname => [
552 #    {          field => 'fieldname',
553 #             type    => 'fieldtype',
554 #             null    => '',
555 #             key     => '',
556 #             default => ''
557 #         },
558 #     ],
559     aqbasket =>  [
560         {
561             field    => 'booksellerid',
562             type    => 'int(11)',
563             null    => 'NOT NULL',
564             key        => '',
565             default    => '1',
566             extra    => '',
567         },
568     ],
569     aqbooksellers =>  [
570         {
571             field    => 'id',
572             type    => 'int(11)',
573             null    => 'NOT NULL',
574             key        => '',
575             default    => '',
576             extra    => 'auto_increment',
577         },
578         {
579             field    => 'listprice',
580             type    => 'varchar(10)',
581             null    => 'NULL',
582             key        => '',
583             default    => '',
584             extra    => '',
585         },
586         {
587             field    => 'invoiceprice',
588             type    => 'varchar(10)',
589             null    => 'NULL',
590             key        => '',
591             default    => '',
592             extra    => '',
593         },
594     ],
595     
596     accountlines =>  [
597         {
598             field    => 'notify_id',
599             type    => 'int(11)',
600             null    => 'NOT NULL',
601             key        => '',
602             default    => '0',
603             extra    => '',
604         },
605         {
606             field    => 'notify_level',
607             type    => 'int(2)',
608             null    => 'NOT NULL',
609             key        => '',
610             default    => '0',
611             extra    => '',
612         },
613     
614     ],
615     
616     borrowers => [
617         {    field => 'firstname',
618              type => 'text',
619              null => 'NULL',
620          },
621         {    field => 'initials',
622              type => 'text',
623              null => 'NULL',
624          },
625         {    field => 'B_email',
626              type => 'text',
627              null => 'NULL',
628              after => 'B_zipcode',
629          },
630          {
631             field => 'streetnumber', # street number (hidden if streettable table is empty)
632             type => 'char(10)',
633             null => 'NULL',
634             after => 'initials',
635         },
636         {
637             field => 'streettype', # street table, list builded from a system table
638             type => 'char(50)',
639             null => 'NULL',
640             after => 'streetnumber',
641         },
642         {    field => 'phone',
643              type => 'text',
644              null => 'NULL',
645          },
646         {
647             field => 'B_streetnumber', # street number (hidden if streettable table is empty)
648             type => 'char(10)',
649             null => 'NULL',
650             after => 'fax',
651         },
652         {
653             field => 'B_streettype', # street table, list builded from a system table
654             type => 'char(50)',
655             null => 'NULL',
656             after => 'B_streetnumber',
657         },
658         {
659             field => 'phonepro',
660             type => 'text',
661             null => 'NULL',
662             after => 'fax',
663         },
664         {
665             field => 'address2', # complement address
666             type => 'text',
667             null => 'NULL',
668             after => 'address',
669         },
670         {
671             field => 'emailpro',
672             type => 'text',
673             null => 'NULL',
674             after => 'fax',
675         },
676         {
677             field => 'contactfirstname', # contact's firstname
678             type => 'text',
679             null => 'NULL',
680             after => 'contactname',
681         },
682         {
683             field => 'contacttitle', # contact's title
684             type => 'text',
685             null => 'NULL',
686             after => 'contactfirstname',
687         },
688         {
689             field => 'branchcode',
690             type  => 'varchar(10)',
691             null  => 'NOT NULL',
692             default    => '',
693             extra => '',
694         },
695         {
696             field => 'categorycode',
697             type  => 'varchar(10)',
698             null  => 'NOT NULL',
699             default    => '',
700             extra => '',
701         }
702     ],
703     
704     biblioitems =>  [
705         {
706             field    => 'lcsort',
707             type    => 'varchar(25)',
708             null    => 'NULL',
709             key        => '',
710             default    => '',
711             extra    => '',
712         },
713         {
714             field    => 'ccode',
715             type    => 'varchar(4)',
716             null    => 'NULL',
717             key        => '',
718             default    => '',
719             extra    => '',
720         },
721     ],
722     branches =>  [
723         {
724             field    => 'branchip',
725             type    => 'varchar(15)',
726             null    => 'NULL',
727             key        => '',
728             default    => '',
729             extra    => '',
730         },
731         {
732             field    => 'branchprinter',
733             type    => 'varchar(100)',
734             null    => 'NULL',
735             key        => '',
736             default    => '',
737             extra    => '',
738         },
739         {
740             field   => 'branchcode',
741             type    => 'varchar(10)',
742             null    => 'NOT NULL',
743             default => '',
744             extra   => '',
745         }
746     ],
747     branchtransfers =>[
748         {
749             field   => 'frombranch',
750             type    => 'VARCHAR(10)',
751             null    => 'NOT NULL',
752             key     => '',
753             default => '',
754             extra   => '',
755         },
756         {
757             field   => 'tobranch',
758             type    => 'VARCHAR(10)',
759             null    => 'NOT NULL',
760             key     => '',
761             default => '',
762         }
763     ],
764     
765     categories =>  [
766         {
767             field    => 'category_type',
768             type    => 'char(1)',
769             null    => 'NOT NULL',
770             key        => '',
771             default    => 'A',
772             extra    => '',
773         },
774         {
775             field   => 'categorycode',
776             type    => 'varchar(10)',
777             null    => 'NOT NULL',
778             key     => 'PRI',
779             default => '',
780             extra   => '',
781         },
782     ],
783     
784     deletedborrowers => [
785         {    field => 'firstname',
786              type => 'text',
787              null => 'NULL',
788          },
789         {    field => 'initials',
790              type => 'text',
791              null => 'NULL',
792          },
793         {    field => 'B_email',
794              type => 'text',
795              null => 'NULL',
796              after => 'B_zipcode',
797          },
798          {
799             field => 'streetnumber', # street number (hidden if streettable table is empty)
800             type => 'char(10)',
801             null => 'NULL',
802             after => 'initials',
803         },
804         {
805             field => 'streettype', # street table, list builded from a system table
806             type => 'char(50)',
807             null => 'NULL',
808             after => 'streetnumber',
809         },
810         {    field => 'phone',
811              type => 'text',
812              null => 'NULL',
813          },
814          {
815             field => 'B_streetnumber', # street number (hidden if streettable table is empty)
816             type => 'char(10)',
817             null => 'NULL',
818             after => 'fax',
819         },
820         {
821             field => 'B_streettype', # street table, list builded from a system table
822             type => 'char(50)',
823             null => 'NULL',
824             after => 'B_streetnumber',
825         },
826         {
827             field => 'phonepro',
828             type => 'text',
829             null => 'NULL',
830             after => 'fax',
831         },
832         {
833             field => 'address2', # complement address
834             type => 'text',
835             null => 'NULL',
836             after => 'address',
837         },
838         {
839             field => 'emailpro',
840             type => 'text',
841             null => 'NULL',
842             after => 'fax',
843         },
844         {
845             field => 'contactfirstname', # contact's firstname
846             type => 'text',
847             null => 'NULL',
848             after => 'contactname',
849         },
850         {
851             field => 'contacttitle', # contact's title
852             type => 'text',
853             null => 'NULL',
854             after => 'contactfirstname',
855         },
856     ],
857     
858     issues =>  [
859         {
860             field    => 'borrowernumber',
861             type    => 'int(11)',
862             null    => 'NULL', # can be null when a borrower is deleted and the foreign key rule executed
863             key        => '',
864             default    => '',
865             extra    => '',
866         },
867         {
868             field    => 'itemnumber',
869             type    => 'int(11)',
870             null    => 'NULL', # can be null when a borrower is deleted and the foreign key rule executed
871             key        => '',
872             default    => '',
873             extra    => '',
874         },
875         {
876             field   => 'branchcode',
877             type    => 'varchar(10)',
878             null    => 'NULL',
879             key     => '',
880             default => '',
881             extra   => '',
882         },
883         {
884             field   => 'issuedate',
885             type    => 'date',
886             null    => '',
887             key     => '',
888             default => '0000-00-00',
889             extra   => '',
890         },
891     ],
892     
893     items => [
894         {
895             field    => 'onloan',
896             type    => 'date',
897             null    => 'NULL',
898             key        => '',
899             default    => '0000-00-00',
900             extra    => '',
901         },
902         {
903             field    => 'cutterextra',
904             type    => 'varchar(45)',
905             null    => 'NULL',
906             key        => '',
907             default    => '',
908             extra    => '',
909         },
910         {
911             field    => 'issue_date',
912             type    => 'date',
913             null    => 'NULL',
914             key        => '',
915             default    => '',
916             extra    => '',
917         },
918         {
919             field    => 'holdingbranch',
920             type    => 'varchar(10)',
921             null    => 'NULL',
922             key        => '',
923             default    => '',
924             extra    => '',
925         },
926         {
927             field    => 'itype',
928             type    => 'varchar(10)',
929             null    => 'NULL',
930             key        => '',
931             default    => '',
932             extra    => '',
933         },
934     ],
935     itemtypes => [
936         {
937             field  => 'itemtype',
938             type   => 'varchar(10)',
939             default    => '',
940             null   => 'NOT NULL',
941             key    => 'PRI',
942             extra  => 'UNIQUE',
943         },
944         {
945             field  => 'summary',
946             type   => 'TEXT',
947             null   => 'NULL',
948             key    => '',
949             extra  => '',
950         },
951     ],
952     opac_news => [
953         {
954             field  => 'expirationdate',
955             type   => 'date',
956             null   => 'null',
957             key    => '',
958             extra  => '',
959         },
960         {
961             field   => 'number',
962             type    => 'int(11)',
963             null    => 'NULL',
964             key     => '',
965             default => '0',
966             extra   => '',
967         },
968     ],
969     reserves =>  [
970         {
971             field    => 'waitingdate',
972             type    => 'date',
973             null    => 'NULL',
974             key        => '',
975             default    => '',
976             extra    => '',
977         },
978     ],
979     serial => [
980         {
981             field   => 'notes',
982             type    => 'TEXT',
983             null    => 'NULL',
984             key     => '',
985             default => '',
986             extra   => ''
987         },
988     ],
989     shelfcontents => [
990         {
991             field => 'dateadded',
992             type => 'timestamp',
993             null    => 'NULL',
994         },
995     ],
996     systempreferences =>  [
997         {
998             field    => 'value',
999             type    => 'text',
1000             null    => 'NULL',
1001             key        => '',
1002             default    => '',
1003             extra    => '',
1004         },
1005         {
1006             field    => 'explanation',
1007             type    => 'text',
1008             null    => 'NULL',
1009             key        => '',
1010             default    => '',
1011             extra    => '',
1012         },
1013     ],
1014     suggestions => [
1015         {
1016             field   => 'reason',
1017             type    => 'text',
1018             null    => 'NULL',
1019             key     => ''  ,
1020             default => '',
1021             extra   =>    '',
1022         }
1023     ],
1024 );
1025
1026 my %indexes = (
1027 #    table => [
1028 #         {    indexname => 'index detail'
1029 #         }
1030 #    ],
1031     aqbooksellers => [
1032         {    indexname => 'PRIMARY',
1033             content => 'id',
1034             type => 'PRI',
1035         }
1036     ],
1037     aqbasket => [
1038         {    indexname => 'booksellerid',
1039             content => 'booksellerid',
1040         },
1041     ],
1042     aqorders => [
1043         {    indexname => 'basketno',
1044             content => 'basketno',
1045         },
1046     ],
1047     aqorderbreakdown => [
1048         {    indexname => 'ordernumber',
1049             content => 'ordernumber',
1050         },
1051         {    indexname => 'bookfundid',
1052             content => 'bookfundid',
1053         },
1054     ],
1055     biblioitems => [
1056         {    indexname => 'isbn',
1057             content => 'isbn',
1058         },
1059         {    indexname => 'publishercode',
1060             content => 'publishercode',
1061         },
1062     ],
1063     branches => [
1064         {
1065             indexname => 'branchcode',
1066             content   => 'branchcode',
1067     
1068         }
1069     ],
1070     currency => [
1071         {    indexname => 'PRIMARY',
1072             content => 'currency',
1073             type => 'PRI',
1074         }
1075     ],
1076     categories => [
1077         {
1078             indexname => 'categorycode',
1079             content   => 'categorycode',
1080         }
1081     ],
1082     items => [
1083         {    indexname => 'homebranch',
1084             content => 'homebranch',
1085         },
1086         {    indexname => 'holdingbranch',
1087             content => 'holdingbranch',
1088         }
1089     ],
1090     itemtypes => [
1091         {
1092             indexname => 'itemtype',
1093             content   => 'itemtype',
1094         }
1095     ],
1096     shelfcontents => [
1097         {    indexname => 'shelfnumber',
1098             content => 'shelfnumber',
1099         },
1100         {    indexname => 'itemnumber',
1101             content => 'itemnumber',
1102         }
1103     ],
1104 );
1105
1106 my %foreign_keys = (
1107 #    table => [
1108 #         {    key => 'the key in table' (must be indexed)
1109 #            foreigntable => 'the foreigntable name', # (the parent)
1110 #            foreignkey => 'the foreign key column(s)' # (in the parent)
1111 #            onUpdate => 'CASCADE|SET NULL|NO ACTION| RESTRICT',
1112 #            onDelete => 'CASCADE|SET NULL|NO ACTION| RESTRICT',
1113 #         }
1114 #    ],
1115     shelfcontents => [
1116         {    key => 'shelfnumber',
1117             foreigntable => 'bookshelf',
1118             foreignkey => 'shelfnumber',
1119             onUpdate => 'CASCADE',
1120             onDelete => 'CASCADE',
1121         },
1122         {    key => 'itemnumber',
1123             foreigntable => 'items',
1124             foreignkey => 'itemnumber',
1125             onUpdate => 'CASCADE',
1126             onDelete => 'CASCADE',
1127         },
1128     ],
1129     # onDelete is RESTRICT on reference tables (branches, itemtype) as we don't want items to be
1130     # easily deleted, but branches/itemtype not too easy to empty...
1131     biblioitems => [
1132         {    key => 'biblionumber',
1133             foreigntable => 'biblio',
1134             foreignkey => 'biblionumber',
1135             onUpdate => 'CASCADE',
1136             onDelete => 'CASCADE',
1137         },
1138         {    key => 'itemtype',
1139             foreigntable => 'itemtypes',
1140             foreignkey => 'itemtype',
1141             onUpdate => 'CASCADE',
1142             onDelete => 'RESTRICT',
1143         },
1144     ],
1145     items => [
1146         {    key => 'biblioitemnumber',
1147             foreigntable => 'biblioitems',
1148             foreignkey => 'biblioitemnumber',
1149             onUpdate => 'CASCADE',
1150             onDelete => 'CASCADE',
1151         },
1152         {    key => 'homebranch',
1153             foreigntable => 'branches',
1154             foreignkey => 'branchcode',
1155             onUpdate => 'CASCADE',
1156             onDelete => 'RESTRICT',
1157         },
1158         {    key => 'holdingbranch',
1159             foreigntable => 'branches',
1160             foreignkey => 'branchcode',
1161             onUpdate => 'CASCADE',
1162             onDelete => 'RESTRICT',
1163         },
1164     ],
1165     aqbasket => [
1166         {    key => 'booksellerid',
1167             foreigntable => 'aqbooksellers',
1168             foreignkey => 'id',
1169             onUpdate => 'CASCADE',
1170             onDelete => 'RESTRICT',
1171         },
1172     ],
1173     aqorders => [
1174         {    key => 'basketno',
1175             foreigntable => 'aqbasket',
1176             foreignkey => 'basketno',
1177             onUpdate => 'CASCADE',
1178             onDelete => 'CASCADE',
1179         },
1180         {    key => 'biblionumber',
1181             foreigntable => 'biblio',
1182             foreignkey => 'biblionumber',
1183             onUpdate => 'SET NULL',
1184             onDelete => 'SET NULL',
1185         },
1186     ],
1187     aqbooksellers => [
1188         {    key => 'listprice',
1189             foreigntable => 'currency',
1190             foreignkey => 'currency',
1191             onUpdate => 'CASCADE',
1192             onDelete => 'CASCADE',
1193         },
1194         {    key => 'invoiceprice',
1195             foreigntable => 'currency',
1196             foreignkey => 'currency',
1197             onUpdate => 'CASCADE',
1198             onDelete => 'CASCADE',
1199         },
1200     ],
1201     aqorderbreakdown => [
1202         {    key => 'ordernumber',
1203             foreigntable => 'aqorders',
1204             foreignkey => 'ordernumber',
1205             onUpdate => 'CASCADE',
1206             onDelete => 'CASCADE',
1207         },
1208         {    key => 'bookfundid',
1209             foreigntable => 'aqbookfund',
1210             foreignkey => 'bookfundid',
1211             onUpdate => 'CASCADE',
1212             onDelete => 'CASCADE',
1213         },
1214     ],
1215     branchtransfers => [
1216         {    key => 'frombranch',
1217             foreigntable => 'branches',
1218             foreignkey => 'branchcode',
1219             onUpdate => 'CASCADE',
1220             onDelete => 'CASCADE',
1221         },
1222         {    key => 'tobranch',
1223             foreigntable => 'branches',
1224             foreignkey => 'branchcode',
1225             onUpdate => 'CASCADE',
1226             onDelete => 'CASCADE',
1227         },
1228         {    key => 'itemnumber',
1229             foreigntable => 'items',
1230             foreignkey => 'itemnumber',
1231             onUpdate => 'CASCADE',
1232             onDelete => 'CASCADE',
1233         },
1234     ],
1235     issuingrules => [
1236         {    key => 'categorycode',
1237             foreigntable => 'categories',
1238             foreignkey => 'categorycode',
1239             onUpdate => 'CASCADE',
1240             onDelete => 'CASCADE',
1241         },
1242         {    key => 'itemtype',
1243             foreigntable => 'itemtypes',
1244             foreignkey => 'itemtype',
1245             onUpdate => 'CASCADE',
1246             onDelete => 'CASCADE',
1247         },
1248     ],
1249     issues => [    # constraint is SET NULL : when a borrower or an item is deleted, we keep the issuing record
1250     # for stat purposes
1251         {    key => 'borrowernumber',
1252             foreigntable => 'borrowers',
1253             foreignkey => 'borrowernumber',
1254             onUpdate => 'SET NULL',
1255             onDelete => 'SET NULL',
1256         },
1257         {    key => 'itemnumber',
1258             foreigntable => 'items',
1259             foreignkey => 'itemnumber',
1260             onUpdate => 'SET NULL',
1261             onDelete => 'SET NULL',
1262         },
1263     ],
1264     reserves => [
1265         {    key => 'borrowernumber',
1266             foreigntable => 'borrowers',
1267             foreignkey => 'borrowernumber',
1268             onUpdate => 'CASCADE',
1269             onDelete => 'CASCADE',
1270         },
1271         {    key => 'biblionumber',
1272             foreigntable => 'biblio',
1273             foreignkey => 'biblionumber',
1274             onUpdate => 'CASCADE',
1275             onDelete => 'CASCADE',
1276         },
1277         {    key => 'itemnumber',
1278             foreigntable => 'items',
1279             foreignkey => 'itemnumber',
1280             onUpdate => 'CASCADE',
1281             onDelete => 'CASCADE',
1282         },
1283         {    key => 'branchcode',
1284             foreigntable => 'branches',
1285             foreignkey => 'branchcode',
1286             onUpdate => 'CASCADE',
1287             onDelete => 'CASCADE',
1288         },
1289     ],
1290     borrowers => [ # foreign keys are RESTRICT as we don't want to delete borrowers when a branch is deleted
1291     # but prevent deleting a branch as soon as it has 1 borrower !
1292         {    key => 'categorycode',
1293             foreigntable => 'categories',
1294             foreignkey => 'categorycode',
1295             onUpdate => 'RESTRICT',
1296             onDelete => 'RESTRICT',
1297         },
1298         {    key => 'branchcode',
1299             foreigntable => 'branches',
1300             foreignkey => 'branchcode',
1301             onUpdate => 'RESTRICT',
1302             onDelete => 'RESTRICT',
1303         },
1304     ],
1305     deletedborrowers => [ # foreign keys are RESTRICT as we don't want to delete borrowers when a branch is deleted
1306     # but prevent deleting a branch as soon as it has 1 borrower !
1307         {    key => 'categorycode',
1308             foreigntable => 'categories',
1309             foreignkey => 'categorycode',
1310             onUpdate => 'RESTRICT',
1311             onDelete => 'RESTRICT',
1312         },
1313         {    key => 'branchcode',
1314             foreigntable => 'branches',
1315             foreignkey => 'branchcode',
1316             onUpdate => 'RESTRICT',
1317             onDelete => 'RESTRICT',
1318         },
1319     ],
1320     accountlines => [
1321         {    key => 'borrowernumber',
1322             foreigntable => 'borrowers',
1323             foreignkey => 'borrowernumber',
1324             onUpdate => 'CASCADE',
1325             onDelete => 'CASCADE',
1326         },
1327         {    key => 'itemnumber',
1328             foreigntable => 'items',
1329             foreignkey => 'itemnumber',
1330             onUpdate => 'SET NULL',
1331             onDelete => 'SET NULL',
1332         },
1333     ],
1334     auth_tag_structure => [
1335         {    key => 'authtypecode',
1336             foreigntable => 'auth_types',
1337             foreignkey => 'authtypecode',
1338             onUpdate => 'CASCADE',
1339             onDelete => 'CASCADE',
1340         },
1341     ],
1342     # FIXME : don't constraint auth_*_table and auth_word, as they may be replaced by zebra
1343 );
1344
1345
1346 # column changes
1347 my %column_change = (
1348     # table
1349     borrowers => [
1350                 {
1351                     from => 'emailaddress',
1352                     to => 'email',
1353                     after => 'city',
1354                 },
1355                 {
1356                     from => 'streetaddress',
1357                     to => 'address',
1358                     after => 'initials',
1359                 },
1360                 {
1361                     from => 'faxnumber',
1362                     to => 'fax',
1363                     after => 'phone',
1364                 },
1365                 {
1366                     from => 'textmessaging',
1367                     to => 'opacnote',
1368                     after => 'userid',
1369                 },
1370                 {
1371                     from => 'altnotes',
1372                     to => 'contactnote',
1373                     after => 'opacnote',
1374                 },
1375                 {
1376                     from => 'physstreet',
1377                     to => 'B_address',
1378                     after => 'fax',
1379                 },
1380                 {
1381                     from => 'streetcity',
1382                     to => 'B_city',
1383                     after => 'B_address',
1384                 },
1385                 {
1386                     from => 'phoneday',
1387                     to => 'mobile',
1388                     after => 'phone',
1389                 },
1390                 {
1391                     from => 'zipcode',
1392                     to => 'zipcode',
1393                     after => 'city',
1394                 },
1395                 {
1396                     from => 'homezipcode',
1397                     to => 'B_zipcode',
1398                     after => 'B_city',
1399                 },
1400                 {
1401                     from => 'altphone',
1402                     to => 'B_phone',
1403                     after => 'B_zipcode',
1404                 },
1405                 {
1406                     from => 'expiry',
1407                     to => 'dateexpiry',
1408                     after => 'dateenrolled',
1409                 },
1410                 {
1411                     from => 'guarantor',
1412                     to => 'guarantorid',
1413                     after => 'contactname',
1414                 },
1415                 {
1416                     from => 'altrelationship',
1417                     to => 'relationship',
1418                     after => 'borrowernotes',
1419                 },
1420             ],
1421
1422     deletedborrowers => [
1423                 {
1424                     from => 'emailaddress',
1425                     to => 'email',
1426                     after => 'city',
1427                 },
1428                 {
1429                     from => 'streetaddress',
1430                     to => 'address',
1431                     after => 'initials',
1432                 },
1433                 {
1434                     from => 'faxnumber',
1435                     to => 'fax',
1436                     after => 'phone',
1437                 },
1438                 {
1439                     from => 'textmessaging',
1440                     to => 'opacnote',
1441                     after => 'userid',
1442                 },
1443                 {
1444                     from => 'altnotes',
1445                     to => 'contactnote',
1446                     after => 'opacnote',
1447                 },
1448                 {
1449                     from => 'physstreet',
1450                     to => 'B_address',
1451                     after => 'fax',
1452                 },
1453                 {
1454                     from => 'streetcity',
1455                     to => 'B_city',
1456                     after => 'B_address',
1457                 },
1458                 {
1459                     from => 'phoneday',
1460                     to => 'mobile',
1461                     after => 'phone',
1462                 },
1463                 {
1464                     from => 'zipcode',
1465                     to => 'zipcode',
1466                     after => 'city',
1467                 },
1468                 {
1469                     from => 'homezipcode',
1470                     to => 'B_zipcode',
1471                     after => 'B_city',
1472                 },
1473                 {
1474                     from => 'altphone',
1475                     to => 'B_phone',
1476                     after => 'B_zipcode',
1477                 },
1478                 {
1479                     from => 'expiry',
1480                     to => 'dateexpiry',
1481                     after => 'dateenrolled',
1482                 },
1483                 {
1484                     from => 'guarantor',
1485                     to => 'guarantorid',
1486                     after => 'contactname',
1487                 },
1488                 {
1489                     from => 'altrelationship',
1490                     to => 'relationship',
1491                     after => 'borrowernotes',
1492                 },
1493             ],
1494         );
1495     
1496
1497 # MOVE all tables TO UTF-8 and innoDB
1498 $sth = $dbh->prepare("show table status");
1499 $sth->execute;
1500 while ( my $table = $sth->fetchrow_hashref ) {
1501      if ($table->{Engine} ne 'InnoDB') {
1502          $dbh->do("ALTER TABLE $table->{Name} TYPE = innodb");
1503          print "moving $table->{Name} to InnoDB\n";
1504      }
1505     next if $table->{Name} eq 'marc_word';
1506     next if $table->{Name} eq 'marc_subfield_table';
1507     next if $table->{Name} eq 'auth_word';
1508     next if $table->{Name} eq 'auth_subfield_table';
1509     unless ($table->{Collation} =~ /^utf8/) {
1510          print "moving $table->{Name} to utf8\n";
1511         $dbh->do("ALTER TABLE $table->{Name} CONVERT TO CHARACTER SET utf8");
1512         $dbh->do("ALTER TABLE $table->{Name} DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci");
1513         # 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 !
1514     } else {
1515     }
1516 }
1517
1518
1519 foreach my $table (keys %column_change) {
1520     $sth = $dbh->prepare("show columns from $table");
1521     $sth->execute();
1522     undef %types;
1523     while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
1524     {
1525         $types{$column}->{type} ="$type";
1526         $types{$column}->{null} = "$null";
1527         $types{$column}->{key} = "$key";
1528         $types{$column}->{default} = "$default";
1529         $types{$column}->{extra} = "$extra";
1530     }    # while
1531     my $tablerows = $column_change{$table};
1532     foreach my $row ( @$tablerows ) {
1533         if ($types{$row->{from}}->{type}) {
1534             print "altering $table $row->{from} to $row->{to}\n";
1535             # ALTER TABLE `borrowers` CHANGE `faxnumber` `fax` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL
1536 #             alter table `borrowers` change `faxnumber` `fax` type text  null after phone
1537             my $sql =
1538                 "alter table `$table` change `$row->{from}` `$row->{to}` $types{$row->{from}}->{type} ".
1539                 ($types{$row->{from}}->{null} eq 'YES'?" NULL":" NOT NULL").
1540                 ($types{$row->{from}}->{default}?" default ".$types{$row->{from}}->{default}:"").
1541                 "$types{$row->{from}}->{extra} after $row->{after} ";
1542 #             print "$sql";
1543             $dbh->do($sql);
1544         }
1545     }
1546 }
1547
1548 # Enter here the field you want to delete from DB.
1549 # FIXME :: there is a %uselessfield before which seems doing the same things.
1550 my %fieldtodelete = (
1551     # tablename => [fieldname1,fieldname2,...]
1552
1553 ); # %fielddelete
1554
1555 print "removing some unused fields...\n";
1556 foreach my $table ( keys %fieldtodelete ) {
1557     foreach my $field ( @{$fieldtodelete{$table}} ){
1558         print "removing ".$field." from ".$table;
1559         my $sth = $dbh->prepare("ALTER TABLE $table DROP $field");
1560         $sth->execute;
1561         if ( $sth->err ) {
1562             print "Error : $sth->errstr \n";
1563         }
1564     }
1565 }
1566
1567 # Enter here the line you want to remove from DB.
1568 my %linetodelete = (
1569     # table name => where clause.
1570     userflags => "bit = 8", # delete the 'reserveforself' flags
1571     
1572 ); # %linetodelete
1573
1574 #-------------------
1575 # Initialize
1576
1577 # Start checking
1578
1579 # Get version of MySQL database engine.
1580 my $mysqlversion = `mysqld --version`;
1581 $mysqlversion =~ /Ver (\S*) /;
1582 $mysqlversion = $1;
1583 if ( $mysqlversion ge '3.23' ) {
1584     print "Could convert to MyISAM database tables...\n" unless $silent;
1585 }
1586
1587 #---------------------------------
1588 # Tables
1589
1590 # Collect all tables into a list
1591 $sth = $dbh->prepare("show tables");
1592 $sth->execute;
1593 while ( my ($table) = $sth->fetchrow ) {
1594     $existingtables{$table} = 1;
1595 }
1596
1597
1598 # Now add any missing tables
1599 foreach $table ( keys %requiretables ) {
1600     unless ( $existingtables{$table} ) {
1601     print "Adding $table table...\n" unless $silent;
1602         my $sth = $dbh->prepare("create table $table $requiretables{$table}");
1603         $sth->execute;
1604         if ( $sth->err ) {
1605             print "Error : $sth->errstr \n";
1606             $sth->finish;
1607         }    # if error
1608     }    # unless exists
1609 }    # foreach
1610
1611 #---------------------------------
1612 # Columns
1613
1614 foreach $table ( keys %requirefields ) {
1615     print "Check table $table\n" if $debug and not $silent;
1616     $sth = $dbh->prepare("show columns from $table");
1617     $sth->execute();
1618     undef %types;
1619     while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
1620     {
1621         $types{$column} = $type;
1622     }    # while
1623     foreach $column ( keys %{ $requirefields{$table} } ) {
1624         print "  Check column $column  [$types{$column}]\n" if $debug and not $silent;
1625         if ( !$types{$column} ) {
1626
1627             # column doesn't exist
1628             print "Adding $column field to $table table...\n" unless $silent;
1629             $query = "alter table $table
1630             add column $column " . $requirefields{$table}->{$column};
1631             print "Execute: $query\n" if $debug;
1632             my $sti = $dbh->prepare($query);
1633             $sti->execute;
1634             if ( $sti->err ) {
1635                 print "**Error : $sti->errstr \n";
1636                 $sti->finish;
1637             }    # if error
1638         }    # if column
1639     }    # foreach column
1640 }    # foreach table
1641
1642 foreach $table ( keys %fielddefinitions ) {
1643     print "Check table $table\n" if $debug;
1644     $sth = $dbh->prepare("show columns from $table");
1645     $sth->execute();
1646     my $definitions;
1647     while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
1648     {
1649         $definitions->{$column}->{type}    = $type;
1650         $definitions->{$column}->{null}    = $null;
1651         $definitions->{$column}->{null}    = 'NULL' if $null eq 'YES';
1652         $definitions->{$column}->{key}     = $key;
1653         $definitions->{$column}->{default} = $default;
1654         $definitions->{$column}->{extra}   = $extra;
1655     }    # while
1656     my $fieldrow = $fielddefinitions{$table};
1657     foreach my $row (@$fieldrow) {
1658         my $field   = $row->{field};
1659         my $type    = $row->{type};
1660         my $null    = $row->{null};
1661 #         $null    = 'YES' if $row->{null} eq 'NULL';
1662         my $key     = $row->{key};
1663         my $default = $row->{default};
1664 #         $default="''" unless $default;
1665         my $extra   = $row->{extra};
1666         my $def     = $definitions->{$field};
1667         my $after    = ($row->{after}?" after ".$row->{after}:"");
1668
1669         unless ( $type eq $def->{type}
1670             && $null eq $def->{null}
1671             && $key eq $def->{key}
1672             && $extra eq $def->{extra} )
1673         {
1674             if ( $null eq '' ) {
1675                 $null = 'NOT NULL';
1676             }
1677             if ( $key eq 'PRI' ) {
1678                 $key = 'PRIMARY KEY';
1679             }
1680             unless ( $extra eq 'auto_increment' ) {
1681                 $extra = '';
1682             }
1683     
1684             # if it's a new column use "add", if it's an old one, use "change".
1685             my $action;
1686             if ($definitions->{$field}->{type}) {
1687                 $action="change $field"
1688             } else {
1689                 $action="add";
1690             }
1691 # if it's a primary key, drop the previous pk, before altering the table
1692             my $sth;
1693             if ($key ne 'PRIMARY KEY') {
1694 #                 warn "alter table $table $action $field $type $null $key $extra default $default $after";
1695                 $sth =$dbh->prepare("alter table $table $action $field $type $null $key $extra default ? $after");
1696             } else {
1697 #             warn "alter table $table drop primary key, $action $field $type $null $key $extra default $default $after";
1698                  # something strange : for indexes UNIQUE, they are reported as primary key here.
1699                  # but if you try to run with drop primary key, it fails.
1700                  # thus, we run the query twice, one will fail, one will succeed.
1701                  # strange...
1702                 $sth =$dbh->prepare("alter table $table drop primary key, $action $field $type $null $key $extra default ? $after");
1703                 $sth =$dbh->prepare("alter table $table $action $field $type $null $key $extra default ? $after");
1704             }
1705 # ALTER TABLE `borrowers` CHANGE `branchcode` `branchcode` VARCHAR( 10 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL 
1706             print "  alter or create $field in $table\n" unless $silent;
1707             $sth->execute($default);
1708         }
1709     }
1710 }
1711
1712 print "removing some unused data...\n";
1713 foreach my $table ( keys %linetodelete ) {
1714     foreach my $where ( @{linetodelete{$table}} ){
1715         print "DELETE FROM ".$table." where ".$where;
1716         print "\n";
1717         my $sth = $dbh->prepare("DELETE FROM $table where $where");
1718         $sth->execute;
1719         if ( $sth->err ) {
1720             print "Error : $sth->errstr \n";
1721         }
1722     }
1723 }
1724
1725 # Populate tables with required data
1726
1727 # synch table and deletedtable.
1728 foreach my $table (('borrowers','items','biblio','biblioitems')) {
1729     my %deletedborrowers;
1730     print "synch'ing $table and deleted$table\n";
1731     $sth = $dbh->prepare("show columns from deleted$table");
1732     $sth->execute;
1733     while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow ) {
1734         $deletedborrowers{$column}=1;
1735     }
1736     $sth = $dbh->prepare("show columns from $table");
1737     $sth->execute;
1738     my $previous;
1739     while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow ) {
1740         unless ($deletedborrowers{$column}) {
1741             my $newcol="alter table deleted$table add $column $type";
1742             if ($null eq 'YES') {
1743                 $newcol .= " NULL ";
1744             } else {
1745                 $newcol .= " NOT NULL ";
1746             }
1747             $newcol .= "default ".$dbh->quote($default) if $default;
1748             $newcol .= " after $previous" if $previous;
1749             $previous=$column;
1750             print "creating column $column\n";
1751             $dbh->do($newcol);
1752         }
1753     }
1754 }
1755 #
1756 # update publisheddate 
1757 #
1758 $sth = $dbh->prepare("select count(*) from serial where publisheddate is NULL");
1759 $sth->execute;
1760 my ($emptypublished) = $sth->fetchrow;
1761 if ($emptypublished) {
1762     print "Updating publisheddate\n";
1763     $dbh->do("update serial set publisheddate=planneddate where publisheddate is NULL");
1764 }
1765 foreach my $table ( keys %tabledata ) {
1766     print "Checking for data required in table $table...\n" unless $silent;
1767     my $tablerows = $tabledata{$table};
1768     foreach my $row (@$tablerows) {
1769         my $uniquefieldrequired = $row->{uniquefieldrequired};
1770         my $uniquevalue         = $row->{$uniquefieldrequired};
1771         my $forceupdate         = $row->{forceupdate};
1772         my $sth                 =
1773           $dbh->prepare(
1774 "select $uniquefieldrequired from $table where $uniquefieldrequired=?"
1775         );
1776         $sth->execute($uniquevalue);
1777         if ($sth->rows) {
1778             foreach my $field (keys %$forceupdate) {
1779                 if ($forceupdate->{$field}) {
1780                     my $sth=$dbh->prepare("update systempreferences set $field=? where $uniquefieldrequired=?");
1781                     $sth->execute($row->{$field}, $uniquevalue);
1782                 }
1783             }
1784         } else {
1785             print "Adding row to $table: " unless $silent;
1786             my @values;
1787             my $fieldlist;
1788             my $placeholders;
1789             foreach my $field ( keys %$row ) {
1790                 next if $field eq 'uniquefieldrequired';
1791                 next if $field eq 'forceupdate';
1792                 my $value = $row->{$field};
1793                 push @values, $value;
1794                 print "  $field => $value" unless $silent;
1795                 $fieldlist .= "$field,";
1796                 $placeholders .= "?,";
1797             }
1798             print "\n" unless $silent;
1799             $fieldlist    =~ s/,$//;
1800             $placeholders =~ s/,$//;
1801             print "insert into $table ($fieldlist) values ($placeholders)";
1802             my $sth =
1803             $dbh->prepare(
1804                 "insert into $table ($fieldlist) values ($placeholders)");
1805             $sth->execute(@values);
1806         }
1807     }
1808 }
1809
1810 #
1811 # check indexes and create them when needed
1812 #
1813 print "Checking for index required...\n" unless $silent;
1814 foreach my $table ( keys %indexes ) {
1815     #
1816     # read all indexes from $table
1817     #
1818     $sth = $dbh->prepare("show index from $table");
1819     $sth->execute;
1820     my %existingindexes;
1821     while ( my ( $table, $non_unique, $key_name, $Seq_in_index, $Column_name, $Collation, $cardinality, $sub_part, $Packed, $comment ) = $sth->fetchrow ) {
1822         $existingindexes{$key_name} = 1;
1823     }
1824     # read indexes to check
1825     my $tablerows = $indexes{$table};
1826     foreach my $row (@$tablerows) {
1827         my $key_name=$row->{indexname};
1828         if ($existingindexes{$key_name} eq 1) {
1829 #             print "$key_name existing";
1830         } else {
1831             print "\tCreating index $key_name in $table\n";
1832             my $sql;
1833             if ($row->{indexname} eq 'PRIMARY') {
1834                 $sql = "alter table $table ADD PRIMARY KEY ($row->{content})";
1835             } else {
1836                 $sql = "alter table $table ADD INDEX $key_name ($row->{content}) $row->{type}";
1837             }
1838              $dbh->do($sql);
1839             print "Error $sql : $dbh->err \n" if $dbh->err;
1840         }
1841     }
1842 }
1843
1844 #
1845 # check foreign keys and create them when needed
1846 #
1847 print "Checking for foreign keys required...\n" unless $silent;
1848 foreach my $table ( keys %foreign_keys ) {
1849     #
1850     # read all indexes from $table
1851     #
1852     $sth = $dbh->prepare("show table status like '$table'");
1853     $sth->execute;
1854     my $stat = $sth->fetchrow_hashref;
1855     # read indexes to check
1856     my $tablerows = $foreign_keys{$table};
1857     foreach my $row (@$tablerows) {
1858         my $foreign_table=$row->{foreigntable};
1859         if ($stat->{'Comment'} =~/$foreign_table/) {
1860 #             print "$foreign_table existing\n";
1861         } else {
1862             print "\tCreating foreign key $foreign_table in $table\n";
1863             # first, drop any orphan value in child table
1864             if ($row->{onDelete} ne "RESTRICT") {
1865                 my $sql = "delete from $table where $row->{key} not in (select $row->{foreignkey} from $row->{foreigntable})";
1866                 $dbh->do($sql);
1867                 print "SQL ERROR: $sql : $dbh->err \n" if $dbh->err;
1868             }
1869             my $sql="alter table $table ADD FOREIGN KEY $row->{key} ($row->{key}) REFERENCES $row->{foreigntable} ($row->{foreignkey})";
1870             $sql .= " on update ".$row->{onUpdate} if $row->{onUpdate};
1871             $sql .= " on delete ".$row->{onDelete} if $row->{onDelete};
1872             $dbh->do($sql);
1873             if ($dbh->err) {
1874                 print "====================
1875 An error occured during :
1876 \t$sql
1877 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).
1878 You can find those values with select
1879 \t$table.* from $table where $row->{key} not in (select $row->{foreignkey} from $row->{foreigntable})
1880 ====================\n
1881 ";
1882             }
1883         }
1884     }
1885 }
1886 # now drop useless tables
1887 foreach $table ( @TableToDelete ) {
1888     if ( $existingtables{$table} ) {
1889         print "Dropping unused table $table\n" if $debug and not $silent;
1890         $dbh->do("drop table $table");
1891         if ( $dbh->err ) {
1892             print "Error : $dbh->errstr \n";
1893         }
1894     }
1895 }
1896
1897 #
1898 # SPECIFIC STUFF
1899 #
1900 #
1901 # create frameworkcode row in biblio table & fill it with marc_biblio.frameworkcode.
1902 #
1903
1904 # 1st, get how many biblio we will have to do...
1905 $sth = $dbh->prepare('select count(*) from marc_biblio');
1906 $sth->execute;
1907 my ($totaltodo) = $sth->fetchrow;
1908
1909 $sth = $dbh->prepare("show columns from biblio");
1910 $sth->execute();
1911 my $definitions;
1912 my $bibliofwexist=0;
1913 while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow ){
1914     $bibliofwexist=1 if $column eq 'frameworkcode';
1915 }
1916 unless ($bibliofwexist) {
1917     print "moving biblioframework to biblio table\n";
1918     $dbh->do('ALTER TABLE `biblio` ADD `frameworkcode` VARCHAR( 4 ) NOT NULL AFTER `biblionumber`');
1919     $sth = $dbh->prepare('select biblionumber,frameworkcode from marc_biblio');
1920     $sth->execute;
1921     my $sth_update = $dbh->prepare('update biblio set frameworkcode=? where biblionumber=?');
1922     my $totaldone=0;
1923     while (my ($biblionumber,$frameworkcode) = $sth->fetchrow) {
1924         $sth_update->execute($frameworkcode,$biblionumber);
1925         $totaldone++;
1926         print "\r$totaldone / $totaltodo" unless ($totaldone % 100);
1927     }
1928     print "\rdone\n";
1929 }
1930
1931 # at last, remove useless fields
1932 foreach $table ( keys %uselessfields ) {
1933     my @fields = split /,/,$uselessfields{$table};
1934     my $fields;
1935     my $exists;
1936     foreach my $fieldtodrop (@fields) {
1937         $fieldtodrop =~ s/\t//g;
1938         $fieldtodrop =~ s/\n//g;
1939         $exists =0;
1940         $sth = $dbh->prepare("show columns from $table");
1941         $sth->execute;
1942         while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
1943         {
1944             $exists =1 if ($column eq $fieldtodrop);
1945         }
1946         if ($exists) {
1947             print "deleting $fieldtodrop field in $table...\n" unless $silent;
1948             my $sth = $dbh->prepare("alter table $table drop $fieldtodrop");
1949             $sth->execute;
1950         }
1951     }
1952 }    # foreach
1953
1954 #
1955 # Changing aqbookfund's primary key 
1956 #
1957 $sth=$dbh->prepare("ALTER TABLE `aqbookfund` DROP PRIMARY KEY , ADD PRIMARY KEY ( `bookfundid` , `branchcode` ) ;");
1958 $sth->execute;
1959
1960 $sth->finish;
1961
1962 exit;
1963
1964 # $Log$
1965 # Revision 1.158  2007/03/09 15:14:57  tipaul
1966 # rel_3_0 moved to HEAD
1967 #
1968 # Revision 1.157.2.56  2007/01/31 16:22:54  btoumi
1969 # -add possibility to use isbn with length of 13 characters
1970 # for  Import datas in the reservoir.
1971 # -modify isbn field in marc_breeding table (varchar 13)
1972 # -add isbn filter (no - )when u read a notice from reservoir
1973 # -add filter to have right field 100
1974 #
1975 # Revision 1.157.2.55  2007/01/30 10:50:19  tipaul
1976 # adding 2 usefull indexes to biblioitems table
1977 #
1978 # Revision 1.157.2.54  2007/01/29 16:45:52  toins
1979 # * adding a new default authorised value : SUGGEST.
1980 # SUGGEST give some reasons to accept or reject a suggestion.
1981 #
1982 # * default value for borrowersMandatoryfield syspref is now "cardnumber|surname|adress"
1983 #
1984 # Revision 1.157.2.53  2007/01/26 20:48:37  hdl
1985 # Serials management : Bugfixes + improvements.
1986 # - Partial dates are now managed
1987 # - next Date Calculation with irregularity tested for 1 week and 1 month.
1988 # - manage if subscription is abouttoexpire or expired.
1989 # - Adding some information on serials pages about subscription.
1990 # - Managing irregularity with numbers.
1991 # - Adding Internal Notes in subscription management.
1992 # - Repeating Button above pages.
1993 #
1994 # Please run Updatedatabase to change irregularity and add internalnotes field  to subscription
1995 #
1996 # Revision 1.157.2.52  2007/01/24 13:57:26  tipaul
1997 # - setting supplierid to auto_increment (HDL : could you check that is works, i'm not 100% sure)
1998 # - removing 22 -> 30 marc_subfield_table -> marcxml stuff, it's now in misc/migration_tools/22_to_30/
1999 #
2000 # Revision 1.157.2.51  2007/01/18 09:58:45  tipaul
2001 # defaulting NOT NULL fields (to '')
2002 #
2003 # Revision 1.157.2.50  2007/01/18 09:39:21  tipaul
2004 # issuedate must be defaulted with ' '
2005 #
2006 # Revision 1.157.2.49  2007/01/18 09:37:30  tipaul
2007 # removing 2 field definitions that were here twice
2008 #
2009 # Revision 1.157.2.48  2007/01/15 09:55:40  toins
2010 # adding a new logging systempref : FinesLog.
2011 #
2012 # Revision 1.157.2.47  2007/01/12 18:09:49  toins
2013 # LetterLog added
2014 #
2015 # Revision 1.157.2.46  2007/01/11 14:35:39  tipaul
2016 # adding Opac Browser feature : the build_browser_and_cloud.pl script will :
2017 # - 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)
2018 # - fill the tags table, that contains the subject cloud.
2019 #
2020 # 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 !
2021 #
2022 # The commit also add the systempreference to hide/show the OpacBrowse in database & in systempref management script.
2023 #
2024 # IMPROVEMENTS to do :
2025 # - 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).
2026 # - add, in parameters section, a place to edit browser descriptions. The build script has to be updated to to avoid deleting existing browser descriptions.
2027 #
2028 # Revision 1.157.2.45  2007/01/10 16:52:52  toins
2029 # Value for Log Features syspref are set to 0 by default.
2030 #
2031 # Revision 1.157.2.44  2007/01/10 16:31:15  toins
2032 # new systems preferences :
2033 #  - CataloguingLog (log the update/creation/deletion of a notice if set to 1)
2034 #  - BorrowersLog ( idem for borrowers )
2035 #  - IssueLog (log all issue if set to 1)
2036 #  - ReturnLog (log all return if set to 1)
2037 #  - SusbcriptionLog (log all creation/deletion/update of a subcription)
2038 #
2039 # All of theses are in a new tab called 'LOGFeatures' in systempreferences.pl
2040 #
2041 # Revision 1.157.2.43  2007/01/10 14:13:17  toins
2042 # opac_news.displayed is replaced by opac_news.number.
2043 # This field say how are ordered the news on the template.
2044 #
2045 # Revision 1.157.2.42  2007/01/09 14:09:01  toins
2046 # 2 field added to opac_news.('expirationdate' and 'displayed').
2047 #
2048 # Revision 1.157.2.41  2006/12/22 17:11:33  tipaul
2049 # adding 3 systempreferences for opac features & a new systempref tab where all systempreferences are located
2050 #
2051 # Revision 1.157.2.40  2006/12/20 16:45:59  tipaul
2052 # ZEBRA update :
2053 # - adding a new table : when a biblio is added/modified/ deleted, an entry is entered in this table
2054 # - the zebraqueue_start.pl script read it & does the stuff.
2055 #
2056 # code coming from head (tumer). it can be run every minut instead of once every day for dev_week code.
2057 #
2058 # I just have commented the previous code (=real time update) in Biblio.pm, we will be able to reactivate it once indexdata fixes zebra update bug !
2059 #
2060 # Revision 1.157.2.39  2006/12/20 11:42:17  toins
2061 # adding table "tags"
2062 #
2063 # Revision 1.157.2.38  2006/12/19 12:06:53  alaurin
2064 # adding a new system preference : RequestOnOpac ;
2065 #
2066 # adding update database
2067 #
2068 # Revision 1.157.2.37  2006/12/19 10:49:21  toins
2069 # fix a minor bug in syspref "expandedSearchOption" and adding it on updatedatabase.
2070 #
2071 # Revision 1.157.2.36  2006/12/13 19:48:09  hdl
2072 # Adding claimdate to serials.
2073 # (Needed to record claimdate
2074 #
2075 # Revision 1.157.2.35  2006/12/08 15:36:57  hdl
2076 # Adding issuedate to issues table.
2077 #
2078 # Revision 1.157.2.34  2006/12/07 16:00:41  hdl
2079 # Adding issuedate to table issues.
2080 # Modifying issuedate on issue (Circ2.pm)
2081 # Modifying report issue_avg_stats to take this change into account. (Need TESTING)
2082 #
2083 # Revision 1.157.2.33  2006/12/06 14:12:18  btoumi
2084 # add BorrowersTitles systempreferences to setup borrowers title
2085 #
2086 # Revision 1.157.2.32  2006/12/06 13:49:41  toins
2087 # deleting additionalauthors, bibliosubjects, bibliosubtitles.
2088 #
2089 # Revision 1.157.2.31  2006/12/05 15:07:16  tipaul
2090 # NEW FEATURE :
2091 # a column (itemtypes.summary) has been added.
2092 #
2093 # If it is empty, no changes at all.
2094 # In admin/itemtypes.pl, the librarian can go and define what (and how) the record appeard below the title.
2095 # The summary must be entered exactly as the authority summary.
2096 # An example is provided in admin/itemtypes.pl add/modify itemtype.
2097 #
2098 # This feature had been requested for a while by my librarians. The 2 uses we can imagine are :
2099 # - for websites => show the link directly in the result list, to avoid 1 clic (& there is no need for a size/editor/publicationyear for web sites)
2100 # - for serial publications => show some serial specific informations.
2101 #
2102 # This commit should do everything that is needed for this feature.
2103 #
2104 # Revision 1.157.2.30  2006/11/29 11:58:18  toins
2105 # re indenting with space.
2106 #
2107 # Revision 1.157.2.29  2006/11/24 21:58:35  kados
2108 # changing items.itemtype to items.itype to avoid problems with
2109 # joins with biblioitems. NOTE: I don't think updatedatabase will remove the
2110 # items.itemtype from your db so you must do that manually if you updated
2111 # from an earlier version of updatedatabase.
2112 #
2113 # Revision 1.157.2.28  2006/11/24 13:54:55  hdl
2114 # Adding serialsadditem
2115 #
2116 # Revision 1.157.2.27  2006/11/24 11:07:09  alaurin
2117 # bug sanop #74 :
2118 #
2119 # Add a new system prefence "AutoLocation"
2120 #  this fonction  switching activation or not Autolocation, if Yes, the Librarian can't change his location, it's defined by branchip,
2121 # if autolocation is setting to "NO", librarian can change his settings ....
2122 #
2123 #
2124 # warn, if autolocation is setting "on", on circulation.pl we don't have anymore the choice to change your library and branchprinter,
2125 # defined on branches : branchip and branchprinter ....
2126 #
2127 # this function could be improved
2128 #
2129 # Revision 1.157.2.26  2006/11/23 11:01:06  toins
2130 # branchtransfers.frombranch & branchtransfers.tobranch must be VARCHAR(10)
2131 #
2132 # Revision 1.157.2.25  2006/11/23 09:05:33  tipaul
2133 # reintroducing move to innoDB (as only innoDB supports extended features like foreign keys)
2134 #
2135 # Revision 1.157.2.24  2006/11/21 09:15:23  toins
2136 # better userflag description
2137 #
2138 # Revision 1.157.2.23  2006/11/21 08:51:01  toins
2139 # 2 new userflags: serials & reports.
2140 #
2141 # Revision 1.157.2.22  2006/11/20 16:59:09  toins
2142 # adding a userflags: 'editauthorities'.
2143 #
2144 # Revision 1.157.2.21  2006/11/17 10:53:04  hdl
2145 # Changing
2146 # - subscription detail :
2147 #     adding manual history (in subscription table)
2148 #     addind subscription summary.
2149 # - menu-serials.inc deleting old link
2150 # - adapting serials-collection.pl
2151 #
2152 # TODO List :
2153 # - Some values are hard coded in subscription-detail
2154 # - subscription-detail.pl relies on subscription-add for edition BUT subscription-add transmits back data to subscription-detail in order to save data back into database. This is a bit odd enough and should not occur.
2155 # - Some more tests on numberlength which doesnot seem to be kept.
2156 #
2157 # Revision 1.157.2.20  2006/11/15 15:15:50  hdl
2158 # Final First Version for New Facility for subscription management.
2159 #
2160 # Now
2161 # use serials-collection.pl for history display
2162 # and serials-edit.pl for serial edition
2163 # subscription add and detail adds a new branch information to help IndependantBranches Library to manage different subscriptions for a serial
2164 #
2165 # This is aimed at replacing serials-receive and statecollection.
2166 #
2167 # Revision 1.157.2.19  2006/11/14 16:28:01  rych
2168 # Adding itemtype field to items
2169 #
2170 # Revision 1.157.2.18  2006/11/14 16:16:58  rych
2171 # fix mysql syntax
2172 #
2173 # Revision 1.157.2.17  2006/11/14 14:39:31  toins
2174 # * delete the userflags "reservforself" which is unused.
2175 # * some new function not use at the moment.
2176 #
2177 # Revision 1.157.2.16  2006/11/02 09:27:30  toins
2178 # issue.branchcode must be varchar(10).
2179 #
2180 # Revision 1.157.2.15  2006/10/31 17:41:51  toins
2181 # items.holdingbranch must be varchar(10)
2182 #
2183 # Revision 1.157.2.14  2006/10/30 09:41:45  btoumi
2184 # remove auto increment for accountno in accountlines table
2185 #
2186 # Revision 1.157.2.13  2006/10/20 10:35:05  alaurin
2187 # new program : branchoverdues.pl
2188 #
2189 # with this program, the librararians will can check , and specify the method of notification of documents in overdue
2190 #
2191 # little explanation :
2192 #
2193 #     - At first, the datas come from accountlines, generated by accounlines (type 'FU')
2194 #     - There is three levels of notification (come from overduerules ....)
2195 #     - there is four methods of notification :
2196 #         - letter (for us, use an openoffice program ....)
2197 #         - Mail (use a batch program)
2198 #         - Phone (simple Method, if this method is selected, we consider that the borrower as been notified)
2199 #         - Considered Lost (For us the third level)
2200 #
2201 #     - At this time we have some parameters hardcoded (Must be improve later)
2202 #
2203 #     - the choice of methods is hardcoded :
2204 #         - for the first overduelevel : three methods : mail,letter,phone
2205 #         -For the second overduelevel :only one method : letter
2206 #         - For the Third Overdue level : only one method : Considered Lost
2207 #
2208 #
2209 # this program will be heavy tested next week ....
2210 #
2211 # Revision 1.157.2.12  2006/10/19 09:04:07  toins
2212 # itemtypes.itemtype is a primary key.
2213 #
2214 # Revision 1.157.2.11  2006/10/18 13:31:13  toins
2215 # Borrowers.categorycode must have 10 chars lenght & categories.categorycode must be a primary key.
2216 #
2217 # Revision 1.157.2.10  2006/10/17 16:18:14  hdl
2218 # Changing primary key in aqbookfund.
2219 # Making it branchcode+aqbookfundid rather than simple aqbookfundid.
2220 #
2221 # Revision 1.157.2.9  2006/10/16 14:23:47  toins
2222 # Borrowers.branchcode must be varchar(10) too.
2223 #
2224 # Revision 1.157.2.7  2006/10/11 15:22:23  tipaul
2225 # - adding some missing fields, coming from dev_week :
2226 # * lcsort & ccode in biblioitems table. lcsort is used for loc callnumbers & ccode is used to have a item level circulation rules. Ccode means C<irc>code
2227 # * onloan & issue_date in items table. They are filled by the misc/update_items.pl script, with circulation values. NOW, onloan & issue_date in items are NOT set by circulation, this decision speed up a lot the circulation rate (with the Date::Manip removal). The price for this is to have the status of the item not real time updated in zebra, but that's worth the price.
2228 # * cutterextra in items, that we should ask tumer to understand what it does  ;-)
2229 #
2230 # Revision 1.157.2.6  2006/10/10 11:25:40  btoumi
2231 # add two tables : notifys , charges
2232 # modify accountlines tables add two fields (notify_id and notify_level)
2233 #
2234 # Revision 1.157.2.5  2006/10/02 09:15:44  hdl
2235 # Serials :
2236 # * synching with NZ-devs on Serials.
2237 # * adding routing lists support,
2238 # * adding serialsadditems support
2239 # * adding publisheddate management
2240 #
2241 # TODO :
2242 # Management for seasonal serials should be fixed in order to be language independant.
2243 #
2244 # Revision 1.157.2.4  2006/09/19 07:44:13  btoumi
2245 # bug fix : modify wrong field name BorrowerMandatoryField
2246 #
2247 # Revision 1.157.2.3  2006/09/18 14:00:24  btoumi
2248 # bug fix :wrond field name for opacnote and contactnote
2249 #
2250 # Revision 1.157.2.2  2006/09/11 13:24:03  alaurin
2251 # marcxml should be a longtext, some biblios can be more than 65535 char long
2252 #
2253 # Revision 1.157.2.1  2006/09/04 08:39:14  toins
2254 # sync with rel_2_2.
2255 #
2256 # Revision 1.157  2006/08/11 10:03:13  tipaul
2257 # the new "includes" features, for personalized templates. Look at koha-devel, i'll write a mail here (& something on the wiki)
2258 #
2259 # Revision 1.152  2006/06/27 09:26:37  btoumi
2260 # modify (initials,phone ) fields property in borrowers and deletedborrowers table
2261 #
2262 # Revision 1.151  2006/06/22 10:33:14  btoumi
2263 # sorry i forget deletedborrowers table
2264 # modify firstname field from deletedborrowers table
2265 #
2266 # Revision 1.149  2006/06/20 22:35:47  rangi
2267 # Code to allow the associated borrowers to work
2268 #
2269 # Revision 1.148  2006/06/17 22:12:01  rangi
2270 # Adding id field to reviews table
2271 #
2272 # Revision 1.147  2006/06/17 03:36:41  rangi
2273 # Table definition for the reviews table
2274 #
2275 # Revision 1.146  2006/06/17 03:29:41  rangi
2276 # Variable to allow librarians to switch reviews on or off
2277 #
2278 # Revision 1.145  2006/06/16 09:45:02  btoumi
2279 # updatedatabase.pl: add change of borrowers table to deletedborrowers table
2280 # deletemem.pl: delete use of warn function
2281 #
2282 # Revision 1.144  2006/06/08 15:36:31  alaurin
2283 # Add a new system preference 'AutomaticItemReturn' :
2284 #
2285 # if this prefence is switched on: the document returned in another library than homebranch, the system automaticly transfer the document to his homebranch (with notification for librarian in returns.tmpl) .
2286 #
2287 # switch off : the document stay in the holdingbranch ...
2288 #
2289 # correcting bugs :
2290 # - comment C4::acquisition (not using in request.pl).
2291 # - correcting date in request.pl
2292 # -add the new call of function getbranches in request.pl
2293 #
2294 # Revision 1.143  2006/06/07 02:02:47  bob_lyon
2295 # merging katipo changes...
2296 #
2297 # adding new preference IssuingInProcess
2298 #
2299 # Revision 1.142  2006/06/06 23:42:46  bob_lyon
2300 # Merging Katipo changes...
2301 #
2302 # Adding new system pref where one can still retrieve a correct reading
2303 # record history if one has moved older data from issues to oldissues table
2304 # to speed up issues speed
2305 #
2306 # Revision 1.141  2006/06/01 03:18:11  rangi
2307 # Adding a new column to the statistics table
2308 #
2309 # Revision 1.140  2006/05/22 22:40:45  rangi
2310 # Adding new systempreference allowing for the library to add borrowers to institutions (rest homes, parishes, schools, classes etc).
2311 #
2312 # Revision 1.139  2006/05/19 19:31:29  tgarip1957
2313 # Added new fields to auth_header and auth_subfield_table to allow ZEBRA use of authorities and new MARC framework like structure.
2314 # Authority tables are modified to be compatible with new MARC frameworks. This change is part of Authority Linking & Zebra authorities. Requires change in Mysql database. It will break head unless all changes regarding this is implemented. This warning will take place on all commits regarding this
2315 #
2316 # Revision 1.138  2006/05/19 16:51:44  alaurin
2317 # update database for :
2318 # - new feature ip and printer management
2319 # adding two fields in branches table (branchip,branchprinter)
2320 #
2321 # - waiting date : adding one field in reserves table(waiting date) to calculate the Maximum delay to pick up a reserved document when it's available
2322 #
2323 # new system preference :
2324 # - ReservesMaxPickUpDelay : Maximum delay to pick up a reserved document
2325 # TransfersMaxDaysWarning : Max delay before considering the transfer as potentialy a problem
2326 #
2327 # Revision 1.137  2006/04/18 09:36:36  plg
2328 # bug fixed: typo fixed in labels and labels_conf tables creation query.
2329 #
2330 # Revision 1.136  2006/04/17 21:55:33  sushi
2331 # Added 'labels' and 'labels_conf' tables, for spine lable tool.
2332 #
2333 # Revision 1.135  2006/04/15 02:37:03  tgarip1957
2334 # Marc record should be set to UTF-8 in leader.Force it.
2335 # XML should be with<record> wrappers
2336 #
2337 # Revision 1.134  2006/04/14 09:37:29  tipaul
2338 # improvements from SAN Ouest Provence :
2339 # * introducing a category_type into categories. It can be A (adult), C (children), P (Professionnal), I (institution/organisation).
2340 # * each category_type has it's own forms to create members.
2341 # * the borrowers table has been heavily modified (many fields changed), to get something more logic & readable
2342 # * reintroducing guarantor/guanrantee system that is now independant from hardcoded C/A for categories
2343 # * updating templates to fit template rules
2344 #
2345 # (see mail feb, 17 on koha-devel "new features for borrowers" for more details)
2346 #
2347 # Revision 1.133  2006/04/13 08:36:42  plg
2348 # new: function C4::Date::get_date_format_string_for_DHTMLcalendar based on
2349 # the system preference prefered date format.
2350 #
2351 # improvement: book fund list and budget list screen redesigned. Filters on
2352 # each field. Columns are not sortable yet. Using DHTML Calendar to fill date
2353 # fields instead of manual filling. Pagination system. From the book fund
2354 # list, you can reach the budget list, filtered on a book fund, or not. A
2355 # budget can be added only from book fund list screen.
2356 #
2357 # bug fixed: branchcode was missing in table aqbudget.
2358 #
2359 # bug fixed: when setting a branchcode to a book fund, all associated budgets
2360 # move to this branchcode.
2361 #
2362 # modification: when adding/modifying budget/fund, MySQL specific "REPLACE..."
2363 # statements replaced by standard SQL compliant statement.
2364 #
2365 # bug fixed: when adding/modifying a budget, if the book fund is associated to
2366 # a branch, the branch selection is disabled and set to the book fund branch.
2367 #
2368 # Revision 1.132  2006/04/06 12:37:05  hdl
2369 # Bugfixing : aqbookfund needed a field.
2370 #
2371 # Revision 1.131  2006/03/03 17:02:22  tipaul
2372 # commit for holidays and news management.
2373 # (some forgotten files)
2374 #
2375 # Revision 1.130  2006/03/03 16:35:21  tipaul
2376 # commit for holidays and news management.
2377 #
2378 # Contrib from Tmer Garip (from Turkey) :
2379 # * holiday :
2380 # in /tools/ the holiday.pl script let you define holidays (days where the library is closed), branch by branch. You can define 3 types of holidays :
2381 # - single day : only this day is closed
2382 # - repet weekly (like "sunday") : the day is holiday every week
2383 # - repet yearly (like "July, 4") : this day is closed every year.
2384 #
2385 # You can also put exception :
2386 # - sunday is holiday, but "2006 March, 5th" the library will be open
2387 #
2388 # The holidays are used for return date calculation : the return date is set to the next date where the library is open. A systempreference (useDaysMode) set ON (Calendar) or OFF (Normal) the calendar calculation.
2389 #
2390 # Revision 1.129  2006/02/27 18:19:33  hdl
2391 # New table used in overduerules.pl tools page.
2392 #
2393 # Revision 1.128  2006/01/25 15:16:06  tipaul
2394 # updating DB :
2395 # * removing useless tables
2396 # * adding useful indexes
2397 # * altering some columns definitions
2398 # * The goal being to have updater working fine for foreign keys.
2399 #
2400 # For me it's done, let me know if it works for you. You can see an updated schema of the DB (with constraints) on the wiki
2401 #
2402 # Revision 1.127  2006/01/24 17:57:17  tipaul
2403 # DB improvements : adding foreign keys on some tables. partial stuff done.
2404 #
2405 # Revision 1.126  2006/01/06 16:39:42  tipaul
2406 # synch'ing head and rel_2_2 (from 2.2.5, including npl templates)
2407 # Seems not to break too many things, but i'm probably wrong here.
2408 # at least, new features/bugfixes from 2.2.5 are here (tested on some features on my head local copy)
2409 #
2410 # - removing useless directories (koha-html and koha-plucene)
2411 #
2412 # Revision 1.125  2006/01/04 15:54:55  tipaul
2413 # utf8 is a : go for beta test in HEAD.
2414 # some explanations :
2415 # - updater/updatedatabase => will transform all tables in innoDB (not related to utf8, just to warn you) AND collate them in utf8 / utf8_general_ci. The SQL command is : ALTER TABLE tablename DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci.
2416 # - *-top.inc will show the pages in utf8
2417 # - THE HARD THING : for me, mysql-client and mysql-server were set up to communicate in iso8859-1, whatever the mysql collation ! Thus, pages were improperly shown, as datas were transmitted in iso8859-1 format ! After a full day of investigation, someone on usenet pointed "set NAMES 'utf8'" to explain that I wanted utf8. I could put this in my.cnf, but if I do that, ALL databases will "speak" in utf8, that's not what we want. Thus, I added a line in Context.pm : everytime a DB handle is opened, the communication is set to utf8.
2418 # - using marcxml field and no more the iso2709 raw marc biblioitems.marc field.
2419 #
2420 # Revision 1.124  2005/10/27 12:09:05  tipaul
2421 # new features for serial module :
2422 # - the last 5 issues are now shown, and their status can be changed (but not reverted to "waited", as there can be only one "waited")
2423 # - the library can create a "distribution list". this paper contains a list of borrowers (selected from the borrower list, or manually entered), and print it for a given issue. once printed, the sheet can be put on the issue and distributed to every reader on the list (one by one).
2424 #
2425 # Revision 1.123  2005/10/26 09:13:37  tipaul
2426 # big commit, still breaking things...
2427 #
2428 # * synch with rel_2_2. Probably the last non manual synch, as rel_2_2 should not be modified deeply.
2429 # * code cleaning (cleaning warnings from perl -w) continued
2430 #
2431 # Revision 1.122  2005/09/02 14:18:38  tipaul
2432 # new feature : image for itemtypes.
2433 #
2434 # * run updater/updatedatabase to create imageurl field in itemtypes.
2435 # * go to Koha >> parameters >> itemtypes >> modify (or add) an itemtype. You will see around 20 nice images to choose between (thanks to owen). If you prefer your own image, you also can type a complete url (http://www.myserver.lib/path/to/my/image.gif)
2436 # * go to OPAC, and search something. In the result list, you now have the picture instead of the text itemtype.
2437 #
2438 # Revision 1.121  2005/08/24 08:49:03  hdl
2439 # Adding a note field in serial table.
2440 # This will allow librarian to mention a note on a peculiar waiting serial number.
2441 #
2442 # Revision 1.120  2005/08/09 14:10:32  tipaul
2443 # 1st commit to go to zebra.
2444 # don't update your cvs if you want to have a working head...
2445 #
2446 # this commit contains :
2447 # * updater/updatedatabase : get rid with marc_* tables, but DON'T remove them. As a lot of things uses them, it would not be a good idea for instance to drop them. If you really want to play, you can rename them to test head without them but being still able to reintroduce them...
2448 # * Biblio.pm : modify MARCgetbiblio to find the raw marc record in biblioitems.marc field, not from marc_subfield_table, modify MARCfindframeworkcode to find frameworkcode in biblio.frameworkcode, modify some other subs to use biblio.biblionumber & get rid of bibid.
2449 # * other files : get rid of bibid and use biblionumber instead.
2450 #
2451 # What is broken :
2452 # * does not do anything on zebra yet.
2453 # * if you rename marc_subfield_table, you can't search anymore.
2454 # * you can view a biblio & bibliodetails, go to MARC editor, but NOT save any modif.
2455 # * don't try to add a biblio, it would add data poorly... (don't try to delete either, it may work, but that would be a surprise ;-) )
2456 #
2457 # IMPORTANT NOTE : you need MARC::XML package (http://search.cpan.org/~esummers/MARC-XML-0.7/lib/MARC/File/XML.pm), that requires a recent version of MARC::Record
2458 # Updatedatabase stores the iso2709 data in biblioitems.marc field & an xml version in biblioitems.marcxml Not sure we will keep it when releasing the stable version, but I think it's a good idea to have something readable in sql, at least for development stage.
2459 #
2460 # Revision 1.119  2005/08/04 16:07:58  tipaul
2461 # Synch really broke this script...
2462 #
2463 # Revision 1.118  2005/08/04 16:02:55  tipaul
2464 # oops... error in synch between 2.2 and head
2465 #
2466 # Revision 1.117  2005/08/04 14:24:39  tipaul
2467 # synch'ing 2.2 and head
2468 #
2469 # Revision 1.116  2005/08/04 08:55:54  tipaul
2470 # Letters / alert system, continuing...
2471 #
2472 # * adding a package Letters.pm, that manages Letters & alerts.
2473 # * adding feature : it's now possible to define a "letter" for any subscription created. If a letter is defined, users in OPAC can put an alert on the subscription. When an issue is marked "arrived", all users in the alert will recieve a mail (as defined in the "letter"). This last part (= send the mail) is not yet developped. (Should be done this week)
2474 # * adding feature : it's now possible to "put to an alert" in OPAC, for any serial subscription. The alert is stored in a new table, called alert. An alert can be put only if the librarian has activated them in subscription (and they activate it just by choosing a "letter" to sent to borrowers on new issues)
2475 # * adding feature : librarian can see in borrower detail which alerts they have put, and a user can see in opac-detail which alert they have put too.
2476 #
2477 # Note that the system should be generic enough to manage any type of alert.
2478 # I plan to extend it soon to virtual shelves : a borrower will be able to put an alert on a virtual shelf, to be warned when something is changed in the virtual shelf (mail being sent once a day by cron, or manually by the shelf owner. Anyway, a mail won't be sent on every change, users would be spammed by Koha ;-) )
2479 #
2480 # Revision 1.115  2005/08/02 16:15:34  tipaul
2481 # adding 2 fields to letter system :
2482 # * module (acquisition, catalogue...) : it will be usefull to show the librarian only letters he may be interested by.
2483 # * title, that will be used as mail subject.
2484 #
2485 # Revision 1.114  2005/07/28 15:10:13  tipaul
2486 # Introducing new "Letters" system : Letters will be used everytime you want to sent something to someone (through mail or paper). For example, sending a mail for overdues use letter that you can put as parameters. Sending a mail to a borrower when a suggestion is validated uses a letter too.
2487 # the letter table contains 3 fields :
2488 # * code => the code of the letter
2489 # * name => the complete name of the letter
2490 # * content => the complete text. It's a TEXT field type, so has no limits.
2491 #
2492 # My next goal now is to work on point 2-I "serial issue alert"
2493 # With this feature, in serials, a user can subscribe the "issue alert". For every issue arrived/missing, a mail is sent to all subscribers of this list. The mail warns the user that the issue is arrive or missing. Will be in head.
2494 # (see mail on koha-devel, 2005/04/07)
2495 #
2496 # The "serial issue alert" will be the 1st to use this letter system that probably needs some tweaking ;-)
2497 #
2498 # Once it will be stabilised default letters (in any languages) could be added during installer to help the library begin with this new feature.
2499 #
2500 # Revision 1.113  2005/07/28 08:38:41  tipaul
2501 # For instance, the return date does not rely on the borrower expiration date. A systempref will be added in Koha, to modify return date calculation schema :
2502 # * ReturnBeforeExpiry = yes => return date can't be after expiry date
2503 # * ReturnBeforeExpiry = no  => return date can be after expiry date
2504 #
2505 # Revision 1.112  2005/07/26 08:19:47  hdl
2506 # Adding IndependantBranches System preference variable in order to manage Branch independancy.
2507 #
2508 # Revision 1.111  2005/07/25 15:35:38  tipaul
2509 # we have decided that moving to Koha 3.0 requires being already in Koha 2.2.x
2510 # So, the updatedatabase script can highly be cleaned (90% removed).
2511 # Let's play with the new Koha DB structure now ;-)
2512 #