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

(-)a/C4/Circulation.pm (+8 lines)
Lines 48-53 use Algorithm::CheckDigits; Link Here
48
use Data::Dumper;
48
use Data::Dumper;
49
use Koha::DateUtils;
49
use Koha::DateUtils;
50
use Koha::Calendar;
50
use Koha::Calendar;
51
use Koha::Items;
52
use Koha::Borrowers;
51
use Koha::Borrower::Debarments;
53
use Koha::Borrower::Debarments;
52
use Koha::Database;
54
use Koha::Database;
53
use Carp;
55
use Carp;
Lines 2127-2132 sub MarkIssueReturned { Link Here
2127
    $sth_del->execute($borrowernumber, $itemnumber);
2129
    $sth_del->execute($borrowernumber, $itemnumber);
2128
2130
2129
    ModItem( { 'onloan' => undef }, undef, $itemnumber );
2131
    ModItem( { 'onloan' => undef }, undef, $itemnumber );
2132
2133
    if ( C4::Context->preference('StoreLastBorrower') ) {
2134
        my $item = Koha::Items->find( $itemnumber );
2135
        my $patron = Koha::Borrowers->find( $borrowernumber );
2136
        $item->last_returned_by( $patron );
2137
    }
2130
}
2138
}
2131
2139
2132
=head2 _debar_user_on_return
2140
=head2 _debar_user_on_return
(-)a/Koha/Item.pm (+33 lines)
Lines 24-29 use Carp; Link Here
24
use Koha::Database;
24
use Koha::Database;
25
25
26
use Koha::Branches;
26
use Koha::Branches;
27
use Koha::Borrowers;
27
28
28
use base qw(Koha::Object);
29
use base qw(Koha::Object);
29
30
Lines 73-78 sub holding_branch { Link Here
73
    return $self->{_holding_branch};
74
    return $self->{_holding_branch};
74
}
75
}
75
76
77
=head3 last_returned_by
78
79
Gets and sets the last borrower to return an item.
80
81
Accepts and returns Koha::Borrower objects
82
83
$item->last_returned_by( $borrowernumber );
84
85
$last_returned_by = $item->last_returned_by();
86
87
=cut
88
89
sub last_returned_by {
90
    my ( $self, $borrower ) = @_;
91
92
    my $items_last_returned_by_rs = Koha::Database->new()->schema()->resultset('ItemsLastBorrower');
93
94
    if ($borrower) {
95
        return $items_last_returned_by_rs->update_or_create(
96
            { borrowernumber => $borrower->borrowernumber, itemnumber => $self->id } );
97
    }
98
    else {
99
        unless ( $self->{_last_returned_by} ) {
100
            my $result = $items_last_returned_by_rs->single( { itemnumber => $self->id } );
101
            if ($result) {
102
                $self->{_last_returned_by} = Koha::Borrowers->find( $result->get_column('borrowernumber') );
103
            }
104
        }
105
106
        return $self->{_last_returned_by};
107
    }
108
}
76
109
77
=head3 type
110
=head3 type
78
111
(-)a/Koha/Schema/Result/ItemsLastBorrower.pm (+133 lines)
Line 0 Link Here
1
use utf8;
2
package Koha::Schema::Result::ItemsLastBorrower;
3
4
# Created by DBIx::Class::Schema::Loader
5
# DO NOT MODIFY THE FIRST PART OF THIS FILE
6
7
=head1 NAME
8
9
Koha::Schema::Result::ItemsLastBorrower
10
11
=cut
12
13
use strict;
14
use warnings;
15
16
use base 'DBIx::Class::Core';
17
18
=head1 TABLE: C<items_last_borrower>
19
20
=cut
21
22
__PACKAGE__->table("items_last_borrower");
23
24
=head1 ACCESSORS
25
26
=head2 id
27
28
  data_type: 'integer'
29
  is_auto_increment: 1
30
  is_nullable: 0
31
32
=head2 itemnumber
33
34
  data_type: 'integer'
35
  is_foreign_key: 1
36
  is_nullable: 0
37
38
=head2 borrowernumber
39
40
  data_type: 'integer'
41
  is_foreign_key: 1
42
  is_nullable: 0
43
44
=head2 created_on
45
46
  data_type: 'timestamp'
47
  datetime_undef_if_invalid: 1
48
  default_value: current_timestamp
49
  is_nullable: 0
50
51
=cut
52
53
__PACKAGE__->add_columns(
54
  "id",
55
  { data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
56
  "itemnumber",
57
  { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
58
  "borrowernumber",
59
  { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
60
  "created_on",
61
  {
62
    data_type => "timestamp",
63
    datetime_undef_if_invalid => 1,
64
    default_value => \"current_timestamp",
65
    is_nullable => 0,
66
  },
67
);
68
69
=head1 PRIMARY KEY
70
71
=over 4
72
73
=item * L</id>
74
75
=back
76
77
=cut
78
79
__PACKAGE__->set_primary_key("id");
80
81
=head1 UNIQUE CONSTRAINTS
82
83
=head2 C<itemnumber>
84
85
=over 4
86
87
=item * L</itemnumber>
88
89
=back
90
91
=cut
92
93
__PACKAGE__->add_unique_constraint("itemnumber", ["itemnumber"]);
94
95
=head1 RELATIONS
96
97
=head2 borrowernumber
98
99
Type: belongs_to
100
101
Related object: L<Koha::Schema::Result::Borrower>
102
103
=cut
104
105
__PACKAGE__->belongs_to(
106
  "borrowernumber",
107
  "Koha::Schema::Result::Borrower",
108
  { borrowernumber => "borrowernumber" },
109
  { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
110
);
111
112
=head2 itemnumber
113
114
Type: belongs_to
115
116
Related object: L<Koha::Schema::Result::Item>
117
118
=cut
119
120
__PACKAGE__->belongs_to(
121
  "itemnumber",
122
  "Koha::Schema::Result::Item",
123
  { itemnumber => "itemnumber" },
124
  { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
125
);
126
127
128
# Created by DBIx::Class::Schema::Loader v0.07040 @ 2015-10-02 12:33:33
129
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ByHKNZCEz4a1AqTnOJgUWA
130
131
132
# You can replace this text with custom code or comments, and it will be preserved on regeneration
133
1;
(-)a/catalogue/moredetail.pl (+2 lines)
Lines 38-43 use C4::Reserves qw(GetReservesFromBiblionumber); Link Here
38
38
39
use Koha::Acquisition::Bookseller;
39
use Koha::Acquisition::Bookseller;
40
use Koha::DateUtils;
40
use Koha::DateUtils;
41
use Koha::Items;
41
42
42
my $query=new CGI;
43
my $query=new CGI;
43
44
Lines 135-140 foreach ( keys %{$data} ) { Link Here
135
136
136
($itemnumber) and @items = (grep {$_->{'itemnumber'} == $itemnumber} @items);
137
($itemnumber) and @items = (grep {$_->{'itemnumber'} == $itemnumber} @items);
137
foreach my $item (@items){
138
foreach my $item (@items){
139
    $item->{object} = Koha::Items->find( $item->{itemnumber} );
138
    $item->{itemlostloop}= GetAuthorisedValues(GetAuthValCode('items.itemlost',$fw),$item->{itemlost}) if GetAuthValCode('items.itemlost',$fw);
140
    $item->{itemlostloop}= GetAuthorisedValues(GetAuthValCode('items.itemlost',$fw),$item->{itemlost}) if GetAuthValCode('items.itemlost',$fw);
139
    $item->{itemdamagedloop}= GetAuthorisedValues(GetAuthValCode('items.damaged',$fw),$item->{damaged}) if GetAuthValCode('items.damaged',$fw);
141
    $item->{itemdamagedloop}= GetAuthorisedValues(GetAuthValCode('items.damaged',$fw),$item->{damaged}) if GetAuthValCode('items.damaged',$fw);
140
    $item->{'collection'}              = $ccodes->{ $item->{ccode} } if ($ccodes);
142
    $item->{'collection'}              = $ccodes->{ $item->{ccode} } if ($ccodes);
(-)a/installer/data/mysql/atomicupdate/bug_14945.sql (+13 lines)
Line 0 Link Here
1
INSERT INTO systempreferences (variable,value,options,explanation,type) VALUES ('StoreLastBorrower','0','','If ON, the last borrower to return an item will be stored in items.last_returned_by','YesNo');
2
3
CREATE TABLE IF NOT EXISTS `items_last_borrower` (
4
  `id` int(11) NOT NULL AUTO_INCREMENT,
5
  `itemnumber` int(11) NOT NULL,
6
  `borrowernumber` int(11) NOT NULL,
7
  `created_on` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
8
  PRIMARY KEY (`id`),
9
  UNIQUE KEY `itemnumber` (`itemnumber`),
10
  KEY `borrowernumber` (`borrowernumber`),
11
  CONSTRAINT `items_last_borrower_ibfk_2` FOREIGN KEY (`borrowernumber`) REFERENCES `borrowers` (`borrowernumber`) ON DELETE CASCADE ON UPDATE CASCADE,
12
  CONSTRAINT `items_last_borrower_ibfk_1` FOREIGN KEY (`itemnumber`) REFERENCES `items` (`itemnumber`) ON DELETE CASCADE ON UPDATE CASCADE
13
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
(-)a/installer/data/mysql/kohastructure.sql (+16 lines)
Lines 1270-1275 CREATE TABLE `items` ( -- holdings/item information Link Here
1270
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
1270
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
1271
1271
1272
--
1272
--
1273
-- Table structure for table `items_last_borrower`
1274
--
1275
1276
CREATE TABLE IF NOT EXISTS `items_last_borrower` (
1277
  `id` int(11) NOT NULL AUTO_INCREMENT,
1278
  `itemnumber` int(11) NOT NULL,
1279
  `borrowernumber` int(11) NOT NULL,
1280
  `created_on` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
1281
  PRIMARY KEY (`id`),
1282
  UNIQUE KEY `itemnumber` (`itemnumber`),
1283
  KEY `borrowernumber` (`borrowernumber`),
1284
  CONSTRAINT `items_last_borrower_ibfk_2` FOREIGN KEY (`borrowernumber`) REFERENCES `borrowers` (`borrowernumber`) ON DELETE CASCADE ON UPDATE CASCADE,
1285
  CONSTRAINT `items_last_borrower_ibfk_1` FOREIGN KEY (`itemnumber`) REFERENCES `items` (`itemnumber`) ON DELETE CASCADE ON UPDATE CASCADE
1286
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
1287
1288
--
1273
-- Table structure for table `itemtypes`
1289
-- Table structure for table `itemtypes`
1274
--
1290
--
1275
1291
(-)a/installer/data/mysql/sysprefs.sql (+1 lines)
Lines 412-417 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
412
('StaffSerialIssueDisplayCount','3','','Number of serial issues to display per subscription in the Staff client','Integer'),
412
('StaffSerialIssueDisplayCount','3','','Number of serial issues to display per subscription in the Staff client','Integer'),
413
('StaticHoldsQueueWeight','0',NULL,'Specify a list of library location codes separated by commas -- the list of codes will be traversed and weighted with first values given higher weight for holds fulfillment -- alternatively, if RandomizeHoldsQueueWeight is set, the list will be randomly selective','Integer'),
413
('StaticHoldsQueueWeight','0',NULL,'Specify a list of library location codes separated by commas -- the list of codes will be traversed and weighted with first values given higher weight for holds fulfillment -- alternatively, if RandomizeHoldsQueueWeight is set, the list will be randomly selective','Integer'),
414
('StatisticsFields','location|itype|ccode', NULL, 'Define Fields (from the items table) used for statistics members','Free'),
414
('StatisticsFields','location|itype|ccode', NULL, 'Define Fields (from the items table) used for statistics members','Free'),
415
('StoreLastBorrower','0','','If ON, the last borrower to return an item will be stored in items.last_returned_by','YesNo'),
415
('SubfieldsToAllowForRestrictedBatchmod','','Define a list of subfields for which edition is authorized when items_batchmod_restricted permission is enabled, separated by spaces. Example: 995\$f 995\$h 995\$j',NULL,'Free'),
416
('SubfieldsToAllowForRestrictedBatchmod','','Define a list of subfields for which edition is authorized when items_batchmod_restricted permission is enabled, separated by spaces. Example: 995\$f 995\$h 995\$j',NULL,'Free'),
416
('SubfieldsToAllowForRestrictedEditing','','Define a list of subfields for which edition is authorized when edit_items_restricted permission is enabled, separated by spaces. Example: 995\$f 995\$h 995\$j',NULL,'Free'),
417
('SubfieldsToAllowForRestrictedEditing','','Define a list of subfields for which edition is authorized when edit_items_restricted permission is enabled, separated by spaces. Example: 995\$f 995\$h 995\$j',NULL,'Free'),
417
('SubfieldsToUseWhenPrefill','','','Define a list of subfields to use when prefilling items (separated by space)','Free'),
418
('SubfieldsToUseWhenPrefill','','','Define a list of subfields to use when prefilling items (separated by space)','Free'),
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/opac.pref (-1 / +7 lines)
Lines 563-568 OPAC: Link Here
563
563
564
    Privacy:
564
    Privacy:
565
        -
565
        -
566
            - pref: StoreLastBorrower
567
              default: 0
568
              choices:
569
                  yes: Store
570
                  no: "Don't store"
571
            - the last patron to return an item. This setting is independent of opacreadinghistory/AnonymousPatron.
572
        -
566
            - pref: AnonSuggestions
573
            - pref: AnonSuggestions
567
              choices:
574
              choices:
568
                  yes: Allow
575
                  yes: Allow
Lines 616-622 OPAC: Link Here
616
            - pref: RestrictedPageTitle
623
            - pref: RestrictedPageTitle
617
              class: long
624
              class: long
618
            - "as title of your restricted page (appears in the breadcrumb and on the top of the restricted page)"
625
            - "as title of your restricted page (appears in the breadcrumb and on the top of the restricted page)"
619
620
    Shelf Browser:
626
    Shelf Browser:
621
        -
627
        -
622
            - pref: OPACShelfBrowser
628
            - pref: OPACShelfBrowser
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/moredetail.tt (+4 lines)
Lines 1-3 Link Here
1
[% USE Koha %]
1
[% INCLUDE 'doc-head-open.inc' %]
2
[% INCLUDE 'doc-head-open.inc' %]
2
<title>Koha &rsaquo; Catalog &rsaquo; Item details for [% title %] [% FOREACH subtitl IN subtitle %] [% subtitl.subfield %][% END %]</title>
3
<title>Koha &rsaquo; Catalog &rsaquo; Item details for [% title %] [% FOREACH subtitl IN subtitle %] [% subtitl.subfield %][% END %]</title>
3
[% INCLUDE 'doc-head-close.inc' %]
4
[% INCLUDE 'doc-head-close.inc' %]
Lines 216-221 Link Here
216
217
217
                <li><span class="label">Last seen:</span>[% IF ( ITEM_DAT.datelastseen ) %][% ITEM_DAT.datelastseen | $KohaDates %] [%END %]&nbsp;</li>
218
                <li><span class="label">Last seen:</span>[% IF ( ITEM_DAT.datelastseen ) %][% ITEM_DAT.datelastseen | $KohaDates %] [%END %]&nbsp;</li>
218
                <li><span class="label">Last borrowed:</span>[% IF (ITEM_DAT.datelastborrowed ) %][% ITEM_DAT.datelastborrowed | $KohaDates %][% END %]&nbsp;</li>
219
                <li><span class="label">Last borrowed:</span>[% IF (ITEM_DAT.datelastborrowed ) %][% ITEM_DAT.datelastborrowed | $KohaDates %][% END %]&nbsp;</li>
220
                [% IF Koha.Preference('StoreLastBorrower') && ITEM_DAT.object.last_returned_by %]
221
                    <li><span class="label">Last returned by:</span> <a href="/cgi-bin/koha/circ/circulation.pl?borrowernumber=[% ITEM_DAT.object.last_returned_by.borrowernumber %]">[% ITEM_DAT.object.last_returned_by.cardnumber %]</a>&nbsp;</li>
222
                [% END %]
219
                [% IF ( ITEM_DAT.card0 ) %]<li><span class="label">Last borrower:</span> <a href="/cgi-bin/koha/circ/circulation.pl?borrowernumber=[% ITEM_DAT.borrower0 %]">[% ITEM_DAT.card0 %]</a>&nbsp;</li>[% END %]
223
                [% IF ( ITEM_DAT.card0 ) %]<li><span class="label">Last borrower:</span> <a href="/cgi-bin/koha/circ/circulation.pl?borrowernumber=[% ITEM_DAT.borrower0 %]">[% ITEM_DAT.card0 %]</a>&nbsp;</li>[% END %]
220
                [% IF ( ITEM_DAT.card1 ) %]<li><span class="label">Previous borrower:</span> <a href="/cgi-bin/koha/circ/circulation.pl?borrowernumber=[% ITEM_DAT.borrower1 %]">[% ITEM_DAT.card1 %]</a>&nbsp;</li>[% END %]
224
                [% IF ( ITEM_DAT.card1 ) %]<li><span class="label">Previous borrower:</span> <a href="/cgi-bin/koha/circ/circulation.pl?borrowernumber=[% ITEM_DAT.borrower1 %]">[% ITEM_DAT.card1 %]</a>&nbsp;</li>[% END %]
221
                [% IF ( ITEM_DAT.card2 ) %]<li><span class="label">Previous borrower:</span> <a href="/cgi-bin/koha/circ/circulation.pl?borrowernumber=[% ITEM_DAT.borrower2 %]">[% ITEM_DAT.card2 %]</a>&nbsp;</li>[% END %]
225
                [% IF ( ITEM_DAT.card2 ) %]<li><span class="label">Previous borrower:</span> <a href="/cgi-bin/koha/circ/circulation.pl?borrowernumber=[% ITEM_DAT.borrower2 %]">[% ITEM_DAT.card2 %]</a>&nbsp;</li>[% END %]
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-privacy.tt (+1 lines)
Lines 77-82 Link Here
77
                            <p>Whatever your privacy rule you choose, you can delete all your reading history immediately by clicking here. <b>BE CAREFUL</b>. Once you've confirmed the deletion, no one can retrieve the list!</p>
77
                            <p>Whatever your privacy rule you choose, you can delete all your reading history immediately by clicking here. <b>BE CAREFUL</b>. Once you've confirmed the deletion, no one can retrieve the list!</p>
78
                            <input type="submit" value="Immediate deletion" class="btn btn-danger" onclick="return confirmDelete(MSG_CONFIRM_AGAIN);" />
78
                            <input type="submit" value="Immediate deletion" class="btn btn-danger" onclick="return confirmDelete(MSG_CONFIRM_AGAIN);" />
79
                        </form>
79
                        </form>
80
                        [% IF Koha.Preference('StoreLastBorrower') %]<p id="store-last-borrower-msg">Please note, the last person to return an item is tracked for the management of items returned damaged.</p>[% END %]
80
                    [% END # / IF Ask_data %]
81
                    [% END # / IF Ask_data %]
81
                </div> <!-- / .userprivacy -->
82
                </div> <!-- / .userprivacy -->
82
            </div> <!-- / .span10 -->
83
            </div> <!-- / .span10 -->
(-)a/t/db_dependent/Circulation/AnonymiseIssueHistory.t (-2 / +82 lines)
Lines 1-8 Link Here
1
use Modern::Perl;
1
use Modern::Perl;
2
use Test::More tests => 3;
2
use Test::More tests => 4;
3
3
4
use C4::Context;
4
use C4::Context;
5
use C4::Circulation;
5
use C4::Circulation;
6
use Koha::Items;
6
use t::lib::Mocks;
7
use t::lib::Mocks;
7
use t::lib::TestBuilder;
8
use t::lib::TestBuilder;
8
9
Lines 136-138 subtest 'AnonymousPatron is not defined' => sub { Link Here
136
    |, undef, $item->{itemnumber});
137
    |, undef, $item->{itemnumber});
137
    is( $borrowernumber_used_to_anonymised, undef, 'With AnonymousPatron is not defined, the issue should have been anonymised anyway' );
138
    is( $borrowernumber_used_to_anonymised, undef, 'With AnonymousPatron is not defined, the issue should have been anonymised anyway' );
138
};
139
};
139
- 
140
141
subtest 'Test StoreLastBorrower' => sub {
142
    plan tests => 4;
143
144
    t::lib::Mocks::mock_preference( 'StoreLastBorrower', '1' );
145
146
    my $patron = $builder->build(
147
        {
148
            source => 'Borrower',
149
            value  => { privacy => 1, }
150
        }
151
    );
152
153
    my $item = $builder->build(
154
        {
155
            source => 'Item',
156
            value  => {
157
                itemlost  => 0,
158
                withdrawn => 0,
159
            },
160
        }
161
    );
162
163
    my $issue = $builder->build(
164
        {
165
            source => 'Issue',
166
            value  => {
167
                borrowernumber => $patron->{borrowernumber},
168
                itemnumber     => $item->{itemnumber},
169
            },
170
        }
171
    );
172
173
    my $item_object   = Koha::Items->find( $item->{itemnumber} );
174
    my $patron_object = $item_object->last_returned_by();
175
    is( $patron_object, undef, 'Koha::Item::last_returned_by returned undef' );
176
177
    my ( $returned, undef, undef ) = C4::Circulation::AddReturn( $item->{barcode}, undef, undef, undef, '2010-10-10' );
178
179
    $item_object   = Koha::Items->find( $item->{itemnumber} );
180
    $patron_object = $item_object->last_returned_by();
181
    is( ref($patron_object), 'Koha::Borrower', 'Koha::Item::last_returned_by returned Koha::Borrower' );
182
183
    $patron = $builder->build(
184
        {
185
            source => 'Borrower',
186
            value  => { privacy => 1, }
187
        }
188
    );
189
190
    $issue = $builder->build(
191
        {
192
            source => 'Issue',
193
            value  => {
194
                borrowernumber => $patron->{borrowernumber},
195
                itemnumber     => $item->{itemnumber},
196
            },
197
        }
198
    );
199
200
    ( $returned, undef, undef ) = C4::Circulation::AddReturn( $item->{barcode}, undef, undef, undef, '2010-10-10' );
201
202
    $item_object   = Koha::Items->find( $item->{itemnumber} );
203
    $patron_object = $item_object->last_returned_by();
204
    is( $patron_object->id, $patron->{borrowernumber}, 'Second patron to return item replaces the first' );
205
206
    $patron = $builder->build(
207
        {
208
            source => 'Borrower',
209
            value  => { privacy => 1, }
210
        }
211
    );
212
    $patron_object = Koha::Borrowers->find( $patron->{borrowernumber} );
213
214
    $item_object->last_returned_by($patron_object);
215
    $item_object = Koha::Items->find( $item->{itemnumber} );
216
    my $patron_object2 = $item_object->last_returned_by();
217
    is( $patron_object->id, $patron_object2->id,
218
        'Calling last_returned_by with Borrower object sets last_returned_by to that borrower' );
219
};

Return to bug 14945