| File: | C4/Barcodes.pm | 
| Coverage: | 17.4% | 
| line | stmt | bran | cond | sub | time | code | 
|---|---|---|---|---|---|---|
| 1 | package C4::Barcodes; | |||||
| 2 | ||||||
| 3 | # Copyright 2008 LibLime | |||||
| 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 | |||||
| 17 | # with Koha; if not, write to the Free Software Foundation, Inc., | |||||
| 18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |||||
| 19 | ||||||
| 20 | 1 1 1  | 311 6 28  | use strict; | |||
| 21 | 1 1 1  | 9 5 40  | use warnings; | |||
| 22 | ||||||
| 23 | 1 1 1  | 9 5 95  | use Carp; | |||
| 24 | ||||||
| 25 | 1 1 1  | 9 4 17  | use C4::Context; | |||
| 26 | 1 1 1  | 10 5 86  | use C4::Debug; | |||
| 27 | 1 1 1  | 9 4 36  | use C4::Dates; | |||
| 28 | 1 1 1  | 216 2 27  | use C4::Barcodes::hbyymmincr; | |||
| 29 | 1 1 1  | 235 2 26  | use C4::Barcodes::annual; | |||
| 30 | 1 1 1  | 222 1 33  | use C4::Barcodes::incremental; | |||
| 31 | ||||||
| 32 | 1 1 1  | 4 2 72  | use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); | |||
| 33 | 1 1 1  | 4 2 37  | use vars qw($debug $cgi_debug); # from C4::Debug, of course | |||
| 34 | 1 1 1  | 4 1 90  | use vars qw($max $prefformat); | |||
| 35 | ||||||
| 36 | BEGIN { | |||||
| 37 | 1  | 2  | $VERSION = 0.01; | |||
| 38 | 1  | 5  | require Exporter; | |||
| 39 | 1  | 18  | @ISA = qw(Exporter); | |||
| 40 | 1  | 2391  | @EXPORT_OK = qw(); | |||
| 41 | } | |||||
| 42 | ||||||
| 43 | sub _prefformat { | |||||
| 44 | 0  | unless (defined $prefformat) { | ||||
| 45 | 0  | unless ($prefformat = C4::Context->preference('autoBarcode')) { | ||||
| 46 | 0  | carp "The autoBarcode syspref is missing/undefined. Assuming 'incremental'."; | ||||
| 47 | 0  | $prefformat = 'incremental'; | ||||
| 48 | } | |||||
| 49 | } | |||||
| 50 | 0  | return $prefformat; | ||||
| 51 | } | |||||
| 52 | ||||||
| 53 | sub initial { | |||||
| 54 | 0  | return '0000001'; | ||||
| 55 | } | |||||
| 56 | sub width { | |||||
| 57 | 0  | return undef; | ||||
| 58 | } | |||||
| 59 | sub process_head($$;$$) { # (self,head,whole,specific) | |||||
| 60 | 0  | my $self = shift; | ||||
| 61 | 0  | return shift; # Default: just return the head unchanged. | ||||
| 62 | } | |||||
| 63 | sub process_tail($$;$$) { # (self,tail,whole,specific) | |||||
| 64 | 0  | my $self = shift; | ||||
| 65 | 0  | return shift; # Default: just return the tail unchanged. | ||||
| 66 | } | |||||
| 67 | sub is_max ($;$) { | |||||
| 68 | 0  | my $self = shift; | ||||
| 69 | 0  | ref($self) or carp "Called is_max on a non-object: '$self'"; | ||||
| 70 | 0  | (@_) and $self->{is_max} = shift; | ||||
| 71 | 0  | return $self->{is_max} || 0; | ||||
| 72 | } | |||||
| 73 | sub value ($;$) { | |||||
| 74 | 0  | my $self = shift; | ||||
| 75 | 0  | if (@_) { | ||||
| 76 | 0  | my $value = shift; | ||||
| 77 | 0  | if (defined $value) { | ||||
| 78 | 0  | $debug and print STDERR " setting barcode value to $value\n"; | ||||
| 79 | } else { | |||||
| 80 | 0  | warn "Error: UNDEF argument to value"; | ||||
| 81 | } | |||||
| 82 | 0  | $self->{value} = $value; | ||||
| 83 | } | |||||
| 84 | 0  | return $self->{value}; | ||||
| 85 | } | |||||
| 86 | sub autoBarcode (;$) { | |||||
| 87 | 0  | (@_) or return _prefformat; | ||||
| 88 | 0  | my $self = shift; | ||||
| 89 | 0  | my $value = $self->{autoBarcode} or return _prefformat; | ||||
| 90 | 0  | $value =~ s/^.*:://; # in case we get C4::Barcodes::incremental, we just want 'incremental' | ||||
| 91 | 0  | return $value; | ||||
| 92 | } | |||||
| 93 | sub parse ($;$) { # return 3 parts of barcode: non-incrementing, incrementing, non-incrementing | |||||
| 94 | 0  | my $self = shift; | ||||
| 95 | 0  | my $barcode = (@_) ? shift : $self->value; | ||||
| 96 | 0  | unless ($barcode =~ /(.*?)(\d+)$/) { # non-greedy match in first part | ||||
| 97 | 0  | carp "Barcode '$barcode' has no incrementing part!"; | ||||
| 98 | 0  | return ($barcode,undef,undef); | ||||
| 99 | } | |||||
| 100 | 0  | $debug and warn "Barcode '$barcode' parses into: '$1', '$2', ''"; | ||||
| 101 | 0  | return ($1,$2,''); # the third part is in anticipation of barcodes that include checkdigits | ||||
| 102 | } | |||||
| 103 | sub max ($;$) { | |||||
| 104 | 0  | my $self = shift; | ||||
| 105 | 0  | if ($self->{is_max}) { | ||||
| 106 | 0  | $debug and print STDERR "max taken from Barcodes value $self->value\n"; | ||||
| 107 | 0  | return $self->value; | ||||
| 108 | } | |||||
| 109 | 0  | $debug and print STDERR "Retrieving max database query.\n"; | ||||
| 110 | 0  | return $self->db_max; | ||||
| 111 | } | |||||
| 112 | sub db_max () { | |||||
| 113 | 0  | my $self = shift; | ||||
| 114 | 0  | my $query = "SELECT max(abs(barcode)) FROM items LIMIT 1"; # Possible problem if multiple barcode types populated | ||||
| 115 | 0  | my $sth = C4::Context->dbh->prepare($query); | ||||
| 116 | 0  | $sth->execute(); | ||||
| 117 | 0  | return $sth->fetchrow_array || $self->initial; | ||||
| 118 | } | |||||
| 119 | sub next_value ($;$) { | |||||
| 120 | 0  | my $self = shift; | ||||
| 121 | 0  | my $specific = (scalar @_) ? 1 : 0; | ||||
| 122 | 0  | my $max = $specific ? shift : $self->max; # optional argument, i.e. next_value after X | ||||
| 123 | 0  | unless ($max) { | ||||
| 124 | 0  | warn "No max barcode ($self->autoBarcode format) found. Using initial value."; | ||||
| 125 | 0  | return $self->initial; | ||||
| 126 | } | |||||
| 127 | 0  | $debug and print STDERR "(current) max barcode found: $max\n"; | ||||
| 128 | 0  | my ($head,$incr,$tail) = $self->parse($max); # for incremental, you'd get ('',the_whole_barcode,'') | ||||
| 129 | 0  | unless (defined $incr) { | ||||
| 130 | 0  | warn "No incrementing part of barcode ($max) returned by parse."; | ||||
| 131 | 0  | return undef; | ||||
| 132 | } | |||||
| 133 | 0  | my $x = length($incr); # number of digits | ||||
| 134 | 0  | $incr =~ /^9+$/ and $x++; # if they're all 9's, we need an extra. | ||||
| 135 | # Note, this enlargement might be undesireable for some barcode formats. | |||||
| 136 | # Those should override next_value() to work accordingly. | |||||
| 137 | 0  | $incr++; | ||||
| 138 | ||||||
| 139 | 0  | $debug and warn "$incr"; | ||||
| 140 | 0  | $head = $self->process_head($head,$max,$specific); | ||||
| 141 | 0  | $tail = $self->process_tail($tail,$max,$specific); | ||||
| 142 | 0  | my $next_value = $head . $incr . $tail; | ||||
| 143 | 0  | $debug and print STDERR "( next ) max barcode found: $next_value\n"; | ||||
| 144 | 0  | return $next_value; | ||||
| 145 | } | |||||
| 146 | sub next ($;$) { | |||||
| 147 | 0  | my $self = shift or return undef; | ||||
| 148 | 0  | (@_) and $self->{next} = shift; | ||||
| 149 | 0  | return $self->{next}; | ||||
| 150 | } | |||||
| 151 | sub previous ($;$) { | |||||
| 152 | 0  | my $self = shift or return undef; | ||||
| 153 | 0  | (@_) and $self->{previous} = shift; | ||||
| 154 | 0  | return $self->{previous}; | ||||
| 155 | } | |||||
| 156 | sub serial ($;$) { | |||||
| 157 | 0  | my $self = shift or return undef; | ||||
| 158 | 0  | (@_) and $self->{serial} = shift; | ||||
| 159 | 0  | return $self->{serial}; | ||||
| 160 | } | |||||
| 161 | sub default_self (;$) { | |||||
| 162 | 0  | (@_) or carp "default_self called with no argument. Reverting to _prefformat."; | ||||
| 163 | 0  | my $autoBarcode = (@_) ? shift : _prefformat; | ||||
| 164 | 0  | $autoBarcode =~ s/^.*:://; # in case we get C4::Barcodes::incremental, we just want 'incremental' | ||||
| 165 | return { | |||||
| 166 | 0  | is_max => 0, | ||||
| 167 | autoBarcode => $autoBarcode, | |||||
| 168 | value => undef, | |||||
| 169 | previous => undef, | |||||
| 170 | 'next' => undef, | |||||
| 171 | serial => 1 | |||||
| 172 | }; | |||||
| 173 | } | |||||
| 174 | ||||||
| 175 | our $types = { | |||||
| 176 | annual => sub {C4::Barcodes::annual->new_object(@_); }, | |||||
| 177 | incremental => sub {C4::Barcodes::incremental->new_object(@_);}, | |||||
| 178 | hbyymmincr => sub {C4::Barcodes::hbyymmincr->new_object(@_); }, | |||||
| 179 | OFF => sub {C4::Barcodes::OFF->new_object(@_); }, | |||||
| 180 | }; | |||||
| 181 | ||||||
| 182 | sub new { | |||||
| 183 | 0  | my $class_or_object = shift; | ||||
| 184 | 0  | my $type = ref($class_or_object) || $class_or_object; | ||||
| 185 | 0  | my $from_obj = ref($class_or_object) ? 1 : 0; # are we building off another Barcodes object? | ||||
| 186 | 0  | if ($from_obj) { | ||||
| 187 | 0  | $debug and print STDERR "Building new(@_) from old Barcodes object\n"; | ||||
| 188 | } | |||||
| 189 | 0  | my $autoBarcodeType = (@_) ? shift : $from_obj ? $class_or_object->autoBarcode : _prefformat; | ||||
| 190 | 0  | $autoBarcodeType =~ s/^.*:://; # in case we get C4::Barcodes::incremental, we just want 'incremental' | ||||
| 191 | 0  | unless ($autoBarcodeType) { | ||||
| 192 | 0  | carp "No autoBarcode format found."; | ||||
| 193 | 0  | return undef; | ||||
| 194 | } | |||||
| 195 | 0  | unless (defined $types->{$autoBarcodeType}) { | ||||
| 196 | 0  | carp "The autoBarcode format '$autoBarcodeType' is unrecognized."; | ||||
| 197 | 0  | return undef; | ||||
| 198 | } | |||||
| 199 | 0  | carp "autoBarcode format = $autoBarcodeType" if $debug; | ||||
| 200 | 0  | my $self; | ||||
| 201 | 0  | if ($autoBarcodeType eq 'OFF') { | ||||
| 202 | 0  | $self = $class_or_object->default_self($autoBarcodeType); | ||||
| 203 | 0  | return bless $self, $class_or_object; | ||||
| 204 | } elsif ($from_obj) { | |||||
| 205 | 0  | $class_or_object->autoBarcode eq $autoBarcodeType | ||||
| 206 | or carp "Cannot create Barcodes object (type '$autoBarcodeType') from " . $class_or_object->autoBarcode . " object!"; | |||||
| 207 | 0  | $self = $class_or_object->new_object(@_); | ||||
| 208 | 0  | $self->serial($class_or_object->serial + 1); | ||||
| 209 | 0  | if ($class_or_object->is_max) { | ||||
| 210 | 0  | $debug and print STDERR "old object was max: ", $class_or_object->value, "\n"; | ||||
| 211 | 0  | $self->previous($class_or_object); | ||||
| 212 | 0  | $class_or_object->next($self); | ||||
| 213 | 0  | $self->value($self->next_value($class_or_object->value)); | ||||
| 214 | 0  | $self->is_max(1) and $class_or_object->is_max(0); # new object is max, old object is no longer max | ||||
| 215 | } else { | |||||
| 216 | 0  | $self->value($self->next_value); | ||||
| 217 | } | |||||
| 218 | } else { | |||||
| 219 | 0  | $debug and print STDERR "trying to create new $autoBarcodeType\n"; | ||||
| 220 | 0 0  | $self = &{$types->{$autoBarcodeType}} (@_); | ||||
| 221 | 0  | $self->value($self->next_value) and $self->is_max(1); | ||||
| 222 | 0  | $self->serial(1); | ||||
| 223 | } | |||||
| 224 | 0  | if ($self) { | ||||
| 225 | 0  | return $self; | ||||
| 226 | } | |||||
| 227 | 0  | carp "Failed new C4::Barcodes::$autoBarcodeType"; | ||||
| 228 | 0  | return undef; | ||||
| 229 | } | |||||
| 230 | ||||||
| 231 | sub new_object { | |||||
| 232 | 0  | my $class_or_object = shift; | ||||
| 233 | 0  | my $type = ref($class_or_object) || $class_or_object; | ||||
| 234 | 0  | my $from_obj = ref($class_or_object) ? 1 : 0; # are we building off another Barcodes object? | ||||
| 235 | 0  | my $self = $class_or_object->default_self($from_obj ? $class_or_object->autoBarcode : 'incremental'); | ||||
| 236 | 0  | bless $self, $type; | ||||
| 237 | 0  | return $self; | ||||
| 238 | } | |||||
| 239 | 1; | |||||