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

(-)a/Koha/CSV/ItemSearch.pm (+306 lines)
Line 0 Link Here
1
package Koha::CSV::ItemSearch;
2
3
# Copyright 2026 Theke Solutions
4
#
5
# This file is part of Koha.
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 <https://www.gnu.org/licenses>.
19
20
use Modern::Perl;
21
22
use C4::Context;
23
use Koha::DateUtils qw( dt_from_string output_pref );
24
use Koha::I18N      qw( __ );
25
26
use base 'Koha::CSV';
27
28
=head1 NAME
29
30
Koha::CSV::ItemSearch - CSV generator for item search results
31
32
=head1 SYNOPSIS
33
34
    my $csv = Koha::CSV::ItemSearch->new();
35
36
    # Print headers
37
    $csv->print_headers($fh);
38
39
    # Print item rows
40
    foreach my $item (@items) {
41
        $csv->print_item($fh, $item);
42
    }
43
44
=head1 DESCRIPTION
45
46
Specialized CSV generator for item search results with predefined headers
47
and formatting.
48
49
=cut
50
51
=head2 new
52
53
    my $csv = Koha::CSV::ItemSearch->new();
54
55
Creates a new Koha::CSV::ItemSearch object with always_quote enabled
56
for Excel compatibility.
57
58
=cut
59
60
sub new {
61
    my ( $class, $params ) = @_;
62
    $params->{always_quote} = 1;
63
    return $class->SUPER::new($params);
64
}
65
66
=head2 headers
67
68
Returns arrayref of column headers for item search CSV export.
69
70
=cut
71
72
sub headers {
73
    return [
74
        __('Title'),
75
        __('Publication date'),
76
        __('Publisher'),
77
        __('Collection'),
78
        __('Barcode'),
79
        __('Item number'),
80
        __('Serial enumeration'),
81
        __('Call number'),
82
        __('Home library'),
83
        __('Current library'),
84
        __('Shelving location'),
85
        __('Item type'),
86
        __('Inventory number'),
87
        __('Not for loan status'),
88
        __('Lost status'),
89
        __('Withdrawn status'),
90
        __('Damaged status'),
91
        __('Date accessioned'),
92
        __('Checkouts'),
93
        __('Last checkout date'),
94
        __('Due date'),
95
    ];
96
}
97
98
=head2 print_headers
99
100
    $csv->print_headers($fh);
101
102
Prints the CSV headers to a filehandle.
103
104
=cut
105
106
sub print_headers {
107
    my ( $self, $fh ) = @_;
108
    return $self->print( $fh, $self->headers );
109
}
110
111
=head2 format_item
112
113
    my $fields = $csv->format_item($item);
114
115
Formats a Koha::Item object into an arrayref of CSV fields.
116
117
=cut
118
119
sub format_item {
120
    my ( $self, $item ) = @_;
121
122
    my $biblio     = $item->biblio;
123
    my $biblioitem = $item->biblioitem;
124
125
    return [
126
        $self->_format_title($item),
127
        $biblioitem->publicationyear || $biblio->copyrightdate || '',
128
        $biblioitem->publishercode || '',
129
        $self->_format_collection($item),
130
        $item->barcode        || '',
131
        $item->itemnumber     || '',
132
        $item->enumchron      || '',
133
        $item->itemcallnumber || '',
134
        $self->_format_branch( $item->homebranch ),
135
        $self->_format_branch( $item->holdingbranch ),
136
        $self->_format_location($item),
137
        $self->_format_itemtype($item),
138
        $item->stocknumber || '',
139
        $self->_format_notforloan($item),
140
        $self->_format_itemlost($item),
141
        $self->_format_withdrawn($item),
142
        $self->_format_damaged($item),
143
        $self->_format_date( $item->dateaccessioned ),
144
        $item->issues || 0,
145
        $self->_format_date( $item->datelastborrowed ),
146
        $self->_format_due_date($item),
147
    ];
148
}
149
150
=head2 print_item
151
152
    $csv->print_item($fh, $item);
153
154
Formats and prints a Koha::Item to a filehandle as a CSV row.
155
156
=cut
157
158
sub print_item {
159
    my ( $self, $fh, $item ) = @_;
160
    return $self->print( $fh, $self->format_item($item) );
161
}
162
163
# Private formatting methods
164
165
sub _format_title {
166
    my ( $self, $item ) = @_;
167
    my $biblio = $item->biblio;
168
    my $title  = $biblio->title  || '';
169
    my $author = $biblio->author || '';
170
171
    if ( C4::Context->preference('marcflavour') eq 'UNIMARC' && $author ) {
172
        return "$title by $author";
173
    }
174
    return $author ? "$title $author" : $title;
175
}
176
177
sub _format_collection {
178
    my ( $self, $item ) = @_;
179
    return '' unless $item->ccode;
180
181
    require Koha::AuthorisedValues;
182
    my $av = Koha::AuthorisedValues->search(
183
        {
184
            category         => 'CCODE',
185
            authorised_value => $item->ccode,
186
        }
187
    )->next;
188
189
    return $av ? $av->lib : $item->ccode;
190
}
191
192
sub _format_branch {
193
    my ( $self, $branchcode ) = @_;
194
    return '' unless $branchcode;
195
196
    require Koha::Libraries;
197
    my $library = Koha::Libraries->find($branchcode);
198
    return $library ? $library->branchname : $branchcode;
199
}
200
201
sub _format_location {
202
    my ( $self, $item ) = @_;
203
    return '' unless $item->location;
204
205
    require Koha::AuthorisedValues;
206
    my $av = Koha::AuthorisedValues->search(
207
        {
208
            category         => 'LOC',
209
            authorised_value => $item->location,
210
        }
211
    )->next;
212
213
    return $av ? $av->lib : $item->location;
214
}
215
216
sub _format_itemtype {
217
    my ( $self, $item ) = @_;
218
    return '' unless $item->itype;
219
220
    require Koha::ItemTypes;
221
    my $itemtype = Koha::ItemTypes->find( $item->itype );
222
    return $itemtype ? $itemtype->description : $item->itype;
223
}
224
225
sub _format_notforloan {
226
    my ( $self, $item ) = @_;
227
    return '' unless defined $item->notforloan;
228
229
    require Koha::AuthorisedValues;
230
    my $av = Koha::AuthorisedValues->search(
231
        {
232
            category         => 'NOT_LOAN',
233
            authorised_value => $item->notforloan,
234
        }
235
    )->next;
236
237
    return $av ? $av->lib : $item->notforloan;
238
}
239
240
sub _format_itemlost {
241
    my ( $self, $item ) = @_;
242
    return '' unless $item->itemlost;
243
244
    require Koha::AuthorisedValues;
245
    my $av = Koha::AuthorisedValues->search(
246
        {
247
            category         => 'LOST',
248
            authorised_value => $item->itemlost,
249
        }
250
    )->next;
251
252
    return $av ? $av->lib : '';
253
}
254
255
sub _format_withdrawn {
256
    my ( $self, $item ) = @_;
257
    return '' unless $item->withdrawn;
258
259
    require Koha::AuthorisedValues;
260
    my $av = Koha::AuthorisedValues->search(
261
        {
262
            category         => 'WITHDRAWN',
263
            authorised_value => $item->withdrawn,
264
        }
265
    )->next;
266
267
    return $av ? $av->lib : '';
268
}
269
270
sub _format_damaged {
271
    my ( $self, $item ) = @_;
272
    return '' unless $item->damaged;
273
274
    require Koha::AuthorisedValues;
275
    my $av = Koha::AuthorisedValues->search(
276
        {
277
            category         => 'DAMAGED',
278
            authorised_value => $item->damaged,
279
        }
280
    )->next;
281
282
    return $av ? $av->lib : '';
283
}
284
285
sub _format_date {
286
    my ( $self, $date ) = @_;
287
    return '' unless $date;
288
289
    return output_pref(
290
        {
291
            dt         => dt_from_string($date),
292
            dateformat => 'iso',
293
            dateonly   => 1,
294
        }
295
    );
296
}
297
298
sub _format_due_date {
299
    my ( $self, $item ) = @_;
300
    my $checkout = $item->checkout;
301
    return '' unless $checkout;
302
303
    return $self->_format_date( $checkout->date_due );
304
}
305
306
1;
(-)a/t/db_dependent/Koha/CSV/ItemSearch.t (-1 / +141 lines)
Line 0 Link Here
0
- 
1
#!/usr/bin/perl
2
3
# Copyright 2026 Theke Solutions
4
#
5
# This file is part of Koha.
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 <https://www.gnu.org/licenses>.
19
20
use Modern::Perl;
21
22
use Test::More tests => 7;
23
use Test::NoWarnings;
24
25
use t::lib::TestBuilder;
26
use t::lib::Mocks;
27
28
BEGIN {
29
    use_ok('Koha::CSV::ItemSearch');
30
}
31
32
my $schema  = Koha::Database->new->schema;
33
my $builder = t::lib::TestBuilder->new;
34
35
subtest 'new() tests' => sub {
36
    plan tests => 2;
37
38
    my $csv = Koha::CSV::ItemSearch->new();
39
    isa_ok( $csv, 'Koha::CSV::ItemSearch', 'Object created' );
40
    is( $csv->always_quote, 1, 'always_quote is enabled for Excel compatibility' );
41
};
42
43
subtest 'headers() tests' => sub {
44
    plan tests => 2;
45
46
    my $csv     = Koha::CSV::ItemSearch->new();
47
    my $headers = $csv->headers();
48
49
    isa_ok( $headers, 'ARRAY', 'headers returns arrayref' );
50
    is( scalar @$headers, 21, 'headers has 21 columns' );
51
};
52
53
subtest 'format_item() tests' => sub {
54
    plan tests => 6;
55
56
    $schema->storage->txn_begin;
57
58
    t::lib::Mocks::mock_preference( 'CSVDelimiter', ',' );
59
60
    # Create test data
61
    my $library = $builder->build_object( { class => 'Koha::Libraries' } );
62
    my $biblio  = $builder->build_sample_biblio(
63
        {
64
            title  => 'Test Title',
65
            author => 'Test Author',
66
        }
67
    );
68
    my $item = $builder->build_sample_item(
69
        {
70
            biblionumber  => $biblio->biblionumber,
71
            homebranch    => $library->branchcode,
72
            holdingbranch => $library->branchcode,
73
            barcode       => '12345678',
74
        }
75
    );
76
77
    my $csv    = Koha::CSV::ItemSearch->new();
78
    my $fields = $csv->format_item($item);
79
80
    isa_ok( $fields, 'ARRAY', 'format_item returns arrayref' );
81
    is( scalar @$fields, 21, 'format_item returns 21 fields' );
82
83
    # Check specific fields
84
    like( $fields->[0], qr/Test Title/, 'Title field contains title' );
85
    is( $fields->[4], '12345678',           'Barcode field correct' );
86
    is( $fields->[5], $item->itemnumber,    'Item number field correct' );
87
    is( $fields->[8], $library->branchname, 'Home library field correct' );
88
89
    $schema->storage->txn_rollback;
90
};
91
92
subtest 'print_headers() tests' => sub {
93
    plan tests => 2;
94
95
    t::lib::Mocks::mock_preference( 'CSVDelimiter', ',' );
96
97
    my $csv = Koha::CSV::ItemSearch->new();
98
99
    my $output = '';
100
    open my $fh, '>', \$output or die "Cannot open string as file: $!";
101
102
    my $status = $csv->print_headers($fh);
103
    ok( $status, 'print_headers returns true' );
104
105
    close $fh;
106
107
    like( $output, qr/^"Title","Publication date"/, 'Headers printed correctly' );
108
};
109
110
subtest 'print_item() tests' => sub {
111
    plan tests => 2;
112
113
    $schema->storage->txn_begin;
114
115
    t::lib::Mocks::mock_preference( 'CSVDelimiter', ',' );
116
117
    my $library = $builder->build_object( { class => 'Koha::Libraries' } );
118
    my $biblio  = $builder->build_sample_biblio( { title => 'Test Book' } );
119
    my $item    = $builder->build_sample_item(
120
        {
121
            biblionumber  => $biblio->biblionumber,
122
            homebranch    => $library->branchcode,
123
            holdingbranch => $library->branchcode,
124
            barcode       => '99999999',
125
        }
126
    );
127
128
    my $csv = Koha::CSV::ItemSearch->new();
129
130
    my $output = '';
131
    open my $fh, '>', \$output or die "Cannot open string as file: $!";
132
133
    my $status = $csv->print_item( $fh, $item );
134
    ok( $status, 'print_item returns true' );
135
136
    close $fh;
137
138
    like( $output, qr/Test Book/, 'Item data printed correctly' );
139
140
    $schema->storage->txn_rollback;
141
};

Return to bug 41620