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

(-)a/C4/Circulation.pm (-2 / +64 lines)
Lines 96-101 BEGIN { Link Here
96
        &GetBranchItemRule
96
        &GetBranchItemRule
97
		&GetBiblioIssues
97
		&GetBiblioIssues
98
		&GetOpenIssue
98
		&GetOpenIssue
99
        &SetIssueNote
99
		&AnonymiseIssueHistory
100
		&AnonymiseIssueHistory
100
        &CheckIfIssuedToPatron
101
        &CheckIfIssuedToPatron
101
        &IsItemIssued
102
        &IsItemIssued
Lines 2540-2546 sub GetOpenIssue { Link Here
2540
=head2 GetIssues
2541
=head2 GetIssues
2541
2542
2542
    $issues = GetIssues({});    # return all issues!
2543
    $issues = GetIssues({});    # return all issues!
2543
    $issues = GetIssues({ borrowernumber => $borrowernumber, biblionumber => $biblionumber });
2544
    $issues = GetIssues({ borrowernumber => $borrowernumber, biblionumber => $biblionumber, issue_id => $issue_id });
2544
2545
2545
Returns all pending issues that match given criteria.
2546
Returns all pending issues that match given criteria.
2546
Returns a arrayref or undef if an error occurs.
2547
Returns a arrayref or undef if an error occurs.
Lines 2555-2560 Allowed criteria are: Link Here
2555
2556
2556
=item * itemnumber
2557
=item * itemnumber
2557
2558
2559
=item * issue_id
2560
2558
=back
2561
=back
2559
2562
2560
=cut
2563
=cut
Lines 2564-2570 sub GetIssues { Link Here
2564
2567
2565
    # Build filters
2568
    # Build filters
2566
    my @filters;
2569
    my @filters;
2567
    my @allowed = qw(borrowernumber biblionumber itemnumber);
2570
    my @allowed = qw(borrowernumber biblionumber itemnumber issue_id);
2568
    foreach (@allowed) {
2571
    foreach (@allowed) {
2569
        if (defined $criteria->{$_}) {
2572
        if (defined $criteria->{$_}) {
2570
            push @filters, {
2573
            push @filters, {
Lines 4133-4138 sub _CalculateAndUpdateFine { Link Here
4133
    }
4136
    }
4134
}
4137
}
4135
4138
4139
=head2 SetIssueNote
4140
4141
  &SetIssueNote($issue_id, $note);
4142
4143
Sets a note to the issuenotes table for the given issue.
4144
4145
=over 4
4146
4147
=item C<$issue_id> is the id of the issue for which to set the note
4148
4149
=item C<$note> is the note to set
4150
4151
=back
4152
4153
Returns:
4154
  True on success
4155
  False on failure
4156
4157
=cut
4158
4159
sub SetIssueNote {
4160
    my ( $issue_id, $note) = @_;
4161
4162
    my $dbh  = C4::Context->dbh;
4163
4164
    unless ( $issue_id =~ /\d+/ ) {
4165
      return;
4166
    }
4167
4168
    my $query = "UPDATE issues SET notedate=NOW(),note=? WHERE issue_id=?";
4169
    my $sth = $dbh->prepare($query);
4170
    return $sth->execute( $note, $issue_id );
4171
}
4172
4173
=head2 SendIssueNote
4174
4175
    &SendIssueNote($biblio, $borrower, $branch);
4176
4177
Sends the issue note to the library (adds it to the message queue).
4178
4179
=cut
4180
4181
sub SendIssueNote {
4182
    my $biblio = shift;
4183
    my $borrower = shift;
4184
    my $branch = shift;
4185
    my $letter = C4::Letters::GetPreparedLetter (
4186
        module => 'circulation',
4187
        letter_code => 'PATRON_NOTE',
4188
        branchcode => $branch,
4189
        tables => {
4190
            'biblio' => $biblio->{biblionumber},
4191
            'borrowers' => $borrower->{borrowernumber},
4192
            },
4193
        );
4194
    warn("issue", $biblio);
4195
    C4::Message->enqueue($letter, $borrower, 'email');
4196
}
4197
4136
1;
4198
1;
4137
4199
4138
__END__
4200
__END__
(-)a/circ/returns.pl (+3 lines)
Lines 285-290 if ($barcode) { Link Here
285
        $materials = $av->count ? $av->next->lib : '';
285
        $materials = $av->count ? $av->next->lib : '';
286
    }
286
    }
287
287
288
    my $issue = GetItemIssue($itemnumber);
289
288
    $template->param(
290
    $template->param(
289
        title            => $biblio->{'title'},
291
        title            => $biblio->{'title'},
290
        homebranch       => $biblio->{'homebranch'},
292
        homebranch       => $biblio->{'homebranch'},
Lines 298-303 if ($barcode) { Link Here
298
        biblionumber     => $biblio->{'biblionumber'},
300
        biblionumber     => $biblio->{'biblionumber'},
299
        borrower         => $borrower,
301
        borrower         => $borrower,
300
        additional_materials => $materials,
302
        additional_materials => $materials,
303
        issue            => $issue,
301
    );
304
    );
302
305
303
    my %input = (
306
    my %input = (
(-)a/installer/data/mysql/atomicupdate/bug_14224-add_new_issue_columns.sql (+2 lines)
Line 0 Link Here
1
ALTER IGNORE TABLE issues ADD `note` mediumtext default NULL;
2
ALTER IGNORE TABLE issues ADD `notedate` datetime default NULL;
(-)a/installer/data/mysql/atomicupdate/bug_14224-add_patron_notice_to_letters.sql (+1 lines)
Line 0 Link Here
1
INSERT IGNORE INTO letter (`module`, `code`, `branchcode`, `name`, `is_html`, `title`, `content`, `message_transport_type`) VALUES ('circulation', 'PATRON_NOTE', '', 'Patron note on item', '0', 'Patron issue note', '<<borrowers.firstname>> <<borrowers.surname>> has added a note to the item <<biblio.item>> - <<biblio.author>> (<<biblio.biblionumber>>).','email');
(-)a/installer/data/mysql/atomicupdate/bug_14224-issue_notes_syspref.sql (+1 lines)
Line 0 Link Here
1
INSERT IGNORE INTO systempreferences (`variable`, `value`, `options`, `explanation`,`type`) VALUES ('AllowIssueNotes', '0', NULL, 'Allow patrons to submit notes about checked out items.','YesNo');
(-)a/installer/data/mysql/kohastructure.sql (+30 lines)
Lines 843-848 CREATE TABLE `import_items` ( Link Here
843
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
843
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
844
844
845
--
845
--
846
-- Table structure for table `issues`
847
--
848
849
DROP TABLE IF EXISTS `issues`;
850
CREATE TABLE `issues` ( -- information related to check outs or issues
851
  `issue_id` int(11) NOT NULL AUTO_INCREMENT, -- primary key for issues table
852
  `borrowernumber` int(11), -- foreign key, linking this to the borrowers table for the patron this item was checked out to
853
  `itemnumber` int(11), -- foreign key, linking this to the items table for the item that was checked out
854
  `date_due` datetime default NULL, -- datetime the item is due (yyyy-mm-dd hh:mm::ss)
855
  `branchcode` varchar(10) default NULL, -- foreign key, linking to the branches table for the location the item was checked out
856
  `returndate` datetime default NULL, -- date the item was returned, will be NULL until moved to old_issues
857
  `lastreneweddate` datetime default NULL, -- date the item was last renewed
858
  `return` varchar(4) default NULL,
859
  `renewals` tinyint(4) default NULL, -- lists the number of times the item was renewed
860
  `auto_renew` BOOLEAN default FALSE, -- automatic renewal
861
  `timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, -- the date and time this record was last touched
862
  `issuedate` datetime default NULL, -- date the item was checked out or issued
863
  `onsite_checkout` int(1) NOT NULL default 0, -- in house use flag
864
  `note` mediumtext default NULL, -- issue note text
865
  `notedate` datetime default NULL, -- datetime of issue note (yyyy-mm-dd hh:mm::ss)
866
  PRIMARY KEY (`issue_id`),
867
  KEY `issuesborridx` (`borrowernumber`),
868
  KEY `itemnumber_idx` (`itemnumber`),
869
  KEY `branchcode_idx` (`branchcode`),
870
  KEY `bordate` (`borrowernumber`,`timestamp`),
871
  CONSTRAINT `issues_ibfk_1` FOREIGN KEY (`borrowernumber`) REFERENCES `borrowers` (`borrowernumber`) ON DELETE RESTRICT ON UPDATE CASCADE,
872
  CONSTRAINT `issues_ibfk_2` FOREIGN KEY (`itemnumber`) REFERENCES `items` (`itemnumber`) ON DELETE RESTRICT ON UPDATE CASCADE
873
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
874
875
--
846
-- Table structure for table `issuingrules`
876
-- Table structure for table `issuingrules`
847
--
877
--
848
878
(-)a/installer/data/mysql/sysprefs.sql (+1 lines)
Lines 27-32 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
27
('AllowNotForLoanOverride','0','','If ON, Koha will allow the librarian to loan a not for loan item.','YesNo'),
27
('AllowNotForLoanOverride','0','','If ON, Koha will allow the librarian to loan a not for loan item.','YesNo'),
28
('AllowOfflineCirculation','0','','If on, enables HTML5 offline circulation functionality.','YesNo'),
28
('AllowOfflineCirculation','0','','If on, enables HTML5 offline circulation functionality.','YesNo'),
29
('AllowPatronToSetCheckoutsVisibilityForGuarantor',  '0', NULL, 'If enabled, the patron can set checkouts to be visible to his or her guarantor',  'YesNo'),
29
('AllowPatronToSetCheckoutsVisibilityForGuarantor',  '0', NULL, 'If enabled, the patron can set checkouts to be visible to his or her guarantor',  'YesNo'),
30
('AllowIssueNotes', '0', NULL, 'Allow patrons to submit notes about checked out items.','YesNo'),
30
('AllowPKIAuth','None','None|Common Name|emailAddress','Use the field from a client-side SSL certificate to look a user in the Koha database','Choice'),
31
('AllowPKIAuth','None','None|Common Name|emailAddress','Use the field from a client-side SSL certificate to look a user in the Koha database','Choice'),
31
('AllowPurchaseSuggestionBranchChoice','0','1','Allow user to choose branch when making a purchase suggestion','YesNo'),
32
('AllowPurchaseSuggestionBranchChoice','0','1','Allow user to choose branch when making a purchase suggestion','YesNo'),
32
('AllowRenewalIfOtherItemsAvailable','0',NULL,'If enabled, allow a patron to renew an item with unfilled holds if other available items can fill that hold.','YesNo'),
33
('AllowRenewalIfOtherItemsAvailable','0',NULL,'If enabled, allow a patron to renew an item with unfilled holds if other available items can fill that hold.','YesNo'),
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref (+6 lines)
Lines 136-141 Circulation: Link Here
136
                  yes: Show
136
                  yes: Show
137
                  no: "Do not show"
137
                  no: "Do not show"
138
            - all items in the "Checked-in items" list, even items that were not checked out.
138
            - all items in the "Checked-in items" list, even items that were not checked out.
139
        -
140
            - pref: AllowIssueNotes
141
              choices:
142
                  yes: Allow
143
                  no: "Don't allow"
144
            - patrons to submit notes about checked out items.
139
145
140
    Checkout Policy:
146
    Checkout Policy:
141
        -
147
        -
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/returns.tt (+10 lines)
Lines 183-188 $(document).ready(function () { Link Here
183
</div>
183
</div>
184
[% END %]
184
[% END %]
185
185
186
<!-- Patron has added an issue note -->
187
[% IF ( issue.note) %]
188
    <div class="dialog message">
189
        <h1>Patron note</h1>
190
        <p>[% issue.notedate %]</p>
191
        <p><a href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=[% itembiblionumber %]"> [% title |html %]</a> [% author %]</p>
192
        <p>[% issue.note %]</p>
193
    </div>
194
[% END %]
195
186
<!-- Patron has fines -->
196
<!-- Patron has fines -->
187
[% IF ( fines ) %]
197
[% IF ( fines ) %]
188
    <div class="dialog alert">
198
    <div class="dialog alert">
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-user.tt (+96 lines)
Lines 120-125 Using this account is not recommended because some parts of Koha will not functi Link Here
120
                        </div>
120
                        </div>
121
                    [% END # / IF patron_flagged %]
121
                    [% END # / IF patron_flagged %]
122
122
123
                    <div class="alert alert-info" id="notesaved" style="display:none"></div>
124
123
                    [% SET OPACMySummaryNote = Koha.Preference('OPACMySummaryNote') %]
125
                    [% SET OPACMySummaryNote = Koha.Preference('OPACMySummaryNote') %]
124
                    [% IF OPACMySummaryNote %][% OPACMySummaryNote %][% END %]
126
                    [% IF OPACMySummaryNote %][% OPACMySummaryNote %][% END %]
125
127
Lines 164-169 Using this account is not recommended because some parts of Koha will not functi Link Here
164
                                                [% IF ( OPACMySummaryHTML ) %]
166
                                                [% IF ( OPACMySummaryHTML ) %]
165
                                                    <th class="nosort">Links</th>
167
                                                    <th class="nosort">Links</th>
166
                                                [% END %]
168
                                                [% END %]
169
                                                [% IF ( AllowIssueNotes ) %]
170
                                                    <th class="nosort">Note</th>
171
                                                [% END %]
167
                                            </tr>
172
                                            </tr>
168
                                        </thead>
173
                                        </thead>
169
                                        <tbody>
174
                                        <tbody>
Lines 280-285 Using this account is not recommended because some parts of Koha will not functi Link Here
280
                                                [% IF ( OPACMySummaryHTML ) %]
285
                                                [% IF ( OPACMySummaryHTML ) %]
281
                                                    <td class="links">[% ISSUE.MySummaryHTML %]</td>
286
                                                    <td class="links">[% ISSUE.MySummaryHTML %]</td>
282
                                                [% END %]
287
                                                [% END %]
288
                                                [% IF ( AllowIssueNotes ) %]
289
                                                    <td class="note">
290
                                                        <input type="text"
291
                                                            name="note"
292
                                                            data-issue_id="[% ISSUE.issue_id%]"
293
                                                            data-origvalue="[% ISSUE.note %]"
294
                                                            value="[% ISSUE.note %]">
295
                                                        </input>
296
                                                        <a class="btn"
297
                                                            name="submitnote"
298
                                                            id="save_[% ISSUE.issue_id %]"
299
                                                            style="display:none;">Submit note</a>
300
                                                    </td>
301
                                                [% END %]
283
                                            </tr>
302
                                            </tr>
284
                                        [% END # /FOREACH ISSUES %]
303
                                        [% END # /FOREACH ISSUES %]
285
                                    </tbody>
304
                                    </tbody>
Lines 782-787 Using this account is not recommended because some parts of Koha will not functi Link Here
782
                [% END %]
801
                [% END %]
783
            [% END %]
802
            [% END %]
784
803
804
            [% IF ( AllowIssueNotes ) %]
805
                $("input[name='note']").keyup(function(e){
806
                    /* prevent submitting of renewselected form */
807
                    if(e.which == 13)
808
                        e.preventDefault();
809
810
                    var $btn_save = $('#save_'+$(this).data('issue_id'));
811
                    var origvalue = $(this).data('origvalue');
812
                    var value = $(this).val();
813
814
                    if(origvalue != value) {
815
                        if(origvalue != "")
816
                            $btn_save.text('Submit changes');
817
                        else
818
                            $btn_save.text('Submit note');
819
                        $btn_save.show();
820
                    } else {
821
                        $btn_save.hide();
822
                    }
823
                });
824
825
                $("a[name='submitnote']").click(function(e){
826
                    var $self = $(this);
827
                    var title = $(this).parent().siblings('.title').html();
828
                    var $noteinput = $(this).siblings('input[name="note"]').first();
829
830
                    var ajaxData = {
831
                        'action': 'issuenote',
832
                        'issue_id': $noteinput.data('issue_id'),
833
                        'note': $noteinput.val(),
834
                    };
835
836
                    $.ajax({
837
                        url: '/cgi-bin/koha/svc/patron_notes/',
838
                        type: 'POST',
839
                        dataType: 'json',
840
                        data: ajaxData,
841
                    })
842
                    .done(function(data) {
843
                        var message = "";
844
                        if(data.status == 'saved') {
845
                            $("#notesaved").removeClass("alert-error");
846
                            $("#notesaved").addClass("alert-info");
847
                            $noteinput.data('origvalue', data.note);
848
                            $noteinput.val(data.note);
849
                            message = "<p>Your note about " + title + " has been saved and sent to the library.</p>";
850
                            $self.hide();
851
                        } else if(data.status == 'removed') {
852
                            $("#notesaved").removeClass("alert-error");
853
                            $("#notesaved").addClass("alert-info");
854
                            $noteinput.data('origvalue', "");
855
                            $noteinput.val("");
856
                            message = "<p>Your note about " + title + " was removed.</p>";
857
                            $self.hide();
858
                        } else {
859
                            $("#notesaved").removeClass("alert-info");
860
                            $("#notesaved").addClass("alert-error");
861
                            message = "<p>Your note about " + title + " could not be saved.</p>" +
862
                                      "<p style=\"font-weight:bold;\">" + data.error + "</p>";
863
                        }
864
865
                        message += "<p style=\"font-style:italic;\">" + data.note + "</p>";
866
                        $("#notesaved").html(message);
867
                    })
868
                    .fail(function(data) {
869
                        $("#notesaved").removeClass("alert-info");
870
                        $("#notesaved").addClass("alert-error");
871
                        var message = "<p>Your note about " + title + " could not be saved.</p>" +
872
                                      "<p style=\"font-weight:bold;\">Ajax request has failed.</p>";
873
                        $("#notesaved").html(message);
874
                    })
875
                    .always(function() {
876
                        $("#notesaved").show();
877
                    });
878
                });
879
            [% END %]
880
785
            $( ".suspend-until" ).datepicker({ minDate: 1 }); // Require that "until date" be in the future
881
            $( ".suspend-until" ).datepicker({ minDate: 1 }); // Require that "until date" be in the future
786
        });
882
        });
787
        //]]>
883
        //]]>
(-)a/opac/opac-user.pl (+3 lines)
Lines 33-38 use C4::Output; Link Here
33
use C4::Biblio;
33
use C4::Biblio;
34
use C4::Items;
34
use C4::Items;
35
use C4::Letters;
35
use C4::Letters;
36
use Koha::Libraries;
36
use Koha::DateUtils;
37
use Koha::DateUtils;
37
use Koha::Holds;
38
use Koha::Holds;
38
use Koha::Database;
39
use Koha::Database;
Lines 258-263 if ($issues){ Link Here
258
}
259
}
259
my $overduesblockrenewing = C4::Context->preference('OverduesBlockRenewing');
260
my $overduesblockrenewing = C4::Context->preference('OverduesBlockRenewing');
260
$canrenew = 0 if ($overduesblockrenewing ne 'allow' and $overdues_count == $count);
261
$canrenew = 0 if ($overduesblockrenewing ne 'allow' and $overdues_count == $count);
262
263
$template->param( AllowIssueNotes => C4::Context->preference('AllowIssueNotes') );
261
$template->param( ISSUES       => \@issuedat );
264
$template->param( ISSUES       => \@issuedat );
262
$template->param( issues_count => $count );
265
$template->param( issues_count => $count );
263
$template->param( canrenew     => $canrenew );
266
$template->param( canrenew     => $canrenew );
(-)a/opac/svc/patron_notes (-1 / +108 lines)
Line 0 Link Here
0
- 
1
#!/usr/bin/perl
2
3
# This file is part of Koha.
4
#
5
# Copyright 2014 BibLibre
6
#
7
# Koha is free software; you can redistribute it and/or modify it
8
# under the terms of the GNU General Public License as published by
9
# the Free Software Foundation; either version 3 of the License, or
10
# (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 Public License
18
# along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20
use Modern::Perl;
21
22
use C4::Service;
23
use C4::Auth qw /check_cookie_auth/;
24
use C4::Letters qw( GetLetters );
25
use CGI::Cookie; # need to check cookies before having CGI parse the POST request
26
use C4::Output qw(:DEFAULT :ajax);
27
use C4::Scrubber;
28
use C4::Circulation;
29
use C4::Members;
30
use C4::Biblio;
31
32
=head1 NAME
33
34
svc/patron_notes - Web service for setting patron notes on items
35
36
=head1 DESCRIPTION
37
38
=cut
39
40
sub ajax_auth_cgi {     # returns CGI object
41
    my $needed_flags = shift;
42
    my %cookies = fetch CGI::Cookie;
43
    my $input = CGI->new;
44
    my $sessid = $cookies{'CGISESSID'}->value;
45
    my ($auth_status, $auth_sessid) = check_cookie_auth($sessid, $needed_flags);
46
    if ($auth_status ne "ok") {
47
        output_with_http_headers $input, undef,
48
        "window.alert('Your CGI session cookie ($sessid) is not current.  " .
49
        "Please refresh the page and try again.');\n", 'js';
50
        exit 0;
51
    }
52
    return $input;
53
}
54
55
# AJAX requests
56
my $is_ajax = is_ajax();
57
my $query = ($is_ajax) ? &ajax_auth_cgi({}) : CGI->new();
58
if ($is_ajax) {
59
    my $action = $query->param('action');
60
61
    # Issue Note
62
    if ( $action eq 'issuenote' && C4::Context->preference('AllowIssueNotes') ) {
63
        my $scrubber = C4::Scrubber->new();
64
        my $note = $query->param('note');
65
        my $issue_id = $query->param('issue_id');
66
        my $clean_note = $scrubber->scrub($note);
67
        my $status = "saved";
68
        my $error = "";
69
        my ($member, $issue);
70
71
        my ( $template, $borrowernumber, $cookie ) = C4::Auth::get_template_and_user({
72
            template_name   => "opac-user.tt",
73
            query           => $query,
74
            type            => "opac",
75
            authnotrequired => 1,
76
        });
77
78
        # verify issue_id
79
        if ( $issue_id =~ /\d+/ ) {
80
            $member = GetMember(borrowernumber => $borrowernumber);
81
            ($issue) = @{C4::Circulation::GetIssues({issue_id => $issue_id})};
82
            if ( $issue->{'borrowernumber'} != $borrowernumber ) {
83
                $status = "fail";
84
                $error = "Invalid issue id!";
85
            }
86
        } else {
87
            $status = "fail";
88
            $error = "Invalid issue id!";
89
        }
90
91
        if ( (not $error) && SetIssueNote($issue_id, $clean_note) ) {
92
            if($clean_note) { # only send email if note not empty
93
                my $branch = Koha::Libraries->find( $issue->{branchcode} );
94
                my $biblio = GetBiblioFromItemNumber($issue->{'itemnumber'});
95
                C4::Circulation::SendIssueNote($biblio, $member, $branch);
96
            } else { # note empty, i.e removed
97
                $status = "removed";
98
            }
99
        } else {
100
            $status = "fail";
101
            $error = "Perhaps the item has already been check in?";
102
        }
103
104
        my $response = "{\"status\": \"$status\", \"note\": \"$clean_note\", \"issue_id\": \"$issue_id\", \"error\": \"$error\"}";
105
        output_with_http_headers($query, undef, $response, 'js');
106
        exit;
107
    } # END Issue Note
108
}

Return to bug 14224