From 4e01dfb62a9b8f590123877ac47a1a1f654c4491 Mon Sep 17 00:00:00 2001 From: Dan Scott Date: Thu, 10 Jan 2013 14:17:33 -0500 Subject: [PATCH] Generic patron barcode generation (OpenSRF and DB) Laurentian University needed the ability to generate barcodes as part of its LDAP integration work, and the first generation (so to speak) of the was specific to LU - including hard-coded prefixes and database functions that include the "lu" name. This commit makes the functionality much more generic and thus more likely to be able to be adopted by other institutions. The principle components are: Database functions: evergreen.actor_generate_barcode([prefix TEXT]) - returns a 14-digit barcode from the evergreen.actor_barcode_seq sequence with a prefix of 'AUTOBC' or the specific prefix of up to 6 characters. If the resulting barcode is all digits, then the 14th character will be a mod10 check digit; otherwise the 14th digit will be '0'. evergreen.actor_update_barcode(usr_id INTEGER[, prefix TEXT]) - generates a new barcode for the specified user, with the optional barcode prefix. evergreen.mod10(barcode TEXT) - given a barcode, generates a mod10 check digit and returns the barcode with the appended check digit OpenSRF method: open-ils.actor.generate_patron_barcode([usr_id INT[, prefix TEXT]]) - generates a new barcode for the patron Signed-off-by: Dan Scott --- .../src/perlmods/lib/OpenILS/Application/Actor.pm | 51 +++++++++++++ .../Pg/upgrade/XXXX.generate_patron_barcodes.sql | 87 ++++++++++++++++++++++ 2 files changed, 138 insertions(+) create mode 100644 Open-ILS/src/sql/Pg/upgrade/XXXX.generate_patron_barcodes.sql diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm index 6ab7a143d1..a5353f492d 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm @@ -4628,6 +4628,57 @@ sub mark_users_contact_invalid { ); } +__PACKAGE__->register_method( + method => "generate_patron_barcode", + api_name => "open-ils.actor.generate_patron_barcode", + signature => { + desc => "Generates a new patron barcode. If a user ID is supplied," . + "that user's card will be updated to point at the new barcode." , + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'User ID', type => 'number'}, + {desc => 'prefix', type => 'string'}, + ], + return => {desc => 'Generated barcode on success'} + } +); + +# evergreen.actor_update_barcode(user_id[, prefix]) generates a barcode, creates +# an actor.card object, and points actor.usr.card to the new actor.card.id; +# prefix is an optional prefix for the barcode +# +# evergreen.actor_generate_barcode([prefix]) just generates a barcode, with +# prefix as an optional prefix for the barcode +sub generate_patron_barcode { + + my( $self, $client, $auth, $user_id, $prefix ) = @_; + + my $e = new_editor( authtoken=>$auth ); + return $e->die_event unless $e->checkauth; + + my $barcode; + if ($user_id) { + return $e->die_event unless $e->allowed('UPDATE_USER'); + my $args = ['evergreen.actor_update_barcode', $user_id]; + if ($prefix) { + push @$args, $prefix; + } + $barcode = $e->json_query( + {from => $args})->[0] + or return $e->die_event; + } else { + my $args = ['evergreen.actor_generate_barcode']; + if ($prefix) { + push @$args, $prefix; + } + $barcode = $e->json_query( + {from => $args})->[0] + or return $e->die_event; + } + + return $barcode; +} + # Putting the following method in open-ils.actor is a bad fit, except in that # it serves an interface that lives under 'actor' in the templates directory, # and in that there's nowhere else obvious to put it (open-ils.trigger is diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.generate_patron_barcodes.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.generate_patron_barcodes.sql new file mode 100644 index 0000000000..14544e76f0 --- /dev/null +++ b/Open-ILS/src/sql/Pg/upgrade/XXXX.generate_patron_barcodes.sql @@ -0,0 +1,87 @@ +-- Provides support for generating patron barcodes, with optional prefixes +-- If all digits, then a mod10 check digit is calculated and appended +CREATE SEQUENCE evergreen.actor_barcode_seq; + +CREATE OR REPLACE FUNCTION evergreen.mod10(barcode TEXT) +RETURNS TEXT AS $$ +use strict; +use warnings; + +my $barcode = shift; +my $total = 0; +my $position = 0; +foreach my $digit (split('', $barcode)) { + $digit = sprintf('%d', $digit); + $position++; + if ($position % 2) { + # Double it + $digit *= 2; + # If less than 10, add to the total + if ($digit < 10) { + $total += $digit; + } else { + $total += $digit - 9; + } + } else { + $total += $digit; + } +} +my $rem = $total % 10; +if ($rem) { + return 10 - $rem; +} +return $rem; +$$ LANGUAGE plperlu; + +CREATE OR REPLACE FUNCTION evergreen.actor_generate_barcode(prefix TEXT DEFAULT 'AUTOBC') +RETURNS TEXT AS $$ +DECLARE + bc_gen TEXT; + mod TEXT; + bc_serial RECORD; + bc_holder TEXT; +BEGIN + LOOP + SELECT lpad(NEXTVAL('evergreen.actor_barcode_seq')::text, 7, '0') AS bc INTO bc_serial; + bc_gen := rpad(COALESCE(prefix, '0'), 6, '0') || bc_serial.bc::text; + IF unnest(regexp_matches(bc_gen, '\D')) IS NOT NULL THEN + bc_gen := rpad(bc_gen, 14, '0'); + ELSE + bc_gen := bc_gen || evergreen.mod10(bc_gen); + END IF; + + SELECT barcode INTO bc_holder FROM actor.card WHERE barcode = bc_gen; + EXIT WHEN bc_holder IS NULL; + END LOOP; + + RETURN bc_gen; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION evergreen.actor_update_barcode(usr_id INT, prefix TEXT DEFAULT NULL) +RETURNS TEXT AS $$ +DECLARE + bc_gen TEXT; + bc_holder TEXT; +BEGIN + + LOOP + IF prefix IS NULL THEN + bc_gen := evergreen.actor_generate_barcode(); + ELSE + bc_gen := evergreen.actor_generate_barcode(prefix); + END IF; + + SELECT barcode INTO bc_holder FROM actor.card WHERE barcode = bc_gen; + EXIT WHEN bc_holder IS NULL; + END LOOP; + + INSERT INTO actor.card (usr, barcode) VALUES (usr_id, bc_gen); + + UPDATE actor.usr + SET card = CURRVAL('actor.card_id_seq') + WHERE id = usr_id; + + RETURN bc_gen; +END; +$$ LANGUAGE plpgsql; -- 2.11.0