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