1 package OpenILS::Application::Acq::Picklist;
2 use base qw/OpenILS::Application/;
3 use strict; use warnings;
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;
12 use OpenILS::Application::AppUtils;
13 use OpenSRF::Utils::Cache;
18 use Digest::MD5 qw/md5_hex/;
19 use OpenILS::Application::Acq::Financials;
22 my $U = 'OpenILS::Application::AppUtils';
25 __PACKAGE__->register_method(
26 method => 'create_picklist',
27 api_name => 'open-ils.acq.picklist.create',
29 desc => 'Creates a new picklist',
31 {desc => 'Authentication token', type => 'string'},
32 {desc => 'Picklist object to create', type => 'object'}
34 return => {desc => 'The ID of the new 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;
54 __PACKAGE__->register_method(
55 method => 'update_picklist',
56 api_name => 'open-ils.acq.picklist.update',
58 desc => 'Updates a new picklist',
60 {desc => 'Authentication token', type => 'string'},
61 {desc => 'Picklist object to update', type => 'object'}
63 return => {desc => '1 on success, Event on error'}
68 my($self, $conn, $auth, $picklist) = @_;
69 my $e = new_editor(xact=>1, authtoken=>$auth);
70 return $e->die_event unless $e->checkauth;
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);
79 return OpenILS::Event->new('BAD_PARAMS') unless $o_picklist->org_unit == $picklist->org_unit;
81 $picklist->edit_time('now');
82 $picklist->editor($e->requestor->id);
83 $e->update_acq_picklist($picklist) or return $e->die_event;
88 __PACKAGE__->register_method(
89 method => 'retrieve_picklist',
90 api_name => 'open-ils.acq.picklist.retrieve',
92 desc => 'Retrieves a picklist',
94 {desc => 'Authentication token', type => 'string'},
95 {desc => 'Picklist ID to retrieve', type => 'number'},
96 {desc => 'Options hash, including "flesh_lineitem_count" to get the count of attached entries', type => 'hash'},
98 return => {desc => 'Picklist object on success, Event on error'}
102 sub retrieve_picklist {
103 my($self, $conn, $auth, $picklist_id, $options) = @_;
104 my $e = new_editor(authtoken=>$auth);
105 return $e->event unless $e->checkauth;
107 return retrieve_picklist_impl($e, $picklist_id, $options);
110 sub retrieve_picklist_impl {
111 my ($e, $picklist_id, $options) = @_;
114 my $picklist = $e->retrieve_acq_picklist($picklist_id)
117 $picklist->entry_count(retrieve_lineitem_count($e, $picklist_id))
118 if $$options{flesh_lineitem_count};
120 if($e->requestor->id != $picklist->owner) {
121 return $e->event unless
122 $e->allowed('VIEW_PICKLIST', $picklist->org_unit, $picklist);
125 $picklist->owner($e->retrieve_actor_user($picklist->owner))
126 if($$options{flesh_owner});
127 $picklist->owner($e->retrieve_actor_user($picklist->owner)->usrname)
128 if($$options{flesh_username});
134 # Returns the number of entries associated with this picklist
135 sub retrieve_lineitem_count {
136 my($e, $picklist_id) = @_;
137 my $count = $e->json_query({
139 jub => [{transform => 'count', column => 'id', alias => 'count'}]
142 where => {picklist => $picklist_id}}
144 return $count->[0]->{count};
149 __PACKAGE__->register_method(
150 method => 'retrieve_picklist_name',
151 api_name => 'open-ils.acq.picklist.name.retrieve',
153 desc => 'Retrieves a picklist by name. Owner is implied by the caller',
155 {desc => 'Authentication token', type => 'string'},
156 {desc => 'Picklist name to retrieve', type => 'string'},
158 return => {desc => 'Picklist object on success, null on not found'}
162 sub retrieve_picklist_name {
163 my($self, $conn, $auth, $name) = @_;
164 my $e = new_editor(authtoken=>$auth);
165 return $e->event unless $e->checkauth;
166 my $picklist = $e->search_acq_picklist(
167 {name => $name, owner => $e->requestor->id})->[0];
168 if($e->requestor->id != $picklist->owner) {
169 return $e->event unless
170 $e->allowed('VIEW_PICKLIST', $picklist->org_unit, $picklist);
177 __PACKAGE__->register_method(
178 method => 'retrieve_user_picklist',
179 api_name => 'open-ils.acq.picklist.user.retrieve',
182 desc => 'Retrieves a user\'s picklists',
184 {desc => 'Authentication token', type => 'string'},
185 {desc => 'Options, including "idlist", whch forces the return
186 of a list of IDs instead of objects', type => 'hash'},
188 return => {desc => 'Picklist object on success, Event on error'}
192 sub retrieve_user_picklist {
193 my($self, $conn, $auth, $options) = @_;
194 my $e = new_editor(authtoken=>$auth);
195 return $e->die_event unless $e->checkauth;
198 # don't grab the PL with name == "", because that is the designated temporary picklist
199 my $list = $e->search_acq_picklist([
201 owner => $e->requestor->id,
204 order_by => $$options{order_by} || {acqpl => 'edit_time DESC'},
205 limit => $$options{limit} || 10,
206 offset => $$options{offset} || 0,
212 for my $id (@$list) {
213 if($$options{idlist}) {
216 my $pl = $e->retrieve_acq_picklist($id);
217 $pl->entry_count(retrieve_lineitem_count($e, $id)) if $$options{flesh_lineitem_count};
218 $pl->owner($e->retrieve_actor_user($pl->owner)) if $$options{flesh_owner};
219 $pl->owner($e->retrieve_actor_user($pl->owner)->usrname) if $$options{flesh_username};
228 __PACKAGE__->register_method(
229 method => 'retrieve_all_user_picklist',
230 api_name => 'open-ils.acq.picklist.user.all.retrieve',
233 desc => 'Retrieves all of the picklists a user is allowed to see',
235 {desc => 'Authentication token', type => 'string'},
236 {desc => 'Options, including "idlist", whch forces the return
237 of a list of IDs instead of objects', type => 'hash'},
239 return => {desc => 'Picklist objects on success, Event on error'}
243 sub retrieve_all_user_picklist {
244 my($self, $conn, $auth, $options) = @_;
245 my $e = new_editor(authtoken=>$auth);
246 return $e->event unless $e->checkauth;
248 my $my_list = $e->search_acq_picklist(
249 {owner=>$e->requestor->id, name=>{'!='=>''}}, {idlist=>1});
251 my $picklist_ids = $e->objects_allowed('VIEW_PICKLIST', 'acqpl');
252 my $p_orgs = $U->user_has_work_perm_at($e, 'VIEW_PICKLIST', {descendants =>1});
253 my $picklist_ids_2 = $e->search_acq_picklist(
254 {name=>{'!='=>''}, org_unit => $p_orgs}, {idlist=>1});
256 return undef unless @$my_list or @$picklist_ids or @$picklist_ids_2;
258 my @list = (@$my_list, @$picklist_ids, @$picklist_ids_2);
260 $dedup{$_} = 1 for @list;
263 return \@list if $$options{idlist};
266 my $picklist = $e->retrieve_acq_picklist($pl) or return $e->event;
267 $picklist->entry_count(retrieve_lineitem_count($e, $picklist->id))
268 if($$options{flesh_lineitem_count});
269 $picklist->owner($e->retrieve_actor_user($picklist->owner))
270 if $$options{flesh_owner};
271 $picklist->owner($e->retrieve_actor_user($picklist->owner)->usrname)
272 if $$options{flesh_username};
273 $conn->respond($picklist);
279 __PACKAGE__->register_method(
280 method => 'retrieve_pl_lineitem',
281 api_name => 'open-ils.acq.lineitem.picklist.retrieve',
284 desc => 'Retrieves lineitem objects according to picklist',
286 {desc => 'Authentication token', type => 'string'},
287 {desc => 'Picklist ID whose entries to retrieve', type => 'number'},
288 {desc => q/Options, including
289 "sort_attr", which defines the attribute to sort on;
290 "sort_attr_type", which defines the attribute type sort on;
291 "sort_dir", which defines the sort order between "asc" and "desc";
292 "limit", retrieval limit;
293 "offset", retrieval offset;
294 "idlist", return a list of IDs instead of objects
295 "flesh_attrs", additionaly return the list of flattened attributes
296 "clear_marc", discards the raw MARC data to reduce data size
297 "flesh_notes", flesh lineitem notes
298 "flesh_cancel_reason", flesh cancel_reason
302 return => {desc => 'Array of lineitem objects or IDs, on success, Event on error'}
307 my $PL_ENTRY_JSON_QUERY = {
308 select => {jub => ["id"], "acqlia" => ["attr_value"]},
313 "field" => "lineitem",
316 "attr_type" => "lineitem_marc_attr_definition",
317 "attr_name" => "author"
322 "order_by" => {"acqlia" => {"attr_value" => {"direction"=>"asc"}}},
324 "where" => {"+jub" => {"picklist"=>2}},
328 sub retrieve_pl_lineitem {
329 my($self, $conn, $auth, $picklist_id, $options) = @_;
330 my $e = new_editor(authtoken=>$auth);
331 return $e->event unless $e->checkauth;
333 # collect the retrieval options
334 my $sort_attr = $$options{sort_attr} || 'title';
335 my $sort_attr_type = $$options{sort_attr_type} || 'lineitem_marc_attr_definition';
336 my $sort_dir = $$options{sort_dir} || 'asc';
337 my $limit = $$options{limit} || 10;
338 my $offset = $$options{offset} || 0;
340 $PL_ENTRY_JSON_QUERY->{where}->{'+jub'}->{picklist} = $picklist_id;
341 $PL_ENTRY_JSON_QUERY->{from}->{jub}->{acqlia}->{filter}->{attr_name} = $sort_attr;
342 $PL_ENTRY_JSON_QUERY->{from}->{jub}->{acqlia}->{filter}->{attr_type} = $sort_attr_type;
343 $PL_ENTRY_JSON_QUERY->{order_by}->{acqlia}->{attr_value}->{direction} = $sort_dir;
344 $PL_ENTRY_JSON_QUERY->{limit} = $limit;
345 $PL_ENTRY_JSON_QUERY->{offset} = $offset;
347 my $entries = $e->json_query($PL_ENTRY_JSON_QUERY);
350 for my $entry (@$entries) {
351 push(@ids, $entry->{id}) unless grep { $_ eq $entry->{id} } @ids;
355 if($$options{idlist}) {
362 if($$options{flesh_attrs} or $$options{flesh_notes} or $$options{flesh_cancel_reason}) {
363 $flesh = {flesh => 2, flesh_fields => {jub => []}};
364 if($$options{flesh_notes}) {
365 push(@{$flesh->{flesh_fields}->{jub}}, 'lineitem_notes');
366 $flesh->{flesh_fields}->{acqlin} = ['alert_text'];
368 push(@{$flesh->{flesh_fields}->{jub}}, 'attributes') if $$options{flesh_attrs};
369 push @{$flesh->{flesh_fields}->{jub}}, 'cancel_reason' if $$options{flesh_cancel_reason};
372 $entry = $e->retrieve_acq_lineitem([$id, $flesh]);
373 my $details = $e->search_acq_lineitem_detail({lineitem => $id}, {idlist=>1});
374 $entry->item_count(scalar(@$details));
375 $entry->clear_marc if $$options{clear_marc};
376 $conn->respond($entry);
383 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}}
388 __PACKAGE__->register_method(
389 method => "record_distribution_formula_application",
390 api_name => "open-ils.acq.distribution_formula.record_application",
392 desc => "Record the application (which actually happens on the " .
393 "client side) of a distribution formula to a PO or a PL",
395 {desc => "Authentication token", type => "string"},
396 {desc => "Formulae applied", "type" => "array"},
397 {desc => "Lineitem ID", "type" => "number"}
399 return => {desc => "acqdfa IDs on success; event on failure"}
403 sub record_distribution_formula_application {
404 my ($self, $conn, $auth, $formulae, $li_id) = @_;
406 my $e = new_editor("authtoken" => $auth, "xact" => 1);
407 return $e->die_event unless $e->checkauth;
409 # We need this to determine relevant OU for testing permissions...
410 my $li = $e->retrieve_acq_lineitem([
413 "flesh_fields" => {"jub" => [qw/purchase_order picklist/]}
415 ]) or return $e->die_event;
417 # ... which we do here.
419 if ($li->purchase_order) {
420 $ou = $li->purchase_order->ordering_agency;
421 } elsif ($li->picklist) {
422 $ou = $li->picklist->org_unit;
425 return new OpenILS::Event("BAD_PARAMS");
428 return $e->die_event unless $e->allowed("CREATE_PURCHASE_ORDER", $ou);
430 # Just deal with it if $formulate is a scalar instead of an array.
431 $formulae = [ $formulae ] if not ref $formulae;
434 foreach (@{$formulae}) {
435 my $acqdfa = new Fieldmapper::acq::distribution_formula_application;
437 $acqdfa->creator($e->requestor->id);
438 $acqdfa->formula($_);
439 $acqdfa->lineitem($li_id);
441 $acqdfa = $e->create_acq_distribution_formula_application($acqdfa)
442 or return $e->die_event;
443 push @results, $acqdfa->id;
446 $e->commit or return $e->die_event;
451 __PACKAGE__->register_method(
452 method => 'ranged_distrib_formulas',
453 api_name => 'open-ils.acq.distribution_formula.ranged.retrieve',
456 desc => 'Ranged distribution formulas, fleshed with entries',
458 {desc => 'Authentication token', type => 'string'},
460 return => {desc => 'List of distribution formulas'}
464 sub ranged_distrib_formulas {
465 my($self, $conn, $auth, $org) = @_;
466 my $e = new_editor(authtoken=>$auth);
467 return $e->event unless $e->checkauth;
468 my $orgs = $U->user_has_work_perm_at($e, 'CREATE_PICKLIST', {descendants =>1});
470 my $forms = $e->search_acq_distribution_formula([
474 flesh_fields => {acqdf => ['entries']},
475 order_by => {acqdfe => ['position']}
481 # how many times has this DF been used
482 my $count = $e->json_query({
483 select => {acqdfa => [{column => 'formula', aggregate => 1, transform => 'count', alias => 'count'}]},
485 where => {formula => $_->id}
488 $_->use_count($count->{count});
495 __PACKAGE__->register_method(
496 method => "ranged_distrib_formula_applications",
497 api_name => "open-ils.acq.distribution_formula_application.ranged.retrieve",
500 desc => "Ranged distribution formulas applications, fleshed with formulas and users",
502 {desc => "Authentication token", type => "string"},
503 {desc => "Lineitem Id", type => "number"}
505 return => {desc => "List of distribution formula applications"}
509 sub ranged_distrib_formula_applications {
510 my ($self, $conn, $auth, $li_id) = @_;
512 my $e = new_editor("authtoken" => $auth);
513 return $e->event unless $e->checkauth;
515 my $li = $e->retrieve_acq_lineitem([
518 "flesh_fields" => {"jub" => [qw/purchase_order picklist/]}
520 ]) or return $e->die_event;
523 return $e->die_event unless $e->allowed(
524 "VIEW_PICKLIST", $li->picklist->org_unit
526 } elsif ($li->purchase_order) {
527 return $e->die_event unless $e->allowed(
528 "VIEW_PURCHASE_ORDER", $li->purchase_order->ordering_agency
531 # For the moment no use cases are forseen for using this
532 # method with LIs that don't belong to a PL or a PO.
534 return new OpenILS::Event("BAD_PARAMS", "note" => "Freestanding LI");
537 my $dfa = $e->search_acq_distribution_formula_application([
538 {"lineitem" => $li_id},
539 {"flesh" => 1, "flesh_fields" => {"acqdfa" => [qw/formula creator/]}}
542 $conn->respond($_) foreach (@$dfa);