From 4c8eca865c51297185d32e9b1e355ecffa874751 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 | 23 ++++-- .../Pg/upgrade/XXXX.generate_patron_barcodes.sql | 87 ++++++++++++++++++++++ 2 files changed, 104 insertions(+), 6 deletions(-) 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 170b129f65..218aa5d914 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm @@ -4628,18 +4628,21 @@ __PACKAGE__->register_method( params => [ {desc => 'Authentication token', type => 'string'}, {desc => 'User ID', type => 'number'}, + {desc => 'prefix', type => 'string'}, ], return => {desc => 'Generated barcode on success'} } ); -# evergreen.lu_update_barcode(user-id) generates a barcode, creates an actor.card -# object, and points actor.usr.card to the new actor.card.id +# 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.lu_generate_barcode() just generates a 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 ) = @_; + my( $self, $client, $auth, $user_id, $prefix ) = @_; my $e = new_editor( authtoken=>$auth ); return $e->die_event unless $e->checkauth; @@ -4647,12 +4650,20 @@ sub generate_patron_barcode { 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 => ['evergreen.lu_update_barcode', $user_id]})->[0] + {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 => ['evergreen.lu_generate_barcode']})->[0] + {from => $args})->[0] or return $e->die_event; } 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