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