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

(-)a/C4/Circulation.pm (-44 lines)
Lines 94-100 BEGIN { Link Here
94
        &GetBranchItemRule
94
        &GetBranchItemRule
95
		&GetBiblioIssues
95
		&GetBiblioIssues
96
		&GetOpenIssue
96
		&GetOpenIssue
97
		&AnonymiseIssueHistory
98
        &CheckIfIssuedToPatron
97
        &CheckIfIssuedToPatron
99
        &IsItemIssued
98
        &IsItemIssued
100
        GetTopIssues
99
        GetTopIssues
Lines 3383-3431 sub DeleteTransfer { Link Here
3383
    return $sth->execute($itemnumber);
3382
    return $sth->execute($itemnumber);
3384
}
3383
}
3385
3384
3386
=head2 AnonymiseIssueHistory
3387
3388
  ($rows,$err_history_not_deleted) = AnonymiseIssueHistory($date,$borrowernumber)
3389
3390
This function write NULL instead of C<$borrowernumber> given on input arg into the table issues.
3391
if C<$borrowernumber> is not set, it will delete the issue history for all borrower older than C<$date>.
3392
3393
If c<$borrowernumber> is set, it will delete issue history for only that borrower, regardless of their opac privacy
3394
setting (force delete).
3395
3396
return the number of affected rows and a value that evaluates to true if an error occurred deleting the history.
3397
3398
=cut
3399
3400
sub AnonymiseIssueHistory {
3401
    my $date           = shift;
3402
    my $borrowernumber = shift;
3403
    my $dbh            = C4::Context->dbh;
3404
    my $query          = "
3405
        UPDATE old_issues
3406
        SET    borrowernumber = ?
3407
        WHERE  returndate < ?
3408
          AND borrowernumber IS NOT NULL
3409
    ";
3410
3411
    # The default of 0 does not work due to foreign key constraints
3412
    # The anonymisation should not fail quietly if AnonymousPatron is not a valid entry
3413
    # Set it to undef (NULL)
3414
    my $anonymouspatron = C4::Context->preference('AnonymousPatron') || undef;
3415
    my @bind_params = ($anonymouspatron, $date);
3416
    if (defined $borrowernumber) {
3417
       $query .= " AND borrowernumber = ?";
3418
       push @bind_params, $borrowernumber;
3419
    } else {
3420
       $query .= " AND (SELECT privacy FROM borrowers WHERE borrowers.borrowernumber=old_issues.borrowernumber) <> 0";
3421
    }
3422
    my $sth = $dbh->prepare($query);
3423
    $sth->execute(@bind_params);
3424
    my $anonymisation_err = $dbh->err;
3425
    my $rows_affected = $sth->rows;  ### doublecheck row count return function
3426
    return ($rows_affected, $anonymisation_err);
3427
}
3428
3429
=head2 SendCirculationAlert
3385
=head2 SendCirculationAlert
3430
3386
3431
Send out a C<check-in> or C<checkout> alert using the messaging system.
3387
Send out a C<check-in> or C<checkout> alert using the messaging system.
(-)a/C4/Members.pm (-46 lines)
Lines 80-86 BEGIN { Link Here
80
80
81
        &GetBorrowersToExpunge
81
        &GetBorrowersToExpunge
82
        &GetBorrowersWhoHaveNeverBorrowed
82
        &GetBorrowersWhoHaveNeverBorrowed
83
        &GetBorrowersWithIssuesHistoryOlderThan
84
83
85
        &GetUpcomingMembershipExpires
84
        &GetUpcomingMembershipExpires
86
85
Lines 1441-1491 sub GetBorrowersWhoHaveNeverBorrowed { Link Here
1441
    return \@results;
1440
    return \@results;
1442
}
1441
}
1443
1442
1444
=head2 GetBorrowersWithIssuesHistoryOlderThan
1445
1446
  $results = &GetBorrowersWithIssuesHistoryOlderThan($date)
1447
1448
this function get all borrowers who has an issue history older than I<$date> given on input arg.
1449
1450
I<$result> is a ref to an array which all elements are a hashref.
1451
This hashref is containt the number of time this borrowers has borrowed before I<$date> and the borrowernumber.
1452
1453
=cut
1454
1455
sub GetBorrowersWithIssuesHistoryOlderThan {
1456
    my $dbh  = C4::Context->dbh;
1457
    my $date = shift ||POSIX::strftime("%Y-%m-%d",localtime());
1458
    my $filterbranch = shift || 
1459
                        ((C4::Context->preference('IndependentBranches')
1460
                             && C4::Context->userenv 
1461
                             && !C4::Context->IsSuperLibrarian()
1462
                             && C4::Context->userenv->{branch})
1463
                         ? C4::Context->userenv->{branch}
1464
                         : "");  
1465
    my $query = "
1466
       SELECT count(borrowernumber) as n,borrowernumber
1467
       FROM old_issues
1468
       WHERE returndate < ?
1469
         AND borrowernumber IS NOT NULL 
1470
    "; 
1471
    my @query_params;
1472
    push @query_params, $date;
1473
    if ($filterbranch){
1474
        $query.="   AND branchcode = ?";
1475
        push @query_params, $filterbranch;
1476
    }    
1477
    $query.=" GROUP BY borrowernumber ";
1478
    warn $query if $debug;
1479
    my $sth = $dbh->prepare($query);
1480
    $sth->execute(@query_params);
1481
    my @results;
1482
1483
    while ( my $data = $sth->fetchrow_hashref ) {
1484
        push @results, $data;
1485
    }
1486
    return \@results;
1487
}
1488
1489
=head2 IssueSlip
1443
=head2 IssueSlip
1490
1444
1491
  IssueSlip($branchcode, $borrowernumber, $quickslip)
1445
  IssueSlip($branchcode, $borrowernumber, $quickslip)
(-)a/Koha/Patrons.pm (-1 / +58 lines)
Lines 1-6 Link Here
1
package Koha::Patrons;
1
package Koha::Patrons;
2
2
3
# Copyright ByWater Solutions 2014
3
# Copyright 2014 ByWater Solutions
4
# Copyright 2016 Koha Development Team
4
#
5
#
5
# This file is part of Koha.
6
# This file is part of Koha.
6
#
7
#
Lines 22-27 use Modern::Perl; Link Here
22
use Carp;
23
use Carp;
23
24
24
use Koha::Database;
25
use Koha::Database;
26
use Koha::DateUtils;
25
27
26
use Koha::ArticleRequests;
28
use Koha::ArticleRequests;
27
use Koha::ArticleRequest::Status;
29
use Koha::ArticleRequest::Status;
Lines 148-153 sub article_requests_finished { Link Here
148
    return $self->{_article_requests_finished};
150
    return $self->{_article_requests_finished};
149
}
151
}
150
152
153
=head3 search_patrons_to_anonymise
154
155
    my $patrons = Koha::Patrons->search_patrons_to_anonymise( $date );
156
157
This method returns all patrons who has an issue history older than a given date.
158
159
=cut
160
161
sub search_patrons_to_anonymise {
162
    my ( $class, $older_than_date, $library ) = @_;
163
    $older_than_date = $older_than_date ? dt_from_string($older_than_date) : dt_from_string;
164
    $library ||=
165
      ( C4::Context->preference('IndependentBranches') && C4::Context->userenv && !C4::Context->IsSuperLibrarian() && C4::Context->userenv->{branch} )
166
      ? C4::Context->userenv->{branch}
167
      : undef;
168
169
    my $dtf = Koha::Database->new->schema->storage->datetime_parser;
170
    my $rs = $class->search(
171
        {   returndate                  => { '<'   =>  $dtf->format_datetime($older_than_date), },
172
            'old_issues.borrowernumber' => { 'not' => undef },
173
            privacy                     => { '<>'  => 0 },                  # Keep forever
174
            ( $library ? ( 'old_issues.branchcode' => $library ) : () ),
175
        },
176
        {   join     => ["old_issues"],
177
            group_by => 'borrowernumber'
178
        }
179
    );
180
    return Koha::Patrons->_new_from_dbic($rs);
181
}
182
183
=head3 anonymise_issue_history
184
185
    Koha::Patrons->search->anonymise_issue_history( $older_than_date );
186
187
Anonymise issue history (old_issues) for all patrons older than the given date.
188
To make sure all the conditions are met, the caller has the responsability to
189
call search_patrons_to_anonymise to filter the Koha::Patrons set
190
191
=cut
192
193
sub anonymise_issue_history {
194
    my ( $self, $older_than_date ) = @_;
195
196
    return unless $older_than_date;
197
    $older_than_date = dt_from_string $older_than_date;
198
199
    # The default of 0 does not work due to foreign key constraints
200
    # The anonymisation should not fail quietly if AnonymousPatron is not a valid entry
201
    # Set it to undef (NULL)
202
    my $dtf = Koha::Database->new->schema->storage->datetime_parser;
203
    my $old_issues_to_anonymise = $self->search_related( 'old_issues', { returndate => { '<' => $dtf->format_datetime($older_than_date) } } );
204
    my $anonymous_patron = C4::Context->preference('AnonymousPatron') || undef;
205
    $old_issues_to_anonymise->update( { 'old_issues.borrowernumber' => $anonymous_patron } );
206
}
207
151
=head3 type
208
=head3 type
152
209
153
=cut
210
=cut
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/cleanborrowers.tt (-2 / +2 lines)
Lines 217-224 Link Here
217
                <h4>No patron records have been removed</h4>
217
                <h4>No patron records have been removed</h4>
218
            [% END %]
218
            [% END %]
219
        [% END %]
219
        [% END %]
220
        [% IF ( do_anonym ) %]
220
        [% IF do_anonym %]
221
            <h4>All checkouts older than [% last_issue_date | $KohaDates %] have been anonymized</h4>
221
            <h4>All checkouts ([% do_anonym %]) older than [% last_issue_date | $KohaDates %] have been anonymized</h4>
222
        [% ELSE %]
222
        [% ELSE %]
223
            <h4>No patron records have been anonymized</h4>
223
            <h4>No patron records have been anonymized</h4>
224
        [% END %]
224
        [% END %]
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-privacy.tt (-3 / +3 lines)
Lines 25-34 Link Here
25
                <div id="userprivacy">
25
                <div id="userprivacy">
26
                    <h3>Your privacy management</h3>
26
                    <h3>Your privacy management</h3>
27
27
28
                    [% IF ( deleted ) %]
28
                    [% IF deleted %]
29
                        <div class="alert alert-success">Your reading history has been deleted.</div>
29
                        <div class="alert alert-success">Your reading history has been deleted.</div>
30
                    [% ELSIF ( err_history_not_deleted ) %]
30
                    [% ELSE %]
31
                        <div class="alert">The deletion of your reading history failed, because there is a problem with the configuration of this feature. Please help to fix the system by informing your library of this error.</div>
31
                        <div class="alert">No reading history to delete</div>
32
                    [% END %]
32
                    [% END %]
33
33
34
                    [% IF ( privacy_updated ) %]
34
                    [% IF ( privacy_updated ) %]
(-)a/misc/cronjobs/batch_anonymise.pl (-4 / +3 lines)
Lines 30-36 BEGIN { Link Here
30
}
30
}
31
31
32
use C4::Context;
32
use C4::Context;
33
use C4::Circulation;
33
use Koha::Patrons;
34
use Date::Calc qw(
34
use Date::Calc qw(
35
  Today
35
  Today
36
  Add_Delta_Days
36
  Add_Delta_Days
Lines 74-81 my ($newyear,$newmonth,$newday) = Add_Delta_Days ($year,$month,$day,(-1)*$days); Link Here
74
my $formatdate = sprintf "%4d-%02d-%02d",$newyear,$newmonth,$newday;
74
my $formatdate = sprintf "%4d-%02d-%02d",$newyear,$newmonth,$newday;
75
$verbose and print "Checkouts before $formatdate will be anonymised.\n";
75
$verbose and print "Checkouts before $formatdate will be anonymised.\n";
76
76
77
my ($rows, $err_history_not_deleted) = AnonymiseIssueHistory($formatdate);
77
my $rows = Koha::Patrons->search_patrons_to_anonymise( $formatdate )->anonymise_issue_history( $formatdate );
78
carp "Anonymisation of reading history failed." if ($err_history_not_deleted);
78
$verbose and print int($rows) . " checkouts anonymised.\n";
79
$verbose and print "$rows checkouts anonymised.\n";
80
79
81
exit(0);
80
exit(0);
(-)a/opac/opac-privacy.pl (-10 / +5 lines)
Lines 21-27 use CGI qw ( -utf8 ); Link Here
21
21
22
use C4::Auth;    # checkauth, getborrowernumber.
22
use C4::Auth;    # checkauth, getborrowernumber.
23
use C4::Context;
23
use C4::Context;
24
use C4::Circulation;
25
use C4::Members;
24
use C4::Members;
26
use C4::Output;
25
use C4::Output;
27
use Koha::Patrons;
26
use Koha::Patrons;
Lines 62-77 elsif ( $op eq "delete_record" ) { Link Here
62
61
63
    # delete all reading records for items returned
62
    # delete all reading records for items returned
64
    # uses a hardcoded date ridiculously far in the future
63
    # uses a hardcoded date ridiculously far in the future
65
    my ( $rows, $err_history_not_deleted ) =
66
      AnonymiseIssueHistory( '2999-12-12', $borrowernumber );
67
64
68
    # confirm the user the deletion has been done
65
    my $rows = eval {
69
    if ( !$err_history_not_deleted ) {
66
        Koha::Patrons->search({ 'me.borrowernumber' => $borrowernumber })->anonymise_issue_history( '2999-12-12' );
70
        $template->param( 'deleted' => 1 );
67
    };
71
    }
68
    $rows = $@ ? 0 : int($rows);
72
    else {
69
    $template->param( 'deleted' => $rows );
73
        $template->param( 'err_history_not_deleted' => 1 );
74
    }
75
}
70
}
76
71
77
# get borrower privacy ....
72
# get borrower privacy ....
(-)a/tools/cleanborrowers.pl (-14 / +11 lines)
Lines 37-47 use CGI qw ( -utf8 ); Link Here
37
use C4::Auth;
37
use C4::Auth;
38
use C4::Output;
38
use C4::Output;
39
use C4::Members;        # GetBorrowersWhoHavexxxBorrowed.
39
use C4::Members;        # GetBorrowersWhoHavexxxBorrowed.
40
use C4::Circulation;    # AnonymiseIssueHistory.
41
use Koha::DateUtils qw( dt_from_string output_pref );
40
use Koha::DateUtils qw( dt_from_string output_pref );
42
use Koha::Patron::Categories;
41
use Koha::Patron::Categories;
43
use Koha::Patrons;
42
use Koha::Patrons;
44
use Date::Calc qw/Today Add_Delta_YM/;
43
use Date::Calc qw/Today Add_Delta_YM/;
44
use Koha::Patrons;
45
use Koha::List::Patron;
45
use Koha::List::Patron;
46
46
47
my $cgi = new CGI;
47
my $cgi = new CGI;
Lines 106-124 if ( $step == 2 ) { Link Here
106
    }
106
    }
107
    _skip_borrowers_with_nonzero_balance($patrons_to_delete);
107
    _skip_borrowers_with_nonzero_balance($patrons_to_delete);
108
108
109
    my $members_to_anonymize;
109
    my $patrons_to_anonymize =
110
    if ( $checkboxes{issue} ) {
110
        $checkboxes{issue}
111
        if ( $branch eq '*' ) {
111
      ? $branch eq '*'
112
            $members_to_anonymize = GetBorrowersWithIssuesHistoryOlderThan($last_issue_date);
112
          ? Koha::Patrons->search_patrons_to_anonymise($last_issue_date)
113
        } else {
113
          : Koha::Patrons->search_patrons_to_anonymise( $last_issue_date, $branch )
114
            $members_to_anonymize = GetBorrowersWithIssuesHistoryOlderThan($last_issue_date, $branch);
114
      : undef;
115
        }
116
    }
117
115
118
    $template->param(
116
    $template->param(
119
        patrons_to_delete    => $patrons_to_delete,
117
        patrons_to_delete    => $patrons_to_delete,
120
        patrons_to_anonymize => $members_to_anonymize,
118
        patrons_to_anonymize => $patrons_to_anonymize,
121
        patron_list_id       => $patron_list_id
119
        patron_list_id       => $patron_list_id,
122
    );
120
    );
123
}
121
}
124
122
Lines 160-168 elsif ( $step == 3 ) { Link Here
160
    # Anonymising all members
158
    # Anonymising all members
161
    if ($do_anonym) {
159
    if ($do_anonym) {
162
        #FIXME: anonymisation errors are not handled
160
        #FIXME: anonymisation errors are not handled
163
        ($totalAno,my $anonymisation_error) = AnonymiseIssueHistory($last_issue_date);
161
        my $rows = Koha::Patrons->search_patrons_to_anonymise( $last_issue_date )->anonymise_issue_history( $last_issue_date );
164
        $template->param(
162
        $template->param(
165
            do_anonym   => '1',
163
            do_anonym   => $rows,
166
        );
164
        );
167
    }
165
    }
168
166
169
- 

Return to bug 16966