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