]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/lib/OpenILS/Application/Acq/Picklist.pm
LP 2061136 follow-up: ng lint --fix
[Evergreen.git] / Open-ILS / src / perlmods / lib / 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::EX q/:try/;
6 use OpenSRF::Utils::Logger qw(:logger);
7 use OpenILS::Utils::Fieldmapper;
8 use OpenILS::Utils::CStoreEditor q/:funcs/;
9 use OpenILS::Const qw/:const/;
10 use OpenSRF::Utils::SettingsClient;
11 use OpenILS::Event;
12 use OpenILS::Application::AppUtils;
13 use OpenSRF::Utils::Cache;
14 use MARC::Record;
15 use MARC::Batch;
16 use MARC::File::XML (BinaryEncoding => 'UTF-8');
17 use MIME::Base64;
18 use Digest::MD5 qw/md5_hex/;
19 use OpenILS::Application::Acq::Financials;
20 use DateTime;
21
22 my $U = 'OpenILS::Application::AppUtils';
23
24
25 __PACKAGE__->register_method(
26     method => 'create_picklist',
27     api_name    => 'open-ils.acq.picklist.create',
28     signature => {
29         desc => 'Creates a new picklist',
30         params => [
31             {desc => 'Authentication token', type => 'string'},
32             {desc => 'Picklist object to create', type => 'object'}
33         ],
34         return => {desc => 'The ID of the new picklist'}
35     }
36 );
37
38 sub create_picklist {
39     my($self, $conn, $auth, $picklist) = @_;
40     my $e = new_editor(xact=>1, authtoken=>$auth);
41     return $e->die_event unless $e->checkauth;
42     $picklist->creator($e->requestor->id);
43     $picklist->editor($e->requestor->id);
44     $picklist->org_unit($e->requestor->ws_ou) unless $picklist->org_unit;
45     return $e->die_event unless $e->allowed('CREATE_PICKLIST', $picklist->org_unit);
46     return OpenILS::Event->new('BAD_PARAMS')
47         unless $e->requestor->id == $picklist->owner;
48     $e->create_acq_picklist($picklist) or return $e->die_event;
49     $e->commit;
50     return $picklist->id;
51 }
52
53
54 __PACKAGE__->register_method(
55     method => 'update_picklist',
56     api_name    => 'open-ils.acq.picklist.update',
57     signature => {
58         desc => 'Updates a new picklist',
59         params => [
60             {desc => 'Authentication token', type => 'string'},
61             {desc => 'Picklist object to update', type => 'object'}
62         ],
63         return => {desc => '1 on success, Event on error'}
64     }
65 );
66
67 sub update_picklist {
68     my($self, $conn, $auth, $picklist) = @_;
69     my $e = new_editor(xact=>1, authtoken=>$auth);
70     return $e->die_event unless $e->checkauth;
71
72     # don't let them change the owner
73     my $o_picklist = $e->retrieve_acq_picklist($picklist->id)
74         or return $e->die_event;
75     if($o_picklist->owner != $e->requestor->id) {
76         return $e->die_event unless 
77             $e->allowed('UPDATE_PICKLIST', $o_picklist->org_unit);
78     }
79     return OpenILS::Event->new('BAD_PARAMS') unless $o_picklist->org_unit == $picklist->org_unit;
80
81     $picklist->edit_time('now');
82     $picklist->editor($e->requestor->id);
83     $e->update_acq_picklist($picklist) or return $e->die_event;
84     $e->commit;
85     return 1;
86 }
87
88 __PACKAGE__->register_method(
89     method => 'retrieve_picklist',
90     api_name    => 'open-ils.acq.picklist.retrieve',
91     authoritative => 1,
92     signature => {
93         desc => 'Retrieves a picklist',
94         params => [
95             {desc => 'Authentication token', type => 'string'},
96             {desc => 'Picklist ID to retrieve', type => 'number'},
97             {desc => 'Options hash, including "flesh_lineitem_count" to get the count of attached entries', type => 'hash'},
98         ],
99         return => {desc => 'Picklist object on success, Event on error'}
100     }
101 );
102
103 sub retrieve_picklist {
104     my($self, $conn, $auth, $picklist_id, $options) = @_;
105     my $e = new_editor(authtoken=>$auth);
106     return $e->event unless $e->checkauth;
107
108     return retrieve_picklist_impl($e, $picklist_id, $options);
109 }
110
111 sub retrieve_picklist_impl {
112     my ($e, $picklist_id, $options) = @_;
113     $options ||= {};
114
115     my $picklist = $e->retrieve_acq_picklist($picklist_id)
116         or return $e->event;
117
118     $picklist->entry_count(retrieve_lineitem_count($e, $picklist_id))
119         if $$options{flesh_lineitem_count};
120
121     if($e->requestor->id != $picklist->owner) {
122         return $e->event unless 
123             $e->allowed('VIEW_PICKLIST', $picklist->org_unit, $picklist);
124     }
125
126     $picklist->owner($e->retrieve_actor_user($picklist->owner)) 
127         if($$options{flesh_owner});
128     $picklist->owner($e->retrieve_actor_user($picklist->owner)->usrname) 
129         if($$options{flesh_username});
130     $picklist->creator($e->retrieve_actor_user($picklist->creator))
131         if($$options{flesh_creator});
132     $picklist->editor($e->retrieve_actor_user($picklist->editor))
133         if($$options{flesh_editor});
134
135     return $picklist;
136 }
137
138
139 # Returns the number of entries associated with this picklist
140 sub retrieve_lineitem_count {
141     my($e, $picklist_id) = @_;
142     my $count = $e->json_query({
143         select => { 
144             jub => [{transform => 'count', column => 'id', alias => 'count'}]
145         }, 
146         from => 'jub', 
147         where => {picklist => $picklist_id}}
148     );
149     return $count->[0]->{count};
150 }
151
152
153
154 __PACKAGE__->register_method(
155     method => 'retrieve_picklist_name',
156     api_name    => 'open-ils.acq.picklist.name.retrieve',
157     authoritative => 1,
158     signature => {
159         desc => 'Retrieves a picklist by name.  Owner is implied by the caller',
160         params => [
161             {desc => 'Authentication token',      type => 'string'},
162             {desc => 'Picklist name to retrieve', type => 'string'},
163         ],
164         return => {desc => 'Picklist object on success, null on not found'}
165     }
166 );
167
168 sub retrieve_picklist_name {
169     my($self, $conn, $auth, $name) = @_;
170     my $e = new_editor(authtoken=>$auth);
171     return $e->event unless $e->checkauth;
172     my $picklist = $e->search_acq_picklist(
173         {name => $name, owner => $e->requestor->id})->[0];
174     if($e->requestor->id != $picklist->owner) {
175         return $e->event unless 
176             $e->allowed('VIEW_PICKLIST', $picklist->org_unit, $picklist);
177     }
178     return $picklist;
179 }
180
181
182
183 __PACKAGE__->register_method(
184     method => 'retrieve_user_picklist',
185     api_name    => 'open-ils.acq.picklist.user.retrieve',
186     stream => 1,
187     signature => {
188         desc => 'Retrieves a  user\'s picklists',
189         params => [
190             {desc => 'Authentication token', type => 'string'},
191             {desc => 'Options, including "idlist", whch forces the return
192                 of a list of IDs instead of objects', type => 'hash'},
193         ],
194         return => {desc => 'Picklist object on success, Event on error'}
195     }
196 );
197
198 sub retrieve_user_picklist {
199     my($self, $conn, $auth, $options) = @_;
200     my $e = new_editor(authtoken=>$auth);
201     return $e->die_event unless $e->checkauth;
202     $options ||= {};
203
204     # don't grab the PL with name == "", because that is the designated temporary picklist
205     my $list = $e->search_acq_picklist([
206             {
207                 owner => $e->requestor->id, 
208                 name => {'!=' => ''}
209             }, {
210                 order_by => $$options{order_by} || {acqpl => 'edit_time DESC'},
211                 limit => $$options{limit} || 10,
212                 offset => $$options{offset} || 0,
213             }
214         ],
215         {idlist=>1}
216     );
217
218     for my $id (@$list) {
219         if($$options{idlist}) {
220             $conn->respond($id);
221         } else {
222             my $pl = $e->retrieve_acq_picklist($id);
223             $pl->entry_count(retrieve_lineitem_count($e, $id)) if $$options{flesh_lineitem_count};
224             $pl->owner($e->retrieve_actor_user($pl->owner)) if $$options{flesh_owner};
225             $pl->owner($e->retrieve_actor_user($pl->owner)->usrname) if $$options{flesh_username};
226             $conn->respond($pl);
227         }
228     }
229
230     return undef;
231 }
232
233
234 __PACKAGE__->register_method(
235     method => 'retrieve_all_user_picklist',
236     api_name    => 'open-ils.acq.picklist.user.all.retrieve',
237     stream => 1,
238     signature => {
239         desc => 'Retrieves all of the picklists a user is allowed to see',
240         params => [
241             {desc => 'Authentication token', type => 'string'},
242             {desc => 'Options, including "idlist", whch forces the return
243                 of a list of IDs instead of objects', type => 'hash'},
244         ],
245         return => {desc => 'Picklist objects on success, Event on error'}
246     }
247 );
248
249 sub retrieve_all_user_picklist {
250     my($self, $conn, $auth, $options) = @_;
251     my $e = new_editor(authtoken=>$auth);
252     return $e->event unless $e->checkauth;
253
254     my $my_list = $e->search_acq_picklist(
255         {owner=>$e->requestor->id, name=>{'!='=>''}}, {idlist=>1});
256
257     my $picklist_ids = $e->objects_allowed('VIEW_PICKLIST', 'acqpl');
258     my $p_orgs = $U->user_has_work_perm_at($e, 'VIEW_PICKLIST', {descendants =>1});
259     my $picklist_ids_2 = $e->search_acq_picklist(
260         {name=>{'!='=>''}, org_unit => $p_orgs}, {idlist=>1});
261
262     return undef unless @$my_list or @$picklist_ids or @$picklist_ids_2;
263
264     my @list = (@$my_list, @$picklist_ids, @$picklist_ids_2);
265     my %dedup;
266     $dedup{$_} = 1 for @list;
267     @list = keys %dedup;
268
269     return \@list if $$options{idlist};
270
271     for my $pl (@list) {
272         my $picklist = $e->retrieve_acq_picklist($pl) or return $e->event;
273         $picklist->entry_count(retrieve_lineitem_count($e, $picklist->id))
274             if($$options{flesh_lineitem_count});
275         $picklist->owner($e->retrieve_actor_user($picklist->owner))
276             if $$options{flesh_owner};
277         $picklist->owner($e->retrieve_actor_user($picklist->owner)->usrname)
278             if $$options{flesh_username};
279         $conn->respond($picklist);
280     }
281
282     return undef;
283 }
284
285 __PACKAGE__->register_method(
286     method => 'retrieve_pl_lineitem',
287     api_name    => 'open-ils.acq.lineitem.picklist.retrieve',
288     stream => 1,
289     signature => {
290         desc => 'Retrieves lineitem objects according to picklist',
291         params => [
292             {desc => 'Authentication token', type => 'string'},
293             {desc => 'Picklist ID whose entries to retrieve', type => 'number'},
294             {desc => q/Options, including 
295                 "sort_attr", which defines the attribute to sort on; 
296                 "sort_attr_type", which defines the attribute type sort on; 
297                 "sort_dir", which defines the sort order between "asc" and "desc";
298                 "limit", retrieval limit;
299                 "offset", retrieval offset;
300                 "idlist", return a list of IDs instead of objects
301                 "flesh_attrs", additionaly return the list of flattened attributes
302                 "clear_marc", discards the raw MARC data to reduce data size
303                 "flesh_notes", flesh lineitem notes
304                 "flesh_cancel_reason", flesh cancel_reason
305                 /, 
306                 type => 'hash'}
307         ],
308         return => {desc => 'Array of lineitem objects or IDs,  on success, Event on error'}
309     }
310 );
311
312
313 my $PL_ENTRY_JSON_QUERY = {
314     select => {jub => ["id"], "acqlia" => ["attr_value"]},
315     "from" => {
316         "jub" => {
317             "acqlia" => {
318                 "fkey" => "id", 
319                 "field" => "lineitem", 
320                 "type" => "left", 
321                 "filter" => {
322                     "attr_type" => "lineitem_marc_attr_definition", 
323                     "attr_name" => "author" 
324                 }
325             }
326         }
327     }, 
328     "order_by" => {"acqlia" => {"attr_value" => {"direction"=>"asc"}}}, 
329     "limit" => 10,
330     "where" => {"+jub" => {"picklist"=>2}},
331     "offset" => 0
332 };
333
334 sub retrieve_pl_lineitem {
335     my($self, $conn, $auth, $picklist_id, $options) = @_;
336     my $e = new_editor(authtoken=>$auth);
337     return $e->event unless $e->checkauth;
338
339     # collect the retrieval options
340     my $sort_attr = $$options{sort_attr} || 'title';
341     my $sort_attr_type = $$options{sort_attr_type} || 'lineitem_marc_attr_definition';
342     my $sort_dir = $$options{sort_dir} || 'asc';
343     my $limit = $$options{limit} || 10;
344     my $offset = $$options{offset} || 0;
345
346     $PL_ENTRY_JSON_QUERY->{where}->{'+jub'}->{picklist} = $picklist_id;
347     $PL_ENTRY_JSON_QUERY->{from}->{jub}->{acqlia}->{filter}->{attr_name} = $sort_attr;
348     $PL_ENTRY_JSON_QUERY->{from}->{jub}->{acqlia}->{filter}->{attr_type} = $sort_attr_type;
349     $PL_ENTRY_JSON_QUERY->{order_by}->{acqlia}->{attr_value}->{direction} = $sort_dir;
350     $PL_ENTRY_JSON_QUERY->{limit} = $limit;
351     $PL_ENTRY_JSON_QUERY->{offset} = $offset;
352
353     my $entries = $e->json_query($PL_ENTRY_JSON_QUERY);
354
355     my @ids;
356     for my $entry (@$entries) {
357         push(@ids, $entry->{id}) unless grep { $_ eq $entry->{id} } @ids;
358     }
359
360     for my $id (@ids) {
361         if($$options{idlist}) {
362             $conn->respond($id);
363             next;
364         } 
365
366         my $entry;
367         my $flesh = {};
368         if($$options{flesh_attrs} or $$options{flesh_notes} or $$options{flesh_cancel_reason}) {
369             $flesh = {flesh => 2, flesh_fields => {jub => []}};
370             if($$options{flesh_notes}) {
371                 push(@{$flesh->{flesh_fields}->{jub}}, 'lineitem_notes');
372                 $flesh->{flesh_fields}->{acqlin} = ['alert_text'];
373             }
374             push(@{$flesh->{flesh_fields}->{jub}}, 'attributes') if $$options{flesh_attrs};
375             push @{$flesh->{flesh_fields}->{jub}}, 'cancel_reason' if $$options{flesh_cancel_reason};
376         }
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 =head1 comment
389
390 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}}
391
392 =cut
393
394
395
396 __PACKAGE__->register_method(
397     method    => "record_distribution_formula_application",
398     api_name  => "open-ils.acq.distribution_formula.record_application",
399     signature => {
400         desc  => "Record the application (which actually happens on the " .
401             "client side) of a distribution formula to a PO or a PL",
402         params => [
403             {desc => "Authentication token", type => "string"},
404             {desc => "Formulae applied", "type" => "array"},
405             {desc => "Lineitem ID", "type" => "number"}
406         ],
407         return => {desc => "acqdfa IDs on success; event on failure"}
408     }
409 );
410
411 sub record_distribution_formula_application {
412     my ($self, $conn, $auth, $formulae, $li_id) = @_;
413
414     my $e = new_editor("authtoken" => $auth, "xact" => 1);
415     return $e->die_event unless $e->checkauth;
416
417     # We need this to determine relevant OU for testing permissions...
418     my $li = $e->retrieve_acq_lineitem([
419         $li_id, {
420             "flesh" => 1,
421             "flesh_fields" => {"jub" => [qw/purchase_order picklist/]}
422         }
423     ]) or return $e->die_event;
424
425     # ... which we do here.
426     my $ou;
427     if ($li->purchase_order) {
428         $ou = $li->purchase_order->ordering_agency;
429     } elsif ($li->picklist) {
430         $ou = $li->picklist->org_unit;
431     } else {
432         $e->rollback;
433         return new OpenILS::Event("BAD_PARAMS");
434     }
435
436     return $e->die_event unless $e->allowed("CREATE_PURCHASE_ORDER", $ou);
437
438     # Just deal with it if $formulate is a scalar instead of an array.
439     $formulae = [ $formulae ] if not ref $formulae;
440
441     my @results = ();
442     foreach (@{$formulae}) {
443         my $acqdfa = new Fieldmapper::acq::distribution_formula_application;
444
445         $acqdfa->creator($e->requestor->id);
446         $acqdfa->formula($_);
447         $acqdfa->lineitem($li_id);
448
449         $acqdfa = $e->create_acq_distribution_formula_application($acqdfa)
450             or return $e->die_event;
451         push @results, $acqdfa->id;
452     }
453
454     $e->commit or return $e->die_event;
455     \@results;
456 }
457
458
459 __PACKAGE__->register_method(
460     method => 'ranged_distrib_formulas',
461     api_name    => 'open-ils.acq.distribution_formula.ranged.retrieve',
462     stream => 1,
463     signature => {
464         desc => 'Ranged distribution formulas, fleshed with entries',
465         params => [
466             {desc => 'Authentication token', type => 'string'},
467             {desc => "offset", type => "number"},
468             {desc => "limit", type => "number"}
469         ],
470         return => {desc => 'List of distribution formulas'}
471     }
472 );
473
474 sub ranged_distrib_formulas {
475     my ($self, $conn, $auth, $offset, $limit) = @_;
476
477     $offset ||= 0;
478     $limit ||= 10;
479
480     my $e = new_editor(authtoken=>$auth);
481     return $e->event unless $e->checkauth;
482     my $orgs = $U->user_has_work_perm_at($e, 'CREATE_PICKLIST', {descendants =>1});
483
484     my $forms = $e->search_acq_distribution_formula([
485         {owner => $orgs},
486         {
487             flesh => 1, 
488             flesh_fields => {acqdf => ['entries']},
489             order_by => {acqdf => "name"},
490             limit => $limit,
491             offset => $offset
492         }
493     ]) or return $e->die_event;
494
495     for (@$forms) {
496
497         # how many times has this DF been used
498         my $count = $e->json_query({
499             select => {acqdfa => [{column => 'formula', aggregate => 1, transform => 'count', alias => 'count'}]}, 
500             from => 'acqdfa', 
501             where => {formula => $_->id}
502         })->[0];
503
504         $_->use_count($count->{count});
505         $conn->respond($_);
506     }
507
508     return undef;
509 }
510
511 __PACKAGE__->register_method(
512     method => "ranged_distrib_formula_applications",
513     api_name => "open-ils.acq.distribution_formula_application.ranged.retrieve",
514     stream => 1,
515     signature => {
516         desc => "Ranged distribution formulas applications, fleshed with formulas and users",
517         params => [
518             {desc => "Authentication token", type => "string"},
519             {desc => "Lineitem Id", type => "number"}
520         ],
521         return => {desc => "List of distribution formula applications"}
522     }
523 );
524
525 sub ranged_distrib_formula_applications {
526     my ($self, $conn, $auth, $li_id) = @_;
527
528     my $e = new_editor("authtoken" => $auth);
529     return $e->event unless $e->checkauth;
530
531     my $li = $e->retrieve_acq_lineitem([
532         $li_id, {
533             "flesh" => 1,
534             "flesh_fields" => {"jub" => [qw/purchase_order picklist/]}
535         }
536     ]) or return $e->die_event;
537
538     if ($li->picklist) {
539         return $e->die_event unless $e->allowed(
540             "VIEW_PICKLIST", $li->picklist->org_unit
541         );
542     } elsif ($li->purchase_order) {
543         return $e->die_event unless $e->allowed(
544             "VIEW_PURCHASE_ORDER", $li->purchase_order->ordering_agency
545         );
546     } else {
547         # For the moment no use cases are forseen for using this
548         # method with LIs that don't belong to a PL or a PO.
549         $e->disconnect;
550         return new OpenILS::Event("BAD_PARAMS", "note" => "Freestanding LI");
551     }
552
553     my $dfa = $e->search_acq_distribution_formula_application([
554         {"lineitem" => $li_id},
555         {"flesh" => 1, "flesh_fields" => {"acqdfa" => [qw/formula creator/]}}
556     ]);
557
558     $conn->respond($_) foreach (@$dfa);
559
560     $e->disconnect;
561     undef;
562 }
563
564 1;