]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Acq/Picklist.pm
Merging acq-experiment to trunk, since rel_1_4 has been branched.
[working/Evergreen.git] / Open-ILS / src / perlmods / OpenILS / Application / Acq / Picklist.pm
1 package OpenILS::Application::Acq::Picklist;
2 use base qw/OpenILS::Application/;
3 use strict; use warnings;
4
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;
10 use OpenILS::Event;
11 use OpenILS::Application::AppUtils;
12 my $U = 'OpenILS::Application::AppUtils';
13
14
15 __PACKAGE__->register_method(
16         method => 'create_picklist',
17         api_name        => 'open-ils.acq.picklist.create',
18         signature => {
19         desc => 'Creates a new picklist',
20         params => [
21             {desc => 'Authentication token', type => 'string'},
22             {desc => 'Picklist object to create', type => 'object'}
23         ],
24         return => {desc => 'The ID of the new picklist'}
25     }
26 );
27
28 sub create_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;
37     $e->commit;
38     return $picklist->id;
39 }
40
41
42 __PACKAGE__->register_method(
43         method => 'update_picklist',
44         api_name        => 'open-ils.acq.picklist.update',
45         signature => {
46         desc => 'Updates a new picklist',
47         params => [
48             {desc => 'Authentication token', type => 'string'},
49             {desc => 'Picklist object to update', type => 'object'}
50         ],
51         return => {desc => '1 on success, Event on error'}
52     }
53 );
54
55 sub update_picklist {
56     my($self, $conn, $auth, $picklist) = @_;
57     my $e = new_editor(xact=>1, authtoken=>$auth);
58     return $e->die_event unless $e->checkauth;
59
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);
66     }
67     return OpenILS::Event->new('BAD_PARAMS') unless $o_picklist->org_unit == $picklist->org_unit;
68
69     $e->update_acq_picklist($picklist) or return $e->die_event;
70     $e->commit;
71     return 1;
72 }
73
74 __PACKAGE__->register_method(
75         method => 'retrieve_picklist',
76         api_name        => 'open-ils.acq.picklist.retrieve',
77         signature => {
78         desc => 'Retrieves a picklist',
79         params => [
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'},
83         ],
84         return => {desc => 'Picklist object on success, Event on error'}
85     }
86 );
87
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;
92
93     my $picklist = $e->retrieve_acq_picklist($picklist_id)
94         or return $e->event;
95
96     $picklist->entry_count(retrieve_lineitem_count($e, $picklist_id))
97         if $$options{flesh_lineitem_count};
98
99     if($e->requestor->id != $picklist->owner) {
100         return $e->event unless 
101             $e->allowed('VIEW_PICKLIST', $picklist->org_unit, $picklist);
102     }
103
104     $picklist->owner($e->retrieve_actor_user($picklist->owner)->usrname) 
105         if($$options{flesh_username});
106
107     return $picklist;
108 }
109
110
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({
115         select => { 
116             jub => [{transform => 'count', column => 'id', alias => 'count'}]
117         }, 
118         from => 'jub', 
119         where => {picklist => $picklist_id}}
120     );
121     return $count->[0]->{count};
122 }
123
124
125
126 __PACKAGE__->register_method(
127         method => 'retrieve_picklist_name',
128         api_name        => 'open-ils.acq.picklist.name.retrieve',
129         signature => {
130         desc => 'Retrieves a picklist by name.  Owner is implied by the caller',
131         params => [
132             {desc => 'Authentication token', type => 'string'},
133             {desc => 'Picklist name to retrieve', type => 'strin'},
134         ],
135         return => {desc => 'Picklist object on success, null on not found'}
136     }
137 );
138
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);
148     }
149     return $picklist;
150 }
151
152
153
154 __PACKAGE__->register_method(
155         method => 'retrieve_user_picklist',
156         api_name        => 'open-ils.acq.picklist.user.retrieve',
157     stream => 1,
158         signature => {
159         desc => 'Retrieves a  user\'s picklists',
160         params => [
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'},
164         ],
165         return => {desc => 'Picklist object on success, Event on error'}
166     }
167 );
168
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;
173
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=>{'!='=>''}},
177         {idlist=>1}
178     );
179
180     for my $id (@$list) {
181         if($$options{idlist}) {
182             $conn->respond($id);
183         } else {
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};
187             $conn->respond($pl);
188         }
189     }
190
191     return undef;
192 }
193
194
195 __PACKAGE__->register_method(
196         method => 'retrieve_all_user_picklist',
197         api_name        => 'open-ils.acq.picklist.user.all.retrieve',
198     stream => 1,
199         signature => {
200         desc => 'Retrieves all of the picklists a user is allowed to see',
201         params => [
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'},
205         ],
206         return => {desc => 'Picklist objects on success, Event on error'}
207     }
208 );
209
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;
214
215     my $my_list = $e->search_acq_picklist(
216         {owner=>$e->requestor->id, name=>{'!='=>''}}, {idlist=>1});
217
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});
222
223     return undef unless @$my_list or @$picklist_ids or @$picklist_ids_2;
224
225     my @list = (@$my_list, @$picklist_ids, @$picklist_ids_2);
226     my %dedup;
227     $dedup{$_} = 1 for @list;
228     @list = keys %dedup;
229
230     return \@list if $$options{idlist};
231
232     for my $pl (@list) {
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);
239     }
240
241     return undef;
242 }
243
244
245 __PACKAGE__->register_method(
246         method => 'delete_picklist',
247         api_name        => 'open-ils.acq.picklist.delete',
248         signature => {
249         desc => q/Deletes a picklist.  It also deletes any lineitems in the "new" state.  
250             Other attached lineitems are detached'/,
251         params => [
252             {desc => 'Authentication token', type => 'string'},
253             {desc => 'Picklist ID to delete', type => 'number'}
254         ],
255         return => {desc => '1 on success, Event on error'}
256     }
257 );
258
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;
263
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);
270     }
271
272     # delete all 'new' lineitems
273     my $lis = $e->search_acq_lineitem({picklist => $picklist->id, state => 'new'});
274     for my $li (@$lis) {
275         $e->delete_acq_lineitem($li) or return $e->die_event;
276     }
277
278     # detach all non-'new' lineitems
279     $lis = $e->search_acq_lineitem({picklist => $picklist->id, state => {'!=' => 'new'}});
280     for my $li (@$lis) {
281         $li->clear_picklist;
282         $e->update_acq_lineitem($li) or return $e->die_event;
283     }
284
285     # remove any picklist-specific object perms
286     my $ops = $e->search_permission_usr_object_perm_map({object_type => 'acqpl', object_id => $picklist->id});
287     for my $op (@$ops) {
288         $e->delete_usr_object_perm_map($op) or return $e->die_event;
289     }
290
291
292     $e->delete_acq_picklist($picklist) or return $e->die_event;
293     $e->commit;
294     return 1;
295 }
296
297 __PACKAGE__->register_method(
298         method => 'retrieve_pl_lineitem',
299         api_name        => 'open-ils.acq.lineitem.picklist.retrieve',
300     stream => 1,
301         signature => {
302         desc => 'Retrieves lineitem objects according to picklist',
303         params => [
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
315                 /, 
316                 type => 'hash'}
317         ],
318         return => {desc => 'Array of lineitem objects or IDs,  on success, Event on error'}
319     }
320 );
321
322
323 my $PL_ENTRY_JSON_QUERY = {
324     select => {jub => ["id"], "acqlia" => ["attr_value"]},
325     "from" => {
326         "jub" => {
327             "acqlia" => {
328                 "fkey" => "id", 
329                 "field" => "lineitem", 
330                 "type" => "left", 
331                 "filter" => {
332                     "attr_type" => "lineitem_marc_attr_definition", 
333                     "attr_name" => "author" 
334                 }
335             }
336         }
337     }, 
338     "order_by" => {"acqlia" => {"attr_value" => {"direction"=>"asc"}}}, 
339     "limit" => 10,
340     "where" => {"+jub" => {"picklist"=>2}},
341     "offset" => 0
342 };
343
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;
348
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;
355
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;
362
363     my $entries = $e->json_query($PL_ENTRY_JSON_QUERY);
364
365     my @ids;
366     push(@ids, $_->{id}) for @$entries;
367
368     for my $id (@ids) {
369         if($$options{idlist}) {
370             $conn->respond($id);
371             next;
372         } 
373
374         my $entry;
375         my $flesh = ($$options{flesh_attrs}) ? 
376             {flesh => 1, flesh_fields => {jub => ['attributes']}} : {};
377
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);
383     }
384
385     return undef;
386 }
387
388 =head comment
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}}
390 =cut
391
392 __PACKAGE__->register_method(
393         method => 'zsearch',
394         api_name => 'open-ils.acq.picklist.search.z3950',
395     stream => 1,
396         signature => {
397         desc => 'Performs a z3950 federated search and creates a picklist and associated lineitems',
398         params => [
399             {desc => 'Authentication token', type => 'string'},
400             {desc => 'Search definition', type => 'object'},
401             {desc => 'Picklist name, optional', type => 'string'},
402         ]
403     }
404 );
405
406 sub zsearch {
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');
411
412     $search->{limit} ||= 10;
413
414     $name ||= '';
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;
419         $picklist = undef;
420     }
421
422     unless($picklist) {
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;
428     }
429
430     my $ses = OpenSRF::AppSession->create('open-ils.search');
431     my $req = $ses->request('open-ils.search.z3950.search_class', $auth, $search);
432
433     while(my $resp = $req->recv(timeout=>60)) {
434
435         my $result = $resp->content;
436         #use Data::Dumper;
437         #$logger->info("results = ".Dumper($resp));
438         my $count = $result->{count};
439         my $total = (($count < $search->{limit}) ? $count : $search->{limit})+1;
440         my $ctr = 0;
441         $conn->respond({total=>$total, progress=>++$ctr});
442
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});
452         }
453     }
454
455     $e->commit;
456     return {complete=>1, picklist_id=>$picklist->id};
457 }
458
459
460
461 1;