[15/40] Initial work on label batch edit interface.
[koha.git] / C4 / Labels / Lib.pm
1 package C4::Labels::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 under the
8 # terms of the GNU General Public License as published by the Free Software
9 # Foundation; either version 2 of the License, or (at your option) any later
10 # version.
11 #
12 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License along with
17 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
18 # Suite 330, Boston, MA  02111-1307 USA
19
20 use strict;
21 use warnings;
22 use Sys::Syslog qw(syslog);
23 use Data::Dumper;
24
25 use C4::Context;
26 use C4::Debug;
27
28 BEGIN {
29     use version; our $VERSION = qv('1.0.0_1');
30     use base qw(Exporter);
31     our @EXPORT_OK = qw(get_all_templates
32                         get_all_layouts
33                         get_all_profiles
34                         get_batch_summary
35                         get_label_summary
36                         get_barcode_types
37                         get_label_types
38                         get_font_types
39                         get_text_justification_types
40                         get_column_names
41                         get_table_names
42                         get_unit_values
43                         html_table
44                         SELECT
45     );
46 }
47
48 #=head2 C4::Labels::Lib::_SELECT()
49 #
50 #    This function returns a recordset upon success and 1 upon failure. Errors are logged to the syslog.
51 #
52 #    examples:
53 #
54 #        my $field_value = _SELECT(field_name, table_name, condition);
55 #
56 #=cut
57
58 sub _SELECT {
59     my @params = @_;
60     my $query = "SELECT $params[0] FROM $params[1]";
61     $params[2] ? $query .= " WHERE $params[2];" : $query .= ';';
62     my $sth = C4::Context->dbh->prepare($query);
63 #    $sth->{'TraceLevel'} = 3;
64     $sth->execute();
65     if ($sth->err) {
66         syslog("LOG_ERR", "C4::Labels::Lib::get_single_field_value : Database returned the following error: %s", $sth->errstr);
67         return 1;
68     }
69     my $record_set = [];
70     while (my $row = $sth->fetchrow_hashref()) {
71         push(@$record_set, $row);
72     }
73     return $record_set;
74 }
75
76 my $barcode_types = [
77     {type => 'CODE39',          name => 'Code 39',              desc => 'Translates the characters 0-9, A-Z, \'-\', \'*\', \'+\', \'$\', \'%\', \'/\', \'.\' and \' \' to a barcode pattern.',                                  selected => 0},
78     {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},
79     {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},
80     {type => 'COOP2OF5',        name => 'COOP2of5',             desc => 'Creates COOP2of5 barcodes from a string consisting of the numeric characters 0-9',                                                                     selected => 0},
81 #    {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},
82 #    {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},
83 #    {type => 'IATA2of5',        name => 'IATA2of5',             desc => 'Creates IATA2of5 barcodes from a string consisting of the numeric characters 0-9',                                                                     selected => 0},
84     {type => 'INDUSTRIAL2OF5',  name => 'Industrial2of5',       desc => 'Creates Industrial2of5 barcodes from a string consisting of the numeric characters 0-9',                                                               selected => 0},
85 #    {type => 'ITF',             name => 'Interleaved2of5',      desc => 'Translates the characters 0-9 to a barcodes. These barcodes could also be called 'Interleaved2of5'.',                                                  selected => 0},
86 #    {type => 'MATRIX2OF5',      name => 'Matrix2of5',           desc => 'Creates Matrix2of5 barcodes from a string consisting of the numeric characters 0-9',                                                                   selected => 0},
87 #    {type => 'NW7',             name => 'NW7',                  desc => 'Creates a NW7 barcodes from a string consisting of the numeric characters 0-9',                                                                        selected => 0},
88 #    {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},
89 #    {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},
90 ];
91
92 my $label_types = [
93     {type => 'BIB',     name => 'Biblio',               desc => 'Only the bibliographic data is printed.',                              selected => 0},
94     {type => 'BARBIB',  name => 'Barcode/Biblio',       desc => 'Barcode proceeds bibliographic data.',                                 selected => 0},
95     {type => 'BIBBAR',  name => 'Biblio/Barcode',       desc => 'Bibliographic data proceeds barcode.',                                 selected => 0},
96     {type => 'ALT',     name => 'Alternating',          desc => 'Barcode and bibliographic data are printed on alternating labels.',    selected => 0},
97     {type => 'BAR',     name => 'Barcode',              desc => 'Only the barcode is printed.',                                         selected => 0},
98 ];
99
100 my $font_types = [
101     {type => 'TR',      name => 'Times-Roman',                  selected => 0},
102     {type => 'TB',      name => 'Times-Bold',                   selected => 0},
103     {type => 'TI',      name => 'Times-Italic',                 selected => 0},
104     {type => 'TBI',     name => 'Times-Bold-Italic',            selected => 0},
105     {type => 'C',       name => 'Courier',                      selected => 0},
106     {type => 'CB',      name => 'Courier-Bold',                 selected => 0},
107     {type => 'CO',      name => 'Courier-Oblique',              selected => 0},
108     {type => 'CBO',     name => 'Courier-Bold-Oblique',         selected => 0},
109     {type => 'H',       name => 'Helvetica',                    selected => 0},
110     {type => 'HB',      name => 'Helvetica-Bold',               selected => 0},
111     {type => 'HBO',     name => 'Helvetica-Bold-Oblique',       selected => 0},
112 ];
113
114 my $text_justification_types = [
115     {type => 'L',       name => 'Left',                         selected => 0},
116     {type => 'C',       name => 'Center',                       selected => 0},
117     {type => 'R',       name => 'Right',                        selected => 0},
118 #    {type => 'F',       name => 'Full',                         selected => 0},    
119 ];
120
121 my $unit_values = [
122     {type       => 'POINT',      desc    => 'PostScript Points',  value   => 1,                 selected => 0},
123     {type       => 'AGATE',      desc    => 'Adobe Agates',       value   => 5.1428571,         selected => 0},
124     {type       => 'INCH',       desc    => 'US Inches',          value   => 72,                selected => 0},
125     {type       => 'MM',         desc    => 'SI Millimeters',     value   => 2.83464567,        selected => 0},
126     {type       => 'CM',         desc    => 'SI Centimeters',     value   => 28.3464567,        selected => 0},
127 ];
128
129 =head2 C4::Labels::Lib::get_all_templates()
130
131     This function returns a reference to a hash containing all templates upon success and 1 upon failure. Errors are logged to the syslog.
132
133     examples:
134
135         my $templates = get_all_templates();
136
137 =cut
138
139 sub get_all_templates {
140     my %params = @_;
141     my @templates = ();
142     my $query = "SELECT " . ($params{'field_list'} ? $params{'field_list'} : '*') . " FROM labels_templates";
143     $query .= ($params{'filter'} ? " WHERE $params{'filter'};" : ';');
144     my $sth = C4::Context->dbh->prepare($query);
145     $sth->execute();
146     if ($sth->err) {
147         syslog("LOG_ERR", "C4::Labels::Lib::get_all_templates : Database returned the following error: %s", $sth->errstr);
148         return -1;
149     }
150     ADD_TEMPLATES:
151     while (my $template = $sth->fetchrow_hashref) {
152         push(@templates, $template);
153     }
154     return \@templates;
155 }
156
157 =head2 C4::Labels::Lib::get_all_layouts()
158
159     This function returns a reference to a hash containing all layouts upon success and 1 upon failure. Errors are logged to the syslog.
160
161     examples:
162
163         my $layouts = get_all_layouts();
164
165 =cut
166
167 sub get_all_layouts {
168     my @layouts = ();
169     my $query = "SELECT * FROM labels_layouts;";
170     my $sth = C4::Context->dbh->prepare($query);
171     $sth->execute();
172     if ($sth->err) {
173         syslog("LOG_ERR", "C4::Labels::Lib::get_all_layouts : Database returned the following error: %s", $sth->errstr);
174         return -1;
175     }
176     ADD_LAYOUTS:
177     while (my $layout = $sth->fetchrow_hashref) {
178         push(@layouts, $layout);
179     }
180     return \@layouts;
181 }
182
183 =head2 C4::Labels::Lib::get_all_profiles()
184
185     This function returns an arrayref whose elements are hashes containing all profiles upon success and 1 upon failure. Errors are logged
186     to the syslog. Two parameters are accepted. The first limits the field(s) returned. This parameter should be string of comma separted
187     fields. ie. "field_1, field_2, ...field_n" The second limits the records returned based on a string containing a valud SQL 'WHERE' filter.
188     NOTE: Do not pass in the keyword 'WHERE.'
189
190     examples:
191
192         my $profiles = get_all_profiles();
193         my $profiles = get_all_profiles(field_list => field_list, filter => filter_string);
194
195 =cut
196
197 sub get_all_profiles {
198     my %params = @_;
199     my @profiles = ();
200     my $query = "SELECT " . ($params{'field_list'} ? $params{'field_list'} : '*') . " FROM printers_profile";
201     $query .= ($params{'filter'} ? " WHERE $params{'filter'};" : ';');
202     my $sth = C4::Context->dbh->prepare($query);
203 #    $sth->{'TraceLevel'} = 3 if $debug;
204     $sth->execute();
205     if ($sth->err) {
206         syslog("LOG_ERR", "C4::Labels::Lib::get_all_profiles : Database returned the following error: %s", $sth->errstr);
207         return -1;
208     }
209     ADD_PROFILES:
210     while (my $profile = $sth->fetchrow_hashref) {
211         push(@profiles, $profile);
212     }
213     return \@profiles;
214 }
215
216 =head2 C4::Labels::Lib::get_batch_summary()
217
218     This function returns an arrayref whose elements are hashes containing the batch_ids of current batches along with the item count
219     for each batch upon success and 1 upon failure. Item counts are stored under the key '_item_count' Errors are logged to the syslog.
220     One parameter is accepted which limits the records returned based on a string containing a valud SQL 'WHERE' filter.
221     
222     NOTE: Do not pass in the keyword 'WHERE.'
223
224     examples:
225
226         my $batches = get_batch_summary();
227         my $batches = get_batch_summary(filter => filter_string);
228
229 =cut
230
231 sub get_batch_summary {
232     my %params = @_;
233     my @batches = ();
234     my $query = "SELECT DISTINCT batch_id FROM labels_batches";
235     $query .= ($params{'filter'} ? " WHERE $params{'filter'};" : ';');
236     my $sth = C4::Context->dbh->prepare($query);
237 #    $sth->{'TraceLevel'} = 3;
238     $sth->execute();
239     if ($sth->err) {
240         syslog("LOG_ERR", "C4::Labels::Lib::get_batch_summary : Database returned the following error on attempted SELECT: %s", $sth->errstr);
241         return -1;
242     }
243     ADD_BATCHES:
244     while (my $batch = $sth->fetchrow_hashref) {
245         my $query = "SELECT count(item_number) FROM labels_batches WHERE batch_id=?;";
246         my $sth1 = C4::Context->dbh->prepare($query);
247         $sth1->execute($batch->{'batch_id'});
248         if ($sth1->err) {
249             syslog("LOG_ERR", "C4::Labels::Lib::get_batch_summary : Database returned the following error on attempted SELECT count: %s", $sth1->errstr);
250             return -1;
251         }
252         my $count = $sth1->fetchrow_arrayref;
253         $batch->{'_item_count'} = @$count[0];
254         push(@batches, $batch);
255     }
256     return \@batches;
257 }
258
259 =head2 C4::Labels::Lib::get_label_summary()
260
261     This function returns an arrayref whose elements are hashes containing the label_ids of current labels along with the item count
262     for each label upon success and 1 upon failure. Item counts are stored under the key '_item_count' Errors are logged to the syslog.
263     One parameter is accepted which limits the records returned based on a string containing a valud SQL 'WHERE' filter.
264     
265     NOTE: Do not pass in the keyword 'WHERE.'
266
267     examples:
268
269         my $labels = get_label_summary();
270         my $labels = get_label_summary(items => @item_list);
271
272 =cut
273
274 sub get_label_summary {
275     my %params = @_;
276     my $label_number = 0;
277     my @label_summaries = ();
278     my $query = "SELECT b.title, b.author, bi.itemtype, i.barcode, i.biblionumber FROM biblio AS b, biblioitems AS bi ,items AS i, labels_batches AS l WHERE itemnumber=? AND l.item_number=i.itemnumber AND i.biblioitemnumber=bi.biblioitemnumber AND bi.biblionumber=b.biblionumber AND l.batch_id=?;";
279     my $sth = C4::Context->dbh->prepare($query);
280     foreach my $item_number (@{$params{'items'}}) {
281         $label_number++;
282         $sth->execute($item_number, $params{'batch_id'});
283         if ($sth->err) {
284             syslog("LOG_ERR", "C4::Labels::Lib::get_label_summary : Database returned the following error on attempted SELECT: %s", $sth->errstr);
285             return -1;
286         }
287         my $record = $sth->fetchrow_hashref;
288         my $label_summary->{'_label_number'} = $label_number;
289         $record->{'author'} =~ s/\W*[^.]$//;  # strip off ugly trailing chars... but not periods
290         $record->{'title'} =~ s/\W*$//;  # strip off ugly trailing chars
291         $record->{'title'} = '<a href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=' . $record->{'biblionumber'} . '"> ' . $record->{'title'} . '</a>';
292         $label_summary->{'_summary'} = $record->{'title'} . " | " . $record->{'author'};
293         $label_summary->{'_item_type'} = $record->{'itemtype'};
294         $label_summary->{'_barcode'} = $record->{'barcode'};
295         $label_summary->{'_item_number'} = $item_number;
296         push (@label_summaries, $label_summary);
297     }
298     return \@label_summaries;
299 }
300
301 =head2 C4::Labels::Lib::get_barcode_types()
302
303     This function returns a reference to an array of hashes containing all barcode types along with their name and description.
304
305     examples:
306
307         my $barcode_types = get_barcode_types();
308
309 =cut
310
311 sub get_barcode_types {
312     return $barcode_types;
313 }
314
315 =head2 C4::Labels::Lib::get_label_types()
316
317     This function returns a reference to an array of hashes containing all label types along with their name and description.
318
319     examples:
320
321         my $label_types = get_label_types();
322
323 =cut
324
325 sub get_label_types {
326     return $label_types;
327 }
328
329 =head2 C4::Labels::Lib::get_font_types()
330
331     This function returns a reference to an array of hashes containing all font types along with their name and description.
332
333     examples:
334
335         my $font_types = get_font_types();
336
337 =cut
338
339 sub get_font_types {
340     return $font_types;
341 }
342
343 =head2 C4::Labels::Lib::get_text_justification_types()
344
345     This function returns a reference to an array of hashes containing all text justification types along with their name and description.
346
347     examples:
348
349         my $text_justification_types = get_text_justification_types();
350
351 =cut
352
353 sub get_text_justification_types {
354     return $text_justification_types;
355 }
356
357 =head2 C4::Labels::Lib::get_unit_values()
358
359     This function returns a reference to an array of  hashes containing all unit types along with their description and multiplier. NOTE: All units are relative to a PostScript Point.
360     There are 72 PS points to the inch.
361
362     examples:
363
364         my $unit_values = get_unit_values();
365
366 =cut
367
368 sub get_unit_values {
369     return $unit_values;
370 }
371
372 =head2 C4::Labels::Lib::get_column_names($table_name)
373
374 Return an arrayref of an array containing the column names of the supplied table.
375
376 =cut
377
378 sub get_column_names {
379     my $table = shift;
380     my $dbh = C4::Context->dbh();
381     my $column_names = [];
382     my $sth = $dbh->column_info(undef,undef,$table,'%');
383     while (my $info = $sth->fetchrow_hashref()){
384         $$column_names[$info->{'ORDINAL_POSITION'}] = $info->{'COLUMN_NAME'};
385     }
386     return $column_names;
387 }
388
389 =head2 C4::Labels::Lib::get_table_names($search_term)
390
391 Return an arrayref of an array containing the table names which contain the supplied search term.
392
393 =cut
394
395 sub get_table_names {
396     my $search_term = shift;
397     my $dbh = C4::Context->dbh();
398     my $table_names = [];
399     my $sth = $dbh->table_info(undef,undef,"%$search_term%");
400     while (my $info = $sth->fetchrow_hashref()){
401         push (@$table_names, $info->{'TABLE_NAME'});
402     }
403     return $table_names;
404 }
405
406 =head2 C4::Labels::Lib::html_table()
407
408     This function returns an arrayref of an array of hashes contianing the supplied data formatted suitably to
409     be passed off as a T::P template parameter and used to build an html table.
410
411     examples:
412
413         my $table = html_table(header_fields, array_of_row_data);
414
415 =cut
416
417 sub html_table {
418     my $headers = shift;
419     my $data = shift;
420     my $table = [];
421     my $fields = [];
422     my @db_columns = ();
423     my ($row_index, $col_index) = (0,0);
424     my $cols = 0;       # number of columns to wrap on
425     my $field_count = 0;
426     my $select_value = undef;
427     POPULATE_HEADER:
428     foreach my $db_column (@$headers) {
429         my @key = keys %$db_column;
430         if ($key[0] eq 'select' ) {
431             push (@db_columns, $key[0]);
432             $$fields[$col_index] = {hidden => 0, select_field => 0, field_name => ($key[0]), field_label => $db_column->{$key[0]}{'label'}};
433             $select_value = $db_column->{$key[0]}{'value'}
434             # do stuff....
435         }
436         else {
437             push (@db_columns, $key[0]);
438             $$fields[$col_index] = {hidden => 0, select_field => 0, field_name => ($key[0]), field_label => $db_column->{$key[0]}};
439         }
440         $field_count++;
441         $col_index++;
442     }
443     $$table[$row_index] = {header_fields => $fields};
444     $cols = $col_index;
445     $field_count *= scalar(@$data);     # total fields to be displayed in the table
446     $col_index = 0;
447     $row_index++;
448     $fields = [];
449     POPULATE_TABLE:
450     foreach my $db_row (@$data) {
451 #        my $element_id = 0;
452         POPULATE_ROW:
453         foreach my $db_column (@db_columns) {
454             if (grep {$db_column eq $_} keys %$db_row) {
455 #                $element_id = $db_row->{$db_column} if $db_column =~ m/id/;
456                 $$fields[$col_index] = {hidden => 0, select_field => 0, field_name => ($db_column . "_tbl"), field_value => $db_row->{$db_column}};
457                 $col_index++;
458                 next POPULATE_ROW;
459             }
460             elsif ($db_column =~ m/^_((.*)_(.*$))/) {   # this a special case
461                 my $table_name = get_table_names($2);
462                 my $record_set = _SELECT($1, @$table_name[0], $2 . "_id = " . $db_row->{$2 . "_id"});
463                 $db_row->{$db_column} = $$record_set[0]{$1};
464                 next POPULATE_ROW;
465             }
466             elsif ($db_column eq 'select' ) {
467                 $$fields[$col_index] = {hidden => 0, select_field => 1, field_name => 'select', field_value => $db_row->{$select_value}};
468             }
469         }
470         $$table[$row_index] = {text_fields => $fields};
471         $col_index = 0;
472         $row_index++;
473         $fields = [];
474     }
475     return $table;
476 }
477
478 1;
479 __END__
480
481 =head1 AUTHOR
482
483 Chris Nighswonger <cnighswonger AT foundations DOT edu>
484
485 =cut