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 <https://www.gnu.org/licenses>.
16
# along with Koha; if not, see <https://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 229-235 use C4::Biblio qw( GetMarcFromKohaField ); Link Here
229
    }
310
    }
230
}
311
}
231
312
232
{
313
sub CheckItemsFramework {
233
    my @framework_codes = Koha::BiblioFrameworks->search()->get_column('frameworkcode');
314
    my @framework_codes = Koha::BiblioFrameworks->search()->get_column('frameworkcode');
234
    push @framework_codes, "";    # The default is not stored in frameworks, we need to force it
315
    push @framework_codes, "";    # The default is not stored in frameworks, we need to force it
235
316
Lines 294-305 use C4::Biblio qw( GetMarcFromKohaField ); Link Here
294
                          $table eq 'items'  ? $i->$column
375
                          $table eq 'items'  ? $i->$column
295
                        : $table eq 'biblio' ? $i->biblio->$column
376
                        : $table eq 'biblio' ? $i->biblio->$column
296
                        :                      $i->biblioitem->$column;
377
                        :                      $i->biblioitem->$column;
297
                    $output .= " {" . $i->itemnumber . " => " . $value . "}\n";
378
                    $output .= " {" . $i->itemnumber . " and " . $i->biblioitem->biblionumber . " => " . $value . "}\n";
298
                }
379
                }
299
                new_item(
380
                new_item(
300
                    sprintf(
381
                    sprintf(
301
                              "The Framework *%s* is using the authorised value's category *%s*, "
382
                              "The Framework *%s* is using the authorised value's category *%s*, "
302
                            . "but the following %s do not have a value defined ({itemnumber => value }):\n%s",
383
                            . "but the following %s do not have a value defined ({itemnumber and biblionumber => value }):\n%s",
303
                        $frameworkcode, $av_category, $kohafield, $output
384
                        $frameworkcode, $av_category, $kohafield, $output
304
                    )
385
                    )
305
                );
386
                );
Lines 308-314 use C4::Biblio qw( GetMarcFromKohaField ); Link Here
308
    }
389
    }
309
}
390
}
310
391
311
{
392
sub CheckItemsTitle {
312
    my $biblios = Koha::Biblios->search(
393
    my $biblios = Koha::Biblios->search(
313
        {
394
        {
314
            -or => [
395
            -or => [
Lines 329-335 use C4::Biblio qw( GetMarcFromKohaField ); Link Here
329
    }
410
    }
330
}
411
}
331
412
332
{
413
sub CheckAgeForCategory {
333
    my $aging_patrons = Koha::Patrons->search(
414
    my $aging_patrons = Koha::Patrons->search(
334
        {
415
        {
335
            -not => {
416
            -not => {
Lines 502-527 sub new_hint { Link Here
502
    say "=> $name";
583
    say "=> $name";
503
}
584
}
504
585
505
=head1 NAME
506
507
search_for_data_inconsistencies.pl
508
509
=head1 SYNOPSIS
586
=head1 SYNOPSIS
510
587
511
    perl search_for_data_inconsistencies.pl
588
search_for_data_inconsistencies.pl [options]
512
589
513
=head1 DESCRIPTION
590
=head2 DESCRIPTION
514
591
515
Catch data inconsistencies in Koha database
592
Catch data inconsistencies in Koha database:
516
593
517
* Items with undefined homebranch and/or holdingbranch
594
    * Items with undefined homebranch and/or holdingbranch
518
* Authorities with undefined authtypecodes/authority types
595
    * Authorities with undefined authtypecodes/authority types
519
* Item types:
596
    * Item types:
520
  * if item types are defined at item level (item-level_itypes=specific item),
597
        * if item types are defined at item level (item-level_itypes=specific item),
521
    then items.itype must be set else biblioitems.itemtype must be set
598
        then items.itype must be set else biblioitems.itemtype must be set
522
  * Item types defined in items or biblioitems must be defined in the itemtypes table
599
        * Item types defined in items or biblioitems must be defined in the itemtypes table
523
* Invalid MARCXML in bibliographic records
600
    * Bibliographic records without a title
524
* Patrons with invalid category types due to lower and upper age limits
601
    * Invalid MARCXML in bibliographic records
602
    * Patrons with invalid category types due to lower and upper age limits
525
* Any date fields in the database (timestamp, datetime, date) set to 0000-00-00
603
* Any date fields in the database (timestamp, datetime, date) set to 0000-00-00
526
604
605
=head2 OPTIONS
606
607
  --check-branch      Check for items without home or holding library
608
  --check-auth        Check for authority records with invalid authority type
609
  --check-status      Check for bibliographic records and items without an item type or with an invalid item type
610
  --check-framework   Check for invalid values in fields where the framework limits to an authorized value category
611
  --check-title       Check for bibliographic records without a title
612
  --check-age         Check for patrons with invalid age for category
613
  --skip-branch       Skip checking for items without home or holding library
614
  --skip-auth         Skip checking for authority records with invalid authority type
615
  --skip-status       Skip checking for bibliographic records and items without an item type or with an invalid item type
616
  --skip-framework    Skip checking for invalid values in fields where the framework limits to an authorized value category
617
  --skip-title        Skip checking for bibliographic records without a title
618
  --skip-age          Skip checking for patrons with invalid age for category
619
  --help              Print usage information
620
621
Note: If no options are provided, all tests will be run.
622
527
=cut
623
=cut
528
- 

Return to bug 36027