Bug 7791: (follow-up) add warning after deletion if some records were not deleted
[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 under the
11 # terms of the GNU General Public License as published by the Free Software
12 # Foundation; either version 2 of the License, or (at your option) any later
13 # version.
14 #
15 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
16 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
17 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
18 #
19 # You should have received a copy of the GNU General Public License along
20 # with Koha; if not, write to the Free Software Foundation, Inc.,
21 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22
23 use strict;
24 use warnings;
25 use C4::Auth;
26 use C4::Koha;
27 use C4::Output;
28 use CGI;
29 use C4::Acquisition;
30 use C4::Budgets;
31 use C4::Branch;
32 use C4::Bookseller qw( GetBookSellerFromId);
33 use C4::Debug;
34 use C4::Biblio;
35 use C4::Members qw/GetMember/;  #needed for permissions checking for changing basketgroup of a basket
36 use C4::Items;
37 use C4::Suggestions;
38 use Date::Calc qw/Add_Delta_Days/;
39
40 =head1 NAME
41
42 basket.pl
43
44 =head1 DESCRIPTION
45
46  This script display all informations about basket for the supplier given
47  on input arg.  Moreover, it allows us to add a new order for this supplier from
48  an existing record, a suggestion or a new record.
49
50 =head1 CGI PARAMETERS
51
52 =over 4
53
54 =item $basketno
55
56 The basket number.
57
58 =item booksellerid
59
60 the supplier this script have to display the basket.
61
62 =item order
63
64 =back
65
66 =cut
67
68 my $query        = new CGI;
69 our $basketno     = $query->param('basketno');
70 my $booksellerid = $query->param('booksellerid');
71
72 my ( $template, $loggedinuser, $cookie, $userflags ) = get_template_and_user(
73     {
74         template_name   => "acqui/basket.tmpl",
75         query           => $query,
76         type            => "intranet",
77         authnotrequired => 0,
78         flagsrequired   => { acquisition => 'order_manage' },
79         debug           => 1,
80     }
81 );
82
83 my $basket = GetBasket($basketno);
84 $booksellerid = $basket->{booksellerid} unless $booksellerid;
85 my ($bookseller) = GetBookSellerFromId($booksellerid);
86
87 unless (CanUserManageBasket($loggedinuser, $basket, $userflags)) {
88     $template->param(
89         cannot_manage_basket => 1,
90         basketno => $basketno,
91         basketname => $basket->{basketname},
92         booksellerid => $booksellerid,
93         name => $bookseller->{name}
94     );
95     output_html_with_http_headers $query, $cookie, $template->output;
96     exit;
97 }
98
99 # FIXME : what about the "discount" percentage?
100 # FIXME : the query->param('booksellerid') below is probably useless. The bookseller is always known from the basket
101 # if no booksellerid in parameter, get it from basket
102 # warn "=>".$basket->{booksellerid};
103 my $op = $query->param('op');
104 if (!defined $op) {
105     $op = q{};
106 }
107
108 my $confirm_pref= C4::Context->preference("BasketConfirmations") || '1';
109 $template->param( skip_confirm_reopen => 1) if $confirm_pref eq '2';
110
111 if ( $op eq 'delete_confirm' ) {
112     my $basketno = $query->param('basketno');
113     my $delbiblio = $query->param('delbiblio');
114     my @orders = GetOrders($basketno);
115 #Delete all orders included in that basket, and all items received.
116     foreach my $myorder (@orders){
117         DelOrder($myorder->{biblionumber},$myorder->{ordernumber});
118     }
119 # if $delbiblio = 1, delete the records if possible
120     if ((defined $delbiblio)and ($delbiblio ==1)){
121         my @cannotdelbiblios ;
122         foreach my $myorder (@orders){
123             my $biblionumber = $myorder->{'biblionumber'};
124             my $countbiblio = CountBiblioInOrders($biblionumber);
125             my $ordernumber = $myorder->{'ordernumber'};
126             my $subscriptions = scalar GetSubscriptionsId ($biblionumber);
127             my $itemcount = GetItemsCount($biblionumber);
128             my $error;
129             if ($countbiblio == 0 && $itemcount == 0 && $subscriptions == 0) {
130                 $error = DelBiblio($myorder->{biblionumber}) }
131             else {
132                 push @cannotdelbiblios, {biblionumber=> ($myorder->{biblionumber}),
133                                          title=> $myorder->{'title'},
134                                          author=> $myorder->{'author'},
135                                          countbiblio=> $countbiblio,
136                                          itemcount=>$itemcount,
137                                          subscriptions=>$subscriptions};
138             }
139             if ($error) {
140                 push @cannotdelbiblios, {biblionumber=> ($myorder->{biblionumber}),
141                                          title=> $myorder->{'title'},
142                                          author=> $myorder->{'author'},
143                                          othererror=> $error};
144             }
145         }
146         $template->param( cannotdelbiblios => \@cannotdelbiblios );
147     }
148  # delete the basket
149     DelBasket($basketno,);
150     $template->param( delete_confirmed => 1 );
151 } elsif ( !$bookseller ) {
152     $template->param( NO_BOOKSELLER => 1 );
153 } elsif ( $op eq 'del_basket') {
154     $template->param( delete_confirm => 1 );
155     if ( C4::Context->preference("IndependentBranches") ) {
156         my $userenv = C4::Context->userenv;
157         unless ( $userenv->{flags} == 1 ) {
158             my $validtest = ( $basket->{creationdate} eq '' )
159               || ( $userenv->{branch} eq $basket->{branch} )
160               || ( $userenv->{branch} eq '' )
161               || ( $basket->{branch}  eq '' );
162             unless ($validtest) {
163                 print $query->redirect("../mainpage.pl");
164                 exit 1;
165             }
166         }
167     }
168     $basket->{creationdate} = ""            unless ( $basket->{creationdate} );
169     $basket->{authorisedby} = $loggedinuser unless ( $basket->{authorisedby} );
170     my $contract = &GetContract($basket->{contractnumber});
171     $template->param(
172         basketno             => $basketno,
173         basketname           => $basket->{'basketname'},
174         basketnote           => $basket->{note},
175         basketbooksellernote => $basket->{booksellernote},
176         basketcontractno     => $basket->{contractnumber},
177         basketcontractname   => $contract->{contractname},
178         creationdate         => $basket->{creationdate},
179         authorisedby         => $basket->{authorisedby},
180         authorisedbyname     => $basket->{authorisedbyname},
181         closedate            => $basket->{closedate},
182         deliveryplace        => $basket->{deliveryplace},
183         billingplace         => $basket->{billingplace},
184         active               => $bookseller->{'active'},
185         booksellerid         => $bookseller->{'id'},
186         name                 => $bookseller->{'name'},
187         address1             => $bookseller->{'address1'},
188         address2             => $bookseller->{'address2'},
189         address3             => $bookseller->{'address3'},
190         address4             => $bookseller->{'address4'},
191       );
192 } elsif ($op eq 'attachbasket' && $template->{'VARS'}->{'CAN_user_acquisition_group_manage'} == 1) {
193       print $query->redirect('/cgi-bin/koha/acqui/basketgroup.pl?basketno=' . $basket->{'basketno'} . '&op=attachbasket&booksellerid=' . $booksellerid);
194     # check if we have to "close" a basket before building page
195 } elsif ($op eq 'export') {
196     print $query->header(
197         -type       => 'text/csv',
198         -attachment => 'basket' . $basket->{'basketno'} . '.csv',
199     );
200     print GetBasketAsCSV($query->param('basketno'), $query);
201     exit;
202 } elsif ($op eq 'close') {
203     my $confirm = $query->param('confirm') || $confirm_pref eq '2';
204     if ($confirm) {
205         my $basketno = $query->param('basketno');
206         my $booksellerid = $query->param('booksellerid');
207         $basketno =~ /^\d+$/ and CloseBasket($basketno);
208         # if requested, create basket group, close it and attach the basket
209         if ($query->param('createbasketgroup')) {
210             my $branchcode;
211             if(C4::Context->userenv and C4::Context->userenv->{'branch'}
212               and C4::Context->userenv->{'branch'} ne "NO_LIBRARY_SET") {
213                 $branchcode = C4::Context->userenv->{'branch'};
214             }
215             my $basketgroupid = NewBasketgroup( { name => $basket->{basketname},
216                             booksellerid => $booksellerid,
217                             deliveryplace => $branchcode,
218                             billingplace => $branchcode,
219                             closed => 1,
220                             });
221             ModBasket( { basketno => $basketno,
222                          basketgroupid => $basketgroupid } );
223             print $query->redirect('/cgi-bin/koha/acqui/basketgroup.pl?booksellerid='.$booksellerid.'&closed=1');
224         } else {
225             print $query->redirect('/cgi-bin/koha/acqui/booksellers.pl?booksellerid=' . $booksellerid);
226         }
227         exit;
228     } else {
229     $template->param(
230         confirm_close   => "1",
231         booksellerid    => $booksellerid,
232         basketno        => $basket->{'basketno'},
233         basketname      => $basket->{'basketname'},
234         basketgroupname => $basket->{'basketname'},
235     );
236     }
237 } elsif ($op eq 'reopen') {
238     ReopenBasket($query->param('basketno'));
239     print $query->redirect('/cgi-bin/koha/acqui/basket.pl?basketno='.$basket->{'basketno'})
240 } elsif ( $op eq 'mod_users' ) {
241     my $basketusers_ids = $query->param('basketusers_ids');
242     my @basketusers = split( /:/, $basketusers_ids );
243     ModBasketUsers($basketno, @basketusers);
244     print $query->redirect("/cgi-bin/koha/acqui/basket.pl?basketno=$basketno");
245     exit;
246 } elsif ( $op eq 'mod_branch' ) {
247     my $branch = $query->param('branch');
248     $branch = undef if(defined $branch and $branch eq '');
249     ModBasket({
250         basketno => $basket->{basketno},
251         branch   => $branch
252     });
253     print $query->redirect("/cgi-bin/koha/acqui/basket.pl?basketno=$basketno");
254     exit;
255 } else {
256     my @branches_loop;
257     # get librarian branch...
258     if ( C4::Context->preference("IndependentBranches") ) {
259         my $userenv = C4::Context->userenv;
260         unless ( $userenv->{flags} == 1 ) {
261             my $validtest = ( $basket->{creationdate} eq '' )
262               || ( $userenv->{branch} eq $basket->{branch} )
263               || ( $userenv->{branch} eq '' )
264               || ( $basket->{branch}  eq '' );
265             unless ($validtest) {
266                 print $query->redirect("../mainpage.pl");
267                 exit 1;
268             }
269         }
270         if (!defined $basket->{branch} or $basket->{branch} eq $userenv->{branch}) {
271             push @branches_loop, {
272                 branchcode => $userenv->{branch},
273                 branchname => $userenv->{branchname},
274                 selected => 1,
275             };
276         }
277     } else {
278         # get branches
279         my $branches = C4::Branch::GetBranches;
280         my @branchcodes = sort {
281             $branches->{$a}->{branchname} cmp $branches->{$b}->{branchname}
282         } keys %$branches;
283         foreach my $branch (@branchcodes) {
284             my $selected = 0;
285             if (defined $basket->{branch}) {
286                 $selected = 1 if $branch eq $basket->{branch};
287             } else {
288                 $selected = 1 if $branch eq C4::Context->userenv->{branch};
289             }
290             push @branches_loop, {
291                 branchcode => $branch,
292                 branchname => $branches->{$branch}->{branchname},
293                 selected => $selected
294             };
295         }
296     }
297
298 #if the basket is closed,and the user has the permission to edit basketgroups, display a list of basketgroups
299     my ($basketgroup, $basketgroups);
300     my $staffuser = GetMember(borrowernumber => $loggedinuser);
301     if ($basket->{closedate} && haspermission($staffuser->{userid}, { acquisition => 'group_manage'} )) {
302         $basketgroups = GetBasketgroups($basket->{booksellerid});
303         for my $bg ( @{$basketgroups} ) {
304             if ($basket->{basketgroupid} && $basket->{basketgroupid} == $bg->{id}){
305                 $bg->{default} = 1;
306                 $basketgroup = $bg;
307             }
308         }
309         my %emptygroup = ( id   =>   undef,
310                            name =>   "No group");
311         if ( ! $basket->{basketgroupid} ) {
312             $emptygroup{default} = 1;
313             $emptygroup{nogroup} = 1;
314         }
315         unshift( @$basketgroups, \%emptygroup );
316     }
317
318     # if the basket is closed, calculate estimated delivery date
319     my $estimateddeliverydate;
320     if( $basket->{closedate} ) {
321         my ($year, $month, $day) = ($basket->{closedate} =~ /(\d+)-(\d+)-(\d+)/);
322         ($year, $month, $day) = Add_Delta_Days($year, $month, $day, $bookseller->{deliverytime});
323         $estimateddeliverydate = "$year-$month-$day";
324     }
325
326     # if new basket, pre-fill infos
327     $basket->{creationdate} = ""            unless ( $basket->{creationdate} );
328     $basket->{authorisedby} = $loggedinuser unless ( $basket->{authorisedby} );
329     $debug
330       and warn sprintf
331       "loggedinuser: $loggedinuser; creationdate: %s; authorisedby: %s",
332       $basket->{creationdate}, $basket->{authorisedby};
333
334     my @basketusers_ids = GetBasketUsers($basketno);
335     my @basketusers;
336     foreach my $basketuser_id (@basketusers_ids) {
337         my $basketuser = GetMember(borrowernumber => $basketuser_id);
338         push @basketusers, $basketuser if $basketuser;
339     }
340
341     #to get active currency
342     my $cur = GetCurrency();
343
344
345     my @results = GetOrders( $basketno );
346     my @books_loop;
347
348     my @book_foot_loop;
349     my %foot;
350     my $total_quantity = 0;
351     my $total_gste = 0;
352     my $total_gsti = 0;
353     my $total_gstvalue = 0;
354     for my $order (@results) {
355         my $line = get_order_infos( $order, $bookseller);
356         if ( $line->{uncertainprice} ) {
357             $template->param( uncertainprices => 1 );
358         }
359
360         push @books_loop, $line;
361
362         $foot{$$line{gstgsti}}{gstgsti} = $$line{gstgsti};
363         $foot{$$line{gstgsti}}{gstvalue} += $$line{gstvalue};
364         $total_gstvalue += $$line{gstvalue};
365         $foot{$$line{gstgsti}}{quantity}  += $$line{quantity};
366         $total_quantity += $$line{quantity};
367         $foot{$$line{gstgsti}}{totalgste} += $$line{totalgste};
368         $total_gste += $$line{totalgste};
369         $foot{$$line{gstgsti}}{totalgsti} += $$line{totalgsti};
370         $total_gsti += $$line{totalgsti};
371     }
372
373     push @book_foot_loop, map {$_} values %foot;
374
375     # Get cancelled orders
376     @results = GetCancelledOrders($basketno);
377     my @cancelledorders_loop;
378     for my $order (@results) {
379         my $line = get_order_infos( $order, $bookseller);
380         push @cancelledorders_loop, $line;
381     }
382
383     my $contract = &GetContract($basket->{contractnumber});
384     my @orders = GetOrders($basketno);
385
386     if ($basket->{basketgroupid}){
387         $basketgroup = GetBasketgroup($basket->{basketgroupid});
388         $basketgroup->{deliveryplacename} = C4::Branch::GetBranchName( $basketgroup->{deliveryplace} );
389         $basketgroup->{billingplacename} = C4::Branch::GetBranchName( $basketgroup->{billingplace} );
390     }
391     my $borrower= GetMember('borrowernumber' => $loggedinuser);
392     my $budgets = GetBudgetHierarchy;
393     my $has_budgets = 0;
394     foreach my $r (@{$budgets}) {
395         if (!defined $r->{budget_amount} || $r->{budget_amount} == 0) {
396             next;
397         }
398         next unless (CanUserUseBudget($loggedinuser, $r, $userflags));
399
400         $has_budgets = 1;
401         last;
402     }
403
404     $template->param(
405         basketno             => $basketno,
406         basketname           => $basket->{'basketname'},
407         basketbranchname     => C4::Branch::GetBranchName($basket->{branch}),
408         basketnote           => $basket->{note},
409         basketbooksellernote => $basket->{booksellernote},
410         basketcontractno     => $basket->{contractnumber},
411         basketcontractname   => $contract->{contractname},
412         branches_loop        => \@branches_loop,
413         creationdate         => $basket->{creationdate},
414         authorisedby         => $basket->{authorisedby},
415         authorisedbyname     => $basket->{authorisedbyname},
416         basketusers_ids      => join(':', @basketusers_ids),
417         basketusers          => \@basketusers,
418         closedate            => $basket->{closedate},
419         estimateddeliverydate=> $estimateddeliverydate,
420         deliveryplace        => C4::Branch::GetBranchName( $basket->{deliveryplace} ),
421         billingplace         => C4::Branch::GetBranchName( $basket->{billingplace} ),
422         active               => $bookseller->{'active'},
423         booksellerid         => $bookseller->{'id'},
424         name                 => $bookseller->{'name'},
425         books_loop           => \@books_loop,
426         book_foot_loop       => \@book_foot_loop,
427         cancelledorders_loop => \@cancelledorders_loop,
428         total_quantity       => $total_quantity,
429         total_gste           => sprintf( "%.2f", $total_gste ),
430         total_gsti           => sprintf( "%.2f", $total_gsti ),
431         total_gstvalue       => sprintf( "%.2f", $total_gstvalue ),
432         currency             => $cur->{'currency'},
433         listincgst           => $bookseller->{listincgst},
434         basketgroups         => $basketgroups,
435         basketgroup          => $basketgroup,
436         grouped              => $basket->{basketgroupid},
437         unclosable           => @orders ? 0 : 1, 
438         has_budgets          => $has_budgets,
439     );
440 }
441
442 sub get_order_infos {
443     my $order = shift;
444     my $bookseller = shift;
445     my $qty = $order->{'quantity'} || 0;
446     if ( !defined $order->{quantityreceived} ) {
447         $order->{quantityreceived} = 0;
448     }
449     my $budget = GetBudget( $order->{'budget_id'} );
450
451     my %line = %{ $order };
452     $line{order_received} = ( $qty == $order->{'quantityreceived'} );
453     $line{basketno}       = $basketno;
454     $line{budget_name}    = $budget->{budget_name};
455     $line{rrp} = ConvertCurrency( $order->{'currency'}, $line{rrp} ); # FIXME from comm
456     if ( $bookseller->{'listincgst'} ) {
457         $line{rrpgsti} = sprintf( "%.2f", $line{rrp} );
458         $line{gstgsti} = sprintf( "%.2f", $line{gstrate} * 100 );
459         $line{rrpgste} = sprintf( "%.2f", $line{rrp} / ( 1 + ( $line{gstgsti} / 100 ) ) );
460         $line{gstgste} = sprintf( "%.2f", $line{gstgsti} / ( 1 + ( $line{gstgsti} / 100 ) ) );
461         $line{ecostgsti} = sprintf( "%.2f", $line{ecost} );
462         $line{ecostgste} = sprintf( "%.2f", $line{ecost} / ( 1 + ( $line{gstgsti} / 100 ) ) );
463         $line{gstvalue} = sprintf( "%.2f", ( $line{ecostgsti} - $line{ecostgste} ) * $line{quantity});
464         $line{totalgste} = sprintf( "%.2f", $order->{quantity} * $line{ecostgste} );
465         $line{totalgsti} = sprintf( "%.2f", $order->{quantity} * $line{ecostgsti} );
466     } else {
467         $line{rrpgsti} = sprintf( "%.2f", $line{rrp} * ( 1 + ( $line{gstrate} ) ) );
468         $line{rrpgste} = sprintf( "%.2f", $line{rrp} );
469         $line{gstgsti} = sprintf( "%.2f", $line{gstrate} * 100 );
470         $line{gstgste} = sprintf( "%.2f", $line{gstrate} * 100 );
471         $line{ecostgsti} = sprintf( "%.2f", $line{ecost} * ( 1 + ( $line{gstrate} ) ) );
472         $line{ecostgste} = sprintf( "%.2f", $line{ecost} );
473         $line{gstvalue} = sprintf( "%.2f", ( $line{ecostgsti} - $line{ecostgste} ) * $line{quantity});
474         $line{totalgste} = sprintf( "%.2f", $order->{quantity} * $line{ecostgste} );
475         $line{totalgsti} = sprintf( "%.2f", $order->{quantity} * $line{ecostgsti} );
476     }
477
478     if ( $line{uncertainprice} ) {
479         $line{rrpgste} .= ' (Uncertain)';
480     }
481     if ( $line{'title'} ) {
482         my $volume      = $order->{'volume'};
483         my $seriestitle = $order->{'seriestitle'};
484         $line{'title'} .= " / $seriestitle" if $seriestitle;
485         $line{'title'} .= " / $volume"      if $volume;
486     } else {
487         $line{'title'} = "Deleted bibliographic notice, can't find title.";
488     }
489
490     my $biblionumber = $order->{'biblionumber'};
491     my $countbiblio = CountBiblioInOrders($biblionumber);
492     my $ordernumber = $order->{'ordernumber'};
493     my @subscriptions = GetSubscriptionsId ($biblionumber);
494     my $itemcount = GetItemsCount($biblionumber);
495     my $holds  = GetHolds ($biblionumber);
496     my @items = GetItemnumbersFromOrder( $ordernumber );
497     my $itemholds;
498     foreach my $item (@items){
499         my $nb = GetItemHolds($biblionumber, $item);
500         if ($nb){
501             $itemholds += $nb;
502         }
503     }
504     # 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
505     $line{can_del_bib}          = 1 if $countbiblio <= 1 && $itemcount == scalar @items && !(@subscriptions) && !($holds);
506     $line{items}                = ($itemcount) - (scalar @items);
507     $line{left_item}            = 1 if $line{items} >= 1;
508     $line{left_biblio}          = 1 if $countbiblio > 1;
509     $line{biblios}              = $countbiblio - 1;
510     $line{left_subscription}    = 1 if scalar @subscriptions >= 1;
511     $line{subscriptions}        = scalar @subscriptions;
512     ($holds >= 1) ? $line{left_holds} = 1 : $line{left_holds} = 0;
513     $line{left_holds_on_order}  = 1 if $line{left_holds}==1 && ($line{items} == 0 || $itemholds );
514     $line{holds}                = $holds;
515     $line{holds_on_order}       = $itemholds?$itemholds:$holds if $line{left_holds_on_order};
516
517
518     my $suggestion   = GetSuggestionInfoFromBiblionumber($line{biblionumber});
519     $line{suggestionid}         = $$suggestion{suggestionid};
520     $line{surnamesuggestedby}   = $$suggestion{surnamesuggestedby};
521     $line{firstnamesuggestedby} = $$suggestion{firstnamesuggestedby};
522
523     foreach my $key (qw(transferred_from transferred_to)) {
524         if ($line{$key}) {
525             my $order = GetOrder($line{$key});
526             my $basket = GetBasket($order->{basketno});
527             my $bookseller = GetBookSellerFromId($basket->{booksellerid});
528             $line{$key} = {
529                 order => $order,
530                 basket => $basket,
531                 bookseller => $bookseller,
532                 timestamp => $line{$key . '_timestamp'},
533             };
534         }
535     }
536
537     return \%line;
538 }
539
540 output_html_with_http_headers $query, $cookie, $template->output;