Bug 17301 - Add callnumber to label-edit-batch.pl
[koha.git] / C4 / Creators / Lib.pm
1 package C4::Creators::Lib;
2
3 # Copyright 2009 Foundations Bible College.
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 <http://www.gnu.org/licenses>.
19
20 use strict;
21 use warnings;
22
23 use autouse 'Data::Dumper' => qw(Dumper);
24
25 use C4::Context;
26 use C4::Debug;
27
28 BEGIN {
29     use base qw(Exporter);
30     our @EXPORT = qw(get_all_templates
31                      get_all_layouts
32                      get_all_profiles
33                      get_all_image_names
34                      get_batch_summary
35                      get_label_summary
36                      get_card_summary
37                      get_barcode_types
38                      get_label_types
39                      get_font_types
40                      get_text_justification_types
41                      get_output_formats
42                      get_table_names
43                      get_unit_values
44                      html_table
45     );
46 }
47
48 =head1 NAME
49
50 C4::Creators::Lib
51
52 =cut
53
54 =head1 FUNCTIONS
55
56 =cut
57
58 #=head2 C4::Creators::Lib::_SELECT()
59 #
60 #    This function returns a recordset upon success and 1 upon failure. Errors are logged to the Apache log.
61 #
62 #    examples:
63 #
64 #        my $field_value = _SELECT(field_name, table_name, condition);
65 #
66 #=cut
67
68 sub _SELECT {
69     my @params = @_;
70     my $query = "SELECT $params[0] FROM $params[1]";
71     $params[2] ? $query .= " WHERE $params[2];" : $query .= ';';
72     my $sth = C4::Context->dbh->prepare($query);
73 #    $sth->{'TraceLevel'} = 3;
74     $sth->execute();
75     if ($sth->err) {
76         warn sprintf('Database returned the following error: %s', $sth->errstr);
77         return 1;
78     }
79     my $record_set = [];
80     while (my $row = $sth->fetchrow_hashref()) {
81         push(@$record_set, $row);
82     }
83     return $record_set;
84 }
85
86 my $barcode_types = [
87     {type => 'CODE39',          name => 'Code 39',              desc => 'Translates the characters 0-9, A-Z, \'-\', \'*\', \'+\', \'$\', \'%\', \'/\', \'.\' and \' \' to a barcode pattern.',                                  selected => 0},
88     {type => 'CODE39MOD',       name => 'Code 39 + Modulo43',   desc => 'Translates the characters 0-9, A-Z, \'-\', \'*\', \'+\', \'$\', \'%\', \'/\', \'.\' and \' \' to a barcode pattern. Encodes Mod 43 checksum.',         selected => 0},
89     {type => 'CODE39MOD10',     name => 'Code 39 + Modulo10',   desc => 'Translates the characters 0-9, A-Z, \'-\', \'*\', \'+\', \'$\', \'%\', \'/\', \'.\' and \' \' to a barcode pattern. Encodes Mod 10 checksum.',         selected => 0},
90     {type => 'COOP2OF5',        name => 'COOP2of5',             desc => 'Creates COOP2of5 barcodes from a string consisting of the numeric characters 0-9',                                                                     selected => 0},
91     {type => 'EAN13',           name => 'EAN13',                desc => 'Creates EAN13 barcodes from a string of 12 or 13 digits. The check number (the 13:th digit) is calculated if not supplied.',                           selected => 0},
92 #    {type => 'EAN8',            name => 'EAN8',                 desc => 'Translates a string of 7 or 8 digits to EAN8 barcodes. The check number (the 8:th digit) is calculated if not supplied.',                              selected => 0},
93 #    {type => 'IATA2of5',        name => 'IATA2of5',             desc => 'Creates IATA2of5 barcodes from a string consisting of the numeric characters 0-9',                                                                     selected => 0},
94     {type => 'INDUSTRIAL2OF5',  name => 'Industrial2of5',       desc => 'Creates Industrial2of5 barcodes from a string consisting of the numeric characters 0-9',                                                               selected => 0},
95 #    {type => 'ITF',             name => 'Interleaved2of5',      desc => 'Translates the characters 0-9 to a barcodes. These barcodes could also be called 'Interleaved2of5'.',                                                  selected => 0},
96 #    {type => 'MATRIX2OF5',      name => 'Matrix2of5',           desc => 'Creates Matrix2of5 barcodes from a string consisting of the numeric characters 0-9',                                                                   selected => 0},
97 #    {type => 'NW7',             name => 'NW7',                  desc => 'Creates a NW7 barcodes from a string consisting of the numeric characters 0-9',                                                                        selected => 0},
98 #    {type => 'UPCA',            name => 'UPCA',                 desc => 'Translates a string of 11 or 12 digits to UPCA barcodes. The check number (the 12:th digit) is calculated if not supplied.',                           selected => 0},
99 #    {type => 'UPCE',            name => 'UPCE',                 desc => 'Translates a string of 6, 7 or 8 digits to UPCE barcodes. If the string is 6 digits long, '0' is added first in the string. The check number (the 8:th digit) is calculated if not supplied.',                                 selected => 0},
100 ];
101
102 my $label_types = [
103     {type => 'BIB',     name => 'Biblio',               desc => 'Only the bibliographic data is printed.',                              selected => 0},
104     {type => 'BARBIB',  name => 'Barcode/Biblio',       desc => 'Barcode proceeds bibliographic data.',                                 selected => 0},
105     {type => 'BIBBAR',  name => 'Biblio/Barcode',       desc => 'Bibliographic data proceeds barcode.',                                 selected => 0},
106     {type => 'ALT',     name => 'Alternating',          desc => 'Barcode and bibliographic data are printed on alternating labels.',    selected => 0},
107     {type => 'BAR',     name => 'Barcode',              desc => 'Only the barcode is printed.',                                         selected => 0},
108 ];
109
110 my $font_types = [
111     {type => 'TR',      name => 'Times-Roman',                  selected => 0},
112     {type => 'TB',      name => 'Times-Bold',                   selected => 0},
113     {type => 'TI',      name => 'Times-Italic',                 selected => 0},
114     {type => 'TBI',     name => 'Times-Bold-Italic',            selected => 0},
115     {type => 'C',       name => 'Courier',                      selected => 0},
116     {type => 'CB',      name => 'Courier-Bold',                 selected => 0},
117     {type => 'CO',      name => 'Courier-Oblique',              selected => 0},
118     {type => 'CBO',     name => 'Courier-Bold-Oblique',         selected => 0},
119     {type => 'H',       name => 'Helvetica',                    selected => 0},
120     {type => 'HO',      name => 'Helvetica-Oblique',            selected => 0},
121     {type => 'HB',      name => 'Helvetica-Bold',               selected => 0},
122     {type => 'HBO',     name => 'Helvetica-Bold-Oblique',       selected => 0},
123 ];
124
125 my $text_justification_types = [
126     {type => 'L',       name => 'Left',                         selected => 0},
127     {type => 'C',       name => 'Center',                       selected => 0},
128     {type => 'R',       name => 'Right',                        selected => 0},
129 #    {type => 'F',       name => 'Full',                         selected => 0},
130 ];
131
132 my $unit_values = [
133     {type       => 'POINT',      desc    => 'PostScript Points',  value   => 1,                 selected => 0},
134     {type       => 'AGATE',      desc    => 'Adobe Agates',       value   => 5.1428571,         selected => 0},
135     {type       => 'INCH',       desc    => 'US Inches',          value   => 72,                selected => 0},
136     {type       => 'MM',         desc    => 'SI Millimeters',     value   => 2.83464567,        selected => 0},
137     {type       => 'CM',         desc    => 'SI Centimeters',     value   => 28.3464567,        selected => 0},
138 ];
139
140 my $output_formats = [
141     {type       => 'pdf',       desc    => 'PDF File'},
142     {type       => 'csv',       desc    => 'CSV File'},
143 ];
144
145 =head2 C4::Creators::Lib::get_all_templates()
146
147   my $templates = get_all_templates();
148
149 This function returns a reference to a hash containing all templates upon success and 1 upon failure. Errors are logged to the Apache log.
150
151 =cut
152
153 sub get_all_templates {
154     my %params = @_;
155     my @templates = ();
156     my $query = "SELECT " . ($params{'field_list'} ? $params{'field_list'} : '*') . " FROM creator_templates";
157     $query .= ($params{'filter'} ? " WHERE $params{'filter'} " : '');
158     $query .= ($params{'orderby'} ? " ORDER BY $params{'orderby'} " : '');
159     my $sth = C4::Context->dbh->prepare($query);
160     $sth->execute();
161     if ($sth->err) {
162         warn sprintf('Database returned the following error: %s', $sth->errstr);
163         return -1;
164     }
165     ADD_TEMPLATES:
166     while (my $template = $sth->fetchrow_hashref) {
167         push(@templates, $template);
168     }
169     return \@templates;
170 }
171
172 =head2 C4::Creators::Lib::get_all_layouts()
173
174   my $layouts = get_all_layouts();
175
176 This function returns a reference to a hash containing all layouts upon success and 1 upon failure. Errors are logged to the Apache log.
177
178 =cut
179
180 sub get_all_layouts {
181     my %params = @_;
182     my @layouts = ();
183     my $query = "SELECT " . ($params{'field_list'} ? $params{'field_list'} : '*') . " FROM creator_layouts";
184     $query .= ($params{'filter'} ? " WHERE $params{'filter'} " : '');
185     $query .= ($params{'orderby'} ? " ORDER BY $params{'orderby'} " : '');
186     my $sth = C4::Context->dbh->prepare($query);
187     $sth->execute();
188     if ($sth->err) {
189         warn sprintf('Database returned the following error: %s', $sth->errstr);
190         return -1;
191     }
192     ADD_LAYOUTS:
193     while (my $layout = $sth->fetchrow_hashref) {
194         push(@layouts, $layout);
195     }
196     return \@layouts;
197 }
198
199 =head2 C4::Creators::Lib::get_all_profiles()
200
201   my $profiles = get_all_profiles();
202
203   my $profiles = get_all_profiles(field_list => field_list, filter => filter_string);
204
205 This function returns an arrayref whose elements are hashes containing all profiles upon success and 1 upon failure. Errors are logged
206 to the Apache log. Two parameters are accepted. The first limits the field(s) returned. This parameter should be string of comma separted
207 fields. ie. "field_1, field_2, ...field_n" The second limits the records returned based on a string containing a valud SQL 'WHERE' filter.
208
209 NOTE: Do not pass in the keyword 'WHERE.'
210
211 =cut
212
213 sub get_all_profiles {
214     my %params = @_;
215     my @profiles = ();
216     my $query = "SELECT " . ($params{'field_list'} ? $params{'field_list'} : '*') . " FROM printers_profile";
217     $query .= ($params{'filter'} ? " WHERE $params{'filter'};" : ';');
218     my $sth = C4::Context->dbh->prepare($query);
219 #    $sth->{'TraceLevel'} = 3 if $debug;
220     $sth->execute();
221     if ($sth->err) {
222         warn sprintf('Database returned the following error: %s', $sth->errstr);
223         return -1;
224     }
225     ADD_PROFILES:
226     while (my $profile = $sth->fetchrow_hashref) {
227         push(@profiles, $profile);
228     }
229     return \@profiles;
230 }
231
232 =head2 C4::Creators::Lib::get_all_image_names()
233
234 =cut
235
236 sub get_all_image_names {
237     my $image_names = [];
238     my $query = "SELECT image_name FROM creator_images";
239     my $sth = C4::Context->dbh->prepare($query);
240 #    $sth->{'TraceLevel'} = 3 if $debug;
241     $sth->execute();
242     if ($sth->err) {
243         warn sprintf('Database returned the following error: %s', $sth->errstr);
244         return -1;
245     }
246     grep {push @$image_names, {type => $$_[0], name => $$_[0], selected => 0}} @{$sth->fetchall_arrayref([0])};
247     return $image_names;
248 }
249
250 =head2 C4::Creators::Lib::get_batch_summary()
251
252   my $batches = get_batch_summary();
253
254   my $batches = get_batch_summary(filter => filter_string);
255
256 This function returns an arrayref whose elements are hashes containing the batch_ids of current batches along with the item count
257 for each batch upon success and 1 upon failure. Item counts are stored under the key '_item_count' Errors are logged to the Apache log.
258 One parameter is accepted which limits the records returned based on a string containing a valud SQL 'WHERE' filter.
259
260 NOTE: Do not pass in the keyword 'WHERE.'
261
262 =cut
263
264 sub get_batch_summary {
265     my %params = @_;
266     my @batches = ();
267     my $query = "SELECT batch_id,count(batch_id) as _item_count FROM creator_batches WHERE creator=?";
268     $query .= ($params{'filter'} ? " AND $params{'filter'}" : '');
269     $query .= " GROUP BY batch_id";
270     my $sth = C4::Context->dbh->prepare($query);
271 #    $sth->{'TraceLevel'} = 3;
272     $sth->execute($params{'creator'});
273     if ($sth->err) {
274         warn sprintf('Database returned the following error on attempted SELECT: %s', $sth->errstr);
275         return -1;
276     }
277     while (my $batch = $sth->fetchrow_hashref) {
278         push(@batches, $batch);
279     }
280     return \@batches;
281 }
282
283 =head2 C4::Creators::Lib::get_label_summary()
284
285   my $labels = get_label_summary();
286
287   my $labels = get_label_summary(items => @item_list);
288
289 This function returns an arrayref whose elements are hashes containing the label_ids of current labels along with the item count
290 for each label upon success and 1 upon failure. Item counts are stored under the key '_item_count' Errors are logged to the Apache log.
291 One parameter is accepted which limits the records returned based on a string containing a valud SQL 'WHERE' filter.
292
293 NOTE: Do not pass in the keyword 'WHERE.'
294
295 =cut
296
297 sub get_label_summary {
298     my %params = @_;
299     my $label_number = 0;
300     my @label_summaries = ();
301     my $query = "     SELECT b.title, b.author, bi.itemtype, i.barcode, i.itemcallnumber, i.biblionumber, i.itype
302                       FROM creator_batches AS c LEFT JOIN items AS i ON (c.item_number=i.itemnumber)
303                       LEFT JOIN biblioitems AS bi ON (i.biblioitemnumber=bi.biblioitemnumber)
304                       LEFT JOIN biblio AS b ON (bi.biblionumber=b.biblionumber)
305                       WHERE itemnumber=? AND batch_id=?;
306                   ";
307     my $sth = C4::Context->dbh->prepare($query);
308     foreach my $item (@{$params{'items'}}) {
309         $label_number++;
310         $sth->execute($item->{'item_number'}, $params{'batch_id'});
311         if ($sth->err) {
312             warn sprintf('Database returned the following error on attempted SELECT: %s', $sth->errstr);
313             return -1;
314         }
315         my $record = $sth->fetchrow_hashref;
316         my $label_summary;
317         $label_summary->{'_label_number'} = $label_number;
318         $record->{'author'} =~ s/[^\.|\w]$// if $record->{'author'};  # strip off ugly trailing chars... but not periods or word chars
319         $record->{'title'} =~ s/\W*$//;  # strip off ugly trailing chars
320         # FIXME contructing staff interface URLs should be done *much* higher up the stack - for the most part, C4 module code
321         # should not know that it's part of a web app
322         $record->{'title'} = '<a href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=' . $record->{'biblionumber'} . '"> ' . $record->{'title'} . '</a>';
323         $label_summary->{'_summary'} = $record->{'title'} . " | " . ($record->{'author'} ? $record->{'author'} : 'N/A');
324         $label_summary->{'_item_type'} = C4::Context->preference("item-level_itypes") ? $record->{'itype'} : $record->{'itemtype'};
325         $label_summary->{'_barcode'} = $record->{'barcode'};
326         $label_summary->{'_item_number'} = $item->{'item_number'};
327         $label_summary->{'_item_cn'} = $record->{'itemcallnumber'};
328         $label_summary->{'_label_id'} = $item->{'label_id'};
329         push (@label_summaries, $label_summary);
330     }
331     return \@label_summaries;
332 }
333
334 =head2 C4::Creators::Lib::get_card_summary()
335
336   my $cards = get_card_summary();
337
338   my $cards = get_card_summary(items => @item_list);
339
340 This function returns an arrayref whose elements are hashes containing the label_ids of current cards along with the item count
341 for each card upon success and 1 upon failure. Item counts are stored under the key '_item_count' Errors are logged to the Apache log.
342 One parameter is accepted which limits the records returned based on a string containing a valud SQL 'WHERE' filter.
343
344 NOTE: Do not pass in the keyword 'WHERE.'
345
346 =cut
347
348 sub get_card_summary {
349     my %params = @_;
350     my $card_number = 0;
351     my @card_summaries = ();
352     my $query = "SELECT CONCAT_WS(', ', surname, firstname) AS name, cardnumber FROM borrowers WHERE borrowernumber=?;";
353     my $sth = C4::Context->dbh->prepare($query);
354     foreach my $item (@{$params{'items'}}) {
355         $card_number++;
356         $sth->execute($item->{'borrower_number'});
357         if ($sth->err) {
358             warn sprintf('Database returned the following error on attempted SELECT: %s', $sth->errstr);
359             return -1;
360         }
361         my $record = $sth->fetchrow_hashref;
362         my $card_summary->{'_card_number'} = $card_number;
363         $card_summary->{'_summary'} = $record->{'name'};
364         $card_summary->{'borrowernumber'} = $item->{'borrower_number'};
365         $card_summary->{'_label_id'} = $item->{'label_id'};
366         push (@card_summaries, $card_summary);
367     }
368     return \@card_summaries;
369 }
370
371 =head2 C4::Creators::Lib::get_barcode_types()
372
373   my $barcode_types = get_barcode_types();
374
375 This function returns a reference to an array of hashes containing all barcode types along with their name and description.
376
377 =cut
378
379 sub get_barcode_types {
380     return $barcode_types;
381 }
382
383 =head2 C4::Creators::Lib::get_label_types()
384
385   my $label_types = get_label_types();
386
387 This function returns a reference to an array of hashes containing all label types along with their name and description.
388
389 =cut
390
391 sub get_label_types {
392     return $label_types;
393 }
394
395 =head2 C4::Creators::Lib::get_font_types()
396
397   my $font_types = get_font_types();
398
399 This function returns a reference to an array of hashes containing all font types along with their name and description.
400
401 =cut
402
403 sub get_font_types {
404     return $font_types;
405 }
406
407 =head2 C4::Creators::Lib::get_text_justification_types()
408
409   my $text_justification_types = get_text_justification_types();
410
411 This function returns a reference to an array of hashes containing all text justification types along with their name and description.
412
413 =cut
414
415 sub get_text_justification_types {
416     return $text_justification_types;
417 }
418
419 =head2 C4::Creators::Lib::get_unit_values()
420
421   my $unit_values = get_unit_values();
422
423 This function returns a reference to an array of  hashes containing all unit types along with their description and multiplier.
424 NOTE: All units are relative to a PostScript Point.
425 There are 72 PS points to the inch.
426
427 =cut
428
429 sub get_unit_values {
430     return $unit_values;
431 }
432
433 =head2 C4::Creators::Lib::get_output_formats()
434
435   my $label_output_formats = get_output_formats();
436
437 This function returns a reference to an array of hashes containing all label output formats along with their description.
438
439 =cut
440
441 sub get_output_formats {
442     return $output_formats;
443 }
444
445
446 =head2 C4::Creators::Lib::get_table_names($search_term)
447
448 Return an arrayref of an array containing the table names which contain the supplied search term.
449
450 =cut
451
452 sub get_table_names {
453     my $search_term = shift;
454     my $dbh = C4::Context->dbh();
455     my $table_names = [];
456     my $sth = $dbh->table_info(undef,undef,"%$search_term%");
457     while (my $info = $sth->fetchrow_hashref()){
458         push (@$table_names, $info->{'TABLE_NAME'});
459     }
460     return $table_names;
461 }
462
463 =head2 C4::Creators::Lib::html_table()
464
465 This function returns an arrayref of an array of hashes contianing the supplied data formatted suitably to
466 be passed off as a template parameter and used to build an html table.
467
468    my $table = html_table(header_fields, array_of_row_data);
469    $template->param(
470        table_loop => $table,
471    );
472
473     html example:
474
475         <table>
476             [% FOREACH table_loo IN table_loop %]
477                 [% IF ( table_loo.header_fields ) %]
478                     <tr>
479                         [% FOREACH header_field IN table_loo.header_fields %]
480                             <th>[% header_field.field_label %]</th>
481                         [% END %]
482                     </tr>
483                 [% ELSE %]
484                     <tr>
485                         [% FOREACH text_field IN table_loo.text_fields %]
486                             [% IF ( text_field.select_field ) %]
487                                 <td><input type="checkbox" name="action" value="[% text_field.field_value %]"></td>
488                             [% ELSE %]
489                                 <td>[% text_field.field_value %]</td>
490                             [% END %]
491                         [% END %]
492                     </tr>
493                 [% END %]
494             [% END %]
495         </table>
496
497 =cut
498
499 sub html_table {
500     my $headers = shift;
501     my $data = shift;
502     return undef if scalar(@$data) == 0;      # no need to generate a table if there is not data to display
503     my $table = [];
504     my $fields = [];
505     my @table_columns = ();
506     my ($row_index, $col_index) = (0,0);
507     my $cols = 0;       # number of columns to wrap on
508     my $field_count = 0;
509     my $select_value = undef;
510     my $link_field = undef;
511     POPULATE_HEADER:
512     foreach my $header (@$headers) {
513         my @key = keys %$header;
514         if ($key[0] eq 'select' ) {
515             push (@table_columns, $key[0]);
516             $$fields[$col_index] = {hidden => 0, select_field => 0, field_name => ($key[0]), field_label => $header->{$key[0]}{'label'}};
517             # do special formatting stuff....
518             $select_value = $header->{$key[0]}{'value'};
519         }
520         else {
521             # do special formatting stuff....
522             $link_field->{$key[0]} = ($header->{$key[0]}{'link_field'} == 1 ? 1 : 0);
523             push (@table_columns, $key[0]);
524             $$fields[$col_index] = {hidden => 0, select_field => 0, field_name => ($key[0]), field_label => $header->{$key[0]}{'label'}};
525         }
526         $field_count++;
527         $col_index++;
528     }
529     $$table[$row_index] = {header_fields => $fields};
530     $cols = $col_index;
531     $field_count *= scalar(@$data);     # total fields to be displayed in the table
532     $col_index = 0;
533     $row_index++;
534     $fields = [];
535     POPULATE_TABLE:
536     foreach my $db_row (@$data) {
537         POPULATE_ROW:
538         foreach my $table_column (@table_columns) {
539             if (grep {$table_column eq $_} keys %$db_row) {
540                 $$fields[$col_index] = {hidden => 0, link_field => $link_field->{$table_column}, select_field => 0, field_name => ($table_column . "_tbl"), field_value => $db_row->{$table_column}};
541                 $col_index++;
542                 next POPULATE_ROW;
543             }
544             elsif ($table_column =~ m/^_((.*)_(.*$))/) {   # this a special case
545                 my $table_name = get_table_names($2);
546                 my $record_set = _SELECT($1, @$table_name[0], $2 . "_id = " . $db_row->{$2 . "_id"});
547                 $$fields[$col_index] = {hidden => 0, link_field => $link_field->{$table_column}, select_field => 0, field_name => ($table_column . "_tbl"), field_value => $$record_set[0]{$1}};
548                 $col_index++;
549                 next POPULATE_ROW;
550             }
551             elsif ($table_column eq 'select' ) {
552                 $$fields[$col_index] = {hidden => 0, select_field => 1, field_name => 'select', field_value => $db_row->{$select_value}};
553             }
554         }
555         $$table[$row_index] = {text_fields => $fields};
556         $col_index = 0;
557         $row_index++;
558         $fields = [];
559     }
560     return $table;
561 }
562
563 1;
564 __END__
565
566 =head1 AUTHOR
567
568 Chris Nighswonger <cnighswonger AT foundations DOT edu>
569
570 =cut