</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>
<!-- ********************************************************************************************************************* -->
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;
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 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.
+$$;
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;
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'),
--- /dev/null
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('XXXX');
+
+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) 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.
+$$;
+
+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;
+
+COMMIT;
--- /dev/null
+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);
+
<!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">
}
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;
--- /dev/null
+[% 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 %]
+
["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(
"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(
"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(
} 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");
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);
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) {
"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(
'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 = {
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);
['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',
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) {
}
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');
<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"/>
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
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
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( {
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; }
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) {
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 {
}
)
}
- }
+ },
+ 'get_barcode' : xulG.get_barcode,
+ 'url_prefix' : xulG.url_prefix
}
);
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");