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

(-)a/C4/Circulation.pm (-44 lines)
Lines 97-103 BEGIN { Link Here
97
        &GetBranchItemRule
97
        &GetBranchItemRule
98
		&GetBiblioIssues
98
		&GetBiblioIssues
99
		&GetOpenIssue
99
		&GetOpenIssue
100
		&AnonymiseIssueHistory
101
        &CheckIfIssuedToPatron
100
        &CheckIfIssuedToPatron
102
        &IsItemIssued
101
        &IsItemIssued
103
        GetTopIssues
102
        GetTopIssues
Lines 3318-3366 sub DeleteTransfer { Link Here
3318
    return $sth->execute($itemnumber);
3317
    return $sth->execute($itemnumber);
3319
}
3318
}
3320
3319
3321
=head2 AnonymiseIssueHistory
3322
3323
  ($rows,$err_history_not_deleted) = AnonymiseIssueHistory($date,$borrowernumber)
3324
3325
This function write NULL instead of C<$borrowernumber> given on input arg into the table issues.
3326
if C<$borrowernumber> is not set, it will delete the issue history for all borrower older than C<$date>.
3327
3328
If c<$borrowernumber> is set, it will delete issue history for only that borrower, regardless of their opac privacy
3329
setting (force delete).
3330
3331
return the number of affected rows and a value that evaluates to true if an error occurred deleting the history.
3332
3333
=cut
3334
3335
sub AnonymiseIssueHistory {
3336
    my $date           = shift;
3337
    my $borrowernumber = shift;
3338
    my $dbh            = C4::Context->dbh;
3339
    my $query          = "
3340
        UPDATE old_issues
3341
        SET    borrowernumber = ?
3342
        WHERE  returndate < ?
3343
          AND borrowernumber IS NOT NULL
3344
    ";
3345
3346
    # The default of 0 does not work due to foreign key constraints
3347
    # The anonymisation should not fail quietly if AnonymousPatron is not a valid entry
3348
    # Set it to undef (NULL)
3349
    my $anonymouspatron = C4::Context->preference('AnonymousPatron') || undef;
3350
    my @bind_params = ($anonymouspatron, $date);
3351
    if (defined $borrowernumber) {
3352
       $query .= " AND borrowernumber = ?";
3353
       push @bind_params, $borrowernumber;
3354
    } else {
3355
       $query .= " AND (SELECT privacy FROM borrowers WHERE borrowers.borrowernumber=old_issues.borrowernumber) <> 0";
3356
    }
3357
    my $sth = $dbh->prepare($query);
3358
    $sth->execute(@bind_params);
3359
    my $anonymisation_err = $dbh->err;
3360
    my $rows_affected = $sth->rows;  ### doublecheck row count return function
3361
    return ($rows_affected, $anonymisation_err);
3362
}
3363
3364
=head2 SendCirculationAlert
3320
=head2 SendCirculationAlert
3365
3321
3366
Send out a C<check-in> or C<checkout> alert using the messaging system.
3322
Send out a C<check-in> or C<checkout> alert using the messaging system.
(-)a/C4/Members.pm (-46 lines)
Lines 83-89 BEGIN { Link Here
83
83
84
        &GetBorrowersToExpunge
84
        &GetBorrowersToExpunge
85
        &GetBorrowersWhoHaveNeverBorrowed
85
        &GetBorrowersWhoHaveNeverBorrowed
86
        &GetBorrowersWithIssuesHistoryOlderThan
87
86
88
        &GetUpcomingMembershipExpires
87
        &GetUpcomingMembershipExpires
89
88
Lines 1599-1649 sub GetBorrowersWhoHaveNeverBorrowed { Link Here
1599
    return \@results;
1598
    return \@results;
1600
}
1599
}
1601
1600
1602
=head2 GetBorrowersWithIssuesHistoryOlderThan
1603
1604
  $results = &GetBorrowersWithIssuesHistoryOlderThan($date)
1605
1606
this function get all borrowers who has an issue history older than I<$date> given on input arg.
1607
1608
I<$result> is a ref to an array which all elements are a hashref.
1609
This hashref is containt the number of time this borrowers has borrowed before I<$date> and the borrowernumber.
1610
1611
=cut
1612
1613
sub GetBorrowersWithIssuesHistoryOlderThan {
1614
    my $dbh  = C4::Context->dbh;
1615
    my $date = shift ||POSIX::strftime("%Y-%m-%d",localtime());
1616
    my $filterbranch = shift || 
1617
                        ((C4::Context->preference('IndependentBranches')
1618
                             && C4::Context->userenv 
1619
                             && !C4::Context->IsSuperLibrarian()
1620
                             && C4::Context->userenv->{branch})
1621
                         ? C4::Context->userenv->{branch}
1622
                         : "");  
1623
    my $query = "
1624
       SELECT count(borrowernumber) as n,borrowernumber
1625
       FROM old_issues
1626
       WHERE returndate < ?
1627
         AND borrowernumber IS NOT NULL 
1628
    "; 
1629
    my @query_params;
1630
    push @query_params, $date;
1631
    if ($filterbranch){
1632
        $query.="   AND branchcode = ?";
1633
        push @query_params, $filterbranch;
1634
    }    
1635
    $query.=" GROUP BY borrowernumber ";
1636
    warn $query if $debug;
1637
    my $sth = $dbh->prepare($query);
1638
    $sth->execute(@query_params);
1639
    my @results;
1640
1641
    while ( my $data = $sth->fetchrow_hashref ) {
1642
        push @results, $data;
1643
    }
1644
    return \@results;
1645
}
1646
1647
=head2 IssueSlip
1601
=head2 IssueSlip
1648
1602
1649
  IssueSlip($branchcode, $borrowernumber, $quickslip)
1603
  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::Patron;
28
use Koha::Patron;
27
29
Lines 37-42 Koha::Patron - Koha Patron Object class Link Here
37
39
38
=cut
40
=cut
39
41
42
=head3 search_patrons_to_anonymise
43
44
    my $patrons = Koha::Patrons->search_patrons_to_anonymise( $date );
45
46
This method returns all patrons who has an issue history older than a given date.
47
48
=cut
49
50
sub search_patrons_to_anonymise {
51
    my ( $class, $older_than_date, $library ) = @_;
52
    $older_than_date = $older_than_date ? dt_from_string($older_than_date) : dt_from_string;
53
    $library ||=
54
      ( C4::Context->preference('IndependentBranches') && C4::Context->userenv && !C4::Context->IsSuperLibrarian() && C4::Context->userenv->{branch} )
55
      ? C4::Context->userenv->{branch}
56
      : undef;
57
58
    my $dtf = Koha::Database->new->schema->storage->datetime_parser;
59
    my $rs = $class->search(
60
        {   returndate                  => { '<'   =>  $dtf->format_datetime($older_than_date), },
61
            'old_issues.borrowernumber' => { 'not' => undef },
62
            privacy                     => { '<>'  => 0 },                  # Keep forever
63
            ( $library ? ( 'old_issues.branchcode' => $library ) : () ),
64
        },
65
        {   join     => ["old_issues"],
66
            group_by => 'borrowernumber'
67
        }
68
    );
69
    return Koha::Patrons->_new_from_dbic($rs);
70
}
71
72
=head3 anonymise_issue_history
73
74
    Koha::Patrons->search->anonymise_issue_history( $older_than_date );
75
76
Anonymise issue history (old_issues) for all patrons older than the given date.
77
To make sure all the conditions are met, the caller has the responsability to
78
call search_patrons_to_anonymise to filter the Koha::Patrons set
79
80
=cut
81
82
sub anonymise_issue_history {
83
    my ( $self, $older_than_date ) = @_;
84
85
    return unless $older_than_date;
86
    $older_than_date = dt_from_string $older_than_date;
87
88
    # The default of 0 does not work due to foreign key constraints
89
    # The anonymisation should not fail quietly if AnonymousPatron is not a valid entry
90
    # Set it to undef (NULL)
91
    my $dtf = Koha::Database->new->schema->storage->datetime_parser;
92
    my $old_issues_to_anonymise = $self->search_related( 'old_issues', { returndate => { '<' => $dtf->format_datetime($older_than_date) } } );
93
    my $anonymous_patron = C4::Context->preference('AnonymousPatron') || undef;
94
    $old_issues_to_anonymise->update( { 'old_issues.borrowernumber' => $anonymous_patron } );
95
}
96
40
=head3 type
97
=head3 type
41
98
42
=cut
99
=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-46 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 Date::Calc qw/Today Add_Delta_YM/;
42
use Date::Calc qw/Today Add_Delta_YM/;
43
use Koha::Patrons;
44
use Koha::List::Patron;
44
use Koha::List::Patron;
45
45
46
my $cgi = new CGI;
46
my $cgi = new CGI;
Lines 105-123 if ( $step == 2 ) { Link Here
105
    }
105
    }
106
    _skip_borrowers_with_nonzero_balance($patrons_to_delete);
106
    _skip_borrowers_with_nonzero_balance($patrons_to_delete);
107
107
108
    my $members_to_anonymize;
108
    my $patrons_to_anonymize =
109
    if ( $checkboxes{issue} ) {
109
        $checkboxes{issue}
110
        if ( $branch eq '*' ) {
110
      ? $branch eq '*'
111
            $members_to_anonymize = GetBorrowersWithIssuesHistoryOlderThan($last_issue_date);
111
          ? Koha::Patrons->search_patrons_to_anonymise($last_issue_date)
112
        } else {
112
          : Koha::Patrons->search_patrons_to_anonymise( $last_issue_date, $branch )
113
            $members_to_anonymize = GetBorrowersWithIssuesHistoryOlderThan($last_issue_date, $branch);
113
      : undef;
114
        }
115
    }
116
114
117
    $template->param(
115
    $template->param(
118
        patrons_to_delete    => $patrons_to_delete,
116
        patrons_to_delete    => $patrons_to_delete,
119
        patrons_to_anonymize => $members_to_anonymize,
117
        patrons_to_anonymize => $patrons_to_anonymize,
120
        patron_list_id       => $patron_list_id
118
        patron_list_id       => $patron_list_id,
121
    );
119
    );
122
}
120
}
123
121
Lines 159-167 elsif ( $step == 3 ) { Link Here
159
    # Anonymising all members
157
    # Anonymising all members
160
    if ($do_anonym) {
158
    if ($do_anonym) {
161
        #FIXME: anonymisation errors are not handled
159
        #FIXME: anonymisation errors are not handled
162
        ($totalAno,my $anonymisation_error) = AnonymiseIssueHistory($last_issue_date);
160
        my $rows = Koha::Patrons->search_patrons_to_anonymise( $last_issue_date )->anonymise_issue_history( $last_issue_date );
163
        $template->param(
161
        $template->param(
164
            do_anonym   => '1',
162
            do_anonym   => $rows,
165
        );
163
        );
166
    }
164
    }
167
165
168
- 

Return to bug 16966