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