Merge branch 'template-toolkit-opac' of git+ssh://yeti.esilibrary.com/home/evergreen...
authorMike Rylander <mrylander@gmail.com>
Tue, 17 May 2011 18:11:05 +0000 (14:11 -0400)
committerMike Rylander <mrylander@gmail.com>
Tue, 17 May 2011 18:11:05 +0000 (14:11 -0400)
21 files changed:
Open-ILS/examples/fm_IDL.xml
Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm
Open-ILS/src/sql/Pg/002.schema.config.sql
Open-ILS/src/sql/Pg/020.schema.functions.sql
Open-ILS/src/sql/Pg/800.fkeys.sql
Open-ILS/src/sql/Pg/950.data.seed-values.sql
Open-ILS/src/sql/Pg/upgrade/0536.schema.lazy_circ-barcode_lookup.sql [new file with mode: 0644]
Open-ILS/web/js/ui/default/conify/global/config/barcode_completion.js [new file with mode: 0644]
Open-ILS/web/opac/locale/en-US/lang.dtd
Open-ILS/web/opac/skin/default/js/holds.js
Open-ILS/web/templates/default/conify/global/config/barcode_completion.tt2 [new file with mode: 0644]
Open-ILS/xul/staff_client/chrome/content/cat/opac.js
Open-ILS/xul/staff_client/chrome/content/main/constants.js
Open-ILS/xul/staff_client/chrome/content/main/menu.js
Open-ILS/xul/staff_client/chrome/content/main/menu_frame_menus.xul
Open-ILS/xul/staff_client/chrome/locale/en-US/offline.properties
Open-ILS/xul/staff_client/server/circ/checkin.js
Open-ILS/xul/staff_client/server/circ/checkout.js
Open-ILS/xul/staff_client/server/circ/copy_status.js
Open-ILS/xul/staff_client/server/patron/barcode_entry.xul
Open-ILS/xul/staff_client/server/patron/display.js

index f71ddd6..a82616b 100644 (file)
@@ -8811,6 +8811,31 @@ SELECT  usr,
                        </actions>
                </permacrud>
        </class>
+       <class id="cbc" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="config::barcode_completion" oils_persist:tablename="config.barcode_completion" reporter:label="Barcode Completions">
+               <fields oils_persist:primary="id" oils_persist:sequence="config.barcode_completion_id_seq">
+                       <field reporter:label="ID" name="id" reporter:datatype="id"/>
+                       <field reporter:label="Active" name="active" reporter:datatype="bool"/>
+                       <field reporter:label="Owner" name="org_unit" reporter:datatype="org_unit"/>
+                       <field reporter:label="Prefix" name="prefix" reporter:datatype="text"/>
+                       <field reporter:label="Suffix" name="suffix" reporter:datatype="text"/>
+                       <field reporter:label="Length" name="length" reporter:datatype="int"/>
+                       <field reporter:label="Padding" name="padding" reporter:datatype="text"/>
+                       <field reporter:label="Padding At End" name="padding_end" reporter:datatype="bool"/>
+                       <field reporter:label="Applies to Items" name="asset" reporter:datatype="bool"/>
+                       <field reporter:label="Applies to Users" name="actor" reporter:datatype="bool"/>
+               </fields>
+               <links>
+                       <link field="org_unit" reltype="has_a" key="id" map="" class="aou"/>
+               </links>
+               <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+                       <actions>
+                               <create permission="UPDATE_ORG_UNIT_SETTING_ALL" context_field="org_unit"/>
+                               <retrieve/>
+                               <update permission="UPDATE_ORG_UNIT_SETTING_ALL" context_field="org_unit"/>
+                               <delete permission="UPDATE_ORG_UNIT_SETTING_ALL" context_field="org_unit"/>
+                       </actions>
+               </permacrud>
+       </class>
 
        <!-- ********************************************************************************************************************* -->
 
index 864ef6e..1502129 100644 (file)
@@ -4275,6 +4275,55 @@ sub user_saved_search_cud {
     return $res;
 }
 
+__PACKAGE__->register_method(
+    method   => "get_barcodes",
+    api_name => "open-ils.actor.get_barcodes"
+);
+
+sub get_barcodes {
+       my( $self, $client, $auth, $org_id, $context, $barcode ) = @_;
+       my $e = new_editor(authtoken => $auth);
+    return $e->event unless $e->checkauth;
+    return $e->event unless $e->allowed('STAFF_LOGIN', $org_id);
 
+    my $db_result = $e->json_query(
+        {   from => [
+                'evergreen.get_barcodes',
+                $org_id, $context, $barcode,
+            ]
+        }
+    );
+    if($context =~ /actor/) {
+        my $filter_result = ();
+        my $patron;
+        foreach my $result (@$db_result) {
+            if($result->{type} eq 'actor') {
+                if($e->requestor->id != $result->{id}) {
+                    $patron = $e->retrieve_actor_user($result->{id});
+                    if(!$patron) {
+                        push(@$filter_result, $e->event);
+                        next;
+                    }
+                    if($e->allowed('VIEW_USER', $patron->home_ou)) {
+                        push(@$filter_result, $result);
+                    }
+                    else {
+                        push(@$filter_result, $e->event);
+                    }
+                }
+                else {
+                    push(@$filter_result, $result);
+                }
+            }
+            else {
+                push(@$filter_result, $result);
+            }
+        }
+        return $filter_result;
+    }
+    else {
+        return $db_result;
+    }
+}
 
 1;
index 4b7b0ef..741f4f1 100644 (file)
@@ -86,7 +86,7 @@ CREATE TRIGGER no_overlapping_deps
     BEFORE INSERT OR UPDATE ON config.db_patch_dependencies
     FOR EACH ROW EXECUTE PROCEDURE evergreen.array_overlap_check ('deprecates');
 
-INSERT INTO config.upgrade_log (version, applied_to) VALUES ('0535', :eg_version); -- dbs
+INSERT INTO config.upgrade_log (version, applied_to) VALUES ('0536', :eg_version); -- miker for tsbere
 
 CREATE TABLE config.bib_source (
        id              SERIAL  PRIMARY KEY,
@@ -887,4 +887,19 @@ Upgrade script % can not be applied:
 END;
 $$ LANGUAGE PLPGSQL;
 
+CREATE TABLE config.barcode_completion (
+    id          SERIAL PRIMARY KEY,
+    active      BOOL NOT NULL DEFAULT true,
+    org_unit    INT NOT NULL, -- REFERENCES actor.org_unit(id) DEFERRABLE INITIALLY DEFERRED,
+    prefix      TEXT,
+    suffix      TEXT,
+    length      INT NOT NULL DEFAULT 0,
+    padding     TEXT,
+    padding_end BOOL NOT NULL DEFAULT false,
+    asset       BOOL NOT NULL DEFAULT true,
+    actor       BOOL NOT NULL DEFAULT true
+);
+
+CREATE TYPE evergreen.barcode_set AS (type TEXT, id BIGINT, barcode TEXT);
+
 COMMIT;
index 981a364..f29239e 100644 (file)
@@ -416,3 +416,92 @@ from an authority record. The primary purpose is to build a unique
 index to defend against duplicated authority records from the same
 thesaurus.
 $$;
+
+CREATE OR REPLACE FUNCTION evergreen.get_barcodes(select_ou INT, type TEXT, in_barcode TEXT) RETURNS SETOF evergreen.barcode_set AS $$
+DECLARE
+    cur_barcode TEXT;
+    barcode_len INT;
+    completion_len  INT;
+    asset_barcodes  TEXT[];
+    actor_barcodes  TEXT[];
+    do_asset    BOOL = false;
+    do_serial   BOOL = false;
+    do_booking  BOOL = false;
+    do_actor    BOOL = false;
+    completion_set  config.barcode_completion%ROWTYPE;
+BEGIN
+
+    IF position('asset' in type) > 0 THEN
+        do_asset = true;
+    END IF;
+    IF position('serial' in type) > 0 THEN
+        do_serial = true;
+    END IF;
+    IF position('booking' in type) > 0 THEN
+        do_booking = true;
+    END IF;
+    IF do_asset OR do_serial OR do_booking THEN
+        asset_barcodes = asset_barcodes || in_barcode;
+    END IF;
+    IF position('actor' in type) > 0 THEN
+        do_actor = true;
+        actor_barcodes = actor_barcodes || in_barcode;
+    END IF;
+
+    barcode_len := length(in_barcode);
+
+    FOR completion_set IN
+      SELECT * FROM config.barcode_completion
+        WHERE active
+        AND org_unit IN (SELECT aou.id FROM actor.org_unit_ancestors(select_ou) aou)
+        LOOP
+        IF completion_set.prefix IS NULL THEN
+            completion_set.prefix := '';
+        END IF;
+        IF completion_set.suffix IS NULL THEN
+            completion_set.suffix := '';
+        END IF;
+        IF completion_set.length = 0 OR completion_set.padding IS NULL OR length(completion_set.padding) = 0 THEN
+            cur_barcode = completion_set.prefix || in_barcode || completion_set.suffix;
+        ELSE
+            completion_len = completion_set.length - length(completion_set.prefix) - length(completion_set.suffix);
+            IF completion_len >= barcode_len THEN
+                IF completion_set.padding_end THEN
+                    cur_barcode = rpad(in_barcode, completion_len, completion_set.padding);
+                ELSE
+                    cur_barcode = lpad(in_barcode, completion_len, completion_set.padding);
+                END IF;
+                cur_barcode = completion_set.prefix || cur_barcode || completion_set.suffix;
+            END IF;
+        END IF;
+        IF completion_set.actor THEN
+            actor_barcodes = actor_barcodes || cur_barcode;
+        END IF;
+        IF completion_set.asset THEN
+            asset_barcodes = asset_barcodes || cur_barcode;
+        END IF;
+    END LOOP;
+
+    IF do_asset AND do_serial THEN
+        RETURN QUERY SELECT 'asset'::TEXT, id, barcode FROM ONLY asset.copy WHERE barcode = ANY(asset_barcodes) AND deleted = false;
+        RETURN QUERY SELECT 'serial'::TEXT, id, barcode FROM serial.unit WHERE barcode = ANY(asset_barcodes) AND deleted = false;
+    ELSIF do_asset THEN
+        RETURN QUERY SELECT 'asset'::TEXT, id, barcode FROM asset.copy WHERE barcode = ANY(asset_barcodes) AND deleted = false;
+    ELSIF do_serial THEN
+        RETURN QUERY SELECT 'serial'::TEXT, id, barcode FROM serial.unit WHERE barcode = ANY(asset_barcodes) AND deleted = false;
+    END IF;
+    IF do_booking THEN
+        RETURN QUERY SELECT 'booking'::TEXT, id::BIGINT, barcode FROM booking.resource WHERE barcode = ANY(asset_barcodes);
+    END IF;
+    IF do_actor THEN
+        RETURN QUERY SELECT 'actor'::TEXT, c.usr::BIGINT, c.barcode FROM actor.card c JOIN actor.usr u ON c.usr = u.id WHERE c.barcode = ANY(actor_barcodes) AND c.active AND NOT u.deleted ORDER BY usr;
+    END IF;
+    RETURN;
+END;
+$$ LANGUAGE plpgsql;
+
+COMMENT ON FUNCTION evergreen.get_barcodes(INT, TEXT, TEXT) IS $$
+Given user input, find an appropriate barcode in the proper class.
+
+Will add prefix/suffix information to do so, and return all results.
+$$;
index 619441d..d0ee887 100644 (file)
@@ -115,6 +115,8 @@ ALTER TABLE config.remote_account ADD CONSTRAINT config_remote_account_owner_fke
 ALTER TABLE config.org_unit_setting_type ADD CONSTRAINT view_perm_fkey FOREIGN KEY (view_perm) REFERENCES permission.perm_list (id) ON UPDATE CASCADE ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED;
 ALTER TABLE config.org_unit_setting_type ADD CONSTRAINT update_perm_fkey FOREIGN KEY (update_perm) REFERENCES permission.perm_list (id) ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED;
 
+ALTER TABLE config.barcode_completion ADD CONSTRAINT config_barcode_completion_org_unit_fkey FOREIGN KEY (org_unit) REFERENCES actor.org_unit (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED;
+
 CREATE INDEX by_heading_and_thesaurus ON authority.record_entry (authority.normalize_heading(marc)) WHERE deleted IS FALSE or deleted = FALSE;
 
 COMMIT;
index 38b7b71..8daf489 100644 (file)
@@ -2508,6 +2508,11 @@ INSERT into config.org_unit_setting_type
     oils_i18n_gettext('circ.selfcheck.auto_override_checkout_events', 'List of checkout/renewal events that the selfcheck interface should automatically override instead instead of alerting and stopping the transaction', 'coust', 'description'),
     'array'),
 
+( 'circ.staff_client.actor_on_checkout',
+    oils_i18n_gettext('circ.staff_client.actor_on_checkout', 'Load patron from Checkout', 'coust', 'label'),
+    oils_i18n_gettext('circ.staff_client.actor_on_checkout', 'When scanning barcodes into Checkout auto-detect if a new patron barcode is scanned and auto-load the new patron.', 'coust', 'description'),
+    'bool'),
+
 ( 'circ.staff_client.do_not_auto_attempt_print',
     oils_i18n_gettext('circ.staff_client.do_not_auto_attempt_print', 'Disable Automatic Print Attempt Type List', 'coust', 'label'),
     oils_i18n_gettext('circ.staff_client.do_not_auto_attempt_print', 'Disable automatic print attempts from staff client interfaces for the receipt types in this list.  Possible values: "Checkout", "Bill Pay", "Hold Slip", "Transit Slip", and "Hold/Transit Slip".  This is different from the Auto-Print checkbox in the pertinent interfaces in that it disables automatic print attempts altogether, rather than encouraging silent printing by suppressing the print dialog.  The Auto-Print checkbox in these interfaces have no effect on the behavior for this setting.  In the case of the Hold, Transit, and Hold/Transit slips, this also suppresses the alert dialogs that precede the print dialog (the ones that offer Print and Do Not Print as options).', 'coust', 'description'),
diff --git a/Open-ILS/src/sql/Pg/upgrade/0536.schema.lazy_circ-barcode_lookup.sql b/Open-ILS/src/sql/Pg/upgrade/0536.schema.lazy_circ-barcode_lookup.sql
new file mode 100644 (file)
index 0000000..5d86035
--- /dev/null
@@ -0,0 +1,117 @@
+-- Evergreen DB patch 0536.schema.lazy_circ-barcode_lookup.sql
+--
+-- FIXME: insert description of change, if needed
+--
+BEGIN;
+
+-- check whether patch can be applied
+SELECT evergreen.update_deps_block_check('0536', :eg_version);
+
+INSERT INTO config.org_unit_setting_type ( name, label, description, datatype) VALUES ( 'circ.staff_client.actor_on_checkout', 'Load patron from Checkout', 'When scanning barcodes into Checkout auto-detect if a new patron barcode is scanned and auto-load the new patron.', 'bool');
+
+CREATE TABLE config.barcode_completion (
+    id          SERIAL  PRIMARY KEY,
+    active      BOOL    NOT NULL DEFAULT true,
+    org_unit    INT     NOT NULL REFERENCES actor.org_unit (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    prefix      TEXT,
+    suffix      TEXT,
+    length      INT     NOT NULL DEFAULT 0,
+    padding     TEXT,
+    padding_end BOOL    NOT NULL DEFAULT false,
+    asset       BOOL    NOT NULL DEFAULT true,
+    actor       BOOL    NOT NULL DEFAULT true
+);
+
+CREATE TYPE evergreen.barcode_set AS (type TEXT, id BIGINT, barcode TEXT);
+
+CREATE OR REPLACE FUNCTION evergreen.get_barcodes(select_ou INT, type TEXT, in_barcode TEXT) RETURNS SETOF evergreen.barcode_set AS $$
+DECLARE
+    cur_barcode TEXT;
+    barcode_len INT;
+    completion_len  INT;
+    asset_barcodes  TEXT[];
+    actor_barcodes  TEXT[];
+    do_asset    BOOL = false;
+    do_serial   BOOL = false;
+    do_booking  BOOL = false;
+    do_actor    BOOL = false;
+    completion_set  config.barcode_completion%ROWTYPE;
+BEGIN
+
+    IF position('asset' in type) > 0 THEN
+        do_asset = true;
+    END IF;
+    IF position('serial' in type) > 0 THEN
+        do_serial = true;
+    END IF;
+    IF position('booking' in type) > 0 THEN
+        do_booking = true;
+    END IF;
+    IF do_asset OR do_serial OR do_booking THEN
+        asset_barcodes = asset_barcodes || in_barcode;
+    END IF;
+    IF position('actor' in type) > 0 THEN
+        do_actor = true;
+        actor_barcodes = actor_barcodes || in_barcode;
+    END IF;
+
+    barcode_len := length(in_barcode);
+
+    FOR completion_set IN
+      SELECT * FROM config.barcode_completion
+        WHERE active
+        AND org_unit IN (SELECT aou.id FROM actor.org_unit_ancestors(select_ou) aou)
+        LOOP
+        IF completion_set.prefix IS NULL THEN
+            completion_set.prefix := '';
+        END IF;
+        IF completion_set.suffix IS NULL THEN
+            completion_set.suffix := '';
+        END IF;
+        IF completion_set.length = 0 OR completion_set.padding IS NULL OR length(completion_set.padding) = 0 THEN
+            cur_barcode = completion_set.prefix || in_barcode || completion_set.suffix;
+        ELSE
+            completion_len = completion_set.length - length(completion_set.prefix) - length(completion_set.suffix);
+            IF completion_len >= barcode_len THEN
+                IF completion_set.padding_end THEN
+                    cur_barcode = rpad(in_barcode, completion_len, completion_set.padding);
+                ELSE
+                    cur_barcode = lpad(in_barcode, completion_len, completion_set.padding);
+                END IF;
+                cur_barcode = completion_set.prefix || cur_barcode || completion_set.suffix;
+            END IF;
+        END IF;
+        IF completion_set.actor THEN
+            actor_barcodes = actor_barcodes || cur_barcode;
+        END IF;
+        IF completion_set.asset THEN
+            asset_barcodes = asset_barcodes || cur_barcode;
+        END IF;
+    END LOOP;
+
+    IF do_asset AND do_serial THEN
+        RETURN QUERY SELECT 'asset'::TEXT, id, barcode FROM ONLY asset.copy WHERE barcode = ANY(asset_barcodes) AND deleted = false;
+        RETURN QUERY SELECT 'serial'::TEXT, id, barcode FROM serial.unit WHERE barcode = ANY(asset_barcodes) AND deleted = false;
+    ELSIF do_asset THEN
+        RETURN QUERY SELECT 'asset'::TEXT, id, barcode FROM asset.copy WHERE barcode = ANY(asset_barcodes) AND deleted = false;
+    ELSIF do_serial THEN
+        RETURN QUERY SELECT 'serial'::TEXT, id, barcode FROM serial.unit WHERE barcode = ANY(asset_barcodes) AND deleted = false;
+    END IF;
+    IF do_booking THEN
+        RETURN QUERY SELECT 'booking'::TEXT, id::BIGINT, barcode FROM booking.resource WHERE barcode = ANY(asset_barcodes);
+    END IF;
+    IF do_actor THEN
+        RETURN QUERY SELECT 'actor'::TEXT, c.usr::BIGINT, c.barcode FROM actor.card c JOIN actor.usr u ON c.usr = u.id WHERE c.barcode = ANY(actor_barcodes) AND c.active AND NOT u.deleted ORDER BY usr;
+    END IF;
+    RETURN;
+END;
+$$ LANGUAGE plpgsql;
+
+COMMENT ON FUNCTION evergreen.get_barcodes(INT, TEXT, TEXT) IS $$
+Given user input, find an appropriate barcode in the proper class.
+
+Will add prefix/suffix information to do so, and return all results.
+$$;
+
+COMMIT;
+
diff --git a/Open-ILS/web/js/ui/default/conify/global/config/barcode_completion.js b/Open-ILS/web/js/ui/default/conify/global/config/barcode_completion.js
new file mode 100644 (file)
index 0000000..0979c49
--- /dev/null
@@ -0,0 +1,15 @@
+dojo.require('dijit.layout.ContentPane');
+dojo.require('dijit.form.Button');
+dojo.require('openils.widget.AutoGrid');
+dojo.require('openils.widget.AutoFieldWidget');
+dojo.require('openils.PermaCrud');
+dojo.require('openils.widget.ProgressDialog');
+
+function load(){
+    cmGrid.overrideWidgetArgs.prefix = {hrbefore : true};
+    cmGrid.overrideWidgetArgs.asset = {hrbefore: true};
+    cmGrid.loadAll({order_by:{cbc:'org_unit'}});
+}
+
+openils.Util.addOnLoad(load);
+
index 9cf9140..38ae9a1 100644 (file)
 <!ENTITY staff.main.menu.admin.local_admin.conify.standing_penalty.label "Standing Penalties">
 <!ENTITY staff.main.menu.admin.local_admin.conify.grp_penalty_threshold.label "Group Penalty Thresholds">
 <!ENTITY staff.main.menu.admin.local_admin.conify.copy_location_order.label "Copy Location Order">
+<!ENTITY staff.main.menu.admin.local_admin.barcode_completion.label "Barcode Completion">
 <!ENTITY staff.main.menu.admin.local_admin.circ_matrix_matchpoint.label "Circulation Policies">
 <!ENTITY staff.main.menu.admin.local_admin.hold_matrix_matchpoint.label "Hold Policies">
 <!ENTITY staff.main.menu.admin.local_admin.work_log.label "Work Log">
index 73514c1..aaf7a2e 100644 (file)
@@ -53,7 +53,24 @@ function _holdsHandleStaffMe() {
 }
 
 function _holdsHandleStaff() {
-       var barcode = xulG.patron_barcode || $('xul_recipient_barcode').value;
+       var barcode = xulG.patron_barcode;
+    if(!barcode) {
+        barcode = $('xul_recipient_barcode').value;
+        if(xulG.get_barcode) {
+            // We have a "complete the barcode" function, call it (actor = users only)
+            var new_barcode = xulG.get_barcode(window, 'actor', barcode);
+            // If we got a result (boolean false is "no result") check it
+            if(new_barcode) {
+                // user_false string means they picked "None of the above"
+                // Abort before any other events can fire
+                if(new_barcode == "user_false") return;
+                // No error means we have a (hopefully valid) completed barcode to use.
+                // Otherwise, fall through to other methods of checking
+                if(typeof new_barcode.ilsevent == 'undefined')
+                    barcode = new_barcode.barcode;
+            }
+        }
+    }
        var user = grabUserByBarcode( G.user.session, barcode );
 
        var evt;
diff --git a/Open-ILS/web/templates/default/conify/global/config/barcode_completion.tt2 b/Open-ILS/web/templates/default/conify/global/config/barcode_completion.tt2
new file mode 100644 (file)
index 0000000..fc81951
--- /dev/null
@@ -0,0 +1,26 @@
+[% ctx.page_title = 'Barcode Completion Configuration' %]
+[% WRAPPER default/base.tt2 %]
+<script type="text/javascript" src='[% ctx.media_prefix %]/js/ui/default/conify/global/config/barcode_completion.js'> </script>
+<div dojoType="dijit.layout.ContentPane" layoutAlign="top" class='oils-header-panel'>
+    <div>Barcode Completion Configuration</div>
+    <div><button dojoType='dijit.form.Button' onClick='cmGrid.showCreatePane()'>New</button></div>
+</div>
+<div dojoType="dijit.layout.ContentPane" layoutAlign="client">
+    <table  jsId="cmGrid"
+            style="height: 600px;"
+            dojoType="openils.widget.AutoGrid"
+            fieldOrder="['id', 'active', 'org_unit', 'prefix', 'suffix', 'length', 'padding', 'padding_end', 'asset', 'actor']"
+            defaultCellWidth='"auto"'
+            query="{id: '*'}"
+            fmClass='cbc'
+            editStyle='pane'
+            editOnEnter='true'
+            showColumnPicker='true'
+            columnPickerPrefix='"conify.config.barcode_completion"'>
+    </table>
+</div>
+
+<div class='hidden'><div dojoType='openils.widget.ProgressDialog' jsId='progressDialog'/></div>
+
+[% END %]
+
index 2fbe422..9684740 100644 (file)
@@ -105,7 +105,8 @@ function set_brief_view() {
     ["url_prefix", "new_tab", "set_tab", "close_tab", "new_patron_tab",
         "set_patron_tab", "volume_item_creator", "get_new_session",
         "holdings_maintenance_tab", "open_chrome_window", "url_prefix",
-        "network_meter", "page_meter", "set_statusbar", "set_help_context"
+        "network_meter", "page_meter", "set_statusbar", "set_help_context",
+        "get_barcode"
     ].forEach(function(k) { content_params[k] = xulG[k]; });
 
     top_pane.set_iframe( 
@@ -300,7 +301,7 @@ function open_acq_orders() {
             "set_patron_tab", "volume_item_creator", "get_new_session",
             "holdings_maintenance_tab", "set_tab_name", "open_chrome_window",
             "url_prefix", "network_meter", "page_meter", "set_statusbar",
-            "set_help_context"
+            "set_help_context", "get_barcode"
         ].forEach(function(k) { content_params[k] = xulG[k]; });
 
         var loc = urls.XUL_BROWSER + "?url=" + window.escape(
@@ -334,7 +335,7 @@ function open_alt_serial_mgmt() {
             "set_patron_tab", "volume_item_creator", "get_new_session",
             "holdings_maintenance_tab", "set_tab_name", "open_chrome_window",
             "url_prefix", "network_meter", "page_meter", "set_statusbar",
-            "set_help_context"
+            "set_help_context", "get_barcode"
         ].forEach(function(k) { content_params[k] = xulG[k]; });
 
         var loc = urls.XUL_BROWSER + "?url=" + window.escape(
@@ -370,7 +371,8 @@ function set_opac() {
                     } catch(E) {
                         g.error.standard_unexpected_error_alert('window_open',E);
                     }
-                }
+                },
+                'get_barcode' : xulG.get_barcode
             },
             'on_url_load' : function(f) {
                 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
@@ -489,6 +491,7 @@ function set_opac() {
         content_params.page_meter = xulG.page_meter;
         content_params.set_statusbar = xulG.set_statusbar;
         content_params.set_help_context = xulG.set_help_context;
+        content_params.get_barcode = xulG.get_barcode;
 
         if (opac_url) { content_params.url = opac_url; } else { content_params.url = xulG.url_prefix( urls.browser ); }
         browser_frame = bottom_pane.set_iframe( xulG.url_prefix(urls.XUL_BROWSER) + '?name=Catalog', {}, content_params);
@@ -616,6 +619,7 @@ function bib_in_new_tab() {
         content_params.page_meter = xulG.page_meter;
         content_params.set_statusbar = xulG.set_statusbar;
         content_params.set_help_context = xulG.set_help_context;
+        content_params.get_barcode = xulG.get_barcode;
 
         xulG.new_tab(xulG.url_prefix(urls.XUL_OPAC_WRAPPER), {}, content_params);
     } catch(E) {
@@ -631,7 +635,7 @@ function batch_receive_in_new_tab() {
             "set_patron_tab", "volume_item_creator", "get_new_session",
             "holdings_maintenance_tab", "set_tab_name", "open_chrome_window",
             "url_prefix", "network_meter", "page_meter", "set_statusbar",
-            "set_help_context"
+            "set_help_context", "get_barcode"
         ].forEach(function(k) { content_params[k] = xulG[k]; });
 
         xulG.new_tab(
index 56ed059..9961357 100644 (file)
@@ -357,7 +357,8 @@ var api = {
     'RECALCULATE_STANDING_PENALTIES' : { 'app' : 'open-ils.actor', 'method' : 'open-ils.actor.user.penalties.update' },
     'USER_ORG_UNIT_OPT_IN_FEATURE' : { 'app' : 'open-ils.actor', 'method' : 'open-ils.actor.user.org_unit_opt_in.enabled' },
     'USER_ORG_UNIT_OPT_IN_CHECK' : { 'app' : 'open-ils.actor', 'method' : 'open-ils.actor.user.org_unit_opt_in.check' },
-    'USER_ORG_UNIT_OPT_IN_CREATE' : { 'app' : 'open-ils.actor', 'method' : 'open-ils.actor.user.org_unit_opt_in.create' }
+    'USER_ORG_UNIT_OPT_IN_CREATE' : { 'app' : 'open-ils.actor', 'method' : 'open-ils.actor.user.org_unit_opt_in.create' },
+    'GET_BARCODES' : { 'app' : 'open-ils.actor', 'method' : 'open-ils.actor.get_barcodes' }
 }
 
 var urls = {
index bc8c11d..47fc718 100644 (file)
@@ -51,6 +51,8 @@ main.menu.prototype = {
 
         urls.remote = params['server'];
 
+        xulG.get_barcode = this.get_barcode;
+
         // Pull in local customizations
         var r = new XMLHttpRequest();
         r.open("GET", obj.url_prefix('/xul/server/skin/custom.js'), false);
@@ -762,6 +764,11 @@ main.menu.prototype = {
                 ['oncommand'],
                 function(event) { open_eg_web_page('conify/global/action/survey', null, event); }
             ],
+            'cmd_local_admin_barcode_completion' : [
+                ['oncommand'],
+                function() { open_eg_web_page('conify/global/config/barcode_completion', 
+                    'menu.local_admin.barcode_completion.tab'); }
+            ],
             'cmd_local_admin_circ_matrix_matchpoint' : [
                 ['oncommand'],
                 function() { open_eg_web_page('conify/global/config/circ_matrix_matchpoint', 
@@ -2046,6 +2053,7 @@ commands:
         content_params.url_prefix = function(url) { return obj.url_prefix(url); };
         content_params.network_meter = obj.network_meter;
         content_params.page_meter = obj.page_meter;
+        content_params.get_barcode = obj.get_barcode;
         content_params.set_statusbar = function(slot,text,tooltiptext,click_handler) {
             var e = document.getElementById('statusbarpanel'+slot);
             if (e) {
@@ -2142,8 +2150,154 @@ commands:
         }
 
         return frame;
-    }
+    },
+
+    'get_barcode' : function(window, context, barcode) {
+        JSAN.use('util.network');
+        JSAN.use('util.sound');
+
+        // Depending on where we were called from data can be found in multiple ways
+        var data;
+        if(this.data) data = this.data;
+        else if(xulG.data) data = xulG.data;        
+        else {
+            JSAN.use('util.data');
+            data = new util.data();
+        }
+        data.stash_retrieve();
+
+        var network = new util.network();
+        var sound = new util.sound();
+
+        // Should return an array. Or an error.
+        var r = network.simple_request('GET_BARCODES', [ ses(), data.list.au[0].ws_ou(), context, barcode ]);
+
+        if(!r) // Nothing?
+            return false;
 
+        // Top-level error, likely means bad session or no STAFF_LOGIN permission.
+        if(typeof r.ilsevent != 'undefined') {
+            // Hand it off to the caller.
+            return r;
+        }
+
+        // No results? Return false
+        if(r.length == 0) return false;
+
+        // One result?
+        if(r.length == 1) {
+            // Return it. If it is an error the caller should deal with it.
+            return r[0];
+        }
+
+        // At this point we have more than one result.
+        // Check to see what we got.
+        var result_filter = {};
+        var valid_r = [];
+        var unique_count = 0;
+        var found_errors = false;
+        var errors = '';
+        var len = r.length;
+
+        // Check each result.
+        for(var i = 0; i < len; ++i) {
+            // If it is an error
+            if(typeof r[i].ilsevent != 'undefined') {
+                // Make note that we found errors
+                found_errors = true;
+                // Grab the error into a string
+                errors += js2JSON(r[i]);
+            }
+            else {
+                // Otherwise, record the type/id combo for later
+                var type = r[i].type;
+                var id = r[i].id;
+                var barcode = r[i].barcode;
+                if(!result_filter[type]) result_filter[type] = {};
+                if(!result_filter[type][id]) {
+                    unique_count++;
+                    result_filter[type][id] = [];
+                }
+                result_filter[type][id].push(barcode);
+                valid_r.push(r[i]);
+            }
+        }
+
+        // Only errors? Return the first one.
+        if(unique_count == 0 && found_errors == true) {
+            return r[0];
+        }
+
+        // No errors, one (unique) result? Return it.
+        if(unique_count == 1 && found_errors == false) return valid_r[0];
+
+        // For possible debugging, dump the errors.
+        if(found_errors) dump(errors);
+
+        // Still here? Must need to have the user pick.
+        if(!xulG.url_prefix) xulG.url_prefix = url_prefix; // Make util.window happy
+        JSAN.use('util.window');
+        var win = new util.window();
+        var url = url_prefix(urls.XUL_FANCY_PROMPT);
+        var title = offlineStrings.getString('barcode_choice.title');
+        var xml = '<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" flex="1">';
+        xml += '<groupbox flex="1" style="overflow: auto; border: solid thin;"><caption label="' + title + '"/>';
+        xml += '<description style="-moz-user-select: text; -moz-user-focus: normal; font-size: large">' + offlineStrings.getString('barcode_choice.prompt') + '</description>';
+        if(found_errors) // Let the user know that one or more possible answers errored out.
+            xml += '<description style="-moz-user=select: text; -moz-user-focus: normal; font-size: large">' + offlineStrings.getString('barcode_choice.errors_found') + '</description>';
+        xml += '</groupbox><groupbox><caption label="' + offlineStrings.getString('barcode_choice.choice_label') + '"/><vbox>';
+
+        len = valid_r.length;
+        // Look at all the non-error answers we got
+        for(var i = 0; i < len; ++i) {
+            // If we still have a filtered answer, display a button.
+            if(result_filter[valid_r[i].type][valid_r[i].id]) {
+                var result_data = false;
+                var barcodes = result_filter[valid_r[i].type][valid_r[i].id];
+                var barcodes_assembled = barcodes.shift();
+                var button_label = '';
+                while(barcodes.length > 0) // Join any secondary barcodes found together
+                    barcodes_assembled = offlineStrings.getFormattedString('barcode_choice.join_barcodes', [barcodes_assembled, barcodes.shift()]);
+                switch(r[i].type) {
+                    case 'actor':
+                        result_data = network.simple_request('BLOB_AU_PARTS_RETRIEVE',
+                            [ ses() , valid_r[i].id, ['family_name', 'first_given_name', 'second_given_name', 'home_ou' ] ]);
+                        button_label = offlineStrings.getFormattedString('barcode_choice.actor',
+                            [barcodes_assembled, result_data[0], result_data[1] + (result_data[2] ? ' ' + result_data[2] : ''), data.hash.aou[ result_data[3] ].name(), data.hash.aou[ result_data[3] ].shortname()]);
+                        break;
+                    case 'booking':
+                        result_data = network.simple_request('FM_ACP_DETAILS_VIA_BARCODE', [ ses(), valid_r[i].barcode ]);
+                        // Note: This falls through intentionally.
+                    case 'asset':
+                    case 'serial':
+                        if(!result_data) // If we fell through this should be set already.
+                            result_data = network.simple_request('FM_ACP_DETAILS', [ ses(), valid_r[i].id ]);
+                        button_label = offlineStrings.getFormattedString('barcode_choice.asset',
+                            [barcodes_assembled, result_data.mvr.title(), data.hash.aou[ result_data.copy.circ_lib() ].name(), data.hash.aou[ result_data.copy.circ_lib() ].shortname()]);
+                        break;
+                }
+                r[i].data = result_data;
+
+                // This ensures we only show each unique id once
+                delete result_filter[valid_r[i].type][valid_r[i].id];
+
+                // If we have more than one context this should label each entry with where it came from
+                // Likely most useful for distinguishing assets from bookings
+                if(context != valid_r[i].type && offlineStrings.testString('barcode_choice.' + valid_r[i].type + '_label'))
+                    button_label = offlineStrings.getFormattedString('barcode_choice.' + valid_r[i].type + '_label', [button_label]);
+
+                xml += '<button label="' + button_label + '" name="fancy_submit" value="' + i + '"/>';
+            }
+        }
+        xml += '<button label="' + offlineStrings.getString('barcode_choice.none') + '" name="fancy_cancel"/>';
+        xml += '</vbox></groupbox></vbox>';
+        var fancy_prompt_data = win.open( url, 'fancy_prompt', 'chrome,resizable,modal,width=500,height=500', { 'xml' : xml, 'title' : title, 'sound' : 'bad' } );
+        if(fancy_prompt_data.fancy_status == 'complete')
+            return valid_r[fancy_prompt_data.fancy_submit];
+        else
+            // user_false is used to indicate the user said "None of the above" to avoid fall-through erroring later.
+            return "user_false";
+    }
 }
 
 dump('exiting main/menu.js\n');
index 2dc7cb5..7fb34b1 100644 (file)
     <command id="cmd_local_admin_age_overdue_circulations_to_lost" />
     <command id="cmd_local_admin_cash_reports" />
     <command id="cmd_local_admin_transit_list" />
+    <command id="cmd_local_admin_barcode_completion"
+             perm="UPDATE_ORG_UNIT_SETTING_ALL" />
     <command id="cmd_local_admin_circ_matrix_matchpoint"
              perm="ADMIN_CIRC_MATRIX_MATCHPOINT VIEW_CIRC_MATRIX_MATCHPOINT"
              />
             <menupopup id="main.menu.admin.local.popup">
                 <menuitem command="cmd_local_admin_age_overdue_circulations_to_lost" label="&staff.server.admin.index.age_overdue_circulations_to_lost.label;" accesskey="&staff.server.admin.index.age_overdue_circulations_to_lost.accesskey;"/>
                 <menuitem label="&staff.server.admin.index.cash_reports;" command="cmd_local_admin_cash_reports"/>
+                <menuitem label="&staff.main.menu.admin.local_admin.barcode_completion.label;" command="cmd_local_admin_barcode_completion"/>
                 <menuitem label="&staff.main.menu.admin.local_admin.circ_matrix_matchpoint.label;" command="cmd_local_admin_circ_matrix_matchpoint"/>
                 <menuitem label="&staff.server.admin.index.closed_dates;" command="cmd_local_admin_closed_dates"/>
                 <menuitem label="&staff.server.admin.index.copy_locations;" command="cmd_local_admin_copy_locations"/>
index 06452b0..26bf180 100644 (file)
@@ -250,6 +250,7 @@ menu.cmd_booking_reservation_pickup.tab=Reservation Pickup
 menu.cmd_booking_reservation_return.tab=Reservation Return
 menu.cmd_booking_pull_list.tab=Booking Pull List
 menu.cmd_booking_capture.tab=Booking Capture
+menu.local_admin.barcode_completion.tab=Barcode Completion
 menu.local_admin.circ_matrix_matchpoint.tab=Circulation Policies
 menu.local_admin.hold_matrix_matchpoint.tab=Hold Policies
 menu.local_admin.work_log.tab=Work Log
@@ -297,3 +298,15 @@ menu.logoff.unsaved_data_warning=This session may have unsaved data. Logoff anyw
 menu.shutdown.unsaved_data_warning=This application may have unsaved data. Exit it anyway?
 hotkeys.Default=Default
 hotkeys.None=No Hotkeys
+barcode_choice.join_barcodes=%1$s / %2$s
+barcode_choice.actor=%1$s : %2$s, %3$s from %4$s (%5$s)
+barcode_choice.asset=%1$s : %2$s from %3$s (%4$s)
+barcode_choice.none=None of the above
+barcode_choice.prompt=After auto completion multiple barcodes may match your input. Please choose the barcode you intended below.
+barcode_choice.errors_found=In addition to the options below, one or more errors were encountered on items not shown.
+barcode_choice.title=Barcode Choice
+barcode_choice.choice_label=Found Barcodes:
+barcode_choice.actor_label=Patron : %1$s
+barcode_choice.asset_label=Item : %1$s
+barcode_choice.serial_label=Serial : %1$s
+barcode_choice.booking_label=Booking : %1$s
index 9a36d7e..75c1edb 100644 (file)
@@ -509,13 +509,17 @@ circ.checkin.prototype = {
             var async_checkbox = document.getElementById('async_checkin');
             if (async_checkbox) { async = async_checkbox.getAttribute('checked') == 'true'; }
             var barcode = textbox.value;
+            // Auto-complete the barcode, items only
+            var barcode_object = xulG.get_barcode(window, 'asset', barcode);
             if (async) {
                 textbox.value = ''; textbox.focus();
             }
-            if (!barcode) return;
-            if (barcode) {
-                if ( obj.test_barcode(barcode) ) { /* good */ } else { /* bad */ return; }
-            }
+            // user_false means the user selected "None of the above", abort before other prompts/errors
+            if(barcode_object == "user_false") return;
+            // Got a barcode without an error? Use it. Otherwise fall through.
+            if(barcode_object && typeof barcode_object.ilsevent == 'undefined')
+                barcode = barcode_object.barcode;
+            if ( obj.test_barcode(barcode) ) { /* good */ } else { /* bad */ return; }
             var placeholder_item = new acp();
             placeholder_item.barcode( barcode );
             var row_params = obj.list.append( { 
index 4227858..c7d3813 100644 (file)
@@ -589,6 +589,27 @@ circ.checkout.prototype = {
         if (! (params.barcode||params.noncat)) { return; }
 
         if (params.barcode) {
+            // Default is "just items"
+            var barcode_context = 'asset';
+            // Add actor (can be any string that includes 'actor') if looking up patrons at checkout
+            if(String( obj.data.hash.aous['circ.staff_client.actor_on_checkout'] ) == 'true')
+                barcode_context += '-actor';
+            // Auto-complete the barcode
+            var in_barcode = xulG.get_barcode(window, barcode_context, params.barcode);
+            // user_false is "None of the above selected", don't error out/fall through as they already said no
+            if(in_barcode == "user_false") return;
+            // We have a barcode and there was no error?
+            if(in_barcode && typeof in_barcode.ilsevent == 'undefined') {
+                // Check if it was an actor barcode (will never happen unless actor was added above)
+                if(in_barcode.type == 'actor') {
+                    // Go to new patron (do not pass go, do not collect $200, do not prompt user)
+                    var horizontal_interface = String( obj.data.hash.aous['ui.circ.patron_summary.horizontal'] ) == 'true';
+                    var loc = xulG.url_prefix( horizontal_interface ? urls.XUL_PATRON_HORIZ_DISPLAY : urls.XUL_PATRON_DISPLAY );
+                    xulG.set_tab( loc, {}, { 'barcode' : in_barcode.barcode } );
+                    return;
+                }
+                params.barcode = in_barcode.barcode;
+            }
 
             if ( obj.test_barcode(params.barcode) ) { /* good */ } else { /* bad */ return; }
 
index bff79af..ca91ad2 100644 (file)
@@ -1049,7 +1049,15 @@ circ.copy_status.prototype = {
         try {
             try { document.getElementById('last_scanned').setAttribute('value',''); } catch(E) {}
             if (!barcode) {
+                // No barcode provided = get barcode
                 barcode = obj.controller.view.copy_status_barcode_entry_textbox.value;
+                // Complete the barcode - just items
+                var barcode_object = xulG.get_barcode(window, 'asset', barcode);
+                // user_false is user said "None of the above" - Abort before other errors/prompts can result
+                if(barcode_object == "user_false") return;
+                // Got a barcode and no error? Use the barcode. Otherwise, fall through with entered barcode.
+                if(barcode_object && typeof barcode_object.ilsevent == 'undefined')
+                    barcode = barcode_object.barcode;
             }
             if (!barcode) { return; }
             if (barcode) {
index 0de36de..557995a 100644 (file)
 
                 tb.disabled = true;
                 document.getElementById('progress').setAttribute('hidden','false');
-                net.simple_request('FM_AU_ID_RETRIEVE_VIA_BARCODE_OR_USERNAME',[ ses(), barcode, null ],
-                    function(req) {
-                        document.getElementById('progress').setAttribute('hidden','true');
-                        tb.disabled = false; tb.select(); tb.focus(); ;
-                        var robj = req.getResultObject();
-                        if (typeof robj.ilsevent != 'undefined') {
-                            sound.bad();
-                            switch(Number(robj.ilsevent)) {
-                                case 1002 /* ACTOR_USER_NOT_FOUND */: 
-                                    add_msg($("patronStrings").getFormattedString('staff.patron.barcode_entry.barcode_not_found', [barcode]));
-                                break;
-                                default:
-                                    add_msg($("patronStrings").getFormattedString('staff.patron.barcode_entry.barcode_retrieval_problem', [barcode, js2JSON(robj)]));
-                            }
-                            return;
-                        }
+                // Auto-complete the barcode, users only. Handily, looks up all we need to know in the process.
+                var barcode_object = xulG.get_barcode(window, 'actor', barcode);
+                document.getElementById('progress').setAttribute('hidden','true');
+                tb.disabled = false; tb.select(); tb.focus(); ;
+                // user_false means the user said "None of the above", so abort without further prompts/actions
+                if(barcode_object == "user_false") return;
+                if(barcode_object == false) {
+                    // Boolean false means the barcode was not found, and the user wasn't prompted.
+                    sound.bad();
+                    add_msg($("patronStrings").getFormattedString('staff.patron.barcode_entry.barcode_not_found', [barcode]));
+                    return;
+                }
+                else if(typeof barcode_object.ilsevent != 'undefined') {
+                    // Getting an ilsevent error otherwise means something, likely permissions issues, went wrong
+                    sound.bad();
+                    add_msg($("patronStrings").getFormattedString('staff.patron.barcode_entry.barcode_retrieval_problem', [barcode, js2JSON(barcode_object)]));
+                    return;
+                }
 
-                        if (g.data.user_org_unit_opt_in_enabled) {
-                            var r = net.simple_request('USER_ORG_UNIT_OPT_IN_CHECK',[ ses(), robj ]);
-                            if (typeof r.ilsevent != 'undefined') {
-                                throw(r);
-                            } else {
+                if (g.data.user_org_unit_opt_in_enabled) {
+                    var r = net.simple_request('USER_ORG_UNIT_OPT_IN_CHECK',[ ses(), barcode_object.id ]);
+                    if (typeof r.ilsevent != 'undefined') {
+                        throw(r);
+                    } else {
 
-                                if (r == 0) {
+                        if (r == 0) {
 
-                                    JSAN.use('patron.util');
-                                    var parts = patron.util.retrieve_name_via_id( ses(), robj );
+                            JSAN.use('patron.util');
+                            var parts;
+                            // Handily, if the user was prompted, we should already have the user's name information returned from autocomplete
+                            // Use it if there, as it saves us a network call.
+                            if(barcode_object.request_data) parts = barcode_object.request_data;
+                            // Otherwise, make the network call
+                            else parts = patron.util.retrieve_name_via_id( ses(), barcode_object.id );
     
-                                    if (0 != g.error.yns_alert(
-                                            $("patronStrings").getFormattedString('staff.patron.barcode_entry.consent_from_patron',
-                                                [parts[0], parts[1] + (parts[2] ? ' ' + parts[2] : ''), g.data.hash.aou[ parts[3] ].name(), g.data.hash.aou[ parts[3] ].shortname()]),
-                                            $("patronStrings").getString('staff.patron.barcode_entry.patron_consent_title'),
-                                            $("patronStrings").getString('staff.patron.barcode_entry.patron_consent_accept'),
-                                            $("patronStrings").getString('staff.patron.barcode_entry.patron_consent_deny'), null,
-                                            $("patronStrings").getString('staff.patron.barcode_entry.patron_consent_confirm')
-                                        )
-                                    ) {
-                                        tb.select(); tb.focus();
-                                        return;
-                                    } else {
-                                        var c = net.simple_request('USER_ORG_UNIT_OPT_IN_CREATE',[ ses(), robj ]);
-                                        if (typeof c.ilsevent != 'undefined') throw(r);
-                                    }
-                                }
-    
-                                sound.good();
-                                spawn(barcode);
+                            if (0 != g.error.yns_alert(
+                                    $("patronStrings").getFormattedString('staff.patron.barcode_entry.consent_from_patron',
+                                        [parts[0], parts[1] + (parts[2] ? ' ' + parts[2] : ''), g.data.hash.aou[ parts[3] ].name(), g.data.hash.aou[ parts[3] ].shortname()]),
+                                    $("patronStrings").getString('staff.patron.barcode_entry.patron_consent_title'),
+                                    $("patronStrings").getString('staff.patron.barcode_entry.patron_consent_accept'),
+                                    $("patronStrings").getString('staff.patron.barcode_entry.patron_consent_deny'), null,
+                                    $("patronStrings").getString('staff.patron.barcode_entry.patron_consent_confirm')
+                                )
+                            ) {
+                                tb.select(); tb.focus();
+                                return;
+                            } else {
+                                var c = net.simple_request('USER_ORG_UNIT_OPT_IN_CREATE',[ ses(), barcode_object.id ]);
+                                if (typeof c.ilsevent != 'undefined') throw(r);
                             }
-                        } else {
-                            sound.good();
-                            spawn(barcode);
                         }
+    
+                        sound.good();
+                        spawn(barcode_object.id, barcode_object.barcode);
                     }
-                );
+                } else {
+                    sound.good();
+                    spawn(barcode_object.id, barcode_object.barcode);
+                }
             } catch(E) {
                 tb.select(); tb.focus();
                 g.error.standard_unexpected_error_alert('barcode_entry.xul',E);
             d.setAttribute('style','color: red');
         }
 
-        function spawn(barcode) {
-            if (xul_param('perm_editor')) { spawn_perm_editor(barcode); } else { spawn_checkout(barcode); }
+        function spawn(id, barcode) {
+            if (xul_param('perm_editor')) { spawn_perm_editor(id); } else { spawn_checkout(barcode); }
         }
 
         function spawn_checkout(barcode) {
             }
         }
 
-        function spawn_perm_editor(barcode) {
+        function spawn_perm_editor(id) {
             try {
-                JSAN.use('patron.util'); var patron_obj = patron.util.retrieve_fleshed_au_via_barcode( ses(), barcode );
-                var loc = urls.XUL_USER_PERM_EDITOR + '?ses=' + window.escape(ses()) + '&usr=' + patron_obj.id();
+                var loc = urls.XUL_USER_PERM_EDITOR + '?ses=' + window.escape(ses()) + '&usr=' + id;
                 if (typeof window.xulG == 'object' && typeof window.xulG.set_tab == 'function') {
                     window.xulG.set_tab( loc, {}, {} );
                 } else {
index 0f7f9ac..f927512 100644 (file)
@@ -831,7 +831,9 @@ patron.display.prototype = {
                                 }
                             )
                         }
-                    }
+                    },
+                    'get_barcode' : xulG.get_barcode,
+                    'url_prefix' : xulG.url_prefix
                 }
             );
             netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");