1 package OpenILS::Application::Acq::Search;
2 use base "OpenILS::Application";
8 use OpenILS::Utils::CStoreEditor q/:funcs/;
9 use OpenILS::Utils::Fieldmapper;
10 use OpenILS::Application::Acq::Lineitem;
11 use OpenILS::Application::Acq::Financials;
12 use OpenILS::Application::Acq::Picklist;
16 \&{"OpenILS::Application::Acq::Lineitem::retrieve_lineitem_impl"},
18 \&{"OpenILS::Application::Acq::Picklist::retrieve_picklist_impl"},
19 "purchase_order" => \&{
20 "OpenILS::Application::Acq::Financials::retrieve_purchase_order_impl"
24 sub F { $Fieldmapper::fieldmap->{"Fieldmapper::" . $_[0]}; }
26 # This subroutine returns 1 if the argument is a) a scalar OR
27 # b) an array of ONLY scalars. Otherwise it returns 0.
30 return 1 unless ref $o;
31 if (ref($o) eq "ARRAY") {
32 foreach (@$o) { return 0 if ref $_; }
38 # Returns 1 if and only if argument is an array of exactly two scalars.
41 if (ref $o eq "ARRAY") {
42 return 1 if (scalar(@$o) == 2 && (!ref $o->[0] && !ref $o->[1]));
47 sub prepare_acqlia_search_and {
51 foreach my $unit (@{$acqlia}) {
54 "select" => {"acqlia" => ["id"]},
56 "where" => {"-and" => [{"lineitem" => {"=" => {"+jub" => "id"}}}]}
59 while (my ($k, $v) = each %$unit) {
60 my $point = $subquery->{"where"}->{"-and"};
62 push @$point, {"definition" => $k};
65 if ($unit->{"__fuzzy"} and not ref $v) {
66 push @$point, {"attr_value" => {"ilike" => "%" . $v . "%"}};
67 } elsif ($unit->{"__between"} and could_be_range($v)) {
68 push @$point, {"attr_value" => {"between" => $v}};
69 } elsif (check_1d_max($v)) {
70 push @$point, {"attr_value" => $v};
76 push @phrases, {"-exists" => $subquery} if $something;
81 sub prepare_acqlia_search_or {
85 my $result = {"+acqlia" => {"-or" => $point}};
87 foreach my $unit (@$acqlia) {
88 while (my ($k, $v) = each %$unit) {
90 if ($unit->{"__fuzzy"} and not ref $v) {
94 "attr_value" => {"ilike" => "%" . $v . "%"}
97 } elsif ($unit->{"__between"} and could_be_range($v)) {
101 "attr_value" => {"between" => $v}
104 } elsif (check_1d_max($v)) {
106 "-and" => {"definition" => $k, "attr_value" => $v}
119 my ($terms, $is_and) = @_;
121 my $conj = $is_and ? "-and" : "-or";
122 my $outer_clause = {};
124 foreach my $class (qw/acqpo acqpl jub/) {
125 next if not exists $terms->{$class};
128 $outer_clause->{$conj} = [] unless $outer_clause->{$conj};
129 foreach my $unit (@{$terms->{$class}}) {
130 while (my ($k, $v) = each %$unit) {
132 if ($unit->{"__fuzzy"} and not ref $v) {
133 push @$clause, {$k => {"ilike" => "%" . $v . "%"}};
134 } elsif ($unit->{"__between"} and could_be_range($v)) {
135 push @$clause, {$k => {"between" => $v}};
136 } elsif (check_1d_max($v)) {
137 push @$clause, {$k => $v};
142 push @{$outer_clause->{$conj}}, {"+" . $class => $clause};
145 if ($terms->{"acqlia"}) {
146 push @{$outer_clause->{$conj}},
147 $is_and ? prepare_acqlia_search_and($terms->{"acqlia"}) :
148 prepare_acqlia_search_or($terms->{"acqlia"});
151 return undef unless scalar keys %$outer_clause;
155 __PACKAGE__->register_method(
156 method => "grand_search",
157 api_name => "open-ils.acq.lineitem.grand_search",
159 desc => q/Returns lineitems based on flexible search terms./,
161 {desc => "Authentication token", type => "string"},
162 {desc => "Field/value pairs for AND'ing", type => "object"},
163 {desc => "Field/value pairs for OR'ing", type => "object"},
164 {desc => "Conjunction between AND pairs and OR pairs " .
165 "(can be 'and' or 'or')", type => "string"},
166 {desc => "Retrieval options (clear_marc, flesh_notes, etc) " .
167 "- XXX detail all the options",
170 return => {desc => "A stream of LIs on success, Event on failure"}
174 __PACKAGE__->register_method(
175 method => "grand_search",
176 api_name => "open-ils.acq.purchase_order.grand_search",
178 desc => q/Returns purchase orders based on flexible search terms.
179 See open-ils.acq.lineitem.grand_search/,
180 return => {desc => "A stream of POs on success, Event on failure"}
184 __PACKAGE__->register_method(
185 method => "grand_search",
186 api_name => "open-ils.acq.picklist.grand_search",
188 desc => q/Returns pick lists based on flexible search terms.
189 See open-ils.acq.lineitem.grand_search/,
190 return => {desc => "A stream of PLs on success, Event on failure"}
195 my ($self, $conn, $auth, $and_terms, $or_terms, $conj, $options) = @_;
196 my $e = new_editor("authtoken" => $auth);
197 return $e->die_event unless $e->checkauth;
199 # What kind of object are we returning? Important: (\w+) had better be
200 # a legit acq classname particle, so don't register any crazy api_names.
201 my $ret_type = ($self->api_name =~ /cq.(\w+).gr/)[0];
202 my $retriever = $RETRIEVERS{$ret_type};
206 F("acq::$ret_type")->{"hint"} =>
207 [{"column" => "id", "transform" => "distinct"}]
214 "fkey" => "purchase_order"
225 $and_terms = prepare_terms($and_terms, 1);
226 $or_terms = prepare_terms($or_terms, 0) and do {
227 $query->{"from"}->{"jub"}->{"acqlia"} = {
228 "type" => "left", "field" => "lineitem", "fkey" => "id",
232 if ($and_terms and $or_terms) {
233 $query->{"where"} = {
234 "-" . (lc $conj eq "or" ? "or" : "and") => [$and_terms, $or_terms]
236 } elsif ($and_terms) {
237 $query->{"where"} = $and_terms;
238 } elsif ($or_terms) {
239 $query->{"where"} = $or_terms;
242 return new OpenILS::Event("BAD_PARAMS", "desc" => "No usable terms");
245 my $results = $e->json_query($query) or return $e->die_event;
246 $conn->respond($retriever->($e, $_->{"id"}, $options)) foreach (@$results);