1 package OpenILS::Application::Acq::Picklist;
2 use base qw/OpenILS::Application/;
3 use strict; use warnings;
5 use OpenSRF::Utils::Logger qw(:logger);
6 use OpenILS::Utils::Fieldmapper;
7 use OpenILS::Utils::CStoreEditor q/:funcs/;
8 use OpenILS::Const qw/:const/;
9 use OpenSRF::Utils::SettingsClient;
11 use OpenILS::Application::AppUtils;
12 my $U = 'OpenILS::Application::AppUtils';
15 __PACKAGE__->register_method(
16 method => 'create_picklist',
17 api_name => 'open-ils.acq.picklist.create',
19 desc => 'Creates a new picklist',
21 {desc => 'Authentication token', type => 'string'},
22 {desc => 'Picklist object to create', type => 'object'}
24 return => {desc => 'The ID of the new picklist'}
29 my($self, $conn, $auth, $picklist) = @_;
30 my $e = new_editor(xact=>1, authtoken=>$auth);
31 return $e->die_event unless $e->checkauth;
32 $picklist->org_unit($e->requestor->ws_ou) unless $picklist->org_unit;
33 return $e->die_event unless $e->allowed('CREATE_PICKLIST', $picklist->org_unit);
34 return OpenILS::Event->new('BAD_PARAMS')
35 unless $e->requestor->id == $picklist->owner;
36 $e->create_acq_picklist($picklist) or return $e->die_event;
42 __PACKAGE__->register_method(
43 method => 'update_picklist',
44 api_name => 'open-ils.acq.picklist.update',
46 desc => 'Updates a new picklist',
48 {desc => 'Authentication token', type => 'string'},
49 {desc => 'Picklist object to update', type => 'object'}
51 return => {desc => '1 on success, Event on error'}
56 my($self, $conn, $auth, $picklist) = @_;
57 my $e = new_editor(xact=>1, authtoken=>$auth);
58 return $e->die_event unless $e->checkauth;
60 # don't let them change the owner
61 my $o_picklist = $e->retrieve_acq_picklist($picklist->id)
62 or return $e->die_event;
63 if($o_picklist->owner != $e->requestor->id) {
64 return $e->die_event unless
65 $e->allowed('UPDATE_PICKLIST', $o_picklist->org_unit);
67 return OpenILS::Event->new('BAD_PARAMS') unless $o_picklist->org_unit == $picklist->org_unit;
69 $e->update_acq_picklist($picklist) or return $e->die_event;
74 __PACKAGE__->register_method(
75 method => 'retrieve_picklist',
76 api_name => 'open-ils.acq.picklist.retrieve',
78 desc => 'Retrieves a picklist',
80 {desc => 'Authentication token', type => 'string'},
81 {desc => 'Picklist ID to retrieve', type => 'number'},
82 {desc => 'Options hash, including "flesh_lineitem_count" to get the count of attached entries', type => 'hash'},
84 return => {desc => 'Picklist object on success, Event on error'}
88 sub retrieve_picklist {
89 my($self, $conn, $auth, $picklist_id, $options) = @_;
90 my $e = new_editor(authtoken=>$auth);
91 return $e->event unless $e->checkauth;
93 my $picklist = $e->retrieve_acq_picklist($picklist_id)
96 $picklist->entry_count(retrieve_lineitem_count($e, $picklist_id))
97 if $$options{flesh_lineitem_count};
99 if($e->requestor->id != $picklist->owner) {
100 return $e->event unless
101 $e->allowed('VIEW_PICKLIST', $picklist->org_unit, $picklist);
104 $picklist->owner($e->retrieve_actor_user($picklist->owner)->usrname)
105 if($$options{flesh_username});
111 # Returns the number of entries associated with this picklist
112 sub retrieve_lineitem_count {
113 my($e, $picklist_id) = @_;
114 my $count = $e->json_query({
116 jub => [{transform => 'count', column => 'id', alias => 'count'}]
119 where => {picklist => $picklist_id}}
121 return $count->[0]->{count};
126 __PACKAGE__->register_method(
127 method => 'retrieve_picklist_name',
128 api_name => 'open-ils.acq.picklist.name.retrieve',
130 desc => 'Retrieves a picklist by name. Owner is implied by the caller',
132 {desc => 'Authentication token', type => 'string'},
133 {desc => 'Picklist name to retrieve', type => 'strin'},
135 return => {desc => 'Picklist object on success, null on not found'}
139 sub retrieve_picklist_name {
140 my($self, $conn, $auth, $name) = @_;
141 my $e = new_editor(authtoken=>$auth);
142 return $e->event unless $e->checkauth;
143 my $picklist = $e->search_acq_picklist(
144 {name => $name, owner => $e->requestor->id})->[0];
145 if($e->requestor->id != $picklist->owner) {
146 return $e->event unless
147 $e->allowed('VIEW_PICKLIST', $picklist->org_unit, $picklist);
154 __PACKAGE__->register_method(
155 method => 'retrieve_user_picklist',
156 api_name => 'open-ils.acq.picklist.user.retrieve',
159 desc => 'Retrieves a user\'s picklists',
161 {desc => 'Authentication token', type => 'string'},
162 {desc => 'Options, including "idlist", whch forces the return
163 of a list of IDs instead of objects', type => 'hash'},
165 return => {desc => 'Picklist object on success, Event on error'}
169 sub retrieve_user_picklist {
170 my($self, $conn, $auth, $options) = @_;
171 my $e = new_editor(authtoken=>$auth);
172 return $e->die_event unless $e->checkauth;
174 # don't grab the PL with name == "", because that is the designated temporary picklist
175 my $list = $e->search_acq_picklist(
176 {owner=>$e->requestor->id, name=>{'!='=>''}},
180 for my $id (@$list) {
181 if($$options{idlist}) {
184 my $pl = $e->retrieve_acq_picklist($id);
185 $pl->entry_count(retrieve_lineitem_count($e, $id)) if $$options{flesh_lineitem_count};
186 $pl->owner($e->retrieve_actor_user($pl->owner)->usrname) if $$options{flesh_username};
195 __PACKAGE__->register_method(
196 method => 'retrieve_all_user_picklist',
197 api_name => 'open-ils.acq.picklist.user.all.retrieve',
200 desc => 'Retrieves all of the picklists a user is allowed to see',
202 {desc => 'Authentication token', type => 'string'},
203 {desc => 'Options, including "idlist", whch forces the return
204 of a list of IDs instead of objects', type => 'hash'},
206 return => {desc => 'Picklist objects on success, Event on error'}
210 sub retrieve_all_user_picklist {
211 my($self, $conn, $auth, $options) = @_;
212 my $e = new_editor(authtoken=>$auth);
213 return $e->event unless $e->checkauth;
215 my $my_list = $e->search_acq_picklist(
216 {owner=>$e->requestor->id, name=>{'!='=>''}}, {idlist=>1});
218 my $picklist_ids = $e->objects_allowed('VIEW_PICKLIST', 'acqpl');
219 my $p_orgs = $U->find_highest_work_orgs($e, 'VIEW_PICKLIST', {descendants =>1});
220 my $picklist_ids_2 = $e->search_acq_picklist(
221 {name=>{'!='=>''}, org_unit => $p_orgs}, {idlist=>1});
223 return undef unless @$my_list or @$picklist_ids or @$picklist_ids_2;
225 my @list = (@$my_list, @$picklist_ids, @$picklist_ids_2);
227 $dedup{$_} = 1 for @list;
230 return \@list if $$options{idlist};
233 my $picklist = $e->retrieve_acq_picklist($pl) or return $e->event;
234 $picklist->entry_count(retrieve_lineitem_count($e, $picklist->id))
235 if($$options{flesh_lineitem_count});
236 $picklist->owner($e->retrieve_actor_user($picklist->owner)->usrname)
237 if $$options{flesh_username};
238 $conn->respond($picklist);
245 __PACKAGE__->register_method(
246 method => 'delete_picklist',
247 api_name => 'open-ils.acq.picklist.delete',
249 desc => q/Deletes a picklist. It also deletes any lineitems in the "new" state.
250 Other attached lineitems are detached'/,
252 {desc => 'Authentication token', type => 'string'},
253 {desc => 'Picklist ID to delete', type => 'number'}
255 return => {desc => '1 on success, Event on error'}
259 sub delete_picklist {
260 my($self, $conn, $auth, $picklist_id) = @_;
261 my $e = new_editor(xact=>1, authtoken=>$auth);
262 return $e->die_event unless $e->checkauth;
264 my $picklist = $e->retrieve_acq_picklist($picklist_id)
265 or return $e->die_event;
266 # don't let anyone delete someone else's picklist
267 if($picklist->owner != $e->requestor->id) {
268 return $e->die_event unless
269 $e->allowed('DELETE_PICKLIST', $picklist->org_unit, $picklist);
272 # delete all 'new' lineitems
273 my $lis = $e->search_acq_lineitem({picklist => $picklist->id, state => 'new'});
275 $e->delete_acq_lineitem($li) or return $e->die_event;
278 # detach all non-'new' lineitems
279 $lis = $e->search_acq_lineitem({picklist => $picklist->id, state => {'!=' => 'new'}});
282 $e->update_acq_lineitem($li) or return $e->die_event;
285 # remove any picklist-specific object perms
286 my $ops = $e->search_permission_usr_object_perm_map({object_type => 'acqpl', object_id => $picklist->id});
288 $e->delete_usr_object_perm_map($op) or return $e->die_event;
292 $e->delete_acq_picklist($picklist) or return $e->die_event;
297 __PACKAGE__->register_method(
298 method => 'retrieve_pl_lineitem',
299 api_name => 'open-ils.acq.lineitem.picklist.retrieve',
302 desc => 'Retrieves lineitem objects according to picklist',
304 {desc => 'Authentication token', type => 'string'},
305 {desc => 'Picklist ID whose entries to retrieve', type => 'number'},
306 {desc => q/Options, including
307 "sort_attr", which defines the attribute to sort on;
308 "sort_attr_type", which defines the attribute type sort on;
309 "sort_dir", which defines the sort order between "asc" and "desc";
310 "limit", retrieval limit;
311 "offset", retrieval offset;
312 "idlist", return a list of IDs instead of objects
313 "flesh_attrs", additionaly return the list of flattened attributes
314 "clear_marc", discards the raw MARC data to reduce data size
318 return => {desc => 'Array of lineitem objects or IDs, on success, Event on error'}
323 my $PL_ENTRY_JSON_QUERY = {
324 select => {jub => ["id"], "acqlia" => ["attr_value"]},
329 "field" => "lineitem",
332 "attr_type" => "lineitem_marc_attr_definition",
333 "attr_name" => "author"
338 "order_by" => {"acqlia" => {"attr_value" => {"direction"=>"asc"}}},
340 "where" => {"+jub" => {"picklist"=>2}},
344 sub retrieve_pl_lineitem {
345 my($self, $conn, $auth, $picklist_id, $options) = @_;
346 my $e = new_editor(authtoken=>$auth);
347 return $e->event unless $e->checkauth;
349 # collect the retrieval options
350 my $sort_attr = $$options{sort_attr} || 'title';
351 my $sort_attr_type = $$options{sort_attr_type} || 'lineitem_marc_attr_definition';
352 my $sort_dir = $$options{sort_dir} || 'asc';
353 my $limit = $$options{limit} || 10;
354 my $offset = $$options{offset} || 0;
356 $PL_ENTRY_JSON_QUERY->{where}->{'+jub'}->{picklist} = $picklist_id;
357 $PL_ENTRY_JSON_QUERY->{from}->{jub}->{acqlia}->{filter}->{attr_name} = $sort_attr;
358 $PL_ENTRY_JSON_QUERY->{from}->{jub}->{acqlia}->{filter}->{attr_type} = $sort_attr_type;
359 $PL_ENTRY_JSON_QUERY->{order_by}->{acqlia}->{attr_value}->{direction} = $sort_dir;
360 $PL_ENTRY_JSON_QUERY->{limit} = $limit;
361 $PL_ENTRY_JSON_QUERY->{offset} = $offset;
363 my $entries = $e->json_query($PL_ENTRY_JSON_QUERY);
366 push(@ids, $_->{id}) for @$entries;
369 if($$options{idlist}) {
375 my $flesh = ($$options{flesh_attrs}) ?
376 {flesh => 1, flesh_fields => {jub => ['attributes']}} : {};
378 $entry = $e->retrieve_acq_lineitem([$id, $flesh]);
379 my $details = $e->search_acq_lineitem_detail({lineitem => $id}, {idlist=>1});
380 $entry->item_count(scalar(@$details));
381 $entry->clear_marc if $$options{clear_marc};
382 $conn->respond($entry);
389 request open-ils.cstore open-ils.cstore.json_query.atomic {"select":{"jub":[{"transform":"count", "attregate":1, "column":"id","alias":"count"}]}, "from":"jub","where":{"picklist":1}}
392 __PACKAGE__->register_method(
394 api_name => 'open-ils.acq.picklist.search.z3950',
397 desc => 'Performs a z3950 federated search and creates a picklist and associated lineitems',
399 {desc => 'Authentication token', type => 'string'},
400 {desc => 'Search definition', type => 'object'},
401 {desc => 'Picklist name, optional', type => 'string'},
407 my($self, $conn, $auth, $search, $name) = @_;
408 my $e = new_editor(authtoken=>$auth, xact=>1);
409 return $e->die_event unless $e->checkauth;
410 return $e->die_event unless $e->allowed('CREATE_PICKLIST');
412 $search->{limit} ||= 10;
415 my $picklist = $e->search_acq_picklist({owner=>$e->requestor->id, name=>$name})->[0];
416 if($name eq '' and $picklist) {
417 my $evt = delete_picklist($self, $conn, $auth, $picklist->id);
418 return $evt unless $evt == 1;
423 $picklist = Fieldmapper::acq::picklist->new;
424 $picklist->owner($e->requestor->id);
425 $picklist->name($name);
426 $picklist->org_unit($e->requestor->ws_ou);
427 $e->create_acq_picklist($picklist) or return $e->die_event;
430 my $ses = OpenSRF::AppSession->create('open-ils.search');
431 my $req = $ses->request('open-ils.search.z3950.search_class', $auth, $search);
433 while(my $resp = $req->recv(timeout=>60)) {
435 my $result = $resp->content;
437 #$logger->info("results = ".Dumper($resp));
438 my $count = $result->{count};
439 my $total = (($count < $search->{limit}) ? $count : $search->{limit})+1;
441 $conn->respond({total=>$total, progress=>++$ctr});
443 for my $rec (@{$result->{records}}) {
444 my $li = Fieldmapper::acq::lineitem->new;
445 $li->picklist($picklist->id);
446 $li->source_label($result->{service});
447 $li->selector($e->requestor->id);
448 $li->marc($rec->{marcxml});
449 $li->eg_bib_id($rec->{bibid}) if $rec->{bibid};
450 $e->create_acq_lineitem($li) or return $e->die_event;
451 $conn->respond({total=>$total, progress=>++$ctr});
456 return {complete=>1, picklist_id=>$picklist->id};