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

(-)a/Koha/Old/Patron.pm (+39 lines)
Lines 29-34 Koha::Old::Patron - Koha Old::Patron Object class Link Here
29
29
30
=cut
30
=cut
31
31
32
=head3 restore_deleted_borrower
33
34
    my $patron = $old_patron->restore_deleted_borrower;
35
36
Restores a deleted patron from the deletedborrowers table back to the borrowers table.
37
This only restores the patron account record itself, not any associated historical data.
38
The patron will retain their original borrowernumber.
39
40
Returns the restored Koha::Patron object on success.
41
Throws an exception on failure.
42
43
=cut
44
45
sub restore_deleted_borrower {
46
    my ($self) = @_;
47
48
    my $schema = Koha::Database->new->schema;
49
    my $restored_patron;
50
51
    $schema->txn_do(
52
        sub {
53
            # Retrive all the data about this patron from deleteborrowers table
54
            my $patron_data = $self->unblessed;
55
56
            # Create the Koha::Patron object
57
            my $patron = Koha::Patron->new($patron_data);
58
59
            # Create the "new" patron using SUPER::store to bypass any Koha::Patron->store() and restore the patron as it was
60
            $restored_patron = $patron->SUPER::store();
61
62
            # Delete the entry from deletedborrowers
63
            Koha::Old::Patrons->search( { borrowernumber => $patron_data->{borrowernumber} } )->delete;
64
65
        }
66
    );
67
68
    return $restored_patron;
69
}
70
32
=head2 Internal methods
71
=head2 Internal methods
33
72
34
=head3 _type
73
=head3 _type
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/restore_deleted_borrowers.tt (+241 lines)
Line 0 Link Here
1
[% USE raw %]
2
[% USE Asset %]
3
[% SET footerjs = 1 %]
4
[% USE Branches %]
5
[% USE Categories %]
6
[% USE KohaDates %]
7
[% PROCESS 'html_helpers.inc' %]
8
[% PROCESS 'i18n.inc' %]
9
[% INCLUDE 'doc-head-open.inc' %]
10
[% FILTER collapse %]
11
    <title>[% t("Restore deleted patrons") | html %] &rsaquo; [% t("Tools") | html %] &rsaquo; [% t("Koha") | html %]</title>
12
[% END %]
13
[% INCLUDE 'doc-head-close.inc' %]
14
</head>
15
16
<body id="tools_restore_deleted_borrowers" class="tools">
17
[% WRAPPER 'header.inc' %]
18
    [% INCLUDE 'patron-search-header.inc' %]
19
[% END %]
20
[% WRAPPER 'sub-header.inc' %]
21
    [% WRAPPER breadcrumbs %]
22
        [% WRAPPER breadcrumb_item %]
23
            <a href="/cgi-bin/koha/tools/tools-home.pl">Tools</a>
24
        [% END %]
25
        [% IF ( view == 'restored' ) %]
26
            [% WRAPPER breadcrumb_item %]
27
                <a href="/cgi-bin/koha/tools/restore_deleted_borrowers.pl">Restore deleted patrons</a>
28
            [% END %]
29
            [% WRAPPER breadcrumb_item bc_active=1 %]
30
                <span>Restored patrons</span>
31
            [% END %]
32
        [% ELSE %]
33
            [% WRAPPER breadcrumb_item bc_active=1 %]
34
                <span>Restore deleted patrons</span>
35
            [% END %]
36
        [% END %]
37
    [% END #/ WRAPPER breadcrumbs %]
38
[% END #/ WRAPPER sub-header.inc %]
39
40
<div class="main container-fluid">
41
    <div class="row">
42
        <div class="col-sm-10 col-sm-push-2 order-sm-1">
43
            <main>
44
                [% IF view == "search" %]
45
                    <h1>Restore deleted patrons</h1>
46
47
                    <div id="search_deleted_borrowers">
48
                        <form method="get" action="/cgi-bin/koha/tools/restore_deleted_borrowers.pl" id="search_form">
49
                            <fieldset class="rows">
50
                                <legend>Search for deleted patrons:</legend>
51
                                <ol>
52
                                    <li>
53
                                        <label for="cardnumber">Cardnumber:</label>
54
                                        <input type="text" id="cardnumber" name="cardnumber" value="[% cardnumber | html %]" />
55
                                    </li>
56
                                    <li>
57
                                        <label for="borrowernumber">Borrowernumber:</label>
58
                                        <input type="text" id="borrowernumber" name="borrowernumber" value="[% borrowernumber | html %]" />
59
                                    </li>
60
                                    <li>
61
                                        <label for="firstname">First name:</label>
62
                                        <input type="text" id="firstname" name="firstname" value="[% firstname | html %]" />
63
                                    </li>
64
                                    <li>
65
                                        <label for="surname">Surname:</label>
66
                                        <input type="text" id="surname" name="surname" value="[% surname | html %]" />
67
                                    </li>
68
                                    <li>
69
                                        <label for="categorycode">Patron category:</label>
70
                                        <select name="categorycode" id="categorycode">
71
                                            <option value="">All categories</option>
72
                                            [% FOREACH cat IN Categories.all() %]
73
                                                <option value="[% cat.categorycode | html %]"> [% cat.description | html %] </option>
74
                                            [% END %]
75
                                        </select>
76
                                    </li>
77
                                    <li>
78
                                        <label for="branchcode">Library:</label>
79
                                        <select name="branchcode" id="branchcode">
80
                                            <option value="">All libraries</option>
81
                                            [% PROCESS options_for_libraries libraries => Branches.all() %]
82
                                        </select>
83
                                    </li>
84
                                </ol>
85
                            </fieldset>
86
                            <fieldset class="action">
87
                                <input type="hidden" name="op" value="search" />
88
                                <input type="submit" class="btn btn-primary" value="Search" />
89
                            </fieldset>
90
                        </form>
91
                    </div>
92
                [% ELSIF view == "results" %]
93
                    <h1>Restore deleted patrons</h1>
94
95
                    [% IF deleted_patrons.size > 0 %]
96
                        <div id="results_wrapper" class="page-section">
97
                            <h3>Deleted patrons found: [% deleted_patrons.size | html %]</h3>
98
99
                            <form method="post" action="/cgi-bin/koha/tools/restore_deleted_borrowers.pl" id="restore_form">
100
                                [% INCLUDE 'csrf-token.inc' %]
101
                                <input type="hidden" name="op" value="cud-restore" />
102
103
                                <table id="deleted_patrons_table">
104
                                    <thead>
105
                                        <tr>
106
                                            <th><input type="checkbox" id="select_all" /></th>
107
                                            <th>Card number</th>
108
                                            <th>Borrowernumber</th>
109
                                            <th>Surname</th>
110
                                            <th>First name</th>
111
                                            <th>Category</th>
112
                                            <th>Library</th>
113
                                            <th>Date deleted</th>
114
                                        </tr>
115
                                    </thead>
116
                                    <tbody>
117
                                        [% FOREACH patron IN deleted_patrons %]
118
                                            <tr>
119
                                                <td>
120
                                                    <input type="checkbox" name="borrowernumber" value="[% patron.borrowernumber | html %]" />
121
                                                </td>
122
                                                <td>[% patron.cardnumber | html %]</td>
123
                                                <td>[% patron.borrowernumber | html %]</td>
124
                                                <td>[% patron.surname | html %]</td>
125
                                                <td>[% patron.firstname | html %]</td>
126
                                                <td>[% patron.category.description | html %]</td>
127
                                                <td>[% patron.library.branchname | html %]</td>
128
                                                <td>[% patron.updated_on | $KohaDates %]</td>
129
                                            </tr>
130
                                        [% END %]
131
                                    </tbody>
132
                                </table>
133
134
                                <fieldset class="action">
135
                                    <input type="submit" class="btn btn-primary" value="Restore selected patrons" id="restore_button" />
136
                                </fieldset>
137
                            </form>
138
                        </div>
139
                    [% ELSE %]
140
                        <div class="dialog message"> No deleted patrons found matching your search criteria. </div>
141
                        <fieldset class="action">
142
                            <a class="btn btn-default" href="/cgi-bin/koha/tools/restore_deleted_borrowers.pl">New search</a>
143
                        </fieldset>
144
                    [% END %]
145
                [% ELSIF view == "restored" %]
146
                    <h1>Patrons restored</h1>
147
148
                    [% IF restored_patrons.size > 0 %]
149
                        <div class="dialog message">
150
                            <p>Successfully restored [% restored_patrons.size | html %] patron(s).</p>
151
                        </div>
152
153
                        <div id="restored_results" class="page-section">
154
                            <table id="restored_patrons_table">
155
                                <thead>
156
                                    <tr>
157
                                        <th>Card number</th>
158
                                        <th>Borrowernumber</th>
159
                                        <th>Name</th>
160
                                        <th>Category</th>
161
                                        <th>Library</th>
162
                                        <th>Actions</th>
163
                                    </tr>
164
                                </thead>
165
                                <tbody>
166
                                    [% FOREACH patron IN restored_patrons %]
167
                                        <tr>
168
                                            <td>[% patron.cardnumber | html %]</td>
169
                                            <td>[% patron.borrowernumber | html %]</td>
170
                                            <td>[% patron.firstname | html %] [% patron.surname | html %]</td>
171
                                            <td>[% patron.category.description | html %]</td>
172
                                            <td>[% patron.library.branchname | html %]</td>
173
                                            <td>
174
                                                <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% patron.borrowernumber | uri %]" class="btn btn-default btn-xs"> <i class="fa fa-user"></i> View patron </a>
175
                                            </td>
176
                                        </tr>
177
                                    [% END %]
178
                                </tbody>
179
                            </table>
180
                        </div>
181
                    [% END %]
182
183
                    [% IF errors.size > 0 %]
184
                        <div class="dialog alert">
185
                            <h3>Errors occurred:</h3>
186
                            <ul>
187
                                [% FOREACH error IN errors %]
188
                                    <li>[% error | html %]</li>
189
                                [% END %]
190
                            </ul>
191
                        </div>
192
                    [% END %]
193
194
                    <fieldset class="action">
195
                        <a class="btn btn-default" href="/cgi-bin/koha/tools/restore_deleted_borrowers.pl">Restore more patrons</a>
196
                    </fieldset>
197
                [% END %]
198
            </main>
199
        </div>
200
        <!-- /.col-sm-10.col-sm-push-2 -->
201
202
        <div class="col-sm-2 col-sm-pull-10">
203
            <aside> [% INCLUDE 'tools-menu.inc' %] </aside>
204
        </div>
205
        <!-- /.col-sm-2.col-sm-pull-10 -->
206
    </div>
207
    <!-- /.row -->
208
</div>
209
<!-- /.main.container-fluid -->
210
211
[% MACRO jsinclude BLOCK %]
212
    [% Asset.js("js/tools-menu.js") | $raw %]
213
    [% INCLUDE 'datatables.inc' %]
214
    <script>
215
        $(document).ready(function() {
216
            // Select all checkbox functionality
217
            $("#select_all").on("click", function() {
218
                $("#deleted_patrons_table tbody input[type='checkbox']").prop('checked', this.checked);
219
            });
220
221
            // Initialize #deleted_patrons_table table
222
            [% IF view == "results" && deleted_patrons.size > 0 %]
223
                $("#deleted_patrons_table").kohaTable({
224
                    "paging": true,
225
                    "order": [[1, 'asc']]
226
                });
227
            [% END %]
228
229
            // Initialize #restored_patrons_table table
230
            [% IF view == "restored" && restored_patrons.size > 0 %]
231
                $("#restored_patrons_table").kohaTable({
232
                    "paging": true,
233
                    "order": [[1, 'asc']]
234
                });
235
            [% END %]
236
237
        });
238
    </script>
239
[% END %]
240
241
[% INCLUDE 'intranet-bottom.inc' %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/tools-home.tt (+5 lines)
Lines 43-48 Link Here
43
                    <dd>Manage patron clubs</dd>
43
                    <dd>Manage patron clubs</dd>
44
                [% END %]
44
                [% END %]
45
45
46
                [% IF (CAN_user_tools_restore_deleted_borrowers) %]
47
                    <dt><a href="/cgi-bin/koha/tools/restore_deleted_borrowers.pl">Restore deleted patrons</a></dt>
48
                    <dd>Restore deleted patrons from the deletedborrowers table</dd>
49
                [% END %]
50
46
                [% IF ( CAN_user_tools_moderate_comments ) %]
51
                [% IF ( CAN_user_tools_moderate_comments ) %]
47
                    <dt>
52
                    <dt>
48
                        <a href="/cgi-bin/koha/reviews/reviewswaiting.pl">Comments</a>
53
                        <a href="/cgi-bin/koha/reviews/reviewswaiting.pl">Comments</a>
(-)a/tools/restore_deleted_borrowers.pl (-1 / +154 lines)
Line 0 Link Here
0
- 
1
#!/usr/bin/perl
2
3
# This file is part of Koha.
4
#
5
# Copyright 2024 Koha Development Team
6
#
7
# Koha is free software; you can redistribute it and/or modify
8
# it under the terms of the GNU General Public License as
9
# published by the Free Software Foundation; either version 3
10
# of the License, or (at your option) any later version.
11
#
12
# Koha is distributed in the hope that it will be useful, but
13
# WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General
18
# Public License along with Koha; if not, see
19
# <https://www.gnu.org/licenses>
20
21
use Modern::Perl;
22
use CGI qw ( -utf8 );
23
use Try::Tiny;
24
use Scalar::Util qw( blessed );
25
26
use C4::Auth   qw( get_template_and_user );
27
use C4::Output qw( output_html_with_http_headers );
28
use C4::Context;
29
30
use Koha::Old::Patrons;
31
use Koha::Patron::Categories;
32
use Koha::Libraries;
33
34
my $input = CGI->new;
35
my $op    = $input->param('op') || 'search';
36
37
my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
38
    {
39
        template_name => "tools/restore_deleted_borrowers.tt",
40
        query         => $input,
41
        type          => "intranet",
42
        flagsrequired => { tools => 'restore_deleted_patrons' },
43
    }
44
);
45
46
if ( $op eq 'search' ) {
47
48
    # Get search parameters
49
    my $cardnumber     = $input->param('cardnumber');
50
    my $borrowernumber = $input->param('borrowernumber');
51
    my $surname        = $input->param('surname');
52
    my $firstname      = $input->param('firstname');
53
    my $email          = $input->param('email');
54
    my $categorycode   = $input->param('categorycode');
55
    my $branchcode     = $input->param('branchcode');
56
57
    # No empty searches, if no search critrea is added, do nothing.
58
    if ( $cardnumber || $borrowernumber || $surname || $firstname || $email || $categorycode || $branchcode ) {
59
60
        # empty search parameters
61
        my %search_params;
62
63
        # these params should be exact matches
64
        $search_params{cardnumber}     = $cardnumber     if $cardnumber;
65
        $search_params{borrowernumber} = $borrowernumber if $borrowernumber;
66
        $search_params{categorycode}   = $categorycode   if $categorycode;
67
        $search_params{branchcode}     = $branchcode     if $branchcode;
68
69
        # these params don't necessarily have to be exact matches
70
        $search_params{surname}   = { 'like' => "%$surname%" }   if $surname;
71
        $search_params{firstname} = { 'like' => "%$firstname%" } if $firstname;
72
        $search_params{email}     = { 'like' => "%$email%" }     if $email;
73
74
        my $deleted_patrons_rs = Koha::Old::Patrons->search(
75
            \%search_params,
76
            {
77
                order_by => { -desc => 'updated_on' },
78
                rows     => 100,
79
            }
80
        );
81
82
        my @deleted_patrons;
83
84
        while ( my $patron = $deleted_patrons_rs->next ) {
85
86
            #collect patron data to use in the results table
87
            my $patron_data = {
88
                borrowernumber => $patron->borrowernumber,
89
                cardnumber     => $patron->cardnumber,
90
                surname        => $patron->surname,
91
                firstname      => $patron->firstname,
92
                email          => $patron->email,
93
                branchcode     => $patron->branchcode,
94
                updated_on     => $patron->updated_on,
95
            };
96
97
            push @deleted_patrons, $patron_data;
98
        }
99
100
        $template->param(
101
            view            => 'results',
102
            deleted_patrons => \@deleted_patrons,
103
            cardnumber      => $cardnumber,
104
            borrowernumber  => $borrowernumber,
105
            surname         => $surname,
106
            firstname       => $firstname,
107
            email           => $email,
108
        );
109
    } else {
110
        $template->param( view => 'search' );
111
    }
112
113
} elsif ( $op eq 'cud-restore' ) {
114
115
    my @borrowernumbers = $input->multi_param('borrowernumber');
116
117
    my @restored_patrons;
118
    my @errors;
119
120
    foreach my $borrowernumber (@borrowernumbers) {
121
        try {
122
            # Find the deleted patron
123
            my $deleted_patron = Koha::Old::Patrons->search( { borrowernumber => $borrowernumber } )->next;
124
125
            unless ($deleted_patron) {
126
                push @errors, "Borrowernumber $borrowernumber not found in deleted patrons";
127
                next;
128
            }
129
130
            # Attempt to restore
131
            my $restored_patron = $deleted_patron->restore_deleted_borrower;
132
133
            push @restored_patrons, {
134
                borrowernumber => $restored_patron->borrowernumber,
135
                cardnumber     => $restored_patron->cardnumber,
136
                firstname      => $restored_patron->firstname,
137
                surname        => $restored_patron->surname,
138
            };
139
        } catch {
140
            push @errors, "Error restoring borrower $borrowernumber: $_";
141
        };
142
    }
143
144
    $template->param(
145
        view             => 'restored',
146
        restored_patrons => \@restored_patrons,
147
        errors           => \@errors,
148
    );
149
150
} else {
151
    $template->param( view => 'search' );
152
}
153
154
output_html_with_http_headers $input, $cookie, $template->output;

Return to bug 34069