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