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