Bug 5260: QA follow-up: Fix error when no notice template is defined
[srvgit] / 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');
112 if (!defined $op) {
113     $op = q{};
114 }
115
116 my $confirm_pref= C4::Context->preference("BasketConfirmations") || '1';
117 $template->param( skip_confirm_reopen => 1) if $confirm_pref eq '2';
118
119 $template->param( email_ok => 1 ) if defined $query->param('email_ok');
120 $template->param( email_error => $query->param('email_error') ) if defined $query->param('email_error');
121
122
123 if ( $op eq 'delete_confirm' ) {
124     my $basketno = $query->param('basketno');
125     my $delbiblio = $query->param('delbiblio');
126     my @orders = GetOrders($basketno);
127 #Delete all orders included in that basket, and all items received.
128     foreach my $myorder (@orders){
129         DelOrder($myorder->{biblionumber},$myorder->{ordernumber});
130     }
131 # if $delbiblio = 1, delete the records if possible
132     if ((defined $delbiblio)and ($delbiblio ==1)){
133         my @cannotdelbiblios ;
134         foreach my $myorder (@orders){
135             my $biblionumber = $myorder->{'biblionumber'};
136             my $countbiblio = CountBiblioInOrders($biblionumber);
137             my $ordernumber = $myorder->{'ordernumber'};
138             my $subscriptions = scalar GetSubscriptionsId ($biblionumber);
139             my $itemcount = GetItemsCount($biblionumber);
140             my $error;
141             if ($countbiblio == 0 && $itemcount == 0 && $subscriptions == 0) {
142                 $error = DelBiblio($myorder->{biblionumber}) }
143             else {
144                 push @cannotdelbiblios, {biblionumber=> ($myorder->{biblionumber}),
145                                          title=> $myorder->{'title'},
146                                          author=> $myorder->{'author'},
147                                          countbiblio=> $countbiblio,
148                                          itemcount=>$itemcount,
149                                          subscriptions=>$subscriptions};
150             }
151             if ($error) {
152                 push @cannotdelbiblios, {biblionumber=> ($myorder->{biblionumber}),
153                                          title=> $myorder->{'title'},
154                                          author=> $myorder->{'author'},
155                                          othererror=> $error};
156             }
157         }
158         $template->param( cannotdelbiblios => \@cannotdelbiblios );
159     }
160  # delete the basket
161     DelBasket($basketno,);
162     $template->param( delete_confirmed => 1 );
163 } elsif ( !$bookseller ) {
164     $template->param( NO_BOOKSELLER => 1 );
165 } elsif ($op eq 'export') {
166     print $query->header(
167         -type       => 'text/csv',
168         -attachment => 'basket' . $basket->{'basketno'} . '.csv',
169     );
170     print GetBasketAsCSV($query->param('basketno'), $query);
171     exit;
172 } elsif ($op eq 'email') {
173     my $redirect_url = '/cgi-bin/koha/acqui/basket.pl?basketno='.$basket->{'basketno'};
174     my $err;
175
176     eval {
177         $err = SendAlerts( 'orderacquisition', $query->param('basketno'), 'ACQORDER' );
178     };
179     if ( $@ ) {
180     $redirect_url .= '&email_error='.$@;
181     } elsif ( ref $err and exists $err->{error} ) {
182         $redirect_url .= '&email_error=' . $err->{error};
183     } else {
184         $redirect_url .= '&email_ok=1';
185     }
186
187     print $query->redirect($redirect_url)
188
189 } elsif ($op eq 'close') {
190     my $confirm = $query->param('confirm') || $confirm_pref eq '2';
191     if ($confirm) {
192         my $basketno = $query->param('basketno');
193         my $booksellerid = $query->param('booksellerid');
194         $basketno =~ /^\d+$/ and CloseBasket($basketno);
195         # if requested, create basket group, close it and attach the basket
196         if ($query->param('createbasketgroup')) {
197             my $branchcode;
198             if(C4::Context->userenv and C4::Context->userenv->{'branch'}
199               and C4::Context->userenv->{'branch'} ne "NO_LIBRARY_SET") {
200                 $branchcode = C4::Context->userenv->{'branch'};
201             }
202             my $basketgroupid = NewBasketgroup( { name => $basket->{basketname},
203                             booksellerid => $booksellerid,
204                             deliveryplace => $branchcode,
205                             billingplace => $branchcode,
206                             closed => 1,
207                             });
208             ModBasket( { basketno => $basketno,
209                          basketgroupid => $basketgroupid } );
210             print $query->redirect('/cgi-bin/koha/acqui/basketgroup.pl?booksellerid='.$booksellerid.'&closed=1');
211         } else {
212             print $query->redirect('/cgi-bin/koha/acqui/booksellers.pl?booksellerid=' . $booksellerid);
213         }
214         exit;
215     } else {
216     $template->param(
217         confirm_close   => "1",
218         booksellerid    => $booksellerid,
219         basketno        => $basket->{'basketno'},
220         basketname      => $basket->{'basketname'},
221         basketgroupname => $basket->{'basketname'},
222     );
223     }
224 } elsif ($op eq 'reopen') {
225     ReopenBasket($query->param('basketno'));
226     print $query->redirect('/cgi-bin/koha/acqui/basket.pl?basketno='.$basket->{'basketno'})
227 }
228 elsif ( $op eq 'ediorder' ) {
229     edi_close_and_order()
230 } elsif ( $op eq 'mod_users' ) {
231     my $basketusers_ids = $query->param('users_ids');
232     my @basketusers = split( /:/, $basketusers_ids );
233     ModBasketUsers($basketno, @basketusers);
234     print $query->redirect("/cgi-bin/koha/acqui/basket.pl?basketno=$basketno");
235     exit;
236 } elsif ( $op eq 'mod_branch' ) {
237     my $branch = $query->param('branch');
238     $branch = undef if(defined $branch and $branch eq '');
239     ModBasket({
240         basketno => $basket->{basketno},
241         branch   => $branch
242     });
243     print $query->redirect("/cgi-bin/koha/acqui/basket.pl?basketno=$basketno");
244     exit;
245 } else {
246     my @branches_loop;
247     # get librarian branch...
248     if ( C4::Context->preference("IndependentBranches") ) {
249         my $userenv = C4::Context->userenv;
250         unless ( C4::Context->IsSuperLibrarian() ) {
251             my $validtest = ( $basket->{creationdate} eq '' )
252               || ( $userenv->{branch} eq $basket->{branch} )
253               || ( $userenv->{branch} eq '' )
254               || ( $basket->{branch}  eq '' );
255             unless ($validtest) {
256                 print $query->redirect("../mainpage.pl");
257                 exit 0;
258             }
259         }
260
261         if (!defined $basket->{branch} or $basket->{branch} eq $userenv->{branch}) {
262             push @branches_loop, {
263                 branchcode => $userenv->{branch},
264                 branchname => $userenv->{branchname},
265                 selected => 1,
266             };
267         }
268     } else {
269         # get branches
270         my $branches = Koha::Libraries->search( {}, { order_by => ['branchname'] } )->unblessed;
271         foreach my $branch (@$branches) {
272             my $selected = 0;
273             if (defined $basket->{branch}) {
274                 $selected = 1 if $branch->{branchcode} eq $basket->{branch};
275             } else {
276                 $selected = 1 if $branch->{branchcode} eq C4::Context->userenv->{branch};
277             }
278             push @branches_loop, {
279                 branchcode => $branch->{branchcode},
280                 branchname => $branch->{branchname},
281                 selected => $selected
282             };
283         }
284     }
285
286 #if the basket is closed,and the user has the permission to edit basketgroups, display a list of basketgroups
287     my ($basketgroup, $basketgroups);
288     my $staffuser = GetMember(borrowernumber => $loggedinuser);
289     if ($basket->{closedate} && haspermission($staffuser->{userid}, { acquisition => 'group_manage'} )) {
290         $basketgroups = GetBasketgroups($basket->{booksellerid});
291         for my $bg ( @{$basketgroups} ) {
292             if ($basket->{basketgroupid} && $basket->{basketgroupid} == $bg->{id}){
293                 $bg->{default} = 1;
294                 $basketgroup = $bg;
295             }
296         }
297     }
298
299     # if the basket is closed, calculate estimated delivery date
300     my $estimateddeliverydate;
301     if( $basket->{closedate} ) {
302         my ($year, $month, $day) = ($basket->{closedate} =~ /(\d+)-(\d+)-(\d+)/);
303         ($year, $month, $day) = Add_Delta_Days($year, $month, $day, $bookseller->{deliverytime});
304         $estimateddeliverydate = sprintf( "%04d-%02d-%02d", $year, $month, $day );
305     }
306
307     # if new basket, pre-fill infos
308     $basket->{creationdate} = ""            unless ( $basket->{creationdate} );
309     $basket->{authorisedby} = $loggedinuser unless ( $basket->{authorisedby} );
310     $debug
311       and warn sprintf
312       "loggedinuser: $loggedinuser; creationdate: %s; authorisedby: %s",
313       $basket->{creationdate}, $basket->{authorisedby};
314
315     my @basketusers_ids = GetBasketUsers($basketno);
316     my @basketusers;
317     foreach my $basketuser_id (@basketusers_ids) {
318         my $basketuser = GetMember(borrowernumber => $basketuser_id);
319         push @basketusers, $basketuser if $basketuser;
320     }
321
322     my $active_currency = Koha::Acquisition::Currencies->get_active;
323
324     my @orders = GetOrders( $basketno );
325     my @books_loop;
326
327     my @book_foot_loop;
328     my %foot;
329     my $total_quantity = 0;
330     my $total_gste = 0;
331     my $total_gsti = 0;
332     my $total_gstvalue = 0;
333     for my $order (@orders) {
334         $order = C4::Acquisition::populate_order_with_prices({ order => $order, booksellerid => $booksellerid, ordering => 1 });
335         my $line = get_order_infos( $order, $bookseller);
336         if ( $line->{uncertainprice} ) {
337             $template->param( uncertainprices => 1 );
338         }
339
340         push @books_loop, $line;
341
342         $foot{$$line{gstrate}}{gstrate} = $$line{gstrate};
343         $foot{$$line{gstrate}}{gstvalue} += $$line{gstvalue};
344         $total_gstvalue += $$line{gstvalue};
345         $foot{$$line{gstrate}}{quantity}  += $$line{quantity};
346         $total_quantity += $$line{quantity};
347         $foot{$$line{gstrate}}{totalgste} += $$line{totalgste};
348         $total_gste += $$line{totalgste};
349         $foot{$$line{gstrate}}{totalgsti} += $$line{totalgsti};
350         $total_gsti += $$line{totalgsti};
351     }
352
353     push @book_foot_loop, map {$_} values %foot;
354
355     # Get cancelled orders
356     my @cancelledorders = GetOrders($basketno, { cancelled => 1 });
357     my @cancelledorders_loop;
358     for my $order (@cancelledorders) {
359         $order = C4::Acquisition::populate_order_with_prices({ order => $order, booksellerid => $booksellerid, ordering => 1 });
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_gste           => sprintf( "%.2f", $total_gste ),
412         total_gsti           => sprintf( "%.2f", $total_gsti ),
413         total_gstvalue       => sprintf( "%.2f", $total_gstvalue ),
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 sub get_order_infos {
431     my $order = shift;
432     my $bookseller = shift;
433     my $qty = $order->{'quantity'} || 0;
434     if ( !defined $order->{quantityreceived} ) {
435         $order->{quantityreceived} = 0;
436     }
437     my $budget = GetBudget($order->{budget_id});
438     my $basket = GetBasket($order->{basketno});
439
440     my %line = %{ $order };
441     # Don't show unreceived standing orders as received
442     $line{order_received} = ( $qty == $order->{'quantityreceived'} && ( $basket->{is_standing} ? $qty : 1 ) );
443     $line{basketno}       = $basketno;
444     $line{budget_name}    = $budget->{budget_name};
445
446     if ( $line{uncertainprice} ) {
447         $line{rrpgste} .= ' (Uncertain)';
448     }
449     if ( $line{'title'} ) {
450         my $volume      = $order->{'volume'};
451         my $seriestitle = $order->{'seriestitle'};
452         $line{'title'} .= " / $seriestitle" if $seriestitle;
453         $line{'title'} .= " / $volume"      if $volume;
454     }
455
456     my $biblionumber = $order->{'biblionumber'};
457     my $countbiblio = CountBiblioInOrders($biblionumber);
458     my $ordernumber = $order->{'ordernumber'};
459     my @subscriptions = GetSubscriptionsId ($biblionumber);
460     my $itemcount = GetItemsCount($biblionumber);
461     my $holds  = GetHolds ($biblionumber);
462     my @items = GetItemnumbersFromOrder( $ordernumber );
463     my $itemholds;
464     foreach my $item (@items){
465         my $nb = GetItemHolds($biblionumber, $item);
466         if ($nb){
467             $itemholds += $nb;
468         }
469     }
470     # 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
471     $line{can_del_bib}          = 1 if $countbiblio <= 1 && $itemcount == scalar @items && !(@subscriptions) && !($holds);
472     $line{items}                = ($itemcount) - (scalar @items);
473     $line{left_item}            = 1 if $line{items} >= 1;
474     $line{left_biblio}          = 1 if $countbiblio > 1;
475     $line{biblios}              = $countbiblio - 1;
476     $line{left_subscription}    = 1 if scalar @subscriptions >= 1;
477     $line{subscriptions}        = scalar @subscriptions;
478     ($holds >= 1) ? $line{left_holds} = 1 : $line{left_holds} = 0;
479     $line{left_holds_on_order}  = 1 if $line{left_holds}==1 && ($line{items} == 0 || $itemholds );
480     $line{holds}                = $holds;
481     $line{holds_on_order}       = $itemholds?$itemholds:$holds if $line{left_holds_on_order};
482
483
484     my $suggestion   = GetSuggestionInfoFromBiblionumber($line{biblionumber});
485     $line{suggestionid}         = $$suggestion{suggestionid};
486     $line{surnamesuggestedby}   = $$suggestion{surnamesuggestedby};
487     $line{firstnamesuggestedby} = $$suggestion{firstnamesuggestedby};
488
489     foreach my $key (qw(transferred_from transferred_to)) {
490         if ($line{$key}) {
491             my $order = GetOrder($line{$key});
492             my $bookseller = Koha::Acquisition::Bookseller->fetch({ id => $basket->{booksellerid} });
493             $line{$key} = {
494                 order => $order,
495                 basket => $basket,
496                 bookseller => $bookseller,
497                 timestamp => $line{$key . '_timestamp'},
498             };
499         }
500     }
501
502     return \%line;
503 }
504
505 output_html_with_http_headers $query, $cookie, $template->output;
506
507
508 sub edi_close_and_order {
509     my $confirm = $query->param('confirm') || $confirm_pref eq '2';
510     if ($confirm) {
511             my $edi_params = {
512                 basketno => $basketno,
513                 ean    => $ean,
514             };
515             if ( $basket->{branch} ) {
516                 $edi_params->{branchcode} = $basket->{branch};
517             }
518             if ( create_edi_order($edi_params) ) {
519                 #$template->param( edifile => 1 );
520             }
521         CloseBasket($basketno);
522
523         # if requested, create basket group, close it and attach the basket
524         if ( $query->param('createbasketgroup') ) {
525             my $branchcode;
526             if (    C4::Context->userenv
527                 and C4::Context->userenv->{'branch'}
528                 and C4::Context->userenv->{'branch'} ne "NO_LIBRARY_SET" )
529             {
530                 $branchcode = C4::Context->userenv->{'branch'};
531             }
532             my $basketgroupid = NewBasketgroup(
533                 {
534                     name          => $basket->{basketname},
535                     booksellerid  => $booksellerid,
536                     deliveryplace => $branchcode,
537                     billingplace  => $branchcode,
538                     closed        => 1,
539                 }
540             );
541             ModBasket(
542                 {
543                     basketno      => $basketno,
544                     basketgroupid => $basketgroupid
545                 }
546             );
547             print $query->redirect(
548 "/cgi-bin/koha/acqui/basketgroup.pl?booksellerid=$booksellerid&closed=1"
549             );
550         }
551         else {
552             print $query->redirect(
553                 "/cgi-bin/koha/acqui/booksellers.pl?booksellerid=$booksellerid"
554             );
555         }
556         exit;
557     }
558     else {
559         $template->param(
560             edi_confirm     => 1,
561             booksellerid    => $booksellerid,
562             basketno        => $basket->{basketno},
563             basketname      => $basket->{basketname},
564             basketgroupname => $basket->{basketname},
565         );
566         if ($ean) {
567             $template->param( ean => $ean );
568         }
569
570     }
571     return;
572 }