<fields oils_persist:primary="id" oils_persist:sequence="acq.picklist_id_seq">
<field reporter:label="Picklist ID" name="id" reporter:datatype="id" />
<field reporter:label="Owner" name="owner" reporter:datatype="link" />
- <field reporter:label="Org Unit" name="org_unit" reporter:datatype="link" />
+ <field reporter:label="Org Unit" name="org_unit" reporter:datatype="org_unit" />
<field reporter:label="Name" name="name" reporter:datatype="text" oils_persist:i18n="true" />
<field reporter:label="Creation Time" name="create_time" reporter:datatype="timestamp" />
<field reporter:label="Edit Time" name="edit_time" reporter:datatype="timestamp" />
sub retrieve_purchase_order_impl {
my($e, $po_id, $options) = @_;
- # let's just always flesh this if it's there. what the hey.
- my $flesh = {
- "flesh" => 1, "flesh_fields" => {"acqpo" => ["cancel_reason"]}
- };
+ my $flesh = {"flesh" => 1, "flesh_fields" => {"acqpo" => []}};
$options ||= {};
+ unless ($options->{"no_flesh_cancel_reason"}) {
+ push @{$flesh->{"flesh_fields"}->{"acqpo"}}, "cancel_reason";
+ }
if ($options->{"flesh_notes"}) {
push @{$flesh->{"flesh_fields"}->{"acqpo"}}, "notes";
}
if ($options->{"flesh_provider"}) {
push @{$flesh->{"flesh_fields"}->{"acqpo"}}, "provider";
}
- my $po = $e->retrieve_acq_purchase_order([$po_id, $flesh])
+
+ my $args = (@{$flesh->{"flesh_fields"}->{"acqpo"}}) ?
+ [$po_id, $flesh] : $po_id;
+
+ my $po = $e->retrieve_acq_purchase_order($args)
or return $e->event;
if($$options{flesh_lineitems}) {
# alias, given names and family name.
sub prepare_au_terms {
my ($terms, $join_num) = @_;
+
my @joins = ();
+ my $nots = 0;
$join_num ||= 0;
foreach my $conj (qw/-and -or/) {
next unless exists $terms->{$conj};
my @new_outer_terms = ();
- foreach my $hint_unit (@{$terms->{$conj}}) {
+ HINT_UNIT: foreach my $hint_unit (@{$terms->{$conj}}) {
my $hint = (keys %$hint_unit)[0];
(my $plain_hint = $hint) =~ y/+//d;
+ if ($hint eq "-not") {
+ $hint_unit = $hint_unit->{$hint};
+ $nots++;
+ redo HINT_UNIT;
+ }
if (my $links = get_fm_links_by_hint($plain_hint) and
$plain_hint ne "acqlia") {
my @new_terms = ();
- foreach my $pair (@{$hint_unit->{$hint}}) {
- my ($attr, $value) = breakdown_term($pair);
- if ($links->{$attr} and
- $links->{$attr}->{"class"} eq "au") {
- push @joins, [$plain_hint, $attr, $join_num];
- push @new_outer_terms, gen_au_term($value, $join_num);
- $join_num++;
- } else {
- push @new_terms, $pair;
- }
- }
- if (@new_terms) {
- $hint_unit->{$hint} = [ @new_terms ];
- } else {
+ my ($attr, $value) = breakdown_term($hint_unit->{$hint});
+ if ($links->{$attr} and
+ $links->{$attr}->{"class"} eq "au") {
+ push @joins, [$plain_hint, $attr, $join_num];
+ my $au_term = gen_au_term($value, $join_num);
+ $au_term = {"-not" => $au_term} if $nots--;
+ push @new_outer_terms, $au_term;
+ $join_num++;
delete $hint_unit->{$hint};
}
}
+ $hint_unit = {"-not" => $hint_unit} if $nots--;
push @new_outer_terms, $hint_unit if scalar keys %$hint_unit;
}
$terms->{$conj} = [ @new_outer_terms ];
foreach my $class (qw/acqpo acqpl jub/) {
next if not exists $terms->{$class};
- my $clause = [];
$outer_clause->{$conj} = [] unless $outer_clause->{$conj};
foreach my $unit (@{$terms->{$class}}) {
my ($k, $v, $fuzzy, $between, $not) = breakdown_term($unit);
next;
}
- push @$clause, $not ? {"-not" => $term_clause} : $term_clause;
+ my $clause = {"+" . $class => $term_clause};
+ $clause = {"-not" => $clause} if $not;
+ push @{$outer_clause->{$conj}}, $clause;
}
- push @{$outer_clause->{$conj}}, {"+" . $class => $clause};
}
if ($terms->{"acqlia"}) {
"from" => {
"jub" => {
"acqpo" => {
- "type" => "left",
+ "type" => "full",
"field" => "id",
"fkey" => "purchase_order"
},
"acqpl" => {
- "type" => "left",
+ "type" => "full",
"field" => "id",
"fkey" => "picklist"
}
};
};
+ # TODO find instances of fields of type "timestamp" and massage the
+ # comparison to match search input (which is only at date precision,
+ # not timestamp).
my $offset = add_au_joins($query->{"from"}, prepare_au_terms($and_terms));
add_au_joins($query->{"from"}, prepare_au_terms($or_terms, $offset));
}
my $results = $e->json_query($query) or return $e->die_event;
- $conn->respond($retriever->($e, $_->{"id"}, $options)) foreach (@$results);
+ if ($options->{"id_list"}) {
+ foreach (@$results) {
+ $conn->respond($_->{"id"}) if $_->{"id"};
+ }
+ } else {
+ foreach (@$results) {
+ $conn->respond($retriever->($e, $_->{"id"}, $options))
+ if $_->{"id"};
+ }
+ }
$e->disconnect;
undef;
}
border:1px solid #888;
}
-
/* INVOICING */
#oils-acq-invoice-table td { padding: 5px; }
#acq-invoice-new-msg { font-weight: bold; margin: 10px;}
#acq-invoice-li-details { padding: 10px; font-weight: bold; border: 1px solid #888; margin: 10px; }
#acq-invoice-create { margin: 10px; }
+#acq-unified-heading { margin-bottom: 10px; }
+#acq-unified-heading-actual { float: left; width: 50%; font-size: 120%; font-weight: bold; }
+#acq-unified-heading-controls { float: right; width: 50%; text-align: right; }
+#acq-unified-form { margin-bottom: 10px; border-bottom: 1px dashed #666; padding-bottom: 10px; }
+#acq-unified-form > div { margin: 6px 0; }
+option[disabled="disabled"] { font-style: italic; }
+#acq-unified-terms-table { width: 100%; }
+#acq-unified-terms-table td { padding: 4px 0; }
+#acq-unified-add-term { padding-bottom: 20px; }
+.acq-unified-option-header { padding-left: 12px; }
+.acq-unified-option-regular { padding-left: 24px; }
+.acq-unified-terms-selector { width: 20%; }
+.acq-unified-terms-widget { width: 60%; }
+.acq-unified-terms-match { width: 15%; }
+.acq-unified-terms-remove { width: 5%; text-align: right; }
+.acq-unified-remover { color: #c00; }
'CONFIRM_FUNDS_AT_STOP': "One or more of the selected funds has a balance below its stop level.\nYou may not be able to activate purchase orders incorporating these copies.\nContinue?",
'CONFIRM_FUNDS_AT_WARNING': "One or more of the selected funds has a balance below its warning level.\nContinue?",
'INVOICE_ITEM_DETAILS' : "${0} <br/> ${1} <br/> ${2}. <br/> Estimated Price: $${3}. <br/> Lineitem ID: ${4} <br/> PO: ${5} <br/> Order Date: ${6}",
+ 'UNNAMED': "Unnamed"
}
} else {
this.baseWidgetValue(this.widgetValue);
- if(this.idlField.name == this.fmIDL.pkey && this.fmIDL.pkey_sequence && !this.selfReference)
+ if(this.idlField.name == this.fmIDL.pkey && this.fmIDL.pkey_sequence && (!this.selfReference && !this.noDisablePkey))
this.widget.attr('disabled', true);
if(this.disableWidgetTest && this.disableWidgetTest(this.idlField.name, this.fmObject))
this.widget.attr('disabled', true);
--- /dev/null
+dojo.require("openils.widget.AutoGrid");
+dojo.require("openils.widget.AutoWidget");
+dojo.require("openils.PermaCrud");
+dojo.require("openils.Util");
+
+var termSelectorFactory;
+var termManager;
+var resultManager;
+var pcrud = new openils.PermaCrud();
+
+/* typing save: add getValue() to all HTML <select> elements */
+HTMLSelectElement.prototype.getValue = function() {
+ return this.options[this.selectedIndex].value;
+}
+
+/* quickly find elements by the value of a "name" attribute */
+function nodeByName(name, root) {
+ return dojo.query("[name='" + name + "']", root)[0];
+}
+
+function hideForm() {
+ openils.Util.hide("acq-unified-hide-form");
+ openils.Util.show("acq-unified-reveal-form", "inline");
+ openils.Util.hide("acq-unified-form");
+}
+
+function revealForm() {
+ openils.Util.hide("acq-unified-reveal-form");
+ openils.Util.show("acq-unified-hide-form", "inline");
+ openils.Util.show("acq-unified-form");
+}
+
+/* The TermSelectorFactory will be instantiated by the TermManager. It
+ * provides HTML select controls whose options are all the searchable
+ * fields. Selecting a field from one of these controls will create the
+ * appopriate type of corresponding widget for the user to enter a search
+ * term against the selected field.
+ */
+function TermSelectorFactory(terms) {
+ var self = this;
+ this.terms = terms;
+
+ this.template = dojo.create("select");
+ this.template.appendChild(
+ dojo.create("option", {
+ "disabled": "disabled",
+ "selected": "selected",
+ "value": "",
+ "innerHTML": "Select Search Field" // XXX i18n
+ })
+ );
+
+ /* Create abbreviations for class names to make field categories
+ * more readable in field selector control. */
+ this._abbreviate = function(s) {
+ var last, result;
+ for (var i = 0; i < s.length; i++) {
+ if (s[i] != " ") {
+ if (!i) result = s[i];
+ else if (last == " ") result += s[i];
+ }
+ last = s[i];
+ }
+ return result;
+ };
+
+ var selectorMethods = {
+ /* Important: within the following functions, "this" refers to one
+ * HTMLSelect object, and "self" refers to the TermSelectorFactory. */
+ "getTerm": function() {
+ var parts = this.getValue().split(":");
+ return {
+ "hint": parts[0],
+ "field": parts[1],
+ "datatype": self.terms[parts[0]][parts[1]].datatype
+ };
+ },
+ "makeWidget": function(parentNode, wStore, callback) {
+ var term = this.getTerm();
+ var widgetKey = this.uniq;
+ if (term.hint == "acqlia") {
+ wStore[widgetKey] = dojo.create(
+ "input", {"type": "text"}, parentNode, "only"
+ );
+ wStore[widgetKey].focus();
+ if (typeof(callback) == "function")
+ callback(term, widgetKey);
+ } else {
+ var widget = new openils.widget.AutoFieldWidget({
+ "fmClass": term.hint,
+ "fmField": term.field,
+ "noDisablePkey": true,
+ "parentNode": dojo.create("span", null, parentNode, "only")
+ });
+ widget.build(
+ function(w) {
+ wStore[widgetKey] = w;
+ w.focus();
+ if (typeof(callback) == "function")
+ callback(term, widgetKey);
+ }
+ );
+ }
+ }
+ }
+
+ for (var hint in this.terms) {
+ var optgroup = dojo.create(
+ "optgroup", {"value": "", "label": this.terms[hint].__label}
+ );
+ var prefix = this._abbreviate(this.terms[hint].__label);
+
+ for (var field in this.terms[hint]) {
+ if (!/^__/.test(field)) {
+ optgroup.appendChild(
+ dojo.create("option", {
+ "class": "acq-unified-option-regular",
+ "value": hint + ":" + field,
+ "innerHTML": prefix + " - " +
+ this.terms[hint][field].label
+ })
+ );
+ }
+ }
+
+ this.template.appendChild(optgroup);
+ }
+
+ this.make = function(n) {
+ var node = dojo.clone(this.template);
+ node.uniq = n;
+ dojo.attr(node, "id", "term-" + n);
+ for (var name in selectorMethods)
+ node[name] = selectorMethods[name];
+ return node;
+ };
+}
+
+/* The term manager retrieves information from the IDL about all the fields
+ * in the classes that we consider searchable for our purpose. It maintains
+ * a dynamic HTML table of search terms, using the TermSelectorFactory
+ * to generate search field selectors, which in turn provide appropriate
+ * widgets for entering search terms. The TermManager provides search term
+ * modifiers (fuzzy searching, not searching). The TermManager also handles
+ * adding and removing rows of search terms, as well as building the search
+ * query to pass to the middle layer from the search term widgets.
+ */
+function TermManager() {
+ var self = this;
+
+ this.terms = {};
+ ["jub", "acqpl", "acqpo"].forEach(
+ function(hint) {
+ var o = {};
+ o.__label = fieldmapper.IDL.fmclasses[hint].label;
+ fieldmapper.IDL.fmclasses[hint].fields.forEach(
+ function(field) {
+ if (!field.virtual) {
+ o[field.name] = {
+ "label": field.label, "datatype": field.datatype
+ };
+ }
+ }
+ );
+ self.terms[hint] = o;
+ }
+ );
+
+ this.terms.acqlia = {"__label": fieldmapper.IDL.fmclasses.acqlia.label};
+ pcrud.retrieveAll("acqliad", {"order_by": {"acqliad": "id"}}).forEach(
+ function(def) {
+ self.terms.acqlia[def.id()] =
+ {"label": def.description(), "datatype": "text"}
+ }
+ );
+
+ this.selectorFactory = new TermSelectorFactory(this.terms);
+ this.template = dojo.byId("acq-unified-terms-tbody").
+ removeChild(dojo.byId("acq-unified-terms-row-tmpl"));
+ dojo.attr(this.template, "id");
+
+ this.rowId = 0;
+ this.widgets = {};
+
+ this._row = function(id) { return dojo.byId("term-row-" + id); };
+ this._selector = function(id) { return dojo.byId("term-" + id); };
+ this._match_how = function(id) { return dojo.byId("term-match-" + id); };
+
+ this.removerButton = function(n) {
+ return dojo.create("button", {
+ "innerHTML": "X",
+ "class": "acq-unified-remover",
+ "onclick": function() { self.removeRow(n); }
+ });
+ };
+
+ this.matchHowAllow = function(id, what, which) {
+ dojo.query(
+ "option[value*='" + what + "']", this._match_how(id)
+ ).forEach(function(o) { o.disabled = !which; });
+ };
+
+ this.getLinkTarget = function(term) {
+ return fieldmapper.IDL.fmclasses[term.hint].
+ field_map[term.field]["class"];
+ };
+
+ this.updateRowWidget = function(id) {
+ var where = nodeByName("widget", this._row(id));
+
+ delete this.widgets[id];
+ dojo.empty(where);
+
+ this._selector(id).makeWidget(
+ where, this.widgets,
+ function(term, key) {
+ var w = self.widgets[key];
+ var can_do_fuzzy;
+ if (term.datatype == "id") {
+ can_do_fuzzy = false;
+ } else if (term.datatype == "link") {
+ can_do_fuzzy = (self.getLinkTarget(term) == "au");
+ } else if (typeof(w.declaredClass) != "undefined") {
+ can_do_fuzzy = Boolean(w.declaredClass.match(/form\.Text/));
+ } else {
+ var type = dojo.attr(w, "type");
+ if (type)
+ can_do_fuzzy = (type == "text");
+ else
+ can_do_fuzzy = false;
+ }
+ self.matchHowAllow(id, "__fuzzy", can_do_fuzzy);
+ }
+ );
+ };
+
+ this.addRow = function() {
+ var uniq = (this.rowId)++;
+
+ var row = dojo.clone(this.template);
+ dojo.attr(row, "id", "term-row-" + uniq);
+
+ var selector = this.selectorFactory.make(uniq);
+ dojo.attr(
+ selector,
+ "onchange",
+ function() { self.updateRowWidget(uniq); }
+ );
+
+ var match_how = dojo.query("select", nodeByName("match", row))[0];
+ dojo.attr(match_how, "id", "term-match-" + uniq);
+ dojo.attr(match_how, "selectedIndex", 0);
+
+ nodeByName("selector", row).appendChild(selector);
+ nodeByName("remove", row).appendChild(this.removerButton(uniq));
+
+ dojo.place(row, "acq-unified-terms-tbody", "last");
+ }
+
+ this.removeRow = function(id) {
+ delete this.widgets[id];
+ dojo.destroy(this._row(id));
+ };
+
+ this.buildSearchObject = function() {
+ var so = {};
+
+ for (var id in this.widgets) {
+ var attr_parts = this._selector(id).getValue().split(":");
+ if (attr_parts.length != 2)
+ continue;
+
+ var hint = attr_parts[0];
+ var attr = attr_parts[1];
+ var match_how =
+ this._match_how(id).getValue().split(",").filter(Boolean);
+
+ var value;
+ try {
+ value = this.widgets[id].attr("value");
+ } catch (E) {
+ value = this.widgets[id].value;
+ }
+
+ if (!so[hint])
+ so[hint] = [];
+ var unit = {};
+ match_how.forEach(function(key) { unit[key] = true; });
+ unit[attr] = value;
+
+ so[hint].push(unit);
+ }
+ return so;
+ };
+}
+
+/* The result manager is used primarily when the users submits a search. It
+ * consults the termManager to get the search query to send to the middl
+ * layer, and it chooses which ML method to call as well as what widgets to use
+ * to display the results.
+ */
+function ResultManager(liTable, poGrid, plGrid) {
+ var self = this;
+
+ this.liTable = liTable;
+ this.poGrid = poGrid;
+ this.plGrid = plGrid;
+ this.poCache = {};
+ this.plCache = {};
+
+ this.result_types = {
+ "lineitem": {
+ "search_options": {
+ "flesh_attrs": true,
+ "flesh_cancel_reason": true,
+ "flesh_notes": true
+ },
+ "revealer": function() {
+ self.liTable.reset();
+ self.liTable.show("list");
+ }
+ },
+ "purchase_order": {
+ "search_options": {
+ "no_flesh_cancel_reason": true
+ },
+ "revealer": function() {
+ self.poGrid.resetStore();
+ self.poCache = {};
+ }
+ },
+ "picklist": {
+ "search_options": {
+ "flesh_lineitem_count": true,
+ "flesh_owner": true
+ },
+ "revealer": function() {
+ self.plGrid.resetStore();
+ self.plCache = {};
+ }
+ },
+ "no_results": {
+ "revealer": function() { alert(localeStrings.NO_RESULTS); }
+ }
+ };
+
+ this._add_lineitem = function(li) {
+ this.liTable.addLineitem(li);
+ };
+
+ this._add_purchase_order = function(po) {
+ this.poCache[po.id()] = po;
+ this.poGrid.store.newItem(acqpo.toStoreItem(po));
+ };
+
+ this._add_picklist = function(pl) {
+ this.plCache[pl.id()] = pl;
+ this.plGrid.store.newItem(acqpl.toStoreItem(pl));
+ };
+
+ this._finish_purchase_order = function() {
+ this.poGrid.hideLoadProgressIndicator();
+ };
+
+ this._finish_picklist = function() {
+ this.plGrid.hideLoadProgressIndicator();
+ };
+
+ this.add = function(which, what) {
+ var name = "_add_" + which;
+ if (this[name]) this[name](what);
+ };
+
+ this.finish = function(which) {
+ var name = "_finish_" + which;
+ if (this[name]) this[name]();
+ };
+
+ this.show = function(which) {
+ openils.Util.objectProperties(this.result_types).forEach(
+ function(rt) {
+ openils.Util[rt == which ? "show" : "hide"](
+ "acq-unified-results-" + rt
+ );
+ }
+ );
+ this.result_types[which].revealer();
+ };
+
+ this.search = function(search_obj) {
+ var count_results = 0;
+ var result_type = dojo.byId("acq-unified-result-type").getValue();
+ var conjunction = dojo.byId("acq-unified-conjunction").getValue();
+
+ /* XXX TODO when result_type can be "lineitem_and_bib" there may be a
+ * totally different ML method to call; not sure how that will work
+ * yet. */
+ var method_name = "open-ils.acq." + result_type + ".unified_search";
+ var params = [
+ openils.User.authtoken,
+ null, null, null,
+ this.result_types[result_type].search_options
+ ];
+
+ params[conjunction == "and" ? 1 : 2] = search_obj;
+
+ fieldmapper.standardRequest(
+ ["open-ils.acq", method_name], {
+ "params": params,
+ "async": true,
+ "onresponse": function(r) {
+ if (r = openils.Util.readResponse(r)) {
+ if (!count_results++)
+ self.show(result_type);
+ self.add(result_type, r);
+ }
+ },
+ "oncomplete": function() {
+ if (!count_results)
+ self.show("no_results");
+ else
+ self.finish(result_type);
+ }
+ }
+ );
+ }
+}
+
+/* The rest of the functions below handle the relatively unorganized
+ * miscellany of the search interface.
+ */
+
+/* onload */
+openils.Util.addOnLoad(
+ function() {
+ termManager = new TermManager();
+ termManager.addRow();
+ resultManager = new ResultManager(
+ new AcqLiTable(),
+ dijit.byId("acq-unified-po-grid"),
+ dijit.byId("acq-unified-pl-grid")
+ );
+ openils.Util.show("acq-unified-body");
+ }
+);
<!ENTITY staff.main.menu.acq.bib_search.accesskey "T">
<!ENTITY staff.main.menu.acq.li_search.label "Lineitem Search">
<!ENTITY staff.main.menu.acq.li_search.accesskey "I">
+<!ENTITY staff.main.menu.acq.unified_search.label "Acquisitions Search">
+<!ENTITY staff.main.menu.acq.unified_search.accesskey "A">
<!ENTITY staff.main.menu.acq.brief_record.label "New Brief Record">
<!ENTITY staff.main.menu.acq.brief_record.accesskey "B">
<!ENTITY staff.main.menu.acq.upload.label "Load Order Record">
--- /dev/null
+[% WRAPPER "default/base.tt2" %]
+[% ctx.page_title = "Acquisitions Search" %]
+<script src="[% ctx.media_prefix %]/js/ui/default/acq/search/unified.js">
+</script>
+<script>
+ /* The only functions in this <script> element are for
+ * formatting/getting fields for autogrids. */
+ function getName(rowIndex, item) {
+ if (item) {
+ return {
+ "name": this.grid.store.getValue(item, "name") ||
+ localeStrings.UNNAMED,
+ "id": this.grid.store.getValue(item, "id")
+ };
+ }
+ }
+
+ function getPlOwnerName(rowIndex, item) {
+ try {
+ return resultManager.plCache[this.grid.store.getValue(item, "id")].
+ owner().usrname();
+ } catch (E) {
+ return "";
+ }
+ }
+
+ function formatPoName(po) {
+ if (po) {
+ return "<a href='" + oilsBasePath + "/acq/po/view/" + po.id +
+ "'>" + po.name + "</a>";
+ }
+ }
+
+ function formatPlName(pl) {
+ if (pl) {
+ return "<a href='" + oilsBasePath + "/acq/picklist/view/" +
+ pl.id + "'>" + pl.name + "</a>";
+ }
+ }
+</script>
+<!-- later: "[% ctx.page_args.0 %]" -->
+<div id="acq-unified-body" class="hidden">
+ <div id="acq-unified-heading">
+ <span id="acq-unified-heading-actual">Acquisitions Search</span>
+ <span id="acq-unified-heading-controls">
+ <button id="acq-unified-hide-form" onclick="hideForm();">
+ Hide Search Form
+ </button>
+ <button id="acq-unified-reveal-form" onclick="revealForm();"
+ class="hidden">
+ Reveal Search Form
+ </button>
+ </span>
+ <div style="clear: both;"><!-- layout; don't remove --></div>
+ </div>
+ <div id="acq-unified-form">
+ <div>
+ <label for="acq-unified-result-type">Search for</label>
+ <select id="acq-unified-result-type">
+ <option value="lineitem">line items</option>
+ <option value="lineitem_and_bib" disabled="disabled">
+ <!-- XXX not yet implemented -->
+ line items & catalog records
+ </option>
+ <option value="picklist">pick lists</option>
+ <option value="purchase_order">purchase orders</option>
+ </select>
+ <label for="acq-unified-conjunction">matching</label>
+ <select id="acq-unified-conjunction">
+ <option value="or">any</option>
+ <option value="and">all</option>
+ </select>
+ <label for="acq-unified-conjunction">
+ of the following terms:
+ </label>
+ </div>
+ <div id="acq-unified-terms">
+ <table id="acq-unified-terms-table">
+ <tbody id="acq-unified-terms-tbody">
+ <tr id="acq-unified-terms-row-tmpl"
+ class="acq-unified-terms-row">
+ <td name="selector"
+ class="acq-unified-terms-selector"></td>
+ <td name="match"
+ class="acq-unified-terms-match">
+ <select>
+ <option value="">matches exactly</option>
+ <option value="__not">
+ does NOT match exactly
+ </option>
+ <option value="__fuzzy">contains</option>
+ <option value="__not,__fuzzy">
+ does NOT contain
+ </option>
+ </select>
+ </td>
+ <td name="widget"
+ class="acq-unified-terms-widget"></td>
+ <td name="remove"
+ class="acq-unified-terms-remove"></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <div id="acq-unified-add-term">
+ <button onclick="termManager.addRow()">Add Search Term</button>
+ </div>
+ <div>
+ <button
+ onclick="resultManager.search(termManager.buildSearchObject())">
+ Search
+ </button>
+ </div>
+ </div>
+ <div id="acq-unified-results-purchase_order" class="hidden">
+ <table
+ id="acq-unified-po-grid"
+ autoHeight="true"
+ dojoType="openils.widget.AutoGrid"
+ query="{id: '*'}"
+ fieldOrder="['name', 'owner', 'ordering_agency', 'provider',
+ 'create_time', 'edit_time', 'state']"
+ suppressFields="['owner', 'editor', 'creator']"
+ defaultCellWidth="'auto'"
+ showPaginator="true"
+ fmClass="acqpo">
+ <thead>
+ <tr>
+ <th field="name" get="getName" formatter="formatPoName">
+ Name
+ </th>
+ </tr>
+ </thead>
+ </table>
+ </div>
+ <div id="acq-unified-results-picklist" class="hidden">
+ <table
+ id="acq-unified-pl-grid"
+ autoHeight="true"
+ dojoType="openils.widget.AutoGrid"
+ query="{id: '*'}"
+ fieldOrder="['name', 'owner', 'entry_count',
+ 'create_time', 'edit_time']"
+ suppressFields="['editor', 'creator']"
+ defaultCellWidth="'auto'"
+ showPaginator="true"
+ fmClass="acqpl">
+ <thead>
+ <tr>
+ <th field="name" get="getName" formatter="formatPlName">
+ Name
+ </th>
+ <th field="owner" get="getPlOwnerName">Owner</th>
+ <th field="entry_count">Entry Count</th>
+ </tr>
+ </thead>
+ </table>
+ </div>
+ <div id="acq-unified-results-no_results" class="hidden">
+ There are no results from your search.
+ </div>
+ <div id="acq-unified-results-lineitem" class="hidden">
+ [% INCLUDE "default/acq/common/li_table.tt2" %]
+ </div>
+</div>
+
+[% END %]
['oncommand'],
function() { open_eg_web_page('acq/picklist/bib_search', 'menu.cmd_acq_bib_search.tab'); }
],
+ 'cmd_acq_unified_search' : [
+ ['oncommand'],
+ function() { open_eg_web_page('acq/search/unified', 'menu.cmd_acq_unified_search.tab'); }
+ ],
'cmd_acq_li_search' : [
['oncommand'],
function() { open_eg_web_page('acq/lineitem/search', 'menu.cmd_acq_li_search.tab'); }
<command id="cmd_acq_user_requests" />
<command id="cmd_acq_bib_search" />
<command id="cmd_acq_li_search" />
+ <command id="cmd_acq_unified_search" />
<command id="cmd_acq_new_brief_record" />
<command id="cmd_acq_view_fund" />
<command id="cmd_acq_view_funding_source" />
<!-- The Acquisitions menu on the main menu -->
<menu id="main.menu.acq" label="&staff.main.menu.acq.label;" accesskey="&staff.main.menu.acq.accesskey;">
<menupopup id="main.menu.acq.popup">
- <menuitem label="&staff.main.menu.acq.picklist.label;" accesskey="&staff.main.menu.acq.picklist.accesskey;" command="cmd_acq_view_picklist"/>
+ <menuitem label="&staff.main.menu.acq.unified_search.label;" accesskey="&staff.main.menu.acq.unified_search.accesskey;" command="cmd_acq_unified_search"/>
<menuitem label="&staff.main.menu.acq.bib_search.label;" accesskey="&staff.main.menu.acq.bib_search.accesskey;" command="cmd_acq_bib_search"/>
<menuitem label="&staff.main.menu.acq.li_search.label;" accesskey="&staff.main.menu.acq.li_search.accesskey;" command="cmd_acq_li_search"/>
+ <menuitem label="&staff.main.menu.acq.po.label;" accesskey="&staff.main.menu.acq.po.accesskey;" command="cmd_acq_view_po" />
+ <menuitem label="&staff.main.menu.acq.picklist.label;" accesskey="&staff.main.menu.acq.picklist.accesskey;" command="cmd_acq_view_picklist"/>
+ <menuseparator />
<menuitem label="&staff.main.menu.acq.upload.label;" accesskey="&staff.main.menu.acq.upload.accesskey;" command="cmd_acq_upload"/>
<menuitem label="&staff.main.menu.acq.brief_record.label;" accesskey="&staff.main.menu.acq.brief_record.accesskey;" command="cmd_acq_new_brief_record"/>
- <menuseparator />
- <menuitem label="&staff.main.menu.acq.po.label;" accesskey="&staff.main.menu.acq.po.accesskey;" command="cmd_acq_view_po" />
<menuitem label="&staff.main.menu.acq.po_events.label;" accesskey="&staff.main.menu.acq.po_events.accesskey;" command="cmd_acq_view_po_events" />
<menuitem label="&staff.main.menu.acq.user_requests.label;" accesskey="&staff.main.menu.acq.user_requests.accesskey;" command="cmd_acq_user_requests" />
<menuseparator />
menu.cmd_local_admin_transit_list.tab=Transits
menu.cmd_acq_view_picklist.tab=Selection Lists
menu.cmd_acq_bib_search.tab=Title Search
+menu.cmd_acq_unified_search.tab=Acquisitions Search
menu.cmd_acq_li_search.tab=Lineitem Search
menu.cmd_acq_upload.tab=Load Order Record
menu.cmd_acq_new_brief_record.tab=New Brief Record