Bug 17182: (QA follow-up) Fix call to GetMarcBiblio
[koha.git] / acqui / basket.pl
1 #!/usr/bin/perl
2
3 #script to show display basket of orders
4
5 # Copyright 2000 - 2004 Katipo
6 # Copyright 2008 - 2009 BibLibre SARL
7 #
8 # This file is part of Koha.
9 #
10 # Koha is free software; you can redistribute it and/or modify it
11 # under the terms of the GNU General Public License as published by
12 # the Free Software Foundation; either version 3 of the License, or
13 # (at your option) any later version.
14 #
15 # Koha is distributed in the hope that it will be useful, but
16 # WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
19 #
20 # You should have received a copy of the GNU General Public License
21 # along with Koha; if not, see <http://www.gnu.org/licenses>.
22
23 use Modern::Perl;
24 use C4::Auth;
25 use C4::Koha;
26 use C4::Output;
27 use CGI qw ( -utf8 );
28 use C4::Acquisition;
29 use C4::Budgets;
30 use C4::Contract;
31 use C4::Debug;
32 use C4::Biblio;
33 use C4::Items;
34 use C4::Suggestions;
35 use Koha::Biblios;
36 use Koha::Acquisition::Booksellers;
37 use Koha::Libraries;
38 use C4::Letters qw/SendAlerts/;
39 use Date::Calc qw/Add_Delta_Days/;
40 use Koha::Database;
41 use Koha::EDI qw( create_edi_order get_edifact_ean );
42 use Koha::CsvProfiles;
43 use Koha::Patrons;
44
45 =head1 NAME
46
47 basket.pl
48
49 =head1 DESCRIPTION
50
51  This script display all informations about basket for the supplier given
52  on input arg.  Moreover, it allows us to add a new order for this supplier from
53  an existing record, a suggestion or a new record.
54
55 =head1 CGI PARAMETERS
56
57 =over 4
58
59 =item $basketno
60
61 The basket number.
62
63 =item booksellerid
64
65 the supplier this script have to display the basket.
66
67 =item order
68
69 =back
70
71 =cut
72
73 our $query        = new CGI;
74 our $basketno     = $query->param('basketno');
75 our $ean          = $query->param('ean');
76 our $booksellerid = $query->param('booksellerid');
77 my $duplinbatch =  $query->param('duplinbatch');
78
79 our ( $template, $loggedinuser, $cookie, $userflags ) = get_template_and_user(
80     {
81         template_name   => "acqui/basket.tt",
82         query           => $query,
83         type            => "intranet",
84         authnotrequired => 0,
85         flagsrequired   => { acquisition => 'order_manage' },
86         debug           => 1,
87     }
88 );
89
90 our $basket = GetBasket($basketno);
91 $booksellerid = $basket->{booksellerid} unless $booksellerid;
92 my $bookseller = Koha::Acquisition::Booksellers->find( $booksellerid );
93 my $schema = Koha::Database->new()->schema();
94 my $rs = $schema->resultset('VendorEdiAccount')->search(
95     { vendor_id => $booksellerid, } );
96 $template->param( ediaccount => ($rs->count > 0));
97
98 unless (CanUserManageBasket($loggedinuser, $basket, $userflags)) {
99     $template->param(
100         cannot_manage_basket => 1,
101         basketno => $basketno,
102         basketname => $basket->{basketname},
103         booksellerid => $booksellerid,
104         booksellername => $bookseller->name,
105     );
106     output_html_with_http_headers $query, $cookie, $template->output;
107     exit;
108 }
109
110 # FIXME : what about the "discount" percentage?
111 # FIXME : the query->param('booksellerid') below is probably useless. The bookseller is always known from the basket
112 # if no booksellerid in parameter, get it from basket
113 # warn "=>".$basket->{booksellerid};
114 my $op = $query->param('op') // 'list';
115
116 our $confirm_pref= C4::Context->preference("BasketConfirmations") || '1';
117 $template->param( skip_confirm_reopen => 1) if $confirm_pref eq '2';
118
119 my @messages;
120
121 if ( $op eq 'delete_confirm' ) {
122     my $basketno = $query->param('basketno');
123     my $delbiblio = $query->param('delbiblio');
124     my @orders = GetOrders($basketno);
125 #Delete all orders included in that basket, and all items received.
126     foreach my $myorder (@orders){
127         DelOrder($myorder->{biblionumber},$myorder->{ordernumber});
128     }
129 # if $delbiblio = 1, delete the records if possible
130     if ((defined $delbiblio)and ($delbiblio ==1)){
131         my @cannotdelbiblios ;
132         foreach my $myorder (@orders){
133             my $biblionumber = $myorder->{'biblionumber'};
134             my $biblio = Koha::Biblios->find( $biblionumber );
135             my $countbiblio = CountBiblioInOrders($biblionumber);
136             my $ordernumber = $myorder->{'ordernumber'};
137             my $cnt_subscriptions = $biblio->subscriptions->count;
138             my $itemcount = $biblio->items->count;
139             my $error;
140             if ($countbiblio == 0 && $itemcount == 0 && not $cnt_subscriptions ) {
141                 $error = DelBiblio($myorder->{biblionumber}) }
142             else {
143                 push @cannotdelbiblios, {biblionumber=> ($myorder->{biblionumber}),
144                                          title=> $myorder->{'title'},
145                                          author=> $myorder->{'author'},
146                                          countbiblio=> $countbiblio,
147                                          itemcount=>$itemcount,
148                                          subscriptions => $cnt_subscriptions};
149             }
150             if ($error) {
151                 push @cannotdelbiblios, {biblionumber=> ($myorder->{biblionumber}),
152                                          title=> $myorder->{'title'},
153                                          author=> $myorder->{'author'},
154                                          othererror=> $error};
155             }
156         }
157         $template->param( cannotdelbiblios => \@cannotdelbiblios );
158     }
159  # delete the basket
160     DelBasket($basketno,);
161     $template->param(
162         delete_confirmed => 1,
163         booksellername => $bookseller->name,
164         booksellerid => $booksellerid,
165     );
166 } elsif ( !$bookseller ) {
167     $template->param( NO_BOOKSELLER => 1 );
168 } elsif ($op eq 'export') {
169     print $query->header(
170         -type       => 'text/csv',
171         -attachment => 'basket' . $basket->{'basketno'} . '.csv',
172     );
173     if ( $query->param('csv_profile') eq 'default'){
174         print GetBasketAsCSV($query->param('basketno'), $query);
175     } else {
176         my $csv_profile_id = $query->param('csv_profile');
177         print  GetBasketAsCSV($query->param('basketno'), $query, $csv_profile_id);
178     }
179     exit;
180 } elsif ($op eq 'email') {
181     my $err = eval {
182         SendAlerts( 'orderacquisition', $query->param('basketno'), 'ACQORDER' );
183     };
184     if ( $@ ) {
185         push @messages, { type => 'error', code => $@ };
186     } elsif ( ref $err and exists $err->{error} ) {
187         push @messages, { type => 'error', code => $err->{error} };
188     } else {
189         push @messages, { type => 'message', code => 'email_sent' };
190     }
191
192     $op = 'list';
193 } elsif ($op eq 'close') {
194     my $confirm = $query->param('confirm') || $confirm_pref eq '2';
195     if ($confirm) {
196         my $basketno = $query->param('basketno');
197         my $booksellerid = $query->param('booksellerid');
198         $basketno =~ /^\d+$/ and CloseBasket($basketno);
199         # if requested, create basket group, close it and attach the basket
200         if ($query->param('createbasketgroup')) {
201             my $branchcode;
202             if(C4::Context->userenv and C4::Context->userenv->{'branch'}
203               and C4::Context->userenv->{'branch'} ne "NO_LIBRARY_SET") {
204                 $branchcode = C4::Context->userenv->{'branch'};
205             }
206             my $basketgroupid = NewBasketgroup( { name => $basket->{basketname},
207                             booksellerid => $booksellerid,
208                             deliveryplace => $branchcode,
209                             billingplace => $branchcode,
210                             closed => 1,
211                             });
212             ModBasket( { basketno => $basketno,
213                          basketgroupid => $basketgroupid } );
214             print $query->redirect('/cgi-bin/koha/acqui/basketgroup.pl?booksellerid='.$booksellerid.'&closed=1');
215         } else {
216             print $query->redirect('/cgi-bin/koha/acqui/booksellers.pl?booksellerid=' . $booksellerid);
217         }
218         exit;
219     } else {
220     $template->param(
221         confirm_close   => "1",
222         booksellerid    => $booksellerid,
223         booksellername  => $bookseller->name,
224         basketno        => $basket->{'basketno'},
225         basketname      => $basket->{'basketname'},
226         basketgroupname => $basket->{'basketname'},
227     );
228     }
229 } elsif ($op eq 'reopen') {
230     ReopenBasket(scalar $query->param('basketno'));
231     print $query->redirect('/cgi-bin/koha/acqui/basket.pl?basketno='.$basket->{'basketno'})
232 }
233 elsif ( $op eq 'ediorder' ) {
234     edi_close_and_order()
235 } elsif ( $op eq 'mod_users' ) {
236     my $basketusers_ids = $query->param('users_ids');
237     my @basketusers = split( /:/, $basketusers_ids );
238     ModBasketUsers($basketno, @basketusers);
239     print $query->redirect("/cgi-bin/koha/acqui/basket.pl?basketno=$basketno");
240     exit;
241 } elsif ( $op eq 'mod_branch' ) {
242     my $branch = $query->param('branch');
243     $branch = undef if(defined $branch and $branch eq '');
244     ModBasket({
245         basketno => $basket->{basketno},
246         branch   => $branch
247     });
248     print $query->redirect("/cgi-bin/koha/acqui/basket.pl?basketno=$basketno");
249     exit;
250 }
251
252 if ( $op eq 'list' ) {
253     my @branches_loop;
254     # get librarian branch...
255     if ( C4::Context->preference("IndependentBranches") ) {
256         my $userenv = C4::Context->userenv;
257         unless ( C4::Context->IsSuperLibrarian() ) {
258             my $validtest = ( $basket->{creationdate} eq '' )
259               || ( $userenv->{branch} eq $basket->{branch} )
260               || ( $userenv->{branch} eq '' )
261               || ( $basket->{branch}  eq '' );
262             unless ($validtest) {
263                 print $query->redirect("../mainpage.pl");
264                 exit 0;
265             }
266         }
267
268         if (!defined $basket->{branch} or $basket->{branch} eq $userenv->{branch}) {
269             push @branches_loop, {
270                 branchcode => $userenv->{branch},
271                 branchname => $userenv->{branchname},
272                 selected => 1,
273             };
274         }
275     } else {
276         # get branches
277         my $branches = Koha::Libraries->search( {}, { order_by => ['branchname'] } )->unblessed;
278         foreach my $branch (@$branches) {
279             my $selected = 0;
280             if (defined $basket->{branch}) {
281                 $selected = 1 if $branch->{branchcode} eq $basket->{branch};
282             } else {
283                 $selected = 1 if $branch->{branchcode} eq C4::Context->userenv->{branch};
284             }
285             push @branches_loop, {
286                 branchcode => $branch->{branchcode},
287                 branchname => $branch->{branchname},
288                 selected => $selected
289             };
290         }
291     }
292
293 #if the basket is closed,and the user has the permission to edit basketgroups, display a list of basketgroups
294     my ($basketgroup, $basketgroups);
295     my $patron = Koha::Patrons->find($loggedinuser);
296     if ($basket->{closedate} && haspermission($patron->userid, { acquisition => 'group_manage'} )) {
297         $basketgroups = GetBasketgroups($basket->{booksellerid});
298         for my $bg ( @{$basketgroups} ) {
299             if ($basket->{basketgroupid} && $basket->{basketgroupid} == $bg->{id}){
300                 $bg->{default} = 1;
301                 $basketgroup = $bg;
302             }
303         }
304     }
305
306     # if the basket is closed, calculate estimated delivery date
307     my $estimateddeliverydate;
308     if( $basket->{closedate} ) {
309         my ($year, $month, $day) = ($basket->{closedate} =~ /(\d+)-(\d+)-(\d+)/);
310         ($year, $month, $day) = Add_Delta_Days($year, $month, $day, $bookseller->deliverytime);
311         $estimateddeliverydate = sprintf( "%04d-%02d-%02d", $year, $month, $day );
312     }
313
314     # if new basket, pre-fill infos
315     $basket->{creationdate} = ""            unless ( $basket->{creationdate} );
316     $basket->{authorisedby} = $loggedinuser unless ( $basket->{authorisedby} );
317     $debug
318       and warn sprintf
319       "loggedinuser: $loggedinuser; creationdate: %s; authorisedby: %s",
320       $basket->{creationdate}, $basket->{authorisedby};
321
322     my @basketusers_ids = GetBasketUsers($basketno);
323     my @basketusers;
324     foreach my $basketuser_id (@basketusers_ids) {
325         # FIXME Could be improved with a search -in
326         my $basket_patron = Koha::Patrons->find( $basketuser_id );
327         push @basketusers, $basket_patron if $basket_patron;
328     }
329
330     my $active_currency = Koha::Acquisition::Currencies->get_active;
331
332     my @orders = GetOrders( $basketno );
333     my @books_loop;
334
335     my @book_foot_loop;
336     my %foot;
337     my $total_quantity = 0;
338     my $total_tax_excluded = 0;
339     my $total_tax_included = 0;
340     my $total_tax_value = 0;
341     for my $order (@orders) {
342         my $line = get_order_infos( $order, $bookseller);
343         if ( $line->{uncertainprice} ) {
344             $template->param( uncertainprices => 1 );
345         }
346
347         $line->{tax_rate} = $line->{tax_rate_on_ordering};
348         $line->{tax_value} = $line->{tax_value_on_ordering};
349
350         push @books_loop, $line;
351
352         $foot{$$line{tax_rate}}{tax_rate} = $$line{tax_rate};
353         $foot{$$line{tax_rate}}{tax_value} += $$line{tax_value};
354         $total_tax_value += $$line{tax_value};
355         $foot{$$line{tax_rate}}{quantity}  += $$line{quantity};
356         $total_quantity += $$line{quantity};
357         $foot{$$line{tax_rate}}{total_tax_excluded} += $$line{total_tax_excluded};
358         $total_tax_excluded += $$line{total_tax_excluded};
359         $foot{$$line{tax_rate}}{total_tax_included} += $$line{total_tax_included};
360         $total_tax_included += $$line{total_tax_included};
361     }
362
363     push @book_foot_loop, map {$_} values %foot;
364
365     # Get cancelled orders
366     my @cancelledorders = GetOrders($basketno, { cancelled => 1 });
367     my @cancelledorders_loop;
368     for my $order (@cancelledorders) {
369         my $line = get_order_infos( $order, $bookseller);
370         push @cancelledorders_loop, $line;
371     }
372
373     my $contract = GetContract({
374         contractnumber => $basket->{contractnumber}
375     });
376
377     if ($basket->{basketgroupid}){
378         $basketgroup = GetBasketgroup($basket->{basketgroupid});
379     }
380     my $budgets = GetBudgetHierarchy;
381     my $has_budgets = 0;
382     foreach my $r (@{$budgets}) {
383         if (!defined $r->{budget_amount} || $r->{budget_amount} == 0) {
384             next;
385         }
386         next unless (CanUserUseBudget($loggedinuser, $r, $userflags));
387
388         $has_budgets = 1;
389         last;
390     }
391
392     $template->param(
393         basketno             => $basketno,
394         basket               => $basket,
395         basketname           => $basket->{'basketname'},
396         basketbranchcode     => $basket->{branch},
397         basketnote           => $basket->{note},
398         basketbooksellernote => $basket->{booksellernote},
399         basketcontractno     => $basket->{contractnumber},
400         basketcontractname   => $contract->{contractname},
401         branches_loop        => \@branches_loop,
402         creationdate         => $basket->{creationdate},
403         authorisedby         => $basket->{authorisedby},
404         authorisedbyname     => $basket->{authorisedbyname},
405         users_ids            => join(':', @basketusers_ids),
406         users                => \@basketusers,
407         closedate            => $basket->{closedate},
408         estimateddeliverydate=> $estimateddeliverydate,
409         is_standing          => $basket->{is_standing},
410         deliveryplace        => $basket->{deliveryplace},
411         billingplace         => $basket->{billingplace},
412         active               => $bookseller->active,
413         booksellerid         => $bookseller->id,
414         booksellername       => $bookseller->name,
415         books_loop           => \@books_loop,
416         book_foot_loop       => \@book_foot_loop,
417         cancelledorders_loop => \@cancelledorders_loop,
418         total_quantity       => $total_quantity,
419         total_tax_excluded   => $total_tax_excluded,
420         total_tax_included   => $total_tax_included,
421         total_tax_value      => $total_tax_value,
422         currency             => $active_currency->currency,
423         listincgst           => $bookseller->listincgst,
424         basketgroups         => $basketgroups,
425         basketgroup          => $basketgroup,
426         grouped              => $basket->{basketgroupid},
427         # The double negatives and booleans here mean:
428         # "A basket cannot be closed if there are no orders in it or it's a standing order basket."
429         #
430         # (The template has another implicit restriction that the order cannot be closed if there
431         # are any orders with uncertain prices.)
432         unclosable           => @orders ? $basket->{is_standing} : 1,
433         has_budgets          => $has_budgets,
434         duplinbatch          => $duplinbatch,
435         csv_profiles         => [ Koha::CsvProfiles->search({ type => 'sql', used_for => 'export_basket' }) ],
436     );
437 }
438
439 $template->param( messages => \@messages );
440 output_html_with_http_headers $query, $cookie, $template->output;
441
442 sub get_order_infos {
443     my $order = shift;
444     my $bookseller = shift;
445     my $qty = $order->{'quantity'} || 0;
446     if ( !defined $order->{quantityreceived} ) {
447         $order->{quantityreceived} = 0;
448     }
449     my $budget = GetBudget($order->{budget_id});
450     my $basket = GetBasket($order->{basketno});
451
452     my %line = %{ $order };
453     # Don't show unreceived standing orders as received
454     $line{order_received} = ( $qty == $order->{'quantityreceived'} && ( $basket->{is_standing} ? $qty : 1 ) );
455     $line{basketno}       = $basketno;
456     $line{budget_name}    = $budget->{budget_name};
457
458     $line{total_tax_included} = $line{ecost_tax_included} * $line{quantity};
459     $line{total_tax_excluded} = $line{ecost_tax_excluded} * $line{quantity};
460     $line{tax_value} = $line{tax_value_on_ordering};
461     $line{tax_rate} = $line{tax_rate_on_ordering};
462
463     if ( $line{uncertainprice} ) {
464         $line{rrp_tax_excluded} .= ' (Uncertain)';
465     }
466     if ( $line{'title'} ) {
467         my $volume      = $order->{'volume'};
468         my $seriestitle = $order->{'seriestitle'};
469         $line{'title'} .= " / $seriestitle" if $seriestitle;
470         $line{'title'} .= " / $volume"      if $volume;
471     }
472
473     my $biblionumber = $order->{'biblionumber'};
474     if ( $biblionumber ) { # The biblio still exists
475         my $biblio = Koha::Biblios->find( $biblionumber );
476         my $countbiblio = CountBiblioInOrders($biblionumber);
477         my $ordernumber = $order->{'ordernumber'};
478         my $cnt_subscriptions = $biblio->subscriptions->count;
479         my $itemcount   = $biblio->items->count;
480         my $holds_count = $biblio->holds->count;
481         my @items = GetItemnumbersFromOrder( $ordernumber );
482         my $itemholds  = $biblio->holds->search({ itemnumber => { -in => \@items } })->count;
483
484         # if the biblio is not in other orders and if there is no items elsewhere and no subscriptions and no holds we can then show the link "Delete order and Biblio" see bug 5680
485         $line{can_del_bib}          = 1 if $countbiblio <= 1 && $itemcount == scalar @items && !($cnt_subscriptions) && !($holds_count);
486         $line{items}                = ($itemcount) - (scalar @items);
487         $line{left_item}            = 1 if $line{items} >= 1;
488         $line{left_biblio}          = 1 if $countbiblio > 1;
489         $line{biblios}              = $countbiblio - 1;
490         $line{left_subscription}    = 1 if $cnt_subscriptions;
491         $line{subscriptions}        = $cnt_subscriptions;
492         ($holds_count >= 1) ? $line{left_holds} = 1 : $line{left_holds} = 0;
493         $line{left_holds_on_order}  = 1 if $line{left_holds}==1 && ($line{items} == 0 || $itemholds );
494         $line{holds}                = $holds_count;
495         $line{holds_on_order}       = $itemholds?$itemholds:$holds_count if $line{left_holds_on_order};
496     }
497
498
499     my $suggestion   = GetSuggestionInfoFromBiblionumber($line{biblionumber});
500     $line{suggestionid}         = $$suggestion{suggestionid};
501     $line{surnamesuggestedby}   = $$suggestion{surnamesuggestedby};
502     $line{firstnamesuggestedby} = $$suggestion{firstnamesuggestedby};
503
504     foreach my $key (qw(transferred_from transferred_to)) {
505         if ($line{$key}) {
506             my $order = GetOrder($line{$key});
507             my $basket = GetBasket($order->{basketno});
508             my $bookseller = Koha::Acquisition::Booksellers->find( $basket->{booksellerid} );
509             $line{$key} = {
510                 order => $order,
511                 basket => $basket,
512                 bookseller => $bookseller,
513                 timestamp => $line{$key . '_timestamp'},
514             };
515         }
516     }
517
518     return \%line;
519 }
520
521 sub edi_close_and_order {
522     my $confirm = $query->param('confirm') || $confirm_pref eq '2';
523     if ($confirm) {
524             my $edi_params = {
525                 basketno => $basketno,
526                 ean    => $ean,
527             };
528             if ( $basket->{branch} ) {
529                 $edi_params->{branchcode} = $basket->{branch};
530             }
531             if ( create_edi_order($edi_params) ) {
532                 #$template->param( edifile => 1 );
533             }
534         CloseBasket($basketno);
535
536         # if requested, create basket group, close it and attach the basket
537         if ( $query->param('createbasketgroup') ) {
538             my $branchcode;
539             if (    C4::Context->userenv
540                 and C4::Context->userenv->{'branch'}
541                 and C4::Context->userenv->{'branch'} ne "NO_LIBRARY_SET" )
542             {
543                 $branchcode = C4::Context->userenv->{'branch'};
544             }
545             my $basketgroupid = NewBasketgroup(
546                 {
547                     name          => $basket->{basketname},
548                     booksellerid  => $booksellerid,
549                     deliveryplace => $branchcode,
550                     billingplace  => $branchcode,
551                     closed        => 1,
552                 }
553             );
554             ModBasket(
555                 {
556                     basketno      => $basketno,
557                     basketgroupid => $basketgroupid
558                 }
559             );
560             print $query->redirect(
561 "/cgi-bin/koha/acqui/basketgroup.pl?booksellerid=$booksellerid&closed=1"
562             );
563         }
564         else {
565             print $query->redirect(
566                 "/cgi-bin/koha/acqui/booksellers.pl?booksellerid=$booksellerid"
567             );
568         }
569         exit;
570     }
571     else {
572         $template->param(
573             edi_confirm     => 1,
574             booksellerid    => $booksellerid,
575             basketno        => $basket->{basketno},
576             basketname      => $basket->{basketname},
577             basketgroupname => $basket->{basketname},
578         );
579         if ($ean) {
580             $template->param( ean => $ean );
581         }
582
583     }
584     return;
585 }