]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Acq/Lineitem.pm
8bc40dfc06495a98e0ae694a5d8cf6f3c110290c
[working/Evergreen.git] / Open-ILS / src / perlmods / OpenILS / Application / Acq / Lineitem.pm
1 package OpenILS::Application::Acq::Lineitem;
2 use base qw/OpenILS::Application/;
3 use strict; use warnings;
4
5 use OpenILS::Event;
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::Application::AppUtils;
12 use OpenILS::Application::Acq::Financials;
13 use OpenILS::Application::Cat::BibCommon;
14 use OpenILS::Application::Cat::AssetCommon;
15 my $U = 'OpenILS::Application::AppUtils';
16
17
18 __PACKAGE__->register_method(
19         method => 'create_lineitem',
20         api_name        => 'open-ils.acq.lineitem.create',
21         signature => {
22         desc => 'Creates a lineitem',
23         params => [
24             {desc => 'Authentication token', type => 'string'},
25             {desc => 'The lineitem object to create', type => 'object'},
26         ],
27         return => {desc => 'ID of newly created lineitem on success, Event on error'}
28     }
29 );
30
31 sub create_lineitem {
32     my($self, $conn, $auth, $li) = @_;
33     my $e = new_editor(xact=>1, authtoken=>$auth);
34     return $e->die_event unless $e->checkauth;
35
36
37     if($li->picklist) {
38         my $picklist = $e->retrieve_acq_picklist($li->picklist)
39             or return $e->die_event;
40
41         if($picklist->owner != $e->requestor->id) {
42             return $e->die_event unless 
43                 $e->allowed('CREATE_PICKLIST', $picklist->org_unit, $picklist);
44         }
45     
46         # indicate the picklist was updated
47         $picklist->edit_time('now');
48         $picklist->editor($e->requestor->id);
49         $e->update_acq_picklist($picklist) or return $e->die_event;
50     }
51
52     if($li->purchase_order) {
53         my $po = $e->retrieve_acq_purchase_order($li->purchase_order)
54             or return $e->die_event;
55         return $e->die_event unless 
56             $e->allowed('MANAGE_PROVIDER', $po->ordering_agency, $po);
57
58         $li->provider($po->provider) unless defined $li->provider;
59     }
60
61     $li->selector($e->requestor->id);
62     $e->create_acq_lineitem($li) or return $e->die_event;
63
64     $e->commit;
65     return $li->id;
66 }
67
68
69 __PACKAGE__->register_method(
70         method => 'retrieve_lineitem',
71         api_name        => 'open-ils.acq.lineitem.retrieve',
72         signature => {
73         desc => 'Retrieves a lineitem',
74         params => [
75             {desc => 'Authentication token',    type => 'string'},
76             {desc => 'lineitem ID to retrieve', type => 'number'},
77             {options => q/Hash of options, including:
78 flesh_attrs         : for attributes,
79 flesh_notes         : for notes,
80 flesh_cancel_reason : for cancel reason,
81 flesh_li_details    : for order details objects,
82 clear_marc          : to clear marcxml from lineitem/, type => 'hash'},
83         ],
84         return => {desc => 'lineitem object on success, Event on error'}
85     }
86 );
87
88
89 sub retrieve_lineitem {
90     my($self, $conn, $auth, $li_id, $options) = @_;
91     my $e = new_editor(authtoken=>$auth);
92     return $e->die_event unless $e->checkauth;
93     return retrieve_lineitem_impl($e, $li_id, $options);
94 }
95
96 sub retrieve_lineitem_impl {
97     my ($e, $li_id, $options) = @_;
98     $options ||= {};
99
100     my $flesh = {
101         flesh => 3,
102         flesh_fields => {
103             jub => ['purchase_order', 'picklist'], # needed for permission check
104             acqlid => [],
105             acqlin => []
106         }
107     };
108
109     my $fields = $flesh->{flesh_fields};
110
111     push(@{$fields->{jub}   },    'attributes') if $$options{flesh_attrs};
112     push(@{$fields->{jub}   },'lineitem_notes') if $$options{flesh_notes};
113     push(@{$fields->{acqlin}},    'alert_text') if $$options{flesh_notes};
114     push(@{$fields->{jub}   }, 'order_summary') if $$options{flesh_order_summary};
115     push(@{$fields->{acqlin}}, 'cancel_reason') if $$options{flesh_cancel_reason};
116
117     if($$options{flesh_li_details}) {
118         push(@{$fields->{jub}   }, 'lineitem_details');
119         push(@{$fields->{acqlid}}, 'fund'         ) if $$options{flesh_fund};
120         push(@{$fields->{acqlid}}, 'fund_debit'   ) if $$options{flesh_fund_debit};
121         push(@{$fields->{acqlid}}, 'cancel_reason') if $$options{flesh_cancel_reason};
122     }
123
124     if($$options{clear_marc}) { # avoid fetching marc blob
125         my @fields = grep { $_ ne 'marc' } Fieldmapper::acq::lineitem->new->real_fields;
126         $flesh->{select} = {jub => [@fields]};
127     }
128
129     my $li = $e->retrieve_acq_lineitem([$li_id, $flesh]) or return $e->event;
130
131     # collect the # of lids
132     if($$options{flesh_li_details}) {
133         $li->item_count(scalar(@{$li->lineitem_details}));
134     } else {
135         my $details = $e->search_acq_lineitem_detail({lineitem => $li_id}, {idlist=>1});
136         $li->item_count(scalar(@$details));
137     }
138
139     # attach claims to LIDs
140     if($$options{flesh_li_details}) {
141         foreach (@{$li->lineitem_details}) {
142             $_->claims(
143                 $e->search_acq_claim([
144                     {"lineitem_detail", $_->id}, {
145                         "flesh" => 1, "flesh_fields" => {"acqcl" => ["type"]}
146                     }
147                 ])
148             );
149         }
150     }
151
152     return $e->event unless (
153         $li->purchase_order and 
154             $e->allowed(['VIEW_PURCHASE_ORDER', 'CREATE_PURCHASE_ORDER'], 
155                 $li->purchase_order->ordering_agency, $li->purchase_order)
156     ) or (
157         $li->picklist and !$li->purchase_order and # user doesn't have view_po perms
158             $e->allowed(['VIEW_PICKLIST', 'CREATE_PICKLIST'], 
159                 $li->picklist->org_unit, $li->picklist)
160     );
161
162     unless ($$options{flesh_po}) {
163         $li->purchase_order(
164             $li->purchase_order ? $li->purchase_order->id : undef
165         );
166     }
167     unless ($$options{flesh_pl}) {
168         $li->picklist($li->picklist ? $li->picklist->id : undef);
169     }
170     return $li;
171 }
172
173
174
175 __PACKAGE__->register_method(
176         method => 'delete_lineitem',
177         api_name        => 'open-ils.acq.lineitem.delete',
178         signature => {
179         desc => 'Deletes a lineitem',
180         params => [
181             {desc => 'Authentication token', type => 'string'},
182             {desc => 'lineitem ID to delete', type => 'number'},
183         ],
184         return => {desc => '1 on success, Event on error'}
185     }
186 );
187
188 sub delete_lineitem {
189     my($self, $conn, $auth, $li_id) = @_;
190     my $e = new_editor(xact=>1, authtoken=>$auth);
191     return $e->die_event unless $e->checkauth;
192
193     my $li = $e->retrieve_acq_lineitem($li_id)
194         or return $e->die_event;
195
196     # XXX check state
197
198     if($li->picklist) {
199         my $picklist = $e->retrieve_acq_picklist($li->picklist)
200             or return $e->die_event;
201         return OpenILS::Event->new('BAD_PARAMS') 
202             if $picklist->owner != $e->requestor->id;
203     } else {
204         # check PO perms
205     }
206
207     # delete the attached lineitem_details
208     my $lid_ids = $e->search_acq_lineitem_detail(
209         {lineitem => $li_id}, {idlist=>1});
210
211     for my $lid_id (@$lid_ids) {
212         $e->delete_acq_lineitem_detail(
213             $e->retrieve_acq_lineitem_detail($lid_id))
214             or return $e->die_event;
215     }
216
217     $e->delete_acq_lineitem($li) or return $e->die_event;
218     $e->commit;
219     return 1;
220 }
221
222
223 __PACKAGE__->register_method(
224         method => 'update_lineitem',
225         api_name        => 'open-ils.acq.lineitem.update',
226         signature => {
227         desc => 'Update one or many lineitems',
228         params => [
229             {desc => 'Authentication token', type => 'string'},
230             {desc => 'lineitem object update', type => 'object'}
231         ],
232         return => {desc => '1 on success, Event on error'}
233     }
234 );
235
236 sub update_lineitem {
237     my($self, $conn, $auth, $li) = @_;
238     my $e = new_editor(xact=>1, authtoken=>$auth);
239     return $e->die_event unless $e->checkauth;
240
241     $li = [$li] unless ref $li eq "ARRAY";
242     foreach (@$li) {
243         my $evt = update_lineitem_impl($e, $_);
244         return $evt if $evt;
245     }
246
247     $e->commit;
248     return 1;
249 }
250
251 sub update_lineitem_impl {
252     my($e, $li) = @_;
253
254     my $orig_li = $e->retrieve_acq_lineitem([
255         $li->id,
256         {   flesh => 1, # grab the lineitem with picklist attached
257             flesh_fields => {jub => ['picklist', 'purchase_order']}
258         }
259     ]) or return $e->die_event;
260
261     # the marc may have been cleared on retrieval...
262     $li->marc($orig_li->marc) unless $li->marc;
263
264     $li->editor($e->requestor->id);
265     $li->edit_time('now');
266     $e->update_acq_lineitem($li) or return $e->die_event;
267     return undef;
268 }
269
270 __PACKAGE__->register_method(
271         method => 'lineitem_search',
272         api_name => 'open-ils.acq.lineitem.search',
273     stream => 1,
274         signature => {
275         desc => 'Searches lineitems',
276         params => [
277             {desc => 'Authentication token',       type => 'string'},
278             {desc => 'Search definition',          type => 'object'},
279             {desc => 'Options hash.  idlist=true', type => 'object'},
280             {desc => 'List of lineitems',          type => 'object/number'},
281         ]
282     }
283 );
284
285 sub lineitem_search {
286     my($self, $conn, $auth, $search, $options) = @_;
287     my $e = new_editor(authtoken=>$auth);
288     return $e->event unless $e->checkauth;
289     return $e->event unless $e->allowed('CREATE_PICKLIST');
290     # XXX needs permissions consideration
291     my $lis = $e->search_acq_lineitem($search, {idlist=>1});
292     for my $li_id (@$lis) {
293         if($$options{idlist}) {
294             $conn->respond($li_id);
295         } else {
296             my $res = retrieve_lineitem($self, $conn, $auth, $li_id, $options);
297             $conn->respond($res) unless $U->event_code($res);
298         }
299     }
300     return undef;
301 }
302
303 __PACKAGE__->register_method (
304     method    => 'lineitems_related_by_bib',
305     api_name  => 'open-ils.acq.lineitems_for_bib.by_bib_id',
306     stream    => 1,
307     signature => q/
308         Retrieves lineitems attached to same bib record, subject to the PO ordering agency.  This variant takes the bib id.
309         @param authtoken Login session key
310         @param bib_id Id for the pertinent bib record.
311         @param options Object for tweaking the selection criteria and fleshing options.
312     /
313 );
314
315 __PACKAGE__->register_method (
316     method    => 'lineitems_related_by_bib',
317     api_name  => 'open-ils.acq.lineitems_for_bib.by_lineitem_id',
318     stream    => 1,
319     signature => q/
320         Retrieves lineitems attached to same bib record, subject to the PO ordering agency.  This variant takes the id for any of the pertinent lineitems.
321         @param authtoken Login session key
322         @param bib_id Id for a pertinent lineitem.
323         @param options Object for tweaking the selection criteria and fleshing options.
324     /
325 );
326
327 __PACKAGE__->register_method (
328     method    => 'lineitems_related_by_bib',
329     api_name  => 'open-ils.acq.lineitems_for_bib.by_lineitem_id.count',
330     stream    => 1,
331     signature => q/See open-ils.acq.lineitems_for_bib.by_lineitem_id. This version returns numbers of lineitems only (XXX may count lineitems we don't actually have permission to retrieve)/
332 );
333
334 sub lineitems_related_by_bib {
335     my($self, $conn, $auth, $test_value, $options) = @_;
336     my $e = new_editor(authtoken => $auth);
337     return $e->event unless $e->checkauth;
338
339     my $perm_orgs = $U->user_has_work_perm_at($e, 'VIEW_PURCHASE_ORDER', {descendants =>1}, $e->requestor->id);
340
341     my $query = {
342         "select"=>{"jub"=>["id"]},
343         "from"=>{"jub" => {"acqpo" => {type => 'left'}, "acqpl" => {type => 'left'}}}, 
344         "where"=>{
345             '-or' => [
346                 { "+acqpo"=>{ "ordering_agency" => $perm_orgs } },
347                 { '+acqpl' => { org_unit => $perm_orgs } }
348             ]
349         },
350         "order_by"=>[{"class"=>"jub", "field"=>"create_time", "direction"=>"desc"}]
351     };
352
353     # Be sure we just return the original LI if no related bibs
354     if ($self->api_name =~ /by_lineitem_id/) {
355         my $orig = retrieve_lineitem($self, $conn, $auth, $test_value) or
356             return $e->die_event;
357         if ($test_value = $orig->eg_bib_id) {
358             $query->{"where"}->{"eg_bib_id"} = $test_value;
359         } else {
360             $query->{"where"}->{"id"} = $orig->id;
361         }
362     } elsif ($test_value) {
363         $query->{"where"}->{"eg_bib_id"} = $test_value;
364     } else {
365         $e->disconnect;
366         return new OpenILS::Event("BAD_PARAMS", "Null bib id");
367     }
368
369     if ($options && defined $options->{lineitem_state}) {
370         $query->{'where'}{'jub'}{'state'} = $options->{lineitem_state};
371     }
372
373     if ($options && defined $options->{po_state}) {
374         $query->{'where'}{'+acqpo'}{'state'} = $options->{po_state};
375     }
376
377     if ($options && defined $options->{order_by}) {
378         $query->{'order_by'} = $options->{order_by};
379     }
380
381     my $results = $e->json_query($query);
382     if ($self->api_name =~ /count$/) {
383         return scalar(@$results);
384     } else {
385         for my $result (@$results) {
386             # retrieve_lineitem takes care of POs and PLs and also handles
387             # options like flesh_notes and permissions checking.
388             $conn->respond(
389                 retrieve_lineitem($self, $conn, $auth, $result->{"id"}, $options)
390             );
391         }
392     }
393
394     return undef;
395 }
396
397
398 __PACKAGE__->register_method(
399     method    => "lineitem_search_by_attributes",
400     api_name  => "open-ils.acq.lineitem.search.by_attributes",
401     stream    => 1,
402     signature => {
403         desc   => "Performs a search against lineitem_attrs",
404         params => [
405             {desc => "Authentication token", type => "string"},
406             {   desc => q/
407 Search definition:
408     attr_value_pairs : list of pairs of (attr definition ID, attr value) where value can be scalar (fuzzy match) or array (exact match)
409     li_states        : list of lineitem states
410     po_agencies      : list of purchase order ordering agencies (org) ids
411
412 At least one of these search terms is required.
413                 /,
414                 type => "object"},
415             {   desc => q/
416 Options hash:
417     idlist      : if set, only return lineitem IDs
418     clear_marc  : if set, strip the MARC xml from the lineitem before delivery
419     flesh_attrs : flesh lineitem attributes;
420                 /,
421                 type => "object"}
422         ]
423     }
424 );
425
426 __PACKAGE__->register_method(
427     method    => "lineitem_search_by_attributes",
428     api_name  => "open-ils.acq.lineitem.search.by_attributes.ident",
429     stream    => 1,
430     signature => {
431         desc => "Performs a search against lineitem_attrs where ident is true.  ".
432             "See open-ils.acq.lineitem.search.by_attributes for params."
433     }
434 );
435
436 sub lineitem_search_by_attributes {
437     my ($self, $conn, $auth, $search, $options) = @_;
438
439     my $e = new_editor(authtoken => $auth, xact => 1);
440     return $e->die_event unless $e->checkauth;
441     # XXX needs permissions consideration
442
443     return [] unless $search;
444     my $attr_value_pairs = $search->{attr_value_pairs};
445     my $li_states = $search->{li_states};
446     my $po_agencies = $search->{po_agencies}; # XXX if none, base it on perms
447
448     my $query = {
449         "select" => {"acqlia" =>
450             [{"column" => "lineitem", "transform" => "distinct"}]
451         },
452         "from" => {
453             "acqlia" => {
454                 "acqliad" => {"field" => "id", "fkey" => "definition"},
455                 "jub" => {
456                     "field" => "id",
457                     "fkey" => "lineitem",
458                     "join" => {
459                         "acqpo" => {
460                             "type" => "left",
461                             "field" => "id",
462                             "fkey" => "purchase_order"
463                         }
464                     }
465                 }
466             }
467         }
468     };
469
470     my $where = {};
471     $where->{"+acqliad"} = {"ident" => "t"}
472         if $self->api_name =~ /\.ident/;
473
474     my $searched_for_something = 0;
475
476     if (ref $attr_value_pairs eq "ARRAY") {
477         $where->{"-or"} = [];
478         foreach (@$attr_value_pairs) {
479             next if @$_ != 2;
480             my ($def, $value) = @$_;
481             push @{$where->{"-or"}}, {
482                 "-and" => {
483                     "attr_value" => (ref $value) ?
484                         $value : {"ilike" => "%" . $value . "%"},
485                     "definition" => $def
486                 }
487             };
488         }
489         $searched_for_something = 1;
490     }
491
492     if ($li_states and @$li_states) {
493         $where->{"+jub"} = {"state" => $li_states};
494         $searched_for_something = 1;
495     }
496
497     if ($po_agencies and @$po_agencies) {
498         $where->{"+acqpo"} = {"ordering_agency" => $po_agencies};
499         $searched_for_something = 1;
500     }
501
502     if (not $searched_for_something) {
503         $e->rollback;
504         return new OpenILS::Event(
505             "BAD_PARAMS", note => "You have provided no search terms."
506         );
507     }
508
509     $query->{"where"} = $where;
510     my $lis = $e->json_query($query);
511
512     for my $li_id_obj (@$lis) {
513         my $li_id = $li_id_obj->{"lineitem"};
514         if($options->{"idlist"}) {
515             $conn->respond($li_id);
516         } else {
517             $conn->respond(
518                 retrieve_lineitem($self, $conn, $auth, $li_id, $options)
519             );
520         }
521     }
522     undef;
523 }
524
525
526 __PACKAGE__->register_method(
527         method    => 'lineitem_search_ident',
528         api_name  => 'open-ils.acq.lineitem.search.ident',
529     stream    => 1,
530         signature => {
531         desc => 'Performs a search against lineitem_attrs where ident is true',
532         params => [
533             {desc => 'Authentication token', type => 'string'},
534             {   desc => q/Search definition. Options are:
535                    attr_values : list of attribute values (required)
536                    li_states : list of lineitem states
537                    po_agencies : list of purchase order ordering agencies (org) ids
538                 /,
539                 type => 'object',
540             },
541             {   desc => q/
542                     Options hash.  Options are:
543                         idlist : if set, only return lineitem IDs
544                         clear_marc : if set, strip the MARC xml from the lineitem before delivery
545                         flesh_attrs : flesh lineitem attributes; 
546                 /,
547                 type => 'object',
548             }
549         ]
550     }
551 );
552
553 my $LI_ATTR_SEARCH = {
554     select => { acqlia => ['lineitem'] },
555     from   => {
556         acqlia => {
557             acqliad => {
558                 field => 'id',
559                 fkey  => 'definition'
560             },
561             jub => {
562                 field => 'id',
563                 fkey  => 'lineitem',
564                 join  => {
565                     acqpo => {
566                         field => 'id',
567                         fkey  => 'purchase_order'
568                     }
569                 }
570             }
571         }
572     }
573 };
574
575 sub lineitem_search_ident {
576     my($self, $conn, $auth, $search, $options) = @_;
577     my $e = new_editor(authtoken=>$auth, xact=>1);
578     return $e->event unless $e->checkauth;
579     # XXX needs permissions consideration
580
581     return [] unless $search;
582     my $attr_values = $search->{attr_values};
583     my $li_states = $search->{li_states};
584     my $po_agencies = $search->{po_agencies}; # XXX if none, base it on perms
585
586     my $where_clause = {
587         '-or' => [],
588         '+acqlia' => {
589             '+acqliad' => {ident => 't'},
590         }
591     };
592
593     push(@{$where_clause->{'-or'}}, {attr_value => {ilike => "%$_%"}}) for @$attr_values;
594
595     $where_clause->{'+jub'} = {state => {in => $li_states}}
596         if $li_states and @$li_states;
597
598     $where_clause->{'+acqpo'} = {ordering_agency => $po_agencies} 
599         if $po_agencies and @$po_agencies;
600
601     $LI_ATTR_SEARCH->{where} = $where_clause;
602
603     my $lis = $e->json_query($LI_ATTR_SEARCH);
604
605     for my $li_id_obj (@$lis) {
606         my $li_id = $li_id_obj->{lineitem};
607         if($$options{idlist}) {
608             $conn->respond($li_id);
609         } else {
610             my $li;
611             if($$options{flesh_attrs}) {
612                 $li = $e->retrieve_acq_lineitem([
613                     $li_id, {flesh => 1, flesh_fields => {jub => ['attributes']}}])
614             } else {
615                 $li = $e->retrieve_acq_lineitem($li_id);
616             }
617             $li->clear_marc if $$options{clear_marc};
618             $conn->respond($li);
619         }
620     }
621     return undef;
622 }
623
624
625
626
627 # this call duplicates a call in Order.pm and makes references to subs that don't exist.  
628 # TODO: Verify then remove.
629 __PACKAGE__->register_method(
630         method    => 'lineitem_detail_CUD_batch',
631         api_name  => 'open-ils.acq.lineitem_detail.cud.batch_',
632     stream    => 1,
633         signature => {
634         desc => q/Creates a new purchase order line item detail.  
635             Additionally creates the associated fund_debit/,
636         params => [
637             {desc => 'Authentication token', type => 'string'},
638             {desc => 'List of lineitem_details to create', type => 'array'},
639         ],
640         return => {desc => 'Streaming response of current position in the array'}
641     }
642 );
643
644 sub lineitem_detail_CUD_batch {
645     my($self, $conn, $auth, $li_details, $options) = @_;
646     my $e = new_editor(xact=>1, authtoken=>$auth);
647     return $e->die_event unless $e->checkauth;
648     my $pos = 0;
649     my $total = scalar(@$li_details);
650     for my $li_detail (@$li_details) {
651         my $res;
652
653         use Data::Dumper;
654         $logger->info(Dumper($li_detail));
655         $logger->info('lid id ' . $li_detail->id);
656         $logger->info('lineitem ' . $li_detail->lineitem);
657
658         if($li_detail->isnew) {
659             $res = create_lineitem_detail_impl($self, $conn, $e, $li_detail, $options);
660         } elsif($li_detail->ischanged) {
661             $res = update_lineitem_detail_impl($self, $conn, $e, $li_detail);
662         } elsif($li_detail->isdeleted) {
663             $res = delete_lineitem_detail_impl($self, $conn, $e, $li_detail->id);
664         }
665         return $e->event if $e->died;
666         $conn->respond({maximum => $total, progress => $pos++, li => $res});
667     }
668     $e->commit;
669     return {complete => 1};
670 }
671
672
673 sub create_lineitem_detail_impl {
674     my($self, $conn, $e, $li_detail, $options) = @_;
675     $options ||= {};
676
677     my $li = $e->retrieve_acq_lineitem($li_detail->lineitem)
678         or return $e->die_event;
679
680     my $evt = update_li_edit_time($e, $li);
681     return $evt if $evt;
682
683     # XXX check lineitem provider perms
684
685     if($li_detail->fund) {
686         my $fund = $e->retrieve_acq_fund($li_detail->fund) or return $e->die_event;
687         return $e->die_event unless 
688             $e->allowed('MANAGE_FUND', $fund->org, $fund);
689     }
690
691     $e->create_acq_lineitem_detail($li_detail) or return $e->die_event;
692
693     unless($li_detail->barcode) {
694         my $pfx = $U->ou_ancestor_setting_value($li_detail->owning_lib, 'acq.tmp_barcode_prefix') || 'ACQ';
695         $li_detail->barcode($pfx.$li_detail->id);
696     }
697     unless($li_detail->cn_label) {
698         my $pfx = $U->ou_ancestor_setting_value($li_detail->owning_lib, 'acq.tmp_callnumber_prefix') || 'ACQ';
699         $li_detail->cn_label($pfx.$li_detail->id);
700     }
701
702     if(my $loc = $U->ou_ancestor_setting_value($li_detail->owning_lib, 'acq.default_copy_location')) {
703         $li_detail->location($loc);
704     }
705
706     $e->update_acq_lineitem_detail($li_detail) or return $e->die_event;
707
708     return $li_detail if $$options{return_obj};
709     return $li_detail->id
710 }
711
712
713 sub update_li_edit_time {
714     my ($e, $li) = @_;
715     # some lineitem edits are allowed after approval time...
716 #    return OpenILS::Event->new('ACQ_LINEITEM_APPROVED', payload => $li->id)
717 #        if $li->state eq 'approved';
718     $li->edit_time('now');
719     $li->editor($e->requestor->id);
720     $e->update_acq_lineitem($li) or return $e->die_event;
721     return undef;
722 }
723
724
725 __PACKAGE__->register_method(
726     method    => 'retrieve_lineitem_detail',
727     api_name  => 'open-ils.acq.lineitem_detail.retrieve',
728     signature => {
729         desc   => q/Updates a lineitem detail/,
730         params => [
731             { desc => 'Authentication token',              type => 'string' },
732             { desc => 'id of lineitem_detail to retrieve', type => 'number' },
733         ],
734         return => { desc => 'object on success, Event on failure' }
735     }
736 );
737 sub retrieve_lineitem_detail {
738     my($self, $conn, $auth, $li_detail_id) = @_;
739     my $e = new_editor(authtoken=>$auth);
740     return $e->event unless $e->checkauth;
741
742     my $li_detail = $e->retrieve_acq_lineitem_detail($li_detail_id)
743         or return $e->event;
744
745     if($li_detail->fund) {
746         my $fund = $e->retrieve_acq_fund($li_detail->fund) or return $e->event;
747         return $e->event unless 
748             $e->allowed('MANAGE_FUND', $fund->org, $fund);
749     }
750
751     # XXX check lineitem perms
752     return $li_detail;
753 }
754
755
756 __PACKAGE__->register_method(
757     method    => 'approve_lineitem',
758     api_name  => 'open-ils.acq.lineitem.approve',
759     signature => {
760         desc   => 'Mark a lineitem as approved',
761         params => [
762             { desc => 'Authentication token', type => 'string' },
763             { desc => 'lineitem ID',          type => 'number' }
764         ],
765         return => { desc => '1 on success, Event on error' }
766     }
767 );
768 sub approve_lineitem {
769     my($self, $conn, $auth, $li_id) = @_;
770     my $e = new_editor(xact=>1, authtoken=>$auth);
771     return $e->die_event unless $e->checkauth;
772
773     # XXX perm checks for each lineitem detail
774
775     my $li = $e->retrieve_acq_lineitem($li_id)
776         or return $e->die_event;
777
778     return OpenILS::Event->new('ACQ_LINEITEM_APPROVED', payload => $li_id)
779         if $li->state eq 'approved';
780
781     my $details = $e->search_acq_lineitem_detail({lineitem => $li_id});
782     return OpenILS::Event->new('ACQ_LINEITEM_NO_COPIES', payload => $li_id)
783         unless scalar(@$details) > 0;
784
785     for my $detail (@$details) {
786         return OpenILS::Event->new('ACQ_LINEITEM_DETAIL_NO_FUND', payload => $detail->id)
787             unless $detail->fund;
788
789         return OpenILS::Event->new('ACQ_LINEITEM_DETAIL_NO_ORG', payload => $detail->id)
790             unless $detail->owning_lib;
791     }
792     
793     $li->state('approved');
794     $li->edit_time('now');
795     $e->update_acq_lineitem($li) or return $e->die_event;
796
797     $e->commit;
798     return 1;
799 }
800
801
802
803 __PACKAGE__->register_method(
804     method    => 'set_lineitem_attr',
805     api_name  => 'open-ils.acq.lineitem_usr_attr.set',
806     signature => {
807         desc   => 'Sets a lineitem_usr_attr value',
808         params => [
809             { desc => 'Authentication token', type => 'string' },
810             { desc => 'Lineitem ID',          type => 'number' },
811             { desc => 'Attr name',            type => 'string' },
812             { desc => 'Attr value',           type => 'string' }
813         ],
814         return => { desc => '1 on success, Event on error' }
815     }
816 );
817
818 __PACKAGE__->register_method(
819     method    => 'set_lineitem_attr',
820     api_name  => 'open-ils.acq.lineitem_local_attr.set',
821     signature => {
822         desc   => 'Sets a lineitem_local_attr value',
823         params => [
824             { desc => 'Authentication token', type => 'string' },
825             { desc => 'Lineitem ID',          type => 'number' },
826             { desc => 'Attr name',            type => 'string' },
827             { desc => 'Attr value',           type => 'string' }
828         ],
829         return => { desc => 'ID of the attr object on success, Event on error' }
830     }
831 );
832
833
834 sub set_lineitem_attr {
835     my($self, $conn, $auth, $li_id, $attr_name, $attr_value) = @_;
836     my $e = new_editor(xact=>1, authtoken=>$auth);
837     return $e->die_event unless $e->checkauth;
838
839     # XXX perm
840
841     my $attr_type = $self->api_name =~ /local_attr/ ?
842         'lineitem_local_attr_definition' : 'lineitem_usr_attr_definition';
843
844     my $attr = $e->search_acq_lineitem_attr({
845         lineitem => $li_id, 
846         attr_type => $attr_type,
847         attr_name => $attr_name})->[0];
848
849     my $find = "search_acq_$attr_type";
850
851     if($attr) {
852         $attr->attr_value($attr_value);
853         $e->update_acq_lineitem_attr($attr) or return $e->die_event;
854     } else {
855         $attr = Fieldmapper::acq::lineitem_attr->new;
856         $attr->lineitem($li_id);
857         $attr->attr_type($attr_type);
858         $attr->attr_name($attr_name);
859         $attr->attr_value($attr_value);
860
861         my $attr_def_id = $e->$find({code => $attr_name}, {idlist=>1})->[0] 
862             or return $e->die_event;
863         $attr->definition($attr_def_id);
864         $e->create_acq_lineitem_attr($attr) or return $e->die_event;
865     }
866
867     $e->commit;
868     return $attr->id;
869 }
870
871 __PACKAGE__->register_method(
872     method    => 'get_lineitem_attr_defs',
873     api_name  => 'open-ils.acq.lineitem_attr_definition.retrieve.all',
874     signature => {
875         desc   => 'Retrieve lineitem attr definitions',
876         params => [ { desc => 'Authentication token', type => 'string' }, ],
877         return => { desc => 'List of attr definitions' }
878     }
879 );
880
881 sub get_lineitem_attr_defs {
882     my($self, $conn, $auth) = @_;
883     my $e = new_editor(authtoken=>$auth);
884     return $e->event unless $e->checkauth;
885     my %results;
886     for my $type (qw/generated marc local usr provider/) {
887         my $call = "retrieve_all_acq_lineitem_${type}_attr_definition";
888         $results{$type} = $e->$call;
889     }
890     return \%results;
891 }
892
893
894 __PACKAGE__->register_method(
895     method    => 'lineitem_note_CUD_batch',
896     api_name  => 'open-ils.acq.lineitem_note.cud.batch',
897     stream    => 1,
898     signature => {
899         desc   => q/Manage lineitem notes/,
900         params => [
901             { desc => 'Authentication token',             type => 'string' },
902             { desc => 'List of lineitem_notes to manage', type => 'array'  },
903         ],
904         return =>
905           { desc => 'Streaming response of current position in the array' }
906     }
907 );
908
909 sub lineitem_note_CUD_batch {
910     my($self, $conn, $auth, $li_notes) = @_;
911
912     my $e = new_editor(xact=>1, authtoken=>$auth);
913     return $e->die_event unless $e->checkauth;
914     # XXX perms
915
916     my $total = @$li_notes;
917     my $count = 0;
918
919     for my $note (@$li_notes) {
920
921         $note->editor($e->requestor->id);
922         $note->edit_time('now');
923
924         if($note->isnew) {
925             $note->creator($e->requestor->id);
926             $note = $e->create_acq_lineitem_note($note) or return $e->die_event;
927
928         } elsif($note->isdeleted) {
929             $e->delete_acq_lineitem_note($note) or return $e->die_event;
930
931         } elsif($note->ischanged) {
932             $e->update_acq_lineitem_note($note) or return $e->die_event;
933         }
934
935         if(!$note->isdeleted) {
936             $note = $e->retrieve_acq_lineitem_note([
937                 $note->id, {
938                     "flesh" => 1, "flesh_fields" => {"acqlin" => ["alert_text"]}
939                 }
940             ]);
941         }
942
943         $conn->respond({maximum => $total, progress => ++$count, note => $note});
944     }
945
946     $e->commit;
947     return {complete => 1};
948 }
949
950 __PACKAGE__->register_method(
951     method => 'ranged_line_item_alert_text',
952     api_name => 'open-ils.acq.line_item_alert_text.ranged.retrieve.all');   # TODO: signature
953
954 sub ranged_line_item_alert_text {
955     my($self, $conn, $auth, $org_id, $depth) = @_;
956     my $e = new_editor(authtoken => $auth);
957     return $e->event unless $e->checkauth;
958     return $e->event unless $e->allowed('ADMIN_ACQ_LINEITEM_ALERT_TEXT', $org_id);
959     return $e->search_acq_lineitem_alert_text(
960         {owning_lib => $U->get_org_full_path($org_id, $depth)});
961 }
962
963
964 __PACKAGE__->register_method(
965     method => "retrieve_lineitem_by_copy_id",
966     api_name => "open-ils.acq.lineitem.retrieve.by_copy_id",
967     signature => {
968         desc => q/Manage lineitem notes/,
969         params => [
970             {desc => "Authentication token", type => "string"},
971             {desc => "Evergreen internal copy ID", type => "number"},
972             {desc => "Hash of options (see open-ils.acq.lineitem.retrieve",
973                 type => "object"}
974         ],
975         return => {
976             desc => "Lineitem associated with given copy",
977             type => "object", class => "jub"
978         }
979     }
980 );
981
982 sub retrieve_lineitem_by_copy_id {
983     my ($self, $conn, $auth, $object_id, $options) = @_;
984
985     my $e = new_editor("authtoken" => $auth);
986     return $e->die_event unless $e->checkauth;
987
988     my $result = $e->json_query({
989         "select" => {"acqlid" => ["lineitem"]},
990         "from" => "acqlid",
991         "where" => {"eg_copy_id" => $object_id}
992     })->[0] or do {
993         $e->disconnect;
994         return new OpenILS::Event("ACQ_LINEITEM_NOT_FOUND");
995     };
996
997     my $li = retrieve_lineitem_impl($e, $result->{"lineitem"}, $options) or
998         return $e->die_event;
999
1000     $e->disconnect;
1001     return $li;
1002 }
1003
1004 1;