Bug 12074: Filter duplicates when adding a batch from a staged file
[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 strict;
24 use warnings;
25 use C4::Auth;
26 use C4::Koha;
27 use C4::Output;
28 use CGI qw ( -utf8 );
29 use C4::Acquisition;
30 use C4::Budgets;
31 use C4::Branch;
32 use C4::Contract;
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 my $duplinbatch =  $query->param('duplinbatch');
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 = Koha::Acquisition::Bookseller->fetch({ id => $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 'export') {
155     print $query->header(
156         -type       => 'text/csv',
157         -attachment => 'basket' . $basket->{'basketno'} . '.csv',
158     );
159     print GetBasketAsCSV($query->param('basketno'), $query);
160     exit;
161 } elsif ($op eq 'close') {
162     my $confirm = $query->param('confirm') || $confirm_pref eq '2';
163     if ($confirm) {
164         my $basketno = $query->param('basketno');
165         my $booksellerid = $query->param('booksellerid');
166         $basketno =~ /^\d+$/ and CloseBasket($basketno);
167         # if requested, create basket group, close it and attach the basket
168         if ($query->param('createbasketgroup')) {
169             my $branchcode;
170             if(C4::Context->userenv and C4::Context->userenv->{'branch'}
171               and C4::Context->userenv->{'branch'} ne "NO_LIBRARY_SET") {
172                 $branchcode = C4::Context->userenv->{'branch'};
173             }
174             my $basketgroupid = NewBasketgroup( { name => $basket->{basketname},
175                             booksellerid => $booksellerid,
176                             deliveryplace => $branchcode,
177                             billingplace => $branchcode,
178                             closed => 1,
179                             });
180             ModBasket( { basketno => $basketno,
181                          basketgroupid => $basketgroupid } );
182             print $query->redirect('/cgi-bin/koha/acqui/basketgroup.pl?booksellerid='.$booksellerid.'&closed=1');
183         } else {
184             print $query->redirect('/cgi-bin/koha/acqui/booksellers.pl?booksellerid=' . $booksellerid);
185         }
186         exit;
187     } else {
188     $template->param(
189         confirm_close   => "1",
190         booksellerid    => $booksellerid,
191         basketno        => $basket->{'basketno'},
192         basketname      => $basket->{'basketname'},
193         basketgroupname => $basket->{'basketname'},
194     );
195     }
196 } elsif ($op eq 'reopen') {
197     ReopenBasket($query->param('basketno'));
198     print $query->redirect('/cgi-bin/koha/acqui/basket.pl?basketno='.$basket->{'basketno'})
199 } elsif ( $op eq 'mod_users' ) {
200     my $basketusers_ids = $query->param('users_ids');
201     my @basketusers = split( /:/, $basketusers_ids );
202     ModBasketUsers($basketno, @basketusers);
203     print $query->redirect("/cgi-bin/koha/acqui/basket.pl?basketno=$basketno");
204     exit;
205 } elsif ( $op eq 'mod_branch' ) {
206     my $branch = $query->param('branch');
207     $branch = undef if(defined $branch and $branch eq '');
208     ModBasket({
209         basketno => $basket->{basketno},
210         branch   => $branch
211     });
212     print $query->redirect("/cgi-bin/koha/acqui/basket.pl?basketno=$basketno");
213     exit;
214 } else {
215     my @branches_loop;
216     # get librarian branch...
217     if ( C4::Context->preference("IndependentBranches") ) {
218         my $userenv = C4::Context->userenv;
219         unless ( C4::Context->IsSuperLibrarian() ) {
220             my $validtest = ( $basket->{creationdate} eq '' )
221               || ( $userenv->{branch} eq $basket->{branch} )
222               || ( $userenv->{branch} eq '' )
223               || ( $basket->{branch}  eq '' );
224             unless ($validtest) {
225                 print $query->redirect("../mainpage.pl");
226                 exit 1;
227             }
228         }
229         if (!defined $basket->{branch} or $basket->{branch} eq $userenv->{branch}) {
230             push @branches_loop, {
231                 branchcode => $userenv->{branch},
232                 branchname => $userenv->{branchname},
233                 selected => 1,
234             };
235         }
236     } else {
237         # get branches
238         my $branches = C4::Branch::GetBranches;
239         my @branchcodes = sort {
240             $branches->{$a}->{branchname} cmp $branches->{$b}->{branchname}
241         } keys %$branches;
242         foreach my $branch (@branchcodes) {
243             my $selected = 0;
244             if (defined $basket->{branch}) {
245                 $selected = 1 if $branch eq $basket->{branch};
246             } else {
247                 $selected = 1 if $branch eq C4::Context->userenv->{branch};
248             }
249             push @branches_loop, {
250                 branchcode => $branch,
251                 branchname => $branches->{$branch}->{branchname},
252                 selected => $selected
253             };
254         }
255     }
256
257 #if the basket is closed,and the user has the permission to edit basketgroups, display a list of basketgroups
258     my ($basketgroup, $basketgroups);
259     my $staffuser = GetMember(borrowernumber => $loggedinuser);
260     if ($basket->{closedate} && haspermission($staffuser->{userid}, { acquisition => 'group_manage'} )) {
261         $basketgroups = GetBasketgroups($basket->{booksellerid});
262         for my $bg ( @{$basketgroups} ) {
263             if ($basket->{basketgroupid} && $basket->{basketgroupid} == $bg->{id}){
264                 $bg->{default} = 1;
265                 $basketgroup = $bg;
266             }
267         }
268     }
269
270     # if the basket is closed, calculate estimated delivery date
271     my $estimateddeliverydate;
272     if( $basket->{closedate} ) {
273         my ($year, $month, $day) = ($basket->{closedate} =~ /(\d+)-(\d+)-(\d+)/);
274         ($year, $month, $day) = Add_Delta_Days($year, $month, $day, $bookseller->{deliverytime});
275         $estimateddeliverydate = sprintf( "%04d-%02d-%02d", $year, $month, $day );
276     }
277
278     # if new basket, pre-fill infos
279     $basket->{creationdate} = ""            unless ( $basket->{creationdate} );
280     $basket->{authorisedby} = $loggedinuser unless ( $basket->{authorisedby} );
281     $debug
282       and warn sprintf
283       "loggedinuser: $loggedinuser; creationdate: %s; authorisedby: %s",
284       $basket->{creationdate}, $basket->{authorisedby};
285
286     my @basketusers_ids = GetBasketUsers($basketno);
287     my @basketusers;
288     foreach my $basketuser_id (@basketusers_ids) {
289         my $basketuser = GetMember(borrowernumber => $basketuser_id);
290         push @basketusers, $basketuser if $basketuser;
291     }
292
293     #to get active currency
294     my $cur = GetCurrency();
295
296
297     my @orders = GetOrders( $basketno );
298     my @books_loop;
299
300     my @book_foot_loop;
301     my %foot;
302     my $total_quantity = 0;
303     my $total_gste = 0;
304     my $total_gsti = 0;
305     my $total_gstvalue = 0;
306     for my $order (@orders) {
307         $order = C4::Acquisition::populate_order_with_prices({ order => $order, booksellerid => $booksellerid, ordering => 1 });
308         my $line = get_order_infos( $order, $bookseller);
309         if ( $line->{uncertainprice} ) {
310             $template->param( uncertainprices => 1 );
311         }
312
313         push @books_loop, $line;
314
315         $foot{$$line{gstrate}}{gstrate} = $$line{gstrate};
316         $foot{$$line{gstrate}}{gstvalue} += $$line{gstvalue};
317         $total_gstvalue += $$line{gstvalue};
318         $foot{$$line{gstrate}}{quantity}  += $$line{quantity};
319         $total_quantity += $$line{quantity};
320         $foot{$$line{gstrate}}{totalgste} += $$line{totalgste};
321         $total_gste += $$line{totalgste};
322         $foot{$$line{gstrate}}{totalgsti} += $$line{totalgsti};
323         $total_gsti += $$line{totalgsti};
324     }
325
326     push @book_foot_loop, map {$_} values %foot;
327
328     # Get cancelled orders
329     my @cancelledorders = GetOrders($basketno, { cancelled => 1 });
330     my @cancelledorders_loop;
331     for my $order (@cancelledorders) {
332         $order = C4::Acquisition::populate_order_with_prices({ order => $order, booksellerid => $booksellerid, ordering => 1 });
333         my $line = get_order_infos( $order, $bookseller);
334         push @cancelledorders_loop, $line;
335     }
336
337     my $contract = GetContract({
338         contractnumber => $basket->{contractnumber}
339     });
340
341     if ($basket->{basketgroupid}){
342         $basketgroup = GetBasketgroup($basket->{basketgroupid});
343         $basketgroup->{deliveryplacename} = C4::Branch::GetBranchName( $basketgroup->{deliveryplace} );
344         $basketgroup->{billingplacename} = C4::Branch::GetBranchName( $basketgroup->{billingplace} );
345     }
346     my $borrower= GetMember('borrowernumber' => $loggedinuser);
347     my $budgets = GetBudgetHierarchy;
348     my $has_budgets = 0;
349     foreach my $r (@{$budgets}) {
350         if (!defined $r->{budget_amount} || $r->{budget_amount} == 0) {
351             next;
352         }
353         next unless (CanUserUseBudget($loggedinuser, $r, $userflags));
354
355         $has_budgets = 1;
356         last;
357     }
358
359     $template->param(
360         basketno             => $basketno,
361         basketname           => $basket->{'basketname'},
362         basketbranchname     => C4::Branch::GetBranchName($basket->{branch}),
363         basketnote           => $basket->{note},
364         basketbooksellernote => $basket->{booksellernote},
365         basketcontractno     => $basket->{contractnumber},
366         basketcontractname   => $contract->{contractname},
367         branches_loop        => \@branches_loop,
368         creationdate         => $basket->{creationdate},
369         authorisedby         => $basket->{authorisedby},
370         authorisedbyname     => $basket->{authorisedbyname},
371         users_ids            => join(':', @basketusers_ids),
372         users                => \@basketusers,
373         closedate            => $basket->{closedate},
374         estimateddeliverydate=> $estimateddeliverydate,
375         deliveryplace        => C4::Branch::GetBranchName( $basket->{deliveryplace} ),
376         billingplace         => C4::Branch::GetBranchName( $basket->{billingplace} ),
377         active               => $bookseller->{'active'},
378         booksellerid         => $bookseller->{'id'},
379         name                 => $bookseller->{'name'},
380         books_loop           => \@books_loop,
381         book_foot_loop       => \@book_foot_loop,
382         cancelledorders_loop => \@cancelledorders_loop,
383         total_quantity       => $total_quantity,
384         total_gste           => sprintf( "%.2f", $total_gste ),
385         total_gsti           => sprintf( "%.2f", $total_gsti ),
386         total_gstvalue       => sprintf( "%.2f", $total_gstvalue ),
387         currency             => $cur->{'currency'},
388         listincgst           => $bookseller->{listincgst},
389         basketgroups         => $basketgroups,
390         basketgroup          => $basketgroup,
391         grouped              => $basket->{basketgroupid},
392         unclosable           => @orders ? 0 : 1, 
393         has_budgets          => $has_budgets,
394         duplinbatch          => $duplinbatch,
395     );
396 }
397
398 sub get_order_infos {
399     my $order = shift;
400     my $bookseller = shift;
401     my $qty = $order->{'quantity'} || 0;
402     if ( !defined $order->{quantityreceived} ) {
403         $order->{quantityreceived} = 0;
404     }
405     my $budget = GetBudget( $order->{'budget_id'} );
406
407     my %line = %{ $order };
408     $line{order_received} = ( $qty == $order->{'quantityreceived'} );
409     $line{basketno}       = $basketno;
410     $line{budget_name}    = $budget->{budget_name};
411
412     if ( $line{uncertainprice} ) {
413         $line{rrpgste} .= ' (Uncertain)';
414     }
415     if ( $line{'title'} ) {
416         my $volume      = $order->{'volume'};
417         my $seriestitle = $order->{'seriestitle'};
418         $line{'title'} .= " / $seriestitle" if $seriestitle;
419         $line{'title'} .= " / $volume"      if $volume;
420     } else {
421         $line{'title'} = "Deleted bibliographic notice, can't find title.";
422     }
423
424     my $biblionumber = $order->{'biblionumber'};
425     my $countbiblio = CountBiblioInOrders($biblionumber);
426     my $ordernumber = $order->{'ordernumber'};
427     my @subscriptions = GetSubscriptionsId ($biblionumber);
428     my $itemcount = GetItemsCount($biblionumber);
429     my $holds  = GetHolds ($biblionumber);
430     my @items = GetItemnumbersFromOrder( $ordernumber );
431     my $itemholds;
432     foreach my $item (@items){
433         my $nb = GetItemHolds($biblionumber, $item);
434         if ($nb){
435             $itemholds += $nb;
436         }
437     }
438     # 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
439     $line{can_del_bib}          = 1 if $countbiblio <= 1 && $itemcount == scalar @items && !(@subscriptions) && !($holds);
440     $line{items}                = ($itemcount) - (scalar @items);
441     $line{left_item}            = 1 if $line{items} >= 1;
442     $line{left_biblio}          = 1 if $countbiblio > 1;
443     $line{biblios}              = $countbiblio - 1;
444     $line{left_subscription}    = 1 if scalar @subscriptions >= 1;
445     $line{subscriptions}        = scalar @subscriptions;
446     ($holds >= 1) ? $line{left_holds} = 1 : $line{left_holds} = 0;
447     $line{left_holds_on_order}  = 1 if $line{left_holds}==1 && ($line{items} == 0 || $itemholds );
448     $line{holds}                = $holds;
449     $line{holds_on_order}       = $itemholds?$itemholds:$holds if $line{left_holds_on_order};
450
451
452     my $suggestion   = GetSuggestionInfoFromBiblionumber($line{biblionumber});
453     $line{suggestionid}         = $$suggestion{suggestionid};
454     $line{surnamesuggestedby}   = $$suggestion{surnamesuggestedby};
455     $line{firstnamesuggestedby} = $$suggestion{firstnamesuggestedby};
456
457     foreach my $key (qw(transferred_from transferred_to)) {
458         if ($line{$key}) {
459             my $order = GetOrder($line{$key});
460             my $basket = GetBasket($order->{basketno});
461             my $bookseller = Koha::Acquisition::Bookseller->fetch({ id => $basket->{booksellerid} });
462             $line{$key} = {
463                 order => $order,
464                 basket => $basket,
465                 bookseller => $bookseller,
466                 timestamp => $line{$key . '_timestamp'},
467             };
468         }
469     }
470
471     return \%line;
472 }
473
474 output_html_with_http_headers $query, $cookie, $template->output;