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