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 3271-3319 sub DeleteTransfer { Link Here
3271
    return $sth->execute($itemnumber);
3270
    return $sth->execute($itemnumber);
3272
}
3271
}
3273
3272
3274
=head2 AnonymiseIssueHistory
3275
3276
  ($rows,$err_history_not_deleted) = AnonymiseIssueHistory($date,$borrowernumber)
3277
3278
This function write NULL instead of C<$borrowernumber> given on input arg into the table issues.
3279
if C<$borrowernumber> is not set, it will delete the issue history for all borrower older than C<$date>.
3280
3281
If c<$borrowernumber> is set, it will delete issue history for only that borrower, regardless of their opac privacy
3282
setting (force delete).
3283
3284
return the number of affected rows and a value that evaluates to true if an error occurred deleting the history.
3285
3286
=cut
3287
3288
sub AnonymiseIssueHistory {
3289
    my $date           = shift;
3290
    my $borrowernumber = shift;
3291
    my $dbh            = C4::Context->dbh;
3292
    my $query          = "
3293
        UPDATE old_issues
3294
        SET    borrowernumber = ?
3295
        WHERE  returndate < ?
3296
          AND borrowernumber IS NOT NULL
3297
    ";
3298
3299
    # The default of 0 does not work due to foreign key constraints
3300
    # The anonymisation should not fail quietly if AnonymousPatron is not a valid entry
3301
    # Set it to undef (NULL)
3302
    my $anonymouspatron = C4::Context->preference('AnonymousPatron') || undef;
3303
    my @bind_params = ($anonymouspatron, $date);
3304
    if (defined $borrowernumber) {
3305
       $query .= " AND borrowernumber = ?";
3306
       push @bind_params, $borrowernumber;
3307
    } else {
3308
       $query .= " AND (SELECT privacy FROM borrowers WHERE borrowers.borrowernumber=old_issues.borrowernumber) <> 0";
3309
    }
3310
    my $sth = $dbh->prepare($query);
3311
    $sth->execute(@bind_params);
3312
    my $anonymisation_err = $dbh->err;
3313
    my $rows_affected = $sth->rows;  ### doublecheck row count return function
3314
    return ($rows_affected, $anonymisation_err);
3315
}
3316
3317
=head2 SendCirculationAlert
3273
=head2 SendCirculationAlert
3318
3274
3319
Send out a C<check-in> or C<checkout> alert using the messaging system.
3275
Send out a C<check-in> or C<checkout> alert using the messaging system.
(-)a/C4/Members.pm (-46 lines)
Lines 72-78 BEGIN { Link Here
72
        &GetBorNotifyAcctRecord
72
        &GetBorNotifyAcctRecord
73
73
74
        &GetBorrowersToExpunge
74
        &GetBorrowersToExpunge
75
        &GetBorrowersWithIssuesHistoryOlderThan
76
75
77
        &IssueSlip
76
        &IssueSlip
78
        GetBorrowersWithEmail
77
        GetBorrowersWithEmail
Lines 1113-1163 sub GetBorrowersToExpunge { Link Here
1113
    return \@results;
1112
    return \@results;
1114
}
1113
}
1115
1114
1116
=head2 GetBorrowersWithIssuesHistoryOlderThan
1117
1118
  $results = &GetBorrowersWithIssuesHistoryOlderThan($date)
1119
1120
this function get all borrowers who has an issue history older than I<$date> given on input arg.
1121
1122
I<$result> is a ref to an array which all elements are a hashref.
1123
This hashref is containt the number of time this borrowers has borrowed before I<$date> and the borrowernumber.
1124
1125
=cut
1126
1127
sub GetBorrowersWithIssuesHistoryOlderThan {
1128
    my $dbh  = C4::Context->dbh;
1129
    my $date = shift ||POSIX::strftime("%Y-%m-%d",localtime());
1130
    my $filterbranch = shift || 
1131
                        ((C4::Context->preference('IndependentBranches')
1132
                             && C4::Context->userenv 
1133
                             && !C4::Context->IsSuperLibrarian()
1134
                             && C4::Context->userenv->{branch})
1135
                         ? C4::Context->userenv->{branch}
1136
                         : "");  
1137
    my $query = "
1138
       SELECT count(borrowernumber) as n,borrowernumber
1139
       FROM old_issues
1140
       WHERE returndate < ?
1141
         AND borrowernumber IS NOT NULL 
1142
    "; 
1143
    my @query_params;
1144
    push @query_params, $date;
1145
    if ($filterbranch){
1146
        $query.="   AND branchcode = ?";
1147
        push @query_params, $filterbranch;
1148
    }    
1149
    $query.=" GROUP BY borrowernumber ";
1150
    warn $query if $debug;
1151
    my $sth = $dbh->prepare($query);
1152
    $sth->execute(@query_params);
1153
    my @results;
1154
1155
    while ( my $data = $sth->fetchrow_hashref ) {
1156
        push @results, $data;
1157
    }
1158
    return \@results;
1159
}
1160
1161
=head2 IssueSlip
1115
=head2 IssueSlip
1162
1116
1163
  IssueSlip($branchcode, $borrowernumber, $quickslip)
1117
  IssueSlip($branchcode, $borrowernumber, $quickslip)
(-)a/Koha/Patrons.pm (-1 / +57 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 181-186 sub article_requests_finished { Link Here
181
    return $self->{_article_requests_finished};
182
    return $self->{_article_requests_finished};
182
}
183
}
183
184
185
=head3 search_patrons_to_anonymise
186
187
    my $patrons = Koha::Patrons->search_patrons_to_anonymise( $date );
188
189
This method returns all patrons who has an issue history older than a given date.
190
191
=cut
192
193
sub search_patrons_to_anonymise {
194
    my ( $class, $older_than_date, $library ) = @_;
195
    $older_than_date = $older_than_date ? dt_from_string($older_than_date) : dt_from_string;
196
    $library ||=
197
      ( C4::Context->preference('IndependentBranches') && C4::Context->userenv && !C4::Context->IsSuperLibrarian() && C4::Context->userenv->{branch} )
198
      ? C4::Context->userenv->{branch}
199
      : undef;
200
201
    my $dtf = Koha::Database->new->schema->storage->datetime_parser;
202
    my $rs = $class->search(
203
        {   returndate                  => { '<'   =>  $dtf->format_datetime($older_than_date), },
204
            'old_issues.borrowernumber' => { 'not' => undef },
205
            privacy                     => { '<>'  => 0 },                  # Keep forever
206
            ( $library ? ( 'old_issues.branchcode' => $library ) : () ),
207
        },
208
        {   join     => ["old_issues"],
209
            group_by => 'borrowernumber'
210
        }
211
    );
212
    return Koha::Patrons->_new_from_dbic($rs);
213
}
214
215
=head3 anonymise_issue_history
216
217
    Koha::Patrons->search->anonymise_issue_history( $older_than_date );
218
219
Anonymise issue history (old_issues) for all patrons older than the given date.
220
To make sure all the conditions are met, the caller has the responsability to
221
call search_patrons_to_anonymise to filter the Koha::Patrons set
222
223
=cut
224
225
sub anonymise_issue_history {
226
    my ( $self, $older_than_date ) = @_;
227
228
    return unless $older_than_date;
229
    $older_than_date = dt_from_string $older_than_date;
230
231
    # The default of 0 does not work due to foreign key constraints
232
    # The anonymisation should not fail quietly if AnonymousPatron is not a valid entry
233
    # Set it to undef (NULL)
234
    my $dtf = Koha::Database->new->schema->storage->datetime_parser;
235
    my $old_issues_to_anonymise = $self->search_related( 'old_issues', { returndate => { '<' => $dtf->format_datetime($older_than_date) } } );
236
    my $anonymous_patron = C4::Context->preference('AnonymousPatron') || undef;
237
    $old_issues_to_anonymise->update( { 'old_issues.borrowernumber' => $anonymous_patron } );
238
}
239
184
=head3 type
240
=head3 type
185
241
186
=cut
242
=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 (-13 / +11 lines)
Lines 42-47 use Koha::DateUtils qw( dt_from_string output_pref ); Link Here
42
use Koha::Patron::Categories;
42
use Koha::Patron::Categories;
43
use Koha::Patrons;
43
use Koha::Patrons;
44
use Date::Calc qw/Today Add_Delta_YM/;
44
use Date::Calc qw/Today Add_Delta_YM/;
45
use Koha::Patrons;
45
use Koha::List::Patron;
46
use Koha::List::Patron;
46
47
47
my $cgi = new CGI;
48
my $cgi = new CGI;
Lines 106-124 if ( $step == 2 ) { Link Here
106
    }
107
    }
107
    _skip_borrowers_with_nonzero_balance($patrons_to_delete);
108
    _skip_borrowers_with_nonzero_balance($patrons_to_delete);
108
109
109
    my $members_to_anonymize;
110
    my $patrons_to_anonymize =
110
    if ( $checkboxes{issue} ) {
111
        $checkboxes{issue}
111
        if ( $branch eq '*' ) {
112
      ? $branch eq '*'
112
            $members_to_anonymize = GetBorrowersWithIssuesHistoryOlderThan($last_issue_date);
113
          ? Koha::Patrons->search_patrons_to_anonymise($last_issue_date)
113
        } else {
114
          : Koha::Patrons->search_patrons_to_anonymise( $last_issue_date, $branch )
114
            $members_to_anonymize = GetBorrowersWithIssuesHistoryOlderThan($last_issue_date, $branch);
115
      : undef;
115
        }
116
    }
117
116
118
    $template->param(
117
    $template->param(
119
        patrons_to_delete    => $patrons_to_delete,
118
        patrons_to_delete    => $patrons_to_delete,
120
        patrons_to_anonymize => $members_to_anonymize,
119
        patrons_to_anonymize => $patrons_to_anonymize,
121
        patron_list_id       => $patron_list_id
120
        patron_list_id       => $patron_list_id,
122
    );
121
    );
123
}
122
}
124
123
Lines 160-168 elsif ( $step == 3 ) { Link Here
160
    # Anonymising all members
159
    # Anonymising all members
161
    if ($do_anonym) {
160
    if ($do_anonym) {
162
        #FIXME: anonymisation errors are not handled
161
        #FIXME: anonymisation errors are not handled
163
        ($totalAno,my $anonymisation_error) = AnonymiseIssueHistory($last_issue_date);
162
        my $rows = Koha::Patrons->search_patrons_to_anonymise( $last_issue_date )->anonymise_issue_history( $last_issue_date );
164
        $template->param(
163
        $template->param(
165
            do_anonym   => '1',
164
            do_anonym   => $rows,
166
        );
165
        );
167
    }
166
    }
168
167
169
- 

Return to bug 16966