View | Details | Raw Unified | Return to bug 36027
Collapse All | Expand All

(-)a/misc/maintenance/search_for_data_inconsistencies.pl (-34 / +129 lines)
Lines 16-22 Link Here
16
# along with Koha; if not, see <http://www.gnu.org/licenses>.
16
# along with Koha; if not, see <http://www.gnu.org/licenses>.
17
17
18
use Modern::Perl;
18
use Modern::Perl;
19
20
use Koha::Script;
19
use Koha::Script;
21
use Koha::AuthorisedValues;
20
use Koha::AuthorisedValues;
22
use Koha::Authorities;
21
use Koha::Authorities;
Lines 27-52 use Koha::Items; Link Here
27
use Koha::ItemTypes;
26
use Koha::ItemTypes;
28
use Koha::Patrons;
27
use Koha::Patrons;
29
use C4::Biblio qw( GetMarcFromKohaField );
28
use C4::Biblio qw( GetMarcFromKohaField );
29
use Getopt::Long;
30
use Pod::Usage;
31
32
our %options = (
33
    'check-branch'    => 0,
34
    'check-auth'      => 0,
35
    'check-status'    => 0,
36
    'check-framework' => 0,
37
    'check-title'     => 0,
38
    'check-age'       => 0,
39
    'skip-branch'     => 1,
40
    'skip-auth'       => 1,
41
    'skip-status'     => 1,
42
    'skip-framework'  => 1,
43
    'skip-title'      => 1,
44
    'skip-age'        => 1,
45
    'help'            => 0,
46
);
47
48
# Parse command line options
49
GetOptions(
50
    'check-branch'    => sub { $options{'check-branch'}    = 1 },
51
    'check-auth'      => sub { $options{'check-auth'}      = 1 },
52
    'check-status'    => sub { $options{'check-status'}    = 1 },
53
    'check-framework' => sub { $options{'check-framework'} = 1 },
54
    'check-title'     => sub { $options{'check-title'}     = 1 },
55
    'check-age'       => sub { $options{'check-age'}       = 1 },
56
    'skip-branch'     => sub { $options{'skip-branch'}     = 0 },
57
    'skip-auth'       => sub { $options{'skip-auth'}       = 0 },
58
    'skip-status'     => sub { $options{'skip-status'}     = 0 },
59
    'skip-framework'  => sub { $options{'skip-framework'}  = 0 },
60
    'skip-title'      => sub { $options{'skip-title'}      = 0 },
61
    'skip-age'        => sub { $options{'skip-age'}        = 0 },
62
    'help'            => sub { $options{'help'}            = 1; },
63
) or pod2usage(2);    # Print usage if invalid options are provided
64
65
# Call handle_invalid_options subroutine if invalid options are provided
66
Getopt::Long::Configure('pass_through');
67
GetOptions(
68
    '<>' => sub {
69
        my ($opt_name) = @_;
70
        handle_invalid_options($opt_name);
71
    }
72
);
73
74
# Print usage and exit if --help flag is provided
75
pod2usage(1) if $options{'help'};
76
77
# Run tests based on options
78
set_skip_options();
79
CheckItemsBranch()     if $options{'check-branch'}    || $options{'skip-branch'};
80
CheckItemsAuthHeader() if $options{'check-auth'}      || $options{'skip-auth'};
81
CheckItemsStatus()     if $options{'check-status'}    || $options{'skip-status'};
82
CheckItemsFramework()  if $options{'check-framework'} || $options{'skip-framework'};
83
CheckItemsTitle()      if $options{'check-title'}     || $options{'skip-title'};
84
CheckAgeForCategory()  if $options{'check-age'}       || $options{'skip-age'};
85
86
# Handle invalid options
87
sub handle_invalid_options {
88
    my ($opt_name) = @_;
89
    print "Invalid option provided: $opt_name\n";
90
    pod2usage(2);
91
}
30
92
31
{
93
# Set skip options based on check options
94
sub set_skip_options {
95
    my $has_check_option = grep { $options{$_} == 1 } grep { /^check-/ } keys %options;
96
    if ($has_check_option) {
97
        foreach my $key ( keys %options ) {
98
            $options{$key} = 0 if $key =~ /^skip-/;
99
        }
100
    }
101
    return %options;
102
}
103
104
sub CheckItemsBranch {
32
    my $items = Koha::Items->search( { -or => { homebranch => undef, holdingbranch => undef } } );
105
    my $items = Koha::Items->search( { -or => { homebranch => undef, holdingbranch => undef } } );
33
    if ( $items->count ) { new_section("Not defined items.homebranch and/or items.holdingbranch") }
106
    if ( $items->count ) { new_section("Not defined items.homebranch and/or items.holdingbranch") }
34
    while ( my $item = $items->next ) {
107
    while ( my $item = $items->next ) {
35
        if ( not $item->homebranch and not $item->holdingbranch ) {
108
        if ( not $item->homebranch and not $item->holdingbranch ) {
36
            new_item(
109
            new_item(
37
                sprintf "Item with itemnumber=%s does not have homebranch and holdingbranch defined",
110
                sprintf
38
                $item->itemnumber
111
                    "Item with itemnumber=%s and biblionumber=%s does not have homebranch and holdingbranch defined",
112
                $item->itemnumber, $item->biblionumber
39
            );
113
            );
40
        } elsif ( not $item->homebranch ) {
114
        } elsif ( not $item->homebranch ) {
41
            new_item( sprintf "Item with itemnumber=%s does not have homebranch defined", $item->itemnumber );
115
            new_item(
116
                sprintf "Item with itemnumber=%s and biblionumber=%s does not have homebranch defined",
117
                $item->itemnumber, $item->biblionumber
118
            );
42
        } else {
119
        } else {
43
            new_item( sprintf "Item with itemnumber=%s does not have holdingbranch defined", $item->itemnumber );
120
            new_item(
121
                sprintf "Item with itemnumber=%s and biblionumber=%s does not have holdingbranch defined",
122
                $item->itemnumber, $item->biblionumber
123
            );
44
        }
124
        }
45
    }
125
    }
46
    if ( $items->count ) { new_hint("Edit these items and set valid homebranch and/or holdingbranch") }
126
    if ( $items->count ) { new_hint("Edit these items and set valid homebranch and/or holdingbranch") }
47
}
127
}
48
128
49
{
129
sub CheckItemsAuthHeader {
130
50
    # No join possible, FK is missing at DB level
131
    # No join possible, FK is missing at DB level
51
    my @auth_types  = Koha::Authority::Types->search->get_column('authtypecode');
132
    my @auth_types  = Koha::Authority::Types->search->get_column('authtypecode');
52
    my $authorities = Koha::Authorities->search( { authtypecode => { 'not in' => \@auth_types } } );
133
    my $authorities = Koha::Authorities->search( { authtypecode => { 'not in' => \@auth_types } } );
Lines 57-66 use C4::Biblio qw( GetMarcFromKohaField ); Link Here
57
            $authority->authtypecode
138
            $authority->authtypecode
58
        );
139
        );
59
    }
140
    }
60
    if ( $authorities->count ) { new_hint("Go to 'Home › Administration › Authority types' to define them") }
141
    if ( $authorities->count ) { new_hint("Go to 'Home -> Administration -> Authority types' to define them") }
61
}
142
}
62
143
63
{
144
sub CheckItemsStatus {
64
    if ( C4::Context->preference('item-level_itypes') ) {
145
    if ( C4::Context->preference('item-level_itypes') ) {
65
        my $items_without_itype = Koha::Items->search( { -or => [ itype => undef, itype => '' ] } );
146
        my $items_without_itype = Koha::Items->search( { -or => [ itype => undef, itype => '' ] } );
66
        if ( $items_without_itype->count ) {
147
        if ( $items_without_itype->count ) {
Lines 69-82 use C4::Biblio qw( GetMarcFromKohaField ); Link Here
69
                if ( defined $item->biblioitem->itemtype && $item->biblioitem->itemtype ne '' ) {
150
                if ( defined $item->biblioitem->itemtype && $item->biblioitem->itemtype ne '' ) {
70
                    new_item(
151
                    new_item(
71
                        sprintf
152
                        sprintf
72
                            "Item with itemnumber=%s does not have a itype value, biblio's item type will be used (%s)",
153
                            "Item with itemnumber=%s and biblionumber = %s does not have a itype value, biblio's item type will be used (%s)",
73
                        $item->itemnumber, $item->biblioitem->itemtype
154
                        $item->itemnumber, $item->biblioitem->biblionumber, $item->biblioitem->itemtype
74
                    );
155
                    );
75
                } else {
156
                } else {
76
                    new_item(
157
                    new_item(
77
                        sprintf
158
                        sprintf
78
                            "Item with itemnumber=%s does not have a itype value, additionally no item type defined for biblionumber=%s",
159
                            "Item with itemnumber=%s and biblionumber = %s does not have a itype value, additionally no item type defined for biblionumber=%s",
79
                        $item->itemnumber, $item->biblioitem->biblionumber
160
                        $item->itemnumber, $item->biblioitem->biblionumber, $item->biblioitem->biblionumber
80
                    );
161
                    );
81
                }
162
                }
82
            }
163
            }
Lines 203-209 use C4::Biblio qw( GetMarcFromKohaField ); Link Here
203
    }
284
    }
204
}
285
}
205
286
206
{
287
sub CheckItemsFramework {
207
    my @framework_codes = Koha::BiblioFrameworks->search()->get_column('frameworkcode');
288
    my @framework_codes = Koha::BiblioFrameworks->search()->get_column('frameworkcode');
208
    push @framework_codes, "";    # The default is not stored in frameworks, we need to force it
289
    push @framework_codes, "";    # The default is not stored in frameworks, we need to force it
209
290
Lines 268-279 use C4::Biblio qw( GetMarcFromKohaField ); Link Here
268
                          $table eq 'items'  ? $i->$column
349
                          $table eq 'items'  ? $i->$column
269
                        : $table eq 'biblio' ? $i->biblio->$column
350
                        : $table eq 'biblio' ? $i->biblio->$column
270
                        :                      $i->biblioitem->$column;
351
                        :                      $i->biblioitem->$column;
271
                    $output .= " {" . $i->itemnumber . " => " . $value . "}\n";
352
                    $output .= " {" . $i->itemnumber . " and " . $i->biblioitem->biblionumber . " => " . $value . "}\n";
272
                }
353
                }
273
                new_item(
354
                new_item(
274
                    sprintf(
355
                    sprintf(
275
                              "The Framework *%s* is using the authorised value's category *%s*, "
356
                              "The Framework *%s* is using the authorised value's category *%s*, "
276
                            . "but the following %s do not have a value defined ({itemnumber => value }):\n%s",
357
                            . "but the following %s do not have a value defined ({itemnumber and biblionumber => value }):\n%s",
277
                        $frameworkcode, $av_category, $kohafield, $output
358
                        $frameworkcode, $av_category, $kohafield, $output
278
                    )
359
                    )
279
                );
360
                );
Lines 282-288 use C4::Biblio qw( GetMarcFromKohaField ); Link Here
282
    }
363
    }
283
}
364
}
284
365
285
{
366
sub CheckItemsTitle {
286
    my $biblios = Koha::Biblios->search(
367
    my $biblios = Koha::Biblios->search(
287
        {
368
        {
288
            -or => [
369
            -or => [
Lines 303-309 use C4::Biblio qw( GetMarcFromKohaField ); Link Here
303
    }
384
    }
304
}
385
}
305
386
306
{
387
sub CheckAgeForCategory {
307
    my $aging_patrons = Koha::Patrons->search(
388
    my $aging_patrons = Koha::Patrons->search(
308
        {
389
        {
309
            -not => {
390
            -not => {
Lines 476-501 sub new_hint { Link Here
476
    say "=> $name";
557
    say "=> $name";
477
}
558
}
478
559
479
=head1 NAME
480
481
search_for_data_inconsistencies.pl
482
483
=head1 SYNOPSIS
560
=head1 SYNOPSIS
484
561
485
    perl search_for_data_inconsistencies.pl
562
search_for_data_inconsistencies.pl [options]
486
563
487
=head1 DESCRIPTION
564
=head2 DESCRIPTION
488
565
489
Catch data inconsistencies in Koha database
566
Catch data inconsistencies in Koha database:
490
567
491
* Items with undefined homebranch and/or holdingbranch
568
    * Items with undefined homebranch and/or holdingbranch
492
* Authorities with undefined authtypecodes/authority types
569
    * Authorities with undefined authtypecodes/authority types
493
* Item types:
570
    * Item types:
494
  * if item types are defined at item level (item-level_itypes=specific item),
571
        * if item types are defined at item level (item-level_itypes=specific item),
495
    then items.itype must be set else biblioitems.itemtype must be set
572
        then items.itype must be set else biblioitems.itemtype must be set
496
  * Item types defined in items or biblioitems must be defined in the itemtypes table
573
        * Item types defined in items or biblioitems must be defined in the itemtypes table
497
* Invalid MARCXML in bibliographic records
574
    * Bibliographic records without a title
498
* Patrons with invalid category types due to lower and upper age limits
575
    * Invalid MARCXML in bibliographic records
576
    * Patrons with invalid category types due to lower and upper age limits
499
* Any date fields in the database (timestamp, datetime, date) set to 0000-00-00
577
* Any date fields in the database (timestamp, datetime, date) set to 0000-00-00
500
578
579
=head2 OPTIONS
580
581
  --check-branch      Check for items without home or holding library
582
  --check-auth        Check for authority records with invalid authority type
583
  --check-status      Check for bibliographic records and items without an item type or with an invalid item type
584
  --check-framework   Check for invalid values in fields where the framework limits to an authorized value category
585
  --check-title       Check for bibliographic records without a title
586
  --check-age         Check for patrons with invalid age for category
587
  --skip-branch       Skip checking for items without home or holding library
588
  --skip-auth         Skip checking for authority records with invalid authority type
589
  --skip-status       Skip checking for bibliographic records and items without an item type or with an invalid item type
590
  --skip-framework    Skip checking for invalid values in fields where the framework limits to an authorized value category
591
  --skip-title        Skip checking for bibliographic records without a title
592
  --skip-age          Skip checking for patrons with invalid age for category
593
  --help              Print usage information
594
595
Note: If no options are provided, all tests will be run.
596
501
=cut
597
=cut
502
- 

Return to bug 36027