Bug 13321: Rename variables
[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->{total_tax_excluded} = Koha::Number::Price->new( $line->{ecost_tax_excluded} * $line->{quantity} )->format;
333         $line->{total_tax_included} = Koha::Number::Price->new( $line->{ecost_tax_included} * $line->{quantity} )->format;
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     if ( $line{uncertainprice} ) {
444         $line{rrp_tax_excluded} .= ' (Uncertain)';
445     }
446     if ( $line{'title'} ) {
447         my $volume      = $order->{'volume'};
448         my $seriestitle = $order->{'seriestitle'};
449         $line{'title'} .= " / $seriestitle" if $seriestitle;
450         $line{'title'} .= " / $volume"      if $volume;
451     }
452
453     my $biblionumber = $order->{'biblionumber'};
454     my $countbiblio = CountBiblioInOrders($biblionumber);
455     my $ordernumber = $order->{'ordernumber'};
456     my @subscriptions = GetSubscriptionsId ($biblionumber);
457     my $itemcount = GetItemsCount($biblionumber);
458     my $holds  = GetHolds ($biblionumber);
459     my @items = GetItemnumbersFromOrder( $ordernumber );
460     my $itemholds;
461     foreach my $item (@items){
462         my $nb = GetItemHolds($biblionumber, $item);
463         if ($nb){
464             $itemholds += $nb;
465         }
466     }
467     # 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
468     $line{can_del_bib}          = 1 if $countbiblio <= 1 && $itemcount == scalar @items && !(@subscriptions) && !($holds);
469     $line{items}                = ($itemcount) - (scalar @items);
470     $line{left_item}            = 1 if $line{items} >= 1;
471     $line{left_biblio}          = 1 if $countbiblio > 1;
472     $line{biblios}              = $countbiblio - 1;
473     $line{left_subscription}    = 1 if scalar @subscriptions >= 1;
474     $line{subscriptions}        = scalar @subscriptions;
475     ($holds >= 1) ? $line{left_holds} = 1 : $line{left_holds} = 0;
476     $line{left_holds_on_order}  = 1 if $line{left_holds}==1 && ($line{items} == 0 || $itemholds );
477     $line{holds}                = $holds;
478     $line{holds_on_order}       = $itemholds?$itemholds:$holds if $line{left_holds_on_order};
479
480
481     my $suggestion   = GetSuggestionInfoFromBiblionumber($line{biblionumber});
482     $line{suggestionid}         = $$suggestion{suggestionid};
483     $line{surnamesuggestedby}   = $$suggestion{surnamesuggestedby};
484     $line{firstnamesuggestedby} = $$suggestion{firstnamesuggestedby};
485
486     foreach my $key (qw(transferred_from transferred_to)) {
487         if ($line{$key}) {
488             my $order = GetOrder($line{$key});
489             my $bookseller = Koha::Acquisition::Bookseller->fetch({ id => $basket->{booksellerid} });
490             $line{$key} = {
491                 order => $order,
492                 basket => $basket,
493                 bookseller => $bookseller,
494                 timestamp => $line{$key . '_timestamp'},
495             };
496         }
497     }
498
499     return \%line;
500 }
501
502 sub edi_close_and_order {
503     my $confirm = $query->param('confirm') || $confirm_pref eq '2';
504     if ($confirm) {
505             my $edi_params = {
506                 basketno => $basketno,
507                 ean    => $ean,
508             };
509             if ( $basket->{branch} ) {
510                 $edi_params->{branchcode} = $basket->{branch};
511             }
512             if ( create_edi_order($edi_params) ) {
513                 #$template->param( edifile => 1 );
514             }
515         CloseBasket($basketno);
516
517         # if requested, create basket group, close it and attach the basket
518         if ( $query->param('createbasketgroup') ) {
519             my $branchcode;
520             if (    C4::Context->userenv
521                 and C4::Context->userenv->{'branch'}
522                 and C4::Context->userenv->{'branch'} ne "NO_LIBRARY_SET" )
523             {
524                 $branchcode = C4::Context->userenv->{'branch'};
525             }
526             my $basketgroupid = NewBasketgroup(
527                 {
528                     name          => $basket->{basketname},
529                     booksellerid  => $booksellerid,
530                     deliveryplace => $branchcode,
531                     billingplace  => $branchcode,
532                     closed        => 1,
533                 }
534             );
535             ModBasket(
536                 {
537                     basketno      => $basketno,
538                     basketgroupid => $basketgroupid
539                 }
540             );
541             print $query->redirect(
542 "/cgi-bin/koha/acqui/basketgroup.pl?booksellerid=$booksellerid&closed=1"
543             );
544         }
545         else {
546             print $query->redirect(
547                 "/cgi-bin/koha/acqui/booksellers.pl?booksellerid=$booksellerid"
548             );
549         }
550         exit;
551     }
552     else {
553         $template->param(
554             edi_confirm     => 1,
555             booksellerid    => $booksellerid,
556             basketno        => $basket->{basketno},
557             basketname      => $basket->{basketname},
558             basketgroupname => $basket->{basketname},
559         );
560         if ($ean) {
561             $template->param( ean => $ean );
562         }
563
564     }
565     return;
566 }