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

(-)a/misc/maintenance/search_for_data_inconsistencies.pl (-259 / +354 lines)
Lines 29-85 use Koha::Patrons; Link Here
29
use C4::Biblio qw( GetMarcFromKohaField );
29
use C4::Biblio qw( GetMarcFromKohaField );
30
use Data::Dumper;
30
use Data::Dumper;
31
31
32
32
# %methods hash defines options and their corresponding subroutines
33
# Each numeric key represents an option, and its associated subroutine reference defines the action to be taken
34
# Option 1: CheckItemsBranch - Check for items without home or holding library
35
# Option 2: CheckItemsAuthHeader - Check for authority records with invalid authority type
36
# Option 3: CheckItemsStatus - Check for bibliographic records and items without an item type or with an invalid item type
37
# Option 4: CheckItemsFramework - Check for invalid values in fields where the framework limits to an authorized value category
38
# Option 5: CheckItemsTitle - Check for bibliographic records without a title
39
# Option 6: CheckRelationshipsLoop - Check for relationships or dependencies between borrowers in a loop
33
40
34
my %methods = (
41
my %methods = (
35
    1 => \&CheckItemsBranch,
42
1 => \&CheckItemsBranch,
36
    2 => \&CheckItemsAuthHeader,
43
2 => \&CheckItemsAuthHeader,
37
    3 => \&CheckItemsStatus,
44
3 => \&CheckItemsStatus,
38
    4 => \&CheckItemsFramework,
45
4 => \&CheckItemsFramework,
39
    5 => \&CheckItemsTitle
46
5 => \&CheckItemsTitle,
47
6 => \&CheckRelationshipsLoop,
40
);
48
);
41
49
42
say "
50
my @methods_to_run;
43
    1 : Check for items without home or holding library
51
44
    2 : Check for authority records with invalid authority type
52
if (@ARGV == 0) {
45
    3 : Check for bibliographic records and items without an item type or with an invalid item type
53
    # If no arguments are provided, add all methods to the list
46
    4 : Check for invalid values in fields where the framework limits to an authorized value category
54
    @methods_to_run = keys %methods;
47
    5 : Check for bibliographic records without a title";
55
} else {
48
56
    foreach my $arg (@ARGV) {
49
57
        if ($arg =~ /^(\d+)$/) {
50
print "Choose method(s) to run (1-5, separated by spaces): ";
58
            my $method_number = $1;
51
my $choices_str = <STDIN>;
59
            if ($method_number >= 1 && $method_number <= 6) {
52
chomp($choices_str);
60
                push @methods_to_run, $method_number;
53
61
            } else {
54
# Split the input string into an array of choices
62
                print "Invalid method number: $method_number\n";
55
my @choices = split(/\s+/, $choices_str);
63
            }
56
64
        } else {
65
            print "Invalid argument: $arg\n";
66
        }
67
    }
68
}
57
69
58
# Run selected methods
70
foreach my $choice (@methods_to_run) {
59
foreach my $choice (@choices) {
71
    if ($choice =~ /^\d+$/ && $choice >= 1 && $choice <= 6) {
60
    if ($choice =~ /^\d+$/ && $choice >= 1 && $choice <= 5) {
61
        if (exists $methods{$choice}) {
72
        if (exists $methods{$choice}) {
73
            print "\n";
62
            $methods{$choice}->();
74
            $methods{$choice}->();
63
        } else {
75
            } else {
64
            print "Method $choice not found\n";
76
            print "Method $choice not found\n";
65
        }
77
        }
66
    } else {
78
        } else {
67
        print "Invalid choice: $choice\n";
79
        print "Invalid choice: $choice\n";
68
    }
80
    }
69
}
81
}
70
82
71
72
73
74
sub CheckItemsBranch {
83
sub CheckItemsBranch {
75
    my $items = Koha::Items->search({ -or => { homebranch => undef, holdingbranch => undef }});
84
    my $items = Koha::Items->search({ -or => { homebranch => undef, holdingbranch => undef }});
76
    if ( $items->count ) { new_section("Not defined items.homebranch and/or items.holdingbranch")}
85
    if ( $items->count ) { new_section("Not defined items.homebranch and/or items.holdingbranch")}
77
    while ( my $item = $items->next ) {
86
    while ( my $item = $items->next ) {
78
        if ( not $item->homebranch and not $item->holdingbranch ) {
87
        if ( not $item->homebranch and not $item->holdingbranch ) {
79
            new_item(sprintf "Item with itemnumber=%s does not have homebranch and holdingbranch defined", $item->itemnumber);
88
            new_item(sprintf "Item with itemnumber=%s does not have homebranch and holdingbranch defined", $item->itemnumber);
80
        } elsif ( not $item->homebranch ) {
89
            } elsif ( not $item->homebranch ) {
81
            new_item(sprintf "Item with itemnumber=%s does not have homebranch defined", $item->itemnumber);
90
            new_item(sprintf "Item with itemnumber=%s does not have homebranch defined", $item->itemnumber);
82
        } else {
91
            } else {
83
            new_item(sprintf "Item with itemnumber=%s does not have holdingbranch defined", $item->itemnumber);
92
            new_item(sprintf "Item with itemnumber=%s does not have holdingbranch defined", $item->itemnumber);
84
        }
93
        }
85
    }
94
    }
Lines 94-387 sub CheckItemsAuthHeader { Link Here
94
    while ( my $authority = $authorities->next ) {
103
    while ( my $authority = $authorities->next ) {
95
        new_item(sprintf "Authority with authid=%s does not have a code defined (%s)", $authority->authid, $authority->authtypecode);
104
        new_item(sprintf "Authority with authid=%s does not have a code defined (%s)", $authority->authid, $authority->authtypecode);
96
    }
105
    }
97
    if ( $authorities->count ) {new_hint("Go to 'Home › Administration › Authority types' to define them")}
106
    if ( $authorities->count ) {new_hint("Go to 'Home › Administration › Authority types' to define them")};
98
}
107
    };
99
108
    
100
sub CheckItemsStatus {
109
    sub CheckItemsStatus {
101
    if ( C4::Context->preference('item-level_itypes') ) {
110
        if ( C4::Context->preference('item-level_itypes') ) {
102
        my $items_without_itype = Koha::Items->search( { -or => [itype => undef,itype => ''] } );
111
            my $items_without_itype = Koha::Items->search( { -or => [itype => undef,itype => ''] } );
103
        if ( $items_without_itype->count ) {
112
            if ( $items_without_itype->count ) {
104
            new_section("Items do not have itype defined");
113
                new_section("Items do not have itype defined");
105
            while ( my $item = $items_without_itype->next ) {
114
                while ( my $item = $items_without_itype->next ) {
106
                if (defined $item->biblioitem->itemtype && $item->biblioitem->itemtype ne '' ) {
115
                    if (defined $item->biblioitem->itemtype && $item->biblioitem->itemtype ne '' ) {
107
                    new_item(
116
                        new_item(
108
                        sprintf "Item with itemnumber=%s does not have a itype value, biblio's item type will be used (%s)",
117
                        sprintf "Item with itemnumber=%s does not have a itype value, biblio's item type will be used (%s)",
109
                        $item->itemnumber, $item->biblioitem->itemtype
118
                        $item->itemnumber, $item->biblioitem->itemtype
110
                    );
119
                        );
111
                } else {
120
                        } else {
112
                    new_item(
121
                        new_item(
113
                        sprintf "Item with itemnumber=%s does not have a itype value, additionally no item type defined for biblionumber=%s",
122
                        sprintf "Item with itemnumber=%s does not have a itype value, additionally no item type defined for biblionumber=%s",
114
                        $item->itemnumber, $item->biblioitem->biblionumber
123
                        $item->itemnumber, $item->biblioitem->biblionumber
115
                    );
124
                        );
116
               }
125
                    }
126
                }
127
                new_hint("The system preference item-level_itypes expects item types to be defined at item level");
117
            }
128
            }
118
            new_hint("The system preference item-level_itypes expects item types to be defined at item level");
129
        }else{
119
        }
130
            my $biblioitems_without_itemtype = Koha::Biblioitems->search( { itemtype => undef } );
120
    }
131
            if ( $biblioitems_without_itemtype->count ) {
121
    else {
132
                new_section("Biblioitems do not have itemtype defined");
122
        my $biblioitems_without_itemtype = Koha::Biblioitems->search( { itemtype => undef } );
133
                while ( my $biblioitem = $biblioitems_without_itemtype->next ) {
123
        if ( $biblioitems_without_itemtype->count ) {
134
                    new_item(
124
            new_section("Biblioitems do not have itemtype defined");
125
            while ( my $biblioitem = $biblioitems_without_itemtype->next ) {
126
                new_item(
127
                    sprintf "Biblioitem with biblioitemnumber=%s does not have a itemtype value",
135
                    sprintf "Biblioitem with biblioitemnumber=%s does not have a itemtype value",
128
                    $biblioitem->biblioitemnumber
136
                    $biblioitem->biblioitemnumber
129
                );
137
                    );
138
                }
139
                new_hint("The system preference item-level_itypes expects item types to be defined at biblio level");
130
            }
140
            }
131
            new_hint("The system preference item-level_itypes expects item types to be defined at biblio level");
132
        }
141
        }
133
    }
142
        
134
143
        my @itemtypes = Koha::ItemTypes->search->get_column('itemtype');
135
    my @itemtypes = Koha::ItemTypes->search->get_column('itemtype');
144
        if ( C4::Context->preference('item-level_itypes') ) {
136
    if ( C4::Context->preference('item-level_itypes') ) {
145
            my $items_with_invalid_itype = Koha::Items->search( { -and => [itype => { not_in => \@itemtypes }, itype => { '!=' => '' }] } );
137
        my $items_with_invalid_itype = Koha::Items->search( { -and => [itype => { not_in => \@itemtypes }, itype => { '!=' => '' }] } );
146
            if ( $items_with_invalid_itype->count ) {
138
        if ( $items_with_invalid_itype->count ) {
147
                new_section("Items have invalid itype defined");
139
            new_section("Items have invalid itype defined");
148
                while ( my $item = $items_with_invalid_itype->next ) {
140
            while ( my $item = $items_with_invalid_itype->next ) {
149
                    new_item(
141
                new_item(
142
                    sprintf "Item with itemnumber=%s, biblionumber=%s does not have a valid itype value (%s)",
150
                    sprintf "Item with itemnumber=%s, biblionumber=%s does not have a valid itype value (%s)",
143
                    $item->itemnumber, $item->biblionumber, $item->itype
151
                    $item->itemnumber, $item->biblionumber, $item->itype
144
                );
152
                    );
153
                }
154
                new_hint("The items must have a itype value that is defined in the item types of Koha (Home › Administration › Item types administration)");
145
            }
155
            }
146
            new_hint("The items must have a itype value that is defined in the item types of Koha (Home › Administration › Item types administration)");
156
        }else{
147
        }
157
            my $biblioitems_with_invalid_itemtype = Koha::Biblioitems->search(
148
    }
149
    else {
150
        my $biblioitems_with_invalid_itemtype = Koha::Biblioitems->search(
151
            { itemtype => { not_in => \@itemtypes } }
158
            { itemtype => { not_in => \@itemtypes } }
152
        );
159
            );
153
        if ( $biblioitems_with_invalid_itemtype->count ) {
160
            if ( $biblioitems_with_invalid_itemtype->count ) {
154
            new_section("Biblioitems do not have itemtype defined");
161
                new_section("Biblioitems do not have itemtype defined");
155
            while ( my $biblioitem = $biblioitems_with_invalid_itemtype->next ) {
162
                while ( my $biblioitem = $biblioitems_with_invalid_itemtype->next ) {
156
                new_item(
163
                    new_item(
157
                    sprintf "Biblioitem with biblioitemnumber=%s does not have a valid itemtype value",
164
                    sprintf "Biblioitem with biblioitemnumber=%s does not have a valid itemtype value",
158
                    $biblioitem->biblioitemnumber
165
                    $biblioitem->biblioitemnumber
159
                );
166
                    );
167
                }
168
                new_hint("The biblioitems must have a itemtype value that is defined in the item types of Koha (Home › Administration › Item types administration)");
160
            }
169
            }
161
            new_hint("The biblioitems must have a itemtype value that is defined in the item types of Koha (Home › Administration › Item types administration)");
162
        }
163
    }
164
165
    my ( @decoding_errors, @ids_not_in_marc );
166
    my $biblios = Koha::Biblios->search;
167
    my ( $biblio_tag,     $biblio_subfield )     = C4::Biblio::GetMarcFromKohaField( "biblio.biblionumber" );
168
    my ( $biblioitem_tag, $biblioitem_subfield ) = C4::Biblio::GetMarcFromKohaField( "biblioitems.biblioitemnumber" );
169
    while ( my $biblio = $biblios->next ) {
170
        my $record = eval{$biblio->metadata->record;};
171
        if ($@) {
172
            push @decoding_errors, $@;
173
            next;
174
        }
175
        my ( $biblionumber, $biblioitemnumber );
176
        if ( $biblio_tag < 10 ) {
177
            my $biblio_control_field = $record->field($biblio_tag);
178
            $biblionumber = $biblio_control_field->data if $biblio_control_field;
179
        } else {
180
            $biblionumber = $record->subfield( $biblio_tag, $biblio_subfield );
181
        }
182
        if ( $biblioitem_tag < 10 ) {
183
            my $biblioitem_control_field = $record->field($biblioitem_tag);
184
            $biblioitemnumber = $biblioitem_control_field->data if $biblioitem_control_field;
185
        } else {
186
            $biblioitemnumber = $record->subfield( $biblioitem_tag, $biblioitem_subfield );
187
        }
170
        }
188
        if ( $biblionumber != $biblio->biblionumber ) {
171
        
189
            push @ids_not_in_marc,
172
        my ( @decoding_errors, @ids_not_in_marc );
190
              {
173
        my $biblios = Koha::Biblios->search;
174
        my ( $biblio_tag,     $biblio_subfield )     = C4::Biblio::GetMarcFromKohaField( "biblio.biblionumber" );
175
        my ( $biblioitem_tag, $biblioitem_subfield ) = C4::Biblio::GetMarcFromKohaField( "biblioitems.biblioitemnumber" );
176
        while ( my $biblio = $biblios->next ) {
177
            my $record = eval{$biblio->metadata->record;};
178
            if ($@) {
179
                push @decoding_errors, $@;
180
                next;
181
            }
182
            my ( $biblionumber, $biblioitemnumber );
183
            if ( $biblio_tag < 10 ) {
184
                my $biblio_control_field = $record->field($biblio_tag);
185
                $biblionumber = $biblio_control_field->data if $biblio_control_field;
186
                } else {
187
                $biblionumber = $record->subfield( $biblio_tag, $biblio_subfield );
188
            }
189
            if ( $biblioitem_tag < 10 ) {
190
                my $biblioitem_control_field = $record->field($biblioitem_tag);
191
                $biblioitemnumber = $biblioitem_control_field->data if $biblioitem_control_field;
192
                } else {
193
                $biblioitemnumber = $record->subfield( $biblioitem_tag, $biblioitem_subfield );
194
            }
195
            if ( $biblionumber != $biblio->biblionumber ) {
196
                push @ids_not_in_marc,
197
                {
191
                biblionumber         => $biblio->biblionumber,
198
                biblionumber         => $biblio->biblionumber,
192
                biblionumber_in_marc => $biblionumber,
199
                biblionumber_in_marc => $biblionumber,
193
              };
200
                };
194
        }
201
            }
195
        if ( $biblioitemnumber != $biblio->biblioitem->biblioitemnumber ) {
202
            if ( $biblioitemnumber != $biblio->biblioitem->biblioitemnumber ) {
196
            push @ids_not_in_marc,
203
                push @ids_not_in_marc,
197
            {
204
                {
198
                biblionumber     => $biblio->biblionumber,
205
                biblionumber     => $biblio->biblionumber,
199
                biblioitemnumber => $biblio->biblioitem->biblioitemnumber,
206
                biblioitemnumber => $biblio->biblioitem->biblioitemnumber,
200
                biblioitemnumber_in_marc => $biblionumber,
207
                biblioitemnumber_in_marc => $biblionumber,
201
            };
208
                };
209
            }
202
        }
210
        }
203
    }
211
        if ( @decoding_errors ) {
204
    if ( @decoding_errors ) {
212
            new_section("Bibliographic records have invalid MARCXML");
205
        new_section("Bibliographic records have invalid MARCXML");
213
            new_item($_) for @decoding_errors;
206
        new_item($_) for @decoding_errors;
214
            new_hint("The bibliographic records must have a valid MARCXML or you will face encoding issues or wrong displays");
207
        new_hint("The bibliographic records must have a valid MARCXML or you will face encoding issues or wrong displays");
215
        }
208
    }
216
        if (@ids_not_in_marc) {
209
    if (@ids_not_in_marc) {
217
            new_section("Bibliographic records have MARCXML without biblionumber or biblioitemnumber");
210
        new_section("Bibliographic records have MARCXML without biblionumber or biblioitemnumber");
218
            for my $id (@ids_not_in_marc) {
211
        for my $id (@ids_not_in_marc) {
219
                if ( exists $id->{biblioitemnumber} ) {
212
            if ( exists $id->{biblioitemnumber} ) {
220
                    new_item(
213
                new_item(
214
                    sprintf(q{Biblionumber %s has biblioitemnumber '%s' but should be '%s' in %s$%s},
221
                    sprintf(q{Biblionumber %s has biblioitemnumber '%s' but should be '%s' in %s$%s},
215
                        $id->{biblionumber},
222
                    $id->{biblionumber},
216
                        $id->{biblioitemnumber},
223
                    $id->{biblioitemnumber},
217
                        $id->{biblioitemnumber_in_marc},
224
                    $id->{biblioitemnumber_in_marc},
218
                        $biblioitem_tag,
225
                    $biblioitem_tag,
219
                        $biblioitem_subfield,
226
                    $biblioitem_subfield,
220
                    )
227
                    )
221
                );
228
                    );
222
            }
229
                }else{
223
            else {
230
                    new_item(
224
                new_item(
225
                    sprintf(q{Biblionumber %s has '%s' in %s$%s},
231
                    sprintf(q{Biblionumber %s has '%s' in %s$%s},
226
                        $id->{biblionumber},
232
                    $id->{biblionumber},
227
                        $id->{biblionumber_in_marc},
233
                    $id->{biblionumber_in_marc},
228
                        $biblio_tag,
234
                    $biblio_tag,
229
                        $biblio_subfield,
235
                    $biblio_subfield,
230
                    )
236
                    )
231
                );
237
                    );
238
                }
232
            }
239
            }
240
            new_hint("The bibliographic records must have the biblionumber and biblioitemnumber in MARCXML");
233
        }
241
        }
234
        new_hint("The bibliographic records must have the biblionumber and biblioitemnumber in MARCXML");
235
    }
242
    }
236
}
243
    
237
244
    sub CheckItemsFramework {
238
sub CheckItemsFramework {
245
        my @framework_codes = Koha::BiblioFrameworks->search()->get_column('frameworkcode');
239
    my @framework_codes = Koha::BiblioFrameworks->search()->get_column('frameworkcode');
246
        push @framework_codes,""; # The default is not stored in frameworks, we need to force it
240
    push @framework_codes,""; # The default is not stored in frameworks, we need to force it
247
        
241
248
        my $invalid_av_per_framework = {};
242
    my $invalid_av_per_framework = {};
249
        foreach my $frameworkcode ( @framework_codes ) {
243
    foreach my $frameworkcode ( @framework_codes ) {
250
            # We are only checking fields that are mapped to DB fields
244
        # We are only checking fields that are mapped to DB fields
251
            my $msss = Koha::MarcSubfieldStructures->search({
245
        my $msss = Koha::MarcSubfieldStructures->search({
246
            frameworkcode => $frameworkcode,
252
            frameworkcode => $frameworkcode,
247
            authorised_value => {
253
            authorised_value => {
248
                '!=' => [ -and => ( undef, '' ) ]
254
            '!=' => [ -and => ( undef, '' ) ]
249
            },
255
            },
250
            kohafield => {
256
            kohafield => {
251
                '!=' => [ -and => ( undef, '' ) ]
257
            '!=' => [ -and => ( undef, '' ) ]
252
            }
258
        }
253
        });
259
        });
254
        while ( my $mss = $msss->next ) {
260
        while ( my $mss = $msss->next ) {
255
            my $kohafield = $mss->kohafield;
261
            my $kohafield = $mss->kohafield;
256
            my $av = $mss->authorised_value;
262
            my $av = $mss->authorised_value;
257
            next if grep {$_ eq $av} qw( branches itemtypes cn_source ); # internal categories
263
            next if grep {$_ eq $av} qw( branches itemtypes cn_source ); # internal categories
258
264
            
259
            my $avs = Koha::AuthorisedValues->search_by_koha_field(
265
            my $avs = Koha::AuthorisedValues->search_by_koha_field(
260
                {
266
            {
261
                    frameworkcode => $frameworkcode,
267
            frameworkcode => $frameworkcode,
262
                    kohafield     => $kohafield,
268
            kohafield     => $kohafield,
263
                }
269
        }
264
            );
270
        );
265
            my $tmp_kohafield = $kohafield;
271
        my $tmp_kohafield = $kohafield;
266
            if ( $tmp_kohafield =~ /^biblioitems/ ) {
272
        if ( $tmp_kohafield =~ /^biblioitems/ ) {
267
                $tmp_kohafield =~ s|biblioitems|biblioitem|;
273
            $tmp_kohafield =~ s|biblioitems|biblioitem|;
268
            } else {
274
            } else {
269
                $tmp_kohafield =~ s|items|me|;
275
            $tmp_kohafield =~ s|items|me|;
270
            }
276
        }
271
            # replace items.attr with me.attr
277
        # replace items.attr with me.attr
272
278
        
273
            # We are only checking biblios with items
279
        # We are only checking biblios with items
274
            my $items = Koha::Items->search(
280
        my $items = Koha::Items->search(
275
                {
281
        {
276
                    $tmp_kohafield =>
282
        $tmp_kohafield =>
277
                      {
283
        {
278
                          -not_in => [ $avs->get_column('authorised_value'), '' ],
284
        -not_in => [ $avs->get_column('authorised_value'), '' ],
279
                          '!='    => undef,
285
        '!='    => undef,
280
                      },
286
        },
281
                    'biblio.frameworkcode' => $frameworkcode
287
        'biblio.frameworkcode' => $frameworkcode
282
                },
288
        },
283
                { join => [ 'biblioitem', 'biblio' ] }
289
        { join => [ 'biblioitem', 'biblio' ] }
284
            );
290
        );
285
            if ( $items->count ) {
291
        if ( $items->count ) {
286
                $invalid_av_per_framework->{ $frameworkcode }->{$av} =
292
            $invalid_av_per_framework->{ $frameworkcode }->{$av} =
287
                  { items => $items, kohafield => $kohafield };
293
            { items => $items, kohafield => $kohafield };
288
            }
289
        }
294
        }
290
    }
295
    }
291
    if (%$invalid_av_per_framework) {
296
}
292
        new_section('Wrong values linked to authorised values');
297
if (%$invalid_av_per_framework) {
293
        for my $frameworkcode ( keys %$invalid_av_per_framework ) {
298
    new_section('Wrong values linked to authorised values');
294
            while ( my ( $av_category, $v ) = each %{$invalid_av_per_framework->{$frameworkcode}} ) {
299
    for my $frameworkcode ( keys %$invalid_av_per_framework ) {
295
                my $items     = $v->{items};
300
        while ( my ( $av_category, $v ) = each %{$invalid_av_per_framework->{$frameworkcode}} ) {
296
                my $kohafield = $v->{kohafield};
301
            my $items     = $v->{items};
297
                my ( $table, $column ) = split '\.', $kohafield;
302
            my $kohafield = $v->{kohafield};
298
                my $output;
303
            my ( $table, $column ) = split '\.', $kohafield;
299
                while ( my $i = $items->next ) {
304
            my $output;
300
                    my $value = $table eq 'items'
305
            while ( my $i = $items->next ) {
301
                        ? $i->$column
306
                my $value = $table eq 'items'
302
                        : $table eq 'biblio'
307
                ? $i->$column
303
                        ? $i->biblio->$column
308
                : $table eq 'biblio'
304
                        : $i->biblioitem->$column;
309
                ? $i->biblio->$column
305
                    $output .= " {" . $i->itemnumber . " => " . $value . "}\n";
310
                : $i->biblioitem->$column;
306
                }
311
                $output .= " {" . $i->itemnumber . " => " . $value . "}\n";
307
                new_item(
308
                    sprintf(
309
                        "The Framework *%s* is using the authorised value's category *%s*, "
310
                        . "but the following %s do not have a value defined ({itemnumber => value }):\n%s",
311
                        $frameworkcode, $av_category, $kohafield, $output
312
                    )
313
                );
314
            }
312
            }
313
            new_item(
314
            sprintf(
315
            "The Framework *%s* is using the authorised value's category *%s*, "
316
            . "but the following %s do not have a value defined ({itemnumber => value }):\n%s",
317
            $frameworkcode, $av_category, $kohafield, $output
318
            )
319
            );
315
        }
320
        }
316
    }
321
    }
317
}
322
}
323
}
318
324
319
sub CheckItemsTitle {
325
sub CheckItemsTitle {
320
    my $biblios = Koha::Biblios->search({
326
my $biblios = Koha::Biblios->search({
321
        -or => [
327
-or => [
322
            title => '',
328
title => '',
323
            title => undef,
329
title => undef,
324
        ]
330
]
325
    });
331
});
326
    if ( $biblios->count ) {
332
if ( $biblios->count ) {
327
        my ( $title_tag, $title_subtag ) = C4::Biblio::GetMarcFromKohaField( 'biblio.title' );
333
    my ( $title_tag, $title_subtag ) = C4::Biblio::GetMarcFromKohaField( 'biblio.title' );
328
        my $title_field = $title_tag // '';
334
    my $title_field = $title_tag // '';
329
        $title_field .= '$'.$title_subtag if $title_subtag;
335
    $title_field .= '$'.$title_subtag if $title_subtag;
330
        new_section("Biblio without title $title_field");
336
    new_section("Biblio without title $title_field");
331
        while ( my $biblio = $biblios->next ) {
337
    while ( my $biblio = $biblios->next ) {
332
            new_item(sprintf "Biblio with biblionumber=%s does not have title defined", $biblio->biblionumber);
338
        new_item(sprintf "Biblio with biblionumber=%s does not have title defined", $biblio->biblionumber);
333
        }
334
        new_hint("Edit these bibliographic records to define a title");
335
    }
339
    }
340
    new_hint("Edit these bibliographic records to define a title");
341
}
336
}
342
}
337
343
338
sub CheckAgeForCategory {
344
sub CheckAgeForCategory {
339
    my $aging_patrons = Koha::Patrons->search(
345
my $aging_patrons = Koha::Patrons->search(
340
        {
346
{
341
            -not => {
347
-not => {
342
                -or => {
348
-or => {
343
                    'me.dateofbirth' => undef,
349
'me.dateofbirth' => undef,
344
                    -and => {
350
-and => {
345
                        'categorycode.dateofbirthrequired' => undef,
351
'categorycode.dateofbirthrequired' => undef,
346
                        'categorycode.upperagelimit'       => undef,
352
'categorycode.upperagelimit'       => undef,
347
                    }
353
}
348
                }
354
}
349
            }
355
}
350
        },
356
},
351
        { prefetch => ['categorycode'] },
357
{ prefetch => ['categorycode'] },
352
        { order_by => [ 'me.categorycode', 'me.borrowernumber' ] },
358
{ order_by => [ 'me.categorycode', 'me.borrowernumber' ] },
353
    );
359
);
354
    my @invalid_patrons;
360
my @invalid_patrons;
355
    while ( my $aging_patron = $aging_patrons->next ) {
361
while ( my $aging_patron = $aging_patrons->next ) {
356
        push @invalid_patrons, $aging_patron unless $aging_patron->is_expired || $aging_patron->is_valid_age;
362
push @invalid_patrons, $aging_patron unless $aging_patron->is_expired || $aging_patron->is_valid_age;
363
}
364
if (@invalid_patrons) {
365
new_section("Patrons with invalid age for category");
366
foreach my $invalid_patron (@invalid_patrons) {
367
my $category = $invalid_patron->category;
368
new_item(
369
sprintf "Patron borrowernumber=%s has an invalid age of %s for their category '%s' (%s to %s)",
370
$invalid_patron->borrowernumber, $invalid_patron->get_age, $category->categorycode,
371
$category->dateofbirthrequired // '0',  $category->upperagelimit // 'unlimited'
372
);
373
}
374
new_hint("You may change the patron's category automatically with misc/cronjobs/update_patrons_category.pl");
375
}
376
}
377
378
sub CheckRelationshipsLoop {
379
my @loop_borrowers_relationships;
380
my @relationships = Koha::Patron::Relationships->search();
381
my @patrons_guarantors = Koha::Patron::Relationships::guarantors(@relationships)->as_list;
382
my @patrons_guarantees = Koha::Patron::Relationships::guarantees(@relationships)->as_list;
383
384
foreach my $patron_guarantor (@patrons_guarantors) {
385
foreach my $patron_guarantee (@patrons_guarantees) {
386
if ($patron_guarantor->borrowernumber == $patron_guarantee->borrowernumber) {
387
388
my $relationship_id;
389
my $guarantee_id;
390
my $size_list;
391
my $tmp_garantor_id = $patron_guarantor->borrowernumber;
392
my @relationship_ids;
393
my @guarantor_to_find;
394
395
push @guarantor_to_find, $patron_guarantor->borrowernumber;
396
397
do {
398
my @relationship_for_go = Koha::Patron::Relationships->search(
399
{
400
-or => [
401
'guarantor_id' => { '=', $tmp_garantor_id },
402
]
403
},
404
)->as_list;
405
$size_list = scalar @relationship_for_go;
406
407
foreach my $relation (@relationship_for_go) {
408
    $relationship_id = $relation->id;
409
    unless (grep { $_ == $relationship_id } @relationship_ids) {
410
        push @relationship_ids, $relationship_id;
357
    }
411
    }
358
    if (@invalid_patrons) {
412
    $guarantee_id = $relation->guarantee_id;
359
        new_section("Patrons with invalid age for category");
413
    
360
        foreach my $invalid_patron (@invalid_patrons) {
414
    if (grep { $_ eq $guarantee_id } @guarantor_to_find) {
361
            my $category = $invalid_patron->category;
415
        last;
362
            new_item(
416
    }
363
                sprintf "Patron borrowernumber=%s has an invalid age of %s for their category '%s' (%s to %s)",
417
    
364
                $invalid_patron->borrowernumber, $invalid_patron->get_age, $category->categorycode,
418
    my @relationship_for_go = Koha::Patron::Relationships->search(
365
                $category->dateofbirthrequired // '0',  $category->upperagelimit // 'unlimited'
419
    {
366
            );
420
    -or => [
421
    'guarantor_id' => { '=', $guarantee_id },
422
    ]
423
    },
424
    )->as_list;
425
    $size_list = scalar @relationship_for_go;
426
    
427
    push @guarantor_to_find, $guarantee_id;
428
    
429
    foreach my $relation (@relationship_for_go) {
430
        $relationship_id = $relation->id;
431
        unless (grep { $_ == $relationship_id } @relationship_ids) {
432
            push @relationship_ids, $relationship_id;
433
        }
434
        $guarantee_id = $relation->guarantee_id;
435
        
436
        if (grep { $_ eq $guarantee_id } @guarantor_to_find) {
437
            last;
367
        }
438
        }
368
        new_hint("You may change the patron's category automatically with misc/cronjobs/update_patrons_category.pl");
439
    }
440
    if (grep { $_ eq $guarantee_id } @guarantor_to_find) {
441
        last;
369
    }
442
    }
370
}
443
}
371
444
445
$tmp_garantor_id = $guarantee_id;
446
} while ((!grep { $_ eq $guarantee_id } @guarantor_to_find) && $size_list > 0);
447
448
if ($patron_guarantor->borrowernumber == $guarantee_id) {
449
    unless (grep {  join("", sort @$_) eq join("", sort @relationship_ids) } @loop_borrowers_relationships) {
450
        push @loop_borrowers_relationships, \@relationship_ids;
451
    }
452
}
453
}
454
}
455
}
456
457
if (scalar @loop_borrowers_relationships > 0) {
458
new_section("The list of relationships id with guarantors who are also guarantee.");
459
foreach my $table (@loop_borrowers_relationships) {
460
new_item(
461
sprintf "Relationships connected : %s |",
462
join(" | ", @$table)
463
);
464
}
465
new_hint("Relationships that form guarantor loops must be deleted");
466
}
467
}
372
468
373
sub new_section {
469
sub new_section {
374
    my ( $name ) = @_;
470
my ( $name ) = @_;
375
    say "\n== $name ==";
471
say "\n== $name ==";
376
}
472
}
377
473
378
sub new_item {
474
sub new_item {
379
    my ( $name ) = @_;
475
my ( $name ) = @_;
380
    say "\t* $name";
476
say "\t* $name";
381
}
477
}
382
sub new_hint {
478
sub new_hint {
383
    my ( $name ) = @_;
479
my ( $name ) = @_;
384
    say "=> $name";
480
say "=> $name";
385
}
481
}
386
482
387
=head1 NAME
483
=head1 NAME
Lines 390-396 search_for_data_inconsistencies.pl Link Here
390
486
391
=head1 SYNOPSIS
487
=head1 SYNOPSIS
392
488
393
    perl search_for_data_inconsistencies.pl
489
perl search_for_data_inconsistencies.pl
394
490
395
=head1 DESCRIPTION
491
=head1 DESCRIPTION
396
492
Lines 399-407 Catch data inconsistencies in Koha database Link Here
399
* Items with undefined homebranch and/or holdingbranch
495
* Items with undefined homebranch and/or holdingbranch
400
* Authorities with undefined authtypecodes/authority types
496
* Authorities with undefined authtypecodes/authority types
401
* Item types:
497
* Item types:
402
  * if item types are defined at item level (item-level_itypes=specific item),
498
* if item types are defined at item level (item-level_itypes=specific item),
403
    then items.itype must be set else biblioitems.itemtype must be set
499
then items.itype must be set else biblioitems.itemtype must be set
404
  * Item types defined in items or biblioitems must be defined in the itemtypes table
500
* Item types defined in items or biblioitems must be defined in the itemtypes table
405
* Invalid MARCXML in bibliographic records
501
* Invalid MARCXML in bibliographic records
406
* Patrons with invalid category types due to lower and upper age limits
502
* Patrons with invalid category types due to lower and upper age limits
407
503
408
- 

Return to bug 36027