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

(-)a/Koha/Item/LastPatron.pm (+44 lines)
Line 0 Link Here
1
package Koha::Item::LastPatron;
2
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it under the
6
# terms of the GNU General Public License as published by the Free Software
7
# Foundation; either version 3 of the License, or (at your option) any later
8
# version.
9
#
10
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
11
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License along
15
# with Koha; if not, write to the Free Software Foundation, Inc.,
16
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
18
use Modern::Perl;
19
20
use Carp;
21
22
use Koha::Database;
23
24
use base qw(Koha::Object);
25
26
=head1 NAME
27
28
Koha::Item::LastPatron - Koha Item LastPatron Object class
29
30
=head1 API
31
32
=head2 Class Methods
33
34
=cut
35
36
=head3 type
37
38
=cut
39
40
sub _type {
41
    return 'ItemsLastBorrower';
42
}
43
44
1;
(-)a/Koha/Item/LastPatrons.pm (+54 lines)
Line 0 Link Here
1
package Koha::Item::LastPatrons;
2
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it under the
6
# terms of the GNU General Public License as published by the Free Software
7
# Foundation; either version 3 of the License, or (at your option) any later
8
# version.
9
#
10
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
11
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License along
15
# with Koha; if not, write to the Free Software Foundation, Inc.,
16
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
18
use Modern::Perl;
19
20
use Carp;
21
22
use Koha::Item::LastPatron;
23
24
use Koha::Database;
25
26
use base qw(Koha::Objects);
27
28
=head1 NAME
29
30
Koha::Item::LastPatrons - Koha Item LastPatron Object set class
31
32
=head1 API
33
34
=head2 Class Methods
35
36
=cut
37
38
=head3 type
39
40
=cut
41
42
sub _type {
43
    return 'ItemsLastBorrower';
44
}
45
46
=head3 object_class
47
48
=cut
49
50
sub object_class {
51
    return 'Koha::Item::LastPatron';
52
}
53
54
1;
(-)a/Koha/Patrons.pm (-12 / +75 lines)
Lines 31-36 use Koha::Patron; Link Here
31
use Koha::Exceptions::Patron;
31
use Koha::Exceptions::Patron;
32
use Koha::Patron::Categories;
32
use Koha::Patron::Categories;
33
use Date::Calc qw( Today Add_Delta_YMD );
33
use Date::Calc qw( Today Add_Delta_YMD );
34
use Koha::Item::LastPatrons;
34
35
35
use base qw(Koha::Objects);
36
use base qw(Koha::Objects);
36
37
Lines 129-135 sub search_upcoming_membership_expires { Link Here
129
130
130
=head3 search_patrons_to_anonymise
131
=head3 search_patrons_to_anonymise
131
132
132
    my $patrons = Koha::Patrons->search_patrons_to_anonymise( { before => $older_than_date, [ library => $library ] } );
133
    my $patrons = Koha::Patrons->search_patrons_to_anonymise( { before => $older_than_date, [ library => $library, for => $table_to_anonymize ] } );
133
134
134
This method returns all patrons who has an issue history older than a given date.
135
This method returns all patrons who has an issue history older than a given date.
135
136
Lines 139-144 sub search_patrons_to_anonymise { Link Here
139
    my ( $class, $params ) = @_;
140
    my ( $class, $params ) = @_;
140
    my $older_than_date = $params->{before};
141
    my $older_than_date = $params->{before};
141
    my $library         = $params->{library};
142
    my $library         = $params->{library};
143
    my $for           = $params->{for};
142
    $older_than_date = $older_than_date ? dt_from_string($older_than_date) : dt_from_string;
144
    $older_than_date = $older_than_date ? dt_from_string($older_than_date) : dt_from_string;
143
    $library ||=
145
    $library ||=
144
      ( C4::Context->preference('IndependentBranches') && C4::Context->userenv && !C4::Context->IsSuperLibrarian() && C4::Context->userenv->{branch} )
146
      ( C4::Context->preference('IndependentBranches') && C4::Context->userenv && !C4::Context->IsSuperLibrarian() && C4::Context->userenv->{branch} )
Lines 147-163 sub search_patrons_to_anonymise { Link Here
147
    my $anonymous_patron = C4::Context->preference('AnonymousPatron') || undef;
149
    my $anonymous_patron = C4::Context->preference('AnonymousPatron') || undef;
148
150
149
    my $dtf = Koha::Database->new->schema->storage->datetime_parser;
151
    my $dtf = Koha::Database->new->schema->storage->datetime_parser;
150
    my $rs = $class->_resultset->search(
152
    my $rs;
151
        {   returndate                  => { '<'   =>  $dtf->format_datetime($older_than_date), },
153
    if($for && $for eq 'items_last_borrower') {
152
            'old_issues.borrowernumber' => { 'not' => undef },
154
        $rs = $class->_resultset->search(
153
            privacy                     => { '<>'  => 0 },                  # Keep forever
155
            {   'items_last_borrowers.created_on' => { '<'   =>  $dtf->format_datetime($older_than_date), },
154
            ( $library ? ( 'old_issues.branchcode' => $library ) : () ),
156
                'items_last_borrowers.borrowernumber' => { 'not' => undef },
155
            ( $anonymous_patron ? ( 'old_issues.borrowernumber' => { '!=' => $anonymous_patron } ) : () ),
157
                'itemnumber.damaged' => 0,
156
        },
158
                'itemnumber.itemlost' => 0,
157
        {   join     => ["old_issues"],
159
                'itemnumber.withdrawn' => 0,
158
            distinct => 1,
160
                ( $library ? ( 'itemnumber.homebranch' => $library ) : () ),
159
        }
161
                ( $anonymous_patron ? ( 'items_last_borrowers.borrowernumber' => { '!=' => $anonymous_patron } ) : () ),
160
    );
162
            },
163
            {   join     => { "items_last_borrowers" => "itemnumber" },
164
                distinct => 1,
165
            }
166
        );
167
    } else {
168
        #for old_issues
169
        $rs = $class->_resultset->search(
170
            {   returndate                  => { '<'   =>  $dtf->format_datetime($older_than_date), },
171
                'old_issues.borrowernumber' => { 'not' => undef },
172
                privacy                     => { '<>'  => 0 },                  # Keep forever
173
                ( $library ? ( 'old_issues.branchcode' => $library ) : () ),
174
                ( $anonymous_patron ? ( 'old_issues.borrowernumber' => { '!=' => $anonymous_patron } ) : () ),
175
            },
176
            {   join     => ["old_issues"],
177
                distinct => 1,
178
            }
179
        );
180
    }
161
    return Koha::Patrons->_new_from_dbic($rs);
181
    return Koha::Patrons->_new_from_dbic($rs);
162
}
182
}
163
183
Lines 200-205 sub anonymise_issue_history { Link Here
200
    return $nb_rows;
220
    return $nb_rows;
201
}
221
}
202
222
223
=head3 anonymise_items_last_borrower
224
225
    Koha::Patrons->search->anonymise_items_last_borrower( { [ before => $older_than_date ] } );
226
227
Anonymise last borrower history (items_last_borrower) for all patrons older than the given date (optional).
228
To make sure all the conditions are met, the caller has the responsibility to
229
call search_patrons_to_anonymise to filter the Koha::Patrons set
230
231
=cut
232
233
sub anonymise_items_last_borrower {
234
    my ( $self, $params ) = @_;
235
236
    my $older_than_date = $params->{before};
237
238
    $older_than_date = dt_from_string $older_than_date if $older_than_date;
239
240
    # The default of 0 does not work due to foreign key constraints
241
    # The anonymisation should not fail quietly if AnonymousPatron is not a valid entry
242
    # Set it to undef (NULL)
243
    my $dtf = Koha::Database->new->schema->storage->datetime_parser;
244
    my $nb_rows = 0;
245
    while ( my $patron = $self->next ) {
246
        my $last_borrowers = Koha::Item::LastPatrons->search(
247
        {
248
            borrowernumber => $patron->borrowernumber,
249
            (
250
                $older_than_date
251
                ? ( created_on =>
252
                      { '<' => $dtf->format_datetime($older_than_date) } )
253
                : ()
254
            )
255
        }
256
        );
257
        my $anonymous_patron = C4::Context->preference('AnonymousPatron') || undef;
258
        while(my $last_borrower = $last_borrowers->next) {
259
            $last_borrower->borrowernumber( $anonymous_patron )->store;
260
            $nb_rows++;
261
        }
262
    }
263
    return $nb_rows;
264
}
265
203
=head3 delete
266
=head3 delete
204
267
205
    Koha::Patrons->search({ some filters here })->delete({ move => 1, verbose => 1 });
268
    Koha::Patrons->search({ some filters here })->delete({ move => 1, verbose => 1 });
(-)a/misc/cronjobs/batch_anonymise.pl (-1 / +4 lines)
Lines 73-81 cronlogaction(); Link Here
73
my ($year,$month,$day) = Today();
73
my ($year,$month,$day) = Today();
74
my ($newyear,$newmonth,$newday) = Add_Delta_Days ($year,$month,$day,(-1)*$days);
74
my ($newyear,$newmonth,$newday) = Add_Delta_Days ($year,$month,$day,(-1)*$days);
75
my $formatdate = sprintf "%4d-%02d-%02d",$newyear,$newmonth,$newday;
75
my $formatdate = sprintf "%4d-%02d-%02d",$newyear,$newmonth,$newday;
76
$verbose and print "Checkouts before $formatdate will be anonymised.\n";
76
$verbose and print "Checkouts and items last borrowers before $formatdate will be anonymised.\n";
77
77
78
my $rows = Koha::Patrons->search_patrons_to_anonymise( { before => $formatdate } )->anonymise_issue_history( { before => $formatdate } );
78
my $rows = Koha::Patrons->search_patrons_to_anonymise( { before => $formatdate } )->anonymise_issue_history( { before => $formatdate } );
79
$verbose and print int($rows) . " checkouts anonymised.\n";
79
$verbose and print int($rows) . " checkouts anonymised.\n";
80
80
81
$rows = Koha::Patrons->search_patrons_to_anonymise( { before => $formatdate , for => 'items_last_borrower'} )->anonymise_items_last_borrower( { before => $formatdate } );
82
$verbose and print int($rows) . " items last borrower anonymised.\n";
83
81
exit(0);
84
exit(0);
(-)a/t/db_dependent/Koha/Patrons.t (-2 / +138 lines)
Lines 19-25 Link Here
19
19
20
use Modern::Perl;
20
use Modern::Perl;
21
21
22
use Test::More tests => 40;
22
use Test::More tests => 41;
23
use Test::Warn;
23
use Test::Warn;
24
use Test::Exception;
24
use Test::Exception;
25
use Test::MockModule;
25
use Test::MockModule;
Lines 40-45 use Koha::Patron::Categories; Link Here
40
use Koha::Patron::Relationship;
40
use Koha::Patron::Relationship;
41
use Koha::Database;
41
use Koha::Database;
42
use Koha::DateUtils;
42
use Koha::DateUtils;
43
use DateTime;
44
use DateTime::Duration;
43
use Koha::Virtualshelves;
45
use Koha::Virtualshelves;
44
46
45
use t::lib::TestBuilder;
47
use t::lib::TestBuilder;
Lines 1178-1183 subtest 'search_patrons_to_anonymise & anonymise_issue_history' => sub { Link Here
1178
    t::lib::Mocks::mock_preference('IndependentBranches', 0);
1180
    t::lib::Mocks::mock_preference('IndependentBranches', 0);
1179
};
1181
};
1180
1182
1183
subtest 'search_patrons_to_anonymise & anonymise_items_last_borrower' => sub {
1184
    plan tests => 3;
1185
1186
    my $branch = $builder->build({ source => 'Branch' });
1187
    my $userenv_patron = $builder->build_object({
1188
        class  => 'Koha::Patrons',
1189
        value  => { branchcode => $branch->{branchcode}, flags => 0 },
1190
    });
1191
    t::lib::Mocks::mock_userenv({ patron => $userenv_patron });
1192
    t::lib::Mocks::mock_preference('StoreLastBorrower', 1);
1193
1194
    subtest 'AnonymousPatron is defined' => sub {
1195
        plan tests => 3;
1196
1197
        my $anonymous = $builder->build( { source => 'Borrower', }, );
1198
        t::lib::Mocks::mock_preference( 'AnonymousPatron', $anonymous->{borrowernumber} );
1199
        t::lib::Mocks::mock_preference('IndependentBranches', 0);
1200
        my $patron = $builder->build(
1201
            {   source => 'Borrower',
1202
                value  => { privacy => 1, }
1203
            }
1204
        );
1205
        my $item = $builder->build(
1206
            {   source => 'Item',
1207
                value  => {
1208
                    itemlost  => 0,
1209
                    withdrawn => 0,
1210
                    damaged => 0
1211
                },
1212
            }
1213
        );
1214
        my $issue = $builder->build(
1215
            {   source => 'Issue',
1216
                value  => {
1217
                    borrowernumber => $patron->{borrowernumber},
1218
                    itemnumber     => $item->{itemnumber},
1219
                },
1220
            }
1221
        );
1222
1223
        my $now = DateTime->now;
1224
        my ( $returned, undef, undef ) = C4::Circulation::AddReturn( $item->{barcode}, undef, undef, $now );
1225
        is( $returned, 1, 'The item should have been returned' );
1226
        my $before = $now + DateTime::Duration->new( days => 1 );
1227
        my $rows_affected = Koha::Patrons->search_patrons_to_anonymise( { before => $before->ymd, for => 'items_last_borrower' } )->anonymise_items_last_borrower( { before => $before->ymd } );
1228
        ok( $rows_affected > 0, 'AnonymiseIssueHistory should affect at least 1 row' );
1229
        my $dbh = C4::Context->dbh;
1230
        my ($borrowernumber_used_to_anonymised) = $dbh->selectrow_array(q|
1231
            SELECT borrowernumber FROM items_last_borrower where itemnumber = ?
1232
        |, undef, $item->{itemnumber});
1233
        is( $borrowernumber_used_to_anonymised, $anonymous->{borrowernumber}, 'AnonymousPatron should have been used to anonymise' );
1234
        Koha::Patrons->find( $patron->{borrowernumber})->delete;
1235
        Koha::Patrons->find( $anonymous->{borrowernumber})->delete;
1236
    };
1237
1238
    subtest 'AnonymousPatron is not defined' => sub {
1239
        plan tests => 3;
1240
1241
        t::lib::Mocks::mock_preference('IndependentBranches', 0);
1242
        t::lib::Mocks::mock_preference( 'AnonymousPatron', '' );
1243
        my $patron = $builder->build(
1244
            {   source => 'Borrower',
1245
                value  => { privacy => 1, }
1246
            }
1247
        );
1248
        my $item = $builder->build(
1249
            {   source => 'Item',
1250
                value  => {
1251
                    itemlost  => 0,
1252
                    withdrawn => 0,
1253
                    damaged => 0
1254
                },
1255
            }
1256
        );
1257
        my $issue = $builder->build(
1258
            {   source => 'Issue',
1259
                value  => {
1260
                    borrowernumber => $patron->{borrowernumber},
1261
                    itemnumber     => $item->{itemnumber},
1262
                },
1263
            }
1264
        );
1265
1266
        my $now = DateTime->now;
1267
        my ( $returned, undef, undef ) = C4::Circulation::AddReturn( $item->{barcode}, undef, undef, $now );
1268
        is( $returned, 1, 'The item should have been returned' );
1269
        my $before = $now + DateTime::Duration->new( days => 1 );
1270
        my $rows_affected = Koha::Patrons->search_patrons_to_anonymise( { before => $before->ymd, for => 'items_last_borrower' } )->anonymise_items_last_borrower( { before => $before->ymd } );
1271
        ok( $rows_affected > 0, 'AnonymiseIssueHistory should affect at least 1 row' );
1272
        my $dbh = C4::Context->dbh;
1273
        my ($borrowernumber_used_to_anonymised) = $dbh->selectrow_array(q|
1274
            SELECT borrowernumber FROM items_last_borrower where itemnumber = ?
1275
        |, undef, $item->{itemnumber});
1276
        is( $borrowernumber_used_to_anonymised, undef, 'undef should have been used to anonymise' );
1277
        Koha::Patrons->find( $patron->{borrowernumber})->delete;
1278
    };
1279
1280
    subtest 'Logged in librarian is not superlibrarian & IndependentBranches' => sub {
1281
        plan tests => 1;
1282
        t::lib::Mocks::mock_preference( 'IndependentBranches', 1 );
1283
        my $patron = $builder->build(
1284
            {   source => 'Borrower',
1285
                value  => { privacy => 1 }    # Another branchcode than the logged in librarian
1286
            }
1287
        );
1288
        my $item = $builder->build(
1289
            {   source => 'Item',
1290
                value  => {
1291
                    itemlost  => 0,
1292
                    withdrawn => 0,
1293
                    damaged => 0
1294
                },
1295
            }
1296
        );
1297
        my $issue = $builder->build(
1298
            {   source => 'Issue',
1299
                value  => {
1300
                    borrowernumber => $patron->{borrowernumber},
1301
                    itemnumber     => $item->{itemnumber},
1302
                },
1303
            }
1304
        );
1305
1306
        my $now = DateTime->now;
1307
        my ( $returned, undef, undef ) = C4::Circulation::AddReturn( $item->{barcode}, undef, undef, $now );
1308
        my $before = $now + DateTime::Duration->new( days => 1 );
1309
        is( Koha::Patrons->search_patrons_to_anonymise( { before => $before->ymd, for => 'items_last_borrower' } )->count, 0 );
1310
        Koha::Patrons->find( $patron->{borrowernumber})->delete;
1311
    };
1312
1313
    $userenv_patron->delete;
1314
    t::lib::Mocks::mock_preference('StoreLastBorrower', 0);
1315
    t::lib::Mocks::mock_preference('IndependentBranches', 0);
1316
};
1317
1181
subtest 'libraries_where_can_see_patrons + can_see_patron_infos + search_limited' => sub {
1318
subtest 'libraries_where_can_see_patrons + can_see_patron_infos + search_limited' => sub {
1182
    plan tests => 3;
1319
    plan tests => 3;
1183
1320
1184
- 

Return to bug 23260