Bug 13276: use t::lib::Mocks::mock_dbh
[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::Contract;
34 use C4::Debug;
35 use C4::Biblio;
36 use C4::Members qw/GetMember/;  #needed for permissions checking for changing basketgroup of a basket
37 use C4::Items;
38 use C4::Suggestions;
39 use Date::Calc qw/Add_Delta_Days/;
40
41 =head1 NAME
42
43 basket.pl
44
45 =head1 DESCRIPTION
46
47  This script display all informations about basket for the supplier given
48  on input arg.  Moreover, it allows us to add a new order for this supplier from
49  an existing record, a suggestion or a new record.
50
51 =head1 CGI PARAMETERS
52
53 =over 4
54
55 =item $basketno
56
57 The basket number.
58
59 =item booksellerid
60
61 the supplier this script have to display the basket.
62
63 =item order
64
65 =back
66
67 =cut
68
69 my $query        = new CGI;
70 our $basketno     = $query->param('basketno');
71 my $booksellerid = $query->param('booksellerid');
72
73 my ( $template, $loggedinuser, $cookie, $userflags ) = get_template_and_user(
74     {
75         template_name   => "acqui/basket.tt",
76         query           => $query,
77         type            => "intranet",
78         authnotrequired => 0,
79         flagsrequired   => { acquisition => 'order_manage' },
80         debug           => 1,
81     }
82 );
83
84 my $basket = GetBasket($basketno);
85 $booksellerid = $basket->{booksellerid} unless $booksellerid;
86 my ($bookseller) = GetBookSellerFromId($booksellerid);
87
88 unless (CanUserManageBasket($loggedinuser, $basket, $userflags)) {
89     $template->param(
90         cannot_manage_basket => 1,
91         basketno => $basketno,
92         basketname => $basket->{basketname},
93         booksellerid => $booksellerid,
94         name => $bookseller->{name}
95     );
96     output_html_with_http_headers $query, $cookie, $template->output;
97     exit;
98 }
99
100 # FIXME : what about the "discount" percentage?
101 # FIXME : the query->param('booksellerid') below is probably useless. The bookseller is always known from the basket
102 # if no booksellerid in parameter, get it from basket
103 # warn "=>".$basket->{booksellerid};
104 my $op = $query->param('op');
105 if (!defined $op) {
106     $op = q{};
107 }
108
109 my $confirm_pref= C4::Context->preference("BasketConfirmations") || '1';
110 $template->param( skip_confirm_reopen => 1) if $confirm_pref eq '2';
111
112 if ( $op eq 'delete_confirm' ) {
113     my $basketno = $query->param('basketno');
114     my $delbiblio = $query->param('delbiblio');
115     my @orders = GetOrders($basketno);
116 #Delete all orders included in that basket, and all items received.
117     foreach my $myorder (@orders){
118         DelOrder($myorder->{biblionumber},$myorder->{ordernumber});
119     }
120 # if $delbiblio = 1, delete the records if possible
121     if ((defined $delbiblio)and ($delbiblio ==1)){
122         my @cannotdelbiblios ;
123         foreach my $myorder (@orders){
124             my $biblionumber = $myorder->{'biblionumber'};
125             my $countbiblio = CountBiblioInOrders($biblionumber);
126             my $ordernumber = $myorder->{'ordernumber'};
127             my $subscriptions = scalar GetSubscriptionsId ($biblionumber);
128             my $itemcount = GetItemsCount($biblionumber);
129             my $error;
130             if ($countbiblio == 0 && $itemcount == 0 && $subscriptions == 0) {
131                 $error = DelBiblio($myorder->{biblionumber}) }
132             else {
133                 push @cannotdelbiblios, {biblionumber=> ($myorder->{biblionumber}),
134                                          title=> $myorder->{'title'},
135                                          author=> $myorder->{'author'},
136                                          countbiblio=> $countbiblio,
137                                          itemcount=>$itemcount,
138                                          subscriptions=>$subscriptions};
139             }
140             if ($error) {
141                 push @cannotdelbiblios, {biblionumber=> ($myorder->{biblionumber}),
142                                          title=> $myorder->{'title'},
143                                          author=> $myorder->{'author'},
144                                          othererror=> $error};
145             }
146         }
147         $template->param( cannotdelbiblios => \@cannotdelbiblios );
148     }
149  # delete the basket
150     DelBasket($basketno,);
151     $template->param( delete_confirmed => 1 );
152 } elsif ( !$bookseller ) {
153     $template->param( NO_BOOKSELLER => 1 );
154 } elsif ( $op eq 'del_basket') {
155     $template->param( delete_confirm => 1 );
156     if ( C4::Context->preference("IndependentBranches") ) {
157         my $userenv = C4::Context->userenv;
158         unless ( C4::Context->IsSuperLibrarian() ) {
159             my $validtest = ( $basket->{creationdate} eq '' )
160               || ( $userenv->{branch} eq $basket->{branch} )
161               || ( $userenv->{branch} eq '' )
162               || ( $basket->{branch}  eq '' );
163             unless ($validtest) {
164                 print $query->redirect("../mainpage.pl");
165                 exit 1;
166             }
167         }
168     }
169     $basket->{creationdate} = ""            unless ( $basket->{creationdate} );
170     $basket->{authorisedby} = $loggedinuser unless ( $basket->{authorisedby} );
171     my $contract = GetContract({
172         contractnumber => $basket->{contractnumber}
173     });
174     $template->param(
175         basketno             => $basketno,
176         basketname           => $basket->{'basketname'},
177         basketnote           => $basket->{note},
178         basketbooksellernote => $basket->{booksellernote},
179         basketcontractno     => $basket->{contractnumber},
180         basketcontractname   => $contract->{contractname},
181         creationdate         => $basket->{creationdate},
182         authorisedby         => $basket->{authorisedby},
183         authorisedbyname     => $basket->{authorisedbyname},
184         closedate            => $basket->{closedate},
185         deliveryplace        => $basket->{deliveryplace},
186         billingplace         => $basket->{billingplace},
187         active               => $bookseller->{'active'},
188         booksellerid         => $bookseller->{'id'},
189         name                 => $bookseller->{'name'},
190         address1             => $bookseller->{'address1'},
191         address2             => $bookseller->{'address2'},
192         address3             => $bookseller->{'address3'},
193         address4             => $bookseller->{'address4'},
194       );
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 ( C4::Context->IsSuperLibrarian() ) {
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     }
310
311     # if the basket is closed, calculate estimated delivery date
312     my $estimateddeliverydate;
313     if( $basket->{closedate} ) {
314         my ($year, $month, $day) = ($basket->{closedate} =~ /(\d+)-(\d+)-(\d+)/);
315         ($year, $month, $day) = Add_Delta_Days($year, $month, $day, $bookseller->{deliverytime});
316         $estimateddeliverydate = "$year-$month-$day";
317     }
318
319     # if new basket, pre-fill infos
320     $basket->{creationdate} = ""            unless ( $basket->{creationdate} );
321     $basket->{authorisedby} = $loggedinuser unless ( $basket->{authorisedby} );
322     $debug
323       and warn sprintf
324       "loggedinuser: $loggedinuser; creationdate: %s; authorisedby: %s",
325       $basket->{creationdate}, $basket->{authorisedby};
326
327     my @basketusers_ids = GetBasketUsers($basketno);
328     my @basketusers;
329     foreach my $basketuser_id (@basketusers_ids) {
330         my $basketuser = GetMember(borrowernumber => $basketuser_id);
331         push @basketusers, $basketuser if $basketuser;
332     }
333
334     #to get active currency
335     my $cur = GetCurrency();
336
337
338     my @results = GetOrders( $basketno );
339     my @books_loop;
340
341     my @book_foot_loop;
342     my %foot;
343     my $total_quantity = 0;
344     my $total_gste = 0;
345     my $total_gsti = 0;
346     my $total_gstvalue = 0;
347     for my $order (@results) {
348         my $line = get_order_infos( $order, $bookseller);
349         if ( $line->{uncertainprice} ) {
350             $template->param( uncertainprices => 1 );
351         }
352
353         push @books_loop, $line;
354
355         $foot{$$line{gstgsti}}{gstgsti} = $$line{gstgsti};
356         $foot{$$line{gstgsti}}{gstvalue} += $$line{gstvalue};
357         $total_gstvalue += $$line{gstvalue};
358         $foot{$$line{gstgsti}}{quantity}  += $$line{quantity};
359         $total_quantity += $$line{quantity};
360         $foot{$$line{gstgsti}}{totalgste} += $$line{totalgste};
361         $total_gste += $$line{totalgste};
362         $foot{$$line{gstgsti}}{totalgsti} += $$line{totalgsti};
363         $total_gsti += $$line{totalgsti};
364     }
365
366     push @book_foot_loop, map {$_} values %foot;
367
368     # Get cancelled orders
369     @results = GetCancelledOrders($basketno);
370     my @cancelledorders_loop;
371     for my $order (@results) {
372         my $line = get_order_infos( $order, $bookseller);
373         push @cancelledorders_loop, $line;
374     }
375
376     my $contract = GetContract({
377         contractnumber => $basket->{contractnumber}
378     });
379     my @orders = GetOrders($basketno);
380
381     if ($basket->{basketgroupid}){
382         $basketgroup = GetBasketgroup($basket->{basketgroupid});
383         $basketgroup->{deliveryplacename} = C4::Branch::GetBranchName( $basketgroup->{deliveryplace} );
384         $basketgroup->{billingplacename} = C4::Branch::GetBranchName( $basketgroup->{billingplace} );
385     }
386     my $borrower= GetMember('borrowernumber' => $loggedinuser);
387     my $budgets = GetBudgetHierarchy;
388     my $has_budgets = 0;
389     foreach my $r (@{$budgets}) {
390         if (!defined $r->{budget_amount} || $r->{budget_amount} == 0) {
391             next;
392         }
393         next unless (CanUserUseBudget($loggedinuser, $r, $userflags));
394
395         $has_budgets = 1;
396         last;
397     }
398
399     $template->param(
400         basketno             => $basketno,
401         basketname           => $basket->{'basketname'},
402         basketbranchname     => C4::Branch::GetBranchName($basket->{branch}),
403         basketnote           => $basket->{note},
404         basketbooksellernote => $basket->{booksellernote},
405         basketcontractno     => $basket->{contractnumber},
406         basketcontractname   => $contract->{contractname},
407         branches_loop        => \@branches_loop,
408         creationdate         => $basket->{creationdate},
409         authorisedby         => $basket->{authorisedby},
410         authorisedbyname     => $basket->{authorisedbyname},
411         basketusers_ids      => join(':', @basketusers_ids),
412         basketusers          => \@basketusers,
413         closedate            => $basket->{closedate},
414         estimateddeliverydate=> $estimateddeliverydate,
415         deliveryplace        => C4::Branch::GetBranchName( $basket->{deliveryplace} ),
416         billingplace         => C4::Branch::GetBranchName( $basket->{billingplace} ),
417         active               => $bookseller->{'active'},
418         booksellerid         => $bookseller->{'id'},
419         name                 => $bookseller->{'name'},
420         books_loop           => \@books_loop,
421         book_foot_loop       => \@book_foot_loop,
422         cancelledorders_loop => \@cancelledorders_loop,
423         total_quantity       => $total_quantity,
424         total_gste           => sprintf( "%.2f", $total_gste ),
425         total_gsti           => sprintf( "%.2f", $total_gsti ),
426         total_gstvalue       => sprintf( "%.2f", $total_gstvalue ),
427         currency             => $cur->{'currency'},
428         listincgst           => $bookseller->{listincgst},
429         basketgroups         => $basketgroups,
430         basketgroup          => $basketgroup,
431         grouped              => $basket->{basketgroupid},
432         unclosable           => @orders ? 0 : 1, 
433         has_budgets          => $has_budgets,
434     );
435 }
436
437 sub get_order_infos {
438     my $order = shift;
439     my $bookseller = shift;
440     my $qty = $order->{'quantity'} || 0;
441     if ( !defined $order->{quantityreceived} ) {
442         $order->{quantityreceived} = 0;
443     }
444     my $budget = GetBudget( $order->{'budget_id'} );
445
446     my %line = %{ $order };
447     $line{order_received} = ( $qty == $order->{'quantityreceived'} );
448     $line{basketno}       = $basketno;
449     $line{budget_name}    = $budget->{budget_name};
450     $line{rrp} = ConvertCurrency( $order->{'currency'}, $line{rrp} ); # FIXME from comm
451     if ( $bookseller->{'listincgst'} ) {
452         $line{rrpgsti} = sprintf( "%.2f", $line{rrp} );
453         $line{gstgsti} = sprintf( "%.2f", $line{gstrate} * 100 );
454         $line{rrpgste} = sprintf( "%.2f", $line{rrp} / ( 1 + ( $line{gstgsti} / 100 ) ) );
455         $line{gstgste} = sprintf( "%.2f", $line{gstgsti} / ( 1 + ( $line{gstgsti} / 100 ) ) );
456         $line{ecostgsti} = sprintf( "%.2f", $line{ecost} );
457         $line{ecostgste} = sprintf( "%.2f", $line{ecost} / ( 1 + ( $line{gstgsti} / 100 ) ) );
458         $line{gstvalue} = sprintf( "%.2f", ( $line{ecostgsti} - $line{ecostgste} ) * $line{quantity});
459         $line{totalgste} = sprintf( "%.2f", $order->{quantity} * $line{ecostgste} );
460         $line{totalgsti} = sprintf( "%.2f", $order->{quantity} * $line{ecostgsti} );
461     } else {
462         $line{rrpgsti} = sprintf( "%.2f", $line{rrp} * ( 1 + ( $line{gstrate} ) ) );
463         $line{rrpgste} = sprintf( "%.2f", $line{rrp} );
464         $line{gstgsti} = sprintf( "%.2f", $line{gstrate} * 100 );
465         $line{gstgste} = sprintf( "%.2f", $line{gstrate} * 100 );
466         $line{ecostgsti} = sprintf( "%.2f", $line{ecost} * ( 1 + ( $line{gstrate} ) ) );
467         $line{ecostgste} = sprintf( "%.2f", $line{ecost} );
468         $line{gstvalue} = sprintf( "%.2f", ( $line{ecostgsti} - $line{ecostgste} ) * $line{quantity});
469         $line{totalgste} = sprintf( "%.2f", $order->{quantity} * $line{ecostgste} );
470         $line{totalgsti} = sprintf( "%.2f", $order->{quantity} * $line{ecostgsti} );
471     }
472
473     if ( $line{uncertainprice} ) {
474         $line{rrpgste} .= ' (Uncertain)';
475     }
476     if ( $line{'title'} ) {
477         my $volume      = $order->{'volume'};
478         my $seriestitle = $order->{'seriestitle'};
479         $line{'title'} .= " / $seriestitle" if $seriestitle;
480         $line{'title'} .= " / $volume"      if $volume;
481     } else {
482         $line{'title'} = "Deleted bibliographic notice, can't find title.";
483     }
484
485     my $biblionumber = $order->{'biblionumber'};
486     my $countbiblio = CountBiblioInOrders($biblionumber);
487     my $ordernumber = $order->{'ordernumber'};
488     my @subscriptions = GetSubscriptionsId ($biblionumber);
489     my $itemcount = GetItemsCount($biblionumber);
490     my $holds  = GetHolds ($biblionumber);
491     my @items = GetItemnumbersFromOrder( $ordernumber );
492     my $itemholds;
493     foreach my $item (@items){
494         my $nb = GetItemHolds($biblionumber, $item);
495         if ($nb){
496             $itemholds += $nb;
497         }
498     }
499     # 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
500     $line{can_del_bib}          = 1 if $countbiblio <= 1 && $itemcount == scalar @items && !(@subscriptions) && !($holds);
501     $line{items}                = ($itemcount) - (scalar @items);
502     $line{left_item}            = 1 if $line{items} >= 1;
503     $line{left_biblio}          = 1 if $countbiblio > 1;
504     $line{biblios}              = $countbiblio - 1;
505     $line{left_subscription}    = 1 if scalar @subscriptions >= 1;
506     $line{subscriptions}        = scalar @subscriptions;
507     ($holds >= 1) ? $line{left_holds} = 1 : $line{left_holds} = 0;
508     $line{left_holds_on_order}  = 1 if $line{left_holds}==1 && ($line{items} == 0 || $itemholds );
509     $line{holds}                = $holds;
510     $line{holds_on_order}       = $itemholds?$itemholds:$holds if $line{left_holds_on_order};
511
512
513     my $suggestion   = GetSuggestionInfoFromBiblionumber($line{biblionumber});
514     $line{suggestionid}         = $$suggestion{suggestionid};
515     $line{surnamesuggestedby}   = $$suggestion{surnamesuggestedby};
516     $line{firstnamesuggestedby} = $$suggestion{firstnamesuggestedby};
517
518     foreach my $key (qw(transferred_from transferred_to)) {
519         if ($line{$key}) {
520             my $order = GetOrder($line{$key});
521             my $basket = GetBasket($order->{basketno});
522             my $bookseller = GetBookSellerFromId($basket->{booksellerid});
523             $line{$key} = {
524                 order => $order,
525                 basket => $basket,
526                 bookseller => $bookseller,
527                 timestamp => $line{$key . '_timestamp'},
528             };
529         }
530     }
531
532     return \%line;
533 }
534
535 output_html_with_http_headers $query, $cookie, $template->output;